From 370ef4f5e663fdfc1d19d2774dfc4ec1ed0af60e Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期六, 29 八月 2026 14:57:48 +0800
Subject: [PATCH] 修复采集器历史状态持续序列化错误

---
 ruoyi-device/src/test/java/com/ruoyi/device/contract/SimulatorPublicContractTest.java |   79 ++++++++++++++++++++++++++++++++++-----
 1 files changed, 69 insertions(+), 10 deletions(-)

diff --git a/ruoyi-device/src/test/java/com/ruoyi/device/contract/SimulatorPublicContractTest.java b/ruoyi-device/src/test/java/com/ruoyi/device/contract/SimulatorPublicContractTest.java
index ba19503..7a55294 100644
--- a/ruoyi-device/src/test/java/com/ruoyi/device/contract/SimulatorPublicContractTest.java
+++ b/ruoyi-device/src/test/java/com/ruoyi/device/contract/SimulatorPublicContractTest.java
@@ -11,6 +11,7 @@
 import java.util.List;
 import java.util.Set;
 import org.junit.jupiter.api.Test;
+import com.alibaba.fastjson2.JSON;
 import com.ruoyi.device.api.DeviceAdapter;
 import com.ruoyi.device.bts.BtsDevelopmentSimulator;
 import com.ruoyi.device.bts.BtsDeviceAdapter;
@@ -22,6 +23,7 @@
 import com.ruoyi.device.simulator.BtsSimulatorStateStore;
 import com.ruoyi.device.simulator.BtsSimulatorStateStore.ChannelState;
 import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorChannelState;
+import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorHistoryAppend;
 import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorHistoryFaults;
 import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorMeasurementRecord;
 import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorMetricState;
@@ -132,6 +134,20 @@
     }
 
     @Test
+    void collectorRawHistoryIsNotEmbeddedInMainSimulatorState() throws Exception
+    {
+        InMemoryBtsSimulatorStateStore store = new InMemoryBtsSimulatorStateStore();
+        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, store))
+        {
+            simulator.advanceCollectors(Instant.parse("2026-08-29T03:30:00Z"));
+
+            String mainState = JSON.toJSONString(store.loadOrInitialize(null));
+            assertFalse(mainState.contains("\"history\":[{"));
+            assertEquals(12, simulator.collectorHistory(1, 0, 100).total());
+        }
+    }
+
+    @Test
     void collectorRealtimeDropsMeasurementsRemovedFromCurrentConfiguration() throws Exception
     {
         Instant startedAt = Instant.parse("2026-08-29T04:00:00Z");
@@ -159,6 +175,18 @@
         {
             assertEquals(4, restored.snapshot().collectors().get(0).latestMeasurements().size());
             assertEquals(12, restored.collectorHistory(1, 0, 100).total());
+        }
+    }
+
+    @Test
+    void collectorRejectsMetricWhitespaceThatWouldBreakRestore() throws Exception
+    {
+        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0))
+        {
+            assertThrows(IllegalArgumentException.class, () -> simulator.configureCollectorChannel(1, "S-01",
+                    List.of(new CollectorMetricState("temperature ", new BigDecimal("25.0"), "℃"))));
+            assertThrows(IllegalArgumentException.class, () -> simulator.configureCollectorChannel(1, "S-01",
+                    List.of(new CollectorMetricState("temperature", new BigDecimal("25.0"), " ℃"))));
         }
     }
 
@@ -347,6 +375,31 @@
     }
 
     @Test
