Commit 3524bfd3b3e1d43d48860e330bf7f098f5763ea4

Authored by puhui999
2 parents 1f9af15e 7b64b7fc

Merge remote-tracking branch 'yudao/feature/iot' into iot

# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java
Showing 31 changed files with 508 additions and 461 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.thingmodel;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * IoT 数据定义的数据类型枚举类
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@AllArgsConstructor
  12 +@Getter
  13 +public enum IotDataSpecsDataTypeEnum {
  14 +
  15 + INT("int"),
  16 + FLOAT("float"),
  17 + DOUBLE("double"),
  18 + ENUM("enum"),
  19 + BOOL("bool"),
  20 + TEXT("text"),
  21 + DATE("date"),
  22 + STRUCT("struct"),
  23 + ARRAY("array");
  24 +
  25 + private final String dataType;
  26 +
  27 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java
1 package cn.iocoder.yudao.module.iot.api.device; 1 package cn.iocoder.yudao.module.iot.api.device;
2 2
3 -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; 3 +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
4 import org.springframework.stereotype.Service; 4 import org.springframework.stereotype.Service;
5 import org.springframework.validation.annotation.Validated; 5 import org.springframework.validation.annotation.Validated;
6 6
@@ -14,7 +14,7 @@ import javax.annotation.Resource; @@ -14,7 +14,7 @@ import javax.annotation.Resource;
14 public class DeviceDataApiImpl implements DeviceDataApi { 14 public class DeviceDataApiImpl implements DeviceDataApi {
15 15
16 @Resource 16 @Resource
17 - private IotDeviceDataService deviceDataService; 17 + private IotDevicePropertyDataService deviceDataService;
18 18
19 @Override 19 @Override
20 public void saveDeviceData(String productKey, String deviceName, String message) { 20 public void saveDeviceData(String productKey, String deviceName, String message) {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java
@@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDevi @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDevi
7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO;
8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; 9 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO;
10 -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; 10 +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
11 import io.swagger.v3.oas.annotations.Operation; 11 import io.swagger.v3.oas.annotations.Operation;
12 import io.swagger.v3.oas.annotations.tags.Tag; 12 import io.swagger.v3.oas.annotations.tags.Tag;
13 import jakarta.annotation.Resource; 13 import jakarta.annotation.Resource;
@@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
29 public class IotDeviceDataController { 29 public class IotDeviceDataController {
30 30
31 @Resource 31 @Resource
32 - private IotDeviceDataService deviceDataService; 32 + private IotDevicePropertyDataService deviceDataService;
33 33
34 // TODO @浩浩:这里的 /latest-list,包括方法名。 34 // TODO @浩浩:这里的 /latest-list,包括方法名。
35 @GetMapping("/latest") 35 @GetMapping("/latest")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java
@@ -31,7 +31,8 @@ public class ThingModelProperty { @@ -31,7 +31,8 @@ public class ThingModelProperty {
31 private String description; 31 private String description;
32 /** 32 /**
33 * 云端可以对该属性进行的操作类型 33 * 云端可以对该属性进行的操作类型
34 - * 关联枚举 {@link IotProductThingModelAccessModeEnum} 34 + *
  35 + * 枚举 {@link IotProductThingModelAccessModeEnum}
35 */ 36 */
36 private String accessMode; 37 private String accessMode;
37 /** 38 /**
@@ -43,6 +44,8 @@ public class ThingModelProperty { @@ -43,6 +44,8 @@ public class ThingModelProperty {
43 private Boolean required; 44 private Boolean required;
44 /** 45 /**
45 * 数据类型,与 dataSpecs 的 dataType 保持一致 46 * 数据类型,与 dataSpecs 的 dataType 保持一致
  47 + *
  48 + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum}
46 */ 49 */
47 private String dataType; 50 private String dataType;
48 /** 51 /**
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java
@@ -20,7 +20,7 @@ public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { @@ -20,7 +20,7 @@ public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs {
20 * 数据长度,单位为字节。取值不能超过 2048。 20 * 数据长度,单位为字节。取值不能超过 2048。
21 * 当 dataType 为 text 时,需传入该参数。 21 * 当 dataType 为 text 时,需传入该参数。
22 */ 22 */
23 - private Long length; 23 + private Integer length;
24 /** 24 /**
25 * 默认值,可选参数,用于存储默认值。 25 * 默认值,可选参数,用于存储默认值。
26 */ 26 */
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java
@@ -16,6 +16,7 @@ public class FieldParser { @@ -16,6 +16,7 @@ public class FieldParser {
16 /** 16 /**
17 * 物模型到td数据类型映射 17 * 物模型到td数据类型映射
18 */ 18 */
  19 + @Deprecated
19 private static final HashMap<String, String> TYPE_MAPPING = new HashMap<>() { 20 private static final HashMap<String, String> TYPE_MAPPING = new HashMap<>() {
20 { 21 {
21 put("INT", "INT"); 22 put("INT", "INT");
@@ -74,6 +75,7 @@ public class FieldParser { @@ -74,6 +75,7 @@ public class FieldParser {
74 /** 75 /**
75 * 获取字段字义 76 * 获取字段字义
76 */ 77 */
  78 + @Deprecated
77 public static String getFieldDefine(TdFieldDO field) { 79 public static String getFieldDefine(TdFieldDO field) {
78 return "`" + field.getFieldName() + "`" + " " 80 return "`" + field.getFieldName() + "`" + " "
79 + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength()) 81 + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength())
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java
@@ -2,13 +2,12 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; @@ -2,13 +2,12 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
5 -import java.util.Set;  
6 -  
7 // TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。 5 // TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。
8 /** 6 /**
9 * 查询DO 7 * 查询DO
10 */ 8 */
11 @Data 9 @Data
  10 +@Deprecated
12 public class SelectDO { 11 public class SelectDO {
13 12
14 // TODO @haoha:database 是个单词 13 // TODO @haoha:database 是个单词
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java
@@ -6,6 +6,7 @@ import java.util.Map; @@ -6,6 +6,7 @@ import java.util.Map;
6 6
7 // TODO @haohao:类似 SelectDO 的想法,只是它是返回。ps:貌似可以在 tdengine 里面,创建一个 query 包,放这种比较特殊的查询和结果对象。dataobject 更多还是实际存储的结构化的 do 7 // TODO @haohao:类似 SelectDO 的想法,只是它是返回。ps:貌似可以在 tdengine 里面,创建一个 query 包,放这种比较特殊的查询和结果对象。dataobject 更多还是实际存储的结构化的 do
8 @Data 8 @Data
  9 +@Deprecated
9 public class SelectVisualDO { 10 public class SelectVisualDO {
10 11
11 /** 12 /**
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java
@@ -6,6 +6,7 @@ import lombok.Data; @@ -6,6 +6,7 @@ import lombok.Data;
6 * tags查询DO 6 * tags查询DO
7 */ 7 */
8 @Data 8 @Data
  9 +@Deprecated
9 public class TagsSelectDO { 10 public class TagsSelectDO {
10 11
11 /** 12 /**
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java
@@ -5,6 +5,7 @@ import lombok.Builder; @@ -5,6 +5,7 @@ import lombok.Builder;
5 import lombok.Data; 5 import lombok.Data;
6 import lombok.NoArgsConstructor; 6 import lombok.NoArgsConstructor;
7 7
  8 +// TODO 芋艿:看看是不是后续简化掉。
8 /** 9 /**
9 * TD 引擎的字段 10 * TD 引擎的字段
10 */ 11 */
@@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor;
12 @NoArgsConstructor 13 @NoArgsConstructor
13 @AllArgsConstructor 14 @AllArgsConstructor
14 @Builder 15 @Builder
  16 +@Deprecated
15 public class TdFieldDO { 17 public class TdFieldDO {
16 18
17 /** 19 /**
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java
@@ -12,6 +12,7 @@ import java.util.List; @@ -12,6 +12,7 @@ import java.util.List;
12 @Data 12 @Data
13 @NoArgsConstructor 13 @NoArgsConstructor
14 @AllArgsConstructor 14 @AllArgsConstructor
  15 +@Deprecated
15 public class TdResponse { 16 public class TdResponse {
16 17
17 public static final int CODE_SUCCESS = 0; 18 public static final int CODE_SUCCESS = 0;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java
@@ -13,6 +13,7 @@ import org.springframework.stereotype.Service; @@ -13,6 +13,7 @@ import org.springframework.stereotype.Service;
13 */ 13 */
14 @Slf4j 14 @Slf4j
15 @Service 15 @Service
  16 +@Deprecated // TODO 芋艿:貌似没用到
16 public class TdRestApi { 17 public class TdRestApi {
17 18
18 @Value("${spring.datasource.dynamic.datasource.tdengine.url}") 19 @Value("${spring.datasource.dynamic.datasource.tdengine.url}")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java
@@ -7,6 +7,7 @@ import lombok.NoArgsConstructor; @@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
7 7
8 import java.util.List; 8 import java.util.List;
9 9
  10 +@Deprecated
10 /** 11 /**
11 * TD 引擎的数据库 12 * TD 引擎的数据库
12 */ 13 */
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java
@@ -16,6 +16,7 @@ import lombok.Builder; @@ -16,6 +16,7 @@ import lombok.Builder;
16 import lombok.Data; 16 import lombok.Data;
17 import lombok.NoArgsConstructor; 17 import lombok.NoArgsConstructor;
18 18
  19 +// TODO @huihui:IotProductThingModelDO => IotThingModelDO
19 /** 20 /**
20 * IoT 产品物模型功能 DO 21 * IoT 产品物模型功能 DO
21 * <p> 22 * <p>
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  9 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
9 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
10 11
11 import java.util.List; 12 import java.util.List;
@@ -47,7 +48,8 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; { @@ -47,7 +48,8 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; {
47 } 48 }
48 49
49 default IotDeviceDO selectByDeviceKey(String deviceKey) { 50 default IotDeviceDO selectByDeviceKey(String deviceKey) {
50 - return selectOne(IotDeviceDO::getDeviceKey, deviceKey); 51 + return selectOne(new LambdaQueryWrapper<IotDeviceDO>()
  52 + .apply("LOWER(device_key) = {0}", deviceKey.toLowerCase()));
51 } 53 }
52 54
53 default List<IotDeviceDO> selectList(Integer deviceType) { 55 default List<IotDeviceDO> selectList(Integer deviceType) {
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; 6 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  8 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
9 10
10 /** 11 /**
@@ -23,7 +24,8 @@ public interface IotProductMapper extends BaseMapperX&lt;IotProductDO&gt; { @@ -23,7 +24,8 @@ public interface IotProductMapper extends BaseMapperX&lt;IotProductDO&gt; {
23 } 24 }
24 25
25 default IotProductDO selectByProductKey(String productKey) { 26 default IotProductDO selectByProductKey(String productKey) {
26 - return selectOne(IotProductDO::getProductKey, productKey); 27 + return selectOne(new LambdaQueryWrapper<IotProductDO>()
  28 + .apply("LOWER(product_key) = {0}", productKey.toLowerCase()));
27 } 29 }
28 30
29 } 31 }
30 \ No newline at end of file 32 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.tdengine;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjectUtil;
  5 +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;
  6 +import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS;
  7 +import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +import org.apache.ibatis.annotations.Param;
  10 +
  11 +import java.util.ArrayList;
  12 +import java.util.List;
  13 +import java.util.stream.Collectors;
  14 +
  15 +@Mapper
  16 +@TDengineDS
  17 +@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错
  18 +public interface IotDevicePropertyDataMapper {
  19 +
  20 + List<TDengineTableField> getProductPropertySTableFieldList(@Param("productKey") String productKey);
  21 +
  22 + void createProductPropertySTable(@Param("productKey") String productKey,
  23 + @Param("fields") List<TDengineTableField> fields);
  24 +
  25 + @SuppressWarnings("SimplifyStreamApiCallChains") // 保持 JDK8 兼容性
  26 + default void alterProductPropertySTable(String productKey,
  27 + List<TDengineTableField> oldFields,
  28 + List<TDengineTableField> newFields) {
  29 + oldFields.removeIf(field -> TDengineTableField.FIELD_TS.equals(field.getField())
  30 + || TDengineTableField.FIELD_DEVICE_KEY.equals(field.getField()));
  31 + List<TDengineTableField> addFields = newFields.stream().filter( // 新增的字段
  32 + newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField())))
  33 + .collect(Collectors.toList());
  34 + List<TDengineTableField> dropFields = oldFields.stream().filter( // 删除的字段
  35 + oldField -> newFields.stream().noneMatch(n -> n.getField().equals(oldField.getField())))
  36 + .collect(Collectors.toList());
  37 + List<TDengineTableField> modifyTypeFields = new ArrayList<>(); // 变更类型的字段
  38 + List<TDengineTableField> modifyLengthFields = new ArrayList<>(); // 变更长度的字段
  39 + newFields.forEach(newField -> {
  40 + TDengineTableField oldField = CollUtil.findOne(oldFields, field -> field.getField().equals(newField.getField()));
  41 + if (oldField == null) {
  42 + return;
  43 + }
  44 + if (ObjectUtil.notEqual(oldField.getType(), newField.getType())) {
  45 + modifyTypeFields.add(newField);
  46 + return;
  47 + }
  48 + if (newField.getLength()!= null) {
  49 + if (newField.getLength() > oldField.getLength()) {
  50 + modifyLengthFields.add(newField);
  51 + } else if (newField.getLength() < oldField.getLength()) {
  52 + // 特殊:TDengine 长度修改时,只允许变长,所以此时认为是修改类型
  53 + modifyTypeFields.add(newField);
  54 + }
  55 + }
  56 + });
  57 +
  58 + // 执行
  59 + addFields.forEach(field -> alterProductPropertySTableAddField(productKey, field));
  60 + dropFields.forEach(field -> alterProductPropertySTableDropField(productKey, field));
  61 + modifyLengthFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field));
  62 + modifyTypeFields.forEach(field -> {
  63 + alterProductPropertySTableDropField(productKey, field);
  64 + alterProductPropertySTableAddField(productKey, field);
  65 + });
  66 + }
  67 +
  68 + void alterProductPropertySTableAddField(@Param("productKey") String productKey,
  69 + @Param("field") TDengineTableField field);
  70 +
  71 + void alterProductPropertySTableModifyField(@Param("productKey") String productKey,
  72 + @Param("field") TDengineTableField field);
  73 +
  74 + void alterProductPropertySTableDropField(@Param("productKey") String productKey,
  75 + @Param("field") TDengineTableField field);
  76 +
  77 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java
@@ -96,7 +96,6 @@ public interface TdEngineDDLMapper { @@ -96,7 +96,6 @@ public interface TdEngineDDLMapper {
96 @TenantIgnore 96 @TenantIgnore
97 void modifyColumnWidthForSuperTable(TdTableDO superTable); 97 void modifyColumnWidthForSuperTable(TdTableDO superTable);
98 98
99 -  
100 /** 99 /**
101 * 修改超级表 - 为超级表添加标签 100 * 修改超级表 - 为超级表添加标签
102 * 101 *
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java
1 package cn.iocoder.yudao.module.iot.emq.service; 1 package cn.iocoder.yudao.module.iot.emq.service;
2 2
3 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
4 -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; 3 +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
5 import jakarta.annotation.Resource; 4 import jakarta.annotation.Resource;
6 import lombok.extern.slf4j.Slf4j; 5 import lombok.extern.slf4j.Slf4j;
7 import org.eclipse.paho.client.mqttv3.MqttClient; 6 import org.eclipse.paho.client.mqttv3.MqttClient;
@@ -21,7 +20,7 @@ import org.springframework.stereotype.Service; @@ -21,7 +20,7 @@ import org.springframework.stereotype.Service;
21 public class EmqxServiceImpl implements EmqxService { 20 public class EmqxServiceImpl implements EmqxService {
22 21
23 @Resource 22 @Resource
24 - private IotDeviceDataService iotDeviceDataService; 23 + private IotDevicePropertyDataService iotDeviceDataService;
25 24
26 // TODO 多线程处理消息 25 // TODO 多线程处理消息
27 @Override 26 @Override
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.tdengine.core;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +/**
  8 + * TDEngine 表字段
  9 + */
  10 +@Data
  11 +@NoArgsConstructor
  12 +@AllArgsConstructor
  13 +public class TDengineTableField {
  14 +
  15 + /**
  16 + * 字段名 - TDengine 默认 ts 字段,默认会被 TDengine 创建
  17 + */
  18 + public static final String FIELD_TS = "ts";
  19 +
  20 + /**
  21 + * 字段名 - 我们系统定义的 device_key 字段,非 TDengine 默认字段
  22 + */
  23 + public static final String FIELD_DEVICE_KEY = "device_key";
  24 +
  25 + public static final String TYPE_TINYINT = "TINYINT";
  26 + public static final String TYPE_INT = "INT";
  27 + public static final String TYPE_FLOAT = "FLOAT";
  28 + public static final String TYPE_DOUBLE = "DOUBLE";
  29 + public static final String TYPE_BOOL = "BOOL";
  30 + public static final String TYPE_NCHAR = "NCHAR";
  31 + public static final String TYPE_TIMESTAMP = "TIMESTAMP";
  32 +
  33 + /**
  34 + * 注释 - TAG 字段
  35 + */
  36 + public static final String NOTE_TAG = "TAG";
  37 +
  38 + /**
  39 + * 字段名
  40 + */
  41 + private String field;
  42 +
  43 + /**
  44 + * 字段类型
  45 + */
  46 + private String type;
  47 +
  48 + /**
  49 + * 字段长度
  50 + */
  51 + private Integer length;
  52 +
  53 + /**
  54 + * 注释
  55 + */
  56 + private String note;
  57 +
  58 + public TDengineTableField(String field, String type) {
  59 + this.field = field;
  60 + this.type = type;
  61 + }
  62 +
  63 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation;
  2 +
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
  4 +
  5 +import java.lang.annotation.*;
  6 +
  7 +/**
  8 + * TDEngine 数据源
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Target({ElementType.TYPE, ElementType.METHOD})
  13 +@Retention(RetentionPolicy.RUNTIME)
  14 +@Documented
  15 +@DS("tdengine")
  16 +public @interface TDengineDS {
  17 +}
0 \ No newline at end of file 18 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java 0 → 100644
  1 +/**
  2 + * iot 模块的 tdengine 拓展封装
  3 + */
  4 +package cn.iocoder.yudao.module.iot.framework.tdengine;
0 \ No newline at end of file 5 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.iot.service.device;  
2 -  
3 -import cn.hutool.core.date.DateUtil;  
4 -import cn.hutool.core.util.StrUtil;  
5 -import cn.hutool.json.JSONObject;  
6 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
7 -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO;  
8 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;  
9 -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;  
10 -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;  
11 -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;  
12 -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO;  
13 -import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;  
14 -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;  
15 -import cn.iocoder.yudao.module.iot.enums.IotConstants;  
16 -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum;  
17 -import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;  
18 -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService;  
19 -import jakarta.annotation.Resource;  
20 -import jakarta.validation.Valid;  
21 -import lombok.extern.slf4j.Slf4j;  
22 -import org.springframework.beans.factory.annotation.Value;  
23 -import org.springframework.stereotype.Service;  
24 -  
25 -import java.time.ZoneId;  
26 -import java.util.ArrayList;  
27 -import java.util.HashMap;  
28 -import java.util.List;  
29 -import java.util.Map;  
30 -  
31 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;  
32 -  
33 -@Slf4j  
34 -@Service  
35 -public class IotDeviceDataServiceImpl implements IotDeviceDataService {  
36 -  
37 - @Value("${spring.datasource.dynamic.datasource.tdengine.url}")  
38 - private String url;  
39 -  
40 - @Resource  
41 - private IotDeviceService deviceService;  
42 - @Resource  
43 - private IotThingModelMessageService thingModelMessageService;  
44 - @Resource  
45 - private IotProductThingModelService thingModelService;  
46 - @Resource  
47 - private TdEngineDMLMapper tdEngineDMLMapper;  
48 -  
49 - @Resource  
50 - private DeviceDataRedisDAO deviceDataRedisDAO;  
51 -  
52 - @Override  
53 - public void saveDeviceData(String productKey, String deviceName, String message) {  
54 - // 1. 根据产品 key 和设备名称,获得设备信息  
55 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(productKey, deviceName);  
56 - // 2. 解析消息,保存数据  
57 - JSONObject jsonObject = new JSONObject(message);  
58 - log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", productKey, deviceName, jsonObject);  
59 - ThingModelMessage thingModelMessage = ThingModelMessage.builder()  
60 - .id(jsonObject.getStr("id"))  
61 - .sys(jsonObject.get("sys"))  
62 - .method(jsonObject.getStr("method"))  
63 - .params(jsonObject.get("params"))  
64 - .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time"))  
65 - .productKey(productKey)  
66 - .deviceName(deviceName)  
67 - .deviceKey(device.getDeviceKey())  
68 - .build();  
69 - thingModelMessageService.saveThingModelMessage(device, thingModelMessage);  
70 - }  
71 -  
72 - @Override  
73 - public List<IotDeviceDataDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) {  
74 - List<IotDeviceDataDO> list = new ArrayList<>();  
75 - // 1. 获取设备信息  
76 - IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());  
77 - // 2. 获取设备属性最新数据  
78 - List<IotProductThingModelDO> thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey());  
79 - thingModelList = filterList(thingModelList, thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType()  
80 - .equals(thingModel.getType()));  
81 -  
82 - // 3. 过滤标识符和属性名称  
83 - if (deviceDataReqVO.getIdentifier() != null) {  
84 - thingModelList = filterList(thingModelList, thingModel -> thingModel.getIdentifier()  
85 - .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase()));  
86 - }  
87 - if (deviceDataReqVO.getName() != null) {  
88 - thingModelList = filterList(thingModelList, thingModel -> thingModel.getName()  
89 - .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase()));  
90 - }  
91 - // 4. 获取设备属性最新数据  
92 - thingModelList.forEach(thingModel -> {  
93 - IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thingModel.getIdentifier());  
94 - if (deviceData == null) {  
95 - deviceData = new IotDeviceDataDO();  
96 - deviceData.setProductKey(device.getProductKey());  
97 - deviceData.setDeviceName(device.getDeviceName());  
98 - deviceData.setIdentifier(thingModel.getIdentifier());  
99 - deviceData.setDeviceId(deviceDataReqVO.getDeviceId());  
100 - deviceData.setThingModelId(thingModel.getId());  
101 - deviceData.setName(thingModel.getName());  
102 - deviceData.setDataType(thingModel.getProperty().getDataType());  
103 - }  
104 - list.add(deviceData);  
105 - });  
106 - return list;  
107 - }  
108 -  
109 - @Override  
110 - public PageResult<Map<String, Object>> getHistoryDeviceProperties(IotDeviceDataPageReqVO deviceDataReqVO) {  
111 - PageResult<Map<String, Object>> pageResult = new PageResult<>();  
112 - // 1. 获取设备信息  
113 - IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());  
114 - // 2. 获取设备属性历史数据  
115 - SelectVisualDO selectVisualDO = new SelectVisualDO();  
116 - selectVisualDO.setDataBaseName(getDatabaseName());  
117 - selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()));  
118 - selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier());  
119 - selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());  
120 - selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());  
121 - Map<String, Object> params = new HashMap<>();  
122 - params.put("rows", deviceDataReqVO.getPageSize());  
123 - params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize());  
124 - selectVisualDO.setParams(params);  
125 - pageResult.setList(tdEngineDMLMapper.selectHistoryDataList(selectVisualDO));  
126 - pageResult.setTotal(tdEngineDMLMapper.selectHistoryCount(selectVisualDO));  
127 - return pageResult;  
128 - }  
129 -  
130 - private String getDatabaseName() {  
131 - return StrUtil.subAfter(url, "/", true);  
132 - }  
133 -  
134 - private static String getDeviceTableName(String productKey, String deviceName) {  
135 - return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName);  
136 - }  
137 -  
138 -}  
139 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java
@@ -9,11 +9,18 @@ import java.util.List; @@ -9,11 +9,18 @@ import java.util.List;
9 import java.util.Map; 9 import java.util.Map;
10 10
11 /** 11 /**
12 - * IoT 设备数据 Service 接口 12 + * IoT 设备【属性】数据 Service 接口
13 * 13 *
14 * @author 芋道源码 14 * @author 芋道源码
15 */ 15 */
16 -public interface IotDeviceDataService { 16 +public interface IotDevicePropertyDataService {
  17 +
  18 + /**
  19 + * 定义设备属性数据的结构
  20 + *
  21 + * @param productId 产品编号
  22 + */
  23 + void defineDevicePropertyData(Long productId);
17 24
18 /** 25 /**
19 * 保存设备数据 26 * 保存设备数据
@@ -40,4 +47,5 @@ public interface IotDeviceDataService { @@ -40,4 +47,5 @@ public interface IotDeviceDataService {
40 * @return 设备属性历史数据 47 * @return 设备属性历史数据
41 */ 48 */
42 PageResult<Map<String, Object>> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO); 49 PageResult<Map<String, Object>> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO);
  50 +
43 } 51 }
44 \ No newline at end of file 52 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.device;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.date.DateUtil;
  5 +import cn.hutool.core.map.MapUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import cn.hutool.json.JSONObject;
  8 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  9 +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO;
  10 +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs;
  11 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
  12 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
  13 +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  14 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO;
  15 +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
  16 +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO;
  17 +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper;
  18 +import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
  19 +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper;
  20 +import cn.iocoder.yudao.module.iot.enums.IotConstants;
  21 +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
  22 +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum;
  23 +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;
  24 +import cn.iocoder.yudao.module.iot.service.product.IotProductService;
  25 +import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
  26 +import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService;
  27 +import jakarta.annotation.Resource;
  28 +import jakarta.validation.Valid;
  29 +import lombok.extern.slf4j.Slf4j;
  30 +import org.springframework.beans.factory.annotation.Value;
  31 +import org.springframework.stereotype.Service;
  32 +
  33 +import java.time.ZoneId;
  34 +import java.util.ArrayList;
  35 +import java.util.HashMap;
  36 +import java.util.List;
  37 +import java.util.Map;
  38 +
  39 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  40 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
  41 +
  42 +/**
  43 + * IoT 设备【属性】数据 Service 实现类
  44 + *
  45 + * @author 芋道源码
  46 + */
  47 +@Service
  48 +@Slf4j
  49 +public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataService {
  50 +
  51 + /**
  52 + * 物模型的数据类型,与 TDengine 数据类型的映射关系
  53 + */
  54 + private static final Map<String, String> TYPE_MAPPING = MapUtil.<String, String>builder()
  55 + .put(IotDataSpecsDataTypeEnum.INT.getDataType(), TDengineTableField.TYPE_INT)
  56 + .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT)
  57 + .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE)
  58 + .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明?
  59 + .put( IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明?
  60 + .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_NCHAR)
  61 + .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP)
  62 + .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!!
  63 + .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!!
  64 + .build();
  65 +
  66 + @Value("${spring.datasource.dynamic.datasource.tdengine.url}")
  67 + private String url;
  68 +
  69 + @Resource
  70 + private IotDeviceService deviceService;
  71 + @Resource
  72 + private IotThingModelMessageService thingModelMessageService;
  73 + @Resource
  74 + private IotProductThingModelService thingModelService;
  75 + @Resource
  76 + private IotProductService productService;
  77 +
  78 + @Resource
  79 + private TdEngineDMLMapper tdEngineDMLMapper;
  80 +
  81 + @Resource
  82 + private DeviceDataRedisDAO deviceDataRedisDAO;
  83 +
  84 + @Resource
  85 + private IotDevicePropertyDataMapper devicePropertyDataMapper;
  86 +
  87 + @Override
  88 + public void defineDevicePropertyData(Long productId) {
  89 + // 1.1 查询产品和物模型
  90 + IotProductDO product = productService.validateProductExists(productId);
  91 + List<IotProductThingModelDO> thingModels = filterList(thingModelService.getProductThingModelListByProductId(productId),
  92 + thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType()));
  93 + // 1.2 解析 DB 里的字段
  94 + List<TDengineTableField> oldFields = new ArrayList<>();
  95 + try {
  96 + oldFields.addAll(devicePropertyDataMapper.getProductPropertySTableFieldList(product.getProductKey()));
  97 + } catch (Exception e) {
  98 + if (!e.getMessage().contains("Table does not exist")) {
  99 + throw e;
  100 + }
  101 + }
  102 +
  103 + // 2.1 情况一:如果是新增的时候,需要创建表
  104 + List<TDengineTableField> newFields = buildTableFieldList(thingModels);
  105 + if (CollUtil.isEmpty(oldFields)) {
  106 + if (CollUtil.isEmpty(newFields)) {
  107 + log.info("[defineDevicePropertyData][productId({}) 没有需要定义的属性]", productId);
  108 + return;
  109 + }
  110 + newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP));
  111 + devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields);
  112 + return;
  113 + }
  114 + // 2.2 情况二:如果是修改的时候,需要更新表
  115 + devicePropertyDataMapper.alterProductPropertySTable(product.getProductKey(), oldFields, newFields);
  116 + }
  117 +
  118 + private List<TDengineTableField> buildTableFieldList(List<IotProductThingModelDO> thingModels) {
  119 + return convertList(thingModels, thingModel -> {
  120 + TDengineTableField field = new TDengineTableField(
  121 + StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写
  122 + TYPE_MAPPING.get(thingModel.getProperty().getDataType()));
  123 + if (thingModel.getProperty().getDataType().equals(IotDataSpecsDataTypeEnum.TEXT.getDataType())) {
  124 + field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength());
  125 + }
  126 + return field;
  127 + });
  128 + }
  129 +
  130 + @Override
  131 + public void saveDeviceData(String productKey, String deviceName, String message) {
  132 + // 1. 根据产品 key 和设备名称,获得设备信息
  133 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(productKey, deviceName);
  134 + // 2. 解析消息,保存数据
  135 + JSONObject jsonObject = new JSONObject(message);
  136 + log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", productKey, deviceName, jsonObject);
  137 + ThingModelMessage thingModelMessage = ThingModelMessage.builder()
  138 + .id(jsonObject.getStr("id"))
  139 + .sys(jsonObject.get("sys"))
  140 + .method(jsonObject.getStr("method"))
  141 + .params(jsonObject.get("params"))
  142 + .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time"))
  143 + .productKey(productKey)
  144 + .deviceName(deviceName)
  145 + .deviceKey(device.getDeviceKey())
  146 + .build();
  147 + thingModelMessageService.saveThingModelMessage(device, thingModelMessage);
  148 + }
  149 +
  150 + @Override
  151 + public List<IotDeviceDataDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) {
  152 + List<IotDeviceDataDO> list = new ArrayList<>();
  153 + // 1. 获取设备信息
  154 + IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
  155 + // 2. 获取设备属性最新数据
  156 + List<IotProductThingModelDO> thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey());
  157 + thingModelList = filterList(thingModelList, thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType()
  158 + .equals(thingModel.getType()));
  159 +
  160 + // 3. 过滤标识符和属性名称
  161 + if (deviceDataReqVO.getIdentifier() != null) {
  162 + thingModelList = filterList(thingModelList, thingModel -> thingModel.getIdentifier()
  163 + .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase()));
  164 + }
  165 + if (deviceDataReqVO.getName() != null) {
  166 + thingModelList = filterList(thingModelList, thingModel -> thingModel.getName()
  167 + .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase()));
  168 + }
  169 + // 4. 获取设备属性最新数据
  170 + thingModelList.forEach(thingModel -> {
  171 + IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thingModel.getIdentifier());
  172 + if (deviceData == null) {
  173 + deviceData = new IotDeviceDataDO();
  174 + deviceData.setProductKey(device.getProductKey());
  175 + deviceData.setDeviceName(device.getDeviceName());
  176 + deviceData.setIdentifier(thingModel.getIdentifier());
  177 + deviceData.setDeviceId(deviceDataReqVO.getDeviceId());
  178 + deviceData.setThingModelId(thingModel.getId());
  179 + deviceData.setName(thingModel.getName());
  180 + deviceData.setDataType(thingModel.getProperty().getDataType());
  181 + }
  182 + list.add(deviceData);
  183 + });
  184 + return list;
  185 + }
  186 +
  187 + @Override
  188 + public PageResult<Map<String, Object>> getHistoryDeviceProperties(IotDeviceDataPageReqVO deviceDataReqVO) {
  189 + PageResult<Map<String, Object>> pageResult = new PageResult<>();
  190 + // 1. 获取设备信息
  191 + IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
  192 + // 2. 获取设备属性历史数据
  193 + SelectVisualDO selectVisualDO = new SelectVisualDO();
  194 + selectVisualDO.setDataBaseName(getDatabaseName());
  195 + selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()));
  196 + selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier());
  197 + selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
  198 + selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
  199 + Map<String, Object> params = new HashMap<>();
  200 + params.put("rows", deviceDataReqVO.getPageSize());
  201 + params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize());
  202 + selectVisualDO.setParams(params);
  203 + pageResult.setList(tdEngineDMLMapper.selectHistoryDataList(selectVisualDO));
  204 + pageResult.setTotal(tdEngineDMLMapper.selectHistoryCount(selectVisualDO));
  205 + return pageResult;
  206 + }
  207 +
  208 + private String getDatabaseName() {
  209 + return StrUtil.subAfter(url, "/", true);
  210 + }
  211 +
  212 + private static String getDeviceTableName(String productKey, String deviceName) {
  213 + return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName);
  214 + }
  215 +
  216 +}
