Commit 0caf6464028614824ab4dc7c491815e5b45897d7

Authored by YunaiV
1 parent 50adebd1

fix(iot): 使用复合主键修复设备消息日志同毫秒覆盖问题

- 将 device_message 改为 ts + id 复合主键,保留同毫秒的不同消息
- 消息 id 为空时生成 UUID,并补充兜底单测
- 查询按 ts、id 倒序排序,保证同毫秒数据分页稳定

验证:消息服务 14 个单测通过,真实 TDengine 3.3.8.4 集成验证通过
注意:已有消息表需单独迁移,服务端需支持 VARCHAR 复合键

相关讨论:https://t.zsxq.com/YP2ao
相关 PR:https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1598
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java
... ... @@ -95,6 +95,9 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
95 95 if (messageDO.getData() != null) {
96 96 messageDO.setData(JsonUtils.toJsonString(messageDO.getData()));
97 97 }
  98 + if (StrUtil.isEmpty(messageDO.getId())) {
  99 + messageDO.setId(IotDeviceMessageUtils.generateMessageId());
  100 + }
98 101 if (messageDO.getTs() == null) {
99 102 messageDO.setTs(System.currentTimeMillis());
100 103 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml
... ... @@ -7,7 +7,7 @@
7 7 <update id="createSTable">
8 8 CREATE STABLE IF NOT EXISTS device_message (
9 9 ts TIMESTAMP,
10   - id NCHAR(50),
  10 + id VARCHAR(50) COMPOSITE KEY,
11 11 report_time TIMESTAMP,
12 12 tenant_id BIGINT,
13 13 server_id NCHAR(50),
... ... @@ -63,7 +63,7 @@
63 63 AND identifier = #{reqVO.identifier}
64 64 </if>
65 65 </where>
66   - ORDER BY ts DESC
  66 + ORDER BY ts DESC, id DESC
67 67 </select>
68 68  
69 69 <select id="selectListByRequestIdsAndReply" resultType="cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO">
... ... @@ -76,7 +76,7 @@
76 76 <foreach collection="requestIds" item="requestId" open="(" close=")" separator=",">
77 77 #{requestId}
78 78 </foreach>
79   - ORDER BY ts DESC
  79 + ORDER BY ts DESC, id DESC
80 80 </select>
81 81  
82 82 <select id="selectCountByCreateTime" resultType="Long">
... ... @@ -106,4 +106,4 @@
106 106 GROUP BY TIMETRUNCATE(ts, 1h)
107 107 </select>
108 108  
109   -</mapper>
110 109 \ No newline at end of file
  110 +</mapper>
... ...
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImplTest.java
... ... @@ -104,6 +104,21 @@ public class IotDeviceMessageServiceImplTest extends BaseMockitoUnitTest {
104 104 }
105 105  
106 106 @Test
  107 + public void testCreateDeviceLogAsync_idFallback_whenBlank() {
  108 + // 准备:构造一条 id 为空的消息,复合主键字段不能为 null
  109 + IotDeviceMessage message = buildMessage(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())
  110 + .setId(null);
  111 +
  112 + // 调用
  113 + service.createDeviceLogAsync(message);
  114 +
  115 + // 断言:mapper.insert 接收到的 messageDO 已补充 id
  116 + ArgumentCaptor<IotDeviceMessageDO> captor = ArgumentCaptor.forClass(IotDeviceMessageDO.class);
  117 + verify(deviceMessageMapper).insert(captor.capture());
  118 + assertNotNull(captor.getValue().getId(), "id 不应为空");
  119 + }
  120 +
  121 + @Test
107 122 public void testCreateDeviceLogAsync_swallowMapperException() {
108 123 // 准备:mapper.insert 抛异常,验证 @Async 方法内部 try/catch 兜底,不向上抛
109 124 IotDeviceMessage message = buildMessage(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
... ...