Ver Fonte

导出划分

cr há 1 mês atrás
pai
commit
8429997998

+ 490 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeContractController.java

@@ -18,8 +18,10 @@ import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.SneakyThrows;
 import org.apache.commons.lang.StringUtils;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.WorkbookUtil;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
@@ -27,6 +29,7 @@ import org.jsoup.nodes.Element;
 import org.jsoup.select.Elements;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springblade.business.vo.NeiYeLedgerVO1;
 import org.springblade.common.utils.CommonUtil;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.boot.ctrl.BladeController;
@@ -48,6 +51,11 @@ import org.springblade.manager.service.impl.WbsTreeContractServiceImpl;
 import org.springblade.manager.utils.FileUtils;
 import org.springblade.manager.utils.RandomNumberHolder;
 import org.springblade.manager.vo.*;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.web.bind.annotation.*;
@@ -152,6 +160,7 @@ public class WbsTreeContractController extends BladeController {
         return iWbsTreeContractService.importTree(file,pkeyId);
     }
 
+
     @GetMapping("/getNameRuleByPkeyId")
     @ApiOperationSupport(order = 31)
     @ApiOperation(value = "根据节点PkeyId获取节点配置")
@@ -828,5 +837,486 @@ public class WbsTreeContractController extends BladeController {
         return R.success("成功");
     }
 
