From 66b42367cb7c0178e756398144ec4254ed86fbc1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 11 Jun 2025 09:56:59 +0800 Subject: [PATCH] =?UTF-8?q?reactor=EF=BC=9A=E3=80=90IoT=20=E7=89=A9?= =?UTF-8?q?=E8=81=94=E7=BD=91=E3=80=91=E9=87=8D=E6=96=B0=E6=A2=B3=E7=90=86?= =?UTF-8?q?=E4=B8=8B=E8=A1=8C=E6=B6=88=E6=81=AF=E7=9A=84=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=88=E6=9C=AA=E6=B5=8B=E8=AF=95=EF=BC=8C=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E7=9B=B8=E4=BA=92=20review=20=E4=BD=9C=E7=94=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/util/object/ObjectUtils.java | 4 + .../upstream/IotDeviceRegisterReqDTO.java | 12 - .../upstream/IotDeviceRegisterSubReqDTO.java | 43 --- .../upstream/IotDeviceTopologyAddReqDTO.java | 44 --- .../admin/device/IotDeviceController.java | 28 +- .../admin/device/IotDeviceLogController.java | 6 +- .../device/IotDevicePropertyController.java | 2 +- .../vo/control/IotDeviceDownstreamReqVO.java | 30 -- .../vo/control/IotDeviceUpstreamReqVO.java | 30 -- .../vo/message/IotDeviceMessageSendReqVO.java | 26 ++ .../record/IotOtaUpgradeRecordRespVO.java | 3 +- .../statistics/IotStatisticsController.java | 2 +- .../dal/dataobject/device/IotDeviceLogDO.java | 100 ------- .../dataobject/device/IotDeviceMessageDO.java | 101 +++++++ .../dataobject/ota/IotOtaUpgradeRecordDO.java | 3 +- .../redis/device/DeviceServerIdRedisDAO.java | 13 +- ...apper.java => IotDeviceMessageMapper.java} | 38 +-- .../config/TDengineTableInitRunner.java | 12 +- .../job/device/IotDeviceOfflineCheckJob.java | 11 +- .../device/IotDeviceLogMessageSubscriber.java | 48 --- .../device/IotDeviceMessageSubscriber.java | 99 +++++++ .../IotDevicePropertyMessageSubscriber.java | 54 ---- .../IotDeviceStateMessageSubscriber.java | 106 ------- .../iot/service/device/IotDeviceService.java | 8 + .../service/device/IotDeviceServiceImpl.java | 19 +- .../control/IotDeviceDownstreamService.java | 24 -- .../IotDeviceDownstreamServiceImpl.java | 274 ------------------ .../control/IotDeviceUpstreamService.java | 50 ---- .../control/IotDeviceUpstreamServiceImpl.java | 222 -------------- .../message/IotDeviceMessageService.java | 49 ++++ .../message/IotDeviceMessageServiceImpl.java | 167 +++++++++++ .../IotDeviceLogService.java | 21 +- .../IotDeviceLogServiceImpl.java | 51 +--- .../IotDevicePropertyService.java | 14 +- .../IotDevicePropertyServiceImpl.java | 24 +- .../product/IotProductServiceImpl.java | 2 +- .../service/rule/IotRuleSceneServiceImpl.java | 15 +- .../IotRuleSceneDeviceControlAction.java | 13 +- ...gMapper.xml => IotDeviceMessageMapper.xml} | 87 +++--- .../enums/IotDeviceMessageIdentifierEnum.java | 45 --- .../enums/IotDeviceMessageMethodEnum.java | 42 ++- .../core/enums/IotDeviceMessageTypeEnum.java | 2 +- .../iot/core/mq/message/IotDeviceMessage.java | 114 ++++---- .../iot/core/util/IotDeviceMessageUtils.java | 20 +- .../alink/IotAlinkDeviceMessageCodec.java | 14 +- .../config/IotGatewayConfiguration.java | 23 +- .../http/router/IotHttpUpstreamHandler.java | 137 --------- .../message/IotDeviceMessageServiceImpl.java | 18 +- 48 files changed, 734 insertions(+), 1536 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceDownstreamReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceUpstreamReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageSendReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/{IotDeviceLogMapper.java => IotDeviceMessageMapper.java} (73%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageSubscriber.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageSubscriber.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceStateMessageSubscriber.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{data => property}/IotDeviceLogService.java (78%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{data => property}/IotDeviceLogServiceImpl.java (63%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{data => property}/IotDevicePropertyService.java (88%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{data => property}/IotDevicePropertyServiceImpl.java (91%) rename yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/{IotDeviceLogMapper.xml => IotDeviceMessageMapper.xml} (51%) delete mode 100644 yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageIdentifierEnum.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java index c08316dc2e..a26c7c12eb 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java @@ -60,4 +60,8 @@ public class ObjectUtils { return Arrays.asList(array).contains(obj); } + public static boolean isNotAllEmpty(Object... objs) { + return !ObjectUtil.isAllEmpty(objs); + } + } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java deleted file mode 100644 index cab55e832b..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java +++ /dev/null @@ -1,12 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; - -import lombok.Data; - -/** - * IoT 设备【注册】自己 Request DTO - * - * @author 芋道源码 - */ -@Data -public class IotDeviceRegisterReqDTO extends IotDeviceUpstreamAbstractReqDTO { -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java deleted file mode 100644 index 0b826fbb14..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; - -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.List; - -/** - * IoT 设备【注册】子设备 Request DTO - * - * @author 芋道源码 - */ -@Data -public class IotDeviceRegisterSubReqDTO extends IotDeviceUpstreamAbstractReqDTO { - - // TODO @芋艿:看看要不要优化命名 - /** - * 子设备数组 - */ - @NotEmpty(message = "子设备不能为空") - private List params; - - /** - * 设备信息 - */ - @Data - public static class Device { - - /** - * 产品标识 - */ - @NotEmpty(message = "产品标识不能为空") - private String productKey; - - /** - * 设备名称 - */ - @NotEmpty(message = "设备名称不能为空") - private String deviceName; - - } - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java deleted file mode 100644 index 18efe7d48f..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; - -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.List; - -// TODO @芋艿:要写清楚,是来自设备网关,还是设备。 -/** - * IoT 设备【拓扑】添加 Request DTO - */ -@Data -public class IotDeviceTopologyAddReqDTO extends IotDeviceUpstreamAbstractReqDTO { - - // TODO @芋艿:看看要不要优化命名 - /** - * 子设备数组 - */ - @NotEmpty(message = "子设备不能为空") - private List params; - - /** - * 设备信息 - */ - @Data - public static class Device { - - /** - * 产品标识 - */ - @NotEmpty(message = "产品标识不能为空") - private String productKey; - - /** - * 设备名称 - */ - @NotEmpty(message = "设备名称不能为空") - private String deviceName; - - // TODO @芋艿:阿里云还有 sign 签名 - - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 0eed0fbc78..f04deffdb7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -6,13 +6,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.message.IotDeviceMessageSendReqVO; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService; -import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceUpstreamService; +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -42,9 +41,7 @@ public class IotDeviceController { @Resource private IotDeviceService deviceService; @Resource - private IotDeviceUpstreamService deviceUpstreamService; - @Resource - private IotDeviceDownstreamService deviceDownstreamService; + private IotDeviceMessageService deviceMessageService; @PostMapping("/create") @Operation(summary = "创建设备") @@ -161,19 +158,12 @@ public class IotDeviceController { ExcelUtils.write(response, "设备导入模板.xls", "数据", IotDeviceImportExcelVO.class, list); } - @PostMapping("/upstream") - @Operation(summary = "设备上行", description = "可用于设备模拟") + // TODO @芋艿:需要重构 + @PostMapping("/send-message") + @Operation(summary = "发送消息", description = "可用于设备模拟") @PreAuthorize("@ss.hasPermission('iot:device:upstream')") - public CommonResult upstreamDevice(@Valid @RequestBody IotDeviceUpstreamReqVO upstreamReqVO) { - deviceUpstreamService.upstreamDevice(upstreamReqVO); - return success(true); - } - - @PostMapping("/downstream") - @Operation(summary = "设备下行", description = "可用于设备模拟") - @PreAuthorize("@ss.hasPermission('iot:device:downstream')") - public CommonResult downstreamDevice(@Valid @RequestBody IotDeviceDownstreamReqVO downstreamReqVO) { - deviceDownstreamService.downstreamDevice(downstreamReqVO); + public CommonResult upstreamDevice(@Valid @RequestBody IotDeviceMessageSendReqVO sendReqVO) { + deviceMessageService.sendDeviceMessage(BeanUtils.toBean(sendReqVO, IotDeviceMessage.class)); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java index 81d1bff945..a4326e754f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java @@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; -import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; +import cn.iocoder.yudao.module.iot.service.device.property.IotDeviceLogService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -32,7 +32,7 @@ public class IotDeviceLogController { @Operation(summary = "获得设备日志分页") @PreAuthorize("@ss.hasPermission('iot:device:log-query')") public CommonResult> getDeviceLogPage(@Valid IotDeviceLogPageReqVO pageReqVO) { - PageResult pageResult = deviceLogService.getDeviceLogPage(pageReqVO); + PageResult pageResult = deviceLogService.getDeviceLogPage(pageReqVO); return success(BeanUtils.toBean(pageResult, IotDeviceLogRespVO.class)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java index 47bf325dda..9447de1847 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java @@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceDownstreamReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceDownstreamReqVO.java deleted file mode 100644 index eefaeffebc..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceDownstreamReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.control; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 设备下行 Request VO") // 服务调用、属性设置、属性获取等 -@Data -public class IotDeviceDownstreamReqVO { - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") - @NotNull(message = "设备编号不能为空") - private Long id; - - @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property") - @NotEmpty(message = "消息类型不能为空") - @InEnum(IotDeviceMessageTypeEnum.class) - private String type; - - @Schema(description = "标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "report") - @NotEmpty(message = "标识符不能为空") - private String identifier; // 参见 IotDeviceMessageIdentifierEnum 枚举类 - - @Schema(description = "请求参数", requiredMode = Schema.RequiredMode.REQUIRED) - private Object data; // 例如说:服务调用的 params、属性设置的 properties - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceUpstreamReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceUpstreamReqVO.java deleted file mode 100644 index 778d75bba8..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceUpstreamReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.control; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 设备上行 Request VO") // 属性上报、事件上报、状态变更等 -@Data -public class IotDeviceUpstreamReqVO { - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") - @NotNull(message = "设备编号不能为空") - private Long id; - - @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property") - @NotEmpty(message = "消息类型不能为空") - @InEnum(IotDeviceMessageTypeEnum.class) - private String type; - - @Schema(description = "标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "report") - @NotEmpty(message = "标识符不能为空") - private String identifier; // 参见 IotDeviceMessageIdentifierEnum 枚举类 - - @Schema(description = "请求参数", requiredMode = Schema.RequiredMode.REQUIRED) - private Object data; // 例如说:属性上报的 properties、事件上报的 params - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageSendReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageSendReqVO.java new file mode 100644 index 0000000000..e93cabbd93 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageSendReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.message; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 设备消息发送 Request VO") // 属性上报、事件上报、状态变更等 +@Data +public class IotDeviceMessageSendReqVO { + + @Schema(description = "请求方法", requiredMode = Schema.RequiredMode.REQUIRED, example = "report") + @NotEmpty(message = "请求方法不能为空") + @InEnum(IotDeviceMessageMethodEnum.class) + private String method; + + @Schema(description = "请求参数") + private Object params; // 例如说:属性上报的 properties、事件上报的 params + + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") + @NotNull(message = "设备编号不能为空") + private Long deviceId; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java index d770374260..ba2a40aa81 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; import com.fhs.core.trans.anno.Trans; @@ -89,7 +90,7 @@ public class IotOtaUpgradeRecordRespVO { * 升级进度描述 *

* 注意,只记录设备最后一次的升级进度描述 - * 如果想看历史记录,可以查看 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO} 设备日志 + * 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志 */ @Schema(description = "升级进度描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private String description; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java index 0086c22943..d623846109 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsR import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; +import cn.iocoder.yudao.module.iot.service.device.property.IotDeviceLogService; import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import io.swagger.v3.oas.annotations.Operation; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java deleted file mode 100644 index deb353f75d..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java +++ /dev/null @@ -1,100 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.device; - -import cn.hutool.core.util.IdUtil; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * IoT 设备日志数据 DO - * - * 目前使用 TDengine 存储 - * - * @author alwayssuper - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDeviceLogDO { - - /** - * 日志编号 - * - * 通过 {@link IdUtil#fastSimpleUUID()} 生成 - */ - private String id; - - /** - * 消息编号 - * - * 对应 {@link IotDeviceMessage#getMessageId()} 字段 - */ - private String messageId; - - /** - * 产品标识 - *

- * 关联 {@link IotProductDO#getProductKey()} - */ - private String productKey; - /** - * 设备名称 - * - * 关联 {@link IotDeviceDO#getDeviceName()} - */ - private String deviceName; - /** - * 设备编号 - * - * 关联 {@link IotDeviceDO#getId()} - */ - private Long deviceId; - - /** - * 日志类型 - * - * 枚举 {@link IotDeviceMessageTypeEnum} - */ - private String type; - /** - * 标识符 - * - * 枚举 {@link IotDeviceMessageIdentifierEnum} - */ - private String identifier; - - /** - * 数据内容 - * - * 存储具体的消息数据内容,通常是 JSON 格式 - */ - private String content; - /** - * 响应码 - * - * 目前只有 server 下行消息给 device 设备时,才会有响应码 - */ - private Integer code; - - /** - * 上报时间戳 - */ - private Long reportTime; - - /** - * 租户编号 - */ - private Long tenantId; - - /** - * 时序时间 - */ - private Long ts; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java new file mode 100644 index 0000000000..0e6d1bdbc5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java @@ -0,0 +1,101 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.device; + +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * IoT 设备消息数据 DO + * + * 目前使用 TDengine 存储 + * + * @author alwayssuper + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotDeviceMessageDO { + + /** + * 消息编号 + */ + private String id; + /** + * 上报时间戳 + */ + private Long reportTime; + /** + * 存储时序时间 + */ + private Long ts; + + /** + * 设备编号 + * + * 关联 {@link IotDeviceDO#getId()} + */ + private Long deviceId; + /** + * 租户编号 + */ + private Long tenantId; + + /** + * 服务编号,该消息由哪个 server 发送 + */ + private String serverId; + + /** + * 是否上行消息 + * + * 由 {@link cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils#isUpstreamMessage(IotDeviceMessage)} 计算。 + * 计算并存储的目的:方便计算多少条上行、多少条下行 + */ + private Boolean upstream; + /** + * 是否回复消息 + * + * 由 {@link cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils#isReplyMessage(IotDeviceMessage)} 计算。 + * 计算并存储的目的:方便计算多少条请求、多少条回复 + */ + private Boolean reply; + + // ========== codec(编解码)字段 ========== + + /** + * 请求编号 + * + * 由设备生成,对应阿里云 IoT 的 Alink 协议中的 id、华为云 IoTDA 协议的 request_id + */ + private String requestId; + /** + * 请求方法 + * + * 枚举 {@link IotDeviceMessageMethodEnum} + * 例如说:thing.property.report 属性上报 + */ + private String method; + /** + * 请求参数 + * + * 例如说:属性上报的 properties、事件上报的 params + */ + private Object params; + /** + * 响应结果 + */ + private Object data; + /** + * 响应错误码 + */ + private Integer code; + /** + * 响应提示 + */ + private String msg; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java index ff4f0e7a09..02c4a0157f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.ota; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -77,7 +78,7 @@ public class IotOtaUpgradeRecordDO extends BaseDO { * 升级进度描述 * * 注意,只记录设备最后一次的升级进度描述 - * 如果想看历史记录,可以查看 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO} 设备日志 + * 如果想看历史记录,可以查看 {@link IotDeviceMessageDO} 设备日志 */ private String description; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceServerIdRedisDAO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceServerIdRedisDAO.java index 7bd8d03bb0..e8f96a1ad5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceServerIdRedisDAO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceServerIdRedisDAO.java @@ -42,17 +42,6 @@ public class DeviceServerIdRedisDAO { return value != null ? (String) value : null; } - /** - * 删除设备关联的网关 serverId - * - * @param productKey 产品标识 - * @param deviceName 设备名称 - */ - public void delete(String productKey, String deviceName) { - String hashKey = buildHashKey(productKey, deviceName); - stringRedisTemplate.opsForHash().delete(RedisKeyConstants.DEVICE_SERVER_ID, hashKey); - } - /** * 构建 HASH KEY * @@ -64,4 +53,4 @@ public class DeviceServerIdRedisDAO { return productKey + StrUtil.COMMA + deviceName; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java similarity index 73% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java index 96741e6095..fbc40496b1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceMessageMapper.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -12,48 +12,48 @@ import java.util.List; import java.util.Map; /** - * 设备日志 {@link IotDeviceLogDO} Mapper 接口 + * 设备消息 {@link IotDeviceMessageDO} Mapper 接口 */ @Mapper @TDengineDS @InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 -public interface IotDeviceLogMapper { +public interface IotDeviceMessageMapper { /** - * 创建设备日志超级表 + * 创建设备消息超级表 */ - void createDeviceLogSTable(); + void createSTable(); /** - * 查询设备日志表是否存在 + * 查询设备消息表是否存在 * * @return 存在则返回表名;不存在则返回 null */ - String showDeviceLogSTable(); + String showSTable(); /** - * 插入设备日志数据 + * 插入设备消息数据 * * 如果子表不存在,会自动创建子表 * - * @param log 设备日志数据 + * @param message 设备消息数据 */ - void insert(IotDeviceLogDO log); + void insert(IotDeviceMessageDO message); /** - * 获得设备日志分页 + * 获得设备消息分页 * * @param reqVO 分页查询条件 - * @return 设备日志列表 + * @return 设备消息列表 */ - IPage selectPage(IPage page, - @Param("reqVO") IotDeviceLogPageReqVO reqVO); + IPage selectPage(IPage page, + @Param("reqVO") IotDeviceLogPageReqVO reqVO); /** - * 统计设备日志数量 + * 统计设备消息数量 * - * @param createTime 创建时间,如果为空,则统计所有日志数量 - * @return 日志数量 + * @param createTime 创建时间,如果为空,则统计所有消息数量 + * @return 消息数量 */ Long selectCountByCreateTime(@Param("createTime") Long createTime); @@ -62,14 +62,14 @@ public interface IotDeviceLogMapper { /** * 查询每个小时设备上行消息数量 */ - List> selectDeviceLogUpCountByHour(@Param("deviceKey") String deviceKey, + List> selectDeviceLogUpCountByHour(@Param("deviceId") Long deviceId, @Param("startTime") Long startTime, @Param("endTime") Long endTime); /** * 查询每个小时设备下行消息数量 */ - List> selectDeviceLogDownCountByHour(@Param("deviceKey") String deviceKey, + List> selectDeviceLogDownCountByHour(@Param("deviceId") Long deviceId, @Param("startTime") Long startTime, @Param("endTime") Long endTime); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java index 3517e1e58c..1de2dcdd35 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.framework.tdengine.config; -import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; @@ -17,16 +17,16 @@ import org.springframework.stereotype.Component; @Slf4j public class TDengineTableInitRunner implements ApplicationRunner { - private final IotDeviceLogService deviceLogService; + private final IotDeviceMessageService deviceMessageService; @Override public void run(ApplicationArguments args) { try { - // 初始化设备日志表 - deviceLogService.defineDeviceLog(); + // 初始化设备消息表 + deviceMessageService.defineDeviceMessageStable(); } catch (Exception ex) { - // 初始化失败时打印错误日志并退出系统 - log.error("[run][TDengine初始化设备日志表结构失败,系统无法正常运行,即将退出]", ex); + // 初始化失败时打印错误消息并退出系统 + log.error("[run][TDengine初始化设备消息表结构失败,系统无法正常运行,即将退出]", ex); System.exit(1); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java index 8451b6670e..11b1934417 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java @@ -7,10 +7,10 @@ import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; import jakarta.annotation.Resource; import org.springframework.stereotype.Component; @@ -43,10 +43,10 @@ public class IotDeviceOfflineCheckJob implements JobHandler { private IotDeviceService deviceService; @Resource private IotDevicePropertyService devicePropertyService; - @Resource - private IotDeviceMessageProducer deviceMessageProducer; + private IotDeviceMessageService deviceMessageService; + // TODO @芋艿:需要重构下; @Override @TenantJob public String execute(String param) { @@ -69,8 +69,7 @@ public class IotDeviceOfflineCheckJob implements JobHandler { } offlineDeviceKeys.add(new String[]{device.getProductKey(), device.getDeviceName()}); // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志等等 - deviceMessageProducer.sendDeviceMessage(IotDeviceMessage.of(device.getProductKey(), device.getDeviceName()) - .ofStateOffline()); + deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateOffline().setDeviceId(device.getId())); } return JsonUtils.toJsonString(offlineDeviceKeys); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageSubscriber.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageSubscriber.java deleted file mode 100644 index 37e7e698a2..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageSubscriber.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.iot.mq.consumer.device; - -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 针对 {@link IotDeviceMessage} 的消费者:记录设备日志 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class IotDeviceLogMessageSubscriber implements IotMessageSubscriber { - - @Resource - private IotMessageBus messageBus; - - @Resource - private IotDeviceLogService deviceLogService; - - @PostConstruct - public void init() { - messageBus.register(this); - } - - @Override - public String getTopic() { - return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC; - } - - @Override - public String getGroup() { - return "iot_device_log_consumer"; - } - - @Override - public void onMessage(IotDeviceMessage message) { - log.info("[onMessage][消息内容({})]", message); - deviceLogService.createDeviceLog(message); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java new file mode 100644 index 0000000000..3da5765010 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.iot.mq.consumer.device; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * 针对 {@link IotDeviceMessage} 的业务处理器:调用 method 对应的逻辑。例如说: + * 1. {@link IotDeviceMessageMethodEnum#PROPERTY_REPORT} 属性上报时,记录设备属性 + * + * @author alwayssuper + */ +@Component +@Slf4j +public class IotDeviceMessageSubscriber implements IotMessageSubscriber { + + @Resource + private IotDeviceService deviceService; + @Resource + private IotDevicePropertyService devicePropertyService; + @Resource + private IotDeviceMessageService deviceMessageService; + + @Resource + private IotMessageBus messageBus; + + @PostConstruct + public void init() { + messageBus.register(this); + } + + @Override + public String getTopic() { + return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC; + } + + @Override + public String getGroup() { + return "iot_device_message_consumer"; + } + + @Override + public void onMessage(IotDeviceMessage message) { + if (!IotDeviceMessageUtils.isUpstreamMessage(message)) { + log.error("[onMessage][message({}) 非上行消息,不进行处理]", message); + return; + } + + // 1.1 更新设备的最后时间 + // TODO 芋艿:后续加缓存; + IotDeviceDO device = deviceService.validateDeviceExists(message.getDeviceId()); + devicePropertyService.updateDeviceReportTime(device.getProductKey(), device.getDeviceName(), LocalDateTime.now()); + // 1.2 更新设备的连接 server + devicePropertyService.updateDeviceServerId(device.getProductKey(), device.getDeviceName(), message.getServerId()); + + // 2. 未上线的设备,强制上线 + forceDeviceOnline(message, device); + + // 3. 核心:处理消息 + deviceMessageService.handleUpstreamDeviceMessage(message, device); + } + + private void forceDeviceOnline(IotDeviceMessage message, IotDeviceDO device) { + // 已经在线,无需处理 + if (ObjectUtil.equal(device.getState(), IotDeviceStateEnum.ONLINE.getState())) { + return; + } + // 如果是 STATE 相关的消息,无需处理,不然就重复处理状态了 + if (ObjectUtils.equalsAny(message.getMethod(), IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(), + IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod())) { + return; + } + + // 特殊:设备非在线时,主动标记设备为在线 + // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志、规则引擎等等 + try { + deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateOnline().setDeviceId(device.getId())); + } catch (Exception e) { + // 注意:即使执行失败,也不影响主流程 + log.error("[forceDeviceOnline][message({}) device({}) 强制设备上线失败]", message, device, e); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageSubscriber.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageSubscriber.java deleted file mode 100644 index 527e48235e..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageSubscriber.java +++ /dev/null @@ -1,54 +0,0 @@ -package cn.iocoder.yudao.module.iot.mq.consumer.device; - -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; -import com.google.common.base.Objects; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 针对 {@link IotDeviceMessage} 的消费者:记录设备属性 - * - * @author alwayssuper - */ -@Component -@Slf4j -public class IotDevicePropertyMessageSubscriber implements IotMessageSubscriber { - - @Resource - private IotDevicePropertyService deviceDataService; - - @Resource - private IotMessageBus messageBus; - - @PostConstruct - public void init() { - messageBus.register(this); - } - - @Override - public String getTopic() { - return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC; - } - - @Override - public String getGroup() { - return "iot_device_property_consumer"; - } - - @Override - public void onMessage(IotDeviceMessage message) { - if (Objects.equal(message.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType()) - && Objects.equal(message.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier())) { - // 保存设备属性 - deviceDataService.saveDeviceProperty(message); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceStateMessageSubscriber.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceStateMessageSubscriber.java deleted file mode 100644 index b355b985d6..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceStateMessageSubscriber.java +++ /dev/null @@ -1,106 +0,0 @@ -package cn.iocoder.yudao.module.iot.mq.consumer.device; - -import cn.hutool.core.util.ObjectUtil; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageIdentifierEnum; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageTypeEnum; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.time.LocalDateTime; -import java.util.Objects; - -/** - * 针对 {@link IotDeviceMessage} 的消费者:记录设备状态 - * - * 特殊:如果是离线的设备,将自动上线 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class IotDeviceStateMessageSubscriber implements IotMessageSubscriber { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotDevicePropertyService devicePropertyService; - - @Resource - private IotMessageBus messageBus; - @Resource - private IotDeviceMessageProducer deviceMessageProducer; - - @PostConstruct - public void init() { - messageBus.register(this); - } - - @Override - public String getTopic() { - return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC; - } - - @Override - public String getGroup() { - return "iot_device_state_consumer"; - } - - @Override - public void onMessage(IotDeviceMessage message) { - // 1.1 只处理上行消息,或者是 STATE 相关的消息 - if (!IotDeviceMessageUtils.isUpstreamMessage(message) - && ObjectUtil.notEqual(message.getType(), IotDeviceMessageTypeEnum.STATE.getType())) { - return; - } - // 1.2 校验设备是否存在 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - message.getProductKey(), message.getDeviceName()); - if (device == null) { - log.error("[onMessage][消息({}) 对应的设备部存在]", message); - return; - } - - // 2. 处理消息 - TenantUtils.execute(device.getTenantId(), () -> onMessage(message, device)); - } - - private void onMessage(IotDeviceMessage message, IotDeviceDO device) { - // 更新设备的最后时间 - devicePropertyService.updateDeviceReportTime(device.getProductKey(), device.getDeviceName(), LocalDateTime.now()); - - // 情况一:STATE 相关的消息 - if (Objects.equals(message.getType(), IotDeviceMessageTypeEnum.STATE.getType())) { - if (Objects.equals(message.getIdentifier(), IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier())) { - deviceService.updateDeviceState(device.getId(), IotDeviceStateEnum.ONLINE.getState()); - devicePropertyService.updateDeviceServerId(device.getProductKey(), device.getDeviceName(), message.getServerId()); - } else { - deviceService.updateDeviceState(device.getId(), IotDeviceStateEnum.OFFLINE.getState()); - devicePropertyService.deleteDeviceServerId(device.getProductKey(), device.getDeviceName()); - } - // TODO 芋艿:子设备的关联 - return; - } - - // 情况二:非 STATE 相关的消息 - devicePropertyService.updateDeviceServerId(device.getProductKey(), device.getDeviceName(), message.getServerId()); - // 特殊:设备非在线时,主动标记设备为在线 - // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志等等 - if (ObjectUtil.notEqual(device.getState(), IotDeviceStateEnum.ONLINE.getState())) { - deviceMessageProducer.sendDeviceMessage(IotDeviceMessage.of(message.getProductKey(), message.getDeviceName()) - .ofStateOnline()); - } - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index e2aa21304f..e25ab722c2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -60,6 +60,14 @@ public interface IotDeviceService { updateDevice(new IotDeviceSaveReqVO().setId(id).setGatewayId(gatewayId)); } + /** + * 更新设备状态 + * + * @param device 设备信息 + * @param state 状态 + */ + void updateDeviceState(IotDeviceDO device, Integer state); + /** * 更新设备状态 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 48a367675b..9795b8338f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -272,12 +272,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { } @Override - public void updateDeviceState(Long id, Integer state) { - // 1. 校验存在 - IotDeviceDO device = validateDeviceExists(id); - - // 2. 更新状态和时间 - IotDeviceDO updateObj = new IotDeviceDO().setId(id).setState(state); + public void updateDeviceState(IotDeviceDO device, Integer state) { + // 1. 更新状态和时间 + IotDeviceDO updateObj = new IotDeviceDO().setId(device.getId()).setState(state); if (device.getOnlineTime() == null && Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) { updateObj.setActiveTime(LocalDateTime.now()); @@ -289,10 +286,18 @@ public class IotDeviceServiceImpl implements IotDeviceService { } deviceMapper.updateById(updateObj); - // 3. 清空对应缓存 + // 2. 清空对应缓存 deleteDeviceCache(device); } + @Override + public void updateDeviceState(Long id, Integer state) { + // 校验存在 + IotDeviceDO device = validateDeviceExists(id); + // 执行更新 + updateDeviceState(device, state); + } + @Override public Long getDeviceCountByProductId(Long productId) { return deviceMapper.selectCountByProductId(productId); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java deleted file mode 100644 index b4d49587e6..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.control; - -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import jakarta.validation.Valid; - -/** - * IoT 设备下行 Service 接口 - * - * 目的:服务端 -> 网关 -> 设备 - * - * @author 芋道源码 - */ -public interface IotDeviceDownstreamService { - - /** - * 设备下行,可用于设备模拟 - * - * @param downstreamReqVO 设备下行请求 VO - * @return 下行消息 - */ - IotDeviceMessage downstreamDevice(@Valid IotDeviceDownstreamReqVO downstreamReqVO); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java deleted file mode 100644 index 9634a40eae..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ /dev/null @@ -1,274 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.control; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; -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.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.Map; -import java.util.Objects; - -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL; - -/** - * IoT 设备下行 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamService { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotDevicePropertyService devicePropertyService; - - @Resource - private IotDeviceMessageProducer deviceMessageProducer; - - @Override - public IotDeviceMessage downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) { - // 1. 校验设备是否存在 - IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId()); - // TODO 芋艿:父设备的处理 - IotDeviceDO parentDevice = null; - - // 2. 构建消息 - IotDeviceMessage message = buildDownstreamDeviceMessage(downstreamReqVO, device, parentDevice); - - // 3.1 发送给网关 - String serverId = devicePropertyService.getDeviceServerId(message.getProductKey(), message.getDeviceName()); - if (StrUtil.isEmpty(serverId)) { - throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL); - } - deviceMessageProducer.sendDeviceMessageToGateway(serverId, message); - - // 3.2 发送给服务器(用于设备日志等的记录) - deviceMessageProducer.sendDeviceMessage(message); - return message; - } - - @SuppressWarnings("unchecked") - private IotDeviceMessage buildDownstreamDeviceMessage(IotDeviceDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { - IotDeviceMessage message = IotDeviceMessage.of(getProductKey(device, parentDevice), - getDeviceName(device, parentDevice)); - // 服务调用 - if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.SERVICE.getType())) { - // TODO @芋艿:待实现 -// return invokeDeviceService(downstreamReqVO, device, parentDevice); - } - // 属性相关 - if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) { - // 属性设置 - if (Objects.equals(downstreamReqVO.getIdentifier(), - IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) { - if (!(downstreamReqVO.getData() instanceof Map)) { - throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); - } - return message.ofPropertySet((Map) downstreamReqVO.getData()); - } - // 属性获取 - if (Objects.equals(downstreamReqVO.getIdentifier(), - IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) { - // TODO @芋艿:待实现 -// return getDeviceProperty(downstreamReqVO, device, parentDevice); - } - } - // 配置下发 - if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType()) - && Objects.equals(downstreamReqVO.getIdentifier(), - IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) { - // TODO @芋艿:待实现 -// return setDeviceConfig(downstreamReqVO, device, parentDevice); - } - // OTA 升级 - if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.OTA.getType())) { - // TODO @芋艿:待实现 -// return otaUpgrade(downstreamReqVO, device, parentDevice); - } - // TODO @芋艿:取消设备的网关的时,要不要下发 REGISTER_UNREGISTER_SUB ? - throw new IllegalArgumentException("不支持的下行消息类型:" + downstreamReqVO); - } - -// /** -// * 调用设备服务 -// * -// * @param downstreamReqVO 下行请求 -// * @param device 设备 -// * @param parentDevice 父设备 -// * @return 下发消息 -// */ -// @SuppressWarnings("unchecked") -// private IotDeviceMessage invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO, -// IotDeviceDO device, IotDeviceDO parentDevice) { -// // 1. 参数校验 -// if (!(downstreamReqVO.getData() instanceof Map)) { -// throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); -// } -// // TODO @super:【可优化】过滤掉不合法的服务 -// -// // 2. 发送请求 -// String url = String.format("sys/%s/%s/thing/service/%s", -// getProductKey(device, parentDevice), getDeviceName(device, parentDevice), -// downstreamReqVO.getIdentifier()); -// IotDeviceServiceInvokeReqDTO reqDTO = new IotDeviceServiceInvokeReqDTO() -// .setParams((Map) downstreamReqVO.getData()); -//// CommonResult result = requestPlugin(url, reqDTO, device); -// CommonResult result = null; -// -// // 3. 发送设备消息 -// IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) -// .setType(IotDeviceMessageTypeEnum.SERVICE.getType()).setIdentifier(reqDTO.getIdentifier()) -// .setData(reqDTO.getParams()); -// sendDeviceMessage(message, device, result.getCode()); -// -// // 4. 如果不成功,抛出异常,提示用户 -// if (result.isError()) { -// log.error("[invokeDeviceService][设备({})服务调用失败,请求参数:({}),响应结果:({})]", -// device.getDeviceKey(), reqDTO, result); -// throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); -// } -// return message; -// } - -// /** -// * 获取设备属性 -// * -// * @param downstreamReqVO 下行请求 -// * @param device 设备 -// * @param parentDevice 父设备 -// * @return 下发消息 -// */ -// @SuppressWarnings("unchecked") -// private IotDeviceMessage getDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO, -// IotDeviceDO device, IotDeviceDO parentDevice) { -// // 1. 参数校验 -// if (!(downstreamReqVO.getData() instanceof List)) { -// throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 List 类型"); -// } -// // TODO @super:【可优化】过滤掉不合法的属性 -// -// // 2. 发送请求 -// String url = String.format("sys/%s/%s/thing/service/property/get", -// getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); -// IotDevicePropertyGetReqDTO reqDTO = new IotDevicePropertyGetReqDTO() -// .setIdentifiers((List) downstreamReqVO.getData()); -//// CommonResult result = requestPlugin(url, reqDTO, device); -// CommonResult result = null; -// -// // 3. 发送设备消息 -// IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) -// .setType(IotDeviceMessageTypeEnum.PROPERTY.getType()) -// .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()) -// .setData(reqDTO.getIdentifiers()); -// sendDeviceMessage(message, device, result.getCode()); -// -// // 4. 如果不成功,抛出异常,提示用户 -// if (result.isError()) { -// log.error("[getDeviceProperty][设备({})属性获取失败,请求参数:({}),响应结果:({})]", -// device.getDeviceKey(), reqDTO, result); -// throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); -// } -// return message; -// } - -// /** -// * 设置设备配置 -// * -// * @param downstreamReqVO 下行请求 -// * @param device 设备 -// * @param parentDevice 父设备 -// * @return 下发消息 -// */ -// @SuppressWarnings({ "unchecked", "unused" }) -// private IotDeviceMessage setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO, -// IotDeviceDO device, IotDeviceDO parentDevice) { -// // 1. 参数转换,无需校验 -// Map config = JsonUtils.parseObject(device.getConfig(), Map.class); -// -// // 2. 发送请求 -// String url = String.format("sys/%s/%s/thing/service/config/set", -// getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); -// IotDeviceConfigSetReqDTO reqDTO = new IotDeviceConfigSetReqDTO() -// .setConfig(config); -//// CommonResult result = requestPlugin(url, reqDTO, device); -// CommonResult result = null; -// -// // 3. 发送设备消息 -// IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) -// .setType(IotDeviceMessageTypeEnum.CONFIG.getType()) -// .setIdentifier(IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier()) -// .setData(reqDTO.getConfig()); -// sendDeviceMessage(message, device, result.getCode()); -// -// // 4. 如果不成功,抛出异常,提示用户 -// if (result.isError()) { -// log.error("[setDeviceConfig][设备({})配置下发失败,请求参数:({}),响应结果:({})]", -// device.getDeviceKey(), reqDTO, result); -// throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); -// } -// return message; -// } - -// /** -// * 设备 OTA 升级 -// * -// * @param downstreamReqVO 下行请求 -// * @param device 设备 -// * @param parentDevice 父设备 -// * @return 下发消息 -// */ -// private IotDeviceMessage otaUpgrade(IotDeviceDownstreamReqVO downstreamReqVO, -// IotDeviceDO device, IotDeviceDO parentDevice) { -// // 1. 参数校验 -// if (!(downstreamReqVO.getData() instanceof Map data)) { -// throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); -// } -// -// // 2. 发送请求 -// String url = String.format("ota/%s/%s/upgrade", -// getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); -// IotDeviceOtaUpgradeReqDTO reqDTO = IotDeviceOtaUpgradeReqDTO.build(data); -//// CommonResult result = requestPlugin(url, reqDTO, device); -// CommonResult result = null; -// -// // 3. 发送设备消息 -// IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) -// .setType(IotDeviceMessageTypeEnum.OTA.getType()) -// .setIdentifier(IotDeviceMessageIdentifierEnum.OTA_UPGRADE.getIdentifier()) -// .setData(downstreamReqVO.getData()); -// sendDeviceMessage(message, device, result.getCode()); -// -// // 4. 如果不成功,抛出异常,提示用户 -// if (result.isError()) { -// log.error("[otaUpgrade][设备({}) OTA 升级失败,请求参数:({}),响应结果:({})]", -// device.getDeviceKey(), reqDTO, result); -// throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); -// } -// return message; -// } - - private String getDeviceName(IotDeviceDO device, IotDeviceDO parentDevice) { - return parentDevice != null ? parentDevice.getDeviceName() : device.getDeviceName(); - } - - private String getProductKey(IotDeviceDO device, IotDeviceDO parentDevice) { - return parentDevice != null ? parentDevice.getProductKey() : device.getProductKey(); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java deleted file mode 100644 index b82b331491..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.control; - -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; -import jakarta.validation.Valid; - -/** - * IoT 设备上行 Service 接口 - * - * 目的:设备 -> 网关 -> 服务端 - * - * @author 芋道源码 - */ -public interface IotDeviceUpstreamService { - - /** - * 设备上行,可用于设备模拟 - * - * @param simulatorReqVO 设备上行请求 VO - */ - void upstreamDevice(@Valid IotDeviceUpstreamReqVO simulatorReqVO); - -// /** -// * 上报设备事件数据 -// * -// * @param reportReqDTO 设备事件 -// */ -// void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO); -// -// /** -// * 注册设备 -// * -// * @param registerReqDTO 注册设备 DTO -// */ -// void registerDevice(IotDeviceRegisterReqDTO registerReqDTO); -// -// /** -// * 注册子设备 -// * -// * @param registerReqDTO 注册子设备 DTO -// */ -// void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO); -// -// /** -// * 添加设备拓扑 -// * -// * @param addReqDTO 添加设备拓扑 DTO -// */ -// void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java deleted file mode 100644 index 0eb82280f7..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ /dev/null @@ -1,222 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device.control; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -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.IotDeviceMessageTypeEnum; -import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.time.LocalDateTime; -import java.util.Map; -import java.util.Objects; - -/** - * IoT 设备上行 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { - - @Resource - private IotDeviceService deviceService; - @Resource - private IotDevicePropertyService devicePropertyService; - - // TODO @芋艿:需要重新实现下; - @Override - @SuppressWarnings("unchecked") - public void upstreamDevice(IotDeviceUpstreamReqVO simulatorReqVO) { - // 1. 校验存在 - IotDeviceDO device = deviceService.validateDeviceExists(simulatorReqVO.getId()); - - // 2.1 情况一:属性上报 - String requestId = IdUtil.fastSimpleUUID(); - if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) { - reportDeviceProperty(((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO() - .setRequestId(requestId).setReportTime(LocalDateTime.now()) - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) - .setProperties((Map) simulatorReqVO.getData())); - return; - } - // 2.2 情况二:事件上报 - if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.EVENT.getType())) { - reportDeviceEvent(((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId) - .setReportTime(LocalDateTime.now()) - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) - .setIdentifier(simulatorReqVO.getIdentifier()) - .setParams((Map) simulatorReqVO.getData())); - return; - } - // 2.3 情况三:状态变更 - if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.STATE.getType())) { - // TODO @芋艿:这里未搞完 - return; - } - throw new IllegalArgumentException("未知的类型:" + simulatorReqVO.getType()); - } - -// @Override TODO 芋艿:待重新实现 - public void reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { - // 1.1 获得设备 - log.info("[reportDeviceProperty][上报设备属性: {}]", reportReqDTO); - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); - if (device == null) { - log.error("[reportDeviceProperty][设备({}/{})不存在]", - reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); - return; - } - - // 2. 发送设备消息 -// IotDeviceMessage message = BeanUtils.toBean(reportReqDTO, IotDeviceMessage.class) -// .setType(IotDeviceMessageTypeEnum.PROPERTY.getType()) -// .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier()) -// .setData(reportReqDTO.getProperties()); -// sendDeviceMessage(message, device); - } - - // @Override TODO 芋艿:待重新实现 - public void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { - // 1.1 获得设备 - log.info("[reportDeviceEvent][上报设备事件: {}]", reportReqDTO); - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); - if (device == null) { - log.error("[reportDeviceEvent][设备({}/{})不存在]", - reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); - return; - } - - // 2. 发送设备消息 -// IotDeviceMessage message = BeanUtils.toBean(reportReqDTO, IotDeviceMessage.class) -// .setType(IotDeviceMessageTypeEnum.EVENT.getType()) -// .setIdentifier(reportReqDTO.getIdentifier()) -// .setData(reportReqDTO.getParams()); -// sendDeviceMessage(message, device); - } - - // @Override TODO 芋艿:待重新实现 - public void registerDevice(IotDeviceRegisterReqDTO registerReqDTO) { - log.info("[registerDevice][注册设备: {}]", registerReqDTO); - registerDevice0(registerReqDTO.getProductKey(), registerReqDTO.getDeviceName(), null, registerReqDTO); - } - - private void registerDevice0(String productKey, String deviceName, Long gatewayId, - IotDeviceUpstreamAbstractReqDTO registerReqDTO) { - // 1.1 注册设备 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(productKey, deviceName); - boolean registerNew = device == null; - if (device == null) { - device = deviceService.createDevice(productKey, deviceName, gatewayId); - log.info("[registerDevice0][消息({}) 设备({}/{}) 成功注册]", registerReqDTO, productKey, device); - } else if (gatewayId != null && ObjUtil.notEqual(device.getGatewayId(), gatewayId)) { - Long deviceId = device.getId(); - TenantUtils.execute(device.getTenantId(), - () -> deviceService.updateDeviceGateway(deviceId, gatewayId)); - log.info("[registerDevice0][消息({}) 设备({}/{}) 更新网关设备编号({})]", - registerReqDTO, productKey, device, gatewayId); - } - // 1.2 记录设备的最后时间 -// updateDeviceLastTime(device, registerReqDTO); - - // 2. 发送设备消息 - if (registerNew) { -// IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class) -// .setType(IotDeviceMessageTypeEnum.REGISTER.getType()) -// .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER.getIdentifier()); -// sendDeviceMessage(message, device); - } - } - - // @Override TODO 芋艿:待重新实现 - public void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO) { - // 1.1 注册子设备 - log.info("[registerSubDevice][注册子设备: {}]", registerReqDTO); - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - registerReqDTO.getProductKey(), registerReqDTO.getDeviceName()); - if (device == null) { - log.error("[registerSubDevice][设备({}/{}) 不存在]", - registerReqDTO.getProductKey(), registerReqDTO.getDeviceName()); - return; - } - if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) { - log.error("[registerSubDevice][设备({}/{}) 不是网关设备({}),无法进行注册]", - registerReqDTO.getProductKey(), registerReqDTO.getDeviceName(), device); - return; - } - // 1.2 记录设备的最后时间 -// updateDeviceLastTime(device, registerReqDTO); - - // 2. 处理子设备 - if (CollUtil.isNotEmpty(registerReqDTO.getParams())) { - registerReqDTO.getParams().forEach(subDevice -> registerDevice0( - subDevice.getProductKey(), subDevice.getDeviceName(), device.getId(), registerReqDTO)); - // TODO @芋艿:后续要处理,每个设备是否成功 - } - - // 3. 发送设备消息 -// IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class) -// .setType(IotDeviceMessageTypeEnum.REGISTER.getType()) -// .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER_SUB.getIdentifier()) -// .setData(registerReqDTO.getParams()); -// sendDeviceMessage(message, device); - } - - // @Override TODO 芋艿:待重新实现 - public void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) { - // 1.1 获得设备 - log.info("[addDeviceTopology][添加设备拓扑: {}]", addReqDTO); - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - addReqDTO.getProductKey(), addReqDTO.getDeviceName()); - if (device == null) { - log.error("[addDeviceTopology][设备({}/{}) 不存在]", - addReqDTO.getProductKey(), addReqDTO.getDeviceName()); - return; - } - if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) { - log.error("[addDeviceTopology][设备({}/{}) 不是网关设备({}),无法进行拓扑添加]", - addReqDTO.getProductKey(), addReqDTO.getDeviceName(), device); - return; - } - - // 2. 处理拓扑 - if (CollUtil.isNotEmpty(addReqDTO.getParams())) { - TenantUtils.execute(device.getTenantId(), () -> { - addReqDTO.getParams().forEach(subDevice -> { - IotDeviceDO subDeviceDO = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - subDevice.getProductKey(), subDevice.getDeviceName()); - // TODO @芋艿:后续要处理,每个设备是否成功 - if (subDeviceDO == null) { - log.error("[addDeviceTopology][子设备({}/{}) 不存在]", - subDevice.getProductKey(), subDevice.getDeviceName()); - return; - } - deviceService.updateDeviceGateway(subDeviceDO.getId(), device.getId()); - log.info("[addDeviceTopology][子设备({}/{}) 添加到网关设备({}) 成功]", - subDevice.getProductKey(), subDevice.getDeviceName(), device); - }); - }); - } - - // 3. 发送设备消息 -// IotDeviceMessage message = BeanUtils.toBean(addReqDTO, IotDeviceMessage.class) -// .setType(IotDeviceMessageTypeEnum.TOPOLOGY.getType()) -// .setIdentifier(IotDeviceMessageIdentifierEnum.TOPOLOGY_ADD.getIdentifier()) -// .setData(addReqDTO.getParams()); -// sendDeviceMessage(message, device); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java new file mode 100644 index 0000000000..31ce61cfee --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.iot.service.device.message; + +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; + +/** + * IoT 设备消息 Service 接口 + * + * @author 芋道源码 + */ +public interface IotDeviceMessageService { + + /** + * 初始化设备消息的 TDengine 超级表 + * + * 系统启动时,会自动初始化一次 + */ + void defineDeviceMessageStable(); + + /** + * 发送设备消息 + * + * @param message 消息(“codec(编解码)字段” 部分字段) + * @param device 设备 + * @return 设备消息 + */ + IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device); + + /** + * 发送设备消息 + * + * @param message 消息(“codec(编解码)字段” 部分字段) + * @return 设备消息 + */ + IotDeviceMessage sendDeviceMessage(IotDeviceMessage message); + + /** + * 处理设备上行的消息,包括如下步骤: + * + * 1. 处理消息 + * 2. 记录消息 + * 3. 回复消息 + * + * @param message 消息 + * @param device 设备 + */ + void handleUpstreamDeviceMessage(IotDeviceMessage message, IotDeviceDO device); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java new file mode 100644 index 0000000000..5ea75f3ce0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java @@ -0,0 +1,167 @@ +package cn.iocoder.yudao.module.iot.service.device.message; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; +import com.google.common.base.Objects; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL; + +/** + * IoT 设备消息 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { + + @Resource + private IotDeviceService deviceService; + @Resource + private IotDevicePropertyService devicePropertyService; + + @Resource + private IotDeviceMessageMapper deviceLogMapper; + + @Resource + private IotDeviceMessageProducer deviceMessageProducer; + + @Override + public void defineDeviceMessageStable() { + if (StrUtil.isNotEmpty(deviceLogMapper.showSTable())) { + log.info("[defineDeviceMessageStable][设备消息超级表已存在,创建跳过]"); + return; + } + log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建开始...]"); + deviceLogMapper.createSTable(); + log.info("[defineDeviceMessageStable][设备消息超级表不存在,创建成功]"); + } + + // TODO @芋艿:要不要异步记录; + private void createDeviceLog(IotDeviceMessage message) { + IotDeviceMessageDO messageDO = BeanUtils.toBean(message, IotDeviceMessageDO.class) + .setUpstream(IotDeviceMessageUtils.isUpstreamMessage(message)); + deviceLogMapper.insert(messageDO); + } + + @Override + public IotDeviceMessage sendDeviceMessage(IotDeviceMessage message) { + IotDeviceDO device = deviceService.validateDeviceExists(message.getDeviceId()); + return sendDeviceMessage(message, device); + } + + // TODO @芋艿:针对连接网关的设备,是不是 productKey、deviceName 需要调整下; + @Override + public IotDeviceMessage sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { + // 1. 补充信息 + appendDeviceMessage(message, device); + + // 2.1 情况一:发送上行消息 + boolean upstream = IotDeviceMessageUtils.isUpstreamMessage(message); + if (upstream) { + deviceMessageProducer.sendDeviceMessage(message); + return message; + } + + // 2.2 情况二:发送下行消息 + // 如果是下行消息,需要校验 serverId 存在 + String serverId = devicePropertyService.getDeviceServerId(device.getProductKey(), device.getDeviceName()); + if (StrUtil.isEmpty(serverId)) { + throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL); + } + deviceMessageProducer.sendDeviceMessageToGateway(serverId, message); + // 特殊:记录消息日志。原因:上行消息,消费时,已经会记录;下行消息,因为消费在 Gateway 端,所以需要在这里记录 + createDeviceLog(message); + return message; + } + + /** + * 补充消息的后端字段 + * + * @param message 消息 + * @param device 设备信息 + * @return 消息 + */ + private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { + message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now()) + .setDeviceId(device.getId()).setTenantId(device.getTenantId()); + // 特殊:如果设备没有指定 requestId,则使用 messageId + if (StrUtil.isEmpty(message.getRequestId())) { + message.setRequestId(message.getId()); + } + return message; + } + + @Override + public void handleUpstreamDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { + // 1. 理消息 + Object replyData = null; + ServiceException serviceException = null; + try { + replyData = handleUpstreamDeviceMessage0(message, device); + } catch (ServiceException ex) { + serviceException = ex; + log.warn("[onMessage][message({}) 业务异常]", message, serviceException); + } catch (Exception ex) { + log.error("[onMessage][message({}) 发生异常]", message, ex); + throw ex; + } + + // 2. 记录消息 + createDeviceLog(message); + + // 3. 回复消息。前提:非 _reply 消息,并且非禁用回复的消息 + if (IotDeviceMessageUtils.isReplyMessage(message) + || IotDeviceMessageMethodEnum.isReplyDisabled(message.getMethod())) { + return; + } + sendDeviceMessage(IotDeviceMessage.replyOf(message.getRequestId(), message.getMethod(), replyData, + serviceException != null ? serviceException.getCode() : null, + serviceException != null ? serviceException.getMessage() : null)); + } + + // TODO @芋艿:可优化:未来逻辑复杂后,可以独立拆除 Processor 处理器 + private Object handleUpstreamDeviceMessage0(IotDeviceMessage message, IotDeviceDO device) { + // 设备上线 + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod())) { + deviceService.updateDeviceState(device, IotDeviceStateEnum.ONLINE.getState()); + // TODO 芋艿:子设备的关联 + return null; + } + // 设备下线 + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod())) { + deviceService.updateDeviceState(device, IotDeviceStateEnum.OFFLINE.getState()); + // TODO 芋艿:子设备的关联 + return null; + } + + // 属性上报 + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_REPORT.getMethod())) { + devicePropertyService.saveDeviceProperty(device, message); + return null; + } + + // TODO @芋艿:这里可以按需,添加别的逻辑; + return null; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogService.java similarity index 78% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogService.java index 5ec6f9f9f5..b9248640f7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogService.java @@ -1,9 +1,8 @@ -package cn.iocoder.yudao.module.iot.service.device.data; +package cn.iocoder.yudao.module.iot.service.device.property; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; import javax.annotation.Nullable; import java.time.LocalDateTime; @@ -17,27 +16,13 @@ import java.util.Map; */ public interface IotDeviceLogService { - /** - * 初始化 TDengine 超级表 - * - * 系统启动时,会自动初始化一次 - */ - void defineDeviceLog(); - - /** - * 插入设备日志 - * - * @param message 设备数据 - */ - void createDeviceLog(IotDeviceMessage message); - /** * 获得设备日志分页 * * @param pageReqVO 分页查询 * @return 设备日志分页 */ - PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO); + PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO); /** * 获得设备日志数量 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogServiceImpl.java similarity index 63% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogServiceImpl.java index 490eee099b..f5fa11490b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDeviceLogServiceImpl.java @@ -1,17 +1,11 @@ -package cn.iocoder.yudao.module.iot.service.device.data; +package cn.iocoder.yudao.module.iot.service.device.property; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceMessageDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceMessageMapper; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -40,39 +34,12 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { private IotDeviceService deviceService; @Resource - private IotDeviceLogMapper deviceLogMapper; + private IotDeviceMessageMapper deviceLogMapper; @Override - public void defineDeviceLog() { - if (StrUtil.isNotEmpty(deviceLogMapper.showDeviceLogSTable())) { - log.info("[defineDeviceLog][设备日志超级表已存在,创建跳过]"); - return; - } - - log.info("[defineDeviceLog][设备日志超级表不存在,创建开始...]"); - deviceLogMapper.createDeviceLogSTable(); - log.info("[defineDeviceLog][设备日志超级表不存在,创建成功]"); - } - - @Override - public void createDeviceLog(IotDeviceMessage message) { - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - message.getProductKey(), message.getDeviceName()); - if (device == null) { - log.error("[createDeviceLog][设备({}/{}) 不存在]", message.getProductKey(), message.getDeviceName()); - return; - } - IotDeviceLogDO log = BeanUtils.toBean(message, IotDeviceLogDO.class) - .setId(IdUtil.fastSimpleUUID()) - .setContent(JsonUtils.toJsonString(message.getData())) - .setTenantId(device.getTenantId()); - deviceLogMapper.insert(log); - } - - @Override - public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { + public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { try { - IPage page = deviceLogMapper.selectPage( + IPage page = deviceLogMapper.selectPage( new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); return new PageResult<>(page.getRecords(), page.getTotal()); } catch (Exception exception) { @@ -92,7 +59,8 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { @Override public List> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) { // TODO @super:不能只基于数据库统计。因为有一些小时,可能出现没数据的情况,导致前端展示的图是不全的。可以参考 CrmStatisticsCustomerService 来实现 - List> list = deviceLogMapper.selectDeviceLogUpCountByHour(deviceKey, startTime, endTime); + // TODO @芋艿:这里实现,需要调整 + List> list = deviceLogMapper.selectDeviceLogUpCountByHour(0L, startTime, endTime); return list.stream() .map(map -> { // 从Timestamp获取时间戳 @@ -108,7 +76,8 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { // TODO @super:getDeviceLogDownCountByHour 融合到 getDeviceLogUpCountByHour @Override public List> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) { - List> list = deviceLogMapper.selectDeviceLogDownCountByHour(deviceKey, startTime, endTime); + // TODO @芋艿:这里实现,需要调整 + List> list = deviceLogMapper.selectDeviceLogDownCountByHour(0L, startTime, endTime); return list.stream() .map(map -> { // 从Timestamp获取时间戳 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyService.java similarity index 88% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyService.java index 799523b670..c6e3a67067 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyService.java @@ -1,9 +1,10 @@ -package cn.iocoder.yudao.module.iot.service.device.data; +package cn.iocoder.yudao.module.iot.service.device.property; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyHistoryPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import jakarta.validation.Valid; @@ -30,9 +31,10 @@ public interface IotDevicePropertyService { /** * 保存设备数据 * + * @param device 设备 * @param message 设备消息 */ - void saveDeviceProperty(IotDeviceMessage message); + void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message); /** * 获得设备属性最新数据 @@ -78,14 +80,6 @@ public interface IotDevicePropertyService { */ void updateDeviceServerId(String productKey, String deviceName, String serverId); - /** - * 删除设备关联的网关 serverId - * - * @param productKey 产品标识 - * @param deviceName 设备名称 - */ - void deleteDeviceServerId(String productKey, String deviceName); - /** * 获得设备关联的网关 serverId * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java similarity index 91% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java index 7f33e87a56..185251cf08 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java @@ -1,11 +1,10 @@ -package cn.iocoder.yudao.module.iot.service.device.data; +package cn.iocoder.yudao.module.iot.service.device.property; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyHistoryPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; @@ -121,21 +120,13 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { } @Override - @TenantIgnore - public void saveDeviceProperty(IotDeviceMessage message) { + public void saveDeviceProperty(IotDeviceDO device, IotDeviceMessage message) { if (!(message.getData() instanceof Map)) { log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message); return; } - // 1. 获得设备信息 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - message.getProductKey(), message.getDeviceName()); - if (device == null) { - log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message); - return; - } - // 2. 根据物模型,拼接合法的属性 + // 1. 根据物模型,拼接合法的属性 // TODO @芋艿:【待定 004】赋能后,属性到底以 thingModel 为准(ik),还是 db 的表结构为准(tl)? List thingModels = thingModelService.getThingModelListByProductKeyFromCache(device.getProductKey()); Map properties = new HashMap<>(); @@ -151,11 +142,11 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { return; } - // 3.1 保存设备属性【数据】 + // 2.1 保存设备属性【数据】 devicePropertyMapper.insert(device, properties, LocalDateTimeUtil.toEpochMilli(message.getReportTime())); - // 3.2 保存设备属性【日志】 + // 2.2 保存设备属性【日志】 Map properties2 = convertMap(properties.entrySet(), Map.Entry::getKey, entry -> IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build()); deviceDataRedisDAO.putAll(device.getProductKey(), device.getDeviceName(), properties2); @@ -209,11 +200,6 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { deviceServerIdRedisDAO.update(productKey, deviceName, serverId); } - @Override - public void deleteDeviceServerId(String productKey, String deviceName) { - deviceServerIdRedisDAO.delete(productKey, deviceName); - } - @Override public String getDeviceServerId(String productKey, String deviceName) { return deviceServerIdRedisDAO.get(productKey, deviceName); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 4a7263c27b..4ccdd77cad 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; +import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java index 89396ebb3f..64982a8b2f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java @@ -309,8 +309,10 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { private List getMatchedRuleSceneListByMessage(IotDeviceMessage message) { // 1. 匹配设备 // TODO @芋艿:可能需要 getSelf(); 缓存 - List ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache( - message.getProductKey(), message.getDeviceName()); + List ruleScenes = null; + // TODO @芋艿:这里需要适配 +// List ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache( +// message.getProductKey(), message.getDeviceName()); if (CollUtil.isEmpty(ruleScenes)) { return ruleScenes; } @@ -329,10 +331,11 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { } // 2.3 多个条件,只需要满足一个即可 IotRuleSceneDO.TriggerCondition matchedCondition = CollUtil.findOne(trigger.getConditions(), condition -> { - if (ObjUtil.notEqual(message.getType(), condition.getType()) - || ObjUtil.notEqual(message.getIdentifier(), condition.getIdentifier())) { - return false; - } + // TODO @芋艿:这里的逻辑,需要适配 +// if (ObjUtil.notEqual(message.getType(), condition.getType()) +// || ObjUtil.notEqual(message.getIdentifier(), condition.getIdentifier())) { +// return false; +// } // 多个条件参数,必须全部满足。所以,下面的逻辑就是找到一个不满足的条件参数 IotRuleSceneDO.TriggerConditionParameter notMatchedParameter = CollUtil.findOne(condition.getParameters(), parameter -> !isTriggerConditionParameterMatched(message, parameter, ruleScene, trigger)); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java index 3a7faee102..ff57e999a0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java @@ -1,13 +1,12 @@ package cn.iocoder.yudao.module.iot.service.rule.action; import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService; +import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -21,10 +20,10 @@ import org.springframework.stereotype.Component; @Slf4j public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction { - @Resource - private IotDeviceDownstreamService deviceDownstreamService; @Resource private IotDeviceService deviceService; + @Resource + private IotDeviceMessageService deviceMessageService; @Override public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { @@ -38,9 +37,9 @@ public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction { return; } try { - IotDeviceMessage downstreamMessage = deviceDownstreamService.downstreamDevice(new IotDeviceDownstreamReqVO() - .setId(device.getId()).setType(control.getType()).setIdentifier(control.getIdentifier()) - .setData(control.getData())); + // TODO @芋艿:@puhui999:这块可能要改,从 type => method + IotDeviceMessage downstreamMessage = deviceMessageService.sendDeviceMessage(IotDeviceMessage.requestOf( + control.getType() + control.getIdentifier(), control.getData()).setDeviceId(device.getId())); log.info("[execute][message({}) config({}) 下发消息({})成功]", message, config, downstreamMessage); } catch (Exception e) { log.error("[execute][message({}) config({}) 下发消息失败]", message, config, e); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml similarity index 51% rename from yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml rename to yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml index 2a200e52b3..5949f56bb5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMessageMapper.xml @@ -2,51 +2,62 @@ - + - - CREATE STABLE IF NOT EXISTS device_log ( + + CREATE STABLE IF NOT EXISTS device_message ( ts TIMESTAMP, id NCHAR(50), - message_id NCHAR(50), - type NCHAR(50), - identifier NCHAR(255), - content NCHAR(1024), - code INT, report_time TIMESTAMP, - tenant_id BIGINT + device_id BIGINT, + tenant_id BIGINT, + server_id NCHAR(50), + upstream BOOL, + request_id NCHAR(50), + method NCHAR(100), + params NCHAR(2048), + data NCHAR(2048), + code INT ) TAGS ( - product_key NCHAR(50), - device_name NCHAR(50) + device_id BIGINT ) - + SHOW STABLES LIKE 'device_message' - INSERT INTO device_log_${productKey}_${deviceName} ( - ts, id, message_id, type, identifier, - content, code, report_time, tenant_id + INSERT INTO device_message_${deviceId} ( + ts, id, report_time, device_id, tenant_id, + server_id, upstream, request_id, method, params, + data, code ) - USING device_log - TAGS ('${productKey}', '${deviceName}') + USING device_message + TAGS (#{deviceId}) VALUES ( - NOW, #{id}, #{messageId}, #{type}, #{identifier}, - #{content}, #{code}, #{reportTime}, #{tenantId} + #{ts}, #{id}, #{reportTime}, #{deviceId}, #{tenantId}, + #{serverId}, #{upstream}, #{requestId}, #{method}, #{params}, + #{data}, #{code} ) - + SELECT ts, id, report_time, device_id, tenant_id, server_id, upstream, + request_id, method, params, data, code + FROM device_message_${reqVO.deviceId} - - AND type = #{reqVO.type} + + AND method = #{reqVO.method} - - AND identifier LIKE CONCAT('%',#{reqVO.identifier},'%') + + AND upstream = #{reqVO.upstream} + + + AND ts >= #{reqVO.startTime} + + + AND ts <= #{reqVO.endTime} ORDER BY ts DESC @@ -54,7 +65,7 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageIdentifierEnum.java deleted file mode 100644 index ae9c9dee34..0000000000 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageIdentifierEnum.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.iot.core.enums; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -// TODO @芋艿:需要添加对应的 DTO,以及上下行的链路,网关、网关服务、设备等 - -/** - * IoT 设备消息标识符枚举 - */ -@Getter -@RequiredArgsConstructor -public enum IotDeviceMessageIdentifierEnum { - - PROPERTY_GET("get"), // 下行 TODO 芋艿:【讨论】貌似这个“上行”更合理?device 主动拉取配置。和 IotDevicePropertyGetReqDTO 一样的配置 - PROPERTY_SET("set"), // 下行 - PROPERTY_REPORT("report"), // 上行 - - STATE_ONLINE("online"), // 上行 - STATE_OFFLINE("offline"), // 上行 - - CONFIG_GET("get"), // 上行 TODO 芋艿:【讨论】暂时没有上行的场景 - CONFIG_SET("set"), // 下行 - - SERVICE_INVOKE("${identifier}"), // 下行 - SERVICE_REPLY_SUFFIX("_reply"), // 芋艿:TODO 芋艿:【讨论】上行 or 下行 - - OTA_UPGRADE("upgrade"), // 下行 - OTA_PULL("pull"), // 上行 - OTA_PROGRESS("progress"), // 上行 - OTA_REPORT("report"), // 上行 - - REGISTER_REGISTER("register"), // 上行 - REGISTER_REGISTER_SUB("register_sub"), // 上行 - REGISTER_UNREGISTER_SUB("unregister_sub"), // 下行 - - TOPOLOGY_ADD("topology_add"), // 下行; - ; - - /** - * 标志符 - */ - private final String identifier; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java index 8fd9d118f5..61b05ad37a 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java @@ -1,8 +1,13 @@ package cn.iocoder.yudao.module.iot.core.enums; +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.Arrays; +import java.util.Set; + /** * IoT 设备消息的方法枚举 * @@ -10,16 +15,47 @@ import lombok.Getter; */ @Getter @AllArgsConstructor -public enum IotDeviceMessageMethodEnum { +public enum IotDeviceMessageMethodEnum implements ArrayValuable { // ========== 设备状态 ========== - STATE_ONLINE("thing.state.online"), - STATE_OFFLINE("thing.state.offline"), + STATE_ONLINE("thing.state.online", true), + STATE_OFFLINE("thing.state.offline", true), + + // ========== 设备属性 ========== + // 可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services + PROPERTY_REPORT("thing.property.report", true), + + PROPERTY_SET("thing.property.set", false), + PROPERTY_GET("thing.property.get", false), + + // ; + public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod).toArray(String[]::new); + + /** + * 不进行 reply 回复的方法集合 + */ + public static final Set REPLY_DISABLED = Set.of(STATE_ONLINE.getMethod(), STATE_OFFLINE.getMethod()); + private final String method; + private final Boolean upstream; + + @Override + public String[] array() { + return ARRAYS; + } + + public static IotDeviceMessageMethodEnum of(String method) { + return ArrayUtil.firstMatch(item -> item.getMethod().equals(method), + IotDeviceMessageMethodEnum.values()); + } + + public static boolean isReplyDisabled(String method) { + return REPLY_DISABLED.contains(method); + } } diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageTypeEnum.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageTypeEnum.java index 6e0feb16e5..e2fe8be204 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageTypeEnum.java @@ -14,7 +14,7 @@ import java.util.Arrays; public enum IotDeviceMessageTypeEnum implements ArrayValuable { STATE("state"), // 设备状态 - PROPERTY("property"), // 设备属性:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务 +// 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 远程配置 diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java index a843dad434..adb66c7060 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.core.mq.message; +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; import lombok.AllArgsConstructor; @@ -43,6 +44,20 @@ public class IotDeviceMessage { */ private LocalDateTime reportTime; + /** + * 设备编号 + */ + private Long deviceId; + /** + * 租户编号 + */ + private Long tenantId; + + /** + * 服务编号,该消息由哪个 server 发送 + */ + private String serverId; + // ========== codec(编解码)字段 ========== /** @@ -72,84 +87,53 @@ public class IotDeviceMessage { * 响应错误码 */ private Integer code; - - // ========== 后端字段 ========== - /** - * 设备编号 + * 返回结果信息 */ - private Long deviceId; - /** - * 租户编号 - */ - private Long tenantId; + private String msg; - /** - * 服务编号,该消息由哪个 server 服务进行消费 - */ - private String serverId; + // ========== 基础方法:只传递“codec(编解码)字段” ========== -// public IotDeviceMessage ofPropertyReport(Map properties) { -// this.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); -// this.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier()); -// this.setData(properties); -// return this; -// } -// -// public IotDeviceMessage ofPropertySet(Map properties) { -// this.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); -// this.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()); -// this.setData(properties); -// return this; -// } -// -// public IotDeviceMessage ofStateOnline() { -// this.setType(IotDeviceMessageTypeEnum.STATE.getType()); -// this.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()); -// return this; -// } -// -// public IotDeviceMessage ofStateOffline() { -// this.setType(IotDeviceMessageTypeEnum.STATE.getType()); -// this.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier()); -// return this; -// } -// -// public static IotDeviceMessage of(String productKey, String deviceName) { -// return of(productKey, deviceName, -// null, null); -// } -// -// public static IotDeviceMessage of(String productKey, String deviceName, -// String serverId) { -// return of(productKey, deviceName, -// null, serverId); -// } -// -// public static IotDeviceMessage of(String productKey, String deviceName, -// LocalDateTime reportTime, String serverId) { -// if (reportTime == null) { -// reportTime = LocalDateTime.now(); -// } -// String messageId = IotDeviceMessageUtils.generateMessageId(); -// return IotDeviceMessage.builder() -// .messageId(messageId).reportTime(reportTime) -// .productKey(productKey).deviceName(deviceName) -// .serverId(serverId).build(); -// } + public static IotDeviceMessage requestOf(String method) { + return requestOf(null, method, null); + } - public static IotDeviceMessage of(String requestId, String method, Object params) { - return of(requestId, method, params, null, null); + public static IotDeviceMessage requestOf(String method, Object params) { + return requestOf(null, method, params); + } + + public static IotDeviceMessage requestOf(String requestId, String method, Object params) { + return of(requestId, method, params, null, null, null); + } + + public static IotDeviceMessage replyOf(String requestId, String method, + Object data, Integer code, String msg) { + if (code == null) { + code = GlobalErrorCodeConstants.SUCCESS.getCode(); + msg = GlobalErrorCodeConstants.SUCCESS.getMsg(); + } + return of(requestId, method, null, data, code, msg); } public static IotDeviceMessage of(String requestId, String method, - Object params, Object data, Integer code) { + Object params, Object data, Integer code, String msg) { // 通用参数 IotDeviceMessage message = new IotDeviceMessage() .setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now()); // 当前参数 - message.setRequestId(requestId).setMethod(method).setParams(params).setData(data).setCode(code); + message.setRequestId(requestId).setMethod(method).setParams(params) + .setData(data).setCode(code).setMsg(msg); return message; } + // ========== 核心方法:在 of 基础方法之上,添加对应 method ========== + + public static IotDeviceMessage buildStateOnline() { + return requestOf(IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod()); + } + + public static IotDeviceMessage buildStateOffline() { + return requestOf(IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod()); + } + } diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java index df19c06868..9c182e24d8 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java @@ -1,8 +1,9 @@ package cn.iocoder.yudao.module.iot.core.util; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.StrUtil; import cn.hutool.system.SystemUtil; +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; /** @@ -18,15 +19,28 @@ public class IotDeviceMessageUtils { return IdUtil.fastSimpleUUID(); } - // TODO @芋艿:需要优化下; /** * 是否是上行消息:由设备发送 * * @param message 消息 * @return 是否 */ + @SuppressWarnings("SimplifiableConditionalExpression") public static boolean isUpstreamMessage(IotDeviceMessage message) { - return StrUtil.isNotEmpty(message.getServerId()); + IotDeviceMessageMethodEnum methodEnum = IotDeviceMessageMethodEnum.of(message.getMethod()); + Assert.notNull(methodEnum, "无法识别的消息方法:" + message.getMethod()); + // 注意:回复消息时,需要取反 + return !isReplyMessage(message) ? methodEnum.getUpstream() : !methodEnum.getUpstream(); + } + + /** + * 是否是回复消息,通过 {@link IotDeviceMessage#getCode()} 非空进行识别 + * + * @param message 消息 + * @return 是否 + */ + public static boolean isReplyMessage(IotDeviceMessage message) { + return message.getCode() != null; } // ========== Topic 相关 ========== diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/IotAlinkDeviceMessageCodec.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/IotAlinkDeviceMessageCodec.java index f54adc6a44..e71728708c 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/IotAlinkDeviceMessageCodec.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/IotAlinkDeviceMessageCodec.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.gateway.codec.alink; import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; @@ -48,18 +49,23 @@ public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec { * 响应结果 */ private Object data; - /** * 响应错误码 */ private Integer code; + /** + * 响应提示 + * + * 特殊:这里阿里云是 message,为了保持和项目的 {@link CommonResult#getMsg()} 一致。 + */ + private String msg; } @Override public byte[] encode(IotDeviceMessage message) { AlinkMessage alinkMessage = new AlinkMessage(message.getRequestId(), AlinkMessage.VERSION_1, - message.getMethod(), message.getParams(), message.getData(), message.getCode()); + message.getMethod(), message.getParams(), message.getData(), message.getCode(), message.getMsg()); return JsonUtils.toJsonByte(alinkMessage); } @@ -69,8 +75,8 @@ public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec { AlinkMessage alinkMessage = JsonUtils.parseObject(bytes, AlinkMessage.class); Assert.notNull(alinkMessage, "消息不能为空"); Assert.equals(alinkMessage.getVersion(), AlinkMessage.VERSION_1, "消息版本号必须是 1.0"); - return IotDeviceMessage.of(alinkMessage.getId(), - alinkMessage.getMethod(), alinkMessage.getParams(), alinkMessage.getData(), alinkMessage.getCode()); + return IotDeviceMessage.of(alinkMessage.getId(), alinkMessage.getMethod(), alinkMessage.getParams(), + alinkMessage.getData(), alinkMessage.getCode(), alinkMessage.getMsg()); } @Override diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java index d730e92782..8fff35a472 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.iot.gateway.config; import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber; import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber; -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -44,16 +42,17 @@ public class IotGatewayConfiguration { @Slf4j public static class MqttProtocolConfiguration { - @Bean - public IotMqttUpstreamProtocol iotMqttUpstreamProtocol(IotGatewayProperties gatewayProperties) { - return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getEmqx()); - } - - @Bean - public IotMqttDownstreamSubscriber iotMqttDownstreamSubscriber(IotMqttUpstreamProtocol mqttUpstreamProtocol, - IotMessageBus messageBus) { - return new IotMqttDownstreamSubscriber(mqttUpstreamProtocol, messageBus); - } + // TODO @haohao:临时注释,避免报错 +// @Bean +// public IotMqttUpstreamProtocol iotMqttUpstreamProtocol(IotGatewayProperties gatewayProperties) { +// return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getEmqx()); +// } +// +// @Bean +// public IotMqttDownstreamSubscriber iotMqttDownstreamSubscriber(IotMqttUpstreamProtocol mqttUpstreamProtocol, +// IotMessageBus messageBus) { +// return new IotMqttDownstreamSubscriber(mqttUpstreamProtocol, messageBus); +// } } } diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java index d59e48b3e1..96c2a3c0f1 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java @@ -1,24 +1,18 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; import cn.hutool.core.text.StrPool; -import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; -import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService; -import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import java.util.Map; - /** * IoT 网关 HTTP 协议的【上行】处理器 * @@ -61,135 +55,4 @@ public class IotHttpUpstreamHandler extends IotHttpAbstractHandler { return CommonResult.success(MapUtil.of("messageId", message.getId())); } - /** - * 判断是否是属性上报路径 - * - * @param path 路径 - * @return 是否是属性上报路径 - */ - private boolean isPropertyPostPath(String path) { - return StrUtil.endWith(path, IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic()); - } - - /** - * 判断是否是事件上报路径 - * - * @param path 路径 - * @return 是否是事件上报路径 - */ - private boolean isEventPostPath(String path) { - return StrUtil.contains(path, IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic()) - && StrUtil.endWith(path, IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic()); - } - - /** - * 处理属性上报请求 - * - * @param routingContext 路由上下文 - * @param productKey 产品 Key - * @param deviceName 设备名称 - * @param body 请求体 - */ - private void handlePropertyPost(RoutingContext routingContext, String productKey, String deviceName, - JsonObject body) { - // 1.1 构建设备消息 - IotDeviceMessage message = IotDeviceMessage.of(productKey, deviceName, protocol.getServerId()) -// .ofPropertyReport(parsePropertiesFromBody(body)) - ; - // 1.2 发送消息 - deviceMessageProducer.sendDeviceMessage(message); - -// // 2. 返回响应 -// sendResponse(routingContext, null); - } - - /** - * 处理事件上报请求 - * - * @param routingContext 路由上下文 - * @param productKey 产品 Key - * @param deviceName 设备名称 - * @param identifier 事件标识符 - * @param body 请求体 - */ - private void handleEventPost(RoutingContext routingContext, String productKey, String deviceName, - String identifier, JsonObject body) { -// // 处理事件上报 -// IotDeviceEventReportReqDTO reportReqDTO = parseEventReportRequest(productKey, deviceName, identifier, -// requestId, body); -// -// // 事件上报 -// CommonResult result = deviceUpstreamApi.reportDeviceEvent(reportReqDTO); - } - - // TODO @芋艿:这块在看看 - /** - * 从请求体解析属性 - * - * @param body 请求体 - * @return 属性映射 - */ - private Map parsePropertiesFromBody(JsonObject body) { - Map properties = MapUtil.newHashMap(); - JsonObject params = body.getJsonObject("params"); - if (CollUtil.isEmpty(params)) { - return properties; - } - - // 将标准格式的 params 转换为平台需要的 properties 格式 - for (String key : params.fieldNames()) { - Object valueObj = params.getValue(key); - // 如果是复杂结构(包含 value 和 time) - if (valueObj instanceof JsonObject) { - JsonObject valueJson = (JsonObject) valueObj; - properties.put(key, valueJson.containsKey("value") ? valueJson.getValue("value") : valueObj); - } else { - properties.put(key, valueObj); - } - } - return properties; - } - -// /** -// * 解析事件上报请求 -// * -// * @param productKey 产品 Key -// * @param deviceName 设备名称 -// * @param identifier 事件标识符 -// * @param requestId 请求 ID -// * @param body 请求体 -// * @return 事件上报请求 DTO -// */ -// private IotDeviceEventReportReqDTO parseEventReportRequest(String productKey, String deviceName, String identifier, -// String requestId, JsonObject body) { -// // 解析参数 -// Map params = parseParamsFromBody(body); -// -// // 构建事件上报请求 DTO -// return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO() -// .setRequestId(requestId) -// .setProcessId(IotNetComponentCommonUtils.getProcessId()) -// .setReportTime(LocalDateTime.now()) -// .setProductKey(productKey) -// .setDeviceName(deviceName)).setIdentifier(identifier).setParams(params); -// } - - /** - * 从请求体解析参数 - * - * @param body 请求体 - * @return 参数映射 - */ - private Map parseParamsFromBody(JsonObject body) { - Map params = MapUtil.newHashMap(); - JsonObject paramsJson = body.getJsonObject("params"); - if (CollUtil.isEmpty(paramsJson)) { - return params; - } - - for (String key : paramsJson.fieldNames()) { - params.put(key, paramsJson.getValue(key)); - } - return params; - } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java index 3c89ca7efe..5987d5561a 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java @@ -96,7 +96,7 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { return null; } - IotDeviceMessage message = IotDeviceMessage.of(null, + IotDeviceMessage message = IotDeviceMessage.requestOf(null, IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(), null); return appendDeviceMessage(message, deviceInfo, serverId); @@ -112,9 +112,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { return null; } - IotDeviceMessage message = IotDeviceMessage.of(null, - IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod(), null); - + IotDeviceMessage message = IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod(), + null); return appendDeviceMessage(message, deviceInfo, serverId); } @@ -122,23 +121,18 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { * 补充消息的后端字段 * * @param message 消息 - * @param deviceInfo 设备信息 + * @param device 设备信息 * @param serverId 设备连接的 serverId * @return 消息 */ private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message, - IotDeviceCacheService.DeviceInfo deviceInfo, String serverId) { + IotDeviceCacheService.DeviceInfo device, String serverId) { message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now()) - .setDeviceId(deviceInfo.getDeviceId()).setTenantId(deviceInfo.getTenantId()).setServerId(serverId); - + .setDeviceId(device.getDeviceId()).setTenantId(device.getTenantId()).setServerId(serverId); // 特殊:如果设备没有指定 requestId,则使用 messageId if (StrUtil.isEmpty(message.getRequestId())) { message.setRequestId(message.getId()); } - - log.debug("[appendDeviceMessage][消息字段补充完成][deviceId: {}][tenantId: {}]", - deviceInfo.getDeviceId(), deviceInfo.getTenantId()); - return message; }