Jelajahi Sumber

【功能优化】IoT:部分实现 IotDevicePropertyMessageConsumer,支持缓存的记录(差设备属性的日志记录)

YunaiV 6 bulan lalu
induk
melakukan
8e80a53a8b
15 mengubah file dengan 245 tambahan dan 215 penghapusan
  1. 2 6
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java
  2. 0 82
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java
  3. 35 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDevicePropertyDO.java
  4. 14 5
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java
  5. 54 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java
  6. 27 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java
  7. 0 43
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java
  8. 1 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java
  9. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java
  10. 9 3
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java
  11. 1 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
  12. 0 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
  13. 5 5
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java
  14. 95 68
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java
  15. 1 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java

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

@@ -4,7 +4,7 @@ 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.deviceData.*;
-import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO;
 import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
 import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
@@ -28,18 +28,14 @@ public class IotDeviceDataController {
 
     @Resource
     private IotDevicePropertyService deviceDataService;
-
     @Resource
-    private IotDeviceLogService iotDeviceLogDataService;
-
-    @Resource // TODO @super:service 之间,不用空行;原因是,这样更简洁;空行,主要是为了“间隔”,提升可读性
     private IotDeviceLogService deviceLogDataService;
 
     // TODO @浩浩:这里的 /latest-list,包括方法名。
     @GetMapping("/latest")
     @Operation(summary = "获取设备属性最新数据")
     public CommonResult<List<IotDeviceDataRespVO>> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) {
-        List<IotDeviceDataDO> list = deviceDataService.getLatestDeviceProperties(deviceDataReqVO);
+        List<IotDevicePropertyDO> list = deviceDataService.getLatestDeviceProperties(deviceDataReqVO);
         return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class));
     }
 

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

@@ -1,82 +0,0 @@
-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.dal.dataobject.thingmodel.IotThingModelDO;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.time.LocalDateTime;
-
-/**
- * IoT 设备数据 DO
- *
- * @author haohao
- */
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class IotDeviceDataDO {
-
-    /**
-     * 设备编号
-     * <p>
-     * 关联 {@link IotDeviceDO#getId()}
-     */
-    private Long deviceId;
-
-    /**
-     * 物模型编号
-     * <p>
-     * 关联 {@link IotThingModelDO#getId()}
-     */
-    private Long thingModelId;
-
-    /**
-     * 产品标识
-     * <p>
-     * 关联 {@link IotProductDO#getProductKey()}
-     */
-    private String productKey;
-
-    /**
-     * 设备名称
-     * <p>
-     * 冗余 {@link IotDeviceDO#getDeviceName()}
-     */
-    private String deviceName;
-
-    /**
-     * 属性标识符
-     * <p>
-     * 关联 {@link IotThingModelDO#getIdentifier()}
-     */
-    private String identifier;
-
-    /**
-     * 属性名称
-     * <p>
-     * 关联 {@link IotThingModelDO#getName()}
-     */
-    private String name;
-
-    /**
-     * 数据类型
-     * <p>
-     * 关联 {@link IotThingModelDO#getProperty()#getDataType()}
-     */
-    private String dataType;
-
-    /**
-     * 更新时间
-     */
-    private LocalDateTime updateTime;
-
-    /**
-     * 最新值
-     */
-    private String value;
-
-}

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

@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.iot.dal.dataobject.device;
+
+import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * IoT 设备属性项 Redis DO
+ *
+ * @see cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants#DEVICE_PROPERTY
+ * @see DevicePropertyRedisDAO
+ *
+ * @author haohao
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class IotDevicePropertyDO {
+
+    /**
+     * 属性值(最新)
+     */
+    private Object value;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+
+}

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

@@ -1,6 +1,6 @@
 package cn.iocoder.yudao.module.iot.dal.redis;
 
-
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
 
 /**
  * Iot Redis Key 枚举类
@@ -10,11 +10,20 @@ package cn.iocoder.yudao.module.iot.dal.redis;
 public interface RedisKeyConstants {
 
     /**
-     * 设备属性数据缓存
+     * 设备属性数据缓存,采用 HASH 结构
      * <p>
-     * KEY 格式:device_property_data:{deviceId}
-     * VALUE 数据类型:String 设备属性数据
+     * KEY 格式:device_property:{deviceKey}
+     * HASH KEY:identifier 属性标识
+     * VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO}
+     */
+    String DEVICE_PROPERTY = "device_property:%s";
+
+    /**
+     * 设备的最后上报时间,采用 ZSET 结构
+     *
+     * KEY 格式:{deviceKey}
+     * SCORE:上报时间
      */
