yeg
2026-09-05 90882aca0b60a5b39e127d30438752a774b3a7ac
ruoyi-admin/src/test/java/com/battery/business/runtime/RedisStateStoreTest.java
@@ -39,6 +39,45 @@
class RedisStateStoreTest
{
    @Test
    void channelParametersAndPlanSurviveRedisRestartAndOnlineChanges() throws Exception
    {
        var stored = new AtomicReference<Object>();
        var configuration = new com.battery.device.simulator.BtsSimulatorStateStore.ChannelConfiguration(
                new java.math.BigDecimal("-3.123456"), new java.math.BigDecimal("8.125"),
                new java.math.BigDecimal("7.2"), new java.math.BigDecimal("29.7"), 2, 2, 5, 120, 30,
                List.of(new com.battery.device.simulator.BtsSimulatorStateStore.SimulatorPlanStep("充电", "cc", 60, 4.2),
                        new com.battery.device.simulator.BtsSimulatorStateStore.SimulatorPlanStep("放电 <验证>", "dc", 90, 3.0)));
        try (var simulator = new BtsDevelopmentSimulator(0, store(stored))) {
            simulator.configureChannel(1, 1, configuration);
            simulator.updateChannel(1, 1, false, DeviceWorkStatus.PAUSED);
            simulator.setAllDevicesPoweredOn(false);
            simulator.setSoftwareAccessible(false);
        }
        try (var simulator = new BtsDevelopmentSimulator(0, store(stored))) {
            assertEquals(configuration, simulator.snapshot().devices().get(0).channels().get(0).configuration());
            simulator.setSoftwareAccessible(true);
            simulator.setAllDevicesPoweredOn(true);
            simulator.updateChannel(1, 1, true, DeviceWorkStatus.WORKING);
            var identity = simulator.discoverChannels().get(0).identity();
            assertEquals(-3.123456, simulator.latestCabinet(identity).currentA());
            assertEquals(8.125, simulator.latestCabinet(identity).voltageV());
            assertEquals("放电 <验证>", simulator.testPlan(identity).rows().get(1).stepName());
            assertEquals("进行中", simulator.testPlan(identity).rows().get(1).executionStatus());
        }
    }
    @Test
    void oldChannelStateReceivesDefaultConfigurationWithoutResettingIdentity()
    {
        var object = JSON.parseObject(JSON.toJSONString(state(7, "127.0.0.9")));
        object.getJSONArray("devices").getJSONObject(0).getJSONArray("channels").getJSONObject(0).remove("configuration");
        var restored = store(new AtomicReference<>(object.toJSONString())).loadOrInitialize(state(1, "127.0.0.2"));
        assertEquals(7, restored.devices().get(0).deviceId());
        assertEquals("127.0.0.9", restored.devices().get(0).ip());
        assertEquals(1, restored.devices().get(0).channels().get(0).configuration().steps().size());
    }
    @Test
    void initializesOnlyWhenStateIsAbsentAndRestoresSavedState()
    {
        AtomicReference<Object> stored = new AtomicReference<>();
@@ -61,7 +100,7 @@
                List.of(new CollectorState(3, "dev-sim-collector-3", "127.0.1.3",
                        "02:43:00:00:00:03", "模拟采集器", "开发模拟器",
                        "Virtual Collector", 3, true)),
                3, 4, List.of(new ChannelTombstone(1, "127.0.0.2", 4, 2)));
                3, 4, List.of(new ChannelTombstone(1, "127.0.0.2", 4, 2)), false, 900L);
        FastJson2JsonRedisSerializer<Object> serializer = new FastJson2JsonRedisSerializer<>(Object.class);
        AtomicReference<Object> stored = new AtomicReference<>(
                serializer.deserialize(serializer.serialize(JSON.toJSONString(expected))));
