liujie
2026-05-15 28e68c33134c4934bdeee1498992c29a989f8878
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
package com.estate.expert.admin.service.impl;
 
import com.estate.expert.admin.component.fuzzy.FuzzyApiComponent;
import com.estate.expert.admin.component.fuzzy.FuzzyApiRequest;
import com.estate.expert.admin.domain.business.device.Device;
import com.estate.expert.admin.domain.business.device.DeviceType;
import com.estate.expert.admin.domain.common.request.PageReq;
import com.estate.expert.admin.domain.common.response.PageResp;
import com.estate.expert.admin.domain.exam.eval.fuzzy.*;
import com.estate.expert.admin.domain.exam.record.error.ErrorRecord;
import com.estate.expert.admin.domain.exam.task.check.CheckTask;
import com.estate.expert.admin.domain.standard.expert.Expert;
import com.estate.expert.admin.domain.standard.expert.matrix.CompareMatrix;
import com.estate.expert.admin.domain.standard.hierachy.HierarchyData;
import com.estate.expert.admin.service.eval.EvalFuzzyService;
import com.estate.expert.admin.service.eval.EvalServiceImpl;
import com.estate.expert.admin.service.expert.DeviceExpertService;
import com.estate.expert.admin.service.expert.ExpertService;
import com.estate.expert.admin.service.expert.SysExpertService;
import com.estate.expert.admin.utils.tree.TreeNode;
import com.estate.expert.admin.utils.tree.helper.TreeHelper;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
 
/**
 * 模糊评价服务实现
 */
@Service
@Log4j2
public class EvalFuzzyServiceImpl extends EvalServiceImpl<EvalFuzzyResult, EvalFuzzyResultDto, EvalFuzzyResultInfo, FuzzyResult> implements EvalFuzzyService {
 
    @Resource
    private EvalFuzzyResultRepository repository;
 
    @Resource
    private FuzzyApiComponent fuzzyApi;
 
    @Override
    public PageResp<EvalFuzzyResultInfo> list(Integer clientId, List<Integer> planIds, List<Integer> taskIds,
                                              LocalDateTime startDate, LocalDateTime endDate, PageReq pageReq) {
        var page = repository.list(clientId, planIds, taskIds, startDate, endDate, pageReq.toPageRequest());
        return PageResp.of(page, page.getContent());
    }
 
    @Override
    public EvalFuzzyResult getByCheckTaskId(Integer checkTaskId) {
        return repository.getFirstByTaskIdAndDeleted(checkTaskId, false);
    }
 
    @Override
    public boolean enableByPass() {
        return true;
    }
 
 
    private final int BASE_LEVEL = 4; // 从这个层开始计算
 
    /**
     * 模糊算法
     *
     * 计算检查分项(第三级)
     * 1x1的矩阵 比较矩阵
     *
     * 从隶属度矩阵查找当前检查分项的隶属度
     * 权重,计算上一级的时候产生
     *
     * largelist用于上一级的输入,隶属度矩阵
     */
    // 根据专家的模型,构建评分树
    @Override
    protected FuzzyResult evaluate(Expert expert, SysExpertService expertService, List<ErrorRecord> records, List<String> byPassPaths){
        // 初始化问题映射表
        var problemMap = initProblemMap(expert, expertService);
        // 根据专家的模型,构建评分树
        var result = initSysResult(expert, expertService);
        return commonEvaluate(expert, expertService, problemMap, result, records, byPassPaths);
    }
 
    @Override
    protected FuzzyResult evaluate(Expert expert, DeviceType deviceType, DeviceExpertService expertService, List<ErrorRecord> errorRecords, List<String> byPassPaths) {     // 初始化问题映射表
        var deviceScoreResult = initDeviceResult(deviceType, expert, expertService);
        var problemMap = initDeviceProblemMap(deviceType, expert, expertService);
        return commonEvaluate(expert, expertService, problemMap, deviceScoreResult, errorRecords, byPassPaths);
    }
 
    @Override
    protected EvalFuzzyResult saveResult(CheckTask checkTask, FuzzyResult sysRes, Map<Integer, FuzzyResult> devResMap, List<Device> devices) {
        if(sysRes != null && sysRes.root() != null)
            checkTask.setSystemFuzzy(sysRes.root().getData().getResp().getRiskPoint());
        else
            checkTask.setSystemFuzzy(null);
 
        var deviceFuzzyMap = new TreeMap<Integer, Float>();
        devResMap.forEach((key, value) -> {
            if(value != null && value.root() != null) {
                deviceFuzzyMap.put(key, value.root().getData().getResp().getRiskPoint());
            }
        });
        checkTask.setDeviceFuzzyMap(deviceFuzzyMap);
        return add(null , new EvalFuzzyResult(checkTask, sysRes, devResMap));
    }
 
