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/system/service/impl/DeviceAdapterServiceImplTest.java |   59 +++++++++++++++++++++++++++++++----------------------------
 1 files changed, 31 insertions(+), 28 deletions(-)

diff --git a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/DeviceAdapterServiceImplTest.java b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/DeviceAdapterServiceImplTest.java
index 41ee209..8001b0d 100644
--- a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/DeviceAdapterServiceImplTest.java
+++ b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/DeviceAdapterServiceImplTest.java
@@ -27,6 +27,8 @@
 import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.device.api.DeviceAdapter;
 import com.ruoyi.device.api.DeviceAdapterException;
+import com.ruoyi.device.discovery.InMemoryDeviceDiscoveryCandidateStore;
+import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore;
 import com.ruoyi.device.model.ChannelIdentity;
 import com.ruoyi.device.model.DetectedChannel;
 import com.ruoyi.device.model.DetectedDeviceMetadata;
@@ -53,12 +55,14 @@
 
     private DeviceAdapterServiceImpl service;
     private TDeviceAdapter adapter;
+    private InMemoryDeviceDiscoveryCandidateStore candidateStore;
 
     @BeforeEach
     void setUp()
     {
+        candidateStore = new InMemoryDeviceDiscoveryCandidateStore();
         service = new DeviceAdapterServiceImpl(adapterMapper, registry,
-                new DeviceDiscoveryPersistenceService(adapterMapper));
+                new DeviceDiscoveryPersistenceService(candidateStore), candidateStore);
         adapter = new TDeviceAdapter();
         adapter.setAdapterId(7L);
         adapter.setAdapterCode("lab-bts");
@@ -95,6 +99,8 @@
             context.registerBean(DeviceAdapterRegistry.class, () -> org.mockito.Mockito.mock(DeviceAdapterRegistry.class));
             context.registerBean(DeviceDiscoveryPersistenceService.class,
                     () -> org.mockito.Mockito.mock(DeviceDiscoveryPersistenceService.class));
+            context.registerBean(DeviceDiscoveryCandidateStore.class,
+                    () -> org.mockito.Mockito.mock(DeviceDiscoveryCandidateStore.class));
             context.register(DeviceAdapterServiceImpl.class);
 
             context.refresh();
@@ -111,14 +117,13 @@
                 new DetectedChannel(new ChannelIdentity("lab-bts", 22, 1, 2, 3), "192.168.1.35", true),
                 new DetectedChannel(new ChannelIdentity("lab-bts", 22, 1, 2, 4), "192.168.1.35", false)));
         stubRegistryExecution();
-        when(adapterMapper.selectCabinetIdByIdentity(7L, 22, 1)).thenReturn(10L);
-
         assertEquals(2, service.discover(7L).channelCount());
         assertEquals(2, service.discover(7L).channelCount());
 
-        verify(adapterMapper, times(4)).upsertDetectedChannel(any(TDetectionChannel.class));
+        var snapshot = candidateStore.snapshot(Instant.now());
+        assertEquals(1, snapshot.cabinets().size());
+        assertEquals(2, snapshot.cabinets().get(0).channels().size());
         verify(adapterMapper, times(2)).updateDiscoverySuccess(eq(7L), any());
-        verify(adapterMapper, times(2)).updateDetectedCabinet(any(TDetectionCabinet.class));
     }
 
     @Test
@@ -129,21 +134,15 @@
         when(deviceAdapter.discoverChannels()).thenReturn(List.of(new DetectedChannel(identity, "127.0.0.8", true,
                 new DetectedDeviceMetadata("开发设备 8", "开发模拟器", "BTS Virtual Cabinet",
                         "02:44:00:16:00:08"))));
-        when(adapterMapper.selectCabinetIdByIdentity(7L, 22, 8)).thenReturn(null);
-        when(adapterMapper.insertDetectedCabinet(any(TDetectionCabinet.class))).thenAnswer(invocation -> {
-            invocation.getArgument(0, TDetectionCabinet.class).setCabinetId(18L);
-            return 1;
-        });
         stubRegistryExecution();
 
         service.discover(7L);
 
