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 42 /**
43 43 * 运行状态
44 44 */
  45 + @Getter
45 46 private volatile boolean running = false;
46 47  
47 48 /**
... ... @@ -61,6 +62,8 @@ public class IotHttpProtocol implements IotProtocol {
61 62 public IotHttpProtocol(ProtocolInstanceProperties properties, IotMessageBus messageBus) {
62 63 this.properties = properties;
63 64 this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());
  65 +
  66 + // 初始化下行消息订阅者
64 67 this.downstreamSubscriber = new IotHttpDownstreamSubscriber(this, messageBus);
65 68 }
66 69  
... ... @@ -169,9 +172,4 @@ public class IotHttpProtocol implements IotProtocol {
169 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 1 package cn.iocoder.yudao.module.iot.gateway.protocol.tcp;
2 2  
3   -import cn.hutool.extra.spring.SpringUtil;
4 3 import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
5 4 import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
6 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 13 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;
15 14 import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
16 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 16 import io.vertx.core.Vertx;
20 17 import io.vertx.core.net.NetServer;
21 18 import io.vertx.core.net.NetServerOptions;
... ... @@ -46,6 +43,7 @@ public class IotTcpProtocol implements IotProtocol {
46 43 /**
47 44 * 运行状态
48 45 */
  46 + @Getter
49 47 private volatile boolean running = false;
50 48  
51 49 /**
... ... @@ -72,6 +70,11 @@ public class IotTcpProtocol implements IotProtocol {
72 70 */
73 71 private final IotTcpFrameCodec frameCodec;
74 72  
  73 + /**
  74 + * TCP 连接管理器(每个 Protocol 实例独立)
  75 + */
  76 + private final IotTcpConnectionManager connectionManager;
  77 +
75 78 public IotTcpProtocol(ProtocolInstanceProperties properties, IotMessageBus messageBus,
76 79 IotMessageSerializerManager serializerManager) {
77 80 this.properties = properties;
... ... @@ -83,14 +86,15 @@ public class IotTcpProtocol implements IotProtocol {
83 86 serializeType = IotSerializeTypeEnum.JSON; // 默认 JSON
84 87 }
85 88 this.serializer = serializerManager.get(serializeType);
86   -
87 89 // 初始化帧编解码器
88 90 IotTcpConfig tcpConfig = properties.getTcp();
89 91 IotTcpConfig.CodecConfig codecConfig = tcpConfig != null ? tcpConfig.getCodec() : null;
90 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 98 IotTcpDownstreamHandler downstreamHandler = new IotTcpDownstreamHandler(connectionManager, frameCodec, serializer);
95 99 this.downstreamSubscriber = new IotTcpDownstreamSubscriber(this, downstreamHandler, messageBus);
96 100 }
... ... @@ -131,12 +135,8 @@ public class IotTcpProtocol implements IotProtocol {
131 135  
132 136 // 1.3 创建服务器并设置连接处理器
133 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 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 140 handler.handle(socket);
141 141 });
142 142  
... ... @@ -197,9 +197,4 @@ public class IotTcpProtocol implements IotProtocol {
197 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 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 3 import cn.hutool.core.util.IdUtil;
6 4 import cn.hutool.extra.spring.SpringUtil;
7 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
... ... @@ -15,7 +13,6 @@ import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
15 13 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
16 14 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;
17 15 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
18   -import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
19 16 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.codec.IotTcpFrameCodec;
20 17 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;
21 18 import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
... ... @@ -26,8 +23,10 @@ import io.vertx.core.buffer.Buffer;
26 23 import io.vertx.core.net.NetSocket;
27 24 import io.vertx.core.parsetools.RecordParser;
28 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 32 * TCP 上行消息处理器
... ... @@ -39,14 +38,6 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
39 38  
40 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 41 private final String serverId;
51 42  
52 43 /**
... ... @@ -57,20 +48,30 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
57 48 * 消息序列化器(处理业务消息序列化/反序列化)
58 49 */
59 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 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 70 this.codec = codec;
69 71 this.serializer = serializer;
70 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 75 this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);
75 76 }
76 77  
... ... @@ -79,42 +80,30 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> {
79 80 String clientId = IdUtil.simpleUUID();
80 81 log.debug("[handle][设备连接,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress());
81 82  
82   - // 设置异常和关闭处理器
  83 + // 1. 设置异常和关闭处理器
83 84 socket.exceptionHandler(ex -> {
84 85 log.warn("[handle][连接异常,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress());
85   - cleanupConnection(socket);
  86 + socket.close();
86 87 });
87 88 socket.closeHandler(v -> {
88 89 log.debug("[handle][连接关闭,客户端 ID: {},地址: {}]", clientId, socket.remoteAddress());
89 90 cleanupConnection(socket);
90 91 });
91 92  
92   - // 设置消息处理器(带拆包支持)
  93 + // 2.1 设置消息处理器
93 94 Handler<Buffer> messageHandler = buffer -> {
94   - // TODO @AI:需要跟 AI 讨论。哪些情况关闭;哪些情况,发送异常消息;
95 95 try {
96 96 processMessage(clientId, buffer, socket);
97 97 } catch (Exception e) {
98   - // TODO @AI:这里能合并到 exceptionHandler 么?还是怎么搞好点;
99   - log.error("[handle][消息解码失败,断开连接,客户端 ID: {},地址: {},错误: {}]",
  98 + log.error("[handle][消息处理失败,客户端 ID: {},地址: {},错误: {}]",
100 99 clientId, socket.remoteAddress(), e.getMessage());
101   - cleanupConnection(socket);
102 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 112 * @param clientId 客户端 ID
124 113 * @param buffer 消息
125 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 139 * @param socket 网络连接
178 140 */
179 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 177 * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
234 178 */
235 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 206 * @param socket 网络连接
269 207 */
270 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 234 .setProductKey(device.getProductKey())
303 235 .setDeviceName(device.getDeviceName())
304 236 .setClientId(clientId);
305   - // 注册连接
306 237 connectionManager.registerConnection(socket, device.getId(), connectionInfo);
307 238 }
308 239  
... ... @@ -312,13 +243,9 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
312 243 * @param device 设备信息
313 244 */
314 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 254 * @param socket 网络连接
328 255 */
329 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 274 * @param socket 网络连接
411 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 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 4 import io.vertx.core.net.NetSocket;
5 5 import lombok.Data;
6 6 import lombok.extern.slf4j.Slf4j;
7   -import org.springframework.stereotype.Component;
8 7  
9 8 import java.util.Map;
10 9 import java.util.concurrent.ConcurrentHashMap;
... ... @@ -20,7 +19,6 @@ import java.util.concurrent.ConcurrentHashMap;
20 19 * @author 芋道源码
21 20 */
22 21 @Slf4j
23   -@Component
24 22 public class IotTcpConnectionManager {
25 23  
26 24 /**
... ... @@ -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 77 public ConnectionInfo getConnectionInfo(NetSocket socket) {
... ...