Commit a9733b4d2af6caf65445ce2ce4aac3295c106618

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