Commit c658ac69c0e584d948b16898b9e23e97a354f95c

Authored by haohao
1 parent af0ff1ce

reactor:【IoT 物联网】合并 MQTT 上行协议与 HTTP 认证协议,优化消息处理逻辑

Showing 11 changed files with 367 additions and 973 deletions
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
@@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; @@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber; 4 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber;
5 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 5 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
6 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber; 6 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber;
7 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttHttpAuthProtocol;  
8 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; 7 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
9 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
10 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 9 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -45,8 +44,11 @@ public class IotGatewayConfiguration { @@ -45,8 +44,11 @@ public class IotGatewayConfiguration {
45 @Slf4j 44 @Slf4j
46 public static class MqttProtocolConfiguration { 45 public static class MqttProtocolConfiguration {
47 46
  47 + /**
  48 + * MQTT 统一协议:集成上行协议和HTTP认证协议
  49 + */
48 @Bean 50 @Bean
49 - public IotMqttUpstreamProtocol iotMqttUpstreamProtocol(IotGatewayProperties gatewayProperties) { 51 + public IotMqttUpstreamProtocol iotMqttUnifiedProtocol(IotGatewayProperties gatewayProperties) {
50 return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getEmqx()); 52 return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getEmqx());
51 } 53 }
52 54
@@ -55,14 +57,6 @@ public class IotGatewayConfiguration { @@ -55,14 +57,6 @@ public class IotGatewayConfiguration {
55 IotMessageBus messageBus) { 57 IotMessageBus messageBus) {
56 return new IotMqttDownstreamSubscriber(mqttUpstreamProtocol, messageBus); 58 return new IotMqttDownstreamSubscriber(mqttUpstreamProtocol, messageBus);
57 } 59 }
58 -  
59 - /**  
60 - * MQTT HTTP 认证协议:提供 HTTP 认证接口供 EMQX 调用  
61 - */  
62 - @Bean  
63 - public IotMqttHttpAuthProtocol mqttHttpAuthProtocol(IotGatewayProperties gatewayProperties) {  
64 - return new IotMqttHttpAuthProtocol(gatewayProperties.getProtocol().getEmqx());  
65 - }  
66 } 60 }
67 61
68 } 62 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttDownstreamSubscriber.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.hutool.json.JSONObject; 4 import cn.hutool.json.JSONObject;
4 -import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;  
5 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;  
6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
7 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 6 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
8 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; 7 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
9 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
10 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 9 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
11 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; 10 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
  11 +import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
12 import jakarta.annotation.PostConstruct; 12 import jakarta.annotation.PostConstruct;
13 import jakarta.annotation.Resource; 13 import jakarta.annotation.Resource;
14 import lombok.RequiredArgsConstructor; 14 import lombok.RequiredArgsConstructor;
@@ -27,7 +27,7 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<IotDevi @@ -27,7 +27,7 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<IotDevi
27 private final IotMessageBus messageBus; 27 private final IotMessageBus messageBus;
28 28
29 @Resource 29 @Resource
30 - private IotDeviceCommonApi deviceApi; 30 + private IotDeviceService deviceService;
31 31
32 @PostConstruct 32 @PostConstruct
33 public void init() { 33 public void init() {
@@ -56,137 +56,87 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<IotDevi @@ -56,137 +56,87 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<IotDevi
56 return; 56 return;
57 } 57 }
58 58
59 - // TODO @haohao:看看怎么融合下  
60 - if (method.startsWith("thing.service.property.")) {  
61 - handlePropertyMessage(message);  
62 - } else if (method.startsWith("thing.service.") && !method.contains("property") && !method.contains("config")  
63 - && !method.contains("ota")) {  
64 - handleServiceMessage(message);  
65 - } else if (method.startsWith("thing.service.config.")) {  
66 - handleConfigMessage(message);  
67 - } else if (method.startsWith("thing.service.ota.")) {  
68 - handleOtaMessage(message);  
69 - } else {  
70 - log.warn("[onMessage][未知的消息方法:{}]", method);  
71 - } 59 + // 处理下行消息
  60 + handleDownstreamMessage(message);
