diff --git a/backend/src/main/java/com/designhub/service/ExportService.java b/backend/src/main/java/com/designhub/service/ExportService.java index 74c92f5..63c5cbf 100644 --- a/backend/src/main/java/com/designhub/service/ExportService.java +++ b/backend/src/main/java/com/designhub/service/ExportService.java @@ -104,109 +104,144 @@ public class ExportService { } } - // 转换为导出数据 - List exportList = new ArrayList<>(); + // 🔥 第一遍遍历:收集所有出现的任务类型名称(用于动态列) + java.util.LinkedHashSet allTypeNames = new java.util.LinkedHashSet<>(); for (Task task : tasks) { - TaskHallExportVO vo = new TaskHallExportVO(); - vo.setTaskNo(task.getTaskNo()); - vo.setTitle(task.getTitle()); - vo.setTaskType(taskTypeMap.getOrDefault(task.getTaskTypeId(), "")); - // 需求类型使用 taskSubType 字段(新品/优化/实拍等) - vo.setDemandType(task.getTaskSubType() != null ? task.getTaskSubType() : ""); - vo.setBrand(brandMap.getOrDefault(task.getBrandId(), "")); - vo.setCategory(categoryMap.getOrDefault(task.getCategoryId(), "")); - vo.setPlatform(platformMap.getOrDefault(task.getPlatformId(), "")); - vo.setSpuInfo(task.getSpuInfo()); - vo.setScore(task.getScore() != null ? task.getScore().toString() : "0"); - vo.setEstimatedHours(task.getEstimatedHours() != null ? task.getEstimatedHours().toString() : "0"); - vo.setQuantity(task.getQuantity() != null ? task.getQuantity() : 1); - vo.setPublisher(userMap.getOrDefault(task.getPublisherId(), "")); - vo.setPublisherDept(deptMap.getOrDefault(task.getPublisherDeptId(), "")); - vo.setReceiver(userMap.getOrDefault(task.getReceiverId(), "")); - vo.setReceiverDept(deptMap.getOrDefault(task.getReceiverDeptId(), "")); - vo.setStatus(getStatusText(task.getStatus())); - vo.setReleaseType(getReleaseTypeText(task.getReleaseType())); - vo.setIsUrgent(task.getIsUrgent() != null && task.getIsUrgent() == 1 ? "是" : "否"); - 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 (items != null) { + for (TaskItem item : items) { 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); + for (Map detail : typeDetails) { 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("; "); + if (typeName != null && !typeName.equals("null")) { + allTypeNames.add(typeName); } - 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()); + log.warn("解析任务类型失败: itemId={}", item.getId()); + } + } + } + } + } + List typeNameList = new ArrayList<>(allTypeNames); + log.info("导出任务大厅数据,发现 {} 种任务类型: {}", typeNameList.size(), typeNameList); + + // 转换为导出数据(使用Map存储动态列) + List> exportList = new ArrayList<>(); + for (Task task : tasks) { + Map row = new java.util.LinkedHashMap<>(); + row.put("taskNo", task.getTaskNo()); + row.put("title", task.getTitle()); + row.put("taskType", taskTypeMap.getOrDefault(task.getTaskTypeId(), "")); + row.put("demandType", task.getTaskSubType() != null ? task.getTaskSubType() : ""); + row.put("brand", brandMap.getOrDefault(task.getBrandId(), "")); + row.put("category", categoryMap.getOrDefault(task.getCategoryId(), "")); + row.put("platform", platformMap.getOrDefault(task.getPlatformId(), "")); + row.put("spuInfo", task.getSpuInfo() != null ? task.getSpuInfo() : ""); + row.put("score", task.getScore() != null ? task.getScore().toString() : "0"); + row.put("estimatedHours", task.getEstimatedHours() != null ? task.getEstimatedHours().toString() : "0"); + row.put("quantity", task.getQuantity() != null ? task.getQuantity() : 1); + + // 🔥 初始化所有任务类型列为空(数量和积分都初始化) + for (String typeName : typeNameList) { + row.put("qty_" + typeName, ""); + row.put("score_" + typeName, ""); + } + + // 🔥 填充该任务的各任务类型数量和积分 + List items = taskItemsMap.get(task.getId()); + if (items != null) { + // 汇总同一任务所有子任务的类型数据 + Map typeQtyMap = new HashMap<>(); + Map typeScoreMap = new HashMap<>(); + + for (TaskItem item : items) { + if (item.getTypeDetails() != null && !item.getTypeDetails().isEmpty()) { + try { + @SuppressWarnings("unchecked") + List> typeDetails = objectMapper.readValue(item.getTypeDetails(), List.class); + for (Map detail : typeDetails) { + String typeName = String.valueOf(detail.get("typeName")); + if (typeName != null && !typeName.equals("null")) { + Object qtyObj = detail.get("quantity"); + Object scoreObj = detail.get("totalScore"); + int qty = qtyObj instanceof Number ? ((Number) qtyObj).intValue() : 0; + double score = scoreObj instanceof Number ? ((Number) scoreObj).doubleValue() : 0; + + typeQtyMap.merge(typeName, qty, Integer::sum); + typeScoreMap.merge(typeName, score, Double::sum); + } + } + } catch (Exception e) { + log.warn("解析任务明细失败: taskId={}, itemId={}", task.getId(), item.getId()); } } } - vo.setTaskDetails(taskDetails.toString()); - vo.setScoreDetails(scoreDetails.toString()); - } else { - vo.setTaskDetails(""); - vo.setScoreDetails(""); + // 填入汇总数据 + for (Map.Entry entry : typeQtyMap.entrySet()) { + row.put("qty_" + entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : typeScoreMap.entrySet()) { + row.put("score_" + entry.getKey(), entry.getValue()); + } } - exportList.add(vo); + row.put("publisher", userMap.getOrDefault(task.getPublisherId(), "")); + row.put("publisherDept", deptMap.getOrDefault(task.getPublisherDeptId(), "")); + row.put("receiver", userMap.getOrDefault(task.getReceiverId(), "")); + row.put("receiverDept", deptMap.getOrDefault(task.getReceiverDeptId(), "")); + row.put("status", getStatusText(task.getStatus())); + row.put("releaseType", getReleaseTypeText(task.getReleaseType())); + row.put("isUrgent", task.getIsUrgent() != null && task.getIsUrgent() == 1 ? "是" : "否"); + row.put("isPipeline", task.getIsPipeline() != null && task.getIsPipeline() == 1 ? "是" : "否"); + row.put("createTime", task.getCreateTime() != null ? task.getCreateTime().format(DATE_FORMATTER) : ""); + row.put("deadline", task.getDeadline() != null ? task.getDeadline().format(DATE_FORMATTER) : ""); + + exportList.add(row); } - // 定义表头 - String[] headers = { - "任务编号", "任务标题", "任务类型", "需求类型", "品牌", "类目", "平台", - "SPU信息", "任务积分", "积分明细", "任务明细", "预估工时(小时)", "数量", - "发布人", "发布部门", "接收人", "接收部门", "状态", "发布类型", - "是否紧急", "是否流水线", "创建时间", "截止时间" - }; + // 🔥 动态构建表头和字段名 + List headerList = new ArrayList<>(); + List fieldList = new ArrayList<>(); - // 定义字段名 - String[] fieldNames = { - "taskNo", "title", "taskType", "demandType", "brand", "category", "platform", - "spuInfo", "score", "scoreDetails", "taskDetails", "estimatedHours", "quantity", - "publisher", "publisherDept", "receiver", "receiverDept", "status", "releaseType", - "isUrgent", "isPipeline", "createTime", "deadline" - }; + // 基本信息列 + headerList.add("任务编号"); fieldList.add("taskNo"); + headerList.add("任务标题"); fieldList.add("title"); + headerList.add("任务类型"); fieldList.add("taskType"); + headerList.add("需求类型"); fieldList.add("demandType"); + headerList.add("品牌"); fieldList.add("brand"); + headerList.add("类目"); fieldList.add("category"); + headerList.add("平台"); fieldList.add("platform"); + headerList.add("SPU信息"); fieldList.add("spuInfo"); + headerList.add("任务积分"); fieldList.add("score"); + headerList.add("预估工时(小时)"); fieldList.add("estimatedHours"); + headerList.add("数量"); fieldList.add("quantity"); + + // 🔥 动态添加任务类型列(数量和积分分开) + for (String typeName : typeNameList) { + headerList.add(typeName + "(数量)"); fieldList.add("qty_" + typeName); + headerList.add(typeName + "(积分)"); fieldList.add("score_" + typeName); + } + + // 其他信息列 + headerList.add("发布人"); fieldList.add("publisher"); + headerList.add("发布部门"); fieldList.add("publisherDept"); + headerList.add("接收人"); fieldList.add("receiver"); + headerList.add("接收部门"); fieldList.add("receiverDept"); + headerList.add("状态"); fieldList.add("status"); + headerList.add("发布类型"); fieldList.add("releaseType"); + headerList.add("是否紧急"); fieldList.add("isUrgent"); + headerList.add("是否流水线"); fieldList.add("isPipeline"); + headerList.add("创建时间"); fieldList.add("createTime"); + headerList.add("截止时间"); fieldList.add("deadline"); + + String[] headers = headerList.toArray(new String[0]); + String[] fieldNames = fieldList.toArray(new String[0]); // 生成文件名 String fileName = "任务大厅数据_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); @@ -318,7 +353,7 @@ public class ExportService { } /** - * 任务大厅导出VO + * 任务大厅导出VO(保留用于兼容,实际使用Map动态列) */ @lombok.Data public static class TaskHallExportVO { @@ -331,8 +366,6 @@ 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; @@ -345,6 +378,7 @@ public class ExportService { private String isPipeline; private String createTime; private String deadline; + // 动态任务类型列通过Map处理,不在此定义 } /** diff --git a/backend/src/main/java/com/designhub/util/ExcelExportUtil.java b/backend/src/main/java/com/designhub/util/ExcelExportUtil.java index 61b0a0c..73284f0 100644 --- a/backend/src/main/java/com/designhub/util/ExcelExportUtil.java +++ b/backend/src/main/java/com/designhub/util/ExcelExportUtil.java @@ -136,9 +136,16 @@ public class ExcelExportUtil { } /** - * 通过反射获取字段值 + * 通过反射获取字段值(支持Map和普通对象) */ + @SuppressWarnings("unchecked") private static Object getFieldValue(Object obj, String fieldName) { + // 🔥 如果是Map类型,直接通过key获取值 + if (obj instanceof java.util.Map) { + return ((java.util.Map) obj).get(fieldName); + } + + // 普通对象通过反射获取 try { Field field = findField(obj.getClass(), fieldName); if (field != null) { diff --git a/更新通知-2026-01-24.md b/更新通知-2026-01-24.md index a1a9e2f..786034b 100644 --- a/更新通知-2026-01-24.md +++ b/更新通知-2026-01-24.md @@ -53,6 +53,7 @@ ### 导出功能 - 修复导出功能报错问题 - 导出数据现在正确显示需求类型(新品/优化等) +- 任务大厅导出数据新增"任务明细"和"积分明细"列 ---