Commit b37814ec9c31916afae4d4cab6a28559ff9f8e7a

Authored by YunaiV
1 parent c3499af5

reactor:【IoT 物联网】调整物模型相关的时序表,通过 productId 和 deviceId 区分

yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyHistoryPageReqVO.java
... ... @@ -20,12 +20,6 @@ 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 productKey; // 非前端传递,后端自己查询设置
25   -
26   - @Schema(description = "设备名称", hidden = true)
27   - private String deviceName; // 非前端传递,后端自己查询设置
28   -
29 23 @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED)
30 24 @NotEmpty(message = "属性标识符不能为空")
31 25 private String identifier;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java
... ... @@ -50,10 +50,6 @@ public interface IotThingModelMapper extends BaseMapperX<IotThingModelDO> {
50 50 return selectList(IotThingModelDO::getProductId, productId);
51 51 }
52 52  
53   - default List<IotThingModelDO> selectListByProductKey(String productKey) {
54   - return selectList(IotThingModelDO::getProductKey, productKey);
55   - }
56   -
57 53 default List<IotThingModelDO> selectListByProductIdAndType(Long productId, Integer type) {
58 54 return selectList(IotThingModelDO::getProductId, productId,
59 55 IotThingModelDO::getType, type);
... ...
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:{deviceId}
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:%d";
21 20  
22 21 /**
23 22 * 设备的最后上报时间,采用 ZSET 结构
... ... @@ -31,7 +30,7 @@ public interface RedisKeyConstants {
31 30 * 设备关联的网关 serverId 缓存,采用 HASH 结构
32 31 *
33 32 * KEY 格式:device_server_id
34   - * HASH KEY:{productKey},{deviceName}
  33 + * HASH KEY:{deviceId}
35 34 * VALUE 数据类型:String serverId
36 35 */
37 36 String DEVICE_SERVER_ID = "iot:device_server_id";
... ... @@ -56,7 +55,7 @@ public interface RedisKeyConstants {
56 55 /**
57 56 * 物模型的数据缓存,使用 Spring Cache 操作(忽略租户)
58 57 *
59   - * KEY 格式:thing_model_${productKey}
  58 + * KEY 格式:thing_model_${productId}
60 59 * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表
61 60 */
62 61 String THING_MODEL_LIST = "iot:thing_model_list";
... ...
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 productKey, String deviceName) {
26   - String redisKey = formatKey(productKey, deviceName);
  25 + public Map<String, IotDevicePropertyDO> get(Long id) {
  26 + String redisKey = formatKey(id);
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 productKey, String deviceName, Map<String, IotDevicePropertyDO> properties) {
  36 + public void putAll(Long id, Map<String, IotDevicePropertyDO> properties) {
37 37 if (CollUtil.isEmpty(properties)) {
38 38 return;
39 39 }
40   - String redisKey = formatKey(productKey, deviceName);
  40 + String redisKey = formatKey(id);
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 productKey, String deviceName) {
47   - return String.format(DEVICE_PROPERTY, productKey, deviceName);
  46 + private static String formatKey(Long id) {
  47 + return String.format(DEVICE_PROPERTY, id);
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
... ... @@ -23,17 +23,17 @@ import java.util.stream.Collectors;
23 23 @InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错
24 24 public interface IotDevicePropertyMapper {
25 25  
26   - List<TDengineTableField> getProductPropertySTableFieldList(@Param("productKey") String productKey);
  26 + List<TDengineTableField> getProductPropertySTableFieldList(@Param("productId") Long productId);
27 27  
28   - void createProductPropertySTable(@Param("productKey") String productKey,
  28 + void createProductPropertySTable(@Param("productId") Long productId,
29 29 @Param("fields") List<TDengineTableField> fields);
30 30  
31 31 @SuppressWarnings("SimplifyStreamApiCallChains") // 保持 JDK8 兼容性
32   - default void alterProductPropertySTable(String productKey,
  32 + default void alterProductPropertySTable(Long productId,
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_name"));
  36 + TDengineTableField.FIELD_TS, "report_time"));
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());
... ... @@ -62,22 +62,22 @@ public interface IotDevicePropertyMapper {
62 62 });
63 63  
64 64 // 执行
65   - addFields.forEach(field -> alterProductPropertySTableAddField(productKey, field));
66   - dropFields.forEach(field -> alterProductPropertySTableDropField(productKey, field));
67   - modifyLengthFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field));
  65 + addFields.forEach(field -> alterProductPropertySTableAddField(productId, field));
  66 + dropFields.forEach(field -> alterProductPropertySTableDropField(productId, field));
  67 + modifyLengthFields.forEach(field -> alterProductPropertySTableModifyField(productId, field));
68 68 modifyTypeFields.forEach(field -> {
69   - alterProductPropertySTableDropField(productKey, field);
70   - alterProductPropertySTableAddField(productKey, field);
  69 + alterProductPropertySTableDropField(productId, field);
  70 + alterProductPropertySTableAddField(productId, field);
71 71 });
72 72 }
73 73  
74   - void alterProductPropertySTableAddField(@Param("productKey") String productKey,
  74 + void alterProductPropertySTableAddField(@Param("productId") Long productId,
75 75 @Param("field") TDengineTableField field);
76 76  
77   - void alterProductPropertySTableModifyField(@Param("productKey") String productKey,
  77 + void alterProductPropertySTableModifyField(@Param("productId") Long productId,
78 78 @Param("field") TDengineTableField field);
79 79  
80   - void alterProductPropertySTableDropField(@Param("productKey") String productKey,
  80 + void alterProductPropertySTableDropField(@Param("productId") Long productId,
81 81 @Param("field") TDengineTableField field);
82 82  
83 83 void insert(@Param("device") IotDeviceDO device,
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java
... ... @@ -20,7 +20,6 @@ import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyMapper;
20 20 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
21 21 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
22 22 import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;
23   -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
24 23 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
25 24 import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
26 25 import com.baomidou.mybatisplus.core.metadata.IPage;
... ... @@ -60,8 +59,6 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
60 59 .build();
61 60  
62 61 @Resource
63   - private IotDeviceService deviceService;
64   - @Resource
65 62 private IotThingModelService thingModelService;
66 63 @Resource
67 64 private IotProductService productService;
... ... @@ -87,7 +84,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
87 84 // 1.2 解析 DB 里的字段
88 85 List<TDengineTableField> oldFields = new ArrayList<>();
89 86 try {
90   - oldFields.addAll(devicePropertyMapper.getProductPropertySTableFieldList(product.getProductKey()));
  87 + oldFields.addAll(devicePropertyMapper.getProductPropertySTableFieldList(product.getId()));
91 88 } catch (Exception e) {
92 89 if (!e.getMessage().contains("Table does not exist")) {
93 90 throw e;
... ... @@ -101,11 +98,11 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
101 98 log.info("[defineDevicePropertyData][productId({}) 没有需要定义的属性]", productId);
102 99 return;
103 100 }
104   - devicePropertyMapper.createProductPropertySTable(product.getProductKey(), newFields);
  101 + devicePropertyMapper.createProductPropertySTable(product.getId(), newFields);
105 102 return;
106 103 }
107 104 // 2.2 情况二:如果是修改的时候,需要更新表
108   - devicePropertyMapper.alterProductPropertySTable(product.getProductKey(), oldFields, newFields);
  105 + devicePropertyMapper.alterProductPropertySTable(product.getId(), oldFields, newFields);
109 106 }
110 107  
111 108 private List<TDengineTableField> buildTableFieldList(List<IotThingModelDO> thingModels) {
... ... @@ -122,16 +119,17 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
122 119  
123 120 @Override
124 121 public void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message) {
125   - if (!(message.getData() instanceof Map)) {
  122 + // TODO @芋艿:这里要改下协议!
  123 + if (!(message.getParams() instanceof Map)) {
126 124 log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message);
127 125 return;
128 126 }
129 127  
130 128 // 1. 根据物模型,拼接合法的属性
131 129 // TODO @芋艿:【待定 004】赋能后,属性到底以 thingModel 为准(ik),还是 db 的表结构为准(tl)?
132   - List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductKeyFromCache(device.getProductKey());
  130 + List<IotThingModelDO> thingModels = thingModelService.getThingModelListByProductIdFromCache(device.getProductId());
133 131 Map<String, Object> properties = new HashMap<>();
134   - ((Map<?, ?>) message.getData()).forEach((key, value) -> {
  132 + ((Map<?, ?>) message.getParams()).forEach((key, value) -> {
135 133 if (CollUtil.findOne(thingModels, thingModel -> thingModel.getIdentifier().equals(key)) == null) {
136 134 log.error("[saveDeviceProperty][消息({}) 的属性({}) 不存在]", message, key);
137 135 return;
... ... @@ -150,25 +148,16 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
150 148 // 2.2 保存设备属性【日志】
151 149 Map<String, IotDevicePropertyDO> properties2 = convertMap(properties.entrySet(), Map.Entry::getKey, entry ->
152 150 IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build());
153   - deviceDataRedisDAO.putAll(device.getProductKey(), device.getDeviceName(), properties2);
  151 + deviceDataRedisDAO.putAll(device.getId(), properties2);
154 152 }
155 153  
156 154 @Override
157 155 public Map<String, IotDevicePropertyDO> getLatestDeviceProperties(Long deviceId) {
158   - // 获取设备信息
159   - IotDeviceDO device = deviceService.validateDeviceExists(deviceId);
160   -
161   - // 获得设备属性
162   - return deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName());
  156 + return deviceDataRedisDAO.get(deviceId);
163 157 }
164 158  
165 159 @Override
166 160 public PageResult<IotDevicePropertyRespVO> getHistoryDevicePropertyPage(IotDevicePropertyHistoryPageReqVO pageReqVO) {
167   - // 获取设备信息
168   - IotDeviceDO device = deviceService.validateDeviceExists(pageReqVO.getDeviceId());
169   - pageReqVO.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName());
170   -
171   - // 分页查询
172 161 try {
173 162 IPage<IotDevicePropertyRespVO> page = devicePropertyMapper.selectPageByHistory(
174 163 new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO);
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java
... ... @@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelS
7 7 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
8 8 import jakarta.validation.Valid;
9 9  
10   -import java.time.LocalDateTime;
11 10 import java.util.List;
12 11  
13 12 /**
... ... @@ -60,10 +59,10 @@ public interface IotThingModelService {
60 59 *
61 60 * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!!
62 61 *
63   - * @param productKey 产品标识
  62 + * @param productId 产品编号
64 63 * @return 产品物模型列表
65 64 */
66   - List<IotThingModelDO> getThingModelListByProductKeyFromCache(String productKey);
  65 + List<IotThingModelDO> getThingModelListByProductIdFromCache(Long productId);
67 66  
68 67 /**
69 68 * 获得产品物模型分页
... ... @@ -81,13 +80,4 @@ public interface IotThingModelService {
81 80 */
82 81 List<IotThingModelDO> getThingModelList(IotThingModelListReqVO reqVO);
83 82  
84   - // TODO @super:用不到,删除下哈。
85   - /**
86   - * 获得物模型数量
87   - *
88   - * @param createTime 创建时间,如果为空,则统计所有物模型数量
89   - * @return 物模型数量
90   - */
91   - Long getThingModelCount(LocalDateTime createTime);
92   -
93 83 }
94 84 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java
... ... @@ -29,7 +29,6 @@ import org.springframework.stereotype.Service;
29 29 import org.springframework.transaction.annotation.Transactional;
30 30 import org.springframework.validation.annotation.Validated;
31 31  
32   -import java.time.LocalDateTime;
33 32 import java.util.*;
34 33  
35 34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -133,10 +132,10 @@ public class IotThingModelServiceImpl implements IotThingModelService {
133 132 }
134 133  
135 134 @Override
136   - @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey")
  135 + @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productId")
137 136 @TenantIgnore // 忽略租户信息,跨租户 productKey 是唯一的
138   - public List<IotThingModelDO> getThingModelListByProductKeyFromCache(String productKey) {
139   - return thingModelMapper.selectListByProductKey(productKey);
  137 + public List<IotThingModelDO> getThingModelListByProductIdFromCache(Long productId) {
  138 + return thingModelMapper.selectListByProductId(productId);
140 139 }
141 140  
142 141 @Override
... ... @@ -364,10 +363,4 @@ public class IotThingModelServiceImpl implements IotThingModelService {
364 363 return SpringUtil.getBean(getClass());
365 364 }
366 365  
367   - // TODO @super:用不到,删除下;
368   - @Override
369   - public Long getThingModelCount(LocalDateTime createTime) {
370   - return thingModelMapper.selectCountByCreateTime(createTime);
371   - }
372   -
373 366 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml
... ... @@ -5,11 +5,11 @@
5 5 <mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyMapper">
6 6  
7 7 <select id="getProductPropertySTableFieldList" resultType="cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField">
8   - DESCRIBE product_property_${productKey}
  8 + DESCRIBE product_property_${productId}
9 9 </select>
10 10  
11 11 <update id="createProductPropertySTable">
12   - CREATE STABLE product_property_${productKey} (
  12 + CREATE STABLE product_property_${productId} (
13 13 ts TIMESTAMP,
14 14 report_time TIMESTAMP,
15 15 <foreach item="field" collection="fields" separator=",">
... ... @@ -20,12 +20,12 @@
20 20 </foreach>
21 21 )
22 22 TAGS (
23   - device_name NCHAR(50)
  23 + device_id BIGINT
24 24 )
25 25 </update>
26 26  
27 27 <update id="alterProductPropertySTableAddField">
28   - ALTER STABLE product_property_${productKey}
  28 + ALTER STABLE product_property_${productId}
29 29 ADD COLUMN ${field.field} ${field.type}
30 30 <if test="field.length != null and field.length > 0">
31 31 (${field.length})
... ... @@ -33,7 +33,7 @@
33 33 </update>
34 34  
35 35 <update id="alterProductPropertySTableModifyField">
36   - ALTER STABLE product_property_${productKey}
  36 + ALTER STABLE product_property_${productId}
37 37 MODIFY COLUMN ${field.field} ${field.type}
38 38 <if test="field.length != null and field.length > 0">
39 39 (${field.length})
... ... @@ -41,14 +41,14 @@
41 41 </update>
42 42  
43 43 <update id="alterProductPropertySTableDropField">
44   - ALTER STABLE product_property_${productKey}
  44 + ALTER STABLE product_property_${productId}
45 45 DROP COLUMN ${field.field}
46 46 </update>
47 47  
48 48 <insert id="insert">
49   - INSERT INTO device_property_${device.productKey}_${device.deviceName}
50   - USING product_property_${device.productKey}
51   - TAGS ('${device.deviceName}')
  49 + INSERT INTO device_property_${device.id}
  50 + USING product_property_${device.productId}
  51 + TAGS ('${device.id}')
52 52 (ts, report_time,
53 53 <foreach item="key" collection="properties.keys" separator=",">
54 54 ${@cn.hutool.core.util.StrUtil@toUnderlineCase(key)}
... ... @@ -63,13 +63,14 @@
63 63 </insert>
64 64  
65 65 <select id="describeSuperTable" resultType="java.util.Map">
66   - DESCRIBE product_property_${productKey}
  66 + DESCRIBE product_property_${productId}
67 67 </select>
68 68  
  69 + <!-- TODO @芋艿:这里查询有点问题 -->
69 70 <select id="selectPageByHistory"
70 71 resultType="cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO">
71 72 SELECT ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} AS `value`, ts AS update_time
72   - FROM device_property_${reqVO.productKey}_${reqVO.deviceName}
  73 + FROM device_property_${reqVO.deviceId}
73 74 WHERE ${@cn.hutool.core.util.StrUtil@toUnderlineCase(reqVO.identifier)} IS NOT NULL
74 75 AND ts BETWEEN ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[0])}
75 76 AND ${@cn.hutool.core.date.LocalDateTimeUtil@toEpochMilli(reqVO.times[1])}
... ...