Commit e89fc2bfbd1ac7d88ff59bb4a580cecc564a20fb

Authored by YunaiV
1 parent f4ba6e75

feat(iot):【协议改造】http 初步改造

Showing 40 changed files with 1467 additions and 633 deletions
yudao-module-iot/yudao-module-iot-biz/pom.xml
@@ -101,18 +101,6 @@ @@ -101,18 +101,6 @@
101 <artifactId>spring-boot-starter-amqp</artifactId> 101 <artifactId>spring-boot-starter-amqp</artifactId>
102 <optional>true</optional> 102 <optional>true</optional>
103 </dependency> 103 </dependency>
104 -  
105 - <!-- IoT 网络组件:接收来自设备的上行数据 -->  
106 - <!-- <dependency>-->  
107 - <!-- <groupId>cn.iocoder.boot</groupId>-->  
108 - <!-- <artifactId>yudao-module-iot-net-component-http</artifactId>-->  
109 - <!-- <version>${revision}</version>-->  
110 - <!-- </dependency>-->  
111 - <!-- <dependency>-->  
112 - <!-- <groupId>cn.iocoder.boot</groupId>-->  
113 - <!-- <artifactId>yudao-module-iot-net-component-emqx</artifactId>-->  
114 - <!-- <version>${revision}</version>-->  
115 - <!-- </dependency>-->  
116 </dependencies> 104 </dependencies>
117 105
118 </project> 106 </project>
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotProtocolTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.core.enums;
  2 +
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  5 +import lombok.Getter;
  6 +import lombok.RequiredArgsConstructor;
  7 +
  8 +import java.util.Arrays;
  9 +
  10 +/**
  11 + * IoT 协议类型枚举
  12 + *
  13 + * 用于定义传输层协议类型
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@RequiredArgsConstructor
  18 +@Getter
  19 +public enum IotProtocolTypeEnum implements ArrayValuable<String> {
  20 +
  21 + TCP("tcp"),
  22 + UDP("udp"),
  23 + WEBSOCKET("websocket"),
  24 + HTTP("http"),
  25 + MQTT("mqtt"),
  26 + EMQX("emqx"),
  27 + COAP("coap"),
  28 + MODBUS_TCP("modbus_tcp");
  29 +
  30 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotProtocolTypeEnum::getType).toArray(String[]::new);
  31 +
  32 + /**
  33 + * 类型
  34 + */
  35 + private final String type;
  36 +
  37 + @Override
  38 + public String[] array() {
  39 + return ARRAYS;
  40 + }
  41 +
  42 + public static IotProtocolTypeEnum of(String type) {
  43 + return ArrayUtil.firstMatch(e -> e.getType().equals(type), values());
  44 + }
  45 +
  46 +}
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotSerializeTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.core.enums;
  2 +
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  5 +import lombok.Getter;
  6 +import lombok.RequiredArgsConstructor;
  7 +
  8 +import java.util.Arrays;
  9 +
  10 +/**
  11 + * IoT 序列化类型枚举
  12 + *
  13 + * 用于定义设备消息的序列化格式
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@RequiredArgsConstructor
  18 +@Getter
  19 +public enum IotSerializeTypeEnum implements ArrayValuable<String> {
  20 +
  21 + JSON("json"),
  22 + BINARY("binary");
  23 +
  24 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotSerializeTypeEnum::getType).toArray(String[]::new);
  25 +
  26 + /**
  27 + * 类型
  28 + */
  29 + private final String type;
  30 +
  31 + @Override
  32 + public String[] array() {
  33 + return ARRAYS;
  34 + }
  35 +
  36 + public static IotSerializeTypeEnum of(String type) {
  37 + return ArrayUtil.firstMatch(e -> e.getType().equals(type), values());
  38 + }
  39 +
  40 +}
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/IotMessageBus.java
@@ -24,4 +24,14 @@ public interface IotMessageBus { @@ -24,4 +24,14 @@ public interface IotMessageBus {
24 */ 24 */
25 void register(IotMessageSubscriber<?> subscriber); 25 void register(IotMessageSubscriber<?> subscriber);
26 26
  27 + /**
  28 + * 取消注册消息订阅者
  29 + *
  30 + * @param subscriber 订阅者
  31 + */
  32 + default void unregister(IotMessageSubscriber<?> subscriber) {
  33 + // TODO 芋艿:暂时不实现,需求量不大,但是
  34 + // throw new UnsupportedOperationException("取消注册消息订阅者功能,尚未实现");
  35 + }
  36 +
27 } 37 }
28 \ No newline at end of file 38 \ No newline at end of file
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/IotMessageSubscriber.java
@@ -26,4 +26,16 @@ public interface IotMessageSubscriber&lt;T&gt; { @@ -26,4 +26,16 @@ public interface IotMessageSubscriber&lt;T&gt; {
26 */ 26 */
27 void onMessage(T message); 27 void onMessage(T message);
28 28
  29 + /**
  30 + * 启动订阅
  31 + */
  32 + default void start() {
  33 + }
  34 +
  35 + /**
  36 + * 停止订阅
  37 + */
  38 + default void stop() {
  39 + }
  40 +
29 } 41 }
30 \ No newline at end of file 42 \ No newline at end of file
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java
@@ -60,7 +60,7 @@ public class IotDeviceMessage { @@ -60,7 +60,7 @@ public class IotDeviceMessage {
60 */ 60 */
61 private String serverId; 61 private String serverId;
62 62
63 - // ========== codec(编解码)字段 ========== 63 + // ========== serialize(序列化)相关字段 ==========
64 64
65 /** 65 /**
66 * 请求编号 66 * 请求编号
@@ -94,7 +94,7 @@ public class IotDeviceMessage { @@ -94,7 +94,7 @@ public class IotDeviceMessage {
94 */ 94 */
95 private String msg; 95 private String msg;
96 96
97 - // ========== 基础方法:只传递"codec(编解码)字段" ========== 97 + // ========== 基础方法:只传递"serialize(序列化)相关字段" ==========
98 98
99 public static IotDeviceMessage requestOf(String method) { 99 public static IotDeviceMessage requestOf(String method) {
100 return requestOf(null, method, null); 100 return requestOf(null, method, null);
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
1 package cn.iocoder.yudao.module.iot.gateway.config; 1 package cn.iocoder.yudao.module.iot.gateway.config;
2 2
3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  4 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolManager;
  5 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;
4 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapDownstreamSubscriber; 6 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapDownstreamSubscriber;
5 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapUpstreamProtocol; 7 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapUpstreamProtocol;
6 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol;
7 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber; 9 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber;
8 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol; 10 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
9 -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber;  
10 -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;  
11 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber; 11 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber;
12 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; 12 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
13 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; 13 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;
@@ -15,12 +15,15 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstrea @@ -15,12 +15,15 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstrea
15 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpDownstreamSubscriber; 15 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpDownstreamSubscriber;
16 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpUpstreamProtocol; 16 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpUpstreamProtocol;
17 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; 17 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;
  18 +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpDownstreamHandler;
18 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpDownstreamSubscriber; 19 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpDownstreamSubscriber;
19 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpUpstreamProtocol; 20 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpUpstreamProtocol;
20 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager; 21 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager;
  22 +import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpDownstreamHandler;
21 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketDownstreamSubscriber; 23 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketDownstreamSubscriber;
22 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketUpstreamProtocol; 24 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketUpstreamProtocol;
23 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager; 25 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager;
  26 +import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketDownstreamHandler;
24 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; 27 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
25 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; 28 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
26 import io.vertx.core.Vertx; 29 import io.vertx.core.Vertx;
@@ -31,35 +34,22 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties @@ -31,35 +34,22 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
31 import org.springframework.context.annotation.Bean; 34 import org.springframework.context.annotation.Bean;
32 import org.springframework.context.annotation.Configuration; 35 import org.springframework.context.annotation.Configuration;
33 36
  37 +
34 @Configuration 38 @Configuration
35 @EnableConfigurationProperties(IotGatewayProperties.class) 39 @EnableConfigurationProperties(IotGatewayProperties.class)
36 @Slf4j 40 @Slf4j
37 public class IotGatewayConfiguration { 41 public class IotGatewayConfiguration {
38 42
39 - /**  
40 - * IoT 网关 HTTP 协议配置类  
41 - */  
42 - @Configuration  
43 - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.http", name = "enabled", havingValue = "true")  
44 - @Slf4j  
45 - public static class HttpProtocolConfiguration {  
46 -  
47 - @Bean(name = "httpVertx", destroyMethod = "close")  
48 - public Vertx httpVertx() {  
49 - return Vertx.vertx();  
50 - }  
51 -  
52 - @Bean  
53 - public IotHttpUpstreamProtocol iotHttpUpstreamProtocol(IotGatewayProperties gatewayProperties,  
54 - @Qualifier("httpVertx") Vertx httpVertx) {  
55 - return new IotHttpUpstreamProtocol(gatewayProperties.getProtocol().getHttp(), httpVertx);  
56 - } 43 + @Bean
  44 + public IotMessageSerializerManager iotMessageSerializerManager() {
  45 + return new IotMessageSerializerManager();
  46 + }
57 47
58 - @Bean  
59 - public IotHttpDownstreamSubscriber iotHttpDownstreamSubscriber(IotHttpUpstreamProtocol httpUpstreamProtocol,  
60 - IotMessageBus messageBus) {  
61 - return new IotHttpDownstreamSubscriber(httpUpstreamProtocol, messageBus);  
62 - } 48 + @Bean
  49 + public IotProtocolManager iotProtocolManager(IotGatewayProperties gatewayProperties,
  50 + IotMessageSerializerManager serializerManager,
  51 + IotMessageBus messageBus) {
  52 + return new IotProtocolManager(gatewayProperties, serializerManager, messageBus);
63 } 53 }
64 54
65 /** 55 /**
@@ -118,11 +108,16 @@ public class IotGatewayConfiguration { @@ -118,11 +108,16 @@ public class IotGatewayConfiguration {
118 } 108 }
119 109
120 @Bean 110 @Bean
  111 + public IotTcpDownstreamHandler iotTcpDownstreamHandler(IotDeviceMessageService messageService,
  112 + IotTcpConnectionManager connectionManager) {
  113 + return new IotTcpDownstreamHandler(messageService, connectionManager);
  114 + }
  115 +
  116 + @Bean
121 public IotTcpDownstreamSubscriber iotTcpDownstreamSubscriber(IotTcpUpstreamProtocol protocolHandler, 117 public IotTcpDownstreamSubscriber iotTcpDownstreamSubscriber(IotTcpUpstreamProtocol protocolHandler,
122 - IotDeviceMessageService messageService,  
123 - IotTcpConnectionManager connectionManager, 118 + IotTcpDownstreamHandler downstreamHandler,
124 IotMessageBus messageBus) { 119 IotMessageBus messageBus) {
125 - return new IotTcpDownstreamSubscriber(protocolHandler, messageService, connectionManager, messageBus); 120 + return new IotTcpDownstreamSubscriber(protocolHandler, downstreamHandler, messageBus);
126 } 121 }
127 122
128 } 123 }
@@ -188,11 +183,17 @@ public class IotGatewayConfiguration { @@ -188,11 +183,17 @@ public class IotGatewayConfiguration {
188 } 183 }
189 184
190 @Bean 185 @Bean
  186 + public IotUdpDownstreamHandler iotUdpDownstreamHandler(IotDeviceMessageService messageService,
  187 + IotUdpSessionManager sessionManager,
  188 + IotUdpUpstreamProtocol protocol) {
  189 + return new IotUdpDownstreamHandler(messageService, sessionManager, protocol);
  190 + }
  191 +
  192 + @Bean
191 public IotUdpDownstreamSubscriber iotUdpDownstreamSubscriber(IotUdpUpstreamProtocol protocolHandler, 193 public IotUdpDownstreamSubscriber iotUdpDownstreamSubscriber(IotUdpUpstreamProtocol protocolHandler,
192 - IotDeviceMessageService messageService,  
193 - IotUdpSessionManager sessionManager, 194 + IotUdpDownstreamHandler downstreamHandler,
194 IotMessageBus messageBus) { 195 IotMessageBus messageBus) {
195 - return new IotUdpDownstreamSubscriber(protocolHandler, messageService, sessionManager, messageBus); 196 + return new IotUdpDownstreamSubscriber(protocolHandler, downstreamHandler, messageBus);
196 } 197 }
197 198
198 } 199 }
@@ -242,11 +243,16 @@ public class IotGatewayConfiguration { @@ -242,11 +243,16 @@ public class IotGatewayConfiguration {
242 } 243 }
243 244
244 @Bean 245 @Bean
  246 + public IotWebSocketDownstreamHandler iotWebSocketDownstreamHandler(IotDeviceMessageService messageService,
  247 + IotWebSocketConnectionManager connectionManager) {
  248 + return new IotWebSocketDownstreamHandler(messageService, connectionManager);
  249 + }
  250 +
  251 + @Bean
245 public IotWebSocketDownstreamSubscriber iotWebSocketDownstreamSubscriber(IotWebSocketUpstreamProtocol protocolHandler, 252 public IotWebSocketDownstreamSubscriber iotWebSocketDownstreamSubscriber(IotWebSocketUpstreamProtocol protocolHandler,
246 - IotDeviceMessageService messageService,  
247 - IotWebSocketConnectionManager connectionManager, 253 + IotWebSocketDownstreamHandler downstreamHandler,
248 IotMessageBus messageBus) { 254 IotMessageBus messageBus) {
249 - return new IotWebSocketDownstreamSubscriber(protocolHandler, messageService, connectionManager, messageBus); 255 + return new IotWebSocketDownstreamSubscriber(protocolHandler, downstreamHandler, messageBus);
250 } 256 }
251 257
252 } 258 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
1 package cn.iocoder.yudao.module.iot.gateway.config; 1 package cn.iocoder.yudao.module.iot.gateway.config;
2 2
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
  4 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpConfig;
  5 +import jakarta.validation.Valid;
3 import jakarta.validation.constraints.NotEmpty; 6 import jakarta.validation.constraints.NotEmpty;
4 import jakarta.validation.constraints.NotNull; 7 import jakarta.validation.constraints.NotNull;
5 import lombok.Data; 8 import lombok.Data;
@@ -24,10 +27,15 @@ public class IotGatewayProperties { @@ -24,10 +27,15 @@ public class IotGatewayProperties {
24 private TokenProperties token; 27 private TokenProperties token;
25 28
26 /** 29 /**
27 - * 协议配置 30 + * 协议配置(旧版,保持兼容)
28 */ 31 */
29 private ProtocolProperties protocol; 32 private ProtocolProperties protocol;
30 33
  34 + /**
  35 + * 协议实例列表(新版)
  36 + */
  37 + private List<ProtocolInstanceProperties> protocols;
  38 +
31 @Data 39 @Data
32 public static class RpcProperties { 40 public static class RpcProperties {
33 41
@@ -89,11 +97,6 @@ public class IotGatewayProperties { @@ -89,11 +97,6 @@ public class IotGatewayProperties {
89 private MqttProperties mqtt; 97 private MqttProperties mqtt;
90 98
91 /** 99 /**
92 - * MQTT WebSocket 组件配置  
93 - */  
94 - private MqttWsProperties mqttWs;  
95 -  
96 - /**  
97 * UDP 组件配置 100 * UDP 组件配置
98 */ 101 */
99 private UdpProperties udp; 102 private UdpProperties udp;
@@ -423,102 +426,6 @@ public class IotGatewayProperties { @@ -423,102 +426,6 @@ public class IotGatewayProperties {
423 } 426 }
424 427
425 @Data 428 @Data
426 - public static class MqttWsProperties {  
427 -  
428 - /**  
429 - * 是否开启  
430 - */  
431 - @NotNull(message = "是否开启不能为空")  
432 - private Boolean enabled;  
433 -  
434 - /**  
435 - * WebSocket 服务器端口(默认:8083)  
436 - */  
437 - private Integer port = 8083;  
438 -  
439 - /**  
440 - * WebSocket 路径(默认:/mqtt)  
441 - */  
442 - @NotEmpty(message = "WebSocket 路径不能为空")  
443 - private String path = "/mqtt";  
444 -  
445 - /**  
446 - * 最大消息大小(字节)  
447 - */  
448 - private Integer maxMessageSize = 8192;  
449 -  
450 - /**  
451 - * 连接超时时间(秒)  
452 - */  
453 - private Integer connectTimeoutSeconds = 60;  
454 -  
455 - /**  
456 - * 保持连接超时时间(秒)  
457 - */  
458 - private Integer keepAliveTimeoutSeconds = 300;  
459 -  
460 - /**  
461 - * 是否启用 SSL(wss://)  
462 - */  
463 - private Boolean sslEnabled = false;  
464 -  
465 - /**  
466 - * SSL 配置  
467 - */  
468 - private SslOptions sslOptions = new SslOptions();  
469 -  
470 - /**  
471 - * WebSocket 子协议(通常为 "mqtt" 或 "mqttv3.1")  
472 - */  
473 - @NotEmpty(message = "WebSocket 子协议不能为空")  
474 - private String subProtocol = "mqtt";  
475 -  
476 - /**  
477 - * 最大帧大小(字节)  
478 - */  
479 - private Integer maxFrameSize = 65536;  
480 -  
481 - /**  
482 - * SSL 配置选项  
483 - */  
484 - @Data  
485 - public static class SslOptions {  
486 -  
487 - /**  
488 - * 密钥证书选项  
489 - */  
490 - private io.vertx.core.net.KeyCertOptions keyCertOptions;  
491 -  
492 - /**  
493 - * 信任选项  
494 - */  
495 - private io.vertx.core.net.TrustOptions trustOptions;  
496 -  
497 - /**  
498 - * SSL 证书路径  
499 - */  
500 - private String certPath;  
501 -  
502 - /**  
503 - * SSL 私钥路径  
504 - */  
505 - private String keyPath;  
506 -  
507 - /**  
508 - * 信任存储路径  
509 - */  
510 - private String trustStorePath;  
511 -  
512 - /**  
513 - * 信任存储密码  
514 - */  
515 - private String trustStorePassword;  
516 -  
517 - }  
518 -  
519 - }  
520 -  
521 - @Data  
522 public static class UdpProperties { 429 public static class UdpProperties {
523 430
524 /** 431 /**
@@ -643,4 +550,125 @@ public class IotGatewayProperties { @@ -643,4 +550,125 @@ public class IotGatewayProperties {
643 550
644 } 551 }
645 552
  553 + // TODO @AI:【暂时忽略】改成 ProtocolProperties
  554 + /**
  555 + * 协议实例配置
  556 + */
  557 + @Data
  558 + public static class ProtocolInstanceProperties {
  559 +
  560 + /**
  561 + * 协议实例 ID,如 "http-alink"、"tcp-binary"
  562 + */
  563 + @NotEmpty(message = "协议实例 ID 不能为空")
  564 + private String id;
  565 + /**
  566 + * 是否启用
  567 + */
  568 + @NotNull(message = "是否启用不能为空")
  569 + private Boolean enabled = true;
  570 + /**
  571 + * 协议类型
  572 + *
  573 + * @see cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum
  574 + */
  575 + @NotEmpty(message = "协议类型不能为空")
  576 + private String type;
  577 + /**
  578 + * 服务端口
  579 + */
  580 + @NotNull(message = "服务端口不能为空")
  581 + private Integer port;
  582 + /**
  583 + * 序列化类型(可选)
  584 + *
  585 + * @see cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum
  586 + *
  587 + * 为什么是可选的呢?
  588 + * 1. {@link IotProtocolTypeEnum#HTTP}、${@link IotProtocolTypeEnum#COAP} 协议,目前强制是 JSON 格式
  589 + * 2. {@link IotProtocolTypeEnum#EMQX} 协议,目前支持根据产品(设备)配置的序列化类型来解析
  590 + */
  591 + private String serialize;
  592 +
  593 + /**
  594 + * HTTP 协议配置
  595 + */
  596 + @Valid
  597 + private IotHttpConfig http;
  598 +
  599 + // TODO @AI:后续改下;
  600 + /**
  601 + * TCP 协议配置(后续扩展)
  602 + */
  603 + @Valid
  604 + private TcpInstanceConfig tcp;
  605 +
  606 + }
  607 +
  608 + /**
  609 + * TCP 协议实例配置(后续扩展)
  610 + */
  611 + @Data
  612 + public static class TcpInstanceConfig {
  613 +
  614 + /**
  615 + * 最大连接数
  616 + */
  617 + private Integer maxConnections = 1000;
  618 +
  619 + /**
  620 + * 心跳超时时间(毫秒)
  621 + */
  622 + private Long keepAliveTimeoutMs = 30000L;
  623 +
  624 + /**
  625 + * 是否启用 SSL
  626 + */
  627 + private Boolean sslEnabled = false;
  628 +
  629 + /**
  630 + * SSL 证书路径
  631 + */
  632 + private String sslCertPath;
  633 +
  634 + /**
  635 + * SSL 私钥路径
  636 + */
  637 + private String sslKeyPath;
  638 +
  639 + /**
  640 + * 拆包配置
  641 + */
  642 + private CodecConfig codec;
  643 +
  644 + /**
  645 + * TCP 拆包配置
  646 + */
  647 + @Data
  648 + public static class CodecConfig {
  649 +
  650 + /**
  651 + * 拆包类型:LENGTH_FIELD / DELIMITER
  652 + */
  653 + private String type;
  654 +
  655 + /**
  656 + * LENGTH_FIELD: 偏移量
  657 + */
  658 + private Integer lengthFieldOffset;
  659 +
  660 + /**
  661 + * LENGTH_FIELD: 长度字段长度
  662 + */
  663 + private Integer lengthFieldLength;
  664 +
  665 + /**
  666 + * DELIMITER: 分隔符
  667 + */
  668 + private String delimiter;
  669 +
  670 + }
  671 +
  672 + }
  673 +
646 } 674 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocol.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
  4 +
  5 +/**
  6 + * IoT 协议接口
  7 + *
  8 + * 定义传输层协议的生命周期管理
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public interface IotProtocol {
  13 +
  14 + /**
  15 + * 获取协议实例 ID
  16 + *
  17 + * @return 协议实例 ID,如 "http-alink"、"tcp-binary"
  18 + */
  19 + String getId();
  20 +
  21 + /**
  22 + * 获取服务器 ID(用于消息追踪,全局唯一)
  23 + *
  24 + * @return 服务器 ID
  25 + */
  26 + String getServerId();
  27 +
  28 + /**
  29 + * 获取协议类型
  30 + *
  31 + * @return 协议类型枚举
  32 + */
  33 + IotProtocolTypeEnum getType();
  34 +
  35 + /**
  36 + * 启动协议服务
  37 + */
  38 + void start();
  39 +
  40 + /**
  41 + * 停止协议服务
  42 + */
  43 + void stop();
  44 +
  45 + /**
  46 + * 检查协议服务是否正在运行
  47 + *
  48 + * @return 是否正在运行
  49 + */
  50 + boolean isRunning();
  51 +
  52 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolDownstreamSubscriber.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  5 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
  6 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  8 +import lombok.AllArgsConstructor;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +
  11 +/**
  12 + * IoT 协议下行消息订阅者抽象类
  13 + *
  14 + * 负责接收来自消息总线的下行消息,并委托给子类进行业务处理
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@AllArgsConstructor
  19 +@Slf4j
  20 +public abstract class IotProtocolDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
  21 +
  22 + private final IotProtocol protocol;
  23 +
  24 + private final IotMessageBus messageBus;
  25 +
  26 + @Override
  27 + public String getTopic() {
  28 + return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());
  29 + }
  30 +
  31 + /**
  32 + * 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group
  33 + */
  34 + @Override
  35 + public String getGroup() {
  36 + return getTopic();
  37 + }
  38 +
  39 + @Override
  40 + public void start() {
  41 + messageBus.register(this);
  42 + log.info("[start][{} 下行消息订阅成功,Topic:{}]", protocol.getType().name(), getTopic());
  43 + }
  44 +
  45 + @Override
  46 + public void stop() {
  47 + messageBus.unregister(this);
  48 + log.info("[stop][{} 下行消息订阅已停止,Topic:{}]", protocol.getType().name(), getTopic());
  49 + }
  50 +
  51 + @Override
  52 + public void onMessage(IotDeviceMessage message) {
  53 + log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]",
  54 + message.getId(), message.getMethod(), message.getDeviceId());
  55 + try {
  56 + // 1. 校验
  57 + String method = message.getMethod();
  58 + if (StrUtil.isBlank(method)) {
  59 + log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]",
  60 + message.getId(), message.getDeviceId());
  61 + return;
  62 + }
  63 +
  64 + // 2. 处理下行消息
  65 + handleMessage(message);
  66 + } catch (Exception e) {
  67 + log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]",
  68 + message.getId(), message.getMethod(), message.getDeviceId(), e);
  69 + }
  70 + }
  71 +
  72 + /**
  73 + * 处理下行消息
  74 + *
  75 + * @param message 下行消息
  76 + */
  77 + protected abstract void handleMessage(IotDeviceMessage message);
  78 +
  79 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.BooleanUtil;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
  6 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  7 +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  8 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol;
  9 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +import org.springframework.context.SmartLifecycle;
  12 +
  13 +import java.util.ArrayList;
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * IoT 协议管理器
  18 + *
  19 + * 负责根据配置创建和管理协议实例
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +@Slf4j
  24 +public class IotProtocolManager implements SmartLifecycle {
  25 +
  26 + private final IotGatewayProperties gatewayProperties;
  27 +
  28 + private final IotMessageSerializerManager serializerManager;
  29 +
  30 + private final IotMessageBus messageBus;
  31 +
  32 + private final List<IotProtocol> protocols = new ArrayList<>();
  33 +
  34 + private volatile boolean running = false;
  35 +
  36 + public IotProtocolManager(IotGatewayProperties gatewayProperties,
  37 + IotMessageSerializerManager serializerManager,
  38 + IotMessageBus messageBus) {
  39 + this.gatewayProperties = gatewayProperties;
  40 + this.serializerManager = serializerManager;
  41 + this.messageBus = messageBus;
  42 + }
  43 +
  44 + @Override
  45 + public void start() {
  46 + if (running) {
  47 + return;
  48 + }
  49 + List<IotGatewayProperties.ProtocolInstanceProperties> protocolConfigs = gatewayProperties.getProtocols();
  50 + if (CollUtil.isEmpty(protocolConfigs)) {
  51 + log.info("[start][没有配置协议实例,跳过启动]");
  52 + return;
  53 + }
  54 +
  55 + for (IotGatewayProperties.ProtocolInstanceProperties config : protocolConfigs) {
  56 + if (BooleanUtil.isFalse(config.getEnabled())) {
  57 + log.info("[start][协议实例 {} 未启用,跳过]", config.getId());
  58 + continue;
  59 + }
  60 + IotProtocol protocol = createProtocol(config);
  61 + if (protocol == null) {
  62 + continue;
  63 + }
  64 + protocol.start();
  65 + protocols.add(protocol);
  66 + }
  67 + running = true;
  68 + log.info("[start][协议管理器启动完成,共启动 {} 个协议实例]", protocols.size());
  69 + }
  70 +
  71 + @Override
  72 + public void stop() {
  73 + if (!running) {
  74 + return;
  75 + }
  76 + for (IotProtocol protocol : protocols) {
  77 + try {
  78 + protocol.stop();
  79 + } catch (Exception e) {
  80 + log.error("[stop][协议实例 {} 停止失败]", protocol.getId(), e);
  81 + }
  82 + }
  83 + protocols.clear();
  84 + running = false;
  85 + log.info("[stop][协议管理器已停止]");
  86 + }
  87 +
  88 + @Override
  89 + public boolean isRunning() {
  90 + return running;
  91 + }
  92 +
  93 + /**
  94 + * 创建协议实例
  95 + *
  96 + * @param config 协议实例配置
  97 + * @return 协议实例
  98 + */
  99 + @SuppressWarnings({"SwitchStatementWithTooFewBranches", "EnhancedSwitchMigration"})
  100 + private IotProtocol createProtocol(IotGatewayProperties.ProtocolInstanceProperties config) {
  101 + IotProtocolTypeEnum protocolType = IotProtocolTypeEnum.of(config.getType());
  102 + if (protocolType == null) {
  103 + log.error("[createProtocol][协议实例 {} 的协议类型 {} 不存在]", config.getId(), config.getType());
  104 + return null;
  105 + }
  106 + switch (protocolType) {
  107 + case HTTP:
  108 + return createHttpProtocol(config);
  109 + // TODO 后续添加其他协议类型
  110 + default:
  111 + throw new IllegalArgumentException(String.format(
  112 + "[createProtocol][协议实例 %s 的协议类型 %s 暂不支持]", config.getId(), protocolType));
  113 + }
  114 + }
  115 +
  116 + /**
  117 + * 创建 HTTP 协议实例
  118 + *
  119 + * @param config 协议实例配置
  120 + * @return HTTP 协议实例
  121 + */
  122 + private IotHttpProtocol createHttpProtocol(IotGatewayProperties.ProtocolInstanceProperties config) {
  123 + return new IotHttpProtocol(config, messageBus);
  124 + }
  125 +
  126 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapDownstreamSubscriber.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.coap; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.coap;
2 2
3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;  
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
7 -import jakarta.annotation.PostConstruct;  
8 -import lombok.RequiredArgsConstructor; 5 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber;
9 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
10 7
11 /** 8 /**
@@ -13,34 +10,17 @@ import lombok.extern.slf4j.Slf4j; @@ -13,34 +10,17 @@ import lombok.extern.slf4j.Slf4j;
13 * 10 *
14 * @author 芋道源码 11 * @author 芋道源码
15 */ 12 */
16 -@RequiredArgsConstructor  
17 @Slf4j 13 @Slf4j
18 -public class IotCoapDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { 14 +public class IotCoapDownstreamSubscriber extends IotProtocolDownstreamSubscriber {
19 15
20 - private final IotCoapUpstreamProtocol protocol;  
21 -  
22 - private final IotMessageBus messageBus;  
23 -  
24 - @PostConstruct  
25 - public void init() {  
26 - messageBus.register(this);  
27 - }  
28 -  
29 - @Override  
30 - public String getTopic() {  
31 - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());  
32 - }  
33 -  
34 - @Override  
35 - public String getGroup() {  
36 - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group  
37 - return getTopic(); 16 + public IotCoapDownstreamSubscriber(IotCoapUpstreamProtocol protocol, IotMessageBus messageBus) {
  17 + super(protocol, messageBus);
38 } 18 }
39 19
40 @Override 20 @Override
41 - public void onMessage(IotDeviceMessage message) { 21 + protected void handleMessage(IotDeviceMessage message) {
42 // 如需支持,可通过 CoAP Observe 模式实现(设备订阅资源,服务器推送变更) 22 // 如需支持,可通过 CoAP Observe 模式实现(设备订阅资源,服务器推送变更)
43 - log.warn("[onMessage][IoT 网关 CoAP 协议暂不支持下行消息,忽略消息:{}]", message); 23 + log.warn("[handleMessage][IoT 网关 CoAP 协议暂不支持下行消息,忽略消息:{}]", message);
44 } 24 }
45 25
46 } 26 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapUpstreamProtocol.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.coap; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.coap;
2 2
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
3 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 4 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
4 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 5 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  6 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
5 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthHandler; 7 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthHandler;
6 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthResource; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthResource;
7 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapRegisterHandler; 9 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapRegisterHandler;
@@ -30,7 +32,9 @@ import java.util.concurrent.TimeUnit; @@ -30,7 +32,9 @@ import java.util.concurrent.TimeUnit;
30 * @author 芋道源码 32 * @author 芋道源码
31 */ 33 */
32 @Slf4j 34 @Slf4j
33 -public class IotCoapUpstreamProtocol { 35 +public class IotCoapUpstreamProtocol implements IotProtocol {
  36 +
  37 + private static final String ID = "coap";
34 38
35 private final IotGatewayProperties.CoapProperties coapProperties; 39 private final IotGatewayProperties.CoapProperties coapProperties;
36 40
@@ -39,11 +43,24 @@ public class IotCoapUpstreamProtocol { @@ -39,11 +43,24 @@ public class IotCoapUpstreamProtocol {
39 @Getter 43 @Getter
40 private final String serverId; 44 private final String serverId;
41 45
  46 + private volatile boolean running = false;
  47 +
42 public IotCoapUpstreamProtocol(IotGatewayProperties.CoapProperties coapProperties) { 48 public IotCoapUpstreamProtocol(IotGatewayProperties.CoapProperties coapProperties) {
43 this.coapProperties = coapProperties; 49 this.coapProperties = coapProperties;
44 this.serverId = IotDeviceMessageUtils.generateServerId(coapProperties.getPort()); 50 this.serverId = IotDeviceMessageUtils.generateServerId(coapProperties.getPort());
45 } 51 }
46 52
  53 + @Override
  54 + public String getId() {
  55 + return ID;
  56 + }
  57 +
  58 + @Override
  59 + public IotProtocolTypeEnum getType() {
  60 + return IotProtocolTypeEnum.COAP;
  61 + }
  62 +
  63 + @Override
47 @PostConstruct 64 @PostConstruct
48 public void start() { 65 public void start() {
49 try { 66 try {
@@ -73,6 +90,7 @@ public class IotCoapUpstreamProtocol { @@ -73,6 +90,7 @@ public class IotCoapUpstreamProtocol {
73 90
74 // 3. 启动服务器 91 // 3. 启动服务器
75 coapServer.start(); 92 coapServer.start();
  93 + running = true;
76 log.info("[start][IoT 网关 CoAP 协议启动成功,端口:{},资源:/auth, /auth/register/device, /topic]", coapProperties.getPort()); 94 log.info("[start][IoT 网关 CoAP 协议启动成功,端口:{},资源:/auth, /auth/register/device, /topic]", coapProperties.getPort());
77 } catch (Exception e) { 95 } catch (Exception e) {
78 log.error("[start][IoT 网关 CoAP 协议启动失败]", e); 96 log.error("[start][IoT 网关 CoAP 协议启动失败]", e);
@@ -80,11 +98,13 @@ public class IotCoapUpstreamProtocol { @@ -80,11 +98,13 @@ public class IotCoapUpstreamProtocol {
80 } 98 }
81 } 99 }
82 100
  101 + @Override
83 @PreDestroy 102 @PreDestroy
84 public void stop() { 103 public void stop() {
85 if (coapServer != null) { 104 if (coapServer != null) {
86 try { 105 try {
87 coapServer.stop(); 106 coapServer.stop();
  107 + running = false;
88 log.info("[stop][IoT 网关 CoAP 协议已停止]"); 108 log.info("[stop][IoT 网关 CoAP 协议已停止]");
89 } catch (Exception e) { 109 } catch (Exception e) {
90 log.error("[stop][IoT 网关 CoAP 协议停止失败]", e); 110 log.error("[stop][IoT 网关 CoAP 协议停止失败]", e);
@@ -92,4 +112,9 @@ public class IotCoapUpstreamProtocol { @@ -92,4 +112,9 @@ public class IotCoapUpstreamProtocol {
92 } 112 }
93 } 113 }
94 114
  115 + @Override
  116 + public boolean isRunning() {
  117 + return running;
  118 + }
  119 +
95 } 120 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/router/IotCoapAuthHandler.java
@@ -24,8 +24,6 @@ import java.util.Map; @@ -24,8 +24,6 @@ import java.util.Map;
24 /** 24 /**
25 * IoT 网关 CoAP 协议的【认证】处理器 25 * IoT 网关 CoAP 协议的【认证】处理器
26 * 26 *
27 - * 参考 {@link cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAuthHandler}  
28 - *  
29 * @author 芋道源码 27 * @author 芋道源码
30 */ 28 */
31 @Slf4j 29 @Slf4j
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/router/IotCoapRegisterHandler.java
@@ -22,7 +22,6 @@ import java.util.Map; @@ -22,7 +22,6 @@ import java.util.Map;
22 * 22 *
23 * @author 芋道源码 23 * @author 芋道源码
24 * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a> 24 * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
25 - * @see cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpRegisterHandler  
26 */ 25 */
27 @Slf4j 26 @Slf4j
28 public class IotCoapRegisterHandler { 27 public class IotCoapRegisterHandler {
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxDownstreamSubscriber.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.emqx; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.emqx;
2 2
3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;  
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 5 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
  6 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber;
7 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxDownstreamHandler; 7 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxDownstreamHandler;
8 -import jakarta.annotation.PostConstruct;  
9 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
10 9
11 /** 10 /**
@@ -14,55 +13,18 @@ import lombok.extern.slf4j.Slf4j; @@ -14,55 +13,18 @@ import lombok.extern.slf4j.Slf4j;
14 * @author 芋道源码 13 * @author 芋道源码
15 */ 14 */
16 @Slf4j 15 @Slf4j
17 -public class IotEmqxDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { 16 +public class IotEmqxDownstreamSubscriber extends IotProtocolDownstreamSubscriber {
18 17
19 private final IotEmqxDownstreamHandler downstreamHandler; 18 private final IotEmqxDownstreamHandler downstreamHandler;
20 19
21 - private final IotMessageBus messageBus;  
22 -  
23 - private final IotEmqxUpstreamProtocol protocol;  
24 -  
25 public IotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol protocol, IotMessageBus messageBus) { 20 public IotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol protocol, IotMessageBus messageBus) {
26 - this.protocol = protocol;  
27 - this.messageBus = messageBus; 21 + super(protocol, messageBus);
28 this.downstreamHandler = new IotEmqxDownstreamHandler(protocol); 22 this.downstreamHandler = new IotEmqxDownstreamHandler(protocol);
29 } 23 }
30 24
31 - @PostConstruct  
32 - public void init() {  
33 - messageBus.register(this);  
34 - }  
35 -  
36 @Override 25 @Override
37 - public String getTopic() {  
38 - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());  
39 - }  
40 -  
41 - @Override  
42 - public String getGroup() {  
43 - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group  
44 - return getTopic();  
45 - }  
46 -  
47 - @Override  
48 - public void onMessage(IotDeviceMessage message) {  
49 - log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]",  
50 - message.getId(), message.getMethod(), message.getDeviceId());  
51 - try {  
52 - // 1. 校验  
53 - String method = message.getMethod();  
54 - if (method == null) {  
55 - log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]",  
56 - message.getId(), message.getDeviceId());  
57 - return;  
58 - }  
59 -  
60 - // 2. 处理下行消息  
61 - downstreamHandler.handle(message);  
62 - } catch (Exception e) {  
63 - log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]",  
64 - message.getId(), message.getMethod(), message.getDeviceId(), e);  
65 - } 26 + protected void handleMessage(IotDeviceMessage message) {
  27 + downstreamHandler.handle(message);
66 } 28 }
67 29
68 -}  
69 \ No newline at end of file 30 \ No newline at end of file
  31 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxUpstreamProtocol.java
@@ -2,8 +2,10 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.emqx; @@ -2,8 +2,10 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.emqx;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
5 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 6 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
6 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 7 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  8 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
7 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxUpstreamHandler; 9 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxUpstreamHandler;
8 import io.netty.handler.codec.mqtt.MqttQoS; 10 import io.netty.handler.codec.mqtt.MqttQoS;
9 import io.vertx.core.Vertx; 11 import io.vertx.core.Vertx;
@@ -28,11 +30,13 @@ import java.util.concurrent.atomic.AtomicBoolean; @@ -28,11 +30,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
28 * @author 芋道源码 30 * @author 芋道源码
29 */ 31 */
30 @Slf4j 32 @Slf4j
31 -public class IotEmqxUpstreamProtocol { 33 +public class IotEmqxUpstreamProtocol implements IotProtocol {
  34 +
  35 + private static final String ID = "emqx";
32 36
33 private final IotGatewayProperties.EmqxProperties emqxProperties; 37 private final IotGatewayProperties.EmqxProperties emqxProperties;
34 38
35 - private volatile boolean isRunning = false; 39 + private volatile boolean running = false;
36 40
37 private final Vertx vertx; 41 private final Vertx vertx;
38 42
@@ -50,9 +54,20 @@ public class IotEmqxUpstreamProtocol { @@ -50,9 +54,20 @@ public class IotEmqxUpstreamProtocol {
50 this.vertx = vertx; 54 this.vertx = vertx;
51 } 55 }
52 56
  57 + @Override
  58 + public String getId() {
  59 + return ID;
  60 + }
  61 +
  62 + @Override
  63 + public IotProtocolTypeEnum getType() {
  64 + return IotProtocolTypeEnum.EMQX;
  65 + }
  66 +
  67 + @Override
53 @PostConstruct 68 @PostConstruct
54 public void start() { 69 public void start() {
55 - if (isRunning) { 70 + if (running) {
56 return; 71 return;
57 } 72 }
58 73
@@ -61,7 +76,7 @@ public class IotEmqxUpstreamProtocol { @@ -61,7 +76,7 @@ public class IotEmqxUpstreamProtocol {
61 startMqttClient(); 76 startMqttClient();
62 77
63 // 2. 标记服务为运行状态 78 // 2. 标记服务为运行状态
64 - isRunning = true; 79 + running = true;
65 log.info("[start][IoT 网关 EMQX 协议启动成功]"); 80 log.info("[start][IoT 网关 EMQX 协议启动成功]");
66 } catch (Exception e) { 81 } catch (Exception e) {
67 log.error("[start][IoT 网关 EMQX 协议服务启动失败,应用将关闭]", e); 82 log.error("[start][IoT 网关 EMQX 协议服务启动失败,应用将关闭]", e);
@@ -88,9 +103,10 @@ public class IotEmqxUpstreamProtocol { @@ -88,9 +103,10 @@ public class IotEmqxUpstreamProtocol {
88 } 103 }
89 } 104 }
90 105
  106 + @Override
91 @PreDestroy 107 @PreDestroy
92 public void stop() { 108 public void stop() {
93 - if (!isRunning) { 109 + if (!running) {
94 return; 110 return;
95 } 111 }
96 112
@@ -98,10 +114,15 @@ public class IotEmqxUpstreamProtocol { @@ -98,10 +114,15 @@ public class IotEmqxUpstreamProtocol {
98 stopMqttClient(); 114 stopMqttClient();
99 115
100 // 2. 标记服务为停止状态 116 // 2. 标记服务为停止状态
101 - isRunning = false; 117 + running = false;
102 log.info("[stop][IoT 网关 MQTT 协议服务已停止]"); 118 log.info("[stop][IoT 网关 MQTT 协议服务已停止]");
103 } 119 }
104 120
  121 + @Override
  122 + public boolean isRunning() {
  123 + return running;
  124 + }
  125 +
105 /** 126 /**
106 * 启动 MQTT 客户端 127 * 启动 MQTT 客户端
107 */ 128 */
@@ -185,7 +206,7 @@ public class IotEmqxUpstreamProtocol { @@ -185,7 +206,7 @@ public class IotEmqxUpstreamProtocol {
185 * 延迟重连 206 * 延迟重连
186 */ 207 */
187 private void reconnectWithDelay() { 208 private void reconnectWithDelay() {
188 - if (!isRunning) { 209 + if (!running) {
189 return; 210 return;
190 } 211 }
191 if (mqttClient != null && mqttClient.isConnected()) { 212 if (mqttClient != null && mqttClient.isConnected()) {
@@ -195,7 +216,7 @@ public class IotEmqxUpstreamProtocol { @@ -195,7 +216,7 @@ public class IotEmqxUpstreamProtocol {
195 long delay = emqxProperties.getReconnectDelayMs(); 216 long delay = emqxProperties.getReconnectDelayMs();
196 log.info("[reconnectWithDelay][将在 {} 毫秒后尝试重连 MQTT Broker]", delay); 217 log.info("[reconnectWithDelay][将在 {} 毫秒后尝试重连 MQTT Broker]", delay);
197 vertx.setTimer(delay, timerId -> { 218 vertx.setTimer(delay, timerId -> {
198 - if (!isRunning) { 219 + if (!running) {
199 return; 220 return;
200 } 221 }
201 if (mqttClient != null && mqttClient.isConnected()) { 222 if (mqttClient != null && mqttClient.isConnected()) {
@@ -305,7 +326,7 @@ public class IotEmqxUpstreamProtocol { @@ -305,7 +326,7 @@ public class IotEmqxUpstreamProtocol {
305 private void setupMqttHandlers() { 326 private void setupMqttHandlers() {
306 // 1. 设置断开重连监听器 327 // 1. 设置断开重连监听器
307 mqttClient.closeHandler(closeEvent -> { 328 mqttClient.closeHandler(closeEvent -> {
308 - if (!isRunning) { 329 + if (!running) {
309 return; 330 return;
310 } 331 }
311 log.warn("[closeHandler][MQTT 连接已断开, 准备重连]"); 332 log.warn("[closeHandler][MQTT 连接已断开, 准备重连]");
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.http;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * IoT HTTP 协议配置
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +@Data
  11 +public class IotHttpConfig {
  12 +
  13 + /**
  14 + * 是否启用 SSL
  15 + */
  16 + private Boolean sslEnabled = false;
  17 +
  18 + /**
  19 + * SSL 证书路径
  20 + */
  21 + private String sslCertPath;
  22 +
  23 + /**
  24 + * SSL 私钥路径
  25 + */
  26 + private String sslKeyPath;
  27 +
  28 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpDownstreamSubscriber.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.http;  
2 -  
3 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;  
4 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;  
5 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
6 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
7 -import jakarta.annotation.PostConstruct;  
8 -import lombok.RequiredArgsConstructor;  
9 -import lombok.extern.slf4j.Slf4j;  
10 -  
11 -/**  
12 - * IoT 网关 HTTP 订阅者:接收下行给设备的消息  
13 - *  
14 - * @author 芋道源码  
15 - */  
16 -@RequiredArgsConstructor  
17 -@Slf4j  
18 -public class IotHttpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {  
19 -  
20 - private final IotHttpUpstreamProtocol protocol;  
21 -  
22 - private final IotMessageBus messageBus;  
23 -  
24 - @PostConstruct  
25 - public void init() {  
26 - messageBus.register(this);  
27 - }  
28 -  
29 - @Override  
30 - public String getTopic() {  
31 - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());  
32 - }  
33 -  
34 - @Override  
35 - public String getGroup() {  
36 - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group  
37 - return getTopic();  
38 - }  
39 -  
40 - @Override  
41 - public void onMessage(IotDeviceMessage message) {  
42 - log.info("[onMessage][IoT 网关 HTTP 协议不支持下行消息,忽略消息:{}]", message);  
43 - }  
44 -  
45 -}  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpProtocol.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.http;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
  4 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  5 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  6 +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolInstanceProperties;
  7 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
  8 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.downstream.IotHttpDownstreamSubscriber;
  9 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpAuthHandler;
  10 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpRegisterHandler;
  11 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpRegisterSubHandler;
  12 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpUpstreamHandler;
  13 +import io.vertx.core.Vertx;
  14 +import io.vertx.core.http.HttpServer;
  15 +import io.vertx.core.http.HttpServerOptions;
  16 +import io.vertx.core.net.PemKeyCertOptions;
  17 +import io.vertx.ext.web.Router;
  18 +import io.vertx.ext.web.handler.BodyHandler;
  19 +import lombok.Getter;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +
  22 +/**
  23 + * IoT HTTP 协议实现
  24 + * <p>
  25 + * 基于 Vert.x 实现 HTTP 服务器,接收设备上行消息
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +@Slf4j
  30 +public class IotHttpProtocol implements IotProtocol {
  31 +
  32 + /**
  33 + * 协议配置
  34 + */
  35 + private final ProtocolInstanceProperties properties;
  36 + /**
  37 + * 消息总线
  38 + */
  39 + private final IotMessageBus messageBus;
  40 + /**
  41 + * 服务器 ID(用于消息追踪,全局唯一)
  42 + */
  43 + @Getter
  44 + private final String serverId;
  45 +
  46 + /**
  47 + * Vert.x 实例(每个 Protocol 自己管理)
  48 + */
  49 + private Vertx vertx;
  50 + /**
  51 + * HTTP 服务器
  52 + */
  53 + private HttpServer httpServer;
  54 + /**
  55 + * 下行消息订阅者
  56 + */
  57 + private IotHttpDownstreamSubscriber downstreamSubscriber;
  58 +
  59 + /**
  60 + * 运行状态
  61 + */
  62 + private volatile boolean running = false;
  63 +
  64 + public IotHttpProtocol(ProtocolInstanceProperties properties, IotMessageBus messageBus) {
  65 + this.properties = properties;
  66 + this.messageBus = messageBus;
  67 + this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());
  68 + this.downstreamSubscriber = new IotHttpDownstreamSubscriber(this, messageBus);
  69 + }
  70 +
  71 + @Override
  72 + public String getId() {
  73 + return properties.getId();
  74 + }
  75 +
  76 + @Override
  77 + public IotProtocolTypeEnum getType() {
  78 + return IotProtocolTypeEnum.HTTP;
  79 + }
  80 +
  81 + @Override
  82 + public void start() {
  83 + if (running) {
  84 + log.warn("[start][IoT HTTP 协议 {} 已经在运行中]", getId());
  85 + return;
  86 + }
  87 +
  88 + // 1.1 创建 Vertx 实例(每个 Protocol 独立管理)
  89 + this.vertx = Vertx.vertx();
  90 +
  91 + // 1.2 创建路由
  92 + Router router = Router.router(vertx);
  93 + router.route().handler(BodyHandler.create());
  94 +
  95 + // 1.3 创建处理器,添加路由处理器
  96 + IotHttpAuthHandler authHandler = new IotHttpAuthHandler(this);
  97 + router.post(IotHttpAuthHandler.PATH).handler(authHandler);
  98 + IotHttpRegisterHandler registerHandler = new IotHttpRegisterHandler();
  99 + router.post(IotHttpRegisterHandler.PATH).handler(registerHandler);
  100 + IotHttpRegisterSubHandler registerSubHandler = new IotHttpRegisterSubHandler();
  101 + router.post(IotHttpRegisterSubHandler.PATH).handler(registerSubHandler);
  102 + IotHttpUpstreamHandler upstreamHandler = new IotHttpUpstreamHandler(this);
  103 + router.post(IotHttpUpstreamHandler.PATH).handler(upstreamHandler);
  104 +
  105 + // 1.4 启动 HTTP 服务器
  106 + IotHttpConfig httpConfig = properties.getHttp();
  107 + HttpServerOptions options = new HttpServerOptions().setPort(properties.getPort());
  108 + if (httpConfig != null && Boolean.TRUE.equals(httpConfig.getSslEnabled())) {
  109 + PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions()
  110 + .setKeyPath(httpConfig.getSslKeyPath())
  111 + .setCertPath(httpConfig.getSslCertPath());
  112 + options = options.setSsl(true).setKeyCertOptions(pemKeyCertOptions);
  113 + }
  114 + try {
  115 + httpServer = vertx.createHttpServer(options)
  116 + .requestHandler(router)
  117 + .listen()
  118 + .result();
  119 + running = true;
  120 + log.info("[start][IoT HTTP 协议 {} 启动成功,端口:{},serverId:{}]",
  121 + getId(), properties.getPort(), serverId);
  122 +
  123 + // 2. 启动下行消息订阅者
  124 + this.downstreamSubscriber = new IotHttpDownstreamSubscriber(this, messageBus);
  125 + this.downstreamSubscriber.start();
  126 + } catch (Exception e) {
  127 + log.error("[start][IoT HTTP 协议 {} 启动失败]", getId(), e);
  128 + // 启动失败时关闭 Vertx
  129 + if (vertx != null) {
  130 + vertx.close();
  131 + vertx = null;
  132 + }
  133 + throw e;
  134 + }
  135 +
  136 + // 2. 启动下行消息订阅者
  137 + this.downstreamSubscriber.start();
  138 + }
  139 +
  140 + @Override
  141 + public void stop() {
  142 + if (!running) {
  143 + return;
  144 + }
  145 + // 1. 停止下行消息订阅者
  146 + if (downstreamSubscriber != null) {
  147 + try {
  148 + downstreamSubscriber.stop();
  149 + log.info("[stop][IoT HTTP 协议 {} 下行消息订阅者已停止]", getId());
  150 + } catch (Exception e) {
  151 + log.error("[stop][IoT HTTP 协议 {} 下行消息订阅者停止失败]", getId(), e);
  152 + }
  153 + downstreamSubscriber = null;
  154 + }
  155 +
  156 + // 2.1 关闭 HTTP 服务器
  157 + if (httpServer != null) {
  158 + try {
  159 + httpServer.close().result();
  160 + log.info("[stop][IoT HTTP 协议 {} 服务器已停止]", getId());
  161 + } catch (Exception e) {
  162 + log.error("[stop][IoT HTTP 协议 {} 服务器停止失败]", getId(), e);
  163 + }
  164 + httpServer = null;
  165 + }
  166 + // 2.2 关闭 Vertx 实例
  167 + if (vertx != null) {
  168 + try {
  169 + vertx.close().result();
  170 + log.info("[stop][IoT HTTP 协议 {} Vertx 已关闭]", getId());
  171 + } catch (Exception e) {
  172 + log.error("[stop][IoT HTTP 协议 {} Vertx 关闭失败]", getId(), e);
  173 + }
  174 + vertx = null;
  175 + }
  176 + running = false;
  177 + log.info("[stop][IoT HTTP 协议 {} 已停止]", getId());
  178 + }
  179 +
  180 + @Override
  181 + public boolean isRunning() {
  182 + return running;
  183 + }
  184 +
  185 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpUpstreamProtocol.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.http;  
2 -  
3 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
4 -import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;  
5 -import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAuthHandler;  
6 -import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpRegisterHandler;  
7 -import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpRegisterSubHandler;  
8 -import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpUpstreamHandler;  
9 -import io.vertx.core.Vertx;  
10 -import io.vertx.core.http.HttpServer;  
11 -import io.vertx.core.http.HttpServerOptions;  
12 -import io.vertx.core.net.PemKeyCertOptions;  
13 -import io.vertx.ext.web.Router;  
14 -import io.vertx.ext.web.handler.BodyHandler;  
15 -import jakarta.annotation.PostConstruct;  
16 -import jakarta.annotation.PreDestroy;  
17 -import lombok.Getter;  
18 -import lombok.extern.slf4j.Slf4j;  
19 -  
20 -/**  
21 - * IoT 网关 HTTP 协议:接收设备上行消息  
22 - *  
23 - * @author 芋道源码  
24 - */  
25 -@Slf4j  
26 -public class IotHttpUpstreamProtocol {  
27 -  
28 - private final IotGatewayProperties.HttpProperties httpProperties;  
29 -  
30 - private final Vertx vertx;  
31 -  
32 - private HttpServer httpServer;  
33 -  
34 - @Getter  
35 - private final String serverId;  
36 -  
37 - public IotHttpUpstreamProtocol(IotGatewayProperties.HttpProperties httpProperties, Vertx vertx) {  
38 - this.httpProperties = httpProperties;  
39 - this.vertx = vertx;  
40 - this.serverId = IotDeviceMessageUtils.generateServerId(httpProperties.getServerPort());  
41 - }  
42 -  
43 - @PostConstruct  
44 - public void start() {  
45 - // 创建路由  
46 - Router router = Router.router(vertx);  
47 - router.route().handler(BodyHandler.create());  
48 -  
49 - // 创建处理器,添加路由处理器  
50 - IotHttpAuthHandler authHandler = new IotHttpAuthHandler(this);  
51 - router.post(IotHttpAuthHandler.PATH).handler(authHandler);  
52 - IotHttpRegisterHandler registerHandler = new IotHttpRegisterHandler();  
53 - router.post(IotHttpRegisterHandler.PATH).handler(registerHandler);  
54 - IotHttpRegisterSubHandler registerSubHandler = new IotHttpRegisterSubHandler();  
55 - router.post(IotHttpRegisterSubHandler.PATH).handler(registerSubHandler);  
56 - IotHttpUpstreamHandler upstreamHandler = new IotHttpUpstreamHandler(this);  
57 - router.post(IotHttpUpstreamHandler.PATH).handler(upstreamHandler);  
58 -  
59 - // 启动 HTTP 服务器  
60 - HttpServerOptions options = new HttpServerOptions()  
61 - .setPort(httpProperties.getServerPort());  
62 - if (Boolean.TRUE.equals(httpProperties.getSslEnabled())) {  
63 - PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions().setKeyPath(httpProperties.getSslKeyPath())  
64 - .setCertPath(httpProperties.getSslCertPath());  
65 - options = options.setSsl(true).setKeyCertOptions(pemKeyCertOptions);  
66 - }  
67 - try {  
68 - httpServer = vertx.createHttpServer(options)  
69 - .requestHandler(router)  
70 - .listen()  
71 - .result();  
72 - log.info("[start][IoT 网关 HTTP 协议启动成功,端口:{}]", httpProperties.getServerPort());  
73 - } catch (Exception e) {  
74 - log.error("[start][IoT 网关 HTTP 协议启动失败]", e);  
75 - throw e;  
76 - }  
77 - }  
78 -  
79 - @PreDestroy  
80 - public void stop() {  
81 - if (httpServer != null) {  
82 - try {  
83 - httpServer.close().result();  
84 - log.info("[stop][IoT 网关 HTTP 协议已停止]");  
85 - } catch (Exception e) {  
86 - log.error("[stop][IoT 网关 HTTP 协议停止失败]", e);  
87 - }  
88 - }  
89 - }  
90 -  
91 -}  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/downstream/IotHttpDownstreamSubscriber.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.downstream;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
  6 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber;
  7 +import lombok.extern.slf4j.Slf4j;
  8 +
  9 +/**
  10 + * IoT 网关 HTTP 订阅者:接收下行给设备的消息
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +
  15 +@Slf4j
  16 +public class IotHttpDownstreamSubscriber extends IotProtocolDownstreamSubscriber {
  17 +
  18 + public IotHttpDownstreamSubscriber(IotProtocol protocol, IotMessageBus messageBus) {
  19 + super(protocol, messageBus);
  20 + }
  21 +
  22 + @Override
  23 + protected void handleMessage(IotDeviceMessage message) {
  24 + log.info("[handleMessage][IoT 网关 HTTP 协议不支持下行消息,忽略消息:{}]", message);
  25 + }
  26 +
  27 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpAuthHandler.java
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; 1 +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
@@ -10,10 +10,10 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; @@ -10,10 +10,10 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
12 import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; 12 import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
13 -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 13 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol;
  14 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAbstractHandler;
14 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; 15 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;
15 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; 16 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
16 -import io.vertx.core.json.JsonObject;  
17 import io.vertx.ext.web.RoutingContext; 17 import io.vertx.ext.web.RoutingContext;
18 18
19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -32,7 +32,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -32,7 +32,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
32 32
33 public static final String PATH = "/auth"; 33 public static final String PATH = "/auth";
34 34
35 - private final IotHttpUpstreamProtocol protocol; 35 + private final String serverId;
36 36
37 private final IotDeviceTokenService deviceTokenService; 37 private final IotDeviceTokenService deviceTokenService;
38 38
@@ -40,8 +40,8 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -40,8 +40,8 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
40 40
41 private final IotDeviceMessageService deviceMessageService; 41 private final IotDeviceMessageService deviceMessageService;
42 42
43 - public IotHttpAuthHandler(IotHttpUpstreamProtocol protocol) {  
44 - this.protocol = protocol; 43 + public IotHttpAuthHandler(IotHttpProtocol protocol) {
  44 + this.serverId = protocol.getServerId();
45 this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); 45 this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);
46 this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class); 46 this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);
47 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); 47 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
@@ -50,32 +50,25 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -50,32 +50,25 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
50 @Override 50 @Override
51 public CommonResult<Object> handle0(RoutingContext context) { 51 public CommonResult<Object> handle0(RoutingContext context) {
52 // 1. 解析参数 52 // 1. 解析参数
53 - JsonObject body = context.body().asJsonObject();  
54 - if (body == null) {  
55 - throw invalidParamException("请求体不能为空");  
56 - }  
57 - String clientId = body.getString("clientId");  
58 - if (StrUtil.isEmpty(clientId)) { 53 + IotDeviceAuthReqDTO request = deserializeRequest(context, IotDeviceAuthReqDTO.class);
  54 + if (StrUtil.isEmpty(request.getClientId())) {
59 throw invalidParamException("clientId 不能为空"); 55 throw invalidParamException("clientId 不能为空");
60 } 56 }
61 - String username = body.getString("username");  
62 - if (StrUtil.isEmpty(username)) { 57 + if (StrUtil.isEmpty(request.getUsername())) {
63 throw invalidParamException("username 不能为空"); 58 throw invalidParamException("username 不能为空");
64 } 59 }
65 - String password = body.getString("password");  
66 - if (StrUtil.isEmpty(password)) { 60 + if (StrUtil.isEmpty(request.getPassword())) {
67 throw invalidParamException("password 不能为空"); 61 throw invalidParamException("password 不能为空");
68 } 62 }
69 63
70 // 2.1 执行认证 64 // 2.1 执行认证
71 - CommonResult<Boolean> result = deviceApi.authDevice(new IotDeviceAuthReqDTO()  
72 - .setClientId(clientId).setUsername(username).setPassword(password)); 65 + CommonResult<Boolean> result = deviceApi.authDevice(request);
73 result.checkError(); 66 result.checkError();
74 if (!BooleanUtil.isTrue(result.getData())) { 67 if (!BooleanUtil.isTrue(result.getData())) {
75 throw exception(DEVICE_AUTH_FAIL); 68 throw exception(DEVICE_AUTH_FAIL);
76 } 69 }
77 // 2.2 生成 Token 70 // 2.2 生成 Token
78 - IotDeviceIdentity deviceInfo = deviceTokenService.parseUsername(username); 71 + IotDeviceIdentity deviceInfo = deviceTokenService.parseUsername(request.getUsername());
79 Assert.notNull(deviceInfo, "设备信息不能为空"); 72 Assert.notNull(deviceInfo, "设备信息不能为空");
80 String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName()); 73 String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName());
81 Assert.notBlank(token, "生成 token 不能为空位"); 74 Assert.notBlank(token, "生成 token 不能为空位");
@@ -83,7 +76,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -83,7 +76,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
83 // 3. 执行上线 76 // 3. 执行上线
84 IotDeviceMessage message = IotDeviceMessage.buildStateUpdateOnline(); 77 IotDeviceMessage message = IotDeviceMessage.buildStateUpdateOnline();
85 deviceMessageService.sendDeviceMessage(message, 78 deviceMessageService.sendDeviceMessage(message,
86 - deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId()); 79 + deviceInfo.getProductKey(), deviceInfo.getDeviceName(), serverId);
87 80
88 // 构建响应数据 81 // 构建响应数据
89 return success(MapUtil.of("token", token)); 82 return success(MapUtil.of("token", token));
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpRegisterHandler.java
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; 1 +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.hutool.extra.spring.SpringUtil; 4 import cn.hutool.extra.spring.SpringUtil;
@@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 6 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
7 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; 7 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
8 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; 8 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;
9 -import io.vertx.core.json.JsonObject; 9 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAbstractHandler;
10 import io.vertx.ext.web.RoutingContext; 10 import io.vertx.ext.web.RoutingContext;
11 11
12 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; 12 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
@@ -33,27 +33,19 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler { @@ -33,27 +33,19 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler {
33 @Override 33 @Override
34 public CommonResult<Object> handle0(RoutingContext context) { 34 public CommonResult<Object> handle0(RoutingContext context) {
35 // 1. 解析参数 35 // 1. 解析参数
36 - JsonObject body = context.body().asJsonObject();  
37 - if (body == null) {  
38 - throw invalidParamException("请求体不能为空");  
39 - }  
40 - String productKey = body.getString("productKey");  
41 - if (StrUtil.isEmpty(productKey)) { 36 + IotDeviceRegisterReqDTO request = deserializeRequest(context, IotDeviceRegisterReqDTO.class);
  37 + if (StrUtil.isEmpty(request.getProductKey())) {
42 throw invalidParamException("productKey 不能为空"); 38 throw invalidParamException("productKey 不能为空");
43 } 39 }
44 - String deviceName = body.getString("deviceName");  
45 - if (StrUtil.isEmpty(deviceName)) { 40 + if (StrUtil.isEmpty(request.getDeviceName())) {
46 throw invalidParamException("deviceName 不能为空"); 41 throw invalidParamException("deviceName 不能为空");
47 } 42 }
48 - String productSecret = body.getString("productSecret");  
49 - if (StrUtil.isEmpty(productSecret)) { 43 + if (StrUtil.isEmpty(request.getProductSecret())) {
50 throw invalidParamException("productSecret 不能为空"); 44 throw invalidParamException("productSecret 不能为空");
51 } 45 }
52 46
53 // 2. 调用动态注册 47 // 2. 调用动态注册
54 - IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO()  
55 - .setProductKey(productKey).setDeviceName(deviceName).setProductSecret(productSecret);  
56 - CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(reqDTO); 48 + CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(request);
57 result.checkError(); 49 result.checkError();
58 50
59 // 3. 返回结果 51 // 3. 返回结果
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterSubHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpRegisterSubHandler.java
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; 1 +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.extra.spring.SpringUtil; 4 import cn.hutool.extra.spring.SpringUtil;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
6 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 6 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
7 import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; 7 import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;
  8 +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;
8 import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; 9 import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;
9 -import io.vertx.core.json.JsonObject; 10 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAbstractHandler;
10 import io.vertx.ext.web.RoutingContext; 11 import io.vertx.ext.web.RoutingContext;
  12 +import lombok.Data;
11 13
12 import java.util.List; 14 import java.util.List;
13 15
@@ -39,29 +41,32 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler { @@ -39,29 +41,32 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler {
39 41
40 @Override 42 @Override
41 public CommonResult<Object> handle0(RoutingContext context) { 43 public CommonResult<Object> handle0(RoutingContext context) {
42 - // 1. 解析通用参数 44 + // 1.1 解析通用参数
43 String productKey = context.pathParam("productKey"); 45 String productKey = context.pathParam("productKey");
44 String deviceName = context.pathParam("deviceName"); 46 String deviceName = context.pathParam("deviceName");
45 -  
46 - // 2. 解析子设备列表  
47 - JsonObject body = context.body().asJsonObject();  
48 - if (body == null) {  
49 - throw invalidParamException("请求体不能为空");  
50 - }  
51 - if (body.getJsonArray("params") == null) { 47 + // 1.2 解析子设备列表
  48 + SubDeviceRegisterRequest request = deserializeRequest(context, SubDeviceRegisterRequest.class);
  49 + if (CollUtil.isEmpty(request.getParams())) {
52 throw invalidParamException("params 不能为空"); 50 throw invalidParamException("params 不能为空");
53 } 51 }
54 - List<cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO> subDevices = JsonUtils.parseArray(  
55 - body.getJsonArray("params").toString(), cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO.class);  
56 52
57 - // 3. 调用子设备动态注册 53 + // 2. 调用子设备动态注册
58 IotSubDeviceRegisterFullReqDTO reqDTO = new IotSubDeviceRegisterFullReqDTO() 54 IotSubDeviceRegisterFullReqDTO reqDTO = new IotSubDeviceRegisterFullReqDTO()
59 - .setGatewayProductKey(productKey).setGatewayDeviceName(deviceName).setSubDevices(subDevices); 55 + .setGatewayProductKey(productKey)
  56 + .setGatewayDeviceName(deviceName)
  57 + .setSubDevices(request.getParams());
60 CommonResult<List<IotSubDeviceRegisterRespDTO>> result = deviceApi.registerSubDevices(reqDTO); 58 CommonResult<List<IotSubDeviceRegisterRespDTO>> result = deviceApi.registerSubDevices(reqDTO);
61 result.checkError(); 59 result.checkError();
62 60
63 - // 4. 返回结果 61 + // 3. 返回结果
64 return success(result.getData()); 62 return success(result.getData());
65 } 63 }
66 64
  65 + @Data
  66 + public static class SubDeviceRegisterRequest {
  67 +
  68 + private List<IotSubDeviceRegisterReqDTO> params;
  69 +
  70 + }
  71 +
67 } 72 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpUpstreamHandler.java
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; 1 +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
@@ -6,31 +6,28 @@ import cn.hutool.core.text.StrPool; @@ -6,31 +6,28 @@ import cn.hutool.core.text.StrPool;
6 import cn.hutool.extra.spring.SpringUtil; 6 import cn.hutool.extra.spring.SpringUtil;
7 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 7 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
9 -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 9 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol;
  10 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAbstractHandler;
10 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; 11 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
11 import io.vertx.ext.web.RoutingContext; 12 import io.vertx.ext.web.RoutingContext;
12 -import lombok.RequiredArgsConstructor;  
13 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
14 14
15 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;  
16 -  
17 /** 15 /**
18 * IoT 网关 HTTP 协议的【上行】处理器 16 * IoT 网关 HTTP 协议的【上行】处理器
19 * 17 *
20 * @author 芋道源码 18 * @author 芋道源码
21 */ 19 */
22 -@RequiredArgsConstructor  
23 @Slf4j 20 @Slf4j
24 public class IotHttpUpstreamHandler extends IotHttpAbstractHandler { 21 public class IotHttpUpstreamHandler extends IotHttpAbstractHandler {
25 22
26 public static final String PATH = "/topic/sys/:productKey/:deviceName/*"; 23 public static final String PATH = "/topic/sys/:productKey/:deviceName/*";
27 24
28 - private final IotHttpUpstreamProtocol protocol; 25 + private final String serverId;
29 26
30 private final IotDeviceMessageService deviceMessageService; 27 private final IotDeviceMessageService deviceMessageService;
31 28
32 - public IotHttpUpstreamHandler(IotHttpUpstreamProtocol protocol) {  
33 - this.protocol = protocol; 29 + public IotHttpUpstreamHandler(IotHttpProtocol protocol) {
  30 + this.serverId = protocol.getServerId();
34 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); 31 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
35 } 32 }
36 33
@@ -41,20 +38,15 @@ public class IotHttpUpstreamHandler extends IotHttpAbstractHandler { @@ -41,20 +38,15 @@ public class IotHttpUpstreamHandler extends IotHttpAbstractHandler {
41 String deviceName = context.pathParam("deviceName"); 38 String deviceName = context.pathParam("deviceName");
42 String method = context.pathParam("*").replaceAll(StrPool.SLASH, StrPool.DOT); 39 String method = context.pathParam("*").replaceAll(StrPool.SLASH, StrPool.DOT);
43 40
44 - // 2.1 解析消息  
45 - if (context.body().buffer() == null) {  
46 - throw invalidParamException("请求体不能为空");  
47 - }  
48 - byte[] bytes = context.body().buffer().getBytes();  
49 - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(bytes,  
50 - productKey, deviceName); 41 + // 2.1 根据 Content-Type 反序列化消息
  42 + IotDeviceMessage message = deserializeRequest(context, IotDeviceMessage.class);
51 Assert.equals(method, message.getMethod(), "method 不匹配"); 43 Assert.equals(method, message.getMethod(), "method 不匹配");
52 // 2.2 发送消息 44 // 2.2 发送消息
53 deviceMessageService.sendDeviceMessage(message, 45 deviceMessageService.sendDeviceMessage(message,
54 - productKey, deviceName, protocol.getServerId()); 46 + productKey, deviceName, serverId);
55 47
56 // 3. 返回结果 48 // 3. 返回结果
57 return CommonResult.success(MapUtil.of("messageId", message.getId())); 49 return CommonResult.success(MapUtil.of("messageId", message.getId()));
58 } 50 }
59 51
60 -}  
61 \ No newline at end of file 52 \ No newline at end of file
  53 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.http.router;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.core.util.ArrayUtil;
4 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
5 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
6 import cn.hutool.extra.spring.SpringUtil; 7 import cn.hutool.extra.spring.SpringUtil;
@@ -9,11 +10,13 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -9,11 +10,13 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
9 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 10 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
10 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 11 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
11 import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; 12 import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
  13 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpAuthHandler;
  14 +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpRegisterHandler;
  15 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;
12 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; 16 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;
13 import io.vertx.core.Handler; 17 import io.vertx.core.Handler;
14 import io.vertx.core.http.HttpHeaders; 18 import io.vertx.core.http.HttpHeaders;
15 import io.vertx.ext.web.RoutingContext; 19 import io.vertx.ext.web.RoutingContext;
16 -import lombok.RequiredArgsConstructor;  
17 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
18 import org.springframework.http.MediaType; 21 import org.springframework.http.MediaType;
19 22
@@ -27,12 +30,13 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @@ -27,12 +30,13 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
27 * 30 *
28 * @author 芋道源码 31 * @author 芋道源码
29 */ 32 */
30 -@RequiredArgsConstructor  
31 @Slf4j 33 @Slf4j
32 public abstract class IotHttpAbstractHandler implements Handler<RoutingContext> { 34 public abstract class IotHttpAbstractHandler implements Handler<RoutingContext> {
33 35
34 private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); 36 private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);
35 37
  38 + private final IotMessageSerializerManager serializerManager = SpringUtil.getBean(IotMessageSerializerManager.class);
  39 +
36 @Override 40 @Override
37 public final void handle(RoutingContext context) { 41 public final void handle(RoutingContext context) {
38 try { 42 try {
@@ -83,12 +87,26 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt; @@ -83,12 +87,26 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt;
83 } 87 }
84 } 88 }
85 89
  90 + // ========== 序列化相关方法 ==========
  91 +
  92 + protected static <T> T deserializeRequest(RoutingContext context, Class<T> clazz) {
  93 + byte[] body = context.body().buffer() != null ? context.body().buffer().getBytes() : null;
  94 + if (ArrayUtil.isEmpty(body)) {
  95 + throw invalidParamException("请求体不能为空");
  96 + }
  97 + return JsonUtils.parseObject(body, clazz);
  98 + }
  99 +
  100 + private static String serializeResponse(Object data) {
  101 + return JsonUtils.toJsonString(data);
  102 + }
  103 +
86 @SuppressWarnings("deprecation") 104 @SuppressWarnings("deprecation")
87 public static void writeResponse(RoutingContext context, Object data) { 105 public static void writeResponse(RoutingContext context, Object data) {
88 context.response() 106 context.response()
89 .setStatusCode(200) 107 .setStatusCode(200)
90 .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) 108 .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE)
91 - .end(JsonUtils.toJsonString(data)); 109 + .end(serializeResponse(data));
92 } 110 }
93 111
94 } 112 }
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.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;  
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 5 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber;
7 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler; 6 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler;
8 -import jakarta.annotation.PostConstruct;  
9 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
10 8
11 /** 9 /**
@@ -16,64 +14,27 @@ import lombok.extern.slf4j.Slf4j; @@ -16,64 +14,27 @@ import lombok.extern.slf4j.Slf4j;
16 * @author 芋道源码 14 * @author 芋道源码
17 */ 15 */
18 @Slf4j 16 @Slf4j
19 -public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {  
20 -  
21 - private final IotMqttUpstreamProtocol upstreamProtocol; 17 +public class IotMqttDownstreamSubscriber extends IotProtocolDownstreamSubscriber {
22 18
23 private final IotMqttDownstreamHandler downstreamHandler; 19 private final IotMqttDownstreamHandler downstreamHandler;
24 20
25 - private final IotMessageBus messageBus;  
26 -  
27 - public IotMqttDownstreamSubscriber(IotMqttUpstreamProtocol upstreamProtocol, 21 + public IotMqttDownstreamSubscriber(IotMqttUpstreamProtocol protocol,
28 IotMqttDownstreamHandler downstreamHandler, 22 IotMqttDownstreamHandler downstreamHandler,
29 IotMessageBus messageBus) { 23 IotMessageBus messageBus) {
30 - this.upstreamProtocol = upstreamProtocol; 24 + super(protocol, messageBus);
31 this.downstreamHandler = downstreamHandler; 25 this.downstreamHandler = downstreamHandler;
32 - this.messageBus = messageBus;  
33 - }  
34 -  
35 - @PostConstruct  
36 - public void subscribe() {  
37 - messageBus.register(this);  
38 - log.info("[subscribe][MQTT 协议下行消息订阅成功,主题:{}]", getTopic());  
39 - }  
40 -  
41 - @Override  
42 - public String getTopic() {  
43 - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(upstreamProtocol.getServerId());  
44 - }  
45 -  
46 - @Override  
47 - public String getGroup() {  
48 - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group  
49 - return getTopic();  
50 } 26 }
51 27
52 @Override 28 @Override
53 - public void onMessage(IotDeviceMessage message) {  
54 - log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]",  
55 - message.getId(), message.getMethod(), message.getDeviceId());  
56 - try {  
57 - // 1. 校验  
58 - String method = message.getMethod();  
59 - if (method == null) {  
60 - log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]",  
61 - message.getId(), message.getDeviceId());  
62 - return;  
63 - }  
64 -  
65 - // 2. 委托给下行处理器处理业务逻辑  
66 - boolean success = downstreamHandler.handleDownstreamMessage(message);  
67 - if (success) {  
68 - log.debug("[onMessage][下行消息处理成功, messageId: {}, method: {}, deviceId: {}]",  
69 - message.getId(), message.getMethod(), message.getDeviceId());  
70 - } else {  
71 - log.warn("[onMessage][下行消息处理失败, messageId: {}, method: {}, deviceId: {}]",  
72 - message.getId(), message.getMethod(), message.getDeviceId());  
73 - }  
74 - } catch (Exception e) {  
75 - log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]",  
76 - message.getId(), message.getMethod(), message.getDeviceId(), e); 29 + protected void handleMessage(IotDeviceMessage message) {
  30 + boolean success = downstreamHandler.handleDownstreamMessage(message);
  31 + if (success) {
  32 + log.debug("[handleMessage][下行消息处理成功, messageId: {}, method: {}, deviceId: {}]",
  33 + message.getId(), message.getMethod(), message.getDeviceId());
  34 + } else {
  35 + log.warn("[handleMessage][下行消息处理失败, messageId: {}, method: {}, deviceId: {}]",
  36 + message.getId(), message.getMethod(), message.getDeviceId());
77 } 37 }
78 } 38 }
  39 +
79 } 40 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttUpstreamProtocol.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.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
3 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 4 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
4 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 5 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  6 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
5 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; 7 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;
6 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamHandler; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamHandler;
7 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; 9 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
@@ -19,7 +21,11 @@ import lombok.extern.slf4j.Slf4j; @@ -19,7 +21,11 @@ import lombok.extern.slf4j.Slf4j;
19 * @author 芋道源码 21 * @author 芋道源码
20 */ 22 */
21 @Slf4j 23 @Slf4j
22 -public class IotMqttUpstreamProtocol { 24 +public class IotMqttUpstreamProtocol implements IotProtocol {
  25 +
  26 + private static final String ID = "mqtt";
  27 +
  28 + private volatile boolean running = false;
23 29
24 private final IotGatewayProperties.MqttProperties mqttProperties; 30 private final IotGatewayProperties.MqttProperties mqttProperties;
25 31
@@ -45,7 +51,23 @@ public class IotMqttUpstreamProtocol { @@ -45,7 +51,23 @@ public class IotMqttUpstreamProtocol {
45 this.serverId = IotDeviceMessageUtils.generateServerId(mqttProperties.getPort()); 51 this.serverId = IotDeviceMessageUtils.generateServerId(mqttProperties.getPort());
46 } 52 }
47 53
  54 + @Override
  55 + public String getId() {
  56 + return ID;
  57 + }
  58 +
  59 + @Override
  60 + public IotProtocolTypeEnum getType() {
  61 + return IotProtocolTypeEnum.MQTT;
  62 + }
  63 +
  64 + @Override
  65 + public boolean isRunning() {
  66 + return running;
  67 + }
  68 +
48 // TODO @haohao:这里的编写,是不是和 tcp 对应的,风格保持一致哈; 69 // TODO @haohao:这里的编写,是不是和 tcp 对应的,风格保持一致哈;
  70 + @Override
49 @PostConstruct 71 @PostConstruct
50 public void start() { 72 public void start() {
51 // 创建服务器选项 73 // 创建服务器选项
@@ -71,6 +93,7 @@ public class IotMqttUpstreamProtocol { @@ -71,6 +93,7 @@ public class IotMqttUpstreamProtocol {
71 // 启动服务器 93 // 启动服务器
72 try { 94 try {
73 mqttServer.listen().result(); 95 mqttServer.listen().result();
  96 + running = true;
74 log.info("[start][IoT 网关 MQTT 协议启动成功,端口:{}]", mqttProperties.getPort()); 97 log.info("[start][IoT 网关 MQTT 协议启动成功,端口:{}]", mqttProperties.getPort());
75 } catch (Exception e) { 98 } catch (Exception e) {
76 log.error("[start][IoT 网关 MQTT 协议启动失败]", e); 99 log.error("[start][IoT 网关 MQTT 协议启动失败]", e);
@@ -78,11 +101,13 @@ public class IotMqttUpstreamProtocol { @@ -78,11 +101,13 @@ public class IotMqttUpstreamProtocol {
78 } 101 }
79 } 102 }
80 103
  104 + @Override
81 @PreDestroy 105 @PreDestroy
82 public void stop() { 106 public void stop() {
83 if (mqttServer != null) { 107 if (mqttServer != null) {
84 try { 108 try {
85 mqttServer.close().result(); 109 mqttServer.close().result();
  110 + running = false;
86 log.info("[stop][IoT 网关 MQTT 协议已停止]"); 111 log.info("[stop][IoT 网关 MQTT 协议已停止]");
87 } catch (Exception e) { 112 } catch (Exception e) {
88 log.error("[stop][IoT 网关 MQTT 协议停止失败]", e); 113 log.error("[stop][IoT 网关 MQTT 协议停止失败]", e);
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpDownstreamSubscriber.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.tcp; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;
2 2
3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;  
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
7 -import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; 5 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber;
8 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpDownstreamHandler; 6 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpDownstreamHandler;
9 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
10 -import jakarta.annotation.PostConstruct;  
11 -import lombok.RequiredArgsConstructor;  
12 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
13 8
14 /** 9 /**
@@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j; @@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j;
17 * @author 芋道源码 12 * @author 芋道源码
18 */ 13 */
19 @Slf4j 14 @Slf4j
20 -@RequiredArgsConstructor  
21 -public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { 15 +public class IotTcpDownstreamSubscriber extends IotProtocolDownstreamSubscriber {
22 16
23 - private final IotTcpUpstreamProtocol protocol; 17 + private final IotTcpDownstreamHandler downstreamHandler;
24 18
25 - private final IotDeviceMessageService messageService;  
26 -  
27 - private final IotTcpConnectionManager connectionManager;  
28 -  
29 - private final IotMessageBus messageBus;  
30 -  
31 - private IotTcpDownstreamHandler downstreamHandler;  
32 -  
33 - @PostConstruct  
34 - public void init() {  
35 - // 初始化下游处理器  
36 - this.downstreamHandler = new IotTcpDownstreamHandler(messageService, connectionManager);  
37 - // 注册下游订阅者  
38 - messageBus.register(this);  
39 - log.info("[init][TCP 下游订阅者初始化完成,服务器 ID: {},Topic: {}]",  
40 - protocol.getServerId(), getTopic());  
41 - }  
42 -  
43 - @Override  
44 - public String getTopic() {  
45 - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());  
46 - }  
47 -  
48 - @Override  
49 - public String getGroup() {  
50 - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group  
51 - return getTopic(); 19 + public IotTcpDownstreamSubscriber(IotTcpUpstreamProtocol protocol,
  20 + IotTcpDownstreamHandler downstreamHandler,
  21 + IotMessageBus messageBus) {
  22 + super(protocol, messageBus);
  23 + this.downstreamHandler = downstreamHandler;
52 } 24 }
53 25
54 @Override 26 @Override
55 - public void onMessage(IotDeviceMessage message) {  
56 - try {  
57 - downstreamHandler.handle(message);  
58 - } catch (Exception e) {  
59 - log.error("[onMessage][处理下行消息失败,设备 ID: {},方法: {},消息 ID: {}]",  
60 - message.getDeviceId(), message.getMethod(), message.getId(), e);  
61 - } 27 + protected void handleMessage(IotDeviceMessage message) {
  28 + downstreamHandler.handle(message);
62 } 29 }
63 30
64 -}  
65 \ No newline at end of file 31 \ No newline at end of file
  32 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpUpstreamProtocol.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.tcp; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;
2 2
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
3 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 4 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
4 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 5 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  6 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
5 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; 7 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;
6 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpUpstreamHandler; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpUpstreamHandler;
7 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; 9 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
@@ -21,7 +23,9 @@ import lombok.extern.slf4j.Slf4j; @@ -21,7 +23,9 @@ import lombok.extern.slf4j.Slf4j;
21 * @author 芋道源码 23 * @author 芋道源码
22 */ 24 */
23 @Slf4j 25 @Slf4j
24 -public class IotTcpUpstreamProtocol { 26 +public class IotTcpUpstreamProtocol implements IotProtocol {
  27 +
  28 + private static final String ID = "tcp";
25 29
26 private final IotGatewayProperties.TcpProperties tcpProperties; 30 private final IotGatewayProperties.TcpProperties tcpProperties;
27 31
@@ -38,6 +42,8 @@ public class IotTcpUpstreamProtocol { @@ -38,6 +42,8 @@ public class IotTcpUpstreamProtocol {
38 42
39 private NetServer tcpServer; 43 private NetServer tcpServer;
40 44
  45 + private volatile boolean running = false;
  46 +
41 public IotTcpUpstreamProtocol(IotGatewayProperties.TcpProperties tcpProperties, 47 public IotTcpUpstreamProtocol(IotGatewayProperties.TcpProperties tcpProperties,
42 IotDeviceService deviceService, 48 IotDeviceService deviceService,
43 IotDeviceMessageService messageService, 49 IotDeviceMessageService messageService,
@@ -51,6 +57,17 @@ public class IotTcpUpstreamProtocol { @@ -51,6 +57,17 @@ public class IotTcpUpstreamProtocol {
51 this.serverId = IotDeviceMessageUtils.generateServerId(tcpProperties.getPort()); 57 this.serverId = IotDeviceMessageUtils.generateServerId(tcpProperties.getPort());
52 } 58 }
53 59
  60 + @Override
  61 + public String getId() {
  62 + return ID;
  63 + }
  64 +
  65 + @Override
  66 + public IotProtocolTypeEnum getType() {
  67 + return IotProtocolTypeEnum.TCP;
  68 + }
  69 +
  70 + @Override
54 @PostConstruct 71 @PostConstruct
55 public void start() { 72 public void start() {
56 // 创建服务器选项 73 // 创建服务器选项
@@ -78,6 +95,7 @@ public class IotTcpUpstreamProtocol { @@ -78,6 +95,7 @@ public class IotTcpUpstreamProtocol {
78 // 启动服务器 95 // 启动服务器
79 try { 96 try {
80 tcpServer.listen().result(); 97 tcpServer.listen().result();
  98 + running = true;
81 log.info("[start][IoT 网关 TCP 协议启动成功,端口:{}]", tcpProperties.getPort()); 99 log.info("[start][IoT 网关 TCP 协议启动成功,端口:{}]", tcpProperties.getPort());
82 } catch (Exception e) { 100 } catch (Exception e) {
83 log.error("[start][IoT 网关 TCP 协议启动失败]", e); 101 log.error("[start][IoT 网关 TCP 协议启动失败]", e);
@@ -85,15 +103,23 @@ public class IotTcpUpstreamProtocol { @@ -85,15 +103,23 @@ public class IotTcpUpstreamProtocol {
85 } 103 }
86 } 104 }
87 105
  106 + @Override
88 @PreDestroy 107 @PreDestroy
89 public void stop() { 108 public void stop() {
90 if (tcpServer != null) { 109 if (tcpServer != null) {
91 try { 110 try {
92 tcpServer.close().result(); 111 tcpServer.close().result();
  112 + running = false;
93 log.info("[stop][IoT 网关 TCP 协议已停止]"); 113 log.info("[stop][IoT 网关 TCP 协议已停止]");
94 } catch (Exception e) { 114 } catch (Exception e) {
95 log.error("[stop][IoT 网关 TCP 协议停止失败]", e); 115 log.error("[stop][IoT 网关 TCP 协议停止失败]", e);
96 } 116 }
97 } 117 }
98 } 118 }
99 -}  
100 \ No newline at end of file 119 \ No newline at end of file
  120 +
  121 + @Override
  122 + public boolean isRunning() {
  123 + return running;
  124 + }
  125 +
  126 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotUdpDownstreamSubscriber.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.udp; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.udp;
2 2
3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;  
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
7 -import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager; 5 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber;
8 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpDownstreamHandler; 6 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpDownstreamHandler;
9 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
10 -import jakarta.annotation.PostConstruct;  
11 -import lombok.RequiredArgsConstructor;  
12 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
13 8
14 /** 9 /**
@@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j; @@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j;
17 * @author 芋道源码 12 * @author 芋道源码
18 */ 13 */
19 @Slf4j 14 @Slf4j
20 -@RequiredArgsConstructor  
21 -public class IotUdpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { 15 +public class IotUdpDownstreamSubscriber extends IotProtocolDownstreamSubscriber {
22 16
23 - private final IotUdpUpstreamProtocol protocol; 17 + private final IotUdpDownstreamHandler downstreamHandler;
24 18
25 - private final IotDeviceMessageService messageService;  
26 -  
27 - private final IotUdpSessionManager sessionManager;  
28 -  
29 - private final IotMessageBus messageBus;  
30 -  
31 - private IotUdpDownstreamHandler downstreamHandler;  
32 -  
33 - @PostConstruct  
34 - public void init() {  
35 - // 初始化下游处理器  
36 - this.downstreamHandler = new IotUdpDownstreamHandler(messageService, sessionManager, protocol);  
37 - // 注册下游订阅者  
38 - messageBus.register(this);  
39 - log.info("[init][UDP 下游订阅者初始化完成,服务器 ID: {},Topic: {}]",  
40 - protocol.getServerId(), getTopic());  
41 - }  
42 -  
43 - @Override  
44 - public String getTopic() {  
45 - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());  
46 - }  
47 -  
48 - @Override  
49 - public String getGroup() {  
50 - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group  
51 - return getTopic(); 19 + public IotUdpDownstreamSubscriber(IotUdpUpstreamProtocol protocol,
  20 + IotUdpDownstreamHandler downstreamHandler,
  21 + IotMessageBus messageBus) {
  22 + super(protocol, messageBus);
  23 + this.downstreamHandler = downstreamHandler;
52 } 24 }
53 25
54 @Override 26 @Override
55 - public void onMessage(IotDeviceMessage message) {  
56 - try {  
57 - downstreamHandler.handle(message);  
58 - } catch (Exception e) {  
59 - log.error("[onMessage][处理下行消息失败,设备 ID: {},方法: {},消息 ID: {}]",  
60 - message.getDeviceId(), message.getMethod(), message.getId(), e);  
61 - } 27 + protected void handleMessage(IotDeviceMessage message) {
  28 + downstreamHandler.handle(message);
62 } 29 }
63 30
64 } 31 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotUdpUpstreamProtocol.java
@@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.udp; @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.udp;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
7 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.IotProtocol;
8 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager; 10 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager;
9 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpUpstreamHandler; 11 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpUpstreamHandler;
10 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; 12 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
@@ -30,7 +32,9 @@ import java.util.List; @@ -30,7 +32,9 @@ import java.util.List;
30 * @author 芋道源码 32 * @author 芋道源码
31 */ 33 */
32 @Slf4j 34 @Slf4j
33 -public class IotUdpUpstreamProtocol { 35 +public class IotUdpUpstreamProtocol implements IotProtocol {
  36 +
  37 + private static final String ID = "udp";
34 38
35 private final IotGatewayProperties.UdpProperties udpProperties; 39 private final IotGatewayProperties.UdpProperties udpProperties;
36 40
@@ -55,6 +59,8 @@ public class IotUdpUpstreamProtocol { @@ -55,6 +59,8 @@ public class IotUdpUpstreamProtocol {
55 59
56 private IotUdpUpstreamHandler upstreamHandler; 60 private IotUdpUpstreamHandler upstreamHandler;
57 61
  62 + private volatile boolean running = false;
  63 +
58 public IotUdpUpstreamProtocol(IotGatewayProperties.UdpProperties udpProperties, 64 public IotUdpUpstreamProtocol(IotGatewayProperties.UdpProperties udpProperties,
59 IotDeviceService deviceService, 65 IotDeviceService deviceService,
60 IotDeviceMessageService messageService, 66 IotDeviceMessageService messageService,
@@ -68,6 +74,17 @@ public class IotUdpUpstreamProtocol { @@ -68,6 +74,17 @@ public class IotUdpUpstreamProtocol {
68 this.serverId = IotDeviceMessageUtils.generateServerId(udpProperties.getPort()); 74 this.serverId = IotDeviceMessageUtils.generateServerId(udpProperties.getPort());
69 } 75 }
70 76
  77 + @Override
  78 + public String getId() {
  79 + return ID;
  80 + }
  81 +
  82 + @Override
  83 + public IotProtocolTypeEnum getType() {
  84 + return IotProtocolTypeEnum.UDP;
  85 + }
  86 +
  87 + @Override
71 @PostConstruct 88 @PostConstruct
72 public void start() { 89 public void start() {
73 // 1. 初始化上行消息处理器 90 // 1. 初始化上行消息处理器
@@ -90,6 +107,7 @@ public class IotUdpUpstreamProtocol { @@ -90,6 +107,7 @@ public class IotUdpUpstreamProtocol {
90 } 107 }
91 // 设置数据包处理器 108 // 设置数据包处理器
92 udpSocket.handler(packet -> upstreamHandler.handle(packet, udpSocket)); 109 udpSocket.handler(packet -> upstreamHandler.handle(packet, udpSocket));
  110 + running = true;
93 log.info("[start][IoT 网关 UDP 协议启动成功,端口:{},接收缓冲区:{} 字节,发送缓冲区:{} 字节]", 111 log.info("[start][IoT 网关 UDP 协议启动成功,端口:{},接收缓冲区:{} 字节,发送缓冲区:{} 字节]",
94 udpProperties.getPort(), udpProperties.getReceiveBufferSize(), 112 udpProperties.getPort(), udpProperties.getReceiveBufferSize(),
95 udpProperties.getSendBufferSize()); 113 udpProperties.getSendBufferSize());
@@ -99,6 +117,7 @@ public class IotUdpUpstreamProtocol { @@ -99,6 +117,7 @@ public class IotUdpUpstreamProtocol {
99 }); 117 });
100 } 118 }
101 119
  120 + @Override
102 @PreDestroy 121 @PreDestroy
103 public void stop() { 122 public void stop() {
104 // 1. 取消会话清理定时器 123 // 1. 取消会话清理定时器
@@ -112,6 +131,7 @@ public class IotUdpUpstreamProtocol { @@ -112,6 +131,7 @@ public class IotUdpUpstreamProtocol {
112 if (udpSocket != null) { 131 if (udpSocket != null) {
113 try { 132 try {
114 udpSocket.close().result(); 133 udpSocket.close().result();
  134 + running = false;
115 log.info("[stop][IoT 网关 UDP 协议已停止]"); 135 log.info("[stop][IoT 网关 UDP 协议已停止]");
116 } catch (Exception e) { 136 } catch (Exception e) {
117 log.error("[stop][IoT 网关 UDP 协议停止失败]", e); 137 log.error("[stop][IoT 网关 UDP 协议停止失败]", e);
@@ -119,6 +139,11 @@ public class IotUdpUpstreamProtocol { @@ -119,6 +139,11 @@ public class IotUdpUpstreamProtocol {
119 } 139 }
120 } 140 }
121 141
  142 + @Override
  143 + public boolean isRunning() {
  144 + return running;
  145 + }
  146 +
122 /** 147 /**
123 * 启动会话清理定时器 148 * 启动会话清理定时器
124 */ 149 */
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotWebSocketDownstreamSubscriber.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.websocket; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.websocket;
2 2
3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;  
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
7 -import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager; 5 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber;
8 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketDownstreamHandler; 6 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketDownstreamHandler;
9 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
10 -import jakarta.annotation.PostConstruct;  
11 -import lombok.RequiredArgsConstructor;  
12 import lombok.extern.slf4j.Slf4j; 7 import lombok.extern.slf4j.Slf4j;
13 8
14 /** 9 /**
@@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j; @@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j;
17 * @author 芋道源码 12 * @author 芋道源码
18 */ 13 */
19 @Slf4j 14 @Slf4j
20 -@RequiredArgsConstructor  
21 -public class IotWebSocketDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { 15 +public class IotWebSocketDownstreamSubscriber extends IotProtocolDownstreamSubscriber {
22 16
23 - private final IotWebSocketUpstreamProtocol protocol; 17 + private final IotWebSocketDownstreamHandler downstreamHandler;
24 18
25 - private final IotDeviceMessageService messageService;  
26 -  
27 - private final IotWebSocketConnectionManager connectionManager;  
28 -  
29 - private final IotMessageBus messageBus;  
30 -  
31 - private IotWebSocketDownstreamHandler downstreamHandler;  
32 -  
33 - @PostConstruct  
34 - public void init() {  
35 - // 初始化下游处理器  
36 - this.downstreamHandler = new IotWebSocketDownstreamHandler(messageService, connectionManager);  
37 - // 注册下游订阅者  
38 - messageBus.register(this);  
39 - log.info("[init][WebSocket 下游订阅者初始化完成,服务器 ID: {},Topic: {}]",  
40 - protocol.getServerId(), getTopic());  
41 - }  
42 -  
43 - @Override  
44 - public String getTopic() {  
45 - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());  
46 - }  
47 -  
48 - @Override  
49 - public String getGroup() {  
50 - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group  
51 - return getTopic(); 19 + public IotWebSocketDownstreamSubscriber(IotWebSocketUpstreamProtocol protocol,
  20 + IotWebSocketDownstreamHandler downstreamHandler,
  21 + IotMessageBus messageBus) {
  22 + super(protocol, messageBus);
  23 + this.downstreamHandler = downstreamHandler;
52 } 24 }
53 25
54 @Override 26 @Override
55 - public void onMessage(IotDeviceMessage message) {  
56 - try {  
57 - downstreamHandler.handle(message);  
58 - } catch (Exception e) {  
59 - log.error("[onMessage][处理下行消息失败,设备 ID: {},方法: {},消息 ID: {}]",  
60 - message.getDeviceId(), message.getMethod(), message.getId(), e);  
61 - } 27 + protected void handleMessage(IotDeviceMessage message) {
  28 + downstreamHandler.handle(message);
62 } 29 }
63 30
64 } 31 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotWebSocketUpstreamProtocol.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.websocket; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.websocket;
2 2
3 import cn.hutool.core.util.ObjUtil; 3 import cn.hutool.core.util.ObjUtil;
  4 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
4 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 5 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
5 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 6 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  7 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
6 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager;
7 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketUpstreamHandler; 9 import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketUpstreamHandler;
8 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; 10 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
@@ -22,7 +24,9 @@ import lombok.extern.slf4j.Slf4j; @@ -22,7 +24,9 @@ import lombok.extern.slf4j.Slf4j;
22 * @author 芋道源码 24 * @author 芋道源码
23 */ 25 */
24 @Slf4j 26 @Slf4j
25 -public class IotWebSocketUpstreamProtocol { 27 +public class IotWebSocketUpstreamProtocol implements IotProtocol {
  28 +
  29 + private static final String ID = "websocket";
26 30
27 private final IotGatewayProperties.WebSocketProperties wsProperties; 31 private final IotGatewayProperties.WebSocketProperties wsProperties;
28 32
@@ -39,6 +43,8 @@ public class IotWebSocketUpstreamProtocol { @@ -39,6 +43,8 @@ public class IotWebSocketUpstreamProtocol {
39 43
40 private HttpServer httpServer; 44 private HttpServer httpServer;
41 45
  46 + private volatile boolean running = false;
  47 +
42 public IotWebSocketUpstreamProtocol(IotGatewayProperties.WebSocketProperties wsProperties, 48 public IotWebSocketUpstreamProtocol(IotGatewayProperties.WebSocketProperties wsProperties,
43 IotDeviceService deviceService, 49 IotDeviceService deviceService,
44 IotDeviceMessageService messageService, 50 IotDeviceMessageService messageService,
@@ -52,6 +58,17 @@ public class IotWebSocketUpstreamProtocol { @@ -52,6 +58,17 @@ public class IotWebSocketUpstreamProtocol {
52 this.serverId = IotDeviceMessageUtils.generateServerId(wsProperties.getPort()); 58 this.serverId = IotDeviceMessageUtils.generateServerId(wsProperties.getPort());
53 } 59 }
54 60
  61 + @Override
  62 + public String getId() {
  63 + return ID;
  64 + }
  65 +
  66 + @Override
  67 + public IotProtocolTypeEnum getType() {
  68 + return IotProtocolTypeEnum.WEBSOCKET;
  69 + }
  70 +
  71 + @Override
55 @PostConstruct 72 @PostConstruct
56 @SuppressWarnings("deprecation") 73 @SuppressWarnings("deprecation")
57 public void start() { 74 public void start() {
@@ -88,6 +105,7 @@ public class IotWebSocketUpstreamProtocol { @@ -88,6 +105,7 @@ public class IotWebSocketUpstreamProtocol {
88 // 3. 启动服务器 105 // 3. 启动服务器
89 try { 106 try {
90 httpServer.listen().result(); 107 httpServer.listen().result();
  108 + running = true;
91 log.info("[start][IoT 网关 WebSocket 协议启动成功,端口:{},路径:{}]", wsProperties.getPort(), wsProperties.getPath()); 109 log.info("[start][IoT 网关 WebSocket 协议启动成功,端口:{},路径:{}]", wsProperties.getPort(), wsProperties.getPath());
92 } catch (Exception e) { 110 } catch (Exception e) {
93 log.error("[start][IoT 网关 WebSocket 协议启动失败]", e); 111 log.error("[start][IoT 网关 WebSocket 协议启动失败]", e);
@@ -95,11 +113,13 @@ public class IotWebSocketUpstreamProtocol { @@ -95,11 +113,13 @@ public class IotWebSocketUpstreamProtocol {
95 } 113 }
96 } 114 }
97 115
  116 + @Override
98 @PreDestroy 117 @PreDestroy
99 public void stop() { 118 public void stop() {
100 if (httpServer != null) { 119 if (httpServer != null) {
101 try { 120 try {
102 httpServer.close().result(); 121 httpServer.close().result();
  122 + running = false;
103 log.info("[stop][IoT 网关 WebSocket 协议已停止]"); 123 log.info("[stop][IoT 网关 WebSocket 协议已停止]");
104 } catch (Exception e) { 124 } catch (Exception e) {
105 log.error("[stop][IoT 网关 WebSocket 协议停止失败]", e); 125 log.error("[stop][IoT 网关 WebSocket 协议停止失败]", e);
@@ -107,4 +127,9 @@ public class IotWebSocketUpstreamProtocol { @@ -107,4 +127,9 @@ public class IotWebSocketUpstreamProtocol {
107 } 127 }
108 } 128 }
109 129
  130 + @Override
  131 + public boolean isRunning() {
  132 + return running;
  133 + }
  134 +
110 } 135 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/IotMessageSerializer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.serialize;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
  4 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  5 +
  6 +/**
  7 + * IoT 设备消息序列化器接口
  8 + *
  9 + * 用于序列化和反序列化设备消息
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +public interface IotMessageSerializer {
  14 +
  15 + /**
  16 + * 序列化消息
  17 + *
  18 + * @param message 消息
  19 + * @return 编码后的消息内容
  20 + */
  21 + byte[] serialize(IotDeviceMessage message);
  22 +
  23 + /**
  24 + * 反序列化消息
  25 + *
  26 + * @param bytes 消息内容
  27 + * @return 解码后的消息内容
  28 + */
  29 + IotDeviceMessage deserialize(byte[] bytes);
  30 +
  31 + /**
  32 + * 获取序列化类型
  33 + *
  34 + * @return 序列化类型枚举
  35 + */
  36 + IotSerializeTypeEnum getType();
  37 +
  38 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/IotMessageSerializerManager.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.serialize;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
  4 +import cn.iocoder.yudao.module.iot.gateway.serialize.binary.IotBinarySerializer;
  5 +import cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +
  8 +import java.util.EnumMap;
  9 +import java.util.Map;
  10 +
  11 +/**
  12 + * IoT 序列化器管理器
  13 + *
  14 + * 负责根据枚举创建和管理序列化器实例
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Slf4j
  19 +public class IotMessageSerializerManager {
  20 +
  21 + private final Map<IotSerializeTypeEnum, IotMessageSerializer> serializerMap = new EnumMap<>(IotSerializeTypeEnum.class);
  22 +
  23 + public IotMessageSerializerManager() {
  24 + // 遍历枚举,创建对应的序列化器
  25 + for (IotSerializeTypeEnum type : IotSerializeTypeEnum.values()) {
  26 + IotMessageSerializer serializer = createSerializer(type);
  27 + serializerMap.put(type, serializer);
  28 + log.info("[IotSerializerManager][序列化器 {} 创建成功]", type);
  29 + }
  30 + }
  31 +
  32 + /**
  33 + * 根据类型创建序列化器
  34 + *
  35 + * @param type 序列化类型
  36 + * @return 序列化器实例
  37 + */
  38 + @SuppressWarnings("EnhancedSwitchMigration")
  39 + private IotMessageSerializer createSerializer(IotSerializeTypeEnum type) {
  40 + switch (type) {
  41 + case JSON:
  42 + return new IotJsonSerializer();
  43 + case BINARY:
  44 + return new IotBinarySerializer();
  45 + default:
  46 + throw new IllegalArgumentException("未知的序列化类型:" + type);
  47 + }
  48 + }
  49 +
  50 + /**
  51 + * 获取序列化器
  52 + *
  53 + * @param type 序列化类型
  54 + * @return 序列化器实例
  55 + */
  56 + public IotMessageSerializer get(IotSerializeTypeEnum type) {
  57 + return serializerMap.get(type);
  58 + }
  59 +
  60 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/binary/IotBinarySerializer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.serialize.binary;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  6 +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
  7 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  8 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  9 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
  10 +import io.vertx.core.buffer.Buffer;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +
  13 +import java.nio.charset.StandardCharsets;
  14 +
  15 +/**
  16 + * 二进制格式的消息序列化器
  17 + *
  18 + * 二进制协议格式(所有数值使用大端序):
  19 + *
  20 + * <pre>
  21 + * +--------+--------+--------+---------------------------+--------+--------+
  22 + * | 魔术字 | 版本号 | 消息类型| 消息长度(4 字节) |
  23 + * +--------+--------+--------+---------------------------+--------+--------+
  24 + * | 消息 ID 长度(2 字节) | 消息 ID (变长字符串) |
  25 + * +--------+--------+--------+--------+--------+--------+--------+--------+
  26 + * | 方法名长度(2 字节) | 方法名(变长字符串) |
  27 + * +--------+--------+--------+--------+--------+--------+--------+--------+
  28 + * | 消息体数据(变长) |
  29 + * +--------+--------+--------+--------+--------+--------+--------+--------+
  30 + * </pre>
  31 + *
  32 + * 消息体格式:
  33 + * - 请求消息:params 数据(JSON)
  34 + * - 响应消息:code (4字节) + msg 长度(2字节) + msg 字符串 + data 数据(JSON)
  35 + *
  36 + * @author 芋道源码
  37 + */
  38 +@Slf4j
  39 +public class IotBinarySerializer implements IotMessageSerializer {
  40 +
  41 + /**
  42 + * 协议魔术字,用于协议识别
  43 + */
  44 + private static final byte MAGIC_NUMBER = (byte) 0x7E;
  45 +
  46 + /**
  47 + * 协议版本号
  48 + */
  49 + private static final byte PROTOCOL_VERSION = (byte) 0x01;
  50 +
  51 + /**
  52 + * 请求消息类型
  53 + */
  54 + private static final byte REQUEST = (byte) 0x01;
  55 +
  56 + /**
  57 + * 响应消息类型
  58 + */
  59 + private static final byte RESPONSE = (byte) 0x02;
  60 +
  61 + /**
  62 + * 协议头部固定长度(魔术字 + 版本号 + 消息类型 + 消息长度)
  63 + */
  64 + private static final int HEADER_FIXED_LENGTH = 7;
  65 +
  66 + /**
  67 + * 最小消息长度(头部 + 消息ID长度 + 方法名长度)
  68 + */
  69 + private static final int MIN_MESSAGE_LENGTH = HEADER_FIXED_LENGTH + 4;
  70 +
  71 + @Override
  72 + public IotSerializeTypeEnum getType() {
  73 + return IotSerializeTypeEnum.BINARY;
  74 + }
  75 +
  76 + @Override
  77 + public byte[] serialize(IotDeviceMessage message) {
  78 + Assert.notNull(message, "消息不能为空");
  79 + Assert.notBlank(message.getMethod(), "消息方法不能为空");
  80 + try {
  81 + // 1. 确定消息类型
  82 + byte messageType = determineMessageType(message);
  83 + // 2. 构建消息体
  84 + byte[] bodyData = buildMessageBody(message, messageType);
  85 + // 3. 构建完整消息
  86 + return buildCompleteMessage(message, messageType, bodyData);
  87 + } catch (Exception e) {
  88 + log.error("[encode][二进制消息编码失败,消息: {}]", message, e);
  89 + throw new RuntimeException("二进制消息编码失败: " + e.getMessage(), e);
  90 + }
  91 + }
  92 +
  93 + @Override
  94 + public IotDeviceMessage deserialize(byte[] bytes) {
  95 + Assert.notNull(bytes, "待解码数据不能为空");
  96 + Assert.isTrue(bytes.length >= MIN_MESSAGE_LENGTH, "数据包长度不足");
  97 + try {
  98 + Buffer buffer = Buffer.buffer(bytes);
  99 + int index = 0;
  100 +
  101 + // 1. 验证魔术字
  102 + byte magic = buffer.getByte(index++);
  103 + Assert.isTrue(magic == MAGIC_NUMBER, "无效的协议魔术字: " + magic);
  104 +
  105 + // 2. 验证版本号
  106 + byte version = buffer.getByte(index++);
  107 + Assert.isTrue(version == PROTOCOL_VERSION, "不支持的协议版本: " + version);
  108 +
  109 + // 3. 读取消息类型
  110 + byte messageType = buffer.getByte(index++);
  111 + Assert.isTrue(messageType == REQUEST || messageType == RESPONSE, "无效的消息类型: " + messageType);
  112 +
  113 + // 4. 读取消息长度
  114 + int messageLength = buffer.getInt(index);
  115 + index += 4;
  116 + Assert.isTrue(messageLength == buffer.length(),
  117 + "消息长度不匹配,期望: " + messageLength + ", 实际: " + buffer.length());
  118 +
  119 + // 5. 读取消息 ID
  120 + short messageIdLength = buffer.getShort(index);
  121 + index += 2;
  122 + String messageId = buffer.getString(index, index + messageIdLength, StandardCharsets.UTF_8.name());
  123 + index += messageIdLength;
  124 +
  125 + // 6. 读取方法名
  126 + short methodLength = buffer.getShort(index);
  127 + index += 2;
  128 + String method = buffer.getString(index, index + methodLength, StandardCharsets.UTF_8.name());
  129 + index += methodLength;
  130 +
  131 + // 7. 解析消息体
  132 + return parseMessageBody(buffer, index, messageType, messageId, method);
  133 + } catch (Exception e) {
  134 + log.error("[decode][二进制消息解码失败,数据长度: {}]", bytes.length, e);
  135 + throw new RuntimeException("二进制消息解码失败: " + e.getMessage(), e);
  136 + }
  137 + }
  138 +
  139 + /**
  140 + * 快速检测是否为二进制格式
  141 + *
  142 + * @param data 数据
  143 + * @return 是否为二进制格式
  144 + */
  145 + public static boolean isBinaryFormat(byte[] data) {
  146 + return data != null && data.length >= 1 && data[0] == MAGIC_NUMBER;
  147 + }
  148 +
  149 + private byte determineMessageType(IotDeviceMessage message) {
  150 + if (message.getCode() != null) {
  151 + return RESPONSE;
  152 + }
  153 + return REQUEST;
  154 + }
  155 +
  156 + private byte[] buildMessageBody(IotDeviceMessage message, byte messageType) {
  157 + Buffer bodyBuffer = Buffer.buffer();
  158 + if (messageType == RESPONSE) {
  159 + // code
  160 + bodyBuffer.appendInt(message.getCode() != null ? message.getCode() : 0);
  161 + // msg
  162 + String msg = message.getMsg() != null ? message.getMsg() : "";
  163 + byte[] msgBytes = StrUtil.utf8Bytes(msg);
  164 + bodyBuffer.appendShort((short) msgBytes.length);
  165 + bodyBuffer.appendBytes(msgBytes);
  166 + // data
  167 + if (message.getData() != null) {
  168 + bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getData()));
  169 + }
  170 + } else {
  171 + // 请求消息只处理 params 参数
  172 + if (message.getParams() != null) {
  173 + bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getParams()));
  174 + }
  175 + }
  176 + return bodyBuffer.getBytes();
  177 + }
  178 +
  179 + private byte[] buildCompleteMessage(IotDeviceMessage message, byte messageType, byte[] bodyData) {
  180 + Buffer buffer = Buffer.buffer();
  181 + // 1. 写入协议头部
  182 + buffer.appendByte(MAGIC_NUMBER);
  183 + buffer.appendByte(PROTOCOL_VERSION);
  184 + buffer.appendByte(messageType);
  185 + // 2. 预留消息长度位置
  186 + int lengthPosition = buffer.length();
  187 + buffer.appendInt(0);
  188 + // 3. 写入消息 ID
  189 + String messageId = StrUtil.isNotBlank(message.getRequestId()) ? message.getRequestId()
  190 + : IotDeviceMessageUtils.generateMessageId();
  191 + byte[] messageIdBytes = StrUtil.utf8Bytes(messageId);
  192 + buffer.appendShort((short) messageIdBytes.length);
  193 + buffer.appendBytes(messageIdBytes);
  194 + // 4. 写入方法名
  195 + byte[] methodBytes = StrUtil.utf8Bytes(message.getMethod());
  196 + buffer.appendShort((short) methodBytes.length);
  197 + buffer.appendBytes(methodBytes);
  198 + // 5. 写入消息体
  199 + buffer.appendBytes(bodyData);
  200 + // 6. 更新消息长度
  201 + buffer.setInt(lengthPosition, buffer.length());
  202 + return buffer.getBytes();
  203 + }
  204 +
  205 + private IotDeviceMessage parseMessageBody(Buffer buffer, int startIndex, byte messageType,
  206 + String messageId, String method) {
  207 + if (startIndex >= buffer.length()) {
  208 + return IotDeviceMessage.of(messageId, method, null, null, null, null);
  209 + }
  210 +
  211 + if (messageType == RESPONSE) {
  212 + return parseResponseMessage(buffer, startIndex, messageId, method);
  213 + } else {
  214 + Object payload = parseJsonData(buffer, startIndex, buffer.length());
  215 + return IotDeviceMessage.of(messageId, method, payload, null, null, null);
  216 + }
  217 + }
  218 +
  219 + private IotDeviceMessage parseResponseMessage(Buffer buffer, int startIndex, String messageId, String method) {
  220 + int index = startIndex;
  221 +
  222 + // 1. 读取响应码
  223 + Integer code = buffer.getInt(index);
  224 + index += 4;
  225 +
  226 + // 2. 读取响应消息
  227 + short msgLength = buffer.getShort(index);
  228 + index += 2;
  229 + String msg = msgLength > 0 ? buffer.getString(index, index + msgLength, StandardCharsets.UTF_8.name()) : null;
  230 + index += msgLength;
  231 +
  232 + // 3. 读取响应数据
  233 + Object data = null;
  234 + if (index < buffer.length()) {
  235 + data = parseJsonData(buffer, index, buffer.length());
  236 + }
  237 +
  238 + return IotDeviceMessage.of(messageId, method, null, data, code, msg);
  239 + }
  240 +
  241 + private Object parseJsonData(Buffer buffer, int startIndex, int endIndex) {
  242 + if (startIndex >= endIndex) {
  243 + return null;
  244 + }
  245 + try {
  246 + String jsonStr = buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name());
  247 + return JsonUtils.parseObject(jsonStr, Object.class);
  248 + } catch (Exception e) {
  249 + log.warn("[parseJsonData][JSON 解析失败,返回原始字符串]", e);
  250 + return buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name());
  251 + }
  252 + }
  253 +
  254 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/json/IotJsonSerializer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.serialize.json;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
  6 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  7 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
  8 +
  9 +/**
  10 + * JSON 格式的消息序列化器
  11 + *
  12 + * 直接使用 JsonUtils 序列化/反序列化 {@link IotDeviceMessage},不包装额外字段
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public class IotJsonSerializer implements IotMessageSerializer {
  17 +
  18 + @Override
  19 + public IotSerializeTypeEnum getType() {
  20 + return IotSerializeTypeEnum.JSON;
  21 + }
  22 +
  23 + @Override
  24 + public byte[] serialize(IotDeviceMessage message) {
  25 + Assert.notNull(message, "消息不能为空");
  26 + return JsonUtils.toJsonByte(message);
  27 + }
  28 +
  29 + @Override
  30 + public IotDeviceMessage deserialize(byte[] bytes) {
  31 + Assert.notNull(bytes, "待解码数据不能为空");
  32 + IotDeviceMessage message = JsonUtils.parseObject(bytes, IotDeviceMessage.class);
  33 + Assert.notNull(message, "消息解码失败");
  34 + return message;
  35 + }
  36 +
  37 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml
@@ -42,14 +42,20 @@ yudao: @@ -42,14 +42,20 @@ yudao:
42 secret: yudaoIotGatewayTokenSecret123456789 # Token 密钥,至少32位 42 secret: yudaoIotGatewayTokenSecret123456789 # Token 密钥,至少32位
43 expiration: 7d 43 expiration: 7d
44 44
45 - # 协议配置  
46 - protocol: 45 + # 协议实例列表(新版配置方式)
  46 + protocols:
47 # ==================================== 47 # ====================================
48 # 针对引入的 HTTP 组件的配置 48 # 针对引入的 HTTP 组件的配置
49 # ==================================== 49 # ====================================
50 - http: 50 + - id: http-json
  51 + type: http
  52 + port: 8092
51 enabled: true 53 enabled: true
52 - server-port: 8092 54 + http:
  55 + ssl-enabled: false
  56 +
  57 + # 协议配置(旧版,保持兼容)
  58 + protocol:
53 # ==================================== 59 # ====================================
54 # 针对引入的 EMQX 组件的配置 60 # 针对引入的 EMQX 组件的配置
55 # ==================================== 61 # ====================================