Commit af37176d5074c7f4cd6771add1a2c8c1b07e1bfc

Authored by haohao
1 parent fbb66402

feat:【IOT】新增 MQTT 协议支持及相关消息解析器,完善协议转换器功能

Showing 15 changed files with 558 additions and 109 deletions
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/IotAlinkMessage.java renamed to 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
... ... @@ -8,16 +8,15 @@ import lombok.Data;
8 8 import java.util.Map;
9 9  
10 10 /**
11   - * IoT Alink 消息模型
  11 + * IoT MQTT 消息模型
12 12 * <p>
13   - * 基于阿里云 Alink 协议规范实现的标准消息格式
14   - * @see <a href="https://help.aliyun.com/zh/iot/user-guide/alink-protocol-1">阿里云物联网 —— Alink 协议</a>
  13 + * 基于 MQTT 协议规范实现的标准消息格式,兼容 Alink 协议
15 14 *
16 15 * @author haohao
17 16 */
18 17 @Data
19 18 @Builder
20   -public class IotAlinkMessage {
  19 +public class IotMqttMessage {
21 20  
22 21 /**
23 22 * 消息 ID
... ... @@ -69,11 +68,11 @@ public class IotAlinkMessage {
69 68 * @param requestId 请求 ID,为空时自动生成
70 69 * @param serviceIdentifier 服务标识符
71 70 * @param params 服务参数
72   - * @return Alink 消息对象
  71 + * @return MQTT 消息对象
73 72 */
74   - public static IotAlinkMessage createServiceInvokeMessage(String requestId, String serviceIdentifier,
  73 + public static IotMqttMessage createServiceInvokeMessage(String requestId, String serviceIdentifier,
75 74 Map<String, Object> params) {
76   - return IotAlinkMessage.builder()
  75 + return IotMqttMessage.builder()
77 76 .id(requestId != null ? requestId : generateRequestId())
78 77 .method("thing.service." + serviceIdentifier)
79 78 .params(params)
... ... @@ -85,10 +84,10 @@ public class IotAlinkMessage {
85 84 *
86 85 * @param requestId 请求 ID,为空时自动生成
87 86 * @param properties 设备属性
88   - * @return Alink 消息对象
  87 + * @return MQTT 消息对象
89 88 */
90   - public static IotAlinkMessage createPropertySetMessage(String requestId, Map<String, Object> properties) {
91   - return IotAlinkMessage.builder()
  89 + public static IotMqttMessage createPropertySetMessage(String requestId, Map<String, Object> properties) {
  90 + return IotMqttMessage.builder()
92 91 .id(requestId != null ? requestId : generateRequestId())
93 92 .method("thing.service.property.set")
94 93 .params(properties)
... ... @@ -100,13 +99,13 @@ public class IotAlinkMessage {
100 99 *
101 100 * @param requestId 请求 ID,为空时自动生成
102 101 * @param identifiers 要获取的属性标识符列表
103   - * @return Alink 消息对象
  102 + * @return MQTT 消息对象
104 103 */
105   - public static IotAlinkMessage createPropertyGetMessage(String requestId, String[] identifiers) {
  104 + public static IotMqttMessage createPropertyGetMessage(String requestId, String[] identifiers) {
106 105 JSONObject params = new JSONObject();
107 106 params.set("identifiers", identifiers);
108 107  
109   - return IotAlinkMessage.builder()
  108 + return IotMqttMessage.builder()
110 109 .id(requestId != null ? requestId : generateRequestId())
111 110 .method("thing.service.property.get")
112 111 .params(params)
... ... @@ -118,10 +117,10 @@ public class IotAlinkMessage {
118 117 *
119 118 * @param requestId 请求 ID,为空时自动生成
120 119 * @param configs 设备配置
121   - * @return Alink 消息对象
  120 + * @return MQTT 消息对象
122 121 */
123   - public static IotAlinkMessage createConfigSetMessage(String requestId, Map<String, Object> configs) {
124   - return IotAlinkMessage.builder()
  122 + public static IotMqttMessage createConfigSetMessage(String requestId, Map<String, Object> configs) {
  123 + return IotMqttMessage.builder()
125 124 .id(requestId != null ? requestId : generateRequestId())
126 125 .method("thing.service.config.set")
127 126 .params(configs)
... ... @@ -133,10 +132,10 @@ public class IotAlinkMessage {
133 132 *
134 133 * @param requestId 请求 ID,为空时自动生成
135 134 * @param otaInfo OTA 升级信息
136   - * @return Alink 消息对象
  135 + * @return MQTT 消息对象
137 136 */
138   - public static IotAlinkMessage createOtaUpgradeMessage(String requestId, Map<String, Object> otaInfo) {
139   - return IotAlinkMessage.builder()
  137 + public static IotMqttMessage createOtaUpgradeMessage(String requestId, Map<String, Object> otaInfo) {
  138 + return IotMqttMessage.builder()
140 139 .id(requestId != null ? requestId : generateRequestId())
141 140 .method("thing.service.ota.upgrade")
142 141 .params(otaInfo)
... ...
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
... ... @@ -7,7 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 7 import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*;
8 8 import cn.iocoder.yudao.module.iot.net.component.core.constants.IotDeviceTopicEnum;
9 9 import cn.iocoder.yudao.module.iot.net.component.core.downstream.IotDeviceDownstreamHandler;
10   -import cn.iocoder.yudao.module.iot.net.component.core.message.IotAlinkMessage;
  10 +import cn.iocoder.yudao.module.iot.net.component.core.message.IotMqttMessage;
11 11 import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils;
12 12 import io.netty.handler.codec.mqtt.MqttQoS;
13 13 import io.vertx.core.buffer.Buffer;
... ... @@ -56,7 +56,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle
56 56 // 构建请求消息
57 57 String requestId = StrUtil.isNotEmpty(reqDTO.getRequestId()) ? reqDTO.getRequestId()
58 58 : IotNetComponentCommonUtils.generateRequestId();
59   - IotAlinkMessage message = IotAlinkMessage.createServiceInvokeMessage(
  59 + IotMqttMessage message = IotMqttMessage.createServiceInvokeMessage(
60 60 requestId, reqDTO.getIdentifier(), reqDTO.getParams());
61 61  
62 62 // 发送消息
... ... @@ -93,7 +93,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle
93 93 // 构建请求消息
94 94 String requestId = StrUtil.isNotEmpty(reqDTO.getRequestId()) ? reqDTO.getRequestId()
95 95 : IotNetComponentCommonUtils.generateRequestId();
96   - IotAlinkMessage message = IotAlinkMessage.createPropertySetMessage(requestId, reqDTO.getProperties());
  96 + IotMqttMessage message = IotMqttMessage.createPropertySetMessage(requestId, reqDTO.getProperties());
97 97  
98 98 // 发送消息
99 99 publishMessage(topic, message.toJsonObject());
... ...
yudao-module-iot/yudao-module-iot-protocol/README.md 0 → 100644
  1 +# IoT 协议模块 (yudao-module-iot-protocol)
  2 +
  3 +## 概述
  4 +
  5 +本模块是物联网协议处理的核心组件,提供统一的协议解析、转换和消息处理功能。作为 `yudao-module-iot-biz` 和
  6 +`yudao-module-iot-gateway-server` 等模块的共享包,实现了协议层面的抽象和统一。
  7 +
  8 +## 主要功能
  9 +
  10 +### 1. 协议消息模型
  11 +
  12 +- **IotMqttMessage**: 基于 MQTT 协议规范的标准消息模型(默认实现)
  13 +- **IotStandardResponse**: 统一的响应格式,支持 MQTT 和 HTTP 协议
  14 +
  15 +### 2. 主题管理
  16 +
  17 +- **IotTopicConstants**: 主题常量定义
  18 +- **IotTopicUtils**: MQTT 主题构建和解析工具
  19 +- **IotHttpTopicUtils**: HTTP 主题构建和解析工具
  20 +- **IotTopicParser**: 高级主题解析器,支持提取设备信息、消息类型等
  21 +
  22 +### 3. 协议转换
  23 +
  24 +- **IotMessageParser**: 消息解析器接口
  25 +- **IotMqttMessageParser**: MQTT 协议解析器实现(默认)
  26 +- **IotHttpMessageParser**: HTTP 协议解析器实现
  27 +- **IotProtocolConverter**: 协议转换器接口
  28 +- **DefaultIotProtocolConverter**: 默认协议转换器实现
  29 +
  30 +### 4. 枚举定义
  31 +
  32 +- **IotProtocolTypeEnum**: 协议类型枚举
  33 +- **IotMessageTypeEnum**: 消息类型枚举
  34 +- **IotMessageDirectionEnum**: 消息方向枚举
  35 +
  36 +## 使用示例
  37 +
  38 +### 1. 构建主题
  39 +
  40 +#### MQTT 主题
  41 +
  42 +```java
  43 +// 构建设备属性设置主题
  44 +String topic = IotTopicUtils.buildPropertySetTopic("productKey", "deviceName");
  45 +// 结果: /sys/productKey/deviceName/thing/service/property/set
  46 +
  47 +// 构建事件上报主题
  48 +String eventTopic = IotTopicUtils.buildEventPostTopic("productKey", "deviceName", "temperature");
  49 +// 结果: /sys/productKey/deviceName/thing/event/temperature/post
  50 +
  51 +// 获取响应主题
  52 +String replyTopic = IotTopicUtils.getReplyTopic(topic);
  53 +// 结果: /sys/productKey/deviceName/thing/service/property/set_reply
  54 +```
  55 +
  56 +#### HTTP 主题
  57 +
  58 +```java
  59 +// 构建属性设置路径
  60 +String propSetPath = IotHttpTopicUtils.buildPropertySetPath("productKey", "deviceName");
  61 +// 结果: /topic/sys/productKey/deviceName/thing/service/property/set
  62 +
  63 +// 构建属性获取路径
  64 +String propGetPath = IotHttpTopicUtils.buildPropertyGetPath("productKey", "deviceName");
  65 +// 结果: /topic/sys/productKey/deviceName/thing/service/property/get
  66 +
  67 +// 构建事件上报路径
  68 +String eventPath = IotHttpTopicUtils.buildEventPostPath("productKey", "deviceName", "alarm");
  69 +// 结果: /topic/sys/productKey/deviceName/thing/event/alarm/post
  70 +
  71 +// 构建自定义主题路径
  72 +String customPath = IotHttpTopicUtils.buildCustomTopicPath("productKey", "deviceName", "user/get");
  73 +// 结果: /topic/productKey/deviceName/user/get
  74 +```
  75 +
  76 +### 2. 解析主题
  77 +
  78 +```java
  79 +// 解析 MQTT 主题信息
  80 +IotTopicParser.TopicInfo info = IotTopicParser.parse("/sys/pk/dn/thing/service/property/set");
  81 +System.out.
  82 +
  83 +println("产品Key: "+info.getProductKey()); // pk
  84 + System.out.
  85 +
  86 +println("设备名称: "+info.getDeviceName()); // dn
  87 + System.out.
  88 +
  89 +println("消息类型: "+info.getMessageType()); // PROPERTY_SET
  90 + System.out.
  91 +
  92 +println("消息方向: "+info.getDirection()); // DOWNSTREAM
  93 +
  94 +// 解析 HTTP 主题信息
  95 +String httpPath = "/topic/sys/pk/dn/thing/service/property/set";
  96 +String actualTopic = IotHttpTopicUtils.extractActualTopic(httpPath); // /sys/pk/dn/thing/service/property/set
  97 +String productKey = IotHttpTopicUtils.parseProductKeyFromTopic(actualTopic); // pk
  98 +String deviceName = IotHttpTopicUtils.parseDeviceNameFromTopic(actualTopic); // dn
  99 +```
  100 +
  101 +### 3. 创建 MQTT 消息
  102 +
  103 +```java
  104 +// 创建属性设置消息
  105 +Map<String, Object> properties = new HashMap<>();
  106 +properties.
  107 +
  108 +put("temperature",25.5);
  109 +
  110 +IotMqttMessage message = IotMqttMessage.createPropertySetMessage("123456", properties);
  111 +
  112 +// 转换为 JSON 字符串
  113 +String json = message.toJsonString();
  114 +```
  115 +
  116 +### 4. HTTP 协议消息处理
  117 +
  118 +#### HTTP 消息格式
  119 +
  120 +```json
  121 +{
  122 + "deviceKey": "productKey/deviceName",
  123 + "messageId": "123456",
  124 + "action": "property.set",
  125 + "version": "1.0",
  126 + "data": {
  127 + "temperature": 25.5,
  128 + "humidity": 60.0
  129 + }
  130 +}
  131 +```
  132 +
  133 +#### 使用 HTTP 协议解析器
  134 +
  135 +```java
  136 +// 创建 HTTP 协议解析器
  137 +IotHttpMessageParser httpParser = new IotHttpMessageParser();
  138 +
  139 +// 解析 HTTP 消息
  140 +String topic = "/topic/sys/productKey/deviceName/thing/service/property/set";
  141 +byte[] payload = httpMessage.getBytes(StandardCharsets.UTF_8);
  142 +IotMqttMessage message = httpParser.parse(topic, payload);
  143 +
  144 +// 格式化 HTTP 响应
  145 +IotStandardResponse response = IotStandardResponse.success("123456", "property.set", data);
  146 +byte[] responseBytes = httpParser.formatResponse(response);
  147 +```
  148 +
  149 +### 5. 使用协议转换器
  150 +
  151 +```java
  152 +
  153 +@Autowired
  154 +private IotProtocolConverter protocolConverter;
  155 +
  156 +// 转换 MQTT 消息(推荐使用)
  157 +IotMqttMessage mqttMessage = protocolConverter.convertToStandardMessage(mqttTopic, mqttPayload, "mqtt");
  158 +
  159 +// 转换 HTTP 消息
  160 +IotMqttMessage httpMessage = protocolConverter.convertToStandardMessage(httpTopic, httpPayload, "http");
  161 +
  162 +// 创建响应
  163 +IotStandardResponse response = IotStandardResponse.success("123456", "property.set", data);
  164 +byte[] responseBytes = protocolConverter.convertFromStandardResponse(response, "mqtt");
  165 +```
  166 +
  167 +### 6. 自定义协议解析器
  168 +
  169 +```java
  170 +
  171 +@Component
  172 +public class CustomMessageParser implements IotMessageParser {
  173 +
  174 + @Override
  175 + public IotMqttMessage parse(String topic, byte[] payload) {
  176 + // 实现自定义协议解析逻辑
  177 + return null;
  178 + }
  179 +
  180 + @Override
  181 + public byte[] formatResponse(IotStandardResponse response) {
  182 + // 实现自定义响应格式化逻辑
  183 + return new byte[0];
  184 + }
  185 +
  186 + @Override
  187 + public boolean canHandle(String topic) {
  188 + // 判断是否能处理该主题
  189 + return topic.startsWith("/custom/");
  190 + }
  191 +}
  192 +
  193 +// 注册到协议转换器
  194 +@Autowired
  195 +private DefaultIotProtocolConverter converter;
  196 +
  197 +@PostConstruct
  198 +public void init() {
  199 + converter.registerParser("custom", new CustomMessageParser());
  200 +}
  201 +```
  202 +
  203 +## 支持的协议类型
  204 +
  205 +- **MQTT**: 标准 MQTT 协议,支持设备属性、事件、服务调用(默认协议)
  206 +- **HTTP**: HTTP 协议,支持设备通过 HTTP API 进行通信
  207 +- **MQTT_RAW**: MQTT 原始协议
  208 +- **TCP**: TCP 协议
  209 +- **UDP**: UDP 协议
  210 +- **CUSTOM**: 自定义协议
  211 +
  212 +## 协议对比
  213 +
  214 +| 协议类型 | 传输方式 | 消息格式 | 主题格式 | 适用场景 |
  215 +|----------|------|------|----------------------------------------------------------------------------------------------------------------------------|---------------|
  216 +| MQTT | MQTT | JSON | `/sys/{productKey}/{deviceName}/...`<br/>`/mqtt/{productKey}/{deviceName}/...`<br/>`/device/{productKey}/{deviceName}/...` | 实时性要求高的设备(推荐) |
  217 +| HTTP | HTTP | JSON | `/topic/sys/{productKey}/{deviceName}/...`<br/>`/topic/{productKey}/{deviceName}/...` | 间歇性通信的设备 |
  218 +| MQTT_RAW | MQTT | 原始 | 自定义格式 | 特殊协议设备 |
  219 +
  220 +## 模块依赖
  221 +
  222 +本模块是一个基础模块,依赖项最小化:
  223 +
  224 +- `yudao-common`: 基础工具类
  225 +- `hutool-all`: 工具库
  226 +- `lombok`: 简化代码
  227 +- `spring-boot-starter`: Spring Boot 基础支持
  228 +
  229 +## 扩展点
  230 +
  231 +### 1. 自定义消息解析器
  232 +
  233 +实现 `IotMessageParser` 接口,支持新的协议格式。
  234 +
  235 +### 2. 自定义协议转换器
  236 +
  237 +实现 `IotProtocolConverter` 接口,提供更复杂的转换逻辑。
  238 +
  239 +### 3. 自定义主题格式
  240 +
  241 +扩展 `IotTopicParser` 的 `parseCustomTopic` 方法,支持自定义主题格式。
  242 +
  243 +## 注意事项
  244 +
  245 +1. 本模块设计为无状态的工具模块,避免引入有状态的组件
  246 +2. 所有的工具类都采用静态方法,便于直接调用
  247 +3. 异常处理采用返回 null 的方式,调用方需要做好空值检查
  248 +4. 日志级别建议设置为 INFO 或 WARN,避免过多的 DEBUG 日志
  249 +5. HTTP 协议解析器使用设备标识 `deviceKey`(格式:`productKey/deviceName`)来标识设备
  250 +
  251 +## 版本更新
  252 +
  253 +- v1.0.0: 基础功能实现,支持 MQTT 协议和 HTTP 协议支持
  254 +- 后续版本将支持更多协议类型和高级功能
0 255 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-protocol/pom.xml
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2 <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3   - xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3 + xmlns="http://maven.apache.org/POM/4.0.0"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4 5 <parent>
5 6 <artifactId>yudao-module-iot</artifactId>
6 7 <groupId>cn.iocoder.boot</groupId>
... ...
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfiguration.java
... ... @@ -4,8 +4,8 @@ import cn.iocoder.yudao.module.iot.protocol.convert.IotProtocolConverter;
4 4 import cn.iocoder.yudao.module.iot.protocol.convert.impl.DefaultIotProtocolConverter;
5 5 import cn.iocoder.yudao.module.iot.protocol.enums.IotProtocolTypeEnum;
6 6 import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
7   -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotAlinkMessageParser;
8 7 import cn.iocoder.yudao.module.iot.protocol.message.impl.IotHttpMessageParser;
  8 +import cn.iocoder.yudao.module.iot.protocol.message.impl.IotMqttMessageParser;
9 9 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
10 10 import org.springframework.context.annotation.Bean;
11 11 import org.springframework.context.annotation.Configuration;
... ... @@ -21,20 +21,21 @@ public class IotProtocolAutoConfiguration {
21 21 /**
22 22 * Bean 名称常量
23 23 */
24   - public static final String IOT_ALINK_MESSAGE_PARSER_BEAN_NAME = "iotAlinkMessageParser";
  24 + public static final String IOT_MQTT_MESSAGE_PARSER_BEAN_NAME = "iotMqttMessageParser";
25 25 public static final String IOT_HTTP_MESSAGE_PARSER_BEAN_NAME = "iotHttpMessageParser";
26 26  
27 27 /**
28   - * 注册 Alink 协议消息解析器
  28 + * 注册 MQTT 协议消息解析器
29 29 *
30   - * @return Alink 协议消息解析器
  30 + * @return MQTT 协议消息解析器
31 31 */
32 32 @Bean
33   - @ConditionalOnMissingBean(name = IOT_ALINK_MESSAGE_PARSER_BEAN_NAME)
34   - public IotMessageParser iotAlinkMessageParser() {
35   - return new IotAlinkMessageParser();
  33 + @ConditionalOnMissingBean(name = IOT_MQTT_MESSAGE_PARSER_BEAN_NAME)
  34 + public IotMessageParser iotMqttMessageParser() {
  35 + return new IotMqttMessageParser();
36 36 }
37 37  
  38 +
38 39 /**
39 40 * 注册 HTTP 协议消息解析器
40 41 *
... ... @@ -50,26 +51,24 @@ public class IotProtocolAutoConfiguration {
50 51 * 注册默认协议转换器
51 52 * <p>
52 53 * 如果用户没有自定义协议转换器,则使用默认实现
53   - * 默认会注册 Alink 和 HTTP 协议解析器
  54 + * 默认会注册 MQTT 和 HTTP 协议解析器
54 55 *
55   - * @param iotAlinkMessageParser Alink 协议解析器
56   - * @param iotHttpMessageParser HTTP 协议解析器
  56 + * @param iotMqttMessageParser MQTT 协议解析器
  57 + * @param iotHttpMessageParser HTTP 协议解析器
57 58 * @return 默认协议转换器
58 59 */
59 60 @Bean
60 61 @ConditionalOnMissingBean
61   - public IotProtocolConverter iotProtocolConverter(IotMessageParser iotAlinkMessageParser,
  62 + public IotProtocolConverter iotProtocolConverter(IotMessageParser iotMqttMessageParser,
62 63 IotMessageParser iotHttpMessageParser) {
63 64 DefaultIotProtocolConverter converter = new DefaultIotProtocolConverter();
64 65  
  66 + // 注册 MQTT 协议解析器(默认实现)
  67 + converter.registerParser(IotProtocolTypeEnum.MQTT.getCode(), iotMqttMessageParser);
  68 +
65 69 // 注册 HTTP 协议解析器
66 70 converter.registerParser(IotProtocolTypeEnum.HTTP.getCode(), iotHttpMessageParser);
67 71  
68   - // 注意:Alink 协议解析器已经在 DefaultIotProtocolConverter 构造函数中注册
69   - // 如果需要使用自定义的 Alink 解析器实例,可以重新注册
70   - // converter.registerParser(IotProtocolTypeEnum.ALINK.getCode(),
71   - // iotAlinkMessageParser);
72   -
73 72 return converter;
74 73 }
75 74 }
76 75 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/IotProtocolConverter.java
1 1 package cn.iocoder.yudao.module.iot.protocol.convert;
2 2  
3   -import cn.iocoder.yudao.module.iot.protocol.message.IotAlinkMessage;
  3 +import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
4 4 import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
5 5  
6 6 /**
... ... @@ -20,7 +20,7 @@ public interface IotProtocolConverter {
20 20 * @param protocol 协议类型
21 21 * @return 标准消息对象,转换失败返回 null
22 22 */
23   - IotAlinkMessage convertToStandardMessage(String topic, byte[] payload, String protocol);
  23 + IotMqttMessage convertToStandardMessage(String topic, byte[] payload, String protocol);
24 24  
25 25 /**
26 26 * 将标准响应转换为字节数组
... ...
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/impl/DefaultIotProtocolConverter.java
... ... @@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.iot.protocol.convert.impl;
3 3 import cn.iocoder.yudao.module.iot.protocol.constants.IotLogConstants;
4 4 import cn.iocoder.yudao.module.iot.protocol.convert.IotProtocolConverter;
5 5 import cn.iocoder.yudao.module.iot.protocol.enums.IotProtocolTypeEnum;
6   -import cn.iocoder.yudao.module.iot.protocol.message.IotAlinkMessage;
7 6 import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
  7 +import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
8 8 import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
9   -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotAlinkMessageParser;
  9 +import cn.iocoder.yudao.module.iot.protocol.message.impl.IotMqttMessageParser;
10 10 import lombok.extern.slf4j.Slf4j;
11 11  
12 12 import java.util.HashMap;
... ... @@ -33,8 +33,9 @@ public class DefaultIotProtocolConverter implements IotProtocolConverter {
33 33 * 构造函数,初始化默认支持的协议
34 34 */
35 35 public DefaultIotProtocolConverter() {
36   - // 注册 Alink 协议解析器
37   - registerParser(IotProtocolTypeEnum.ALINK.getCode(), new IotAlinkMessageParser());
  36 + // 注册 MQTT 协议解析器作为默认实现
  37 + IotMqttMessageParser mqttParser = new IotMqttMessageParser();
  38 + registerParser(IotProtocolTypeEnum.MQTT.getCode(), mqttParser);
38 39 }
39 40  
40 41 /**
... ... @@ -59,7 +60,7 @@ public class DefaultIotProtocolConverter implements IotProtocolConverter {
59 60 }
60 61  
61 62 @Override
62   - public IotAlinkMessage convertToStandardMessage(String topic, byte[] payload, String protocol) {
  63 + public IotMqttMessage convertToStandardMessage(String topic, byte[] payload, String protocol) {
63 64 IotMessageParser parser = parsers.get(protocol);
64 65 if (parser == null) {
65 66 log.warn(IotLogConstants.Converter.UNSUPPORTED_PROTOCOL, protocol);
... ... @@ -108,13 +109,13 @@ public class DefaultIotProtocolConverter implements IotProtocolConverter {
108 109 * @param payload 消息负载
109 110 * @return 解析后的标准消息,如果无法解析返回 null
110 111 */
111   - public IotAlinkMessage autoConvert(String topic, byte[] payload) {
  112 + public IotMqttMessage autoConvert(String topic, byte[] payload) {
112 113 // 遍历所有解析器,找到能处理该主题的解析器
113 114 for (Map.Entry<String, IotMessageParser> entry : parsers.entrySet()) {
114 115 IotMessageParser parser = entry.getValue();
115 116 if (parser.canHandle(topic)) {
116 117 try {
117   - IotAlinkMessage message = parser.parse(topic, payload);
  118 + IotMqttMessage message = parser.parse(topic, payload);
118 119 if (message != null) {
119 120 log.debug(IotLogConstants.Converter.AUTO_SELECT_PROTOCOL, entry.getKey(), topic);
120 121 return message;
... ...
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotProtocolTypeEnum.java
... ... @@ -13,9 +13,9 @@ import lombok.Getter;
13 13 public enum IotProtocolTypeEnum {
14 14  
15 15 /**
16   - * Alink 协议(阿里云物联网协议)
  16 + * MQTT 协议(默认实现)
17 17 */
18   - ALINK("alink", "Alink 协议"),
  18 + MQTT("mqtt", "MQTT 协议"),
19 19  
20 20 /**
21 21 * MQTT 原始协议
... ...
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMessageParser.java
... ... @@ -16,7 +16,7 @@ public interface IotMessageParser {
16 16 * @param payload 消息负载
17 17 * @return 解析后的标准消息,如果解析失败返回 null
18 18 */
19   - IotAlinkMessage parse(String topic, byte[] payload);
  19 + IotMqttMessage parse(String topic, byte[] payload);
20 20  
21 21 /**
22 22 * 格式化响应消息
... ...
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotAlinkMessage.java renamed to yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMqttMessage.java
... ... @@ -8,16 +8,16 @@ import lombok.Data;
8 8 import java.util.Map;
9 9  
10 10 /**
11   - * IoT Alink 消息模型
  11 + * IoT MQTT 消息模型
12 12 * <p>
13   - * 基于阿里云 Alink 协议规范实现的标准消息格式
  13 + * 基于 MQTT 协议规范实现的标准消息格式,支持设备属性、事件、服务调用等标准功能
14 14 *
15 15 * @author haohao
16   - * @see <a href="https://help.aliyun.com/zh/iot/user-guide/alink-protocol-1">阿里云物联网 —— Alink 协议</a>
  16 + * @see <a href="https://mqtt.org/">MQTT 协议官方规范</a>
17 17 */
18 18 @Data
19 19 @Builder
20   -public class IotAlinkMessage {
  20 +public class IotMqttMessage {
21 21  
22 22 /**
23 23 * 消息 ID
... ... @@ -69,11 +69,11 @@ public class IotAlinkMessage {
69 69 * @param requestId 请求 ID,为空时自动生成
70 70 * @param serviceIdentifier 服务标识符
71 71 * @param params 服务参数
72   - * @return Alink 消息对象
  72 + * @return MQTT 消息对象
73 73 */
74   - public static IotAlinkMessage createServiceInvokeMessage(String requestId, String serviceIdentifier,
75   - Map<String, Object> params) {
76   - return IotAlinkMessage.builder()
  74 + public static IotMqttMessage createServiceInvokeMessage(String requestId, String serviceIdentifier,
  75 + Map<String, Object> params) {
  76 + return IotMqttMessage.builder()
77 77 .id(requestId != null ? requestId : generateRequestId())
78 78 .method("thing.service." + serviceIdentifier)
79 79 .params(params)
... ... @@ -85,10 +85,10 @@ public class IotAlinkMessage {
85 85 *
86 86 * @param requestId 请求 ID,为空时自动生成
87 87 * @param properties 设备属性
88   - * @return Alink 消息对象
  88 + * @return MQTT 消息对象
89 89 */
90   - public static IotAlinkMessage createPropertySetMessage(String requestId, Map<String, Object> properties) {
91   - return IotAlinkMessage.builder()
  90 + public static IotMqttMessage createPropertySetMessage(String requestId, Map<String, Object> properties) {
  91 + return IotMqttMessage.builder()
92 92 .id(requestId != null ? requestId : generateRequestId())
93 93 .method("thing.service.property.set")
94 94 .params(properties)
... ... @@ -100,13 +100,13 @@ public class IotAlinkMessage {
100 100 *
101 101 * @param requestId 请求 ID,为空时自动生成
102 102 * @param identifiers 要获取的属性标识符列表
103   - * @return Alink 消息对象
  103 + * @return MQTT 消息对象
104 104 */
105   - public static IotAlinkMessage createPropertyGetMessage(String requestId, String[] identifiers) {
  105 + public static IotMqttMessage createPropertyGetMessage(String requestId, String[] identifiers) {
106 106 JSONObject params = new JSONObject();
107 107 params.set("identifiers", identifiers);
108 108  
109   - return IotAlinkMessage.builder()
  109 + return IotMqttMessage.builder()
110 110 .id(requestId != null ? requestId : generateRequestId())
111 111 .method("thing.service.property.get")
112 112 .params(params)
... ... @@ -118,10 +118,10 @@ public class IotAlinkMessage {
118 118 *
119 119 * @param requestId 请求 ID,为空时自动生成
120 120 * @param configs 设备配置
121   - * @return Alink 消息对象
  121 + * @return MQTT 消息对象
122 122 */
123   - public static IotAlinkMessage createConfigSetMessage(String requestId, Map<String, Object> configs) {
124   - return IotAlinkMessage.builder()
  123 + public static IotMqttMessage createConfigSetMessage(String requestId, Map<String, Object> configs) {
  124 + return IotMqttMessage.builder()
125 125 .id(requestId != null ? requestId : generateRequestId())
126 126 .method("thing.service.config.set")
127 127 .params(configs)
... ... @@ -133,10 +133,10 @@ public class IotAlinkMessage {
133 133 *
134 134 * @param requestId 请求 ID,为空时自动生成
135 135 * @param otaInfo OTA 升级信息
136   - * @return Alink 消息对象
  136 + * @return MQTT 消息对象
137 137 */
138   - public static IotAlinkMessage createOtaUpgradeMessage(String requestId, Map<String, Object> otaInfo) {
139   - return IotAlinkMessage.builder()
  138 + public static IotMqttMessage createOtaUpgradeMessage(String requestId, Map<String, Object> otaInfo) {
  139 + return IotMqttMessage.builder()
140 140 .id(requestId != null ? requestId : generateRequestId())
141 141 .method("thing.service.ota.upgrade")
142 142 .params(otaInfo)
... ...
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParser.java
... ... @@ -6,8 +6,8 @@ import cn.hutool.json.JSONUtil;
6 6 import cn.iocoder.yudao.module.iot.protocol.constants.IotHttpConstants;
7 7 import cn.iocoder.yudao.module.iot.protocol.constants.IotLogConstants;
8 8 import cn.iocoder.yudao.module.iot.protocol.constants.IotTopicConstants;
9   -import cn.iocoder.yudao.module.iot.protocol.message.IotAlinkMessage;
10 9 import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
  10 +import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
11 11 import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
12 12 import lombok.extern.slf4j.Slf4j;
13 13  
... ... @@ -61,7 +61,7 @@ public class IotHttpMessageParser implements IotMessageParser {
61 61 public static final String TOPIC_PATH_PREFIX = IotHttpConstants.Path.TOPIC_PREFIX;
62 62  
63 63 @Override
64   - public IotAlinkMessage parse(String topic, byte[] payload) {
  64 + public IotMqttMessage parse(String topic, byte[] payload) {
65 65 if (payload == null || payload.length == 0) {
66 66 log.warn(IotLogConstants.Http.RECEIVED_EMPTY_MESSAGE, topic);
67 67 return null;
... ... @@ -92,7 +92,7 @@ public class IotHttpMessageParser implements IotMessageParser {
92 92 * @param message 认证消息JSON
93 93 * @return 标准消息格式
94 94 */
95   - private IotAlinkMessage parseAuthMessage(String message) {
  95 + private IotMqttMessage parseAuthMessage(String message) {
96 96 if (!JSONUtil.isTypeJSON(message)) {
97 97 log.warn(IotLogConstants.Http.AUTH_MESSAGE_NOT_JSON, message);
98 98 return null;
... ... @@ -121,7 +121,7 @@ public class IotHttpMessageParser implements IotMessageParser {
121 121 params.put(IotHttpConstants.AuthField.SIGN_METHOD,
122 122 json.getStr(IotHttpConstants.AuthField.SIGN_METHOD, IotHttpConstants.DefaultValue.SIGN_METHOD));
123 123  
124   - return IotAlinkMessage.builder()
  124 + return IotMqttMessage.builder()
125 125 .id(generateMessageId())
126 126 .method(IotHttpConstants.Method.DEVICE_AUTH)
127 127 .version(json.getStr(IotHttpConstants.AuthField.VERSION, IotHttpConstants.DefaultValue.VERSION))
... ... @@ -136,7 +136,7 @@ public class IotHttpMessageParser implements IotMessageParser {
136 136 * @param message 消息内容
137 137 * @return 标准消息格式
138 138 */
139   - private IotAlinkMessage parseDataMessage(String topic, String message) {
  139 + private IotMqttMessage parseDataMessage(String topic, String message) {
140 140 // 提取实际的主题,去掉 /topic 前缀
141 141 String actualTopic = topic.substring(TOPIC_PATH_PREFIX.length()); // 直接移除/topic前缀
142 142  
... ... @@ -156,7 +156,7 @@ public class IotHttpMessageParser implements IotMessageParser {
156 156 * @param message JSON消息
157 157 * @return 标准消息格式
158 158 */
159   - private IotAlinkMessage parseJsonDataMessage(String topic, String message) {
  159 + private IotMqttMessage parseJsonDataMessage(String topic, String message) {
160 160 JSONObject json = JSONUtil.parseObj(message);
161 161  
162 162 // 生成消息ID
... ... @@ -181,7 +181,7 @@ public class IotHttpMessageParser implements IotMessageParser {
181 181 paramsMap.put(IotHttpConstants.MessageField.DATA, params);
182 182 }
183 183  
184   - return IotAlinkMessage.builder()
  184 + return IotMqttMessage.builder()
185 185 .id(messageId)
186 186 .method(method)
187 187 .version(json.getStr(IotHttpConstants.MessageField.VERSION,
... ... @@ -197,11 +197,11 @@ public class IotHttpMessageParser implements IotMessageParser {
197 197 * @param message 原始消息
198 198 * @return 标准消息格式
199 199 */
200   - private IotAlinkMessage parseRawDataMessage(String topic, String message) {
  200 + private IotMqttMessage parseRawDataMessage(String topic, String message) {
201 201 Map<String, Object> params = new HashMap<>();
202 202 params.put(IotHttpConstants.MessageField.DATA, message);
203 203  
204   - return IotAlinkMessage.builder()
  204 + return IotMqttMessage.builder()
205 205 .id(generateMessageId())
206 206 .method(inferMethodFromTopic(topic))
207 207 .version(IotHttpConstants.DefaultValue.MESSAGE_VERSION)
... ... @@ -263,7 +263,7 @@ public class IotHttpMessageParser implements IotMessageParser {
263 263 * @return 消息ID
264 264 */
265 265 private String generateMessageId() {
266   - return IotAlinkMessage.generateRequestId();
  266 + return IotMqttMessage.generateRequestId();
267 267 }
268 268  
269 269 @Override
... ...
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotAlinkMessageParser.java renamed to yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParser.java
... ... @@ -4,8 +4,8 @@ import cn.hutool.core.util.StrUtil;
4 4 import cn.hutool.json.JSONObject;
5 5 import cn.hutool.json.JSONUtil;
6 6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
7   -import cn.iocoder.yudao.module.iot.protocol.message.IotAlinkMessage;
8 7 import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
  8 +import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
9 9 import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
10 10 import cn.iocoder.yudao.module.iot.protocol.util.IotTopicUtils;
11 11 import lombok.extern.slf4j.Slf4j;
... ... @@ -14,26 +14,26 @@ import java.nio.charset.StandardCharsets;
14 14 import java.util.Map;
15 15  
16 16 /**
17   - * IoT Alink 协议消息解析器实现
  17 + * IoT MQTT 协议消息解析器实现
18 18 * <p>
19   - * 基于阿里云 Alink 协议规范实现的消息解析器
  19 + * 基于 MQTT 协议规范实现的消息解析器,支持设备属性、事件、服务调用等标准功能
20 20 *
21 21 * @author haohao
22 22 */
23 23 @Slf4j
24   -public class IotAlinkMessageParser implements IotMessageParser {
  24 +public class IotMqttMessageParser implements IotMessageParser {
25 25  
26 26 @Override
27   - public IotAlinkMessage parse(String topic, byte[] payload) {
  27 + public IotMqttMessage parse(String topic, byte[] payload) {
28 28 if (payload == null || payload.length == 0) {
29   - log.warn("[Alink] 收到空消息内容, topic={}", topic);
  29 + log.warn("[MQTT] 收到空消息内容, topic={}", topic);
30 30 return null;
31 31 }
32 32  
33 33 try {
34 34 String message = new String(payload, StandardCharsets.UTF_8);
35 35 if (!JSONUtil.isTypeJSON(message)) {
36   - log.warn("[Alink] 收到非JSON格式消息, topic={}, message={}", topic, message);
  36 + log.warn("[MQTT] 收到非JSON格式消息, topic={}, message={}", topic, message);
37 37 return null;
38 38 }
39 39  
... ... @@ -45,20 +45,21 @@ public class IotAlinkMessageParser implements IotMessageParser {
45 45 // 尝试从 topic 中解析方法
46 46 method = IotTopicUtils.parseMethodFromTopic(topic);
47 47 if (StrUtil.isBlank(method)) {
48   - log.warn("[Alink] 无法确定消息方法, topic={}, message={}", topic, message);
  48 + log.warn("[MQTT] 无法确定消息方法, topic={}, message={}", topic, message);
49 49 return null;
50 50 }
51 51 }
52 52  
  53 + @SuppressWarnings("unchecked")
53 54 Map<String, Object> params = (Map<String, Object>) json.getObj("params", Map.class);
54   - return IotAlinkMessage.builder()
  55 + return IotMqttMessage.builder()
55 56 .id(id)
56 57 .method(method)
57 58 .version(json.getStr("version", "1.0"))
58 59 .params(params)
59 60 .build();
60 61 } catch (Exception e) {
61   - log.error("[Alink] 解析消息失败, topic={}", topic, e);
  62 + log.error("[MQTT] 解析消息失败, topic={}", topic, e);
62 63 return null;
63 64 }
64 65 }
... ... @@ -69,14 +70,18 @@ public class IotAlinkMessageParser implements IotMessageParser {
69 70 String json = JsonUtils.toJsonString(response);
70 71 return json.getBytes(StandardCharsets.UTF_8);
71 72 } catch (Exception e) {
72   - log.error("[Alink] 格式化响应失败", e);
  73 + log.error("[MQTT] 格式化响应失败", e);
73 74 return new byte[0];
74 75 }
75 76 }
76 77  
77 78 @Override
78 79 public boolean canHandle(String topic) {
79   - // Alink 协议处理所有系统主题
80   - return topic != null && topic.startsWith("/sys/");
  80 + // MQTT 协议支持更多主题格式
  81 + return topic != null && (
  82 + topic.startsWith("/sys/") || // 兼容现有系统主题
  83 + topic.startsWith("/mqtt/") || // 新的通用 MQTT 主题
  84 + topic.startsWith("/device/") // 设备主题
  85 + );
81 86 }
82 87 }
83 88 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfigurationTest.java
... ... @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.protocol.config;
3 3 import cn.iocoder.yudao.module.iot.protocol.convert.IotProtocolConverter;
4 4 import cn.iocoder.yudao.module.iot.protocol.enums.IotProtocolTypeEnum;
5 5 import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
6   -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotAlinkMessageParser;
7 6 import cn.iocoder.yudao.module.iot.protocol.message.impl.IotHttpMessageParser;
  7 +import cn.iocoder.yudao.module.iot.protocol.message.impl.IotMqttMessageParser;
8 8 import org.junit.jupiter.api.BeforeEach;
9 9 import org.junit.jupiter.api.Test;
10 10  
... ... @@ -25,12 +25,12 @@ class IotProtocolAutoConfigurationTest {
25 25 }
26 26  
27 27 @Test
28   - void testIotAlinkMessageParser() {
29   - // 测试 Alink 协议解析器 Bean 创建
30   - IotMessageParser parser = configuration.iotAlinkMessageParser();
  28 + void testIotMqttMessageParser() {
  29 + // 测试 MQTT 协议解析器 Bean 创建
  30 + IotMessageParser parser = configuration.iotMqttMessageParser();
31 31  
32 32 assertNotNull(parser);
33   - assertInstanceOf(IotAlinkMessageParser.class, parser);
  33 + assertInstanceOf(IotMqttMessageParser.class, parser);
34 34 }
35 35  
36 36 @Test
... ... @@ -45,16 +45,16 @@ class IotProtocolAutoConfigurationTest {
45 45 @Test
46 46 void testIotProtocolConverter() {
47 47 // 创建解析器实例
48   - IotMessageParser alinkParser = configuration.iotAlinkMessageParser();
  48 + IotMessageParser mqttParser = configuration.iotMqttMessageParser();
49 49 IotMessageParser httpParser = configuration.iotHttpMessageParser();
50 50  
51 51 // 测试协议转换器 Bean 创建
52   - IotProtocolConverter converter = configuration.iotProtocolConverter(alinkParser, httpParser);
  52 + IotProtocolConverter converter = configuration.iotProtocolConverter(mqttParser, httpParser);
53 53  
54 54 assertNotNull(converter);
55 55  
56 56 // 验证支持的协议
57   - assertTrue(converter.supportsProtocol(IotProtocolTypeEnum.ALINK.getCode()));
  57 + assertTrue(converter.supportsProtocol(IotProtocolTypeEnum.MQTT.getCode()));
58 58 assertTrue(converter.supportsProtocol(IotProtocolTypeEnum.HTTP.getCode()));
59 59  
60 60 // 验证支持的协议数量
... ... @@ -65,7 +65,7 @@ class IotProtocolAutoConfigurationTest {
65 65 @Test
66 66 void testBeanNameConstants() {
67 67 // 测试 Bean 名称常量定义
68   - assertEquals("iotAlinkMessageParser", IotProtocolAutoConfiguration.IOT_ALINK_MESSAGE_PARSER_BEAN_NAME);
  68 + assertEquals("iotMqttMessageParser", IotProtocolAutoConfiguration.IOT_MQTT_MESSAGE_PARSER_BEAN_NAME);
69 69 assertEquals("iotHttpMessageParser", IotProtocolAutoConfiguration.IOT_HTTP_MESSAGE_PARSER_BEAN_NAME);
70 70 }
71 71 }
72 72 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParserTest.java
1 1 package cn.iocoder.yudao.module.iot.protocol.message.impl;
2 2  
3 3 import cn.hutool.json.JSONObject;
4   -import cn.iocoder.yudao.module.iot.protocol.message.IotAlinkMessage;
  4 +import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
5 5 import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
6 6 import org.junit.jupiter.api.BeforeEach;
7 7 import org.junit.jupiter.api.Test;
... ... @@ -58,7 +58,7 @@ class IotHttpMessageParserTest {
58 58 byte[] payload = authMessage.toString().getBytes(StandardCharsets.UTF_8);
59 59  
60 60 // 解析消息
61   - IotAlinkMessage result = parser.parse(topic, payload);
  61 + IotMqttMessage result = parser.parse(topic, payload);
62 62  
63 63 // 验证结果
64 64 assertNotNull(result);
... ... @@ -88,7 +88,7 @@ class IotHttpMessageParserTest {
88 88 byte[] payload = authMessage.toString().getBytes(StandardCharsets.UTF_8);
89 89  
90 90 // 解析消息
91   - IotAlinkMessage result = parser.parse(topic, payload);
  91 + IotMqttMessage result = parser.parse(topic, payload);
92 92  
93 93 // 验证结果
94 94 assertNull(result);
... ... @@ -113,7 +113,7 @@ class IotHttpMessageParserTest {
113 113 byte[] payload = dataMessage.toString().getBytes(StandardCharsets.UTF_8);
114 114  
115 115 // 解析消息
116   - IotAlinkMessage result = parser.parse(topic, payload);
  116 + IotMqttMessage result = parser.parse(topic, payload);
117 117  
118 118 // 验证结果
119 119 assertNotNull(result);
... ... @@ -132,7 +132,7 @@ class IotHttpMessageParserTest {
132 132 byte[] payload = rawData.getBytes(StandardCharsets.UTF_8);
133 133  
134 134 // 解析消息
135   - IotAlinkMessage result = parser.parse(topic, payload);
  135 + IotMqttMessage result = parser.parse(topic, payload);
136 136  
137 137 // 验证结果
138 138 assertNotNull(result);
... ... @@ -161,7 +161,7 @@ class IotHttpMessageParserTest {
161 161 String rawData = "test data";
162 162 byte[] payload = rawData.getBytes(StandardCharsets.UTF_8);
163 163  
164   - IotAlinkMessage result = parser.parse(topic, payload);
  164 + IotMqttMessage result = parser.parse(topic, payload);
165 165 assertNotNull(result);
166 166 assertEquals(expectedMethod, result.getMethod());
167 167 }
... ...
yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParserTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.protocol.message.impl;
  2 +
  3 +import cn.hutool.json.JSONObject;
  4 +import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
  5 +import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
  6 +import org.junit.jupiter.api.BeforeEach;
  7 +import org.junit.jupiter.api.Test;
  8 +
  9 +import java.nio.charset.StandardCharsets;
  10 +import java.util.HashMap;
  11 +import java.util.Map;
  12 +
  13 +import static org.junit.jupiter.api.Assertions.*;
  14 +
  15 +/**
  16 + * IoT MQTT 消息解析器测试类
  17 + *
  18 + * @author haohao
  19 + */
  20 +class IotMqttMessageParserTest {
  21 +
  22 + private IotMqttMessageParser parser;
  23 +
  24 + @BeforeEach
  25 + void setUp() {
  26 + parser = new IotMqttMessageParser();
  27 + }
  28 +
  29 + @Test
  30 + void testParseValidJsonMessage() {
  31 + // 构建有效的 JSON 消息
  32 + JSONObject message = new JSONObject();
  33 + message.set("id", "123456");
  34 + message.set("version", "1.0");
  35 + message.set("method", "thing.service.property.set");
  36 +
  37 + Map<String, Object> params = new HashMap<>();
  38 + params.put("temperature", 25.5);
  39 + params.put("humidity", 60.0);
  40 + message.set("params", params);
  41 +
  42 + String topic = "/sys/productKey/deviceName/thing/service/property/set";
  43 + byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8);
  44 +
  45 + // 解析消息
  46 + IotMqttMessage result = parser.parse(topic, payload);
  47 +
  48 + // 验证结果
  49 + assertNotNull(result);
  50 + assertEquals("123456", result.getId());
  51 + assertEquals("1.0", result.getVersion());
  52 + assertEquals("thing.service.property.set", result.getMethod());
  53 + assertNotNull(result.getParams());
  54 + assertEquals(25.5, ((Number) result.getParams().get("temperature")).doubleValue());
  55 + assertEquals(60.0, ((Number) result.getParams().get("humidity")).doubleValue());
  56 + }
  57 +
  58 + @Test
  59 + void testParseMessageWithoutMethod() {
  60 + // 构建没有 method 字段的消息,应该从 topic 中解析
  61 + JSONObject message = new JSONObject();
  62 + message.set("id", "789012");
  63 + message.set("version", "1.0");
  64 +
  65 + Map<String, Object> params = new HashMap<>();
  66 + params.put("voltage", 3.3);
  67 + message.set("params", params);
  68 +
  69 + String topic = "/sys/productKey/deviceName/thing/service/property/set";
  70 + byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8);
  71 +
  72 + // 解析消息
  73 + IotMqttMessage result = parser.parse(topic, payload);
  74 +
  75 + // 验证结果
  76 + assertNotNull(result);
  77 + assertEquals("789012", result.getId());
  78 + assertEquals("1.0", result.getVersion());
  79 + assertNotNull(result.getMethod()); // 应该从 topic 中解析出方法
  80 + assertNotNull(result.getParams());
  81 + assertEquals(3.3, ((Number) result.getParams().get("voltage")).doubleValue());
  82 + }
  83 +
  84 + @Test
  85 + void testParseInvalidJsonMessage() {
  86 + String topic = "/sys/productKey/deviceName/thing/service/property/set";
  87 + byte[] payload = "invalid json".getBytes(StandardCharsets.UTF_8);
  88 +
  89 + // 解析消息
  90 + IotMqttMessage result = parser.parse(topic, payload);
  91 +
  92 + // 验证结果
  93 + assertNull(result);
  94 + }
  95 +
  96 + @Test
  97 + void testParseEmptyPayload() {
  98 + String topic = "/sys/productKey/deviceName/thing/service/property/set";
  99 +
  100 + // 测试 null payload
  101 + IotMqttMessage result1 = parser.parse(topic, null);
  102 + assertNull(result1);
  103 +
  104 + // 测试空 payload
  105 + IotMqttMessage result2 = parser.parse(topic, new byte[0]);
  106 + assertNull(result2);
  107 + }
  108 +
  109 + @Test
  110 + void testFormatResponse() {
  111 + // 创建标准响应
  112 + IotStandardResponse response = IotStandardResponse.success("123456", "property.set", null);
  113 +
  114 + // 格式化响应
  115 + byte[] result = parser.formatResponse(response);
  116 +
  117 + // 验证结果
  118 + assertNotNull(result);
  119 + assertTrue(result.length > 0);
  120 +
  121 + // 验证 JSON 格式
  122 + String jsonString = new String(result, StandardCharsets.UTF_8);
  123 + assertTrue(jsonString.contains("123456"));
  124 + assertTrue(jsonString.contains("property.set"));
  125 + }
  126 +
  127 + @Test
  128 + void testCanHandle() {
  129 + // 测试支持的主题格式
  130 + assertTrue(parser.canHandle("/sys/productKey/deviceName/thing/service/property/set"));
  131 + assertTrue(parser.canHandle("/mqtt/productKey/deviceName/property/set"));
  132 + assertTrue(parser.canHandle("/device/productKey/deviceName/data"));
  133 +
  134 + // 测试不支持的主题格式
  135 + assertFalse(parser.canHandle("/http/device/productKey/deviceName/property/set"));
  136 + assertFalse(parser.canHandle("/unknown/topic"));
  137 + assertFalse(parser.canHandle(null));
  138 + assertFalse(parser.canHandle(""));
  139 + }
  140 +
  141 + @Test
  142 + void testParseMqttTopicFormat() {
  143 + // 测试新的 MQTT 主题格式
  144 + JSONObject message = new JSONObject();
  145 + message.set("id", "mqtt001");
  146 + message.set("version", "1.0");
  147 + message.set("method", "device.property.report");
  148 +
  149 + Map<String, Object> params = new HashMap<>();
  150 + params.put("signal", 85);
  151 + message.set("params", params);
  152 +
  153 + String topic = "/mqtt/productKey/deviceName/property/report";
  154 + byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8);
  155 +
  156 + // 解析消息
  157 + IotMqttMessage result = parser.parse(topic, payload);
  158 +
  159 + // 验证结果
  160 + assertNotNull(result);
  161 + assertEquals("mqtt001", result.getId());
  162 + assertEquals("device.property.report", result.getMethod());
  163 + assertEquals(85, ((Number) result.getParams().get("signal")).intValue());
  164 + }
  165 +
  166 + @Test
  167 + void testParseDeviceTopicFormat() {
  168 + // 测试设备主题格式
  169 + JSONObject message = new JSONObject();
  170 + message.set("id", "device001");
  171 + message.set("version", "1.0");
  172 + message.set("method", "sensor.data");
  173 +
  174 + Map<String, Object> params = new HashMap<>();
  175 + params.put("timestamp", System.currentTimeMillis());
  176 + message.set("params", params);
  177 +
  178 + String topic = "/device/productKey/deviceName/sensor/data";
  179 + byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8);
  180 +
  181 + // 解析消息
  182 + IotMqttMessage result = parser.parse(topic, payload);
  183 +
  184 + // 验证结果
  185 + assertNotNull(result);
  186 + assertEquals("device001", result.getId());
  187 + assertEquals("sensor.data", result.getMethod());
  188 + assertNotNull(result.getParams().get("timestamp"));
  189 + }
  190 +}
0 191 \ No newline at end of file
... ...