feat: add conditional multi-level leave approval
This commit is contained in:
+13
@@ -20,6 +20,7 @@ import com.all8ai.aioa.workflow.domain.LeaveWorkflowGateway
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.MessageDigest
|
||||
import java.time.Instant
|
||||
import java.time.Duration
|
||||
import java.util.UUID
|
||||
|
||||
@Service
|
||||
@@ -71,11 +72,21 @@ class LeaveRequestService(
|
||||
|
||||
@Transactional
|
||||
fun submit(actor: CurrentUser, id: UUID, idempotencyKey: String, expectedVersion: Long): LeaveRequest {
|
||||
val existing = getOwn(actor, id)
|
||||
val durationMinutes = Duration.between(existing.startsAt, existing.endsAt).toMinutes()
|
||||
val approverId = routingRepository.findDepartmentManager(actor.tenantId, actor.id)
|
||||
?: throw ApiException(HttpStatus.CONFLICT, "APPROVER_NOT_FOUND", "未找到当前部门的有效主管")
|
||||
if (approverId == actor.id) {
|
||||
throw ApiException(HttpStatus.CONFLICT, "SELF_APPROVAL_NOT_ALLOWED", "申请人不能审批自己的申请")
|
||||
}
|
||||
val oaAdministratorId = routingRepository.findOaAdministrator(actor.tenantId)
|
||||
?: throw ApiException(HttpStatus.CONFLICT, "OA_ADMIN_NOT_FOUND", "未找到有效的 OA 管理员")
|
||||
if (durationMinutes > 1440 && oaAdministratorId == actor.id) {
|
||||
throw ApiException(HttpStatus.CONFLICT, "SELF_APPROVAL_NOT_ALLOWED", "申请人不能复核自己的长期请假")
|
||||
}
|
||||
if (durationMinutes > 1440 && oaAdministratorId == approverId) {
|
||||
throw ApiException(HttpStatus.CONFLICT, "APPROVER_SEPARATION_REQUIRED", "长期请假的主管和 OA 复核人必须为不同人员")
|
||||
}
|
||||
val outcome = transition(
|
||||
actor = actor,
|
||||
id = id,
|
||||
@@ -93,6 +104,8 @@ class LeaveRequestService(
|
||||
outcome.leaveRequest.id,
|
||||
actor.id,
|
||||
approverId,
|
||||
oaAdministratorId,
|
||||
durationMinutes,
|
||||
)
|
||||
repository.attachWorkflow(
|
||||
actor.tenantId,
|
||||
|
||||
+3
-1
@@ -2,6 +2,8 @@ package com.all8ai.aioa.approval.domain
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
fun interface ApprovalRoutingRepository {
|
||||
interface ApprovalRoutingRepository {
|
||||
fun findDepartmentManager(tenantId: UUID, applicantId: UUID): UUID?
|
||||
|
||||
fun findOaAdministrator(tenantId: UUID): UUID?
|
||||
}
|
||||
|
||||
+23
@@ -39,4 +39,27 @@ class JooqApprovalRoutingRepository(
|
||||
tenantId,
|
||||
applicantId,
|
||||
)?.get("user_id", UUID::class.java)
|
||||
|
||||
override fun findOaAdministrator(tenantId: UUID): UUID? =
|
||||
dsl.fetchOne(
|
||||
"""
|
||||
SELECT ur.user_id
|
||||
FROM authz.user_role ur
|
||||
JOIN authz.role role
|
||||
ON role.tenant_id = ur.tenant_id
|
||||
AND role.id = ur.role_id
|
||||
AND role.code = 'oa_admin'
|
||||
AND role.status = 'ACTIVE'
|
||||
JOIN identity.user_account account
|
||||
ON account.tenant_id = ur.tenant_id
|
||||
AND account.id = ur.user_id
|
||||
AND account.status = 'ACTIVE'
|
||||
WHERE ur.tenant_id = ?
|
||||
AND ur.effective_from <= CURRENT_TIMESTAMP
|
||||
AND (ur.effective_until IS NULL OR ur.effective_until > CURRENT_TIMESTAMP)
|
||||
ORDER BY ur.effective_from, ur.user_id
|
||||
LIMIT 1
|
||||
""".trimIndent(),
|
||||
tenantId,
|
||||
)?.get("user_id", UUID::class.java)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ interface LeaveWorkflowGateway {
|
||||
leaveRequestId: UUID,
|
||||
applicantId: UUID,
|
||||
approverId: UUID,
|
||||
oaAdministratorId: UUID,
|
||||
durationMinutes: Long,
|
||||
): StartedProcess
|
||||
|
||||
fun listAssignedTasks(assigneeId: UUID): List<WorkflowTask>
|
||||
|
||||
+4
@@ -22,6 +22,8 @@ class FlowableLeaveWorkflowGateway(
|
||||
leaveRequestId: UUID,
|
||||
applicantId: UUID,
|
||||
approverId: UUID,
|
||||
oaAdministratorId: UUID,
|
||||
durationMinutes: Long,
|
||||
): StartedProcess {
|
||||
val process = runtimeService.createProcessInstanceBuilder()
|
||||
.processDefinitionKey(PROCESS_DEFINITION_KEY)
|
||||
@@ -32,6 +34,8 @@ class FlowableLeaveWorkflowGateway(
|
||||
"businessId" to leaveRequestId.toString(),
|
||||
"applicantId" to applicantId.toString(),
|
||||
"approverId" to approverId.toString(),
|
||||
"oaAdministratorId" to oaAdministratorId.toString(),
|
||||
"durationMinutes" to durationMinutes,
|
||||
),
|
||||
)
|
||||
.start()
|
||||
|
||||
Reference in New Issue
Block a user