From b90dbb55941c641dcfeadc837f77e98697e178f7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 31 Mar 2026 11:09:37 +0800 Subject: [PATCH] fix: fallback receiving approvals to parent leaders --- .../service/TaskApprovalService.java | 86 ++++++++++--- .../designhub/service/TaskItemService.java | 117 ++++++++++++++---- .../com/designhub/service/TaskService.java | 11 +- 3 files changed, 161 insertions(+), 53 deletions(-) diff --git a/backend/src/main/java/com/designhub/service/TaskApprovalService.java b/backend/src/main/java/com/designhub/service/TaskApprovalService.java index 076caf2..7a6fdc6 100644 --- a/backend/src/main/java/com/designhub/service/TaskApprovalService.java +++ b/backend/src/main/java/com/designhub/service/TaskApprovalService.java @@ -273,19 +273,16 @@ public class TaskApprovalService { // 3. 所有任务都需要接单部门负责人审核(无论是否预先指定了设计师) String releaseType = task.getReleaseType(); - - // 确定接单部门和负责人,并把部门落库,避免后续部门调整时出现待审核任务无法追溯归属 - Long receivingDeptId = determineReceivingDeptId(task); - Long receivingLeaderId = null; - if (receivingDeptId != null) { - Department receivingDept = departmentMapper.selectById(receivingDeptId); - if (receivingDept != null) { - receivingLeaderId = receivingDept.getLeaderId(); - } - } - if (receivingLeaderId == null) { - throw new BusinessException(ResultCode.PARAM_ERROR, "无法确定接单部门负责人,请在系统设置中为接单部门配置负责人"); - } + + // 确定接单审核部门和负责人。 + // 若具体接单小组未配置负责人,则自动回退到最近的有负责人的父级接单部门。 + Department receivingApprovalDept = resolveReceivingApprovalDepartment(task); + if (receivingApprovalDept == null || receivingApprovalDept.getLeaderId() == null) { + throw new BusinessException(ResultCode.PARAM_ERROR, "无法确定接单部门负责人,请在系统设置中为接单部门配置负责人"); + } + + Long receivingDeptId = receivingApprovalDept.getId(); + Long receivingLeaderId = receivingApprovalDept.getLeaderId(); // 更新任务状态为待接单部门审核 task.setApprovalStatus("pending_receiving_approval"); @@ -1041,12 +1038,7 @@ public class TaskApprovalService { * 确定接单部门负责人(支持三级分类继承二级分类的配置) */ public Long determineReceivingLeader(Task task) { - Long receivingDeptId = determineReceivingDeptId(task); - if (receivingDeptId == null) { - return null; - } - - Department dept = departmentMapper.selectById(receivingDeptId); + Department dept = resolveReceivingApprovalDepartment(task); if (dept == null || dept.getLeaderId() == null) { return null; } @@ -1055,6 +1047,62 @@ public class TaskApprovalService { return dept.getLeaderId(); } + /** + * 确定任务当前应该流转到哪个接单审核部门。 + * - 优先保留子任务/任务类型计算出的实际接单部门 + * - 如果该部门没有负责人,则自动回退到最近的有负责人的父级接单部门 + */ + public Department resolveReceivingApprovalDepartment(Task task) { + Long receivingDeptId = determineReceivingDeptId(task); + if (receivingDeptId == null) { + return null; + } + + return resolveNearestReceivingDeptWithLeader(receivingDeptId); + } + + private Department resolveNearestReceivingDeptWithLeader(Long deptId) { + if (deptId == null) { + return null; + } + + Long currentDeptId = deptId; + Department fallbackDept = null; + + while (currentDeptId != null && currentDeptId != 0) { + Department currentDept = departmentMapper.selectById(currentDeptId); + if (currentDept == null) { + break; + } + + if (!"receiving".equals(currentDept.getDeptType()) + || !Integer.valueOf(1).equals(currentDept.getStatus()) + || Integer.valueOf(1).equals(currentDept.getDeleted())) { + currentDeptId = currentDept.getParentId(); + continue; + } + + if (fallbackDept == null) { + fallbackDept = currentDept; + } + + if (currentDept.getLeaderId() != null) { + if (!currentDept.getId().equals(deptId)) { + log.info("接单部门 {} 未配置负责人,自动回退到父级部门 {}(负责人ID={})", + deptId, currentDept.getId(), currentDept.getLeaderId()); + } + return currentDept; + } + + currentDeptId = currentDept.getParentId(); + } + + if (fallbackDept != null) { + log.warn("接单部门 {} 及其父级接单部门均未配置负责人", deptId); + } + return null; + } + /** * 应用任务更新 */ diff --git a/backend/src/main/java/com/designhub/service/TaskItemService.java b/backend/src/main/java/com/designhub/service/TaskItemService.java index de65dbb..80d927f 100644 --- a/backend/src/main/java/com/designhub/service/TaskItemService.java +++ b/backend/src/main/java/com/designhub/service/TaskItemService.java @@ -1106,15 +1106,13 @@ public class TaskItemService { if (!unassignedItems.isEmpty()) { Set notifiedLeaderIds = new HashSet<>(); for (TaskItem nextItem : unassignedItems) { - Long targetDeptId = getTargetDeptIdFromTaskType(nextItem.getTaskTypeId()); - if (targetDeptId != null) { - com.designhub.entity.Department receiverDept = departmentMapper.selectById(targetDeptId); - if (receiverDept != null && receiverDept.getLeaderId() != null - && !notifiedLeaderIds.contains(receiverDept.getLeaderId())) { - notifiedLeaderIds.add(receiverDept.getLeaderId()); + com.designhub.entity.Department approvalDept = resolveReceivingApprovalDeptForItem(nextItem); + if (approvalDept != null && approvalDept.getLeaderId() != null) { + if (!notifiedLeaderIds.contains(approvalDept.getLeaderId())) { + notifiedLeaderIds.add(approvalDept.getLeaderId()); Notification notification = new Notification(); - notification.setReceiverId(receiverDept.getLeaderId()); + notification.setReceiverId(approvalDept.getLeaderId()); notification.setNotificationType("task_approval"); notification.setTitle("新子任务组待审核"); notification.setContent("任务《" + mainTask.getTitle() + "》第 " + nextDeptIndex + " 个部门共 " + @@ -1126,10 +1124,10 @@ public class TaskItemService { notificationService.sendNotification(notification); log.info("已通知接单部门负责人(部门: {}, 负责人ID: {})审核第 {} 个部门子任务", - receiverDept.getDeptName(), receiverDept.getLeaderId(), nextDeptIndex); + approvalDept.getDeptName(), approvalDept.getLeaderId(), nextDeptIndex); } } else { - log.warn("子任务 {} 的任务类型未配置接单部门", nextItem.getItemNo()); + log.warn("子任务 {} 无法确定可审核的接单部门负责人", nextItem.getItemNo()); } } @@ -1355,21 +1353,17 @@ public class TaskItemService { // 从子任务的任务类型配置获取接单部门 TaskItem firstItem = checkItems.get(0); - Long targetDeptId = getTargetDeptIdFromTaskType(firstItem.getTaskTypeId()); - boolean hasPermission = false; - // 检查任务类型配置的接单部门 - if (targetDeptId != null) { - com.designhub.entity.Department receiverDept = departmentMapper.selectById(targetDeptId); - if (receiverDept != null && approverId.equals(receiverDept.getLeaderId())) { - hasPermission = true; - } + // 优先检查子任务实际归属的审核部门(支持子部门自动回退到有负责人的父级接单部门) + com.designhub.entity.Department approvalDept = resolveReceivingApprovalDeptForItem(firstItem); + if (approvalDept != null && approverId.equals(approvalDept.getLeaderId())) { + hasPermission = true; } // 兼容:也检查主任务的 receiver_dept_id if (!hasPermission && mainTask.getReceiverDeptId() != null) { - com.designhub.entity.Department receiverDept = departmentMapper.selectById(mainTask.getReceiverDeptId()); + com.designhub.entity.Department receiverDept = resolveNearestReceivingDeptWithLeader(mainTask.getReceiverDeptId()); if (receiverDept != null && approverId.equals(receiverDept.getLeaderId())) { hasPermission = true; } @@ -1679,7 +1673,7 @@ public class TaskItemService { // 2. 如果任务已指定接单部门,检查用户是否是该部门的负责人 if (!hasPermission && mainTask.getReceiverDeptId() != null) { - com.designhub.entity.Department receiverDept = departmentMapper.selectById(mainTask.getReceiverDeptId()); + com.designhub.entity.Department receiverDept = resolveNearestReceivingDeptWithLeader(mainTask.getReceiverDeptId()); if (receiverDept != null && rejectorId.equals(receiverDept.getLeaderId())) { hasPermission = true; log.info("用户 {} 是任务接单部门 {} 的负责人", rejectorId, mainTask.getReceiverDeptId()); @@ -1707,7 +1701,7 @@ public class TaskItemService { ); java.util.Set groupDeptIds = groupItems.stream() - .map(TaskItem::getReceiverDeptId) + .map(this::resolveReceivingApprovalDeptIdForItem) .filter(java.util.Objects::nonNull) .collect(java.util.stream.Collectors.toSet()); @@ -1860,6 +1854,13 @@ public class TaskItemService { hasPermission = true; log.info("匹配成功(通过任务类型配置)"); } + if (!hasPermission) { + com.designhub.entity.Department approvalDept = resolveReceivingApprovalDeptForItem(item); + if (approvalDept != null && approvalDept.getId().equals(receiverDeptId)) { + hasPermission = true; + log.info("匹配成功(通过子任务接单部门负责人兜底)"); + } + } // 兼容:也检查主任务的 receiver_dept_id if (!hasPermission && mainTask.getReceiverDeptId() != null && mainTask.getReceiverDeptId().equals(receiverDeptId)) { @@ -1975,17 +1976,14 @@ public class TaskItemService { groupInfo.put("subTaskTypeId", item.getTaskTypeId()); // 添加审核人信息(目标部门负责人) - Long targetDeptId = getTargetDeptIdFromTaskType(item.getTaskTypeId()); - if (targetDeptId != null) { - com.designhub.entity.Department targetDept = departmentMapper.selectById(targetDeptId); - if (targetDept != null && targetDept.getLeaderId() != null) { - com.designhub.entity.User leader = userMapper.selectById(targetDept.getLeaderId()); + com.designhub.entity.Department approvalDept = resolveReceivingApprovalDeptForItem(item); + if (approvalDept != null && approvalDept.getLeaderId() != null) { + com.designhub.entity.User leader = userMapper.selectById(approvalDept.getLeaderId()); if (leader != null) { groupInfo.put("currentApproverName", leader.getRealName()); groupInfo.put("currentApproverRole", "receiving_leader"); groupInfo.put("approvalStatus", "pending_receiving_approval"); } - } } // 计算子任务组总积分(BigDecimal类型) @@ -2162,6 +2160,73 @@ public class TaskItemService { com.designhub.entity.Department department = departmentMapper.selectById(deptId); return department != null ? department.getDeptName() : null; } + + private Long resolveReceivingApprovalDeptIdForItem(TaskItem item) { + com.designhub.entity.Department approvalDept = resolveReceivingApprovalDeptForItem(item); + return approvalDept != null ? approvalDept.getId() : null; + } + + private com.designhub.entity.Department resolveReceivingApprovalDeptForItem(TaskItem item) { + if (item == null) { + return null; + } + + if (item.getReceiverDeptId() != null) { + com.designhub.entity.Department dept = resolveNearestReceivingDeptWithLeader(item.getReceiverDeptId()); + if (dept != null) { + return dept; + } + } + + Long targetDeptId = getTargetDeptIdFromTaskType(item.getTaskTypeId()); + if (targetDeptId == null) { + return null; + } + + return resolveNearestReceivingDeptWithLeader(targetDeptId); + } + + private com.designhub.entity.Department resolveNearestReceivingDeptWithLeader(Long deptId) { + if (deptId == null) { + return null; + } + + Long currentDeptId = deptId; + com.designhub.entity.Department fallbackDept = null; + + while (currentDeptId != null && currentDeptId != 0) { + com.designhub.entity.Department currentDept = departmentMapper.selectById(currentDeptId); + if (currentDept == null) { + break; + } + + if (!"receiving".equals(currentDept.getDeptType()) + || currentDept.getStatus() == null || currentDept.getStatus() != 1 + || (currentDept.getDeleted() != null && currentDept.getDeleted() == 1)) { + currentDeptId = currentDept.getParentId(); + continue; + } + + if (fallbackDept == null) { + fallbackDept = currentDept; + } + + if (currentDept.getLeaderId() != null) { + if (!currentDept.getId().equals(deptId)) { + log.info("接单部门 {} 未配置负责人,子任务审核自动回退到父级部门 {}(负责人ID={})", + deptId, currentDept.getId(), currentDept.getLeaderId()); + } + return currentDept; + } + + currentDeptId = currentDept.getParentId(); + } + + if (fallbackDept != null) { + log.warn("接单部门 {} 及其父级接单部门均未配置负责人", deptId); + } + return null; + } /** * 从子任务编号解析部门序号 diff --git a/backend/src/main/java/com/designhub/service/TaskService.java b/backend/src/main/java/com/designhub/service/TaskService.java index de76c93..4748c29 100644 --- a/backend/src/main/java/com/designhub/service/TaskService.java +++ b/backend/src/main/java/com/designhub/service/TaskService.java @@ -4000,14 +4000,9 @@ public class TaskService { taskApprovalService.logApproval(task.getId(), completerId, 0, "complete_detail", "已完善任务明细"); // 确定接单部门负责人,并把部门归属同步写入任务,避免后续组织架构调整时待审核任务失联 - Long receivingDeptId = taskApprovalService.determineReceivingDeptId(task); - Long receivingLeaderId = null; - if (receivingDeptId != null) { - Department receivingDept = departmentService.getDepartmentById(receivingDeptId); - if (receivingDept != null) { - receivingLeaderId = receivingDept.getLeaderId(); - } - } + Department receivingApprovalDept = taskApprovalService.resolveReceivingApprovalDepartment(task); + Long receivingDeptId = receivingApprovalDept != null ? receivingApprovalDept.getId() : null; + Long receivingLeaderId = receivingApprovalDept != null ? receivingApprovalDept.getLeaderId() : null; if (receivingLeaderId != null) { // 更新任务状态为待接单部门审核 task.setApprovalStatus("pending_receiving_approval");