+    @SneakyThrows
+    @PostMapping("/exportTree")
+    @ApiOperationSupport(order = 31)
+    @ApiOperation(value = "工程划分-导出划分")
+    public ResponseEntity<Resource> exportTree(Long contractId, HttpServletResponse response) {
+        String templatePath = "C:\\upload\\excel\\gcdc.xlsx";
+        String outputPath = "C:\\upload\\excel\\111.xlsx";
+
+        // 查询数据
+        String sql = "select * from m_wbs_tree_contract where contract_id = " + contractId +
+                " and is_deleted = 0 and type = 1 and node_type != 6 and p_id != 0";
+        List<WbsTreeContract> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(WbsTreeContract.class));
+
+        InputStream templateStream = new FileInputStream(new File(templatePath));
+        org.apache.poi.ss.usermodel.Workbook workbook = WorkbookFactory.create(templateStream);
+        Sheet templateSheet = workbook.getSheetAt(0);
+
+        // 移除默认的Sheet1
+        workbook.removeSheetAt(0);
+
+        // 创建居中的单元格样式
+        CellStyle centerStyle = workbook.createCellStyle();
+        centerStyle.setAlignment(HorizontalAlignment.CENTER);
+        centerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+        // 按单位工程分组
+        Map<Long, List<WbsTreeContract>> unitProjectMap = list.stream()
+                .filter(item -> item.getNodeType() == 18) // 单位工程
+                .collect(Collectors.toMap(
+                        WbsTreeContract::getPKeyId,
+                        unit -> list.stream()
+                                .filter(item -> item.getAncestors() != null &&
+                                        item.getAncestors().contains(unit.getPKeyId().toString()))
+                                .collect(Collectors.toList())
+                ));
+
+        // 为每个单位工程创建sheet
+        for (Map.Entry<Long, List<WbsTreeContract>> entry : unitProjectMap.entrySet()) {
+            Long unitId = entry.getKey();
+            List<WbsTreeContract> unitProjects = entry.getValue();
+
+            // 获取单位工程名称
+            String unitName = unitProjects.stream()
+                    .filter(item -> item.getPKeyId().equals(unitId))
+                    .findFirst()
+                    .map(WbsTreeContract::getNodeName)
+                    .orElse("未知单位工程");
+            // 创建安全的sheet名称
+            String safeUnitName = unitName.replaceAll("[\\\\/*\\[\\]:?]", "_");
+            if (safeUnitName.length() > 31) {
+                safeUnitName = safeUnitName.substring(0, 31);
+            }
+            String validSheetName = WorkbookUtil.createSafeSheetName(safeUnitName);
+
+            // 创建sheet并设置名称
+            Sheet sheet = workbook.createSheet(validSheetName);
+
+            // 创建表头(两行)- 使用模板sheet
+            createHeaderRows(sheet, templateSheet, centerStyle);
+
+            // 处理数据行
+            processDataRows(sheet, unitProjects, unitId, centerStyle);
+
+            // 设置列宽自动调整
+            autoSizeColumns(sheet);
+
+            // 只隐藏pkeyId列(不保护,允许用户取消隐藏)
+            hidePkeyIdColumnsOnly(sheet);
+        }
+
+        // 保存到本地文件
+        saveWorkbookToFile(workbook, outputPath);
+
+        // 同时返回给浏览器下载
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        workbook.write(outputStream);
+        workbook.close();
+
+        ByteArrayResource resource = new ByteArrayResource(outputStream.toByteArray());
+        return ResponseEntity.ok()
+                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=engineering_division.xlsx")
+                .contentType(MediaType.APPLICATION_OCTET_STREAM)
+                .contentLength(resource.contentLength())
+                .body(resource);
+    }
+
+    // 只隐藏pkeyId列(不设置保护,允许用户取消隐藏)
+    private void hidePkeyIdColumnsOnly(Sheet sheet) {
+        int[] pkeyIdColumns = {2, 5, 8, 11, 14};
+
+        // 只隐藏列,不设置任何保护
+        for (int col : pkeyIdColumns) {
+            sheet.setColumnHidden(col, true);
+        }
+
+        // 特别注意:不调用 sheet.protectSheet() 方法
+        // 这样用户就可以自由调整列宽和取消隐藏列
+    }
+
+    // 创建表头行(两行)- 添加居中样式
+    private void createHeaderRows(Sheet sheet, Sheet templateSheet, CellStyle centerStyle) {
+        // 复制第一行
+        Row templateHeaderRow = templateSheet.getRow(0);
+        Row headerRow = sheet.createRow(0);
+        for (int i = 0; i < 15; i++) {
+            Cell templateCell = templateHeaderRow.getCell(i);
+            Cell newCell = headerRow.createCell(i);
+            if (templateCell != null) {
+                newCell.setCellValue(templateCell.getStringCellValue());
+                newCell.setCellStyle(centerStyle);
+            }
+        }
+
+        // 复制第二行
+        Row templateSubHeaderRow = templateSheet.getRow(1);
+        Row subHeaderRow = sheet.createRow(1);
+        for (int i = 0; i < 15; i++) {
+            Cell templateCell = templateSubHeaderRow.getCell(i);
+            Cell newCell = subHeaderRow.createCell(i);
+            if (templateCell != null) {
+                newCell.setCellValue(templateCell.getStringCellValue());
+                newCell.setCellStyle(centerStyle);
+            }
+        }
+
+        // 从模板复制合并区域
+        copyMergedRegionsFromTemplate(sheet, templateSheet);
+    }
+
+    // 处理数据行 - 添加居中样式参数
+    private void processDataRows(Sheet sheet, List<WbsTreeContract> unitProjects, Long unitId, CellStyle centerStyle) {
+        int rowNum = 2; // 从第3行开始(0-based,所以是第3行)
+        Map<Integer, List<CellRangeAddress>> mergeMap = new HashMap<>();
+
+        // 按照层级顺序查找叶子节点:子分项 -> 分项 -> 子分部 -> 分部 -> 单位工程
+        List<WbsTreeContract> leafNodes = findLeafNodesByPriority(unitProjects);
+
+        for (WbsTreeContract leafNode : leafNodes) {
+            Row row = sheet.createRow(rowNum);
+
+            // 解析祖级节点
+            String[] ancestors = leafNode.getAncestors().split(",");
+
+            // 转换为 List<Long>
+            List<Long> ancestorIds = Arrays.stream(ancestors)
+                    .map(Long::parseLong)
+                    .collect(Collectors.toList());
+
+            // 根据叶子节点类型填充各级工程数据
+            fillEngineeringDataByLeafType(row, unitProjects, ancestorIds, mergeMap, rowNum, centerStyle, leafNode.getNodeType());
+
+            rowNum++;
+        }
+
+        // 应用合并单元格
+        applyMergedRegions(sheet, mergeMap, centerStyle);
+    }
+
+    // 按照优先级查找叶子节点
+    private List<WbsTreeContract> findLeafNodesByPriority(List<WbsTreeContract> projects) {
+        // 按照节点类型优先级:子分项(5) -> 分项(4) -> 子分部(3) -> 分部(2) -> 单位工程(18)
+        List<WbsTreeContract> leafNodes = new ArrayList<>();
+
+        // 1. 先找子分项工程 (节点类型5)
+        leafNodes = projects.stream()
+                .filter(item -> item.getNodeType() == 5)
+                .collect(Collectors.toList());
+
+        if (!leafNodes.isEmpty()) {
+            return leafNodes;
+        }
+
+        // 2. 如果没有子分项,找分项工程 (节点类型4)
+        leafNodes = projects.stream()
+                .filter(item -> item.getNodeType() == 4)
+                .collect(Collectors.toList());
+
+        if (!leafNodes.isEmpty()) {
+            return leafNodes;
+        }
+
+        // 3. 如果没有分项,找子分部工程 (节点类型3)
+        leafNodes = projects.stream()
+                .filter(item -> item.getNodeType() == 3)
+                .collect(Collectors.toList());
+
+        if (!leafNodes.isEmpty()) {
+            return leafNodes;
+        }
+
+        // 4. 如果没有子分部,找分部工程 (节点类型2)
+        leafNodes = projects.stream()
+                .filter(item -> item.getNodeType() == 2)
+                .collect(Collectors.toList());
+
+        if (!leafNodes.isEmpty()) {
+            return leafNodes;
+        }
+
+        // 5. 最后找单位工程 (节点类型18)
+        return projects.stream()
+                .filter(item -> item.getNodeType() == 18)
+                .collect(Collectors.toList());
+    }
+
+    // 根据叶子节点类型填充工程数据
+    private void fillEngineeringDataByLeafType(Row row, List<WbsTreeContract> projects, List<Long> ancestorIds,
+                                               Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum,
+                                               CellStyle centerStyle, int leafNodeType) {
+
+        switch (leafNodeType) {
+            case 5: // 子分项工程 - 显示所有层级
+                fillLevelData(row, projects, ancestorIds, 0, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
+                fillLevelData(row, projects, ancestorIds, 3, 5, mergeMap, rowNum, 2, centerStyle);  // 分部工程
+                fillLevelData(row, projects, ancestorIds, 6, 8, mergeMap, rowNum, 3, centerStyle);  // 子分部工程
+                fillLevelData(row, projects, ancestorIds, 9, 11, mergeMap, rowNum, 4, centerStyle); // 分项工程
+                fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 12, 13, 14, mergeMap, rowNum, 5, centerStyle); // 子分项工程
+                break;
+
+            case 4: // 分项工程 - 显示到分项
+                fillLevelData(row, projects, ancestorIds, 0, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
+                fillLevelData(row, projects, ancestorIds, 3, 5, mergeMap, rowNum, 2, centerStyle);  // 分部工程
+                fillLevelData(row, projects, ancestorIds, 6, 8, mergeMap, rowNum, 3, centerStyle);  // 子分部工程
+                fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 9, 10, 11, mergeMap, rowNum, 4, centerStyle); // 分项工程
+                break;
+
+            case 3: // 子分部工程 - 显示到子分部
+                fillLevelData(row, projects, ancestorIds, 0, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
+                fillLevelData(row, projects, ancestorIds, 3, 5, mergeMap, rowNum, 2, centerStyle);  // 分部工程
+                fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 6, 7, 8, mergeMap, rowNum, 3, centerStyle); // 子分部工程
+                break;
+
+            case 2: // 分部工程 - 显示到分部
+                fillLevelData(row, projects, ancestorIds, 0, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
+                fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 3, 4, 5, mergeMap, rowNum, 2, centerStyle); // 分部工程
+                break;
+
+            case 18: // 单位工程 - 只显示单位工程
+                fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 0, 1, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
+                break;
+
+            default:
+                // 其他情况,尝试填充所有能找到的层级
+                fillAllAvailableLevels(row, projects, ancestorIds, mergeMap, rowNum, centerStyle);
+        }
+    }
+
+    // 填充叶子节点数据
+    private void fillLeafNodeData(Row row, List<WbsTreeContract> nodes, Long nodeId,
+                                  int codeCol, int nameCol, int idCol,
+                                  Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum, int expectedType, CellStyle centerStyle) {
+        WbsTreeContract node = findNodeById(nodes, nodeId);
+        if (node != null && node.getNodeType() == expectedType) {
+            // 划分编码(如果有)
+            if (node.getPartitionCode() != null && !node.getPartitionCode().isEmpty()) {
+                setCellValue(row, codeCol, node.getPartitionCode(), mergeMap, rowNum, centerStyle);
+            }
+            // 名称(一定有)
+            setCellValue(row, nameCol, node.getNodeName(), mergeMap, rowNum, centerStyle);
+            // pkeyId
+            setCellValue(row, idCol, node.getPKeyId().toString(), mergeMap, rowNum, centerStyle);
+        }
+    }
+
+    // 填充所有能找到的层级
+    private void fillAllAvailableLevels(Row row, List<WbsTreeContract> nodes, List<Long> ancestorIds,
+                                        Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum, CellStyle centerStyle) {
+        // 尝试填充单位工程
+        if (ancestorIds.size() > 0) {
+            WbsTreeContract unit = findNodeByIdAndType(nodes, ancestorIds, 18);
+            if (unit != null) {
+                fillLeafNodeData(row, nodes, unit.getPKeyId(), 0, 1, 2, mergeMap, rowNum, 18, centerStyle);
+            }
+        }
+
+        // 尝试填充分部工程
+        if (ancestorIds.size() > 1) {
+            WbsTreeContract division = findNodeByIdAndType(nodes, ancestorIds, 2);
+            if (division != null) {
+                fillLeafNodeData(row, nodes, division.getPKeyId(), 3, 4, 5, mergeMap, rowNum, 2, centerStyle);
+            }
+        }
+
+        // 尝试填充子分部工程
+        if (ancestorIds.size() > 2) {
+            WbsTreeContract subDivision = findNodeByIdAndType(nodes, ancestorIds, 3);
+            if (subDivision != null) {
+                fillLeafNodeData(row, nodes, subDivision.getPKeyId(), 6, 7, 8, mergeMap, rowNum, 3, centerStyle);
+            }
+        }
+
+        // 尝试填充分项工程
+        if (ancestorIds.size() > 3) {
+            WbsTreeContract item = findNodeByIdAndType(nodes, ancestorIds, 4);
+            if (item != null) {
+                fillLeafNodeData(row, nodes, item.getPKeyId(), 9, 10, 11, mergeMap, rowNum, 4, centerStyle);
+            }
+        }
+
+        // 尝试填充子分项工程
+        if (ancestorIds.size() > 4) {
+            WbsTreeContract subItem = findNodeByIdAndType(nodes, ancestorIds, 5);
+            if (subItem != null) {
+                fillLeafNodeData(row, nodes, subItem.getPKeyId(), 12, 13, 14, mergeMap, rowNum, 5, centerStyle);
+            }
+        }
+    }
+
+    // 根据ID查找节点
+    private WbsTreeContract findNodeById(List<WbsTreeContract> list, Long id) {
+        return list.stream()
+                .filter(item -> item.getPKeyId().equals(id))
+                .findFirst()
+                .orElse(null);
+    }
+
+    // 填充各级工程数据 - 添加居中样式参数
+    private void fillLevelData(Row row, List<WbsTreeContract> projects, List<Long> ancestors,
+                               int startCol, int endCol,
+                               Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum, int nodeType, CellStyle centerStyle) {
+        if (!ancestors.isEmpty()) {
+            WbsTreeContract node = findNodeByIdAndType(projects, ancestors, nodeType);
+            if (node != null) {
+                // 划分编码(如果有)
+                if (node.getPartitionCode() != null && !node.getPartitionCode().isEmpty()) {
+                    setCellValue(row, startCol, node.getPartitionCode(), mergeMap, rowNum, centerStyle);
+                }
+                // 名称(一定有)
+                setCellValue(row, startCol + 1, node.getNodeName(), mergeMap, rowNum, centerStyle);
+                // pkeyId
+                setCellValue(row, endCol, node.getPKeyId().toString(), mergeMap, rowNum, centerStyle);
+            }
+        }
+    }
+
+
+    // 设置单元格值并记录合并信息 - 添加居中样式参数
+    private void setCellValue(Row row, int col, String value, Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum, CellStyle centerStyle) {
+        Cell cell = row.getCell(col);
+        if (cell == null) {
+            cell = row.createCell(col);
+        }
+        cell.setCellValue(value);
+        cell.setCellStyle(centerStyle); // 设置居中样式和边框
+
+        if (!mergeMap.containsKey(col)) {
+            mergeMap.put(col, new ArrayList<>());
+        }
+        mergeMap.get(col).add(new CellRangeAddress(rowNum, rowNum, col, col));
+    }
+
+    // 应用合并单元格 - 添加居中样式参数
+    private void applyMergedRegions(Sheet sheet, Map<Integer, List<CellRangeAddress>> mergeMap, CellStyle centerStyle) {
+        for (List<CellRangeAddress> regions : mergeMap.values()) {
+            if (regions.size() > 1) {
+                // 按行号排序
+                regions.sort(Comparator.comparingInt(CellRangeAddress::getFirstRow));
+
+                int col = regions.get(0).getFirstColumn();
+                int mergeStart = -1;
+                int mergeEnd = -1;
+                String lastValue = null;
+
+                for (CellRangeAddress region : regions) {
+                    int rowNum = region.getFirstRow();
+                    Row row = sheet.getRow(rowNum);
+                    if (row == null) continue;
+
+                    Cell cell = row.getCell(col);
+                    if (cell == null) continue;  // 修复:这里应该是 continue 而不是跳过
+
+                    String currentValue = getCellStringValue(cell);
+
+                    if (lastValue == null) {
+                        // 第一个单元格
+                        mergeStart = rowNum;
+                        mergeEnd = rowNum;
+                        lastValue = currentValue;
+                    } else if (currentValue.equals(lastValue)) {
+                        // 值相同,扩展合并范围
+                        mergeEnd = rowNum;
+                    } else {
+                        // 值不同,合并之前的区域
+                        if (mergeStart < mergeEnd) {
+                            CellRangeAddress mergedRegion = new CellRangeAddress(mergeStart, mergeEnd, col, col);
+                            sheet.addMergedRegion(mergedRegion);
+                            // 设置合并后单元格居中
+                            setMergedRegionStyle(sheet, mergedRegion, centerStyle);
+                        }
+                        // 开始新的合并区域
+                        mergeStart = rowNum;
+                        mergeEnd = rowNum;
+                        lastValue = currentValue;
+                    }
+                }
+
+                // 合并最后一段区域
+                if (mergeStart < mergeEnd) {
+                    CellRangeAddress mergedRegion = new CellRangeAddress(mergeStart, mergeEnd, col, col);
+                    sheet.addMergedRegion(mergedRegion);
+                    // 设置合并后单元格居中
+                    setMergedRegionStyle(sheet, mergedRegion, centerStyle);
+                }
+            }
+        }
+    }
+
+    // 获取单元格的字符串值
+    private String getCellStringValue(Cell cell) {
+        if (cell == null) {
+            return "";
+        }
+        return cell.getStringCellValue();
+    }
+
+    // 设置合并区域样式为居中
+    private void setMergedRegionStyle(Sheet sheet, CellRangeAddress mergedRegion, CellStyle style) {
+        for (int rowNum = mergedRegion.getFirstRow(); rowNum <= mergedRegion.getLastRow(); rowNum++) {
+            Row row = sheet.getRow(rowNum);
+            if (row != null) {
+                for (int colNum = mergedRegion.getFirstColumn(); colNum <= mergedRegion.getLastColumn(); colNum++) {
+                    Cell cell = row.getCell(colNum);
+                    if (cell != null) {
+                        cell.setCellStyle(style);
+                    }
+                }
+            }
+        }
+    }
+
+    // 自动调整列宽
+    private void autoSizeColumns(Sheet sheet) {
+        for (int i = 0; i < 15; i++) {
+            sheet.autoSizeColumn(i);
+            // 设置最小列宽
+            if (sheet.getColumnWidth(i) < 3000) {
+                sheet.setColumnWidth(i, 3000);
+            }
+        }
+    }
+
+    // 保存Workbook到本地文件
+    private void saveWorkbookToFile(org.apache.poi.ss.usermodel.Workbook workbook, String filePath) {
+        try {
+            File file = new File(filePath);
+            File parentDir = file.getParentFile();
+            if (parentDir != null && !parentDir.exists()) {
+                parentDir.mkdirs();
+            }
+
+            try (FileOutputStream fileOut = new FileOutputStream(file)) {
+                workbook.write(fileOut);
+                System.out.println("文件已成功保存到: " + filePath);
+            }
+        } catch (IOException e) {
+            System.err.println("保存文件失败: " + e.getMessage());
+            e.printStackTrace();
+            throw new RuntimeException("保存文件失败", e);
+        }
+    }
+
+    // 根据ID和类型查找节点
+    private WbsTreeContract findNodeByIdAndType(List<WbsTreeContract> list, List<Long> ids, int nodeType) {
+        return list.stream()
+                .filter(item -> ids.contains(item.getPKeyId()) && item.getNodeType() == nodeType)
+                .findFirst()
+                .orElse(null);
+    }
+
+
+    // 从模板复制合并区域
+    private void copyMergedRegionsFromTemplate(Sheet targetSheet, Sheet templateSheet) {
+        for (int i = 0; i < templateSheet.getNumMergedRegions(); i++) {
+            CellRangeAddress mergedRegion = templateSheet.getMergedRegion(i);
+            // 只复制前两行的合并区域
+            if (mergedRegion.getFirstRow() <= 1) {
+                targetSheet.addMergedRegion(mergedRegion);
+            }
+        }
+    }
+
 
 }