fix: 修复一级需求部门任务在接单部门审核时按接单部门拆分显示子任务组

This commit is contained in:
Your Name 2026-01-22 18:10:02 +08:00
parent f96f093f99
commit 8d65610643

View File

@ -944,6 +944,17 @@ public class TaskService {
// 查询处于审批流程中的任务(包括一级需求审核和明细完善状态)
wrapper.in(Task::getApprovalStatus, "pending_primary_approval", "pending_detail_completion", "pending_demand_approval", "pending_receiving_approval");
// 🔥 过滤掉已经拆分成子任务组的一级需求部门任务
// 一级需求部门任务在接单部门审核时,应该显示子任务组,而不是主任务
// 条件:isPrimaryTask=1 AND isPipeline=1 AND approvalStatus='pending_receiving_approval' 的任务应该被过滤
wrapper.and(w -> w
.ne(Task::getIsPrimaryTask, 1) // 不是一级需求部门任务
.or()
.eq(Task::getIsPipeline, 0) // 或者不是流水线任务
.or()
.ne(Task::getApprovalStatus, "pending_receiving_approval") // 或者不是待接单部门审核状态
);
// 检查用户是否是管理员
User user = userService.getUserById(userId);
boolean isAdmin = user != null && Integer.valueOf(1).equals(user.getIsAdmin());
@ -4079,6 +4090,7 @@ public class TaskService {
/**
* 从DTO创建任务子项(用于一级需求部门任务明细完善)
* 按接单部门分组,与普通流水线任务保持一致
*/
@Transactional(rollbackFor = Exception.class)
public void createTaskItemsFromDTO(Long taskId, List<com.designhub.dto.PipelineTaskDTO.TaskItemDTO> taskItems) {
@ -4087,57 +4099,240 @@ public class TaskService {
throw new BusinessException(ResultCode.NOT_FOUND, "任务不存在");
}
log.info("为任务 {} 创建子任务,共 {} 个", taskId, taskItems.size());
log.info("为任务 {} 创建子任务(按接单部门分组),共 {} 个任务类型", taskId, taskItems.size());
int itemIndex = 1;
BigDecimal totalScore = BigDecimal.ZERO;
BigDecimal totalHours = BigDecimal.ZERO;
int totalQuantity = 0;
// **步骤1:按接单部门和设计师自动分组**
int currentGroupNo = 1;
String lastGroupKey = null; // 格式:deptKey_receiverId
for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : taskItems) {
// 获取任务类型信息
for (int i = 0; i < taskItems.size(); i++) {
com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto = taskItems.get(i);
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
if (demandType == null) {
throw new BusinessException(ResultCode.PARAM_ERROR, "任务类型不存在: " + itemDto.getTaskTypeId());
}
// 计算积分和工时
BigDecimal itemScore = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity()));
BigDecimal itemHours = demandType.getEstimatedHours() != null
? demandType.getEstimatedHours().multiply(BigDecimal.valueOf(itemDto.getQuantity()))
: BigDecimal.ZERO;
// 获取当前任务类型的接单部门配置
String currentDeptKey;
totalScore = totalScore.add(itemScore);
totalHours = totalHours.add(itemHours);
totalQuantity += itemDto.getQuantity();
// 🔥 优先使用用户选择的接单部门
if (itemDto.getSelectedDeptId() != null) {
currentDeptKey = String.valueOf(itemDto.getSelectedDeptId());
log.info("任务类型 {} 使用用户选择的接单部门: {}", demandType.getName(), currentDeptKey);
} else {
// 否则使用任务类型配置的 allowedDeptIds(支持三级分类继承二级分类的配置)
currentDeptKey = demandType.getAllowedDeptIds();
if (currentDeptKey == null && demandType.getParentId() != null) {
// 如果当前类型没有配置,尝试获取父级的配置
var parentType = configService.getDemandTypeById(demandType.getParentId());
if (parentType != null) {
currentDeptKey = parentType.getAllowedDeptIds();
log.info("三级分类 {} 继承父级 {} 的接单部门配置: {}",
demandType.getName(), parentType.getName(), currentDeptKey);
}
}
}
// 🔥 构建分组键:部门 + 设计师(如果指定了设计师)
String currentGroupKey;
if (itemDto.getReceiverId() != null) {
// 如果指定了设计师,分组键 = 部门_设计师ID
currentGroupKey = (currentDeptKey != null ? currentDeptKey : "null") + "_" + itemDto.getReceiverId();
} else {
// 没有指定设计师,分组键 = 部门_0
currentGroupKey = (currentDeptKey != null ? currentDeptKey : "null") + "_0";
}
// 判断是否需要创建新分组
boolean needNewGroup = false;
if (i > 0) {
// 如果分组键不同,则需要创建新分组
if (lastGroupKey == null || !lastGroupKey.equals(currentGroupKey)) {
needNewGroup = true;
}
}
if (needNewGroup) {
currentGroupNo++;
log.info("检测到分组变化,创建新分组: groupNo={}, 前一分组键={}, 当前分组键={}",
currentGroupNo, lastGroupKey, currentGroupKey);
}
// 记录当前分组号(临时存储在sortOrder字段,稍后会恢复)
itemDto.setSortOrder(currentGroupNo * 1000 + i); // 高位存group_no,低位存原始顺序
lastGroupKey = currentGroupKey;
}
log.info("分组完成,共 {} 个子任务组", currentGroupNo);
// **步骤2:两级分组 - 先按部门分组,再按设计师分组**
// 收集所有分组键和对应的任务项
Map<Integer, List<com.designhub.dto.PipelineTaskDTO.TaskItemDTO>> groupedItems = new java.util.LinkedHashMap<>();
for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : taskItems) {
int itemGroupNo = itemDto.getSortOrder() / 1000;
groupedItems.computeIfAbsent(itemGroupNo, k -> new java.util.ArrayList<>()).add(itemDto);
}
// 按部门重新组织分组,计算部门序号
Map<String, List<Integer>> deptToGroups = new java.util.LinkedHashMap<>();
Map<Integer, String> groupToDeptKey = new java.util.HashMap<>();
for (int i = 0; i < taskItems.size(); i++) {
com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto = taskItems.get(i);
int itemGroupNo = itemDto.getSortOrder() / 1000;
// 获取部门键
String deptKey;
if (itemDto.getSelectedDeptId() != null) {
deptKey = String.valueOf(itemDto.getSelectedDeptId());
} else {
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
deptKey = demandType.getAllowedDeptIds();
if (deptKey == null && demandType.getParentId() != null) {
var parentType = configService.getDemandTypeById(demandType.getParentId());
if (parentType != null) {
deptKey = parentType.getAllowedDeptIds();
}
}
}
if (deptKey == null) deptKey = "null";
// 记录分组到部门的映射
if (!groupToDeptKey.containsKey(itemGroupNo)) {
groupToDeptKey.put(itemGroupNo, deptKey);
deptToGroups.computeIfAbsent(deptKey, k -> new java.util.ArrayList<>()).add(itemGroupNo);
}
}
// 计算每个部门的序号
Map<String, Integer> deptIndexMap = new java.util.LinkedHashMap<>();
int deptIndex = 1;
for (String deptKey : deptToGroups.keySet()) {
deptIndexMap.put(deptKey, deptIndex++);
}
// 计算每个分组在其部门内的子序号
Map<Integer, Integer> groupSubIndexMap = new java.util.HashMap<>();
for (Map.Entry<String, List<Integer>> entry : deptToGroups.entrySet()) {
List<Integer> groups = entry.getValue();
for (int subIdx = 0; subIdx < groups.size(); subIdx++) {
groupSubIndexMap.put(groups.get(subIdx), subIdx + 1);
}
}
log.info("部门分组统计: 共 {} 个部门, 各部门子任务数: {}", deptToGroups.size(),
deptToGroups.entrySet().stream()
.map(e -> e.getKey() + "=" + e.getValue().size())
.collect(Collectors.joining(", ")));
// **步骤3:为每个分组创建一个子任务**
int itemIndex = 1;
BigDecimal totalScore = BigDecimal.ZERO;
BigDecimal totalHours = BigDecimal.ZERO;
int totalQuantity = 0;
for (Map.Entry<Integer, List<com.designhub.dto.PipelineTaskDTO.TaskItemDTO>> entry : groupedItems.entrySet()) {
int groupNo = entry.getKey();
List<com.designhub.dto.PipelineTaskDTO.TaskItemDTO> groupItems = entry.getValue();
// 获取该分组的部门序号和部门内子序号
String deptKey = groupToDeptKey.get(groupNo);
int currentDeptIndex = deptIndexMap.getOrDefault(deptKey, 1);
int subIndex = groupSubIndexMap.getOrDefault(groupNo, 1);
// 汇总该分组的积分、工时、数量
BigDecimal groupTotalScoreDecimal = BigDecimal.ZERO;
BigDecimal groupTotalHours = BigDecimal.ZERO;
int groupTotalQuantity = 0;
Long firstTaskTypeId = null;
// 构建任务类型明细 JSON 数组
java.util.List<java.util.Map<String, Object>> typeDetailsList = new java.util.ArrayList<>();
for (com.designhub.dto.PipelineTaskDTO.TaskItemDTO itemDto : groupItems) {
var demandType = configService.getDemandTypeById(itemDto.getTaskTypeId());
BigDecimal itemScoreDecimal = demandType.getUnitScore().multiply(BigDecimal.valueOf(itemDto.getQuantity()));
BigDecimal itemHours = demandType.getEstimatedHours() != null
? demandType.getEstimatedHours().multiply(BigDecimal.valueOf(itemDto.getQuantity()))
: BigDecimal.ZERO;
groupTotalScoreDecimal = groupTotalScoreDecimal.add(itemScoreDecimal);
groupTotalHours = groupTotalHours.add(itemHours);
groupTotalQuantity += itemDto.getQuantity();
if (firstTaskTypeId == null) {
firstTaskTypeId = itemDto.getTaskTypeId();
}
// 添加任务类型明细
java.util.Map<String, Object> detail = new java.util.LinkedHashMap<>();
detail.put("typeId", itemDto.getTaskTypeId());
detail.put("typeName", demandType.getName());
detail.put("quantity", itemDto.getQuantity());
detail.put("unitScore", demandType.getUnitScore());
detail.put("totalScore", itemScoreDecimal.setScale(2, java.math.RoundingMode.HALF_UP).doubleValue());
// 如果指定了设计师,也保存到明细中
if (itemDto.getReceiverId() != null) {
detail.put("receiverId", itemDto.getReceiverId());
User receiver = userService.getUserById(itemDto.getReceiverId());
if (receiver != null) {
detail.put("receiverName", receiver.getRealName());
}
}
typeDetailsList.add(detail);
}
BigDecimal groupTotalScore = groupTotalScoreDecimal.setScale(2, java.math.RoundingMode.HALF_UP);
// 创建子任务
TaskItem item = new TaskItem();
item.setTaskId(taskId);
item.setItemNo(task.getTaskNo() + "-" + String.format("%02d", itemIndex));
item.setTaskTypeId(itemDto.getTaskTypeId());
item.setTaskTypeName(demandType.getName());
item.setQuantity(itemDto.getQuantity());
item.setUnitScore(demandType.getUnitScore());
item.setTotalScore(itemScore.setScale(2, java.math.RoundingMode.HALF_UP));
item.setEstimatedHours(itemHours);
// 使用新的编号格式:任务编号-部门序号-部门内子任务序号
item.setItemNo(generateTaskItemNoWithDept(task.getTaskNo(), currentDeptIndex, subIndex));
item.setGroupNo(groupNo);
item.setTaskTypeId(firstTaskTypeId);
// 保存任务类型明细 JSON
try {
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
item.setTypeDetails(objectMapper.writeValueAsString(typeDetailsList));
} catch (Exception e) {
log.warn("序列化任务类型明细失败: {}", e.getMessage());
}
item.setQuantity(groupTotalQuantity);
// 计算平均单价(保留2位小数)
item.setUnitScore(groupTotalQuantity > 0
? groupTotalScore.divide(BigDecimal.valueOf(groupTotalQuantity), 2, java.math.RoundingMode.HALF_UP)
: BigDecimal.ZERO);
item.setTotalScore(groupTotalScore);
item.setEstimatedHours(groupTotalHours);
item.setSortOrder(itemIndex);
item.setProgress(0);
item.setStatus("pending");
item.setIsLocked(itemIndex == 1 ? 0 : 1); // 第一个子任务解锁,其他锁定
// 设置接单部门(如果任务类型有配置)
if (demandType.getAllowedDeptIds() != null && !demandType.getAllowedDeptIds().isEmpty()) {
String[] deptIds = demandType.getAllowedDeptIds().split(",");
if (deptIds.length == 1) {
item.setReceiverDeptId(Long.parseLong(deptIds[0].trim()));
// 🔥 设置子任务的接单部门
// 优先使用用户选择的部门,否则尝试从任务类型配置获取
if (!groupItems.isEmpty()) {
Long selectedDeptId = groupItems.get(0).getSelectedDeptId();
if (selectedDeptId != null) {
item.setReceiverDeptId(selectedDeptId);
log.info("子任务 {} 设置用户选择的接单部门: {}", item.getItemNo(), selectedDeptId);
} else if (deptKey != null && !deptKey.equals("null") && !deptKey.contains(",")) {
// 任务类型只配置了一个部门,自动设置
try {
Long singleDeptId = Long.parseLong(deptKey.trim());
item.setReceiverDeptId(singleDeptId);
log.info("子任务 {} 自动设置唯一的接单部门: {}", item.getItemNo(), singleDeptId);
} catch (NumberFormatException e) {
log.warn("无法解析部门ID: {}", deptKey);
}
}
}
// 如果指定了设计师
if (itemDto.getReceiverId() != null) {
item.setReceiverId(itemDto.getReceiverId());
User receiver = userService.getUserById(itemDto.getReceiverId());
if (!groupItems.isEmpty() && groupItems.get(0).getReceiverId() != null) {
Long receiverId = groupItems.get(0).getReceiverId();
item.setReceiverId(receiverId);
User receiver = userService.getUserById(receiverId);
if (receiver != null) {
item.setReceiverName(receiver.getRealName());
if (item.getReceiverDeptId() == null && receiver.getDeptId() != null) {
@ -4146,26 +4341,13 @@ public class TaskService {
}
}
// 构建任务类型明细 JSON
java.util.List<java.util.Map<String, Object>> typeDetailsList = new java.util.ArrayList<>();
java.util.Map<String, Object> detail = new java.util.LinkedHashMap<>();
detail.put("typeId", itemDto.getTaskTypeId());
detail.put("typeName", demandType.getName());
detail.put("quantity", itemDto.getQuantity());
detail.put("unitScore", demandType.getUnitScore());
detail.put("totalScore", itemScore.setScale(2, java.math.RoundingMode.HALF_UP).doubleValue());
typeDetailsList.add(detail);
try {
com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
item.setTypeDetails(objectMapper.writeValueAsString(typeDetailsList));
} catch (Exception e) {
log.warn("序列化任务类型明细失败: {}", e.getMessage());
}
taskItemMapper.insert(item);
log.info("创建子任务: itemNo={}, typeId={}, score={}", item.getItemNo(), itemDto.getTaskTypeId(), itemScore);
log.info("创建子任务组: itemNo={}, groupNo={}, deptIndex={}, subIndex={}, types={}, score={}",
item.getItemNo(), groupNo, currentDeptIndex, subIndex, typeDetailsList.size(), groupTotalScore);
totalScore = totalScore.add(groupTotalScore);
totalHours = totalHours.add(groupTotalHours);
totalQuantity += groupTotalQuantity;
itemIndex++;
}
@ -4179,7 +4361,7 @@ public class TaskService {
}
taskMapper.updateById(task);
log.info("任务 {} 子任务创建完成,共 {} 个子任务,总积分 {}", taskId, taskItems.size(), totalScore);
log.info("任务 {} 子任务创建完成,共 {} 个子任务组,总积分 {}", taskId, groupedItems.size(), totalScore);
}
}