Commit 5f7bb8041f984980e9b56b66947e60a6dc2c6152

Authored by YunaiV
1 parent d718f801

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

Showing 13 changed files with 146 additions and 35 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java
1 1 package cn.iocoder.yudao.module.iot.api.device;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO;
5   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
6   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
7   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO;
  4 +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*;
8 5 import cn.iocoder.yudao.module.iot.enums.ApiConstants;
9 6 import jakarta.validation.Valid;
10 7 import org.springframework.web.bind.annotation.PostMapping;
... ... @@ -47,6 +44,15 @@ public interface IotDeviceUpstreamApi {
47 44 @PostMapping(PREFIX + "/report-event")
48 45 CommonResult<Boolean> reportDeviceEvent(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO);
49 46  
  47 + // TODO @芋艿:这个需要 plugins 接入下
  48 + /**
  49 + * 注册设备
  50 + *
  51 + * @param registerReqDTO 注册设备 DTO
  52 + */
  53 + @PostMapping(PREFIX + "/register")
  54 + CommonResult<Boolean> registerDevice(@Valid @RequestBody IotDeviceRegisterReqDTO registerReqDTO);
  55 +
50 56 // ========== 插件相关 ==========
51 57  
52 58 /**
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * IoT 设备【注册】注册 Request DTO
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +@Data
  11 +public class IotDeviceRegisterReqDTO extends IotDeviceUpstreamAbstractReqDTO {
  12 +}
... ...
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 {
26 26 OTA_UPGRADE("upgrade"), // 下行
27 27 OTA_PULL("pull"), // 上行
28 28 OTA_PROGRESS("progress"), // 上行
29   - OTA_REPORT("report"),; // 上行
  29 + OTA_REPORT("report"), // 上行
  30 +
  31 + REGISTER_REGISTER("register"), // 上行
  32 + REGISTER_SUB_REGISTER("sub_register"), // 上行
  33 + REGISTER_SUB_UNREGISTER("sub_unregister"),; // 下行
30 34  
31 35 /**
32 36 * 标志符
... ...
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;
14 14 public enum IotDeviceMessageTypeEnum implements ArrayValuable<String> {
15 15  
16 16 STATE("state"), // 设备状态
17   - PROPERTY("property"), // 设备属性
18   - EVENT("event"), // 设备事件
19   - SERVICE("service"), // 设备服务
20   - CONFIG("config"), // 设备配置
21   - OTA("ota"),; // 设备 OTA
  17 + PROPERTY("property"), // 设备属性:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
  18 + EVENT("event"), // 设备事件:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
  19 + SERVICE("service"), // 设备服务:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
  20 + CONFIG("config"), // 设备配置:可参考 https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1 远程配置
  21 + OTA("ota"), // 设备 OTA:可参考 https://help.aliyun.com/zh/iot/user-guide/ota-update OTA 升级
  22 + REGISTER("register"),; // 设备注册:可参考 https://help.aliyun.com/zh/iot/user-guide/register-devices 设备身份注册
22 23  
23 24 public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageTypeEnum::getType).toArray(String[]::new);
24 25  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java
1 1 package cn.iocoder.yudao.module.iot.api.device;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO;
5   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
6   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
7   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO;
  4 +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*;
8 5 import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceUpstreamService;
9 6 import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService;
10 7 import org.springframework.validation.annotation.Validated;
... ... @@ -46,6 +43,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi {
46 43 return success(true);
47 44 }
48 45  
  46 + @Override
  47 + public CommonResult<Boolean> registerDevice(IotDeviceRegisterReqDTO registerReqDTO) {
  48 + deviceUpstreamService.registerDevice(registerReqDTO);
  49 + return success(true);
  50 + }
  51 +
49 52 // ========== 插件相关 ==========
50 53  
51 54 @Override
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java
1 1 package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotEmpty;
4 5 import jakarta.validation.constraints.Size;
5 6 import lombok.Data;
6 7  
... ... @@ -18,6 +19,7 @@ public class IotDeviceSaveReqVO {
18 19 private String deviceKey;
19 20  
20 21 @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
  22 + @NotEmpty(message = "设备名称不能为空")
21 23 private String deviceName;
22 24  
23 25 @Schema(description = "备注名称", example = "张三")
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java
1 1 package cn.iocoder.yudao.module.iot.dal.dataobject.product;
2 2  
3   -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  3 +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
4 4 import com.baomidou.mybatisplus.annotation.KeySequence;
5 5 import com.baomidou.mybatisplus.annotation.TableId;
6 6 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -19,7 +19,7 @@ import lombok.*;
19 19 @Builder
20 20 @NoArgsConstructor
21 21 @AllArgsConstructor
22   -public class IotProductDO extends BaseDO {
  22 +public class IotProductDO extends TenantBaseDO {
23 23  
24 24 /**
25 25 * 产品 ID
... ...
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;
4 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
5 5 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
6 6 import jakarta.validation.Valid;
  7 +import jakarta.validation.constraints.NotEmpty;
7 8  
8 9 import javax.annotation.Nullable;
9 10 import java.util.Collection;
... ... @@ -17,7 +18,7 @@ import java.util.List;
17 18 public interface IotDeviceService {
18 19  
19 20 /**
20   - * 创建设备
  21 + * 【管理员】创建设备
21 22 *
22 23 * @param createReqVO 创建信息
23 24 * @return 编号
... ... @@ -25,6 +26,16 @@ public interface IotDeviceService {
25 26 Long createDevice(@Valid IotDeviceSaveReqVO createReqVO);
26 27  
27 28 /**
  29 + * 【设备注册】创建设备
  30 + *
  31 + * @param productKey 产品标识
  32 + * @param deviceName 设备名称
  33 + * @return 设备
  34 + */
  35 + IotDeviceDO createDevice(@NotEmpty(message = "产品标识不能为空") String productKey,
  36 + @NotEmpty(message = "设备名称不能为空") String deviceName);
  37 +
  38 + /**
28 39 * 更新设备
29 40 *
30 41 * @param updateReqVO 更新信息
... ...
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 {
71 71 }
72 72 });
73 73 // 1.3 校验设备名称在同一产品下是否唯一
74   - if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) {
  74 + if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceName()) != null) {
75 75 throw exception(DEVICE_NAME_EXISTS);
76 76 }
77 77 // 1.4 校验父设备是否为合法网关
... ... @@ -82,24 +82,55 @@ public class IotDeviceServiceImpl implements IotDeviceService {
82 82 // 1.5 校验分组存在
83 83 deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds());
84 84  
85   - // 2.1 转换 VO 为 DO
86   - // TODO @芋艿:各种 mqtt 是不是可以简化!
87   - IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> {
88   - o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
89   - // 生成并设置必要的字段
90   - o.setDeviceSecret(generateDeviceSecret())
91   - .setMqttClientId(generateMqttClientId())
92   - .setMqttUsername(generateMqttUsername(o.getDeviceName(), o.getProductKey()))
93   - .setMqttPassword(generateMqttPassword());
94   - // 设置设备状态为未激活
95   - o.setState(IotDeviceStateEnum.INACTIVE.getState());
96   - });
97   - // 2.2 插入到数据库
  85 + // 2. 插入到数据库
  86 + IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class);
  87 + initDevice(device, product);
98 88 deviceMapper.insert(device);
99 89 return device.getId();
100 90 }
101 91  
102 92 @Override
  93 + public IotDeviceDO createDevice(String productKey, String deviceName) {
  94 + // 1.1 校验产品是否存在
  95 + IotProductDO product = TenantUtils.executeIgnore(() ->
  96 + productService.getProductByProductKey(productKey));
  97 + if (product == null) {
  98 + throw exception(PRODUCT_NOT_EXISTS);
  99 + }
  100 + // 1.2 校验设备标识是否唯一
  101 + String deviceKey = generateDeviceKey();
  102 + TenantUtils.executeIgnore(() -> {
  103 + if (deviceMapper.selectByDeviceKey(deviceKey) != null) {
  104 + throw exception(PRODUCT_KEY_EXISTS);
  105 + }
  106 + });
  107 + return TenantUtils.execute(product.getTenantId(), () -> {
  108 + // 1.3 校验设备名称在同一产品下是否唯一
  109 + if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), deviceName) != null) {
  110 + throw exception(DEVICE_NAME_EXISTS);
  111 + }
  112 +
  113 + // 2. 插入到数据库
  114 + IotDeviceDO device = new IotDeviceDO().setDeviceName(deviceName).setDeviceKey(deviceKey);
  115 + initDevice(device, product);
  116 + deviceMapper.insert(device);
  117 + return device;
  118 + });
  119 + }
  120 +
  121 + private void initDevice(IotDeviceDO device, IotProductDO product) {
  122 + device.setProductId(product.getId()).setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
  123 + // 生成并设置必要的字段
  124 + // TODO @芋艿:各种 mqtt 是不是可以简化!
  125 + device.setDeviceSecret(generateDeviceSecret())
  126 + .setMqttClientId(generateMqttClientId())
  127 + .setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey()))
  128 + .setMqttPassword(generateMqttPassword());
  129 + // 设置设备状态为未激活
  130 + device.setState(IotDeviceStateEnum.INACTIVE.getState());
  131 + }
  132 +
  133 + @Override
103 134 public void updateDevice(IotDeviceSaveReqVO updateReqVO) {
104 135 updateReqVO.setDeviceKey(null).setDeviceName(null).setProductId(null); // 不允许更新
105 136 // 1.1 校验存在
... ...
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;
2 2  
3 3 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO;
4 4 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
  5 +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceRegisterReqDTO;
5 6 import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
6 7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO;
7 8 import jakarta.validation.Valid;
... ... @@ -43,4 +44,11 @@ public interface IotDeviceUpstreamService {
43 44 */
44 45 void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO);
45 46  
  47 + /**
  48 + * 注册设备
  49 + *
  50 + * @param registerReqDTO 注册设备 DTO
  51 + */
  52 + void registerDevice(IotDeviceRegisterReqDTO registerReqDTO);
  53 +
