yeg
2026-08-30 b985e6140e7f395ead7dc55e41e6b16d97f9f02b
简化开发模拟器历史数据管理

启动时清理上一轮 dev-sim Redis 历史,运行期间限制历史长度并重置演示计数,避免历史数据拖慢启动。
7个文件已修改
61 ■■■■■ 已修改文件
ruoyi-admin/src/main/java/com/ruoyi/config/RedisBtsSimulatorStateStore.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/test/java/com/ruoyi/config/RedisStateStoreTest.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-device/src/main/java/com/ruoyi/device/bts/BtsDevelopmentSimulator.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-device/src/main/java/com/ruoyi/device/simulator/BtsSimulatorStateStore.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-device/src/main/java/com/ruoyi/device/simulator/DevelopmentCollectorSimulation.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-device/src/main/java/com/ruoyi/device/simulator/InMemoryBtsSimulatorStateStore.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-device/src/test/java/com/ruoyi/device/contract/SimulatorPublicContractTest.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/config/RedisBtsSimulatorStateStore.java
@@ -47,6 +47,29 @@
        return restoreState(stored);
    }
    @Override
    public SimulatorState resetHistoryForDemoStartup(SimulatorState state)
    {
        pendingEmbeddedHistory = List.of();
        Set<Object> historyKeys = redisTemplate.opsForSet().members(HISTORY_KEYS_KEY);
        if (historyKeys != null && !historyKeys.isEmpty()) redisTemplate.delete(historyKeys);
        redisTemplate.delete(HISTORY_KEYS_KEY);
        List<com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorState> collectors = state.collectors()
                .stream()
                .map(collector -> new com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorState(
                        collector.collectorId(), collector.deviceCode(), collector.ip(), collector.mac(),
                        collector.name(), collector.manufacturer(), collector.model(), collector.channelCount(),
                        collector.online(), collector.sampleIntervalMs(), 1L,
                        null, collector.channels(), List.of(), 0L,
                        collector.historyFaults()))
                .toList();
        SimulatorState reset = new SimulatorState(state.version(), state.devices(), collectors,
                state.nextDeviceId(), state.nextCollectorId(), state.tombstones());
        redisTemplate.opsForValue().set(STATE_KEY, JSON.toJSONString(reset));
        return reset;
    }
    private SimulatorState restoreState(Object stored)
    {
        ParsedSimulatorState parsed = parseState(stored);
@@ -88,6 +111,7 @@
                        List<Object> records = new ArrayList<>(
                                append.records().stream().map(JSON::toJSONString).toList());
                        typed.opsForList().rightPushAll(historyKey, records);
                        typed.opsForList().trim(historyKey, -MAX_HISTORY_RECORDS, -1);
                        typed.opsForSet().add(HISTORY_KEYS_KEY, historyKey);
                    }
                }
ruoyi-admin/src/test/java/com/ruoyi/config/RedisStateStoreTest.java
@@ -125,7 +125,7 @@
    }
    @Test
    void doesNotOverwriteDamagedEmbeddedHistoryBeforeValidation()
    void clearsLegacyEmbeddedHistoryForDemoStartup() throws Exception
    {
        AtomicReference<Object> stored = new AtomicReference<>("{\"version\":1,\"devices\":[],"
                + "\"collectors\":[{\"collectorId\":1,\"deviceCode\":\"dev-sim-collector-1\","
@@ -145,10 +145,11 @@
                + "\"historyFaults\":{\"missingSequences\":[],\"outOfOrder\":false}}],"
                + "\"nextDeviceId\":1,\"nextCollectorId\":2,\"tombstones\":[]}");
        assertThrows(IllegalStateException.class,
                () -> new BtsDevelopmentSimulator(0, store(stored)));
        assertEquals(true, stored.get().toString().contains("\"history\":[{"));
        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, store(stored)))
        {
            assertEquals(0L, simulator.snapshot().collectors().get(0).historyRecordCount());
        }
        assertEquals(false, stored.get().toString().contains("\"history\":[{"));
    }
    @Test
