Commit 046b2d51e96eb24138ce7b7dcb2f99e8b94c511f

Authored by YunaiV
1 parent 1d8ab8ff

feat(iot):【协议改造】tcp 初步改造(70%)

yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpProtocol.java
@@ -42,6 +42,7 @@ public class IotHttpProtocol implements IotProtocol { @@ -42,6 +42,7 @@ public class IotHttpProtocol implements IotProtocol {
42 /** 42 /**
43 * 运行状态 43 * 运行状态
44 */ 44 */
  45 + @Getter
45 private volatile boolean running = false; 46 private volatile boolean running = false;
46 47
47 /** 48 /**
@@ -61,6 +62,8 @@ public class IotHttpProtocol implements IotProtocol { @@ -61,6 +62,8 @@ public class IotHttpProtocol implements IotProtocol {
61 public IotHttpProtocol(ProtocolInstanceProperties properties, IotMessageBus messageBus) { 62 public IotHttpProtocol(ProtocolInstanceProperties properties, IotMessageBus messageBus) {
62 this.properties = properties; 63 this.properties = properties;
63 this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort()); 64 this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());
  65 +
  66 + // 初始化下行消息订阅者
64 this.downstreamSubscriber = new IotHttpDownstreamSubscriber(this, messageBus); 67 this.downstreamSubscriber = new IotHttpDownstreamSubscriber(this, messageBus);
65 } 68 }
66 69
@@ -169,9 +172,4 @@ public class IotHttpProtocol implements IotProtocol { @@ -169,9 +172,4 @@ public class IotHttpProtocol implements IotProtocol {
169 log.info("[stop][IoT HTTP 协议 {} 已停止]", getId()); 172 log.info("[stop][IoT HTTP 协议 {} 已停止]", getId());
170 } 173 }
171 174
172 - @Override  
173 - public boolean isRunning() {  
174 - return running;  
175 - }  
176 -  
177 } 175 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpProtocol.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.hutool.extra.spring.SpringUtil;  
4 import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; 3 import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
5 import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum; 4 import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
6 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 5 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
@@ -14,8 +13,6 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.upstream.IotTcpU @@ -14,8 +13,6 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.upstream.IotTcpU
14 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; 13 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;
15 import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer; 14 import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
16 import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager; 15 import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;
17 -import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;  
18 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;  
19 import io.vertx.core.Vertx; 16 import io.vertx.core.Vertx;
20 import io.vertx.core.net.NetServer; 17 import io.vertx.core.net.NetServer;
21 import io.vertx.core.net.NetServerOptions; 18 import io.vertx.core.net.NetServerOptions;
@@ -46,6 +43,7 @@ public class IotTcpProtocol implements IotProtocol { @@ -46,6 +43,7 @@ public class IotTcpProtocol implements IotProtocol {
46 /** 43 /**
47 * 运行状态 44 * 运行状态
48 */ 45 */
  46 + @Getter
49 private volatile boolean running = false; 47 private volatile boolean running = false;
50 48
51 /** 49 /**
@@ -72,6 +70,11 @@ public class IotTcpProtocol implements IotProtocol { @@ -72,6 +70,11 @@ public class IotTcpProtocol implements IotProtocol {
72 */ 70 */
73 private final IotTcpFrameCodec frameCodec; 71 private final IotTcpFrameCodec frameCodec;
74 72
  73 + /**
  74 + * TCP 连接管理器(每个 Protocol 实例独立)
  75 + */
  76 + private final IotTcpConnectionManager connectionManager;
  77 +
75 public IotTcpProtocol(ProtocolInstanceProperties properties, IotMessageBus messageBus, 78 public IotTcpProtocol(ProtocolInstanceProperties properties, IotMessageBus messageBus,
76 IotMessageSerializerManager serializerManager) { 79 IotMessageSerializerManager serializerManager) {
77 this.properties = properties; 80 this.properties = properties;
@@ -83,14 +86,15 @@ public class IotTcpProtocol implements IotProtocol { @@ -83,14 +86,15 @@ public class IotTcpProtocol implements IotProtocol {
83 serializeType = IotSerializeTypeEnum.JSON; // 默认 JSON 86 serializeType = IotSerializeTypeEnum.JSON; // 默认 JSON
84 } 87 }
85 this.serializer = serializerManager.get(serializeType); 88 this.serializer = serializerManager.get(serializeType);
86 -  
87 // 初始化帧编解码器 89 // 初始化帧编解码器
88 IotTcpConfig tcpConfig = properties.getTcp(); 90 IotTcpConfig tcpConfig = properties.getTcp();
89 IotTcpConfig.CodecConfig codecConfig = tcpConfig != null ? tcpConfig.getCodec() : null; 91 IotTcpConfig.CodecConfig codecConfig = tcpConfig != null ? tcpConfig.getCodec() : null;
90 this.frameCodec = IotTcpFrameCodec.create(codecConfig); 92 this.frameCodec = IotTcpFrameCodec.create(codecConfig);
91 93
  94 + // 初始化连接管理器
  95 + this.connectionManager = new IotTcpConnectionManager();
  96 +
92 // 初始化下行消息订阅者 97 // 初始化下行消息订阅者
93 - IotTcpConnectionManager connectionManager = SpringUtil.getBean(IotTcpConnectionManager.class);  
94 IotTcpDownstreamHandler downstreamHandler = new IotTcpDownstreamHandler(connectionManager, frameCodec, serializer); 98 IotTcpDownstreamHandler downstreamHandler = new IotTcpDownstreamHandler(connectionManager, frameCodec, serializer);
95 this.downstreamSubscriber = new IotTcpDownstreamSubscriber(this, downstreamHandler, messageBus); 99 this.downstreamSubscriber = new IotTcpDownstreamSubscriber(this, downstreamHandler, messageBus);
96 } 100 }
@@ -131,12 +135,8 @@ public class IotTcpProtocol implements IotProtocol { @@ -131,12 +135,8 @@ public class IotTcpProtocol implements IotProtocol {
131 135
132 // 1.3 创建服务器并设置连接处理器 136 // 1.3 创建服务器并设置连接处理器
133 tcpServer = vertx.createNetServer(options); 137 tcpServer = vertx.createNetServer(options);
134 - IotDeviceService deviceService = SpringUtil.getBean(IotDeviceService.class);  
135 - IotDeviceMessageService messageService = SpringUtil.getBean(IotDeviceMessageService.class);  
136 - IotTcpConnectionManager connectionManager = SpringUtil.getBean(IotTcpConnectionManager.class);  
137 tcpServer.connectHandler(socket -> { 138 tcpServer.connectHandler(socket -> {
138 - IotTcpUpstreamHandler handler = new IotTcpUpstreamHandler(this, messageService, deviceService,  
139 - connectionManager, frameCodec, serializer); 139 + IotTcpUpstreamHandler handler = new IotTcpUpstreamHandler(serverId, frameCodec, serializer, connectionManager);
140 handler.handle(socket); 140 handler.handle(socket);
141 }); 141 });
142 142
@@ -197,9 +197,4 @@ public class IotTcpProtocol implements IotProtocol { @@ -197,9 +197,4 @@ public class IotTcpProtocol implements IotProtocol {
197 log.info("[stop][IoT TCP 协议 {} 已停止]", getId()); 197 log.info("[stop][IoT TCP 协议 {} 已停止]", getId());
198 } 198 }
199 199
200 - @Override  
201 - public boolean isRunning() {  
202 - return running;  
203 - }  
204 -  
205 } 200 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/handler/upstream/IotTcpUpstreamHandler.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.upstream; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.handler.upstream;
2 2
3 -import cn.hutool.core.map.MapUtil;  
4 -import cn.hutool.core.util.BooleanUtil;  
5 import cn.hutool.core.util.IdUtil; 3 import cn.hutool.core.util.IdUtil;
6 import cn.hutool.extra.spring.SpringUtil; 4 import cn.hutool.extra.spring.SpringUtil;
7 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
@@ -15,7 +13,6 @@ import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; @@ -15,7 +13,6 @@ import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
15 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; 13 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
16 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; 14 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;
17 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 15 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
18 -import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;  
19 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec; 16 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;
20 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; 17 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;
21 import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer; 18 import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
@@ -26,8 +23,10 @@ import io.vertx.core.buffer.Buffer; @@ -26,8 +23,10 @@ import io.vertx.core.buffer.Buffer;
26 import io.vertx.core.net.NetSocket; 23 import io.vertx.core.net.NetSocket;
27 import io.vertx.core.parsetools.RecordParser; 24 import io.vertx.core.parsetools.RecordParser;
28 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
  26 +import org.springframework.util.Assert;
29 27
30 -import java.util.Map; 28 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.SUCCESS;
  29 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;
31 30
32 /** 31 /**
33 * TCP 上行消息处理器 32 * TCP 上行消息处理器
@@ -39,14 +38,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { @@ -39,14 +38,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
39 38
40 private static final String AUTH_METHOD = "auth"; 39 private static final String AUTH_METHOD = "auth";
41 40
42 - private final IotDeviceMessageService deviceMessageService;  
43 -  
44 - private final IotDeviceService deviceService;  
45 -  
46 - private final IotTcpConnectionManager connectionManager;  
47 -  
48 - private final IotDeviceCommonApi deviceApi;  
49 -  
50 private final String serverId; 41 private final String serverId;
51 42
52 /** 43 /**
@@ -57,20 +48,30 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { @@ -57,20 +48,30 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
57 * 消息序列化器(处理业务消息序列化/反序列化) 48 * 消息序列化器(处理业务消息序列化/反序列化)
58 */ 49 */
59 private final IotMessageSerializer serializer; 50 private final IotMessageSerializer serializer;
  51 + /**
  52 + * TCP 连接管理器(每个 Protocol 实例独立)
  53 + */
  54 + private final IotTcpConnectionManager connectionManager;
  55 +
  56 + // ===================== Spring 依赖(构造时注入) =====================
60 57
61 - public IotTcpUpstreamHandler(IotProtocol protocol,  
62 - IotDeviceMessageService deviceMessageService,  
63 - IotDeviceService deviceService,  
64 - IotTcpConnectionManager connectionManager, 58 + private final IotDeviceMessageService deviceMessageService;
  59 + private final IotDeviceService deviceService;
  60 + private final IotDeviceCommonApi deviceApi;
  61 +
  62 + public IotTcpUpstreamHandler(String serverId,
65 IotTcpFrameCodec codec, 63 IotTcpFrameCodec codec,
66 - IotMessageSerializer serializer) {  
67 - this.serverId = protocol.getServerId(); 64 + IotMessageSerializer serializer,
  65 + IotTcpConnectionManager connectionManager) {
  66 + Assert.notNull(codec, "TCP FrameCodec 必须配置");
  67 + Assert.notNull(serializer, "消息序列化器必须配置");
  68 + Assert.notNull(connectionManager, "连接管理器不能为空");
  69 + this.serverId = serverId;
68 this.codec = codec; 70 this.codec = codec;
69 this.serializer = serializer; 71 this.serializer = serializer;
70 this.connectionManager = connectionManager; 72 this.connectionManager = connectionManager;
71 - // TODO @AI:都通过 springutil 获取下;  
72 - this.deviceMessageService = deviceMessageService;  
73 - this.deviceService = deviceService; 73 + this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
  74 + this.deviceService = SpringUtil.getBean(IotDeviceService.class);
74 this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class); 75 this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);
75 } 76 }
76 77
@@ -79,42 +80,30 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { @@ -79,42 +80,30 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
79 String clientId = IdUtil.simpleUUID(); 80 String clientId = IdUtil.simpleUUID();
80 log.debug("[handle][设备连接,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress()); 81 log.debug("[handle][设备连接,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress());
81 82
82 - // 设置异常和关闭处理器 83 + // 1. 设置异常和关闭处理器
83 socket.exceptionHandler(ex -> { 84 socket.exceptionHandler(ex -> {
84 log.warn("[handle][连接异常,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress()); 85 log.warn("[handle][连接异常,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress());
85 - cleanupConnection(socket); 86 + socket.close();
86 }); 87 });
87 socket.closeHandler(v -> { 88 socket.closeHandler(v -> {
88 log.debug("[handle][连接关闭,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress()); 89 log.debug("[handle][连接关闭,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress());
89 cleanupConnection(socket); 90 cleanupConnection(socket);
90 }); 91 });
91 92
92 - // 设置消息处理器(带拆包支持) 93 + // 2.1 设置消息处理器
93 Handler<Buffer> messageHandler = buffer -> { 94 Handler<Buffer> messageHandler = buffer -> {
94 - // TODO @AI:需要跟 AI 讨论。哪些情况关闭;哪些情况,发送异常消息;  
95 try { 95 try {
96 processMessage(clientId, buffer, socket); 96 processMessage(clientId, buffer, socket);
97 } catch (Exception e) { 97 } catch (Exception e) {
98 - // TODO @AI:这里能合并到 exceptionHandler 么?还是怎么搞好点;  
99 - log.error("[handle][消息解码失败,断开连接,客户端 ID: {},地址: {},错误: {}]", 98 + log.error("[handle][消息处理失败,客户端 ID: {},地址: {},错误: {}]",
100 clientId, socket.remoteAddress(), e.getMessage()); 99 clientId, socket.remoteAddress(), e.getMessage());
101 - cleanupConnection(socket);  
102 socket.close(); 100 socket.close();
103 } 101 }
104 }; 102 };
105 -  
106 - // 根据是否配置了 FrameCodec 来决定是否使用拆包器  
107 - // TODO @AI:必须配置!  
108 - if (codec != null) {  
109 - // 使用拆包器处理粘包/拆包  
110 - RecordParser parser = codec.createDecodeParser(messageHandler);  
111 - socket.handler(parser);  
112 - log.debug("[handle][启用 {} 拆包器,客户端 ID: {}]", codec.getType(), clientId);  
113 - } else {  
114 - // 未配置拆包器,直接处理原始数据(可能存在粘包问题)  
115 - socket.handler(messageHandler);  
116 - log.debug("[handle][未配置拆包器,客户端 ID: {}]", clientId);  
117 - } 103 + // 2.2 使用拆包器处理粘包/拆包
  104 + RecordParser parser = codec.createDecodeParser(messageHandler);
  105 + socket.handler(parser);
  106 + log.debug("[handle][启用 {} 拆包器,客户端 ID: {}]", codec.getType(), clientId);
118 } 107 }
119 108
120 /** 109 /**
@@ -123,49 +112,22 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -123,49 +112,22 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
123 * @param clientId 客户端 ID 112 * @param clientId 客户端 ID
124 * @param buffer 消息 113 * @param buffer 消息
125 * @param socket 网络连接 114 * @param socket 网络连接
126 - * @throws Exception 消息解码失败时抛出异常  
127 */ 115 */
128 - private void processMessage(String clientId, Buffer buffer, NetSocket socket) throws Exception {  
129 - // 1. 基础检查  
130 - // TODO @AI:不太应该为空?!  
131 - if (buffer == null || buffer.length() == 0) {  
132 - return;  
133 - }  
134 -  
135 - // 2. 反序列化消息  
136 - IotDeviceMessage message;  
137 - try {  
138 - message = serializer.deserialize(buffer.getBytes());  
139 - if (message == null) {  
140 - throw new IllegalArgumentException("反序列化后消息为空");  
141 - }  
142 - } catch (Exception e) {  
143 - // TODO @AI:是不是不用 try catch?  
144 - throw new Exception("消息反序列化失败: " + e.getMessage(), e);  
145 - }  
146 -  
147 - // 3. 根据消息类型路由处理  
148 - try {  
149 - if (AUTH_METHOD.equals(message.getMethod())) {  
150 - // 认证请求  
151 - handleAuthenticationRequest(clientId, message, socket);  
152 - } else if (IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod().equals(message.getMethod())) {  
153 - // 设备动态注册请求  
154 - handleRegisterRequest(clientId, message, socket);  
155 - } else {  
156 - // 业务消息  
157 - handleBusinessRequest(clientId, message, socket);  
158 - }  
159 - } catch (Exception e) {  
160 - // TODO @AI:如果参数不正确,不断开连接;  
161 - log.error("[processMessage][处理消息失败,客户端 ID: {},消息方法: {}]", clientId, message.getMethod(), e);  
162 - // 发送错误响应,避免客户端一直等待  
163 - // TODO @AI:发送失败,是不是不用 try catch?  
164 - try {  
165 - sendErrorResponse(socket, message.getRequestId(), "消息处理失败");  
166 - } catch (Exception responseEx) {  
167 - log.error("[processMessage][发送错误响应失败,客户端 ID: {}]", clientId, responseEx);  
168 - } 116 + private void processMessage(String clientId, Buffer buffer, NetSocket socket) {
  117 + // 1. 反序列化消息
  118 + IotDeviceMessage message = serializer.deserialize(buffer.getBytes());
  119 + Assert.notNull(message, "反序列化后消息为空");
  120 +
  121 + // 2. 根据消息类型路由处理
  122 + if (AUTH_METHOD.equals(message.getMethod())) {
  123 + // 认证请求
  124 + handleAuthenticationRequest(clientId, message, socket);
  125 + } else if (IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod().equals(message.getMethod())) {
  126 + // 设备动态注册请求
  127 + handleRegisterRequest(clientId, message, socket);
  128 + } else {
  129 + // 业务消息
  130 + handleBusinessRequest(clientId, message, socket);
169 } 131 }
170 } 132 }
171 133
@@ -177,51 +139,33 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -177,51 +139,33 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
177 * @param socket 网络连接 139 * @param socket 网络连接
178 */ 140 */
179 private void handleAuthenticationRequest(String clientId, IotDeviceMessage message, NetSocket socket) { 141 private void handleAuthenticationRequest(String clientId, IotDeviceMessage message, NetSocket socket) {
180 - try {  
181 - // 1.1 解析认证参数  
182 - // TODO @AI:直接 JsonUtils.convertObject(params, IotDeviceAuthReqDTO.class);然后,校验参数,不正确抛出 invalid exception;和 http 那一样;  
183 - IotDeviceAuthReqDTO authParams = parseAuthParams(message.getParams());  
184 - if (authParams == null) {  
185 - log.warn("[handleAuthenticationRequest][认证参数解析失败,客户端 ID: {}]", clientId);  
186 - sendErrorResponse(socket, message.getRequestId(), "认证参数不完整");  
187 - return;  
188 - }  
189 - // 1.2 执行认证  
190 - if (!validateDeviceAuth(authParams)) {  
191 - log.warn("[handleAuthenticationRequest][认证失败,客户端 ID: {},username: {}]",  
192 - clientId, authParams.getUsername());  
193 - sendErrorResponse(socket, message.getRequestId(), "认证失败");  
194 - return;  
195 - }  
196 -  
197 - // 2.1 解析设备信息  
198 - IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());  
199 - // TODO @AI:这里就断言 deviceInfo 不为空了?!  
200 - if (deviceInfo == null) {  
201 - sendErrorResponse(socket, message.getRequestId(), "解析设备信息失败");  
202 - return;  
203 - }  
204 - // 2.2 获取设备信息  
205 - IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(), deviceInfo.getDeviceName());  
206 - // TODO @AI:这里就断言 device 不为空了?!  
207 - if (device == null) {  
208 - sendErrorResponse(socket, message.getRequestId(), "设备不存在");  
209 - return;  
210 - }  
211 -  
212 - // 3.1 注册连接  
213 - registerConnection(socket, device, clientId);  
214 - // 3.2 发送上线消息  
215 - sendOnlineMessage(device);  
216 - // 3.3 发送成功响应  
217 - sendSuccessResponse(socket, message.getRequestId(), "认证成功");  
218 - log.info("[handleAuthenticationRequest][认证成功,设备 ID: {},设备名: {}]",  
219 - device.getId(), device.getDeviceName());  
220 - } catch (Exception e) {  
221 - // TODO @AI:最大化去掉 try catch;(这个方法里的)  
222 - log.error("[handleAuthenticationRequest][认证处理异常,客户端 ID: {}]", clientId, e);  
223 - sendErrorResponse(socket, message.getRequestId(), "认证处理异常"); 142 + // 1. 解析认证参数
  143 + IotDeviceAuthReqDTO authParams = JsonUtils.convertObject(message.getParams(), IotDeviceAuthReqDTO.class);
  144 + Assert.notNull(authParams, "认证参数不能为空");
  145 + Assert.hasText(authParams.getUsername(), "username 不能为空");
  146 + Assert.hasText(authParams.getPassword(), "password 不能为空");
  147 +
  148 + // 2.1 执行认证
  149 + CommonResult<Boolean> authResult = deviceApi.authDevice(authParams);
  150 + if (authResult.isError()) {
  151 + log.warn("[handleAuthenticationRequest][认证失败,客户端 ID: {},username: {}]", clientId, authParams.getUsername());
  152 + sendErrorResponse(socket, message.getRequestId(), AUTH_METHOD, authResult.getCode(), authResult.getMsg());
  153 + return;
224 } 154 }
  155 + // 2.2 解析设备信息
  156 + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());
  157 + Assert.notNull(deviceInfo, "解析设备信息失败");
  158 + // 2.3 获取设备信息
  159 + IotDeviceRespDTO device = deviceService.getDeviceFromCache(deviceInfo.getProductKey(), deviceInfo.getDeviceName());
  160 + Assert.notNull(device, "设备不存在");
  161 +
  162 + // 3.1 注册连接
  163 + registerConnection(socket, device, clientId);
  164 + // 3.2 发送上线消息
  165 + sendOnlineMessage(device);
  166 + // 3.3 发送成功响应
  167 + sendSuccessResponse(socket, message.getRequestId(), AUTH_METHOD, "认证成功");
  168 + log.info("[handleAuthenticationRequest][认证成功,设备 ID: {},设备名: {}]", device.getId(), device.getDeviceName());
225 } 169 }
226 170
227 /** 171 /**
@@ -233,31 +177,25 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -233,31 +177,25 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
233 * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a> 177 * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
234 */ 178 */
235 private void handleRegisterRequest(String clientId, IotDeviceMessage message, NetSocket socket) { 179 private void handleRegisterRequest(String clientId, IotDeviceMessage message, NetSocket socket) {
236 - try {  
237 - // 1. 解析注册参数  
238 - IotDeviceRegisterReqDTO params = parseRegisterParams(message.getParams());  
239 - if (params == null) {  
240 - log.warn("[handleRegisterRequest][注册参数解析失败,客户端 ID: {}]", clientId);  
241 - sendErrorResponse(socket, message.getRequestId(), "注册参数不完整");  
242 - return;  
243 - }  
244 -  
245 - // 2. 调用动态注册  
246 - CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params);  
247 - if (result.isError()) {  
248 - log.warn("[handleRegisterRequest][注册失败,客户端 ID: {},错误: {}]", clientId, result.getMsg());  
249 - sendErrorResponse(socket, message.getRequestId(), result.getMsg());  
250 - return;  
251 - }  
252 -  
253 - // 3. 发送成功响应(包含 deviceSecret)  
254 - sendRegisterSuccessResponse(socket, message.getRequestId(), result.getData());  
255 - log.info("[handleRegisterRequest][注册成功,客户端 ID: {},设备名: {}]",  
256 - clientId, params.getDeviceName());  
257 - } catch (Exception e) {  
258 - log.error("[handleRegisterRequest][注册处理异常,客户端 ID: {}]", clientId, e);  
259 - sendErrorResponse(socket, message.getRequestId(), "注册处理异常"); 180 + // 1. 解析注册参数
  181 + IotDeviceRegisterReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceRegisterReqDTO.class);
  182 + Assert.notNull(params, "注册参数不能为空");
  183 + Assert.hasText(params.getProductKey(), "productKey 不能为空");
  184 + Assert.hasText(params.getDeviceName(), "deviceName 不能为空");
  185 +
  186 + // 2. 调用动态注册
  187 + CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params);
  188 + if (result.isError()) {
  189 + log.warn("[handleRegisterRequest][注册失败,客户端 ID: {},错误: {}]", clientId, result.getMsg());
  190 + sendErrorResponse(socket, message.getRequestId(), IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(),
  191 + result.getCode(), result.getMsg());
  192 + return;
260 } 193 }
  194 +
  195 + // 3. 发送成功响应
  196 + sendSuccessResponse(socket, message.getRequestId(),
  197 + IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), result.getData());
  198 + log.info("[handleRegisterRequest][注册成功,客户端 ID: {},设备名: {}]", clientId, params.getDeviceName());
