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/TDetectionCabinetServiceImplTest.java | 137 ++++++++++++++++++++++++++-------------------
1 files changed, 80 insertions(+), 57 deletions(-)
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImplTest.java b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImplTest.java
index 2305e44..9207f29 100644
--- a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImplTest.java
+++ b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImplTest.java
@@ -28,6 +28,11 @@
import com.ruoyi.system.domain.TDetectionCabinet;
import com.ruoyi.system.domain.vo.DiscoveredCabinetVo;
import com.ruoyi.system.mapper.TDetectionCabinetMapper;
+import com.ruoyi.device.discovery.InMemoryDeviceDiscoveryCandidateStore;
+import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore.CabinetCandidate;
+import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore.ChannelCandidate;
+import com.ruoyi.device.model.ChannelIdentity;
+import com.ruoyi.device.model.DeviceWorkStatus;
/**
* 检测柜业务层单元测试,验证字段规范化、状态限制及唯一性校验等关键规则。
@@ -38,8 +43,8 @@
@Mock
private TDetectionCabinetMapper detectionCabinetMapper;
- @InjectMocks
private TDetectionCabinetServiceImpl detectionCabinetService;
+ private InMemoryDeviceDiscoveryCandidateStore candidateStore;
private TDetectionCabinet detectionCabinet;
@@ -49,6 +54,9 @@
@BeforeEach
void setUp()
{
+ candidateStore = new InMemoryDeviceDiscoveryCandidateStore();
+ detectionCabinetService = new TDetectionCabinetServiceImpl(detectionCabinetMapper, candidateStore);
+ candidateStore.recordCabinets(List.of(candidate("CABINET-001", true, DeviceWorkStatus.WORKING)));
detectionCabinet = new TDetectionCabinet();
detectionCabinet.setCabinetCode(" CABINET-001 ");
detectionCabinet.setCabinetName(" 一号检测柜 ");
@@ -67,19 +75,15 @@
@Test
void insertDetectionCabinetShouldPromoteDiscoveredCandidate()
{
- TDetectionCabinet candidate = new TDetectionCabinet();
- candidate.setCabinetId(7L);
- candidate.setCabinetCode("CABINET-001");
- candidate.setMacAddress("AA:BB:CC:DD:EE:FF");
- candidate.setRegistrationStatus("DISCOVERED");
- when(detectionCabinetMapper.selectByCabinetCode("CABINET-001")).thenReturn(candidate);
- when(detectionCabinetMapper.selectByMacAddress("AA:BB:CC:DD:EE:FF")).thenReturn(candidate);
- when(detectionCabinetMapper.registerDiscoveredCabinet(any(TDetectionCabinet.class))).thenReturn(1);
+ when(detectionCabinetMapper.insertDetectionCabinet(any(TDetectionCabinet.class))).thenAnswer(invocation -> {
+ invocation.getArgument(0, TDetectionCabinet.class).setCabinetId(7L);
+ return 1;
+ });
int rows = detectionCabinetService.insertDetectionCabinet(detectionCabinet);
ArgumentCaptor<TDetectionCabinet> captor = ArgumentCaptor.forClass(TDetectionCabinet.class);
- verify(detectionCabinetMapper).registerDiscoveredCabinet(captor.capture());
+ verify(detectionCabinetMapper).insertDetectionCabinet(captor.capture());
TDetectionCabinet savedCabinet = captor.getValue();
assertEquals(1, rows);
assertEquals(7L, savedCabinet.getCabinetId());
@@ -88,13 +92,13 @@
assertEquals("192.168.1.10", savedCabinet.getIpAddress());
assertEquals("AA:BB:CC:DD:EE:FF", savedCabinet.getMacAddress());
assertEquals(new BigDecimal("120.50"), savedCabinet.getCapacity());
- assertNull(savedCabinet.getOnlineStatus());
+ assertEquals("OFFLINE", savedCabinet.getOnlineStatus());
assertEquals("0", savedCabinet.getStatus());
assertNull(savedCabinet.getCurrentTest());
}
/**
- * 验证设备选择查询不读取旧检测柜测试字段,并改由持久化通道状态聚合。
+ * 验证设备选择查询不再从 MySQL 读取发现候选。
*/
@Test
void discoveredCabinetSqlShouldNotReadLegacyTestColumns() throws IOException
@@ -104,17 +108,28 @@
{
assertNotNull(input);
String mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8);
- int selectStart = mapperXml.indexOf("<select id=\"selectDiscoveredCabinetList\"");
- int selectEnd = mapperXml.indexOf("</select>", selectStart);
- String selectSql = mapperXml.substring(selectStart, selectEnd);
- String activeChannelSql = sqlFragment(mapperXml, "activeCabinetChannelExists");
+ assertFalse(mapperXml.contains("selectDiscoveredCabinetList"));
+ assertFalse(mapperXml.contains("registration_status = 'DISCOVERED'"));
+ }
+ }
- assertFalse(selectSql.contains("cabinet.test_status"));
- assertFalse(selectSql.contains("cabinet.current_test"));
- assertTrue(selectSql.contains("activeCabinetChannelExists"));
- assertTrue(activeChannelSql.contains("t_detection_channel"));
- assertTrue(activeChannelSql.contains("work_status"));
- assertTrue(selectSql.contains("registration_status = 'DISCOVERED'"));
+ /**
+ * 验证删除正式检测柜只取消人工登记,不删除适配器持续维护的设备事实。
+ */
+ @Test
+ void deleteDetectionCabinetSqlShouldReturnOnlineDeviceToDiscoveryPool() throws IOException
+ {
+ try (InputStream input = getClass().getResourceAsStream(
+ "/mapper/system/TDetectionCabinetMapper.xml"))
+ {
+ assertNotNull(input);
+ String mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8);
+ int deleteStart = mapperXml.indexOf("<update id=\"deleteDetectionCabinetById\"");
+ int deleteEnd = mapperXml.indexOf("</update>", deleteStart);
+ String deleteSql = mapperXml.substring(deleteStart, deleteEnd);
+
+ assertTrue(deleteSql.contains("set del_flag = cabinet_id"));
+ assertTrue(deleteSql.contains("registration_status = 'REGISTERED'"));
}
}
@@ -132,23 +147,24 @@
int selectStart = mapperXml.indexOf("<sql id=\"selectDetectionCabinetVo\"");
int selectEnd = mapperXml.indexOf("</sql>", selectStart);
String selectSql = mapperXml.substring(selectStart, selectEnd);
- String activeChannelSql = sqlFragment(mapperXml, "activeCabinetChannelExists");
-
assertFalse(selectSql.contains("online_status, test_status"));
assertFalse(selectSql.contains("current_test, status"));
- assertTrue(selectSql.contains("activeCabinetChannelExists"));
- assertTrue(activeChannelSql.contains("t_detection_channel"));
- assertTrue(activeChannelSql.contains("work_status"));
assertTrue(selectSql.contains("as test_status"));
assertTrue(selectSql.contains("as current_test"));
+ String activeTestSql = sqlFragment(mapperXml, "activeCabinetTestExists");
+ assertTrue(activeTestSql.contains("t_test_channel_binding"));
+ assertTrue(activeTestSql.contains("t_detection_channel"));
+ assertTrue(activeTestSql.contains("test_fact.test_status"));
+ assertFalse(activeTestSql.contains("t_test_battery"));
+ assertFalse(activeTestSql.contains("battery_fact.cabinet_id"));
int listStart = mapperXml.indexOf("<select id=\"selectDetectionCabinetList\"");
int listEnd = mapperXml.indexOf("</select>", listStart);
assertTrue(mapperXml.substring(listStart, listEnd).contains("registration_status = 'REGISTERED'"));
- int registerStart = mapperXml.indexOf("<update id=\"registerDiscoveredCabinet\"");
- int registerEnd = mapperXml.indexOf("</update>", registerStart);
+ int registerStart = mapperXml.indexOf("<insert id=\"insertDetectionCabinet\"");
+ int registerEnd = mapperXml.indexOf("</insert>", registerStart);
String registerSql = mapperXml.substring(registerStart, registerEnd);
- assertTrue(registerSql.contains("registration_status = 'REGISTERED'"));
- assertTrue(registerSql.contains("create_by = #{createBy}"));
+ assertTrue(registerSql.contains("'REGISTERED'"));
+ assertTrue(registerSql.contains("#{createBy}"));
}
}
@@ -159,24 +175,25 @@
return mapperXml.substring(start, end);
}
+ private CabinetCandidate candidate(String deviceCode, boolean online, DeviceWorkStatus workStatus)
+ {
+ java.time.Instant observedAt = java.time.Instant.now();
+ List<ChannelCandidate> channels = java.util.stream.IntStream.rangeClosed(1, 8)
+ .mapToObj(channelId -> new ChannelCandidate(
+ new ChannelIdentity("dev-sim", 22, 1, 1, channelId), online, workStatus, observedAt))
+ .toList();
+ return new CabinetCandidate("dev-sim", null, deviceCode, 22, 1, "192.168.1.10",
+ "AA:BB:CC:DD:EE:FF", "一号模拟柜", "开发模拟器", "BTS Virtual Cabinet",
+ channels, observedAt, observedAt.plusSeconds(60));
+ }
+
/**
- * 验证设备选择列表复用已持久化检测柜,并保持原前端字段和状态文本。
+ * 验证设备选择列表读取 Redis 候选,并保持原前端字段和状态文本。
*/
@Test
void selectDiscoveredCabinetListShouldMapPersistedCabinetFacts()
{
- TDetectionCabinet persisted = new TDetectionCabinet();
- persisted.setCabinetCode("CABINET-001");
- persisted.setIpAddress("192.168.1.10");
- persisted.setMacAddress("AA:BB:CC:DD:EE:FF");
- persisted.setCabinetName("一号模拟柜");
- persisted.setManufacturer("开发模拟器");
- persisted.setModel("BTS Virtual Cabinet");
- persisted.setChannelCount(6);
- persisted.setOnlineStatus("ONLINE");
- persisted.setObservedWorkStatus("ACTIVE");
- when(detectionCabinetMapper.selectDiscoveredCabinetList())
- .thenReturn(List.of(persisted));
+ when(detectionCabinetMapper.selectRegisteredCabinetCodes()).thenReturn(List.of());
List<DiscoveredCabinetVo> devices = detectionCabinetService.selectDiscoveredCabinetList();
@@ -190,7 +207,7 @@
assertEquals("一号模拟柜", devices.get(0).getName());
assertEquals("开发模拟器", devices.get(0).getManufacturer());
assertEquals("BTS Virtual Cabinet", devices.get(0).getModel());
- assertEquals(6, devices.get(0).getChannelCount());
+ assertEquals(8, devices.get(0).getChannelCount());
}
/**
@@ -208,34 +225,40 @@
() -> detectionCabinetService.insertDetectionCabinet(detectionCabinet));
assertEquals("设备编码已存在", exception.getMessage());
- verify(detectionCabinetMapper, never()).registerDiscoveredCabinet(any(TDetectionCabinet.class));
+ verify(detectionCabinetMapper, never()).insertDetectionCabinet(any(TDetectionCabinet.class));
}
@Test
void insertDetectionCabinetShouldRequireDiscoveredCandidate()
{
- when(detectionCabinetMapper.selectByCabinetCode("CABINET-001")).thenReturn(null);
+ detectionCabinetService = new TDetectionCabinetServiceImpl(detectionCabinetMapper,
+ new InMemoryDeviceDiscoveryCandidateStore());
ServiceException exception = assertThrows(ServiceException.class,
() -> detectionCabinetService.insertDetectionCabinet(detectionCabinet));
- assertEquals("请先选择发现候选设备", exception.getMessage());
- verify(detectionCabinetMapper, never()).registerDiscoveredCabinet(any());
+ assertEquals("请先选择在线的发现候选设备", exception.getMessage());
+ verify(detectionCabinetMapper, never()).insertDetectionCabinet(any());
}
- /**
- * 验证非法 IP 地址会阻止新增。
- */
@Test
- void insertDetectionCabinetShouldRejectInvalidIpAddress()
+ void insertDetectionCabinetShouldValidateAuthoritativeCandidateTopology()
{
detectionCabinet.setIpAddress("invalid-ip");
+ detectionCabinet.setMacAddress("invalid-mac");
+ detectionCabinet.setChannelCount(99);
+ when(detectionCabinetMapper.insertDetectionCabinet(any(TDetectionCabinet.class))).thenAnswer(invocation -> {
+ invocation.getArgument(0, TDetectionCabinet.class).setCabinetId(7L);
+ return 1;
+ });
- ServiceException exception = assertThrows(ServiceException.class,
- () -> detectionCabinetService.insertDetectionCabinet(detectionCabinet));
+ assertEquals(1, detectionCabinetService.insertDetectionCabinet(detectionCabinet));
- assertEquals("IP地址格式不正确", exception.getMessage());
- verify(detectionCabinetMapper, never()).registerDiscoveredCabinet(any(TDetectionCabinet.class));
+ ArgumentCaptor<TDetectionCabinet> captor = ArgumentCaptor.forClass(TDetectionCabinet.class);
+ verify(detectionCabinetMapper).insertDetectionCabinet(captor.capture());
+ assertEquals("192.168.1.10", captor.getValue().getIpAddress());
+ assertEquals("AA:BB:CC:DD:EE:FF", captor.getValue().getMacAddress());
+ assertEquals(8, captor.getValue().getChannelCount());
}
/**
--
Gitblit v1.7.1