-    String DEVICE_PROPERTY_DATA = "device_property_data:%s_%s_%s";
+    String DEVICE_REPORT_TIME = "device_report_time";
 
 }

+ 54 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java

@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.module.iot.dal.redis.device;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
+import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.util.Collections;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+import static cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants.DEVICE_PROPERTY;
+
+/**
+ * {@link IotDevicePropertyDO} 的 Redis DAO
+ */
+@Repository
+public class DevicePropertyRedisDAO {
+
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
+
+    public Map<String, IotDevicePropertyDO> get(String deviceKey) {
+        String redisKey = formatKey(deviceKey);
+        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(redisKey);
+        if (CollUtil.isEmpty(entries)) {
+            return Collections.emptyMap();
+        }
+        return convertMap(entries.values(), key -> (String) key,
+                value -> JsonUtils.parseObject((String) value, IotDevicePropertyDO.class));
+    }
+
+    public void set(String deviceKey, Map<String, IotDevicePropertyDO> properties) {
+        if (CollUtil.isEmpty(properties)) {
+            return;
+        }
+        String redisKey = formatKey(deviceKey);
+        stringRedisTemplate.opsForHash().putAll(redisKey, convertMap(properties.entrySet(),
+                Map.Entry::getKey,
+                entry -> JsonUtils.toJsonString(entry.getValue())));
+    }
+
+    public void delete(String deviceKey) {
+        String redisKey = formatKey(deviceKey);
+        stringRedisTemplate.delete(redisKey);
+    }
+
+    private static String formatKey(String deviceKey) {
+        return String.format(DEVICE_PROPERTY, deviceKey);
+    }
+
+}

+ 27 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java

@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.iot.dal.redis.device;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants;
+import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+
+/**
+ * 设备的最后上报时间的 Redis DAO
+ *
+ * @author 芋道源码
+ */
+@Repository
+public class DeviceReportTimeRedisDAO {
+
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
+
+    public void update(String deviceKey, LocalDateTime reportTime) {
+        stringRedisTemplate.opsForZSet().add(RedisKeyConstants.DEVICE_REPORT_TIME, deviceKey,
+                LocalDateTimeUtil.toEpochMilli(reportTime));
+    }
+
+}

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

@@ -1,43 +0,0 @@
-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);
-    }
-}

+ 1 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java

