Commit a9733b4d2af6caf65445ce2ce4aac3295c106618
1 parent
b6c7937a
【代码评审】IoT:整体实现
Showing
18 changed files
with
83 additions
and
84 deletions
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java
| ... | ... | @@ -43,8 +43,9 @@ public class IotPluginCommonAutoConfiguration { |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | 45 | @Bean(initMethod = "init", destroyMethod = "stop") |
| 46 | - public IotPluginInstanceHeartbeatJob pluginInstanceHeartbeatJob( | |
| 47 | - IotDeviceUpstreamApi deviceDataApi, IotDeviceDownstreamServer deviceDownstreamServer, IotPluginCommonProperties commonProperties) { | |
| 46 | + public IotPluginInstanceHeartbeatJob pluginInstanceHeartbeatJob(IotDeviceUpstreamApi deviceDataApi, | |
| 47 | + IotDeviceDownstreamServer deviceDownstreamServer, | |
| 48 | + IotPluginCommonProperties commonProperties) { | |
| 48 | 49 | return new IotPluginInstanceHeartbeatJob(deviceDataApi, deviceDownstreamServer, commonProperties); |
| 49 | 50 | } |
| 50 | 51 | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java
| ... | ... | @@ -25,6 +25,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC |
| 25 | 25 | @RequiredArgsConstructor |
| 26 | 26 | public class IotDeviceConfigSetVertxHandler implements Handler<RoutingContext> { |
| 27 | 27 | |
| 28 | + // TODO @haohao:是不是可以把 PATH、Method 所有的,抽到一个枚举类里?因为 topic、path、method 相当于不同的几个表达? | |
| 28 | 29 | public static final String PATH = "/sys/:productKey/:deviceName/thing/service/config/set"; |
| 29 | 30 | public static final String METHOD = "thing.service.config.set"; |
| 30 | 31 | |
| ... | ... | @@ -57,12 +58,9 @@ public class IotDeviceConfigSetVertxHandler implements Handler<RoutingContext> { |
| 57 | 58 | CommonResult<Boolean> result = deviceDownstreamHandler.setDeviceConfig(reqDTO); |
| 58 | 59 | |
| 59 | 60 | // 3. 响应结果 |
| 60 | - IotStandardResponse response; | |
| 61 | - if (result.isSuccess()) { | |
| 62 | - response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); | |
| 63 | - } else { | |
| 64 | - response = IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); | |
| 65 | - } | |
| 61 | + IotStandardResponse response = result.isSuccess() ? | |
| 62 | + IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()) | |
| 63 | + : IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); | |
| 66 | 64 | IotPluginCommonUtils.writeJsonResponse(routingContext, response); |
| 67 | 65 | } catch (Exception e) { |
| 68 | 66 | log.error("[handle][请求参数({}) 配置设置异常]", reqDTO, e); | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java
| ... | ... | @@ -62,15 +62,14 @@ public class IotDeviceOtaUpgradeVertxHandler implements Handler<RoutingContext> |
| 62 | 62 | CommonResult<Boolean> result = deviceDownstreamHandler.upgradeDeviceOta(reqDTO); |
| 63 | 63 | |
| 64 | 64 | // 3. 响应结果 |
| 65 | - IotStandardResponse response; | |
| 66 | - if (result.isSuccess()) { | |
| 67 | - response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); | |
| 68 | - } else { | |
| 69 | - response = IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); | |
| 70 | - } | |
| 65 | + // TODO @haohao:可以考虑 IotStandardResponse.of(requestId, method, CommonResult) | |
| 66 | + IotStandardResponse response = result.isSuccess() ? | |
| 67 | + IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()) | |
| 68 | + :IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); | |
| 71 | 69 | IotPluginCommonUtils.writeJsonResponse(routingContext, response); |
| 72 | 70 | } catch (Exception e) { |
| 73 | 71 | log.error("[handle][请求参数({}) OTA 升级异常]", reqDTO, e); |
| 72 | + // TODO @haohao:可以考虑 IotStandardResponse.of(requestId, method, ErrorCode) | |
| 74 | 73 | IotStandardResponse errorResponse = IotStandardResponse.error( |
| 75 | 74 | reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); |
| 76 | 75 | IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.plugin.common.pojo; |
| 2 | 2 | |
| 3 | 3 | import lombok.Data; |
| 4 | -import lombok.experimental.Accessors; | |
| 5 | 4 | |
| 6 | 5 | // TODO @芋艿:1)后续考虑,要不要叫 IoT 网关之类的 Response;2)包名 pojo |
| 7 | 6 | /** |
| ... | ... | @@ -12,7 +11,6 @@ import lombok.experimental.Accessors; |
| 12 | 11 | * @author haohao |
| 13 | 12 | */ |
| 14 | 13 | @Data |
| 15 | -@Accessors(chain = true) | |
| 16 | 14 | public class IotStandardResponse { |
| 17 | 15 | |
| 18 | 16 | /** |
| ... | ... | @@ -92,4 +90,5 @@ public class IotStandardResponse { |
| 92 | 90 | .setMethod(method) |
| 93 | 91 | .setVersion("1.0"); |
| 94 | 92 | } |
| 93 | + | |
| 95 | 94 | } |
| 96 | 95 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java
| ... | ... | @@ -14,7 +14,7 @@ import org.springframework.context.annotation.Bean; |
| 14 | 14 | import org.springframework.context.annotation.Configuration; |
| 15 | 15 | |
| 16 | 16 | /** |
| 17 | - * IoT 插件 Emqx 的专用自动配置类 | |
| 17 | + * IoT 插件 EMQX 的专用自动配置类 | |
| 18 | 18 | * |
| 19 | 19 | * @author haohao |
| 20 | 20 | */ |
| ... | ... | @@ -34,7 +34,7 @@ public class IotPluginEmqxAutoConfiguration { |
| 34 | 34 | .setClientId("yudao-iot-downstream-" + IdUtil.fastSimpleUUID()) |
| 35 | 35 | .setUsername(emqxProperties.getMqttUsername()) |
| 36 | 36 | .setPassword(emqxProperties.getMqttPassword()) |
| 37 | - .setSsl(emqxProperties.isMqttSsl()); | |
| 37 | + .setSsl(emqxProperties.getMqttSsl()); | |
| 38 | 38 | return MqttClient.create(vertx, options); |
| 39 | 39 | } |
| 40 | 40 | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java
| ... | ... | @@ -14,6 +14,8 @@ import org.springframework.validation.annotation.Validated; |
| 14 | 14 | @Data |
| 15 | 15 | public class IotPluginEmqxProperties { |
| 16 | 16 | |
| 17 | + // TODO @haohao:参数校验,加下,啊哈 | |
| 18 | + | |
| 17 | 19 | /** |
| 18 | 20 | * 服务主机 |
| 19 | 21 | */ |
| ... | ... | @@ -21,12 +23,11 @@ public class IotPluginEmqxProperties { |
| 21 | 23 | /** |
| 22 | 24 | * 服务端口 |
| 23 | 25 | */ |
| 24 | - private int mqttPort; | |
| 26 | + private Integer mqttPort; | |
| 25 | 27 | /** |
| 26 | 28 | * 服务用户名 |
| 27 | 29 | */ |
| 28 | 30 | private String mqttUsername; |
| 29 | - | |
| 30 | 31 | /** |
| 31 | 32 | * 服务密码 |
| 32 | 33 | */ |
| ... | ... | @@ -34,7 +35,7 @@ public class IotPluginEmqxProperties { |
| 34 | 35 | /** |
| 35 | 36 | * 是否启用 SSL |
| 36 | 37 | */ |
| 37 | - private boolean mqttSsl; | |
| 38 | + private Boolean mqttSsl; | |
| 38 | 39 | |
| 39 | 40 | /** |
| 40 | 41 | * 订阅的主题列表 |
| ... | ... | @@ -44,6 +45,6 @@ public class IotPluginEmqxProperties { |
| 44 | 45 | /** |
| 45 | 46 | * 认证端口 |
| 46 | 47 | */ |
| 47 | - private int authPort; | |
| 48 | + private Integer authPort; | |
| 48 | 49 | |
| 49 | 50 | } | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java
| ... | ... | @@ -25,6 +25,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 25 | 25 | |
| 26 | 26 | private static final String SYS_TOPIC_PREFIX = "/sys/"; |
| 27 | 27 | |
| 28 | + // TODO @haohao:是不是可以类似 IotDeviceConfigSetVertxHandler 的建议,抽到统一的枚举类 | |
| 28 | 29 | // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。;回复 都使用 Alink 格式,方便后续扩展。 |
| 29 | 30 | // 设备服务调用 标准 JSON |
| 30 | 31 | // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier} |
| ... | ... | @@ -63,7 +64,6 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 63 | 64 | // 构建请求消息 |
| 64 | 65 | String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); |
| 65 | 66 | JSONObject request = buildServiceRequest(requestId, reqDTO.getIdentifier(), reqDTO.getParams()); |
| 66 | - | |
| 67 | 67 | // 发送消息 |
| 68 | 68 | publishMessage(topic, request); |
| 69 | 69 | |
| ... | ... | @@ -82,9 +82,8 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 82 | 82 | |
| 83 | 83 | @Override |
| 84 | 84 | public CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO reqDTO) { |
| 85 | - log.info("[setProperty][开始设置设备属性][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); | |
| 86 | - | |
| 87 | 85 | // 验证参数 |
| 86 | + log.info("[setProperty][开始设置设备属性][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); | |
| 88 | 87 | if (reqDTO.getProductKey() == null || reqDTO.getDeviceName() == null) { |
| 89 | 88 | log.error("[setProperty][参数不完整][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); |
| 90 | 89 | return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); |
| ... | ... | @@ -96,7 +95,6 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 96 | 95 | // 构建请求消息 |
| 97 | 96 | String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); |
| 98 | 97 | JSONObject request = buildPropertySetRequest(requestId, reqDTO.getProperties()); |
| 99 | - | |
| 100 | 98 | // 发送消息 |
| 101 | 99 | publishMessage(topic, request); |
| 102 | 100 | |
| ... | ... | @@ -132,6 +130,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 132 | 130 | return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + PROPERTY_SET_TOPIC; |
| 133 | 131 | } |
| 134 | 132 | |
| 133 | + // TODO @haohao:这个,后面搞个对象,会不会好点哈? | |
| 135 | 134 | /** |
| 136 | 135 | * 构建服务调用请求 |
| 137 | 136 | */ |
| ... | ... | @@ -168,7 +167,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle |
| 168 | 167 | } |
| 169 | 168 | |
| 170 | 169 | /** |
| 171 | - * 生成请求ID | |
| 170 | + * 生成请求 ID | |
| 172 | 171 | */ |
| 173 | 172 | private String generateRequestId() { |
| 174 | 173 | return IdUtil.fastSimpleUUID(); | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java
| ... | ... | @@ -37,10 +37,6 @@ public class IotDeviceUpstreamServer { |
| 37 | 37 | */ |
| 38 | 38 | private static final int CONNECTION_TIMEOUT_MS = 10000; |
| 39 | 39 | /** |
| 40 | - * 主题分隔符 | |
| 41 | - */ | |
| 42 | - private static final String TOPIC_SEPARATOR = ","; | |
| 43 | - /** | |
| 44 | 40 | * 默认 QoS 级别 |
| 45 | 41 | */ |
| 46 | 42 | private static final MqttQoS DEFAULT_QOS = MqttQoS.AT_LEAST_ONCE; |
| ... | ... | @@ -84,42 +80,40 @@ public class IotDeviceUpstreamServer { |
| 84 | 80 | */ |
| 85 | 81 | public void start() { |
| 86 | 82 | if (isRunning) { |
| 87 | - log.warn("服务已经在运行中,请勿重复启动"); | |
| 83 | + log.warn("[start][服务已经在运行中,请勿重复启动]"); | |
| 88 | 84 | return; |
| 89 | 85 | } |
| 90 | - | |
| 91 | - log.info("[start] 开始启动服务"); | |
| 86 | + log.info("[start][开始启动服务]"); | |
| 92 | 87 | |
| 93 | 88 | // 1. 启动 HTTP 服务器 |
| 94 | 89 | CompletableFuture<Void> httpFuture = server.listen(emqxProperties.getAuthPort()) |
| 95 | 90 | .toCompletionStage() |
| 96 | 91 | .toCompletableFuture() |
| 97 | - .thenAccept(v -> log.info("[start] HTTP服务器启动完成,端口: {}", server.actualPort())); | |
| 92 | + .thenAccept(v -> log.info("[start][HTTP服务器启动完成,端口: {}]", server.actualPort())); | |
| 98 | 93 | |
| 99 | 94 | // 2. 连接 MQTT Broker |
| 100 | 95 | CompletableFuture<Void> mqttFuture = connectMqtt() |
| 101 | 96 | .toCompletionStage() |
| 102 | 97 | .toCompletableFuture() |
| 103 | 98 | .thenAccept(v -> { |
| 104 | - // 3. 添加 MQTT 断开重连监听器 | |
| 99 | + // 2.1 添加 MQTT 断开重连监听器 | |
| 105 | 100 | client.closeHandler(closeEvent -> { |
| 106 | - log.warn("[closeHandler] MQTT连接已断开,准备重连"); | |
| 101 | + log.warn("[closeHandler][MQTT连接已断开,准备重连]"); | |
| 107 | 102 | reconnectWithDelay(); |
| 108 | 103 | }); |
| 109 | - | |
| 110 | - // 4. 设置 MQTT 消息处理器 | |
| 104 | + // 2. 设置 MQTT 消息处理器 | |
| 111 | 105 | setupMessageHandler(); |
| 112 | 106 | }); |
| 113 | 107 | |
| 114 | - // 等待所有服务启动完成 | |
| 108 | + // 3. 等待所有服务启动完成 | |
| 115 | 109 | CompletableFuture.allOf(httpFuture, mqttFuture) |
| 116 | 110 | .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) |
| 117 | 111 | .whenComplete((result, error) -> { |
| 118 | 112 | if (error != null) { |
| 119 | - log.error("[start] 服务启动失败", error); | |
| 113 | + log.error("[start][服务启动失败]", error); | |
| 120 | 114 | } else { |
| 121 | 115 | isRunning = true; |
| 122 | - log.info("[start] 所有服务启动完成"); | |
| 116 | + log.info("[start][所有服务启动完成]"); | |
| 123 | 117 | } |
| 124 | 118 | }); |
| 125 | 119 | } |
| ... | ... | @@ -129,7 +123,7 @@ public class IotDeviceUpstreamServer { |
| 129 | 123 | */ |
| 130 | 124 | private void setupMessageHandler() { |
| 131 | 125 | client.publishHandler(mqttMessageHandler::handle); |
| 132 | - log.debug("[setupMessageHandler] MQTT消息处理器设置完成"); | |
| 126 | + log.debug("[setupMessageHandler][MQTT消息处理器设置完成]"); | |
| 133 | 127 | } |
| 134 | 128 | |
| 135 | 129 | /** |
| ... | ... | @@ -137,12 +131,12 @@ public class IotDeviceUpstreamServer { |
| 137 | 131 | */ |
| 138 | 132 | private void reconnectWithDelay() { |
| 139 | 133 | if (!isRunning) { |
| 140 | - log.info("[reconnectWithDelay] 服务已停止,不再尝试重连"); | |
| 134 | + log.info("[reconnectWithDelay][服务已停止,不再尝试重连]"); | |
| 141 | 135 | return; |
| 142 | 136 | } |
| 143 | 137 | |
| 144 | 138 | vertx.setTimer(RECONNECT_DELAY_MS, id -> { |
| 145 | - log.info("[reconnectWithDelay] 开始重新连接MQTT"); | |
| 139 | + log.info("[reconnectWithDelay][开始重新连接 MQTT]"); | |
| 146 | 140 | connectMqtt(); |
| 147 | 141 | }); |
| 148 | 142 | } |
| ... | ... | @@ -155,28 +149,28 @@ public class IotDeviceUpstreamServer { |
| 155 | 149 | private Future<Void> connectMqtt() { |
| 156 | 150 | return client.connect(emqxProperties.getMqttPort(), emqxProperties.getMqttHost()) |
| 157 | 151 | .compose(connAck -> { |
| 158 | - log.info("[connectMqtt] MQTT客户端连接成功"); | |
| 152 | + log.info("[connectMqtt][MQTT客户端连接成功]"); | |
| 159 | 153 | return subscribeToTopics(); |
| 160 | 154 | }) |
| 161 | - .recover(err -> { | |
| 162 | - log.error("[connectMqtt] 连接MQTT Broker失败: {}", err.getMessage()); | |
| 155 | + .recover(error -> { | |
| 156 | + log.error("[connectMqtt][连接MQTT Broker失败:]", error); | |
| 163 | 157 | reconnectWithDelay(); |
| 164 | - return Future.failedFuture(err); | |
| 158 | + return Future.failedFuture(error); | |
| 165 | 159 | }); |
| 166 | 160 | } |
| 167 | 161 | |
| 168 | 162 | /** |
| 169 | 163 | * 订阅设备上行消息主题 |
| 170 | 164 | * |
| 171 | - * @return 订阅结果的Future | |
| 165 | + * @return 订阅结果的 Future | |
| 172 | 166 | */ |
| 173 | 167 | private Future<Void> subscribeToTopics() { |
| 174 | 168 | String[] topics = emqxProperties.getMqttTopics(); |
| 175 | 169 | if (ArrayUtil.isEmpty(topics)) { |
| 176 | - log.warn("[subscribeToTopics] 未配置MQTT主题,跳过订阅"); | |
| 170 | + log.warn("[subscribeToTopics][未配置MQTT主题,跳过订阅]"); | |
| 177 | 171 | return Future.succeededFuture(); |
| 178 | 172 | } |
| 179 | - log.info("[subscribeToTopics] 开始订阅设备上行消息主题"); | |
| 173 | + log.info("[subscribeToTopics][开始订阅设备上行消息主题]"); | |
| 180 | 174 | |
| 181 | 175 | Future<Void> compositeFuture = Future.succeededFuture(); |
| 182 | 176 | for (String topic : topics) { |
| ... | ... | @@ -186,11 +180,11 @@ public class IotDeviceUpstreamServer { |
| 186 | 180 | } |
| 187 | 181 | compositeFuture = compositeFuture.compose(v -> client.subscribe(trimmedTopic, DEFAULT_QOS.value()) |
| 188 | 182 | .<Void>map(ack -> { |
| 189 | - log.info("[subscribeToTopics] 成功订阅主题: {}", trimmedTopic); | |
| 183 | + log.info("[subscribeToTopics][成功订阅主题: {}]", trimmedTopic); | |
| 190 | 184 | return null; |
| 191 | 185 | }) |
| 192 | - .recover(err -> { | |
| 193 | - log.error("[subscribeToTopics] 订阅主题失败: {}, 原因: {}", trimmedTopic, err.getMessage()); | |
| 186 | + .recover(error -> { | |
| 187 | + log.error("[subscribeToTopics][订阅主题失败: {}]", trimmedTopic, error); | |
| 194 | 188 | return Future.<Void>succeededFuture(); // 继续订阅其他主题 |
| 195 | 189 | })); |
| 196 | 190 | } |
| ... | ... | @@ -202,10 +196,10 @@ public class IotDeviceUpstreamServer { |
| 202 | 196 | */ |
| 203 | 197 | public void stop() { |
| 204 | 198 | if (!isRunning) { |
| 205 | - log.warn("[stop] 服务未运行,无需停止"); | |
| 199 | + log.warn("[stop][服务未运行,无需停止]"); | |
| 206 | 200 | return; |
| 207 | 201 | } |
| 208 | - log.info("[stop] 开始关闭服务"); | |
| 202 | + log.info("[stop][开始关闭服务]"); | |
| 209 | 203 | isRunning = false; |
| 210 | 204 | |
| 211 | 205 | try { |
| ... | ... | @@ -224,14 +218,14 @@ public class IotDeviceUpstreamServer { |
| 224 | 218 | .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) |
| 225 | 219 | .whenComplete((result, error) -> { |
| 226 | 220 | if (error != null) { |
| 227 | - log.error("[stop] 服务关闭过程中发生异常", error); | |
| 221 | + log.error("[stop][服务关闭过程中发生异常]", error); | |
| 228 | 222 | } else { |
| 229 | - log.info("[stop] 所有服务关闭完成"); | |
| 223 | + log.info("[stop][所有服务关闭完成]"); | |
| 230 | 224 | } |
| 231 | 225 | }); |
| 232 | 226 | } catch (Exception e) { |
| 233 | - log.error("[stop] 关闭服务异常", e); | |
| 234 | - throw new RuntimeException("关闭IoT设备上行服务失败", e); | |
| 227 | + log.error("[stop][关闭服务异常]", e); | |
| 228 | + throw new RuntimeException("关闭 IoT 设备上行服务失败", e); | |
| 235 | 229 | } |
| 236 | 230 | } |
| 237 | 231 | } |
| 238 | 232 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java
| ... | ... | @@ -15,7 +15,7 @@ import java.util.Collections; |
| 15 | 15 | /** |
| 16 | 16 | * IoT EMQX 连接认证的 Vert.x Handler |
| 17 | 17 | * |
| 18 | - * <a href="https://docs.emqx.com/zh/emqx/latest/access-control/authn/http.html">EMQX HTTP</a> | |
| 18 | + * 参考:<a href="https://docs.emqx.com/zh/emqx/latest/access-control/authn/http.html">EMQX HTTP</a> | |
| 19 | 19 | * |
| 20 | 20 | * 注意:该处理器需要返回特定格式:{"result": "allow"} 或 {"result": "deny"}, |
| 21 | 21 | * 以符合 EMQX 认证插件的要求,因此不使用 IotStandardResponse 实体类 |
| ... | ... | @@ -31,7 +31,6 @@ public class IotDeviceAuthVertxHandler implements Handler<RoutingContext> { |
| 31 | 31 | private final IotDeviceUpstreamApi deviceUpstreamApi; |
| 32 | 32 | |
| 33 | 33 | @Override |
| 34 | - @SuppressWarnings("unchecked") | |
| 35 | 34 | public void handle(RoutingContext routingContext) { |
| 36 | 35 | try { |
| 37 | 36 | // 构建认证请求 DTO | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java
| ... | ... | @@ -23,7 +23,7 @@ import java.util.Map; |
| 23 | 23 | /** |
| 24 | 24 | * IoT 设备 MQTT 消息处理器 |
| 25 | 25 | * |
| 26 | - * 参考:"<a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services">设备属性、事件、服务</a>"> | |
| 26 | + * 参考:<a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services">设备属性、事件、服务</a> | |
| 27 | 27 | */ |
| 28 | 28 | @Slf4j |
| 29 | 29 | public class IotDeviceMqttMessageHandler { | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java
| ... | ... | @@ -18,7 +18,7 @@ import java.util.Collections; |
| 18 | 18 | /** |
| 19 | 19 | * IoT EMQX Webhook 事件处理的 Vert.x Handler |
| 20 | 20 | * |
| 21 | - * <a href="https://docs.emqx.com/zh/emqx/latest/data-integration/webhook.html">EMQX Webhook</a> | |
| 21 | + * 参考:<a href="https://docs.emqx.com/zh/emqx/latest/data-integration/webhook.html">EMQX Webhook</a> | |
| 22 | 22 | * |
| 23 | 23 | * 注意:该处理器需要返回特定格式:{"result": "success"} 或 {"result": "error"}, |
| 24 | 24 | * 以符合 EMQX Webhook 插件的要求,因此不使用 IotStandardResponse 实体类。 |
| ... | ... | @@ -51,8 +51,7 @@ public class IotDeviceWebhookVertxHandler implements Handler<RoutingContext> { |
| 51 | 51 | handleClientDisconnected(clientId, username); |
| 52 | 52 | break; |
| 53 | 53 | default: |
| 54 | - log.info("[handle][未处理的 Webhook 事件] event={}, clientId={}, username={}", event, clientId, | |
| 55 | - username); | |
| 54 | + log.info("[handle][未处理的 Webhook 事件] event={}, clientId={}, username={}", event, clientId, username); | |
| 56 | 55 | break; |
| 57 | 56 | } |
| 58 | 57 | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java
| ... | ... | @@ -23,10 +23,8 @@ public class IotHttpVertxPlugin extends SpringPlugin { |
| 23 | 23 | public void start() { |
| 24 | 24 | log.info("[HttpVertxPlugin][HttpVertxPlugin 插件启动开始...]"); |
| 25 | 25 | try { |
| 26 | - // 1. 获取插件上下文 | |
| 27 | 26 | ApplicationContext pluginContext = getApplicationContext(); |
| 28 | 27 | Assert.notNull(pluginContext, "pluginContext 不能为空"); |
| 29 | - | |
| 30 | 28 | log.info("[HttpVertxPlugin][HttpVertxPlugin 插件启动成功...]"); |
| 31 | 29 | } catch (Exception e) { |
| 32 | 30 | log.error("[HttpVertxPlugin][HttpVertxPlugin 插件开启动异常...]", e); |
| ... | ... | @@ -43,6 +41,7 @@ public class IotHttpVertxPlugin extends SpringPlugin { |
| 43 | 41 | } |
| 44 | 42 | } |
| 45 | 43 | |
| 44 | + // TODO @芋艿:思考下,未来要不要。。。 | |
| 46 | 45 | @Override |
| 47 | 46 | protected ApplicationContext createApplicationContext() { |
| 48 | 47 | // 创建插件自己的 ApplicationContext |
| ... | ... | @@ -52,6 +51,7 @@ public class IotHttpVertxPlugin extends SpringPlugin { |
| 52 | 51 | // 继续使用插件自己的 ClassLoader 以加载插件内部的类 |
| 53 | 52 | pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); |
| 54 | 53 | // 扫描当前插件的自动配置包 |
| 54 | + // TODO @芋艿:后续看看,怎么配置类包 | |
| 55 | 55 | pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.http.config"); |
| 56 | 56 | pluginContext.refresh(); |
| 57 | 57 | return pluginContext; | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java
| ... | ... | @@ -34,6 +34,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC |
| 34 | 34 | @Slf4j |
| 35 | 35 | public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> { |
| 36 | 36 | |
| 37 | + // TODO @haohao:要不要类似 IotDeviceConfigSetVertxHandler 写的,把这些 PATH、METHOD 之类的抽走 | |
| 37 | 38 | /** |
| 38 | 39 | * 属性上报路径 |
| 39 | 40 | */ |
| ... | ... | @@ -49,6 +50,7 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> { |
| 49 | 50 | |
| 50 | 51 | private final IotDeviceUpstreamApi deviceUpstreamApi; |
| 51 | 52 | |
| 53 | + // TODO @haohao:要不要分成多个 Handler?每个只解决一个问题哈。 | |
| 52 | 54 | @Override |
| 53 | 55 | public void handle(RoutingContext routingContext) { |
| 54 | 56 | String path = routingContext.request().path(); |
| ... | ... | @@ -102,7 +104,6 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> { |
| 102 | 104 | IotPluginCommonUtils.writeJsonResponse(routingContext, response); |
| 103 | 105 | } catch (Exception e) { |
| 104 | 106 | log.error("[handle][处理上行请求异常] path={}", path, e); |
| 105 | - // 构建错误响应 | |
| 106 | 107 | String method = path.contains("/property/") ? PROPERTY_METHOD |
| 107 | 108 | : EVENT_METHOD_PREFIX + (routingContext.pathParams().containsKey("identifier") |
| 108 | 109 | ? routingContext.pathParam("identifier") |
| ... | ... | @@ -115,27 +116,28 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> { |
| 115 | 116 | /** |
| 116 | 117 | * 更新设备状态 |
| 117 | 118 | * |
| 118 | - * @param productKey 产品Key | |
| 119 | + * @param productKey 产品 Key | |
| 119 | 120 | * @param deviceName 设备名称 |
| 120 | 121 | */ |
| 121 | 122 | private void updateDeviceState(String productKey, String deviceName) { |
| 122 | - deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setState(IotDeviceStateEnum.ONLINE.getState())); | |
| 123 | + deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO() | |
| 124 | + .setRequestId(IdUtil.fastSimpleUUID()).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) | |
| 125 | + .setProductKey(productKey).setDeviceName(deviceName)).setState(IotDeviceStateEnum.ONLINE.getState())); | |
| 123 | 126 | } |
| 124 | 127 | |
| 125 | 128 | /** |
| 126 | 129 | * 解析属性上报请求 |
| 127 | 130 | * |
| 128 | - * @param productKey 产品Key | |
| 131 | + * @param productKey 产品 Key | |
| 129 | 132 | * @param deviceName 设备名称 |
| 130 | - * @param requestId 请求ID | |
| 133 | + * @param requestId 请求 ID | |
| 131 | 134 | * @param body 请求体 |
| 132 | - * @return 属性上报请求DTO | |
| 135 | + * @return 属性上报请求 DTO | |
| 133 | 136 | */ |
| 134 | 137 | @SuppressWarnings("unchecked") |
| 135 | 138 | private IotDevicePropertyReportReqDTO parsePropertyReportRequest(String productKey, String deviceName, String requestId, JsonObject body) { |
| 136 | 139 | // 按照标准 JSON 格式处理属性数据 |
| 137 | 140 | Map<String, Object> properties = new HashMap<>(); |
| 138 | - // 优先使用 params 字段,符合标准 | |
| 139 | 141 | Map<String, Object> params = body.getJsonObject("params") != null ? body.getJsonObject("params").getMap() : null; |
| 140 | 142 | if (params != null) { |
| 141 | 143 | // 将标准格式的 params 转换为平台需要的 properties 格式 |
| ... | ... | @@ -153,24 +155,25 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> { |
| 153 | 155 | } |
| 154 | 156 | |
| 155 | 157 | // 构建属性上报请求 DTO |
| 156 | - return ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO().setRequestId(requestId).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setProperties(properties); | |
| 158 | + return ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO().setRequestId(requestId) | |
| 159 | + .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) | |
| 160 | + .setProductKey(productKey).setDeviceName(deviceName)).setProperties(properties); | |
| 157 | 161 | } |
| 158 | 162 | |
| 159 | 163 | /** |
| 160 | 164 | * 解析事件上报请求 |
| 161 | 165 | * |
| 162 | - * @param productKey 产品Key | |
| 166 | + * @param productKey 产品K ey | |
| 163 | 167 | * @param deviceName 设备名称 |
| 164 | 168 | * @param identifier 事件标识符 |
| 165 | - * @param requestId 请求ID | |
| 169 | + * @param requestId 请求 ID | |
| 166 | 170 | * @param body 请求体 |
| 167 | - * @return 事件上报请求DTO | |
| 171 | + * @return 事件上报请求 DTO | |
| 168 | 172 | */ |
| 169 | 173 | private IotDeviceEventReportReqDTO parseEventReportRequest(String productKey, String deviceName, String identifier, String requestId, JsonObject body) { |
| 170 | - // 按照标准JSON格式处理事件参数 | |
| 174 | + // 按照标准 JSON 格式处理事件参数 | |
| 171 | 175 | Map<String, Object> params; |
| 172 | - // 优先使用params字段,符合标准 | |
| 173 | - if (body.getJsonObject("params") != null) { | |
| 176 | + if (body.containsKey("params")) { | |
| 174 | 177 | params = body.getJsonObject("params").getMap(); |
| 175 | 178 | } else { |
| 176 | 179 | // 兼容旧格式 |
| ... | ... | @@ -178,6 +181,8 @@ public class IotDeviceUpstreamVertxHandler implements Handler<RoutingContext> { |
| 178 | 181 | } |
| 179 | 182 | |
| 180 | 183 | // 构建事件上报请求 DTO |
| 181 | - return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setIdentifier(identifier).setParams(params); | |
| 184 | + return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId) | |
| 185 | + .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) | |
| 186 | + .setProductKey(productKey).setDeviceName(deviceName)).setIdentifier(identifier).setParams(params); | |
| 182 | 187 | } |
| 183 | 188 | } |
| 184 | 189 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/pom.xml
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java
yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java