Commit 3ca4cf265a0323bc06768a5be88955b1a9c1cdda

Authored by 安浩浩
1 parent bf41d47f

feat:【IoT 物联网】优化根据消息方法和回复状态构建主题逻辑

yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxDownstreamHandler.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router;
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import cn.hutool.extra.spring.SpringUtil; 4 import cn.hutool.extra.spring.SpringUtil;
5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
6 -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;  
7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
9 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
@@ -69,58 +68,11 @@ public class IotEmqxDownstreamHandler { @@ -69,58 +68,11 @@ public class IotEmqxDownstreamHandler {
69 * @return 构建的主题,如果方法不支持返回 null 68 * @return 构建的主题,如果方法不支持返回 null
70 */ 69 */
71 private String buildTopicByMethod(IotDeviceMessage message, String productKey, String deviceName) { 70 private String buildTopicByMethod(IotDeviceMessage message, String productKey, String deviceName) {
72 - // 1. 解析消息方法  
73 - IotDeviceMessageMethodEnum methodEnum = IotDeviceMessageMethodEnum.of(message.getMethod());  
74 - if (methodEnum == null) {  
75 - log.warn("[buildTopicByMethod][未知的消息方法: {}]", message.getMethod());  
76 - return null;  
77 - }  
78 -  
79 - // 2. 根据消息方法和回复状态,构建 topic 71 + // 1. 判断是否为回复消息
80 boolean isReply = IotDeviceMessageUtils.isReplyMessage(message); 72 boolean isReply = IotDeviceMessageUtils.isReplyMessage(message);
81 73
82 - // TODO @haohao:看看基于 message 的 method 去反向推导;  
83 - // 3. 根据消息方法类型构建对应的主题  
84 - switch (methodEnum) {  
85 - case PROPERTY_POST:  
86 - // 属性上报:只支持回复消息(下行)  
87 - if (isReply) {  
88 - return IotMqttTopicUtils.buildPropertyPostReplyTopic(productKey, deviceName);  
89 - }  
90 - break;  
91 - case PROPERTY_SET:  
92 - // 属性设置:只支持非回复消息(下行)  
93 - if (!isReply) {  
94 - return IotMqttTopicUtils.buildPropertySetTopic(productKey, deviceName);  
95 - }  
96 - break;  
97 - case EVENT_POST:  
98 - // 事件上报:只支持回复消息(下行)  
99 - if (isReply) {  
100 - return IotMqttTopicUtils.buildEventPostReplyTopicGeneric(productKey, deviceName);  
101 - }  
102 - break;  
103 - case SERVICE_INVOKE:  
104 - // 服务调用:支持请求和回复  
105 - if (isReply) {  
106 - return IotMqttTopicUtils.buildServiceReplyTopicGeneric(productKey, deviceName);  
107 - } else {  
108 - return IotMqttTopicUtils.buildServiceTopicGeneric(productKey, deviceName);  
109 - }  
110 - case CONFIG_PUSH:  
111 - // 配置推送:平台向设备推送配置(下行请求),设备回复确认(上行回复)  
112 - if (!isReply) {  
113 - return IotMqttTopicUtils.buildConfigPushTopic(productKey, deviceName);  
114 - }  
115 - break;  
116 - default:  
117 - log.warn("[buildTopicByMethod][未处理的消息方法: {}]", methodEnum);  
118 - break;  
119 - }  
120 -  
121 - log.warn("[buildTopicByMethod][暂时不支持的下行消息: method={}, isReply={}]",  
122 - message.getMethod(), isReply);  
123 - return null; 74 + // 2. 根据消息方法类型构建对应的主题
  75 + return IotMqttTopicUtils.buildTopicByMethod(message.getMethod(), productKey, deviceName, isReply);
124 } 76 }
125 77
126 } 78 }
127 \ No newline at end of file 79 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/util/IotMqttTopicUtils.java
1 package cn.iocoder.yudao.module.iot.gateway.util; 1 package cn.iocoder.yudao.module.iot.gateway.util;
2 2
  3 +import cn.hutool.core.util.StrUtil;
  4 +
