Просмотр исходного кода

【新增功能】 设备数据存储和展示

安浩浩 9 месяцев назад
Родитель
Сommit
624f5283b3
20 измененных файлов с 523 добавлено и 108 удалено
  1. 1 1
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java
  2. 4 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
  3. 38 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java
  4. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java
  5. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java
  6. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java
  7. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceStatusUpdateReqVO.java
  8. 21 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java
  9. 42 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java
  10. 46 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java
  11. 71 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java
  12. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
  13. 20 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java
  14. 43 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java
  15. 12 5
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java
  16. 56 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java
  17. 3 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
  18. 3 3
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
  19. 49 57
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java
  20. 109 31
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java

+ 1 - 1
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java

@@ -30,7 +30,7 @@ public enum IotProductFunctionTypeEnum implements IntArrayValuable {
      */
     private final String description;
 
-    public static IotProductFunctionTypeEnum valueOf(Integer type) {
+    public static IotProductFunctionTypeEnum valueOfType(Integer type) {
         for (IotProductFunctionTypeEnum value : values()) {
             if (value.getType().equals(type)) {
                 return value;

+ 4 - 4
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java

@@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.iot.controller.admin.device;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceRespVO;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
 import io.swagger.v3.oas.annotations.Operation;

+ 38 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java

@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.iot.controller.admin.device;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
+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.service.device.IotDeviceDataService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - IoT 设备数据")
+@RestController
+@RequestMapping("/iot/device/data")
+@Validated
+public class IotDeviceDataController {
+
+    @Resource
+    private IotDeviceDataService deviceDataService;
+
+    @GetMapping("/latest-data")
+    @Operation(summary = "获取设备属性最新数据")
+    public CommonResult<List<IotDeviceDataRespVO>> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) {
+        List<IotDeviceDataDO> list = deviceDataService.getDevicePropertiesLatestData(deviceDataReqVO);
+        return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class));
+    }
+
+}

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java → yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
+package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
 
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.validation.InEnum;

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java → yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
+package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
 
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceSaveReqVO.java → yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
+package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceStatusUpdateReqVO.java → yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceStatusUpdateReqVO.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
+package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
 
 import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;

+ 21 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - IoT 设备数据 Request VO")
+@Data
+public class IotDeviceDataReqVO {
+
+    @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
+    private Long deviceId;
+
+    @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String identifier;
+
+    @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String name;
+
+}

+ 42 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - IoT 设备数据 Response VO")
+@Data
+public class IotDeviceDataRespVO {
+
+    @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
+    private Long deviceId;
+
+    @Schema(description = "物模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816")
+    private Long thinkModelFunctionId;
+
+    @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String productKey;
+
+    @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+    private String deviceName;
+
+    @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String identifier;
+
+    @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String name;
+
+    @Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String dataType;
+
+    @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDateTime updateTime;
+
+    @Schema(description = "最新值", requiredMode = Schema.RequiredMode.REQUIRED)
+    private String value;
+
+}

+ 46 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java

@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.iot.convert.device;
+
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
+import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+@Mapper
+public interface IotDeviceDataConvert {
+
+    IotDeviceDataConvert INSTANCE = Mappers.getMapper(IotDeviceDataConvert.class);
+
+//    default  List<IotDeviceDataRespVO> convert(Map<String, Object> deviceData, IotDeviceDO device){
+//        List<IotDeviceDataRespVO> list = new ArrayList<>();
+//        deviceData.forEach((identifier, value) -> {
+////            ThingModelProperty property = ThingModelService.INSTANCE.getProperty(device.getProductId(), identifier);
+////            if (Objects.isNull(property)) {
+////                return;
+////            }
+//            IotDeviceDataRespVO vo = new IotDeviceDataRespVO();
+//            vo.setDeviceId(device.getId());
+//            vo.setProductKey(device.getProductKey());
+//            vo.setDeviceName(device.getDeviceName());
+//            vo.setIdentifier(identifier);
+////            vo.setName(property.getName());
+////            vo.setDataType(property.getDataType().getType());
+//            vo.setUpdateTime(device.getUpdateTime());
+//            vo.setValue(value.toString());
+//            list.add(vo);
+//        });
+//        return list;
+//    }
+}

+ 71 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java

@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.iot.dal.dataobject.device;
+
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * IoT 设备数据 DO
+ *
+ * @author haohao
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class IotDeviceDataDO {
+
+    /**
+     * 设备编号
+     */
+    private Long deviceId;
+
+    /**
+     * 物模型编号
+     */
+    private Long thinkModelFunctionId;
+
+    /**
+     * 产品标识
+     */
+    private String productKey;
+
+    /**
+     * 设备名称
+     */
+    private String deviceName;
+
+    /**
+     * 属性标识符
+     */
+    private String identifier;
+
+    /**
+     * 属性名称
+     */
+    private String name;
+
+    /**
+     * 数据类型
+     */
+    private String dataType;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+
+    /**
+     * 最新值
+     */
+    private String value;
+
+}

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.mysql.device;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 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.IotDevicePageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import org.apache.ibatis.annotations.Mapper;
 

+ 20 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java

@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.iot.dal.redis;
+
+
+
+/**
+ * Iot Redis Key 枚举类
+ *
+ * @author 芋道源码
+ */
+public interface RedisKeyConstants {
+
+    /**
+     * 设备属性数据缓存
+     * <p>
+     * KEY 格式:device_property_data:{deviceId}
+     * VALUE 数据类型:String 设备属性数据
+     */
+    String DEVICE_PROPERTY_DATA = "device_property_data:%s_%s_%s";
+
+}

+ 43 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java

@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.iot.dal.redis.deviceData;
+
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
+import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants.DEVICE_PROPERTY_DATA;
+
+/**
+ * {@link IotDeviceDataDO} 的 Redis DAO
+ */
+@Repository
+public class DeviceDataRedisDAO {
+
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
+
+    public IotDeviceDataDO get(String productKey, String deviceName, String identifier) {
+        String redisKey = formatKey(productKey, deviceName, identifier);
+        return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), IotDeviceDataDO.class);
+    }
+
+    public void set(IotDeviceDataDO deviceData) {
+        String redisKey = formatKey(deviceData.getProductKey(), deviceData.getDeviceName(), deviceData.getIdentifier());
+        stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(deviceData));
+    }
+
+    public void delete(String productKey, String deviceName, String identifier) {
+        String redisKey = formatKey(productKey, deviceName, identifier);
+        stringRedisTemplate.delete(redisKey);
+    }
+
+    private static String formatKey(String productKey, String deviceName, String identifier) {
+        return String.format(DEVICE_PROPERTY_DATA, productKey, deviceName, identifier);
+    }
+}