72 } catch (Exception e) { 61 } catch (Exception e) {
73 log.error("[onMessage][处理下行消息失败:{}]", message, e); 62 log.error("[onMessage][处理下行消息失败:{}]", message, e);
74 } 63 }
75 } 64 }
76 65
77 /** 66 /**
78 - * 处理属性相关消息 67 + * 处理下行消息
79 * 68 *
80 * @param message 设备消息 69 * @param message 设备消息
81 */ 70 */
82 - private void handlePropertyMessage(IotDeviceMessage message) {  
83 - String method = message.getMethod();  
84 -  
85 - // 通过 deviceId 获取设备信息  
86 - // TODO @haohao:通过缓存拿;  
87 - IotDeviceGetReqDTO reqDTO = new IotDeviceGetReqDTO();  
88 - reqDTO.setId(message.getDeviceId());  
89 - IotDeviceRespDTO deviceInfo = deviceApi.getDevice(reqDTO).getData(); 71 + private void handleDownstreamMessage(IotDeviceMessage message) {
  72 + // 1. 获取设备信息(使用缓存)
  73 + IotDeviceRespDTO deviceInfo = deviceService.getDeviceFromCache(message.getDeviceId());
90 if (deviceInfo == null) { 74 if (deviceInfo == null) {
91 - log.warn("[handlePropertyMessage][设备信息不存在][deviceId: {}]", message.getDeviceId()); 75 + log.warn("[handleDownstreamMessage][设备信息不存在][deviceId: {}]", message.getDeviceId());
92 return; 76 return;
93 } 77 }
94 78
95 - String productKey = deviceInfo.getProductKey();  
96 - String deviceName = deviceInfo.getDeviceName();  
97 -  
98 - if ("thing.service.property.set".equals(method)) {  
99 - // 属性设置  
100 - String topic = IotDeviceTopicEnum.buildPropertySetTopic(productKey, deviceName);  
101 - JSONObject payload = buildDownstreamPayload(message, method);  
102 - protocol.publishMessage(topic, payload.toString());  
103 - log.info("[handlePropertyMessage][发送属性设置消息][topic: {}]", topic);  
104 - } else if ("thing.service.property.get".equals(method)) {  
105 - // 属性获取  
106 - String topic = IotDeviceTopicEnum.buildPropertyGetTopic(productKey, deviceName);  
107 - JSONObject payload = buildDownstreamPayload(message, method);  
108 - protocol.publishMessage(topic, payload.toString());  
109 - log.info("[handlePropertyMessage][发送属性获取消息][topic: {}]", topic);  
110 - } else {  
111 - log.warn("[handlePropertyMessage][未知的属性操作:{}]", method);  
112 - }  
113 - }  
114 -  
115 - /**  
116 - * 处理服务调用消息  
117 - *  
118 - * @param message 设备消息  
119 - */  
120 - private void handleServiceMessage(IotDeviceMessage message) {  
121 - String method = message.getMethod();  
122 -  
123 - // 通过 deviceId 获取设备信息  
124 - IotDeviceGetReqDTO reqDTO = new IotDeviceGetReqDTO();  
125 - reqDTO.setId(message.getDeviceId());  
126 - IotDeviceRespDTO deviceInfo = deviceApi.getDevice(reqDTO).getData();  
127 - if (deviceInfo == null) {  
128 - log.warn("[handleServiceMessage][设备信息不存在][deviceId: {}]", message.getDeviceId()); 79 + // 2. 根据方法构建主题
  80 + String topic = buildTopicByMethod(message.getMethod(), deviceInfo.getProductKey(), deviceInfo.getDeviceName());
  81 + if (StrUtil.isBlank(topic)) {
  82 + log.warn("[handleDownstreamMessage][未知的消息方法:{}]", message.getMethod());
129 return; 83 return;
130 } 84 }
131 85
132 - String productKey = deviceInfo.getProductKey();  
133 - String deviceName = deviceInfo.getDeviceName();  
134 -  
135 - // 从方法中提取服务标识符  
136 - String serviceIdentifier = method.substring("thing.service.".length()); 86 + // 3. 构建载荷
  87 + JSONObject payload = buildDownstreamPayload(message, message.getMethod());
137 88
138 - String topic = IotDeviceTopicEnum.buildServiceTopic(productKey, deviceName, serviceIdentifier);  
139 - JSONObject payload = buildDownstreamPayload(message, method); 89 + // 4. 发送消息
140 protocol.publishMessage(topic, payload.toString()); 90 protocol.publishMessage(topic, payload.toString());
141 - log.info("[handleServiceMessage][发送服务调用消息][topic: {}]", topic); 91 + log.info("[handleDownstreamMessage][发送下行消息成功][method: {}][topic: {}]", message.getMethod(), topic);
142 } 92 }
143 93
144 /** 94 /**
145 - * 处理配置设置消息 95 + * 根据方法构建主题
146 * 96 *
147 - * @param message 设备消息 97 + * @param method 消息方法
  98 + * @param productKey 产品标识
  99 + * @param deviceName 设备名称
  100 + * @return 构建的主题,如果方法不支持返回 null
148 */ 101 */
149 - private void handleConfigMessage(IotDeviceMessage message) {  
150 - // 通过 deviceId 获取设备信息  
151 - IotDeviceGetReqDTO reqDTO = new IotDeviceGetReqDTO();  
152 - reqDTO.setId(message.getDeviceId());  
153 - IotDeviceRespDTO deviceInfo = deviceApi.getDevice(reqDTO).getData();  
154 - if (deviceInfo == null) {  
155 - log.warn("[handleConfigMessage][设备信息不存在][deviceId: {}]", message.getDeviceId());  
156 - return; 102 + private String buildTopicByMethod(String method, String productKey, String deviceName) {
  103 + if (StrUtil.isBlank(method)) {
  104 + return null;
157 } 105 }
158 106
159 - String productKey = deviceInfo.getProductKey();  
160 - String deviceName = deviceInfo.getDeviceName(); 107 + // 属性相关操作
  108 + if (method.startsWith("thing.service.property.")) {
  109 + if ("thing.service.property.set".equals(method)) {
  110 + return IotDeviceTopicEnum.buildPropertySetTopic(productKey, deviceName);
  111 + } else if ("thing.service.property.get".equals(method)) {
  112 + return IotDeviceTopicEnum.buildPropertyGetTopic(productKey, deviceName);
  113 + }
  114 + return null;
  115 + }
161 116
162 - String topic = IotDeviceTopicEnum.buildConfigSetTopic(productKey, deviceName);  
163 - JSONObject payload = buildDownstreamPayload(message, message.getMethod());  
164 - protocol.publishMessage(topic, payload.toString());  
165 - log.info("[handleConfigMessage][发送配置设置消息][topic: {}]", topic);  
166 - } 117 + // 配置设置操作
  118 + if (method.startsWith("thing.service.config.")) {
  119 + return IotDeviceTopicEnum.buildConfigSetTopic(productKey, deviceName);
  120 + }
167 121
168 - /**  
169 - * 处理 OTA 升级消息  
170 - *  
171 - * @param message 设备消息  
172 - */  
173 - private void handleOtaMessage(IotDeviceMessage message) {  
174 - // 通过 deviceId 获取设备信息  
175 - IotDeviceGetReqDTO reqDTO = new IotDeviceGetReqDTO();  
176 - reqDTO.setId(message.getDeviceId());  
177 - IotDeviceRespDTO deviceInfo = deviceApi.getDevice(reqDTO).getData();  
178 - if (deviceInfo == null) {  
179 - log.warn("[handleOtaMessage][设备信息不存在][deviceId: {}]", message.getDeviceId());  
180 - return; 122 + // OTA 升级操作
  123 + if (method.startsWith("thing.service.ota.")) {
  124 + return IotDeviceTopicEnum.buildOtaUpgradeTopic(productKey, deviceName);
181 } 125 }
182 126
183 - String productKey = deviceInfo.getProductKey();  
184 - String deviceName = deviceInfo.getDeviceName(); 127 + // 一般服务调用操作
  128 + if (method.startsWith("thing.service.")) {
  129 + // 排除属性、配置、OTA相关的服务调用
  130 + if (method.contains("property") || method.contains("config") || method.contains("ota")) {
  131 + return null; // 已在上面处理
  132 + }
  133 + // 从方法中提取服务标识符
  134 + String serviceIdentifier = method.substring("thing.service.".length());
  135 + return IotDeviceTopicEnum.buildServiceTopic(productKey, deviceName, serviceIdentifier);
  136 + }
185 137
186 - String topic = IotDeviceTopicEnum.buildOtaUpgradeTopic(productKey, deviceName);  
187 - JSONObject payload = buildDownstreamPayload(message, message.getMethod());  
188 - protocol.publishMessage(topic, payload.toString());  
189 - log.info("[handleOtaMessage][发送 OTA 升级消息][topic: {}]", topic); 138 + // 不支持的方法
  139 + return null;
190 } 140 }
191 141
192 /** 142 /**
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttHttpAuthProtocol.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;  
2 -  
3 -import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;  
4 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttHttpAuthHandler;  
5 -import io.vertx.core.AbstractVerticle;  
6 -import io.vertx.core.Vertx;  
7 -import io.vertx.core.http.HttpServer;  
8 -import io.vertx.ext.web.Router;  
9 -import io.vertx.ext.web.handler.BodyHandler;  
10 -import jakarta.annotation.PostConstruct;  
11 -import jakarta.annotation.PreDestroy;  
12 -import lombok.RequiredArgsConstructor;  
13 -import lombok.extern.slf4j.Slf4j;  
14 -  
15 -// TODO @haohao:可以融合到 IotMqttUpstreamProtocol 么?主要考虑,一个协议,一个 protocol;  
16 -/**  
17 - * IoT 网关 MQTT HTTP 认证协议:提供 HTTP 认证接口供 EMQX 调用  
18 - *  
19 - * @author 芋道源码  
20 - */  
21 -@RequiredArgsConstructor  
22 -@Slf4j  
23 -public class IotMqttHttpAuthProtocol extends AbstractVerticle {  
24 -  
25 - private final IotGatewayProperties.EmqxProperties emqxProperties;  
26 -  
27 - private HttpServer httpServer;  
28 - private Vertx vertx;  
29 -  
30 - @Override  
31 - @PostConstruct  
32 - public void start() {  
33 - log.info("[start][开始启动 MQTT HTTP 认证协议服务]");  
34 -  
35 - // 创建 Vertx 实例  
36 - this.vertx = Vertx.vertx();  
37 -  
38 - // 创建路由  
39 - Router router = Router.router(vertx);  
40 - router.route().handler(BodyHandler.create());  
41 -  
42 - // 创建 MQTT 认证处理器  
43 - IotMqttHttpAuthHandler authHandler = new IotMqttHttpAuthHandler();  
44 -  
45 - // 添加 MQTT 认证路由  
46 - router.post("/mqtt/auth/authenticate").handler(authHandler::authenticate);  
47 - router.post("/mqtt/auth/connected").handler(authHandler::connected);  
48 - router.post("/mqtt/auth/disconnected").handler(authHandler::disconnected);  
49 -  
50 - // 启动 HTTP 服务器,使用独立端口  
51 - int authPort = getAuthPort();  
52 - try {  
53 - httpServer = vertx.createHttpServer()  
54 - .requestHandler(router)  
55 - .listen(authPort)  
56 - .result();  
57 - log.info("[start][MQTT HTTP 认证协议启动成功,端口:{}]", authPort);  
58 - } catch (Exception e) {  
59 - log.error("[start][MQTT HTTP 认证协议启动失败]", e);  
60 - throw e;  
61 - }  
62 - }  
63 -  
64 - @Override  
65 - @PreDestroy  
66 - public void stop() {  
67 - if (httpServer != null) {  
68 - try {  
69 - httpServer.close().result();  
70 - log.info("[stop][MQTT HTTP 认证协议已停止]");  
71 - } catch (Exception e) {  
72 - log.error("[stop][MQTT HTTP 认证协议停止失败]", e);  
73 - }  
74 - }  
75 -  
76 - if (vertx != null) {  
77 - try {  
78 - vertx.close();  
79 - log.info("[stop][Vertx 已关闭]");  
80 - } catch (Exception e) {  
81 - log.warn("[stop][关闭 Vertx 异常]", e);  
82 - }  
83 - }  
84 - }  
85 -  
86 - // TODO @haohao:这里不搞默认,必须填写哈;  
87 - /**  
88 - * 获取认证服务端口  
89 - * 从配置中获取,默认使用 8090 端口  
90 - */  
91 - private int getAuthPort() {  
92 - return emqxProperties.getHttpAuthPort() != null ? emqxProperties.getHttpAuthPort() : 8090;  
93 - }  
94 -  
95 - public String getServerId() {  
96 - return "mqtt_auth_gateway_" + getAuthPort();  
97 - }  
98 -}  
99 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttUpstreamProtocol.http deleted
1 -### TODO 芋艿:这个文件是不是要留着;  
2 -### IoT MQTT HTTP 认证服务测试文件  
3 -### 用于测试独立的 MQTT HTTP 认证服务接口  
4 -### 服务由 IotMqttHttpAuthProtocol 启动,运行在配置的端口上(默认 8090)  
5 -### 注意:这些接口通常由 EMQX 调用,也可以用于手动测试  
6 -  
7 -### 配置说明:  
8 -### 在 application-local.yaml 中可以配置端口:  
9 -### yudao:  
10 -### iot:  
11 -### gateway:  
12 -### protocol:  
13 -### emqx:  
14 -### http-auth-port: 8090 # 可以修改为其他端口  
15 -  
16 -### 1. MQTT 设备认证接口 => 成功认证  
17 -POST http://localhost:8090/mqtt/auth/authenticate  
18 -Content-Type: application/json  
19 -  
20 -{  
21 - "clientid": "test_product.test_device",  
22 - "username": "test_device&test_product",  
23 - "password": "test_device_secret_hmac_password"  
24 -}  
25 -  
26 -### 2. MQTT 设备认证接口 => 参数不完整(clientid 为空)  
27 -POST http://localhost:8090/mqtt/auth/authenticate  
28 -Content-Type: application/json  
29 -  
30 -{  
31 - "clientid": "",  
32 - "username": "test_device&test_product",  
33 - "password": "test_device_secret"  
34 -}  
35 -  
36 -### 3. MQTT 设备认证接口 => 参数不完整(username 为空)  
37 -POST http://localhost:8090/mqtt/auth/authenticate  
38 -Content-Type: application/json  
39 -  
40 -{  
41 - "clientid": "test_product.test_device",  
42 - "username": "",  
43 - "password": "test_device_secret"  
44 -}  
45 -  
46 -### 4. MQTT 设备认证接口 => 参数不完整(password 为空)  
47 -POST http://localhost:8090/mqtt/auth/authenticate  
48 -Content-Type: application/json  
49 -  
50 -{  
51 - "clientid": "test_product.test_device",  
52 - "username": "test_device&test_product",  
53 - "password": ""  
54 -}  
55 -  
56 -### 5. MQTT 设备认证接口 => 认证失败(错误的设备密钥)  
57 -POST http://localhost:8090/mqtt/auth/authenticate  
58 -Content-Type: application/json  
59 -  
60 -{  
61 - "clientid": "test_product.test_device",  
62 - "username": "test_device&test_product",  
63 - "password": "wrong_password"  
64 -}  
65 -  
66 -### 6. EMQX 客户端连接事件钩子 => 设备上线  
67 -POST http://localhost:8090/mqtt/auth/connected  
68 -Content-Type: application/json  
69 -  
70 -{  
71 - "clientid": "test_product.test_device",  
72 - "username": "test_device&test_product",  
73 - "timestamp": 1703808000000,  
74 - "peername": "192.168.1.100:52036",  
75 - "sockname": "127.0.0.1:1883"  
76 -}  
77 -  
78 -### 7. EMQX 客户端连接事件钩子 => 用户名为空  
79 -POST http://localhost:8090/mqtt/auth/connected  
80 -Content-Type: application/json  
81 -  
82 -{  
83 - "clientid": "test_product.test_device",  
84 - "username": "",  
85 - "timestamp": 1703808000000,  
86 - "peername": "192.168.1.100:52036",  
87 - "sockname": "127.0.0.1:1883"  
88 -}  
89 -  
90 -### 8. EMQX 客户端断开连接事件钩子 => 设备下线(正常断开)  
91 -POST http://localhost:8090/mqtt/auth/disconnected  
92 -Content-Type: application/json  
93 -  
94 -{  
95 - "clientid": "test_product.test_device",  
96 - "username": "test_device&test_product",  
97 - "reason": "normal",  
98 - "timestamp": 1703808060000  
99 -}  
100 -  
101 -### 9. 使用自定义端口测试(如果配置了不同端口)  
102 -### 假设配置了端口 8091:  
103 -### POST http://localhost:8091/mqtt/auth/authenticate  
104 -### Content-Type: application/json  
105 -###  
106 -### {  
107 -### "clientid": "test_product.test_device",  
108 -### "username": "test_device&test_product",  
109 -### "password": "test_device_secret_hmac_password"  
110 -### }  
111 -  
112 -### EMQX 配置参考:  
113 -### authentication = [  
114 -### {  
115 -### mechanism = http  
116 -### method = post  
117 -### url = "http://localhost:8090/mqtt/auth/authenticate" # 使用配置的端口  
118 -### body = {  
119 -### clientid = "${clientid}"  
120 -### username = "${username}"  
121 -### password = "${password}"  
122 -### }  
123 -### headers = {  
124 -### "content-type" = "application/json"  
125 -### }  
126 -### }  
127 -### ]  
128 -###  
129 -### webhooks = [  
130 -### {  
131 -### name = "client_connected"  
132 -### url = "http://localhost:8090/mqtt/auth/connected" # 使用配置的端口  
133 -### events = ["client.connected"]  
134 -### },  
135 -### {  
136 -### name = "client_disconnected"  
137 -### url = "http://localhost:8090/mqtt/auth/disconnected" # 使用配置的端口  
138 -### events = ["client.disconnected"]  
139 -### }  
140 -### ]  
141 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttUpstreamProtocol.java
@@ -6,15 +6,18 @@ import cn.hutool.core.util.ObjUtil; @@ -6,15 +6,18 @@ import cn.hutool.core.util.ObjUtil;
6 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
8 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 8 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
9 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamRouter; 9 +import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttHttpAuthHandler;
  10 +import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamHandler;