3 /** 5 /**
4 * IoT 网关 MQTT 主题工具类 6 * IoT 网关 MQTT 主题工具类
5 * <p> 7 * <p>
@@ -16,12 +18,6 @@ public final class IotMqttTopicUtils { @@ -16,12 +18,6 @@ public final class IotMqttTopicUtils {
16 */ 18 */
17 private static final String SYS_TOPIC_PREFIX = "/sys/"; 19 private static final String SYS_TOPIC_PREFIX = "/sys/";
18 20
19 - // TODO @haohao:这个要删除哇?  
20 - /**  
21 - * 服务调用主题前缀  
22 - */  
23 - private static final String SERVICE_TOPIC_PREFIX = "/thing/";  
24 -  
25 // ========== MQTT HTTP 接口路径常量 ========== 21 // ========== MQTT HTTP 接口路径常量 ==========
26 22
27 /** 23 /**
@@ -37,98 +33,32 @@ public final class IotMqttTopicUtils { @@ -37,98 +33,32 @@ public final class IotMqttTopicUtils {
37 */ 33 */
38 public static final String MQTT_EVENT_PATH = "/mqtt/event"; 34 public static final String MQTT_EVENT_PATH = "/mqtt/event";
39 35
40 - // TODO @haohao:这个要删除哇?  
41 - /**  
42 - * MQTT 授权接口路径(预留)  
43 - * 对应 EMQX HTTP 授权插件的授权检查接口  
44 - */  
45 - public static final String MQTT_AUTHZ_PATH = "/mqtt/authz";  
46 -  
47 // ========== 工具方法 ========== 36 // ========== 工具方法 ==========
48 37
49 /** 38 /**
50 - * 构建设备主题前缀  
51 - *  
52 - * @param productKey 产品 Key  
53 - * @param deviceName 设备名称  
54 - * @return 设备主题前缀:/sys/{productKey}/{deviceName}  
55 - */  
56 - private static String buildDeviceTopicPrefix(String productKey, String deviceName) {  
57 - return SYS_TOPIC_PREFIX + productKey + "/" + deviceName;  
58 - }  
59 -  
60 - /**  
61 - * 构建设备属性设置主题 39 + * 根据消息方法构建对应的主题
62 * 40 *
  41 + * @param method 消息方法,例如 thing.property.post
63 * @param productKey 产品 Key 42 * @param productKey 产品 Key
64 * @param deviceName 设备名称 43 * @param deviceName 设备名称
  44 + * @param isReply 是否为回复消息
65 * @return 完整的主题路径 45 * @return 完整的主题路径
66 */ 46 */
67 - public static String buildPropertySetTopic(String productKey, String deviceName) {  
68 - return buildDeviceTopicPrefix(productKey, deviceName) + "/thing/property/set";  
69 - } 47 + public static String buildTopicByMethod(String method, String productKey, String deviceName, boolean isReply) {
  48 + if (StrUtil.isBlank(method)) {
  49 + return null;
  50 + }
70 51
71 - /**  
72 - * 构建设备属性上报回复主题  
73 - * <p>  
74 - * 当设备上报属性时,会收到该主题的回复  
75 - *  
76 - * @param productKey 产品 Key  
77 - * @param deviceName 设备名称  
78 - * @return 完整的主题路径  
79 - */  
80 - public static String buildPropertyPostReplyTopic(String productKey, String deviceName) {  
81 - return buildDeviceTopicPrefix(productKey, deviceName) + "/thing/property/post_reply";  
82 - } 52 + // 1. 将点分隔符转换为斜杠
  53 + String topicSuffix = method.replace('.', '/');
83 54
84 - /**  
85 - * 构建设备配置推送主题  
86 - *  
87 - * @param productKey 产品 Key  
88 - * @param deviceName 设备名称  
89 - * @return 完整的主题路径  
90 - */  
91 - public static String buildConfigPushTopic(String productKey, String deviceName) {  
92 - return buildDeviceTopicPrefix(productKey, deviceName) + "/thing/config/push";  
93 - } 55 + // 2. 对于回复消息,添加 _reply 后缀
  56 + if (isReply) {
  57 + topicSuffix += "_reply";
  58 + }
94 59
95 - /**  
96 - * 构建设备事件上报通用回复主题  
97 - * <p>  
98 - * 不包含具体的事件标识符,事件标识符通过消息 data 中的 identifier 字段传递  
99 - *  
100 - * @param productKey 产品 Key  
101 - * @param deviceName 设备名称  
102 - * @return 完整的主题路径  
103 - */  
104 - public static String buildEventPostReplyTopicGeneric(String productKey, String deviceName) {  
105 - return buildDeviceTopicPrefix(productKey, deviceName) + "/thing/event/post_reply";  
106 - }  
107 -  
108 - /**  
109 - * 构建设备服务调用通用主题  
110 - * <p>  
111 - * 不包含具体的服务标识符,服务标识符通过消息 data 中的 identifier 字段传递  
112 - *  
113 - * @param productKey 产品 Key  
114 - * @param deviceName 设备名称  
115 - * @return 完整的主题路径  
116 - */  
117 - public static String buildServiceTopicGeneric(String productKey, String deviceName) {  
118 - return buildDeviceTopicPrefix(productKey, deviceName) + "/thing/service/invoke";  
119 - }  
120 -  
121 - /**  
122 - * 构建设备服务调用通用回复主题  
123 - * <p>  
124 - * 不包含具体的服务标识符,服务标识符通过消息 data 中的 identifier 字段传递  
125 - *  
126 - * @param productKey 产品 Key  
127 - * @param deviceName 设备名称  
128 - * @return 完整的主题路径  
129 - */  
130 - public static String buildServiceReplyTopicGeneric(String productKey, String deviceName) {  
131 - return buildDeviceTopicPrefix(productKey, deviceName) + "/thing/service/invoke_reply"; 60 + // 3. 构建完整主题
  61 + return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + "/" + topicSuffix;
132 } 62 }
133 63
134 } 64 }
135 \ No newline at end of file 65 \ No newline at end of file