feat(WeightDeviceDataService): enhance anomaly detection with detailed field comparison and numeric tolerance

This commit is contained in:
2025-10-09 20:55:25 +08:00
parent 5435966b62
commit 6736b6c22a

View File

@@ -54,6 +54,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Service
@@ -267,29 +268,104 @@ where 二次过磅时间>='%s' and 二次过磅时间<='%s'
private boolean isAnomalousData(Map<String, Object> sysData, Object bfData) {
// 检查系统数据和业务数据是否一致
// 这里可以根据业务规则来定义什么是异常数据
if (sysData == null || bfData == null) {
return true;
}
// 示例检查逻辑:检查数量是否匹配
try {
Object sysCnt = sysData.get("cnt");
if (bfData instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> bfMap = (Map<String, Object>) bfData;
Object bfCnt = bfMap.get("cnt");
if (!(bfData instanceof Map)) {
log.warn("业务数据格式不正确期望Map类型");
return true;
}
@SuppressWarnings("unchecked")
Map<String, Object> bfMap = (Map<String, Object>) bfData;
// 检查所有重要字段:毛重、皮重、净重、扣重、实重、数量
String[] checkFields = {"mz", "pz", "jz", "kz", "sz", "cnt"};
for (String field : checkFields) {
Object sysValue = sysData.get(field);
Object bfValue = bfMap.get(field);
if (sysCnt != null && bfCnt != null) {
return !sysCnt.toString().equals(bfCnt.toString());
// 如果任一值为null需要两者都为null才算一致
if (sysValue == null || bfValue == null) {
if (sysValue != bfValue) {
log.debug("字段 {} 存在null值不一致: 系统={}, 业务={}", field, sysValue, bfValue);
return true;
}
continue;
}
// 对于数值字段,进行数值比较
if (isNumericField(field)) {
if (!compareNumericValues(sysValue, bfValue)) {
log.debug("数值字段 {} 不匹配: 系统={}, 业务={}", field, sysValue, bfValue);
return true;
}
} else {
// 对于非数值字段,进行字符串比较
if (!sysValue.toString().equals(bfValue.toString())) {
log.debug("字段 {} 不匹配: 系统={}, 业务={}", field, sysValue, bfValue);
return true;
}
}
}
return false;
} catch (Exception e) {
log.warn("检查异常数据时发生错误", e);
return true;
}
}
private boolean isNumericField(String fieldName) {
// 重量相关字段都是数值类型
return fieldName.equals("mz") || fieldName.equals("pz") || fieldName.equals("jz")
|| fieldName.equals("kz") || fieldName.equals("sz") || fieldName.equals("cnt");
}
private boolean compareNumericValues(Object value1, Object value2) {
try {
// 转换为Double进行比较容忍小的浮点误差
Double num1 = parseToDouble(value1);
Double num2 = parseToDouble(value2);
if (num1 == null || num2 == null) {
return Objects.equals(num1, num2);
}
// 对于重量数据容忍0.01的误差(精确到分)
return Math.abs(num1 - num2) < 0.01;
} catch (Exception e) {
log.debug("数值比较失败,回退到字符串比较: {} vs {}", value1, value2);
return value1.toString().equals(value2.toString());
}
}
private Double parseToDouble(Object value) {
if (value == null) {
return null;
}
return false;
try {
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
String str = value.toString().trim();
if (str.isEmpty()) {
return 0.0;
}
return Double.parseDouble(str);
} catch (NumberFormatException e) {
log.debug("无法解析数值: {}", value);
return null;
}
}
private List<LocalDate> binarySearchAnomalousDates(String deviceId, LocalDate startDate, LocalDate endDate) {