feat: add parallel OA and HR leave review

This commit is contained in:
selfrelease
2026-07-18 08:57:45 +08:00
parent 4741fb26c8
commit e13b6c7778
17 changed files with 162 additions and 27 deletions
@@ -87,6 +87,16 @@ class LeaveRequestService(
if (durationMinutes > 1440 && oaAdministratorId == approverId) {
throw ApiException(HttpStatus.CONFLICT, "APPROVER_SEPARATION_REQUIRED", "长期请假的主管和 OA 复核人必须为不同人员")
}
val requiresParallelAnnualReview = durationMinutes > 1440 && existing.type == LeaveType.ANNUAL
val hrReviewerId = if (requiresParallelAnnualReview) {
routingRepository.findHrReviewer(actor.tenantId)
?: throw ApiException(HttpStatus.CONFLICT, "HR_REVIEWER_NOT_FOUND", "未找到有效的人力资源复核人")
} else {
oaAdministratorId
}
if (requiresParallelAnnualReview && hrReviewerId in setOf(actor.id, approverId, oaAdministratorId)) {
throw ApiException(HttpStatus.CONFLICT, "APPROVER_SEPARATION_REQUIRED", "长期年假的主管、OA 和 HR 复核人必须为不同人员")
}
val outcome = transition(
actor = actor,
id = id,
@@ -105,7 +115,9 @@ class LeaveRequestService(
actor.id,
approverId,
oaAdministratorId,
hrReviewerId,
durationMinutes,
existing.type.name,
)
repository.attachWorkflow(
actor.tenantId,
@@ -6,4 +6,6 @@ interface ApprovalRoutingRepository {
fun findDepartmentManager(tenantId: UUID, applicantId: UUID): UUID?
fun findOaAdministrator(tenantId: UUID): UUID?
fun findHrReviewer(tenantId: UUID): UUID?
}
@@ -41,6 +41,12 @@ class JooqApprovalRoutingRepository(
)?.get("user_id", UUID::class.java)
override fun findOaAdministrator(tenantId: UUID): UUID? =
findUserByRole(tenantId, "oa_admin")
override fun findHrReviewer(tenantId: UUID): UUID? =
findUserByRole(tenantId, "hr_reviewer")
private fun findUserByRole(tenantId: UUID, roleCode: String): UUID? =
dsl.fetchOne(
"""
SELECT ur.user_id
@@ -48,7 +54,7 @@ class JooqApprovalRoutingRepository(
JOIN authz.role role
ON role.tenant_id = ur.tenant_id
AND role.id = ur.role_id
AND role.code = 'oa_admin'
AND role.code = ?
AND role.status = 'ACTIVE'
JOIN identity.user_account account
ON account.tenant_id = ur.tenant_id
@@ -60,6 +66,7 @@ class JooqApprovalRoutingRepository(
ORDER BY ur.effective_from, ur.user_id
LIMIT 1
""".trimIndent(),
roleCode,
tenantId,
)?.get("user_id", UUID::class.java)
}
@@ -10,7 +10,9 @@ interface LeaveWorkflowGateway {
applicantId: UUID,
approverId: UUID,
oaAdministratorId: UUID,
hrReviewerId: UUID,
durationMinutes: Long,
leaveType: String,
): StartedProcess
fun listAssignedTasks(assigneeId: UUID): List<WorkflowTask>
@@ -23,7 +23,9 @@ class FlowableLeaveWorkflowGateway(
applicantId: UUID,
approverId: UUID,
oaAdministratorId: UUID,
hrReviewerId: UUID,
durationMinutes: Long,
leaveType: String,
): StartedProcess {
val process = runtimeService.createProcessInstanceBuilder()
.processDefinitionKey(PROCESS_DEFINITION_KEY)
@@ -35,7 +37,9 @@ class FlowableLeaveWorkflowGateway(
"applicantId" to applicantId.toString(),
"approverId" to approverId.toString(),
"oaAdministratorId" to oaAdministratorId.toString(),
"hrReviewerId" to hrReviewerId.toString(),
"durationMinutes" to durationMinutes,
"leaveType" to leaveType,
),
)
.start()