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);
|
}
|
}
|