Commit a4e80d45fed7fe541e207510aea2ce7581b4c29d

Authored by haohao
1 parent c658ac69

fix:【IoT 物联网】修复设备属性上报和配置设置主题,优化 MQTT 消息处理逻辑,更新相关配置文件

yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageServiceImpl.java
... ... @@ -144,12 +144,13 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
144 144  
145 145 // 3. 回复消息。前提:非 _reply 消息,并且非禁用回复的消息
146 146 if (IotDeviceMessageUtils.isReplyMessage(message)
147   - || IotDeviceMessageMethodEnum.isReplyDisabled(message.getMethod())
148   - || StrUtil.isEmpty(message.getServerId())) {
  147 + || IotDeviceMessageMethodEnum.isReplyDisabled(message.getMethod())
  148 + || StrUtil.isEmpty(message.getServerId())) {
149 149 return;
150 150 }
151 151 try {
152   - IotDeviceMessage replyMessage = IotDeviceMessage.replyOf(message.getRequestId(), message.getMethod(), replyData,
  152 + IotDeviceMessage replyMessage = IotDeviceMessage.replyOf(message.getRequestId(), message.getMethod(),
  153 + replyData,
153 154 serviceException != null ? serviceException.getCode() : null,
154 155 serviceException != null ? serviceException.getMessage() : null);
155 156 sendDeviceMessage(replyMessage, device, message.getServerId());
... ... @@ -175,7 +176,8 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
175 176 }
176 177  
177 178 // 属性上报
178   - if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_REPORT.getMethod())) {
  179 + if (Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_REPORT.getMethod()) ||
  180 + Objects.equal(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())) {
179 181 devicePropertyService.saveDeviceProperty(device, message);
180 182 return null;
181 183 }
... ...
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java
... ... @@ -24,6 +24,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> {
24 24  
25 25 // ========== 设备属性 ==========
26 26 // 可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services
  27 + PROPERTY_POST("thing.property.post", true),
27 28 PROPERTY_REPORT("thing.property.report", true),
28 29  
29 30 PROPERTY_SET("thing.property.set", false),
... ... @@ -33,7 +34,8 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> {
33 34  
34 35 ;
35 36  
36   - public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod).toArray(String[]::new);
  37 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageMethodEnum::getMethod)
  38 + .toArray(String[]::new);
