Selaa lähdekoodia

【功能新增】IoT:设备注册 register 逻辑

YunaiV 6 kuukautta sitten
vanhempi
commit
5f7bb8041f
13 muutettua tiedostoa jossa 146 lisäystä ja 35 poistoa
  1. 10 4
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java
  2. 12 0
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java
  3. 5 1
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java
  4. 6 5
      yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java
  5. 7 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java
  6. 2 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java
  7. 2 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java
  8. 12 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
  9. 45 14
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
  10. 8 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java
  11. 24 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java
  12. 8 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
  13. 5 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java

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

@@ -1,10 +1,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.control.upstream.IotDeviceEventReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*;
 import cn.iocoder.yudao.module.iot.enums.ApiConstants;
 import jakarta.validation.Valid;
 import org.springframework.web.bind.annotation.PostMapping;
@@ -47,6 +44,15 @@ public interface IotDeviceUpstreamApi {
     @PostMapping(PREFIX + "/report-event")
     CommonResult<Boolean> reportDeviceEvent(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO);
 
+    // TODO @芋艿:这个需要 plugins 接入下
+    /**
+     * 注册设备
+     *
+     * @param registerReqDTO 注册设备 DTO
+     */
+    @PostMapping(PREFIX + "/register")
+    CommonResult<Boolean> registerDevice(@Valid @RequestBody IotDeviceRegisterReqDTO registerReqDTO);
+
     // ========== 插件相关 ==========
 
     /**

+ 12 - 0
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java

@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
+
+import lombok.Data;
+
+/**
+ * IoT 设备【注册】注册 Request DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class IotDeviceRegisterReqDTO extends IotDeviceUpstreamAbstractReqDTO {
+}

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

@@ -26,7 +26,11 @@ public enum IotDeviceMessageIdentifierEnum {
     OTA_UPGRADE("upgrade"), // 下行
     OTA_PULL("pull"), // 上行
     OTA_PROGRESS("progress"), // 上行
-    OTA_REPORT("report"),; // 上行
+    OTA_REPORT("report"), // 上行
+
+    REGISTER_REGISTER("register"), // 上行
+    REGISTER_SUB_REGISTER("sub_register"), // 上行
+    REGISTER_SUB_UNREGISTER("sub_unregister"),; // 下行
 
     /**
      * 标志符

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

@@ -14,11 +14,12 @@ import java.util.Arrays;
 public enum IotDeviceMessageTypeEnum implements ArrayValuable<String> {
 
     STATE("state"), // 设备状态
-    PROPERTY("property"), // 设备属性
-    EVENT("event"), // 设备事件
-    SERVICE("service"), // 设备服务
-    CONFIG("config"), // 设备配置
-    OTA("ota"),; // 设备 OTA
+    PROPERTY("property"), // 设备属性:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
+    EVENT("event"), // 设备事件:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
+    SERVICE("service"), // 设备服务:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
+    CONFIG("config"), // 设备配置:可参考 https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1 远程配置
+    OTA("ota"), // 设备 OTA:可参考 https://help.aliyun.com/zh/iot/user-guide/ota-update OTA 升级
+    REGISTER("register"),; // 设备注册:可参考 https://help.aliyun.com/zh/iot/user-guide/register-devices 设备身份注册
 
     public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageTypeEnum::getType).toArray(String[]::new);
 

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

@@ -1,10 +1,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.control.upstream.IotDeviceEventReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*;
 import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceUpstreamService;
 import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService;
 import org.springframework.validation.annotation.Validated;
@@ -46,6 +43,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi {
         return success(true);
     }
 
+    @Override
+    public CommonResult<Boolean> registerDevice(IotDeviceRegisterReqDTO registerReqDTO) {
+        deviceUpstreamService.registerDevice(registerReqDTO);
+        return success(true);
+    }
+
     // ========== 插件相关 ==========
 
     @Override

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

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
 
 import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.Size;
 import lombok.Data;
 
@@ -18,6 +19,7 @@ public class IotDeviceSaveReqVO {
     private String deviceKey;
 
     @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+    @NotEmpty(message = "设备名称不能为空")
     private String deviceName;
 
     @Schema(description = "备注名称", example = "张三")

+ 2 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java

@@ -1,6 +1,6 @@
 package cn.iocoder.yudao.module.iot.dal.dataobject.product;
 
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -19,7 +19,7 @@ import lombok.*;
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class IotProductDO extends BaseDO {
+public class IotProductDO extends TenantBaseDO {
 
     /**
      * 产品 ID

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

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
 
 import javax.annotation.Nullable;
 import java.util.Collection;
@@ -17,13 +18,23 @@ import java.util.List;
 public interface IotDeviceService {
 
     /**
-     * 创建设备
+     * 【管理员】创建设备
      *
      * @param createReqVO 创建信息
      * @return 编号
      */
     Long createDevice(@Valid IotDeviceSaveReqVO createReqVO);
 
+    /**
+     * 【设备注册】创建设备
+     *
+     * @param productKey 产品标识
+     * @param deviceName 设备名称
+     * @return 设备
+     */
+    IotDeviceDO createDevice(@NotEmpty(message = "产品标识不能为空") String productKey,
+                             @NotEmpty(message = "设备名称不能为空") String deviceName);
+
     /**
      * 更新设备
      *

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

@@ -71,7 +71,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
             }
         });
         // 1.3 校验设备名称在同一产品下是否唯一
-        if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) {
+        if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceName()) != null) {
             throw exception(DEVICE_NAME_EXISTS);
         }
         // 1.4 校验父设备是否为合法网关
@@ -82,23 +82,54 @@ public class IotDeviceServiceImpl implements IotDeviceService {
         // 1.5 校验分组存在
         deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds());
 
-        // 2.1 转换 VO 为 DO
-        // TODO @芋艿:各种 mqtt 是不是可以简化!
-        IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> {
-            o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
-            // 生成并设置必要的字段
-            o.setDeviceSecret(generateDeviceSecret())
-                    .setMqttClientId(generateMqttClientId())
-                    .setMqttUsername(generateMqttUsername(o.getDeviceName(), o.getProductKey()))
-                    .setMqttPassword(generateMqttPassword());
-            // 设置设备状态为未激活
-            o.setState(IotDeviceStateEnum.INACTIVE.getState());
-        });
-        // 2.2 插入到数据库
+        // 2. 插入到数据库
+        IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class);
+        initDevice(device, product);
         deviceMapper.insert(device);
         return device.getId();
     }
 
+    @Override
+    public IotDeviceDO createDevice(String productKey, String deviceName) {
+        // 1.1 校验产品是否存在
+        IotProductDO product = TenantUtils.executeIgnore(() ->
+                productService.getProductByProductKey(productKey));
+        if (product == null) {
+            throw exception(PRODUCT_NOT_EXISTS);
+        }
+        // 1.2 校验设备标识是否唯一
+        String deviceKey = generateDeviceKey();
+        TenantUtils.executeIgnore(() -> {
+            if (deviceMapper.selectByDeviceKey(deviceKey) != null) {
+                throw exception(PRODUCT_KEY_EXISTS);
+            }
+        });
+        return TenantUtils.execute(product.getTenantId(), () -> {
+            // 1.3 校验设备名称在同一产品下是否唯一
+            if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), deviceName) != null) {
+                throw exception(DEVICE_NAME_EXISTS);
+            }
+
+            // 2. 插入到数据库
+            IotDeviceDO device = new IotDeviceDO().setDeviceName(deviceName).setDeviceKey(deviceKey);
+            initDevice(device, product);
+            deviceMapper.insert(device);
+            return device;
+        });
+    }
+
+    private void initDevice(IotDeviceDO device, IotProductDO product) {
+        device.setProductId(product.getId()).setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
+        // 生成并设置必要的字段
+        // TODO @芋艿:各种 mqtt 是不是可以简化!
+        device.setDeviceSecret(generateDeviceSecret())
+                .setMqttClientId(generateMqttClientId())
+                .setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey()))
+                .setMqttPassword(generateMqttPassword());
+        // 设置设备状态为未激活
+        device.setState(IotDeviceStateEnum.INACTIVE.getState());
+    }
+
     @Override
     public void updateDevice(IotDeviceSaveReqVO updateReqVO) {
         updateReqVO.setDeviceKey(null).setDeviceName(null).setProductId(null); // 不允许更新

+ 8 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.device.control;
 
 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO;
 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceRegisterReqDTO;
 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO;
 import jakarta.validation.Valid;
@@ -43,4 +44,11 @@ public interface IotDeviceUpstreamService {
      */
     void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO);
 
