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 implements EvalFuzzyService { @Resource private EvalFuzzyResultRepository repository; @Resource private FuzzyApiComponent fuzzyApi; @Override public PageResp list(Integer clientId, List planIds, List 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 records, List 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 errorRecords, List 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 devResMap, List devices) { if(sysRes != null && sysRes.root() != null) checkTask.setSystemFuzzy(sysRes.root().getData().getResp().getRiskPoint()); else checkTask.setSystemFuzzy(null); var deviceFuzzyMap = new TreeMap(); 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 problemMap, FuzzyResult result, List records, List 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 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.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.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 problemMap, FuzzyResult result, List records, List 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); } }