From 064b3381df854d0613f0dac1cbc6bd45fcb0db1c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 26 Dec 2024 07:55:15 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91IoT=EF=BC=9A=E5=BC=B1=E5=8C=96=20TdEngineDDLM?= =?UTF-8?q?apper=20=E5=B0=81=E8=A3=85=EF=BC=8C=E7=94=B1=E6=AF=8F=E4=B8=AA?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E7=8B=AC=E7=AB=8B=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thingmodel/IotDataSpecsDataTypeEnum.java | 27 ++ .../iot/api/device/DeviceDataApiImpl.java | 4 +- .../admin/device/IotDeviceDataController.java | 4 +- .../thingmodel/model/ThingModelProperty.java | 5 +- .../ThingModelDateOrTextDataSpecs.java | 2 +- .../dal/dataobject/tdengine/FieldParser.java | 2 + .../iot/dal/dataobject/tdengine/SelectDO.java | 3 +- .../dataobject/tdengine/SelectVisualDO.java | 1 + .../dal/dataobject/tdengine/TagsSelectDO.java | 1 + .../dal/dataobject/tdengine/TdFieldDO.java | 2 + .../dal/dataobject/tdengine/TdResponse.java | 1 + .../dal/dataobject/tdengine/TdRestApi.java | 1 + .../dal/dataobject/tdengine/TdTableDO.java | 1 + .../thingmodel/IotProductThingModelDO.java | 1 + .../tdengine/IotDevicePropertyDataMapper.java | 55 ++++ .../iot/dal/tdengine/TdEngineDDLMapper.java | 1 - .../iot/emq/service/EmqxServiceImpl.java | 5 +- .../tdengine/core/TDengineTableField.java | 63 +++++ .../tdengine/core/annotation/TDengineDS.java | 17 ++ .../iot/framework/tdengine/package-info.java | 4 + ...java => IotDevicePropertyDataService.java} | 12 +- ... => IotDevicePropertyDataServiceImpl.java} | 82 +++++- .../product/IotProductServiceImpl.java | 8 +- .../tdengine/IotSuperTableService.java | 19 -- .../tdengine/IotSuperTableServiceImpl.java | 260 ------------------ .../IotProductThingModelService.java | 7 - .../IotProductThingModelServiceImpl.java | 15 - .../device/IotDevicePropertyDataMapper.xml | 45 +++ 28 files changed, 329 insertions(+), 319 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDeviceDataService.java => IotDevicePropertyDataService.java} (84%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDeviceDataServiceImpl.java => IotDevicePropertyDataServiceImpl.java} (60%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java new file mode 100644 index 0000000000..5133b00385 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.enums.thingmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IoT 数据定义的数据类型枚举类 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum IotDataSpecsDataTypeEnum { + + INT("int"), + FLOAT("float"), + DOUBLE("double"), + ENUM("enum"), + BOOL("bool"), + TEXT("text"), + DATE("date"), + STRUCT("struct"), + ARRAY("array"); + + private final String dataType; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java index d62c20cb54..b4a2a62dba 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.api.device; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -14,7 +14,7 @@ import javax.annotation.Resource; public class DeviceDataApiImpl implements DeviceDataApi { @Resource - private IotDeviceDataService deviceDataService; + private IotDevicePropertyDataService deviceDataService; @Override public void saveDeviceData(String productKey, String deviceName, String message) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 77bc78c665..a626951edb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/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 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; public class IotDeviceDataController { @Resource - private IotDeviceDataService deviceDataService; + private IotDevicePropertyDataService deviceDataService; // TODO @浩浩:这里的 /latest-list,包括方法名。 @GetMapping("/latest") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index 0c9105a8b6..f296cecd62 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -30,7 +30,8 @@ public class ThingModelProperty { private String description; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotProductThingModelAccessModeEnum} + * + * 枚举 {@link IotProductThingModelAccessModeEnum} */ private String accessMode; /** @@ -42,6 +43,8 @@ public class ThingModelProperty { private Boolean required; /** * 数据类型,与 dataSpecs 的 dataType 保持一致 + * + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum} */ private String dataType; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java index 8d5ddff62a..9d2c88ae12 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java +++ b/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 { * 数据长度,单位为字节。取值不能超过 2048。 * 当 dataType 为 text 时,需传入该参数。 */ - private Long length; + private Integer length; /** * 默认值,可选参数,用于存储默认值。 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index d627817791..2e4e0f507e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/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 { /** * 物模型到td数据类型映射 */ + @Deprecated private static final HashMap TYPE_MAPPING = new HashMap<>() { { put("INT", "INT"); @@ -74,6 +75,7 @@ public class FieldParser { /** * 获取字段字义 */ + @Deprecated public static String getFieldDefine(TdFieldDO field) { return "`" + field.getFieldName() + "`" + " " + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength()) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java index 542dd1e7b7..fb4cd459a6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java +++ b/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; import lombok.Data; -import java.util.Set; - // TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。 /** * 查询DO */ @Data +@Deprecated public class SelectDO { // TODO @haoha:database 是个单词 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java index 44acc3ac54..584562bfa6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java +++ b/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; // TODO @haohao:类似 SelectDO 的想法,只是它是返回。ps:貌似可以在 tdengine 里面,创建一个 query 包,放这种比较特殊的查询和结果对象。dataobject 更多还是实际存储的结构化的 do @Data +@Deprecated public class SelectVisualDO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java index 9fae91599d..3538308241 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java +++ b/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; * tags查询DO */ @Data +@Deprecated public class TagsSelectDO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java index 2e17515095..10c9b9fe7d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java +++ b/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; import lombok.Data; import lombok.NoArgsConstructor; +// TODO 芋艿:看看是不是后续简化掉。 /** * TD 引擎的字段 */ @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder +@Deprecated public class TdFieldDO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java index aca5cece5b..8b64f6854d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java +++ b/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; @Data @NoArgsConstructor @AllArgsConstructor +@Deprecated public class TdResponse { public static final int CODE_SUCCESS = 0; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java index b2d1ac0292..1bb793a45a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java +++ b/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; */ @Slf4j @Service +@Deprecated // TODO 芋艿:貌似没用到 public class TdRestApi { @Value("${spring.datasource.dynamic.datasource.tdengine.url}") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java index 3b30352e4c..3265c3ebed 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java +++ b/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; import java.util.List; +@Deprecated /** * TD 引擎的数据库 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java index 3d9e5e5fe0..f2edc5db7f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java +++ b/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; import lombok.Data; import lombok.NoArgsConstructor; +// TODO @huihui:IotProductThingModelDO => IotThingModelDO /** * IoT 产品物模型功能 DO *

diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java new file mode 100644 index 0000000000..5409189286 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.stream.Collectors; + +@Mapper +@TDengineDS +@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 +public interface IotDevicePropertyDataMapper { + + List getProductPropertySTableFieldList(@Param("productKey") String productKey); + + void createProductPropertySTable(@Param("productKey") String productKey, + @Param("fields") List fields); + + @SuppressWarnings("SimplifyStreamApiCallChains") // 保持 JDK8 兼容性 + default void alterProductPropertySTable(String productKey, + List oldFields, + List newFields) { + oldFields.removeIf(field -> TDengineTableField.FIELD_TS.equals(field.getField()) + || TDengineTableField.FIELD_DEVICE_KEY.equals(field.getField())); + List addFields = newFields.stream().filter( // 新增的字段 + newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) + .collect(Collectors.toList()); + List modifyFields = newFields.stream().filter( // 更新的字段 + newField -> oldFields.stream().anyMatch(oldField -> oldField.getField().equals(newField.getField()) + && (ObjectUtil.notEqual(oldField.getType(), newField.getType()) + || (newField.getLength() != null && ObjectUtil.notEqual(oldField.getLength(), newField.getLength()))))) + .collect(Collectors.toList()); + List dropFields = oldFields.stream().filter( // 删除的字段 + oldField -> newFields.stream().noneMatch(n -> n.getField().equals(oldField.getField()))) + .collect(Collectors.toList()); + addFields.forEach(field -> alterProductPropertySTableAddField(productKey, field)); + // TODO 芋艿:tdengine 只允许 modify 长度;如果 type 变化,只能 drop + add + modifyFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field)); + dropFields.forEach(field -> alterProductPropertySTableDropField(productKey, field)); + } + + void alterProductPropertySTableAddField(@Param("productKey") String productKey, + @Param("field") TDengineTableField field); + + void alterProductPropertySTableModifyField(@Param("productKey") String productKey, + @Param("field") TDengineTableField field); + + void alterProductPropertySTableDropField(@Param("productKey") String productKey, + @Param("field") TDengineTableField field); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java index 3d7aa84ddd..eeb061cc55 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java +++ b/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 { @TenantIgnore void modifyColumnWidthForSuperTable(TdTableDO superTable); - /** * 修改超级表 - 为超级表添加标签 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index 343f9ba21c..46217a22bb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.emq.service; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttClient; @@ -21,7 +20,7 @@ import org.springframework.stereotype.Service; public class EmqxServiceImpl implements EmqxService { @Resource - private IotDeviceDataService iotDeviceDataService; + private IotDevicePropertyDataService iotDeviceDataService; // TODO 多线程处理消息 @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java new file mode 100644 index 0000000000..427c681892 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.iot.framework.tdengine.core; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * TDEngine 表字段 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TDengineTableField { + + /** + * 字段名 - TDengine 默认 ts 字段,默认会被 TDengine 创建 + */ + public static final String FIELD_TS = "ts"; + + /** + * 字段名 - 我们系统定义的 device_key 字段,非 TDengine 默认字段 + */ + public static final String FIELD_DEVICE_KEY = "device_key"; + + public static final String TYPE_TINYINT = "TINYINT"; + public static final String TYPE_INT = "INT"; + public static final String TYPE_FLOAT = "FLOAT"; + public static final String TYPE_DOUBLE = "DOUBLE"; + public static final String TYPE_BOOL = "BOOL"; + public static final String TYPE_NCHAR = "NCHAR"; + public static final String TYPE_TIMESTAMP = "TIMESTAMP"; + + /** + * 注释 - TAG 字段 + */ + public static final String NOTE_TAG = "TAG"; + + /** + * 字段名 + */ + private String field; + + /** + * 字段类型 + */ + private String type; + + /** + * 字段长度 + */ + private Integer length; + + /** + * 注释 + */ + private String note; + + public TDengineTableField(String field, String type) { + this.field = field; + this.type = type; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java new file mode 100644 index 0000000000..e3960d026d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * TDEngine 数据源 + * + * @author 芋道源码 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("tdengine") +public @interface TDengineDS { +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java new file mode 100644 index 0000000000..f92428f7b1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java @@ -0,0 +1,4 @@ +/** + * iot 模块的 tdengine 拓展封装 + */ +package cn.iocoder.yudao.module.iot.framework.tdengine; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java similarity index 84% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java index 1c246e2c70..08375cb092 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java +++ b/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; import java.util.Map; /** - * IoT 设备数据 Service 接口 + * IoT 设备【属性】数据 Service 接口 * * @author 芋道源码 */ -public interface IotDeviceDataService { +public interface IotDevicePropertyDataService { + + /** + * 定义设备属性数据的结构 + * + * @param productId 产品编号 + */ + void defineDevicePropertyData(Long productId); /** * 保存设备数据 @@ -40,4 +47,5 @@ public interface IotDeviceDataService { * @return 设备属性历史数据 */ PageResult> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java similarity index 60% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 4cb39c0bc5..e0de0ac3d8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -1,19 +1,27 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import jakarta.annotation.Resource; @@ -28,11 +36,32 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; -@Slf4j +/** + * IoT 设备【属性】数据 Service 实现类 + * + * @author 芋道源码 + */ @Service -public class IotDeviceDataServiceImpl implements IotDeviceDataService { +@Slf4j +public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataService { + + /** + * 物模型的数据类型,与 TDengine 数据类型的映射关系 + */ + private static final Map TYPE_MAPPING = MapUtil.builder() + .put(IotDataSpecsDataTypeEnum.INT.getDataType(), TDengineTableField.TYPE_INT) + .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT) + .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE) + .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? + .put( IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? + .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_NCHAR) + .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP) + .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! + .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! + .build(); @Value("${spring.datasource.dynamic.datasource.tdengine.url}") private String url; @@ -43,12 +72,61 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { private IotThingModelMessageService thingModelMessageService; @Resource private IotProductThingModelService thingModelService; + @Resource + private IotProductService productService; + @Resource private TdEngineDMLMapper tdEngineDMLMapper; @Resource private DeviceDataRedisDAO deviceDataRedisDAO; + @Resource + private IotDevicePropertyDataMapper devicePropertyDataMapper; + + @Override + public void defineDevicePropertyData(Long productId) { + // 1.1 查询产品和物模型 + IotProductDO product = productService.validateProductExists(productId); + List thingModels = filterList(thingModelService.getProductThingModelListByProductId(productId), + thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); + // 1.2 解析 DB 里的字段 + List oldFields = new ArrayList<>(); + try { + oldFields.addAll(devicePropertyDataMapper.getProductPropertySTableFieldList(product.getProductKey())); + } catch (Exception e) { + if (!e.getMessage().contains("Table does not exist")) { + throw e; + } + } + + // 2.1 情况一:如果是新增的时候,需要创建表 + List newFields = buildTableFieldList(thingModels); + if (CollUtil.isEmpty(oldFields)) { + if (CollUtil.isEmpty(newFields)) { + log.info("[defineDevicePropertyData][productId({}) 没有需要定义的属性]", productId); + return; + } + newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP)); + devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); + return; + } + // 2.2 情况二:如果是修改的时候,需要更新表 + devicePropertyDataMapper.alterProductPropertySTable(product.getProductKey(), oldFields, newFields); + } + + private List buildTableFieldList(List thingModels) { + return convertList(thingModels, thingModel -> { + TDengineTableField field = new TDengineTableField( + thingModel.getIdentifier().toLowerCase(), // TODO 芋艿:为什么要转成小写? + TYPE_MAPPING.get(thingModel.getProperty().getDataType())); + if (thingModel.getProperty().getDataType().equals(IotDataSpecsDataTypeEnum.TEXT.getDataType())) { + field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength()); + } + return field; + }); + } + @Override public void saveDeviceData(String productKey, String deviceName, String message) { // 1. 根据产品 key 和设备名称,获得设备信息 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index a53aee55d2..1d2e8892a3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; @@ -39,6 +40,9 @@ public class IotProductServiceImpl implements IotProductService { @Resource @Lazy // 延迟加载,解决循环依赖 private IotThingModelMessageService thingModelMessageService; + @Resource + @Lazy // 延迟加载,解决循环依赖 + private IotDevicePropertyDataService devicePropertyDataService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { @@ -119,8 +123,8 @@ public class IotProductServiceImpl implements IotProductService { // 3. 产品是发布状态 if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { // 3.1 创建产品超级表数据模型 - thingModelFunctionService.createSuperTableDataModel(id); - // 3.2 创建物模型日志超级表数据模型 + devicePropertyDataService.defineDevicePropertyData(id); + // 3.2 创建物模型日志超级表数据模型 TODO 待定:message 要不要分; thingModelMessageService.createSuperTable(id); } productMapper.updateById(updateObj); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java deleted file mode 100644 index bb6d8c0772..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - - -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; - -import java.util.List; - -/** - * IoT 超级表服务,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 - */ -public interface IotSuperTableService { - - /** - * 创建超级表数据模型 - */ - void createSuperTableDataModel(IotProductDO product, List thingModelList); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java deleted file mode 100644 index 1727fae624..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java +++ /dev/null @@ -1,260 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * IoT 超级表服务实现类,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 - */ -@Service -@Slf4j -public class IotSuperTableServiceImpl implements IotSuperTableService { - - @Resource - private TdEngineDDLMapper tdEngineDDLMapper; - - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") - private String url; - - @Override - public void createSuperTableDataModel(IotProductDO product, List thingModelList) { - ThingModelRespVO thingModel = buildThingModel(product, thingModelList); - - if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) { - log.warn("物模型属性列表为空,不创建超级表"); - return; - } - - String superTableName = getSuperTableName(product.getDeviceType(), product.getProductKey()); - String databaseName = getDatabaseName(); - - List> results = tdEngineDDLMapper.showSuperTables(new TdTableDO(databaseName, superTableName)); - int tableExists = results == null || results.isEmpty() ? 0 : results.size(); - if (tableExists > 0) { - updateSuperTable(thingModel, product.getDeviceType()); - } else { - createSuperTable(thingModel, product.getDeviceType()); - } - } - - /** - * 创建超级表 - */ - private void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - // 解析物模型,获取字段列表 - List schemaFields = new ArrayList<>(); - schemaFields.add(TdFieldDO.builder() - .fieldName("time") - .dataType("TIMESTAMP") - .build()); - schemaFields.addAll(FieldParser.parse(thingModel)); - - // 设置超级表的标签 - List tagsFields = List.of( - TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_type").dataType("INT").build() - ); - - // 获取超级表的名称和数据库名称 - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); - String databaseName = getDatabaseName(); - - // 创建超级表 - tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); - } - - /** - * 更新超级表 - */ - private void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); - try { - List oldFields = getTableFields(superTableName); - List newFields = FieldParser.parse(thingModel); - - updateTableFields(superTableName, oldFields, newFields); - } catch (Exception e) { - log.error("更新物模型超级表失败: {}", e.getMessage(), e); - } - } - - /** - * 获取表的字段信息 - */ - private List getTableFields(String tableName) { - List> tableDescription = tdEngineDDLMapper.describeSuperTable(new TdTableDO(getDatabaseName(), tableName)); - if (CollUtil.isEmpty(tableDescription)) { - return Collections.emptyList(); - } - - return tableDescription.stream() - .filter(map -> !"TAG".equals(map.get("note"))) - .filter(map -> !"time".equals(map.get("field"))) - .map(map -> TdFieldDO.builder() - .fieldName((String) map.get("field")) - .dataType((String) map.get("type")) - .dataLength((Integer) map.get("length")) - .build()) - .collect(Collectors.toList()); - } - - /** - * 更新表的字段,包括新增、修改和删除字段 - */ - private void updateTableFields(String tableName, List oldFields, List newFields) { - String databaseName = getDatabaseName(); - - // 获取新增、修改、删除的字段 - List addFields = getAddFields(oldFields, newFields); - List modifyFields = getModifyFields(oldFields, newFields); - List dropFields = getDropFields(oldFields, newFields); - - // 添加新增字段 - if (CollUtil.isNotEmpty(addFields)) { - for (TdFieldDO addField : addFields) { - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(addField) - .build()); - } - } - // 删除旧字段 - if (CollUtil.isNotEmpty(dropFields)) { - for (TdFieldDO dropField : dropFields) { - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(dropField) - .build()); - } - } - // 修改字段(先删除再添加) - if (CollUtil.isNotEmpty(modifyFields)) { - for (TdFieldDO modifyField : modifyFields) { - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(modifyField) - .build()); - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(modifyField) - .build()); - } - } - } - - /** - * 获取需要新增的字段 - */ - private List getAddFields(List oldFields, List newFields) { - Set oldFieldNames = oldFields.stream() - .map(TdFieldDO::getFieldName) - .collect(Collectors.toSet()); - return newFields.stream() - .filter(f -> !oldFieldNames.contains(f.getFieldName())) - .collect(Collectors.toList()); - } - - /** - * 获取需要修改的字段 - */ - private List getModifyFields(List oldFields, List newFields) { - Map oldFieldMap = oldFields.stream() - .collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f)); - - return newFields.stream() - .filter(f -> { - TdFieldDO oldField = oldFieldMap.get(f.getFieldName()); - return oldField != null && ( - !oldField.getDataType().equals(f.getDataType()) || - !Objects.equals(oldField.getDataLength(), f.getDataLength()) - ); - }) - .collect(Collectors.toList()); - } - - /** - * 获取需要删除的字段 - */ - private List getDropFields(List oldFields, List newFields) { - Set newFieldNames = newFields.stream() - .map(TdFieldDO::getFieldName) - .collect(Collectors.toSet()); - return oldFields.stream() - .filter(f -> !"time".equals(f.getFieldName())) - .filter(f -> !newFieldNames.contains(f.getFieldName())) - .collect(Collectors.toList()); - } - - /** - * 构建物模型 - */ - private ThingModelRespVO buildThingModel(IotProductDO product, List thingModelList) { - ThingModelRespVO thingModel = new ThingModelRespVO(); - thingModel.setId(product.getId()); - thingModel.setProductKey(product.getProductKey()); - - List properties = thingModelList.stream() - .filter(item -> IotProductThingModelTypeEnum.PROPERTY.equals( - IotProductThingModelTypeEnum.valueOfType(item.getType()))) - .map(this::buildThingModelProperty) - .collect(Collectors.toList()); - - ThingModelRespVO.Model model = new ThingModelRespVO.Model(); - model.setProperties(properties); - thingModel.setModel(model); - - return thingModel; - } - - /** - * 构建物模型属性 - */ - private ThingModelProperty buildThingModelProperty(IotProductThingModelDO thingModel) { - ThingModelProperty property = BeanUtil.copyProperties(thingModel, ThingModelProperty.class); - property.setDataType(thingModel.getProperty().getDataType()); - return property; - } - - /** - * 获取数据库名称 - */ - private String getDatabaseName() { - int index = url.lastIndexOf("/"); - return index != -1 ? url.substring(index + 1) : url; - } - - /** - * 获取超级表名称 - */ - private String getSuperTableName(Integer deviceType, String productKey) { - String prefix = switch (deviceType) { - case 1 -> "gateway_sub_"; - case 2 -> "gateway_"; - default -> "device_"; - }; - return (prefix + productKey).toLowerCase(); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java index 04086d0a76..6df29e1018 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java @@ -61,13 +61,6 @@ public interface IotProductThingModelService { */ PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO); - /** - * 创建超级表数据模型 - * - * @param productId 产品编号 - */ - void createSuperTableDataModel(Long productId); - /** * 获得产品物模型列表 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java index ddb800eeee..a53fb69473 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java @@ -21,7 +21,6 @@ import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -50,8 +49,6 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ @Resource private IotProductService productService; - @Resource - private IotSuperTableService dbStructureDataService; @Override @Transactional(rollbackFor = Exception.class) @@ -183,18 +180,6 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ return productThingModelMapper.selectPage(pageReqVO); } - @Override - public void createSuperTableDataModel(Long productId) { - // 1. 查询产品 - IotProductDO product = productService.getProduct(productId); - - // 2. 查询产品的物模型功能列表 - List thingModelList = productThingModelMapper.selectListByProductId(productId); - - // 3. 生成 TDengine 的数据模型 - dbStructureDataService.createSuperTableDataModel(product, thingModelList); - } - @Override public List getProductThingModelListByProductKey(String productKey) { return productThingModelMapper.selectListByProductKey(productKey); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml new file mode 100644 index 0000000000..9894e27a0e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + CREATE STABLE product_property_${productKey} + + ${field.field} ${field.type} + + (${field.length}) + + + TAGS ( + device_key NCHAR(50) + ) + + + + ALTER STABLE product_property_${productKey} + ADD COLUMN ${field.field} ${field.type} + + (${field.length}) + + + + + ALTER STABLE product_property_${productKey} + MODIFY COLUMN ${field.field} ${field.type} + + (${field.length}) + + + + + ALTER STABLE product_property_${productKey} + DROP COLUMN ${field.field} + + + \ No newline at end of file From 245ab4e62d6b29addc266f6f176f912c262c3e1b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 26 Dec 2024 09:55:19 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E4=BF=AE=E5=A4=8D=20device=20?= =?UTF-8?q?=E5=BB=BA=E8=A1=A8=E6=97=B6=EF=BC=8Ctdengine=20=E5=88=86?= =?UTF-8?q?=E6=88=90=20length=20=E5=92=8C=20type=20=E7=9A=84=E6=83=85?= =?UTF-8?q?=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tdengine/IotDevicePropertyDataMapper.java | 36 +++++++++++++++---- .../IotDevicePropertyDataServiceImpl.java | 1 + .../service/device/IotDeviceServiceImpl.java | 1 + .../product/IotProductServiceImpl.java | 1 + 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java index 5409189286..6053444fea 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; @@ -7,6 +8,7 @@ import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -29,18 +31,38 @@ public interface IotDevicePropertyDataMapper { List addFields = newFields.stream().filter( // 新增的字段 newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) .collect(Collectors.toList()); - List modifyFields = newFields.stream().filter( // 更新的字段 - newField -> oldFields.stream().anyMatch(oldField -> oldField.getField().equals(newField.getField()) - && (ObjectUtil.notEqual(oldField.getType(), newField.getType()) - || (newField.getLength() != null && ObjectUtil.notEqual(oldField.getLength(), newField.getLength()))))) - .collect(Collectors.toList()); List dropFields = oldFields.stream().filter( // 删除的字段 oldField -> newFields.stream().noneMatch(n -> n.getField().equals(oldField.getField()))) .collect(Collectors.toList()); + List modifyTypeFields = new ArrayList<>(); // 变更类型的字段 + List modifyLengthFields = new ArrayList<>(); // 变更长度的字段 + newFields.forEach(newField -> { + TDengineTableField oldField = CollUtil.findOne(oldFields, field -> field.getField().equals(newField.getField())); + if (oldField == null) { + return; + } + if (ObjectUtil.notEqual(oldField.getType(), newField.getType())) { + modifyTypeFields.add(newField); + return; + } + if (newField.getLength()!= null) { + if (newField.getLength() > oldField.getLength()) { + modifyLengthFields.add(newField); + } else if (newField.getLength() < oldField.getLength()) { + // 特殊:TDengine 长度修改时,只允许变长,所以此时认为是修改类型 + modifyTypeFields.add(newField); + } + } + }); + + // 执行 addFields.forEach(field -> alterProductPropertySTableAddField(productKey, field)); - // TODO 芋艿:tdengine 只允许 modify 长度;如果 type 变化,只能 drop + add - modifyFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field)); dropFields.forEach(field -> alterProductPropertySTableDropField(productKey, field)); + modifyLengthFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field)); + modifyTypeFields.forEach(field -> { + alterProductPropertySTableDropField(productKey, field); + alterProductPropertySTableAddField(productKey, field); + }); } void alterProductPropertySTableAddField(@Param("productKey") String productKey, diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index e0de0ac3d8..5213decf67 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -100,6 +100,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe } } + // TODO 芋艿:建表的时候,表名要小写么? // 2.1 情况一:如果是新增的时候,需要创建表 List newFields = buildTableFieldList(thingModels); if (CollUtil.isEmpty(oldFields)) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index a43521ff92..39bac89747 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -60,6 +60,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { throw exception(PRODUCT_NOT_EXISTS); } // 1.2 校验设备标识是否唯一 + // TODO 芋艿:校验时,需要跨租户唯一,避免 TDEngine 无法处理;并且要忽略大小写 if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { throw exception(DEVICE_KEY_EXISTS); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 1d2e8892a3..6b7a95f62a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -47,6 +47,7 @@ public class IotProductServiceImpl implements IotProductService { @Override public Long createProduct(IotProductSaveReqVO createReqVO) { // 1. 生成 ProductKey + // TODO 芋艿:校验时,需要跨租户唯一,避免 TDEngine 无法处理;并且要忽略大小写 if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { throw exception(PRODUCT_KEY_EXISTS); } From 09a26666ec0036c0536ce891de7aef303ea53779 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 26 Dec 2024 12:51:36 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91IoT=EF=BC=9A=E8=A7=A3=E5=86=B3=20device=20?= =?UTF-8?q?=E5=BB=BA=E8=A1=A8=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8Ctdengine?= =?UTF-8?q?=20=E9=BB=98=E8=AE=A4=E5=AD=97=E6=AE=B5=E9=83=BD=E6=98=AF?= =?UTF-8?q?=E5=B0=8F=E5=86=99=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C=E9=80=9A?= =?UTF-8?q?=E8=BF=87=20=5F=20=E8=A7=A3=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/service/device/IotDevicePropertyDataServiceImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 5213decf67..99c04a843b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -100,7 +100,6 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe } } - // TODO 芋艿:建表的时候,表名要小写么? // 2.1 情况一:如果是新增的时候,需要创建表 List newFields = buildTableFieldList(thingModels); if (CollUtil.isEmpty(oldFields)) { @@ -119,7 +118,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe private List buildTableFieldList(List thingModels) { return convertList(thingModels, thingModel -> { TDengineTableField field = new TDengineTableField( - thingModel.getIdentifier().toLowerCase(), // TODO 芋艿:为什么要转成小写? + StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写 TYPE_MAPPING.get(thingModel.getProperty().getDataType())); if (thingModel.getProperty().getDataType().equals(IotDataSpecsDataTypeEnum.TEXT.getDataType())) { field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength()); From 7b64b7fc69ad38c4cd9bd0616e68059d0661579f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 26 Dec 2024 12:57:56 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E8=B7=A8=E7=A7=9F=E6=88=B7?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=20ProductKey=20=E5=92=8C=20DeviceKey?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=E8=B7=A8=E7=A7=9F=E6=88=B7=E7=9A=84?= =?UTF-8?q?=20Tdengine=20=E8=A1=A8=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/dal/mysql/device/IotDeviceMapper.java | 4 +++- .../dal/mysql/product/IotProductMapper.java | 4 +++- .../service/device/IotDeviceServiceImpl.java | 10 ++++++---- .../service/product/IotProductServiceImpl.java | 18 +++++++++--------- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 893d7eefda..2ed10eee55 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/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; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -47,7 +48,8 @@ public interface IotDeviceMapper extends BaseMapperX { } default IotDeviceDO selectByDeviceKey(String deviceKey) { - return selectOne(IotDeviceDO::getDeviceKey, deviceKey); + return selectOne(new LambdaQueryWrapper() + .apply("LOWER(device_key) = {0}", deviceKey.toLowerCase())); } default List selectList(Integer deviceType) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java index aef149ae72..0f56251003 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java +++ b/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; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; /** @@ -23,7 +24,8 @@ public interface IotProductMapper extends BaseMapperX { } default IotProductDO selectByProductKey(String productKey) { - return selectOne(IotProductDO::getProductKey, productKey); + return selectOne(new LambdaQueryWrapper() + .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 39bac89747..1896b98b7d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/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; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; @@ -60,10 +61,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { throw exception(PRODUCT_NOT_EXISTS); } // 1.2 校验设备标识是否唯一 - // TODO 芋艿:校验时,需要跨租户唯一,避免 TDEngine 无法处理;并且要忽略大小写 - if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { - throw exception(DEVICE_KEY_EXISTS); - } + TenantUtils.executeIgnore(() -> { + if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { + throw exception(PRODUCT_KEY_EXISTS); + } + }); // 1.3 校验设备名称在同一产品下是否唯一 if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) { throw exception(DEVICE_NAME_EXISTS); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 6b7a95f62a..d384e44c04 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.product; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; @@ -9,7 +10,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -34,9 +34,6 @@ public class IotProductServiceImpl implements IotProductService { @Resource private IotProductMapper productMapper; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotProductThingModelService thingModelFunctionService; @Resource @Lazy // 延迟加载,解决循环依赖 private IotThingModelMessageService thingModelMessageService; @@ -46,11 +43,14 @@ public class IotProductServiceImpl implements IotProductService { @Override public Long createProduct(IotProductSaveReqVO createReqVO) { - // 1. 生成 ProductKey - // TODO 芋艿:校验时,需要跨租户唯一,避免 TDEngine 无法处理;并且要忽略大小写 - if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { - throw exception(PRODUCT_KEY_EXISTS); - } + // 1. 校验 ProductKey + TenantUtils.executeIgnore(() -> { + // 为什么忽略租户?避免多个租户之间,productKey 重复,导致 TDengine 设备属性表重复 + if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { + throw exception(PRODUCT_KEY_EXISTS); + } + }); + // 2. 插入 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus());