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<Map<String, CompareMatrix>> {
|
|
private TreeRoot<HierarchyData> hierarchyTree;
|
|
@Override
|
protected String[] getHeaders() {
|
// 判断矩阵使用动态列头,这里返回空数组
|
return new String[0];
|
}
|
|
@Override
|
public String getName() {
|
return "判断矩阵";
|
}
|
|
@Override
|
protected Map<String, CompareMatrix> 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<String, CompareMatrix> data) {
|
// 判断矩阵需要多 Sheet 处理,不使用此方法
|
throw new UnsupportedOperationException("判断矩阵需要使用 saveWorkbook 方法处理多个 Sheet");
|
}
|
|
@Override
|
public void saveWorkbook(OutputStream outputStream, Map<String, CompareMatrix> 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<String, CompareMatrix> loadFromWorkbook(String filePath) throws IOException {
|
return loadFromWorkbook(WorkbookFactory.create(new java.io.FileInputStream(filePath)));
|
}
|
|
@Override
|
public Map<String, CompareMatrix> loadFromWorkbook(java.io.InputStream inputStream) throws IOException {
|
return loadFromWorkbook(WorkbookFactory.create(inputStream));
|
}
|
|
private Map<String, CompareMatrix> loadFromWorkbook(Workbook workbook) {
|
Map<String, CompareMatrix> 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<HierarchyData> sheetNameToTreeNode(TreeRoot<HierarchyData> 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<rows;i++){
|
for(int j=i;j<rows;j++){
|
// All numbers should be positive, and elements symmetrical along the main diagonal should be opposite numbers to each other
|
BizResultCode.MATRIX_ERROR.check((values[i][j]*values[j][i]!=1) | (values[i][j]<=0));
|
}
|
}
|
}
|
|
}
|