+ 12 - 5
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java

@@ -1,12 +1,11 @@
 package cn.iocoder.yudao.module.iot.service.device;
 
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO;
-import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
 import jakarta.validation.Valid;
 
+import java.util.List;
+
 /**
  * IoT 设备数据 Service 接口
  *
@@ -23,4 +22,12 @@ public interface IotDeviceDataService {
      * @param message     消息
      */
     void saveDeviceData(String productKey, String deviceName, String message);
+
+    /**
+     * 获得设备属性最新数据
+     *
+     * @param deviceId 设备编号
+     * @return 设备属性最新数据
+     */
+    List<IotDeviceDataDO> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceId);
 }

+ 56 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java

@@ -1,13 +1,23 @@
 package cn.iocoder.yudao.module.iot.service.device;
 
 import cn.hutool.json.JSONObject;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO;
 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.tdengine.ThingModelMessage;
+import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
+import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
+import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
 import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService;
+import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService;
 import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @Slf4j
 @Service
 public class IotDeviceDataServiceImpl implements IotDeviceDataService {
@@ -16,6 +26,11 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
     private IotDeviceService deviceService;
     @Resource
     private IotThingModelMessageService thingModelMessageService;
+    @Resource
+    private IotThinkModelFunctionService thinkModelFunctionService;
+
+    @Resource
+    private DeviceDataRedisDAO deviceDataRedisDAO;
 
     @Override
     public void saveDeviceData(String productKey, String deviceName, String message) {
@@ -34,6 +49,46 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService {
                 .deviceName(deviceName)
                 .deviceKey(device.getDeviceKey())
                 .build();
-        thingModelMessageService.saveThingModelMessage(device,thingModelMessage);
+        thingModelMessageService.saveThingModelMessage(device, thingModelMessage);
+    }
+
+    @Override
+    public List<IotDeviceDataDO> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) {
+        List<IotDeviceDataDO> list = new ArrayList<>();
+        // 1. 获取设备信息
+        IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
+        // 2. 获取设备属性最新数据
+        List<IotThinkModelFunctionDO> thinkModelFunctionList = thinkModelFunctionService.getThinkModelFunctionListByProductKey(device.getProductKey());
+        thinkModelFunctionList = thinkModelFunctionList.stream()
+                .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType()
+                        .equals(function.getType())).toList();
+
+        // 3. 过滤标识符和属性名称
+        if (deviceDataReqVO.getIdentifier() != null) {
+            thinkModelFunctionList = thinkModelFunctionList.stream()
+                    .filter(function -> function.getIdentifier().toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase()))
+                    .toList();
+        }
+        if (deviceDataReqVO.getName() != null) {
+            thinkModelFunctionList = thinkModelFunctionList.stream()
+                    .filter(function -> function.getName().toLowerCase().contains(deviceDataReqVO.getName().toLowerCase()))
+                    .toList();
+        }
+        // 4. 获取设备属性最新数据
+        thinkModelFunctionList.forEach(function -> {
+            IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), function.getIdentifier());
+            if (deviceData == null) {
+                deviceData = new IotDeviceDataDO();
+                deviceData.setProductKey(device.getProductKey());
+                deviceData.setDeviceName(device.getDeviceName());
+                deviceData.setIdentifier(function.getIdentifier());
+                deviceData.setDeviceId(deviceDataReqVO.getDeviceId());
+                deviceData.setThinkModelFunctionId(function.getId());
+                deviceData.setName(function.getName());
+                deviceData.setDataType(function.getProperty().getDataType().getType());
+            }
+            list.add(deviceData);
+        });
+        return list;
     }
 }