46 54 }
... ...
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;
7 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 9 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
10   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO;
11   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
12   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
13   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceUpstreamAbstractReqDTO;
  10 +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*;
14 11 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO;
15 12 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
16 13 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
... ... @@ -165,6 +162,29 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
165 162 sendDeviceMessage(message, device);
166 163 }
167 164  
  165 + @Override
  166 + public void registerDevice(IotDeviceRegisterReqDTO registerReqDTO) {
  167 + // 1.1 注册设备
  168 + log.info("[registerDevice][注册设备: {}]", registerReqDTO);
  169 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
  170 + registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
  171 + boolean register = device == null;
  172 + if (device == null) {
  173 + device = deviceService.createDevice(registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
  174 + log.info("[registerDevice][请求({}) 成功注册设备({})]", registerReqDTO, device);
  175 + }
  176 + // 1.2 记录设备的最后时间
  177 + updateDeviceLastTime(device, registerReqDTO);
  178 +
  179 + // 2. 发送设备消息
  180 + if (register) {
  181 + IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class)
  182 + .setType(IotDeviceMessageTypeEnum.REGISTER.getType())
  183 + .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER.getIdentifier());
  184 + sendDeviceMessage(message, device);
  185 + }
  186 + }
  187 +
168 188 private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) {
169 189 // 1. 【异步】记录设备与插件实例的映射
170 190 pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId());
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
... ... @@ -46,6 +46,14 @@ public interface IotProductService {
46 46 IotProductDO getProduct(Long id);
47 47  
48 48 /**
  49 + * 根据产品 key 获得产品
  50 + *
  51 + * @param productKey 产品 key
  52 + * @return 产品
  53 + */
  54 + IotProductDO getProductByProductKey(String productKey);
  55 +
  56 + /**
49 57 * 校验产品存在
50 58 *
51 59 * @param id 编号
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
... ... @@ -106,6 +106,11 @@ public class IotProductServiceImpl implements IotProductService {
106 106 }
107 107  
108 108 @Override
  109 + public IotProductDO getProductByProductKey(String productKey) {
  110 + return productMapper.selectByProductKey(productKey);
  111 + }
  112 +
  113 + @Override
109 114 public PageResult<IotProductDO> getProductPage(IotProductPageReqVO pageReqVO) {
110 115 return productMapper.selectPage(pageReqVO);
111 116 }
... ...