    private FuzzyResult commonEvaluate(Expert expert, ExpertService expertService,
            TreeMap<Integer, Boolean> problemMap, FuzzyResult result,
                                       List<ErrorRecord> records, List<String> byPassPaths){
        // 遍历错误记录,将隐患数量累加到对应的节点上
        count(problemMap, result, records, byPassPaths);
 
        // 标记要参与评分的节点
        mark(result);
 
        // 剪枝,只计算头3层
        result = prune(result);
        if(result.getContent() == null)
            return null;
 
        // 从专家中获取隶属度和判断矩阵
        update(result, expert, expertService);
 
        // 调用模糊计算API,去更新整个树的分值
        TreeHelper.travel(result.root(), this::computeNode);
 
        // 对个别需要显示的数值进行更新,便于前端展示
        prettyDisplay(result);
 
//        TreeHelper.printTree(result.root());
        return result;
    }
 
    /**
     * 标记节点,用于裁剪不需要参与计算的节点
     */
    private void mark(FuzzyResult result) {
        TreeHelper.travel(result.root(), (node) -> {
            // 只去更新非叶子节点的include属性
            if(node.getChildren().isEmpty())
                return;
 
            // 如果有子节点参与评分,当前节点也参与评分
            boolean include = false;
            for (var child : node.getChildren().values()) {
                if(child.getData().isInclude())
                    include = true;
            }
            node.getData().setInclude(include);
        });
    }
 
    // 对个别需要显示的数值进行更新,便于前端展示
    private void prettyDisplay(FuzzyResult result) {
        TreeHelper.travel(result.root(), (node) -> {
            var fuzzyValue = node.getData();
            // totalErrorCounts = errorCounts + childErrorCounts
//            fuzzyValue.setTotalErrorCounts(Stream.of(0,1,2,3)
//                    .map(i -> fuzzyValue.getErrorCounts()[i] + fuzzyValue.getChildErrorCounts()[i])
//                    .mapToInt(Integer::intValue)
//                    .toArray());
            fuzzyValue.setMembershipList(fuzzyValue.getResp().getLargeList());
            fuzzyValue.setRiskLevel(FuzzyRiskLevel.values()[5 - fuzzyValue.getResp().getRiskInterval()]);
            fuzzyValue.setScore(fuzzyValue.getResp().getRiskPoint());
 
            // 由于从第4层开算
            if(node.getDepth() >= BASE_LEVEL)
                return;
 
            // 更新权重显示
            int i = 0;
            var children = node.getChildren().values().stream().toList();
            for(var child : children) {
                child.getData().setWeight(fuzzyValue.getResp().getWeight()[i]);
                i++;
            }
        });
    }
 
    private void computeNode(TreeNode<FuzzyValue> node)
    {
//        log.info(node.getName());
        var fuzzyValue = node.getData();
        var compareMatrix = fuzzyValue.getCompareMatrix();
 
        float[][] membershipData;
        float[] subRiskPoint = new float[node.getChildren().size()];
        if(node.getChildren().isEmpty() || node.getDepth() == BASE_LEVEL) {
            membershipData = new float[][]{fuzzyValue.getMembershipList()};
            subRiskPoint = new float[0];
        }else {
            membershipData = node.getChildren().values().stream()
                    .map(child -> child.getData().getResp().getLargeList())
                    .toArray(float[][]::new);
 
            int i = 0;
            for(var child : node.getChildren().values()) {
                subRiskPoint[i] = child.getData().getResp().getRiskPoint();
                i++;
            }
        }
 
        // 调用fuzzyAPI,计算检查得分
        var request = FuzzyApiRequest.builder()
                .n(compareMatrix.getN())
                .membership_data(membershipData)
                .index(compareMatrix.getIndexCols())
                .compare_matrix_data(compareMatrix.getValues())
                .sub_riskpoint(subRiskPoint)
                .num_experts(1)
                .build();
 
//        log.info("node: {}, request : {}", node.getNamePath(), request);
//        log.info("path: {}, depth : {}", node.getPath(), node.getDepth());
        var response = fuzzyApi.call(request);
        fuzzyValue.setResp(response);
    }
 