+    void collectorDeletionDoesNotReplaceMemoryWhenAtomicPersistenceFails() throws Exception
+    {
+        InMemoryBtsSimulatorStateStore delegate = new InMemoryBtsSimulatorStateStore();
+        BtsSimulatorStateStore failing = new BtsSimulatorStateStore()
+        {
+            @Override public SimulatorState loadOrInitialize(SimulatorState value)
+            { return delegate.loadOrInitialize(value); }
+            @Override public void save(SimulatorState value) { delegate.save(value); }
+            @Override public void saveAndDeleteCollectorHistory(SimulatorState value, String collectorCode)
+            { throw new IllegalStateException("redis unavailable"); }
+            @Override public java.util.List<RecentRequest> loadRecentRequests()
+            { return delegate.loadRecentRequests(); }
+            @Override public void saveRecentRequests(java.util.List<RecentRequest> requests)
+            { delegate.saveRecentRequests(requests); }
+            @Override public void reset(SimulatorState value) { delegate.reset(value); }
+        };
+
+        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, failing))
+        {
+            assertThrows(IllegalStateException.class, () -> simulator.deleteCollector(1));
+            assertEquals(1, simulator.snapshot().collectors().size());
+        }
+    }
+
+    @Test
     void developmentSimulatorRestoresUnfinishedTombstones() throws Exception
     {
         InMemoryBtsSimulatorStateStore store = new InMemoryBtsSimulatorStateStore();
@@ -390,17 +443,20 @@
         Instant observedAt = Instant.parse("2026-08-29T05:00:00Z");
         List<CollectorChannelState> channels = List.of(new CollectorChannelState("S-01", List.of(
                 new CollectorMetricState("temperature", new BigDecimal("25.0"), "℃"))));
+        CollectorMeasurementRecord record = new CollectorMeasurementRecord(
+                "dev-sim", "wrong-collector", "S-01", 1L, observedAt,
+                "2026-08-29 13:00:00.000", observedAt, "temperature",
+                new BigDecimal("25.0"), "℃");
         CollectorState collector = new CollectorState(1, "dev-sim-collector-1", "127.0.1.2",
                 "02:43:00:00:00:01", "模拟采集器", "开发模拟器", "Virtual Collector", 1, true,
-                1000, 2L, observedAt, channels, List.of(), List.of(new CollectorMeasurementRecord(
-                        "dev-sim", "wrong-collector", "S-01", 1L, observedAt,
-                        "2026-08-29 13:00:00.000", observedAt, "temperature",
-                        new BigDecimal("25.0"), "℃")), CollectorHistoryFaults.none());
+                1000, 2L, observedAt, channels, List.of(), 1L, CollectorHistoryFaults.none());
         SimulatorState damaged = new SimulatorState(BtsSimulatorStateStore.CURRENT_VERSION,
                 List.of(), List.of(collector), 1, 2, List.of());
+        InMemoryBtsSimulatorStateStore store = new InMemoryBtsSimulatorStateStore(damaged);
+        store.save(damaged, List.of(new CollectorHistoryAppend(collector.deviceCode(), List.of(record))));
 
         assertThrows(IllegalStateException.class,
-                () -> new BtsDevelopmentSimulator(0, new InMemoryBtsSimulatorStateStore(damaged)));
+                () -> new BtsDevelopmentSimulator(0, store));
     }
 
     @Test
@@ -409,16 +465,19 @@
         Instant observedAt = Instant.parse("2026-08-29T06:00:00Z");
         List<CollectorChannelState> channels = List.of(new CollectorChannelState("S-01", List.of(
                 new CollectorMetricState("temperature", new BigDecimal("25.0"), "℃"))));
+        CollectorMeasurementRecord record = new CollectorMeasurementRecord(
+                "dev-sim", "dev-sim-collector-1", "S-01", 1L, observedAt,
+                "2026-08-29 14:00:00.000", observedAt, "temperature",
+                new BigDecimal("25.0"), "℃");
         CollectorState collector = new CollectorState(1, "dev-sim-collector-1", "127.0.1.2",
                 "02:43:00:00:00:01", "模拟采集器", "开发模拟器", "Virtual Collector", 1, true,
-                1000, 1L, observedAt, channels, List.of(), List.of(new CollectorMeasurementRecord(
-                        "dev-sim", "dev-sim-collector-1", "S-01", 1L, observedAt,
-                        "2026-08-29 14:00:00.000", observedAt, "temperature",
-                        new BigDecimal("25.0"), "℃")), CollectorHistoryFaults.none());
+                1000, 1L, observedAt, channels, List.of(), 1L, CollectorHistoryFaults.none());
         SimulatorState damaged = new SimulatorState(BtsSimulatorStateStore.CURRENT_VERSION,
                 List.of(), List.of(collector), 1, 2, List.of());
+        InMemoryBtsSimulatorStateStore store = new InMemoryBtsSimulatorStateStore(damaged);
+        store.save(damaged, List.of(new CollectorHistoryAppend(collector.deviceCode(), List.of(record))));
 
         assertThrows(IllegalStateException.class,
-                () -> new BtsDevelopmentSimulator(0, new InMemoryBtsSimulatorStateStore(damaged)));
+                () -> new BtsDevelopmentSimulator(0, store));
     }
 }

--
Gitblit v1.7.1