From 5882ba8863d77fdcc168b176c3ffb30e8a20556a Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期一, 13 七月 2026 17:54:25 +0800
Subject: [PATCH] 缩短二维码原始参数

---
 backend/src/main/java/com/sinata/appointment/service/ApprovalEngineService.java |  405 ++++++++++++++++++++++++++++++---------------------------
 1 files changed, 214 insertions(+), 191 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..47ab6cc 100644
--- a/backend/src/main/java/com/sinata/appointment/service/ApprovalEngineService.java
+++ b/backend/src/main/java/com/sinata/appointment/service/ApprovalEngineService.java
@@ -17,6 +17,7 @@
 import com.sinata.appointment.entity.MpPassLocalUnitMember;
 import com.sinata.appointment.entity.MpStaffGroupMember;
 import com.sinata.appointment.entity.MpUser;
+import com.sinata.appointment.entity.SysDept;
 import com.sinata.appointment.exception.BizException;
 import com.sinata.appointment.mapper.ApprovalInstanceMapper;
 import com.sinata.appointment.mapper.ApprovalTaskMapper;
@@ -25,6 +26,7 @@
 import com.sinata.appointment.mapper.MpPassLocalUnitMemberMapper;
 import com.sinata.appointment.mapper.MpStaffGroupMemberMapper;
 import com.sinata.appointment.mapper.MpUserMapper;
+import com.sinata.appointment.mapper.SysDeptMapper;
 import com.sinata.appointment.constants.BizErrorCode;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
@@ -48,11 +50,13 @@
 
     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;
     private final ApprovalTaskMapper approvalTaskMapper;
     private final MpUserMapper mpUserMapper;
+    private final SysDeptMapper sysDeptMapper;
     private final MpPassLocalUnitMapper mpPassLocalUnitMapper;
     private final MpPassLocalUnitMemberMapper mpPassLocalUnitMemberMapper;
     private final MpStaffGroupMemberMapper mpStaffGroupMemberMapper;
@@ -118,15 +122,15 @@
         }
         if (ApprovalNodeCode.LEVEL2_DEPT_LEADER.equals(nodeCode)) {
             Long deptId = resolveVisitedDeptId(order);
-            return listEmployeeIdsByDeptAndLeaderFlag(deptId, true);
+            return getDeptLeaderIds(deptId);
         }
         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();
-            return listEmployeeIdsByDeptAndLeaderFlag(competentDeptId, true);
+            return getDeptLeaderIds(competentDeptId);
         }
         if (ApprovalNodeCode.LEVEL3_LOCAL_DEPT_LEADER.equals(nodeCode)) {
             return List.of();
@@ -161,7 +165,59 @@
         }
     }
 
-    private List<Long> listEmployeeIdsByDeptAndLeaderFlag(Long deptId, boolean leaderFlag) {
+    private List<Long> getDeptLeaderIds(Long deptId) {
+        if (deptId == null || deptId <= 0) {
+            return List.of();
+        }
+        SysDept dept = sysDeptMapper.selectById(deptId);
+        if (dept == null || dept.getDelFlag() != null && dept.getDelFlag() == 1) {
+            return List.of();
+        }
+        List<Long> leaderIds = parseDeptLeaderIds(dept);
+        if (leaderIds.isEmpty()) {
+            return List.of();
+        }
+        List<MpUser> leaders =
+                mpUserMapper.selectList(
+                        new LambdaQueryWrapper<MpUser>()
+                                .eq(MpUser::getDelFlag, 0)
+                                .eq(MpUser::getStatus, 1)
+                                .eq(MpUser::getUserType, MiniUserType.EMPLOYEE)
+                                .in(MpUser::getId, leaderIds));
+        Map<Long, MpUser> leaderMap = new LinkedHashMap<>();
+        for (MpUser leader : leaders) {
+            leaderMap.put(leader.getId(), leader);
+        }
+        List<Long> result = new ArrayList<>();
+        for (Long id : leaderIds) {
+            if (leaderMap.containsKey(id)) {
+                result.add(id);
+            }
+        }
+        return result;
+    }
+
+    private List<Long> parseDeptLeaderIds(SysDept dept) {
+        List<Long> ids = new ArrayList<>();
+        Set<Long> seen = new LinkedHashSet<>();
+        if (StringUtils.hasText(dept.getLeaderIds())) {
+            for (String part : dept.getLeaderIds().split(",")) {
+                if (!StringUtils.hasText(part)) {
+                    continue;
+                }
+                try {
+                    Long id = Long.parseLong(part.trim());
+                    if (id > 0 && seen.add(id)) {
+                        ids.add(id);
+                    }
+                } catch (NumberFormatException ignored) {
+                }
+            }
+        }
+        return ids;
+    }
+
+    private List<Long> listAllActiveEmployeeIdsByDept(Long deptId) {
         if (deptId == null || deptId <= 0) {
             return List.of();
         }
@@ -171,9 +227,12 @@
                                 .eq(MpUser::getDelFlag, 0)
                                 .eq(MpUser::getStatus, 1)
                                 .eq(MpUser::getUserType, MiniUserType.EMPLOYEE)
-                                .eq(MpUser::getDeptId, deptId)
-                                .eq(MpUser::getDeptLeaderFlag, leaderFlag));
-        return users.stream().map(MpUser::getId).filter(Objects::nonNull).toList();
+                                .eq(MpUser::getDeptId, deptId));
+        return users.stream().map(MpUser::getId).filter(Objects::nonNull).distinct().toList();
+    }
+
+    private List<Long> listEmployeeIdsByDeptWithLeaderFallback(Long deptId) {
+        return listAllActiveEmployeeIdsByDept(deptId);
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -252,6 +311,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 +340,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 +439,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 +458,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 +471,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 +572,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 +736,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