10 import io.netty.handler.codec.mqtt.MqttQoS; 11 import io.netty.handler.codec.mqtt.MqttQoS;
11 import io.vertx.core.Vertx; 12 import io.vertx.core.Vertx;
12 import io.vertx.core.buffer.Buffer; 13 import io.vertx.core.buffer.Buffer;
  14 +import io.vertx.core.http.HttpServer;
  15 +import io.vertx.ext.web.Router;
  16 +import io.vertx.ext.web.handler.BodyHandler;
13 import io.vertx.mqtt.MqttClient; 17 import io.vertx.mqtt.MqttClient;
14 import io.vertx.mqtt.MqttClientOptions; 18 import io.vertx.mqtt.MqttClientOptions;
15 import jakarta.annotation.PostConstruct; 19 import jakarta.annotation.PostConstruct;
16 import jakarta.annotation.PreDestroy; 20 import jakarta.annotation.PreDestroy;
17 -import lombok.RequiredArgsConstructor;  
18 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
19 22
20 import java.util.List; 23 import java.util.List;
@@ -22,11 +25,14 @@ import java.util.concurrent.CompletableFuture; @@ -22,11 +25,14 @@ import java.util.concurrent.CompletableFuture;
22 import java.util.concurrent.TimeUnit; 25 import java.util.concurrent.TimeUnit;
23 26
24 /** 27 /**
25 - * IoT 网关 MQTT 协议:接收设备上行消息 28 + * IoT 网关 MQTT 统一协议
  29 + * <p>
  30 + * 集成了 MQTT 上行协议和 HTTP 认证协议的功能:
  31 + * 1. MQTT 客户端:连接 EMQX,处理设备上行和下行消息
  32 + * 2. HTTP 认证服务:为 EMQX 提供设备认证接口
26 * 33 *
27 * @author 芋道源码 34 * @author 芋道源码
28 */ 35 */
29 -@RequiredArgsConstructor  
30 @Slf4j 36 @Slf4j
31 public class IotMqttUpstreamProtocol { 37 public class IotMqttUpstreamProtocol {
32 38
@@ -37,30 +43,143 @@ public class IotMqttUpstreamProtocol { @@ -37,30 +43,143 @@ public class IotMqttUpstreamProtocol {
37 43
38 private final IotGatewayProperties.EmqxProperties emqxProperties; 44 private final IotGatewayProperties.EmqxProperties emqxProperties;
39 45
  46 + // 共享资源
40 private Vertx vertx; 47 private Vertx vertx;
  48 +
  49 + // MQTT 客户端相关
41 private MqttClient mqttClient; 50 private MqttClient mqttClient;
42 - private IotMqttUpstreamRouter messageRouter; 51 + private IotMqttUpstreamHandler upstreamHandler;
  52 +
  53 + // HTTP 认证服务相关
  54 + private HttpServer httpAuthServer;
  55 + private IotMqttHttpAuthHandler authHandler;
43 56
44 /** 57 /**
45 * 服务运行状态标志 58 * 服务运行状态标志
46 */ 59 */
47 private volatile boolean isRunning = false; 60 private volatile boolean isRunning = false;
48 61
  62 + /**
  63 + * 构造函数
  64 + */
  65 + public IotMqttUpstreamProtocol(IotGatewayProperties.EmqxProperties emqxProperties) {
  66 + this.emqxProperties = emqxProperties;
  67 + }
  68 +
49 @PostConstruct 69 @PostConstruct
50 public void start() { 70 public void start() {
51 if (isRunning) { 71 if (isRunning) {
52 - log.warn("[start][MQTT 协议服务已经在运行中,请勿重复启动]"); 72 + log.warn("[start][MQTT 统一协议服务已经在运行中,请勿重复启动]");
  73 + return;
  74 + }
  75 + log.info("[start][开始启动 MQTT 统一协议服务]");
  76 +
  77 + try {
  78 + // 1. 创建共享的 Vertx 实例
  79 + this.vertx = Vertx.vertx();
  80 + log.info("[start][共享 Vertx 实例创建成功]");
  81 +
  82 + // 2. 启动 HTTP 认证服务
  83 + startHttpAuthServer();
  84 +
  85 + // 3. 启动 MQTT 客户端
  86 + startMqttClient();
  87 +
  88 + isRunning = true;
  89 + log.info("[start][MQTT 统一协议服务启动完成]");
  90 + } catch (Exception e) {
  91 + log.error("[start][MQTT 统一协议服务启动失败]", e);
  92 + // 启动失败时清理资源
  93 + stop();
  94 + throw e;
  95 + }
  96 + }
  97 +
  98 + @PreDestroy
  99 + public void stop() {
  100 + if (!isRunning) {
  101 + log.warn("[stop][MQTT 统一协议服务已经停止,无需再次停止]");
53 return; 102 return;
54 } 103 }
55 - log.info("[start][开始启动 MQTT 协议服务]"); 104 + log.info("[stop][开始停止 MQTT 统一协议服务]");
  105 +
  106 + // 1. 停止 MQTT 客户端
  107 + stopMqttClient();
  108 +
  109 + // 2. 停止 HTTP 认证服务
  110 + stopHttpAuthServer();
  111 +
  112 + // 3. 关闭 Vertx 实例
  113 + if (vertx != null) {
  114 + try {
  115 + vertx.close();
  116 + log.info("[stop][Vertx 实例已关闭]");
  117 + } catch (Exception e) {
  118 + log.warn("[stop][关闭 Vertx 实例失败]", e);
  119 + }
  120 + }
  121 +
  122 + isRunning = false;
  123 + log.info("[stop][MQTT 统一协议服务已停止]");
  124 + }
  125 +
  126 + /**
  127 + * 启动 HTTP 认证服务
  128 + */
  129 + private void startHttpAuthServer() {
  130 + log.info("[startHttpAuthServer][开始启动 HTTP 认证服务]");
  131 +
  132 + // 创建路由
  133 + Router router = Router.router(vertx);
  134 + router.route().handler(BodyHandler.create());
  135 +
  136 + // 创建认证处理器
  137 + this.authHandler = new IotMqttHttpAuthHandler();
  138 +
  139 + // 添加认证路由
  140 + router.post("/mqtt/auth/authenticate").handler(authHandler::authenticate);
  141 + router.post("/mqtt/auth/connected").handler(authHandler::connected);
  142 + router.post("/mqtt/auth/disconnected").handler(authHandler::disconnected);
  143 +
  144 + // 启动 HTTP 服务器
  145 + int authPort = emqxProperties.getHttpAuthPort();
  146 + try {
  147 + httpAuthServer = vertx.createHttpServer()
  148 + .requestHandler(router)
  149 + .listen(authPort)
  150 + .result();
  151 + log.info("[startHttpAuthServer][HTTP 认证服务启动成功,端口:{}]", authPort);
  152 + } catch (Exception e) {
  153 + log.error("[startHttpAuthServer][HTTP 认证服务启动失败]", e);
  154 + throw e;
  155 + }
  156 + }
  157 +
  158 + /**
  159 + * 停止 HTTP 认证服务
  160 + */
  161 + private void stopHttpAuthServer() {
  162 + if (httpAuthServer != null) {
  163 + try {
  164 + httpAuthServer.close().result();
  165 + log.info("[stopHttpAuthServer][HTTP 认证服务已停止]");
  166 + } catch (Exception e) {
  167 + log.error("[stopHttpAuthServer][HTTP 认证服务停止失败]", e);
  168 + }
  169 + }
  170 + }
56 171
57 - // 初始化组件  
58 - this.vertx = Vertx.vertx();  
59 - this.messageRouter = new IotMqttUpstreamRouter(this); 172 + /**
  173 + * 启动 MQTT 客户端
  174 + */
  175 + private void startMqttClient() {
  176 + log.info("[startMqttClient][开始启动 MQTT 客户端]");
  177 +
  178 + // 初始化消息处理器
  179 + this.upstreamHandler = new IotMqttUpstreamHandler();
60 180
61 // 创建 MQTT 客户端 181 // 创建 MQTT 客户端
62 MqttClientOptions options = new MqttClientOptions() 182 MqttClientOptions options = new MqttClientOptions()
63 - // TODO @haohao:clientid 建议也配置文件;想通名字,会冲突哇?  
64 .setClientId("yudao-iot-gateway-" + IdUtil.fastSimpleUUID()) 183 .setClientId("yudao-iot-gateway-" + IdUtil.fastSimpleUUID())
65 .setUsername(emqxProperties.getMqttUsername()) 184 .setUsername(emqxProperties.getMqttUsername())
66 .setPassword(emqxProperties.getMqttPassword()) 185 .setPassword(emqxProperties.getMqttPassword())
@@ -72,14 +191,10 @@ public class IotMqttUpstreamProtocol { @@ -72,14 +191,10 @@ public class IotMqttUpstreamProtocol {
72 connectMqtt(); 191 connectMqtt();
73 } 192 }
74 193
75 - @PreDestroy  
76 - public void stop() {  
77 - if (!isRunning) {  
78 - log.warn("[stop][MQTT 协议服务已经停止,无需再次停止]");  
79 - return;  
80 - }  
81 - log.info("[stop][开始停止 MQTT 协议服务]");  
82 - 194 + /**
  195 + * 停止 MQTT 客户端
  196 + */
  197 + private void stopMqttClient() {
83 // 1. 取消 MQTT 主题订阅 198 // 1. 取消 MQTT 主题订阅
84 if (mqttClient != null && mqttClient.isConnected()) { 199 if (mqttClient != null && mqttClient.isConnected()) {
85 List<String> topicList = emqxProperties.getMqttTopics(); 200 List<String> topicList = emqxProperties.getMqttTopics();
@@ -87,9 +202,9 @@ public class IotMqttUpstreamProtocol { @@ -87,9 +202,9 @@ public class IotMqttUpstreamProtocol {
87 for (String topic : topicList) { 202 for (String topic : topicList) {
88 try { 203 try {
89 mqttClient.unsubscribe(topic); 204 mqttClient.unsubscribe(topic);
90 - log.debug("[stop][取消订阅主题: {}]", topic); 205 + log.debug("[stopMqttClient][取消订阅主题: {}]", topic);
91 } catch (Exception e) { 206 } catch (Exception e) {
92 - log.warn("[stop][取消订阅主题异常: {}]", topic, e); 207 + log.warn("[stopMqttClient][取消订阅主题异常: {}]", topic, e);
93 } 208 }
94 } 209 }
95 } 210 }
@@ -99,33 +214,20 @@ public class IotMqttUpstreamProtocol { @@ -99,33 +214,20 @@ public class IotMqttUpstreamProtocol {
99 try { 214 try {
100 if (mqttClient != null && mqttClient.isConnected()) { 215 if (mqttClient != null && mqttClient.isConnected()) {
101 mqttClient.disconnect(); 216 mqttClient.disconnect();
  217 + log.info("[stopMqttClient][MQTT 客户端已断开]");
102 } 218 }
103 } catch (Exception e) { 219 } catch (Exception e) {
104 - log.warn("[stop][关闭 MQTT 客户端异常]", e); 220 + log.warn("[stopMqttClient][关闭 MQTT 客户端异常]", e);
105 } 221 }
106 -  
107 - // 3. 关闭 Vertx  
108 - try {  
109 - if (vertx != null) {  
110 - vertx.close();  
111 - }  
112 - } catch (Exception e) {  
113 - log.warn("[stop][关闭 Vertx 异常]", e);  
114 - }  
115 -  
116 - // 4. 更新状态  
117 - isRunning = false;  
118 - log.info("[stop][MQTT 协议服务已停止]");  
119 } 222 }
120 223
121 /** 224 /**
122 * 连接 MQTT Broker 并订阅主题 225 * 连接 MQTT Broker 并订阅主题
123 */ 226 */
124 private void connectMqtt() { 227 private void connectMqtt() {
125 - // 检查必要的 MQTT 配置  
126 - // TODO @haohao:这些通过配置文件的 validate 去做哈,简化下;  
127 String host = emqxProperties.getMqttHost(); 228 String host = emqxProperties.getMqttHost();
128 Integer port = emqxProperties.getMqttPort(); 229 Integer port = emqxProperties.getMqttPort();
  230 +
129 if (StrUtil.isBlank(host)) { 231 if (StrUtil.isBlank(host)) {
130 String msg = "[connectMqtt][MQTT Host 为空,无法连接]"; 232 String msg = "[connectMqtt][MQTT Host 为空,无法连接]";
131 log.error(msg); 233 log.error(msg);
@@ -133,7 +235,7 @@ public class IotMqttUpstreamProtocol { @@ -133,7 +235,7 @@ public class IotMqttUpstreamProtocol {
133 } 235 }
134 if (port == null) { 236 if (port == null) {
135 log.warn("[connectMqtt][MQTT Port 为 null,使用默认端口 1883]"); 237 log.warn("[connectMqtt][MQTT Port 为 null,使用默认端口 1883]");
136 - port = 1883; // 默认 MQTT 端口 238 + port = 1883;
137 } 239 }
138 240
139 final Integer finalPort = port; 241 final Integer finalPort = port;
@@ -152,7 +254,6 @@ public class IotMqttUpstreamProtocol { @@ -152,7 +254,6 @@ public class IotMqttUpstreamProtocol {
152 // 订阅主题 254 // 订阅主题
153 subscribeToTopics(); 255 subscribeToTopics();
154 }) 256 })
155 - // TODO @haohao:这个要不要改成,必须连接成功?不做重试;不然启动也蛮危险的?  
156 .exceptionally(error -> { 257 .exceptionally(error -> {
157 log.error("[connectMqtt][连接 MQTT Broker 失败]", error); 258 log.error("[connectMqtt][连接 MQTT Broker 失败]", error);
158 reconnectWithDelay(); 259 reconnectWithDelay();
@@ -162,10 +263,9 @@ public class IotMqttUpstreamProtocol { @@ -162,10 +263,9 @@ public class IotMqttUpstreamProtocol {
162 // 等待连接完成 263 // 等待连接完成
163 try { 264 try {
164 connectFuture.get(10, TimeUnit.SECONDS); 265 connectFuture.get(10, TimeUnit.SECONDS);
165 - isRunning = true;  
166 - log.info("[connectMqtt][MQTT 协议服务启动完成]"); 266 + log.info("[connectMqtt][MQTT 客户端启动完成]");
167 } catch (Exception e) { 267 } catch (Exception e) {
168 - log.error("[connectMqtt][MQTT 协议服务启动失败]", e); 268 + log.error("[connectMqtt][MQTT 客户端启动失败]", e);
169 } 269 }
170 } 270 }
171 271
@@ -173,7 +273,7 @@ public class IotMqttUpstreamProtocol { @@ -173,7 +273,7 @@ public class IotMqttUpstreamProtocol {
173 * 设置 MQTT 消息处理器 273 * 设置 MQTT 消息处理器
174 */ 274 */
175 private void setupMessageHandler() { 275 private void setupMessageHandler() {
176 - mqttClient.publishHandler(messageRouter::route); 276 + mqttClient.publishHandler(upstreamHandler::handle);
177 log.debug("[setupMessageHandler][MQTT 消息处理器设置完成]"); 277 log.debug("[setupMessageHandler][MQTT 消息处理器设置完成]");
178 } 278 }
179 279
@@ -182,76 +282,54 @@ public class IotMqttUpstreamProtocol { @@ -182,76 +282,54 @@ public class IotMqttUpstreamProtocol {
182 */ 282 */
183 private void subscribeToTopics() { 283 private void subscribeToTopics() {
184 List<String> topicList = emqxProperties.getMqttTopics(); 284 List<String> topicList = emqxProperties.getMqttTopics();
185 - // TODO @haohao:建议 topicList 直接 validate 校验  
186 if (CollUtil.isEmpty(topicList)) { 285 if (CollUtil.isEmpty(topicList)) {
187 - log.warn("[subscribeToTopics][未配置 MQTT 主题,使用默认主题]");  
188 - topicList = List.of("/sys/#"); // 默认订阅所有系统主题 286 + log.warn("[subscribeToTopics][没有配置要订阅的主题]");
  287 + return;
189 } 288 }
190 289
191 for (String topic : topicList) { 290 for (String topic : topicList) {
192 - // TODO @haohao:直接 validate 校验;嘿嘿,主要保证核心逻辑,简单点  
193 - if (StrUtil.isBlank(topic)) {  
194 - log.warn("[subscribeToTopics][跳过空主题]");  
195 - continue;  
196 - }  
197 -  
198 - mqttClient.subscribe(topic, DEFAULT_QOS.value())  
199 - .onSuccess(ack -> log.info("[subscribeToTopics][订阅主题成功: {}]", topic))  
200 - .onFailure(err -> log.error("[subscribeToTopics][订阅主题失败: {}]", topic, err)); 291 + mqttClient.subscribe(topic, DEFAULT_QOS.value(), subscribeResult -> {
  292 + if (subscribeResult.succeeded()) {
  293 + log.info("[subscribeToTopics][订阅主题成功: {}]", topic);
  294 + } else {
  295 + log.error("[subscribeToTopics][订阅主题失败: {}]", topic, subscribeResult.cause());
  296 + }
  297 + });
201 } 298 }
202 } 299 }
203 300
204 /** 301 /**
205 - * 重连 MQTT 客户端 302 + * 延迟重连
206 */ 303 */
207 private void reconnectWithDelay() { 304 private void reconnectWithDelay() {
208 - if (!isRunning) {  
209 - log.info("[reconnectWithDelay][服务已停止,不再尝试重连]");  
210 - return;  
211 - }  
212 -  
213 - // 默认重连延迟 5 秒  
214 - int reconnectDelayMs = 5000;  
215 - vertx.setTimer(reconnectDelayMs, id -> {  
216 - log.info("[reconnectWithDelay][开始重新连接 MQTT]");  
217 - connectMqtt(); 305 + vertx.setTimer(5000, timerId -> {
  306 + if (isRunning && (mqttClient == null || !mqttClient.isConnected())) {
  307 + log.info("[reconnectWithDelay][开始重连 MQTT Broker]");
  308 + connectMqtt();
  309 + }
218 }); 310 });
219 } 311 }
220 312
221 /** 313 /**
222 - * 发布消息到 MQTT 314 + * 发布消息到 MQTT Broker
223 * 315 *
224 * @param topic 主题 316 * @param topic 主题
225 * @param payload 消息内容 317 * @param payload 消息内容
226 */ 318 */
227 public void publishMessage(String topic, String payload) { 319 public void publishMessage(String topic, String payload) {
228 - if (mqttClient == null || !mqttClient.isConnected()) {  
229 - log.warn("[publishMessage][MQTT 客户端未连接,无法发送消息][topic: {}]", topic);  
230 - return; 320 + if (mqttClient != null && mqttClient.isConnected()) {
  321 + mqttClient.publish(topic, Buffer.buffer(payload), DEFAULT_QOS, false, false);
  322 + log.debug("[publishMessage][发布消息成功][topic: {}]", topic);
  323 + } else {
  324 + log.warn("[publishMessage][MQTT 客户端未连接,无法发布消息][topic: {}]", topic);
231 } 325 }
232 -  
233 - mqttClient.publish(topic, Buffer.buffer(payload), DEFAULT_QOS, false, false)  
234 - .onSuccess(v -> log.debug("[publishMessage][发送消息成功][topic: {}]", topic))  
235 - .onFailure(err -> log.error("[publishMessage][发送消息失败][topic: {}]", topic, err));  
236 } 326 }
237 327
238 /** 328 /**
239 * 获取服务器 ID 329 * 获取服务器 ID
240 - *  
241 - * @return 服务器 ID  
242 */ 330 */
243 public String getServerId() { 331 public String getServerId() {
244 return IotDeviceMessageUtils.generateServerId(emqxProperties.getMqttPort()); 332 return IotDeviceMessageUtils.generateServerId(emqxProperties.getMqttPort());
245 } 333 }
246 334
247 - // TODO @haohao:这个要删除哇?  
248 - /**  
249 - * 获取 MQTT 客户端  
250 - *  
251 - * @return MQTT 客户端  
252 - */  
253 - public MqttClient getMqttClient() {  
254 - return mqttClient;  
255 - }  
256 -  
257 } 335 }
258 \ No newline at end of file 336 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttAbstractHandler.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;  
2 -  
3 -import lombok.extern.slf4j.Slf4j;  
4 -  
5 -/**  
6 - * IoT 网关 MQTT 协议的路由处理器抽象基类  
7 - * <p>  
8 - * 提供通用的处理方法,所有 MQTT 消息处理器都应继承此类  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -@Slf4j  
13 -public abstract class IotMqttAbstractHandler {  
14 -  
15 - /**  
16 - * 处理 MQTT 消息  
17 - *  
18 - * @param topic 主题  
19 - * @param payload 消息内容  
20 - */  
21 - public abstract void handle(String topic, String payload);  
22 -  
23 - /**  
24 - * 解析主题,获取主题各部分  
25 - *  
26 - * @param topic 主题  
27 - * @return 主题各部分数组,如果解析失败返回 null  
28 - */  
29 - protected String[] parseTopic(String topic) {  
30 - String[] topicParts = topic.split("/");  
31 - if (topicParts.length < 7) {  
32 - log.warn("[parseTopic][主题格式不正确][topic: {}]", topic);  
33 - return null;  
34 - }  
35 - return topicParts;  
36 - }  
37 -  
38 -}  
39 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttEventHandler.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;  
2 -  
3 -import cn.hutool.json.JSONObject;  
4 -import cn.hutool.json.JSONUtil;  
5 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
6 -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;  
7 -import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;  
8 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;  
9 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
10 -import lombok.RequiredArgsConstructor;  
11 -import lombok.extern.slf4j.Slf4j;  
12 -  
13 -import java.nio.charset.StandardCharsets;  
14 -  
15 -/**  
16 - * IoT 网关 MQTT 事件处理器  
17 - * <p>  
18 - * 处理设备事件相关的 MQTT 消息  
19 - *  
20 - * @author 芋道源码  
21 - */  
22 -@RequiredArgsConstructor  
23 -@Slf4j  
24 -public class IotMqttEventHandler extends IotMqttAbstractHandler {  
25 -  
26 - private final IotMqttUpstreamProtocol protocol;  
27 - private final IotDeviceMessageProducer deviceMessageProducer;  
28 - private final IotDeviceMessageService deviceMessageService;  
29 -  
30 - @Override  
31 - public void handle(String topic, String payload) {  
32 - try {  
33 - log.info("[handle][接收到设备事件上报][topic: {}]", topic);  
34 -  
35 - // 解析消息内容  
36 - String[] topicParts = parseTopic(topic);  
37 - if (topicParts == null) {  
38 - return;  
39 - }  
40 -  
41 - // 构建设备消息  
42 - String productKey = topicParts[2];  
43 - String deviceName = topicParts[3];  
44 - String eventIdentifier = getEventIdentifier(topicParts, topic);  
45 - if (eventIdentifier == null) {  
46 - return;  
47 - }  
48 -  
49 - // 使用 IotDeviceMessageService 解码消息  
50 - byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);  
51 - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(  
52 - messageBytes, productKey, deviceName);  
53 -  
54 - // 发送消息  
55 - deviceMessageProducer.sendDeviceMessage(message);  
56 - log.info("[handle][处理设备事件上报成功][topic: {}]", topic);  
57 -  
58 - // 发送响应消息  
59 - // TODO @haohao:这里应该只 ack 哈;reply 在 biz 业务处理了。handleUpstreamDeviceMessage  
60 - String method = "thing.event." + eventIdentifier + ".post";  
61 - sendResponse(topic, JSONUtil.parseObj(payload), method);  
62 - } catch (Exception e) {  
63 - log.error("[handle][处理设备事件上报失败][topic: {}][payload: {}]", topic, payload, e);  
64 - }  
65 - }  
66 -  
67 - /**  
68 - * 从主题部分中获取事件标识符  
69 - *  
70 - * @param topicParts 主题各部分  
71 - * @param topic 原始主题,用于日志  
72 - * @return 事件标识符,如果获取失败返回 null  
73 - */  
74 - private String getEventIdentifier(String[] topicParts, String topic) {  
75 - try {  
76 - return topicParts[6];  
77 - } catch (ArrayIndexOutOfBoundsException e) {  
78 - log.warn("[getEventIdentifier][无法从主题中获取事件标识符][topic: {}]", topic);  
79 - return null;  
80 - }  
81 - }  
82 -  
83 - /**  
84 - * 发送响应消息  
85 - *  
86 - * @param topic 原始主题  
87 - * @param jsonObject 原始消息 JSON 对象  
88 - * @param method 响应方法  
89 - */  
90 - private void sendResponse(String topic, JSONObject jsonObject, String method) {  
91 - try {  
92 - String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);  
93 -  
94 - // 构建响应消息  
95 - JSONObject response = new JSONObject();  
96 - response.set("id", jsonObject.getStr("id"));  
97 - response.set("code", 200);  
98 - response.set("method", method);  
99 - response.set("data", new JSONObject());  
100 -  
101 - // 发送响应  
102 - protocol.publishMessage(replyTopic, response.toString());  
103 - log.debug("[sendResponse][发送响应消息成功][topic: {}]", replyTopic);  
104 - } catch (Exception e) {  
105 - log.error("[sendResponse][发送响应消息失败][topic: {}]", topic, e);  
106 - }  
107 - }  
108 -  
109 -}  
110 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttPropertyHandler.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;  
2 -  
3 -import cn.hutool.json.JSONObject;  
4 -import cn.hutool.json.JSONUtil;  
5 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
6 -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;  
7 -import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;  
8 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;  
9 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
10 -import lombok.RequiredArgsConstructor;  
11 -import lombok.extern.slf4j.Slf4j;  
12 -  
13 -import java.nio.charset.StandardCharsets;  
14 -  
15 -/**  
16 - * IoT 网关 MQTT 属性处理器  
17 - * <p>  
18 - * 处理设备属性相关的 MQTT 消息  
19 - *  
20 - * @author 芋道源码  
21 - */  
22 -@RequiredArgsConstructor  
23 -@Slf4j  
24 -public class IotMqttPropertyHandler extends IotMqttAbstractHandler {  
25 -  
26 - private final IotMqttUpstreamProtocol protocol;  
27 - private final IotDeviceMessageProducer deviceMessageProducer;  
28 - private final IotDeviceMessageService deviceMessageService;  
29 -  
30 - @Override  
31 - public void handle(String topic, String payload) {  
32 - if (topic.endsWith(IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic())) {  
33 - // 属性上报  
34 - handlePropertyPost(topic, payload);  
35 - } else if (topic.contains(IotDeviceTopicEnum.PROPERTY_SET_TOPIC.getTopic())) {  
36 - // 属性设置响应  
37 - handlePropertySetReply(topic, payload);  
38 - } else if (topic.contains(IotDeviceTopicEnum.PROPERTY_GET_TOPIC.getTopic())) {  
39 - // 属性获取响应  
40 - handlePropertyGetReply(topic, payload);  
41 - } else {  
42 - log.warn("[handle][未知的属性主题][topic: {}]", topic);  
43 - }  
44 - }  
45 -  
46 - /**  
47 - * 处理设备属性上报消息  
48 - *  
49 - * @param topic 主题  
50 - * @param payload 消息内容  
51 - */  
52 - private void handlePropertyPost(String topic, String payload) {  
53 - try {  
54 - log.info("[handlePropertyPost][接收到设备属性上报][topic: {}]", topic);  
55 -  
56 - // 解析主题获取设备信息  
57 - String[] topicParts = parseTopic(topic);  
58 - if (topicParts == null) {  
59 - return;  
60 - }  
61 -  
62 - String productKey = topicParts[2];  
63 - String deviceName = topicParts[3];  
64 -  
65 - // 使用 IotDeviceMessageService 解码消息  
66 - byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);  
67 - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(  
68 - messageBytes, productKey, deviceName);  
69 -  
70 - // 发送消息  
71 - deviceMessageProducer.sendDeviceMessage(message);  
72 - log.info("[handlePropertyPost][处理设备属性上报成功][topic: {}]", topic);  
73 -  
74 - // 发送响应消息  
75 - sendResponse(topic, JSONUtil.parseObj(payload), "thing.event.property.post");  
76 - } catch (Exception e) {  
77 - log.error("[handlePropertyPost][处理设备属性上报失败][topic: {}][payload: {}]", topic, payload, e);  
78 - }  
79 - }  
80 -  
81 - /**  
82 - * 处理属性设置响应消息  
83 - *  
84 - * @param topic 主题  
85 - * @param payload 消息内容  
86 - */  
87 - private void handlePropertySetReply(String topic, String payload) {  
88 - try {  
89 - log.info("[handlePropertySetReply][接收到属性设置响应][topic: {}]", topic);  
90 -  
91 - // 解析主题获取设备信息  
92 - String[] topicParts = parseTopic(topic);  
93 - if (topicParts == null) {  
94 - return;  
95 - }  
96 -  
97 - String productKey = topicParts[2];  
98 - String deviceName = topicParts[3];  
99 -  
100 - // 使用 IotDeviceMessageService 解码消息  
101 - byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);  
102 - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(  
103 - messageBytes, productKey, deviceName);  
104 -  
105 - // 发送消息  
106 - deviceMessageProducer.sendDeviceMessage(message);  
107 - log.info("[handlePropertySetReply][处理属性设置响应成功][topic: {}]", topic);  
108 - } catch (Exception e) {  
109 - log.error("[handlePropertySetReply][处理属性设置响应失败][topic: {}][payload: {}]", topic, payload, e);  
110 - }  
111 - }  
112 -  
113 - /**  
114 - * 处理属性获取响应消息  
115 - *  
116 - * @param topic 主题  
117 - * @param payload 消息内容  
118 - */  
119 - private void handlePropertyGetReply(String topic, String payload) {  
120 - try {  
121 - log.info("[handlePropertyGetReply][接收到属性获取响应][topic: {}]", topic);  
122 -  
123 - // 解析主题获取设备信息  
124 - String[] topicParts = parseTopic(topic);  
125 - if (topicParts == null) {  
126 - return;  
127 - }  
128 -  
129 - String productKey = topicParts[2];  
130 - String deviceName = topicParts[3];  
131 -  
132 - // 使用 IotDeviceMessageService 解码消息  
133 - byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);  
134 - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(  
135 - messageBytes, productKey, deviceName);  
136 -  
137 - // 发送消息  
138 - deviceMessageProducer.sendDeviceMessage(message);  
139 - log.info("[handlePropertyGetReply][处理属性获取响应成功][topic: {}]", topic);  
140 - } catch (Exception e) {  
141 - log.error("[handlePropertyGetReply][处理属性获取响应失败][topic: {}][payload: {}]", topic, payload, e);  
142 - }  
143 - }  
144 -  
145 - /**  
146 - * 发送响应消息  
147 - *  
148 - * @param topic 原始主题  
149 - * @param jsonObject 原始消息 JSON 对象  
150 - * @param method 响应方法  
151 - */  
152 - private void sendResponse(String topic, JSONObject jsonObject, String method) {  
153 - try {  
154 - String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);  
155 -  
156 - // 构建响应消息  
157 - JSONObject response = new JSONObject();  
158 - response.set("id", jsonObject.getStr("id"));  
159 - response.set("code", 200);  
160 - response.set("method", method);  
161 - response.set("data", new JSONObject());  
162 -  
163 - // 发送响应  
164 - protocol.publishMessage(replyTopic, response.toString());  
165 - log.debug("[sendResponse][发送响应消息成功][topic: {}]", replyTopic);  
166 - } catch (Exception e) {  
167 - log.error("[sendResponse][发送响应消息失败][topic: {}]", topic, e);  
168 - }  
169 - }  
170 -  
171 -}  
172 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttServiceHandler.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;  
2 -  
3 -import cn.hutool.json.JSONObject;  
4 -import cn.hutool.json.JSONUtil;  
5 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
6 -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;  
7 -import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;  
8 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;  
9 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
10 -import lombok.RequiredArgsConstructor;  
11 -import lombok.extern.slf4j.Slf4j;  
12 -  
13 -import java.nio.charset.StandardCharsets;  
14 -  
15 -/**  
16 - * IoT 网关 MQTT 服务处理器  
17 - * <p>  
18 - * 处理设备服务调用相关的 MQTT 消息  
19 - *  
20 - * @author 芋道源码  
21 - */  
22 -@RequiredArgsConstructor  
23 -@Slf4j  
24 -public class IotMqttServiceHandler extends IotMqttAbstractHandler {  
25 -  
26 - private final IotMqttUpstreamProtocol protocol;  
27 - private final IotDeviceMessageProducer deviceMessageProducer;  
28 - private final IotDeviceMessageService deviceMessageService;  
29 -  
30 - @Override  
31 - public void handle(String topic, String payload) {  
32 - try {  
33 - log.info("[handle][接收到设备服务调用响应][topic: {}]", topic);  
34 -  
35 - // 解析消息内容  
36 - String[] topicParts = parseTopic(topic);  
37 - if (topicParts == null) {  
38 - return;  
39 - }  
40 -  
41 - // 构建设备消息  
42 - String productKey = topicParts[2];  
43 - String deviceName = topicParts[3];  
44 - String serviceIdentifier = getServiceIdentifier(topicParts, topic);  
45 - if (serviceIdentifier == null) {  
46 - return;  
47 - }  
48 -  
49 - // 使用 IotDeviceMessageService 解码消息  
50 - byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);  
51 - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(  
52 - messageBytes, productKey, deviceName);  
53 -  
54 - // 发送消息  
55 - deviceMessageProducer.sendDeviceMessage(message);  
56 - log.info("[handle][处理设备服务调用响应成功][topic: {}]", topic);  
57 -  
58 - // 发送响应消息  
59 - String method = "thing.service." + serviceIdentifier;  
60 - sendResponse(topic, JSONUtil.parseObj(payload), method);  
61 - } catch (Exception e) {  
62 - log.error("[handle][处理设备服务调用响应失败][topic: {}][payload: {}]", topic, payload, e);  
63 - }  
64 - }  
65 -  
66 - /**  
67 - * 从主题部分中获取服务标识符  
68 - *  
69 - * @param topicParts 主题各部分  
70 - * @param topic 原始主题,用于日志  
71 - * @return 服务标识符,如果获取失败返回 null  
72 - */  
73 - private String getServiceIdentifier(String[] topicParts, String topic) {  
74 - try {  
75 - // 服务主题格式:/sys/{productKey}/{deviceName}/thing/service/{serviceIdentifier}  
76 - return topicParts[6];  
77 - } catch (ArrayIndexOutOfBoundsException e) {  
78 - log.warn("[getServiceIdentifier][无法从主题中获取服务标识符][topic: {}]", topic);  
79 - return null;  
80 - }  
81 - }  
82 -  
83 - /**  
84 - * 发送响应消息  
85 - *  
86 - * @param topic 原始主题  
87 - * @param jsonObject 原始消息 JSON 对象  
88 - * @param method 响应方法  
89 - */  
90 - private void sendResponse(String topic, JSONObject jsonObject, String method) {  
91 - try {  
92 - String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);  
93 -  
94 - // 构建响应消息  
95 - JSONObject response = new JSONObject();  
96 - response.set("id", jsonObject.getStr("id"));  
97 - response.set("code", 200);  
98 - response.set("method", method);  
99 - response.set("data", new JSONObject());  
100 -  
101 - // 发送响应  
102 - protocol.publishMessage(replyTopic, response.toString());  
103 - log.debug("[sendResponse][发送响应消息成功][topic: {}]", replyTopic);  
104 - } catch (Exception e) {  
105 - log.error("[sendResponse][发送响应消息失败][topic: {}]", topic, e);  
106 - }  
107 - }  
108 -  
109 -}  
110 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.hutool.extra.spring.SpringUtil;
  5 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  6 +import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
  7 +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
  8 +import io.vertx.mqtt.messages.MqttPublishMessage;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +
  11 +import java.nio.charset.StandardCharsets;
  12 +
  13 +/**
  14 + * IoT 网关 MQTT 协议的【上行】处理器
  15 + * <p>
  16 + * 处理设备上行消息,包括事件上报、属性上报、服务调用响应等
  17 + *
  18 + * @author 芋道源码
  19 + */
  20 +@Slf4j
  21 +public class IotMqttUpstreamHandler {
  22 +
  23 + private final IotDeviceMessageProducer deviceMessageProducer;
  24 + private final IotDeviceMessageService deviceMessageService;
  25 +
  26 + public IotMqttUpstreamHandler() {
  27 + this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class);
  28 + this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
  29 + }
  30 +
  31 + /**
  32 + * 处理 MQTT 发布消息
  33 + */
  34 + public void handle(MqttPublishMessage message) {
  35 + String topic = message.topicName();
  36 + String payload = message.payload().toString(StandardCharsets.UTF_8);
  37 +
  38 + if (StrUtil.isBlank(topic)) {
  39 + log.warn("[handle][主题为空,忽略消息]");
  40 + return;
  41 + }
  42 +
  43 + if (StrUtil.isBlank(payload)) {
  44 + log.warn("[handle][消息内容为空][topic: {}]", topic);
  45 + return;
  46 + }
  47 +
  48 + log.debug("[handle][收到 MQTT 消息][topic: {}]", topic);
  49 + handle(topic, payload);
  50 + }
  51 +
  52 + /**
  53 + * 处理 MQTT 消息
  54 + *
  55 + * @param topic 主题
  56 + * @param payload 消息内容
  57 + */
  58 + public void handle(String topic, String payload) {
  59 + try {
  60 + // 1. 识别并验证消息类型
  61 + String messageType = getMessageType(topic);
  62 + if (messageType == null) {
  63 + log.warn("[handle][未知的消息类型][topic: {}]", topic);
  64 + return;
  65 + }
  66 +
  67 + // 2. 处理消息
  68 + processMessage(topic, payload, messageType);
  69 +
  70 + } catch (Exception e) {
  71 + log.error("[handle][处理消息失败][topic: {}][payload: {}]", topic, payload, e);
  72 + }
  73 + }
  74 +
  75 + /**
  76 + * 处理消息的统一逻辑
  77 + */
  78 + private void processMessage(String topic, String payload, String messageType) {
  79 + log.info("[processMessage][接收到{}][topic: {}]", messageType, topic);
  80 +
  81 + // 解析主题获取设备信息
  82 + String[] topicParts = parseTopic(topic);
  83 + if (topicParts == null) {
  84 + return;
  85 + }
  86 +
  87 + String productKey = topicParts[2];
  88 + String deviceName = topicParts[3];
  89 +
  90 + // 解码消息
  91 + byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);
  92 + IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(
  93 + messageBytes, productKey, deviceName);
  94 +
  95 + // 发送消息到队列
  96 + deviceMessageProducer.sendDeviceMessage(message);
  97 +
  98 + // 记录成功日志
  99 + log.info("[processMessage][处理{}成功][topic: {}]", messageType, topic);
  100 + }
  101 +
  102 + /**
  103 + * 识别消息类型
  104 + *
  105 + * @param topic 主题
  106 + * @return 消息类型描述,如果不支持返回 null
  107 + */
  108 + private String getMessageType(String topic) {
  109 + // 设备事件上报: /sys/{productKey}/{deviceName}/thing/event/{eventIdentifier}/post
  110 + if (topic.contains("/thing/event/") && topic.endsWith("/post")) {
  111 + return "设备事件上报";
  112 + }
  113 +
  114 + // 设备属性操作: /sys/{productKey}/{deviceName}/thing/property/post
  115 + // 或属性响应: /sys/{productKey}/{deviceName}/thing/service/property/set_reply
  116 + if (topic.endsWith("/thing/property/post") ||
  117 + topic.contains("/thing/service/property/set") ||
  118 + topic.contains("/thing/service/property/get")) {
  119 + return "设备属性操作";
  120 + }
  121 +
  122 + // 设备服务调用: /sys/{productKey}/{deviceName}/thing/service/{serviceIdentifier}
  123 + if (topic.contains("/thing/service/") && !topic.contains("/property/")) {
  124 + return "设备服务调用";
  125 + }
  126 +
  127 + // 不支持的消息类型
  128 + return null;
  129 + }
  130 +
  131 + /**
  132 + * 解析主题,获取主题各部分
  133 + *
  134 + * @param topic 主题
  135 + * @return 主题各部分数组,如果解析失败返回 null
  136 + */
  137 + private String[] parseTopic(String topic) {
  138 + String[] topicParts = topic.split("/");
  139 + if (topicParts.length < 7) {
  140 + log.warn("[parseTopic][主题格式不正确][topic: {}]", topic);
  141 + return null;
  142 + }
  143 + return topicParts;
  144 + }
  145 +}
0 \ No newline at end of file 146 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamRouter.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;  
2 -  
3 -import cn.hutool.core.util.StrUtil;  
4 -import cn.hutool.extra.spring.SpringUtil;  
5 -import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;  
6 -import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;  
7 -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;  
8 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
9 -import io.vertx.mqtt.messages.MqttPublishMessage;  
10 -import lombok.RequiredArgsConstructor;  
11 -import lombok.extern.slf4j.Slf4j;  
12 -  
13 -/**  
14 - * IoT 网关 MQTT 上行路由器  
15 - * <p>  
16 - * 根据消息主题路由到不同的处理器  
17 - *  
18 - * @author 芋道源码  
19 - */  
20 -@RequiredArgsConstructor  
21 -@Slf4j  
22 -public class IotMqttUpstreamRouter {  
23 -  
24 - private final IotMqttUpstreamProtocol protocol;  
25 - private final IotDeviceMessageProducer deviceMessageProducer;  
26 - private final IotDeviceMessageService deviceMessageService;  
27 -  
28 - // 处理器实例  
29 - private IotMqttPropertyHandler propertyHandler;  
30 - private IotMqttEventHandler eventHandler;  
31 - private IotMqttServiceHandler serviceHandler;  
32 -  
33 - public IotMqttUpstreamRouter(IotMqttUpstreamProtocol protocol) {  
34 - this.protocol = protocol;  
35 - this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class);  
36 - this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);  
37 - // 初始化处理器  
38 - this.propertyHandler = new IotMqttPropertyHandler(protocol, deviceMessageProducer, deviceMessageService);  
39 - this.eventHandler = new IotMqttEventHandler(protocol, deviceMessageProducer, deviceMessageService);  
40 - this.serviceHandler = new IotMqttServiceHandler(protocol, deviceMessageProducer, deviceMessageService);  
41 - }  
42 -  
43 - /**  
44 - * 路由 MQTT 消息  
45 - *  
46 - * @param message MQTT 发布消息  
47 - */  
48 - public void route(MqttPublishMessage message) {  
49 - String topic = message.topicName();  
50 - String payload = message.payload().toString();  
51 - log.info("[route][接收到 MQTT 消息][topic: {}][payload: {}]", topic, payload);  
52 -  
53 - try {  
54 - if (StrUtil.isEmpty(payload)) {  
55 - log.warn("[route][消息内容为空][topic: {}]", topic);  
56 - return;  
57 - }  
58 -  
59 - // 根据主题路由到相应的处理器  
60 - if (isPropertyTopic(topic)) {  
61 - propertyHandler.handle(topic, payload);  
62 - } else if (isEventTopic(topic)) {  
63 - eventHandler.handle(topic, payload);  
64 - } else if (isServiceTopic(topic)) {  
65 - serviceHandler.handle(topic, payload);  
66 - } else {  
67 - log.warn("[route][未知的消息类型][topic: {}]", topic);  
68 - }  
69 - } catch (Exception e) {  
70 - log.error("[route][处理 MQTT 消息失败][topic: {}][payload: {}]", topic, payload, e);  
71 - }  
72 - }  
73 -  
74 - /**  
75 - * 判断是否为属性相关主题  
76 - *  
77 - * @param topic 主题  
78 - * @return 是否为属性主题  
79 - */  
80 - private boolean isPropertyTopic(String topic) {  
81 - return topic.endsWith(IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic()) ||  
82 - topic.contains(IotDeviceTopicEnum.PROPERTY_SET_TOPIC.getTopic()) ||  
83 - topic.contains(IotDeviceTopicEnum.PROPERTY_GET_TOPIC.getTopic());  
84 - }  
85 -  
86 - /**  
87 - * 判断是否为事件相关主题  
88 - *  
89 - * @param topic 主题  
90 - * @return 是否为事件主题  
91 - */  
92 - private boolean isEventTopic(String topic) {  
93 - return topic.contains(IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic()) &&  
94 - topic.endsWith(IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic());  
95 - }  
96 -  
97 - /**  
98 - * 判断是否为服务相关主题  
99 - *  
100 - * @param topic 主题  
101 - * @return 是否为服务主题  
102 - */  
103 - private boolean isServiceTopic(String topic) {  
104 - return topic.contains(IotDeviceTopicEnum.SERVICE_TOPIC_PREFIX.getTopic()) &&  
105 - !isPropertyTopic(topic); // 排除属性相关的服务调用  
106 - }  
107 -  
108 -}  
109 \ No newline at end of file 0 \ No newline at end of file