+ 3 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java

@@ -1,7 +1,9 @@
 package cn.iocoder.yudao.module.iot.service.device;
 
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
 import jakarta.validation.*;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 

+ 3 - 3
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java

@@ -5,9 +5,9 @@ import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
 import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper;

+ 49 - 57
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java

@@ -1,12 +1,12 @@
 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.thinkmodelfunction.thingModel.ThingModelProperty;
 import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.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.TdRestApi;
 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
 import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
 import jakarta.annotation.Resource;
@@ -14,10 +14,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Service
@@ -27,9 +24,6 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
     @Resource
     private IotTdEngineService iotTdEngineService;
 
-    @Resource
-    private TdRestApi tdRestApi;
-
     @Value("${spring.datasource.dynamic.datasource.tdengine.url}")
     private String url;
 
@@ -37,39 +31,25 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
     public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) {
         // 1. 解析物模型,获得字段列表
         List<TdFieldDO> schemaFields = new ArrayList<>();
-        schemaFields.add(TdFieldDO.builder().
-                fieldName("time").
-                dataType("TIMESTAMP").
-                build());
+        schemaFields.add(TdFieldDO.builder()
+                .fieldName("time")
+                .dataType("TIMESTAMP")
+                .build());
         schemaFields.addAll(FieldParser.parse(thingModel));
 
         // 3. 设置超级表的标签
-        List<TdFieldDO> tagsFields = new ArrayList<>();
-        tagsFields.add(TdFieldDO.builder().
-                fieldName("product_key").
-                dataType("NCHAR").
-                dataLength(64).
-                build());
-        tagsFields.add(TdFieldDO.builder().
-                fieldName("device_key").
-                dataType("NCHAR").
-                dataLength(64).
-                build());
-        tagsFields.add(TdFieldDO.builder().
-                fieldName("device_name").
-                dataType("NCHAR").
-                dataLength(64).
-                build());
-        tagsFields.add(TdFieldDO.builder().
-                fieldName("device_type").
-                dataType("INT").
-                build());
+        List<TdFieldDO> tagsFields = Arrays.asList(
+                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()
+        );
 
         // 4. 获取超级表的名称
         String superTableName = getProductPropertySTableName(deviceType, thingModel.getProductKey());
 
         // 5. 创建超级表
-        String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
+        String dataBaseName = getDatabaseName();
         iotTdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName);
     }
 
@@ -81,7 +61,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
             List<TdFieldDO> newFields = FieldParser.parse(thingModel);
 
             updateTableFields(tbName, oldFields, newFields);
-        } catch (Throwable e) {
+        } catch (Exception e) {
             log.error("更新物模型超级表失败", e);
         }
     }