0 \ No newline at end of file 217 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; 10 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
11 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; 11 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
  12 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
12 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; 13 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
13 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 14 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
14 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; 15 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
@@ -60,9 +61,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -60,9 +61,11 @@ public class IotDeviceServiceImpl implements IotDeviceService {
60 throw exception(PRODUCT_NOT_EXISTS); 61 throw exception(PRODUCT_NOT_EXISTS);
61 } 62 }
62 // 1.2 校验设备标识是否唯一 63 // 1.2 校验设备标识是否唯一
63 - if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) {  
64 - throw exception(DEVICE_KEY_EXISTS);  
65 - } 64 + TenantUtils.executeIgnore(() -> {
  65 + if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) {
  66 + throw exception(PRODUCT_KEY_EXISTS);
  67 + }
  68 + });
66 // 1.3 校验设备名称在同一产品下是否唯一 69 // 1.3 校验设备名称在同一产品下是否唯一
67 if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) { 70 if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) {
68 throw exception(DEVICE_NAME_EXISTS); 71 throw exception(DEVICE_NAME_EXISTS);
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
@@ -2,13 +2,14 @@ package cn.iocoder.yudao.module.iot.service.product; @@ -2,13 +2,14 @@ package cn.iocoder.yudao.module.iot.service.product;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
5 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; 6 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;
6 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; 7 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 8 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
8 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; 9 import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper;
9 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; 10 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
  11 +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService;
10 import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; 12 import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
11 -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService;  
12 import com.baomidou.dynamic.datasource.annotation.DSTransactional; 13 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
13 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
14 import org.springframework.context.annotation.Lazy; 15 import org.springframework.context.annotation.Lazy;
@@ -34,18 +35,22 @@ public class IotProductServiceImpl implements IotProductService { @@ -34,18 +35,22 @@ public class IotProductServiceImpl implements IotProductService {
34 private IotProductMapper productMapper; 35 private IotProductMapper productMapper;
35 36
36 @Resource 37 @Resource
37 - @Lazy // 延迟加载,解决循环依赖  
38 - private IotProductThingModelService thingModelFunctionService;  
39 - @Resource  
40 @Lazy // 延迟加载,解决循环依赖 38 @Lazy // 延迟加载,解决循环依赖
41 private IotThingModelMessageService thingModelMessageService; 39 private IotThingModelMessageService thingModelMessageService;
  40 + @Resource
  41 + @Lazy // 延迟加载,解决循环依赖
  42 + private IotDevicePropertyDataService devicePropertyDataService;
42 43
43 @Override 44 @Override
44 public Long createProduct(IotProductSaveReqVO createReqVO) { 45 public Long createProduct(IotProductSaveReqVO createReqVO) {
45 - // 1. 生成 ProductKey  
46 - if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) {  
47 - throw exception(PRODUCT_KEY_EXISTS);  
48 - } 46 + // 1. 校验 ProductKey
  47 + TenantUtils.executeIgnore(() -> {
  48 + // 为什么忽略租户?避免多个租户之间,productKey 重复,导致 TDengine 设备属性表重复
  49 + if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) {
  50 + throw exception(PRODUCT_KEY_EXISTS);
  51 + }
  52 + });
  53 +
49 // 2. 插入 54 // 2. 插入
50 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) 55 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class)
51 .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()); 56 .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus());
@@ -119,8 +124,8 @@ public class IotProductServiceImpl implements IotProductService { @@ -119,8 +124,8 @@ public class IotProductServiceImpl implements IotProductService {
119 // 3. 产品是发布状态 124 // 3. 产品是发布状态
120 if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { 125 if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) {
121 // 3.1 创建产品超级表数据模型 126 // 3.1 创建产品超级表数据模型
122 - thingModelFunctionService.createSuperTableDataModel(id);  
123 - // 3.2 创建物模型日志超级表数据模型 127 + devicePropertyDataService.defineDevicePropertyData(id);
  128 + // 3.2 创建物模型日志超级表数据模型 TODO 待定:message 要不要分;
124 thingModelMessageService.createSuperTable(id); 129 thingModelMessageService.createSuperTable(id);
125 } 130 }
126 productMapper.updateById(updateObj); 131 productMapper.updateById(updateObj);
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java deleted
1 -package cn.iocoder.yudao.module.iot.service.tdengine;  
2 -  
3 -  
4 -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;  
5 -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO;  
6 -  
7 -import java.util.List;  
8 -  
9 -/**  
10 - * IoT 超级表服务,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。  
11 - */  
12 -public interface IotSuperTableService {  
13 -  
14 - /**  
15 - * 创建超级表数据模型  
16 - */  
17 - void createSuperTableDataModel(IotProductDO product, List<IotProductThingModelDO> thingModelList);  
18 -  
19 -}  
20 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java deleted
1 -package cn.iocoder.yudao.module.iot.service.tdengine;  
2 -  
3 -import cn.hutool.core.bean.BeanUtil;  
4 -import cn.hutool.core.collection.CollUtil;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelRespVO;  
7 -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;  
8 -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser;  
9 -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;  
10 -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO;  
11 -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO;  
12 -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper;  
13 -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum;  
14 -import jakarta.annotation.Resource;  
15 -import lombok.extern.slf4j.Slf4j;  
16 -import org.springframework.beans.factory.annotation.Value;  
17 -import org.springframework.stereotype.Service;  
18 -  
19 -import java.util.*;  
20 -import java.util.stream.Collectors;  
21 -  
22 -/**  
23 - * IoT 超级表服务实现类,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。  
24 - */  
25 -@Service  
26 -@Slf4j  
27 -public class IotSuperTableServiceImpl implements IotSuperTableService {  
28 -  
29 - @Resource  
30 - private TdEngineDDLMapper tdEngineDDLMapper;  
31 -  
32 - @Value("${spring.datasource.dynamic.datasource.tdengine.url}")  
33 - private String url;  
34 -  
35 - @Override  
36 - public void createSuperTableDataModel(IotProductDO product, List<IotProductThingModelDO> thingModelList) {  
37 - ThingModelRespVO thingModel = buildThingModel(product, thingModelList);  
38 -  
39 - if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) {  
40 - log.warn("物模型属性列表为空,不创建超级表");  
41 - return;  
42 - }  
43 -  
44 - String superTableName = getSuperTableName(product.getDeviceType(), product.getProductKey());  
45 - String databaseName = getDatabaseName();  
46 -  
47 - List<Map<String, Object>> results = tdEngineDDLMapper.showSuperTables(new TdTableDO(databaseName, superTableName));  
48 - int tableExists = results == null || results.isEmpty() ? 0 : results.size();  
49 - if (tableExists > 0) {  
50 - updateSuperTable(thingModel, product.getDeviceType());  
51 - } else {  
52 - createSuperTable(thingModel, product.getDeviceType());  
53 - }  
54 - }  
55 -  
56 - /**  
57 - * 创建超级表  
58 - */  
59 - private void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) {  
60 - // 解析物模型,获取字段列表  
61 - List<TdFieldDO> schemaFields = new ArrayList<>();  
62 - schemaFields.add(TdFieldDO.builder()  
63 - .fieldName("time")  
64 - .dataType("TIMESTAMP")  
65 - .build());  
66 - schemaFields.addAll(FieldParser.parse(thingModel));  
67 -  
68 - // 设置超级表的标签  
69 - List<TdFieldDO> tagsFields = List.of(  
70 - TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(),  
71 - TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(),  
72 - TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(),  
73 - TdFieldDO.builder().fieldName("device_type").dataType("INT").build()  
74 - );  
75 -  
76 - // 获取超级表的名称和数据库名称  
77 - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey());  
78 - String databaseName = getDatabaseName();  
79 -  
80 - // 创建超级表  
81 - tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields));  
82 - }  
83 -  
84 - /**  
85 - * 更新超级表  
86 - */  
87 - private void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) {  
88 - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey());  
89 - try {  
90 - List<TdFieldDO> oldFields = getTableFields(superTableName);  
91 - List<TdFieldDO> newFields = FieldParser.parse(thingModel);  
92 -  
93 - updateTableFields(superTableName, oldFields, newFields);  
94 - } catch (Exception e) {  
95 - log.error("更新物模型超级表失败: {}", e.getMessage(), e);  
96 - }  
97 - }  
98 -  
99 - /**  
100 - * 获取表的字段信息  
101 - */  
102 - private List<TdFieldDO> getTableFields(String tableName) {  
103 - List<Map<String, Object>> tableDescription = tdEngineDDLMapper.describeSuperTable(new TdTableDO(getDatabaseName(), tableName));  
104 - if (CollUtil.isEmpty(tableDescription)) {  
105 - return Collections.emptyList();  
106 - }  
107 -  
108 - return tableDescription.stream()  
109 - .filter(map -> !"TAG".equals(map.get("note")))  
110 - .filter(map -> !"time".equals(map.get("field")))  
111 - .map(map -> TdFieldDO.builder()  
112 - .fieldName((String) map.get("field"))  
113 - .dataType((String) map.get("type"))  
114 - .dataLength((Integer) map.get("length"))  
115 - .build())  
116 - .collect(Collectors.toList());  
117 - }  
118 -  
119 - /**  
120 - * 更新表的字段,包括新增、修改和删除字段  
121 - */  
122 - private void updateTableFields(String tableName, List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {  
123 - String databaseName = getDatabaseName();  
124 -  
125 - // 获取新增、修改、删除的字段  
126 - List<TdFieldDO> addFields = getAddFields(oldFields, newFields);  
127 - List<TdFieldDO> modifyFields = getModifyFields(oldFields, newFields);  
128 - List<TdFieldDO> dropFields = getDropFields(oldFields, newFields);  
129 -  
130 - // 添加新增字段  
131 - if (CollUtil.isNotEmpty(addFields)) {  
132 - for (TdFieldDO addField : addFields) {  
133 - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder()  
134 - .dataBaseName(databaseName)  
135 - .superTableName(tableName)  
136 - .column(addField)  
137 - .build());  
138 - }  
139 - }  
140 - // 删除旧字段  
141 - if (CollUtil.isNotEmpty(dropFields)) {  
142 - for (TdFieldDO dropField : dropFields) {  
143 - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder()  
144 - .dataBaseName(databaseName)  
145 - .superTableName(tableName)  
146 - .column(dropField)  
147 - .build());  
148 - }  
149 - }  
150 - // 修改字段(先删除再添加)  
151 - if (CollUtil.isNotEmpty(modifyFields)) {  
152 - for (TdFieldDO modifyField : modifyFields) {  
153 - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder()  
154 - .dataBaseName(databaseName)  
155 - .superTableName(tableName)  
156 - .column(modifyField)  
157 - .build());  
158 - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder()  
159 - .dataBaseName(databaseName)  
160 - .superTableName(tableName)  
161 - .column(modifyField)  
162 - .build());  
163 - }  
164 - }  
165 - }  
166 -  
167 - /**  
168 - * 获取需要新增的字段  
169 - */  
170 - private List<TdFieldDO> getAddFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {  
171 - Set<String> oldFieldNames = oldFields.stream()  
172 - .map(TdFieldDO::getFieldName)  
173 - .collect(Collectors.toSet());  
174 - return newFields.stream()  
175 - .filter(f -> !oldFieldNames.contains(f.getFieldName()))  
176 - .collect(Collectors.toList());  
177 - }  
178 -  
179 - /**  
180 - * 获取需要修改的字段  
181 - */  
182 - private List<TdFieldDO> getModifyFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {  
183 - Map<String, TdFieldDO> oldFieldMap = oldFields.stream()  
184 - .collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f));  
185 -  
186 - return newFields.stream()  
187 - .filter(f -> {  
188 - TdFieldDO oldField = oldFieldMap.get(f.getFieldName());  
189 - return oldField != null && (  
190 - !oldField.getDataType().equals(f.getDataType()) ||  
191 - !Objects.equals(oldField.getDataLength(), f.getDataLength())  
192 - );  
193 - })  
194 - .collect(Collectors.toList());  
195 - }  
196 -  
197 - /**  
198 - * 获取需要删除的字段  
199 - */  
200 - private List<TdFieldDO> getDropFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {  
201 - Set<String> newFieldNames = newFields.stream()  
202 - .map(TdFieldDO::getFieldName)  
203 - .collect(Collectors.toSet());  
204 - return oldFields.stream()  
205 - .filter(f -> !"time".equals(f.getFieldName()))  
206 - .filter(f -> !newFieldNames.contains(f.getFieldName()))  
207 - .collect(Collectors.toList());  
208 - }  
209 -  
210 - /**  
211 - * 构建物模型  
212 - */  
213 - private ThingModelRespVO buildThingModel(IotProductDO product, List<IotProductThingModelDO> thingModelList) {  
214 - ThingModelRespVO thingModel = new ThingModelRespVO();  
215 - thingModel.setId(product.getId());  
216 - thingModel.setProductKey(product.getProductKey());  
217 -  
218 - List<ThingModelProperty> properties = thingModelList.stream()  
219 - .filter(item -> IotProductThingModelTypeEnum.PROPERTY.equals(  
220 - IotProductThingModelTypeEnum.valueOfType(item.getType())))  
221 - .map(this::buildThingModelProperty)  
222 - .collect(Collectors.toList());  
223 -  
224 - ThingModelRespVO.Model model = new ThingModelRespVO.Model();  
225 - model.setProperties(properties);  
226 - thingModel.setModel(model);  
227 -  
228 - return thingModel;  
229 - }  
230 -  
231 - /**  
232 - * 构建物模型属性  
233 - */  
234 - private ThingModelProperty buildThingModelProperty(IotProductThingModelDO thingModel) {  
235 - ThingModelProperty property = BeanUtil.copyProperties(thingModel, ThingModelProperty.class);  
236 - property.setDataType(thingModel.getProperty().getDataType());  
237 - return property;  
238 - }  
239 -  
240 - /**  
241 - * 获取数据库名称  
242 - */  
243 - private String getDatabaseName() {  
244 - int index = url.lastIndexOf("/");  
245 - return index != -1 ? url.substring(index + 1) : url;  
246 - }  
247 -  
248 - /**  
249 - * 获取超级表名称  
250 - */  
251 - private String getSuperTableName(Integer deviceType, String productKey) {  
252 - String prefix = switch (deviceType) {  
253 - case 1 -> "gateway_sub_";  
254 - case 2 -> "gateway_";  
255 - default -> "device_";  
256 - };  
257 - return (prefix + productKey).toLowerCase();  
258 - }  
259 -  
260 -}  
261 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java
@@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotProductThingModelMapp @@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotProductThingModelMapp
16 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; 16 import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum;
17 import cn.iocoder.yudao.module.iot.enums.thingmodel.*; 17 import cn.iocoder.yudao.module.iot.enums.thingmodel.*;
18 import cn.iocoder.yudao.module.iot.service.product.IotProductService; 18 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
19 -import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService;  
20 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
21 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
22 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
@@ -44,8 +43,6 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ @@ -44,8 +43,6 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ
44 43
45 @Resource 44 @Resource
46 private IotProductService productService; 45 private IotProductService productService;
47 - @Resource  
48 - private IotSuperTableService superTableService;  
49 46
50 @Override 47 @Override
51 @Transactional(rollbackFor = Exception.class) 48 @Transactional(rollbackFor = Exception.class)
@@ -75,18 +72,6 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ @@ -75,18 +72,6 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ
75 } 72 }
76 73
77 @Override 74 @Override
78 - public void createSuperTableDataModel(Long productId) {  
79 - // 1. 查询产品  
80 - IotProductDO product = productService.getProduct(productId);  
81 -  
82 - // 2. 查询产品的物模型功能列表  
83 - List<IotProductThingModelDO> thingModelList = productThingModelMapper.selectListByProductId(productId);  
84 -  
85 - // 3. 生成 TDengine 的数据模型  
86 - superTableService.createSuperTableDataModel(product, thingModelList);  
87 - }  
88 -  
89 - @Override  
90 @Transactional(rollbackFor = Exception.class) 75 @Transactional(rollbackFor = Exception.class)
91 public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) { 76 public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) {
92 // 1. 校验功能是否存在 77 // 1. 校验功能是否存在
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper
  3 + PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4 + "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5 +<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper">
  6 +
  7 + <select id="getProductPropertySTableFieldList" resultType="cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField">
  8 + DESCRIBE product_property_${productKey}
  9 + </select>
  10 +
  11 + <update id="createProductPropertySTable">
  12 + CREATE STABLE product_property_${productKey}
  13 + <foreach item="field" collection="fields" separator="," open="(" close=")">
  14 + ${field.field} ${field.type}
  15 + <if test="field.length != null and field.length > 0">
  16 + (${field.length})
  17 + </if>
  18 + </foreach>
  19 + TAGS (
  20 + device_key NCHAR(50)
  21 + )
  22 + </update>
  23 +
  24 + <update id="alterProductPropertySTableAddField">
  25 + ALTER STABLE product_property_${productKey}
  26 + ADD COLUMN ${field.field} ${field.type}
  27 + <if test="field.length != null and field.length > 0">
  28 + (${field.length})
  29 + </if>
  30 + </update>
  31 +
  32 + <update id="alterProductPropertySTableModifyField">
  33 + ALTER STABLE product_property_${productKey}
  34 + MODIFY COLUMN ${field.field} ${field.type}
  35 + <if test="field.length != null and field.length > 0">
  36 + (${field.length})
  37 + </if>
  38 + </update>
  39 +
  40 + <update id="alterProductPropertySTableDropField">
  41 + ALTER STABLE product_property_${productKey}
  42 + DROP COLUMN ${field.field}
  43 + </update>
  44 +
  45 +</mapper>
0 \ No newline at end of file 46 \ No newline at end of file