    // 从专家中获取隶属度和判断矩阵
    private void update(FuzzyResult fuzzyResult, Expert expert, ExpertService expertService){
        TreeHelper.travel(fuzzyResult.root(), (node) -> {
            var path = node.getPath();
            var fuzzyValue = node.getData();
 
            // 只从第3层算
            if(node.getChildren().isEmpty() || node.getDepth() == BASE_LEVEL) {
                // 从专家中获取隶属度矩阵
                // 叶子节点,可以直接从专家中获取
                // 非叶子节点,需要由下级节点计算得到
                fuzzyValue.setMembershipList(expert.getMembershipMatrix().getData(fuzzyValue.getTotalErrorCounts()));
                fuzzyValue.setCompareMatrix(new CompareMatrix(new String[]{"空"}));
            }else{
                // 从专家中获取判断矩阵
                var indexCols = node.getChildren().values().stream().map(TreeNode::getName).toArray(String[]::new);
                var compareMatrix = expertService.getFuzzyMatrix(expert.getId(), path, indexCols);
                fuzzyValue.setCompareMatrix(compareMatrix);
            }
        });
    }
 
    private FuzzyResult initSysResult(Expert expert, ExpertService expertService) {
        var tree = expertService.getHierarchyTree(expert.getId());
        var root = TreeHelper.<HierarchyData, FuzzyValue>convertTree(tree.root(), (node, newNode) ->
                FuzzyValue.builder()
                        .build()
        );
        return new FuzzyResult(root);
    }
 
    private FuzzyResult initDeviceResult(DeviceType deviceType, Expert expert, DeviceExpertService expertService){
        var tree = expertService.getHierarchyTree(expert.getId());
        var root = TreeHelper.<HierarchyData, FuzzyValue>convertTree(tree.root(), (node, newNode) ->
                FuzzyValue.builder().build(), (n) -> {
            //删除掉非相关掉设备类型的节点
            return n.getDepth() != 1 || deviceType.hierarchyPath().equals(n.getPath());
        });
 
        return new FuzzyResult(root);
    }
 
    private void count(TreeMap<Integer, Boolean> problemMap, FuzzyResult result, List<ErrorRecord> records, List<String> byPassPaths) {
        // 遍历错误记录,将隐患数量累加到对应的节点上
        if(records != null) {
            records.forEach(errorRecord -> {
                var problem = errorRecord.getProblem();
                var ps = problemMap.get(problem.getId());
 
                // 如果问题不在模型中,不计算
                // 如果问题已经计算过,也不计算(同一问题的记录只计算一次)
                if (ps == null || ps)
                    return;
 
                problemMap.put(problem.getId(), true);
 
                // 计算隐患数量
                var node = result.get(problem.getHierarchyPath());
                node.getData().addError(errorRecord.getLevel());
                node.getData().setInclude(true);
            });
        }
 
        // 轮空项,如果设置了,则需要参与计算
        if(byPassPaths != null){
            byPassPaths.forEach(path -> {
                var node = result.get(path);
                if(node == null)
                    return;
                var scoreValue = node.getData();
                scoreValue.setInclude(true);
                node.setData(scoreValue);
            });
        }
 
        // 累加到上一级
        TreeHelper.travel(result.root(), (node) -> {
            var nodeData = node.getData();
            for(var child : node.getChildren().values()) {
                var childData = child.getData();
                for(var i = 0; i < 4; i++) {
                    nodeData.getChildErrorCounts()[i] += childData.getErrorCounts()[i] + childData.getChildErrorCounts()[i];
                }
            }
        });
 
        // 更新totalErrorCounts
        TreeHelper.travel(result.root(), (node) -> {
            var nodeData = node.getData();
            var t = new int[4];
            for(var i = 0; i < 4; i++) {
                t[i] = nodeData.getErrorCounts()[i] + nodeData.getChildErrorCounts()[i];
            }
            nodeData.setTotalErrorCounts(t);
        });
    }
 
    private FuzzyResult prune(FuzzyResult result) {
        var root = TreeHelper.pruneTree(result.root(), (node) -> {
//            var path = node.getPath();
//            var sumErrors = Arrays.stream(node.getData().getChildErrorCounts()).sum()
//                    + Arrays.stream(node.getData().getErrorCounts()).sum();
//            // 只显示前四级的节点,同时优化掉没有参与评分的节点
//            return sumErrors >= 0;
//            return node.getDepth() <= 5 && sumErrors >= 0;
            return node.getData().isInclude() ;
        });
 
        return new FuzzyResult(root);
    }
}