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 20 @NotNull(message = "设备编号不能为空")
21 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 29 @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED)
27 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 9 */
10 10 public interface RedisKeyConstants {
11 11  
12   - // TODO @芋艿:弱化 deviceKey;使用 product_key + device_name 替代
13 12 /**
14 13 * 设备属性的数据缓存,采用 HASH 结构
15 14 * <p>
16   - * KEY 格式:device_property:{deviceKey}
  15 + * KEY 格式:device_property:{productKey},${deviceName}
17 16 * HASH KEY:identifier 属性标识
18 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 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 22 @Resource
23 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 27 Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(redisKey);
28 28 if (CollUtil.isEmpty(entries)) {
29 29 return Collections.emptyMap();
... ... @@ -33,18 +33,18 @@ public class DevicePropertyRedisDAO {
33 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 37 if (CollUtil.isEmpty(properties)) {
38 38 return;
39 39 }
40   - String redisKey = formatKey(deviceKey);
  40 + String redisKey = formatKey(productKey, deviceName);
41 41 stringRedisTemplate.opsForHash().putAll(redisKey, convertMap(properties.entrySet(),
42 42 Map.Entry::getKey,
43 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 33 List<TDengineTableField> oldFields,
34 34 List<TDengineTableField> newFields) {
35 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 37 List<TDengineTableField> addFields = newFields.stream().filter( // 新增的字段
38 38 newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField())))
39 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 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 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
5 6 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
6 7 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
7 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 12 import lombok.extern.slf4j.Slf4j;
9   -import org.springframework.context.event.EventListener;
10   -import org.springframework.scheduling.annotation.Async;
11 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 18 * @author alwayssuper
19 19 */
20 20 @Component
21 21 @Slf4j
22   -public class IotDevicePropertyMessageConsumer {
  22 +public class IotDevicePropertyMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
23 23  
24 24 @Resource
25 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 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 8 import org.springframework.scheduling.annotation.Async;
9 9 import org.springframework.stereotype.Component;
10 10  
  11 +// TODO @puhui999:后面重构哈
11 12 /**
12 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 128 return;
129 129 }
130 130 // 1. 获得设备信息
131   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(message.getProductKey(), message.getDeviceName());
  131 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
  132 + message.getProductKey(), message.getDeviceName());
132 133 if (device == null) {
133 134 log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message);
134 135 return;
... ... @@ -155,9 +156,9 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
155 156 LocalDateTimeUtil.toEpochMilli(message.getReportTime()));
156 157  
157 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 164 @Override
... ... @@ -166,15 +167,16 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
166 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 173 @Override
173 174 public PageResult<IotDevicePropertyRespVO> getHistoryDevicePropertyPage(IotDevicePropertyHistoryPageReqVO pageReqVO) {
174 175 // 获取设备信息
175 176 IotDeviceDO device = deviceService.validateDeviceExists(pageReqVO.getDeviceId());
176   - pageReqVO.setDeviceKey(device.getDeviceKey());
  177 + pageReqVO.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName());
177 178  
  179 + // 分页查询
178 180 try {
179 181 IPage<IotDevicePropertyRespVO> page = devicePropertyMapper.selectPageByHistory(
180 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 20 </foreach>
21 21 )
22 22 TAGS (
23   - device_key NCHAR(50)
  23 + device_name NCHAR(50)
24 24 )
25 25 </update>
26 26  
... ... @@ -46,9 +46,9 @@
46 46 </update>
47 47  
48 48 <insert id="insert">
49   - INSERT INTO device_property_${device.deviceKey}
  49 + INSERT INTO device_property_${device.productKey}_${device.deviceName}
50 50 USING product_property_${device.productKey}
51   - TAGS ('${device.deviceKey}')
  51 + TAGS ('${device.deviceName}')
52 52 (ts, report_time,
53 53 <foreach item="key" collection="properties.keys" separator=",">
54 54 ${@cn.hutool.core.util.StrUtil@toUnderlineCase(key)}
... ... @@ -66,9 +66,10 @@
66 66 DESCRIBE product_property_${productKey}
67 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 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 73 WHERE ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} IS NOT NULL
73 74 AND ts BETWEEN ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[0])}
74 75 AND ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[1])}
... ...