261 } 199 }
262 200
263 /** 201 /**
@@ -268,25 +206,19 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -268,25 +206,19 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
268 * @param socket 网络连接 206 * @param socket 网络连接
269 */ 207 */
270 private void handleBusinessRequest(String clientId, IotDeviceMessage message, NetSocket socket) { 208 private void handleBusinessRequest(String clientId, IotDeviceMessage message, NetSocket socket) {
271 - try {  
272 - // 1. 检查认证状态  
273 - if (connectionManager.isNotAuthenticated(socket)) {  
274 - log.warn("[handleBusinessRequest][设备未认证,客户端 ID: {}]", clientId);  
275 - sendErrorResponse(socket, message.getRequestId(), "请先进行认证");  
276 - return;  
277 - }  
278 -  
279 - // 2. 获取认证信息并处理业务消息  
280 - IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);  
281 -  
282 - // 3. 发送消息到消息总线  
283 - deviceMessageService.sendDeviceMessage(message, connectionInfo.getProductKey(),  
284 - connectionInfo.getDeviceName(), serverId);  
285 - log.info("[handleBusinessRequest][发送消息到消息总线,客户端 ID: {},消息: {}",  
286 - clientId, message.toString());  
287 - } catch (Exception e) {  
288 - log.error("[handleBusinessRequest][业务请求处理异常,客户端 ID: {}]", clientId, e); 209 + // 1. 获取认证信息并处理业务消息
  210 + IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);
  211 + if (connectionInfo == null) {
  212 + log.error("[handleBusinessRequest][无法获取连接信息,客户端 ID: {}]", clientId);
  213 + sendErrorResponse(socket, message.getRequestId(), message.getMethod(),
  214 + UNAUTHORIZED.getCode(), "设备未认证,无法处理业务消息");
  215 + return;
289 } 216 }
  217 +
  218 + // 2. 发送消息到消息总线
  219 + deviceMessageService.sendDeviceMessage(message, connectionInfo.getProductKey(),
  220 + connectionInfo.getDeviceName(), serverId);
  221 + log.info("[handleBusinessRequest][发送消息到消息总线,客户端 ID: {},消息: {}", clientId, message);
