Commit 4254c06c3764a8c8b25d4182ef8d50f03ef88bad

Authored by YunaiV
1 parent 5f7bb804

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

yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java
... ... @@ -53,6 +53,15 @@ public interface IotDeviceUpstreamApi {
53 53 @PostMapping(PREFIX + "/register")
54 54 CommonResult<Boolean> registerDevice(@Valid @RequestBody IotDeviceRegisterReqDTO registerReqDTO);
55 55  
  56 + // TODO @芋艿:这个需要 plugins 接入下
  57 + /**
  58 + * 注册子设备
  59 + *
  60 + * @param registerReqDTO 注册子设备 DTO
  61 + */
  62 + @PostMapping(PREFIX + "/register-sub")
  63 + CommonResult<Boolean> registerSubDevice(@Valid @RequestBody IotDeviceRegisterSubReqDTO registerReqDTO);
  64 +
56 65 // ========== 插件相关 ==========
57 66  
58 67 /**
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
3 3 import lombok.Data;
4 4  
5 5 /**
6   - * IoT 设备【注册】注册 Request DTO
  6 + * IoT 设备【注册】自己 Request DTO
7 7 *
8 8 * @author 芋道源码
9 9 */
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
  2 +
  3 +import jakarta.validation.constraints.NotEmpty;
  4 +import lombok.Data;
  5 +
  6 +import java.util.List;
  7 +
  8 +/**
  9 + * IoT 设备【注册】子设备 Request DTO
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Data
  14 +public class IotDeviceRegisterSubReqDTO extends IotDeviceUpstreamAbstractReqDTO {
  15 +
  16 + /**
  17 + * 子设备数组
  18 + */
  19 + @NotEmpty(message = "子设备不能为空")
  20 + private List<Device> params;
  21 +
  22 + /**
  23 + * 设备信息
  24 + */
  25 + @Data
  26 + public static class Device {
  27 +
  28 + /**
  29 + * 产品标识
  30 + */
  31 + @NotEmpty(message = "产品标识不能为空")
  32 + private String productKey;
  33 +
  34 + /**
  35 + * 设备名称
  36 + */
  37 + @NotEmpty(message = "设备名称不能为空")
  38 + private String deviceName;
  39 +
  40 + }
  41 +
  42 +}
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java
... ... @@ -29,8 +29,8 @@ public enum IotDeviceMessageIdentifierEnum {
29 29 OTA_REPORT("report"), // 上行
30 30  
31 31 REGISTER_REGISTER("register"), // 上行
32   - REGISTER_SUB_REGISTER("sub_register"), // 上行
33   - REGISTER_SUB_UNREGISTER("sub_unregister"),; // 下行
  32 + REGISTER_REGISTER_SUB("register_sub"), // 上行
  33 + REGISTER_UNREGISTER_SUB("unregister_sub"),; // 下行
34 34  
35 35 /**
36 36 * 标志符
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java
... ... @@ -46,4 +46,14 @@ public enum IotProductDeviceTypeEnum implements ArrayValuable&lt;Integer&gt; {
46 46 return GATEWAY.getType().equals(type);
47 47 }
48 48  
  49 + /**
  50 + * 判断是否是网关子设备
  51 + *
  52 + * @param type 类型
  53 + * @return 是否是网关子设备
  54 + */
  55 + public static boolean isGatewaySub(Integer type) {
  56 + return GATEWAY_SUB.getType().equals(type);
  57 + }
  58 +
49 59 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java
... ... @@ -49,6 +49,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi {
49 49 return success(true);
50 50 }
51 51  
  52 + @Override
  53 + public CommonResult<Boolean> registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO) {
  54 + deviceUpstreamService.registerSubDevice(registerReqDTO);
  55 + return success(true);
  56 + }
  57 +
52 58 // ========== 插件相关 ==========
53 59  
54 60 @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;
5 4 import jakarta.validation.constraints.Size;
6 5 import lombok.Data;
7 6  
... ... @@ -18,8 +17,7 @@ public class IotDeviceSaveReqVO {
18 17 @Size(max = 50, message = "设备编号长度不能超过 50 个字符")
19 18 private String deviceKey;
20 19  
21   - @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
22   - @NotEmpty(message = "设备名称不能为空")
  20 + @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.AUTO, example = "王五")
23 21 private String deviceName;
24 22  
25 23 @Schema(description = "备注名称", example = "张三")
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
... ... @@ -30,10 +30,12 @@ public interface IotDeviceService {
30 30 *
31 31 * @param productKey 产品标识
32 32 * @param deviceName 设备名称
  33 + * @param gatewayId 网关设备 ID
33 34 * @return 设备
34 35 */
35 36 IotDeviceDO createDevice(@NotEmpty(message = "产品标识不能为空") String productKey,
36   - @NotEmpty(message = "设备名称不能为空") String deviceName);
  37 + @NotEmpty(message = "设备名称不能为空") String deviceName,
  38 + Long gatewayId);
