Commit 18ed7b50beedd93331ee42bec0ccb4db7ca248c0
1 parent
a2750693
feat(iot):【网关设备:60%】整体初步实现(优化部分代码)
Showing
14 changed files
with
155 additions
and
216 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
| @@ -40,8 +40,6 @@ public interface ErrorCodeConstants { | @@ -40,8 +40,6 @@ public interface ErrorCodeConstants { | ||
| 40 | ErrorCode DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID = new ErrorCode(1_050_003_101, "子设备用户名格式无效"); | 40 | ErrorCode DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID = new ErrorCode(1_050_003_101, "子设备用户名格式无效"); |
| 41 | ErrorCode DEVICE_TOPO_SUB_DEVICE_AUTH_FAILED = new ErrorCode(1_050_003_102, "子设备认证失败"); | 41 | ErrorCode DEVICE_TOPO_SUB_DEVICE_AUTH_FAILED = new ErrorCode(1_050_003_102, "子设备认证失败"); |
| 42 | ErrorCode DEVICE_TOPO_SUB_NOT_BINDTO_GATEWAY = new ErrorCode(1_050_003_103, "子设备【{}/{}】未绑定到该网关"); | 42 | ErrorCode DEVICE_TOPO_SUB_NOT_BINDTO_GATEWAY = new ErrorCode(1_050_003_103, "子设备【{}/{}】未绑定到该网关"); |
| 43 | - // TODO @AI:这里的错误码校验,要不要使用? | ||
| 44 | - ErrorCode DEVICE_TOPO_SUB_DEVICE_NOT_BOUND = new ErrorCode(1_050_003_104, "子设备【{}/{}】未绑定到任何网关"); | ||
| 45 | // 子设备注册相关错误码 1-050-003-200 | 43 | // 子设备注册相关错误码 1-050-003-200 |
| 46 | ErrorCode DEVICE_SUB_REGISTER_PARAMS_INVALID = new ErrorCode(1_050_003_200, "子设备注册参数无效"); | 44 | ErrorCode DEVICE_SUB_REGISTER_PARAMS_INVALID = new ErrorCode(1_050_003_200, "子设备注册参数无效"); |
| 47 | ErrorCode DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_201, "产品【{}】不是网关子设备类型"); | 45 | ErrorCode DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_201, "产品【{}】不是网关子设备类型"); |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
| @@ -471,7 +471,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { | @@ -471,7 +471,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { | ||
| 471 | 471 | ||
| 472 | // 3. 校验子设备拓扑关系:子设备必须先绑定到某网关才能认证上线 | 472 | // 3. 校验子设备拓扑关系:子设备必须先绑定到某网关才能认证上线 |
| 473 | if (IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType()) | 473 | if (IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType()) |
| 474 | - && device.getGatewayId() != null) { | 474 | + && device.getGatewayId() == null) { |
| 475 | log.warn("[authDevice][子设备({}/{}) 未绑定到任何网关,认证失败]", productKey, deviceName); | 475 | log.warn("[authDevice][子设备({}/{}) 未绑定到任何网关,认证失败]", productKey, deviceName); |
| 476 | return false; | 476 | return false; |
| 477 | } | 477 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java
| 1 | package cn.iocoder.yudao.module.iot.service.device.message; | 1 | package cn.iocoder.yudao.module.iot.service.device.message; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 3 | import cn.hutool.core.date.LocalDateTimeUtil; | 4 | import cn.hutool.core.date.LocalDateTimeUtil; |
| 4 | import cn.hutool.core.lang.Assert; | 5 | import cn.hutool.core.lang.Assert; |
| 5 | import cn.hutool.core.map.MapUtil; | 6 | import cn.hutool.core.map.MapUtil; |
| 7 | +import cn.hutool.core.util.ObjectUtil; | ||
| 6 | import cn.hutool.core.util.StrUtil; | 8 | import cn.hutool.core.util.StrUtil; |
| 7 | import cn.hutool.extra.spring.SpringUtil; | 9 | import cn.hutool.extra.spring.SpringUtil; |
| 8 | import cn.iocoder.yudao.framework.common.exception.ServiceException; | 10 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| @@ -183,10 +185,9 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -183,10 +185,9 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 183 | // 2. 记录消息 | 185 | // 2. 记录消息 |
| 184 | getSelf().createDeviceLogAsync(message); | 186 | getSelf().createDeviceLogAsync(message); |
| 185 | 187 | ||
| 186 | - // TODO @AI:我在想,是不是批量上传后,还是得 reply 。因为打包上传的时候,只是那条消息的回复。然后,需要单独给每个子消息回复,后续至于怎么使用,是不是得看具体业务了;例如说:1)批量上传属性,默认回复是批量上传的消息;然后,每个属性、事件拆包消息,单独回复,后续网关设备按需回复给子设备。 | ||
| 187 | - // 3. 回复消息。前提:非 _reply 消息、非禁用回复的消息、非拆包消息 | 188 | + // 3. 回复消息。前提:非 _reply 消息、非禁用回复的消息 |
| 188 | if (IotDeviceMessageUtils.isReplyMessage(message) | 189 | if (IotDeviceMessageUtils.isReplyMessage(message) |
| 189 | - || !message.needReply() | 190 | + || IotDeviceMessageMethodEnum.isReplyDisabled(message.getMethod()) |
| 190 | || StrUtil.isEmpty(message.getServerId())) { | 191 | || StrUtil.isEmpty(message.getServerId())) { |
| 191 | return; | 192 | return; |
| 192 | } | 193 | } |
| @@ -242,7 +243,6 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -242,7 +243,6 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 242 | if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod())) { | 243 | if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod())) { |
| 243 | return handleTopoDelete(message, device); | 244 | return handleTopoDelete(message, device); |
| 244 | } | 245 | } |
| 245 | - | ||
| 246 | // 获取拓扑关系 | 246 | // 获取拓扑关系 |
| 247 | if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_GET.getMethod())) { | 247 | if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.TOPO_GET.getMethod())) { |
| 248 | return handleTopoGet(device); | 248 | return handleTopoGet(device); |
| @@ -267,57 +267,81 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -267,57 +267,81 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 267 | * @return 响应数据 | 267 | * @return 响应数据 |
| 268 | */ | 268 | */ |
| 269 | private Object handleTopoAdd(IotDeviceMessage message, IotDeviceDO gatewayDevice) { | 269 | private Object handleTopoAdd(IotDeviceMessage message, IotDeviceDO gatewayDevice) { |
| 270 | - // TODO @AI:这里是不是 1.1 1.2 1.3 这样?修改前,我们确认下,有没模块是这么写的;iot 消息处理里; | ||
| 271 | - // 1. 校验网关设备类型 | 270 | + // 1.1 校验网关设备类型 |
| 272 | if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { | 271 | if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { |
| 273 | throw exception(DEVICE_NOT_GATEWAY); | 272 | throw exception(DEVICE_NOT_GATEWAY); |
| 274 | } | 273 | } |
| 275 | - | ||
| 276 | - // 2. 解析参数 | 274 | + // 1.2 解析参数 |
| 277 | // TODO @AI:是不是 parseObject 增加一个方法,允许传入 object 类型,避免先转 jsonString 再 parseObject ; | 275 | // TODO @AI:是不是 parseObject 增加一个方法,允许传入 object 类型,避免先转 jsonString 再 parseObject ; |
| 278 | IotDeviceTopoAddReqDTO params = JsonUtils.parseObject(JsonUtils.toJsonString(message.getParams()), | 276 | IotDeviceTopoAddReqDTO params = JsonUtils.parseObject(JsonUtils.toJsonString(message.getParams()), |
| 279 | IotDeviceTopoAddReqDTO.class); | 277 | IotDeviceTopoAddReqDTO.class); |
| 280 | - if (params == null) { | 278 | + if (params == null || CollUtil.isEmpty(params.getSubDevices())) { |
| 281 | throw exception(DEVICE_TOPO_PARAMS_INVALID); | 279 | throw exception(DEVICE_TOPO_PARAMS_INVALID); |
| 282 | } | 280 | } |
| 283 | 281 | ||
| 284 | - // 3. 解析子设备信息 | ||
| 285 | - IotDeviceAuthUtils.DeviceInfo subDeviceInfo = IotDeviceAuthUtils.parseUsername(params.getUsername()); | 282 | + // 2. 遍历处理每个子设备 |
| 283 | + // TODO @AI:processTopoAddSubDevice 不要抽成小方法; | ||
| 284 | + List<IotDeviceDO> addedSubDevices = new ArrayList<>(params.getSubDevices().size()); | ||
| 285 | + for (IotDeviceAuthReqDTO subDeviceAuth : params.getSubDevices()) { | ||
| 286 | + try { | ||
| 287 | + IotDeviceDO subDevice = processTopoAddSubDevice(subDeviceAuth, gatewayDevice); | ||
| 288 | + addedSubDevices.add(subDevice); | ||
| 289 | + } catch (Exception ex) { | ||
| 290 | + log.warn("[handleTopoAdd][网关({}/{}) 添加子设备失败,username={}]", | ||
| 291 | + gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), | ||
| 292 | + subDeviceAuth.getUsername(), ex); | ||
| 293 | + } | ||
| 294 | + } | ||
| 295 | + // TODO @AI:http://help.aliyun.com/zh/marketplace/add-topological-relationship 要回复的! | ||
| 296 | + | ||
| 297 | + // 3. 发送拓扑变更通知 | ||
| 298 | + // TODO @AI:这里不应该发,它更多发生在,管理后台改动后,主动下发;http://help.aliyun.com/zh/marketplace/notify-gateway-topology-changes | ||
| 299 | + for (IotDeviceDO subDevice : addedSubDevices) { | ||
| 300 | + sendTopoChangeNotify(gatewayDevice, "add", subDevice); | ||
| 301 | + } | ||
| 302 | + return null; | ||
| 303 | + } | ||
| 304 | + | ||
| 305 | + /** | ||
| 306 | + * 处理单个子设备的拓扑添加 | ||
| 307 | + * | ||
| 308 | + * @param subDeviceAuth 子设备认证信息 | ||
| 309 | + * @param gatewayDevice 网关设备 | ||
| 310 | + * @return 添加成功的子设备,失败返回 null | ||
| 311 | + */ | ||
| 312 | + private IotDeviceDO processTopoAddSubDevice(IotDeviceAuthReqDTO subDeviceAuth, IotDeviceDO gatewayDevice) { | ||
| 313 | + // 1.1 解析子设备信息 | ||
| 314 | + IotDeviceAuthUtils.DeviceInfo subDeviceInfo = IotDeviceAuthUtils.parseUsername(subDeviceAuth.getUsername()); | ||
| 286 | if (subDeviceInfo == null) { | 315 | if (subDeviceInfo == null) { |
| 287 | throw exception(DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID); | 316 | throw exception(DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID); |
| 288 | } | 317 | } |
| 289 | - | ||
| 290 | - // 4. 校验子设备认证信息 | ||
| 291 | - // TODO @AI:链式调用; | ||
| 292 | - IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO(); | ||
| 293 | - authReqDTO.setClientId(params.getClientId()); | ||
| 294 | - authReqDTO.setUsername(params.getUsername()); | ||
| 295 | - authReqDTO.setPassword(params.getPassword()); | ||
| 296 | - if (!deviceService.authDevice(authReqDTO)) { | 318 | + // 1.2 校验子设备认证信息 |
| 319 | + if (!deviceService.authDevice(subDeviceAuth)) { | ||
| 297 | throw exception(DEVICE_TOPO_SUB_DEVICE_AUTH_FAILED); | 320 | throw exception(DEVICE_TOPO_SUB_DEVICE_AUTH_FAILED); |
| 298 | } | 321 | } |
| 299 | 322 | ||
| 300 | - // 5. 获取子设备 | 323 | + // 1.3 获取子设备 |
| 301 | IotDeviceDO subDevice = deviceService.getDeviceFromCache(subDeviceInfo.getProductKey(), subDeviceInfo.getDeviceName()); | 324 | IotDeviceDO subDevice = deviceService.getDeviceFromCache(subDeviceInfo.getProductKey(), subDeviceInfo.getDeviceName()); |
| 302 | if (subDevice == null) { | 325 | if (subDevice == null) { |
| 303 | throw exception(DEVICE_NOT_EXISTS); | 326 | throw exception(DEVICE_NOT_EXISTS); |
| 304 | } | 327 | } |
| 305 | - | ||
| 306 | - // 6. 校验子设备类型 | 328 | + // 1.4 校验子设备类型 |
| 307 | if (!IotProductDeviceTypeEnum.isGatewaySub(subDevice.getDeviceType())) { | 329 | if (!IotProductDeviceTypeEnum.isGatewaySub(subDevice.getDeviceType())) { |
| 308 | throw exception(DEVICE_NOT_GATEWAY_SUB, subDevice.getProductKey(), subDevice.getDeviceName()); | 330 | throw exception(DEVICE_NOT_GATEWAY_SUB, subDevice.getProductKey(), subDevice.getDeviceName()); |
| 309 | } | 331 | } |
| 332 | + // 1.5 校验子设备是否已绑定到其他网关 | ||
| 333 | + if (subDevice.getGatewayId() != null | ||
| 334 | + && ObjectUtil.notEqual(subDevice.getGatewayId(), gatewayDevice.getId())) { | ||
| 335 | + throw exception(DEVICE_GATEWAY_BINDTO_EXISTS, subDevice.getProductKey(), subDevice.getDeviceName()); | ||
| 336 | + } | ||
| 310 | 337 | ||
| 311 | - // 7. 绑定拓扑关系 | ||
| 312 | - // TODO @AI:这里要考虑,校验是不是老设备已经绑定到其他网关了? | 338 | + // 2. 绑定拓扑关系 |
| 339 | + // TODO @AI:上面的校验,貌似和 bindDeviceGateway 里的,有点重复; | ||
| 313 | deviceService.bindDeviceGateway(Collections.singletonList(subDevice.getId()), gatewayDevice.getId()); | 340 | deviceService.bindDeviceGateway(Collections.singletonList(subDevice.getId()), gatewayDevice.getId()); |
| 314 | - log.info("[handleTopoAdd][网关({}/{}) 绑定子设备({}/{})]", | 341 | + log.info("[processTopoAddSubDevice][网关({}/{}) 绑定子设备({}/{})]", |
| 315 | gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), | 342 | gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), |
| 316 | subDevice.getProductKey(), subDevice.getDeviceName()); | 343 | subDevice.getProductKey(), subDevice.getDeviceName()); |
| 317 | - | ||
| 318 | - // 8. 发送拓扑变更通知 | ||
| 319 | - sendTopoChangeNotify(gatewayDevice, "add", subDevice); | ||
| 320 | - return null; | 344 | + return subDevice; |
| 321 | } | 345 | } |
| 322 | 346 | ||
| 323 | // TODO @AI:是不是更适合在 deviceService 里面处理? | 347 | // TODO @AI:是不是更适合在 deviceService 里面处理? |
| @@ -335,41 +359,70 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -335,41 +359,70 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 335 | throw exception(DEVICE_NOT_GATEWAY); | 359 | throw exception(DEVICE_NOT_GATEWAY); |
| 336 | } | 360 | } |
| 337 | 361 | ||
| 338 | - // 2. 解析参数 | 362 | + // 2. 解析参数(数组格式) |
| 339 | IotDeviceTopoDeleteReqDTO params = JsonUtils.parseObject(JsonUtils.toJsonString(message.getParams()), | 363 | IotDeviceTopoDeleteReqDTO params = JsonUtils.parseObject(JsonUtils.toJsonString(message.getParams()), |
| 340 | IotDeviceTopoDeleteReqDTO.class); | 364 | IotDeviceTopoDeleteReqDTO.class); |
| 341 | - if (params == null) { | 365 | + if (params == null || params.getSubDevices() == null || params.getSubDevices().isEmpty()) { |
| 342 | throw exception(DEVICE_TOPO_PARAMS_INVALID); | 366 | throw exception(DEVICE_TOPO_PARAMS_INVALID); |
| 343 | } | 367 | } |
| 344 | 368 | ||
| 345 | - // 3. 获取子设备 | ||
| 346 | - IotDeviceDO subDevice = deviceService.getDeviceFromCache(params.getProductKey(), params.getDeviceName()); | 369 | + // 3. 遍历处理每个子设备 |
| 370 | + List<IotDeviceDO> deletedSubDevices = new ArrayList<>(); | ||
| 371 | + for (IotDeviceTopoDeleteReqDTO.SubDevice subDeviceInfo : params.getSubDevices()) { | ||
| 372 | + try { | ||
| 373 | + IotDeviceDO subDevice = processTopoDeleteSubDevice(subDeviceInfo, gatewayDevice); | ||
| 374 | + deletedSubDevices.add(subDevice); | ||
| 375 | + } catch (Exception ex) { | ||
| 376 | + log.warn("[handleTopoDelete][网关({}/{}) 删除子设备失败,productKey={}, deviceName={}]", | ||
| 377 | + gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), | ||
| 378 | + subDeviceInfo.getProductKey(), subDeviceInfo.getDeviceName(), ex); | ||
| 379 | + } | ||
| 380 | + } | ||
| 381 | + | ||
| 382 | + // 4. 发送拓扑变更通知 | ||
| 383 | + for (IotDeviceDO subDevice : deletedSubDevices) { | ||
| 384 | + sendTopoChangeNotify(gatewayDevice, "delete", subDevice); | ||
| 385 | + } | ||
| 386 | + return null; | ||
| 387 | + } | ||
| 388 | + | ||
| 389 | + // TODO @AI:是不是更适合在 deviceService 里面处理? | ||
| 390 | + | ||
| 391 | + /** | ||
| 392 | + * 处理单个子设备的拓扑删除 | ||
| 393 | + * | ||
| 394 | + * @param subDeviceInfo 子设备标识 | ||
| 395 | + * @param gatewayDevice 网关设备 | ||
| 396 | + * @return 删除成功的子设备,失败返回 null | ||
| 397 | + */ | ||
| 398 | + private IotDeviceDO processTopoDeleteSubDevice(IotDeviceTopoDeleteReqDTO.SubDevice subDeviceInfo, | ||
| 399 | + IotDeviceDO gatewayDevice) { | ||
| 400 | + // 1. 获取子设备 | ||
| 401 | + IotDeviceDO subDevice = deviceService.getDeviceFromCache(subDeviceInfo.getProductKey(), subDeviceInfo.getDeviceName()); | ||
| 347 | if (subDevice == null) { | 402 | if (subDevice == null) { |
| 348 | throw exception(DEVICE_NOT_EXISTS); | 403 | throw exception(DEVICE_NOT_EXISTS); |
| 349 | } | 404 | } |
| 350 | 405 | ||
| 351 | - // 4. 校验子设备是否绑定到该网关 | 406 | + // 2. 校验子设备是否绑定到该网关 |
| 352 | if (!Objects.equal(subDevice.getGatewayId(), gatewayDevice.getId())) { | 407 | if (!Objects.equal(subDevice.getGatewayId(), gatewayDevice.getId())) { |
| 353 | - throw exception(DEVICE_TOPO_SUB_NOT_BINDTO_GATEWAY, params.getProductKey(), params.getDeviceName()); | 408 | + throw exception(DEVICE_TOPO_SUB_NOT_BINDTO_GATEWAY, subDeviceInfo.getProductKey(), subDeviceInfo.getDeviceName()); |
| 354 | } | 409 | } |
| 355 | 410 | ||
| 356 | - // 5. 解绑拓扑关系 | 411 | + // 3. 解绑拓扑关系 |
| 357 | deviceService.unbindDeviceGateway(Collections.singletonList(subDevice.getId())); | 412 | deviceService.unbindDeviceGateway(Collections.singletonList(subDevice.getId())); |
| 358 | - log.info("[handleTopoDelete][网关({}/{}) 解绑子设备({}/{})]", | 413 | + log.info("[processTopoDeleteSubDevice][网关({}/{}) 解绑子设备({}/{})]", |
| 359 | gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), | 414 | gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), |
| 360 | subDevice.getProductKey(), subDevice.getDeviceName()); | 415 | subDevice.getProductKey(), subDevice.getDeviceName()); |
| 361 | 416 | ||
| 362 | - // 6. 子设备下线 | 417 | + // 4. 子设备下线 |
| 363 | if (Objects.equal(subDevice.getState(), IotDeviceStateEnum.ONLINE.getState())) { | 418 | if (Objects.equal(subDevice.getState(), IotDeviceStateEnum.ONLINE.getState())) { |
| 364 | deviceService.updateDeviceState(subDevice, IotDeviceStateEnum.OFFLINE.getState()); | 419 | deviceService.updateDeviceState(subDevice, IotDeviceStateEnum.OFFLINE.getState()); |
| 365 | } | 420 | } |
| 366 | 421 | ||
| 367 | - // 7. 发送拓扑变更通知 | ||
| 368 | - sendTopoChangeNotify(gatewayDevice, "delete", subDevice); | ||
| 369 | - | ||
| 370 | - return null; | 422 | + return subDevice; |
| 371 | } | 423 | } |
| 372 | 424 | ||
| 425 | + // TODO @AI:是不是更适合在 deviceService 里面处理? | ||
| 373 | /** | 426 | /** |
| 374 | * 处理获取拓扑关系请求 | 427 | * 处理获取拓扑关系请求 |
| 375 | * | 428 | * |
| @@ -391,6 +444,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -391,6 +444,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 391 | .setDeviceName(subDevice.getDeviceName())); | 444 | .setDeviceName(subDevice.getDeviceName())); |
| 392 | } | 445 | } |
| 393 | 446 | ||
| 447 | + // TODO @AI:是不是更适合在 deviceService 里面处理? | ||
| 394 | /** | 448 | /** |
| 395 | * 发送拓扑变更通知 | 449 | * 发送拓扑变更通知 |
| 396 | * | 450 | * |
| @@ -427,6 +481,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -427,6 +481,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 427 | 481 | ||
| 428 | // ========== 子设备注册处理方法 ========== | 482 | // ========== 子设备注册处理方法 ========== |
| 429 | 483 | ||
| 484 | + // TODO @AI:是不是更适合在 deviceService 里面处理? | ||
| 430 | /** | 485 | /** |
| 431 | * 处理子设备动态注册请求 | 486 | * 处理子设备动态注册请求 |
| 432 | * | 487 | * |
| @@ -469,6 +524,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -469,6 +524,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 469 | return results; | 524 | return results; |
| 470 | } | 525 | } |
| 471 | 526 | ||
| 527 | + // TODO @AI:是不是更适合在 deviceService 里面处理? | ||
| 472 | /** | 528 | /** |
| 473 | * 注册单个子设备 | 529 | * 注册单个子设备 |
| 474 | * | 530 | * |
| @@ -539,11 +595,12 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -539,11 +595,12 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 539 | } | 595 | } |
| 540 | 596 | ||
| 541 | // 2. 处理网关自身属性 | 597 | // 2. 处理网关自身属性 |
| 598 | + // TODO @AI:是不是经过总线会更好: | ||
| 599 | + // TODO @AI:是不是少处理了 event 事件? | ||
| 542 | if (params.getProperties() != null && !params.getProperties().isEmpty()) { | 600 | if (params.getProperties() != null && !params.getProperties().isEmpty()) { |
| 543 | Map<String, Object> gatewayProperties = convertPackProperties(params.getProperties()); | 601 | Map<String, Object> gatewayProperties = convertPackProperties(params.getProperties()); |
| 544 | IotDeviceMessage gatewayMsg = IotDeviceMessage.builder() | 602 | IotDeviceMessage gatewayMsg = IotDeviceMessage.builder() |
| 545 | .id(IotDeviceMessageUtils.generateMessageId()) | 603 | .id(IotDeviceMessageUtils.generateMessageId()) |
| 546 | - .parentMessageId(packMessage.getId()) | ||
| 547 | .deviceId(gatewayDevice.getId()) | 604 | .deviceId(gatewayDevice.getId()) |
| 548 | .tenantId(gatewayDevice.getTenantId()) | 605 | .tenantId(gatewayDevice.getTenantId()) |
| 549 | .serverId(packMessage.getServerId()) | 606 | .serverId(packMessage.getServerId()) |
| @@ -596,7 +653,6 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -596,7 +653,6 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 596 | Map<String, Object> properties = convertPackProperties(subDeviceData.getProperties()); | 653 | Map<String, Object> properties = convertPackProperties(subDeviceData.getProperties()); |
| 597 | IotDeviceMessage subMsg = IotDeviceMessage.builder() | 654 | IotDeviceMessage subMsg = IotDeviceMessage.builder() |
| 598 | .id(IotDeviceMessageUtils.generateMessageId()) | 655 | .id(IotDeviceMessageUtils.generateMessageId()) |
| 599 | - .parentMessageId(packMessage.getId()) | ||
| 600 | .deviceId(subDevice.getId()) | 656 | .deviceId(subDevice.getId()) |
| 601 | .tenantId(subDevice.getTenantId()) | 657 | .tenantId(subDevice.getTenantId()) |
| 602 | .serverId(packMessage.getServerId()) | 658 | .serverId(packMessage.getServerId()) |
| @@ -608,8 +664,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -608,8 +664,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 608 | getSelf().createDeviceLogAsync(subMsg); | 664 | getSelf().createDeviceLogAsync(subMsg); |
| 609 | } | 665 | } |
| 610 | 666 | ||
| 611 | - // 3. 处理子设备事件(如果需要) | ||
| 612 | - // TODO: 事件处理可以后续扩展 | 667 | + // 3. 处理子设备事件 |
| 668 | + // TODO @AI:事件处理可以后续扩展 | ||
| 613 | } | 669 | } |
| 614 | 670 | ||
| 615 | /** | 671 | /** |
| @@ -633,7 +689,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | @@ -633,7 +689,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | ||
| 633 | 689 | ||
| 634 | // ========== 网关下线联动处理 ========== | 690 | // ========== 网关下线联动处理 ========== |
| 635 | 691 | ||
| 636 | - // TODO @AI:是不是写到 deviceService 里更合适?更解耦。 | 692 | + // TODO 芋艿:是不是写到 deviceService 里更合适?更解耦。 |
| 637 | /** | 693 | /** |
| 638 | * 处理网关下线,联动所有子设备下线 | 694 | * 处理网关下线,联动所有子设备下线 |
| 639 | * | 695 | * |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java
| @@ -43,7 +43,6 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | @@ -43,7 +43,6 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | ||
| 43 | PROPERTY_POST("thing.property.post", "属性上报", true), | 43 | PROPERTY_POST("thing.property.post", "属性上报", true), |
| 44 | PROPERTY_SET("thing.property.set", "属性设置", false), | 44 | PROPERTY_SET("thing.property.set", "属性设置", false), |
| 45 | 45 | ||
| 46 | - // TODO @AI:改成 thing.property.pack.post | ||
| 47 | PROPERTY_PACK_POST("thing.event.property.pack.post", "批量上报(属性 + 事件 + 子设备)", true), // 网关独有 | 46 | PROPERTY_PACK_POST("thing.event.property.pack.post", "批量上报(属性 + 事件 + 子设备)", true), // 网关独有 |
| 48 | 47 | ||
| 49 | // ========== 设备事件 ========== | 48 | // ========== 设备事件 ========== |
| @@ -77,8 +76,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | @@ -77,8 +76,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | ||
| 77 | */ | 76 | */ |
| 78 | public static final Set<String> REPLY_DISABLED = SetUtils.asSet( | 77 | public static final Set<String> REPLY_DISABLED = SetUtils.asSet( |
| 79 | STATE_UPDATE.getMethod(), | 78 | STATE_UPDATE.getMethod(), |
| 80 | - OTA_PROGRESS.getMethod(), // 参考阿里云,OTA 升级进度上报,不进行回复 | ||
| 81 | - TOPO_CHANGE.getMethod() // 拓扑变更通知,下行消息,不需要回复 TODO @AI:看看阿里云的文档,确认下是不是这样的 | 79 | + OTA_PROGRESS.getMethod() // 参考阿里云,OTA 升级进度上报,不进行回复 |
| 82 | ); | 80 | ); |
| 83 | 81 | ||
| 84 | private final String method; | 82 | private final String method; |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java
| @@ -94,42 +94,6 @@ public class IotDeviceMessage { | @@ -94,42 +94,6 @@ public class IotDeviceMessage { | ||
| 94 | */ | 94 | */ |
| 95 | private String msg; | 95 | private String msg; |
| 96 | 96 | ||
| 97 | - /** | ||
| 98 | - * 父消息 ID | ||
| 99 | - * <p> | ||
| 100 | - * - null:原始消息,需要 reply | ||
| 101 | - * - 非 null:从父消息(如 pack)拆分而来,不需要单独 reply | ||
| 102 | - */ | ||
| 103 | - private String parentMessageId; | ||
| 104 | - | ||
| 105 | - // TODO @TODO @AI:抽到工具类里,具体哪个,一起讨论下; | ||
| 106 | - /** | ||
| 107 | - * 判断是否需要发送 reply | ||
| 108 | - * | ||
| 109 | - * @return 是否需要回复 | ||
| 110 | - */ | ||
| 111 | - public boolean needReply() { | ||
| 112 | - // 1. 来自拆包的消息,不单独 reply | ||
| 113 | - if (parentMessageId != null) { | ||
| 114 | - return false; | ||
| 115 | - } | ||
| 116 | - // 2. 某些方法本身不需要 reply(如 STATE_UPDATE) | ||
| 117 | - if (IotDeviceMessageMethodEnum.isReplyDisabled(method)) { | ||
| 118 | - return false; | ||
| 119 | - } | ||
| 120 | - return true; | ||
| 121 | - } | ||
| 122 | - | ||
| 123 | - // TODO @AI:没用的字段,删除删除 | ||
| 124 | - /** | ||
| 125 | - * 获取原始消息 ID(用于日志追踪) | ||
| 126 | - * | ||
| 127 | - * @return 原始消息 ID | ||
| 128 | - */ | ||
| 129 | - public String getOriginMessageId() { | ||
| 130 | - return parentMessageId != null ? parentMessageId : id; | ||
| 131 | - } | ||
| 132 | - | ||
| 133 | // ========== 基础方法:只传递"codec(编解码)字段" ========== | 97 | // ========== 基础方法:只传递"codec(编解码)字段" ========== |
| 134 | 98 | ||
| 135 | public static IotDeviceMessage requestOf(String method) { | 99 | public static IotDeviceMessage requestOf(String method) { |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/IotDeviceIdentify.java
0 → 100644
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotSubDeviceRegisterReqDTO.java
| @@ -3,14 +3,13 @@ package cn.iocoder.yudao.module.iot.core.topic.auth; | @@ -3,14 +3,13 @@ package cn.iocoder.yudao.module.iot.core.topic.auth; | ||
| 3 | import jakarta.validation.constraints.NotEmpty; | 3 | import jakarta.validation.constraints.NotEmpty; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | -// TODO @AI:不用带 device 关键字; | ||
| 7 | -// TODO @AI:挂个阿里云的链接,https://help.aliyun.com/zh/iot/user-guide/register-devices 的「子设备的 MQTT 动态注册」小节 | ||
| 8 | /** | 6 | /** |
| 9 | * IoT 子设备动态注册 Request DTO | 7 | * IoT 子设备动态注册 Request DTO |
| 10 | * <p> | 8 | * <p> |
| 11 | * 用于 thing.sub.register 消息的 params 数组元素 | 9 | * 用于 thing.sub.register 消息的 params 数组元素 |
| 12 | * | 10 | * |
| 13 | * @author 芋道源码 | 11 | * @author 芋道源码 |
| 12 | + * @see <a href="http://help.aliyun.com/zh/marketplace/dynamic-registration-of-sub-devices">阿里云 - 动态注册子设备</a> | ||
| 14 | */ | 13 | */ |
| 15 | @Data | 14 | @Data |
| 16 | public class IotSubDeviceRegisterReqDTO { | 15 | public class IotSubDeviceRegisterReqDTO { |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotSubDeviceRegisterRespDTO.java
| @@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.iot.core.topic.auth; | @@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.iot.core.topic.auth; | ||
| 2 | 2 | ||
| 3 | import lombok.Data; | 3 | import lombok.Data; |
| 4 | 4 | ||
| 5 | -// TODO @AI:修复建议,参考 /Users/yunai/Java/ruoyi-vue-pro-jdk25/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotSubDeviceRegisterReqDTO.java | ||
| 6 | /** | 5 | /** |
| 7 | * IoT 子设备动态注册 Response DTO | 6 | * IoT 子设备动态注册 Response DTO |
| 8 | * <p> | 7 | * <p> |
| 9 | * 用于 thing.sub.register 响应的设备信息 | 8 | * 用于 thing.sub.register 响应的设备信息 |
| 10 | * | 9 | * |
| 11 | * @author 芋道源码 | 10 | * @author 芋道源码 |
| 11 | + * @see <a href="http://help.aliyun.com/zh/marketplace/dynamic-registration-of-sub-devices">阿里云 - 动态注册子设备</a> | ||
| 12 | */ | 12 | */ |
| 13 | @Data | 13 | @Data |
| 14 | public class IotSubDeviceRegisterRespDTO { | 14 | public class IotSubDeviceRegisterRespDTO { |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/package-info.java
| 1 | /** | 1 | /** |
| 2 | - * TODO @AI:写下注释 | 2 | + * IoT Topic 消息体 DTO 定义 |
| 3 | + * <p> | ||
| 4 | + * 定义设备与平台通信的消息体结构,遵循(参考)阿里云 Alink 协议规范 | ||
| 5 | + * | ||
| 6 | + * @see <a href="https://help.aliyun.com/zh/iot/user-guide/alink-protocol-1">阿里云 Alink 协议</a> | ||
| 3 | */ | 7 | */ |
| 4 | -package cn.iocoder.yudao.module.iot.core.topic; | ||
| 5 | \ No newline at end of file | 8 | \ No newline at end of file |
| 9 | +package cn.iocoder.yudao.module.iot.core.topic; |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertyPackPostReqDTO.java
| @@ -6,41 +6,18 @@ import lombok.experimental.Accessors; | @@ -6,41 +6,18 @@ import lombok.experimental.Accessors; | ||
| 6 | import java.util.List; | 6 | import java.util.List; |
| 7 | import java.util.Map; | 7 | import java.util.Map; |
| 8 | 8 | ||
| 9 | -// TODO @AI:挂个阿里云的链接,http://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 的「设备批量上报属性、事件」小节 | ||
| 10 | /** | 9 | /** |
| 11 | * IoT 设备属性批量上报 Request DTO | 10 | * IoT 设备属性批量上报 Request DTO |
| 12 | * <p> | 11 | * <p> |
| 13 | * 用于 thing.event.property.pack.post 消息的 params 参数 | 12 | * 用于 thing.event.property.pack.post 消息的 params 参数 |
| 14 | - * 参考阿里云 Alink 协议 | ||
| 15 | * | 13 | * |
| 16 | * @author 芋道源码 | 14 | * @author 芋道源码 |
| 15 | + * @see <a href="http://help.aliyun.com/zh/marketplace/gateway-reports-data-in-batches">阿里云 - 网关批量上报数据</a> | ||
| 17 | */ | 16 | */ |
| 18 | @Data | 17 | @Data |
| 19 | public class IotDevicePropertyPackPostReqDTO { | 18 | public class IotDevicePropertyPackPostReqDTO { |
| 20 | 19 | ||
| 21 | - // TODO @AI:去掉里面的 time,直接平铺值(可能就是直接的 map);例如说 "Power":value, 而不是 PropertyValue "properties": { | ||
| 22 | - // "Power": [ | ||
| 23 | - // { | ||
| 24 | - // "value": "on", | ||
| 25 | - // "time": 1524448722000 | ||
| 26 | - // }, | ||
| 27 | - // { | ||
| 28 | - // "value": "off", | ||
| 29 | - // "time": 1524448722001 | ||
| 30 | - // } | ||
| 31 | - // ], | ||
| 32 | - // "WF": [ | ||
| 33 | - // { | ||
| 34 | - // "value": 3, | ||
| 35 | - // "time": 1524448722000 | ||
| 36 | - // }, | ||
| 37 | - // { | ||
| 38 | - // "value": 4, | ||
| 39 | - // "time": 1524448722009 | ||
| 40 | - // } | ||
| 41 | - // ] | ||
| 42 | - // } | ||
| 43 | - | 20 | + // TODO @AI:不用 PropertyValue,直接使用 Object 接收就行! |
| 44 | /** | 21 | /** |
| 45 | * 网关自身属性 | 22 | * 网关自身属性 |
| 46 | * <p> | 23 | * <p> |
| @@ -49,27 +26,6 @@ public class IotDevicePropertyPackPostReqDTO { | @@ -49,27 +26,6 @@ public class IotDevicePropertyPackPostReqDTO { | ||
| 49 | */ | 26 | */ |
| 50 | private Map<String, PropertyValue> properties; | 27 | private Map<String, PropertyValue> properties; |
| 51 | 28 | ||
| 52 | - // TODO @AI:EventValue { | ||
| 53 | - // | ||
| 54 | - // "method": "thing.event.post", | ||
| 55 | - // | ||
| 56 | - // "version": "1.0", | ||
| 57 | - // | ||
| 58 | - // "params": { | ||
| 59 | - // | ||
| 60 | - // "identifier": "eat", | ||
| 61 | - // | ||
| 62 | - // "params": { | ||
| 63 | - // | ||
| 64 | - // "rice": 100 | ||
| 65 | - // | ||
| 66 | - // } | ||
| 67 | - // | ||
| 68 | - // } | ||
| 69 | - // | ||
| 70 | - //} | ||
| 71 | - | ||
| 72 | - | ||
| 73 | /** | 29 | /** |
| 74 | * 网关自身事件 | 30 | * 网关自身事件 |
| 75 | * <p> | 31 | * <p> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoAddReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; | ||
| 3 | import jakarta.validation.constraints.NotEmpty; | 4 | import jakarta.validation.constraints.NotEmpty; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | 6 | ||
| 6 | -// TODO @AI:得一起讨论下,到底使用什么后缀合适:1)一方面要体现出请求、响应;2)一方面体现出上下行(设备 to server,还是 server to 设备),可以一起讨论? | ||
| 7 | -// TODO @AI:文档地址:https://help.aliyun.com/zh/iot/user-guide/manage-topological-relationships?spm=a2c4g.11186623.help-menu-30520.d_2_2_7_3_2.2e983f47Z2iGbo&scm=20140722.H_89299._.OR_help-T_cn~zh-V_1#section-w33-vyg-12b | 7 | +import java.util.List; |
| 8 | + | ||
| 8 | /** | 9 | /** |
| 9 | * IoT 设备拓扑添加 Request DTO | 10 | * IoT 设备拓扑添加 Request DTO |
| 10 | * <p> | 11 | * <p> |
| 11 | * 用于 thing.topo.add 消息的 params 参数 | 12 | * 用于 thing.topo.add 消息的 params 参数 |
| 12 | * | 13 | * |
| 13 | * @author 芋道源码 | 14 | * @author 芋道源码 |
| 15 | + * @see <a href="http://help.aliyun.com/zh/marketplace/add-topological-relationship">阿里云 - 添加拓扑关系</a> | ||
| 14 | */ | 16 | */ |
| 15 | @Data | 17 | @Data |
| 16 | public class IotDeviceTopoAddReqDTO { | 18 | public class IotDeviceTopoAddReqDTO { |
| 17 | 19 | ||
| 18 | - // TODO @AI:是个数组; | ||
| 19 | - // TODO @AI:有响应结果的; | ||
| 20 | - | ||
| 21 | - /** | ||
| 22 | - * 子设备客户端 ID | ||
| 23 | - */ | ||
| 24 | - @NotEmpty(message = "客户端 ID 不能为空") | ||
| 25 | - private String clientId; | ||
| 26 | - | ||
| 27 | - /** | ||
| 28 | - * 子设备用户名 | ||
| 29 | - */ | ||
| 30 | - @NotEmpty(message = "用户名不能为空") | ||
| 31 | - private String username; | ||
| 32 | - | ||
| 33 | /** | 20 | /** |
| 34 | - * 子设备认证密码 | 21 | + * 子设备认证信息列表 |
| 22 | + * <p> | ||
| 23 | + * 复用 {@link IotDeviceAuthReqDTO},包含 clientId、username、password | ||
| 35 | */ | 24 | */ |
| 36 | - @NotEmpty(message = "密码不能为空") | ||
| 37 | - private String password; | 25 | + @NotEmpty(message = "子设备认证信息列表不能为空") |
| 26 | + private List<IotDeviceAuthReqDTO> subDevices; | ||
| 38 | 27 | ||
| 39 | } | 28 | } |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoDeleteReqDTO.java
| @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.iot.core.topic.topo; | @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.iot.core.topic.topo; | ||
| 3 | import jakarta.validation.constraints.NotEmpty; | 3 | import jakarta.validation.constraints.NotEmpty; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | -// TODO @AI:是个数组;https://help.aliyun.com/zh/iot/user-guide/manage-topological-relationships?spm=a2c4g.11186623.help-menu-30520.d_2_2_7_3_2.540c390beZSDOG&scm=20140722.H_89299._.OR_help-T_cn~zh-V_1#section-rb1-wzw-y2b | ||
| 7 | - | ||
| 8 | -// todo @AI:删除完,需要有个 reply 响应; | 6 | +import java.util.List; |
| 9 | 7 | ||
| 10 | /** | 8 | /** |
| 11 | * IoT 设备拓扑删除 Request DTO | 9 | * IoT 设备拓扑删除 Request DTO |
| @@ -13,20 +11,35 @@ import lombok.Data; | @@ -13,20 +11,35 @@ import lombok.Data; | ||
| 13 | * 用于 thing.topo.delete 消息的 params 参数 | 11 | * 用于 thing.topo.delete 消息的 params 参数 |
| 14 | * | 12 | * |
| 15 | * @author 芋道源码 | 13 | * @author 芋道源码 |
| 14 | + * @see <a href="https://help.aliyun.com/zh/marketplace/delete-a-topological-relationship">阿里云 - 删除拓扑关系</a> | ||
| 16 | */ | 15 | */ |
| 17 | @Data | 16 | @Data |
| 18 | public class IotDeviceTopoDeleteReqDTO { | 17 | public class IotDeviceTopoDeleteReqDTO { |
| 19 | 18 | ||
| 19 | + // TODO @AI:应该是数组;IotDeviceIdentify | ||
| 20 | /** | 20 | /** |
| 21 | - * 子设备 ProductKey | 21 | + * 子设备标识列表 |
| 22 | */ | 22 | */ |
| 23 | - @NotEmpty(message = "产品标识不能为空") | ||
| 24 | - private String productKey; | 23 | + private List<SubDevice> subDevices; |
| 25 | 24 | ||
| 26 | /** | 25 | /** |
| 27 | - * 子设备 DeviceName | 26 | + * 子设备标识 |
| 28 | */ | 27 | */ |
| 29 | - @NotEmpty(message = "设备名称不能为空") | ||
| 30 | - private String deviceName; | 28 | + @Data |
| 29 | + public static class SubDevice { | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 子设备 ProductKey | ||
| 33 | + */ | ||
| 34 | + @NotEmpty(message = "产品标识不能为空") | ||
| 35 | + private String productKey; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 子设备 DeviceName | ||
| 39 | + */ | ||
| 40 | + @NotEmpty(message = "设备名称不能为空") | ||
| 41 | + private String deviceName; | ||
| 42 | + | ||
| 43 | + } | ||
| 31 | 44 | ||
| 32 | } | 45 | } |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoRespDTO.java
| @@ -2,17 +2,19 @@ package cn.iocoder.yudao.module.iot.core.topic.topo; | @@ -2,17 +2,19 @@ package cn.iocoder.yudao.module.iot.core.topic.topo; | ||
| 2 | 2 | ||
| 3 | import lombok.Data; | 3 | import lombok.Data; |
| 4 | 4 | ||
| 5 | -// TODO @AI:是不是改成 IotDeviceTopoGetRespDTO | 5 | +// TODO @AI:IotDeviceTopoGetRespDTO |
| 6 | /** | 6 | /** |
| 7 | * IoT 设备拓扑关系 Response DTO | 7 | * IoT 设备拓扑关系 Response DTO |
| 8 | * <p> | 8 | * <p> |
| 9 | * 用于 thing.topo.get 响应的子设备信息 | 9 | * 用于 thing.topo.get 响应的子设备信息 |
| 10 | * | 10 | * |
| 11 | * @author 芋道源码 | 11 | * @author 芋道源码 |
| 12 | + * @see <a href="https://help.aliyun.com/zh/marketplace/obtain-topological-relationship">阿里云 - 获取拓扑关系</a> | ||
| 12 | */ | 13 | */ |
| 13 | @Data | 14 | @Data |
| 14 | public class IotDeviceTopoRespDTO { | 15 | public class IotDeviceTopoRespDTO { |
| 15 | 16 | ||
| 17 | + // TODO @AI:应该是数组;IotDeviceIdentify | ||
| 16 | /** | 18 | /** |
| 17 | * 子设备 ProductKey | 19 | * 子设备 ProductKey |
| 18 | */ | 20 | */ |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/util/IotMqttTopicUtils.java
| @@ -63,50 +63,4 @@ public final class IotMqttTopicUtils { | @@ -63,50 +63,4 @@ public final class IotMqttTopicUtils { | ||
| 63 | return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + "/" + topicSuffix; | 63 | return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + "/" + topicSuffix; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | - /** | ||
| 67 | - * 构建拓扑管理 Topic | ||
| 68 | - * <p> | ||
| 69 | - * 拓扑管理类 Topic 使用网关设备的 productKey/deviceName | ||
| 70 | - * | ||
| 71 | - * @param method 方法,如 thing.topo.add | ||
| 72 | - * @param gatewayProductKey 网关 ProductKey | ||
| 73 | - * @param gatewayDeviceName 网关 DeviceName | ||
| 74 | - * @param isReply 是否为响应 | ||
| 75 | - * @return Topic | ||
| 76 | - */ | ||
| 77 | - public static String buildTopoTopic(String method, String gatewayProductKey, | ||
| 78 | - String gatewayDeviceName, boolean isReply) { | ||
| 79 | - return buildTopicByMethod(method, gatewayProductKey, gatewayDeviceName, isReply); | ||
| 80 | - } | ||
| 81 | - | ||
| 82 | - /** | ||
| 83 | - * 判断是否为拓扑管理 Topic(通过 method 判断) | ||
| 84 | - * | ||
| 85 | - * @param method 消息方法 | ||
| 86 | - * @return 是否为拓扑管理 Topic | ||
| 87 | - */ | ||
| 88 | - public static boolean isTopoMethod(String method) { | ||
| 89 | - return method != null && method.startsWith("thing.topo."); | ||
| 90 | - } | ||
| 91 | - | ||
| 92 | - /** | ||
| 93 | - * 判断是否为子设备注册 Topic | ||
| 94 | - * | ||
| 95 | - * @param method 消息方法 | ||
| 96 | - * @return 是否为子设备注册 Topic | ||
| 97 | - */ | ||
| 98 | - public static boolean isSubDeviceRegisterMethod(String method) { | ||
| 99 | - return "thing.sub.register".equals(method); | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - /** | ||
| 103 | - * 判断是否为批量上报 Topic | ||
| 104 | - * | ||
| 105 | - * @param method 消息方法 | ||
| 106 | - * @return 是否为批量上报 Topic | ||
| 107 | - */ | ||
| 108 | - public static boolean isPackPostMethod(String method) { | ||
| 109 | - return "thing.event.property.pack.post".equals(method); | ||
| 110 | - } | ||
| 111 | - | ||
| 112 | } | 66 | } |
| 113 | \ No newline at end of file | 67 | \ No newline at end of file |