-        var cabinet = org.mockito.ArgumentCaptor.forClass(TDetectionCabinet.class);
-        verify(adapterMapper).insertDetectedCabinet(cabinet.capture());
-        assertEquals("开发设备 8", cabinet.getValue().getCabinetName());
-        assertEquals("开发模拟器", cabinet.getValue().getManufacturer());
-        assertEquals("BTS Virtual Cabinet", cabinet.getValue().getModel());
-        assertEquals("02:44:00:16:00:08", cabinet.getValue().getMacAddress());
+        var cabinet = candidateStore.snapshot(Instant.now()).cabinets().get(0);
+        assertEquals("开发设备 8", cabinet.name());
+        assertEquals("开发模拟器", cabinet.manufacturer());
+        assertEquals("BTS Virtual Cabinet", cabinet.model());
+        assertEquals("02:44:00:16:00:08", cabinet.mac());
     }
 
     @Test
@@ -157,14 +156,12 @@
                 new RealtimeSnapshot(identity, DeviceWorkStatus.WORKING, 1, 2, "cc", null,
                         BigDecimal.ONE, BigDecimal.TEN, null, null, 3L, 2L, null,
                         Instant.parse("2026-08-26T01:02:03Z"), Map.of())));
-        when(adapterMapper.selectCabinetIdByIdentity(7L, 22, 1)).thenReturn(10L);
         stubRegistryExecution();
 
         service.discover(7L);
 
-        var channel = org.mockito.ArgumentCaptor.forClass(TDetectionChannel.class);
-        verify(adapterMapper).upsertDetectedChannel(channel.capture());
-        assertEquals("WORKING", channel.getValue().getWorkStatus());
+        var channel = candidateStore.snapshot(Instant.now()).cabinets().get(0).channels().get(0);
+        assertEquals(DeviceWorkStatus.WORKING, channel.workStatus());
     }
 
     @Test
@@ -174,19 +171,14 @@
         when(deviceAdapter.discoverChannels()).thenReturn(List.of(
                 new DetectedChannel(new ChannelIdentity("lab-bts", 22, 1, 1, 1), "127.0.0.1", false),
                 new DetectedChannel(new ChannelIdentity("lab-bts", 22, 1, 1, 2), "127.0.0.1", false)));
-        when(adapterMapper.selectCabinetIdByIdentity(7L, 22, 1)).thenReturn(10L);
         stubRegistryExecution();
 
         service.discover(7L);
 
-        var cabinet = org.mockito.ArgumentCaptor.forClass(TDetectionCabinet.class);
-        var channels = org.mockito.ArgumentCaptor.forClass(TDetectionChannel.class);
-        verify(adapterMapper).updateDetectedCabinet(cabinet.capture());
-        verify(adapterMapper, times(2)).upsertDetectedChannel(channels.capture());
+        var cabinet = candidateStore.snapshot(Instant.now()).cabinets().get(0);
         verify(deviceAdapter, never()).inquire(any());
-        assertEquals("OFFLINE", cabinet.getValue().getOnlineStatus());
-        assertTrue(channels.getAllValues().stream()
-                .allMatch(channel -> "OFFLINE".equals(channel.getOnlineStatus())));
+        assertEquals(false, cabinet.online());
+        assertTrue(cabinet.channels().stream().noneMatch(channel -> channel.online()));
     }
 
     @Test
@@ -218,6 +210,17 @@
     }
 
     @Test
+    void rejectsReservedDevelopmentSimulatorCodeFromDatabaseConfiguration()
+    {
+        adapter.setAdapterCode("dev-sim");
+
+        assertThrows(ServiceException.class, () -> service.insert(adapter));
+        assertThrows(ServiceException.class, () -> service.ensureManagedAdapter(adapter));
+        verify(adapterMapper, never()).insertDeviceAdapter(any());
+        verify(adapterMapper, never()).upsertManagedDeviceAdapter(any());
+    }
+
+    @Test
     void developmentSimulatorProvisioningIsIdempotentAndReadOnly()
     {
         String mapperXml;

--
Gitblit v1.7.1