Commit d01a6e215894a4d27130528131ae9e646ce3a4c3
1 parent
867ec8c0
fix(iot): 修复多协议处理器的空值校验和错误码问题
1. HTTP: 增加请求体空值保护,避免 NPE 导致 500 2. HTTP: 修复 Vertx 资源泄漏,改为 Spring 管理生命周期 3. UDP/MQTT/WS/TCP: 增加动态注册参数必填字段校验 4. EMQX: 事件接口解析失败时返回空响应体,符合 Webhook 规范 5. CoAP: method 不匹配返回 4.00 而非 5.00
Showing
12 changed files
with
117 additions
and
105 deletions
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
| ... | ... | @@ -44,9 +44,15 @@ public class IotGatewayConfiguration { |
| 44 | 44 | @Slf4j |
| 45 | 45 | public static class HttpProtocolConfiguration { |
| 46 | 46 | |
| 47 | + @Bean(name = "httpVertx", destroyMethod = "close") | |
| 48 | + public Vertx httpVertx() { | |
| 49 | + return Vertx.vertx(); | |
| 50 | + } | |
| 51 | + | |
| 47 | 52 | @Bean |
| 48 | - public IotHttpUpstreamProtocol iotHttpUpstreamProtocol(IotGatewayProperties gatewayProperties) { | |
| 49 | - return new IotHttpUpstreamProtocol(gatewayProperties.getProtocol().getHttp()); | |
| 53 | + public IotHttpUpstreamProtocol iotHttpUpstreamProtocol(IotGatewayProperties gatewayProperties, | |
| 54 | + @Qualifier("httpVertx") Vertx httpVertx) { | |
| 55 | + return new IotHttpUpstreamProtocol(gatewayProperties.getProtocol().getHttp(), httpVertx); | |
| 50 | 56 | } |
| 51 | 57 | |
| 52 | 58 | @Bean | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/router/IotCoapUpstreamHandler.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.coap.router; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.lang.Assert; | |
| 5 | 4 | import cn.hutool.core.map.MapUtil; |
| 6 | 5 | import cn.hutool.core.text.StrPool; |
| 7 | 6 | import cn.hutool.core.util.ArrayUtil; |
| ... | ... | @@ -93,7 +92,10 @@ public class IotCoapUpstreamHandler { |
| 93 | 92 | |
| 94 | 93 | // 2.2 解码消息 |
| 95 | 94 | IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(payload, productKey, deviceName); |
| 96 | - Assert.equals(method, message.getMethod(), "method 不匹配"); | |
| 95 | + if (ObjUtil.notEqual(method, message.getMethod())) { | |
| 96 | + IotCoapUtils.respondError(exchange, CoAP.ResponseCode.BAD_REQUEST, "method 不匹配"); | |
| 97 | + return; | |
| 98 | + } | |
| 97 | 99 | // 2.3 发送消息到消息总线 |
| 98 | 100 | deviceMessageService.sendDeviceMessage(message, productKey, deviceName, protocol.getServerId()); |
| 99 | 101 | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxAuthEventHandler.java
| ... | ... | @@ -104,7 +104,7 @@ public class IotEmqxAuthEventHandler { |
| 104 | 104 | JsonObject body = null; |
| 105 | 105 | try { |
| 106 | 106 | // 1. 解析请求体 |
| 107 | - body = parseRequestBody(context); | |
| 107 | + body = parseEventRequestBody(context); | |
| 108 | 108 | if (body == null) { |
| 109 | 109 | return; |
| 110 | 110 | } |
| ... | ... | @@ -153,7 +153,9 @@ public class IotEmqxAuthEventHandler { |
| 153 | 153 | } |
| 154 | 154 | |
| 155 | 155 | /** |
| 156 | - * 解析请求体 | |
| 156 | + * 解析认证接口请求体 | |
| 157 | + * <p> | |
| 158 | + * 认证接口解析失败时返回 JSON 格式响应(包含 result 字段) | |
| 157 | 159 | * |
| 158 | 160 | * @param context 路由上下文 |
| 159 | 161 | * @return 请求体JSON对象,解析失败时返回null |
| ... | ... | @@ -175,6 +177,30 @@ public class IotEmqxAuthEventHandler { |
| 175 | 177 | } |
| 176 | 178 | |
| 177 | 179 | /** |
| 180 | + * 解析事件接口请求体 | |
| 181 | + * <p> | |
| 182 | + * 事件接口解析失败时仅返回 200 状态码,无响应体(符合 EMQX Webhook 规范) | |
| 183 | + * | |
| 184 | + * @param context 路由上下文 | |
| 185 | + * @return 请求体JSON对象,解析失败时返回null | |
| 186 | + */ | |
| 187 | + private JsonObject parseEventRequestBody(RoutingContext context) { | |
| 188 | + try { | |
| 189 | + JsonObject body = context.body().asJsonObject(); | |
| 190 | + if (body == null) { | |
| 191 | + log.info("[parseEventRequestBody][请求体为空]"); | |
| 192 | + context.response().setStatusCode(SUCCESS_STATUS_CODE).end(); | |
| 193 | + return null; | |
| 194 | + } | |
| 195 | + return body; | |
| 196 | + } catch (Exception e) { | |
| 197 | + log.error("[parseEventRequestBody][body({}) 解析请求体失败]", context.body().asString(), e); | |
| 198 | + context.response().setStatusCode(SUCCESS_STATUS_CODE).end(); | |
| 199 | + return null; | |
| 200 | + } | |
| 201 | + } | |
| 202 | + | |
| 203 | + /** | |
| 178 | 204 | * 执行设备认证 |
| 179 | 205 | * |
| 180 | 206 | * @param clientId 客户端ID | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpUpstreamProtocol.java
| ... | ... | @@ -6,7 +6,6 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAuthHandl |
| 6 | 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpRegisterHandler; |
| 7 | 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpRegisterSubHandler; |
| 8 | 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpUpstreamHandler; |
| 9 | -import io.vertx.core.AbstractVerticle; | |
| 10 | 9 | import io.vertx.core.Vertx; |
| 11 | 10 | import io.vertx.core.http.HttpServer; |
| 12 | 11 | import io.vertx.core.http.HttpServerOptions; |
| ... | ... | @@ -24,25 +23,26 @@ import lombok.extern.slf4j.Slf4j; |
| 24 | 23 | * @author 芋道源码 |
| 25 | 24 | */ |
| 26 | 25 | @Slf4j |
| 27 | -public class IotHttpUpstreamProtocol extends AbstractVerticle { | |
| 26 | +public class IotHttpUpstreamProtocol { | |
| 28 | 27 | |
| 29 | 28 | private final IotGatewayProperties.HttpProperties httpProperties; |
| 30 | 29 | |
| 30 | + private final Vertx vertx; | |
| 31 | + | |
| 31 | 32 | private HttpServer httpServer; |
| 32 | 33 | |
| 33 | 34 | @Getter |
| 34 | 35 | private final String serverId; |
| 35 | 36 | |
| 36 | - public IotHttpUpstreamProtocol(IotGatewayProperties.HttpProperties httpProperties) { | |
| 37 | + public IotHttpUpstreamProtocol(IotGatewayProperties.HttpProperties httpProperties, Vertx vertx) { | |
| 37 | 38 | this.httpProperties = httpProperties; |
| 39 | + this.vertx = vertx; | |
| 38 | 40 | this.serverId = IotDeviceMessageUtils.generateServerId(httpProperties.getServerPort()); |
| 39 | 41 | } |
| 40 | 42 | |
| 41 | - @Override | |
| 42 | 43 | @PostConstruct |
| 43 | 44 | public void start() { |
| 44 | 45 | // 创建路由 |
| 45 | - Vertx vertx = Vertx.vertx(); | |
| 46 | 46 | Router router = Router.router(vertx); |
| 47 | 47 | router.route().handler(BodyHandler.create()); |
| 48 | 48 | |
| ... | ... | @@ -76,7 +76,6 @@ public class IotHttpUpstreamProtocol extends AbstractVerticle { |
| 76 | 76 | } |
| 77 | 77 | } |
| 78 | 78 | |
| 79 | - @Override | |
| 80 | 79 | @PreDestroy |
| 81 | 80 | public void stop() { |
| 82 | 81 | if (httpServer != null) { | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java
| ... | ... | @@ -51,6 +51,9 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { |
| 51 | 51 | public CommonResult<Object> handle0(RoutingContext context) { |
| 52 | 52 | // 1. 解析参数 |
| 53 | 53 | JsonObject body = context.body().asJsonObject(); |
| 54 | + if (body == null) { | |
| 55 | + throw invalidParamException("请求体不能为空"); | |
| 56 | + } | |
| 54 | 57 | String clientId = body.getString("clientId"); |
| 55 | 58 | if (StrUtil.isEmpty(clientId)) { |
| 56 | 59 | throw invalidParamException("clientId 不能为空"); | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterHandler.java
| ... | ... | @@ -34,6 +34,9 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler { |
| 34 | 34 | public CommonResult<Object> handle0(RoutingContext context) { |
| 35 | 35 | // 1. 解析参数 |
| 36 | 36 | JsonObject body = context.body().asJsonObject(); |
| 37 | + if (body == null) { | |
| 38 | + throw invalidParamException("请求体不能为空"); | |
| 39 | + } | |
| 37 | 40 | String productKey = body.getString("productKey"); |
| 38 | 41 | if (StrUtil.isEmpty(productKey)) { |
| 39 | 42 | throw invalidParamException("productKey 不能为空"); | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterSubHandler.java
| ... | ... | @@ -11,6 +11,7 @@ import io.vertx.ext.web.RoutingContext; |
| 11 | 11 | |
| 12 | 12 | import java.util.List; |
| 13 | 13 | |
| 14 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; | |
| 14 | 15 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 15 | 16 | |
| 16 | 17 | /** |
| ... | ... | @@ -44,6 +45,12 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler { |
| 44 | 45 | |
| 45 | 46 | // 2. 解析子设备列表 |
| 46 | 47 | JsonObject body = context.body().asJsonObject(); |
| 48 | + if (body == null) { | |
| 49 | + throw invalidParamException("请求体不能为空"); | |
| 50 | + } | |
| 51 | + if (body.getJsonArray("params") == null) { | |
| 52 | + throw invalidParamException("params 不能为空"); | |
| 53 | + } | |
| 47 | 54 | List<cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO> subDevices = JsonUtils.parseArray( |
| 48 | 55 | body.getJsonArray("params").toString(), cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO.class); |
| 49 | 56 | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java
| ... | ... | @@ -12,6 +12,8 @@ import io.vertx.ext.web.RoutingContext; |
| 12 | 12 | import lombok.RequiredArgsConstructor; |
| 13 | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | 14 | |
| 15 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; | |
| 16 | + | |
| 15 | 17 | /** |
| 16 | 18 | * IoT 网关 HTTP 协议的【上行】处理器 |
| 17 | 19 | * |
| ... | ... | @@ -40,6 +42,9 @@ public class IotHttpUpstreamHandler extends IotHttpAbstractHandler { |
| 40 | 42 | String method = context.pathParam("*").replaceAll(StrPool.SLASH, StrPool.DOT); |
| 41 | 43 | |
| 42 | 44 | // 2.1 解析消息 |
| 45 | + if (context.body().buffer() == null) { | |
| 46 | + throw invalidParamException("请求体不能为空"); | |
| 47 | + } | |
| 43 | 48 | byte[] bytes = context.body().buffer().getBytes(); |
| 44 | 49 | IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(bytes, |
| 45 | 50 | productKey, deviceName); | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java
| ... | ... | @@ -330,15 +330,15 @@ public class IotMqttUpstreamHandler { |
| 330 | 330 | String clientId = endpoint.clientIdentifier(); |
| 331 | 331 | try { |
| 332 | 332 | // 1. 解析注册参数 |
| 333 | - IotDeviceRegisterReqDTO registerParams = parseRegisterParams(message.getParams()); | |
| 334 | - if (registerParams == null) { | |
| 333 | + IotDeviceRegisterReqDTO params = parseRegisterParams(message.getParams()); | |
| 334 | + if (params == null) { | |
| 335 | 335 | log.warn("[handleRegisterRequest][注册参数解析失败,客户端 ID: {}]", clientId); |
| 336 | 336 | sendRegisterErrorResponse(endpoint, productKey, deviceName, message.getRequestId(), "注册参数不完整"); |
| 337 | 337 | return; |
| 338 | 338 | } |
| 339 | 339 | |
| 340 | 340 | // 2. 调用动态注册 API |
| 341 | - CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(registerParams); | |
| 341 | + CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params); | |
| 342 | 342 | if (result.isError()) { |
| 343 | 343 | log.warn("[handleRegisterRequest][注册失败,客户端 ID: {},错误: {}]", clientId, result.getMsg()); |
| 344 | 344 | sendRegisterErrorResponse(endpoint, productKey, deviceName, message.getRequestId(), result.getMsg()); |
| ... | ... | @@ -348,7 +348,7 @@ public class IotMqttUpstreamHandler { |
| 348 | 348 | // 3. 发送成功响应(包含 deviceSecret) |
| 349 | 349 | sendRegisterSuccessResponse(endpoint, productKey, deviceName, message.getRequestId(), result.getData()); |
| 350 | 350 | log.info("[handleRegisterRequest][注册成功,设备名: {},客户端 ID: {}]", |
| 351 | - registerParams.getDeviceName(), clientId); | |
| 351 | + params.getDeviceName(), clientId); | |
| 352 | 352 | } catch (Exception e) { |
| 353 | 353 | log.error("[handleRegisterRequest][注册处理异常,客户端 ID: {}]", clientId, e); |
| 354 | 354 | sendRegisterErrorResponse(endpoint, productKey, deviceName, message.getRequestId(), "注册处理异常"); |
| ... | ... | @@ -361,36 +361,27 @@ public class IotMqttUpstreamHandler { |
| 361 | 361 | * @param params 参数对象(通常为 Map 类型) |
| 362 | 362 | * @return 注册参数 DTO,解析失败时返回 null |
| 363 | 363 | */ |
| 364 | - @SuppressWarnings("unchecked") | |
| 364 | + @SuppressWarnings({"unchecked", "DuplicatedCode"}) | |
| 365 | 365 | private IotDeviceRegisterReqDTO parseRegisterParams(Object params) { |
| 366 | 366 | if (params == null) { |
| 367 | 367 | return null; |
| 368 | 368 | } |
| 369 | - | |
| 370 | 369 | try { |
| 371 | 370 | // 参数默认为 Map 类型,直接转换 |
| 372 | 371 | if (params instanceof Map) { |
| 373 | 372 | Map<String, Object> paramMap = (Map<String, Object>) params; |
| 374 | - String productKey = MapUtil.getStr(paramMap, "productKey"); | |
| 375 | - String deviceName = MapUtil.getStr(paramMap, "deviceName"); | |
| 376 | - String productSecret = MapUtil.getStr(paramMap, "productSecret"); | |
| 377 | - if (StrUtil.hasBlank(productKey, deviceName, productSecret)) { | |
| 378 | - return null; | |
| 379 | - } | |
| 380 | 373 | return new IotDeviceRegisterReqDTO() |
| 381 | - .setProductKey(productKey) | |
| 382 | - .setDeviceName(deviceName) | |
| 383 | - .setProductSecret(productSecret); | |
| 374 | + .setProductKey(MapUtil.getStr(paramMap, "productKey")) | |
| 375 | + .setDeviceName(MapUtil.getStr(paramMap, "deviceName")) | |
| 376 | + .setProductSecret(MapUtil.getStr(paramMap, "productSecret")); | |
| 384 | 377 | } |
| 385 | - | |
| 386 | 378 | // 如果已经是目标类型,直接返回 |
| 387 | 379 | if (params instanceof IotDeviceRegisterReqDTO) { |
| 388 | 380 | return (IotDeviceRegisterReqDTO) params; |
| 389 | 381 | } |
| 390 | 382 | |
| 391 | 383 | // 其他情况尝试 JSON 转换 |
| 392 | - String jsonStr = JsonUtils.toJsonString(params); | |
| 393 | - return JsonUtils.parseObject(jsonStr, IotDeviceRegisterReqDTO.class); | |
| 384 | + return JsonUtils.convertObject(params, IotDeviceRegisterReqDTO.class); | |
| 394 | 385 | } catch (Exception e) { |
| 395 | 386 | log.error("[parseRegisterParams][解析注册参数({})失败]", params, e); |
| 396 | 387 | return null; | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpUpstreamHandler.java
| ... | ... | @@ -12,9 +12,9 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; |
| 12 | 12 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; |
| 13 | 13 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 14 | 14 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 15 | +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | |
| 15 | 16 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; |
| 16 | 17 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; |
| 17 | -import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | |
| 18 | 18 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; |
| 19 | 19 | import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpBinaryDeviceMessageCodec; |
| 20 | 20 | import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpJsonDeviceMessageCodec; |
| ... | ... | @@ -27,6 +27,8 @@ import io.vertx.core.buffer.Buffer; |
| 27 | 27 | import io.vertx.core.net.NetSocket; |
| 28 | 28 | import lombok.extern.slf4j.Slf4j; |
| 29 | 29 | |
| 30 | +import java.util.Map; | |
| 31 | + | |
| 30 | 32 | /** |
| 31 | 33 | * TCP 上行消息处理器 |
| 32 | 34 | * |
| ... | ... | @@ -78,6 +80,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 78 | 80 | |
| 79 | 81 | // 设置消息处理器 |
| 80 | 82 | socket.handler(buffer -> { |
| 83 | + // TODO @AI:TODO @芋艿:这里应该有拆粘包的问题; | |
| 81 | 84 | try { |
| 82 | 85 | processMessage(clientId, buffer, socket); |
| 83 | 86 | } catch (Exception e) { |
| ... | ... | @@ -209,15 +212,15 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 209 | 212 | NetSocket socket) { |
| 210 | 213 | try { |
| 211 | 214 | // 1. 解析注册参数 |
| 212 | - IotDeviceRegisterReqDTO registerParams = parseRegisterParams(message.getParams()); | |
| 213 | - if (registerParams == null) { | |
| 215 | + IotDeviceRegisterReqDTO params = parseRegisterParams(message.getParams()); | |
| 216 | + if (params == null) { | |
| 214 | 217 | log.warn("[handleRegisterRequest][注册参数解析失败,客户端 ID: {}]", clientId); |
| 215 | 218 | sendErrorResponse(socket, message.getRequestId(), "注册参数不完整", codecType); |
| 216 | 219 | return; |
| 217 | 220 | } |
| 218 | 221 | |
| 219 | 222 | // 2. 调用动态注册 |
| 220 | - CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(registerParams); | |
| 223 | + CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params); | |
| 221 | 224 | if (result.isError()) { |
| 222 | 225 | log.warn("[handleRegisterRequest][注册失败,客户端 ID: {},错误: {}]", clientId, result.getMsg()); |
| 223 | 226 | sendErrorResponse(socket, message.getRequestId(), result.getMsg(), codecType); |
| ... | ... | @@ -227,7 +230,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 227 | 230 | // 3. 发送成功响应(包含 deviceSecret) |
| 228 | 231 | sendRegisterSuccessResponse(socket, message.getRequestId(), result.getData(), codecType); |
| 229 | 232 | log.info("[handleRegisterRequest][注册成功,客户端 ID: {},设备名: {}]", |
| 230 | - clientId, registerParams.getDeviceName()); | |
| 233 | + clientId, params.getDeviceName()); | |
| 231 | 234 | } catch (Exception e) { |
| 232 | 235 | log.error("[handleRegisterRequest][注册处理异常,客户端 ID: {}]", clientId, e); |
| 233 | 236 | sendErrorResponse(socket, message.getRequestId(), "注册处理异常", codecType); |
| ... | ... | @@ -419,30 +422,27 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 419 | 422 | * @param params 参数对象(通常为 Map 类型) |
| 420 | 423 | * @return 认证参数 DTO,解析失败时返回 null |
| 421 | 424 | */ |
| 422 | - @SuppressWarnings("unchecked") | |
| 425 | + @SuppressWarnings({"unchecked", "DuplicatedCode"}) | |
| 423 | 426 | private IotDeviceAuthReqDTO parseAuthParams(Object params) { |
| 424 | 427 | if (params == null) { |
| 425 | 428 | return null; |
| 426 | 429 | } |
| 427 | - | |
| 428 | 430 | try { |
| 429 | 431 | // 参数默认为 Map 类型,直接转换 |
| 430 | - if (params instanceof java.util.Map) { | |
| 431 | - java.util.Map<String, Object> paramMap = (java.util.Map<String, Object>) params; | |
| 432 | + if (params instanceof Map) { | |
| 433 | + Map<String, Object> paramMap = (Map<String, Object>) params; | |
| 432 | 434 | return new IotDeviceAuthReqDTO() |
| 433 | 435 | .setClientId(MapUtil.getStr(paramMap, "clientId")) |
| 434 | 436 | .setUsername(MapUtil.getStr(paramMap, "username")) |
| 435 | 437 | .setPassword(MapUtil.getStr(paramMap, "password")); |
| 436 | 438 | } |
| 437 | - | |
| 438 | 439 | // 如果已经是目标类型,直接返回 |
| 439 | 440 | if (params instanceof IotDeviceAuthReqDTO) { |
| 440 | 441 | return (IotDeviceAuthReqDTO) params; |
| 441 | 442 | } |
| 442 | 443 | |
| 443 | 444 | // 其他情况尝试 JSON 转换 |
| 444 | - String jsonStr = JsonUtils.toJsonString(params); | |
| 445 | - return JsonUtils.parseObject(jsonStr, IotDeviceAuthReqDTO.class); | |
| 445 | + return JsonUtils.convertObject(params, IotDeviceAuthReqDTO.class); | |
| 446 | 446 | } catch (Exception e) { |
| 447 | 447 | log.error("[parseAuthParams][解析认证参数({})失败]", params, e); |
| 448 | 448 | return null; |
| ... | ... | @@ -455,28 +455,20 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { |
| 455 | 455 | * @param params 参数对象(通常为 Map 类型) |
| 456 | 456 | * @return 注册参数 DTO,解析失败时返回 null |
| 457 | 457 | */ |
| 458 | - @SuppressWarnings("unchecked") | |
| 458 | + @SuppressWarnings({"unchecked", "DuplicatedCode"}) | |
| 459 | 459 | private IotDeviceRegisterReqDTO parseRegisterParams(Object params) { |
| 460 | 460 | if (params == null) { |
| 461 | 461 | return null; |
| 462 | 462 | } |
| 463 | - | |
| 464 | 463 | try { |
| 465 | 464 | // 参数默认为 Map 类型,直接转换 |
| 466 | - if (params instanceof java.util.Map) { | |
| 467 | - java.util.Map<String, Object> paramMap = (java.util.Map<String, Object>) params; | |
| 468 | - String productKey = MapUtil.getStr(paramMap, "productKey"); | |
| 469 | - String deviceName = MapUtil.getStr(paramMap, "deviceName"); | |
| 470 | - String productSecret = MapUtil.getStr(paramMap, "productSecret"); | |
| 471 | - if (StrUtil.hasBlank(productKey, deviceName, productSecret)) { | |
| 472 | - return null; | |
| 473 | - } | |
| 465 | + if (params instanceof Map) { | |
| 466 | + Map<String, Object> paramMap = (Map<String, Object>) params; | |
| 474 | 467 | return new IotDeviceRegisterReqDTO() |
| 475 | - .setProductKey(productKey) | |
| 476 | - .setDeviceName(deviceName) | |
| 477 | - .setProductSecret(productSecret); | |
| 468 | + .setProductKey(MapUtil.getStr(paramMap, "productKey")) | |
| 469 | + .setDeviceName(MapUtil.getStr(paramMap, "deviceName")) | |
| 470 | + .setProductSecret(MapUtil.getStr(paramMap, "productSecret")); | |
| 478 | 471 | } |
| 479 | - | |
| 480 | 472 | // 如果已经是目标类型,直接返回 |
| 481 | 473 | if (params instanceof IotDeviceRegisterReqDTO) { |
| 482 | 474 | return (IotDeviceRegisterReqDTO) params; | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/router/IotUdpUpstreamHandler.java
| ... | ... | @@ -225,15 +225,15 @@ public class IotUdpUpstreamHandler { |
| 225 | 225 | String addressKey = sessionManager.buildAddressKey(senderAddress); |
| 226 | 226 | try { |
| 227 | 227 | // 1. 解析注册参数 |
| 228 | - IotDeviceRegisterReqDTO registerParams = parseRegisterParams(message.getParams()); | |
| 229 | - if (registerParams == null) { | |
| 228 | + IotDeviceRegisterReqDTO params = parseRegisterParams(message.getParams()); | |
| 229 | + if (params == null) { | |
| 230 | 230 | log.warn("[handleRegisterRequest][注册参数解析失败,来源: {}]", addressKey); |
| 231 | 231 | sendErrorResponse(socket, senderAddress, message.getRequestId(), "注册参数不完整", codecType); |
| 232 | 232 | return; |
| 233 | 233 | } |
| 234 | 234 | |
| 235 | 235 | // 2. 调用动态注册 |
| 236 | - CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(registerParams); | |
| 236 | + CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params); | |
| 237 | 237 | if (result.isError()) { |
| 238 | 238 | log.warn("[handleRegisterRequest][注册失败,来源: {},错误: {}]", addressKey, result.getMsg()); |
| 239 | 239 | sendErrorResponse(socket, senderAddress, message.getRequestId(), result.getMsg(), codecType); |
| ... | ... | @@ -243,7 +243,7 @@ public class IotUdpUpstreamHandler { |
| 243 | 243 | // 3. 发送成功响应(包含 deviceSecret) |
| 244 | 244 | sendRegisterSuccessResponse(socket, senderAddress, message.getRequestId(), result.getData(), codecType); |
| 245 | 245 | log.info("[handleRegisterRequest][注册成功,设备名: {},来源: {}]", |
| 246 | - registerParams.getDeviceName(), addressKey); | |
| 246 | + params.getDeviceName(), addressKey); | |
| 247 | 247 | } catch (Exception e) { |
| 248 | 248 | log.error("[handleRegisterRequest][注册处理异常,来源: {}]", addressKey, e); |
| 249 | 249 | sendErrorResponse(socket, senderAddress, message.getRequestId(), "注册处理异常", codecType); |
| ... | ... | @@ -484,7 +484,6 @@ public class IotUdpUpstreamHandler { |
| 484 | 484 | if (params == null) { |
| 485 | 485 | return null; |
| 486 | 486 | } |
| 487 | - | |
| 488 | 487 | try { |
| 489 | 488 | // 参数默认为 Map 类型,直接转换 |
| 490 | 489 | if (params instanceof Map) { |
| ... | ... | @@ -494,15 +493,13 @@ public class IotUdpUpstreamHandler { |
| 494 | 493 | .setUsername(MapUtil.getStr(paramMap, "username")) |
| 495 | 494 | .setPassword(MapUtil.getStr(paramMap, "password")); |
| 496 | 495 | } |
| 497 | - | |
| 498 | 496 | // 如果已经是目标类型,直接返回 |
| 499 | 497 | if (params instanceof IotDeviceAuthReqDTO) { |
| 500 | 498 | return (IotDeviceAuthReqDTO) params; |
| 501 | 499 | } |
| 502 | 500 | |
| 503 | 501 | // 其他情况尝试 JSON 转换 |
| 504 | - String jsonStr = JsonUtils.toJsonString(params); | |
| 505 | - return JsonUtils.parseObject(jsonStr, IotDeviceAuthReqDTO.class); | |
| 502 | + return JsonUtils.convertObject(params, IotDeviceAuthReqDTO.class); | |
| 506 | 503 | } catch (Exception e) { |
| 507 | 504 | log.error("[parseAuthParams][解析认证参数({})失败]", params, e); |
| 508 | 505 | return null; |
| ... | ... | @@ -515,36 +512,27 @@ public class IotUdpUpstreamHandler { |
| 515 | 512 | * @param params 参数对象(通常为 Map 类型) |
| 516 | 513 | * @return 注册参数 DTO,解析失败时返回 null |
| 517 | 514 | */ |
| 518 | - @SuppressWarnings("unchecked") | |
| 515 | + @SuppressWarnings({"unchecked", "DuplicatedCode"}) | |
| 519 | 516 | private IotDeviceRegisterReqDTO parseRegisterParams(Object params) { |
| 520 | 517 | if (params == null) { |
| 521 | 518 | return null; |
| 522 | 519 | } |
| 523 | - | |
| 524 | 520 | try { |
| 525 | 521 | // 参数默认为 Map 类型,直接转换 |
| 526 | 522 | if (params instanceof Map) { |
| 527 | 523 | Map<String, Object> paramMap = (Map<String, Object>) params; |
| 528 | - String productKey = MapUtil.getStr(paramMap, "productKey"); | |
| 529 | - String deviceName = MapUtil.getStr(paramMap, "deviceName"); | |
| 530 | - String productSecret = MapUtil.getStr(paramMap, "productSecret"); | |
| 531 | - if (StrUtil.hasBlank(productKey, deviceName, productSecret)) { | |
| 532 | - return null; | |
| 533 | - } | |
| 534 | 524 | return new IotDeviceRegisterReqDTO() |
| 535 | - .setProductKey(productKey) | |
| 536 | - .setDeviceName(deviceName) | |
| 537 | - .setProductSecret(productSecret); | |
| 525 | + .setProductKey(MapUtil.getStr(paramMap, "productKey")) | |
| 526 | + .setDeviceName(MapUtil.getStr(paramMap, "deviceName")) | |
| 527 | + .setProductSecret(MapUtil.getStr(paramMap, "productSecret")); | |
| 538 | 528 | } |
| 539 | - | |
| 540 | 529 | // 如果已经是目标类型,直接返回 |
| 541 | 530 | if (params instanceof IotDeviceRegisterReqDTO) { |
| 542 | 531 | return (IotDeviceRegisterReqDTO) params; |
| 543 | 532 | } |
| 544 | 533 | |
| 545 | 534 | // 其他情况尝试 JSON 转换 |
| 546 | - String jsonStr = JsonUtils.toJsonString(params); | |
| 547 | - return JsonUtils.parseObject(jsonStr, IotDeviceRegisterReqDTO.class); | |
| 535 | + return JsonUtils.convertObject(params, IotDeviceRegisterReqDTO.class); | |
| 548 | 536 | } catch (Exception e) { |
| 549 | 537 | log.error("[parseRegisterParams][解析注册参数({})失败]", params, e); |
| 550 | 538 | return null; | ... | ... |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/router/IotWebSocketUpstreamHandler.java
| ... | ... | @@ -25,6 +25,8 @@ import io.vertx.core.Handler; |
| 25 | 25 | import io.vertx.core.http.ServerWebSocket; |
| 26 | 26 | import lombok.extern.slf4j.Slf4j; |
| 27 | 27 | |
| 28 | +import java.util.Map; | |
| 29 | + | |
| 28 | 30 | |
| 29 | 31 | /** |
| 30 | 32 | * WebSocket 上行消息处理器 |
| ... | ... | @@ -204,15 +206,16 @@ public class IotWebSocketUpstreamHandler implements Handler<ServerWebSocket> { |
| 204 | 206 | private void handleRegisterRequest(String clientId, IotDeviceMessage message, ServerWebSocket socket) { |
| 205 | 207 | try { |
| 206 | 208 | // 1. 解析注册参数 |
| 207 | - IotDeviceRegisterReqDTO registerParams = parseRegisterParams(message.getParams()); | |
| 208 | - if (registerParams == null) { | |
| 209 | + IotDeviceRegisterReqDTO params = parseRegisterParams(message.getParams()); | |
| 210 | + if (params == null | |
| 211 | + || StrUtil.hasEmpty(params.getProductKey(), params.getDeviceName(), params.getProductSecret())) { | |
| 209 | 212 | log.warn("[handleRegisterRequest][注册参数解析失败,客户端 ID: {}]", clientId); |
| 210 | 213 | sendErrorResponse(socket, message.getRequestId(), "注册参数不完整"); |
| 211 | 214 | return; |
| 212 | 215 | } |
| 213 | 216 | |
| 214 | 217 | // 2. 调用动态注册 |
| 215 | - CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(registerParams); | |
| 218 | + CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(params); | |
| 216 | 219 | if (result.isError()) { |
| 217 | 220 | log.warn("[handleRegisterRequest][注册失败,客户端 ID: {},错误: {}]", clientId, result.getMsg()); |
| 218 | 221 | sendErrorResponse(socket, message.getRequestId(), result.getMsg()); |
| ... | ... | @@ -222,7 +225,7 @@ public class IotWebSocketUpstreamHandler implements Handler<ServerWebSocket> { |
| 222 | 225 | // 3. 发送成功响应(包含 deviceSecret) |
| 223 | 226 | sendRegisterSuccessResponse(socket, message.getRequestId(), result.getData()); |
| 224 | 227 | log.info("[handleRegisterRequest][注册成功,客户端 ID: {},设备名: {}]", |
| 225 | - clientId, registerParams.getDeviceName()); | |
| 228 | + clientId, params.getDeviceName()); | |
| 226 | 229 | } catch (Exception e) { |
| 227 | 230 | log.error("[handleRegisterRequest][注册处理异常,客户端 ID: {}]", clientId, e); |
| 228 | 231 | sendErrorResponse(socket, message.getRequestId(), "注册处理异常"); |
| ... | ... | @@ -384,31 +387,27 @@ public class IotWebSocketUpstreamHandler implements Handler<ServerWebSocket> { |
| 384 | 387 | * @param params 参数对象(通常为 Map 类型) |
| 385 | 388 | * @return 认证参数 DTO,解析失败时返回 null |
| 386 | 389 | */ |
| 387 | - @SuppressWarnings("unchecked") | |
| 390 | + @SuppressWarnings({"unchecked", "DuplicatedCode"}) | |
| 388 | 391 | private IotDeviceAuthReqDTO parseAuthParams(Object params) { |
| 389 | 392 | if (params == null) { |
| 390 | 393 | return null; |
| 391 | 394 | } |
| 392 | - | |
| 393 | 395 | try { |
| 394 | 396 | // 参数默认为 Map 类型,直接转换 |
| 395 | - if (params instanceof java.util.Map) { | |
| 396 | - java.util.Map<String, Object> paramMap = (java.util.Map<String, Object>) params; | |
| 397 | + if (params instanceof Map) { | |
| 398 | + Map<String, Object> paramMap = (Map<String, Object>) params; | |
| 397 | 399 | return new IotDeviceAuthReqDTO() |
| 398 | 400 | .setClientId(MapUtil.getStr(paramMap, "clientId")) |
| 399 | 401 | .setUsername(MapUtil.getStr(paramMap, "username")) |
| 400 | 402 | .setPassword(MapUtil.getStr(paramMap, "password")); |
| 401 | 403 | } |
| 402 | - | |
| 403 | 404 | // 如果已经是目标类型,直接返回 |
| 404 | 405 | if (params instanceof IotDeviceAuthReqDTO) { |
| 405 | 406 | return (IotDeviceAuthReqDTO) params; |
| 406 | 407 | } |
| 407 | 408 | |
| 408 | 409 | // 其他情况尝试 JSON 转换 |
| 409 | - // TODO @芋艿:要不要优化下; | |
| 410 | - String jsonStr = JsonUtils.toJsonString(params); | |
| 411 | - return JsonUtils.convertObject(jsonStr, IotDeviceAuthReqDTO.class); | |
| 410 | + return JsonUtils.convertObject(params, IotDeviceAuthReqDTO.class); | |
| 412 | 411 | } catch (Exception e) { |
| 413 | 412 | log.error("[parseAuthParams][解析认证参数({})失败]", params, e); |
| 414 | 413 | return null; |
| ... | ... | @@ -426,31 +425,22 @@ public class IotWebSocketUpstreamHandler implements Handler<ServerWebSocket> { |
| 426 | 425 | if (params == null) { |
| 427 | 426 | return null; |
| 428 | 427 | } |
| 429 | - | |
| 430 | 428 | try { |
| 431 | 429 | // 参数默认为 Map 类型,直接转换 |
| 432 | - if (params instanceof java.util.Map) { | |
| 433 | - java.util.Map<String, Object> paramMap = (java.util.Map<String, Object>) params; | |
| 434 | - String productKey = MapUtil.getStr(paramMap, "productKey"); | |
| 435 | - String deviceName = MapUtil.getStr(paramMap, "deviceName"); | |
| 436 | - String productSecret = MapUtil.getStr(paramMap, "productSecret"); | |
| 437 | - if (StrUtil.hasBlank(productKey, deviceName, productSecret)) { | |
| 438 | - return null; | |
| 439 | - } | |
| 430 | + if (params instanceof Map) { | |
| 431 | + Map<String, Object> paramMap = (Map<String, Object>) params; | |
| 440 | 432 | return new IotDeviceRegisterReqDTO() |
| 441 | - .setProductKey(productKey) | |
| 442 | - .setDeviceName(deviceName) | |
| 443 | - .setProductSecret(productSecret); | |
| 433 | + .setProductKey(MapUtil.getStr(paramMap, "productKey")) | |
| 434 | + .setDeviceName(MapUtil.getStr(paramMap, "deviceName")) | |
| 435 | + .setProductSecret(MapUtil.getStr(paramMap, "productSecret")); | |
| 444 | 436 | } |
| 445 | - | |
| 446 | 437 | // 如果已经是目标类型,直接返回 |
| 447 | 438 | if (params instanceof IotDeviceRegisterReqDTO) { |
| 448 | 439 | return (IotDeviceRegisterReqDTO) params; |
| 449 | 440 | } |
| 450 | 441 | |
| 451 | 442 | // 其他情况尝试 JSON 转换 |
| 452 | - String jsonStr = JsonUtils.toJsonString(params); | |
| 453 | - return JsonUtils.parseObject(jsonStr, IotDeviceRegisterReqDTO.class); | |
| 443 | + return JsonUtils.convertObject(params, IotDeviceRegisterReqDTO.class); | |
| 454 | 444 | } catch (Exception e) { |
| 455 | 445 | log.error("[parseRegisterParams][解析注册参数({})失败]", params, e); |
| 456 | 446 | return null; | ... | ... |