From 8089f3a319efb919fce809b8e144c3fcc0694b4f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 27 Jan 2025 14:15:07 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E3=80=91IoT=EF=BC=9A=201.=20DeviceDataApi=20=3D>=20IotDeviceUp?= =?UTF-8?q?streamApi=EF=BC=8C=E5=B9=B6=E6=96=B0=E5=BB=BA=20upstream=20?= =?UTF-8?q?=E5=8C=85=202.=20ThingModelMessage=20=3D>=20IotDeviceMessage=20?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E6=B6=88=E6=81=AF=203.=20=E5=9F=BA=E4=BA=8E?= =?UTF-8?q?=20spring=20event=20=E5=BC=82=E6=AD=A5=E6=B6=88=E8=B4=B9=20IotD?= =?UTF-8?q?eviceMessage=EF=BC=8C=E5=B9=B6=E5=AE=9E=E7=8E=B0=20IotDeviceLog?= =?UTF-8?q?MessageConsumer=20=E8=AE=B0=E5=BD=95=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/db/TenantDatabaseInterceptor.java | 2 +- .../tenant/core/util/TenantUtils.java | 20 ++ ...DataApi.java => IotDeviceUpstreamApi.java} | 33 +-- .../dto/IotDeviceEventReportReqDTO.java | 22 +- .../dto/IotDevicePropertyReportReqDTO.java | 23 +- .../dto/IotDeviceStatusUpdateReqDTO.java | 21 +- .../dto/IotDeviceUpstreamAbstractReqDTO.java | 46 +++ .../IotDeviceMessageIdentifierEnum.java | 22 ++ .../device/IotDeviceMessageTypeEnum.java | 22 ++ ...mpl.java => IoTDeviceUpstreamApiImpl.java} | 22 +- .../admin/device/IotDeviceDataController.java | 10 +- .../dal/dataobject/device/IotDeviceDO.java | 2 + .../dal/dataobject/device/IotDeviceLogDO.java | 36 ++- .../tdengine/ThingModelMessage.java | 70 ----- .../dal/tdengine/IotDeviceLogDataMapper.java | 32 +- .../iot/emq/service/EmqxServiceImpl.java | 8 +- .../iot/framework/aspect/TaosAspect.java | 1 + .../config/SecurityConfiguration.java | 29 ++ .../framework/security/core/package-info.java | 4 + .../TDengineTableInitConfiguration.java | 4 +- .../iot/job/plugin/PluginInstancesJob.java | 1 + .../device/IotDeviceLogMessageConsumer.java | 30 ++ .../IotDevicePropertyMessageConsumer.java | 34 +++ .../deviceconsumer/DeviceConsumer.java | 41 --- .../iot/mq/consumer/rule/package-info.java | 4 + .../iot/mq/message/IotDeviceMessage.java | 66 +++++ .../module/iot/mq/message/package-info.java | 4 - .../IotDeviceProducer.java} | 13 +- .../module/iot/mq/producer/package-info.java | 4 + .../device/IotDeviceLogDataServiceImpl.java | 77 ----- .../IotDeviceLogService.java} | 20 +- .../device/data/IotDeviceLogServiceImpl.java | 60 ++++ .../IotDevicePropertyService.java} | 12 +- .../IotDevicePropertyServiceImpl.java} | 60 +--- .../upstream/IotDeviceUpstreamService.java | 37 +++ .../IotDeviceUpstreamServiceImpl.java | 103 +++++++ .../product/IotProductServiceImpl.java | 4 +- .../tdengine/IotThingModelMessageService.java | 21 -- .../IotThingModelMessageServiceImpl.java | 277 ------------------ .../mapper/device/IotDeviceLogDataMapper.xml | 33 +-- .../common/api/DeviceDataApiClient.java | 81 ++--- .../YudaoDeviceDataApiAutoConfiguration.java | 4 +- .../yudao/module/iot/plugin/EmqxPlugin.java | 4 +- .../plugin/http/config/HttpVertxPlugin.java | 4 +- .../config/HttpVertxPluginConfiguration.java | 4 +- .../plugin/http/service/HttpVertxHandler.java | 11 +- 46 files changed, 648 insertions(+), 790 deletions(-) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/{DeviceDataApi.java => IotDeviceUpstreamApi.java} (72%) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/{DeviceDataApiImpl.java => IoTDeviceUpstreamApiImpl.java} (69%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageConsumer.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/{simulatesend/SimulateSendProducer.java => device/IotDeviceProducer.java} (50%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDeviceLogDataService.java => data/IotDeviceLogService.java} (55%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDevicePropertyDataService.java => data/IotDevicePropertyService.java} (82%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDevicePropertyDataServiceImpl.java => data/IotDevicePropertyServiceImpl.java} (81%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java index e220f8bcf0..8ea1a96b87 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java @@ -13,7 +13,7 @@ import java.util.Set; /** * 基于 MyBatis Plus 多租户的功能,实现 DB 层面的多租户的功能 * - * @author + * @author 芋道源码 */ public class TenantDatabaseInterceptor implements TenantLineHandler { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java index 7ec9c69e33..b05b3c06be 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java @@ -45,6 +45,7 @@ public class TenantUtils { * * @param tenantId 租户编号 * @param callable 逻辑 + * @return 结果 */ public static V execute(Long tenantId, Callable callable) { Long oldTenantId = TenantContextHolder.getTenantId(); @@ -78,6 +79,25 @@ public class TenantUtils { } } + /** + * 忽略租户,执行对应的逻辑 + * + * @param callable 逻辑 + * @return 结果 + */ + public static V executeIgnore(Callable callable) { + Boolean oldIgnore = TenantContextHolder.isIgnore(); + try { + TenantContextHolder.setIgnore(true); + // 执行逻辑 + return callable.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + TenantContextHolder.setIgnore(oldIgnore); + } + } + /** * 将多租户编号,添加到 header 中 * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java similarity index 72% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java index c2d36e18c6..996ff1f5a3 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java @@ -5,49 +5,44 @@ import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; import cn.iocoder.yudao.module.iot.enums.ApiConstants; -import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; -// TODO 芋艿:名字可能看情况改下 /** - * 设备数据 API + * 设备数据 Upstream 上行 API + * + * 目的:设备 -> 插件 -> 服务端 * * @author haohao */ -public interface DeviceDataApi { +public interface IotDeviceUpstreamApi { - // TODO @芋艿:可能会调整 - String PREFIX = ApiConstants.PREFIX + "/device-data"; + String PREFIX = ApiConstants.PREFIX + "/device/upstream"; /** * 更新设备状态 * - * @param updateReqDTO 更新请求 + * @param updateReqDTO 更新设备状态 DTO */ @PutMapping(PREFIX + "/update-status") - @PermitAll // TODO 芋艿:后续看看怎么优化下 CommonResult updateDeviceStatus(@Valid @RequestBody IotDeviceStatusUpdateReqDTO updateReqDTO); + /** + * 上报设备属性数据 + * + * @param reportReqDTO 上报设备属性数据 DTO + */ + @PostMapping(PREFIX + "/report-property") + CommonResult reportDevicePropertyData(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO); + /** * 上报设备事件数据 * * @param reportReqDTO 设备事件 */ @PostMapping(PREFIX + "/report-event") - @PermitAll // TODO 芋艿:后续看看怎么优化下 CommonResult reportDeviceEventData(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO); - /** - * 上报设备属性数据 - * - * @param reportReqDTO 设备数据 - */ - @PostMapping(PREFIX + "/report-property") - @PermitAll // TODO 芋艿:后续看看怎么优化下 - CommonResult reportDevicePropertyData(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO); - - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java index 373905c946..6376eb8a41 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java @@ -1,10 +1,9 @@ package cn.iocoder.yudao.module.iot.api.device.dto; import jakarta.validation.constraints.NotEmpty; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import java.util.Map; @@ -12,24 +11,9 @@ import java.util.Map; * IoT 设备【事件】数据上报 Request DTO */ @Data +@SuperBuilder @NoArgsConstructor -@AllArgsConstructor -@Builder -public class IotDeviceEventReportReqDTO { - - // TODO 芋艿:要不要 id - // TODO 芋艿:要不要 time - - /** - * 产品标识 - */ - @NotEmpty(message = "产品标识不能为空") - private String productKey; - /** - * 设备名称 - */ - @NotEmpty(message = "设备名称不能为空") - private String deviceName; +public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { /** * 事件标识 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java index 37a4c6c984..9173a39ca9 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java @@ -1,10 +1,9 @@ package cn.iocoder.yudao.module.iot.api.device.dto; import jakarta.validation.constraints.NotEmpty; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import java.util.Map; @@ -12,28 +11,14 @@ import java.util.Map; * IoT 设备【属性】数据上报 Request DTO */ @Data +@SuperBuilder @NoArgsConstructor -@AllArgsConstructor -@Builder -public class IotDevicePropertyReportReqDTO { +public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { - // TODO 芋艿:要不要 id - // TODO 芋艿:要不要 time - - /** - * 产品标识 - */ - @NotEmpty(message = "产品标识不能为空") - private String productKey; - /** - * 设备名称 - */ - @NotEmpty(message = "设备名称不能为空") - private String deviceName; /** * 属性参数 */ @NotEmpty(message = "属性参数不能为空") - private Map params; + private Map properties; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java index 0b08f2bd11..111427a03c 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java @@ -3,33 +3,18 @@ package cn.iocoder.yudao.module.iot.api.device.dto; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import jakarta.validation.constraints.NotEmpty; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; /** * IoT 设备状态更新 Request DTO */ @Data +@SuperBuilder @NoArgsConstructor -@AllArgsConstructor -@Builder -public class IotDeviceStatusUpdateReqDTO { +public class IotDeviceStatusUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO { - // TODO 芋艿:要不要 id - // TODO 芋艿:要不要 time - - /** - * 产品标识 - */ - @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/IotDeviceUpstreamAbstractReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java new file mode 100644 index 0000000000..b2a843effc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.api.device.dto; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.time.LocalDateTime; + +/** + * IoT 设备上行的抽象 Request DTO + * + * @author 芋道源码 + */ +@Data +@SuperBuilder +@NoArgsConstructor +public abstract class IotDeviceUpstreamAbstractReqDTO { + + /** + * 请求编号 + */ + private String requestId; + + /** + * 插件标识 + */ + private String pluginKey; + + /** + * 产品标识 + */ + @NotEmpty(message = "产品标识不能为空") + private String productKey; + /** + * 设备名称 + */ + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + + /** + * 上报时间 + */ + private LocalDateTime reportTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java new file mode 100644 index 0000000000..bb639f49a2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.enums.device; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * IoT 设备消息标识符枚举 + */ +@Getter +@RequiredArgsConstructor +public enum IotDeviceMessageIdentifierEnum { + + PROPERTY_GET("get"), + PROPERTY_SET("set"), + PROPERTY_REPORT("report"); + + /** + * 标志符 + */ + private final String identifier; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java new file mode 100644 index 0000000000..b3f00e8600 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.enums.device; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * IoT 设备消息类型枚举 + */ +@Getter +@RequiredArgsConstructor +public enum IotDeviceMessageTypeEnum { + + STATE("state"), // 设备状态 + PROPERTY("property"), // 设备属性 + EVENT("event"); // 设备事件 + + /** + * 属性 + */ + private final String type; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java similarity index 69% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java index cdcfdfdfd6..0e6df14804 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; +import cn.iocoder.yudao.module.iot.service.device.upstream.IotDeviceUpstreamService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; @@ -13,28 +13,30 @@ import javax.annotation.Resource; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; /** - * 设备数据 API 实现类 + * * 设备数据 Upstream 上行 API 实现类 */ @RestController @Validated -public class DeviceDataApiImpl implements DeviceDataApi { +public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { @Resource - private IotDevicePropertyDataService deviceDataService; + private IotDeviceUpstreamService deviceUpstreamService; @Override public CommonResult updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { - return success(true); - } - - @Override - public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + deviceUpstreamService.updateDeviceStatus(updateReqDTO); return success(true); } @Override public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { - deviceDataService.saveDeviceData(reportReqDTO); + deviceUpstreamService.reportDevicePropertyData(reportReqDTO); + return success(true); + } + + @Override + public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + deviceUpstreamService.reportDeviceEventData(reportReqDTO); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 638c880467..15366cf9c3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -6,8 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -27,13 +27,13 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; public class IotDeviceDataController { @Resource - private IotDevicePropertyDataService deviceDataService; + private IotDevicePropertyService deviceDataService; @Resource - private IotDeviceLogDataService iotDeviceLogDataService; + private IotDeviceLogService iotDeviceLogDataService; @Resource // TODO @super:service 之间,不用空行;原因是,这样更简洁;空行,主要是为了“间隔”,提升可读性 - private IotDeviceLogDataService deviceLogDataService; + private IotDeviceLogService deviceLogDataService; // TODO @浩浩:这里的 /latest-list,包括方法名。 @GetMapping("/latest") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java index f396855f13..f1d5f9792f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java @@ -36,6 +36,8 @@ public class IotDeviceDO extends BaseDO { private Long id; /** * 设备唯一标识符,全局唯一,用于识别设备 + * + * 类似阿里云 QueryDeviceInfo 的 IotInstanceId */ private String deviceKey; /** 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 index 158b0f57b9..afd0669414 100644 --- 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 @@ -1,6 +1,10 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; +import cn.hutool.core.util.IdUtil; 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 cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -19,37 +23,51 @@ import lombok.NoArgsConstructor; @AllArgsConstructor public class IotDeviceLogDO { - // TODO @芋艿:消息 ID 的生成逻辑 /** - * 消息 ID + * 日志编号 + * + * 通过 {@link IdUtil#fastSimpleUUID()} 生成 */ private String id; + /** + * 请求编号 + * + * 对应 {@link IotDeviceMessage#getRequestId()} 字段 + */ + private String requestId; + /** * 产品标识 *

* 关联 {@link IotProductDO#getProductKey()} */ private String productKey; - + /** + * 设备名称 + * + * 关联 {@link IotDeviceDO#getDeviceName()} + */ + private String deviceName; /** * 设备标识 *

* 关联 {@link IotDeviceDO#getDeviceKey()}} */ - private String deviceKey; + private String deviceKey; // 非存储字段,用于 TDengine 的 TAG - // TODO @super:枚举类 /** * 日志类型 + * + * 枚举 {@link IotDeviceMessageTypeEnum} */ private String type; - - // TODO @super:枚举类 /** - * 标识符:用于标识具体的属性、事件或服务 + * 标识符 + * + * 枚举 {@link IotDeviceMessageIdentifierEnum} */ - private String subType; + private String identifier; /** * 数据内容 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java deleted file mode 100644 index d5009dc244..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.HashMap; -import java.util.Map; - -/** - * 物模型消息 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class ThingModelMessage { - - /** - * 消息ID - */ - private String id; - - /** - * 扩展功能的参数 - */ - private Object sys; - - /** - * 请求方法 例如:thing.event.property.post - */ - private String method; - - /** - * 请求参数 - */ - private Object params; - - /** - * 属性上报时间戳 - */ - private Long time; - - /** - * 设备信息 - */ - private String productKey; - - /** - * 设备名称 - */ - private String deviceName; - - /** - * 设备 key - */ - private String deviceKey; - - /** - * 转换为 Map 类型 - */ - public Map dataToMap() { - Map mapData = new HashMap<>(); - if (params instanceof Map) { - ((Map) params).forEach((key, value) -> mapData.put(key.toString(), value)); - } - return mapData; - } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java index f9d18bdccd..cd9f387a71 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java @@ -22,21 +22,13 @@ public interface IotDeviceLogDataMapper { */ void createDeviceLogSTable(); - // TODO @super:单个参数,不用加 @Param - // TODO @芋艿:在瞅瞅 - //讨论:艿菇这里有些特殊情况,我也学习了一下这块知识: - // 如果使用的是Java 8及以上版本,并且编译器保留了参数名(通过编译器选项-parameters启用),则可以去掉@Param注解。MyBatis会自动使用参数的实际名称 - // 但在TDengine中 @Param去掉后TDengine会报错,以下是大模型的回答: - // 不用加 @Param在普通的 MySQL 场景下是正确的 - 对于 MyBatis,当方法只有一个参数时,确实可以不用添加 @Param 注解。 - //但是在 TDengine 的场景下,情况不同: - //TDengine 的特殊性: - //TDengine 使用特殊的 SQL 语法 - //需要处理超级表(STable)和子表的概念 - //参数绑定的方式与普通 MySQL 不同 - //为什么这里必须要 @Param: - //XML 中使用了 ${log.deviceKey} 这样的参数引用方式 - //需要在 SQL 中动态构建表名(device_log_${log.deviceKey}) - //没有 @Param("log") 的话,MyBatis 无法正确解析参数 + /** + * 查询设备日志表是否存在 + * + * @return 存在则返回表名;不存在则返回 null + */ + String showDeviceLogSTable(); + /** * 插入设备日志数据 * @@ -44,7 +36,7 @@ public interface IotDeviceLogDataMapper { * * @param log 设备日志数据 */ - void insert(@Param("log") IotDeviceLogDO log); + void insert(IotDeviceLogDO log); /** * 获得设备日志分页 @@ -62,12 +54,4 @@ public interface IotDeviceLogDataMapper { */ Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO); - // TODO @芋艿:这个方法名,后续看看叫啥好 - /** - * 查询设备日志表是否存在 - * - * @return 不存在返回 null - */ - Object checkDeviceLogSTableExists(); - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index 222d1d50af..ca9a6bac7b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.emq.service; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttClient; @@ -20,7 +20,7 @@ import org.springframework.scheduling.annotation.Async; public class EmqxServiceImpl implements EmqxService { @Resource - private IotDevicePropertyDataService iotDeviceDataService; + private IotDevicePropertyService iotDeviceDataService; // TODO 多线程处理消息 @Override @@ -35,8 +35,8 @@ public class EmqxServiceImpl implements EmqxService { String deviceName = topic.split("/")[3]; String message = new String(mqttMessage.getPayload()); IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder() - .productKey(productKey) - .deviceName(deviceName) +// .productKey(productKey) +// .deviceName(deviceName) // .properties(message) // TODO 芋艿:临时去掉,看看 .build(); iotDeviceDataService.saveDeviceData(createDTO); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java index 7c9fe70009..61b03dc42e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component; import java.sql.Timestamp; import java.util.Map; +// TODO @haohao:这个还需要的么? /** * TaosAspect 是一个处理 Taos 数据库返回值的切面。 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java new file mode 100644 index 0000000000..9cf00cc104 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.framework.security.config; + +import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; +import cn.iocoder.yudao.module.iot.enums.ApiConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; + +/** + * IoT 模块的 Security 配置 + */ +@Configuration(proxyBeanMethods = false, value = "iotSecurityConfiguration") +public class SecurityConfiguration { + + @Bean("iotAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + + @Override + public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { + // RPC 服务的安全配置 + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); + } + + }; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java new file mode 100644 index 0000000000..c714d10274 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package cn.iocoder.yudao.module.iot.framework.security.core; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java index 9f3c79c227..3e84ac11c8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.framework.tdengine.config; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; @@ -17,7 +17,7 @@ import org.springframework.context.annotation.Configuration; @RequiredArgsConstructor public class TDengineTableInitConfiguration implements ApplicationRunner { - private final IotDeviceLogDataService deviceLogService; + private final IotDeviceLogService deviceLogService; @Override public void run(ApplicationArguments args) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java index d32148b47c..fbcfea3404 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; +// TODO 芋艿:后续再看看 /** * 插件实例 Job * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageConsumer.java new file mode 100644 index 0000000000..2972677918 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageConsumer.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.iot.mq.consumer.device; + +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +/** + * 针对 {@link IotDeviceMessage} 的消费者,记录设备日志 + * + * @author 芋道源码 + */ +@Component +@Slf4j +public class IotDeviceLogMessageConsumer { + + @Resource + private IotDeviceLogService deviceLogService; + + @EventListener + @Async + 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/IotDevicePropertyMessageConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java new file mode 100644 index 0000000000..63b4c9a5e0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.mq.consumer.device; + +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * 针对 {@link IotDeviceMessage} 的消费者,记录设备属性 + * + * @author alwayssuper + */ +@Component +@Slf4j +public class IotDevicePropertyMessageConsumer { + + @Resource + private IotDevicePropertyService deviceDataService; + + @EventListener + @Async + public void onMessage(IotDeviceMessage message) { + log.info("[onMessage][消息内容({})]", message); + + // 设备日志记录 + // TODO @芋艿:重新写下 +// deviceLogDataService.createDeviceLog(message); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java deleted file mode 100644 index 7403fb6686..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.iot.mq.consumer.deviceconsumer; - - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link ThingModelMessage} 的消费者 - * - * @author alwayssuper - */ -@Component -@Slf4j -public class DeviceConsumer { - - @Resource - private IotDeviceLogDataService deviceLogDataService; - @Resource - private IotDevicePropertyDataService deviceDataService; - - // TODO @芋艿:这块先用ThingModelMessage,后续看看用啥替代 - @EventListener - @Async - public void onMessage(ThingModelMessage message) { - log.info("[onMessage][消息内容({})]", message); - //TODO:数据插入这块整体写的比较混乱,整体借鉴了浩浩哥之前写的逻辑,目前是通过模拟设备科插入数据了,但之前的逻辑有大量弃用的部分,后续看看怎么完善 - - // 设备数据记录 - deviceDataService.saveDeviceDataTest(message); - // 设备日志记录 - deviceLogDataService.saveDeviceLog(message); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java new file mode 100644 index 0000000000..3920443172 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 芋艿:未来实现一个 IotRuleMessageConsumer + */ +package cn.iocoder.yudao.module.iot.mq.consumer.rule; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java new file mode 100644 index 0000000000..db42b4fe16 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.iot.mq.message; + +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 设备消息 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class IotDeviceMessage { + + /** + * 请求编号 + */ + private String requestId; + + /** + * 设备信息 + */ + private String productKey; + /** + * 设备名称 + */ + private String deviceName; + /** + * 设备标识 + */ + private String deviceKey; + + /** + * 消息类型 + * + * 枚举 {@link IotDeviceMessageTypeEnum} + */ + private String type; + /** + * 标识符 + * + * 枚举 {@link IotDeviceMessageIdentifierEnum} + */ + private String identifier; + + /** + * 请求参数 + * + * 例如说:属性上报的 properties、事件上报的 params + */ + private Object data; + + /** + * 上报时间 + */ + private LocalDateTime reportTime; + + // TODO @芋艿 code; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java deleted file mode 100644 index c3adf7c061..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 消息队列的消息 - */ -package cn.iocoder.yudao.module.iot.mq.message; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java similarity index 50% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java index 7366f4da54..c3855fbfe6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java @@ -1,31 +1,30 @@ -package cn.iocoder.yudao.module.iot.mq.producer.simulatesend; +package cn.iocoder.yudao.module.iot.mq.producer.device; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; -// TODO @芋艿:@alwayssuper:是不是还没用起来哈?Producer 最好属于某个模块; /** - * SimulateSend 模拟设备上报的 Producer + * Iot 设备相关消息的 Producer * * @author alwayssuper * @since 2024/12/17 16:35 */ @Slf4j @Component -public class SimulateSendProducer { +public class IotDeviceProducer { @Resource private ApplicationContext applicationContext; /** - * 发送 {@link ThingModelMessage} 消息 + * 发送 {@link IotDeviceMessage} 消息 * * @param thingModelMessage 物模型消息 */ - public void sendSimulateMessage(ThingModelMessage thingModelMessage) { + public void sendDeviceMessage(IotDeviceMessage thingModelMessage) { applicationContext.publishEvent(thingModelMessage); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java new file mode 100644 index 0000000000..37d0ba016d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 芋艿:临时占位 + */ +package cn.iocoder.yudao.module.iot.mq.producer; \ 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/IotDeviceLogDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java deleted file mode 100644 index bfe3199551..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.hutool.json.JSONUtil; -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.deviceData.IotDeviceDataSimulatorSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.List; - -/** - * IoT 设备日志数据 Service 实现了 - * - * @author alwayssuper - */ -@Service -@Slf4j -@Validated -public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ - - @Resource - private IotDeviceLogDataMapper deviceLogDataMapper; - - @Override - public void defineDeviceLog() { - if (deviceLogDataMapper.checkDeviceLogSTableExists() != null) { - log.info("[defineDeviceLog][设备日志超级表已存在,跳过创建]"); - return; - } - - log.info("[defineDeviceLog][设备日志超级表不存在,开始创建]"); - deviceLogDataMapper.createDeviceLogSTable(); - log.info("[defineDeviceLog][设备日志超级表不存在,创建完成]"); - } - - @Override - public void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { - // 1. 转换请求对象为 DO - IotDeviceLogDO iotDeviceLogDO = BeanUtils.toBean(simulatorReqVO, IotDeviceLogDO.class); - - // 2. 处理时间字段 -// iotDeviceLogDO.setTs(currentTime); // TODO @super:TS在SQL中直接NOW 咱们的TS数据获取是走哪一种;走 now() - - // 3. 插入数据 - deviceLogDataMapper.insert(iotDeviceLogDO); - } - - @Override - public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { - // TODO @芋艿:增加一个表不存在的 try catch - List list = deviceLogDataMapper.selectPage(pageReqVO); - Long total = deviceLogDataMapper.selectCount(pageReqVO); - return new PageResult<>(list, total); - } - - @Override - public void saveDeviceLog(ThingModelMessage message) { - IotDeviceLogDO log = IotDeviceLogDO.builder() - .id(message.getId()) - .deviceKey(message.getDeviceKey()) - .productKey(message.getProductKey()) - .type(message.getMethod()) // 消息类型,使用method作为类型 TODO 芋艿:在看看 - .subType("property") // TODO 芋艿:这块先写死,后续优化 - .content(JSONUtil.toJsonStr(message)) // TODO 芋艿:后续优化 - .reportTime(message.getTime()) // 上报时间 TODO 芋艿:在想想时间 - .build(); - deviceLogDataMapper.insert(log); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java similarity index 55% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java index 637c8f51a3..ff695a7820 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java @@ -1,17 +1,16 @@ -package cn.iocoder.yudao.module.iot.service.device; +package cn.iocoder.yudao.module.iot.service.device.data; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; /** * IoT 设备日志数据 Service 接口 * * @author alwayssuper */ -public interface IotDeviceLogDataService { +public interface IotDeviceLogService { /** * 初始化 TDengine 超级表 @@ -23,11 +22,9 @@ public interface IotDeviceLogDataService { /** * 插入设备日志 * - * 当该设备第一次插入日志时,自动创建该设备的设备日志子表 - * - * @param simulatorReqVO 设备日志模拟数据 + * @param message 设备数据 */ - void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO); + void createDeviceLog(IotDeviceMessage message); /** * 获得设备日志分页 @@ -37,11 +34,4 @@ public interface IotDeviceLogDataService { */ PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO); - /** - * 插入设备日志 - * - * @param message 设备数据 - */ - void saveDeviceLog(ThingModelMessage message); - } 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/data/IotDeviceLogServiceImpl.java new file mode 100644 index 0000000000..fa5398a79a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.iot.service.device.data; + +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.deviceData.IotDeviceLogPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * IoT 设备日志数据 Service 实现类 + * + * @author alwayssuper + */ +@Service +@Slf4j +@Validated +public class IotDeviceLogServiceImpl implements IotDeviceLogService { + + @Resource + private IotDeviceLogDataMapper deviceLogDataMapper; + + @Override + public void defineDeviceLog() { + if (StrUtil.isNotEmpty(deviceLogDataMapper.showDeviceLogSTable())) { + log.info("[defineDeviceLog][设备日志超级表已存在,创建跳过]"); + return; + } + + log.info("[defineDeviceLog][设备日志超级表不存在,创建开始...]"); + deviceLogDataMapper.createDeviceLogSTable(); + log.info("[defineDeviceLog][设备日志超级表不存在,创建成功]"); + } + + @Override + public void createDeviceLog(IotDeviceMessage message) { + IotDeviceLogDO log = BeanUtils.toBean(message, IotDeviceLogDO.class) + .setId(IdUtil.fastSimpleUUID()) + .setContent(JsonUtils.toJsonString(message.getData())); + deviceLogDataMapper.insert(log); + } + + @Override + public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { + // TODO @芋艿:增加一个表不存在的 try catch + List list = deviceLogDataMapper.selectPage(pageReqVO); + Long total = deviceLogDataMapper.selectCount(pageReqVO); + return new PageResult<>(list, total); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java index 672785694e..828f2fef5e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java @@ -1,11 +1,10 @@ -package cn.iocoder.yudao.module.iot.service.device; +package cn.iocoder.yudao.module.iot.service.device.data; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import jakarta.validation.Valid; import java.util.List; @@ -16,7 +15,7 @@ import java.util.Map; * * @author 芋道源码 */ -public interface IotDevicePropertyDataService { +public interface IotDevicePropertyService { /** * 定义设备属性数据的结构 @@ -32,13 +31,6 @@ public interface IotDevicePropertyDataService { */ void saveDeviceData(IotDevicePropertyReportReqDTO createDTO); - /** - * 保存设备数据 - * - * @param thingModelMessage 设备数据 - */ - void saveDeviceDataTest(ThingModelMessage thingModelMessage); - /** * 模拟设备 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java similarity index 81% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index abd788948f..741ba70cb3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -1,9 +1,8 @@ -package cn.iocoder.yudao.module.iot.service.device; +package cn.iocoder.yudao.module.iot.service.device.data; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; @@ -16,18 +15,15 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; -import cn.iocoder.yudao.module.iot.mq.producer.simulatesend.SimulateSendProducer; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -53,7 +49,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DATA_C */ @Service @Slf4j -public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataService { +public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { /** * 物模型的数据类型,与 TDengine 数据类型的映射关系 @@ -76,25 +72,16 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe @Resource private IotDeviceService deviceService; @Resource - private IotThingModelMessageService thingModelMessageService; - @Resource private IotThingModelService thingModelService; @Resource private IotProductService productService; - @Resource - private SimulateSendProducer simulateSendProducer; - - @Resource - private TdEngineDMLMapper tdEngineDMLMapper; - @Resource private DeviceDataRedisDAO deviceDataRedisDAO; @Resource private IotDevicePropertyDataMapper devicePropertyDataMapper; - @Override public void defineDevicePropertyData(Long productId) { // 1.1 查询产品和物模型 @@ -144,28 +131,8 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe // 1. 根据产品 key 和设备名称,获得设备信息 IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(createDTO.getProductKey(), createDTO.getDeviceName()); // 2. 解析消息,保存数据 - JSONObject jsonObject = new JSONObject(createDTO.getParams()); + JSONObject jsonObject = new JSONObject(createDTO.getProperties()); log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", createDTO.getProductKey(), createDTO.getDeviceName(), jsonObject); - ThingModelMessage thingModelMessage = ThingModelMessage.builder() - .id(jsonObject.getStr("id")) - .sys(jsonObject.get("sys")) - .method(jsonObject.getStr("method")) - .params(jsonObject.get("params")) - .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time")) - .productKey(createDTO.getProductKey()) - .deviceName(createDTO.getDeviceName()) - .deviceKey(device.getDeviceKey()) - .build(); - thingModelMessageService.saveThingModelMessage(device, thingModelMessage); - } - - //TODO @芋艿:后续捋一捋这块逻辑,先借鉴一下目前的代码 - @Override - public void saveDeviceDataTest(ThingModelMessage thingModelMessage) { - // 1. 根据产品 key 和设备名称,获得设备信息 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(thingModelMessage.getProductKey(), thingModelMessage.getDeviceName()); - // 2. 保存数据 - thingModelMessageService.saveThingModelMessage(device, thingModelMessage); } //TODO @芋艿:copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化 @@ -182,20 +149,17 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe throw exception(DEVICE_DATA_CONTENT_JSON_PARSE_ERROR); } + // TODO @芋艿:后续优化 // 3. 构建物模型消息 - ThingModelMessage thingModelMessage = ThingModelMessage.builder() - .id(IdUtil.fastSimpleUUID()) // TODO:后续优化 - .sys(null)// TODO:这块先写死,后续优化 - .method("thing.event.property.post") // TODO:这块先写死,后续优化 - .params(contentJson) // 将 content 作为 params - .time(simulatorReqVO.getReportTime()) // 使用上报时间 - .productKey(simulatorReqVO.getProductKey()) - .deviceName(device.getDeviceName()) - .deviceKey(device.getDeviceKey()) - .build(); +// IotDeviceMessage thingModelMessage = IotDeviceMessage.builder() +// .params(contentJson) // 将 content 作为 params +// .time(simulatorReqVO.getReportTime()) // 使用上报时间 +// .productKey(simulatorReqVO.getProductKey()) +// .deviceName(device.getDeviceName()) +// .build(); // 4. 发送模拟消息 - simulateSendProducer.sendSimulateMessage(thingModelMessage); +// simulateSendProducer.sendDeviceMessage(thingModelMessage); } @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java new file mode 100644 index 0000000000..16d387d7b0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.iot.service.device.upstream; + +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; + +/** + * 设备上行 Service 接口 + * + * 目的:设备 -> 插件 -> 服务端 + * + * @author 芋道源码 + */ +public interface IotDeviceUpstreamService { + + /** + * 更新设备状态 + * + * @param updateReqDTO 更新设备状态 DTO + */ + void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO); + + /** + * 上报设备属性数据 + * + * @param reportReqDTO 上报设备属性数据 DTO + */ + void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO); + + /** + * 上报设备事件数据 + * + * @param reportReqDTO 设备事件 + */ + void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java new file mode 100644 index 0000000000..d930d0ad08 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.iot.service.device.upstream; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceUpstreamAbstractReqDTO; +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.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; + +/** + * 设备上行 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { + + @Resource + private IotDeviceService deviceService; + + @Resource + private IotDeviceProducer deviceProducer; + + @Override + public void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { + log.info("[updateDeviceStatus][更新设备状态: {}]", updateReqDTO); + // TODO 芋艿:插件状态 + } + + @Override + public void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { + // 1.1 获得设备 + log.info("[reportDevicePropertyData][上报设备属性数据: {}]", reportReqDTO); + IotDeviceDO device = getDevice(reportReqDTO); + if (device == null) { + log.error("[reportDevicePropertyData][设备({}/{})不存在]", + reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); + return; + } + // 1.2 记录设备的最后时间 + updateDeviceLastTime(device, reportReqDTO); + + // 2. 发送设备消息 + IotDeviceMessage message = BeanUtils.toBean(reportReqDTO, IotDeviceMessage.class) + .setType(IotDeviceMessageTypeEnum.PROPERTY.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier()) + .setData(reportReqDTO.getProperties()); + sendDeviceMessage(message, device); + } + + @Override + public void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + log.info("[reportDeviceEventData][上报设备事件数据: {}]", reportReqDTO); + + // TODO 芋艿:待实现 + } + + private IotDeviceDO getDevice(IotDeviceUpstreamAbstractReqDTO reqDTO) { + return TenantUtils.executeIgnore(() -> // 需要忽略租户,因为请求时,未带租户编号 + deviceService.getDeviceByProductKeyAndDeviceName(reqDTO.getProductKey(), reqDTO.getDeviceName())); + } + + private void updateDeviceLastTime(IotDeviceDO deviceDO, IotDeviceUpstreamAbstractReqDTO reqDTO) { + // TODO 芋艿:插件状态 + // TODO 芋艿:操作时间 + } + + private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { + // 1. 完善消息 + message.setDeviceKey(device.getDeviceKey()); + if (StrUtil.isEmpty(message.getRequestId())) { + message.setRequestId(IdUtil.fastSimpleUUID()); + } + if (message.getReportTime() == null) { + message.setReportTime(LocalDateTime.now()); + } + + // 2. 发送消息 + try { + deviceProducer.sendDeviceMessage(message); + log.info("[sendDeviceMessage][message({}) 发送消息成功]", message); + } catch (Exception e) { + log.error("[sendDeviceMessage][message({}) 发送消息失败]", message, e); + } + } + +} 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 ad3ff94e2b..3fa0bcf0b1 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.IotDevicePropertyDataService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -35,7 +35,7 @@ public class IotProductServiceImpl implements IotProductService { @Resource @Lazy // 延迟加载,解决循环依赖 - private IotDevicePropertyDataService devicePropertyDataService; + private IotDevicePropertyService devicePropertyDataService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java deleted file mode 100644 index 7f52411d89..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; - -/** - * 物模型消息 Service - */ -public interface IotThingModelMessageService { - - /** - * 保存物模型消息 - * - * @param device 设备 - * @param thingModelMessage 物模型消息 - */ - void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage); - - - -} \ 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/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java deleted file mode 100644 index c35bb3a28b..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ /dev/null @@ -1,277 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; -import cn.iocoder.yudao.module.iot.enums.IotConstants; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; - -/** - * 物模型消息 Service 实现类 - */ -@Slf4j -@Service -public class IotThingModelMessageServiceImpl implements IotThingModelMessageService { - - private static final String TAG_NOTE = "TAG"; - private static final String NOTE = "note"; - private static final String TIME = "time"; - private static final String DEVICE_KEY = "device_key"; - private static final String DEVICE_NAME = "device_name"; - private static final String PRODUCT_KEY = "product_key"; - private static final String DEVICE_TYPE = "device_type"; - - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") - private String url; - - @Resource - private IotThingModelService iotThingModelService; - @Resource - private IotDeviceService iotDeviceService; - @Resource - private IotProductService productService; - - @Resource - private TdEngineDDLMapper tdEngineDDLMapper; - @Resource - private TdEngineDMLMapper tdEngineDMLMapper; - - @Resource - private IotDevicePropertyDataMapper iotDevicePropertyDataMapper; - - @Resource - private DeviceDataRedisDAO deviceDataRedisDAO; - - // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感 - @Override - @TenantIgnore - public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { - // 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态 - if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { - // 1.1 创建设备表 -// createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); - iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() - .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); - } - - // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 - Map params = thingModelMessage.dataToMap(); - List thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); - if (thingModelList.isEmpty()) { - return; - } - - // 3. 过滤并收集有效的属性字段,缓存设备属性 - List schemaFieldValues = filterAndCollectValidFields(params, thingModelList, device, thingModelMessage.getTime()); - if (schemaFieldValues.size() == 0) { // 没有字段,无需保存 - return; - } - - // 4. 构建并保存设备属性数据 -// tdEngineDMLMapper.insertData(TdTableDO.builder() -// .dataBaseName(getDatabaseName()) -// .tableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())) -// .columns(schemaFieldValues) -// .build()); - // TODO:复用了旧逻辑,先过渡一下 - iotDevicePropertyDataMapper.insertDevicePropertyData(TdTableDO.builder() - .productKey(device.getProductKey()) - .deviceKey(device.getDeviceKey()) - .columns(schemaFieldValues) - .build()); - } - - private List getValidThingModelList(String productKey) { - return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), - thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); - } - -// @Override -// @TenantIgnore -// public void createSuperTable(Long productId) { -// // 1. 查询产品 -// IotProductDO product = productService.getProduct(productId); -// // 2. 创建日志超级表 -// tdThingModelMessageMapper.createSuperTable(product.getProductKey()); -// -// // 2. 获取超级表的名称和数据库名称 -// // TODO @alwayssuper:最好 databaseName、superTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法 -//// String databaseName = IotTdDatabaseUtils.getDatabaseName(url); -//// String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey()); -//// -//// // 解析物模型,获取字段列表 -//// List schemaFields = List.of( -//// TdFieldDO.builder().fieldName("time").dataType("TIMESTAMP").build(), -//// TdFieldDO.builder().fieldName("id").dataType("NCHAR").dataLength(64).build(), -//// TdFieldDO.builder().fieldName("sys").dataType("NCHAR").dataLength(2048).build(), -//// TdFieldDO.builder().fieldName("method").dataType("NCHAR").dataLength(256).build(), -//// TdFieldDO.builder().fieldName("params").dataType("NCHAR").dataLength(2048).build() -//// ); -//// // 设置超级表的标签 -//// List tagsFields = List.of( -//// TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build() -//// ); -//// // 3. 创建超级表 -//// tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); -// } - - private List getValidFunctionList(String productKey) { - return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), - thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); - } - - private List filterAndCollectValidFields(Map params, List thingModelList, IotDeviceDO device, Long time) { - // 1. 获取属性标识符集合 - Set propertyIdentifiers = convertSet(thingModelList, IotThingModelDO::getIdentifier); - - // 2. 构建属性标识符和属性的映射 - Map thingModelMap = convertMap(thingModelList, IotThingModelDO::getIdentifier); - - // 3. 过滤并收集有效的属性字段 - List schemaFieldValues = new ArrayList<>(); - //TODO:新版本是使用ts字段 -// schemaFieldValues.add(new TdFieldDO(TIME, time)); - params.forEach((key, val) -> { - if (propertyIdentifiers.contains(key)) { - schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val)); - // 缓存设备属性 - // TODO @haohao:这个缓存的写入,可以使用的时候 cache 么?被动读 - setDeviceDataCache(device, thingModelMap.get(key), val, time); - } - }); - return schemaFieldValues; - } - - /** - * 缓存设备属性 - * - * @param device 设备信息 - * @param iotThingModelDO 物模型属性 - * @param val 属性值 - * @param time 时间 - */ - private void setDeviceDataCache(IotDeviceDO device, IotThingModelDO iotThingModelDO, Object val, Long time) { - IotDeviceDataDO deviceData = IotDeviceDataDO.builder() - .productKey(device.getProductKey()) - .deviceName(device.getDeviceName()) - .identifier(iotThingModelDO.getIdentifier()) - .value(val != null ? val.toString() : null) - .updateTime(DateUtil.toLocalDateTime(new Date(time))) - .deviceId(device.getId()) - .thingModelId(iotThingModelDO.getId()) - .name(iotThingModelDO.getName()) - .dataType(iotThingModelDO.getProperty().getDataType()) - .build(); - deviceDataRedisDAO.set(deviceData); - } - - /** - * 创建设备数据表 - * - * @param deviceType 设备类型 - * @param productKey 产品 Key - * @param deviceName 设备名称 - * @param deviceKey 设备 Key - */ - private void createDeviceTable(Integer deviceType, String productKey, String deviceName, String deviceKey) { - // 1. 获取超级表名和数据库名 - String superTableName = getProductPropertySTableName(deviceType, productKey); - String dataBaseName = getDatabaseName(); - - // 2. 获取超级表的结构信息 - List> maps = tdEngineDDLMapper.describeSuperTable(new TdTableDO(dataBaseName, superTableName)); - List tagsFieldValues = new ArrayList<>(); - if (maps != null) { - // 2.1 过滤出 TAG 类型的字段 - List> taggedNotesList = CollectionUtils.filterList(maps, map -> TAG_NOTE.equals(map.get(NOTE))); - - // 2.2 解析字段信息 - tagsFieldValues = FieldParser.parse(taggedNotesList.stream() - .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) - .collect(Collectors.toList())); - - // 2.3 设置 TAG 字段的值 - for (TdFieldDO tagsFieldValue : tagsFieldValues) { - switch (tagsFieldValue.getFieldName()) { - case PRODUCT_KEY -> tagsFieldValue.setFieldValue(productKey); - case DEVICE_KEY -> tagsFieldValue.setFieldValue(deviceKey); - case DEVICE_NAME -> tagsFieldValue.setFieldValue(deviceName); - case DEVICE_TYPE -> tagsFieldValue.setFieldValue(deviceType); - } - } - } - - // 3. 创建设备数据表 - String tableName = getDeviceTableName(productKey, deviceName); - tdEngineDDLMapper.createTable(TdTableDO.builder().build() - .setDataBaseName(dataBaseName) - .setSuperTableName(superTableName) - .setTableName(tableName) - .setTags(tagsFieldValues)); - } - - - - /** - * 获取数据库名称 - * - * @return 数据库名称 - */ - private String getDatabaseName() { - return StrUtil.subAfter(url, "/", true); - } - - /** - * 获取产品属性表名 - * - * @param deviceType 设备类型 - * @param productKey 产品 Key - * @return 产品属性表名 - */ - private static String getProductPropertySTableName(Integer deviceType, String productKey) { - // TODO @haohao:枚举下,会好点哈。 - return switch (deviceType) { - case 1 -> String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); - case 2 -> String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); - default -> String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); - }; - } - - /** - * 获取设备表名 - * - * @param productKey 产品 Key - * @param deviceName 设备名称 - * @return 设备表名 - */ - private static String getDeviceTableName(String productKey, String deviceName) { - return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml index 039180857b..bbc1fb7185 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml @@ -9,8 +9,9 @@ ts TIMESTAMP, id NCHAR(50), product_key NCHAR(50), + device_name NCHAR(50), type NCHAR(50), - sub_type NCHAR(50), + identifier NCHAR(255), content NCHAR(1024), report_time TIMESTAMP ) TAGS ( @@ -18,18 +19,23 @@ ) + + - INSERT INTO device_log_${log.deviceKey} (ts, id, product_key, type, subType, content, report_time) + INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, report_time) USING device_log - TAGS ('${log.deviceKey}') + TAGS ('${deviceKey}') VALUES ( NOW, - #{log.id}, - #{log.productKey}, - #{log.type}, - #{log.subType}, - #{log.content}, - #{log.reportTime} + #{id}, + #{productKey}, + #{deviceName}, + #{type}, + #{identifier}, + #{content}, + #{reportTime} ) @@ -51,6 +57,7 @@ LIMIT #{reqVO.pageSize} OFFSET #{reqVO.pageNo} + - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java index 9fdb29ea85..a10e9ec3d5 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java @@ -1,99 +1,62 @@ package cn.iocoder.yudao.module.iot.plugin.common.api; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -/** - * 用于通过 {@link RestTemplate} 向远程 IoT 服务发送设备数据相关的请求, - * 包括设备状态更新、事件数据上报、属性数据上报等操作。 - */ +// TODO @haohao:类注释,写一下,比较好 +// TODO @haohao:类名要改下 @Slf4j -@RequiredArgsConstructor -public class DeviceDataApiClient implements DeviceDataApi { +public class DeviceDataApiClient implements IotDeviceUpstreamApi { + + public static final String URL_PREFIX = "/rpc-api/iot/device/upstream"; - /** - * 用于发送 HTTP 请求的工具 - */ private final RestTemplate restTemplate; - - /** - * 远程 IoT 服务的基础 URL - * 例如:http://127.0.0.1:8080 - */ private final String deviceDataUrl; + // 可以通过构造器把 RestTemplate 和 baseUrl 注入进来 + // TODO @haohao:可以用 lombok 简化 + public DeviceDataApiClient(RestTemplate restTemplate, String deviceDataUrl) { + this.restTemplate = restTemplate; + this.deviceDataUrl = deviceDataUrl; + } + // TODO @haohao:返回结果,不用 CommonResult 哈。 @Override public CommonResult updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { - String url = deviceDataUrl + "/rpc-api/iot/device-data/update-status"; + String url = deviceDataUrl + URL_PREFIX + "/update-status"; return doPost(url, updateReqDTO, "updateDeviceStatus"); } @Override public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { - String url = deviceDataUrl + "/rpc-api/iot/device-data/report-event"; + String url = deviceDataUrl + URL_PREFIX + "/report-event"; return doPost(url, reportReqDTO, "reportDeviceEventData"); } @Override public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { - String url = deviceDataUrl + "/rpc-api/iot/device-data/report-property"; + String url = deviceDataUrl + URL_PREFIX + "/report-property"; return doPost(url, reportReqDTO, "reportDevicePropertyData"); } - + // TODO @haohao:未来可能有 get 类型哈 /** - * 发送 GET 请求 - * - * @param 请求体类型 - * @param url 请求 URL - * @param requestBody 请求体 - * @param actionName 操作名称 - * @return 响应结果 - */ - private CommonResult doGet(String url, T requestBody, String actionName) { - log.info("[{}] Sending request to URL: {}", actionName, url); - try { - CommonResult response = restTemplate.getForObject(url, CommonResult.class); - if (response != null && response.isSuccess()) { - return success(true); - } else { - log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response); - return CommonResult.error(500, "Request failed"); - } - } catch (Exception e) { - log.error("[{}] Error sending request to URL: {}", actionName, url, e); - return CommonResult.error(400, "Request error: " + e.getMessage()); - } - } - - /** - * 发送 POST 请求 - * - * @param 请求体类型 - * @param url 请求 URL - * @param requestBody 请求体 - * @param actionName 操作名称 - * @return 响应结果 + * 将与远程服务交互的通用逻辑抽取成一个私有方法 */ private CommonResult doPost(String url, T requestBody, String actionName) { log.info("[{}] Sending request to URL: {}", actionName, url); try { - CommonResult response = restTemplate.postForObject(url, requestBody, CommonResult.class); - if (response != null && response.isSuccess()) { - return success(true); - } else { - log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response); - return CommonResult.error(500, "Request failed"); - } + // 这里指定返回类型为 CommonResult,根据后台服务返回的实际结构做调整 + restTemplate.postForObject(url, requestBody, CommonResult.class); + // TODO @haohao:check 结果,是否成功 + return success(true); } catch (Exception e) { log.error("[{}] Error sending request to URL: {}", actionName, url, e); return CommonResult.error(400, "Request error: " + e.getMessage()); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java index 2c1554474e..ef613d5be6 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin.common.config; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -43,7 +43,7 @@ public class YudaoDeviceDataApiAutoConfiguration { * @return DeviceDataApi 实例 */ @Bean - public DeviceDataApi deviceDataApi(RestTemplate restTemplate) { + public IotDeviceUpstreamApi deviceDataApi(RestTemplate restTemplate) { return new DeviceDataApiClient(restTemplate, deviceDataUrl); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java index 27b90426b2..b5fed5518b 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin; import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import lombok.extern.slf4j.Slf4j; import org.pf4j.Plugin; import org.pf4j.PluginWrapper; @@ -27,7 +27,7 @@ public class EmqxPlugin extends Plugin { executorService = Executors.newSingleThreadExecutor(); } - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); + IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class); if (deviceDataApi == null) { log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); return; diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java index f9a589a246..2c263673ae 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; import cn.hutool.core.lang.Assert; import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import lombok.extern.slf4j.Slf4j; import org.pf4j.PluginWrapper; import org.pf4j.spring.SpringPlugin; @@ -64,7 +64,7 @@ public class HttpVertxPlugin extends SpringPlugin { protected void prepareRefresh() { // 在刷新容器前注册主程序中的 Bean ConfigurableListableBeanFactory beanFactory = this.getBeanFactory(); - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); + IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class); beanFactory.registerSingleton("deviceDataApi", deviceDataApi); super.prepareRefresh(); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java index e61a4cf8ff..af54d1f532 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; import io.vertx.core.Vertx; import io.vertx.ext.web.Router; @@ -61,7 +61,7 @@ public class HttpVertxPluginConfiguration { * @return HttpVertxHandler 实例 */ @Bean - public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) { + public HttpVertxHandler httpVertxHandler(IotDeviceUpstreamApi deviceDataApi) { return new HttpVertxHandler(deviceDataApi); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java index df55c68fd6..ec4431d2d1 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java @@ -2,19 +2,21 @@ package cn.iocoder.yudao.module.iot.plugin.http.service; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import io.vertx.core.Handler; import io.vertx.ext.web.RequestBody; import io.vertx.ext.web.RoutingContext; import lombok.extern.slf4j.Slf4j; +import java.util.Map; + @Slf4j public class HttpVertxHandler implements Handler { - private final DeviceDataApi deviceDataApi; + private final IotDeviceUpstreamApi deviceDataApi; - public HttpVertxHandler(DeviceDataApi deviceDataApi) { + public HttpVertxHandler(IotDeviceUpstreamApi deviceDataApi) { this.deviceDataApi = deviceDataApi; } @@ -23,6 +25,7 @@ public class HttpVertxHandler implements Handler { String productKey = ctx.pathParam("productKey"); String deviceName = ctx.pathParam("deviceName"); + // TODO @haohao:requestBody.asJsonObject() 貌似天然就是 json 对象哈? RequestBody requestBody = ctx.body(); JSONObject jsonData; try { @@ -43,7 +46,7 @@ public class HttpVertxHandler implements Handler { IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder() .productKey(productKey) .deviceName(deviceName) - .params(jsonData) + .properties((Map) requestBody.asJsonObject().getMap().get("properties")) .build(); deviceDataApi.reportDevicePropertyData(reportReqDTO);