@@ -70,6 +109,23 @@
        assertEquals(expected, restored);
        assertEquals(DeviceWorkStatus.WORKING, restored.devices().get(0).channels().get(0).workStatus());
    }
    @Test
    void restoresChannelPlanWhenRedisReturnsNestedJsonObjects()
    {
        JSONObject state = JSON.parseObject(JSON.toJSONString(state(1, "127.0.0.2")));
        JSONObject configuration = state.getJSONArray("devices").getJSONObject(0)
                .getJSONArray("channels").getJSONObject(0).getJSONObject("configuration");
        configuration.getJSONArray("steps").forEach(item -> ((JSONObject) item).put("@type", JSONObject.class.getName()));
        FastJson2JsonRedisSerializer<Object> serializer = new FastJson2JsonRedisSerializer<>(Object.class);
        Object restoredValue = serializer.deserialize(serializer.serialize(state));
        SimulatorState restored = store(new AtomicReference<>(restoredValue))
                .loadOrInitialize(state(9, "127.0.0.9"));
        assertEquals("恒流充电", restored.devices().get(0).channels().get(0)
                .configuration().steps().get(0).name());
    }
    @Test
@@ -86,70 +142,8 @@
        CollectorState collector = restored.collectors().get(0);
        assertEquals(1000, collector.sampleIntervalMs());
        assertEquals(1L, collector.nextSequence());
        assertEquals(List.of("S-01", "S-02"),
                collector.channels().stream().map(channel -> channel.channelCode()).toList());
        assertEquals(0L, collector.historyRecordCount());
    }
    @Test
    void migratesEmbeddedCollectorHistoryToAppendOnlyRedisList()
    {
        AtomicReference<Object> stored = new AtomicReference<>("{\"version\":1,\"devices\":[],"
                + "\"collectors\":[{\"collectorId\":1,\"deviceCode\":\"dev-sim-collector-1\","
                + "\"ip\":\"127.0.1.2\",\"mac\":\"02:43:00:00:00:01\","
                + "\"name\":\"模拟采集器\",\"manufacturer\":\"开发模拟器\","
                + "\"model\":\"Virtual Collector\",\"channelCount\":1,\"online\":true,"
                + "\"sampleIntervalMs\":1000,\"nextSequence\":2,"
                + "\"lastSampledAt\":\"2026-08-29T05:00:00Z\","
                + "\"channels\":[{\"channelCode\":\"S-01\",\"metrics\":[{"
                + "\"metricCode\":\"temperature\",\"value\":25.1,\"unit\":\"℃\"}]}],"
                + "\"history\":[{\"adapterCode\":\"dev-sim\","
                + "\"collectorCode\":\"dev-sim-collector-1\",\"channelCode\":\"S-01\","
                + "\"sequenceId\":1,\"deviceTime\":\"2026-08-29T05:00:00Z\","
                + "\"deviceTimeRaw\":\"2026-08-29 13:00:00.000\","
                + "\"serverReceivedAt\":\"2026-08-29T05:00:00Z\","
                + "\"metricCode\":\"temperature\",\"value\":25.1,\"unit\":\"℃\"}],"
                + "\"historyFaults\":{\"missingSequences\":[],\"outOfOrder\":false}}],"
                + "\"nextDeviceId\":1,\"nextCollectorId\":2,\"tombstones\":[]}");
        RedisBtsSimulatorStateStore store = store(stored);
        SimulatorState restored = store.loadOrInitialize(state(1, "127.0.0.2"));
        assertEquals(1L, restored.collectors().get(0).historyRecordCount());
        assertEquals(1, store.loadCollectorHistoryPage("dev-sim-collector-1", 0, 1000, false)
                .records().size());
        assertEquals(true, stored.get().toString().contains("\"history\":[{"));
        store.completeValidatedRestore(restored);
        assertEquals(false, stored.get().toString().contains("\"history\":[{"));
    }
    @Test
    void clearsLegacyEmbeddedHistoryForDemoStartup() throws Exception
    {
        AtomicReference<Object> stored = new AtomicReference<>("{\"version\":1,\"devices\":[],"
                + "\"collectors\":[{\"collectorId\":1,\"deviceCode\":\"dev-sim-collector-1\","
                + "\"ip\":\"127.0.1.2\",\"mac\":\"02:43:00:00:00:01\","
                + "\"name\":\"模拟采集器\",\"manufacturer\":\"开发模拟器\","
                + "\"model\":\"Virtual Collector\",\"channelCount\":1,\"online\":true,"
                + "\"sampleIntervalMs\":1000,\"nextSequence\":2,"
                + "\"lastSampledAt\":\"2026-08-29T05:00:00Z\","
                + "\"channels\":[{\"channelCode\":\"S-01\",\"metrics\":[{"
                + "\"metricCode\":\"temperature\",\"value\":25.1,\"unit\":\"℃\"}]}],"
                + "\"history\":[{\"adapterCode\":\"dev-sim\","
                + "\"collectorCode\":\"wrong-collector\",\"channelCode\":\"S-01\","
                + "\"sequenceId\":1,\"deviceTime\":\"2026-08-29T05:00:00Z\","
                + "\"deviceTimeRaw\":\"2026-08-29 13:00:00.000\","
                + "\"serverReceivedAt\":\"2026-08-29T05:00:00Z\","
                + "\"metricCode\":\"temperature\",\"value\":25.1,\"unit\":\"℃\"}],"
                + "\"historyFaults\":{\"missingSequences\":[],\"outOfOrder\":false}}],"
                + "\"nextDeviceId\":1,\"nextCollectorId\":2,\"tombstones\":[]}");
        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, store(stored)))
        {
            assertEquals(0L, simulator.snapshot().collectors().get(0).historyRecordCount());
        }
        assertEquals(false, stored.get().toString().contains("\"history\":[{"));
    }
    @Test