ruoyi-device/src/main/java/com/ruoyi/device/bts/BtsDevelopmentSimulator.java
@@ -62,6 +62,7 @@
    {
        this.stateStore = stateStore;
        state = stateStore.loadOrInitialize(defaultState());
        state = stateStore.resetHistoryForDemoStartup(state);
        validateRestoredState();
        stateStore.completeValidatedRestore(state);
        simulator = ScriptedBtsTcpSimulator.responding(port, this::respond);
ruoyi-device/src/main/java/com/ruoyi/device/simulator/BtsSimulatorStateStore.java
@@ -10,10 +10,15 @@
{
    int CURRENT_VERSION = 1;
    int MAX_HISTORY_BATCH_SIZE = 1000;
    /** 开发演示只保留有限历史,避免 Redis 随运行时间无限增长。 */
    long MAX_HISTORY_RECORDS = 20_000L;
    /** 读取已有状态;仅在状态不存在时原子写入并返回默认状态。 */
    SimulatorState loadOrInitialize(SimulatorState defaultState);
    /** 开发模拟器启动时清理上一轮演示历史;正式业务存储不需要实现该动作。 */
    default SimulatorState resetHistoryForDemoStartup(SimulatorState state) { return state; }
    /** 完整保存下一版模拟器状态。 */
    void save(SimulatorState state);
ruoyi-device/src/main/java/com/ruoyi/device/simulator/DevelopmentCollectorSimulation.java
@@ -131,7 +131,8 @@
        CollectorState next = copy(current, current.ip(), current.channelCount(), current.online(),
                current.sampleIntervalMs(),
                sequence, lastSampledAt, current.channels(), List.copyOf(latest.values()),
                current.historyRecordCount() + appendedRecords.size(),
                Math.min(BtsSimulatorStateStore.MAX_HISTORY_RECORDS,
                        current.historyRecordCount() + appendedRecords.size()),
                current.historyFaults());
        return new CollectorAdvance(next, appendedRecords);
    }
ruoyi-device/src/main/java/com/ruoyi/device/simulator/InMemoryBtsSimulatorStateStore.java
@@ -41,8 +41,13 @@
    public synchronized void save(SimulatorState state, List<CollectorHistoryAppend> historyAppends)
    {
        for (CollectorHistoryAppend append : historyAppends)
            collectorHistory.computeIfAbsent(append.collectorCode(), key -> new ArrayList<>())
                    .addAll(append.records());
        {
            ArrayList<CollectorMeasurementRecord> history = collectorHistory.computeIfAbsent(
                    append.collectorCode(), key -> new ArrayList<>());
            history.addAll(append.records());
            int overflow = history.size() - (int) MAX_HISTORY_RECORDS;
            if (overflow > 0) history.subList(0, overflow).clear();
        }
        this.state = state;
    }
ruoyi-device/src/test/java/com/ruoyi/device/contract/SimulatorPublicContractTest.java
@@ -158,7 +158,7 @@
    }
    @Test
    void collectorKeepsAppendOnlyHistoryBeyondPreviousDisplayLimit() throws Exception
    void collectorKeepsOnlyBoundedDemoHistory() throws Exception
    {
        Instant startedAt = Instant.parse("2026-08-29T03:00:00Z");
        TrackingHistoryStore store = new TrackingHistoryStore();
@@ -171,8 +171,8 @@
            store.resetReadStats();
            var page = simulator.collectorHistory(1, 0, 1);
            assertEquals(60_012, page.total());
            assertEquals(1, page.records().get(0).sequenceId());
            assertEquals(BtsSimulatorStateStore.MAX_HISTORY_RECORDS, page.total());
            assertTrue(page.records().get(0).sequenceId() > 1);
            assertEquals(1, store.historyReads);
            assertEquals(1, store.recordsRead);
        }
@@ -182,7 +182,7 @@
        {
            assertTrue(store.historyReads > 1);
            assertEquals(1000, store.maximumRequestedLimit);
            assertEquals(60_012, store.recordsRead);
            assertEquals(BtsSimulatorStateStore.MAX_HISTORY_RECORDS, store.recordsRead);
        }
    }