比对模式的检测报告生成和下载

This commit is contained in:
2025-09-23 16:16:31 +08:00
parent 35e52e0722
commit e42121ba4c
23 changed files with 4013 additions and 143 deletions

View File

@@ -12,7 +12,7 @@ import javax.validation.constraints.NotNull;
* @data 2025-09-10
*/
@Data
public class MonitorResultVO {
public class MonitorResultVO implements Comparable<MonitorResultVO> {
/**
* 监测点id
@@ -69,4 +69,21 @@ public class MonitorResultVO {
@ApiModelProperty(value = "数据源类型", required = true)
@NotBlank(message = DetectionValidMessage.DEV_MONITOR_RESULT_TYPE_NOT_BLANK)
private String resultType;
/**
* 根据线路号排序
*/
@Override
public int compareTo(MonitorResultVO other) {
if (this.monitorNum == null && other.monitorNum == null) {
return 0;
}
if (this.monitorNum == null) {
return 1;
}
if (other.monitorNum == null) {
return -1;
}
return this.monitorNum.compareTo(other.monitorNum);
}
}

View File

@@ -1,9 +1,13 @@
package com.njcn.gather.result.service;
import com.njcn.gather.device.pojo.vo.PqDevVO;
import com.njcn.gather.report.pojo.DevReportParam;
import com.njcn.gather.report.pojo.result.ContrastTestResult;
import com.njcn.gather.report.pojo.result.SingleTestResult;
import com.njcn.gather.result.pojo.param.ResultParam;
import com.njcn.gather.result.pojo.vo.*;
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
import com.njcn.gather.system.dictionary.pojo.po.DictTree;
import java.util.List;
import java.util.Map;
@@ -88,13 +92,6 @@ public interface IResultService {
*/
SingleTestResult getFinalContent(List<PqScriptDtlDataVO> checkDataVOList, String planCode, String devId, Integer lineNo, List<String> tableKeys);
/**
* 获取段落中指定的key对应的值
*
* @param itemCode 测试大项code
* @param pKeys 待填充的值
*/
Map<String, String> getParagraphKeysValue(String itemCode, List<String> pKeys);
/**
* 获取比对式表单头
@@ -128,4 +125,14 @@ public interface IResultService {
* @return
*/
List<ContrastTestItemVO> getCheckItem(String devId, String chnNum, Integer num);
/**
* 获取设备比对式结果,用于出比对检测的报告
* @param devReportParam 设备报告参数
* @param pqDevVO 设备信息 省去一次sql查询
* @return 该设备的比对式结果
*/
Map<Integer, List<ContrastTestResult>> getContrastResultForReport(DevReportParam devReportParam, PqDevVO pqDevVO);
ContrastTestResult getContrastResultHarm(MonitorResultVO monitorResultVO, List<String> scriptId, Integer planCode, DictTree dictTree);
}

View File

@@ -3,6 +3,7 @@ package com.njcn.gather.result.service.impl;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjectUtil;
@@ -34,6 +35,7 @@ import com.njcn.gather.device.pojo.enums.CommonEnum;
import com.njcn.gather.device.pojo.enums.PatternEnum;
import com.njcn.gather.device.pojo.po.PqDev;
import com.njcn.gather.device.pojo.po.PqStandardDev;
import com.njcn.gather.device.pojo.vo.PqDevVO;
import com.njcn.gather.device.service.IPqDevService;
import com.njcn.gather.device.service.IPqStandardDevService;
import com.njcn.gather.err.service.IPqErrSysService;
@@ -45,10 +47,12 @@ import com.njcn.gather.plan.pojo.po.AdPlanTestConfig;
import com.njcn.gather.plan.service.IAdPlanService;
import com.njcn.gather.plan.service.IAdPlanTestConfigService;
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
import com.njcn.gather.report.pojo.DevReportParam;
import com.njcn.gather.report.pojo.constant.PowerConstant;
import com.njcn.gather.report.pojo.enums.AffectEnum;
import com.njcn.gather.report.pojo.enums.ItemReportKeyEnum;
import com.njcn.gather.report.pojo.enums.PowerIndexEnum;
import com.njcn.gather.report.pojo.result.ContrastTestResult;
import com.njcn.gather.report.pojo.result.SingleTestResult;
import com.njcn.gather.result.pojo.enums.ResultUnitEnum;
import com.njcn.gather.result.pojo.param.ResultParam;
@@ -1520,43 +1524,6 @@ public class ResultServiceImpl implements IResultService {
return JSONUtil.toBean(filedValue, DetectionData.class);
}
/**
* 获取段落中指定的key对应的值目前主要为测试大项名称服务通过code匹配
*
* @param itemCode 测试大项code
* @param pKeys 待填充的值
*/
@Override
public Map<String, String> getParagraphKeysValue(String itemCode, List<String> pKeys) {
Map<String, String> map = new HashMap<>();
if (CollUtil.isNotEmpty(pKeys)) {
for (String pKey : pKeys) {
ItemReportKeyEnum reportKeyEnum = ItemReportKeyEnum.getByKey(pKey);
if (Objects.nonNull(reportKeyEnum)) {
if (reportKeyEnum.getKey().equals(ItemReportKeyEnum.NAME.getKey())) {
PowerIndexEnum indexEnum = PowerIndexEnum.getByKey(itemCode);
if (Objects.nonNull(indexEnum)) {
map.put(reportKeyEnum.getKey(), indexEnum.getDesc());
} else {
log.error("电能指标枚举未找到测试项");
}
} else if (reportKeyEnum.getKey().equals(ItemReportKeyEnum.NAME_DETAIL.getKey())) {
PowerIndexEnum indexEnum = PowerIndexEnum.getByKey(itemCode);
if (Objects.nonNull(indexEnum)) {
map.put(reportKeyEnum.getKey(), indexEnum.getDesc().concat("测量准确度"));
} else {
log.error("电能指标枚举未找到测试项");
}
}
} else {
log.error("段落枚举未找到占用符");
}
}
}
return map;
}
@Override
public FormContentVO getContrastFormContent(ResultParam.QueryParam queryParam) {
FormContentVO formContentVO = new FormContentVO();
@@ -1843,6 +1810,798 @@ public class ResultServiceImpl implements IResultService {
return result;
}
/**
* 获取对比结果
*
* @param devReportParam 设备报告参数
* @param pqDevVO 设备信息 省去一次sql查询
*/
@Override
public Map<Integer, List<ContrastTestResult>> getContrastResultForReport(DevReportParam devReportParam, PqDevVO pqDevVO) {
Map<Integer/*回路号*/, List<ContrastTestResult>> finalContent = new LinkedHashMap<>();
List<ContrastTestResult> contrastTestResults = new ArrayList<>();
// 获取该设备下参与测试的回路(已有检测结论的)
List<MonitorResultVO> monitorResultVOS = this.getMonitorResult(devReportParam.getDevId());
if (CollectionUtil.isNotEmpty(monitorResultVOS)) {
Collections.sort(monitorResultVOS);
// 获取该计划下所有的检测指标,注:如果用户应用的是录波数据,频率这个指标不用出结果,因为当前的算法无法得出合理的结果
List<String> scriptList = adPlanService.getScriptListContrast(devReportParam.getPlanId());
// 对测试项按字典表排个序
scriptList = dictTreeService.sort(scriptList);
AdPlan adPlan = adPlanService.getById(devReportParam.getPlanId());
if (CollectionUtil.isNotEmpty(scriptList)) {
for (MonitorResultVO monitorResultVO : monitorResultVOS) {
int monitorNum = monitorResultVO.getMonitorNum();
// 看看当前这个回路结论的来源,可能是某次的实时数据、某次的测试下的某次录波数据、某次统计数据
for (String scriptId : scriptList) {
// 获取该指标的code需要判断是否为谐波还是非谐波的指标
DictTree dictTree = dictTreeService.getById(scriptId);
// 本处的scriptId为父级我需要获取自己
List<String> subScriptIds = dictTreeService.getChildIds(scriptId);
String scriptCode = dictTree.getCode();
if (PowerConstant.TIME.contains(scriptCode)) {
// 谐波类谐波类稍微特殊一点存在2~50次的数据所以需要特殊处理
ContrastTestResult contrastTestResult = getContrastResultHarm(monitorResultVO, subScriptIds, adPlan.getCode(), dictTree);
if (Objects.nonNull(contrastTestResult)) {
contrastTestResults.add(contrastTestResult);
}
} else {
// 非谐波类非谐波需要注意T相和三相
ContrastTestResult contrastTestResult = getContrastResultNonHarm(monitorResultVO, subScriptIds, adPlan.getCode(), dictTree);
if (Objects.nonNull(contrastTestResult)) {
contrastTestResults.add(contrastTestResult);
}
}
}
finalContent.put(monitorNum, contrastTestResults);
}
}
return finalContent;
}
return Collections.emptyMap();
}
/**
* 获取谐波类数据的结果数据
*
* @param monitorResultVO 数据来源信息
* @param subScriptIds 实际检测指标id
* @param planCode 计划code表名需要
* @param dictTree 测试指标信息
* @return 结果数据
*/
@Override
public ContrastTestResult getContrastResultHarm(MonitorResultVO monitorResultVO, List<String> subScriptIds, Integer planCode, DictTree dictTree) {
ContrastHarmonicResult result = contrastHarmonicService.getContrastResultHarm(planCode, monitorResultVO.getMonitorId(), subScriptIds, monitorResultVO.getResultType(), Integer.parseInt(monitorResultVO.getWhichTime()));
// 收集组数数据
int numOfData = contrastHarmonicService.getNumOfData(planCode, monitorResultVO.getMonitorId(), subScriptIds, monitorResultVO.getResultType(), Integer.parseInt(monitorResultVO.getWhichTime()));
if (Objects.nonNull(result)) {
// 谐波类的获取2~50次的数据
ContrastTestResult contrastTestResult = new ContrastTestResult();
contrastTestResult.setScriptCode(dictTree.getCode());
contrastTestResult.setScriptName(dictTree.getName());
contrastTestResult.setHarmonic(true);
// 判断是否为间谐波
boolean isInterHarmonic = "HSV".equals(dictTree.getCode()) || "HSI".equals(dictTree.getCode());
// 根据指标代码确定小数位数
Integer decimalPlaces = getDecimalPlacesByScriptCode(dictTree.getCode());
// 默认结果是符合的,后续解析过程中存在不符合的,则改为不符合
// 构建谐波数据结果Map: 次数 -> 相别 -> List<Map<占位符名称, 占位符值>>
List<Map<String, Map<String, Map<String, String>>>> harmonicResult = new ArrayList<>();
List<String> allResult = new ArrayList<>();
// 收集特殊情况信息
Map<Integer, List<String>> zeroFilteredMap = new LinkedHashMap<>(); // 双零过滤的次数和相别
Map<Integer, Map<String, List<String>>> unComparableMap = new LinkedHashMap<>(); // 无法比较的次数和相别(按组数分组)
Map<Integer, Map<String, List<String>>> unqualifiedMap = new LinkedHashMap<>(); // 不符合的次数和相别(按组数分组)
int totalDataPoints = 0; // 统计总的数据点数
int zeroFilteredPoints = 0; // 统计双零过滤的数据点数
// 遍历 2~50 次谐波
for (int harmNum = 2; harmNum <= 50; harmNum++) {
String harmKey = String.valueOf(harmNum);
Map<String, Map<String, Map<String, String>>> checkResultHarmonic = new LinkedHashMap<>();
List<String> zeroFilteredPhases = new ArrayList<>(); // 当前次数被过滤的相别
try {
Map<String, Map<String, String>> phaseData = new LinkedHashMap<>();
boolean hasNonZeroData = false; // 标记是否有非双零数据
for (String phase : PowerConstant.PHASE_ABC) {
Field field = result.getClass().getDeclaredField(phase + "Value" + harmKey);
field.setAccessible(true);
String valueJson = (String) field.get(result);
Map<String, String> singlePhaseData = parseHarmonicPhaseData(valueJson, phase.toUpperCase() + "", harmNum, numOfData, dictTree.getCode(), decimalPlaces);
if (CollUtil.isNotEmpty(singlePhaseData)) {
totalDataPoints++; // 统计总数据点
// 检查是否为双零情况
String standardVal = singlePhaseData.get(ItemReportKeyEnum.STANDARD.getKey());
String testVal = singlePhaseData.get(ItemReportKeyEnum.TEST.getKey());
// 判断是否为双零标准值和被检值都为0或"0.0"或"0"
boolean isZeroFiltered = isZeroValue(standardVal) && isZeroValue(testVal);
if (isZeroFiltered) {
zeroFilteredPoints++; // 统计双零点
// 双零情况,记录但不加入结果判定
zeroFilteredPhases.add(phase.toUpperCase() + "");
// 将结果改为特殊标记,不参与整体结论判定
singlePhaseData.put(ItemReportKeyEnum.RESULT.getKey(), "双零过滤");
} else {
// 有非双零数据
hasNonZeroData = true;
// 非双零情况,正常处理结果
String resultTemp = singlePhaseData.get(ItemReportKeyEnum.RESULT.getKey());
if (StrUtil.isNotBlank(resultTemp)) {
allResult.add(resultTemp);
// 收集特殊情况
if ("无法比较".equals(resultTemp)) {
String numOfDataStr = singlePhaseData.get(ItemReportKeyEnum.NUM_OF_DATA.getKey());
unComparableMap.computeIfAbsent(harmNum, k -> new LinkedHashMap<>())
.computeIfAbsent(numOfDataStr, k -> new ArrayList<>())
.add(phase.toUpperCase() + "");
} else if ("不符合".equals(resultTemp)) {
String numOfDataStr = singlePhaseData.get(ItemReportKeyEnum.NUM_OF_DATA.getKey());
unqualifiedMap.computeIfAbsent(harmNum, k -> new LinkedHashMap<>())
.computeIfAbsent(numOfDataStr, k -> new ArrayList<>())
.add(phase.toUpperCase() + "");
}
}
}
phaseData.put(phase.toUpperCase() + "", singlePhaseData);
}
}
// 记录当前次数的双零过滤情况
if (!zeroFilteredPhases.isEmpty()) {
zeroFilteredMap.put(harmNum, zeroFilteredPhases);
}
// 只有当该次谐波有非双零数据时,才添加到结果集中
if (hasNonZeroData) {
checkResultHarmonic.put(harmKey, phaseData);
// 如果该次谐波有数据添加到结果Map中
if (CollUtil.isNotEmpty(checkResultHarmonic)) {
harmonicResult.add(checkResultHarmonic);
}
}
} catch (NoSuchFieldException | IllegalAccessException e) {
log.warn("获取第{}次谐波数据失败: {}", harmNum, e.getMessage());
}
}
// 判断是否所有数据都是双零
boolean allDataIsZero = (totalDataPoints > 0 && totalDataPoints == zeroFilteredPoints);
// 生成specialCase描述
String specialCase = generateHarmonicSpecialCase(zeroFilteredMap, unComparableMap, unqualifiedMap,
totalDataPoints, zeroFilteredPoints, isInterHarmonic);
// 设置检测结果
String overallResult;
if (allDataIsZero) {
// 如果所有数据都是双零,结果为符合
overallResult = "符合";
} else {
// 根据所有相数据的结果判断总体结论
overallResult = determineOverallResult(allResult);
}
contrastTestResult.setResult(overallResult);
contrastTestResult.setSpecialCase(specialCase);
contrastTestResult.setCheckResultHarmonic(harmonicResult);
return contrastTestResult;
}
return null;
}
/**
* 判断值是否为零
* @param value 字符串值
* @return 是否为零
*/
private boolean isZeroValue(String value) {
if (StrUtil.isBlank(value) || "/".equals(value) || "null".equals(value)) {
return false; // 空值或无效值不算零
}
try {
double d = Double.parseDouble(value);
return d == 0.0;
} catch (NumberFormatException e) {
return false;
}
}
/**
* 生成谐波类特殊情况描述
* @param zeroFilteredMap 双零过滤的次数和相别
* @param unComparableMap 无法比较的次数和相别
* @param unqualifiedMap 不符合的次数和相别
* @param totalDataPoints 总数据点数
* @param zeroFilteredPoints 双零过滤的数据点数
* @param isInterHarmonic 是否为间谐波
* @return 特殊情况描述
*/
private String generateHarmonicSpecialCase(Map<Integer, List<String>> zeroFilteredMap,
Map<Integer, Map<String, List<String>>> unComparableMap,
Map<Integer, Map<String, List<String>>> unqualifiedMap,
int totalDataPoints, int zeroFilteredPoints,
boolean isInterHarmonic) {
StringBuilder specialCaseDesc = new StringBuilder();
boolean hasZeroFiltered = false;
boolean allZero = false;
// 1. 检查双零过滤情况
if (!zeroFilteredMap.isEmpty()) {
hasZeroFiltered = true;
// 判断是否所有数据都是双零
allZero = (totalDataPoints > 0 && totalDataPoints == zeroFilteredPoints);
if (allZero) {
// 所有次数的所有相别都是双零,直接返回
specialCaseDesc.append("因所有谐波次数的标准值与被检值均为0检测结论为符合。");
return specialCaseDesc.toString();
}
}
// 2. 处理无法比较情况
if (!unComparableMap.isEmpty()) {
if (specialCaseDesc.length() > 0) {
specialCaseDesc.append(" ");
} else {
specialCaseDesc.append("注:");
}
// 合并相同情况的次数和相别
Map<String, Map<String, List<Integer>>> groupedUnComparable = new LinkedHashMap<>();
for (Map.Entry<Integer, Map<String, List<String>>> harmEntry : unComparableMap.entrySet()) {
Integer harmNum = harmEntry.getKey();
for (Map.Entry<String, List<String>> dataEntry : harmEntry.getValue().entrySet()) {
String numOfData = dataEntry.getKey();
List<String> phases = dataEntry.getValue();
String phaseKey = String.join("", phases);
groupedUnComparable.computeIfAbsent(numOfData, k -> new LinkedHashMap<>())
.computeIfAbsent(phaseKey, k -> new ArrayList<>())
.add(harmNum);
}
}
// 生成描述
for (Map.Entry<String, Map<String, List<Integer>>> dataEntry : groupedUnComparable.entrySet()) {
for (Map.Entry<String, List<Integer>> phaseEntry : dataEntry.getValue().entrySet()) {
String phases = phaseEntry.getKey();
List<Integer> harmNums = phaseEntry.getValue();
specialCaseDesc.append("").append(formatHarmNumbers(harmNums, isInterHarmonic)).append("次谐波");
specialCaseDesc.append(phases).append("无样本数据满足误差比较的前置条件,无法执行有效性判定。");
}
}
}
// 3. 处理不符合情况
if (!unqualifiedMap.isEmpty()) {
if (specialCaseDesc.length() > 0) {
specialCaseDesc.append(" ");
} else {
specialCaseDesc.append("注:");
}
// 重新组织数据结构:收集所有相同组数的次数-相别组合
Map<String, List<String>> groupedByNumOfData = new LinkedHashMap<>();
for (Map.Entry<Integer, Map<String, List<String>>> harmEntry : unqualifiedMap.entrySet()) {
Integer harmNum = harmEntry.getKey();
for (Map.Entry<String, List<String>> dataEntry : harmEntry.getValue().entrySet()) {
String numOfData = dataEntry.getKey();
List<String> phases = dataEntry.getValue();
// 如果一个次数下有多个相别,合并成一个描述
String harmPhaseDesc;
String displayHarmNum = getDisplayHarmNumber(harmNum, isInterHarmonic);
if (phases.size() == 1) {
harmPhaseDesc = "" + displayHarmNum + "次谐波" + phases.get(0);
} else {
harmPhaseDesc = "" + displayHarmNum + "次谐波" + String.join("", phases);
}
groupedByNumOfData.computeIfAbsent(numOfData, k -> new ArrayList<>())
.add(harmPhaseDesc);
}
}
// 生成描述
for (Map.Entry<String, List<String>> entry : groupedByNumOfData.entrySet()) {
String numOfData = entry.getKey();
List<String> harmPhaseList = entry.getValue();
// 合并描述第4次谐波A相、B相、C相、第32次谐波A相、第44次谐波C相
specialCaseDesc.append(String.join("", harmPhaseList));
specialCaseDesc.append("收集有效组数为").append(numOfData)
.append("组,误差计算结果不符合误差标准要求。");
}
}
// 4. 最后添加双零过滤的笼统描述(如果有部分双零)
if (hasZeroFiltered && !allZero) {
if (specialCaseDesc.length() > 0) {
specialCaseDesc.append(" ");
} else {
specialCaseDesc.append("注:");
}
specialCaseDesc.append("表格不展示标准值与被检值均为0的数据。");
}
return specialCaseDesc.length() > 0 ? specialCaseDesc.toString() : null;
}
/**
* 获取显示用的谐波次数
* @param harmNum 原始次数2-50
* @param isInterHarmonic 是否为间谐波
* @return 显示用的次数字符串
*/
private String getDisplayHarmNumber(int harmNum, boolean isInterHarmonic) {
if (isInterHarmonic) {
// 间谐波2->1.5, 3->2.5, 4->3.5 ... 50->49.5
double displayNum = harmNum - 0.5;
// 如果是整数则显示整数,否则显示小数
if (displayNum == (int) displayNum) {
return String.valueOf((int) displayNum);
}
return String.valueOf(displayNum);
}
return String.valueOf(harmNum);
}
/**
* 格式化谐波次数列表
* @param harmNumbers 谐波次数列表
* @param isInterHarmonic 是否为间谐波
* @return 格式化的字符串,如"2、3、5"或"2-5、7、9-11"(间谐波为"1.5、2.5、3.5"
*/
private String formatHarmNumbers(List<Integer> harmNumbers, boolean isInterHarmonic) {
if (harmNumbers.isEmpty()) {
return "";
}
if (harmNumbers.size() == 1) {
return getDisplayHarmNumber(harmNumbers.get(0), isInterHarmonic);
}
Collections.sort(harmNumbers);
if (isInterHarmonic) {
// 间谐波不使用范围表示,直接用顿号分隔
List<String> displayNumbers = new ArrayList<>();
for (Integer harmNum : harmNumbers) {
displayNumbers.add(getDisplayHarmNumber(harmNum, true));
}
return String.join("", displayNumbers);
} else {
// 普通谐波使用范围表示
List<String> parts = new ArrayList<>();
int start = harmNumbers.get(0);
int end = start;
for (int i = 1; i < harmNumbers.size(); i++) {
int current = harmNumbers.get(i);
if (current == end + 1) {
end = current;
} else {
if (start == end) {
parts.add(String.valueOf(start));
} else if (end - start == 1) {
parts.add(start + "" + end);
} else {
parts.add(start + "-" + end);
}
start = current;
end = current;
}
}
// 处理最后一组
if (start == end) {
parts.add(String.valueOf(start));
} else if (end - start == 1) {
parts.add(start + "" + end);
} else {
parts.add(start + "-" + end);
}
return String.join("", parts);
}
}
/**
* 获取非谐波类数据的结果数据
*
* @param monitorResultVO 数据来源信息
* @param subScriptId 实际检测指标id
* @param planCode 计划code表名需要
* @param dictTree 测试指标信息
* @return 结果数据
*/
private ContrastTestResult getContrastResultNonHarm(MonitorResultVO monitorResultVO, List<String> subScriptId, Integer planCode, DictTree dictTree) {
ContrastNonHarmonicResult result = contrastNonHarmonicService.getContrastResultHarm(planCode, monitorResultVO.getMonitorId(), subScriptId, monitorResultVO.getResultType(), Integer.parseInt(monitorResultVO.getWhichTime()));
// 收集组数数据
int numOfData = contrastNonHarmonicService.getNumOfData(planCode, monitorResultVO.getMonitorId(), subScriptId, monitorResultVO.getResultType(), Integer.parseInt(monitorResultVO.getWhichTime()));
if (Objects.nonNull(result)) {
// 非谐波类
ContrastTestResult contrastTestResult = new ContrastTestResult();
contrastTestResult.setScriptCode(dictTree.getCode());
contrastTestResult.setScriptName(dictTree.getName());
contrastTestResult.setHarmonic(false);
List<String> allResult = new ArrayList<>();
Map<String, Map<String, String>> checkResultNonHarmonic = new LinkedHashMap<>();
// 根据指标代码确定小数位数
Integer decimalPlaces = getDecimalPlacesByScriptCode(dictTree.getCode());
try {
// 非谐波的需要注意是否为T相还是ABC三相的
if (PowerConstant.T_PHASE.contains(dictTree.getCode())) {
Field field;
if (PowerConstant.TB_PHASE.contains(dictTree.getCode())) {
field = result.getClass().getDeclaredField("bValue");
} else {
field = result.getClass().getDeclaredField("tValue");
}
field.setAccessible(true);
String valueJson = (String) field.get(result);
Map<String, String> singlePhaseData = parseNonHarmonicPhaseData(valueJson, "T相", numOfData, dictTree.getCode(), decimalPlaces);
checkResultNonHarmonic.put("T相", singlePhaseData);
if (CollUtil.isNotEmpty(singlePhaseData)) {
String resultTemp = singlePhaseData.get(ItemReportKeyEnum.RESULT.getKey());
if (StrUtil.isNotBlank(resultTemp)) {
allResult.add(resultTemp);
}
}
} else {
// ABC三相
for (String phase : PowerConstant.PHASE_ABC) {
Field field = result.getClass().getDeclaredField(phase + "Value");
field.setAccessible(true);
String valueJson = (String) field.get(result);
Map<String, String> singlePhaseData = parseNonHarmonicPhaseData(valueJson, phase.toUpperCase() + "", numOfData, dictTree.getCode(), decimalPlaces);
checkResultNonHarmonic.put(phase.toUpperCase() + "", singlePhaseData);
if (CollUtil.isNotEmpty(singlePhaseData)) {
String resultTemp = singlePhaseData.get(ItemReportKeyEnum.RESULT.getKey());
if (StrUtil.isNotBlank(resultTemp)) {
allResult.add(resultTemp);
}
}
}
}
// 生成 specialCase 描述
StringBuilder specialCaseDesc = new StringBuilder();
List<String> unComparablePhases = new ArrayList<>();
Map<String, List<String>> unqualifiedPhasesMap = new LinkedHashMap<>(); // 按组数分组
// 遍历每个相别的结果,收集特殊情况信息
for (Map.Entry<String, Map<String, String>> entry : checkResultNonHarmonic.entrySet()) {
String phase = entry.getKey();
Map<String, String> phaseData = entry.getValue();
String phaseResult = phaseData.get(ItemReportKeyEnum.RESULT.getKey());
if ("无法比较".equals(phaseResult)) {
unComparablePhases.add(phase);
} else if ("不符合".equals(phaseResult)) {
String numOfDataStr = phaseData.get(ItemReportKeyEnum.NUM_OF_DATA.getKey());
// 按组数分组存储相别
unqualifiedPhasesMap.computeIfAbsent(numOfDataStr, k -> new ArrayList<>()).add(phase);
}
}
// 生成无法比较的描述
if (!unComparablePhases.isEmpty()) {
specialCaseDesc.append("注:");
if (unComparablePhases.size() == 1) {
specialCaseDesc.append(unComparablePhases.get(0));
} else {
specialCaseDesc.append(String.join("", unComparablePhases));
}
specialCaseDesc.append("无样本数据满足误差比较的前置条件,无法执行有效性判定。");
}
// 生成不符合的描述(合并相同组数的相别)
if (!unqualifiedPhasesMap.isEmpty()) {
if (specialCaseDesc.length() > 0) {
specialCaseDesc.append(" ");
} else {
specialCaseDesc.append("注:");
}
for (Map.Entry<String, List<String>> entry : unqualifiedPhasesMap.entrySet()) {
String numOfDataStr = entry.getKey();
List<String> phases = entry.getValue();
if (phases.size() == 1) {
specialCaseDesc.append(phases.get(0));
} else {
specialCaseDesc.append(String.join("", phases));
}
specialCaseDesc.append("收集有效组数为").append(numOfDataStr).append("组,误差计算结果不符合误差标准要求。");
if (unqualifiedPhasesMap.size() > 1) {
specialCaseDesc.append(" ");
}
}
}
// 设置检测结果 - 根据所有相数据的结果判断总体结论
String overallResult = determineOverallResult(allResult);
contrastTestResult.setResult(overallResult);
contrastTestResult.setSpecialCase(specialCaseDesc.length() > 0 ? specialCaseDesc.toString() : null);
contrastTestResult.setCheckResultNonHarmonic(checkResultNonHarmonic);
return contrastTestResult;
} catch (Exception e) {
log.warn("获取{}数据失败", dictTree.getName());
}
}
return null;
}
/**
* 解析非谐波数据
*
* @param valueJson JSON格式的数据
* @param phase 相别标识
* @param numOfData 收集组数
* @param scriptCode 脚本代码,用于判断是否需要特殊格式化
* @param decimalPlaces 小数位数null则不格式化
* @return 解析后的数据列表
*/
private Map<String, String> parseNonHarmonicPhaseData(String valueJson, String phase, int numOfData, String scriptCode, Integer decimalPlaces) {
try {
// 解析JSON数据假设是 DetectionData 对象列表
List<DetectionData> dataList = JSON.parseArray(valueJson, DetectionData.class);
Map<String, String> dataMap = new LinkedHashMap<>();
if (CollUtil.isNotEmpty(dataList)) {
DetectionData detectionData = dataList.get(0);
// 相别
dataMap.put(ItemReportKeyEnum.PHASE.getKey(), phase);
// 有效组数 todo... 目前是对齐组数
dataMap.put(ItemReportKeyEnum.NUM_OF_DATA.getKey(), String.valueOf(numOfData));
// 标准值 - 根据参数决定是否格式化
String standardValue = String.valueOf(detectionData.getResultData());
if (decimalPlaces != null && detectionData.getResultData() != null) {
standardValue = formatSignificantDigits(detectionData.getResultData(), decimalPlaces);
}
dataMap.put(ItemReportKeyEnum.STANDARD.getKey(), standardValue);
// 被检值 - 根据参数决定是否格式化
String testValue = String.valueOf(detectionData.getData());
if (decimalPlaces != null && detectionData.getData() != null) {
testValue = formatSignificantDigits(detectionData.getData(), decimalPlaces);
}
dataMap.put(ItemReportKeyEnum.TEST.getKey(), testValue);
// 误差 - 根据参数决定是否格式化
String errorValue = String.valueOf(detectionData.getErrorData());
if (decimalPlaces != null && detectionData.getErrorData() != null) {
errorValue = formatSignificantDigits(detectionData.getErrorData().doubleValue(), decimalPlaces);
}
dataMap.put(ItemReportKeyEnum.ERROR.getKey(), errorValue);
// 误差范围 - 根据参数决定是否格式化
String errorScope = String.valueOf(detectionData.getRadius());
if (decimalPlaces != null && detectionData.getRadius() != null) {
errorScope = formatErrorRange(detectionData.getRadius(), decimalPlaces);
}
dataMap.put(ItemReportKeyEnum.A_ERROR_SCOPE.getKey(), errorScope);
// 结论
dataMap.put(ItemReportKeyEnum.RESULT.getKey(), getTestResult(detectionData.getIsData()));
} else {
// 相别
dataMap.put(ItemReportKeyEnum.PHASE.getKey(), phase);
// 有效组数
dataMap.put(ItemReportKeyEnum.NUM_OF_DATA.getKey(), String.valueOf(0));
// 标准值
dataMap.put(ItemReportKeyEnum.STANDARD.getKey(), "/");
// 被检值
dataMap.put(ItemReportKeyEnum.TEST.getKey(), "/");
// 误差
dataMap.put(ItemReportKeyEnum.ERROR.getKey(), "/");
// 误差范围
dataMap.put(ItemReportKeyEnum.A_ERROR_SCOPE.getKey(), "/");
// 结论
dataMap.put(ItemReportKeyEnum.RESULT.getKey(), "无法比较");
}
return dataMap;
} catch (Exception e) {
log.warn("获取数据失败");
}
return new HashMap<>();
}
/**
* 根据所有相数据的检测结果判断总体指标结论
* 判断原则:所有都符合就符合,有一个不符合就不符合,如果所有都是无法比较就无法比较
*
* @param allResult 所有相数据的检测结果集合
* @return 总体指标结论
*/
private String determineOverallResult(List<String> allResult) {
if (CollUtil.isEmpty(allResult)) {
return "无法比较";
}
// 统计各种结果的数量
// 是否有不符合
boolean hasUnqualified = false;
// 是否有符合
boolean hasQualified = false;
// 是否有无法比较
boolean hasUncomparable = false;
for (String result : allResult) {
if ("不符合".equals(result)) {
hasUnqualified = true;
} else if ("符合".equals(result)) {
hasQualified = true;
} else if ("无法比较".equals(result)) {
hasUncomparable = true;
}
}
// 按照优先级判断:有一个不符合就不符合
if (hasUnqualified) {
return "不符合";
}
// 如果没有不符合,且有符合的,就是符合
if (hasQualified) {
return "符合";
}
// 如果全部都是无法比较
if (hasUncomparable) {
return "无法比较";
}
// 默认返回无法比较(理论上不会到这里)
return "无法比较";
}
/**
* 解析谐波相数据
*
* @param jsonData JSON格式的数据
* @param phase 相别标识
* @param harmNum 谐波次数
* @param numOfData 收集组数
* @param scriptCode 脚本代码,用于判断是否需要特殊格式化
* @param decimalPlaces 小数位数null则不格式化
* @return 解析后的数据列表
*/
private Map<String, String> parseHarmonicPhaseData(String jsonData, String phase, int harmNum, int numOfData, String scriptCode, Integer decimalPlaces) {
try {
// 解析JSON数据假设是 DetectionData 对象列表
List<DetectionData> dataList = JSON.parseArray(jsonData, DetectionData.class);
Map<String, String> dataMap = new LinkedHashMap<>();
if (CollUtil.isNotEmpty(dataList)) {
DetectionData detectionData = dataList.get(0);
// 次数
dataMap.put(ItemReportKeyEnum.TIME.getKey(), String.valueOf(harmNum));
// 相别
dataMap.put(ItemReportKeyEnum.PHASE.getKey(), phase);
// 有效组数 todo... 目前是对齐组数
dataMap.put(ItemReportKeyEnum.NUM_OF_DATA.getKey(), String.valueOf(numOfData));
// 标准值 - 根据参数决定是否格式化
String standardValue = String.valueOf(detectionData.getResultData());
if (decimalPlaces != null && detectionData.getResultData() != null) {
standardValue = formatSignificantDigits(detectionData.getResultData(), decimalPlaces);
}
dataMap.put(ItemReportKeyEnum.STANDARD.getKey(), standardValue);
// 被检值 - 根据参数决定是否格式化
String testValue = String.valueOf(detectionData.getData());
if (decimalPlaces != null && detectionData.getData() != null) {
testValue = formatSignificantDigits(detectionData.getData(), decimalPlaces);
}
dataMap.put(ItemReportKeyEnum.TEST.getKey(), testValue);
// 误差 - 根据参数决定是否格式化
String errorValue = String.valueOf(detectionData.getErrorData());
if (decimalPlaces != null && detectionData.getErrorData() != null) {
errorValue = formatSignificantDigits(detectionData.getErrorData().doubleValue(), decimalPlaces);
}
dataMap.put(ItemReportKeyEnum.ERROR.getKey(), errorValue);
// 误差范围 - 根据参数决定是否格式化
String errorScope = String.valueOf(detectionData.getRadius());
if (decimalPlaces != null && detectionData.getRadius() != null) {
errorScope = formatErrorRange(detectionData.getRadius(), decimalPlaces);
}
dataMap.put(ItemReportKeyEnum.A_ERROR_SCOPE.getKey(), errorScope);
// 结论
dataMap.put(ItemReportKeyEnum.RESULT.getKey(), getTestResult(detectionData.getIsData()));
} else {
// 次数
dataMap.put(ItemReportKeyEnum.TIME.getKey(), String.valueOf(harmNum));
// 相别
dataMap.put(ItemReportKeyEnum.PHASE.getKey(), phase);
// 有效组数
dataMap.put(ItemReportKeyEnum.NUM_OF_DATA.getKey(), "0");
// 标准值
dataMap.put(ItemReportKeyEnum.STANDARD.getKey(), "/");
// 被检值
dataMap.put(ItemReportKeyEnum.TEST.getKey(), "/");
// 误差
dataMap.put(ItemReportKeyEnum.ERROR.getKey(), "/");
// 误差范围
dataMap.put(ItemReportKeyEnum.A_ERROR_SCOPE.getKey(), "/");
// 结论
dataMap.put(ItemReportKeyEnum.RESULT.getKey(), "无法比较");
}
return dataMap;
} catch (Exception e) {
log.error("解析{}第{}次谐波数据失败: {}", phase, harmNum, e.getMessage());
}
return new HashMap<>();
}
/**
* 检查是否有不合格的数据
*
* @param dataList 数据列表
* @return true表示有不合格数据
*/
private boolean hasUnqualifiedData(List<Map<String, String>> dataList) {
if (CollUtil.isEmpty(dataList)) {
return false;
}
for (Map<String, String> data : dataList) {
String resultFlag = data.get("resultFlag");
// resultFlag: 1-合格2-不合格4-无法比较
if ("2".equals(resultFlag)) {
return true;
}
}
return false;
}
/**
* 获取检测结果
*
* @param monitorResultVO 回路检测结论信息
*/
private String getTestResult(MonitorResultVO monitorResultVO) {
Integer checkResult = monitorResultVO.getCheckResult();
return getTestResult(checkResult);
}
/**
* 获取检测结果
*
* @param checkResult 回路检测结论信息
*/
private String getTestResult(Integer checkResult) {
switch (checkResult) {
case 2:
return "不符合";
case 4:
return "无法比较";
default:
// 因为只有 1、2、4所以这里直接返回符合
return "符合";
}
}
private Map<String, List<RawResultDataVO>> getResultMap(DictTree dictTree, List<String> adTypeList, String monitorId, String unit, Integer num, Integer waveNum, Boolean isWave, String code) {
Map<String, List<RawResultDataVO>> resultMap = new LinkedHashMap<>();
@@ -2511,4 +3270,129 @@ public class ResultServiceImpl implements IResultService {
}
return info;
}
/**
* 格式化数值,保留指定的小数位数
*
* @param value 原始数值
* @param decimalPlaces 小数位数
* @return 格式化后的字符串
*/
private String formatSignificantDigits(double value, int decimalPlaces) {
if (value == 0.0) {
return "0";
}
// 使用BigDecimal进行精确计算
BigDecimal bd = new BigDecimal(String.valueOf(value));
// 保留指定的小数位数,四舍五入
bd = bd.setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP);
// 移除末尾的零(可选,根据需要决定是否保留)
String result = bd.stripTrailingZeros().toPlainString();
return result;
}
/**
* 格式化误差范围,将 "-0.057735~0.057735" 格式化为 "±0.05774"
*
* @param radiusValue 误差范围值(通常是正数,表示±范围)
* @return 格式化后的误差范围字符串
*/
private String formatErrorRange(double radiusValue) {
return formatErrorRangeWithDecimalPlaces(radiusValue, 5);
}
/**
* 格式化误差范围,支持自定义小数位数
*
* @param radiusValue 误差范围值(通常是正数,表示±范围)
* @param decimalPlaces 小数位数
* @return 格式化后的误差范围字符串
*/
/**
* 格式化误差范围字符串,支持处理"-0.05~0.05"格式并转换为"±0.05"格式
*
* @param errorRange 误差范围字符串
* @param decimalPlaces 小数位数
* @return 格式化后的误差范围
*/
private String formatErrorRange(String errorRange, Integer decimalPlaces) {
if (errorRange == null || decimalPlaces == null) {
return errorRange;
}
try {
// 尝试直接解析为数字
double radiusValue = Double.parseDouble(errorRange);
return formatErrorRangeWithDecimalPlaces(radiusValue, decimalPlaces);
} catch (NumberFormatException e) {
// 处理"-0.05~0.05"这种格式
if (errorRange.contains("~")) {
String[] parts = errorRange.split("~");
if (parts.length == 2) {
try {
double minValue = Double.parseDouble(parts[0].trim());
double maxValue = Double.parseDouble(parts[1].trim());
// 取绝对值的最大值作为范围
double absMax = Math.max(Math.abs(minValue), Math.abs(maxValue));
return formatErrorRangeWithDecimalPlaces(absMax, decimalPlaces);
} catch (NumberFormatException ex) {
log.warn("无法解析误差范围格式: {}", errorRange);
}
}
}
// 如果无法解析,返回原值
return errorRange;
}
}
private String formatErrorRangeWithDecimalPlaces(double radiusValue, int decimalPlaces) {
if (radiusValue == 0.0) {
return "±0";
}
// 取绝对值并保留指定小数位数
double absValue = Math.abs(radiusValue);
String formattedValue = formatSignificantDigits(absValue, decimalPlaces);
return "±" + formattedValue;
}
/**
* 根据指标代码获取对应的小数位数
*
* @param scriptCode 指标代码
* @return 小数位数null表示不格式化
*/
private Integer getDecimalPlacesByScriptCode(String scriptCode) {
if (scriptCode == null) {
return null;
}
switch (scriptCode) {
// 保留2位小数
case "FREQ": // 频率
return 2;
// 保留3位小数
case "I": // 电流
return 3;
// 保留4位小数
case "IMBV": // 电压不平衡度
case "IMBA": // 电流不平衡度
return 4;
// 保留5位小数
case "V": // 电压
case "HV": // 电压谐波
case "HI": // 电流谐波
case "HP": // 功率谐波
case "HSV": // 电压间谐波
case "HSI": // 电流间谐波
return 5;
// 其他指标不格式化
default:
return null;
}
}
}