37 39  
38 40 /**
39 41 * 不进行 reply 回复的方法集合
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
... ... @@ -128,6 +128,10 @@ public class IotGatewayProperties {
128 128 */
129 129 private Boolean mqttSsl;
130 130 /**
  131 + * MQTT客户端 ID
  132 + */
  133 + private String mqttClientId;
  134 + /**
131 135 * MQTT 主题
132 136 */
133 137 private List<String> mqttTopics;
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/enums/IotDeviceTopicEnum.java
... ... @@ -14,52 +14,40 @@ import lombok.RequiredArgsConstructor;
14 14 @Getter
15 15 public enum IotDeviceTopicEnum {
16 16  
17   - // TODO @haohao:SYS_TOPIC_PREFIX、SERVICE_TOPIC_PREFIX、REPLY_SUFFIX 类似这种,要不搞成这个里面的静态变量?不是枚举值
18   - /**
19   - * 系统主题前缀
20   - */
21   - SYS_TOPIC_PREFIX("/sys/", "系统主题前缀"),
22   -
23   - /**
24   - * 服务调用主题前缀
25   - */
26   - SERVICE_TOPIC_PREFIX("/thing/service/", "服务调用主题前缀"),
27   -
28   - // TODO @haohao:注释时,中英文之间,有个空格;
29 17 /**
30 18 * 设备属性设置主题
31   - * 请求 Topic:/sys/${productKey}/${deviceName}/thing/service/property/set
32   - * 响应 Topic:/sys/${productKey}/${deviceName}/thing/service/property/set_reply
  19 + * 请求 Topic:/sys/${productKey}/${deviceName}/thing/property/set
  20 + * 响应 Topic:/sys/${productKey}/${deviceName}/thing/property/set_reply
33 21 */
34   - PROPERTY_SET_TOPIC("/thing/service/property/set", "设备属性设置主题"),
  22 + PROPERTY_SET_TOPIC("/thing/property/set", "设备属性设置主题"),
35 23  
36 24 /**
37 25 * 设备属性获取主题
38   - * 请求 Topic:/sys/${productKey}/${deviceName}/thing/service/property/get
39   - * 响应 Topic:/sys/${productKey}/${deviceName}/thing/service/property/get_reply
  26 + * 请求 Topic:/sys/${productKey}/${deviceName}/thing/property/get
  27 + * 响应 Topic:/sys/${productKey}/${deviceName}/thing/property/get_reply
40 28 */
41   - PROPERTY_GET_TOPIC("/thing/service/property/get", "设备属性获取主题"),
  29 + PROPERTY_GET_TOPIC("/thing/property/get", "设备属性获取主题"),
42 30  
43 31 /**
44 32 * 设备配置设置主题
45   - * 请求 Topic:/sys/${productKey}/${deviceName}/thing/service/config/set
46   - * 响应 Topic:/sys/${productKey}/${deviceName}/thing/service/config/set_reply
  33 + * 请求 Topic:/sys/${productKey}/${deviceName}/thing/config/set
  34 + * 响应 Topic:/sys/${productKey}/${deviceName}/thing/config/set_reply
47 35 */
48   - CONFIG_SET_TOPIC("/thing/service/config/set", "设备配置设置主题"),
  36 + CONFIG_SET_TOPIC("/thing/config/set", "设备配置设置主题"),
49 37  
50 38 /**
51   - * 设备OTA升级主题
52   - * 请求 Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade
53   - * 响应 Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade_reply
  39 + * 设备 OTA 升级主题
  40 + * 请求 Topic:/sys/${productKey}/${deviceName}/thing/ota/upgrade
  41 + * 响应 Topic:/sys/${productKey}/${deviceName}/thing/ota/upgrade_reply
54 42 */
55   - OTA_UPGRADE_TOPIC("/thing/service/ota/upgrade", "设备OTA升级主题"),
  43 + OTA_UPGRADE_TOPIC("/thing/ota/upgrade", "设备 OTA 升级主题"),
56 44  
57 45 /**
58 46 * 设备属性上报主题
59 47 * 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post
60 48 * 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply
61 49 */
62   - PROPERTY_POST_TOPIC("/thing/event/property/post", "设备属性上报主题"),
  50 + PROPERTY_POST_TOPIC("/thing/property/post", "设备属性上报主题"),
63 51  
64 52 /**
65 53 * 设备事件上报主题前缀
... ... @@ -69,95 +57,194 @@ public enum IotDeviceTopicEnum {
69 57 /**
70 58 * 设备事件上报主题后缀
71 59 */
72   - EVENT_POST_TOPIC_SUFFIX("/post", "设备事件上报主题后缀"),
  60 + EVENT_POST_TOPIC_SUFFIX("/post", "设备事件上报主题后缀");
  61 +
  62 + // ========== 静态常量 ==========
  63 +
  64 + /**
  65 + * 系统主题前缀
  66 + */
  67 + public static final String SYS_TOPIC_PREFIX = "/sys/";
  68 +
  69 + /**
  70 + * 服务调用主题前缀
  71 + */
  72 + public static final String SERVICE_TOPIC_PREFIX = "/thing/";
73 73  
74 74 /**
75 75 * 响应主题后缀
76 76 */
77   - REPLY_SUFFIX("_reply", "响应主题后缀");
  77 + public static final String REPLY_SUFFIX = "_reply";
  78 +
  79 + // ========== 方法常量 ==========
  80 +
  81 + /**
  82 + * 服务方法前缀
  83 + */
  84 + public static final String SERVICE_METHOD_PREFIX = "thing.";
  85 +
  86 + /**
  87 + * 属性服务方法前缀
  88 + */
  89 + public static final String PROPERTY_SERVICE_METHOD_PREFIX = "thing.property.";
  90 +
  91 + /**
  92 + * 配置服务方法前缀
  93 + */
  94 + public static final String CONFIG_SERVICE_METHOD_PREFIX = "thing.config.";
  95 +
  96 + /**
  97 + * OTA 服务方法前缀
  98 + */
  99 + public static final String OTA_SERVICE_METHOD_PREFIX = "thing.ota.";
  100 +
  101 + /**
  102 + * 属性设置方法
  103 + */
  104 + public static final String PROPERTY_SET_METHOD = "thing.property.set";
  105 +
  106 + /**
  107 + * 属性获取方法
  108 + */
  109 + public static final String PROPERTY_GET_METHOD = "thing.property.get";
  110 +
  111 + // ========== 主题匹配常量 ==========
  112 +
  113 + /**
  114 + * 事件上报主题模式
  115 + */
  116 + public static final String EVENT_POST_TOPIC_PATTERN = "/thing/event/";
  117 +
  118 + /**
  119 + * 主题后缀:post
  120 + */
  121 + public static final String POST_SUFFIX = "/post";
  122 +
  123 + /**
  124 + * 属性上报主题后缀
  125 + */
  126 + public static final String PROPERTY_POST_SUFFIX = "/thing/property/post";
  127 +
  128 + /**
  129 + * 属性设置响应主题包含
  130 + */
  131 + public static final String PROPERTY_SET_TOPIC_CONTAINS = "/thing/property/set";
  132 +
  133 + /**
  134 + * 属性获取响应主题包含
  135 + */
  136 + public static final String PROPERTY_GET_TOPIC_CONTAINS = "/thing/property/get";
  137 +
  138 + // ========== MQTT 认证路径常量 ==========
  139 +
  140 + /**
  141 + * MQTT 认证路径
  142 + */
  143 + public static final String MQTT_AUTH_AUTHENTICATE_PATH = "/mqtt/auth/authenticate";
  144 +
  145 + /**
  146 + * MQTT 连接事件路径
  147 + */
  148 + public static final String MQTT_AUTH_CONNECTED_PATH = "/mqtt/auth/connected";
  149 +
  150 + /**
  151 + * MQTT 断开事件路径
  152 + */
  153 + public static final String MQTT_AUTH_DISCONNECTED_PATH = "/mqtt/auth/disconnected";
78 154  
79 155 private final String topic;
80 156 private final String description;
81 157  
  158 + // ========== 工具方法 ==========
  159 +
  160 + /**
  161 + * 构建设备主题前缀
  162 + *
  163 + * @param productKey 产品 Key
  164 + * @param deviceName 设备名称
  165 + * @return 设备主题前缀:/sys/{productKey}/{deviceName}
  166 + */
  167 + private static String buildDeviceTopicPrefix(String productKey, String deviceName) {
  168 + return SYS_TOPIC_PREFIX + productKey + "/" + deviceName;
  169 + }
  170 +
82 171 /**
83 172 * 构建设备服务调用主题
84 173 *
85   - * @param productKey 产品Key
  174 + * @param productKey 产品 Key
86 175 * @param deviceName 设备名称
87 176 * @param serviceIdentifier 服务标识符
88 177 * @return 完整的主题路径
89 178 */
90 179 public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) {
91   - // TODO @haohao:貌似 SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName 是统一的;
92   - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName +
93   - SERVICE_TOPIC_PREFIX.getTopic() + serviceIdentifier;
  180 + return buildDeviceTopicPrefix(productKey, deviceName) + SERVICE_TOPIC_PREFIX + serviceIdentifier;
94 181 }
95 182  
96 183 /**
97 184 * 构建设备属性设置主题
98 185 *
99   - * @param productKey 产品Key
  186 + * @param productKey 产品 Key
100 187 * @param deviceName 设备名称
101 188 * @return 完整的主题路径
102 189 */
103 190 public static String buildPropertySetTopic(String productKey, String deviceName) {
104   - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_SET_TOPIC.getTopic();
  191 + return buildDeviceTopicPrefix(productKey, deviceName) + PROPERTY_SET_TOPIC.getTopic();
105 192 }
106 193  
107 194 /**
108 195 * 构建设备属性获取主题
109 196 *
110   - * @param productKey 产品Key
  197 + * @param productKey 产品 Key
111 198 * @param deviceName 设备名称
112 199 * @return 完整的主题路径
113 200 */
114 201 public static String buildPropertyGetTopic(String productKey, String deviceName) {
115   - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_GET_TOPIC.getTopic();
  202 + return buildDeviceTopicPrefix(productKey, deviceName) + PROPERTY_GET_TOPIC.getTopic();
116 203 }
117 204  
118 205 /**
119 206 * 构建设备配置设置主题
120 207 *
121   - * @param productKey 产品Key
  208 + * @param productKey 产品 Key
122 209 * @param deviceName 设备名称
123 210 * @return 完整的主题路径
124 211 */
125 212 public static String buildConfigSetTopic(String productKey, String deviceName) {
126   - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + CONFIG_SET_TOPIC.getTopic();
  213 + return buildDeviceTopicPrefix(productKey, deviceName) + CONFIG_SET_TOPIC.getTopic();
127 214 }
128 215  
129 216 /**
130 217 * 构建设备 OTA 升级主题
131 218 *
132   - * @param productKey 产品Key
  219 + * @param productKey 产品 Key
133 220 * @param deviceName 设备名称
134 221 * @return 完整的主题路径
135 222 */
136 223 public static String buildOtaUpgradeTopic(String productKey, String deviceName) {
137   - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + OTA_UPGRADE_TOPIC.getTopic();
  224 + return buildDeviceTopicPrefix(productKey, deviceName) + OTA_UPGRADE_TOPIC.getTopic();
138 225 }
139 226  
140 227 /**
141 228 * 构建设备属性上报主题
142 229 *
143   - * @param productKey 产品Key
  230 + * @param productKey 产品 Key
144 231 * @param deviceName 设备名称
145 232 * @return 完整的主题路径
146 233 */
147 234 public static String buildPropertyPostTopic(String productKey, String deviceName) {
148   - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_POST_TOPIC.getTopic();
  235 + return buildDeviceTopicPrefix(productKey, deviceName) + PROPERTY_POST_TOPIC.getTopic();
149 236 }
150 237  
151 238 /**
152 239 * 构建设备事件上报主题
153 240 *
154   - * @param productKey 产品Key
  241 + * @param productKey 产品 Key
155 242 * @param deviceName 设备名称
156 243 * @param eventIdentifier 事件标识符
157 244 * @return 完整的主题路径
158 245 */
159 246 public static String buildEventPostTopic(String productKey, String deviceName, String eventIdentifier) {
160   - return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName +
  247 + return buildDeviceTopicPrefix(productKey, deviceName) +
161 248 EVENT_POST_TOPIC_PREFIX.getTopic() + eventIdentifier + EVENT_POST_TOPIC_SUFFIX.getTopic();
162 249 }
163 250  
... ... @@ -168,7 +255,7 @@ public enum IotDeviceTopicEnum {
168 255 * @return 响应主题
169 256 */
170 257 public static String getReplyTopic(String requestTopic) {
171   - return requestTopic + REPLY_SUFFIX.getTopic();
  258 + return requestTopic + REPLY_SUFFIX;
172 259 }
173 260  
174 261 }
175 262 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttDownstreamSubscriber.java
... ... @@ -105,33 +105,33 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi
105 105 }
106 106  
107 107 // 属性相关操作
108   - if (method.startsWith("thing.service.property.")) {
109   - if ("thing.service.property.set".equals(method)) {
  108 + if (method.startsWith(IotDeviceTopicEnum.PROPERTY_SERVICE_METHOD_PREFIX)) {
  109 + if (IotDeviceTopicEnum.PROPERTY_SET_METHOD.equals(method)) {
110 110 return IotDeviceTopicEnum.buildPropertySetTopic(productKey, deviceName);
111   - } else if ("thing.service.property.get".equals(method)) {
  111 + } else if (IotDeviceTopicEnum.PROPERTY_GET_METHOD.equals(method)) {
112 112 return IotDeviceTopicEnum.buildPropertyGetTopic(productKey, deviceName);
113 113 }
114 114 return null;
115 115 }
116 116  
117 117 // 配置设置操作
118   - if (method.startsWith("thing.service.config.")) {
  118 + if (method.startsWith(IotDeviceTopicEnum.CONFIG_SERVICE_METHOD_PREFIX)) {
119 119 return IotDeviceTopicEnum.buildConfigSetTopic(productKey, deviceName);
120 120 }
121 121  
122 122 // OTA 升级操作
123   - if (method.startsWith("thing.service.ota.")) {
  123 + if (method.startsWith(IotDeviceTopicEnum.OTA_SERVICE_METHOD_PREFIX)) {
124 124 return IotDeviceTopicEnum.buildOtaUpgradeTopic(productKey, deviceName);
125 125 }
126 126  
127 127 // 一般服务调用操作
128   - if (method.startsWith("thing.service.")) {
129   - // 排除属性、配置、OTA相关的服务调用
  128 + if (method.startsWith(IotDeviceTopicEnum.SERVICE_METHOD_PREFIX)) {
  129 + // 排除属性、配置、OTA 相关的服务调用
130 130 if (method.contains("property") || method.contains("config") || method.contains("ota")) {
131 131 return null; // 已在上面处理
132 132 }
133 133 // 从方法中提取服务标识符
134   - String serviceIdentifier = method.substring("thing.service.".length());
  134 + String serviceIdentifier = method.substring(IotDeviceTopicEnum.SERVICE_METHOD_PREFIX.length());
135 135 return IotDeviceTopicEnum.buildServiceTopic(productKey, deviceName, serviceIdentifier);
136 136 }
137 137  
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttUpstreamProtocol.java
1 1 package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.util.IdUtil;
5 4 import cn.hutool.core.util.ObjUtil;
6 5 import cn.hutool.core.util.StrUtil;
7 6 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
8 7 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  8 +import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
9 9 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttHttpAuthHandler;
10 10 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamHandler;
11 11 import io.netty.handler.codec.mqtt.MqttQoS;
... ... @@ -137,9 +137,9 @@ public class IotMqttUpstreamProtocol {
137 137 this.authHandler = new IotMqttHttpAuthHandler();
138 138  
139 139 // 添加认证路由
140   - router.post("/mqtt/auth/authenticate").handler(authHandler::authenticate);
141   - router.post("/mqtt/auth/connected").handler(authHandler::connected);
142   - router.post("/mqtt/auth/disconnected").handler(authHandler::disconnected);
  140 + router.post(IotDeviceTopicEnum.MQTT_AUTH_AUTHENTICATE_PATH).handler(authHandler::authenticate);
  141 + router.post(IotDeviceTopicEnum.MQTT_AUTH_CONNECTED_PATH).handler(authHandler::connected);
  142 + router.post(IotDeviceTopicEnum.MQTT_AUTH_DISCONNECTED_PATH).handler(authHandler::disconnected);
143 143  
144 144 // 启动 HTTP 服务器
145 145 int authPort = emqxProperties.getHttpAuthPort();
... ... @@ -176,11 +176,13 @@ public class IotMqttUpstreamProtocol {
176 176 log.info("[startMqttClient][开始启动 MQTT 客户端]");
177 177  
178 178 // 初始化消息处理器
179   - this.upstreamHandler = new IotMqttUpstreamHandler();
  179 + this.upstreamHandler = new IotMqttUpstreamHandler(this);
180 180  
181 181 // 创建 MQTT 客户端
  182 + log.info("[startMqttClient][使用 MQTT 客户端 ID: {}]", emqxProperties.getMqttClientId());
  183 +
182 184 MqttClientOptions options = new MqttClientOptions()
183   - .setClientId("yudao-iot-gateway-" + IdUtil.fastSimpleUUID())
  185 + .setClientId(emqxProperties.getMqttClientId())
184 186 .setUsername(emqxProperties.getMqttUsername())
185 187 .setPassword(emqxProperties.getMqttPassword())
186 188 .setSsl(ObjUtil.defaultIfNull(emqxProperties.getMqttSsl(), false));
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.util.StrUtil;
4 4 import cn.hutool.extra.spring.SpringUtil;
5 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 6 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
  7 +import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
7 8 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
8 9 import io.vertx.mqtt.messages.MqttPublishMessage;
9 10 import lombok.extern.slf4j.Slf4j;
... ... @@ -22,10 +23,12 @@ public class IotMqttUpstreamHandler {
22 23  
23 24 private final IotDeviceMessageProducer deviceMessageProducer;
24 25 private final IotDeviceMessageService deviceMessageService;
  26 + private final String serverId;
25 27  
26   - public IotMqttUpstreamHandler() {
  28 + public IotMqttUpstreamHandler(cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol protocol) {
27 29 this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class);
28 30 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
  31 + this.serverId = protocol.getServerId();
29 32 }
30 33  
31 34 /**
... ... @@ -92,8 +95,8 @@ public class IotMqttUpstreamHandler {
92 95 IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(
93 96 messageBytes, productKey, deviceName);
94 97  
95   - // 发送消息到队列
96   - deviceMessageProducer.sendDeviceMessage(message);
  98 + // 发送消息到队列(需要补充设备信息)
  99 + deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId);
97 100  
98 101 // 记录成功日志
99 102 log.info("[processMessage][处理{}成功][topic: {}]", messageType, topic);
... ... @@ -106,22 +109,46 @@ public class IotMqttUpstreamHandler {
106 109 * @return 消息类型描述,如果不支持返回 null
107 110 */
108 111 private String getMessageType(String topic) {
109   - // 设备事件上报: /sys/{productKey}/{deviceName}/thing/event/{eventIdentifier}/post
110   - if (topic.contains("/thing/event/") && topic.endsWith("/post")) {
  112 + if (StrUtil.isBlank(topic)) {
  113 + return null;
  114 + }
  115 +
  116 + // 按优先级匹配主题类型,避免误匹配
  117 +
  118 + // 1. 设备属性上报: /sys/{productKey}/{deviceName}/thing/event/property/post
  119 + if (isPropertyPostTopic(topic)) {
  120 + return IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getDescription();
  121 + }
  122 +
  123 + // 2. 设备事件上报: /sys/{productKey}/{deviceName}/thing/event/{eventIdentifier}/post
  124 + if (isEventPostTopic(topic)) {
111 125 return "设备事件上报";
112 126 }
113 127  
114   - // 设备属性操作: /sys/{productKey}/{deviceName}/thing/property/post
115   - // 或属性响应: /sys/{productKey}/{deviceName}/thing/service/property/set_reply
116   - if (topic.endsWith("/thing/property/post") ||
117   - topic.contains("/thing/service/property/set") ||
118   - topic.contains("/thing/service/property/get")) {
119   - return "设备属性操作";
  128 + // 3. 设备属性设置响应: /sys/{productKey}/{deviceName}/thing/service/property/set_reply
  129 + if (isPropertySetReplyTopic(topic)) {
  130 + return "设备属性设置响应";
  131 + }
  132 +
  133 + // 4. 设备属性获取响应: /sys/{productKey}/{deviceName}/thing/service/property/get_reply
  134 + if (isPropertyGetReplyTopic(topic)) {
  135 + return "设备属性获取响应";
  136 + }
  137 +
  138 + // 5. 设备配置设置响应: /sys/{productKey}/{deviceName}/thing/service/config/set_reply
  139 + if (isConfigSetReplyTopic(topic)) {
  140 + return IotDeviceTopicEnum.CONFIG_SET_TOPIC.getDescription() + "响应";
120 141 }
121 142  
122   - // 设备服务调用: /sys/{productKey}/{deviceName}/thing/service/{serviceIdentifier}
123   - if (topic.contains("/thing/service/") && !topic.contains("/property/")) {
124   - return "设备服务调用";
  143 + // 6. 设备 OTA 升级响应:
  144 + // /sys/{productKey}/{deviceName}/thing/service/ota/upgrade_reply
  145 + if (isOtaUpgradeReplyTopic(topic)) {
  146 + return IotDeviceTopicEnum.OTA_UPGRADE_TOPIC.getDescription() + "响应";
  147 + }
  148 +
  149 + // 7. 其他服务调用响应: 通用服务调用响应
  150 + if (isServiceReplyTopic(topic)) {
  151 + return "设备服务调用响应";
125 152 }
126 153  
127 154 // 不支持的消息类型
... ... @@ -129,6 +156,65 @@ public class IotMqttUpstreamHandler {
129 156 }
130 157  
131 158 /**
  159 + * 判断是否为属性上报主题
  160 + */
  161 + private boolean isPropertyPostTopic(String topic) {
  162 + return topic.contains(IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic());
  163 + }
  164 +
  165 + /**
  166 + * 判断是否为事件上报主题
  167 + */
  168 + private boolean isEventPostTopic(String topic) {
  169 + return topic.contains(IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic())
  170 + && topic.endsWith(IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic())
  171 + && !topic.contains("property"); // 排除属性上报主题
  172 + }
  173 +
  174 + /**
  175 + * 判断是否为属性设置响应主题
  176 + */
  177 + private boolean isPropertySetReplyTopic(String topic) {
  178 + return topic.contains(IotDeviceTopicEnum.PROPERTY_SET_TOPIC.getTopic())
  179 + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX);
  180 + }
  181 +
  182 + /**
  183 + * 判断是否为属性获取响应主题
  184 + */
  185 + private boolean isPropertyGetReplyTopic(String topic) {
  186 + return topic.contains(IotDeviceTopicEnum.PROPERTY_GET_TOPIC.getTopic())
  187 + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX);
  188 + }
  189 +
  190 + /**
  191 + * 判断是否为配置设置响应主题
  192 + */
  193 + private boolean isConfigSetReplyTopic(String topic) {
  194 + return topic.contains(IotDeviceTopicEnum.CONFIG_SET_TOPIC.getTopic())
  195 + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX);
  196 + }
  197 +
  198 + /**
  199 + * 判断是否为 OTA 升级响应主题
  200 + */
  201 + private boolean isOtaUpgradeReplyTopic(String topic) {
  202 + return topic.contains(IotDeviceTopicEnum.OTA_UPGRADE_TOPIC.getTopic())
  203 + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX);
  204 + }
  205 +
  206 + /**
  207 + * 判断是否为服务调用响应主题(排除已处理的特殊服务)
  208 + */
  209 + private boolean isServiceReplyTopic(String topic) {
  210 + return topic.contains(IotDeviceTopicEnum.SERVICE_TOPIC_PREFIX)
  211 + && topic.endsWith(IotDeviceTopicEnum.REPLY_SUFFIX)
  212 + && !topic.contains("property")
  213 + && !topic.contains("config")
  214 + && !topic.contains("ota");
  215 + }
  216 +
  217 + /**
132 218 * 解析主题,获取主题各部分
133 219 *
134 220 * @param topic 主题
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application-local.yaml
... ... @@ -39,9 +39,7 @@ yudao:
39 39 mqtt-password: public # MQTT 密码
40 40 mqtt-ssl: false # 是否开启 SSL
41 41 mqtt-topics:
42   - - "/sys/#" # 系统主题(设备上报)
43   - - "/ota/#" # OTA 升级主题
44   - - "/config/#" # 配置主题
  42 + - "/sys/#" # 系统主题
45 43  
46 44 # 消息总线配置
47 45 message-bus:
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml
... ... @@ -40,9 +40,7 @@ yudao:
40 40 enabled: true
41 41 mqtt-ssl: false
42 42 mqtt-topics:
43   - - "/sys/#" # 系统主题(设备上报)
44   - - "/ota/#" # OTA 升级主题
45   - - "/config/#" # 配置主题
  43 + - "/sys/#" # 系统主题
46 44  
47 45 # 消息总线配置
48 46 message-bus:
... ...