“mitao”
2026-05-16 881332a25bcbbe3bebb696f6d0728fba559ef635
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
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));
            }
        }
    }
 
}