From e45e74e5ae207f7642be9a88ed99b593de89fb3b Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期三, 24 六月 2026 16:54:46 +0800
Subject: [PATCH] 1.0
---
backend/src/main/java/com/sinata/appointment/service/ApprovalEngineService.java | 357 ++++++++++++++++++++++++++++------------------------------
1 files changed, 172 insertions(+), 185 deletions(-)
diff --git a/backend/src/main/java/com/sinata/appointment/service/ApprovalEngineService.java b/backend/src/main/java/com/sinata/appointment/service/ApprovalEngineService.java
index 1b1bba9..0825a05 100644
--- a/backend/src/main/java/com/sinata/appointment/service/ApprovalEngineService.java
+++ b/backend/src/main/java/com/sinata/appointment/service/ApprovalEngineService.java
@@ -48,6 +48,7 @@
private static final TypeReference<List<Long>> LONG_LIST_TYPE = new TypeReference<>() {};
private static final String AUTO_APPROVE_COMMENT = "前序节点已审批,系统自动同意";
+ private static final String LOCAL_UNIT_GROUP_TOKEN = ":UNIT:";
private final BizApprovalNodeConfigMapper bizApprovalNodeConfigMapper;
private final ApprovalInstanceMapper approvalInstanceMapper;
@@ -122,7 +123,7 @@
}
if (ApprovalNodeCode.LEVEL4_VISIT_DEPT.equals(nodeCode)) {
Long competentDeptId = getVisitCompetentDeptId();
- return listEmployeeIdsByDeptAndLeaderFlag(competentDeptId, false);
+ return listEmployeeIdsByDeptWithLeaderFallback(competentDeptId);
}
if (ApprovalNodeCode.LEVEL5_VISIT_DEPT_LEADER.equals(nodeCode)) {
Long competentDeptId = getVisitCompetentDeptId();
@@ -174,6 +175,28 @@
.eq(MpUser::getDeptId, deptId)
.eq(MpUser::getDeptLeaderFlag, leaderFlag));
return users.stream().map(MpUser::getId).filter(Objects::nonNull).toList();
+ }
+
+ private List<Long> listAllActiveEmployeeIdsByDept(Long deptId) {
+ if (deptId == null || deptId <= 0) {
+ return List.of();
+ }
+ List<MpUser> users =
+ mpUserMapper.selectList(
+ new LambdaQueryWrapper<MpUser>()
+ .eq(MpUser::getDelFlag, 0)
+ .eq(MpUser::getStatus, 1)
+ .eq(MpUser::getUserType, MiniUserType.EMPLOYEE)
+ .eq(MpUser::getDeptId, deptId));
+ return users.stream().map(MpUser::getId).filter(Objects::nonNull).distinct().toList();
+ }
+
+ private List<Long> listEmployeeIdsByDeptWithLeaderFallback(Long deptId) {
+ List<Long> normalEmployees = listEmployeeIdsByDeptAndLeaderFlag(deptId, false);
+ if (normalEmployees != null && !normalEmployees.isEmpty()) {
+ return normalEmployees;
+ }
+ return listEmployeeIdsByDeptAndLeaderFlag(deptId, true);
}
@Transactional(rollbackFor = Exception.class)
@@ -252,6 +275,26 @@
inst.setStatus(ApprovalInstanceStatus.REJECTED);
inst.setFinishedAt(LocalDateTime.now());
approvalInstanceMapper.updateById(inst);
+ List<ApprovalTask> remainingTasks =
+ approvalTaskMapper.selectList(
+ new LambdaQueryWrapper<ApprovalTask>()
+ .eq(ApprovalTask::getInstanceId, inst.getId())
+ .in(
+ ApprovalTask::getTaskStatus,
+ ApprovalTaskStatus.PENDING,
+ ApprovalTaskStatus.WAITING));
+ for (ApprovalTask t : remainingTasks) {
+ t.setTaskStatus(ApprovalTaskStatus.REJECTED);
+ t.setComment(null);
+ approvalTaskMapper.updateById(t);
+ }
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void cancelInstance(ApprovalInstance inst, String reason) {
+ inst.setStatus(ApprovalInstanceStatus.CANCELLED);
+ inst.setFinishedAt(LocalDateTime.now());
+ approvalInstanceMapper.updateById(inst);
List<ApprovalTask> pending =
approvalTaskMapper.selectList(
new LambdaQueryWrapper<ApprovalTask>()
@@ -261,7 +304,7 @@
ApprovalTaskStatus.PENDING,
ApprovalTaskStatus.WAITING));
for (ApprovalTask t : pending) {
- t.setTaskStatus(ApprovalTaskStatus.REJECTED);
+ t.setTaskStatus(ApprovalTaskStatus.CANCELLED);
t.setComment(reason);
approvalTaskMapper.updateById(t);
}
@@ -360,94 +403,16 @@
}
private int createTasksForLocalUnitNode(Long instanceId, AppointmentOrder order, String initialStatus) {
- List<Long> localUnitIds = parseLongJsonArray(order.getPassLocalUnitIds());
- if (localUnitIds.isEmpty()) {
- return 0;
- }
- List<MpPassLocalUnit> units =
- mpPassLocalUnitMapper.selectList(
- new LambdaQueryWrapper<MpPassLocalUnit>()
- .eq(MpPassLocalUnit::getDelFlag, 0)
- .in(MpPassLocalUnit::getId, localUnitIds));
- if (units.isEmpty()) {
- return 0;
- }
- Map<Long, MpPassLocalUnit> unitMap =
- units.stream().collect(java.util.stream.Collectors.toMap(MpPassLocalUnit::getId, x -> x, (a, b) -> a));
-
- LinkedHashSet<Long> deptIds = new LinkedHashSet<>();
- LinkedHashSet<Long> groupIds = new LinkedHashSet<>();
- LinkedHashMap<String, List<Long>> tempGroups = new LinkedHashMap<>();
-
- for (Long unitId : localUnitIds) {
- MpPassLocalUnit unit = unitMap.get(unitId);
- if (unit == null || unit.getApproverType() == null) {
- continue;
- }
- if (unit.getApproverType() == LocalUnitApproverType.DEPT) {
- for (Long deptId : parseLongJsonArray(unit.getApproverDeptIds())) {
- if (deptId != null && deptId > 0) {
- deptIds.add(deptId);
- }
- }
- continue;
- }
- if (unit.getApproverType() == LocalUnitApproverType.STAFF_GROUP) {
- Long gid = unit.getApproverGroupId();
- if (gid != null && gid > 0) {
- groupIds.add(gid);
- }
- continue;
- }
- if (unit.getApproverType() == LocalUnitApproverType.STAFF) {
- List<MpPassLocalUnitMember> members =
- mpPassLocalUnitMemberMapper.selectList(
- new LambdaQueryWrapper<MpPassLocalUnitMember>()
- .eq(MpPassLocalUnitMember::getDelFlag, 0)
- .eq(MpPassLocalUnitMember::getUnitId, unitId)
- .orderByAsc(MpPassLocalUnitMember::getId));
- LinkedHashSet<Long> mids = new LinkedHashSet<>();
- for (MpPassLocalUnitMember m : members) {
- if (m.getMpUserId() != null && m.getMpUserId() > 0) {
- mids.add(m.getMpUserId());
- }
- }
- if (mids.isEmpty()) {
- continue;
- }
- List<Long> ordered = mids.stream().toList();
- String sign = buildTempGroupSign(ordered);
- tempGroups.putIfAbsent(sign, ordered);
- }
- }
-
+ List<LocalUnitApprovalGroup> groups = resolveLocalUnitApprovalGroups(order, false);
int total = 0;
- for (Long deptId : deptIds) {
- List<Long> assignees = listEmployeeIdsByDeptAndLeaderFlag(deptId, true);
- String key = ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER + ":DEPT:" + deptId;
- total += createTasksWithGroupKey(instanceId, ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER, key, assignees, initialStatus);
- }
- for (Long groupId : groupIds) {
- List<MpStaffGroupMember> members =
- mpStaffGroupMemberMapper.selectList(
- new LambdaQueryWrapper<MpStaffGroupMember>()
- .eq(MpStaffGroupMember::getDelFlag, 0)
- .eq(MpStaffGroupMember::getGroupId, groupId)
- .orderByAsc(MpStaffGroupMember::getId));
- LinkedHashSet<Long> mids = new LinkedHashSet<>();
- for (MpStaffGroupMember m : members) {
- if (m.getMpUserId() != null && m.getMpUserId() > 0) {
- mids.add(m.getMpUserId());
- }
- }
- List<Long> assignees = filterActiveEmployeeIds(mids.stream().toList());
- String key = ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER + ":GROUP:" + groupId;
- total += createTasksWithGroupKey(instanceId, ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER, key, assignees, initialStatus);
- }
- for (Map.Entry<String, List<Long>> e : tempGroups.entrySet()) {
- List<Long> assignees = filterActiveEmployeeIds(e.getValue());
- String key = ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER + ":TMP:" + e.getKey();
- total += createTasksWithGroupKey(instanceId, ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER, key, assignees, initialStatus);
+ for (LocalUnitApprovalGroup group : groups) {
+ total +=
+ createTasksWithGroupKey(
+ instanceId,
+ ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER,
+ buildLocalUnitGroupKey(group.unitId()),
+ group.assigneeIds(),
+ initialStatus);
}
return total;
}
@@ -457,7 +422,9 @@
List<Long> assignees = resolveAssignees(nodeCode, order);
int created = createTasksWithGroupKey(inst.getId(), nodeCode, nodeCode, assignees, initialStatus);
if (created <= 0) {
- throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "节点无审批人: " + nodeCode);
+ throw new BizException(
+ BizErrorCode.CONFIG_NOT_FOUND,
+ "节点无审批人: " + resolveNodeName(nodeCode));
}
return created;
}
@@ -468,88 +435,100 @@
if (!Boolean.TRUE.equals(order.getInvolveOtherLocalUnit()) || localUnitIds.isEmpty()) {
throw new BizException(
BizErrorCode.CONFIG_NOT_FOUND,
- "节点无审批人: " + ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER);
+ "节点无审批人: " + resolveNodeName(ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER));
}
+ List<LocalUnitApprovalGroup> groups = resolveLocalUnitApprovalGroups(order, true);
+ int total = 0;
+ for (LocalUnitApprovalGroup group : groups) {
+ total +=
+ createTasksWithGroupKey(
+ instanceId,
+ ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER,
+ buildLocalUnitGroupKey(group.unitId()),
+ group.assigneeIds(),
+ initialStatus);
+ }
+ if (total <= 0) {
+ throw new BizException(
+ BizErrorCode.CONFIG_NOT_FOUND,
+ "节点无审批人: " + resolveNodeName(ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER));
+ }
+ return total;
+ }
+
+ private List<LocalUnitApprovalGroup> resolveLocalUnitApprovalGroups(
+ AppointmentOrder order, boolean strict) {
+ List<Long> localUnitIds = parseLongJsonArray(order.getPassLocalUnitIds());
+ if (localUnitIds.isEmpty()) {
+ return List.of();
+ }
List<MpPassLocalUnit> units =
mpPassLocalUnitMapper.selectList(
new LambdaQueryWrapper<MpPassLocalUnit>()
.eq(MpPassLocalUnit::getDelFlag, 0)
.in(MpPassLocalUnit::getId, localUnitIds));
- if (units.size() != localUnitIds.size()) {
- throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位配置缺失,无法生成审批人");
- }
Map<Long, MpPassLocalUnit> unitMap =
units.stream()
.collect(
java.util.stream.Collectors.toMap(
MpPassLocalUnit::getId, x -> x, (a, b) -> a));
+ if (strict && unitMap.size() != localUnitIds.size()) {
+ throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位配置缺失,无法生成审批人");
+ }
- LinkedHashSet<Long> deptIds = new LinkedHashSet<>();
- LinkedHashSet<Long> groupIds = new LinkedHashSet<>();
- LinkedHashMap<String, List<Long>> tempGroups = new LinkedHashMap<>();
-
+ List<LocalUnitApprovalGroup> groups = new ArrayList<>();
for (Long unitId : localUnitIds) {
MpPassLocalUnit unit = unitMap.get(unitId);
- if (unit == null || unit.getApproverType() == null) {
+ if (unit == null) {
+ if (strict) {
+ throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位配置缺失,无法生成审批人");
+ }
+ continue;
+ }
+ List<Long> assignees = resolveLocalUnitAssignees(unitId, unit, strict);
+ if (assignees.isEmpty()) {
+ continue;
+ }
+ groups.add(new LocalUnitApprovalGroup(unitId, assignees));
+ }
+ return groups;
+ }
+
+ private List<Long> resolveLocalUnitAssignees(Long unitId, MpPassLocalUnit unit, boolean strict) {
+ Integer approverType = unit.getApproverType();
+ if (approverType == null) {
+ if (strict) {
throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位审批类型缺失: " + unitId);
}
- if (unit.getApproverType() == LocalUnitApproverType.DEPT) {
- List<Long> ids = parseLongJsonArray(unit.getApproverDeptIds());
- if (ids.isEmpty()) {
+ return List.of();
+ }
+ if (approverType == LocalUnitApproverType.DEPT) {
+ List<Long> deptIds = parseLongJsonArray(unit.getApproverDeptIds());
+ if (deptIds.isEmpty()) {
+ if (strict) {
throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位审批部门未配置: " + unitId);
}
- deptIds.addAll(ids);
- continue;
+ return List.of();
}
- if (unit.getApproverType() == LocalUnitApproverType.STAFF_GROUP) {
- Long gid = unit.getApproverGroupId();
- if (gid == null || gid <= 0) {
+ LinkedHashSet<Long> assignees = new LinkedHashSet<>();
+ for (Long deptId : deptIds) {
+ List<Long> deptApprovers = listAllActiveEmployeeIdsByDept(deptId);
+ if (strict && deptApprovers.isEmpty()) {
+ throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "审批部门无可用审批人: " + deptId);
+ }
+ assignees.addAll(deptApprovers);
+ }
+ return assignees.stream().toList();
+ }
+ if (approverType == LocalUnitApproverType.STAFF_GROUP) {
+ Long groupId = unit.getApproverGroupId();
+ if (groupId == null || groupId <= 0) {
+ if (strict) {
throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位审批分组未配置: " + unitId);
}
- groupIds.add(gid);
- continue;
+ return List.of();
}
- if (unit.getApproverType() == LocalUnitApproverType.STAFF) {
- List<MpPassLocalUnitMember> members =
- mpPassLocalUnitMemberMapper.selectList(
- new LambdaQueryWrapper<MpPassLocalUnitMember>()
- .eq(MpPassLocalUnitMember::getDelFlag, 0)
- .eq(MpPassLocalUnitMember::getUnitId, unitId)
- .orderByAsc(MpPassLocalUnitMember::getId));
- LinkedHashSet<Long> mids = new LinkedHashSet<>();
- for (MpPassLocalUnitMember m : members) {
- if (m.getMpUserId() != null && m.getMpUserId() > 0) {
- mids.add(m.getMpUserId());
- }
- }
- if (mids.isEmpty()) {
- throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位审批人员未配置: " + unitId);
- }
- List<Long> ordered = mids.stream().toList();
- String sign = buildTempGroupSign(ordered);
- tempGroups.putIfAbsent(sign, ordered);
- continue;
- }
- throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位审批类型无效: " + unitId);
- }
-
- int total = 0;
- for (Long deptId : deptIds) {
- List<Long> assignees = listEmployeeIdsByDeptAndLeaderFlag(deptId, true);
- if (assignees.isEmpty()) {
- throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "审批部门无负责人: " + deptId);
- }
- String key = ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER + ":DEPT:" + deptId;
- total +=
- createTasksWithGroupKey(
- instanceId,
- ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER,
- key,
- assignees,
- initialStatus);
- }
- for (Long groupId : groupIds) {
List<MpStaffGroupMember> members =
mpStaffGroupMemberMapper.selectList(
new LambdaQueryWrapper<MpStaffGroupMember>()
@@ -557,44 +536,58 @@
.eq(MpStaffGroupMember::getGroupId, groupId)
.orderByAsc(MpStaffGroupMember::getId));
LinkedHashSet<Long> mids = new LinkedHashSet<>();
- for (MpStaffGroupMember m : members) {
- if (m.getMpUserId() != null && m.getMpUserId() > 0) {
- mids.add(m.getMpUserId());
+ for (MpStaffGroupMember member : members) {
+ if (member.getMpUserId() != null && member.getMpUserId() > 0) {
+ mids.add(member.getMpUserId());
}
}
List<Long> assignees = filterActiveEmployeeIds(mids.stream().toList());
- if (assignees.isEmpty()) {
+ if (strict && assignees.isEmpty()) {
throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "审批分组无有效成员: " + groupId);
}
- String key = ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER + ":GROUP:" + groupId;
- total +=
- createTasksWithGroupKey(
- instanceId,
- ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER,
- key,
- assignees,
- initialStatus);
+ return assignees;
}
- for (Map.Entry<String, List<Long>> e : tempGroups.entrySet()) {
- List<Long> assignees = filterActiveEmployeeIds(e.getValue());
- if (assignees.isEmpty()) {
- throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "临时审批组无有效成员");
+ if (approverType == LocalUnitApproverType.STAFF) {
+ List<MpPassLocalUnitMember> members =
+ mpPassLocalUnitMemberMapper.selectList(
+ new LambdaQueryWrapper<MpPassLocalUnitMember>()
+ .eq(MpPassLocalUnitMember::getDelFlag, 0)
+ .eq(MpPassLocalUnitMember::getUnitId, unitId)
+ .orderByAsc(MpPassLocalUnitMember::getId));
+ LinkedHashSet<Long> mids = new LinkedHashSet<>();
+ for (MpPassLocalUnitMember member : members) {
+ if (member.getMpUserId() != null && member.getMpUserId() > 0) {
+ mids.add(member.getMpUserId());
+ }
}
- String key = ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER + ":TMP:" + e.getKey();
- total +=
- createTasksWithGroupKey(
- instanceId,
- ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER,
- key,
- assignees,
- initialStatus);
+ List<Long> assignees = filterActiveEmployeeIds(mids.stream().toList());
+ if (strict && assignees.isEmpty()) {
+ throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位审批人员未配置: " + unitId);
+ }
+ return assignees;
}
- if (total <= 0) {
- throw new BizException(
- BizErrorCode.CONFIG_NOT_FOUND,
- "节点无审批人: " + ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER);
+ if (strict) {
+ throw new BizException(BizErrorCode.CONFIG_NOT_FOUND, "属地单位审批类型无效: " + unitId);
}
- return total;
+ return List.of();
+ }
+
+ private String buildLocalUnitGroupKey(Long unitId) {
+ return ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER + LOCAL_UNIT_GROUP_TOKEN + unitId;
+ }
+
+ private String resolveNodeName(String nodeCode) {
+ if (!StringUtils.hasText(nodeCode)) {
+ return "未知节点";
+ }
+ return switch (nodeCode) {
+ case ApprovalNodeCode.LEVEL1_HANDLER -> "经办人(一级)";
+ case ApprovalNodeCode.LEVEL2_DEPT_LEADER -> "部门负责人(二级)";
+ case ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER -> "属地管理部门领导(三级)";
+ case ApprovalNodeCode.LEVEL4_VISIT_DEPT -> "访客主管部门(四级)";
+ case ApprovalNodeCode.LEVEL5_VISIT_DEPT_LEADER -> "访客主管部门领导(五级)";
+ default -> nodeCode;
+ };
}
private int createTasksWithGroupKey(
@@ -707,11 +700,5 @@
return out;
}
- private String buildTempGroupSign(List<Long> memberIds) {
- String joined = memberIds.stream().map(String::valueOf).collect(java.util.stream.Collectors.joining("-"));
- if (joined.length() <= 40) {
- return joined;
- }
- return Integer.toHexString(joined.hashCode());
- }
+ private record LocalUnitApprovalGroup(Long unitId, List<Long> assigneeIds) {}
}
--
Gitblit v1.7.1