From 800a85f7bc6a327109a54d62993b7e0be97eb2eb Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 9 Jun 2025 23:28:24 +0800 Subject: [PATCH] =?UTF-8?q?reactor=EF=BC=9A=E3=80=90IoT=20=E7=89=A9?= =?UTF-8?q?=E8=81=94=E7=BD=91=E3=80=91=E6=B8=85=E7=90=86=20components=20?= =?UTF-8?q?=E5=92=8C=20protocol=EF=BC=8C=E5=9F=BA=E6=9C=AC=E5=B7=B2?= =?UTF-8?q?=E7=BB=8F=E8=9E=8D=E5=90=88=E5=88=B0=20gateway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/pom.xml | 2 - .../downstream/IotDeviceConfigSetReqDTO.java | 22 -- .../IotDeviceDownstreamAbstractReqDTO.java | 30 -- .../downstream/IotDeviceOtaUpgradeReqDTO.java | 66 ---- .../IotDevicePropertyGetReqDTO.java | 24 -- .../IotDevicePropertySetReqDTO.java | 22 -- .../IotDeviceServiceInvokeReqDTO.java | 26 -- .../upstream/IotDeviceOtaProgressReqDTO.java | 35 -- .../upstream/IotDeviceOtaPullReqDTO.java | 21 -- .../upstream/IotDeviceOtaReportReqDTO.java | 21 -- .../yudao/module/iot/enums/ApiConstants.java | 16 - .../module/iot/enums/DictTypeConstants.java | 3 - .../enums/plugin/IotPluginDeployTypeEnum.java | 37 -- .../iot/enums/plugin/IotPluginStatusEnum.java | 37 -- .../iot/enums/plugin/IotPluginTypeEnum.java | 37 -- .../product/IotProductScriptLanguageEnum.java | 47 --- .../product/IotProductScriptStatusEnum.java | 54 --- .../product/IotProductScriptTypeEnum.java | 50 --- .../IotDeviceDownstreamServiceImpl.java | 2 +- .../gateway/protocol/mqtt/package-info.java | 1 - .../yudao-module-iot-net-components/pom.xml | 25 -- .../pom.xml | 65 ---- .../core/message/IotMqttMessage.java | 153 -------- .../core/pojo/IotStandardResponse.java | 93 ----- .../pom.xml | 44 --- .../IotNetComponentEmqxAutoConfiguration.java | 83 ----- .../config/IotNetComponentEmqxProperties.java | 82 ----- .../IotDeviceDownstreamHandlerImpl.java | 121 ------ .../upstream/IotDeviceUpstreamServer.java | 261 ------------- .../router/IotDeviceAuthVertxHandler.java | 64 ---- .../router/IotDeviceMqttMessageHandler.java | 287 --------------- .../router/IotDeviceWebhookVertxHandler.java | 152 -------- ...ot.autoconfigure.AutoConfiguration.imports | 1 - .../src/main/resources/application.yml | 18 - .../pom.xml | 37 -- .../IotNetComponentServerConfiguration.java | 52 --- .../IotNetComponentServerProperties.java | 34 -- .../yudao-module-iot-protocol/README.md | 254 ------------- .../yudao-module-iot-protocol/pom.xml | 71 ---- .../config/IotProtocolAutoConfiguration.java | 74 ---- .../protocol/constants/IotHttpConstants.java | 166 --------- .../protocol/constants/IotLogConstants.java | 91 ----- .../protocol/constants/IotTopicConstants.java | 157 -------- .../convert/IotProtocolConverter.java | 48 --- .../impl/DefaultIotProtocolConverter.java | 132 ------- .../enums/IotMessageDirectionEnum.java | 49 --- .../protocol/enums/IotMessageTypeEnum.java | 140 ------- .../protocol/enums/IotProtocolTypeEnum.java | 79 ---- .../protocol/message/IotMessageParser.java | 36 -- .../iot/protocol/message/IotMqttMessage.java | 154 -------- .../protocol/message/IotStandardResponse.java | 95 ----- .../message/impl/IotHttpMessageParser.java | 348 ------------------ .../message/impl/IotMqttMessageParser.java | 87 ----- .../iot/protocol/util/IotHttpTopicUtils.java | 279 -------------- .../iot/protocol/util/IotTopicParser.java | 237 ------------ .../iot/protocol/util/IotTopicUtils.java | 184 --------- ...ot.autoconfigure.AutoConfiguration.imports | 1 - .../IotProtocolAutoConfigurationTest.java | 71 ---- .../example/AliyunHttpProtocolExample.java | 166 --------- .../impl/IotHttpMessageParserTest.java | 259 ------------- .../impl/IotMqttMessageParserTest.java | 190 ---------- .../protocol/util/IotHttpTopicUtilsTest.java | 186 ---------- .../iot/protocol/util/IotTopicUtilsTest.java | 81 ---- 63 files changed, 1 insertion(+), 5759 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceConfigSetReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceOtaUpgradeReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaProgressReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaPullReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaReportReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptLanguageEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptStatusEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptTypeEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/package-info.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/message/IotMqttMessage.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/pojo/IotStandardResponse.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxAutoConfiguration.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxProperties.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/downstream/IotDeviceDownstreamHandlerImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/IotDeviceUpstreamServer.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceAuthVertxHandler.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceMqttMessageHandler.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceWebhookVertxHandler.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/application.yml delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerConfiguration.java delete mode 100644 yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerProperties.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/README.md delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfiguration.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotHttpConstants.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotLogConstants.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotTopicConstants.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/IotProtocolConverter.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/impl/DefaultIotProtocolConverter.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageDirectionEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageTypeEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotProtocolTypeEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMessageParser.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMqttMessage.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotStandardResponse.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParser.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParser.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtils.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicParser.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtils.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfigurationTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/example/AliyunHttpProtocolExample.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParserTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParserTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtilsTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtilsTest.java diff --git a/yudao-module-iot/pom.xml b/yudao-module-iot/pom.xml index ebc0c5e368..074c42e17a 100644 --- a/yudao-module-iot/pom.xml +++ b/yudao-module-iot/pom.xml @@ -9,8 +9,6 @@ yudao-module-iot-api yudao-module-iot-biz - yudao-module-iot-net-components - yudao-module-iot-protocol yudao-module-iot-core yudao-module-iot-gateway diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceConfigSetReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceConfigSetReqDTO.java deleted file mode 100644 index 9624b671ee..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceConfigSetReqDTO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; - -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.util.Map; - -/** - * IoT 设备【配置】设置 Request DTO - * - * @author 芋道源码 - */ -@Data -public class IotDeviceConfigSetReqDTO extends IotDeviceDownstreamAbstractReqDTO { - - /** - * 配置 - */ - @NotNull(message = "配置不能为空") - private Map config; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java deleted file mode 100644 index e78bea6fba..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; - -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -/** - * IoT 设备下行的抽象 Request DTO - * - * @author 芋道源码 - */ -@Data -public abstract class IotDeviceDownstreamAbstractReqDTO { - - /** - * 请求编号 - */ - private String requestId; - - /** - * 产品标识 - */ - @NotEmpty(message = "产品标识不能为空") - private String productKey; - /** - * 设备名称 - */ - @NotEmpty(message = "设备名称不能为空") - private String deviceName; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceOtaUpgradeReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceOtaUpgradeReqDTO.java deleted file mode 100644 index 8eccec42ec..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceOtaUpgradeReqDTO.java +++ /dev/null @@ -1,66 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; - -import cn.hutool.core.map.MapUtil; -import lombok.Data; - -import java.util.Map; - -/** - * IoT 设备【OTA】升级下发 Request DTO(更新固件消息) - * - * @author 芋道源码 - */ -@Data -public class IotDeviceOtaUpgradeReqDTO extends IotDeviceDownstreamAbstractReqDTO { - - /** - * 固件编号 - */ - private Long firmwareId; - /** - * 固件版本 - */ - private String version; - - /** - * 签名方式 - * - * 例如说:MD5、SHA256 - */ - private String signMethod; - /** - * 固件文件签名 - */ - private String fileSign; - /** - * 固件文件大小 - */ - private Long fileSize; - /** - * 固件文件 URL - */ - private String fileUrl; - - /** - * 自定义信息,建议使用 JSON 格式 - */ - private String information; - - public static IotDeviceOtaUpgradeReqDTO build(Map map) { - return new IotDeviceOtaUpgradeReqDTO() - .setFirmwareId(MapUtil.getLong(map, "firmwareId")).setVersion((String) map.get("version")) - .setSignMethod((String) map.get("signMethod")).setFileSign((String) map.get("fileSign")) - .setFileSize(MapUtil.getLong(map, "fileSize")).setFileUrl((String) map.get("fileUrl")) - .setInformation((String) map.get("information")); - } - - public static Map build(IotDeviceOtaUpgradeReqDTO dto) { - return MapUtil.builder() - .put("firmwareId", dto.getFirmwareId()).put("version", dto.getVersion()) - .put("signMethod", dto.getSignMethod()).put("fileSign", dto.getFileSign()) - .put("fileSize", dto.getFileSize()).put("fileUrl", dto.getFileUrl()) - .put("information", dto.getInformation()) - .build(); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java deleted file mode 100644 index d9ae963214..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; - -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.List; - -// TODO @芋艿:从 server => plugin => device 是否有必要?从阿里云 iot 来看,没有这个功能?! -// TODO @芋艿:是不是改成 read 更好?在看看阿里云的 topic 设计 -/** - * IoT 设备【属性】获取 Request DTO - * - * @author 芋道源码 - */ -@Data -public class IotDevicePropertyGetReqDTO extends IotDeviceDownstreamAbstractReqDTO { - - /** - * 属性标识数组 - */ - @NotEmpty(message = "属性标识数组不能为空") - private List identifiers; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java deleted file mode 100644 index 170fe80f69..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; - -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.Map; - -/** - * IoT 设备【属性】设置 Request DTO - * - * @author 芋道源码 - */ -@Data -public class IotDevicePropertySetReqDTO extends IotDeviceDownstreamAbstractReqDTO { - - /** - * 属性参数 - */ - @NotEmpty(message = "属性参数不能为空") - private Map properties; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java deleted file mode 100644 index 0a2b3f0bf8..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; - -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.Map; - -/** - * IoT 设备【服务】调用 Request DTO - * - * @author 芋道源码 - */ -@Data -public class IotDeviceServiceInvokeReqDTO extends IotDeviceDownstreamAbstractReqDTO { - - /** - * 服务标识 - */ - @NotEmpty(message = "服务标识不能为空") - private String identifier; - /** - * 调用参数 - */ - private Map params; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaProgressReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaProgressReqDTO.java deleted file mode 100644 index a88a72e919..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaProgressReqDTO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; - -import lombok.Data; - -// TODO @芋艿:待实现:/ota/${productKey}/${deviceName}/progress -/** - * IoT 设备【OTA】升级进度 Request DTO(上报更新固件进度) - * - * @author 芋道源码 - */ -@Data -public class IotDeviceOtaProgressReqDTO extends IotDeviceUpstreamAbstractReqDTO { - - /** - * 固件编号 - */ - private Long firmwareId; - - /** - * 升级状态 - * - * 枚举 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum} - */ - private Integer status; - /** - * 升级进度,百分比 - */ - private Integer progress; - - /** - * 升级进度描述 - */ - private String description; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaPullReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaPullReqDTO.java deleted file mode 100644 index 6328704e58..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaPullReqDTO.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; - -// TODO @芋艿:待实现:/ota/${productKey}/${deviceName}/pull -/** - * IoT 设备【OTA】升级下拉 Request DTO(拉取固件更新) - * - * @author 芋道源码 - */ -public class IotDeviceOtaPullReqDTO { - - /** - * 固件编号 - */ - private Long firmwareId; - - /** - * 固件版本 - */ - private String version; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaReportReqDTO.java deleted file mode 100644 index 2b3b91c985..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaReportReqDTO.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; - -// TODO @芋艿:待实现:/ota/${productKey}/${deviceName}/report -/** - * IoT 设备【OTA】上报 Request DTO(上报固件版本) - * - * @author 芋道源码 - */ -public class IotDeviceOtaReportReqDTO { - - /** - * 固件编号 - */ - private Long firmwareId; - - /** - * 固件版本 - */ - private String version; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java deleted file mode 100644 index 2c4147be1f..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums; - -import cn.iocoder.yudao.framework.common.enums.RpcConstants; - -/** - * API 相关的枚举 - * - * @author 芋道源码 - */ -public class ApiConstants { - - public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/iot"; - - public static final String VERSION = "1.0.0"; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java index d8f0cc60d2..04df143bed 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java @@ -15,8 +15,5 @@ public class DictTypeConstants { public static final String VALIDATE_TYPE = "iot_validate_type"; public static final String DEVICE_STATE = "iot_device_state"; - - public static final String IOT_DATA_BRIDGE_DIRECTION_ENUM = "iot_data_bridge_direction_enum"; - public static final String IOT_DATA_BRIDGE_TYPE_ENUM = "iot_data_bridge_type_enum"; } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java deleted file mode 100644 index b6ef4f0cc3..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums.plugin; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * IoT 部署方式枚举 - * - * @author haohao - */ -@RequiredArgsConstructor -@Getter -public enum IotPluginDeployTypeEnum implements ArrayValuable { - - JAR(0, "JAR 部署"), - STANDALONE(1, "独立部署"); - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotPluginDeployTypeEnum::getDeployType).toArray(Integer[]::new); - - /** - * 部署方式 - */ - private final Integer deployType; - /** - * 部署方式名 - */ - private final String name; - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java deleted file mode 100644 index 7e3fa657e2..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums.plugin; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * IoT 插件状态枚举 - * - * @author haohao - */ -@RequiredArgsConstructor -@Getter -public enum IotPluginStatusEnum implements ArrayValuable { - - STOPPED(0, "停止"), - RUNNING(1, "运行"); - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotPluginStatusEnum::getStatus).toArray(Integer[]::new); - - /** - * 状态 - */ - private final Integer status; - /** - * 状态名 - */ - private final String name; - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java deleted file mode 100644 index ec0b72f9fd..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums.plugin; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * IoT 插件类型枚举 - * - * @author haohao - */ -@AllArgsConstructor -@Getter -public enum IotPluginTypeEnum implements ArrayValuable { - - NORMAL(0, "普通插件"), - DEVICE(1, "设备插件"); - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotPluginTypeEnum::getType).toArray(Integer[]::new); - - /** - * 类型 - */ - private final Integer type; - /** - * 类型名 - */ - private final String name; - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptLanguageEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptLanguageEnum.java deleted file mode 100644 index 92e5d2cfb8..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptLanguageEnum.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums.product; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * IoT 产品脚本语言枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum IotProductScriptLanguageEnum implements ArrayValuable { - - JAVASCRIPT("javascript", "JavaScript"), - JAVA("java", "Java"), - PYTHON("python", "Python"), - ; - - public static final String[] ARRAYS = Arrays.stream(values()).map(IotProductScriptLanguageEnum::getCode) - .toArray(String[]::new); - - /** - * 编码 - */ - private final String code; - /** - * 名称 - */ - private final String name; - - @Override - public String[] array() { - return ARRAYS; - } - - public static IotProductScriptLanguageEnum getByCode(String code) { - return Arrays.stream(values()) - .filter(type -> type.getCode().equals(code)) - .findFirst() - .orElse(null); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptStatusEnum.java deleted file mode 100644 index f9036bedf0..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptStatusEnum.java +++ /dev/null @@ -1,54 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums.product; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -// TODO @haohao:要不复用 commonstatus? -/** - * IoT 产品脚本状态枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum IotProductScriptStatusEnum implements ArrayValuable { - - ENABLE(0, "启用"), - DISABLE(1, "禁用"), - ; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotProductScriptStatusEnum::getStatus) - .toArray(Integer[]::new); - - /** - * 状态值 - */ - private final Integer status; - /** - * 状态名 - */ - private final String name; - - @Override - public Integer[] array() { - return ARRAYS; - } - - public static IotProductScriptStatusEnum getByStatus(Integer status) { - return Arrays.stream(values()) - .filter(type -> type.getStatus().equals(status)) - .findFirst() - .orElse(null); - } - - public static boolean isEnable(Integer status) { - return ENABLE.getStatus().equals(status); - } - - public static boolean isDisable(Integer status) { - return DISABLE.getStatus().equals(status); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptTypeEnum.java deleted file mode 100644 index d1b2ee8fa8..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptTypeEnum.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums.product; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * IoT 产品脚本类型枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum IotProductScriptTypeEnum implements ArrayValuable { - - PROPERTY_PARSER(1, "property_parser", "属性解析"), - EVENT_PARSER(2, "event_parser", "事件解析"), - COMMAND_ENCODER(3, "command_encoder", "命令编码"), - ; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotProductScriptTypeEnum::getCode) - .toArray(Integer[]::new); - - /** - * 编码 - */ - private final Integer code; - /** - * 类型 - */ - private final String type; - /** - * 名称 - */ - private final String name; - - @Override - public Integer[] array() { - return ARRAYS; - } - - public static IotProductScriptTypeEnum getByCode(Integer code) { - return Arrays.stream(values()) - .filter(type -> type.getCode().equals(code)) - .findFirst() - .orElse(null); - } -} \ 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/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index fd59f4b5c3..9634a40eae 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -55,7 +55,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic if (StrUtil.isEmpty(serverId)) { throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL); } - deviceMessageProducer.sendGatewayDeviceMessage(serverId, message); + deviceMessageProducer.sendDeviceMessageToGateway(serverId, message); // 3.2 发送给服务器(用于设备日志等的记录) deviceMessageProducer.sendDeviceMessage(message); diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/package-info.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/package-info.java deleted file mode 100644 index 94fbf0910d..0000000000 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/pom.xml b/yudao-module-iot/yudao-module-iot-net-components/pom.xml deleted file mode 100644 index d90f4a55e4..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/pom.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - yudao-module-iot - cn.iocoder.boot - ${revision} - - 4.0.0 - - yudao-module-iot-net-components - pom - - ${project.artifactId} - - 物联网网络组件模块,提供与物联网设备通讯、管理的网络组件实现 - - - - yudao-module-iot-net-component-core - yudao-module-iot-net-component-emqx - yudao-module-iot-net-component-server - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/pom.xml b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/pom.xml deleted file mode 100644 index 6bbf140fd9..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/pom.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - yudao-module-iot-net-components - cn.iocoder.boot - ${revision} - - 4.0.0 - - yudao-module-iot-net-component-core - jar - - ${project.artifactId} - - 物联网网络组件核心模块 - - - - - cn.iocoder.boot - yudao-module-iot-api - ${revision} - - - - cn.iocoder.boot - yudao-module-iot-core - ${revision} - - - - - cn.iocoder.boot - yudao-module-iot-protocol - ${revision} - - - - org.springframework.boot - spring-boot-starter - - - - - org.springframework - spring-web - - - - - io.vertx - vertx-web - true - - - - - org.springframework.boot - spring-boot-starter-validation - true - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/message/IotMqttMessage.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/message/IotMqttMessage.java deleted file mode 100644 index af9933cfa8..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/message/IotMqttMessage.java +++ /dev/null @@ -1,153 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.core.message; - -import cn.hutool.core.util.IdUtil; -import cn.hutool.json.JSONObject; -import lombok.Builder; -import lombok.Data; - -import java.util.Map; - -/** - * IoT MQTT 消息模型 - *

- * 基于 MQTT 协议规范实现的标准消息格式,兼容 Alink 协议 - * - * @author haohao - */ -@Data -@Builder -public class IotMqttMessage { - - /** - * 消息 ID - */ - private String id; - - /** - * 协议版本 - */ - @Builder.Default - private String version = "1.0"; - - /** - * 消息方法 - */ - private String method; - - /** - * 消息参数 - */ - private Map params; - - /** - * 转换为 JSONObject - * - * @return JSONObject 对象 - */ - public JSONObject toJsonObject() { - JSONObject json = new JSONObject(); - json.set("id", id); - json.set("version", version); - json.set("method", method); - json.set("params", params != null ? params : new JSONObject()); - return json; - } - - /** - * 转换为 JSON 字符串 - * - * @return JSON 字符串 - */ - public String toJsonString() { - return toJsonObject().toString(); - } - - /** - * 创建设备服务调用消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param serviceIdentifier 服务标识符 - * @param params 服务参数 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createServiceInvokeMessage(String requestId, String serviceIdentifier, - Map params) { - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service." + serviceIdentifier) - .params(params) - .build(); - } - - /** - * 创建设备属性设置消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param properties 设备属性 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createPropertySetMessage(String requestId, Map properties) { - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service.property.set") - .params(properties) - .build(); - } - - /** - * 创建设备属性获取消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param identifiers 要获取的属性标识符列表 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createPropertyGetMessage(String requestId, String[] identifiers) { - JSONObject params = new JSONObject(); - params.set("identifiers", identifiers); - - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service.property.get") - .params(params) - .build(); - } - - /** - * 创建设备配置设置消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param configs 设备配置 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createConfigSetMessage(String requestId, Map configs) { - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service.config.set") - .params(configs) - .build(); - } - - /** - * 创建设备 OTA 升级消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param otaInfo OTA 升级信息 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createOtaUpgradeMessage(String requestId, Map otaInfo) { - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service.ota.upgrade") - .params(otaInfo) - .build(); - } - - /** - * 生成请求 ID - * - * @return 请求 ID - */ - public static String generateRequestId() { - return IdUtil.fastSimpleUUID(); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/pojo/IotStandardResponse.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/pojo/IotStandardResponse.java deleted file mode 100644 index ce5adc36af..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/pojo/IotStandardResponse.java +++ /dev/null @@ -1,93 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.core.pojo; - -import cn.hutool.core.util.StrUtil; -import lombok.Data; - -/** - * IoT 标准协议响应实体类 - *

- * 用于统一 MQTT 和 HTTP 的响应格式 - * - * @author haohao - */ -@Data -public class IotStandardResponse { - - /** - * 消息 ID - */ - private String id; - - /** - * 状态码 - */ - private Integer code; - - /** - * 响应数据 - */ - private Object data; - - /** - * 响应消息 - */ - private String message; - - /** - * 方法名 - */ - private String method; - - /** - * 协议版本 - */ - private String version; - - /** - * 创建成功响应 - * - * @param id 消息 ID - * @param method 方法名 - * @return 成功响应 - */ - public static IotStandardResponse success(String id, String method) { - return success(id, method, null); - } - - /** - * 创建成功响应 - * - * @param id 消息 ID - * @param method 方法名 - * @param data 响应数据 - * @return 成功响应 - */ - public static IotStandardResponse success(String id, String method, Object data) { - return new IotStandardResponse() - .setId(id) - .setCode(200) - .setData(data) - .setMessage("success") - .setMethod(method) - .setVersion("1.0"); - } - - /** - * 创建错误响应 - * - * @param id 消息 ID - * @param method 方法名 - * @param code 错误码 - * @param message 错误消息 - * @return 错误响应 - */ - public static IotStandardResponse error(String id, String method, Integer code, String message) { - return new IotStandardResponse() - .setId(id) - .setCode(code) - .setData(null) - .setMessage(StrUtil.blankToDefault(message, "error")) - .setMethod(method) - .setVersion("1.0"); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/pom.xml b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/pom.xml deleted file mode 100644 index 7bb896e229..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - yudao-module-iot-net-components - cn.iocoder.boot - ${revision} - - 4.0.0 - yudao-module-iot-net-component-emqx - jar - - ${project.artifactId} - - 物联网网络组件 EMQX 模块 - - - - - cn.iocoder.boot - yudao-module-iot-net-component-core - ${revision} - - - - - io.vertx - vertx-web - - - io.vertx - vertx-mqtt - - - - - org.springframework.boot - spring-boot-starter-validation - true - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxAutoConfiguration.java deleted file mode 100644 index 68b10ee17e..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxAutoConfiguration.java +++ /dev/null @@ -1,83 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.emqx.config; - -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.IotDeviceUpstreamServer; -import io.vertx.core.Vertx; -import io.vertx.mqtt.MqttClient; -import io.vertx.mqtt.MqttClientOptions; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.event.EventListener; - -/** - * IoT 网络组件 EMQX 的自动配置类 - * - * @author haohao - */ -@AutoConfiguration -@EnableConfigurationProperties(IotNetComponentEmqxProperties.class) -@ConditionalOnProperty(prefix = "yudao.iot.component.emqx", name = "enabled", havingValue = "true") -@ComponentScan(basePackages = { - "cn.iocoder.yudao.module.iot.net.component.emqx" // 只扫描 EMQX 组件包 -}) // TODO @haohao:自动配置后,不需要这个哈。 -@Slf4j -public class IotNetComponentEmqxAutoConfiguration { - - /** - * 初始化 EMQX 组件 - * - * @param event 应用启动事件 - */ - @EventListener(ApplicationStartedEvent.class) - public void initialize(ApplicationStartedEvent event) { - log.info("[IotNetComponentEmqxAutoConfiguration][开始初始化]"); - - // 从应用上下文中获取需要的 Bean - // TODO @芋艿:看看要不要监听下 - - log.info("[initialize][IoT EMQX 组件初始化完成]"); - } - - /** - * 创建 Vert.x 实例 - */ - @Bean(name = "emqxVertx") - public Vertx vertx() { - return Vertx.vertx(); - } - - /** - * 创建 MQTT 客户端 - */ - @Bean - public MqttClient mqttClient(@Qualifier("emqxVertx") Vertx vertx, IotNetComponentEmqxProperties emqxProperties) { - MqttClientOptions options = new MqttClientOptions() - .setClientId("yudao-iot-downstream-" + IdUtil.fastSimpleUUID()) - .setUsername(emqxProperties.getMqttUsername()) - .setPassword(emqxProperties.getMqttPassword()); - // 设置 SSL 选项 - options.setSsl(ObjUtil.defaultIfNull(emqxProperties.getMqttSsl(), false)); - return MqttClient.create(vertx, options); - } - - /** - * 创建设备上行服务器 - */ - @Bean(name = "emqxDeviceUpstreamServer", initMethod = "start", destroyMethod = "stop") - public IotDeviceUpstreamServer deviceUpstreamServer( - IotDeviceUpstreamApi deviceUpstreamApi, - IotNetComponentEmqxProperties emqxProperties, - @Qualifier("emqxVertx") Vertx vertx, - MqttClient mqttClient) { - return new IotDeviceUpstreamServer(emqxProperties, deviceUpstreamApi, vertx, mqttClient); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxProperties.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxProperties.java deleted file mode 100644 index 7b230f5e5a..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxProperties.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.emqx.config; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -/** - * IoT EMQX 网络组件配置属性 - * - * @author haohao - */ -@ConfigurationProperties(prefix = "yudao.iot.component.emqx") -@Data -@Validated -public class IotNetComponentEmqxProperties { - - /** - * 是否启用 EMQX 组件 - */ - private Boolean enabled; - - /** - * MQTT 服务主机 - */ - @NotBlank(message = "MQTT 服务器主机不能为空") - private String mqttHost; - - /** - * MQTT 服务端口 - */ - @NotNull(message = "MQTT 服务器端口不能为空") - private Integer mqttPort; - - /** - * MQTT 服务用户名 - */ - @NotBlank(message = "MQTT 服务器用户名不能为空") - private String mqttUsername; - - /** - * MQTT 服务密码 - */ - @NotBlank(message = "MQTT 服务器密码不能为空") - private String mqttPassword; - - /** - * 是否启用 SSL - */ - @NotNull(message = "MQTT SSL 配置不能为空") - private Boolean mqttSsl; - - /** - * 订阅的主题列表 - */ - @NotEmpty(message = "MQTT 订阅主题不能为空") - private String[] mqttTopics; - - /** - * 认证端口 - */ - @NotNull(message = "认证端口不能为空") - private Integer authPort; - - // TODO @haohao:可以使用 Duration 类型,可读性更好 - /** - * 重连延迟时间(毫秒) - *

- * 默认值:5000 毫秒 - */ - private Integer reconnectDelayMs = 5000; - - /** - * 连接超时时间(毫秒) - *

- * 默认值:10000 毫秒 - */ - private Integer connectionTimeoutMs = 10000; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/downstream/IotDeviceDownstreamHandlerImpl.java deleted file mode 100644 index d8e91a676f..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/downstream/IotDeviceDownstreamHandlerImpl.java +++ /dev/null @@ -1,121 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.emqx.downstream; - -// TODO @芋艿:后续再支持下;@haohao;改成消费者 -///** -// * EMQX 网络组件的 {@link IotDeviceDownstreamHandler} 实现类 -// * -// * @author 芋道源码 -// */ -//@Slf4j -//public class IotDeviceDownstreamHandlerImpl { -// -// /** -// * MQTT 客户端 -// */ -// private final MqttClient mqttClient; -// -// /** -// * 构造函数 -// * -// * @param mqttClient MQTT 客户端 -// */ -// public IotDeviceDownstreamHandlerImpl(MqttClient mqttClient) { -// this.mqttClient = mqttClient; -// } -// -// @Override -// public CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO reqDTO) { -// log.info("[invokeService][开始调用设备服务][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); -// -// // 验证参数 -// if (reqDTO.getProductKey() == null || reqDTO.getDeviceName() == null || reqDTO.getIdentifier() == null) { -// log.error("[invokeService][参数不完整][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); -// return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); -// } -// -// try { -// // 构建请求主题 -// String topic = IotDeviceTopicEnum.buildServiceTopic(reqDTO.getProductKey(), reqDTO.getDeviceName(), -// reqDTO.getIdentifier()); -// -// // 构建请求消息 -// String requestId = StrUtil.isNotEmpty(reqDTO.getRequestId()) ? reqDTO.getRequestId() -// : IotNetComponentCommonUtils.generateRequestId(); -// IotMqttMessage message = IotMqttMessage.createServiceInvokeMessage( -// requestId, reqDTO.getIdentifier(), reqDTO.getParams()); -// -// // 发送消息 -// publishMessage(topic, message.toJsonObject()); -// -// log.info("[invokeService][调用设备服务成功][requestId: {}][topic: {}]", requestId, topic); -// return CommonResult.success(true); -// } catch (Exception e) { -// log.error("[invokeService][调用设备服务异常][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO), e); -// return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); -// } -// } -// -// @Override -// public CommonResult getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { -// // 暂未实现,返回成功 -// return CommonResult.success(true); -// } -// -// @Override -// public CommonResult setDeviceProperty(IotDevicePropertySetReqDTO reqDTO) { -// log.info("[setProperty][开始设置设备属性][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); -// -// // 验证参数 -// if (reqDTO.getProductKey() == null || reqDTO.getDeviceName() == null) { -// log.error("[setProperty][参数不完整][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); -// return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); -// } -// -// try { -// // 构建请求主题 -// String topic = IotDeviceTopicEnum.buildPropertySetTopic(reqDTO.getProductKey(), reqDTO.getDeviceName()); -// -// // 构建请求消息 -// String requestId = StrUtil.isNotEmpty(reqDTO.getRequestId()) ? reqDTO.getRequestId() -// : IotNetComponentCommonUtils.generateRequestId(); -// IotMqttMessage message = IotMqttMessage.createPropertySetMessage(requestId, reqDTO.getProperties()); -// -// // 发送消息 -// publishMessage(topic, message.toJsonObject()); -// -// log.info("[setProperty][设置设备属性成功][requestId: {}][topic: {}]", requestId, topic); -// return CommonResult.success(true); -// } catch (Exception e) { -// log.error("[setProperty][设置设备属性异常][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO), e); -// return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); -// } -// } -// -// @Override -// public CommonResult setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO) { -// // 暂未实现,返回成功 -// return CommonResult.success(true); -// } -// -// @Override -// public CommonResult upgradeDeviceOta(IotDeviceOtaUpgradeReqDTO upgradeReqDTO) { -// // 暂未实现,返回成功 -// return CommonResult.success(true); -// } -// -// /** -// * 发布 MQTT 消息 -// * -// * @param topic 主题 -// * @param payload 消息内容 -// */ -// private void publishMessage(String topic, JSONObject payload) { -// mqttClient.publish( -// topic, -// Buffer.buffer(payload.toString()), -// MqttQoS.AT_LEAST_ONCE, -// false, -// false); -// log.info("[publishMessage][发送消息成功][topic: {}][payload: {}]", topic, payload); -// } -//} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/IotDeviceUpstreamServer.java deleted file mode 100644 index 6b83aa3795..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/IotDeviceUpstreamServer.java +++ /dev/null @@ -1,261 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.emqx.upstream; - -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.net.component.emqx.config.IotNetComponentEmqxProperties; -import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router.IotDeviceAuthVertxHandler; -import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router.IotDeviceMqttMessageHandler; -import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router.IotDeviceWebhookVertxHandler; -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.Future; -import io.vertx.core.Vertx; -import io.vertx.core.http.HttpServer; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.handler.BodyHandler; -import io.vertx.mqtt.MqttClient; -import lombok.extern.slf4j.Slf4j; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -/** - * IoT 设备上行服务端,接收来自 device 设备的请求,转发给 server 服务器 - *

- * 协议:HTTP、MQTT - * - * @author haohao - */ -@Slf4j -public class IotDeviceUpstreamServer { - - /** - * 默认 QoS 级别 - */ - private static final MqttQoS DEFAULT_QOS = MqttQoS.AT_LEAST_ONCE; - - private final Vertx vertx; - private final HttpServer server; - private final MqttClient client; - private final IotNetComponentEmqxProperties emqxProperties; - private final IotDeviceMqttMessageHandler mqttMessageHandler; - - /** - * 服务运行状态标志 - */ - private volatile boolean isRunning = false; - - public IotDeviceUpstreamServer(IotNetComponentEmqxProperties emqxProperties, - IotDeviceUpstreamApi deviceUpstreamApi, - Vertx vertx, - MqttClient client) { - this.vertx = vertx; - this.emqxProperties = emqxProperties; - this.client = client; - - // 创建 Router 实例 - Router router = Router.router(vertx); - router.route().handler(BodyHandler.create()); // 处理 Body - router.post(IotDeviceAuthVertxHandler.PATH) - // MQTT 认证不必须通过 HTTP 进行,但 HTTP 认证是 EMQX 等 MQTT 服务器支持的一种灵活的认证方式 - .handler(new IotDeviceAuthVertxHandler(deviceUpstreamApi)); - // 添加 Webhook 处理器,用于处理设备连接和断开连接事件 - router.post(IotDeviceWebhookVertxHandler.PATH) - .handler(new IotDeviceWebhookVertxHandler(deviceUpstreamApi)); - // 创建 HttpServer 实例 - this.server = vertx.createHttpServer().requestHandler(router); - this.mqttMessageHandler = new IotDeviceMqttMessageHandler(deviceUpstreamApi, client); - } - - /** - * 启动 HTTP 服务器、MQTT 客户端 - */ - public void start() { - if (isRunning) { - log.warn("[start][服务已经在运行中,请勿重复启动]"); - return; - } - log.info("[start][开始启动服务]"); - - // 检查 authPort 是否为 null - // TODO @haohao:authPort 里面搞默认值?包括下面,这个类不搞任何默认值,都交给 emqxProperties - Integer authPort = emqxProperties.getAuthPort(); - if (authPort == null) { - log.warn("[start][authPort 为 null,使用默认端口 8080]"); - authPort = 8080; // 默认端口 - } - - // 获取连接超时时间 - int connectionTimeoutMs = emqxProperties.getConnectionTimeoutMs() != null - ? emqxProperties.getConnectionTimeoutMs() - : 10000; - - // 1. 启动 HTTP 服务器 - final Integer finalAuthPort = authPort; // 为 lambda 表达式创建 final 变量 - CompletableFuture httpFuture = server.listen(finalAuthPort) - .toCompletionStage() - .toCompletableFuture() - .thenAccept(v -> log.info("[start][HTTP 服务器启动完成,端口: {}]", server.actualPort())); - - // 2. 连接 MQTT Broker - CompletableFuture mqttFuture = connectMqtt() - .toCompletionStage() - .toCompletableFuture() - .thenAccept(v -> { - // 2.1 添加 MQTT 断开重连监听器 - client.closeHandler(closeEvent -> { - log.warn("[closeHandler][MQTT 连接已断开,准备重连]"); - reconnectWithDelay(); - }); - // 2.2 设置 MQTT 消息处理器 - setupMessageHandler(); - }); - - // 3. 等待所有服务启动完成 - CompletableFuture.allOf(httpFuture, mqttFuture) - .orTimeout(connectionTimeoutMs, TimeUnit.MILLISECONDS) - .whenComplete((result, error) -> { - if (error != null) { - log.error("[start][服务启动失败]", error); - } else { - isRunning = true; - log.info("[start][所有服务启动完成]"); - } - }); - } - - /** - * 设置 MQTT 消息处理器 - */ - private void setupMessageHandler() { - client.publishHandler(mqttMessageHandler::handle); - log.debug("[setupMessageHandler][MQTT 消息处理器设置完成]"); - } - - /** - * 重连 MQTT 客户端 - */ - private void reconnectWithDelay() { - if (!isRunning) { - log.info("[reconnectWithDelay][服务已停止,不再尝试重连]"); - return; - } - - // 获取重连延迟时间 - int reconnectDelayMs = emqxProperties.getReconnectDelayMs() != null - ? emqxProperties.getReconnectDelayMs() - : 5000; - - vertx.setTimer(reconnectDelayMs, id -> { - log.info("[reconnectWithDelay][开始重新连接 MQTT]"); - connectMqtt(); - }); - } - - /** - * 连接 MQTT Broker 并订阅主题 - * - * @return 连接结果的 Future - */ - private Future connectMqtt() { - // 检查必要的 MQTT 配置 - String host = emqxProperties.getMqttHost(); - Integer port = emqxProperties.getMqttPort(); - if (StrUtil.isBlank(host)) { - String msg = "[connectMqtt][MQTT Host 为空,无法连接]"; - log.error(msg); - return Future.failedFuture(new IllegalStateException(msg)); - } - if (port == null) { - log.warn("[connectMqtt][MQTT Port 为 null,使用默认端口 1883]"); - port = 1883; // 默认 MQTT 端口 - } - - final Integer finalPort = port; - return client.connect(finalPort, host) - .compose(connAck -> { - log.info("[connectMqtt][MQTT 客户端连接成功]"); - return subscribeToTopics(); - }) - .recover(error -> { - log.error("[connectMqtt][连接 MQTT Broker 失败:]", error); - reconnectWithDelay(); - return Future.failedFuture(error); - }); - } - - /** - * 订阅设备上行消息主题 - * - * @return 订阅结果的 Future - */ - private Future subscribeToTopics() { - String[] topics = emqxProperties.getMqttTopics(); - if (ArrayUtil.isEmpty(topics)) { - log.warn("[subscribeToTopics][未配置 MQTT 主题或为 null,使用默认主题]"); - topics = new String[]{"/device/#"}; // 默认订阅所有设备上下行主题 - } - - // 使用协调器追踪多个 Future 的完成状态 - Future result = Future.succeededFuture(); - for (String topic : topics) { - if (StrUtil.isBlank(topic)) { - log.warn("[subscribeToTopics][跳过空主题]"); - continue; - } - - result = result.compose(v -> client.subscribe(topic, DEFAULT_QOS.value()) - .map(ack -> { - log.info("[subscribeToTopics][订阅主题成功: {}]", topic); - return null; - }) - .recover(err -> { - log.error("[subscribeToTopics][订阅主题失败: {}]", topic, err); - return Future.failedFuture(err); - })); - } - return result; - } - - /** - * 停止服务 - */ - public void stop() { - if (!isRunning) { - log.warn("[stop][服务已经停止,无需再次停止]"); - return; - } - log.info("[stop][开始停止服务]"); - - // 1. 取消 MQTT 主题订阅 - if (client.isConnected()) { - for (String topic : emqxProperties.getMqttTopics()) { - try { - client.unsubscribe(topic); - } catch (Exception e) { - log.warn("[stop][取消订阅主题异常: {}]", topic, e); - } - } - } - - // 2. 关闭 MQTT 客户端 - try { - if (client.isConnected()) { - client.disconnect(); - } - } catch (Exception e) { - log.warn("[stop][关闭 MQTT 客户端异常]", e); - } - - // 3. 关闭 HTTP 服务器 - try { - server.close(); - } catch (Exception e) { - log.warn("[stop][关闭 HTTP 服务器异常]", e); - } - - // 4. 更新状态 - isRunning = false; - log.info("[stop][服务已停止]"); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceAuthVertxHandler.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceAuthVertxHandler.java deleted file mode 100644 index 72d4b4c161..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceAuthVertxHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceEmqxAuthReqDTO; -import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; -import io.vertx.core.Handler; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.web.RoutingContext; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import java.util.Collections; - -/** - * IoT EMQX 连接认证的 Vert.x Handler - *

- * 参考:EMQX HTTP - *

- * 注意:该处理器需要返回特定格式:{"result": "allow"} 或 {"result": "deny"}, - * 以符合 EMQX 认证插件的要求,因此不使用 IotStandardResponse 实体类 - * - * @author haohao - */ -@RequiredArgsConstructor -@Slf4j -public class IotDeviceAuthVertxHandler implements Handler { - - public static final String PATH = "/mqtt/auth"; - - private final IotDeviceUpstreamApi deviceUpstreamApi; - - @Override - public void handle(RoutingContext routingContext) { - try { - // 构建认证请求 DTO - JsonObject json = routingContext.body().asJsonObject(); - String clientId = json.getString("clientid"); - String username = json.getString("username"); - String password = json.getString("password"); - IotDeviceEmqxAuthReqDTO authReqDTO = new IotDeviceEmqxAuthReqDTO() - .setClientId(clientId) - .setUsername(username) - .setPassword(password); - - // 调用认证 API - CommonResult authResult = deviceUpstreamApi.authenticateEmqxConnection(authReqDTO); - if (authResult.getCode() != 0 || !authResult.getData()) { - // 注意:这里必须返回 {"result": "deny"} 格式,以符合 EMQX 认证插件的要求 - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny")); - return; - } - - // 响应结果 - // 注意:这里必须返回 {"result": "allow"} 格式,以符合 EMQX 认证插件的要求 - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "allow")); - } catch (Exception e) { - log.error("[handle][EMQX 认证异常]", e); - // 注意:这里必须返回 {"result": "deny"} 格式,以符合 EMQX 认证插件的要求 - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny")); - } - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceMqttMessageHandler.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceMqttMessageHandler.java deleted file mode 100644 index d61e41b567..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceMqttMessageHandler.java +++ /dev/null @@ -1,287 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.net.component.core.constants.IotDeviceTopicEnum; -import cn.iocoder.yudao.module.iot.net.component.core.pojo.IotStandardResponse; -import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; -import io.netty.handler.codec.mqtt.MqttQoS; -import io.vertx.core.buffer.Buffer; -import io.vertx.mqtt.MqttClient; -import io.vertx.mqtt.messages.MqttPublishMessage; -import lombok.extern.slf4j.Slf4j; - -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * IoT 设备 MQTT 消息处理器 - *

- * 参考:设备属性、事件、服务 - */ -@Slf4j -public class IotDeviceMqttMessageHandler { - - // TODO @haohao:下面的,有办法也抽到 IotDeviceTopicEnum 么?想的是,尽量把这些 method、topic、url 统一化; - private static final String PROPERTY_METHOD = "thing.event.property.post"; - private static final String EVENT_METHOD_PREFIX = "thing.event."; - private static final String EVENT_METHOD_SUFFIX = ".post"; - - private final IotDeviceUpstreamApi deviceUpstreamApi; - private final MqttClient mqttClient; - - public IotDeviceMqttMessageHandler(IotDeviceUpstreamApi deviceUpstreamApi, MqttClient mqttClient) { - this.deviceUpstreamApi = deviceUpstreamApi; - this.mqttClient = mqttClient; - } - - /** - * 处理MQTT消息 - * - * @param message MQTT发布消息 - */ - public void handle(MqttPublishMessage message) { - String topic = message.topicName(); - String payload = message.payload().toString(); - log.info("[messageHandler][接收到消息][topic: {}][payload: {}]", topic, payload); - - try { - if (StrUtil.isEmpty(payload)) { - log.warn("[messageHandler][消息内容为空][topic: {}]", topic); - return; - } - handleMessage(topic, payload); - } catch (Exception e) { - log.error("[messageHandler][处理消息失败][topic: {}][payload: {}]", topic, payload, e); - } - } - - /** - * 根据主题类型处理消息 - * - * @param topic 主题 - * @param payload 消息内容 - */ - private void handleMessage(String topic, String payload) { - // 校验前缀 - if (!topic.startsWith(IotDeviceTopicEnum.SYS_TOPIC_PREFIX.getTopic())) { - log.warn("[handleMessage][未知的消息类型][topic: {}]", topic); - return; - } - - // 处理设备属性上报消息 - if (topic.endsWith(IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic())) { - log.info("[handleMessage][接收到设备属性上报][topic: {}]", topic); - handlePropertyPost(topic, payload); - return; - } - - // 处理设备事件上报消息 - if (topic.contains(IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic()) && - topic.endsWith(IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic())) { - log.info("[handleMessage][接收到设备事件上报][topic: {}]", topic); - handleEventPost(topic, payload); - return; - } - - // 未知消息类型 - log.warn("[handleMessage][未知的消息类型][topic: {}]", topic); - } - - /** - * 处理设备属性上报消息 - * - * @param topic 主题 - * @param payload 消息内容 - */ - private void handlePropertyPost(String topic, String payload) { - try { - // 解析消息内容 - JSONObject jsonObject = JSONUtil.parseObj(payload); - String[] topicParts = parseTopic(topic); - if (topicParts == null) { - return; - } - - // 构建设备属性上报请求对象 - IotDevicePropertyReportReqDTO reportReqDTO = buildPropertyReportDTO(jsonObject, topicParts); - - // 调用上游 API 处理设备上报数据 - deviceUpstreamApi.reportDeviceProperty(reportReqDTO); - log.info("[handlePropertyPost][处理设备属性上报成功][topic: {}]", topic); - - // 发送响应消息 - sendResponse(topic, jsonObject, PROPERTY_METHOD, null); - } catch (Exception e) { - log.error("[handlePropertyPost][处理设备属性上报失败][topic: {}][payload: {}]", topic, payload, e); - } - } - - /** - * 处理设备事件上报消息 - * - * @param topic 主题 - * @param payload 消息内容 - */ - private void handleEventPost(String topic, String payload) { - try { - // 解析消息内容 - JSONObject jsonObject = JSONUtil.parseObj(payload); - String[] topicParts = parseTopic(topic); - if (topicParts == null) { - return; - } - - // 构建设备事件上报请求对象 - IotDeviceEventReportReqDTO reportReqDTO = buildEventReportDTO(jsonObject, topicParts); - - // 调用上游 API 处理设备上报数据 - deviceUpstreamApi.reportDeviceEvent(reportReqDTO); - log.info("[handleEventPost][处理设备事件上报成功][topic: {}]", topic); - - // 从 topic 中获取事件标识符 - String eventIdentifier = getEventIdentifier(topicParts, topic); - if (eventIdentifier == null) { - return; - } - - // 发送响应消息 - String method = EVENT_METHOD_PREFIX + eventIdentifier + EVENT_METHOD_SUFFIX; - sendResponse(topic, jsonObject, method, null); - } catch (Exception e) { - log.error("[handleEventPost][处理设备事件上报失败][topic: {}][payload: {}]", topic, payload, e); - } - } - - /** - * 解析主题,获取主题各部分 - * - * @param topic 主题 - * @return 主题各部分数组,如果解析失败返回null - */ - private String[] parseTopic(String topic) { - String[] topicParts = topic.split("/"); - if (topicParts.length < 7) { - log.warn("[parseTopic][主题格式不正确][topic: {}]", topic); - return null; - } - return topicParts; - } - - /** - * 从主题部分中获取事件标识符 - * - * @param topicParts 主题各部分 - * @param topic 原始主题,用于日志 - * @return 事件标识符,如果获取失败返回null - */ - private String getEventIdentifier(String[] topicParts, String topic) { - try { - return topicParts[6]; - } catch (ArrayIndexOutOfBoundsException e) { - log.warn("[getEventIdentifier][无法从主题中获取事件标识符][topic: {}][topicParts: {}]", - topic, Arrays.toString(topicParts)); - return null; - } - } - - /** - * 发送响应消息 - * - * @param topic 原始主题 - * @param jsonObject 原始消息JSON对象 - * @param method 响应方法 - * @param customData 自定义数据,可为 null - */ - private void sendResponse(String topic, JSONObject jsonObject, String method, Object customData) { - String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic); - - // 响应结果 - IotStandardResponse response = IotStandardResponse.success( - jsonObject.getStr("id"), method, customData); - try { - mqttClient.publish(replyTopic, Buffer.buffer(JsonUtils.toJsonString(response)), - MqttQoS.AT_LEAST_ONCE, false, false); - log.info("[sendResponse][发送响应消息成功][topic: {}]", replyTopic); - } catch (Exception e) { - log.error("[sendResponse][发送响应消息失败][topic: {}][response: {}]", replyTopic, response, e); - } - } - - /** - * 构建设备属性上报请求对象 - * - * @param jsonObject 消息内容 - * @param topicParts 主题部分 - * @return 设备属性上报请求对象 - */ - private IotDevicePropertyReportReqDTO buildPropertyReportDTO(JSONObject jsonObject, String[] topicParts) { - // TODO @haohao:IotDevicePropertyReportReqDTO 可以考虑链式哈。其它也是,尽量让同类参数在一行;这样,阅读起来更聚焦; - IotDevicePropertyReportReqDTO reportReqDTO = new IotDevicePropertyReportReqDTO(); - reportReqDTO.setRequestId(jsonObject.getStr("id")); - reportReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId()); - reportReqDTO.setReportTime(LocalDateTime.now()); - reportReqDTO.setProductKey(topicParts[2]); - reportReqDTO.setDeviceName(topicParts[3]); - - // 只使用标准 JSON格式处理属性数据 - JSONObject params = jsonObject.getJSONObject("params"); - if (params == null) { - log.warn("[buildPropertyReportDTO][消息格式不正确,缺少params字段][jsonObject: {}]", jsonObject); - params = new JSONObject(); - } - - // 将标准格式的params转换为平台需要的properties格式 - Map properties = new HashMap<>(); - for (Map.Entry entry : params.entrySet()) { - String key = entry.getKey(); - Object valueObj = entry.getValue(); - - // 如果是复杂结构(包含value和time) - if (valueObj instanceof JSONObject valueJson) { - properties.put(key, valueJson.getOrDefault("value", valueObj)); - } else { - properties.put(key, valueObj); - } - } - reportReqDTO.setProperties(properties); - - return reportReqDTO; - } - - /** - * 构建设备事件上报请求对象 - * - * @param jsonObject 消息内容 - * @param topicParts 主题部分 - * @return 设备事件上报请求对象 - */ - private IotDeviceEventReportReqDTO buildEventReportDTO(JSONObject jsonObject, String[] topicParts) { - IotDeviceEventReportReqDTO reportReqDTO = new IotDeviceEventReportReqDTO(); - reportReqDTO.setRequestId(jsonObject.getStr("id")); - reportReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId()); - reportReqDTO.setReportTime(LocalDateTime.now()); - reportReqDTO.setProductKey(topicParts[2]); - reportReqDTO.setDeviceName(topicParts[3]); - reportReqDTO.setIdentifier(topicParts[6]); - - // 只使用标准JSON格式处理事件参数 - JSONObject params = jsonObject.getJSONObject("params"); - if (params == null) { - log.warn("[buildEventReportDTO][消息格式不正确,缺少params字段][jsonObject: {}]", jsonObject); - params = new JSONObject(); - } - reportReqDTO.setParams(params); - - return reportReqDTO; - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceWebhookVertxHandler.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceWebhookVertxHandler.java deleted file mode 100644 index 4c3550dbe8..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceWebhookVertxHandler.java +++ /dev/null @@ -1,152 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils; -import io.vertx.core.Handler; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.web.RoutingContext; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import java.time.LocalDateTime; -import java.util.Collections; - -/** - * IoT EMQX Webhook 事件处理的 Vert.x Handler - *

- * 参考:EMQX Webhook - *

- * 注意:该处理器需要返回特定格式:{"result": "success"} 或 {"result": "error"}, - * 以符合 EMQX Webhook 插件的要求,因此不使用 IotStandardResponse 实体类。 - * - * @author haohao - */ -@RequiredArgsConstructor -@Slf4j -public class IotDeviceWebhookVertxHandler implements Handler { - - public static final String PATH = "/mqtt/webhook"; - - private final IotDeviceUpstreamApi deviceUpstreamApi; - - @Override - public void handle(RoutingContext routingContext) { - try { - // 解析请求体 - JsonObject json = routingContext.body().asJsonObject(); - String event = json.getString("event"); - String clientId = json.getString("clientid"); - String username = json.getString("username"); - - // 处理不同的事件类型 - switch (event) { - case "client.connected": - handleClientConnected(clientId, username); - break; - case "client.disconnected": - handleClientDisconnected(clientId, username); - break; - default: - log.info("[handle][未处理的 Webhook 事件] event={}, clientId={}, username={}", event, clientId, username); - break; - } - - // 返回成功响应 - // 注意:这里必须返回 {"result": "success"} 格式,以符合 EMQX Webhook 插件的要求 - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "success")); - } catch (Exception e) { - log.error("[handle][处理 Webhook 事件异常]", e); - // 注意:这里必须返回 {"result": "error"} 格式,以符合 EMQX Webhook 插件的要求 - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "error")); - } - } - - /** - * 处理客户端连接事件 - * - * @param clientId 客户端ID - * @param username 用户名 - */ - private void handleClientConnected(String clientId, String username) { - // 解析产品标识和设备名称 - if (StrUtil.isEmpty(username) || "undefined".equals(username)) { - log.warn("[handleClientConnected][客户端连接事件,但用户名为空] clientId={}", clientId); - return; - } - String[] parts = parseUsername(username); - if (parts == null) { - return; - } - - // 更新设备状态为在线 - IotDeviceStateUpdateReqDTO updateReqDTO = new IotDeviceStateUpdateReqDTO(); - updateReqDTO.setProductKey(parts[1]); - updateReqDTO.setDeviceName(parts[0]); - updateReqDTO.setState(IotDeviceStateEnum.ONLINE.getState()); - updateReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId()); - updateReqDTO.setReportTime(LocalDateTime.now()); - CommonResult result = deviceUpstreamApi.updateDeviceState(updateReqDTO); - if (result.getCode() != 0 || !result.getData()) { - log.error("[handleClientConnected][更新设备状态为在线失败] clientId={}, username={}, code={}, msg={}", - clientId, username, result.getCode(), result.getMsg()); - } else { - log.info("[handleClientConnected][更新设备状态为在线成功] clientId={}, username={}", clientId, username); - } - } - - /** - * 处理客户端断开连接事件 - * - * @param clientId 客户端ID - * @param username 用户名 - */ - private void handleClientDisconnected(String clientId, String username) { - // 解析产品标识和设备名称 - if (StrUtil.isEmpty(username) || "undefined".equals(username)) { - log.warn("[handleClientDisconnected][客户端断开连接事件,但用户名为空] clientId={}", clientId); - return; - } - String[] parts = parseUsername(username); - if (parts == null) { - return; - } - - // 更新设备状态为离线 - IotDeviceStateUpdateReqDTO offlineReqDTO = new IotDeviceStateUpdateReqDTO(); - offlineReqDTO.setProductKey(parts[1]); - offlineReqDTO.setDeviceName(parts[0]); - offlineReqDTO.setState(IotDeviceStateEnum.OFFLINE.getState()); - offlineReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId()); - offlineReqDTO.setReportTime(LocalDateTime.now()); - CommonResult offlineResult = deviceUpstreamApi.updateDeviceState(offlineReqDTO); - if (offlineResult.getCode() != 0 || !offlineResult.getData()) { - log.error("[handleClientDisconnected][更新设备状态为离线失败] clientId={}, username={}, code={}, msg={}", - clientId, username, offlineResult.getCode(), offlineResult.getMsg()); - } else { - log.info("[handleClientDisconnected][更新设备状态为离线成功] clientId={}, username={}", clientId, username); - } - } - - /** - * 解析用户名,格式为 deviceName&productKey - * - * @param username 用户名 - * @return 解析结果,[0] 为 deviceName,[1] 为 productKey,解析失败返回 null - */ - private String[] parseUsername(String username) { - if (StrUtil.isEmpty(username)) { - return null; - } - String[] parts = username.split("&"); - if (parts.length != 2) { - log.warn("[parseUsername][用户名格式({})不正确,无法解析产品标识和设备名称]", username); - return null; - } - return parts; - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index c5597d25af..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -cn.iocoder.yudao.module.iot.net.component.emqx.config.IotNetComponentEmqxAutoConfiguration \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/application.yml deleted file mode 100644 index 01002c653a..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/application.yml +++ /dev/null @@ -1,18 +0,0 @@ -# EMQX组件默认配置 -yudao: - iot: - component: - # 核心组件配置 - core: - plugin-key: emqx # 插件的唯一标识 - # EMQX组件配置 -# emqx: -# enabled: true # 启用EMQX组件 -# mqtt-host: 127.0.0.1 # MQTT服务器主机地址 -# mqtt-port: 1883 # MQTT服务器端口 -# mqtt-username: yudao # MQTT服务器用户名 -# mqtt-password: 123456 # MQTT服务器密码 -# mqtt-ssl: false # 是否启用SSL -# mqtt-topics: # 订阅的主题列表 -# - "/sys/#" -# auth-port: 8101 # 认证端口 diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/pom.xml b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/pom.xml deleted file mode 100644 index 457feee683..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/pom.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - 4.0.0 - - cn.iocoder.boot - yudao-module-iot-net-components - ${revision} - - - yudao-module-iot-net-component-server - jar - - ${project.artifactId} - - IoT 网络组件的独立运行服务,用于聚合和启动多个网络组件实例。 - - - - - - org.springframework.boot - spring-boot-starter-web - - - - - cn.iocoder.boot - yudao-module-iot-net-component-emqx - ${revision} - - - - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerConfiguration.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerConfiguration.java deleted file mode 100644 index 7d646c5343..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.server.config; - -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.net.component.server.upstream.IotComponentUpstreamClient; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.web.client.RestTemplate; - -/** - * IoT 网络组件服务器配置类 - * - * @author haohao - */ -@Configuration -@EnableConfigurationProperties(IotNetComponentServerProperties.class) -@EnableScheduling -public class IotNetComponentServerConfiguration { - - /** - * 配置 RestTemplate - * - * @param properties 配置 - * @return RestTemplate - */ - @Bean - // TODO @haohao:貌似要独立一个 restTemplate 的名字?不然容易冲突; - public RestTemplate restTemplate(IotNetComponentServerProperties properties) { - return new RestTemplateBuilder() - .connectTimeout(properties.getUpstreamConnectTimeout()) - .readTimeout(properties.getUpstreamReadTimeout()) - .build(); - } - - /** - * 配置设备上行客户端 - * - * @param properties 配置 - * @param restTemplate RestTemplate - * @return 上行客户端 - */ - @Bean - @Primary - public IotDeviceUpstreamApi deviceUpstreamApi(IotNetComponentServerProperties properties, - RestTemplate restTemplate) { - return new IotComponentUpstreamClient(properties, restTemplate); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerProperties.java b/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerProperties.java deleted file mode 100644 index bc0a65a6dc..0000000000 --- a/yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerProperties.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.iot.net.component.server.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -import java.time.Duration; - -/** - * IoT 网络组件服务配置属性 - * - * @author haohao - */ -@ConfigurationProperties(prefix = "yudao.iot.component.server") -@Validated -@Data -public class IotNetComponentServerProperties { - - /** - * 上行 URL,用于向主应用程序上报数据 - */ - private String upstreamUrl = "http://127.0.0.1:48080"; - - /** - * 上行连接超时时间 - */ - private Duration upstreamConnectTimeout = Duration.ofSeconds(30); - - /** - * 上行读取超时时间 - */ - private Duration upstreamReadTimeout = Duration.ofSeconds(30); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/README.md b/yudao-module-iot/yudao-module-iot-protocol/README.md deleted file mode 100644 index 77dd02c1af..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/README.md +++ /dev/null @@ -1,254 +0,0 @@ -# IoT 协议模块 (yudao-module-iot-protocol) - -## 概述 - -本模块是物联网协议处理的核心组件,提供统一的协议解析、转换和消息处理功能。作为 `yudao-module-iot-biz` 和 -`yudao-module-iot-gateway-server` 等模块的共享包,实现了协议层面的抽象和统一。 - -## 主要功能 - -### 1. 协议消息模型 - -- **IotMqttMessage**: 基于 MQTT 协议规范的标准消息模型(默认实现) -- **IotStandardResponse**: 统一的响应格式,支持 MQTT 和 HTTP 协议 - -### 2. 主题管理 - -- **IotTopicConstants**: 主题常量定义 -- **IotTopicUtils**: MQTT 主题构建和解析工具 -- **IotHttpTopicUtils**: HTTP 主题构建和解析工具 -- **IotTopicParser**: 高级主题解析器,支持提取设备信息、消息类型等 - -### 3. 协议转换 - -- **IotMessageParser**: 消息解析器接口 -- **IotMqttMessageParser**: MQTT 协议解析器实现(默认) -- **IotHttpMessageParser**: HTTP 协议解析器实现 -- **IotProtocolConverter**: 协议转换器接口 -- **DefaultIotProtocolConverter**: 默认协议转换器实现 - -### 4. 枚举定义 - -- **IotProtocolTypeEnum**: 协议类型枚举 -- **IotMessageTypeEnum**: 消息类型枚举 -- **IotMessageDirectionEnum**: 消息方向枚举 - -## 使用示例 - -### 1. 构建主题 - -#### MQTT 主题 - -```java -// 构建设备属性设置主题 -String topic = IotTopicUtils.buildPropertySetTopic("productKey", "deviceName"); -// 结果: /sys/productKey/deviceName/thing/service/property/set - -// 构建事件上报主题 -String eventTopic = IotTopicUtils.buildEventPostTopic("productKey", "deviceName", "temperature"); -// 结果: /sys/productKey/deviceName/thing/event/temperature/post - -// 获取响应主题 -String replyTopic = IotTopicUtils.getReplyTopic(topic); -// 结果: /sys/productKey/deviceName/thing/service/property/set_reply -``` - -#### HTTP 主题 - -```java -// 构建属性设置路径 -String propSetPath = IotHttpTopicUtils.buildPropertySetPath("productKey", "deviceName"); -// 结果: /topic/sys/productKey/deviceName/thing/service/property/set - -// 构建属性获取路径 -String propGetPath = IotHttpTopicUtils.buildPropertyGetPath("productKey", "deviceName"); -// 结果: /topic/sys/productKey/deviceName/thing/service/property/get - -// 构建事件上报路径 -String eventPath = IotHttpTopicUtils.buildEventPostPath("productKey", "deviceName", "alarm"); -// 结果: /topic/sys/productKey/deviceName/thing/event/alarm/post - -// 构建自定义主题路径 -String customPath = IotHttpTopicUtils.buildCustomTopicPath("productKey", "deviceName", "user/get"); -// 结果: /topic/productKey/deviceName/user/get -``` - -### 2. 解析主题 - -```java -// 解析 MQTT 主题信息 -IotTopicParser.TopicInfo info = IotTopicParser.parse("/sys/pk/dn/thing/service/property/set"); -System.out. - -println("产品Key: "+info.getProductKey()); // pk - System.out. - -println("设备名称: "+info.getDeviceName()); // dn - System.out. - -println("消息类型: "+info.getMessageType()); // PROPERTY_SET - System.out. - -println("消息方向: "+info.getDirection()); // DOWNSTREAM - -// 解析 HTTP 主题信息 -String httpPath = "/topic/sys/pk/dn/thing/service/property/set"; -String actualTopic = IotHttpTopicUtils.extractActualTopic(httpPath); // /sys/pk/dn/thing/service/property/set -String productKey = IotHttpTopicUtils.parseProductKeyFromTopic(actualTopic); // pk -String deviceName = IotHttpTopicUtils.parseDeviceNameFromTopic(actualTopic); // dn -``` - -### 3. 创建 MQTT 消息 - -```java -// 创建属性设置消息 -Map properties = new HashMap<>(); -properties. - -put("temperature",25.5); - -IotMqttMessage message = IotMqttMessage.createPropertySetMessage("123456", properties); - -// 转换为 JSON 字符串 -String json = message.toJsonString(); -``` - -### 4. HTTP 协议消息处理 - -#### HTTP 消息格式 - -```json -{ - "deviceKey": "productKey/deviceName", - "messageId": "123456", - "action": "property.set", - "version": "1.0", - "data": { - "temperature": 25.5, - "humidity": 60.0 - } -} -``` - -#### 使用 HTTP 协议解析器 - -```java -// 创建 HTTP 协议解析器 -IotHttpMessageParser httpParser = new IotHttpMessageParser(); - -// 解析 HTTP 消息 -String topic = "/topic/sys/productKey/deviceName/thing/service/property/set"; -byte[] payload = httpMessage.getBytes(StandardCharsets.UTF_8); -IotMqttMessage message = httpParser.parse(topic, payload); - -// 格式化 HTTP 响应 -IotStandardResponse response = IotStandardResponse.success("123456", "property.set", data); -byte[] responseBytes = httpParser.formatResponse(response); -``` - -### 5. 使用协议转换器 - -```java - -@Autowired -private IotProtocolConverter protocolConverter; - -// 转换 MQTT 消息(推荐使用) -IotMqttMessage mqttMessage = protocolConverter.convertToStandardMessage(mqttTopic, mqttPayload, "mqtt"); - -// 转换 HTTP 消息 -IotMqttMessage httpMessage = protocolConverter.convertToStandardMessage(httpTopic, httpPayload, "http"); - -// 创建响应 -IotStandardResponse response = IotStandardResponse.success("123456", "property.set", data); -byte[] responseBytes = protocolConverter.convertFromStandardResponse(response, "mqtt"); -``` - -### 6. 自定义协议解析器 - -```java - -@Component -public class CustomMessageParser implements IotMessageParser { - - @Override - public IotMqttMessage parse(String topic, byte[] payload) { - // 实现自定义协议解析逻辑 - return null; - } - - @Override - public byte[] formatResponse(IotStandardResponse response) { - // 实现自定义响应格式化逻辑 - return new byte[0]; - } - - @Override - public boolean canHandle(String topic) { - // 判断是否能处理该主题 - return topic.startsWith("/custom/"); - } -} - -// 注册到协议转换器 -@Autowired -private DefaultIotProtocolConverter converter; - -@PostConstruct -public void init() { - converter.registerParser("custom", new CustomMessageParser()); -} -``` - -## 支持的协议类型 - -- **MQTT**: 标准 MQTT 协议,支持设备属性、事件、服务调用(默认协议) -- **HTTP**: HTTP 协议,支持设备通过 HTTP API 进行通信 -- **MQTT_RAW**: MQTT 原始协议 -- **TCP**: TCP 协议 -- **UDP**: UDP 协议 -- **CUSTOM**: 自定义协议 - -## 协议对比 - -| 协议类型 | 传输方式 | 消息格式 | 主题格式 | 适用场景 | -|----------|------|------|----------------------------------------------------------------------------------------------------------------------------|---------------| -| MQTT | MQTT | JSON | `/sys/{productKey}/{deviceName}/...`
`/mqtt/{productKey}/{deviceName}/...`
`/device/{productKey}/{deviceName}/...` | 实时性要求高的设备(推荐) | -| HTTP | HTTP | JSON | `/topic/sys/{productKey}/{deviceName}/...`
`/topic/{productKey}/{deviceName}/...` | 间歇性通信的设备 | -| MQTT_RAW | MQTT | 原始 | 自定义格式 | 特殊协议设备 | - -## 模块依赖 - -本模块是一个基础模块,依赖项最小化: - -- `yudao-common`: 基础工具类 -- `hutool-all`: 工具库 -- `lombok`: 简化代码 -- `spring-boot-starter`: Spring Boot 基础支持 - -## 扩展点 - -### 1. 自定义消息解析器 - -实现 `IotMessageParser` 接口,支持新的协议格式。 - -### 2. 自定义协议转换器 - -实现 `IotProtocolConverter` 接口,提供更复杂的转换逻辑。 - -### 3. 自定义主题格式 - -扩展 `IotTopicParser` 的 `parseCustomTopic` 方法,支持自定义主题格式。 - -## 注意事项 - -1. 本模块设计为无状态的工具模块,避免引入有状态的组件 -2. 所有的工具类都采用静态方法,便于直接调用 -3. 异常处理采用返回 null 的方式,调用方需要做好空值检查 -4. 日志级别建议设置为 INFO 或 WARN,避免过多的 DEBUG 日志 -5. HTTP 协议解析器使用设备标识 `deviceKey`(格式:`productKey/deviceName`)来标识设备 - -## 版本更新 - -- v1.0.0: 基础功能实现,支持 MQTT 协议和 HTTP 协议支持 -- 后续版本将支持更多协议类型和高级功能 \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/pom.xml b/yudao-module-iot/yudao-module-iot-protocol/pom.xml deleted file mode 100644 index 0a4e4552dd..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/pom.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - yudao-module-iot - cn.iocoder.boot - ${revision} - - 4.0.0 - - yudao-module-iot-protocol - jar - - ${project.artifactId} - - 物联网协议模块,提供 topic 解析、协议转换等功能 - 作为 iot-biz 和 iot-gateway 的共享包 - - - - - - cn.iocoder.boot - yudao-common - - - - - cn.iocoder.boot - yudao-spring-boot-starter-web - provided - - - - - org.projectlombok - lombok - - - - cn.hutool - hutool-all - - - - - io.vertx - vertx-core - provided - - - io.vertx - vertx-web - provided - - - - - org.junit.jupiter - junit-jupiter - test - - - org.springframework.boot - spring-boot-starter-test - test - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfiguration.java deleted file mode 100644 index 758f1f00ca..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfiguration.java +++ /dev/null @@ -1,74 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.config; - -import cn.iocoder.yudao.module.iot.protocol.convert.IotProtocolConverter; -import cn.iocoder.yudao.module.iot.protocol.convert.impl.DefaultIotProtocolConverter; -import cn.iocoder.yudao.module.iot.protocol.enums.IotProtocolTypeEnum; -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser; -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotHttpMessageParser; -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotMqttMessageParser; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * IoT 协议模块自动配置类 - * - * @author haohao - */ -@Configuration(proxyBeanMethods = false) -public class IotProtocolAutoConfiguration { - - /** - * Bean 名称常量 - */ - public static final String IOT_MQTT_MESSAGE_PARSER_BEAN_NAME = "iotMqttMessageParser"; - public static final String IOT_HTTP_MESSAGE_PARSER_BEAN_NAME = "iotHttpMessageParser"; - - /** - * 注册 MQTT 协议消息解析器 - * - * @return MQTT 协议消息解析器 - */ - @Bean - @ConditionalOnMissingBean(name = IOT_MQTT_MESSAGE_PARSER_BEAN_NAME) - public IotMessageParser iotMqttMessageParser() { - return new IotMqttMessageParser(); - } - - - /** - * 注册 HTTP 协议消息解析器 - * - * @return HTTP 协议消息解析器 - */ - @Bean - @ConditionalOnMissingBean(name = IOT_HTTP_MESSAGE_PARSER_BEAN_NAME) - public IotMessageParser iotHttpMessageParser() { - return new IotHttpMessageParser(); - } - - /** - * 注册默认协议转换器 - *

- * 如果用户没有自定义协议转换器,则使用默认实现 - * 默认会注册 MQTT 和 HTTP 协议解析器 - * - * @param iotMqttMessageParser MQTT 协议解析器 - * @param iotHttpMessageParser HTTP 协议解析器 - * @return 默认协议转换器 - */ - @Bean - @ConditionalOnMissingBean - public IotProtocolConverter iotProtocolConverter(IotMessageParser iotMqttMessageParser, - IotMessageParser iotHttpMessageParser) { - DefaultIotProtocolConverter converter = new DefaultIotProtocolConverter(); - - // 注册 MQTT 协议解析器(默认实现) - converter.registerParser(IotProtocolTypeEnum.MQTT.getCode(), iotMqttMessageParser); - - // 注册 HTTP 协议解析器 - converter.registerParser(IotProtocolTypeEnum.HTTP.getCode(), iotHttpMessageParser); - - return converter; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotHttpConstants.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotHttpConstants.java deleted file mode 100644 index aeb4b3240f..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotHttpConstants.java +++ /dev/null @@ -1,166 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.constants; - -/** - * IoT HTTP 协议常量类 - *

- * 用于统一管理 HTTP 协议中的常量,包括路径、字段名、默认值等 - * - * @author haohao - */ -public class IotHttpConstants { - - /** - * 路径常量 - */ - public static class Path { - /** - * 认证路径 - */ - public static final String AUTH = "/auth"; - - /** - * 主题路径前缀 - */ - public static final String TOPIC_PREFIX = "/topic"; - } - - /** - * 认证字段常量 - */ - public static class AuthField { - /** - * 产品Key - */ - public static final String PRODUCT_KEY = "productKey"; - - /** - * 设备名称 - */ - public static final String DEVICE_NAME = "deviceName"; - - /** - * 客户端ID - */ - public static final String CLIENT_ID = "clientId"; - - /** - * 时间戳 - */ - public static final String TIMESTAMP = "timestamp"; - - /** - * 签名 - */ - public static final String SIGN = "sign"; - - /** - * 签名方法 - */ - public static final String SIGN_METHOD = "signmethod"; - - /** - * 版本 - */ - public static final String VERSION = "version"; - } - - /** - * 消息字段常量 - */ - public static class MessageField { - /** - * 消息ID - */ - public static final String ID = "id"; - - /** - * 方法名 - */ - public static final String METHOD = "method"; - - /** - * 版本 - */ - public static final String VERSION = "version"; - - /** - * 参数 - */ - public static final String PARAMS = "params"; - - /** - * 数据 - */ - public static final String DATA = "data"; - } - - /** - * 响应字段常量 - */ - public static class ResponseField { - /** - * 状态码 - */ - public static final String CODE = "code"; - - /** - * 消息 - */ - public static final String MESSAGE = "message"; - - /** - * 信息 - */ - public static final String INFO = "info"; - - /** - * 令牌 - */ - public static final String TOKEN = "token"; - - /** - * 消息ID - */ - public static final String MESSAGE_ID = "messageId"; - } - - /** - * 默认值常量 - */ - public static class DefaultValue { - /** - * 默认签名方法 - */ - public static final String SIGN_METHOD = "hmacmd5"; - - /** - * 默认版本 - */ - public static final String VERSION = "default"; - - /** - * 默认消息版本 - */ - public static final String MESSAGE_VERSION = "1.0"; - - /** - * 未知方法名 - */ - public static final String UNKNOWN_METHOD = "unknown"; - } - - /** - * 方法名常量 - */ - public static class Method { - /** - * 设备认证 - */ - public static final String DEVICE_AUTH = "device.auth"; - - /** - * 自定义消息 - */ - public static final String CUSTOM_MESSAGE = "custom.message"; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotLogConstants.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotLogConstants.java deleted file mode 100644 index 05b7179870..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotLogConstants.java +++ /dev/null @@ -1,91 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.constants; - -/** - * IoT 协议日志消息常量类 - *

- * 用于统一管理协议模块中的日志消息常量 - * - * @author haohao - */ -public class IotLogConstants { - - /** - * HTTP 协议日志消息 - */ - public static class Http { - /** - * 收到空消息内容 - */ - public static final String RECEIVED_EMPTY_MESSAGE = "[HTTP] 收到空消息内容, topic={}"; - - /** - * 不支持的路径格式 - */ - public static final String UNSUPPORTED_PATH_FORMAT = "[HTTP] 不支持的路径格式, topic={}"; - - /** - * 解析消息失败 - */ - public static final String PARSE_MESSAGE_FAILED = "[HTTP] 解析消息失败, topic={}"; - - /** - * 认证消息非JSON格式 - */ - public static final String AUTH_MESSAGE_NOT_JSON = "[HTTP] 认证消息非JSON格式, message={}"; - - /** - * 认证消息缺少必需字段 - */ - public static final String AUTH_MESSAGE_MISSING_REQUIRED_FIELDS = "[HTTP] 认证消息缺少必需字段, message={}"; - - /** - * 格式化响应失败 - */ - public static final String FORMAT_RESPONSE_FAILED = "[HTTP] 格式化响应失败"; - } - - /** - * 协议转换器日志消息 - */ - public static class Converter { - /** - * 注册协议解析器 - */ - public static final String REGISTER_PARSER = "[协议转换器] 注册协议解析器: protocol={}, parser={}"; - - /** - * 移除协议解析器 - */ - public static final String REMOVE_PARSER = "[协议转换器] 移除协议解析器: protocol={}"; - - /** - * 不支持的协议类型 - */ - public static final String UNSUPPORTED_PROTOCOL = "[协议转换器] 不支持的协议类型: protocol={}"; - - /** - * 转换消息失败 - */ - public static final String CONVERT_MESSAGE_FAILED = "[协议转换器] 转换消息失败: protocol={}, topic={}"; - - /** - * 格式化响应失败 - */ - public static final String FORMAT_RESPONSE_FAILED = "[协议转换器] 格式化响应失败: protocol={}"; - - /** - * 自动选择协议 - */ - public static final String AUTO_SELECT_PROTOCOL = "[协议转换器] 自动选择协议: protocol={}, topic={}"; - - /** - * 协议解析失败,尝试下一个 - */ - public static final String PROTOCOL_PARSE_FAILED_TRY_NEXT = "[协议转换器] 协议解析失败,尝试下一个: protocol={}, topic={}"; - - /** - * 无法自动识别协议 - */ - public static final String CANNOT_AUTO_RECOGNIZE_PROTOCOL = "[协议转换器] 无法自动识别协议: topic={}"; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotTopicConstants.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotTopicConstants.java deleted file mode 100644 index 59453518cd..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotTopicConstants.java +++ /dev/null @@ -1,157 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.constants; - -/** - * IoT 设备主题常量类 - *

- * 用于统一管理 MQTT 协议中的主题常量,基于 Alink 协议规范 - * - * @author haohao - */ -public class IotTopicConstants { - - /** - * 系统主题前缀 - */ - public static final String SYS_TOPIC_PREFIX = "/sys/"; - - /** - * 服务调用主题前缀 - */ - public static final String SERVICE_TOPIC_PREFIX = "/thing/service/"; - - /** - * 设备属性设置主题 - * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/set - * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/property/set_reply - */ - public static final String PROPERTY_SET_TOPIC = "/thing/service/property/set"; - - /** - * 设备属性获取主题 - * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/get - * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/property/get_reply - */ - public static final String PROPERTY_GET_TOPIC = "/thing/service/property/get"; - - /** - * 设备配置设置主题 - * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/config/set - * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/config/set_reply - */ - public static final String CONFIG_SET_TOPIC = "/thing/service/config/set"; - - /** - * 设备OTA升级主题 - * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade - * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade_reply - */ - public static final String OTA_UPGRADE_TOPIC = "/thing/service/ota/upgrade"; - - /** - * 设备属性上报主题 - * 请求Topic:/sys/${productKey}/${deviceName}/thing/event/property/post - * 响应Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply - */ - public static final String PROPERTY_POST_TOPIC = "/thing/event/property/post"; - - /** - * 设备事件上报主题前缀 - */ - public static final String EVENT_POST_TOPIC_PREFIX = "/thing/event/"; - - /** - * 设备事件上报主题后缀 - */ - public static final String EVENT_POST_TOPIC_SUFFIX = "/post"; - - /** - * 响应主题后缀 - */ - public static final String REPLY_SUFFIX = "_reply"; - - /** - * 方法名前缀常量 - */ - public static class MethodPrefix { - /** - * 物模型服务前缀 - */ - public static final String THING_SERVICE = "thing.service."; - - /** - * 物模型事件前缀 - */ - public static final String THING_EVENT = "thing.event."; - } - - /** - * 完整方法名常量 - */ - public static class Method { - /** - * 属性设置方法 - */ - public static final String PROPERTY_SET = "thing.service.property.set"; - - /** - * 属性获取方法 - */ - public static final String PROPERTY_GET = "thing.service.property.get"; - - /** - * 属性上报方法 - */ - public static final String PROPERTY_POST = "thing.event.property.post"; - - /** - * 配置设置方法 - */ - public static final String CONFIG_SET = "thing.service.config.set"; - - /** - * OTA升级方法 - */ - public static final String OTA_UPGRADE = "thing.service.ota.upgrade"; - - /** - * 设备上线方法 - */ - public static final String DEVICE_ONLINE = "device.online"; - - /** - * 设备下线方法 - */ - public static final String DEVICE_OFFLINE = "device.offline"; - - /** - * 心跳方法 - */ - public static final String HEARTBEAT = "heartbeat"; - } - - /** - * 主题关键字常量 - */ - public static class Keyword { - /** - * 事件关键字 - */ - public static final String EVENT = "event"; - - /** - * 服务关键字 - */ - public static final String SERVICE = "service"; - - /** - * 属性关键字 - */ - public static final String PROPERTY = "property"; - - /** - * 上报关键字 - */ - public static final String POST = "post"; - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/IotProtocolConverter.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/IotProtocolConverter.java deleted file mode 100644 index b942feb97f..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/IotProtocolConverter.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.convert; - -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage; -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse; - -/** - * IoT 协议转换器接口 - *

- * 用于在不同协议之间进行转换 - * - * @author haohao - */ -public interface IotProtocolConverter { - - /** - * 将字节数组转换为标准消息 - * - * @param topic 主题 - * @param payload 消息负载 - * @param protocol 协议类型 - * @return 标准消息对象,转换失败返回 null - */ - IotMqttMessage convertToStandardMessage(String topic, byte[] payload, String protocol); - - /** - * 将标准响应转换为字节数组 - * - * @param response 标准响应 - * @param protocol 协议类型 - * @return 字节数组,转换失败返回空数组 - */ - byte[] convertFromStandardResponse(IotStandardResponse response, String protocol); - - /** - * 检查是否支持指定协议 - * - * @param protocol 协议类型 - * @return 如果支持返回 true,否则返回 false - */ - boolean supportsProtocol(String protocol); - - /** - * 获取支持的协议类型列表 - * - * @return 协议类型数组 - */ - String[] getSupportedProtocols(); -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/impl/DefaultIotProtocolConverter.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/impl/DefaultIotProtocolConverter.java deleted file mode 100644 index 798eca01a0..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/impl/DefaultIotProtocolConverter.java +++ /dev/null @@ -1,132 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.convert.impl; - -import cn.iocoder.yudao.module.iot.protocol.constants.IotLogConstants; -import cn.iocoder.yudao.module.iot.protocol.convert.IotProtocolConverter; -import cn.iocoder.yudao.module.iot.protocol.enums.IotProtocolTypeEnum; -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser; -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage; -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse; -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotMqttMessageParser; -import lombok.extern.slf4j.Slf4j; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * 默认 IoT 协议转换器实现 - *

- * 支持多种协议的转换,可以通过注册不同的消息解析器来扩展支持的协议 - * - * @author haohao - */ -@Slf4j -public class DefaultIotProtocolConverter implements IotProtocolConverter { - - /** - * 消息解析器映射 - * Key: 协议类型,Value: 消息解析器 - */ - private final Map parsers = new HashMap<>(); - - /** - * 构造函数,初始化默认支持的协议 - */ - public DefaultIotProtocolConverter() { - // 注册 MQTT 协议解析器作为默认实现 - IotMqttMessageParser mqttParser = new IotMqttMessageParser(); - registerParser(IotProtocolTypeEnum.MQTT.getCode(), mqttParser); - } - - /** - * 注册消息解析器 - * - * @param protocol 协议类型 - * @param parser 消息解析器 - */ - public void registerParser(String protocol, IotMessageParser parser) { - parsers.put(protocol, parser); - log.info(IotLogConstants.Converter.REGISTER_PARSER, protocol, parser.getClass().getSimpleName()); - } - - /** - * 移除消息解析器 - * - * @param protocol 协议类型 - */ - public void removeParser(String protocol) { - parsers.remove(protocol); - log.info(IotLogConstants.Converter.REMOVE_PARSER, protocol); - } - - @Override - public IotMqttMessage convertToStandardMessage(String topic, byte[] payload, String protocol) { - IotMessageParser parser = parsers.get(protocol); - if (parser == null) { - log.warn(IotLogConstants.Converter.UNSUPPORTED_PROTOCOL, protocol); - return null; - } - - try { - return parser.parse(topic, payload); - } catch (Exception e) { - log.error(IotLogConstants.Converter.CONVERT_MESSAGE_FAILED, protocol, topic, e); - return null; - } - } - - @Override - public byte[] convertFromStandardResponse(IotStandardResponse response, String protocol) { - IotMessageParser parser = parsers.get(protocol); - if (parser == null) { - log.warn(IotLogConstants.Converter.UNSUPPORTED_PROTOCOL, protocol); - return new byte[0]; - } - - try { - return parser.formatResponse(response); - } catch (Exception e) { - log.error(IotLogConstants.Converter.FORMAT_RESPONSE_FAILED, protocol, e); - return new byte[0]; - } - } - - @Override - public boolean supportsProtocol(String protocol) { - return parsers.containsKey(protocol); - } - - @Override - public String[] getSupportedProtocols() { - Set protocols = parsers.keySet(); - return protocols.toArray(new String[0]); - } - - /** - * 根据主题自动选择合适的协议解析器 - * - * @param topic 主题 - * @param payload 消息负载 - * @return 解析后的标准消息,如果无法解析返回 null - */ - public IotMqttMessage autoConvert(String topic, byte[] payload) { - // 遍历所有解析器,找到能处理该主题的解析器 - for (Map.Entry entry : parsers.entrySet()) { - IotMessageParser parser = entry.getValue(); - if (parser.canHandle(topic)) { - try { - IotMqttMessage message = parser.parse(topic, payload); - if (message != null) { - log.debug(IotLogConstants.Converter.AUTO_SELECT_PROTOCOL, entry.getKey(), topic); - return message; - } - } catch (Exception e) { - log.debug(IotLogConstants.Converter.PROTOCOL_PARSE_FAILED_TRY_NEXT, entry.getKey(), topic); - } - } - } - - log.warn(IotLogConstants.Converter.CANNOT_AUTO_RECOGNIZE_PROTOCOL, topic); - return null; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageDirectionEnum.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageDirectionEnum.java deleted file mode 100644 index 6cce13894e..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageDirectionEnum.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * IoT 消息方向枚举 - * - * @author haohao - */ -@Getter -@AllArgsConstructor -public enum IotMessageDirectionEnum { - - /** - * 上行消息(设备到平台) - */ - UPSTREAM("upstream", "上行"), - - /** - * 下行消息(平台到设备) - */ - DOWNSTREAM("downstream", "下行"); - - /** - * 方向编码 - */ - private final String code; - - /** - * 方向名称 - */ - private final String name; - - /** - * 根据编码获取消息方向 - * - * @param code 方向编码 - * @return 消息方向枚举,如果未找到返回 null - */ - public static IotMessageDirectionEnum getByCode(String code) { - for (IotMessageDirectionEnum direction : values()) { - if (direction.getCode().equals(code)) { - return direction; - } - } - return null; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageTypeEnum.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageTypeEnum.java deleted file mode 100644 index b2425dd991..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageTypeEnum.java +++ /dev/null @@ -1,140 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.enums; - -import cn.iocoder.yudao.module.iot.protocol.constants.IotTopicConstants; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * IoT 消息类型枚举 - * - * @author haohao - */ -@Getter -@AllArgsConstructor -public enum IotMessageTypeEnum { - - /** - * 属性上报 - */ - PROPERTY_POST("property.post", "属性上报"), - - /** - * 属性设置 - */ - PROPERTY_SET("property.set", "属性设置"), - - /** - * 属性获取 - */ - PROPERTY_GET("property.get", "属性获取"), - - /** - * 事件上报 - */ - EVENT_POST("event.post", "事件上报"), - - /** - * 服务调用 - */ - SERVICE_INVOKE("service.invoke", "服务调用"), - - /** - * 配置设置 - */ - CONFIG_SET("config.set", "配置设置"), - - /** - * OTA 升级 - */ - OTA_UPGRADE("ota.upgrade", "OTA升级"), - - /** - * 设备上线 - */ - DEVICE_ONLINE("device.online", "设备上线"), - - /** - * 设备下线 - */ - DEVICE_OFFLINE("device.offline", "设备下线"), - - /** - * 心跳 - */ - HEARTBEAT("heartbeat", "心跳"); - - /** - * 消息类型编码 - */ - private final String code; - - /** - * 消息类型名称 - */ - private final String name; - - /** - * 根据编码获取消息类型 - * - * @param code 消息类型编码 - * @return 消息类型枚举,如果未找到返回 null - */ - public static IotMessageTypeEnum getByCode(String code) { - for (IotMessageTypeEnum type : values()) { - if (type.getCode().equals(code)) { - return type; - } - } - return null; - } - - /** - * 根据方法名获取消息类型 - * - * @param method 方法名 - * @return 消息类型枚举,如果未找到返回 null - */ - public static IotMessageTypeEnum getByMethod(String method) { - if (method == null) { - return null; - } - - // 处理 thing.service.xxx 格式 - if (method.startsWith(IotTopicConstants.MethodPrefix.THING_SERVICE)) { - String servicePart = method.substring(IotTopicConstants.MethodPrefix.THING_SERVICE.length()); - if ("property.set".equals(servicePart)) { - return PROPERTY_SET; - } else if ("property.get".equals(servicePart)) { - return PROPERTY_GET; - } else if ("config.set".equals(servicePart)) { - return CONFIG_SET; - } else if ("ota.upgrade".equals(servicePart)) { - return OTA_UPGRADE; - } else { - return SERVICE_INVOKE; - } - } - - // 处理 thing.event.xxx 格式 - if (method.startsWith(IotTopicConstants.MethodPrefix.THING_EVENT)) { - String eventPart = method.substring(IotTopicConstants.MethodPrefix.THING_EVENT.length()); - if ("property.post".equals(eventPart)) { - return PROPERTY_POST; - } else { - return EVENT_POST; - } - } - - // 其他类型 - switch (method) { - case IotTopicConstants.Method.DEVICE_ONLINE: - return DEVICE_ONLINE; - case IotTopicConstants.Method.DEVICE_OFFLINE: - return DEVICE_OFFLINE; - case IotTopicConstants.Method.HEARTBEAT: - return HEARTBEAT; - default: - return null; - } - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotProtocolTypeEnum.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotProtocolTypeEnum.java deleted file mode 100644 index a83262bab5..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotProtocolTypeEnum.java +++ /dev/null @@ -1,79 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * IoT 协议类型枚举 - * - * @author haohao - */ -@Getter -@AllArgsConstructor -public enum IotProtocolTypeEnum { - - /** - * MQTT 协议(默认实现) - */ - MQTT("mqtt", "MQTT 协议"), - - /** - * MQTT 原始协议 - */ - MQTT_RAW("mqtt_raw", "MQTT 原始协议"), - - /** - * HTTP 协议 - */ - HTTP("http", "HTTP 协议"), - - /** - * TCP 协议 - */ - TCP("tcp", "TCP 协议"), - - /** - * UDP 协议 - */ - UDP("udp", "UDP 协议"), - - /** - * 自定义协议 - */ - CUSTOM("custom", "自定义协议"); - - /** - * 协议编码 - */ - private final String code; - - /** - * 协议名称 - */ - private final String name; - - /** - * 根据编码获取协议类型 - * - * @param code 协议编码 - * @return 协议类型枚举,如果未找到返回 null - */ - public static IotProtocolTypeEnum getByCode(String code) { - for (IotProtocolTypeEnum type : values()) { - if (type.getCode().equals(code)) { - return type; - } - } - return null; - } - - /** - * 检查是否为有效的协议编码 - * - * @param code 协议编码 - * @return 如果有效返回 true,否则返回 false - */ - public static boolean isValidCode(String code) { - return getByCode(code) != null; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMessageParser.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMessageParser.java deleted file mode 100644 index d92beb4429..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMessageParser.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.message; - -/** - * IoT 消息解析器接口 - *

- * 用于解析不同协议的消息内容 - * - * @author haohao - */ -public interface IotMessageParser { - - /** - * 解析消息 - * - * @param topic 主题 - * @param payload 消息负载 - * @return 解析后的标准消息,如果解析失败返回 null - */ - IotMqttMessage parse(String topic, byte[] payload); - - /** - * 格式化响应消息 - * - * @param response 标准响应 - * @return 格式化后的响应字节数组 - */ - byte[] formatResponse(IotStandardResponse response); - - /** - * 检查是否能够处理指定主题的消息 - * - * @param topic 主题 - * @return 如果能处理返回 true,否则返回 false - */ - boolean canHandle(String topic); -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMqttMessage.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMqttMessage.java deleted file mode 100644 index 36cc1a7f06..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMqttMessage.java +++ /dev/null @@ -1,154 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.message; - -import cn.hutool.core.util.IdUtil; -import cn.hutool.json.JSONObject; -import lombok.Builder; -import lombok.Data; - -import java.util.Map; - -/** - * IoT MQTT 消息模型 - *

- * 基于 MQTT 协议规范实现的标准消息格式,支持设备属性、事件、服务调用等标准功能 - * - * @author haohao - * @see MQTT 协议官方规范 - */ -@Data -@Builder -public class IotMqttMessage { - - /** - * 消息 ID - */ - private String id; - - /** - * 协议版本 - */ - @Builder.Default - private String version = "1.0"; - - /** - * 消息方法 - */ - private String method; - - /** - * 消息参数 - */ - private Map params; - - /** - * 转换为 JSONObject - * - * @return JSONObject 对象 - */ - public JSONObject toJsonObject() { - JSONObject json = new JSONObject(); - json.set("id", id); - json.set("version", version); - json.set("method", method); - json.set("params", params != null ? params : new JSONObject()); - return json; - } - - /** - * 转换为 JSON 字符串 - * - * @return JSON 字符串 - */ - public String toJsonString() { - return toJsonObject().toString(); - } - - /** - * 创建设备服务调用消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param serviceIdentifier 服务标识符 - * @param params 服务参数 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createServiceInvokeMessage(String requestId, String serviceIdentifier, - Map params) { - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service." + serviceIdentifier) - .params(params) - .build(); - } - - /** - * 创建设备属性设置消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param properties 设备属性 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createPropertySetMessage(String requestId, Map properties) { - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service.property.set") - .params(properties) - .build(); - } - - /** - * 创建设备属性获取消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param identifiers 要获取的属性标识符列表 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createPropertyGetMessage(String requestId, String[] identifiers) { - JSONObject params = new JSONObject(); - params.set("identifiers", identifiers); - - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service.property.get") - .params(params) - .build(); - } - - /** - * 创建设备配置设置消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param configs 设备配置 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createConfigSetMessage(String requestId, Map configs) { - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service.config.set") - .params(configs) - .build(); - } - - /** - * 创建设备 OTA 升级消息 - * - * @param requestId 请求 ID,为空时自动生成 - * @param otaInfo OTA 升级信息 - * @return MQTT 消息对象 - */ - public static IotMqttMessage createOtaUpgradeMessage(String requestId, Map otaInfo) { - return IotMqttMessage.builder() - .id(requestId != null ? requestId : generateRequestId()) - .method("thing.service.ota.upgrade") - .params(otaInfo) - .build(); - } - - /** - * 生成请求 ID - * - * @return 请求 ID - */ - public static String generateRequestId() { - return IdUtil.fastSimpleUUID(); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotStandardResponse.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotStandardResponse.java deleted file mode 100644 index bde1065395..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotStandardResponse.java +++ /dev/null @@ -1,95 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.message; - -import cn.hutool.core.util.StrUtil; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * IoT 标准协议响应实体类 - *

- * 用于统一 MQTT 和 HTTP 的响应格式 - * - * @author haohao - */ -@Data -@Accessors(chain = true) -public class IotStandardResponse { - - /** - * 消息 ID - */ - private String id; - - /** - * 状态码 - */ - private Integer code; - - /** - * 响应数据 - */ - private Object data; - - /** - * 响应消息 - */ - private String message; - - /** - * 方法名 - */ - private String method; - - /** - * 协议版本 - */ - private String version; - - /** - * 创建成功响应 - * - * @param id 消息 ID - * @param method 方法名 - * @return 成功响应 - */ - public static IotStandardResponse success(String id, String method) { - return success(id, method, null); - } - - /** - * 创建成功响应 - * - * @param id 消息 ID - * @param method 方法名 - * @param data 响应数据 - * @return 成功响应 - */ - public static IotStandardResponse success(String id, String method, Object data) { - return new IotStandardResponse() - .setId(id) - .setCode(200) - .setData(data) - .setMessage("success") - .setMethod(method) - .setVersion("1.0"); - } - - /** - * 创建错误响应 - * - * @param id 消息 ID - * @param method 方法名 - * @param code 错误码 - * @param message 错误消息 - * @return 错误响应 - */ - public static IotStandardResponse error(String id, String method, Integer code, String message) { - return new IotStandardResponse() - .setId(id) - .setCode(code) - .setData(null) - .setMessage(StrUtil.blankToDefault(message, "error")) - .setMethod(method) - .setVersion("1.0"); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParser.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParser.java deleted file mode 100644 index 2ce4625c34..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParser.java +++ /dev/null @@ -1,348 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.message.impl; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.module.iot.protocol.constants.IotHttpConstants; -import cn.iocoder.yudao.module.iot.protocol.constants.IotLogConstants; -import cn.iocoder.yudao.module.iot.protocol.constants.IotTopicConstants; -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser; -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage; -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse; -import lombok.extern.slf4j.Slf4j; - -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -/** - * IoT HTTP 协议消息解析器实现 - *

- * 参考阿里云IoT平台HTTPS协议标准,支持设备认证和数据上报两种消息类型: - *

- * 1. 设备认证消息格式: - * - *

- * POST /auth HTTP/1.1
- * Content-Type: application/json
- * {
- *   "productKey": "a1AbC***",
- *   "deviceName": "device01",
- *   "clientId": "device01_001",
- *   "timestamp": "1501668289957",
- *   "sign": "xxxxx",
- *   "signmethod": "hmacsha1",
- *   "version": "default"
- * }
- * 
- *

- * 2. 数据上报消息格式: - * - *

- * POST /topic/${topic} HTTP/1.1
- * password: ${token}
- * Content-Type: application/octet-stream
- * ${payload}
- * 
- * - * @author haohao - */ -@Slf4j -public class IotHttpMessageParser implements IotMessageParser { - - /** - * 认证路径 - */ - public static final String AUTH_PATH = IotHttpConstants.Path.AUTH; - - /** - * 主题路径前缀 - */ - public static final String TOPIC_PATH_PREFIX = IotHttpConstants.Path.TOPIC_PREFIX; - - @Override - public IotMqttMessage parse(String topic, byte[] payload) { - if (payload == null || payload.length == 0) { - log.warn(IotLogConstants.Http.RECEIVED_EMPTY_MESSAGE, topic); - return null; - } - - try { - String message = new String(payload, StandardCharsets.UTF_8); - - // 判断是认证请求还是数据上报 - if (AUTH_PATH.equals(topic)) { - return parseAuthMessage(message); - } else if (topic.startsWith(TOPIC_PATH_PREFIX)) { - return parseDataMessage(topic, message); - } else { - log.warn(IotLogConstants.Http.UNSUPPORTED_PATH_FORMAT, topic); - return null; - } - - } catch (Exception e) { - log.error(IotLogConstants.Http.PARSE_MESSAGE_FAILED, topic, e); - return null; - } - } - - /** - * 解析设备认证消息 - * - * @param message 认证消息JSON - * @return 标准消息格式 - */ - private IotMqttMessage parseAuthMessage(String message) { - if (!JSONUtil.isTypeJSON(message)) { - log.warn(IotLogConstants.Http.AUTH_MESSAGE_NOT_JSON, message); - return null; - } - - JSONObject json = JSONUtil.parseObj(message); - - // 验证必需字段 - String productKey = json.getStr(IotHttpConstants.AuthField.PRODUCT_KEY); - String deviceName = json.getStr(IotHttpConstants.AuthField.DEVICE_NAME); - String clientId = json.getStr(IotHttpConstants.AuthField.CLIENT_ID); - String sign = json.getStr(IotHttpConstants.AuthField.SIGN); - - if (StrUtil.hasBlank(productKey, deviceName, clientId, sign)) { - log.warn(IotLogConstants.Http.AUTH_MESSAGE_MISSING_REQUIRED_FIELDS, message); - return null; - } - - // 构建认证消息 - Map params = new HashMap<>(); - params.put(IotHttpConstants.AuthField.PRODUCT_KEY, productKey); - params.put(IotHttpConstants.AuthField.DEVICE_NAME, deviceName); - params.put(IotHttpConstants.AuthField.CLIENT_ID, clientId); - params.put(IotHttpConstants.AuthField.TIMESTAMP, json.getStr(IotHttpConstants.AuthField.TIMESTAMP)); - params.put(IotHttpConstants.AuthField.SIGN, sign); - params.put(IotHttpConstants.AuthField.SIGN_METHOD, - json.getStr(IotHttpConstants.AuthField.SIGN_METHOD, IotHttpConstants.DefaultValue.SIGN_METHOD)); - - return IotMqttMessage.builder() - .id(generateMessageId()) - .method(IotHttpConstants.Method.DEVICE_AUTH) - .version(json.getStr(IotHttpConstants.AuthField.VERSION, IotHttpConstants.DefaultValue.VERSION)) - .params(params) - .build(); - } - - /** - * 解析数据上报消息 - * - * @param topic 主题路径,格式:/topic/${actualTopic} - * @param message 消息内容 - * @return 标准消息格式 - */ - private IotMqttMessage parseDataMessage(String topic, String message) { - // 提取实际的主题,去掉 /topic 前缀 - String actualTopic = topic.substring(TOPIC_PATH_PREFIX.length()); // 直接移除/topic前缀 - - // 尝试解析为JSON格式 - if (JSONUtil.isTypeJSON(message)) { - return parseJsonDataMessage(actualTopic, message); - } else { - // 原始数据格式 - return parseRawDataMessage(actualTopic, message); - } - } - - /** - * 解析JSON格式的数据消息 - * - * @param topic 实际主题 - * @param message JSON消息 - * @return 标准消息格式 - */ - private IotMqttMessage parseJsonDataMessage(String topic, String message) { - JSONObject json = JSONUtil.parseObj(message); - - // 生成消息ID - String messageId = json.getStr(IotHttpConstants.MessageField.ID); - if (StrUtil.isBlank(messageId)) { - messageId = generateMessageId(); - } - - // 获取方法名 - String method = json.getStr(IotHttpConstants.MessageField.METHOD); - if (StrUtil.isBlank(method)) { - // 根据主题推断方法名 - method = inferMethodFromTopic(topic); - } - - // 获取参数 - Object params = json.get(IotHttpConstants.MessageField.PARAMS); - Map paramsMap = new HashMap<>(); - if (params instanceof Map) { - paramsMap.putAll((Map) params); - } else if (params != null) { - paramsMap.put(IotHttpConstants.MessageField.DATA, params); - } - - return IotMqttMessage.builder() - .id(messageId) - .method(method) - .version(json.getStr(IotHttpConstants.MessageField.VERSION, - IotHttpConstants.DefaultValue.MESSAGE_VERSION)) - .params(paramsMap) - .build(); - } - - /** - * 解析原始数据消息 - * - * @param topic 实际主题 - * @param message 原始消息 - * @return 标准消息格式 - */ - private IotMqttMessage parseRawDataMessage(String topic, String message) { - Map params = new HashMap<>(); - params.put(IotHttpConstants.MessageField.DATA, message); - - return IotMqttMessage.builder() - .id(generateMessageId()) - .method(inferMethodFromTopic(topic)) - .version(IotHttpConstants.DefaultValue.MESSAGE_VERSION) - .params(params) - .build(); - } - - /** - * 根据主题推断方法名 - * - * @param topic 主题 - * @return 方法名 - */ - private String inferMethodFromTopic(String topic) { - if (StrUtil.isBlank(topic)) { - return IotHttpConstants.DefaultValue.UNKNOWN_METHOD; - } - - // 标准系统主题解析 - if (topic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) { - if (topic.contains(IotTopicConstants.PROPERTY_SET_TOPIC)) { - return IotTopicConstants.Method.PROPERTY_SET; - } else if (topic.contains(IotTopicConstants.PROPERTY_GET_TOPIC)) { - return IotTopicConstants.Method.PROPERTY_GET; - } else if (topic.contains(IotTopicConstants.PROPERTY_POST_TOPIC)) { - return IotTopicConstants.Method.PROPERTY_POST; - } else if (topic.contains(IotTopicConstants.EVENT_POST_TOPIC_PREFIX) - && topic.endsWith(IotTopicConstants.EVENT_POST_TOPIC_SUFFIX)) { - // 自定义事件上报 - String[] parts = topic.split("/"); - // 查找event关键字的位置 - for (int i = 0; i < parts.length; i++) { - if (IotTopicConstants.Keyword.EVENT.equals(parts[i]) && i + 1 < parts.length) { - String eventId = parts[i + 1]; - return IotTopicConstants.MethodPrefix.THING_EVENT + eventId + ".post"; - } - } - } else if (topic.contains(IotTopicConstants.SERVICE_TOPIC_PREFIX) - && !topic.contains(IotTopicConstants.Keyword.PROPERTY)) { - // 自定义服务调用 - String[] parts = topic.split("/"); - // 查找service关键字的位置 - for (int i = 0; i < parts.length; i++) { - if (IotTopicConstants.Keyword.SERVICE.equals(parts[i]) && i + 1 < parts.length) { - String serviceId = parts[i + 1]; - return IotTopicConstants.MethodPrefix.THING_SERVICE + serviceId; - } - } - } - } - - // 自定义主题 - return IotHttpConstants.Method.CUSTOM_MESSAGE; - } - - /** - * 生成消息ID - * - * @return 消息ID - */ - private String generateMessageId() { - return IotMqttMessage.generateRequestId(); - } - - @Override - public byte[] formatResponse(IotStandardResponse response) { - try { - JSONObject httpResponse = new JSONObject(); - - // 判断是否为认证响应 - if (IotHttpConstants.Method.DEVICE_AUTH.equals(response.getMethod())) { - // 认证响应格式 - httpResponse.set(IotHttpConstants.ResponseField.CODE, response.getCode()); - httpResponse.set(IotHttpConstants.ResponseField.MESSAGE, response.getMessage()); - - if (response.getCode() == 200 && response.getData() != null) { - JSONObject info = new JSONObject(); - if (response.getData() instanceof Map) { - Map dataMap = (Map) response.getData(); - info.putAll(dataMap); - } else { - info.set(IotHttpConstants.ResponseField.TOKEN, response.getData().toString()); - } - httpResponse.set(IotHttpConstants.ResponseField.INFO, info); - } - } else { - // 数据上报响应格式 - httpResponse.set(IotHttpConstants.ResponseField.CODE, response.getCode()); - httpResponse.set(IotHttpConstants.ResponseField.MESSAGE, response.getMessage()); - - if (response.getCode() == 200) { - JSONObject info = new JSONObject(); - info.set(IotHttpConstants.ResponseField.MESSAGE_ID, response.getId()); - httpResponse.set(IotHttpConstants.ResponseField.INFO, info); - } - } - - String json = httpResponse.toString(); - return json.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - log.error(IotLogConstants.Http.FORMAT_RESPONSE_FAILED, e); - return new byte[0]; - } - } - - @Override - public boolean canHandle(String topic) { - // 支持认证路径和主题路径 - return topic != null && (AUTH_PATH.equals(topic) || topic.startsWith(TOPIC_PATH_PREFIX)); - } - - /** - * 从设备标识中解析产品Key和设备名称 - * - * @param deviceKey 设备标识,格式:productKey/deviceName - * @return 包含产品Key和设备名称的数组,[0]为产品Key,[1]为设备名称 - */ - public static String[] parseDeviceKey(String deviceKey) { - if (StrUtil.isBlank(deviceKey)) { - return null; - } - - String[] parts = deviceKey.split("/"); - if (parts.length != 2) { - return null; - } - - return new String[]{parts[0], parts[1]}; - } - - /** - * 构建设备标识 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return 设备标识,格式:productKey/deviceName - */ - public static String buildDeviceKey(String productKey, String deviceName) { - if (StrUtil.isBlank(productKey) || StrUtil.isBlank(deviceName)) { - return null; - } - return productKey + "/" + deviceName; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParser.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParser.java deleted file mode 100644 index 3c31a72ed7..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParser.java +++ /dev/null @@ -1,87 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.message.impl; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser; -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage; -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse; -import cn.iocoder.yudao.module.iot.protocol.util.IotTopicUtils; -import lombok.extern.slf4j.Slf4j; - -import java.nio.charset.StandardCharsets; -import java.util.Map; - -/** - * IoT MQTT 协议消息解析器实现 - *

- * 基于 MQTT 协议规范实现的消息解析器,支持设备属性、事件、服务调用等标准功能 - * - * @author haohao - */ -@Slf4j -public class IotMqttMessageParser implements IotMessageParser { - - @Override - public IotMqttMessage parse(String topic, byte[] payload) { - if (payload == null || payload.length == 0) { - log.warn("[MQTT] 收到空消息内容, topic={}", topic); - return null; - } - - try { - String message = new String(payload, StandardCharsets.UTF_8); - if (!JSONUtil.isTypeJSON(message)) { - log.warn("[MQTT] 收到非JSON格式消息, topic={}, message={}", topic, message); - return null; - } - - JSONObject json = JSONUtil.parseObj(message); - String id = json.getStr("id"); - String method = json.getStr("method"); - - if (StrUtil.isBlank(method)) { - // 尝试从 topic 中解析方法 - method = IotTopicUtils.parseMethodFromTopic(topic); - if (StrUtil.isBlank(method)) { - log.warn("[MQTT] 无法确定消息方法, topic={}, message={}", topic, message); - return null; - } - } - - @SuppressWarnings("unchecked") - Map params = (Map) json.getObj("params", Map.class); - return IotMqttMessage.builder() - .id(id) - .method(method) - .version(json.getStr("version", "1.0")) - .params(params) - .build(); - } catch (Exception e) { - log.error("[MQTT] 解析消息失败, topic={}", topic, e); - return null; - } - } - - @Override - public byte[] formatResponse(IotStandardResponse response) { - try { - String json = JsonUtils.toJsonString(response); - return json.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - log.error("[MQTT] 格式化响应失败", e); - return new byte[0]; - } - } - - @Override - public boolean canHandle(String topic) { - // MQTT 协议支持更多主题格式 - return topic != null && ( - topic.startsWith("/sys/") || // 兼容现有系统主题 - topic.startsWith("/mqtt/") || // 新的通用 MQTT 主题 - topic.startsWith("/device/") // 设备主题 - ); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtils.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtils.java deleted file mode 100644 index 4e0aeb851c..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtils.java +++ /dev/null @@ -1,279 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.util; - -import cn.hutool.core.util.StrUtil; - -/** - * IoT HTTP 协议主题工具类 - *

- * 参考阿里云IoT平台HTTPS协议标准,支持以下路径格式: - * 1. 设备认证:/auth - * 2. 数据上报:/topic/${actualTopic} - *

- * 其中 actualTopic 遵循MQTT主题规范,例如: - * - /sys/{productKey}/{deviceName}/thing/service/property/set - * - /{productKey}/{deviceName}/user/get - * - * @author haohao - */ -public class IotHttpTopicUtils { - - /** - * 设备认证路径 - */ - public static final String AUTH_PATH = "/auth"; - - /** - * 数据上报路径前缀 - */ - public static final String TOPIC_PATH_PREFIX = "/topic"; - - /** - * 系统主题前缀 - */ - public static final String SYS_TOPIC_PREFIX = "/sys"; - - /** - * 构建设备认证路径 - * - * @return 认证路径 - */ - public static String buildAuthPath() { - return AUTH_PATH; - } - - /** - * 构建数据上报路径 - * - * @param actualTopic 实际的MQTT主题 - * @return HTTP数据上报路径 - */ - public static String buildTopicPath(String actualTopic) { - if (StrUtil.isBlank(actualTopic)) { - return null; - } - return TOPIC_PATH_PREFIX + actualTopic; - } - - /** - * 构建系统属性设置路径 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return HTTP路径 - */ - public static String buildPropertySetPath(String productKey, String deviceName) { - if (StrUtil.hasBlank(productKey, deviceName)) { - return null; - } - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/service/property/set"; - return buildTopicPath(actualTopic); - } - - /** - * 构建系统属性获取路径 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return HTTP路径 - */ - public static String buildPropertyGetPath(String productKey, String deviceName) { - if (StrUtil.hasBlank(productKey, deviceName)) { - return null; - } - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/service/property/get"; - return buildTopicPath(actualTopic); - } - - /** - * 构建系统属性上报路径 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return HTTP路径 - */ - public static String buildPropertyPostPath(String productKey, String deviceName) { - if (StrUtil.hasBlank(productKey, deviceName)) { - return null; - } - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/event/property/post"; - return buildTopicPath(actualTopic); - } - - /** - * 构建系统事件上报路径 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @param eventIdentifier 事件标识符 - * @return HTTP路径 - */ - public static String buildEventPostPath(String productKey, String deviceName, String eventIdentifier) { - if (StrUtil.hasBlank(productKey, deviceName, eventIdentifier)) { - return null; - } - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/event/" + eventIdentifier - + "/post"; - return buildTopicPath(actualTopic); - } - - /** - * 构建系统服务调用路径 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @param serviceIdentifier 服务标识符 - * @return HTTP路径 - */ - public static String buildServiceInvokePath(String productKey, String deviceName, String serviceIdentifier) { - if (StrUtil.hasBlank(productKey, deviceName, serviceIdentifier)) { - return null; - } - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/service/" - + serviceIdentifier; - return buildTopicPath(actualTopic); - } - - /** - * 构建自定义主题路径 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @param customPath 自定义路径 - * @return HTTP路径 - */ - public static String buildCustomTopicPath(String productKey, String deviceName, String customPath) { - if (StrUtil.hasBlank(productKey, deviceName, customPath)) { - return null; - } - String actualTopic = "/" + productKey + "/" + deviceName + "/" + customPath; - return buildTopicPath(actualTopic); - } - - /** - * 从HTTP路径中提取实际主题 - * - * @param httpPath HTTP路径,格式:/topic/${actualTopic} - * @return 实际主题,如果解析失败返回null - */ - public static String extractActualTopic(String httpPath) { - if (StrUtil.isBlank(httpPath) || !httpPath.startsWith(TOPIC_PATH_PREFIX)) { - return null; - } - return httpPath.substring(TOPIC_PATH_PREFIX.length()); // 直接移除/topic前缀 - } - - /** - * 从主题中解析产品Key - * - * @param topic 主题,支持系统主题和自定义主题 - * @return 产品Key,如果无法解析则返回null - */ - public static String parseProductKeyFromTopic(String topic) { - if (StrUtil.isBlank(topic)) { - return null; - } - - String[] parts = topic.split("/"); - - // 系统主题格式:/sys/{productKey}/{deviceName}/... - if (parts.length >= 4 && "sys".equals(parts[1])) { - return parts[2]; - } - - // 自定义主题格式:/{productKey}/{deviceName}/... - // 确保不是不完整的系统主题格式 - if (parts.length >= 3 && StrUtil.isNotBlank(parts[1]) && !"sys".equals(parts[1])) { - return parts[1]; - } - - return null; - } - - /** - * 从主题中解析设备名称 - * - * @param topic 主题,支持系统主题和自定义主题 - * @return 设备名称,如果无法解析则返回null - */ - public static String parseDeviceNameFromTopic(String topic) { - if (StrUtil.isBlank(topic)) { - return null; - } - - String[] parts = topic.split("/"); - - // 系统主题格式:/sys/{productKey}/{deviceName}/... - if (parts.length >= 4 && "sys".equals(parts[1])) { - return parts[3]; - } - - // 自定义主题格式:/{productKey}/{deviceName}/... - // 确保不是不完整的系统主题格式 - if (parts.length >= 3 && StrUtil.isNotBlank(parts[2]) && !"sys".equals(parts[1])) { - return parts[2]; - } - - return null; - } - - /** - * 检查是否为认证路径 - * - * @param path 路径 - * @return 如果是认证路径返回true,否则返回false - */ - public static boolean isAuthPath(String path) { - return AUTH_PATH.equals(path); - } - - /** - * 检查是否为数据上报路径 - * - * @param path 路径 - * @return 如果是数据上报路径返回true,否则返回false - */ - public static boolean isTopicPath(String path) { - return path != null && path.startsWith(TOPIC_PATH_PREFIX); - } - - /** - * 检查是否为有效的HTTP路径 - * - * @param path 路径 - * @return 如果是有效的HTTP路径返回true,否则返回false - */ - public static boolean isValidHttpPath(String path) { - return isAuthPath(path) || isTopicPath(path); - } - - /** - * 检查是否为系统主题 - * - * @param topic 主题 - * @return 如果是系统主题返回true,否则返回false - */ - public static boolean isSystemTopic(String topic) { - return topic != null && topic.startsWith(SYS_TOPIC_PREFIX); - } - - /** - * 构建响应主题路径 - * - * @param requestPath 请求路径 - * @return 响应路径,如果无法构建返回null - */ - public static String buildReplyPath(String requestPath) { - String actualTopic = extractActualTopic(requestPath); - if (actualTopic == null) { - return null; - } - - // 为系统主题添加_reply后缀 - if (isSystemTopic(actualTopic)) { - String replyTopic = actualTopic + "_reply"; - return buildTopicPath(replyTopic); - } - - return null; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicParser.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicParser.java deleted file mode 100644 index 05873d2bdb..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicParser.java +++ /dev/null @@ -1,237 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.util; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.protocol.constants.IotTopicConstants; -import cn.iocoder.yudao.module.iot.protocol.enums.IotMessageDirectionEnum; -import cn.iocoder.yudao.module.iot.protocol.enums.IotMessageTypeEnum; -import lombok.Data; - -/** - * IoT 主题解析器 - *

- * 用于解析各种格式的 IoT 主题,提取其中的关键信息 - * - * @author haohao - */ -public class IotTopicParser { - - /** - * 主题解析结果 - */ - @Data - public static class TopicInfo { - /** - * 产品Key - */ - private String productKey; - - /** - * 设备名称 - */ - private String deviceName; - - /** - * 消息类型 - */ - private IotMessageTypeEnum messageType; - - /** - * 消息方向 - */ - private IotMessageDirectionEnum direction; - - /** - * 服务标识符(仅服务调用时有效) - */ - private String serviceIdentifier; - - /** - * 事件标识符(仅事件上报时有效) - */ - private String eventIdentifier; - - /** - * 是否为响应主题 - */ - private boolean isReply; - - /** - * 原始主题 - */ - private String originalTopic; - } - - /** - * 解析主题 - * - * @param topic 主题字符串 - * @return 解析结果,如果解析失败返回 null - */ - public static TopicInfo parse(String topic) { - if (StrUtil.isBlank(topic)) { - return null; - } - - TopicInfo info = new TopicInfo(); - info.setOriginalTopic(topic); - - // 检查是否为响应主题 - boolean isReply = topic.endsWith(IotTopicConstants.REPLY_SUFFIX); - info.setReply(isReply); - - // 移除响应后缀,便于后续解析 - String normalizedTopic = isReply ? topic.substring(0, topic.length() - IotTopicConstants.REPLY_SUFFIX.length()) - : topic; - - // 解析系统主题 - if (normalizedTopic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) { - return parseSystemTopic(info, normalizedTopic); - } - - // 解析自定义主题 - return parseCustomTopic(info, normalizedTopic); - } - - /** - * 解析系统主题 - * 格式:/sys/{productKey}/{deviceName}/thing/service/{identifier} - * 或:/sys/{productKey}/{deviceName}/thing/event/{identifier}/post - */ - private static TopicInfo parseSystemTopic(TopicInfo info, String topic) { - String[] parts = topic.split("/"); - if (parts.length < 6) { - return null; - } - - // 解析产品Key和设备名称 - info.setProductKey(parts[2]); - info.setDeviceName(parts[3]); - - // 判断消息方向:包含 /post 通常是上行,其他是下行 - info.setDirection(topic.contains("/post") || topic.contains("/reply") ? IotMessageDirectionEnum.UPSTREAM - : IotMessageDirectionEnum.DOWNSTREAM); - - // 解析具体的消息类型 - if (topic.contains("/thing/service/")) { - return parseServiceTopic(info, topic, parts); - } else if (topic.contains("/thing/event/")) { - return parseEventTopic(info, topic, parts); - } - - return null; - } - - /** - * 解析服务相关主题 - */ - private static TopicInfo parseServiceTopic(TopicInfo info, String topic, String[] parts) { - // 查找 service 关键字的位置 - int serviceIndex = -1; - for (int i = 0; i < parts.length; i++) { - if ("service".equals(parts[i])) { - serviceIndex = i; - break; - } - } - - if (serviceIndex == -1 || serviceIndex + 1 >= parts.length) { - return null; - } - - String serviceType = parts[serviceIndex + 1]; - - // 根据服务类型确定消息类型 - switch (serviceType) { - case "property": - if (serviceIndex + 2 < parts.length) { - String operation = parts[serviceIndex + 2]; - if ("set".equals(operation)) { - info.setMessageType(IotMessageTypeEnum.PROPERTY_SET); - } else if ("get".equals(operation)) { - info.setMessageType(IotMessageTypeEnum.PROPERTY_GET); - } - } - break; - case "config": - if (serviceIndex + 2 < parts.length && "set".equals(parts[serviceIndex + 2])) { - info.setMessageType(IotMessageTypeEnum.CONFIG_SET); - } - break; - case "ota": - if (serviceIndex + 2 < parts.length && "upgrade".equals(parts[serviceIndex + 2])) { - info.setMessageType(IotMessageTypeEnum.OTA_UPGRADE); - } - break; - default: - // 自定义服务 - info.setMessageType(IotMessageTypeEnum.SERVICE_INVOKE); - info.setServiceIdentifier(serviceType); - break; - } - - return info; - } - - /** - * 解析事件相关主题 - */ - private static TopicInfo parseEventTopic(TopicInfo info, String topic, String[] parts) { - // 查找 event 关键字的位置 - int eventIndex = -1; - for (int i = 0; i < parts.length; i++) { - if ("event".equals(parts[i])) { - eventIndex = i; - break; - } - } - - if (eventIndex == -1 || eventIndex + 1 >= parts.length) { - return null; - } - - String eventType = parts[eventIndex + 1]; - - if ("property".equals(eventType) && eventIndex + 2 < parts.length && "post".equals(parts[eventIndex + 2])) { - info.setMessageType(IotMessageTypeEnum.PROPERTY_POST); - } else { - // 自定义事件 - info.setMessageType(IotMessageTypeEnum.EVENT_POST); - info.setEventIdentifier(eventType); - } - - return info; - } - - /** - * 解析自定义主题 - * 这里可以根据实际需求扩展自定义主题的解析逻辑 - */ - private static TopicInfo parseCustomTopic(TopicInfo info, String topic) { - // TODO: 根据业务需要实现自定义主题解析逻辑 - return info; - } - - /** - * 检查主题是否为有效的系统主题 - * - * @param topic 主题 - * @return 如果是有效的系统主题返回 true,否则返回 false - */ - public static boolean isValidSystemTopic(String topic) { - TopicInfo info = parse(topic); - return info != null && - StrUtil.isNotBlank(info.getProductKey()) && - StrUtil.isNotBlank(info.getDeviceName()) && - info.getMessageType() != null; - } - - /** - * 检查主题是否为响应主题 - * - * @param topic 主题 - * @return 如果是响应主题返回 true,否则返回 false - */ - public static boolean isReplyTopic(String topic) { - return topic != null && topic.endsWith(IotTopicConstants.REPLY_SUFFIX); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtils.java b/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtils.java deleted file mode 100644 index 6bd447e5a9..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtils.java +++ /dev/null @@ -1,184 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.util; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.protocol.constants.IotTopicConstants; - -/** - * IoT 主题工具类 - *

- * 用于构建和解析设备主题 - * - * @author haohao - */ -public class IotTopicUtils { - - /** - * 构建设备服务调用主题 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @param serviceIdentifier 服务标识符 - * @return 完整的主题路径 - */ - public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) { - return buildDeviceBaseTopic(productKey, deviceName) + - IotTopicConstants.SERVICE_TOPIC_PREFIX + serviceIdentifier; - } - - /** - * 构建设备属性设置主题 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return 完整的主题路径 - */ - public static String buildPropertySetTopic(String productKey, String deviceName) { - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.PROPERTY_SET_TOPIC; - } - - /** - * 构建设备属性获取主题 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return 完整的主题路径 - */ - public static String buildPropertyGetTopic(String productKey, String deviceName) { - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.PROPERTY_GET_TOPIC; - } - - /** - * 构建设备配置设置主题 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return 完整的主题路径 - */ - public static String buildConfigSetTopic(String productKey, String deviceName) { - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.CONFIG_SET_TOPIC; - } - - /** - * 构建设备 OTA 升级主题 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return 完整的主题路径 - */ - public static String buildOtaUpgradeTopic(String productKey, String deviceName) { - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.OTA_UPGRADE_TOPIC; - } - - /** - * 构建设备属性上报主题 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return 完整的主题路径 - */ - public static String buildPropertyPostTopic(String productKey, String deviceName) { - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.PROPERTY_POST_TOPIC; - } - - /** - * 构建设备事件上报主题 - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @param eventIdentifier 事件标识符 - * @return 完整的主题路径 - */ - public static String buildEventPostTopic(String productKey, String deviceName, String eventIdentifier) { - return buildDeviceBaseTopic(productKey, deviceName) + - IotTopicConstants.EVENT_POST_TOPIC_PREFIX + eventIdentifier + IotTopicConstants.EVENT_POST_TOPIC_SUFFIX; - } - - /** - * 获取响应主题 - * - * @param requestTopic 请求主题 - * @return 响应主题 - */ - public static String getReplyTopic(String requestTopic) { - return requestTopic + IotTopicConstants.REPLY_SUFFIX; - } - - /** - * 构建设备基础主题 - * 格式: /sys/${productKey}/${deviceName} - * - * @param productKey 产品Key - * @param deviceName 设备名称 - * @return 设备基础主题 - */ - public static String buildDeviceBaseTopic(String productKey, String deviceName) { - return IotTopicConstants.SYS_TOPIC_PREFIX + productKey + "/" + deviceName; - } - - /** - * 从主题中解析产品Key - * 格式: /sys/${productKey}/${deviceName}/... - * - * @param topic 主题 - * @return 产品Key,如果无法解析则返回null - */ - public static String parseProductKeyFromTopic(String topic) { - if (StrUtil.isBlank(topic) || !topic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) { - return null; - } - - String[] parts = topic.split("/"); - if (parts.length < 4) { - return null; - } - - return parts[2]; - } - - /** - * 从主题中解析设备名称 - * 格式: /sys/${productKey}/${deviceName}/... - * - * @param topic 主题 - * @return 设备名称,如果无法解析则返回null - */ - public static String parseDeviceNameFromTopic(String topic) { - if (StrUtil.isBlank(topic) || !topic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) { - return null; - } - - String[] parts = topic.split("/"); - if (parts.length < 4) { - return null; - } - - return parts[3]; - } - - /** - * 从主题中解析方法名 - * 例如:从 /sys/pk/dn/thing/service/property/set 解析出 property.set - * - * @param topic 主题 - * @return 方法名,如果无法解析则返回null - */ - public static String parseMethodFromTopic(String topic) { - if (StrUtil.isBlank(topic) || !topic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) { - return null; - } - - // 服务调用主题 - if (topic.contains("/thing/service/")) { - String servicePart = topic.substring(topic.indexOf("/thing/service/") + "/thing/service/".length()); - return servicePart.replace("/", "."); - } - - // 事件上报主题 - if (topic.contains("/thing/event/")) { - String eventPart = topic.substring(topic.indexOf("/thing/event/") + "/thing/event/".length()); - return "event." + eventPart.replace("/", "."); - } - - return null; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/yudao-module-iot/yudao-module-iot-protocol/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted file mode 100644 index 2b1cf8d5aa..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ /dev/null @@ -1 +0,0 @@ -cn.iocoder.yudao.module.iot.protocol.config.IotProtocolAutoConfiguration \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfigurationTest.java b/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfigurationTest.java deleted file mode 100644 index 31b6c63acb..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfigurationTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.config; - -import cn.iocoder.yudao.module.iot.protocol.convert.IotProtocolConverter; -import cn.iocoder.yudao.module.iot.protocol.enums.IotProtocolTypeEnum; -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser; -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotHttpMessageParser; -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotMqttMessageParser; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link IotProtocolAutoConfiguration} 单元测试 - * - * @author haohao - */ -class IotProtocolAutoConfigurationTest { - - private IotProtocolAutoConfiguration configuration; - - @BeforeEach - void setUp() { - configuration = new IotProtocolAutoConfiguration(); - } - - @Test - void testIotMqttMessageParser() { - // 测试 MQTT 协议解析器 Bean 创建 - IotMessageParser parser = configuration.iotMqttMessageParser(); - - assertNotNull(parser); - assertInstanceOf(IotMqttMessageParser.class, parser); - } - - @Test - void testIotHttpMessageParser() { - // 测试 HTTP 协议解析器 Bean 创建 - IotMessageParser parser = configuration.iotHttpMessageParser(); - - assertNotNull(parser); - assertInstanceOf(IotHttpMessageParser.class, parser); - } - - @Test - void testIotProtocolConverter() { - // 创建解析器实例 - IotMessageParser mqttParser = configuration.iotMqttMessageParser(); - IotMessageParser httpParser = configuration.iotHttpMessageParser(); - - // 测试协议转换器 Bean 创建 - IotProtocolConverter converter = configuration.iotProtocolConverter(mqttParser, httpParser); - - assertNotNull(converter); - - // 验证支持的协议 - assertTrue(converter.supportsProtocol(IotProtocolTypeEnum.MQTT.getCode())); - assertTrue(converter.supportsProtocol(IotProtocolTypeEnum.HTTP.getCode())); - - // 验证支持的协议数量 - String[] supportedProtocols = converter.getSupportedProtocols(); - assertEquals(2, supportedProtocols.length); - } - - @Test - void testBeanNameConstants() { - // 测试 Bean 名称常量定义 - assertEquals("iotMqttMessageParser", IotProtocolAutoConfiguration.IOT_MQTT_MESSAGE_PARSER_BEAN_NAME); - assertEquals("iotHttpMessageParser", IotProtocolAutoConfiguration.IOT_HTTP_MESSAGE_PARSER_BEAN_NAME); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/example/AliyunHttpProtocolExample.java b/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/example/AliyunHttpProtocolExample.java deleted file mode 100644 index a1c1dae562..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/example/AliyunHttpProtocolExample.java +++ /dev/null @@ -1,166 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.example; - -import cn.hutool.json.JSONObject; -import cn.iocoder.yudao.module.iot.protocol.util.IotHttpTopicUtils; - -/** - * 阿里云IoT平台HTTPS协议示例 - *

- * 参考阿里云IoT平台HTTPS连接通信标准,演示设备认证和数据上报的完整流程 - * - * @author haohao - */ -public class AliyunHttpProtocolExample { - - public static void main(String[] args) { - System.out.println("=== 阿里云IoT平台HTTPS协议演示 ===\n"); - - // 演示设备认证 - demonstrateDeviceAuth(); - - // 演示数据上报 - demonstrateDataUpload(); - - // 演示路径构建 - demonstratePathBuilding(); - } - - /** - * 演示设备认证流程 - */ - private static void demonstrateDeviceAuth() { - System.out.println("1. 设备认证流程:"); - System.out.println("认证路径: " + IotHttpTopicUtils.buildAuthPath()); - - // 构建认证请求消息 - JSONObject authRequest = new JSONObject(); - authRequest.set("productKey", "a1GFjLP****"); - authRequest.set("deviceName", "device123"); - authRequest.set("clientId", "device123_001"); - authRequest.set("timestamp", String.valueOf(System.currentTimeMillis())); - authRequest.set("sign", "4870141D4067227128CBB4377906C3731CAC221C"); - authRequest.set("signmethod", "hmacsha1"); - authRequest.set("version", "default"); - - System.out.println("认证请求消息:"); - System.out.println(authRequest.toString()); - - // 模拟认证响应 - JSONObject authResponse = new JSONObject(); - authResponse.set("code", 0); - authResponse.set("message", "success"); - - JSONObject info = new JSONObject(); - info.set("token", "6944e5bfb92e4d4ea3918d1eda39****"); - authResponse.set("info", info); - - System.out.println("认证响应:"); - System.out.println(authResponse.toString()); - System.out.println(); - } - - /** - * 演示数据上报流程 - */ - private static void demonstrateDataUpload() { - System.out.println("2. 数据上报流程:"); - - String productKey = "a1GFjLP****"; - String deviceName = "device123"; - - // 属性上报 - String propertyPostPath = IotHttpTopicUtils.buildPropertyPostPath(productKey, deviceName); - System.out.println("属性上报路径: " + propertyPostPath); - - // Alink格式的属性上报消息 - JSONObject propertyMessage = new JSONObject(); - propertyMessage.set("id", "123456"); - propertyMessage.set("version", "1.0"); - propertyMessage.set("method", "thing.event.property.post"); - - JSONObject propertyParams = new JSONObject(); - JSONObject properties = new JSONObject(); - properties.set("temperature", 25.6); - properties.set("humidity", 60.3); - propertyParams.set("properties", properties); - propertyMessage.set("params", propertyParams); - - System.out.println("属性上报消息:"); - System.out.println(propertyMessage.toString()); - - // 事件上报 - String eventPostPath = IotHttpTopicUtils.buildEventPostPath(productKey, deviceName, "temperatureAlert"); - System.out.println("\n事件上报路径: " + eventPostPath); - - JSONObject eventMessage = new JSONObject(); - eventMessage.set("id", "123457"); - eventMessage.set("version", "1.0"); - eventMessage.set("method", "thing.event.temperatureAlert.post"); - - JSONObject eventParams = new JSONObject(); - eventParams.set("value", new JSONObject().set("alertLevel", "high").set("currentTemp", 45.2)); - eventParams.set("time", System.currentTimeMillis()); - eventMessage.set("params", eventParams); - - System.out.println("事件上报消息:"); - System.out.println(eventMessage.toString()); - - // 模拟数据上报响应 - JSONObject uploadResponse = new JSONObject(); - uploadResponse.set("code", 0); - uploadResponse.set("message", "success"); - - JSONObject responseInfo = new JSONObject(); - responseInfo.set("messageId", 892687470447040L); - uploadResponse.set("info", responseInfo); - - System.out.println("\n数据上报响应:"); - System.out.println(uploadResponse.toString()); - System.out.println(); - } - - /** - * 演示路径构建功能 - */ - private static void demonstratePathBuilding() { - System.out.println("3. 路径构建功能:"); - - String productKey = "smartProduct"; - String deviceName = "sensor001"; - - // 系统主题路径 - System.out.println("系统主题路径:"); - System.out.println(" 属性设置: " + IotHttpTopicUtils.buildPropertySetPath(productKey, deviceName)); - System.out.println(" 属性获取: " + IotHttpTopicUtils.buildPropertyGetPath(productKey, deviceName)); - System.out.println(" 属性上报: " + IotHttpTopicUtils.buildPropertyPostPath(productKey, deviceName)); - System.out.println(" 事件上报: " + IotHttpTopicUtils.buildEventPostPath(productKey, deviceName, "alarm")); - System.out.println(" 服务调用: " + IotHttpTopicUtils.buildServiceInvokePath(productKey, deviceName, "reboot")); - - // 自定义主题路径 - System.out.println("\n自定义主题路径:"); - System.out.println(" 用户主题: " + IotHttpTopicUtils.buildCustomTopicPath(productKey, deviceName, "user/get")); - - // 响应路径 - String requestPath = IotHttpTopicUtils.buildPropertySetPath(productKey, deviceName); - String replyPath = IotHttpTopicUtils.buildReplyPath(requestPath); - System.out.println("\n响应路径:"); - System.out.println(" 请求路径: " + requestPath); - System.out.println(" 响应路径: " + replyPath); - - // 路径解析 - System.out.println("\n路径解析:"); - String testPath = "/topic/sys/smartProduct/sensor001/thing/service/property/set"; - String actualTopic = IotHttpTopicUtils.extractActualTopic(testPath); - System.out.println(" HTTP路径: " + testPath); - System.out.println(" 实际主题: " + actualTopic); - System.out.println(" 产品Key: " + IotHttpTopicUtils.parseProductKeyFromTopic(actualTopic)); - System.out.println(" 设备名称: " + IotHttpTopicUtils.parseDeviceNameFromTopic(actualTopic)); - System.out.println(" 是否为系统主题: " + IotHttpTopicUtils.isSystemTopic(actualTopic)); - - // 路径类型检查 - System.out.println("\n路径类型检查:"); - System.out.println(" 认证路径检查: " + IotHttpTopicUtils.isAuthPath("/auth")); - System.out.println(" 数据路径检查: " + IotHttpTopicUtils.isTopicPath("/topic/test")); - System.out.println(" 有效路径检查: " + IotHttpTopicUtils.isValidHttpPath("/topic/sys/test/device/property")); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParserTest.java b/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParserTest.java deleted file mode 100644 index 5fb6f5ed3b..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParserTest.java +++ /dev/null @@ -1,259 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.message.impl; - -import cn.hutool.json.JSONObject; -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage; -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link IotHttpMessageParser} 单元测试 - *

- * 测试阿里云IoT平台HTTPS协议标准的消息解析功能 - * - * @author haohao - */ -class IotHttpMessageParserTest { - - private IotHttpMessageParser parser; - - @BeforeEach - void setUp() { - parser = new IotHttpMessageParser(); - } - - @Test - void testCanHandle() { - // 测试能处理的路径 - assertTrue(parser.canHandle("/auth")); - assertTrue(parser.canHandle("/topic/sys/test/device1/thing/service/property/set")); - assertTrue(parser.canHandle("/topic/test/device1/user/get")); - - // 测试不能处理的路径 - assertFalse(parser.canHandle("/sys/test/device1/thing/service/property/set")); - assertFalse(parser.canHandle("/unknown/path")); - assertFalse(parser.canHandle(null)); - assertFalse(parser.canHandle("")); - } - - @Test - void testParseAuthMessage() { - // 构建认证消息 - JSONObject authMessage = new JSONObject(); - authMessage.set("productKey", "a1GFjLP****"); - authMessage.set("deviceName", "device123"); - authMessage.set("clientId", "device123_001"); - authMessage.set("timestamp", "1501668289957"); - authMessage.set("sign", "4870141D4067227128CBB4377906C3731CAC221C"); - authMessage.set("signmethod", "hmacsha1"); - authMessage.set("version", "default"); - - String topic = "/auth"; - byte[] payload = authMessage.toString().getBytes(StandardCharsets.UTF_8); - - // 解析消息 - IotMqttMessage result = parser.parse(topic, payload); - - // 验证结果 - assertNotNull(result); - assertNotNull(result.getId()); - assertEquals("device.auth", result.getMethod()); - assertEquals("default", result.getVersion()); - assertNotNull(result.getParams()); - - Map params = result.getParams(); - assertEquals("a1GFjLP****", params.get("productKey")); - assertEquals("device123", params.get("deviceName")); - assertEquals("device123_001", params.get("clientId")); - assertEquals("1501668289957", params.get("timestamp")); - assertEquals("4870141D4067227128CBB4377906C3731CAC221C", params.get("sign")); - assertEquals("hmacsha1", params.get("signmethod")); - } - - @Test - void testParseAuthMessageWithMissingFields() { - // 构建缺少必需字段的认证消息 - JSONObject authMessage = new JSONObject(); - authMessage.set("productKey", "a1GFjLP****"); - authMessage.set("deviceName", "device123"); - // 缺少 clientId 和 sign - - String topic = "/auth"; - byte[] payload = authMessage.toString().getBytes(StandardCharsets.UTF_8); - - // 解析消息 - IotMqttMessage result = parser.parse(topic, payload); - - // 验证结果 - assertNull(result); - } - - @Test - void testParseJsonDataMessage() { - // 构建JSON格式的数据消息 - JSONObject dataMessage = new JSONObject(); - dataMessage.set("id", "123456"); - dataMessage.set("version", "1.0"); - dataMessage.set("method", "thing.event.property.post"); - - JSONObject params = new JSONObject(); - JSONObject properties = new JSONObject(); - properties.set("temperature", 25.6); - properties.set("humidity", 60.3); - params.set("properties", properties); - dataMessage.set("params", params); - - String topic = "/topic/sys/a1GFjLP****/device123/thing/event/property/post"; - byte[] payload = dataMessage.toString().getBytes(StandardCharsets.UTF_8); - - // 解析消息 - IotMqttMessage result = parser.parse(topic, payload); - - // 验证结果 - assertNotNull(result); - assertEquals("123456", result.getId()); - assertEquals("thing.event.property.post", result.getMethod()); - assertEquals("1.0", result.getVersion()); - assertNotNull(result.getParams()); - assertNotNull(result.getParams().get("properties")); - } - - @Test - void testParseRawDataMessage() { - // 原始数据消息 - String rawData = "temperature:25.6,humidity:60.3"; - String topic = "/topic/sys/a1GFjLP****/device123/thing/event/property/post"; - byte[] payload = rawData.getBytes(StandardCharsets.UTF_8); - - // 解析消息 - IotMqttMessage result = parser.parse(topic, payload); - - // 验证结果 - assertNotNull(result); - assertNotNull(result.getId()); - assertEquals("thing.event.property.post", result.getMethod()); - assertEquals("1.0", result.getVersion()); - assertNotNull(result.getParams()); - assertEquals(rawData, result.getParams().get("data")); - } - - @Test - void testInferMethodFromTopic() { - // 测试系统主题方法推断 - testInferMethod("/sys/test/device/thing/service/property/set", "thing.service.property.set"); - testInferMethod("/sys/test/device/thing/service/property/get", "thing.service.property.get"); - testInferMethod("/sys/test/device/thing/event/property/post", "thing.event.property.post"); - testInferMethod("/sys/test/device/thing/event/alarm/post", "thing.event.alarm.post"); - testInferMethod("/sys/test/device/thing/service/reboot", "thing.service.reboot"); - - // 测试自定义主题 - testInferMethod("/test/device/user/get", "custom.message"); - } - - private void testInferMethod(String actualTopic, String expectedMethod) { - String topic = "/topic" + actualTopic; - String rawData = "test data"; - byte[] payload = rawData.getBytes(StandardCharsets.UTF_8); - - IotMqttMessage result = parser.parse(topic, payload); - assertNotNull(result); - assertEquals(expectedMethod, result.getMethod()); - } - - @Test - void testFormatAuthResponse() { - // 创建认证成功响应 - Map data = new HashMap<>(); - data.put("token", "6944e5bfb92e4d4ea3918d1eda39****"); - - IotStandardResponse response = IotStandardResponse.success("auth123", "device.auth", data); - - // 格式化响应 - byte[] result = parser.formatResponse(response); - - // 验证结果 - assertNotNull(result); - assertTrue(result.length > 0); - - String responseStr = new String(result, StandardCharsets.UTF_8); - JSONObject responseJson = new JSONObject(responseStr); - - assertEquals(200, responseJson.getInt("code")); - assertEquals("success", responseJson.getStr("message")); - assertNotNull(responseJson.get("info")); - - JSONObject info = responseJson.getJSONObject("info"); - assertEquals("6944e5bfb92e4d4ea3918d1eda39****", info.getStr("token")); - } - - @Test - void testFormatDataResponse() { - // 创建数据上报响应 - IotStandardResponse response = IotStandardResponse.success("123456", "thing.event.property.post", null); - - // 格式化响应 - byte[] result = parser.formatResponse(response); - - // 验证结果 - assertNotNull(result); - assertTrue(result.length > 0); - - String responseStr = new String(result, StandardCharsets.UTF_8); - JSONObject responseJson = new JSONObject(responseStr); - - assertEquals(200, responseJson.getInt("code")); - assertEquals("success", responseJson.getStr("message")); - assertNotNull(responseJson.get("info")); - - JSONObject info = responseJson.getJSONObject("info"); - assertEquals("123456", info.getStr("messageId")); - } - - @Test - void testParseInvalidMessage() { - String topic = "/topic/sys/test/device/thing/service/property/set"; - - // 测试空消息 - assertNull(parser.parse(topic, null)); - assertNull(parser.parse(topic, new byte[0])); - - // 测试不支持的路径 - byte[] validPayload = "test data".getBytes(StandardCharsets.UTF_8); - assertNull(parser.parse("/unknown/path", validPayload)); - } - - @Test - void testParseDeviceKey() { - // 测试有效的设备标识 - String[] result1 = IotHttpMessageParser.parseDeviceKey("productKey/deviceName"); - assertNotNull(result1); - assertEquals(2, result1.length); - assertEquals("productKey", result1[0]); - assertEquals("deviceName", result1[1]); - - // 测试无效的设备标识 - assertNull(IotHttpMessageParser.parseDeviceKey(null)); - assertNull(IotHttpMessageParser.parseDeviceKey("")); - assertNull(IotHttpMessageParser.parseDeviceKey("invalid")); - assertNull(IotHttpMessageParser.parseDeviceKey("product/device/extra")); - } - - @Test - void testBuildDeviceKey() { - // 测试构建设备标识 - assertEquals("productKey/deviceName", - IotHttpMessageParser.buildDeviceKey("productKey", "deviceName")); - - // 测试无效参数 - assertNull(IotHttpMessageParser.buildDeviceKey(null, "deviceName")); - assertNull(IotHttpMessageParser.buildDeviceKey("productKey", null)); - assertNull(IotHttpMessageParser.buildDeviceKey("", "deviceName")); - assertNull(IotHttpMessageParser.buildDeviceKey("productKey", "")); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParserTest.java b/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParserTest.java deleted file mode 100644 index c25beaae7c..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParserTest.java +++ /dev/null @@ -1,190 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.message.impl; - -import cn.hutool.json.JSONObject; -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage; -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * IoT MQTT 消息解析器测试类 - * - * @author haohao - */ -class IotMqttMessageParserTest { - - private IotMqttMessageParser parser; - - @BeforeEach - void setUp() { - parser = new IotMqttMessageParser(); - } - - @Test - void testParseValidJsonMessage() { - // 构建有效的 JSON 消息 - JSONObject message = new JSONObject(); - message.set("id", "123456"); - message.set("version", "1.0"); - message.set("method", "thing.service.property.set"); - - Map params = new HashMap<>(); - params.put("temperature", 25.5); - params.put("humidity", 60.0); - message.set("params", params); - - String topic = "/sys/productKey/deviceName/thing/service/property/set"; - byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8); - - // 解析消息 - IotMqttMessage result = parser.parse(topic, payload); - - // 验证结果 - assertNotNull(result); - assertEquals("123456", result.getId()); - assertEquals("1.0", result.getVersion()); - assertEquals("thing.service.property.set", result.getMethod()); - assertNotNull(result.getParams()); - assertEquals(25.5, ((Number) result.getParams().get("temperature")).doubleValue()); - assertEquals(60.0, ((Number) result.getParams().get("humidity")).doubleValue()); - } - - @Test - void testParseMessageWithoutMethod() { - // 构建没有 method 字段的消息,应该从 topic 中解析 - JSONObject message = new JSONObject(); - message.set("id", "789012"); - message.set("version", "1.0"); - - Map params = new HashMap<>(); - params.put("voltage", 3.3); - message.set("params", params); - - String topic = "/sys/productKey/deviceName/thing/service/property/set"; - byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8); - - // 解析消息 - IotMqttMessage result = parser.parse(topic, payload); - - // 验证结果 - assertNotNull(result); - assertEquals("789012", result.getId()); - assertEquals("1.0", result.getVersion()); - assertNotNull(result.getMethod()); // 应该从 topic 中解析出方法 - assertNotNull(result.getParams()); - assertEquals(3.3, ((Number) result.getParams().get("voltage")).doubleValue()); - } - - @Test - void testParseInvalidJsonMessage() { - String topic = "/sys/productKey/deviceName/thing/service/property/set"; - byte[] payload = "invalid json".getBytes(StandardCharsets.UTF_8); - - // 解析消息 - IotMqttMessage result = parser.parse(topic, payload); - - // 验证结果 - assertNull(result); - } - - @Test - void testParseEmptyPayload() { - String topic = "/sys/productKey/deviceName/thing/service/property/set"; - - // 测试 null payload - IotMqttMessage result1 = parser.parse(topic, null); - assertNull(result1); - - // 测试空 payload - IotMqttMessage result2 = parser.parse(topic, new byte[0]); - assertNull(result2); - } - - @Test - void testFormatResponse() { - // 创建标准响应 - IotStandardResponse response = IotStandardResponse.success("123456", "property.set", null); - - // 格式化响应 - byte[] result = parser.formatResponse(response); - - // 验证结果 - assertNotNull(result); - assertTrue(result.length > 0); - - // 验证 JSON 格式 - String jsonString = new String(result, StandardCharsets.UTF_8); - assertTrue(jsonString.contains("123456")); - assertTrue(jsonString.contains("property.set")); - } - - @Test - void testCanHandle() { - // 测试支持的主题格式 - assertTrue(parser.canHandle("/sys/productKey/deviceName/thing/service/property/set")); - assertTrue(parser.canHandle("/mqtt/productKey/deviceName/property/set")); - assertTrue(parser.canHandle("/device/productKey/deviceName/data")); - - // 测试不支持的主题格式 - assertFalse(parser.canHandle("/http/device/productKey/deviceName/property/set")); - assertFalse(parser.canHandle("/unknown/topic")); - assertFalse(parser.canHandle(null)); - assertFalse(parser.canHandle("")); - } - - @Test - void testParseMqttTopicFormat() { - // 测试新的 MQTT 主题格式 - JSONObject message = new JSONObject(); - message.set("id", "mqtt001"); - message.set("version", "1.0"); - message.set("method", "device.property.report"); - - Map params = new HashMap<>(); - params.put("signal", 85); - message.set("params", params); - - String topic = "/mqtt/productKey/deviceName/property/report"; - byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8); - - // 解析消息 - IotMqttMessage result = parser.parse(topic, payload); - - // 验证结果 - assertNotNull(result); - assertEquals("mqtt001", result.getId()); - assertEquals("device.property.report", result.getMethod()); - assertEquals(85, ((Number) result.getParams().get("signal")).intValue()); - } - - @Test - void testParseDeviceTopicFormat() { - // 测试设备主题格式 - JSONObject message = new JSONObject(); - message.set("id", "device001"); - message.set("version", "1.0"); - message.set("method", "sensor.data"); - - Map params = new HashMap<>(); - params.put("timestamp", System.currentTimeMillis()); - message.set("params", params); - - String topic = "/device/productKey/deviceName/sensor/data"; - byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8); - - // 解析消息 - IotMqttMessage result = parser.parse(topic, payload); - - // 验证结果 - assertNotNull(result); - assertEquals("device001", result.getId()); - assertEquals("sensor.data", result.getMethod()); - assertNotNull(result.getParams().get("timestamp")); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtilsTest.java b/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtilsTest.java deleted file mode 100644 index 836bc8f95a..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtilsTest.java +++ /dev/null @@ -1,186 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.util; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link IotHttpTopicUtils} 单元测试 - * - * @author haohao - */ -class IotHttpTopicUtilsTest { - - @Test - void testBuildAuthPath() { - assertEquals("/auth", IotHttpTopicUtils.buildAuthPath()); - } - - @Test - void testBuildTopicPath() { - // 测试正常路径 - assertEquals("/topic/sys/test/device/thing/service/property/set", - IotHttpTopicUtils.buildTopicPath("/sys/test/device/thing/service/property/set")); - - // 测试空路径 - assertNull(IotHttpTopicUtils.buildTopicPath(null)); - assertNull(IotHttpTopicUtils.buildTopicPath("")); - } - - @Test - void testBuildPropertySetPath() { - String result = IotHttpTopicUtils.buildPropertySetPath("testProduct", "testDevice"); - assertEquals("/topic/sys/testProduct/testDevice/thing/service/property/set", result); - - // 测试无效参数 - assertNull(IotHttpTopicUtils.buildPropertySetPath(null, "testDevice")); - assertNull(IotHttpTopicUtils.buildPropertySetPath("testProduct", null)); - assertNull(IotHttpTopicUtils.buildPropertySetPath("", "testDevice")); - assertNull(IotHttpTopicUtils.buildPropertySetPath("testProduct", "")); - } - - @Test - void testBuildPropertyGetPath() { - String result = IotHttpTopicUtils.buildPropertyGetPath("testProduct", "testDevice"); - assertEquals("/topic/sys/testProduct/testDevice/thing/service/property/get", result); - } - - @Test - void testBuildPropertyPostPath() { - String result = IotHttpTopicUtils.buildPropertyPostPath("testProduct", "testDevice"); - assertEquals("/topic/sys/testProduct/testDevice/thing/event/property/post", result); - } - - @Test - void testBuildEventPostPath() { - String result = IotHttpTopicUtils.buildEventPostPath("testProduct", "testDevice", "alarm"); - assertEquals("/topic/sys/testProduct/testDevice/thing/event/alarm/post", result); - - // 测试无效参数 - assertNull(IotHttpTopicUtils.buildEventPostPath(null, "testDevice", "alarm")); - assertNull(IotHttpTopicUtils.buildEventPostPath("testProduct", null, "alarm")); - assertNull(IotHttpTopicUtils.buildEventPostPath("testProduct", "testDevice", null)); - } - - @Test - void testBuildServiceInvokePath() { - String result = IotHttpTopicUtils.buildServiceInvokePath("testProduct", "testDevice", "reboot"); - assertEquals("/topic/sys/testProduct/testDevice/thing/service/reboot", result); - - // 测试无效参数 - assertNull(IotHttpTopicUtils.buildServiceInvokePath(null, "testDevice", "reboot")); - assertNull(IotHttpTopicUtils.buildServiceInvokePath("testProduct", null, "reboot")); - assertNull(IotHttpTopicUtils.buildServiceInvokePath("testProduct", "testDevice", null)); - } - - @Test - void testBuildCustomTopicPath() { - String result = IotHttpTopicUtils.buildCustomTopicPath("testProduct", "testDevice", "user/get"); - assertEquals("/topic/testProduct/testDevice/user/get", result); - - // 测试无效参数 - assertNull(IotHttpTopicUtils.buildCustomTopicPath(null, "testDevice", "user/get")); - assertNull(IotHttpTopicUtils.buildCustomTopicPath("testProduct", null, "user/get")); - assertNull(IotHttpTopicUtils.buildCustomTopicPath("testProduct", "testDevice", null)); - } - - @Test - void testExtractActualTopic() { - // 测试正常提取 - String actualTopic = IotHttpTopicUtils - .extractActualTopic("/topic/sys/testProduct/testDevice/thing/service/property/set"); - assertEquals("/sys/testProduct/testDevice/thing/service/property/set", actualTopic); - - // 测试无效路径 - assertNull(IotHttpTopicUtils.extractActualTopic("/auth")); - assertNull(IotHttpTopicUtils.extractActualTopic("/unknown/path")); - assertNull(IotHttpTopicUtils.extractActualTopic(null)); - assertNull(IotHttpTopicUtils.extractActualTopic("")); - } - - @Test - void testParseProductKeyFromTopic() { - // 测试系统主题 - assertEquals("testProduct", - IotHttpTopicUtils.parseProductKeyFromTopic("/sys/testProduct/testDevice/thing/service/property/set")); - - // 测试自定义主题 - assertEquals("testProduct", IotHttpTopicUtils.parseProductKeyFromTopic("/testProduct/testDevice/user/get")); - - // 测试无效主题 - assertNull(IotHttpTopicUtils.parseProductKeyFromTopic("/sys")); - assertNull(IotHttpTopicUtils.parseProductKeyFromTopic("/single")); - assertNull(IotHttpTopicUtils.parseProductKeyFromTopic("")); - assertNull(IotHttpTopicUtils.parseProductKeyFromTopic(null)); - } - - @Test - void testParseDeviceNameFromTopic() { - // 测试系统主题 - assertEquals("testDevice", - IotHttpTopicUtils.parseDeviceNameFromTopic("/sys/testProduct/testDevice/thing/service/property/set")); - - // 测试自定义主题 - assertEquals("testDevice", IotHttpTopicUtils.parseDeviceNameFromTopic("/testProduct/testDevice/user/get")); - - // 测试无效主题 - assertNull(IotHttpTopicUtils.parseDeviceNameFromTopic("/sys/testProduct")); - assertNull(IotHttpTopicUtils.parseDeviceNameFromTopic("/testProduct")); - assertNull(IotHttpTopicUtils.parseDeviceNameFromTopic("")); - assertNull(IotHttpTopicUtils.parseDeviceNameFromTopic(null)); - } - - @Test - void testIsAuthPath() { - assertTrue(IotHttpTopicUtils.isAuthPath("/auth")); - assertFalse(IotHttpTopicUtils.isAuthPath("/topic/test")); - assertFalse(IotHttpTopicUtils.isAuthPath("/unknown")); - assertFalse(IotHttpTopicUtils.isAuthPath(null)); - assertFalse(IotHttpTopicUtils.isAuthPath("")); - } - - @Test - void testIsTopicPath() { - assertTrue(IotHttpTopicUtils.isTopicPath("/topic/sys/test/device/property")); - assertTrue(IotHttpTopicUtils.isTopicPath("/topic/test")); - assertFalse(IotHttpTopicUtils.isTopicPath("/auth")); - assertFalse(IotHttpTopicUtils.isTopicPath("/unknown")); - assertFalse(IotHttpTopicUtils.isTopicPath(null)); - assertFalse(IotHttpTopicUtils.isTopicPath("")); - } - - @Test - void testIsValidHttpPath() { - assertTrue(IotHttpTopicUtils.isValidHttpPath("/auth")); - assertTrue(IotHttpTopicUtils.isValidHttpPath("/topic/test")); - assertFalse(IotHttpTopicUtils.isValidHttpPath("/unknown")); - assertFalse(IotHttpTopicUtils.isValidHttpPath(null)); - assertFalse(IotHttpTopicUtils.isValidHttpPath("")); - } - - @Test - void testIsSystemTopic() { - assertTrue(IotHttpTopicUtils.isSystemTopic("/sys/testProduct/testDevice/thing/service/property/set")); - assertFalse(IotHttpTopicUtils.isSystemTopic("/testProduct/testDevice/user/get")); - assertFalse(IotHttpTopicUtils.isSystemTopic("/unknown")); - assertFalse(IotHttpTopicUtils.isSystemTopic(null)); - assertFalse(IotHttpTopicUtils.isSystemTopic("")); - } - - @Test - void testBuildReplyPath() { - // 测试系统主题响应路径 - String requestPath = "/topic/sys/testProduct/testDevice/thing/service/property/set"; - String replyPath = IotHttpTopicUtils.buildReplyPath(requestPath); - assertEquals("/topic/sys/testProduct/testDevice/thing/service/property/set_reply", replyPath); - - // 测试非系统主题 - String customPath = "/topic/testProduct/testDevice/user/get"; - assertNull(IotHttpTopicUtils.buildReplyPath(customPath)); - - // 测试无效路径 - assertNull(IotHttpTopicUtils.buildReplyPath("/auth")); - assertNull(IotHttpTopicUtils.buildReplyPath("/unknown")); - assertNull(IotHttpTopicUtils.buildReplyPath(null)); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtilsTest.java b/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtilsTest.java deleted file mode 100644 index fa882c3aa0..0000000000 --- a/yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtilsTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.iot.protocol.util; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -/** - * {@link IotTopicUtils} 单元测试 - * - * @author haohao - */ -class IotTopicUtilsTest { - - @Test - void testBuildPropertySetTopic() { - String topic = IotTopicUtils.buildPropertySetTopic("testProduct", "testDevice"); - assertEquals("/sys/testProduct/testDevice/thing/service/property/set", topic); - } - - @Test - void testBuildPropertyGetTopic() { - String topic = IotTopicUtils.buildPropertyGetTopic("testProduct", "testDevice"); - assertEquals("/sys/testProduct/testDevice/thing/service/property/get", topic); - } - - @Test - void testBuildEventPostTopic() { - String topic = IotTopicUtils.buildEventPostTopic("testProduct", "testDevice", "temperature"); - assertEquals("/sys/testProduct/testDevice/thing/event/temperature/post", topic); - } - - @Test - void testGetReplyTopic() { - String requestTopic = "/sys/testProduct/testDevice/thing/service/property/set"; - String replyTopic = IotTopicUtils.getReplyTopic(requestTopic); - assertEquals("/sys/testProduct/testDevice/thing/service/property/set_reply", replyTopic); - } - - @Test - void testParseProductKeyFromTopic() { - String topic = "/sys/testProduct/testDevice/thing/service/property/set"; - String productKey = IotTopicUtils.parseProductKeyFromTopic(topic); - assertEquals("testProduct", productKey); - } - - @Test - void testParseDeviceNameFromTopic() { - String topic = "/sys/testProduct/testDevice/thing/service/property/set"; - String deviceName = IotTopicUtils.parseDeviceNameFromTopic(topic); - assertEquals("testDevice", deviceName); - } - - @Test - void testParseMethodFromTopic() { - // 测试属性设置 - String topic1 = "/sys/testProduct/testDevice/thing/service/property/set"; - String method1 = IotTopicUtils.parseMethodFromTopic(topic1); - assertEquals("property.set", method1); - - // 测试事件上报 - String topic2 = "/sys/testProduct/testDevice/thing/event/temperature/post"; - String method2 = IotTopicUtils.parseMethodFromTopic(topic2); - assertEquals("event.temperature.post", method2); - - // 测试无效主题 - String method3 = IotTopicUtils.parseMethodFromTopic("/invalid/topic"); - assertNull(method3); - } - - @Test - void testParseInvalidTopic() { - // 测试空主题 - assertNull(IotTopicUtils.parseProductKeyFromTopic("")); - assertNull(IotTopicUtils.parseProductKeyFromTopic(null)); - - // 测试格式错误的主题 - assertNull(IotTopicUtils.parseProductKeyFromTopic("/invalid")); - assertNull(IotTopicUtils.parseDeviceNameFromTopic("/sys/product")); - } -} \ No newline at end of file