diff --git a/backend/src/main/java/com/designhub/service/ExportService.java b/backend/src/main/java/com/designhub/service/ExportService.java index ca9ee6e..74c92f5 100644 --- a/backend/src/main/java/com/designhub/service/ExportService.java +++ b/backend/src/main/java/com/designhub/service/ExportService.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.designhub.entity.*; import com.designhub.mapper.*; import com.designhub.util.ExcelExportUtil; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -26,6 +27,7 @@ import java.util.Map; public class ExportService { private final TaskMapper taskMapper; + private final TaskItemMapper taskItemMapper; private final UserMapper userMapper; private final DepartmentMapper departmentMapper; private final DesignerService designerService; @@ -34,6 +36,7 @@ public class ExportService { private final CategoryMapper categoryMapper; private final PlatformMapper platformMapper; private final org.springframework.jdbc.core.JdbcTemplate jdbcTemplate; + private final ObjectMapper objectMapper; private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @@ -87,6 +90,20 @@ public class ExportService { Map userMap = loadUserMap(); Map deptMap = loadDeptMap(); + // 预加载所有相关任务的子任务明细 + List taskIds = tasks.stream().map(Task::getId).toList(); + Map> taskItemsMap = new HashMap<>(); + if (!taskIds.isEmpty()) { + List allItems = taskItemMapper.selectList( + new LambdaQueryWrapper() + .in(TaskItem::getTaskId, taskIds) + .orderBy(true, true, TaskItem::getGroupNo) + ); + for (TaskItem item : allItems) { + taskItemsMap.computeIfAbsent(item.getTaskId(), k -> new ArrayList<>()).add(item); + } + } + // 转换为导出数据 List exportList = new ArrayList<>(); for (Task task : tasks) { @@ -113,23 +130,82 @@ public class ExportService { vo.setIsPipeline(task.getIsPipeline() != null && task.getIsPipeline() == 1 ? "是" : "否"); vo.setCreateTime(task.getCreateTime() != null ? task.getCreateTime().format(DATE_FORMATTER) : ""); vo.setDeadline(task.getDeadline() != null ? task.getDeadline().format(DATE_FORMATTER) : ""); + + // 🔥 添加任务明细和积分明细 + List items = taskItemsMap.get(task.getId()); + if (items != null && !items.isEmpty()) { + StringBuilder taskDetails = new StringBuilder(); + StringBuilder scoreDetails = new StringBuilder(); + + for (int i = 0; i < items.size(); i++) { + TaskItem item = items.get(i); + if (i > 0) { + taskDetails.append("\n"); + scoreDetails.append("\n"); + } + + // 解析typeDetails JSON + if (item.getTypeDetails() != null && !item.getTypeDetails().isEmpty()) { + try { + @SuppressWarnings("unchecked") + List> typeDetails = objectMapper.readValue(item.getTypeDetails(), List.class); + StringBuilder itemDetail = new StringBuilder(); + StringBuilder itemScore = new StringBuilder(); + + for (int j = 0; j < typeDetails.size(); j++) { + Map detail = typeDetails.get(j); + String typeName = String.valueOf(detail.get("typeName")); + Object qtyObj = detail.get("quantity"); + Object scoreObj = detail.get("totalScore"); + int qty = qtyObj instanceof Number ? ((Number) qtyObj).intValue() : 1; + String score = scoreObj != null ? String.valueOf(scoreObj) : "0"; + + if (j > 0) { + itemDetail.append("; "); + itemScore.append("; "); + } + itemDetail.append(typeName).append(" x").append(qty); + itemScore.append(typeName).append(": ").append(score).append("分"); + } + + // 如果有多个子任务组,添加组号前缀 + if (items.size() > 1) { + taskDetails.append("第").append(item.getGroupNo()).append("组: "); + scoreDetails.append("第").append(item.getGroupNo()).append("组: "); + } + taskDetails.append(itemDetail); + scoreDetails.append(itemScore); + } catch (Exception e) { + log.warn("解析任务明细失败: taskId={}, itemId={}, error={}", + task.getId(), item.getId(), e.getMessage()); + } + } + } + + vo.setTaskDetails(taskDetails.toString()); + vo.setScoreDetails(scoreDetails.toString()); + } else { + vo.setTaskDetails(""); + vo.setScoreDetails(""); + } + exportList.add(vo); } // 定义表头 String[] headers = { "任务编号", "任务标题", "任务类型", "需求类型", "品牌", "类目", "平台", - "SPU信息", "任务积分", "预估工时(小时)", "数量", "发布人", "发布部门", - "接收人", "接收部门", "状态", "发布类型", "是否紧急", "是否流水线", - "创建时间", "截止时间" + "SPU信息", "任务积分", "积分明细", "任务明细", "预估工时(小时)", "数量", + "发布人", "发布部门", "接收人", "接收部门", "状态", "发布类型", + "是否紧急", "是否流水线", "创建时间", "截止时间" }; // 定义字段名 String[] fieldNames = { "taskNo", "title", "taskType", "demandType", "brand", "category", "platform", - "spuInfo", "score", "estimatedHours", "quantity", "publisher", "publisherDept", - "receiver", "receiverDept", "status", "releaseType", "isUrgent", "isPipeline", - "createTime", "deadline" + "spuInfo", "score", "scoreDetails", "taskDetails", "estimatedHours", "quantity", + "publisher", "publisherDept", "receiver", "receiverDept", "status", "releaseType", + "isUrgent", "isPipeline", "createTime", "deadline" }; // 生成文件名 @@ -255,6 +331,8 @@ public class ExportService { private String platform; private String spuInfo; private String score; + private String scoreDetails; // 积分明细 + private String taskDetails; // 任务明细 private String estimatedHours; private Integer quantity; private String publisher; diff --git a/更新通知-2026-01-24.md b/更新通知-2026-01-24.md new file mode 100644 index 0000000..a1a9e2f --- /dev/null +++ b/更新通知-2026-01-24.md @@ -0,0 +1,70 @@ +# 系统更新通知 + +**更新日期**:2026年1月20日 - 1月24日 + +--- + +## 新功能 + +### 任务节点查询(1月24日) +- 新增任务节点查询功能,点击顶部导航栏的搜索图标即可使用 +- 支持通过任务编号或任务标题查询任务当前进度 +- 可查看完整的任务流程时间线和相关人员信息 + +### 任务搜索增强(1月22日) +- 扩展任务搜索功能,支持搜索任务卡片的所有信息(品牌、平台、SPU、发起人等) + +### 数据分析增强(1月21日-23日) +- 一级需求部门排名统计现在显示所有部门(包括没有发起任务的) +- 一级需求部门排名表格增加一级部门和二级部门名称列 +- 新增用户维度的数据分析功能 +- 新增任务明细类型分析(一级、二级、三级分类统计) + +### 任务大厅权限扩展(1月21日) +- 一级需求部门和需求部门成员现在可以查看任务大厅所有任务卡片 + +--- + +## 功能优化 + +### 一级需求任务流程 +- 一级需求部门发起任务现在必须上传预览图 +- 任务卡片和子任务卡片现在显示一级需求人和需求部门发起人信息 +- 优化一级需求任务的发起人显示,现在显示明细完善人作为发起人 +- 一级需求发起人现在可以收到任务各节点的进度通知(审核通过、驳回、接单、进度更新、完成等) +- 明细完善人现在可以撤回和编辑草稿状态的任务 +- 支持审核时选择子部门成员完善任务明细 +- 支持一级需求部门任务跨部门分配成员 + +### 界面优化 +- 任务卡片需求人名字过长时显示省略号,鼠标悬停显示完整信息 +- 优化全局滚动条样式 +- 子任务状态 pending_approval 现在显示为中文"待审核" +- 审核通过的任务正确显示"待接单"或"进行中"状态 + +### 流水线任务通知 +- 流水线任务(如建模+美工)如果预先指定了第二节点的设计师,当第一节点完成后会自动通知该设计师 +- 子任务组审核通过后的通知现在显示完整的任务明细积分 + +### 积分池 +- 子部门现在可以共享上级部门的积分池(当子部门积分为0且未单独充值时) +- 二级部门支持独立配置积分池 + +### 导出功能 +- 修复导出功能报错问题 +- 导出数据现在正确显示需求类型(新品/优化等) + +--- + +## 问题修复 + +- 修复任务类型分析数据未正确聚合到上级分类的问题 +- 修复子任务状态在某些情况下未正确更新的问题 +- 修复预先指定设计师的子任务在审核时未自动识别的问题 +- 修复数据分析页面用户需求排名的积分统计错误 +- 修复进度检查错误扣分问题(排除流水线任务的主任务) +- 修复接单部门驳回任务时的退回逻辑(没有负责人时退回给发起人) + +--- + +如有问题请联系管理员。