@@ -90,14 +70,15 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
     private List<TdFieldDO> getTableFields(String tableName) {
         List<TdFieldDO> fields = new ArrayList<>();
         // 获取超级表的描述信息
-        List<Map<String, Object>> maps = iotTdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), tableName);
+        List<Map<String, Object>> maps = iotTdEngineService.describeSuperTable(getDatabaseName(), tableName);
         if (maps != null) {
-            // 过滤掉 note 字段为 TAG 的记录
-            maps = maps.stream().filter(map -> !"TAG".equals(map.get("note"))).toList();
-            // 过滤掉 time 字段
-            maps = maps.stream().filter(map -> !"time".equals(map.get("field"))).toList();
+            // 过滤掉 note 字段为 TAG 的记录和 time 字段
+            List<Map<String, Object>> filteredMaps = maps.stream()
+                    .filter(map -> !"TAG".equals(map.get("note")))
+                    .filter(map -> !"time".equals(map.get("field")))
+                    .toList();
             // 解析字段信息
-            fields = FieldParser.parse(maps.stream()
+            fields = FieldParser.parse(filteredMaps.stream()
                     .map(map -> List.of(map.get("field"), map.get("type"), map.get("length")))
                     .collect(Collectors.toList()));
         }
@@ -113,7 +94,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
         // 获取删除字段
         List<TdFieldDO> dropFields = getDropFields(oldFields, newFields);
 
-        String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
+        String dataBaseName = getDatabaseName();
         // 添加新增字段
         if (CollUtil.isNotEmpty(addFields)) {
             iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, addFields);
@@ -131,25 +112,37 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
 
     // 获取新增字段
     private List<TdFieldDO> getAddFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
+        Set<String> oldFieldNames = oldFields.stream()
+                .map(TdFieldDO::getFieldName)
+                .collect(Collectors.toSet());
         return newFields.stream()
-                .filter(f -> oldFields.stream().noneMatch(old -> old.getFieldName().equals(f.getFieldName())))
+                .filter(f -> !oldFieldNames.contains(f.getFieldName()))
                 .collect(Collectors.toList());
     }
 
     // 获取修改字段
     private List<TdFieldDO> getModifyFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
+        Map<String, TdFieldDO> oldFieldMap = oldFields.stream()
+                .collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f));
+
         return newFields.stream()
-                .filter(f -> oldFields.stream().anyMatch(old ->
-                        old.getFieldName().equals(f.getFieldName()) &&
-                                (!old.getDataType().equals(f.getDataType()) || !Objects.equals(old.getDataLength(), f.getDataLength()))))
+                .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<TdFieldDO> getDropFields(List<TdFieldDO> oldFields, List<TdFieldDO> newFields) {
+        Set<String> newFieldNames = newFields.stream()
+                .map(TdFieldDO::getFieldName)
+                .collect(Collectors.toSet());
         return oldFields.stream()
-                .filter(f -> !"time".equals(f.getFieldName()) && !"device_id".equals(f.getFieldName()) &&
-                        newFields.stream().noneMatch(n -> n.getFieldName().equals(f.getFieldName())))
+                .filter(f -> !"time".equals(f.getFieldName()) && !"device_id".equals(f.getFieldName()))
+                .filter(f -> !newFieldNames.contains(f.getFieldName()))
                 .collect(Collectors.toList());
     }
 
@@ -157,13 +150,13 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
     public void createSuperTableDataModel(IotProductDO product, List<IotThinkModelFunctionDO> functionList) {
         ThingModelRespVO thingModel = buildThingModel(product, functionList);
 
-        if (thingModel.getModel().getProperties().isEmpty()) {
+        if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) {
             log.warn("物模型属性列表为空,不创建超级表");
             return;
         }
 
         String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey());
-        String dataBaseName = url.substring(url.lastIndexOf("/") + 1);
+        String dataBaseName = getDatabaseName();
         Integer tableExists = iotTdEngineService.checkSuperTableExists(dataBaseName, superTableName);
 
         if (tableExists != null && tableExists > 0) {
@@ -180,7 +173,8 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
 
         ThingModelRespVO.Model model = new ThingModelRespVO.Model();
         List<ThingModelProperty> properties = functionList.stream()
-                .filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals(IotProductFunctionTypeEnum.valueOf(function.getType())))
+                .filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals(
+                        IotProductFunctionTypeEnum.valueOfType(function.getType())))
                 .map(this::buildThingModelProperty)
                 .collect(Collectors.toList());
 
@@ -191,14 +185,15 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
     }
 
     private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) {
-        ThingModelProperty property = new ThingModelProperty();
-        property.setIdentifier(function.getIdentifier());
-        property.setName(function.getName());
-        property.setDescription(function.getDescription());
+        ThingModelProperty property = BeanUtil.copyProperties(function, ThingModelProperty.class);
         property.setDataType(function.getProperty().getDataType());
         return property;
     }
 