37 39  
38 40 /**
39 41 * 更新设备
... ... @@ -42,6 +44,17 @@ public interface IotDeviceService {
42 44 */
43 45 void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO);
44 46  
  47 + // TODO @芋艿:先这么实现。未来看情况,要不要自己实现
  48 + /**
  49 + * 更新设备的所属网关
  50 + *
  51 + * @param id 编号
  52 + * @param gatewayId 网关设备 ID
  53 + */
  54 + default void updateDeviceGateway(Long id, Long gatewayId) {
  55 + updateDevice(new IotDeviceSaveReqVO().setId(id).setGatewayId(gatewayId));
  56 + }
  57 +
45 58 /**
46 59 * 更新设备状态
47 60 *
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
... ... @@ -64,22 +64,10 @@ public class IotDeviceServiceImpl implements IotDeviceService {
64 64 if (product == null) {
65 65 throw exception(PRODUCT_NOT_EXISTS);
66 66 }
67   - // 1.2 校验设备标识是否唯一
68   - TenantUtils.executeIgnore(() -> {
69   - if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) {
70   - throw exception(PRODUCT_KEY_EXISTS);
71   - }
72   - });
73   - // 1.3 校验设备名称在同一产品下是否唯一
74   - if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceName()) != null) {
75   - throw exception(DEVICE_NAME_EXISTS);
76   - }
77   - // 1.4 校验父设备是否为合法网关
78   - if (IotProductDeviceTypeEnum.isGateway(product.getDeviceType())
79   - && createReqVO.getGatewayId() != null) {
80   - validateGatewayDeviceExists(createReqVO.getGatewayId());
81   - }
82   - // 1.5 校验分组存在
  67 + // 1.2 统一校验
  68 + validateCreateDeviceParam(product.getProductKey(), createReqVO.getDeviceName(), createReqVO.getDeviceKey(),
  69 + createReqVO.getGatewayId(), product);
  70 + // 1.3 校验分组存在
83 71 deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds());
84 72  
85 73 // 2. 插入到数据库
... ... @@ -90,34 +78,47 @@ public class IotDeviceServiceImpl implements IotDeviceService {
90 78 }
91 79  
92 80 @Override
93   - public IotDeviceDO createDevice(String productKey, String deviceName) {
  81 + public IotDeviceDO createDevice(String productKey, String deviceName, Long gatewayId) {
  82 + String deviceKey = generateDeviceKey();
94 83 // 1.1 校验产品是否存在
95 84 IotProductDO product = TenantUtils.executeIgnore(() ->
96 85 productService.getProductByProductKey(productKey));
97 86 if (product == null) {
98 87 throw exception(PRODUCT_NOT_EXISTS);
99 88 }
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 89 return TenantUtils.execute(product.getTenantId(), () -> {
108   - // 1.3 校验设备名称在同一产品下是否唯一
109   - if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), deviceName) != null) {
110   - throw exception(DEVICE_NAME_EXISTS);
111   - }
  90 + // 1.2 校验设备名称在同一产品下是否唯一
  91 + validateCreateDeviceParam(productKey, deviceName, deviceKey, gatewayId, product);
112 92  
113 93 // 2. 插入到数据库
114   - IotDeviceDO device = new IotDeviceDO().setDeviceName(deviceName).setDeviceKey(deviceKey);
  94 + IotDeviceDO device = new IotDeviceDO().setDeviceName(deviceName).setDeviceKey(deviceKey)
  95 + .setGatewayId(gatewayId);
115 96 initDevice(device, product);
116 97 deviceMapper.insert(device);
117 98 return device;
118 99 });
119 100 }
120 101  
  102 + private void validateCreateDeviceParam(String productKey, String deviceName, String deviceKey,
  103 + Long gatewayId, IotProductDO product) {
  104 + TenantUtils.executeIgnore(() -> {
  105 + // 校验设备名称在同一产品下是否唯一
  106 + if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) {
  107 + throw exception(DEVICE_NAME_EXISTS);
  108 + }
  109 + // 校验设备标识是否唯一
  110 + if (deviceMapper.selectByDeviceKey(deviceKey) != null) {
  111 + throw exception(DEVICE_KEY_EXISTS);
  112 + }
  113 + });
  114 +
  115 + // 校验父设备是否为合法网关
  116 + if (IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType())
  117 + && gatewayId != null) {
  118 + validateGatewayDeviceExists(gatewayId);
  119 + }
  120 + }
  121 +
121 122 private void initDevice(IotDeviceDO device, IotProductDO product) {
122 123 device.setProductId(product.getId()).setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
123 124 // 生成并设置必要的字段
... ... @@ -136,7 +137,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
136 137 // 1.1 校验存在
137 138 IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
138 139 // 1.2 校验父设备是否为合法网关
139   - if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType())
  140 + if (IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType())
140 141 && updateReqVO.getGatewayId() != null) {
141 142 validateGatewayDeviceExists(updateReqVO.getGatewayId());
142 143 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java
1 1 package cn.iocoder.yudao.module.iot.service.device.control;
2 2  
3   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO;
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;
6   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
  3 +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*;
7 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO;
8 5 import jakarta.validation.Valid;
9 6  
... ... @@ -51,4 +48,11 @@ public interface IotDeviceUpstreamService {
51 48 */
52 49 void registerDevice(IotDeviceRegisterReqDTO registerReqDTO);
53 50  
  51 + /**
  52 + * 注册子设备
  53 + *
  54 + * @param registerReqDTO 注册子设备 DTO
  55 + */
  56 + void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO);
  57 +
