Ver Fonte

【功能新增】IoT:设备状态上传的部分实现

YunaiV há 6 meses atrás
pai
commit
f5f8c418dc
14 ficheiros alterados com 96 adições e 52 exclusões
  1. 8 0
      yudao-module-iot/yudao-module-iot-api/pom.xml
  2. 3 4
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java
  3. 0 4
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java
  4. 0 4
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java
  5. 4 8
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStateUpdateReqDTO.java
  6. 3 4
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java
  7. 5 1
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java
  8. 3 3
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java
  9. 2 3
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java
  10. 4 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
  11. 2 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java
  12. 40 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java
  13. 4 4
      yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java
  14. 18 7
      yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java

+ 8 - 0
yudao-module-iot/yudao-module-iot-api/pom.xml

@@ -12,6 +12,7 @@
     <packaging>jar</packaging>
 
     <name>${project.artifactId}</name>
+    <!-- TODO 芋艿:需要在整理下,特别是 PF4J -->
     <description>
         物联网 模块 API,暴露给其它模块调用
     </description>
@@ -42,6 +43,13 @@
             </exclusions>
         </dependency>
 
+        <!-- 工具类相关 -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
+        </dependency>
+
         <!-- 参数校验 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>

+ 3 - 4
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java

@@ -3,11 +3,10 @@ package cn.iocoder.yudao.module.iot.api.device;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO;
 import cn.iocoder.yudao.module.iot.enums.ApiConstants;
 import jakarta.validation.Valid;
 import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 
 /**
@@ -26,8 +25,8 @@ public interface IotDeviceUpstreamApi {
      *
      * @param updateReqDTO 更新设备状态 DTO
      */
-    @PutMapping(PREFIX + "/update-status")
-    CommonResult<Boolean> updateDeviceStatus(@Valid @RequestBody IotDeviceStatusUpdateReqDTO updateReqDTO);
+    @PostMapping(PREFIX + "/update-state")
+    CommonResult<Boolean> updateDeviceState(@Valid @RequestBody IotDeviceStateUpdateReqDTO updateReqDTO);
 
     /**
      * 上报设备属性数据

+ 0 - 4
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java

@@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.iot.api.device.dto;
 
 import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.experimental.SuperBuilder;
 
 import java.util.Map;
 
@@ -11,8 +9,6 @@ import java.util.Map;
  * IoT 设备【事件】数据上报 Request DTO
  */
 @Data
-@SuperBuilder
-@NoArgsConstructor
 public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
 
     /**

+ 0 - 4
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java

@@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.iot.api.device.dto;
 
 import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.experimental.SuperBuilder;
 
 import java.util.Map;
 
@@ -11,8 +9,6 @@ import java.util.Map;
  * IoT 设备【属性】数据上报 Request DTO
  */
 @Data
-@SuperBuilder
-@NoArgsConstructor
 public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
 
     /**

+ 4 - 8
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java → yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStateUpdateReqDTO.java

@@ -2,24 +2,20 @@ package cn.iocoder.yudao.module.iot.api.device.dto;
 
 import cn.iocoder.yudao.framework.common.validation.InEnum;
 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
-import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
 import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.experimental.SuperBuilder;
 
 /**
  * IoT 设备状态更新 Request DTO
  */
 @Data