+    private String getDatabaseName() {
+        return url.substring(url.lastIndexOf("/") + 1);
+    }
+
     static String getProductPropertySTableName(Integer deviceType, String productKey) {
         return switch (deviceType) {
             case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase();
@@ -207,7 +202,4 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService
         };
     }
 
-    static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) {
-        return String.format("%s_%s_%s", deviceType, productKey, deviceKey).toLowerCase();
-    }
 }

+ 109 - 31
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java

@@ -1,13 +1,16 @@
 package cn.iocoder.yudao.module.iot.service.tdengine;
 
+import cn.hutool.core.date.DateUtil;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
 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.tdengine.FieldParser;
 import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage;
 import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
+import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
 import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum;
 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
@@ -17,9 +20,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Slf4j
@@ -36,11 +37,14 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
     @Resource
     private IotTdEngineService iotTdEngineService;
 
+    @Resource
+    private DeviceDataRedisDAO deviceDataRedisDAO;
+
     @Override
     @TenantIgnore
     public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) {
         // 判断设备状态,如果为未激活状态,创建数据表
-        if (device.getStatus().equals(0)) {
+        if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) {
             // 创建设备数据表
             createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey());
             // 更新设备状态
@@ -50,53 +54,99 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
             iotDeviceService.updateDeviceStatus(updateReqVO);
         }
 
-        // 1. 获取设备属性
+        // 获取设备属性
         Map<String, Object> params = thingModelMessage.dataToMap();
 
-        // 2. 物模型校验,过滤非物模型属性
-        List<IotThinkModelFunctionDO> thinkModelFunctionListByProductKey = iotThinkModelFunctionService.getThinkModelFunctionListByProductKey(thingModelMessage.getProductKey());
+        // 物模型校验,过滤非物模型属性
+        List<IotThinkModelFunctionDO> functionList = iotThinkModelFunctionService
+                .getThinkModelFunctionListByProductKey(thingModelMessage.getProductKey())
+                .stream()
+                .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType()))
+                .toList();
 
-        // 2.1 筛选是属性 IotProductFunctionTypeEnum
-        thinkModelFunctionListByProductKey.removeIf(iotThinkModelFunctionDO -> !iotThinkModelFunctionDO.getType().equals(IotProductFunctionTypeEnum.PROPERTY.getType()));
-        if (thinkModelFunctionListByProductKey.isEmpty()) {
+        if (functionList.isEmpty()) {
             return;
         }
-        // 2.2 获取属性名称
-        Map<String, String> thingModelProperties = thinkModelFunctionListByProductKey.stream().collect(Collectors.toMap(IotThinkModelFunctionDO::getIdentifier, IotThinkModelFunctionDO::getName));
 
-        // 4. 保存属性记录
-        List<TdFieldDO> schemaFieldValues = new ArrayList<>();
+        // 获取属性标识符集合
+        Set<String> propertyIdentifiers = functionList.stream()
+                .map(IotThinkModelFunctionDO::getIdentifier)
+                .collect(Collectors.toSet());
 
-        // 1. 设置字段名
-        schemaFieldValues.add(new TdFieldDO("time", thingModelMessage.getTime()));
+        Map<String, IotThinkModelFunctionDO> functionMap = functionList.stream()
+                .collect(Collectors.toMap(IotThinkModelFunctionDO::getIdentifier, function -> function));
 
-        // 2. 遍历新属性
+        // 过滤并收集有效的属性字段
+        List<TdFieldDO> schemaFieldValues = new ArrayList<>();
+        schemaFieldValues.add(new TdFieldDO("time", thingModelMessage.getTime()));
         params.forEach((key, val) -> {
-            if (thingModelProperties.containsKey(key)) {
+            if (propertyIdentifiers.contains(key)) {
                 schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val));
+                // 缓存设备属性
+                setDeviceDataCache(device, functionMap.get(key), val, thingModelMessage.getTime());
             }
         });
+        if (schemaFieldValues.size() == 1) {
+            return;
+        }
 
-        // 3. 保存设备属性
+        // 构建并保存设备属性
         TableDO tableData = new TableDO();
-        tableData.setDataBaseName(url.substring(url.lastIndexOf("/") + 1));
+        tableData.setDataBaseName(getDatabaseName());
         tableData.setSuperTableName(getProductPropertySTableName(device.getDeviceType(), device.getProductKey()));
-        tableData.setTableName("device_" + device.getProductKey().toLowerCase() + "_" + device.getDeviceName().toLowerCase());
+        tableData.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()));
         tableData.setSchemaFieldValues(schemaFieldValues);
 
