From 210663040bf65ee00b5f86545fdc31bd0356fddf Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期四, 27 八月 2026 16:31:53 +0800
Subject: [PATCH] feat(device): persist simulator discovery in redis

---
 ruoyi-admin/src/test/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorControllerTest.java |   79 ++++++++++++++++++++++++++++++++++++++-
 1 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorControllerTest.java b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorControllerTest.java
index 8d60d0b..518fba3 100644
--- a/ruoyi-admin/src/test/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorControllerTest.java
+++ b/ruoyi-admin/src/test/java/com/ruoyi/web/controller/device/BtsDevelopmentSimulatorControllerTest.java
@@ -4,14 +4,21 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import java.lang.reflect.Method;
 import java.net.InetAddress;
+import java.time.Instant;
+import java.util.List;
 import org.junit.jupiter.api.Test;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.web.bind.annotation.GetMapping;
 import com.ruoyi.common.annotation.Anonymous;
 import com.ruoyi.device.bts.BtsDevelopmentSimulator;
+import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore.CabinetCandidate;
+import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore.ChannelCandidate;
+import com.ruoyi.device.discovery.InMemoryDeviceDiscoveryCandidateStore;
+import com.ruoyi.device.model.ChannelIdentity;
 import com.ruoyi.device.model.DeviceWorkStatus;
 import com.ruoyi.web.config.BtsDevelopmentSimulatorWebConfig;
 
@@ -22,7 +29,7 @@
     {
         try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0))
         {
-            BtsDevelopmentSimulatorController controller = new BtsDevelopmentSimulatorController(simulator);
+            BtsDevelopmentSimulatorController controller = controller(simulator);
 
             controller.updateDevice(1,
                     new BtsDevelopmentSimulatorController.DeviceStateCommand("127.0.0.2", 4, false));
@@ -40,7 +47,7 @@
     {
         try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0))
         {
-            BtsDevelopmentSimulatorController controller = new BtsDevelopmentSimulatorController(simulator);
+            BtsDevelopmentSimulatorController controller = controller(simulator);
 
             var created = controller.createDevice(
                     new BtsDevelopmentSimulatorController.CreateDeviceCommand("127.0.0.8", 6));
@@ -57,6 +64,49 @@
 
             controller.deleteDevice(deviceId);
             assertEquals(1, controller.state().devices().size());
+        }
+    }
+
+    @Test
+    void controllerConfiguresCollectorsSeparatelyFromCabinets() throws Exception
+    {
+        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0))
+        {
+            BtsDevelopmentSimulatorController controller = controller(simulator);
+            var created = controller.createCollector(
+                    new BtsDevelopmentSimulatorController.CreateCollectorCommand("127.0.1.8", 6));
+            int collectorId = created.collectors().stream()
+                    .filter(collector -> collector.ip().equals("127.0.1.8"))
+                    .findFirst().orElseThrow().collectorId();
+
+            controller.updateCollector(collectorId,
+                    new BtsDevelopmentSimulatorController.CollectorStateCommand("127.0.1.9", 3, false));
+
+            assertEquals(1, controller.state().devices().size());
+            assertEquals(2, controller.state().collectors().size());
+            assertFalse(controller.state().collectors().stream()
+                    .filter(collector -> collector.collectorId() == collectorId)
+                    .findFirst().orElseThrow().online());
+
+            controller.deleteCollector(collectorId);
+            assertEquals(1, controller.state().collectors().size());
+            assertEquals(1, controller.state().devices().size());
+        }
+    }
+
+    @Test
+    void virtualIpMustBeUniqueAcrossCabinetsAndCollectors() throws Exception
+    {
+        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0))
+        {
+            BtsDevelopmentSimulatorController controller = controller(simulator);
+
+            assertThrows(org.springframework.web.server.ResponseStatusException.class,
+                    () -> controller.createCollector(
+                            new BtsDevelopmentSimulatorController.CreateCollectorCommand("127.0.0.2", 4)));
+            assertThrows(org.springframework.web.server.ResponseStatusException.class,
+                    () -> controller.createDevice(
+                            new BtsDevelopmentSimulatorController.CreateDeviceCommand("127.0.1.2", 4)));
         }
     }
 
@@ -85,6 +135,31 @@
         assertTrue(BtsDevelopmentSimulatorController.class.isAnnotationPresent(Anonymous.class));
     }
 
+    @Test
+    void resettingSimulatorRemovesExistingDevelopmentCandidates() throws Exception
+    {
+        Instant now = Instant.parse("2026-08-27T00:00:00Z");
+        InMemoryDeviceDiscoveryCandidateStore candidateStore = new InMemoryDeviceDiscoveryCandidateStore();
+        candidateStore.recordCabinets(List.of(new CabinetCandidate("dev-sim", null, "dev-sim-22-8", 22, 8,
+                "127.0.0.8", null, "旧模拟柜", null, null,
+                List.of(new ChannelCandidate(new ChannelIdentity("dev-sim", 22, 8, 1, 1), true,
+                        DeviceWorkStatus.STOPPED, now)), now, now.plusSeconds(30))));
+        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0))
+        {
+            BtsDevelopmentSimulatorController controller =
+                    new BtsDevelopmentSimulatorController(simulator, candidateStore);
+
+            controller.reset();
+
+            assertEquals(List.of(), candidateStore.snapshot(now).cabinets());
+        }
+    }
+
+    private BtsDevelopmentSimulatorController controller(BtsDevelopmentSimulator simulator)
+    {
+        return new BtsDevelopmentSimulatorController(simulator, new InMemoryDeviceDiscoveryCandidateStore());
+    }
+
     private MockHttpServletRequest requestFrom(String remoteAddress)
     {
         MockHttpServletRequest request = new MockHttpServletRequest("GET", "/dev/bts-simulator/state");

--
Gitblit v1.7.1