+    /**
+     * 注册设备
+     *
+     * @param registerReqDTO 注册设备 DTO
+     */
+    void registerDevice(IotDeviceRegisterReqDTO registerReqDTO);
+
 }

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

@@ -7,10 +7,7 @@ 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.control.upstream.IotDeviceEventReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
-import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceUpstreamAbstractReqDTO;
+import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*;
 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
@@ -165,6 +162,29 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
         sendDeviceMessage(message, device);
     }
 
+    @Override
+    public void registerDevice(IotDeviceRegisterReqDTO registerReqDTO) {
+        // 1.1 注册设备
+        log.info("[registerDevice][注册设备: {}]", registerReqDTO);
+        IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
+                registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
+        boolean register = device == null;
+        if (device == null) {
+            device = deviceService.createDevice(registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
+            log.info("[registerDevice][请求({}) 成功注册设备({})]", registerReqDTO, device);
+        }
+        // 1.2 记录设备的最后时间
+        updateDeviceLastTime(device, registerReqDTO);
+
+        // 2. 发送设备消息
+        if (register) {
+            IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class)
+                    .setType(IotDeviceMessageTypeEnum.REGISTER.getType())
+                    .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER.getIdentifier());
+            sendDeviceMessage(message, device);
+        }
+    }
+
     private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) {
         // 1. 【异步】记录设备与插件实例的映射
         pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId());

+ 8 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java

@@ -45,6 +45,14 @@ public interface IotProductService {
      */
     IotProductDO getProduct(Long id);
 
+    /**
+     * 根据产品 key 获得产品
+     *
+     * @param productKey 产品 key
+     * @return 产品
+     */
+    IotProductDO getProductByProductKey(String productKey);
+
     /**
      * 校验产品存在
      *

+ 5 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java

@@ -105,6 +105,11 @@ public class IotProductServiceImpl implements IotProductService {
         return productMapper.selectById(id);
     }
 
+    @Override
+    public IotProductDO getProductByProductKey(String productKey) {
+        return productMapper.selectByProductKey(productKey);
+    }
+
     @Override
     public PageResult<IotProductDO> getProductPage(IotProductPageReqVO pageReqVO) {
         return productMapper.selectPage(pageReqVO);