-        // 4. 保存数据
         iotTdEngineService.insertData(tableData);
     }
 
+    /**
+     * 缓存设备属性
+     *
+     * @param device                  设备信息
+     * @param iotThinkModelFunctionDO 物模型属性
+     * @param val                     属性值
+     * @param time                    时间
+     */
+    private void setDeviceDataCache(IotDeviceDO device, IotThinkModelFunctionDO iotThinkModelFunctionDO, Object val, Long time) {
+        IotDeviceDataDO deviceData = IotDeviceDataDO.builder()
+                .productKey(device.getProductKey())
+                .deviceName(device.getDeviceName())
+                .identifier(iotThinkModelFunctionDO.getIdentifier())
+                .value(val != null ? val.toString() : null)
+                .updateTime(DateUtil.toLocalDateTime(new Date(time)))
+                .deviceId(device.getId())
+                .thinkModelFunctionId(iotThinkModelFunctionDO.getId())
+                .name(iotThinkModelFunctionDO.getName())
+                .dataType(iotThinkModelFunctionDO.getProperty().getDataType().getType())
+                .build();
+        deviceDataRedisDAO.set(deviceData);
+    }
+
+    /**
+     * 创建设备数据表
+     *
+     * @param deviceType 设备类型
+     * @param productKey 产品 Key
+     * @param deviceName 设备名称
+     * @param deviceKey  设备 Key
+     */
     private void createDeviceTable(Integer deviceType, String productKey, String deviceName, String deviceKey) {
+        String superTableName = getProductPropertySTableName(deviceType, productKey);
+        String dataBaseName = getDatabaseName();
+
+        List<Map<String, Object>> maps = iotTdEngineService.describeSuperTable(dataBaseName, superTableName);
         List<TdFieldDO> tagsFieldValues = new ArrayList<>();
-        String SuperTableName = getProductPropertySTableName(deviceType, productKey);
-        List<Map<String, Object>> maps = iotTdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), SuperTableName);
+
         if (maps != null) {
-            List<Map<String, Object>> taggedNotesList = maps.stream().filter(map -> "TAG".equals(map.get("note"))).toList();
+            List<Map<String, Object>> taggedNotesList = maps.stream()
+                    .filter(map -> "TAG".equals(map.get("note")))
+                    .toList();
+
             tagsFieldValues = FieldParser.parse(taggedNotesList.stream()
                     .map(map -> List.of(map.get("field"), map.get("type"), map.get("length")))
                     .collect(Collectors.toList()));
+
             for (TdFieldDO tagsFieldValue : tagsFieldValues) {
                 switch (tagsFieldValue.getFieldName()) {
                     case "product_key" -> tagsFieldValue.setFieldValue(productKey);
@@ -107,21 +157,49 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ
             }
         }
 
-        // 1. 创建设备数据表
-        String tableName = "device_" + productKey.toLowerCase() + "_" + deviceName.toLowerCase();
+        // 创建设备数据表
+        String tableName = getDeviceTableName(productKey, deviceName);
         TableDO tableDto = new TableDO();
-        tableDto.setDataBaseName(url.substring(url.lastIndexOf("/") + 1));
-        tableDto.setSuperTableName(SuperTableName);
+        tableDto.setDataBaseName(dataBaseName);
+        tableDto.setSuperTableName(superTableName);
         tableDto.setTableName(tableName);
         tableDto.setTagsFieldValues(tagsFieldValues);
+
         iotTdEngineService.createTable(tableDto);
     }
 
-    static String getProductPropertySTableName(Integer deviceType, String productKey) {
+    /**
+     * 获取数据库名称
+     *
+     * @return 数据库名称
+     */
+    private String getDatabaseName() {
+        return url.substring(url.lastIndexOf("/") + 1);
+    }
+
+    /**
+     * 获取产品属性表名
+     *
+     * @param deviceType 设备类型
+     * @param productKey 产品 Key
+     * @return 产品属性表名
+     */
+    private static String getProductPropertySTableName(Integer deviceType, String productKey) {
         return switch (deviceType) {
             case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase();
             case 2 -> String.format("gateway_%s", productKey).toLowerCase();
             default -> String.format("device_%s", productKey).toLowerCase();
         };
     }
+
+    /**
+     * 获取设备表名
+     *
+     * @param productKey 产品 Key
+     * @param deviceName 设备名称
+     * @return 设备表名
+     */
+    private static String getDeviceTableName(String productKey, String deviceName) {
+        return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase());
+    }
 }