From cbf301a41d3ad43a61b472a780f932f3975a5614 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期六, 05 九月 2026 20:39:17 +0800
Subject: [PATCH] 修复多通道温度覆盖与实时阈值异常漏报
---
battery-business/src/main/java/com/battery/business/service/impl/TTestLiveServiceImpl.java | 72 ++++++++++++++++++++++++++---------
1 files changed, 53 insertions(+), 19 deletions(-)
diff --git a/battery-business/src/main/java/com/battery/business/service/impl/TTestLiveServiceImpl.java b/battery-business/src/main/java/com/battery/business/service/impl/TTestLiveServiceImpl.java
index 620ad61..1b38027 100644
--- a/battery-business/src/main/java/com/battery/business/service/impl/TTestLiveServiceImpl.java
+++ b/battery-business/src/main/java/com/battery/business/service/impl/TTestLiveServiceImpl.java
@@ -1,6 +1,7 @@
package com.battery.business.service.impl;
import java.time.Instant;
+import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -56,26 +57,11 @@
}
List<Map<String, Object>> exceptions = new ArrayList<>();
java.util.Set<String> offlineSources = new java.util.HashSet<>();
- // 完成或暂停后只保留历史异常事实,实时控制台不再展示开放预警。
- if (receiving && mapper != null) {
- mapper.selectWarningExceptions(testId, "OPEN").forEach(item -> {
- // 离线待办的处理状态不代表当前设备状态,实时提醒只由下方当前来源生成。
- if ("DEVICE_OFFLINE".equals(item.getExceptionType())) return;
- Map<String, Object> warning = new LinkedHashMap<>();
- warning.put("exceptionId", item.getExceptionId());
- warning.put("exceptionType", item.getExceptionType());
- warning.put("sourceType", item.getSourceType());
- warning.put("sourceIdentity", item.getSourceIdentity());
- warning.put("metricCode", item.getMetricCode());
- warning.put("value", item.getAbnormalValue());
- warning.put("unit", item.getUnit());
- warning.put("status", item.getExceptionStatus());
- warning.put("message", "测试预警异常");
- exceptions.add(warning);
- });
- }
+ // 实时提醒只反映当前设备和测量事实,异常管理待办是否处理不参与展示判定。
+ java.util.Set<String> thresholdSources = new java.util.HashSet<>();
if (receiving) {
for (LiveSource source : sources) {
+ if ("ONLINE".equals(source.deviceStatus())) appendThresholdWarnings(test, source, exceptions, thresholdSources);
if (!"OFFLINE".equals(source.deviceStatus())) continue;
String identity = offlineIdentity(test, source);
if (!offlineSources.add(source.sourceType() + ":" + identity)) continue;
@@ -87,6 +73,52 @@
String testStatus = test.getTestStatus();
TestPlanSnapshot testPlan = detectorTestPlan(test);
return new LiveState(testId, testStatus, test.getReceptionStatus(), Instant.now().toEpochMilli(), sources, exceptions, testPlan);
+ }
+
+ private void appendThresholdWarnings(TTest test, LiveSource source, List<Map<String, Object>> warnings,
+ java.util.Set<String> seen)
+ {
+ var snapshot = source.latestSnapshot();
+ if ("CABINET_CHANNEL".equals(source.sourceType())) {
+ if (!receivedDuringTest(test, snapshot.get("deviceTime"), snapshot.get("observedAt"))) return;
+ String identity = offlineIdentity(test, source);
+ for (String[] field : List.of(new String[] { "voltageV", "VOLTAGE", "V" },
+ new String[] { "currentA", "CURRENT", "A" }, new String[] { "capacityAh", "CAPACITY", "Ah" },
+ new String[] { "energyWh", "ENERGY", "Wh" }))
+ appendThresholdValue(test, source.sourceType(), identity, field[1], snapshot.get(field[0]), field[2], warnings, seen);
+ }
+ else if (snapshot.get("measurements") instanceof List<?> measurements) {
+ for (Object item : measurements) if (item instanceof Map<?, ?> measurement
+ && receivedDuringTest(test, measurement.get("deviceTime"), measurement.get("serverReceivedAt"))) {
+ appendThresholdValue(test, source.sourceType(), source.sourceId() + ":" + measurement.get("channelCode"),
+ String.valueOf(measurement.get("metricCode")), measurement.get("value"),
+ String.valueOf(measurement.get("unit")), warnings, seen);
+ }
+ }
+ }
+
+ private boolean receivedDuringTest(TTest test, Object deviceTime, Object observedAt)
+ {
+ return TestDataReceptionServiceImpl.accepted(test,
+ deviceTime instanceof Number time ? Instant.ofEpochMilli(time.longValue()) : null,
+ observedAt instanceof Number time ? Instant.ofEpochMilli(time.longValue()) : null);
+ }
+
+ private void appendThresholdValue(TTest test, String sourceType, String identity, String metricCode,
+ Object value, String unit, List<Map<String, Object>> warnings, java.util.Set<String> seen)
+ {
+ if (!(value instanceof Number number) || !Double.isFinite(number.doubleValue())) return;
+ BigDecimal measurement = value instanceof BigDecimal decimal ? decimal : new BigDecimal(value.toString());
+ for (var rule : TestWarningExceptionService.exceededRules(test, metricCode, measurement)) {
+ if (!seen.add(rule.getThresholdId() + ":" + sourceType + ":" + identity + ":" + metricCode)) continue;
+ Map<String, Object> warning = new LinkedHashMap<>();
+ warning.put("exceptionType", "THRESHOLD"); warning.put("thresholdId", rule.getThresholdId());
+ warning.put("sourceType", sourceType); warning.put("sourceIdentity", identity);
+ warning.put("metricCode", metricCode); warning.put("value", measurement); warning.put("unit", unit);
+ warning.put("minValue", rule.getMinValue()); warning.put("maxValue", rule.getMaxValue());
+ warning.put("status", "OPEN"); warning.put("message", "当前测量超出预警阈值");
+ warnings.add(warning);
+ }
}
private String offlineIdentity(TTest test, LiveSource source)
@@ -161,6 +193,7 @@
if (channel.latestSnapshot() == null) return value;
value.put("source", "检测柜");
value.put("observedAt", channel.latestSnapshot().receivedAt().toEpochMilli());
+ value.put("deviceTime", channel.latestSnapshot().deviceTime() == null ? null : channel.latestSnapshot().deviceTime().toEpochMilli());
// BTS 核心采样字段是快照的一等事实,不能只依赖动态 measurements 展示。
value.put("currentA", channel.latestSnapshot().currentA());
value.put("voltageV", channel.latestSnapshot().voltageV());
@@ -205,7 +238,8 @@
value.put("source", "采集器"); value.put("sampleIntervalMs", collector.sampleIntervalMs());
// 在业务响应边界转换毫秒时间,设备时间与服务器接收时间分别保留。
value.put("measurements", collector.measurements().stream().filter(item -> battery.getCollectorChannels() != null
- && battery.getCollectorChannels().stream().anyMatch(binding -> binding.getChannelCode().equals(item.channelCode()))).map(item -> {
+ && battery.getCollectorChannels().stream().anyMatch(binding -> binding.getChannelCode().equals(item.channelCode())
+ && TestWarningExceptionService.matchesCollectorMetric(binding.getMeasurementCode(), item.metricCode()))).map(item -> {
Map<String, Object> measurement = new LinkedHashMap<>();
measurement.put("metricCode", item.metricCode());
measurement.put("channelCode", item.channelCode());
--
Gitblit v1.7.1