瀏覽代碼

批量导入EXCEL模板

cr 3 天之前
父節點
當前提交
9d35cd11f5

+ 96 - 46
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeContractController.java

@@ -432,6 +432,13 @@ public class WbsTreeContractController extends BladeController {
             }
         }
     }
+    // 1. 工具方法:统一主表名匹配格式(保留原逻辑,确保无字符差异)
+    private String getMatchableMainName(String name) {
+        if (name == null) return "";
+        String cleanName = name.replaceAll("\\s+", ""); // 去空格
+        cleanName = cleanName.replaceAll("[\\\\/:*?\"<>|_-]", ""); // 去特殊字符
+        return cleanName.toLowerCase(); // 转小写
+    }
     @SneakyThrows
     @GetMapping("/download-node-excel")
     @ApiOperationSupport(order = 14)
@@ -444,45 +451,96 @@ public class WbsTreeContractController extends BladeController {
         if (ObjectUtil.isEmpty(formList)) {
             throw new ServiceException("该节点下没有找到对应的表单数据");
         }
-        // 对formList进行排序:主表优先,复制表按数字顺序排列
-        formList.sort((contract1, contract2) -> {
-            String name1 = contract1.getNodeName().replaceAll("\\s+", "");
-            String name2 = contract2.getNodeName().replaceAll("\\s+", "");
-
-            boolean isCopy1 = name1.contains("__");
-            boolean isCopy2 = name2.contains("__");
+        // 2. 第一步:批量收集所有主表,创建分组(按主表在formList中的原始顺序)
+        Map<String, List<WbsTreeContract>> mainCopyGroupMap = new LinkedHashMap<>(); // 保持主表原始顺序
+        List<WbsTreeContract> copyTableList = new ArrayList<>(); // 临时存储所有复制表
+
+            // 2.1 第一次遍历:分离主表和复制表
+        for (WbsTreeContract contract : formList) {
+            String nodeName = contract.getNodeName();
+            boolean isCopy = nodeName.contains("__");
+
+            if (isCopy) {
+                // 复制表:先暂存,不立即匹配
+                copyTableList.add(contract);
+            } else {
+                // 主表:创建分组(按原始顺序插入)
+                String matchableMainName = getMatchableMainName(nodeName);
+                if (!mainCopyGroupMap.containsKey(matchableMainName)) {
+                    List<WbsTreeContract> groupList = new ArrayList<>();
+                    groupList.add(contract); // 主表放在分组第一位
+                    mainCopyGroupMap.put(matchableMainName, groupList);
+                } else {
+                    // 重复主表:仍放在分组第一位,保证主表优先
+                    mainCopyGroupMap.get(matchableMainName).add(0, contract);
+                }
+            }
+        }
 
-            // 如果一个是主表,一个是复制表,主表排在前面
-            if (isCopy1 != isCopy2) {
-                return isCopy1 ? 1 : -1;
+            // 3. 第二步:批量处理所有复制表,匹配主表分组
+        List<WbsTreeContract> orphanCopyList = new ArrayList<>();
+        for (WbsTreeContract copyContract : copyTableList) {
+            String nodeName = copyContract.getNodeName();
+            String[] parts = nodeName.split("__", 2);
+            if (parts.length < 2) {
+                orphanCopyList.add(copyContract);
+                continue;
             }
 
-            // 如果都是主表或都是复制表
-            if (!isCopy1 && !isCopy2) {
-                // 都是主表,按字母顺序排序
-                return name1.compareTo(name2);
-            } else {
-                // 都是复制表,按主表名称和数字排序
-                String[] parts1 = name1.split("__", 2);
-                String[] parts2 = name2.split("__", 2);
-
-                // 先比较主表名称
-                int mainNameCompare = parts1[0].compareTo(parts2[0]);
-                if (mainNameCompare != 0) {
-                    return mainNameCompare;
+            // 复制表拆分+统一格式,匹配主表分组
+            String rawMainName = parts[0];
+            String matchableMainName = getMatchableMainName(rawMainName);
+            boolean foundMatch = false;
+
+            // 遍历主表分组,找匹配的分组
+            for (Map.Entry<String, List<WbsTreeContract>> groupEntry : mainCopyGroupMap.entrySet()) {
+                if (groupEntry.getKey().equals(matchableMainName)) {
+                    groupEntry.getValue().add(copyContract); // 加入对应主表分组
+                    foundMatch = true;
+                    break;
                 }
+            }
 
-                // 主表名称相同,比较数字部分
+            if (!foundMatch) {
+                orphanCopyList.add(copyContract);
+            }
+        }
+
+            // 4. 第三步:处理每个主表分组,对复制表排序(保持主表原始顺序)
+        List<WbsTreeContract> sortedList = new ArrayList<>();
+        for (Map.Entry<String, List<WbsTreeContract>> entry : mainCopyGroupMap.entrySet()) {
+            List<WbsTreeContract> groupList = entry.getValue();
+            if (groupList.size() <= 1) {
+                // 只有主表,直接加入
+                sortedList.addAll(groupList);
+                continue;
+            }
+
+            // 拆分主表(第一位)和复制表(剩余)
+            WbsTreeContract mainContract = groupList.get(0);
+            List<WbsTreeContract> copyList = groupList.subList(1, groupList.size());
+
+            // 复制表按数字排序
+            copyList.sort((c1, c2) -> {
+                String numStr1 = c1.getNodeName().split("__", 2)[1];
+                String numStr2 = c2.getNodeName().split("__", 2)[1];
                 try {
-                    int num1 = Integer.parseInt(parts1[1]);
-                    int num2 = Integer.parseInt(parts2[1]);
-                    return Integer.compare(num1, num2);
+                    return Integer.compare(Integer.parseInt(numStr1), Integer.parseInt(numStr2));
                 } catch (NumberFormatException e) {
-                    // 如果解析数字失败,按字符串比较
-                    return parts1[1].compareTo(parts2[1]);
+                    return numStr1.compareTo(numStr2);
                 }
-            }
-        });
+            });
+
+            // 主表 + 排序后的复制表,加入最终列表
+            sortedList.add(mainContract);
+            sortedList.addAll(copyList);
+        }
+
+        // 5. 第四步:加入无对应主表的复制表(排在最后)
+        sortedList.addAll(orphanCopyList);
+
+        // 6. 替换原列表
+        formList = sortedList;
 
         // 获取节点及祖先节点信息用于构建文件名
         WbsTreeContract node = wbsTreeContractServiceImpl.getById(nodeId);
@@ -553,21 +611,15 @@ public class WbsTreeContractController extends BladeController {
                 throw new ServiceException("所有表单均无法生成有效Excel内容");
             }
 
-            // 处理包含特殊符号的文件名(如#、空格、中文等)
-             String originalFileName = excelName + ".xlsx";
-              // 使用UTF-8编码,并替换特殊字符(符合RFC 5987标准)
+            String originalFileName = excelName + ".xlsx";
+                // 使用UTF-8编码,并替换特殊字符(符合RFC 5987标准)
             String encodedFileName = URLEncoder.encode(originalFileName, StandardCharsets.UTF_8.name())
                     .replaceAll("\\+", "%20") // 空格编码为%20(而非+)
-                    .replaceAll("%23", "#")
-                    .replaceAll("%26", "&"); // 保留#不编码(或根据需求调整)
+                    .replaceAll("%23", "#"); // 保留#不编码(或根据需求调整)
 
               // 设置响应头,采用RFC 5987标准格式(指定字符集)
-              // 双格式响应头:同时支持旧版和新版浏览器
-             response.setHeader("Content-Disposition",
-                    "attachment; " +
-                            "filename=\"" + encodedFileName + "\"; " +  // 旧格式(部分浏览器依赖)
-                            "filename*=UTF-8''" + encodedFileName       // 新标准格式
-             );
+            response.setHeader("Content-Disposition",
+                    "attachment; filename*=" + encodedFileName);
             response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
 
             // 写入输出流
@@ -829,12 +881,10 @@ public class WbsTreeContractController extends BladeController {
                     continue;
                 }
                 Map<String, String> dataMap = getDataMap(sheetResult);
-                // 对所有value中的单引号进行转义处理
                 for (Map.Entry<String, String> entry : dataMap.entrySet()) {
                     String value = entry.getValue();
-                    if (value != null && value.contains("''")) {
-                        // 将单引号转义为 \'
-                        value = value.replaceAll("'", "''");
+                    if (value != null) {
+                        value = value.replace("'", "''");
                         entry.setValue(value);
                     }
                 }