diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java index 5927f44b96..0762ade9de 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java @@ -35,4 +35,11 @@ public interface IotConstants { */ String DEVICE_STABLE_NAME_FORMAT = "device_%s"; + /** + * 获取物模型消息记录设备名 + *

+ * 格式为 thing_model_message_{productKey}_{deviceName} + */ + String THINK_MODEL_MESSAGE_TABLE_NAME_FORMAT = "thing_model_message_%s_%s"; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java index 7b3fb097c4..94c4b83d51 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java @@ -1,10 +1,13 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @Data -public class ThingModelArgument { +@JsonIgnoreProperties(ignoreUnknown = true) +public class ThinkModelArgument { public static final String DIRECTION_INPUT = "input"; public static final String DIRECTION_OUTPUT = "output"; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java new file mode 100644 index 0000000000..13309c81ed --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.xmlbeans.impl.xb.xsdschema.Public; + + +/** + * TD 物模型消息日志的数据库 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ThinkModelMessageDO { + /** + * 数据库名称 + */ + private String dataBaseName; + + // TODO @haohao:superTableName 和 tableName 是不是合并。因为每个 mapper 操作的时候,有且只会使用到其中一个。 + /** + * 超级表名称 + */ + private String superTableName; + + /** + * 表名称 + */ + private String tableName; + + /** + * 消息ID + */ + private String id; + + /** + * 扩展功能的参数 + */ + private Object sys; + + /** + * 请求方法 例如:thing.event.property.post + */ + private String method; + + /** + * 请求参数 + */ + private Object params; + + /** + * 属性上报时间戳 + */ + private Long time; + + + /** + * 设备 key + */ + private String deviceKey; + + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java new file mode 100644 index 0000000000..4eb59975d8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessageDO; +import com.baomidou.dynamic.datasource.annotation.DS; +import org.apache.ibatis.annotations.Mapper; + +/** + * 处理 TD 中物模型消息日志的操作 + */ +@Mapper +@DS("tdengine") +public interface TdThinkModelMessageMapper { + + /** + * 创建物模型消息日志超级表超级表 + * + */ + @TenantIgnore + void createSuperTable(ThinkModelMessageDO superTable); + + /** + * 创建子表 + * + */ + @TenantIgnore + void createTableWithTag(ThinkModelMessageDO table); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java new file mode 100644 index 0000000000..76cde71be7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.mq.producer.simulatesend; + +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * SimulateSend 模拟设备上报的 Producer + * + * @author alwayssuper + * @since 2024/12/17 16:35 + */ +@Slf4j +@Component +public class SimulateSendProducer { + @Resource + private ApplicationContext applicationContext; + +} 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 4fd6cfff0a..dba26352ba 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,7 +7,8 @@ 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.thingmodel.IotProductThingModelService; +import cn.iocoder.yudao.module.iot.service.tdengine.IotThinkModelMessageService; +import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -33,8 +34,11 @@ public class IotProductServiceImpl implements IotProductService { private IotProductMapper productMapper; @Resource - @Lazy - private IotProductThingModelService thingModelService; + @Lazy // 延迟加载,解决循环依赖 + private IotProductThinkModelService thinkModelFunctionService; + @Resource + @Lazy // 延迟加载,解决循环依赖 + private IotThinkModelMessageService thinkModelMessageService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { @@ -114,8 +118,10 @@ public class IotProductServiceImpl implements IotProductService { IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build(); // 3. 产品是发布状态 if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { - // 3.1 创建超级表数据模型 - thingModelService.createSuperTableDataModel(id); + // 3.1 创建产品超级表数据模型 + thinkModelFunctionService.createSuperTableDataModel(id); + // 3.2 创建物模型日志超级表数据模型 + thinkModelMessageService.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/IotThingModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java index ffcb3063c5..76b3dfe326 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; /** * 物模型消息 Service @@ -14,5 +14,13 @@ public interface IotThingModelMessageService { * @param device 设备 * @param thingModelMessage 物模型消息 */ - void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage); + void saveThinkModelMessage(IotDeviceDO device, ThinkModelMessage thingModelMessage); + + /** + * 创建物模型消息日志超级表 + * + * @param productId 产品编号 + */ + void createSuperTable(Long productId); + } \ 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/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index f5d589287f..5870628916 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -12,14 +12,21 @@ 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.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdThinkModelMessageMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; +import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; +import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -56,19 +63,29 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private TdEngineDDLMapper tdEngineDDLMapper; @Resource private TdEngineDMLMapper tdEngineDMLMapper; - + @Resource + private IotProductService productService; @Resource private DeviceDataRedisDAO deviceDataRedisDAO; + @Resource + private IotTdDatabaseUtils iotTdDatabaseUtils; + + @Resource + private TdThinkModelMessageMapper tdThinkModelMessageMapper; + // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感 @Override @TenantIgnore public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { // 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态 if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { + // 1.1 创建设备表 createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); + // 1.2 创建物模型日志设备表 + createThinkModelMessageDeviceTable(device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); } // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 @@ -95,6 +112,28 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private List getValidThingModelList(String productKey) { return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey), thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); + @Override + @TenantIgnore + public void createSuperTable(Long productId) { + // 1. 查询产品 + IotProductDO product = productService.getProduct(productId); + + // 2. 获取超级表的名称和数据库名称 + String databaseName = iotTdDatabaseUtils.getDatabaseName(); + String superTableName = IotTdDatabaseUtils.getThinkModelMessageSuperTableName(product.getProductKey()); + + // 3. 创建超级表 + tdThinkModelMessageMapper.createSuperTable(ThinkModelMessageDO.builder().build() + .setDataBaseName(databaseName) + .setSuperTableName(superTableName)); + } + + private List getValidFunctionList(String productKey) { + return iotProductThinkModelService + .getProductThinkModelListByProductKey(productKey) + .stream() + .filter(function -> IotProductThinkModelTypeEnum.PROPERTY.getType().equals(function.getType())) + .toList(); } private List filterAndCollectValidFields(Map params, List thingModelList, IotDeviceDO device, Long time) { @@ -186,6 +225,30 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ .setTags(tagsFieldValues)); } + /** + * 创建物模型日志设备数据表 + * + * @param productKey 产品 Key + * @param deviceName 设备名称 + * @param deviceKey 设备 Key + * + */ + private void createThinkModelMessageDeviceTable(String productKey, String deviceName, String deviceKey){ + + // 1. 获取超级表的名称、数据库名称、设备日志表名称 + String databaseName = iotTdDatabaseUtils.getDatabaseName(); + String superTableName = IotTdDatabaseUtils.getThinkModelMessageSuperTableName(productKey); + String thinkModelMessageDeviceTableName = IotTdDatabaseUtils.getThinkModelMessageDeviceTableName(productKey, deviceName); + + // 2. 创建物模型日志设备数据表 + tdThinkModelMessageMapper.createTableWithTag(ThinkModelMessageDO.builder().build() + .setDataBaseName(databaseName) + .setSuperTableName(superTableName) + .setTableName(thinkModelMessageDeviceTableName) + .setDeviceKey(deviceKey)); + + } + /** * 获取数据库名称 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java new file mode 100644 index 0000000000..b928ec7169 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.iot.util; + +import cn.iocoder.yudao.module.iot.enums.IotConstants; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * TD数据库工具类 + * + * @author AlwaysSuper + */ +@Component +public class IotTdDatabaseUtils { + + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") + private String url; + + /** + * 获取数据库名称 + */ + public String getDatabaseName() { + int index = url.lastIndexOf("/"); + return index != -1 ? url.substring(index + 1) : url; + } + + /** + * 获取产品超级表表名 + * + * @param deviceType 设备类型 + * @param productKey 产品 Key + * @return 产品超级表表名 + */ + public static String getProductSuperTableName(Integer deviceType, String productKey) { + return switch (deviceType) { + case 1 -> String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); + case 2 -> String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); + default -> String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); + }; + } + + /** + * 获取物模型日志超级表表名 + * + * @param productKey 产品 Key + * @return 物模型日志超级表表名 + * + */ + public static String getThinkModelMessageSuperTableName(String productKey) { + return String.format("thing_model_message_", productKey).toLowerCase(); + } + + /** + * 获取物模型日志设备表名 + * + * @param productKey 产品 Key + * @param deviceName 设备名称 + * @return 物模型日志设备表名 + */ + public static String getThinkModelMessageDeviceTableName(String productKey, String deviceName) { + return String.format(IotConstants.THINK_MODEL_MESSAGE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml new file mode 100644 index 0000000000..476ca97abc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + CREATE STABLE ${dataBaseName}.${superTableName}( + ts TIMESTAMP, + id VARCHAR(255), + sys VARCHAR(2048), + method VARCHAR(255), + params VARCHAR(2048) + )TAGS ( + deviceKey VARCHAR(255) + ) + + + + + CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} + USING ${dataBaseName}.${superTableName}( + ts, + id , + sys , + method , + params + )TAGS( + #{deviceKey} + ) + + \ No newline at end of file