54 58 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java
1 1 package cn.iocoder.yudao.module.iot.service.device.control;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.hutool.core.lang.Assert;
4 5 import cn.hutool.core.util.IdUtil;
5 6 import cn.hutool.core.util.ObjUtil;
... ... @@ -13,6 +14,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
13 14 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum;
14 15 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum;
15 16 import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
  17 +import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
16 18 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
17 19 import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer;
18 20 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
... ... @@ -164,20 +166,30 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
164 166  
165 167 @Override
166 168 public void registerDevice(IotDeviceRegisterReqDTO registerReqDTO) {
167   - // 1.1 注册设备
168 169 log.info("[registerDevice][注册设备: {}]", registerReqDTO);
169   - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
170   - registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
171   - boolean register = device == null;
  170 + registerDevice0(registerReqDTO.getProductKey(), registerReqDTO.getDeviceName(), null, registerReqDTO);
  171 + }
  172 +
  173 + private void registerDevice0(String productKey, String deviceName, Long gatewayId,
  174 + IotDeviceUpstreamAbstractReqDTO registerReqDTO) {
  175 + // 1.1 注册设备
  176 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(productKey, deviceName);
  177 + boolean registerNew = device == null;
172 178 if (device == null) {
173   - device = deviceService.createDevice(registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
174   - log.info("[registerDevice][请求({}) 成功注册设备({})]", registerReqDTO, device);
  179 + device = deviceService.createDevice(productKey, deviceName, gatewayId);
  180 + log.info("[registerDevice0][消息({}) 设备({}/{}) 成功注册]", registerReqDTO, productKey, device);
  181 + } else if (gatewayId != null && ObjUtil.notEqual(device.getGatewayId(), gatewayId)) {
  182 + Long deviceId = device.getId();
  183 + TenantUtils.execute(device.getTenantId(),
  184 + () -> deviceService.updateDeviceGateway(deviceId, gatewayId));
  185 + log.info("[registerDevice0][消息({}) 设备({}/{}) 更新网关设备编号({})]",
  186 + registerReqDTO, productKey, device, gatewayId);
175 187 }
176 188 // 1.2 记录设备的最后时间
177 189 updateDeviceLastTime(device, registerReqDTO);
178 190  
179 191 // 2. 发送设备消息
180   - if (register) {
  192 + if (registerNew) {
181 193 IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class)
182 194 .setType(IotDeviceMessageTypeEnum.REGISTER.getType())
183 195 .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER.getIdentifier());
... ... @@ -185,6 +197,39 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
185 197 }
186 198 }
187 199  
  200 + @Override
  201 + public void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO) {
  202 + // 1.1 注册子设备
  203 + log.info("[registerSubDevice][注册子设备: {}]", registerReqDTO);
  204 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
  205 + registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
  206 + if (device == null) {
  207 + log.error("[registerSubDevice][设备({}/{}) 不存在]",
  208 + registerReqDTO.getProductKey(), registerReqDTO.getDeviceName());
  209 + return;
  210 + }
  211 + if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {
  212 + log.error("[registerSubDevice][设备({}/{}) 不是网关设备({}),无法进行注册]",
  213 + registerReqDTO.getProductKey(), registerReqDTO.getDeviceName(), device);
  214 + return;
  215 + }
  216 + // 1.2 记录设备的最后时间
  217 + updateDeviceLastTime(device, registerReqDTO);
  218 +
  219 + // 2. 处理子设备
  220 + if (CollUtil.isNotEmpty(registerReqDTO.getParams())) {
  221 + registerReqDTO.getParams().forEach(subDevice -> registerDevice0(
  222 + subDevice.getProductKey(), subDevice.getDeviceName(), device.getId(), registerReqDTO));
  223 + }
  224 +
  225 + // 3. 发送设备消息
  226 + IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class)
  227 + .setType(IotDeviceMessageTypeEnum.REGISTER.getType())
  228 + .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER_SUB.getIdentifier())
  229 + .setData(registerReqDTO.getParams());
  230 + sendDeviceMessage(message, device);
  231 + }
  232 +
188 233 private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) {
189 234 // 1. 【异步】记录设备与插件实例的映射
190 235 pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId());
... ...