Browse Source

Merge branch 'feature/iot' of https://gitee.com/alwayssuper/ruoyi-vue-pro into feature/iot

# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java
YunaiV 7 months ago
parent
commit
92de5b1f09

+ 7 - 0
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";
 
+    /**
+     * 获取物模型消息记录设备名
+     * <p>
+     * 格式为 thing_model_message_{productKey}_{deviceName}
+     */
+    String THINK_MODEL_MESSAGE_TABLE_NAME_FORMAT = "thing_model_message_%s_%s";
+
 }

+ 4 - 1
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";

+ 66 - 0
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;
+
+
+}

+ 29 - 0
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);
+}

+ 20 - 0
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;
+
+}

+ 11 - 5
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);
     }

+ 10 - 2
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);
+
 }

+ 64 - 1
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<IotProductThingModelDO> 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<IotProductThinkModelDO> getValidFunctionList(String productKey) {
+        return iotProductThinkModelService
+                .getProductThinkModelListByProductKey(productKey)
+                .stream()
+                .filter(function -> IotProductThinkModelTypeEnum.PROPERTY.getType().equals(function.getType()))
+                .toList();
     }
 
     private List<TdFieldDO> filterAndCollectValidFields(Map<String, Object> params, List<IotProductThingModelDO> 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));
+
+    }
+
     /**
      * 获取数据库名称
      *

+ 63 - 0
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());
+    }
+
+}

+ 34 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.iocoder.yudao.module.iot.dal.tdengine.TdThinkModelMessageMapper">
+
+
+    <!-- 创建物模型消息日志超级表 -->
+    <update id="createSuperTable">
+        CREATE STABLE ${dataBaseName}.${superTableName}(
+        ts TIMESTAMP,
+        id VARCHAR(255),
+        sys VARCHAR(2048),
+        method VARCHAR(255),
+        params VARCHAR(2048)
+        )TAGS (
+        deviceKey VARCHAR(255)
+        )
+    </update>
+
+    <!-- 创建物模型消息日志子表,带有deviceKey的TAG -->
+    <update id="createTableWithTag">
+        CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName}
+        USING ${dataBaseName}.${superTableName}(
+            ts,
+            id ,
+            sys ,
+            method ,
+            params
+        )TAGS(
+            #{deviceKey}
+        )
+    </update>
+</mapper>