Commit 1498389d260435221c6c143d7f0628c44473efca

Authored by YunaiV
1 parent 0bb01eae

feat:【IoT 物联网】增加(重构) IotDevicePropertyMessageSubscriber 记录设备属性

yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyHistoryPageReqVO.java
@@ -20,8 +20,11 @@ public class IotDevicePropertyHistoryPageReqVO extends PageParam { @@ -20,8 +20,11 @@ public class IotDevicePropertyHistoryPageReqVO extends PageParam {
20 @NotNull(message = "设备编号不能为空") 20 @NotNull(message = "设备编号不能为空")
21 private Long deviceId; 21 private Long deviceId;
22 22
23 - @Schema(description = "设备 Key", hidden = true)  
24 - private String deviceKey; // 非前端传递,后端自己查询设置 23 + @Schema(description = "产品 Key", hidden = true)
  24 + private String productKey; // 非前端传递,后端自己查询设置
  25 +
  26 + @Schema(description = "设备名称", hidden = true)
  27 + private String deviceName; // 非前端传递,后端自己查询设置
25 28
26 @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED) 29 @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED)
27 @NotEmpty(message = "属性标识符不能为空") 30 @NotEmpty(message = "属性标识符不能为空")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java
@@ -9,15 +9,14 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; @@ -9,15 +9,14 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
9 */ 9 */
10 public interface RedisKeyConstants { 10 public interface RedisKeyConstants {
11 11
12 - // TODO @芋艿:弱化 deviceKey;使用 product_key + device_name 替代  
13 /** 12 /**
14 * 设备属性的数据缓存,采用 HASH 结构 13 * 设备属性的数据缓存,采用 HASH 结构
15 * <p> 14 * <p>
16 - * KEY 格式:device_property:{deviceKey} 15 + * KEY 格式:device_property:{productKey},${deviceName}
17 * HASH KEY:identifier 属性标识 16 * HASH KEY:identifier 属性标识
18 * VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO} 17 * VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO}
19 */ 18 */
20 - String DEVICE_PROPERTY = "iot:device_property:%s"; 19 + String DEVICE_PROPERTY = "iot:device_property:%s,%s";
21 20
22 /** 21 /**
23 * 设备的最后上报时间,采用 ZSET 结构 22 * 设备的最后上报时间,采用 ZSET 结构
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java
@@ -22,8 +22,8 @@ public class DevicePropertyRedisDAO { @@ -22,8 +22,8 @@ public class DevicePropertyRedisDAO {
22 @Resource 22 @Resource
23 private StringRedisTemplate stringRedisTemplate; 23 private StringRedisTemplate stringRedisTemplate;
24 24
25 - public Map<String, IotDevicePropertyDO> get(String deviceKey) {  
26 - String redisKey = formatKey(deviceKey); 25 + public Map<String, IotDevicePropertyDO> get(String productKey, String deviceName) {
  26 + String redisKey = formatKey(productKey, deviceName);
27 Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(redisKey); 27 Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(redisKey);
28 if (CollUtil.isEmpty(entries)) { 28 if (CollUtil.isEmpty(entries)) {
29 return Collections.emptyMap(); 29 return Collections.emptyMap();
@@ -33,18 +33,18 @@ public class DevicePropertyRedisDAO { @@ -33,18 +33,18 @@ public class DevicePropertyRedisDAO {
33 entry -> JsonUtils.parseObject((String) entry.getValue(), IotDevicePropertyDO.class)); 33 entry -> JsonUtils.parseObject((String) entry.getValue(), IotDevicePropertyDO.class));
34 } 34 }
35 35
36 - public void putAll(String deviceKey, Map<String, IotDevicePropertyDO> properties) { 36 + public void putAll(String productKey, String deviceName, Map<String, IotDevicePropertyDO> properties) {
37 if (CollUtil.isEmpty(properties)) { 37 if (CollUtil.isEmpty(properties)) {
38 return; 38 return;
39 } 39 }
40 - String redisKey = formatKey(deviceKey); 40 + String redisKey = formatKey(productKey, deviceName);
41 stringRedisTemplate.opsForHash().putAll(redisKey, convertMap(properties.entrySet(), 41 stringRedisTemplate.opsForHash().putAll(redisKey, convertMap(properties.entrySet(),
42 Map.Entry::getKey, 42 Map.Entry::getKey,
43 entry -> JsonUtils.toJsonString(entry.getValue()))); 43 entry -> JsonUtils.toJsonString(entry.getValue())));
44 } 44 }
45 45
46 - private static String formatKey(String deviceKey) {  
47 - return String.format(DEVICE_PROPERTY, deviceKey); 46 + private static String formatKey(String productKey, String deviceName) {
  47 + return String.format(DEVICE_PROPERTY, productKey, deviceName);
48 } 48 }
49 49
50 } 50 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java
@@ -33,7 +33,7 @@ public interface IotDevicePropertyMapper { @@ -33,7 +33,7 @@ public interface IotDevicePropertyMapper {
33 List<TDengineTableField> oldFields, 33 List<TDengineTableField> oldFields,
34 List<TDengineTableField> newFields) { 34 List<TDengineTableField> newFields) {
35 oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(), 35 oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(),
36 - TDengineTableField.FIELD_TS, "report_time", "device_key")); 36 + TDengineTableField.FIELD_TS, "report_time", "device_name"));
37 List<TDengineTableField> addFields = newFields.stream().filter( // 新增的字段 37 List<TDengineTableField> addFields = newFields.stream().filter( // 新增的字段
38 newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) 38 newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField())))
39 .collect(Collectors.toList()); 39 .collect(Collectors.toList());
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageSubscriber.java
1 package cn.iocoder.yudao.module.iot.mq.consumer.device; 1 package cn.iocoder.yudao.module.iot.mq.consumer.device;
2 2
3 -import cn.hutool.core.util.ObjectUtil; 3 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  4 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; 6 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
6 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; 7 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
7 import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; 8 import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
  9 +import com.google.common.base.Objects;
  10 +import jakarta.annotation.PostConstruct;
  11 +import jakarta.annotation.Resource;
8 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
9 -import org.springframework.context.event.EventListener;  
10 -import org.springframework.scheduling.annotation.Async;  
11 import org.springframework.stereotype.Component; 13 import org.springframework.stereotype.Component;
12 14
13 -import jakarta.annotation.Resource;  
14 -  
15 /** 15 /**
16 - * 针对 {@link IotDeviceMessage} 的消费者,记录设备属性 16 + * 针对 {@link IotDeviceMessage} 的消费者:记录设备属性
17 * 17 *
18 * @author alwayssuper 18 * @author alwayssuper
19 */ 19 */
20 @Component 20 @Component
21 @Slf4j 21 @Slf4j
22 -public class IotDevicePropertyMessageConsumer { 22 +public class IotDevicePropertyMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
23 23
24 @Resource 24 @Resource
25 private IotDevicePropertyService deviceDataService; 25 private IotDevicePropertyService deviceDataService;
26 26
27 - @EventListener  
28 - @Async 27 + @Resource
  28 + private IotMessageBus messageBus;
  29 +
  30 + @PostConstruct
  31 + public void init() {
  32 + messageBus.register(this);
  33 + }
  34 +
  35 + @Override
  36 + public String getTopic() {
  37 + return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC;
  38 + }
  39 +
  40 + @Override
  41 + public String getGroup() {
  42 + return "iot_device_property_consumer";
  43 + }
  44 +
  45 + @Override
29 public void onMessage(IotDeviceMessage message) { 46 public void onMessage(IotDeviceMessage message) {
30 - if (ObjectUtil.notEqual(message.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())  
31 - || ObjectUtil.notEqual(message.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier())) {  
32 - return; 47 + if (Objects.equal(message.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())
  48 + && Objects.equal(message.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier())) {
  49 + // 保存设备属性
  50 + deviceDataService.saveDeviceProperty(message);
33 } 51 }
34 - log.info("[onMessage][消息内容({})]", message);  
35 -  
36 - // 保存设备属性  
37 - deviceDataService.saveDeviceProperty(message);  
38 } 52 }
39 53
40 } 54 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java
@@ -8,6 +8,7 @@ import org.springframework.context.event.EventListener; @@ -8,6 +8,7 @@ import org.springframework.context.event.EventListener;
8 import org.springframework.scheduling.annotation.Async; 8 import org.springframework.scheduling.annotation.Async;
9 import org.springframework.stereotype.Component; 9 import org.springframework.stereotype.Component;
10 10
  11 +// TODO @puhui999:后面重构哈
11 /** 12 /**
12 * 针对 {@link IotDeviceMessage} 的消费者,处理规则场景 13 * 针对 {@link IotDeviceMessage} 的消费者,处理规则场景
13 * 14 *
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java
@@ -128,7 +128,8 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { @@ -128,7 +128,8 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
128 return; 128 return;
129 } 129 }
130 // 1. 获得设备信息 130 // 1. 获得设备信息
131 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(message.getProductKey(), message.getDeviceName()); 131 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
  132 + message.getProductKey(), message.getDeviceName());
132 if (device == null) { 133 if (device == null) {
133 log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message); 134 log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message);
134 return; 135 return;
@@ -155,9 +156,9 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { @@ -155,9 +156,9 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
155 LocalDateTimeUtil.toEpochMilli(message.getReportTime())); 156 LocalDateTimeUtil.toEpochMilli(message.getReportTime()));
156 157
157 // 3.2 保存设备属性【日志】 158 // 3.2 保存设备属性【日志】
158 - // TODO @芋艿:这里要调整下;  
159 - deviceDataRedisDAO.putAll(device.getDeviceKey(), convertMap(properties.entrySet(), Map.Entry::getKey,  
160 - entry -> IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build())); 159 + Map<String, IotDevicePropertyDO> properties2 = convertMap(properties.entrySet(), Map.Entry::getKey, entry ->
  160 + IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build());
  161 + deviceDataRedisDAO.putAll(device.getProductKey(), device.getDeviceName(), properties2);
161 } 162 }
162 163
163 @Override 164 @Override
@@ -166,15 +167,16 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { @@ -166,15 +167,16 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
166 IotDeviceDO device = deviceService.validateDeviceExists(deviceId); 167 IotDeviceDO device = deviceService.validateDeviceExists(deviceId);
167 168
168 // 获得设备属性 169 // 获得设备属性
169 - return deviceDataRedisDAO.get(device.getDeviceKey()); 170 + return deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName());
170 } 171 }
171 172
172 @Override 173 @Override
173 public PageResult<IotDevicePropertyRespVO> getHistoryDevicePropertyPage(IotDevicePropertyHistoryPageReqVO pageReqVO) { 174 public PageResult<IotDevicePropertyRespVO> getHistoryDevicePropertyPage(IotDevicePropertyHistoryPageReqVO pageReqVO) {
174 // 获取设备信息 175 // 获取设备信息
175 IotDeviceDO device = deviceService.validateDeviceExists(pageReqVO.getDeviceId()); 176 IotDeviceDO device = deviceService.validateDeviceExists(pageReqVO.getDeviceId());
176 - pageReqVO.setDeviceKey(device.getDeviceKey()); 177 + pageReqVO.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName());
177 178
  179 + // 分页查询
178 try { 180 try {
179 IPage<IotDevicePropertyRespVO> page = devicePropertyMapper.selectPageByHistory( 181 IPage<IotDevicePropertyRespVO> page = devicePropertyMapper.selectPageByHistory(
180 new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); 182 new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO);
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 </foreach> 20 </foreach>
21 ) 21 )
22 TAGS ( 22 TAGS (
23 - device_key NCHAR(50) 23 + device_name NCHAR(50)
24 ) 24 )
25 </update> 25 </update>
26 26
@@ -46,9 +46,9 @@ @@ -46,9 +46,9 @@
46 </update> 46 </update>
47 47
48 <insert id="insert"> 48 <insert id="insert">
49 - INSERT INTO device_property_${device.deviceKey} 49 + INSERT INTO device_property_${device.productKey}_${device.deviceName}
50 USING product_property_${device.productKey} 50 USING product_property_${device.productKey}
51 - TAGS ('${device.deviceKey}') 51 + TAGS ('${device.deviceName}')
52 (ts, report_time, 52 (ts, report_time,
53 <foreach item="key" collection="properties.keys" separator=","> 53 <foreach item="key" collection="properties.keys" separator=",">
54 ${@cn.hutool.core.util.StrUtil@toUnderlineCase(key)} 54 ${@cn.hutool.core.util.StrUtil@toUnderlineCase(key)}
@@ -66,9 +66,10 @@ @@ -66,9 +66,10 @@
66 DESCRIBE product_property_${productKey} 66 DESCRIBE product_property_${productKey}
67 </select> 67 </select>
68 68
69 - <select id="selectPageByHistory" resultType="cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO"> 69 + <select id="selectPageByHistory"
  70 + resultType="cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO">
70 SELECT ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} AS `value`, ts AS update_time 71 SELECT ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} AS `value`, ts AS update_time
71 - FROM device_property_${reqVO.deviceKey} 72 + FROM device_property_${reqVO.productKey}_${reqVO.deviceName}
72 WHERE ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} IS NOT NULL 73 WHERE ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} IS NOT NULL
73 AND ts BETWEEN ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[0])} 74 AND ts BETWEEN ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[0])}
74 AND ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[1])} 75 AND ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[1])}