diff --git a/backend/src/main/java/com/designhub/service/TaskItemService.java b/backend/src/main/java/com/designhub/service/TaskItemService.java index 2983ef4..be23bf9 100644 --- a/backend/src/main/java/com/designhub/service/TaskItemService.java +++ b/backend/src/main/java/com/designhub/service/TaskItemService.java @@ -475,77 +475,82 @@ public class TaskItemService { Map result = new HashMap<>(); result.put("completedItem", item); - // 6. 🔥 关键步骤:检查是否是顺序流水线,基于group_no解锁下一组 + // 6. 🔥 关键步骤:检查是否是顺序流水线,基于部门解锁下一个部门 + // 新逻辑:同一部门内的所有子任务并行,部门间串行 Task mainTask = taskMapper.selectById(item.getTaskId()); if (mainTask != null && "sequential".equals(mainTask.getPipelineMode())) { - // 获取当前子任务的分组号 - Integer currentGroupNo = item.getGroupNo(); - if (currentGroupNo == null) { - currentGroupNo = 1; // 兼容旧数据 - } + // 从子任务编号解析部门序号(格式:TASK...-部门序号-部门内序号) + int currentDeptIndex = parseDeptIndexFromItemNo(item.getItemNo()); + log.info("当前子任务 {} 属于第 {} 个部门", item.getItemNo(), currentDeptIndex); - // 检查当前组的所有子任务是否都已完成 - List currentGroupItems = taskItemMapper.selectList( + // 获取当前部门的所有子任务(通过编号匹配) + List allItems = taskItemMapper.selectList( new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() .eq(TaskItem::getTaskId, item.getTaskId()) - .eq(TaskItem::getGroupNo, currentGroupNo) ); - boolean allGroupCompleted = currentGroupItems.stream() + // 筛选出同一部门的子任务 + List currentDeptItems = allItems.stream() + .filter(i -> parseDeptIndexFromItemNo(i.getItemNo()) == currentDeptIndex) + .collect(java.util.stream.Collectors.toList()); + + // 检查当前部门的所有子任务是否都已完成 + boolean allDeptCompleted = currentDeptItems.stream() .allMatch(i -> "completed".equals(i.getStatus())); - if (allGroupCompleted) { - log.info("✅ 第 {} 组所有子任务已完成,准备解锁下一组", currentGroupNo); + log.info("当前部门共 {} 个子任务,已完成: {}", currentDeptItems.size(), + currentDeptItems.stream().filter(i -> "completed".equals(i.getStatus())).count()); + + if (allDeptCompleted) { + log.info("✅ 第 {} 个部门的所有子任务已完成,准备解锁下一个部门", currentDeptIndex); - // 🔥 收集当前组所有子任务的下载链接,传递给下一组 + // 🔥 收集当前部门所有子任务的下载链接,传递给下一个部门 StringBuilder prevDownloadUrls = new StringBuilder(); - for (TaskItem groupItem : currentGroupItems) { - if (groupItem.getDownloadUrl() != null && !groupItem.getDownloadUrl().isEmpty()) { + for (TaskItem deptItem : currentDeptItems) { + if (deptItem.getDownloadUrl() != null && !deptItem.getDownloadUrl().isEmpty()) { if (prevDownloadUrls.length() > 0) { prevDownloadUrls.append(","); } - prevDownloadUrls.append(groupItem.getDownloadUrl()); + prevDownloadUrls.append(deptItem.getDownloadUrl()); } // 同时继承前面环节的下载链接 - if (groupItem.getPrevDownloadUrl() != null && !groupItem.getPrevDownloadUrl().isEmpty()) { + if (deptItem.getPrevDownloadUrl() != null && !deptItem.getPrevDownloadUrl().isEmpty()) { if (prevDownloadUrls.length() > 0) { prevDownloadUrls.append(","); } - prevDownloadUrls.append(groupItem.getPrevDownloadUrl()); + prevDownloadUrls.append(deptItem.getPrevDownloadUrl()); } } String prevDownloadUrlStr = prevDownloadUrls.toString(); - log.info("前置环节素材下载链接: {}", prevDownloadUrlStr); + log.info("前置部门素材下载链接: {}", prevDownloadUrlStr); - // 解锁下一组的所有子任务 - Integer nextGroupNo = currentGroupNo + 1; - List nextGroupItems = taskItemMapper.selectList( - new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .eq(TaskItem::getTaskId, item.getTaskId()) - .eq(TaskItem::getGroupNo, nextGroupNo) - .eq(TaskItem::getStatus, "locked") - ); + // 找出下一个部门的所有子任务(状态为locked) + int nextDeptIndex = currentDeptIndex + 1; + List nextDeptItems = allItems.stream() + .filter(i -> parseDeptIndexFromItemNo(i.getItemNo()) == nextDeptIndex) + .filter(i -> "locked".equals(i.getStatus())) + .collect(java.util.stream.Collectors.toList()); - if (!nextGroupItems.isEmpty()) { - // 批量解锁下一组的所有子任务,状态设为待审核,并传递前置环节的下载链接 - for (TaskItem nextItem : nextGroupItems) { + if (!nextDeptItems.isEmpty()) { + // 批量解锁下一个部门的所有子任务,状态设为待审核,并传递前置环节的下载链接 + for (TaskItem nextItem : nextDeptItems) { nextItem.setStatus("pending_approval"); // 待接单部门负责人审核 nextItem.setIsLocked(0); nextItem.setPrevDownloadUrl(prevDownloadUrlStr); // 🔥 传递前置环节的下载链接 nextItem.setUpdateTime(LocalDateTime.now()); taskItemMapper.updateById(nextItem); - log.info("✅ 解锁子任务: {} (组号: {}, ID: {}),待接单部门负责人审核,已传递前置素材链接", - nextItem.getItemNo(), nextGroupNo, nextItem.getId()); + log.info("✅ 解锁子任务: {} (部门序号: {}, ID: {}),待接单部门负责人审核,已传递前置素材链接", + nextItem.getItemNo(), nextDeptIndex, nextItem.getId()); } - result.put("unlockedGroupNo", nextGroupNo); - result.put("unlockedCount", nextGroupItems.size()); + result.put("unlockedDeptIndex", nextDeptIndex); + result.put("unlockedCount", nextDeptItems.size()); // 发送通知给接单部门负责人 // 从子任务的任务类型配置中获取allowed_dept_ids Set notifiedLeaderIds = new HashSet<>(); - for (TaskItem nextItem : nextGroupItems) { + for (TaskItem nextItem : nextDeptItems) { Long targetDeptId = getTargetDeptIdFromTaskType(nextItem.getTaskTypeId()); if (targetDeptId != null) { com.designhub.entity.Department receiverDept = departmentMapper.selectById(targetDeptId); @@ -557,29 +562,29 @@ public class TaskItemService { notification.setReceiverId(receiverDept.getLeaderId()); notification.setNotificationType("task_approval"); notification.setTitle("新子任务组待审核"); - notification.setContent("任务《" + mainTask.getTitle() + "》第 " + nextGroupNo + " 组共 " + - nextGroupItems.size() + " 个子任务已解锁,请审核并分配!"); + notification.setContent("任务《" + mainTask.getTitle() + "》第 " + nextDeptIndex + " 个部门共 " + + nextDeptItems.size() + " 个子任务已解锁,请审核并分配!"); notification.setRelatedId(item.getTaskId()); notification.setRelatedType("task"); notification.setPriority(2); // 重要 notification.setPushDingtalk(1); // 推送到钉钉 notificationService.sendNotification(notification); - log.info("已通知接单部门负责人(部门: {}, 负责人ID: {})审核第 {} 组子任务", - receiverDept.getDeptName(), receiverDept.getLeaderId(), nextGroupNo); + log.info("已通知接单部门负责人(部门: {}, 负责人ID: {})审核第 {} 个部门子任务", + receiverDept.getDeptName(), receiverDept.getLeaderId(), nextDeptIndex); } } else { log.warn("子任务 {} 的任务类型未配置接单部门", nextItem.getItemNo()); } } - log.info("第 {} 组 {} 个子任务已解锁,等待接单部门负责人审核,已通知 {} 个部门负责人", - nextGroupNo, nextGroupItems.size(), notifiedLeaderIds.size()); + log.info("第 {} 个部门 {} 个子任务已解锁,等待接单部门负责人审核,已通知 {} 个部门负责人", + nextDeptIndex, nextDeptItems.size(), notifiedLeaderIds.size()); } else { - log.info("没有更多子任务组需要解锁"); + log.info("没有更多部门的子任务需要解锁"); } } else { - log.info("当前组还有子任务未完成,暂不解锁下一组"); + log.info("当前部门还有子任务未完成,暂不解锁下一个部门"); } } @@ -1310,6 +1315,33 @@ public class TaskItemService { return new java.util.ArrayList<>(groupMap.values()); } + /** + * 从子任务编号解析部门序号 + * 编号格式:任务编号-部门序号-部门内序号,例如 TASK20260114-01-02 + * @param itemNo 子任务编号 + * @return 部门序号,解析失败返回1 + */ + private int parseDeptIndexFromItemNo(String itemNo) { + if (itemNo == null || itemNo.isEmpty()) { + return 1; + } + + String[] parts = itemNo.split("-"); + if (parts.length >= 3) { + try { + // 新格式:TASK...-部门序号-部门内序号 + // 倒数第二段是部门序号 + return Integer.parseInt(parts[parts.length - 2]); + } catch (NumberFormatException e) { + log.warn("解析子任务编号部门序号失败: {}", itemNo); + } + } else if (parts.length == 2) { + // 旧格式:TASK...-序号,认为是第一个部门 + return 1; + } + return 1; + } + /** * 从任务类型配置中获取目标接单部门ID * 如果当前类型未配置,则递归向上查找父级类型的配置 diff --git a/backend/src/main/java/com/designhub/service/TaskService.java b/backend/src/main/java/com/designhub/service/TaskService.java index 493f685..116d2cc 100644 --- a/backend/src/main/java/com/designhub/service/TaskService.java +++ b/backend/src/main/java/com/designhub/service/TaskService.java @@ -567,15 +567,18 @@ public class TaskService { // 关键:根据流水线模式和分组设置初始状态 if ("sequential".equals(dto.getPipelineMode())) { - // 顺序流水线:只有第一个部门的第一个子任务可接单 - if (currentDeptIndex == 1 && subIndex == 1) { + // 顺序流水线:第一个部门的所有子任务都可接单(部门内并行) + // 只有当第一个部门的所有子任务完成后,才解锁下一个部门的子任务 + if (currentDeptIndex == 1) { item.setStatus("pending"); item.setIsLocked(0); item.setPrevItemId(null); + log.info("子任务 {} 属于第一个部门,设置为待接单状态", item.getItemNo()); } else { - // 其他子任务都是 locked(锁定) + // 其他部门的子任务都是 locked(锁定),等待前一个部门完成后解锁 item.setStatus("locked"); item.setIsLocked(1); + log.info("子任务 {} 属于第{}个部门,设置为锁定状态", item.getItemNo(), currentDeptIndex); } } else { // 并行流水线:全部待接单 @@ -1575,12 +1578,13 @@ public class TaskService { // 根据流水线模式和分组设置初始状态 if ("sequential".equals(dto.getPipelineMode())) { - // 顺序流水线:只有第一个部门的第一个子任务可接单 - if (currentDeptIndex == 1 && subIndex == 1) { + // 顺序流水线:第一个部门的所有子任务都可接单(部门内并行) + if (currentDeptIndex == 1) { item.setStatus("pending"); item.setIsLocked(0); item.setPrevItemId(null); } else { + // 其他部门的子任务锁定,等待前一个部门完成 item.setStatus("locked"); item.setIsLocked(1); }