package com.estate.expert.admin.io.processor.impl.matrix; import com.estate.expert.admin.domain.standard.expert.matrix.CompareMatrix; import com.estate.expert.admin.domain.standard.hierachy.HierarchyData; import com.estate.expert.admin.enums.BizResultCode; import com.estate.expert.admin.io.processor.DataProcessorBase; import com.estate.expert.admin.utils.tree.TreeNode; import com.estate.expert.admin.utils.tree.TreeRoot; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; import java.io.IOException; import java.io.OutputStream; import java.util.Map; import java.util.TreeMap; /** * 比较矩阵处理器(判断矩阵) * * 用于处理模糊数学专家的判断矩阵导入导出 * 每个 Sheet 对应一个层级节点的判断矩阵 */ @Slf4j @AllArgsConstructor public class CompareMatrixProcessor extends DataProcessorBase> { private TreeRoot hierarchyTree; @Override protected String[] getHeaders() { // 判断矩阵使用动态列头,这里返回空数组 return new String[0]; } @Override public String getName() { return "判断矩阵"; } @Override protected Map parseSheet(org.apache.poi.xssf.usermodel.XSSFSheet sheet) { // 判断矩阵需要多 Sheet 处理,不使用此方法 throw new UnsupportedOperationException("判断矩阵需要使用 loadFromWorkbook 方法处理多个 Sheet"); } @Override protected void writeSheet(org.apache.poi.xssf.usermodel.XSSFSheet sheet, Map data) { // 判断矩阵需要多 Sheet 处理,不使用此方法 throw new UnsupportedOperationException("判断矩阵需要使用 saveWorkbook 方法处理多个 Sheet"); } @Override public void saveWorkbook(OutputStream outputStream, Map data) throws IOException { Workbook workbook = WorkbookFactory.create(true); if(data != null && !data.isEmpty()) { for(var entry : data.entrySet()) { String hierarchyPath = entry.getKey(); CompareMatrix matrix = entry.getValue(); // 根据路径获取节点 var node = hierarchyTree.get(hierarchyPath); if(node == null) { log.warn("找不到路径对应的节点: {}", hierarchyPath); continue; } // 跳过根节点(根节点没有子节点需要比较) if("/".equals(hierarchyPath) || hierarchyPath == null || hierarchyPath.length() <= 1) { log.debug("跳过根节点或无效路径: {}", hierarchyPath); continue; } // 创建 Sheet,使用路径格式:_1-2-3 String sheetName = "_" + hierarchyPath.substring(1).replace("/", "-"); Sheet sheet = workbook.createSheet(sheetName); // 写入标题行(第0行) Row headerRow = sheet.createRow(0); headerRow.createCell(0).setCellValue(""); // 左上角空单元格 String[] indexCols = matrix.getIndexCols(); for(int i = 0; i < indexCols.length; i++) { headerRow.createCell(i + 1).setCellValue(indexCols[i]); } // 写入数据行 float[][] values = matrix.getValues(); for(int i = 0; i < values.length; i++) { Row dataRow = sheet.createRow(i + 1); dataRow.createCell(0).setCellValue(indexCols[i]); // 行索引 for(int j = 0; j < values[i].length; j++) { dataRow.createCell(j + 1).setCellValue(values[i][j]); } } // 在最后一行写入路径信息(用于显示) int lastRowNum = values.length + 2; // 标题行 + 数据行 + 空行 Row pathRow = sheet.createRow(lastRowNum); pathRow.createCell(0).setCellValue("体系路径:"); pathRow.createCell(1).setCellValue(hierarchyPath); // 如果有名称路径,也显示出来 if(node.getNames() != null && !node.getNames().isEmpty()) { Row nameRow = sheet.createRow(lastRowNum + 1); nameRow.createCell(0).setCellValue("体系名称:"); nameRow.createCell(1).setCellValue(String.join(" / ", node.getNames())); } } } workbook.write(outputStream); workbook.close(); } @Override public Map loadFromWorkbook(String filePath) throws IOException { return loadFromWorkbook(WorkbookFactory.create(new java.io.FileInputStream(filePath))); } @Override public Map loadFromWorkbook(java.io.InputStream inputStream) throws IOException { return loadFromWorkbook(WorkbookFactory.create(inputStream)); } private Map loadFromWorkbook(Workbook workbook) { Map compareMatrixMap = new TreeMap<>(); int numberOfSheets = workbook.getNumberOfSheets(); for(int i = 0; i < numberOfSheets; i++) { try { String sheetName = workbook.getSheetName(i); var node = sheetNameToTreeNode(hierarchyTree, sheetName); if(node == null) { log.warn("Sheet名称 {} 对应的节点不存在", sheetName); continue; } var matrix = loadSingleCompareMatrix(workbook, i); compareMatrixMap.put(node.getPath(), matrix); } catch (Exception e) { log.error("读取第 {} 个 Sheet 失败", i, e); } } return compareMatrixMap; } /** * 从 Sheet 名称解析出树节点 * Sheet 名称格式:_1-2-3 */ private TreeNode sheetNameToTreeNode(TreeRoot hierarchy, String sheetName) { if(!sheetName.startsWith("_")) { log.warn("Sheet 名称格式不正确: {}", sheetName); return null; } String[] split = sheetName.split("_"); if(split.length < 2) { log.warn("Sheet 名称格式不正确: {}", sheetName); return null; } String hierarchyPath = "/" + split[1].replace("-", "/"); return hierarchy.get(hierarchyPath); } /** * 从单个 Sheet 读取判断矩阵 */ private CompareMatrix loadSingleCompareMatrix(Workbook workbook, int sheetIndex) { Sheet sheet = workbook.getSheetAt(sheetIndex); // 读取第0行第1列开始的索引列(列名) Row indexRow = sheet.getRow(0); if(indexRow == null) { throw new RuntimeException("Sheet " + sheetIndex + " 的标题行为空"); } int totalCols = indexRow.getLastCellNum(); String[] indexCols = new String[totalCols - 1]; for(int i = 1; i < totalCols; i++) { Cell cell = indexRow.getCell(i); indexCols[i - 1] = cell != null ? cell.getStringCellValue() : ""; } // 矩阵大小由列数决定(方阵),只读取矩阵数据行 int matrixSize = indexCols.length; float[][] matrixValues = new float[matrixSize][matrixSize]; for(int i = 0; i < matrixSize; i++) { Row dataRow = sheet.getRow(i + 1); // 从第1行开始读取 if(dataRow == null) { // 如果行不存在,填充默认值1.0 for(int j = 0; j < matrixSize; j++) { matrixValues[i][j] = 1.0f; } continue; } for(int j = 0; j < matrixSize; j++) { Cell cell = dataRow.getCell(j + 1); // 从第1列开始读取 if(cell != null) { try { matrixValues[i][j] = (float) cell.getNumericCellValue(); } catch (Exception e) { matrixValues[i][j] = 1.0f; } } else { matrixValues[i][j] = 1.0f; } } } return new CompareMatrix(indexCols, matrixValues); } public static void validate(CompareMatrix matrix) { var values = matrix.getValues(); int rows = values.length; for (float[] value : values) { BizResultCode.MATRIX_SIZE_ERROR.check(value.length != rows); } for (int i=0;i