From 9c0fd80309b277420c492180ae9f98f779f0f5c8 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期日, 30 八月 2026 23:29:33 +0800
Subject: [PATCH] 实现设备自然结束与区间补采恢复

---
 ruoyi-admin/src/main/java/com/ruoyi/config/RedisBtsSimulatorStateStore.java |   71 +++++++++++++++++++++++++++--------
 1 files changed, 54 insertions(+), 17 deletions(-)

diff --git a/ruoyi-admin/src/main/java/com/ruoyi/config/RedisBtsSimulatorStateStore.java b/ruoyi-admin/src/main/java/com/ruoyi/config/RedisBtsSimulatorStateStore.java
index 796c15e..cae53ec 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/config/RedisBtsSimulatorStateStore.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/config/RedisBtsSimulatorStateStore.java
@@ -15,6 +15,8 @@
 import com.alibaba.fastjson2.JSONObject;
 import com.ruoyi.device.simulator.BtsSimulatorStateStore;
 import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsDeviceProfile;
+import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsHistoryBatch;
+import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsHistoryRecord;
 import com.ruoyi.device.model.DeviceWorkStatus;
 
 /** 将开发模拟器完整状态写入 Redis,主状态不设置普通 TTL。 */
@@ -25,6 +27,8 @@
     static final String RECENT_REQUESTS_KEY = "dev:bts-simulator:recent-requests:v1";
     static final String HISTORY_KEYS_KEY = "dev:bts-simulator:collector-history-keys:v1";
     static final String HISTORY_KEY_PREFIX = "dev:bts-simulator:collector-history:v1:";
+    static final String BTS_HISTORY_KEYS_KEY = "dev:bts-simulator:bts-history-keys:v1";
+    static final String BTS_HISTORY_KEY_PREFIX = "dev:bts-simulator:bts-history:v1:";
     private static final long RECENT_REQUESTS_TTL_HOURS = 1;
 
     private final RedisTemplate<Object, Object> redisTemplate;
@@ -50,24 +54,26 @@
     @Override
     public SimulatorState resetHistoryForDemoStartup(SimulatorState state)
     {
+        if (pendingEmbeddedHistory.isEmpty()) return state;
+        java.util.Map<String, Integer> embeddedCounts = new java.util.HashMap<>();
+        pendingEmbeddedHistory.forEach(append -> embeddedCounts.put(append.collectorCode(), append.records().size()));
+        List<CollectorState> collectors = state.collectors().stream().map(collector -> {
+            int embeddedCount = embeddedCounts.getOrDefault(collector.deviceCode(), 0);
+            if (embeddedCount == 0) return collector;
+            long remaining = Math.max(0L, collector.historyRecordCount() - embeddedCount);
+            return new CollectorState(collector.collectorId(), collector.deviceCode(), collector.ip(), collector.mac(),
+                    collector.name(), collector.manufacturer(), collector.model(), collector.channelCount(),
+                    collector.online(), collector.sampleIntervalMs(), collector.nextSequence(), collector.lastSampledAt(),
+                    collector.channels(), collector.latestMeasurements().stream()
+                            .filter(item -> collector.deviceCode().equals(item.collectorCode()))
+                            .toList(), remaining, collector.historyFaults());
+        }).toList();
+        // 不删除独立历史 Redis key;这里只阻止旧内嵌数据在本次启动时迁移回来。
         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;
+        SimulatorState cleaned = new SimulatorState(state.version(), state.devices(), collectors, state.nextDeviceId(),
+                state.nextCollectorId(), state.tombstones());
+        save(cleaned);
+        return cleaned;
     }
 
     private SimulatorState restoreState(Object stored)
@@ -176,6 +182,34 @@
     }
 
     @Override
+    public BtsHistoryBatch loadBtsHistoryPage(String historyKey, long offset, int limit)
+    {
+        if (offset < 0 || limit < 1 || limit > MAX_HISTORY_BATCH_SIZE)
+            throw new IllegalArgumentException("invalid BTS history page");
+        String key = BTS_HISTORY_KEY_PREFIX + historyKey;
+        Long size = redisTemplate.opsForList().size(key);
+        long total = size == null ? 0 : size;
+        long from = Math.min(offset, total);
+        int count = (int) Math.min(limit, total - from);
+        List<Object> values = count == 0 ? List.of() : redisTemplate.opsForList().range(key, from, from + count - 1);
+        List<BtsHistoryRecord> records = values == null ? List.of() : values.stream()
+                .map(item -> JSON.parseObject(item.toString(), BtsHistoryRecord.class)).toList();
+        long next = from + records.size();
+        return new BtsHistoryBatch(offset, next, next < total, total, records);
+    }
+
+    @Override
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void appendBtsHistory(String historyKey, BtsHistoryRecord record)
+    {
+        String key = BTS_HISTORY_KEY_PREFIX + historyKey;
+        RedisOperations<Object, Object> operations = redisTemplate;
+        operations.opsForList().rightPush(key, JSON.toJSONString(record));
+        operations.opsForList().trim(key, -MAX_HISTORY_RECORDS, -1);
+        operations.opsForSet().add(BTS_HISTORY_KEYS_KEY, key);
+    }
+
+    @Override
     @SuppressWarnings({ "rawtypes", "unchecked" })
     public void saveAndDeleteCollectorHistory(SimulatorState state, String collectorCode)
     {
@@ -218,6 +252,9 @@
         Set<Object> historyKeys = redisTemplate.opsForSet().members(HISTORY_KEYS_KEY);
         if (historyKeys != null && !historyKeys.isEmpty()) redisTemplate.delete(historyKeys);
         redisTemplate.delete(HISTORY_KEYS_KEY);
+        Set<Object> btsHistoryKeys = redisTemplate.opsForSet().members(BTS_HISTORY_KEYS_KEY);
+        if (btsHistoryKeys != null && !btsHistoryKeys.isEmpty()) redisTemplate.delete(btsHistoryKeys);
+        redisTemplate.delete(BTS_HISTORY_KEYS_KEY);
         redisTemplate.delete(RECENT_REQUESTS_KEY);
         save(defaultState);
     }

--
Gitblit v1.7.1