-@SuperBuilder
-@NoArgsConstructor
-public class IotDeviceStatusUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO {
+public class IotDeviceStateUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO {
 
     /**
      * 设备状态
      */
-    @NotEmpty(message = "设备状态不能为空")
+    @NotNull(message = "设备状态不能为空")
     @InEnum(IotDeviceStateEnum.class) // 只使用:在线、离线
-    private Integer status;
+    private Integer state;
 
 }

+ 3 - 4
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java

@@ -1,9 +1,9 @@
 package cn.iocoder.yudao.module.iot.api.device.dto;
 
+import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
-import lombok.NoArgsConstructor;
-import lombok.experimental.SuperBuilder;
 
 import java.time.LocalDateTime;
 
@@ -13,8 +13,6 @@ import java.time.LocalDateTime;
  * @author 芋道源码
  */
 @Data
-@SuperBuilder
-@NoArgsConstructor
 public abstract class IotDeviceUpstreamAbstractReqDTO {
 
     /**
@@ -41,6 +39,7 @@ public abstract class IotDeviceUpstreamAbstractReqDTO {
     /**
      * 上报时间
      */
+    @JsonSerialize(using = TimestampLocalDateTimeSerializer.class) // 解决 iot plugins 序列化 LocalDateTime 是数组,导致无法解析的问题
     private LocalDateTime reportTime;
 
 }

+ 5 - 1
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java

@@ -12,7 +12,11 @@ public enum IotDeviceMessageIdentifierEnum {
 
     PROPERTY_GET("get"),
     PROPERTY_SET("set"),
-    PROPERTY_REPORT("report");
+    PROPERTY_REPORT("report"),
+
+    STATE_ONLINE("online"),
+    STATE_OFFLINE("offline");
+
 
     /**
      * 标志符

+ 3 - 3
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.api.device;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO;
 import cn.iocoder.yudao.module.iot.service.device.upstream.IotDeviceUpstreamService;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.RestController;
@@ -23,8 +23,8 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi {
     private IotDeviceUpstreamService deviceUpstreamService;
 
     @Override
-    public CommonResult<Boolean> updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
-        deviceUpstreamService.updateDeviceStatus(updateReqDTO);
+    public CommonResult<Boolean> updateDeviceState(IotDeviceStateUpdateReqDTO updateReqDTO) {
+        deviceUpstreamService.updateDeviceState(updateReqDTO);
         return success(true);
     }
 

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

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.iot.emq.service;
 
-import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
 import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
@@ -34,11 +33,11 @@ public class EmqxServiceImpl implements EmqxService {
             String productKey = topic.split("/")[2];
             String deviceName = topic.split("/")[3];
             String message = new String(mqttMessage.getPayload());
-            IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder()
+//            IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder()
 //                    .productKey(productKey)
 //                    .deviceName(deviceName)
 //                    .properties(message) // TODO 芋艿:临时去掉,看看
-                    .build();
+//                    .build();
 //            iotDeviceDataService.saveDeviceProperty(createDTO);
         }
     }

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

@@ -395,10 +395,10 @@ public class IotDeviceServiceImpl implements IotDeviceService {
         // 2.1 情况一:属性上报
         String requestId = IdUtil.fastSimpleUUID();
         if (Objects.equals(reportReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) {
-            deviceUpstreamService.reportDeviceProperty(IotDevicePropertyReportReqDTO.builder()
-                    .requestId(requestId).reportTime(LocalDateTime.now())
-                    .productKey(device.getProductKey()).deviceName(device.getDeviceName())
-                    .properties((Map<String, Object>) reportReqVO.getData()).build());
+            deviceUpstreamService.reportDeviceProperty(((IotDevicePropertyReportReqDTO)
+                    new IotDevicePropertyReportReqDTO().setRequestId(requestId).setReportTime(LocalDateTime.now())
+                            .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName()))
+                    .setProperties((Map<String, Object>) reportReqVO.getData()));
             return;
         }
         // 2.2 情况二:事件上报

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

@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.service.device.upstream;
 
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO;
 
 /**
  * 设备上行 Service 接口
@@ -18,7 +18,7 @@ public interface IotDeviceUpstreamService {
      *
      * @param updateReqDTO 更新设备状态 DTO
      */
-    void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO);
+    void updateDeviceState(IotDeviceStateUpdateReqDTO updateReqDTO);
 
     /**
      * 上报设备属性数据

+ 40 - 4
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java

@@ -1,15 +1,20 @@
 package cn.iocoder.yudao.module.iot.service.device.upstream;
 
+import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceUpstreamAbstractReqDTO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer;
 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
@@ -19,6 +24,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import java.time.LocalDateTime;
+import java.util.Objects;
 
 /**
  * 设备上行 Service 实现类
@@ -37,9 +43,39 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
     private IotDeviceProducer deviceProducer;
 
     @Override
-    public void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
-        log.info("[updateDeviceStatus][更新设备状态: {}]", updateReqDTO);
-        // TODO 芋艿:插件状态
+    public void updateDeviceState(IotDeviceStateUpdateReqDTO updateReqDTO) {
+        Assert.isTrue(ObjectUtils.equalsAny(updateReqDTO.getState(),
+                IotDeviceStateEnum.ONLINE.getState(), IotDeviceStateEnum.OFFLINE.getState()),
+                "状态不合法");
+        // 1.1 获得设备
+        log.info("[updateDeviceState][更新设备状态: {}]", updateReqDTO);
+        IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
+                updateReqDTO.getProductKey(), updateReqDTO.getDeviceName());
+        if (device == null) {
+            log.error("[updateDeviceState][设备({}/{}) 不存在]",
+                    updateReqDTO.getProductKey(), updateReqDTO.getDeviceName());
+            return;
+        }
+        // 1.2 记录设备的最后时间
+        updateDeviceLastTime(device, updateReqDTO);
+        // 1.3 当前状态一致,不处理
+        if (Objects.equals(device.getState(), updateReqDTO.getState())) {
+            return;
+        }
+
+        // 2. 更新设备状态
+        TenantUtils.executeIgnore(() ->
+                deviceService.updateDeviceState(device.getId(), updateReqDTO.getState()));
+
+        // 3. TODO 芋艿:子设备的关联
+
+        // 4. 发送设备消息
+        IotDeviceMessage message = BeanUtils.toBean(updateReqDTO, IotDeviceMessage.class)
+                .setType(IotDeviceMessageTypeEnum.STATE.getType())
+                .setIdentifier(ObjUtil.equals(updateReqDTO.getState(), IotDeviceStateEnum.ONLINE.getState())
+                        ? IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()
+                        : IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier());
+        sendDeviceMessage(message, device);
     }
 
     @Override

+ 4 - 4
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java

@@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.client.RestTemplate;
 
@@ -29,9 +29,9 @@ public class DeviceDataApiClient implements IotDeviceUpstreamApi {
 
     // TODO @haohao:返回结果,不用 CommonResult 哈。
     @Override
-    public CommonResult<Boolean> updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
-        String url = deviceDataUrl + URL_PREFIX + "/update-status";
-        return doPost(url, updateReqDTO, "updateDeviceStatus");
+    public CommonResult<Boolean> updateDeviceState(IotDeviceStateUpdateReqDTO updateReqDTO) {
+        String url = deviceDataUrl + URL_PREFIX + "/update-state";
+        return doPost(url, updateReqDTO, "updateDeviceState");
     }
 
     @Override

+ 18 - 7
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java

@@ -1,14 +1,18 @@
 package cn.iocoder.yudao.module.iot.plugin.http.service;
 
+import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
 import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
 import io.vertx.core.Handler;
 import io.vertx.ext.web.RequestBody;
 import io.vertx.ext.web.RoutingContext;
 import lombok.extern.slf4j.Slf4j;
 
+import java.time.LocalDateTime;
 import java.util.Map;
 
 @Slf4j
@@ -43,20 +47,27 @@ public class HttpVertxHandler implements Handler<RoutingContext> {
         String id = jsonData.getStr("id");
 
         try {
-            IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder()
-                    .productKey(productKey)
-                    .deviceName(deviceName)
-                    .properties((Map<String, Object>) requestBody.asJsonObject().getMap().get("properties"))
-                    .build();
+            // TODO @haohao:pluginKey 需要设置
+            // 设备上线
+            deviceDataApi.updateDeviceState(((IotDeviceStateUpdateReqDTO)
+                    new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID())
+                            .setPluginKey("http")
+                            .setReportTime(LocalDateTime.now())
+                            .setProductKey(productKey).setDeviceName(deviceName))
+                    .setState(IotDeviceStateEnum.ONLINE.getState()));
 
-            deviceDataApi.reportDeviceProperty(reportReqDTO);
+            // 属性上报
+            deviceDataApi.reportDeviceProperty(((IotDevicePropertyReportReqDTO)
+                    new IotDevicePropertyReportReqDTO().setRequestId(IdUtil.fastSimpleUUID())
+                            .setPluginKey("http").setReportTime(LocalDateTime.now())
+                            .setProductKey(productKey).setDeviceName(deviceName))
+                    .setProperties((Map<String, Object>) requestBody.asJsonObject().getMap().get("properties")));
 
             ctx.response()
                     .setStatusCode(200)
                     .putHeader("Content-Type", "application/json; charset=UTF-8")
                     .end(createResponseJson(200, new JSONObject(), id, "success",
                             "thing.event.property.post", "1.0").toString());
-
         } catch (Exception e) {
             log.error("[HttpVertxHandler] 上报属性数据失败", e);
             ctx.response()