@@ -77,6 +77,7 @@ public interface IotDevicePropertyDataMapper {
     void alterProductPropertySTableDropField(@Param("productKey") String productKey,
                                              @Param("field") TDengineTableField field);
 
+    // TODO @芋艿:待实现
     /**
      * 获取历史数据列表
      *

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java

@@ -39,7 +39,7 @@ public class EmqxServiceImpl implements EmqxService {
 //                    .deviceName(deviceName)
 //                    .properties(message) // TODO 芋艿:临时去掉,看看
                     .build();
-            iotDeviceDataService.saveDeviceData(createDTO);
+//            iotDeviceDataService.saveDeviceProperty(createDTO);
         }
     }
 

+ 9 - 3
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java

@@ -1,5 +1,8 @@
 package cn.iocoder.yudao.module.iot.mq.consumer.device;
 
+import cn.hutool.core.util.ObjectUtil;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
 import lombok.extern.slf4j.Slf4j;
@@ -24,11 +27,14 @@ public class IotDevicePropertyMessageConsumer {
     @EventListener
     @Async
     public void onMessage(IotDeviceMessage message) {
+        if (ObjectUtil.notEqual(message.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())
+                || ObjectUtil.notEqual(message.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier())) {
+            return;
+        }
         log.info("[onMessage][消息内容({})]", message);
 
-        // 设备日志记录
-        // TODO @芋艿:重新写下
-//        deviceLogDataService.createDeviceLog(message);
+        // 保存设备属性
+        deviceDataService.saveDeviceProperty(message);
     }
 
 }

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

@@ -113,6 +113,7 @@ public interface IotDeviceService {
      */
     Long getDeviceCountByGroupId(Long groupId);
 
+    // TODO @芋艿:增加缓存
     /**
      * 根据产品 key 和设备名称,获得设备信息
      *

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

@@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException;
 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;
@@ -247,7 +246,6 @@ public class IotDeviceServiceImpl implements IotDeviceService {
     }
 
     @Override
-    @TenantIgnore
     public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) {
         return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
     }

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

@@ -1,10 +1,10 @@
 package cn.iocoder.yudao.module.iot.service.device.data;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO;
-import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
+import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 import jakarta.validation.Valid;
 
 import java.util.List;
@@ -27,9 +27,9 @@ public interface IotDevicePropertyService {
     /**
      * 保存设备数据
      *
-     * @param createDTO 设备数据
+     * @param message 设备消息
      */
-    void saveDeviceData(IotDevicePropertyReportReqDTO createDTO);
+    void saveDeviceProperty(IotDeviceMessage message);
 
     /**
      * 模拟设备
@@ -44,7 +44,7 @@ public interface IotDevicePropertyService {
      * @param deviceId 设备编号
      * @return 设备属性最新数据
      */
-    List<IotDeviceDataDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceId);
+    List<IotDevicePropertyDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceId);
 
     /**
      * 获得设备属性历史数据

+ 95 - 68
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java

@@ -1,27 +1,26 @@
 package cn.iocoder.yudao.module.iot.service.device.data;
 
 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.hutool.json.JSONUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
+import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO;
 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.device.IotDevicePropertyDO;
 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.thingmodel.IotThingModelDO;
-import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO;
+import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO;
 import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper;
 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.IotThingModelTypeEnum;
 import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField;
+import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
 import cn.iocoder.yudao.module.iot.service.product.IotProductService;
 import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService;
@@ -31,15 +30,13 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DATA_CONTENT_JSON_PARSE_ERROR;
 
 /**
@@ -77,7 +74,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
     private IotProductService productService;
 
     @Resource
-    private DeviceDataRedisDAO deviceDataRedisDAO;
+    private DevicePropertyRedisDAO deviceDataRedisDAO;
 
     @Resource
     private IotDevicePropertyDataMapper devicePropertyDataMapper;
@@ -126,13 +123,41 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
     }
 
     @Override
-    public void saveDeviceData(IotDevicePropertyReportReqDTO createDTO) {
-        // TODO 芋艿:这块需要实现
-        // 1. 根据产品 key 和设备名称,获得设备信息
-        IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(createDTO.getProductKey(), createDTO.getDeviceName());
-        // 2. 解析消息,保存数据
-        JSONObject jsonObject = new JSONObject(createDTO.getProperties());
-        log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", createDTO.getProductKey(), createDTO.getDeviceName(), jsonObject);
+    public void saveDeviceProperty(IotDeviceMessage message) {
+        if (!(message.getData() instanceof Map)) {
+            log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message);
+            return;
+        }
+        // 1. 获得设备信息
+        IotDeviceDO device = TenantUtils.executeIgnore(() ->
+                deviceService.getDeviceByProductKeyAndDeviceName(message.getProductKey(), message.getDeviceName()));
+        if (device == null) {
+            log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message);
+            return;
+        }
+
+        // 2. 根据物模型,拼接合法的属性
+        List<IotThingModelDO> thingModels = TenantUtils.executeIgnore(() ->
+                thingModelService.getThingModelListByProductId(device.getProductId()));
+        Map<String, Object> properties = new HashMap<>();
+        ((Map<?, ?>) message.getData()).forEach((key, value) -> {
+            if (CollUtil.findOne(thingModels, thingModel -> thingModel.getIdentifier().equals(key)) == null) {
+                log.error("[saveDeviceProperty][消息({}) 的属性({}) 不存在]", message, key);
+                return;
+            }
+            properties.put((String) key, value);
+        });
+        if (CollUtil.isEmpty(properties)) {
+            log.error("[saveDeviceProperty][消息({}) 没有合法的属性]", message);
+            return;
+        }
+
+        // 3.1 保存属性【数据】
+        // TODO 芋艿,未实现
+
+        // 3.2 保存属性【日志】
+        deviceDataRedisDAO.set(message.getDeviceKey(), convertMap(properties.entrySet(), Map.Entry::getKey,
+                entry -> IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build()));
     }
 
     //TODO @芋艿:copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化
@@ -163,62 +188,64 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
     }
 
     @Override
-    public List<IotDeviceDataDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) {
-        List<IotDeviceDataDO> list = new ArrayList<>();
-        // 1. 获取设备信息
-        IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
-        // 2. 获取设备属性最新数据
-        List<IotThingModelDO> thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey());
-        thingModelList = filterList(thingModelList, thingModel -> IotThingModelTypeEnum.PROPERTY.getType()
-                .equals(thingModel.getType()));
-
-        // 3. 过滤标识符和属性名称
-        if (deviceDataReqVO.getIdentifier() != null) {
-            thingModelList = filterList(thingModelList, thingModel -> thingModel.getIdentifier()
-                    .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase()));
-        }
-        if (deviceDataReqVO.getName() != null) {
-            thingModelList = filterList(thingModelList, thingModel -> thingModel.getName()
-                    .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase()));
-        }
-        // 4. 获取设备属性最新数据
-        thingModelList.forEach(thingModel -> {
-            IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thingModel.getIdentifier());
-            if (deviceData == null) {
-                deviceData = new IotDeviceDataDO();
-                deviceData.setProductKey(device.getProductKey());
-                deviceData.setDeviceName(device.getDeviceName());
-                deviceData.setIdentifier(thingModel.getIdentifier());
-                deviceData.setDeviceId(deviceDataReqVO.getDeviceId());
-                deviceData.setThingModelId(thingModel.getId());
-                deviceData.setName(thingModel.getName());
-                deviceData.setDataType(thingModel.getProperty().getDataType());
-            }
-            list.add(deviceData);
-        });
-        return list;
+    public List<IotDevicePropertyDO> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) {
+//        List<IotDevicePropertyDO> list = new ArrayList<>();
+//        // 1. 获取设备信息
+//        IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
+//        // 2. 获取设备属性最新数据
+//        List<IotThingModelDO> thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey());
+//        thingModelList = filterList(thingModelList, thingModel -> IotThingModelTypeEnum.PROPERTY.getType()
+//                .equals(thingModel.getType()));
+//
+//        // 3. 过滤标识符和属性名称
+//        if (deviceDataReqVO.getIdentifier() != null) {
+//            thingModelList = filterList(thingModelList, thingModel -> thingModel.getIdentifier()
+//                    .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase()));
+//        }
+//        if (deviceDataReqVO.getName() != null) {
+//            thingModelList = filterList(thingModelList, thingModel -> thingModel.getName()
+//                    .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase()));
+//        }
+//        // 4. 获取设备属性最新数据
+//        thingModelList.forEach(thingModel -> {
+//            IotDevicePropertyDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thingModel.getIdentifier());
+//            if (deviceData == null) {
+//                deviceData = new IotDevicePropertyDO();
+//                deviceData.setProductKey(device.getProductKey());
+//                deviceData.setDeviceName(device.getDeviceName());
+//                deviceData.setIdentifier(thingModel.getIdentifier());
+//                deviceData.setDeviceId(deviceDataReqVO.getDeviceId());
+//                deviceData.setThingModelId(thingModel.getId());
+//                deviceData.setName(thingModel.getName());
+//                deviceData.setDataType(thingModel.getProperty().getDataType());
+//            }
+//            list.add(deviceData);
+//        });
+//        return list;
+        return null; // TODO 芋艿:晚点实现
     }
 
     @Override
     public PageResult<Map<String, Object>> getHistoryDeviceProperties(IotDeviceDataPageReqVO deviceDataReqVO) {
-        PageResult<Map<String, Object>> pageResult = new PageResult<>();
-        // 1. 获取设备信息
-        IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
-        // 2. 获取设备属性历史数据
-        SelectVisualDO selectVisualDO = new SelectVisualDO();
-        selectVisualDO.setDataBaseName(getDatabaseName());
-        selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()));
-        selectVisualDO.setDeviceKey(device.getDeviceKey());
-        selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier());
-        selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
-        selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
-        Map<String, Object> params = new HashMap<>();
-        params.put("rows", deviceDataReqVO.getPageSize());
-        params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize());
-        selectVisualDO.setParams(params);
-        pageResult.setList(devicePropertyDataMapper.selectHistoryDataList(selectVisualDO));
-        pageResult.setTotal(devicePropertyDataMapper.selectHistoryCount(selectVisualDO));
-        return pageResult;
+//        PageResult<Map<String, Object>> pageResult = new PageResult<>();
+//        // 1. 获取设备信息
+//        IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId());
+//        // 2. 获取设备属性历史数据
+//        SelectVisualDO selectVisualDO = new SelectVisualDO();
+//        selectVisualDO.setDataBaseName(getDatabaseName());
+//        selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName()));
+//        selectVisualDO.setDeviceKey(device.getDeviceKey());
+//        selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier());
+//        selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
+//        selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime());
+//        Map<String, Object> params = new HashMap<>();
+//        params.put("rows", deviceDataReqVO.getPageSize());
+//        params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize());
+//        selectVisualDO.setParams(params);
+//        pageResult.setList(devicePropertyDataMapper.selectHistoryDataList(selectVisualDO));
+//        pageResult.setTotal(devicePropertyDataMapper.selectHistoryCount(selectVisualDO));
+//        return pageResult;
+        return null; // TODO 芋艿:晚点实现
     }
 
     private String getDatabaseName() {

+ 1 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java

@@ -46,6 +46,7 @@ public interface IotThingModelService {
      */
     IotThingModelDO getThingModel(Long id);
 
+    // TODO @芋艿:增加缓存
     /**
      * 获得产品物模型列表
      *