@@ -164,58 +158,6 @@
        assertEquals("Unsupported BTS simulator state version: 2", exception.getMessage());
        assertEquals(true, stored.get().toString().contains("\"version\":2"));
    }
    @Test
    void atomicallySavesCollectorDeletionAndRemovesIndependentHistory()
    {
        AtomicReference<Object> stored = new AtomicReference<>();
        RedisBtsSimulatorStateStore store = store(stored);
        SimulatorState current = state(1, "127.0.0.2");
        store.loadOrInitialize(current);
        var record = new com.battery.device.simulator.BtsSimulatorStateStore.CollectorMeasurementRecord(
                "dev-sim", "dev-sim-collector-1", "S-01", 1L,
                java.time.Instant.parse("2026-08-29T05:00:00Z"), "2026-08-29 13:00:00.000",
                java.time.Instant.parse("2026-08-29T05:00:00Z"), "temperature",
                new java.math.BigDecimal("25.1"), "℃");
        store.save(current, List.of(
                new com.battery.device.simulator.BtsSimulatorStateStore.CollectorHistoryAppend(
                        "dev-sim-collector-1", List.of(record))));
        SimulatorState next = state(7, "127.0.0.9");
        store.saveAndDeleteCollectorHistory(next, "dev-sim-collector-1");
        assertEquals(next, store.loadOrInitialize(current));
        assertEquals(List.of(), store.loadCollectorHistoryPage("dev-sim-collector-1", 0, 1000, false)
                .records());
    }
    @Test
    void readsOnlyRequestedCollectorHistoryRange()
    {
        AtomicReference<Object> stored = new AtomicReference<>();
        RedisTemplate<Object, Object> redis = redis(stored);
        RedisBtsSimulatorStateStore store = new RedisBtsSimulatorStateStore(redis);
        SimulatorState current = state(1, "127.0.0.2");
        store.loadOrInitialize(current);
        var record = new com.battery.device.simulator.BtsSimulatorStateStore.CollectorMeasurementRecord(
                "dev-sim", "dev-sim-collector-1", "S-01", 1L,
                java.time.Instant.parse("2026-08-29T05:00:00Z"), "2026-08-29 13:00:00.000",
                java.time.Instant.parse("2026-08-29T05:00:00Z"), "temperature",
                new java.math.BigDecimal("25.1"), "℃");
        store.save(current, List.of(
                new com.battery.device.simulator.BtsSimulatorStateStore.CollectorHistoryAppend(
                        "dev-sim-collector-1", java.util.Collections.nCopies(1010, record))));
        ListOperations<Object, Object> lists = redis.opsForList();
        clearInvocations(lists);
        var page = store.loadCollectorHistoryPage("dev-sim-collector-1", 1000, 10, false);
        assertEquals(1010, page.total());
        assertEquals(10, page.records().size());
        assertEquals(false, page.hasMore());
        verify(lists).range(RedisBtsSimulatorStateStore.HISTORY_KEY_PREFIX + "dev-sim-collector-1",
                1000L, 1009L);
    }
    @Test