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 3 import cn.hutool.core.util.StrUtil;
4 4 import cn.hutool.extra.spring.SpringUtil;
5 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
6   -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
7 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
9 8 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
... ... @@ -69,58 +68,11 @@ public class IotEmqxDownstreamHandler {
69 68 * @return 构建的主题,如果方法不支持返回 null
70 69 */
71 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 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 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 1 package cn.iocoder.yudao.module.iot.gateway.util;
2 2  
  3 +import cn.hutool.core.util.StrUtil;
  4 +
3 5 /**
4 6 * IoT 网关 MQTT 主题工具类
5 7 * <p>
... ... @@ -16,12 +18,6 @@ public final class IotMqttTopicUtils {
16 18 */
17 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 21 // ========== MQTT HTTP 接口路径常量 ==========
26 22  
27 23 /**
... ... @@ -37,98 +33,32 @@ public final class IotMqttTopicUtils {
37 33 */
38 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 42 * @param productKey 产品 Key
64 43 * @param deviceName 设备名称
  44 + * @param isReply 是否为回复消息
65 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 65 \ No newline at end of file
... ...