290 } 222 }
291 223
292 /** 224 /**
@@ -302,7 +234,6 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -302,7 +234,6 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
302 .setProductKey(device.getProductKey()) 234 .setProductKey(device.getProductKey())
303 .setDeviceName(device.getDeviceName()) 235 .setDeviceName(device.getDeviceName())
304 .setClientId(clientId); 236 .setClientId(clientId);
305 - // 注册连接  
306 connectionManager.registerConnection(socket, device.getId(), connectionInfo); 237 connectionManager.registerConnection(socket, device.getId(), connectionInfo);
307 } 238 }
308 239
@@ -312,13 +243,9 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -312,13 +243,9 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
312 * @param device 设备信息 243 * @param device 设备信息
313 */ 244 */
314 private void sendOnlineMessage(IotDeviceRespDTO device) { 245 private void sendOnlineMessage(IotDeviceRespDTO device) {
315 - try {  
316 - IotDeviceMessage onlineMessage = IotDeviceMessage.buildStateUpdateOnline();  
317 - deviceMessageService.sendDeviceMessage(onlineMessage, device.getProductKey(),  
318 - device.getDeviceName(), serverId);  
319 - } catch (Exception e) {  
320 - log.error("[sendOnlineMessage][发送上线消息失败,设备: {}]", device.getDeviceName(), e);  
321 - } 246 + IotDeviceMessage onlineMessage = IotDeviceMessage.buildStateUpdateOnline();
  247 + deviceMessageService.sendDeviceMessage(onlineMessage, device.getProductKey(),
  248 + device.getDeviceName(), serverId);
322 } 249 }
323 250
324 /** 251 /**
@@ -327,182 +254,57 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -327,182 +254,57 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
327 * @param socket 网络连接 254 * @param socket 网络连接
328 */ 255 */
329 private void cleanupConnection(NetSocket socket) { 256 private void cleanupConnection(NetSocket socket) {
330 - try {  
331 - // 1. 发送离线消息(如果已认证)  
332 - IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);  
333 - if (connectionInfo != null) {  
334 - IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();  
335 - deviceMessageService.sendDeviceMessage(offlineMessage, connectionInfo.getProductKey(),  
336 - connectionInfo.getDeviceName(), serverId);  
337 - }  
338 -  
339 - // 2. 注销连接  
340 - connectionManager.unregisterConnection(socket);  
341 - } catch (Exception e) {  
342 - log.error("[cleanupConnection][清理连接失败]", e);  
343 - }  
344 - }  
345 -  
346 - /**  
347 - * 发送响应消息  
348 - *  
349 - * @param socket 网络连接  
350 - * @param success 是否成功  
351 - * @param message 消息  
352 - * @param requestId 请求 ID  
353 - */  
354 - private void sendResponse(NetSocket socket, boolean success, String message, String requestId) {  
355 - try {  
356 - // TODO @AI:是不是不用  
357 - Object responseData = MapUtil.builder()  
358 - .put("success", success)  
359 - .put("message", message)  
360 - .build();  
361 -  
362 - int code = success ? 0 : 401;  
363 - IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, AUTH_METHOD, responseData,  
364 - code, message);  
365 -  
366 - // 序列化 + 帧编码  
367 - byte[] serializedData = serializer.serialize(responseMessage);  
368 - Buffer frameData = codec.encode(serializedData);  
369 - socket.write(frameData);  
370 -  
371 - } catch (Exception e) {  
372 - log.error("[sendResponse][发送响应失败,requestId: {}]", requestId, e); 257 + // 1. 发送离线消息
  258 + IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);
  259 + if (connectionInfo != null) {
  260 + IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();
  261 + deviceMessageService.sendDeviceMessage(offlineMessage, connectionInfo.getProductKey(),
  262 + connectionInfo.getDeviceName(), serverId);
373 } 263 }
374 - }  
375 264
376 - // TODO @AI:合并到 handleAuthenticationRequest 里;  
377 - /**  
378 - * 验证设备认证信息  
379 - *  
380 - * @param authParams 认证参数  
381 - * @return 是否认证成功  
382 - */  
383 - private boolean validateDeviceAuth(IotDeviceAuthReqDTO authParams) {  
384 - try {  
385 - CommonResult<Boolean> result = deviceApi.authDevice(new IotDeviceAuthReqDTO()  
386 - .setClientId(authParams.getClientId()).setUsername(authParams.getUsername())  
387 - .setPassword(authParams.getPassword()));  
388 - result.checkError();  
389 - return BooleanUtil.isTrue(result.getData());  
390 - } catch (Exception e) {  
391 - log.error("[validateDeviceAuth][设备认证异常,username: {}]", authParams.getUsername(), e);  
392 - return false;  
393 - } 265 + // 2. 注销连接
  266 + connectionManager.unregisterConnection(socket);
394 } 267 }
395 268
396 - /**  
397 - * 发送错误响应  
398 - *  
399 - * @param socket 网络连接  
400 - * @param requestId 请求 ID  
401 - * @param errorMessage 错误消息  
402 - */  
403 - private void sendErrorResponse(NetSocket socket, String requestId, String errorMessage) {  
404 - sendResponse(socket, false, errorMessage, requestId);  
405 - } 269 + // ===================== 发送响应消息 =====================
406 270
407 /** 271 /**
408 * 发送成功响应 272 * 发送成功响应
409 * 273 *
410 * @param socket 网络连接 274 * @param socket 网络连接
411 * @param requestId 请求 ID 275 * @param requestId 请求 ID
412 - * @param message 消息  
413 - */  
414 - @SuppressWarnings("SameParameterValue")  
415 - private void sendSuccessResponse(NetSocket socket, String requestId, String message) {  
416 - sendResponse(socket, true, message, requestId);  
417 - }  
418 -  
419 - /**  
420 - * 解析认证参数  
421 - *  
422 - * @param params 参数对象(通常为 Map 类型)  
423 - * @return 认证参数 DTO,解析失败时返回 null 276 + * @param method 方法名
  277 + * @param data 响应数据
424 */ 278 */
425 - @SuppressWarnings({"unchecked", "DuplicatedCode"})  
426 - private IotDeviceAuthReqDTO parseAuthParams(Object params) {  
427 - if (params == null) {  
428 - return null;  
429 - }  
430 - try {  
431 - // 参数默认为 Map 类型,直接转换  
432 - if (params instanceof Map) {  
433 - Map<String, Object> paramMap = (Map<String, Object>) params;  
434 - return new IotDeviceAuthReqDTO()  
435 - .setClientId(MapUtil.getStr(paramMap, "clientId"))  
436 - .setUsername(MapUtil.getStr(paramMap, "username"))  
437 - .setPassword(MapUtil.getStr(paramMap, "password"));  
438 - }  
439 - // 如果已经是目标类型,直接返回  
440 - if (params instanceof IotDeviceAuthReqDTO) {  
441 - return (IotDeviceAuthReqDTO) params;  
442 - }  
443 -  
444 - // 其他情况尝试 JSON 转换  
445 - return JsonUtils.convertObject(params, IotDeviceAuthReqDTO.class);  
446 - } catch (Exception e) {  
447 - log.error("[parseAuthParams][解析认证参数({})失败]", params, e);  
448 - return null;  
449 - } 279 + private void sendSuccessResponse(NetSocket socket, String requestId, String method, Object data) {
  280 + IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, SUCCESS.getCode(), null);
  281 + writeResponse(socket, responseMessage);
450 } 282 }
451 283
452 /** 284 /**
453 - * 解析注册参数 285 + * 发送错误响应
454 * 286 *
455 - * @param params 参数对象(通常为 Map 类型)  
456 - * @return 注册参数 DTO,解析失败时返回 null 287 + * @param socket 网络连接
  288 + * @param requestId 请求 ID
  289 + * @param method 方法名
  290 + * @param code 错误码
  291 + * @param msg 错误消息
457 */ 292 */
458 - @SuppressWarnings({"unchecked", "DuplicatedCode"})  
459 - private IotDeviceRegisterReqDTO parseRegisterParams(Object params) {  
460 - if (params == null) {  
461 - return null;  
462 - }  
463 - try {  
464 - // 参数默认为 Map 类型,直接转换  
465 - if (params instanceof Map) {  
466 - Map<String, Object> paramMap = (Map<String, Object>) params;  
467 - return new IotDeviceRegisterReqDTO()  
468 - .setProductKey(MapUtil.getStr(paramMap, "productKey"))  
469 - .setDeviceName(MapUtil.getStr(paramMap, "deviceName"))  
470 - .setProductSecret(MapUtil.getStr(paramMap, "productSecret"));  
471 - }  
472 - // 如果已经是目标类型,直接返回  
473 - if (params instanceof IotDeviceRegisterReqDTO) {  
474 - return (IotDeviceRegisterReqDTO) params;  
475 - }  
476 -  
477 - // 其他情况尝试 JSON 转换  
478 - String jsonStr = JsonUtils.toJsonString(params);  
479 - return JsonUtils.parseObject(jsonStr, IotDeviceRegisterReqDTO.class);  
480 - } catch (Exception e) {  
481 - log.error("[parseRegisterParams][解析注册参数({})失败]", params, e);  
482 - return null;  
483 - } 293 + private void sendErrorResponse(NetSocket socket, String requestId, String method, Integer code, String msg) {
  294 + IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, null, code, msg);
  295 + writeResponse(socket, responseMessage);
484 } 296 }
485 297
486 /** 298 /**
487 - * 发送注册成功响应(包含 deviceSecret) 299 + * 写入响应到 Socket
488 * 300 *
489 - * @param socket 网络连接  
490 - * @param requestId 请求 ID  
491 - * @param registerResp 注册响应 301 + * @param socket 网络连接
  302 + * @param responseMessage 响应消息
492 */ 303 */
493 - private void sendRegisterSuccessResponse(NetSocket socket, String requestId,  
494 - IotDeviceRegisterRespDTO registerResp) {  
495 - try {  
496 - // 1. 构建响应消息(参考 HTTP 返回格式,直接返回 IotDeviceRegisterRespDTO)  
497 - IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId,  
498 - IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), registerResp, 0, null);  
499 - // 2. 序列化 + 帧编码  
500 - byte[] serializedData = serializer.serialize(responseMessage);  
501 - Buffer frameData = codec.encode(serializedData);  
502 - socket.write(frameData);  
503 - } catch (Exception e) {  
504 - log.error("[sendRegisterSuccessResponse][发送注册成功响应失败,requestId: {}]", requestId, e);  
505 - } 304 + private void writeResponse(NetSocket socket, IotDeviceMessage responseMessage) {
  305 + byte[] serializedData = serializer.serialize(responseMessage);
  306 + Buffer frameData = codec.encode(serializedData);
  307 + socket.write(frameData);
506 } 308 }
507 309
508 } 310 }
509 \ No newline at end of file 311 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/manager/IotTcpConnectionManager.java
@@ -4,7 +4,6 @@ import io.vertx.core.buffer.Buffer; @@ -4,7 +4,6 @@ import io.vertx.core.buffer.Buffer;
4 import io.vertx.core.net.NetSocket; 4 import io.vertx.core.net.NetSocket;
5 import lombok.Data; 5 import lombok.Data;
6 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
7 -import org.springframework.stereotype.Component;  
8 7
9 import java.util.Map; 8 import java.util.Map;
10 import java.util.concurrent.ConcurrentHashMap; 9 import java.util.concurrent.ConcurrentHashMap;
@@ -20,7 +19,6 @@ import java.util.concurrent.ConcurrentHashMap; @@ -20,7 +19,6 @@ import java.util.concurrent.ConcurrentHashMap;
20 * @author 芋道源码 19 * @author 芋道源码
21 */ 20 */
22 @Slf4j 21 @Slf4j
23 -@Component  
24 public class IotTcpConnectionManager { 22 public class IotTcpConnectionManager {
25 23
26 /** 24 /**
@@ -74,22 +72,6 @@ public class IotTcpConnectionManager { @@ -74,22 +72,6 @@ public class IotTcpConnectionManager {
74 } 72 }
75 73
76 /** 74 /**
77 - * 检查连接是否已认证  
78 - */  
79 - public boolean isAuthenticated(NetSocket socket) {  
80 - ConnectionInfo info = connectionMap.get(socket);  
81 - return info != null;  
82 - }  
83 -  
84 - // TODO @AI:是不是可以去掉;因为现在只有认证成功的,才会注册连接;  
85 - /**  
86 - * 检查连接是否未认证  
87 - */  
88 - public boolean isNotAuthenticated(NetSocket socket) {  
89 - return !isAuthenticated(socket);  
90 - }  
91 -  
92 - /**  
93 * 获取连接信息 75 * 获取连接信息
94 */ 76 */
95 public ConnectionInfo getConnectionInfo(NetSocket socket) { 77 public ConnectionInfo getConnectionInfo(NetSocket socket) {