Commit 4ea6e08f99da94b52b2ca8fdd3569c08c8c45613

Authored by haohao
1 parent 800a85f7

feat:【IoT 物联网】新增设备信息查询功能,优化 MQTT 消息处理逻辑

Showing 16 changed files with 765 additions and 188 deletions
yudao-module-iot/yudao-module-iot-biz/pom.xml
@@ -34,13 +34,6 @@ @@ -34,13 +34,6 @@
34 <version>${revision}</version> 34 <version>${revision}</version>
35 </dependency> 35 </dependency>
36 36
37 - <!-- IoT 协议模块 -->  
38 - <dependency>  
39 - <groupId>cn.iocoder.boot</groupId>  
40 - <artifactId>yudao-module-iot-protocol</artifactId>  
41 - <version>${revision}</version>  
42 - </dependency>  
43 -  
44 <dependency> 37 <dependency>
45 <groupId>cn.iocoder.boot</groupId> 38 <groupId>cn.iocoder.boot</groupId>
46 <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId> 39 <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java
@@ -4,6 +4,9 @@ import cn.iocoder.yudao.framework.common.enums.RpcConstants; @@ -4,6 +4,9 @@ import cn.iocoder.yudao.framework.common.enums.RpcConstants;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 5 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
  7 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO;
  8 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
7 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 10 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
8 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
9 import jakarta.annotation.security.PermitAll; 12 import jakarta.annotation.security.PermitAll;
@@ -35,4 +38,25 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { @@ -35,4 +38,25 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi {
35 return success(deviceService.authDevice(authReqDTO)); 38 return success(deviceService.authDevice(authReqDTO));
36 } 39 }
37 40
  41 + @Override
  42 + @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/info")
  43 + @PermitAll
  44 + public CommonResult<IotDeviceInfoRespDTO> getDeviceInfo(@RequestBody IotDeviceInfoReqDTO infoReqDTO) {
  45 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
  46 + infoReqDTO.getProductKey(), infoReqDTO.getDeviceName());
  47 +
  48 + if (device == null) {
  49 + return success(null);
  50 + }
  51 +
  52 + IotDeviceInfoRespDTO respDTO = new IotDeviceInfoRespDTO();
  53 + respDTO.setDeviceId(device.getId());
  54 + respDTO.setProductKey(device.getProductKey());
  55 + respDTO.setDeviceName(device.getDeviceName());
  56 + respDTO.setDeviceKey(device.getDeviceKey());
  57 + respDTO.setTenantId(device.getTenantId());
  58 +
  59 + return success(respDTO);
  60 + }
  61 +
38 } 62 }
39 \ No newline at end of file 63 \ No newline at end of file
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.iot.core.biz; @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.iot.core.biz;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
  5 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO;
  6 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO;
5 7
6 /** 8 /**
7 * IoT 设备通用 API 9 * IoT 设备通用 API
@@ -10,6 +12,20 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; @@ -10,6 +12,20 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
10 */ 12 */
11 public interface IotDeviceCommonApi { 13 public interface IotDeviceCommonApi {
12 14
  15 + /**
  16 + * 设备认证
  17 + *
  18 + * @param authReqDTO 认证请求
  19 + * @return 认证结果
  20 + */
13 CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO); 21 CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO);
14 22
  23 + /**
  24 + * 获取设备信息
  25 + *
  26 + * @param infoReqDTO 设备信息请求
  27 + * @return 设备信息
  28 + */
  29 + CommonResult<IotDeviceInfoRespDTO> getDeviceInfo(IotDeviceInfoReqDTO infoReqDTO);
  30 +
15 } 31 }
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.core.biz.dto;
  2 +
  3 +import jakarta.validation.constraints.NotBlank;
  4 +import lombok.Data;
  5 +
  6 +/**
  7 + * IoT 设备信息查询 Request DTO
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +@Data
  12 +public class IotDeviceInfoReqDTO {
  13 +
  14 + /**
  15 + * 产品标识
  16 + */
  17 + @NotBlank(message = "产品标识不能为空")
  18 + private String productKey;
  19 +
  20 + /**
  21 + * 设备名称
  22 + */
  23 + @NotBlank(message = "设备名称不能为空")
  24 + private String deviceName;
  25 +
  26 +}
0 \ No newline at end of file 27 \ No newline at end of file
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceInfoRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.core.biz.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +/**
  6 + * IoT 设备信息 Response DTO
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +@Data
  11 +public class IotDeviceInfoRespDTO {
  12 +
  13 + /**
  14 + * 设备编号
  15 + */
  16 + private Long deviceId;
  17 +
  18 + /**
  19 + * 产品标识
  20 + */
  21 + private String productKey;
  22 +
  23 + /**
  24 + * 设备名称
  25 + */
  26 + private String deviceName;
  27 +
  28 + /**
  29 + * 设备密钥
  30 + */
  31 + private String deviceKey;
  32 +
  33 + /**
  34 + * 租户编号
  35 + */
  36 + private Long tenantId;
  37 +
  38 +}
0 \ No newline at end of file 39 \ 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
1 package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;
2 2
3 import cn.hutool.json.JSONObject; 3 import cn.hutool.json.JSONObject;
4 -import cn.hutool.json.JSONUtil;  
5 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
6 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; 4 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
7 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; 5 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
9 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
10 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; 8 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
  9 +import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceCacheService;
11 import jakarta.annotation.PostConstruct; 10 import jakarta.annotation.PostConstruct;
  11 +import jakarta.annotation.Resource;
12 import lombok.RequiredArgsConstructor; 12 import lombok.RequiredArgsConstructor;
13 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
14 14
@@ -24,6 +24,9 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi @@ -24,6 +24,9 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi
24 private final IotMqttUpstreamProtocol protocol; 24 private final IotMqttUpstreamProtocol protocol;
25 private final IotMessageBus messageBus; 25 private final IotMessageBus messageBus;
26 26
  27 + @Resource
  28 + private IotDeviceCacheService deviceCacheService;
  29 +
27 @PostConstruct 30 @PostConstruct
28 public void init() { 31 public void init() {
29 messageBus.register(this); 32 messageBus.register(this);
@@ -45,24 +48,24 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi @@ -45,24 +48,24 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi
45 log.info("[onMessage][接收到下行消息:{}]", message); 48 log.info("[onMessage][接收到下行消息:{}]", message);
46 49
47 try { 50 try {
48 - // 根据消息类型处理不同的下行消息  
49 - String messageType = message.getType();  
50 - switch (messageType) {  
51 - case "property":  
52 - handlePropertyMessage(message);  
53 - break;  
54 - case "service":  
55 - handleServiceMessage(message);  
56 - break;  
57 - case "config":  
58 - handleConfigMessage(message);  
59 - break;  
60 - case "ota":  
61 - handleOtaMessage(message);  
62 - break;  
63 - default:  
64 - log.warn("[onMessage][未知的消息类型:{}]", messageType);  
65 - break; 51 + // 根据消息方法处理不同的下行消息
  52 + String method = message.getMethod();
  53 + if (method == null) {
  54 + log.warn("[onMessage][消息方法为空]");
  55 + return;
  56 + }
  57 +
  58 + if (method.startsWith("thing.service.property.")) {
  59 + handlePropertyMessage(message);
  60 + } else if (method.startsWith("thing.service.") && !method.contains("property") && !method.contains("config")
  61 + && !method.contains("ota")) {
  62 + handleServiceMessage(message);
  63 + } else if (method.startsWith("thing.service.config.")) {
  64 + handleConfigMessage(message);
  65 + } else if (method.startsWith("thing.service.ota.")) {
  66 + handleOtaMessage(message);
  67 + } else {
  68 + log.warn("[onMessage][未知的消息方法:{}]", method);
66 } 69 }
67 } catch (Exception e) { 70 } catch (Exception e) {
68 log.error("[onMessage][处理下行消息失败:{}]", message, e); 71 log.error("[onMessage][处理下行消息失败:{}]", message, e);
@@ -75,24 +78,32 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi @@ -75,24 +78,32 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi
75 * @param message 设备消息 78 * @param message 设备消息
76 */ 79 */
77 private void handlePropertyMessage(IotDeviceMessage message) { 80 private void handlePropertyMessage(IotDeviceMessage message) {
78 - String identifier = message.getIdentifier();  
79 - String productKey = message.getProductKey();  
80 - String deviceName = message.getDeviceName(); 81 + String method = message.getMethod();
81 82
82 - if ("set".equals(identifier)) { 83 + // 通过 deviceId 获取设备信息
  84 + IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfoById(message.getDeviceId());
  85 + if (deviceInfo == null) {
  86 + log.warn("[handlePropertyMessage][设备信息不存在][deviceId: {}]", message.getDeviceId());
  87 + return;
  88 + }
  89 +
  90 + String productKey = deviceInfo.getProductKey();
  91 + String deviceName = deviceInfo.getDeviceName();
  92 +
  93 + if ("thing.service.property.set".equals(method)) {
83 // 属性设置 94 // 属性设置
84 String topic = IotDeviceTopicEnum.buildPropertySetTopic(productKey, deviceName); 95 String topic = IotDeviceTopicEnum.buildPropertySetTopic(productKey, deviceName);
85 - JSONObject payload = buildDownstreamPayload(message, "thing.service.property.set"); 96 + JSONObject payload = buildDownstreamPayload(message, method);
86 protocol.publishMessage(topic, payload.toString()); 97 protocol.publishMessage(topic, payload.toString());
87 log.info("[handlePropertyMessage][发送属性设置消息][topic: {}]", topic); 98 log.info("[handlePropertyMessage][发送属性设置消息][topic: {}]", topic);
88 - } else if ("get".equals(identifier)) { 99 + } else if ("thing.service.property.get".equals(method)) {
89 // 属性获取 100 // 属性获取
90 String topic = IotDeviceTopicEnum.buildPropertyGetTopic(productKey, deviceName); 101 String topic = IotDeviceTopicEnum.buildPropertyGetTopic(productKey, deviceName);
91 - JSONObject payload = buildDownstreamPayload(message, "thing.service.property.get"); 102 + JSONObject payload = buildDownstreamPayload(message, method);
92 protocol.publishMessage(topic, payload.toString()); 103 protocol.publishMessage(topic, payload.toString());
93 log.info("[handlePropertyMessage][发送属性获取消息][topic: {}]", topic); 104 log.info("[handlePropertyMessage][发送属性获取消息][topic: {}]", topic);
94 } else { 105 } else {
95 - log.warn("[handlePropertyMessage][未知的属性操作:{}]", identifier); 106 + log.warn("[handlePropertyMessage][未知的属性操作:{}]", method);
96 } 107 }
97 } 108 }
98 109
@@ -102,12 +113,23 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi @@ -102,12 +113,23 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi
102 * @param message 设备消息 113 * @param message 设备消息
103 */ 114 */
104 private void handleServiceMessage(IotDeviceMessage message) { 115 private void handleServiceMessage(IotDeviceMessage message) {
105 - String identifier = message.getIdentifier();  
106 - String productKey = message.getProductKey();  
107 - String deviceName = message.getDeviceName(); 116 + String method = message.getMethod();
  117 +
  118 + // 通过 deviceId 获取设备信息
  119 + IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfoById(message.getDeviceId());
  120 + if (deviceInfo == null) {
  121 + log.warn("[handleServiceMessage][设备信息不存在][deviceId: {}]", message.getDeviceId());
  122 + return;
  123 + }
  124 +
  125 + String productKey = deviceInfo.getProductKey();
  126 + String deviceName = deviceInfo.getDeviceName();
108 127
109 - String topic = IotDeviceTopicEnum.buildServiceTopic(productKey, deviceName, identifier);  
110 - JSONObject payload = buildDownstreamPayload(message, "thing.service." + identifier); 128 + // 从方法中提取服务标识符
  129 + String serviceIdentifier = method.substring("thing.service.".length());
  130 +
  131 + String topic = IotDeviceTopicEnum.buildServiceTopic(productKey, deviceName, serviceIdentifier);
  132 + JSONObject payload = buildDownstreamPayload(message, method);
111 protocol.publishMessage(topic, payload.toString()); 133 protocol.publishMessage(topic, payload.toString());
112 log.info("[handleServiceMessage][发送服务调用消息][topic: {}]", topic); 134 log.info("[handleServiceMessage][发送服务调用消息][topic: {}]", topic);
113 } 135 }
@@ -118,11 +140,18 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi @@ -118,11 +140,18 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi
118 * @param message 设备消息 140 * @param message 设备消息
119 */ 141 */
120 private void handleConfigMessage(IotDeviceMessage message) { 142 private void handleConfigMessage(IotDeviceMessage message) {
121 - String productKey = message.getProductKey();  
122 - String deviceName = message.getDeviceName(); 143 + // 通过 deviceId 获取设备信息
  144 + IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfoById(message.getDeviceId());
  145 + if (deviceInfo == null) {
  146 + log.warn("[handleConfigMessage][设备信息不存在][deviceId: {}]", message.getDeviceId());
  147 + return;
  148 + }
  149 +
  150 + String productKey = deviceInfo.getProductKey();
  151 + String deviceName = deviceInfo.getDeviceName();
123 152
124 String topic = IotDeviceTopicEnum.buildConfigSetTopic(productKey, deviceName); 153 String topic = IotDeviceTopicEnum.buildConfigSetTopic(productKey, deviceName);
125 - JSONObject payload = buildDownstreamPayload(message, "thing.service.config.set"); 154 + JSONObject payload = buildDownstreamPayload(message, message.getMethod());
126 protocol.publishMessage(topic, payload.toString()); 155 protocol.publishMessage(topic, payload.toString());
127 log.info("[handleConfigMessage][发送配置设置消息][topic: {}]", topic); 156 log.info("[handleConfigMessage][发送配置设置消息][topic: {}]", topic);
128 } 157 }
@@ -133,11 +162,18 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi @@ -133,11 +162,18 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi
133 * @param message 设备消息 162 * @param message 设备消息
134 */ 163 */
135 private void handleOtaMessage(IotDeviceMessage message) { 164 private void handleOtaMessage(IotDeviceMessage message) {
136 - String productKey = message.getProductKey();  
137 - String deviceName = message.getDeviceName(); 165 + // 通过 deviceId 获取设备信息
  166 + IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfoById(message.getDeviceId());
  167 + if (deviceInfo == null) {
  168 + log.warn("[handleOtaMessage][设备信息不存在][deviceId: {}]", message.getDeviceId());
  169 + return;
  170 + }
  171 +
  172 + String productKey = deviceInfo.getProductKey();
  173 + String deviceName = deviceInfo.getDeviceName();
138 174
139 String topic = IotDeviceTopicEnum.buildOtaUpgradeTopic(productKey, deviceName); 175 String topic = IotDeviceTopicEnum.buildOtaUpgradeTopic(productKey, deviceName);
140 - JSONObject payload = buildDownstreamPayload(message, "thing.service.ota.upgrade"); 176 + JSONObject payload = buildDownstreamPayload(message, message.getMethod());
141 protocol.publishMessage(topic, payload.toString()); 177 protocol.publishMessage(topic, payload.toString());
142 log.info("[handleOtaMessage][发送 OTA 升级消息][topic: {}]", topic); 178 log.info("[handleOtaMessage][发送 OTA 升级消息][topic: {}]", topic);
143 } 179 }
@@ -151,7 +187,7 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi @@ -151,7 +187,7 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber&lt;IotDevi
151 */ 187 */
152 private JSONObject buildDownstreamPayload(IotDeviceMessage message, String method) { 188 private JSONObject buildDownstreamPayload(IotDeviceMessage message, String method) {
153 JSONObject payload = new JSONObject(); 189 JSONObject payload = new JSONObject();
154 - payload.set("id", message.getMessageId()); 190 + payload.set("id", message.getId()); // 使用正确的消息ID字段
155 payload.set("version", "1.0"); 191 payload.set("version", "1.0");
156 payload.set("method", method); 192 payload.set("method", method);
157 payload.set("params", message.getData()); 193 payload.set("params", message.getData());
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttAuthRouter.java
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
10 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 10 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
11 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; 11 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
12 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; 12 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;
  13 +import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService;
13 import lombok.RequiredArgsConstructor; 14 import lombok.RequiredArgsConstructor;
14 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
15 16
@@ -28,12 +29,14 @@ public class IotMqttAuthRouter { @@ -28,12 +29,14 @@ public class IotMqttAuthRouter {
28 private final IotDeviceMessageProducer deviceMessageProducer; 29 private final IotDeviceMessageProducer deviceMessageProducer;
29 private final IotDeviceTokenService deviceTokenService; 30 private final IotDeviceTokenService deviceTokenService;
30 private final IotDeviceCommonApi deviceCommonApi; 31 private final IotDeviceCommonApi deviceCommonApi;
  32 + private final IotDeviceMessageService deviceMessageService;
31 33
32 public IotMqttAuthRouter(IotMqttUpstreamProtocol protocol) { 34 public IotMqttAuthRouter(IotMqttUpstreamProtocol protocol) {
33 this.protocol = protocol; 35 this.protocol = protocol;
34 this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class); 36 this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class);
35 this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); 37 this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);
36 this.deviceCommonApi = SpringUtil.getBean(IotDeviceCommonApi.class); 38 this.deviceCommonApi = SpringUtil.getBean(IotDeviceCommonApi.class);
  39 + this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
37 } 40 }
38 41
39 /** 42 /**
@@ -125,15 +128,15 @@ public class IotMqttAuthRouter { @@ -125,15 +128,15 @@ public class IotMqttAuthRouter {
125 } 128 }
126 129
127 try { 130 try {
128 - // 发送设备状态消息  
129 - IotDeviceMessage message = IotDeviceMessage.of(  
130 - deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId());  
131 - 131 + // 使用 IotDeviceMessageService 构建设备状态消息
  132 + IotDeviceMessage message;
132 if (online) { 133 if (online) {
133 - message = message.ofStateOnline(); 134 + message = deviceMessageService.buildDeviceMessageOfStateOnline(
  135 + deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId());
134 log.info("[handleDeviceStateChange][发送设备上线消息成功][username: {}]", username); 136 log.info("[handleDeviceStateChange][发送设备上线消息成功][username: {}]", username);
135 } else { 137 } else {
136 - message = message.ofStateOffline(); 138 + message = deviceMessageService.buildDeviceMessageOfStateOffline(
  139 + deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId());
137 log.info("[handleDeviceStateChange][发送设备下线消息成功][username: {}]", username); 140 log.info("[handleDeviceStateChange][发送设备下线消息成功][username: {}]", username);
138 } 141 }
139 142
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttEventHandler.java
@@ -6,10 +6,11 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; @@ -6,10 +6,11 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; 6 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
7 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; 7 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
8 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
  9 +import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService;
9 import lombok.RequiredArgsConstructor; 10 import lombok.RequiredArgsConstructor;
10 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
11 12
12 -import java.util.Map; 13 +import java.nio.charset.StandardCharsets;
13 14
14 /** 15 /**
15 * IoT 网关 MQTT 事件处理器 16 * IoT 网关 MQTT 事件处理器
@@ -24,6 +25,7 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler { @@ -24,6 +25,7 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler {
24 25
25 private final IotMqttUpstreamProtocol protocol; 26 private final IotMqttUpstreamProtocol protocol;
26 private final IotDeviceMessageProducer deviceMessageProducer; 27 private final IotDeviceMessageProducer deviceMessageProducer;
  28 + private final IotDeviceMessageService deviceMessageService;
27 29
28 @Override 30 @Override
29 public void handle(String topic, String payload) { 31 public void handle(String topic, String payload) {
@@ -31,7 +33,6 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler { @@ -31,7 +33,6 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler {
31 log.info("[handle][接收到设备事件上报][topic: {}]", topic); 33 log.info("[handle][接收到设备事件上报][topic: {}]", topic);
32 34
33 // 解析消息内容 35 // 解析消息内容
34 - JSONObject jsonObject = JSONUtil.parseObj(payload);  
35 String[] topicParts = parseTopic(topic); 36 String[] topicParts = parseTopic(topic);
36 if (topicParts == null) { 37 if (topicParts == null) {
37 return; 38 return;
@@ -45,12 +46,10 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler { @@ -45,12 +46,10 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler {
45 return; 46 return;
46 } 47 }
47 48
48 - Map<String, Object> eventData = parseEventDataFromPayload(jsonObject);  
49 - IotDeviceMessage message = IotDeviceMessage.of(productKey, deviceName, protocol.getServerId());  
50 - // 设置事件消息类型和标识符  
51 - message.setType("event");  
52 - message.setIdentifier(eventIdentifier);  
53 - message.setData(eventData); 49 + // 使用 IotDeviceMessageService 解码消息
  50 + byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);
  51 + IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(
  52 + messageBytes, productKey, deviceName, protocol.getServerId());
54 53
55 // 发送消息 54 // 发送消息
56 deviceMessageProducer.sendDeviceMessage(message); 55 deviceMessageProducer.sendDeviceMessage(message);
@@ -58,7 +57,7 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler { @@ -58,7 +57,7 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler {
58 57
59 // 发送响应消息 58 // 发送响应消息
60 String method = "thing.event." + eventIdentifier + ".post"; 59 String method = "thing.event." + eventIdentifier + ".post";
61 - sendResponse(topic, jsonObject, method); 60 + sendResponse(topic, JSONUtil.parseObj(payload), method);
62 } catch (Exception e) { 61 } catch (Exception e) {
63 log.error("[handle][处理设备事件上报失败][topic: {}][payload: {}]", topic, payload, e); 62 log.error("[handle][处理设备事件上报失败][topic: {}][payload: {}]", topic, payload, e);
64 } 63 }
@@ -81,21 +80,6 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler { @@ -81,21 +80,6 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler {
81 } 80 }
82 81
83 /** 82 /**
84 - * 从消息载荷解析事件数据  
85 - *  
86 - * @param jsonObject 消息 JSON 对象  
87 - * @return 事件数据映射  
88 - */  
89 - private Map<String, Object> parseEventDataFromPayload(JSONObject jsonObject) {  
90 - JSONObject params = jsonObject.getJSONObject("params");  
91 - if (params == null) {  
92 - log.warn("[parseEventDataFromPayload][消息格式不正确,缺少 params 字段][jsonObject: {}]", jsonObject);  
93 - return Map.of();  
94 - }  
95 - return params;  
96 - }  
97 -  
98 - /**  
99 * 发送响应消息 83 * 发送响应消息
100 * 84 *
101 * @param topic 原始主题 85 * @param topic 原始主题
@@ -103,18 +87,22 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler { @@ -103,18 +87,22 @@ public class IotMqttEventHandler extends IotMqttAbstractHandler {
103 * @param method 响应方法 87 * @param method 响应方法
104 */ 88 */
105 private void sendResponse(String topic, JSONObject jsonObject, String method) { 89 private void sendResponse(String topic, JSONObject jsonObject, String method) {
106 - String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);  
107 -  
108 - // 构建响应消息  
109 - JSONObject response = new JSONObject();  
110 - response.set("id", jsonObject.getStr("id"));  
111 - response.set("code", 200);  
112 - response.set("method", method);  
113 - response.set("data", new JSONObject());  
114 -  
115 - // 发送响应  
116 - protocol.publishMessage(replyTopic, response.toString());  
117 - log.debug("[sendResponse][发送响应消息][topic: {}]", replyTopic); 90 + try {
  91 + String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);
  92 +
  93 + // 构建响应消息
  94 + JSONObject response = new JSONObject();
  95 + response.set("id", jsonObject.getStr("id"));
  96 + response.set("code", 200);
  97 + response.set("method", method);
  98 + response.set("data", new JSONObject());
  99 +
  100 + // 发送响应
  101 + protocol.publishMessage(replyTopic, response.toString());
  102 + log.debug("[sendResponse][发送响应消息成功][topic: {}]", replyTopic);
  103 + } catch (Exception e) {
  104 + log.error("[sendResponse][发送响应消息失败][topic: {}]", topic, e);
  105 + }
118 } 106 }
119 107
120 } 108 }
121 \ No newline at end of file 109 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttPropertyHandler.java
@@ -6,10 +6,11 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; @@ -6,10 +6,11 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; 6 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
7 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; 7 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
8 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
  9 +import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService;
9 import lombok.RequiredArgsConstructor; 10 import lombok.RequiredArgsConstructor;
10 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
11 12
12 -import java.util.Map; 13 +import java.nio.charset.StandardCharsets;
13 14
14 /** 15 /**
15 * IoT 网关 MQTT 属性处理器 16 * IoT 网关 MQTT 属性处理器
@@ -24,6 +25,7 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler { @@ -24,6 +25,7 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler {
24 25
25 private final IotMqttUpstreamProtocol protocol; 26 private final IotMqttUpstreamProtocol protocol;
26 private final IotDeviceMessageProducer deviceMessageProducer; 27 private final IotDeviceMessageProducer deviceMessageProducer;
  28 + private final IotDeviceMessageService deviceMessageService;
27 29
28 @Override 30 @Override
29 public void handle(String topic, String payload) { 31 public void handle(String topic, String payload) {
@@ -51,27 +53,26 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler { @@ -51,27 +53,26 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler {
51 try { 53 try {
52 log.info("[handlePropertyPost][接收到设备属性上报][topic: {}]", topic); 54 log.info("[handlePropertyPost][接收到设备属性上报][topic: {}]", topic);
53 55
54 - // 解析消息内容  
55 - JSONObject jsonObject = JSONUtil.parseObj(payload); 56 + // 解析主题获取设备信息
56 String[] topicParts = parseTopic(topic); 57 String[] topicParts = parseTopic(topic);
57 if (topicParts == null) { 58 if (topicParts == null) {
58 return; 59 return;
59 } 60 }
60 61
61 - // 构建设备消息  
62 String productKey = topicParts[2]; 62 String productKey = topicParts[2];
63 String deviceName = topicParts[3]; 63 String deviceName = topicParts[3];
64 - Map<String, Object> properties = parsePropertiesFromPayload(jsonObject);  
65 64
66 - IotDeviceMessage message = IotDeviceMessage.of(productKey, deviceName, protocol.getServerId())  
67 - .ofPropertyReport(properties); 65 + // 使用 IotDeviceMessageService 解码消息
  66 + byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);
  67 + IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(
  68 + messageBytes, productKey, deviceName, protocol.getServerId());
68 69
69 // 发送消息 70 // 发送消息
70 deviceMessageProducer.sendDeviceMessage(message); 71 deviceMessageProducer.sendDeviceMessage(message);
71 log.info("[handlePropertyPost][处理设备属性上报成功][topic: {}]", topic); 72 log.info("[handlePropertyPost][处理设备属性上报成功][topic: {}]", topic);
72 73
73 // 发送响应消息 74 // 发送响应消息
74 - sendResponse(topic, jsonObject, "thing.event.property.post"); 75 + sendResponse(topic, JSONUtil.parseObj(payload), "thing.event.property.post");
75 } catch (Exception e) { 76 } catch (Exception e) {
76 log.error("[handlePropertyPost][处理设备属性上报失败][topic: {}][payload: {}]", topic, payload, e); 77 log.error("[handlePropertyPost][处理设备属性上报失败][topic: {}][payload: {}]", topic, payload, e);
77 } 78 }
@@ -86,7 +87,24 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler { @@ -86,7 +87,24 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler {
86 private void handlePropertySetReply(String topic, String payload) { 87 private void handlePropertySetReply(String topic, String payload) {
87 try { 88 try {
88 log.info("[handlePropertySetReply][接收到属性设置响应][topic: {}]", topic); 89 log.info("[handlePropertySetReply][接收到属性设置响应][topic: {}]", topic);
89 - // TODO: 处理属性设置响应逻辑 90 +
  91 + // 解析主题获取设备信息
  92 + String[] topicParts = parseTopic(topic);
  93 + if (topicParts == null) {
  94 + return;
  95 + }
  96 +
  97 + String productKey = topicParts[2];
  98 + String deviceName = topicParts[3];
  99 +
  100 + // 使用 IotDeviceMessageService 解码消息
  101 + byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);
  102 + IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(
  103 + messageBytes, productKey, deviceName, protocol.getServerId());
  104 +
  105 + // 发送消息
  106 + deviceMessageProducer.sendDeviceMessage(message);
  107 + log.info("[handlePropertySetReply][处理属性设置响应成功][topic: {}]", topic);
90 } catch (Exception e) { 108 } catch (Exception e) {
91 log.error("[handlePropertySetReply][处理属性设置响应失败][topic: {}][payload: {}]", topic, payload, e); 109 log.error("[handlePropertySetReply][处理属性设置响应失败][topic: {}][payload: {}]", topic, payload, e);
92 } 110 }
@@ -101,28 +119,30 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler { @@ -101,28 +119,30 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler {
101 private void handlePropertyGetReply(String topic, String payload) { 119 private void handlePropertyGetReply(String topic, String payload) {
102 try { 120 try {
103 log.info("[handlePropertyGetReply][接收到属性获取响应][topic: {}]", topic); 121 log.info("[handlePropertyGetReply][接收到属性获取响应][topic: {}]", topic);
104 - // TODO: 处理属性获取响应逻辑 122 +
  123 + // 解析主题获取设备信息
  124 + String[] topicParts = parseTopic(topic);
  125 + if (topicParts == null) {
  126 + return;
  127 + }
  128 +
  129 + String productKey = topicParts[2];
  130 + String deviceName = topicParts[3];
  131 +
  132 + // 使用 IotDeviceMessageService 解码消息
  133 + byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);
  134 + IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(
  135 + messageBytes, productKey, deviceName, protocol.getServerId());
  136 +
  137 + // 发送消息
  138 + deviceMessageProducer.sendDeviceMessage(message);
  139 + log.info("[handlePropertyGetReply][处理属性获取响应成功][topic: {}]", topic);
105 } catch (Exception e) { 140 } catch (Exception e) {
106 log.error("[handlePropertyGetReply][处理属性获取响应失败][topic: {}][payload: {}]", topic, payload, e); 141 log.error("[handlePropertyGetReply][处理属性获取响应失败][topic: {}][payload: {}]", topic, payload, e);
107 } 142 }
108 } 143 }
109 144
110 /** 145 /**
111 - * 从消息载荷解析属性  
112 - *  
113 - * @param jsonObject 消息 JSON 对象  
114 - * @return 属性映射  
115 - */  
116 - private Map<String, Object> parsePropertiesFromPayload(JSONObject jsonObject) {  
117 - JSONObject params = jsonObject.getJSONObject("params");  
118 - if (params == null) {  
119 - log.warn("[parsePropertiesFromPayload][消息格式不正确,缺少 params 字段][jsonObject: {}]", jsonObject);  
120 - return Map.of();  
121 - }  
122 - return params;  
123 - }  
124 -  
125 - /**  
126 * 发送响应消息 146 * 发送响应消息
127 * 147 *
128 * @param topic 原始主题 148 * @param topic 原始主题
@@ -130,18 +150,22 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler { @@ -130,18 +150,22 @@ public class IotMqttPropertyHandler extends IotMqttAbstractHandler {
130 * @param method 响应方法 150 * @param method 响应方法
131 */ 151 */
132 private void sendResponse(String topic, JSONObject jsonObject, String method) { 152 private void sendResponse(String topic, JSONObject jsonObject, String method) {
133 - String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);  
134 -  
135 - // 构建响应消息  
136 - JSONObject response = new JSONObject();  
137 - response.set("id", jsonObject.getStr("id"));  
138 - response.set("code", 200);  
139 - response.set("method", method);  
140 - response.set("data", new JSONObject());  
141 -  
142 - // 发送响应  
143 - protocol.publishMessage(replyTopic, response.toString());  
144 - log.debug("[sendResponse][发送响应消息][topic: {}]", replyTopic); 153 + try {
  154 + String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);
  155 +
  156 + // 构建响应消息
  157 + JSONObject response = new JSONObject();
  158 + response.set("id", jsonObject.getStr("id"));
  159 + response.set("code", 200);
  160 + response.set("method", method);
  161 + response.set("data", new JSONObject());
  162 +
  163 + // 发送响应
  164 + protocol.publishMessage(replyTopic, response.toString());
  165 + log.debug("[sendResponse][发送响应消息成功][topic: {}]", replyTopic);
  166 + } catch (Exception e) {
  167 + log.error("[sendResponse][发送响应消息失败][topic: {}]", topic, e);
  168 + }
145 } 169 }
146 170
147 } 171 }
148 \ No newline at end of file 172 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttServiceHandler.java
@@ -6,10 +6,11 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; @@ -6,10 +6,11 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; 6 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
7 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; 7 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
8 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; 8 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
  9 +import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService;
9 import lombok.RequiredArgsConstructor; 10 import lombok.RequiredArgsConstructor;
10 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
11 12
12 -import java.util.Map; 13 +import java.nio.charset.StandardCharsets;
13 14
14 /** 15 /**
15 * IoT 网关 MQTT 服务处理器 16 * IoT 网关 MQTT 服务处理器
@@ -24,6 +25,7 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler { @@ -24,6 +25,7 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler {
24 25
25 private final IotMqttUpstreamProtocol protocol; 26 private final IotMqttUpstreamProtocol protocol;
26 private final IotDeviceMessageProducer deviceMessageProducer; 27 private final IotDeviceMessageProducer deviceMessageProducer;
  28 + private final IotDeviceMessageService deviceMessageService;
27 29
28 @Override 30 @Override
29 public void handle(String topic, String payload) { 31 public void handle(String topic, String payload) {
@@ -31,7 +33,6 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler { @@ -31,7 +33,6 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler {
31 log.info("[handle][接收到设备服务调用响应][topic: {}]", topic); 33 log.info("[handle][接收到设备服务调用响应][topic: {}]", topic);
32 34
33 // 解析消息内容 35 // 解析消息内容
34 - JSONObject jsonObject = JSONUtil.parseObj(payload);  
35 String[] topicParts = parseTopic(topic); 36 String[] topicParts = parseTopic(topic);
36 if (topicParts == null) { 37 if (topicParts == null) {
37 return; 38 return;
@@ -45,12 +46,10 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler { @@ -45,12 +46,10 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler {
45 return; 46 return;
46 } 47 }
47 48
48 - Map<String, Object> serviceData = parseServiceDataFromPayload(jsonObject);  
49 - IotDeviceMessage message = IotDeviceMessage.of(productKey, deviceName, protocol.getServerId());  
50 - // 设置服务消息类型和标识符  
51 - message.setType("service");  
52 - message.setIdentifier(serviceIdentifier);  
53 - message.setData(serviceData); 49 + // 使用 IotDeviceMessageService 解码消息
  50 + byte[] messageBytes = payload.getBytes(StandardCharsets.UTF_8);
  51 + IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(
  52 + messageBytes, productKey, deviceName, protocol.getServerId());
54 53
55 // 发送消息 54 // 发送消息
56 deviceMessageProducer.sendDeviceMessage(message); 55 deviceMessageProducer.sendDeviceMessage(message);
@@ -58,7 +57,7 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler { @@ -58,7 +57,7 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler {
58 57
59 // 发送响应消息 58 // 发送响应消息
60 String method = "thing.service." + serviceIdentifier; 59 String method = "thing.service." + serviceIdentifier;
61 - sendResponse(topic, jsonObject, method); 60 + sendResponse(topic, JSONUtil.parseObj(payload), method);
62 } catch (Exception e) { 61 } catch (Exception e) {
63 log.error("[handle][处理设备服务调用响应失败][topic: {}][payload: {}]", topic, payload, e); 62 log.error("[handle][处理设备服务调用响应失败][topic: {}][payload: {}]", topic, payload, e);
64 } 63 }
@@ -82,21 +81,6 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler { @@ -82,21 +81,6 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler {
82 } 81 }
83 82
84 /** 83 /**
85 - * 从消息载荷解析服务数据  
86 - *  
87 - * @param jsonObject 消息 JSON 对象  
88 - * @return 服务数据映射  
89 - */  
90 - private Map<String, Object> parseServiceDataFromPayload(JSONObject jsonObject) {  
91 - JSONObject params = jsonObject.getJSONObject("params");  
92 - if (params == null) {  
93 - log.warn("[parseServiceDataFromPayload][消息格式不正确,缺少 params 字段][jsonObject: {}]", jsonObject);  
94 - return Map.of();  
95 - }  
96 - return params;  
97 - }  
98 -  
99 - /**  
100 * 发送响应消息 84 * 发送响应消息
101 * 85 *
102 * @param topic 原始主题 86 * @param topic 原始主题
@@ -104,18 +88,22 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler { @@ -104,18 +88,22 @@ public class IotMqttServiceHandler extends IotMqttAbstractHandler {
104 * @param method 响应方法 88 * @param method 响应方法
105 */ 89 */
106 private void sendResponse(String topic, JSONObject jsonObject, String method) { 90 private void sendResponse(String topic, JSONObject jsonObject, String method) {
107 - String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);  
108 -  
109 - // 构建响应消息  
110 - JSONObject response = new JSONObject();  
111 - response.set("id", jsonObject.getStr("id"));  
112 - response.set("code", 200);  
113 - response.set("method", method);  
114 - response.set("data", new JSONObject());  
115 -  
116 - // 发送响应  
117 - protocol.publishMessage(replyTopic, response.toString());  
118 - log.debug("[sendResponse][发送响应消息][topic: {}]", replyTopic); 91 + try {
  92 + String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);
  93 +
  94 + // 构建响应消息
  95 + JSONObject response = new JSONObject();
  96 + response.set("id", jsonObject.getStr("id"));
  97 + response.set("code", 200);
  98 + response.set("method", method);
  99 + response.set("data", new JSONObject());
  100 +
  101 + // 发送响应
  102 + protocol.publishMessage(replyTopic, response.toString());
  103 + log.debug("[sendResponse][发送响应消息成功][topic: {}]", replyTopic);
  104 + } catch (Exception e) {
  105 + log.error("[sendResponse][发送响应消息失败][topic: {}]", topic, e);
  106 + }
119 } 107 }
120 108
121 } 109 }
122 \ No newline at end of file 110 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamRouter.java
@@ -5,6 +5,7 @@ import cn.hutool.extra.spring.SpringUtil; @@ -5,6 +5,7 @@ import cn.hutool.extra.spring.SpringUtil;
5 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; 5 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
6 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; 6 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
7 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; 7 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
  8 +import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService;
8 import io.vertx.mqtt.messages.MqttPublishMessage; 9 import io.vertx.mqtt.messages.MqttPublishMessage;
9 import lombok.RequiredArgsConstructor; 10 import lombok.RequiredArgsConstructor;
10 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
@@ -22,6 +23,7 @@ public class IotMqttUpstreamRouter { @@ -22,6 +23,7 @@ public class IotMqttUpstreamRouter {
22 23
23 private final IotMqttUpstreamProtocol protocol; 24 private final IotMqttUpstreamProtocol protocol;
24 private final IotDeviceMessageProducer deviceMessageProducer; 25 private final IotDeviceMessageProducer deviceMessageProducer;
  26 + private final IotDeviceMessageService deviceMessageService;
25 27
26 // 处理器实例 28 // 处理器实例
27 private IotMqttPropertyHandler propertyHandler; 29 private IotMqttPropertyHandler propertyHandler;
@@ -31,10 +33,11 @@ public class IotMqttUpstreamRouter { @@ -31,10 +33,11 @@ public class IotMqttUpstreamRouter {
31 public IotMqttUpstreamRouter(IotMqttUpstreamProtocol protocol) { 33 public IotMqttUpstreamRouter(IotMqttUpstreamProtocol protocol) {
32 this.protocol = protocol; 34 this.protocol = protocol;
33 this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class); 35 this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class);
  36 + this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
34 // 初始化处理器 37 // 初始化处理器
35 - this.propertyHandler = new IotMqttPropertyHandler(protocol, deviceMessageProducer);  
36 - this.eventHandler = new IotMqttEventHandler(protocol, deviceMessageProducer);  
37 - this.serviceHandler = new IotMqttServiceHandler(protocol, deviceMessageProducer); 38 + this.propertyHandler = new IotMqttPropertyHandler(protocol, deviceMessageProducer, deviceMessageService);
  39 + this.eventHandler = new IotMqttEventHandler(protocol, deviceMessageProducer, deviceMessageService);
  40 + this.serviceHandler = new IotMqttServiceHandler(protocol, deviceMessageProducer, deviceMessageService);
38 } 41 }
39 42
40 /** 43 /**
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.service.device;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +/**
  8 + * IoT 设备缓存 Service 接口
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public interface IotDeviceCacheService {
  13 +
  14 + /**
  15 + * 设备信息
  16 + */
  17 + @Data
  18 + @AllArgsConstructor
  19 + @NoArgsConstructor
  20 + class DeviceInfo {
  21 + /**
  22 + * 设备编号
  23 + */
  24 + private Long deviceId;
  25 + /**
  26 + * 产品标识
  27 + */
  28 + private String productKey;
  29 + /**
  30 + * 设备名称
  31 + */
  32 + private String deviceName;
  33 + /**
  34 + * 设备密钥
  35 + */
  36 + private String deviceKey;
  37 + /**
  38 + * 租户编号
  39 + */
  40 + private Long tenantId;
  41 + }
  42 +
  43 + /**
  44 + * 根据 productKey 和 deviceName 获取设备信息
  45 + *
  46 + * @param productKey 产品标识
  47 + * @param deviceName 设备名称
  48 + * @return 设备信息,如果不存在返回 null
  49 + */
  50 + DeviceInfo getDeviceInfo(String productKey, String deviceName);
  51 +
  52 + /**
  53 + * 根据 deviceId 获取设备信息
  54 + *
  55 + * @param deviceId 设备编号
  56 + * @return 设备信息,如果不存在返回 null
  57 + */
  58 + DeviceInfo getDeviceInfoById(Long deviceId);
  59 +
  60 + /**
  61 + * 清除设备缓存
  62 + *
  63 + * @param deviceId 设备编号
  64 + */
  65 + void evictDeviceCache(Long deviceId);
  66 +
  67 + /**
  68 + * 清除设备缓存
  69 + *
  70 + * @param productKey 产品标识
  71 + * @param deviceName 设备名称
  72 + */
  73 + void evictDeviceCache(String productKey, String deviceName);
  74 +
  75 +}
0 \ No newline at end of file 76 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceCacheServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.service.device;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.hutool.extra.spring.SpringUtil;
  5 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
  7 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO;
  8 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import java.util.concurrent.ConcurrentHashMap;
  13 +import java.util.concurrent.locks.ReentrantLock;
  14 +
  15 +/**
  16 + * IoT 设备缓存 Service 实现类
  17 + * <p>
  18 + * 使用本地缓存 + 远程 API 的方式获取设备信息,提高性能并避免敏感信息传输
  19 + *
  20 + * @author 芋道源码
  21 + */
  22 +@Service
  23 +@Slf4j
  24 +public class IotDeviceCacheServiceImpl implements IotDeviceCacheService {
  25 +
  26 + /**
  27 + * 设备信息本地缓存
  28 + * Key: deviceId
  29 + * Value: DeviceInfo
  30 + */
  31 + private final ConcurrentHashMap<Long, DeviceInfo> deviceIdCache = new ConcurrentHashMap<>();
  32 +
  33 + /**
  34 + * 设备名称到设备ID的映射缓存
  35 + * Key: productKey:deviceName
  36 + * Value: deviceId
  37 + */
  38 + private final ConcurrentHashMap<String, Long> deviceNameCache = new ConcurrentHashMap<>();
  39 +
  40 + /**
  41 + * 锁对象,防止并发请求同一设备信息
  42 + */
  43 + private final ConcurrentHashMap<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();
  44 +
  45 + @Override
  46 + public DeviceInfo getDeviceInfo(String productKey, String deviceName) {
  47 + if (StrUtil.isEmpty(productKey) || StrUtil.isEmpty(deviceName)) {
  48 + log.warn("[getDeviceInfo][参数为空][productKey: {}][deviceName: {}]", productKey, deviceName);
  49 + return null;
  50 + }
  51 +
  52 + String cacheKey = buildDeviceNameCacheKey(productKey, deviceName);
  53 +
  54 + // 1. 先从缓存获取 deviceId
  55 + Long deviceId = deviceNameCache.get(cacheKey);
  56 + if (deviceId != null) {
  57 + DeviceInfo deviceInfo = deviceIdCache.get(deviceId);
  58 + if (deviceInfo != null) {
  59 + log.debug("[getDeviceInfo][缓存命中][productKey: {}][deviceName: {}][deviceId: {}]",
  60 + productKey, deviceName, deviceId);
  61 + return deviceInfo;
  62 + }
  63 + }
  64 +
  65 + // 2. 缓存未命中,从远程 API 获取
  66 + return loadDeviceInfoFromApi(productKey, deviceName, cacheKey);
  67 + }
  68 +
  69 + @Override
  70 + public DeviceInfo getDeviceInfoById(Long deviceId) {
  71 + if (deviceId == null) {
  72 + log.warn("[getDeviceInfoById][deviceId 为空]");
  73 + return null;
  74 + }
  75 +
  76 + // 1. 先从缓存获取
  77 + DeviceInfo deviceInfo = deviceIdCache.get(deviceId);
  78 + if (deviceInfo != null) {
  79 + log.debug("[getDeviceInfoById][缓存命中][deviceId: {}]", deviceId);
  80 + return deviceInfo;
  81 + }
  82 +
  83 + // 2. 缓存未命中,从远程 API 获取
  84 + return loadDeviceInfoByIdFromApi(deviceId);
  85 + }
  86 +
  87 + @Override
  88 + public void evictDeviceCache(Long deviceId) {
  89 + if (deviceId == null) {
  90 + return;
  91 + }
  92 +
  93 + DeviceInfo deviceInfo = deviceIdCache.remove(deviceId);
  94 + if (deviceInfo != null) {
  95 + String cacheKey = buildDeviceNameCacheKey(deviceInfo.getProductKey(), deviceInfo.getDeviceName());
  96 + deviceNameCache.remove(cacheKey);
  97 + log.info("[evictDeviceCache][清除设备缓存][deviceId: {}]", deviceId);
  98 + }
  99 + }
  100 +
  101 + @Override
  102 + public void evictDeviceCache(String productKey, String deviceName) {
  103 + if (StrUtil.isEmpty(productKey) || StrUtil.isEmpty(deviceName)) {
  104 + return;
  105 + }
  106 +
  107 + String cacheKey = buildDeviceNameCacheKey(productKey, deviceName);
  108 + Long deviceId = deviceNameCache.remove(cacheKey);
  109 + if (deviceId != null) {
  110 + deviceIdCache.remove(deviceId);
  111 + log.info("[evictDeviceCache][清除设备缓存][productKey: {}][deviceName: {}]", productKey, deviceName);
  112 + }
  113 + }
  114 +
  115 + /**
  116 + * 从远程 API 加载设备信息
  117 + *
  118 + * @param productKey 产品标识
  119 + * @param deviceName 设备名称
  120 + * @param cacheKey 缓存键
  121 + * @return 设备信息
  122 + */
  123 + private DeviceInfo loadDeviceInfoFromApi(String productKey, String deviceName, String cacheKey) {
  124 + // 使用锁防止并发请求同一设备信息
  125 + ReentrantLock lock = lockMap.computeIfAbsent(cacheKey, k -> new ReentrantLock());
  126 + lock.lock();
  127 + try {
  128 + // 双重检查,防止重复请求
  129 + Long deviceId = deviceNameCache.get(cacheKey);
  130 + if (deviceId != null) {
  131 + DeviceInfo deviceInfo = deviceIdCache.get(deviceId);
  132 + if (deviceInfo != null) {
  133 + return deviceInfo;
  134 + }
  135 + }
  136 +
  137 + log.info("[loadDeviceInfoFromApi][从远程API获取设备信息][productKey: {}][deviceName: {}]",
  138 + productKey, deviceName);
  139 +
  140 + try {
  141 + // 调用远程 API 获取设备信息
  142 + IotDeviceCommonApi deviceCommonApi = SpringUtil.getBean(IotDeviceCommonApi.class);
  143 + IotDeviceInfoReqDTO reqDTO = new IotDeviceInfoReqDTO();
  144 + reqDTO.setProductKey(productKey);
  145 + reqDTO.setDeviceName(deviceName);
  146 +
  147 + CommonResult<IotDeviceInfoRespDTO> result = deviceCommonApi.getDeviceInfo(reqDTO);
  148 +
  149 + if (result == null || !result.isSuccess() || result.getData() == null) {
  150 + log.warn("[loadDeviceInfoFromApi][远程API调用失败][productKey: {}][deviceName: {}][result: {}]",
  151 + productKey, deviceName, result);
  152 + return null;
  153 + }
  154 +
  155 + IotDeviceInfoRespDTO respDTO = result.getData();
  156 + DeviceInfo deviceInfo = new DeviceInfo(
  157 + respDTO.getDeviceId(),
  158 + respDTO.getProductKey(),
  159 + respDTO.getDeviceName(),
  160 + respDTO.getDeviceKey(),
  161 + respDTO.getTenantId());
  162 +
  163 + // 缓存设备信息
  164 + cacheDeviceInfo(deviceInfo, cacheKey);
  165 +
  166 + log.info("[loadDeviceInfoFromApi][设备信息获取成功并已缓存][deviceInfo: {}]", deviceInfo);
  167 + return deviceInfo;
  168 +
  169 + } catch (Exception e) {
  170 + log.error("[loadDeviceInfoFromApi][远程API调用异常][productKey: {}][deviceName: {}]",
  171 + productKey, deviceName, e);
  172 + return null;
  173 + }
  174 + } finally {
  175 + lock.unlock();
  176 + // 清理锁对象,避免内存泄漏
  177 + if (lockMap.size() > 1000) { // 简单的清理策略
  178 + lockMap.entrySet().removeIf(entry -> !entry.getValue().hasQueuedThreads());
  179 + }
  180 + }
  181 + }
  182 +
  183 + /**
  184 + * 从远程 API 根据 deviceId 加载设备信息
  185 + *
  186 + * @param deviceId 设备编号
  187 + * @return 设备信息
  188 + */
  189 + private DeviceInfo loadDeviceInfoByIdFromApi(Long deviceId) {
  190 + String lockKey = "deviceId:" + deviceId;
  191 + ReentrantLock lock = lockMap.computeIfAbsent(lockKey, k -> new ReentrantLock());
  192 + lock.lock();
  193 + try {
  194 + // 双重检查
  195 + DeviceInfo deviceInfo = deviceIdCache.get(deviceId);
  196 + if (deviceInfo != null) {
  197 + return deviceInfo;
  198 + }
  199 +
  200 + log.info("[loadDeviceInfoByIdFromApi][从远程API获取设备信息][deviceId: {}]", deviceId);
  201 +
  202 + try {
  203 + // TODO: 这里需要添加根据 deviceId 获取设备信息的 API
  204 + // 暂时返回 null,等待 API 完善
  205 + log.warn("[loadDeviceInfoByIdFromApi][根据deviceId获取设备信息的API尚未实现][deviceId: {}]", deviceId);
  206 + return null;
  207 +
  208 + } catch (Exception e) {
  209 + log.error("[loadDeviceInfoByIdFromApi][远程API调用异常][deviceId: {}]", deviceId, e);
  210 + return null;
  211 + }
  212 + } finally {
  213 + lock.unlock();
  214 + }
  215 + }
  216 +
  217 + /**
  218 + * 缓存设备信息
  219 + *
  220 + * @param deviceInfo 设备信息
  221 + * @param cacheKey 缓存键
  222 + */
  223 + private void cacheDeviceInfo(DeviceInfo deviceInfo, String cacheKey) {
  224 + if (deviceInfo != null && deviceInfo.getDeviceId() != null) {
  225 + deviceIdCache.put(deviceInfo.getDeviceId(), deviceInfo);
  226 + deviceNameCache.put(cacheKey, deviceInfo.getDeviceId());
  227 + }
  228 + }
  229 +
  230 + /**
  231 + * 构建设备名称缓存键
  232 + *
  233 + * @param productKey 产品标识
  234 + * @param deviceName 设备名称
  235 + * @return 缓存键
  236 + */
  237 + private String buildDeviceNameCacheKey(String productKey, String deviceName) {
  238 + return productKey + ":" + deviceName;
  239 + }
  240 +
  241 +}
0 \ No newline at end of file 242 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/IotDeviceClientServiceImpl.java
1 package cn.iocoder.yudao.module.iot.gateway.service.device; 1 package cn.iocoder.yudao.module.iot.gateway.service.device;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.core.bean.BeanUtil;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 6 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 7 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
  8 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoReqDTO;
  9 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceInfoRespDTO;
7 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 10 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
8 import jakarta.annotation.PostConstruct; 11 import jakarta.annotation.PostConstruct;
9 import jakarta.annotation.Resource; 12 import jakarta.annotation.Resource;
10 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
11 import org.springframework.boot.web.client.RestTemplateBuilder; 14 import org.springframework.boot.web.client.RestTemplateBuilder;
  15 +import org.springframework.core.ParameterizedTypeReference;
  16 +import org.springframework.http.HttpEntity;
  17 +import org.springframework.http.HttpMethod;
  18 +import org.springframework.http.ResponseEntity;
12 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
13 import org.springframework.web.client.RestTemplate; 20 import org.springframework.web.client.RestTemplate;
14 21
  22 +import java.util.LinkedHashMap;
  23 +
15 import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; 24 import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
16 25
17 /** 26 /**
@@ -43,6 +52,11 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi { @@ -43,6 +52,11 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi {
43 return doPost("/auth", authReqDTO); 52 return doPost("/auth", authReqDTO);
44 } 53 }
45 54
  55 + @Override
  56 + public CommonResult<IotDeviceInfoRespDTO> getDeviceInfo(IotDeviceInfoReqDTO infoReqDTO) {
  57 + return doPostForDeviceInfo("/info", infoReqDTO);
  58 + }
  59 +
46 @SuppressWarnings("unchecked") 60 @SuppressWarnings("unchecked")
47 private <T> CommonResult<Boolean> doPost(String url, T requestBody) { 61 private <T> CommonResult<Boolean> doPost(String url, T requestBody) {
48 try { 62 try {
@@ -57,4 +71,37 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi { @@ -57,4 +71,37 @@ public class IotDeviceClientServiceImpl implements IotDeviceCommonApi {
57 } 71 }
58 } 72 }
59 73
  74 + @SuppressWarnings("unchecked")
  75 + private <T> CommonResult<IotDeviceInfoRespDTO> doPostForDeviceInfo(String url, T requestBody) {
  76 + try {
  77 + // 使用 ParameterizedTypeReference 来处理泛型类型
  78 + ParameterizedTypeReference<CommonResult<LinkedHashMap<String, Object>>> typeRef = new ParameterizedTypeReference<CommonResult<LinkedHashMap<String, Object>>>() {
  79 + };
  80 +
  81 + HttpEntity<T> requestEntity = new HttpEntity<>(requestBody);
  82 + ResponseEntity<CommonResult<LinkedHashMap<String, Object>>> response = restTemplate.exchange(url,
  83 + HttpMethod.POST, requestEntity, typeRef);
  84 +
  85 + CommonResult<LinkedHashMap<String, Object>> rawResult = response.getBody();
  86 + log.info("[doPostForDeviceInfo][url({}) requestBody({}) rawResult({})]", url, requestBody, rawResult);
  87 + Assert.notNull(rawResult, "请求结果不能为空");
  88 +
  89 + // 手动转换数据类型
  90 + CommonResult<IotDeviceInfoRespDTO> result = new CommonResult<>();
  91 + result.setCode(rawResult.getCode());
  92 + result.setMsg(rawResult.getMsg());
  93 +
  94 + if (rawResult.isSuccess() && rawResult.getData() != null) {
  95 + // 将 LinkedHashMap 转换为 IotDeviceInfoRespDTO
  96 + IotDeviceInfoRespDTO deviceInfo = BeanUtil.toBean(rawResult.getData(), IotDeviceInfoRespDTO.class);
  97 + result.setData(deviceInfo);
  98 + }
  99 +
  100 + return result;
  101 + } catch (Exception e) {
  102 + log.error("[doPostForDeviceInfo][url({}) requestBody({}) 发生异常]", url, requestBody, e);
  103 + return CommonResult.error(INTERNAL_SERVER_ERROR);
  104 + }
  105 + }
  106 +
60 } 107 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageService.java
@@ -12,7 +12,7 @@ public interface IotDeviceMessageService { @@ -12,7 +12,7 @@ public interface IotDeviceMessageService {
12 /** 12 /**
13 * 编码消息 13 * 编码消息
14 * 14 *
15 - * @param message 消息 15 + * @param message 消息
16 * @param productKey 产品 Key 16 * @param productKey 产品 Key
17 * @param deviceName 设备名称 17 * @param deviceName 设备名称
18 * @return 编码后的消息内容 18 * @return 编码后的消息内容
@@ -23,10 +23,10 @@ public interface IotDeviceMessageService { @@ -23,10 +23,10 @@ public interface IotDeviceMessageService {
23 /** 23 /**
24 * 解码消息 24 * 解码消息
25 * 25 *
26 - * @param bytes 消息内容 26 + * @param bytes 消息内容
27 * @param productKey 产品 Key 27 * @param productKey 产品 Key
28 * @param deviceName 设备名称 28 * @param deviceName 设备名称
29 - * @param serverId 设备连接的 serverId 29 + * @param serverId 设备连接的 serverId
30 * @return 解码后的消息内容 30 * @return 解码后的消息内容
31 */ 31 */
32 IotDeviceMessage decodeDeviceMessage(byte[] bytes, 32 IotDeviceMessage decodeDeviceMessage(byte[] bytes,
@@ -35,8 +35,21 @@ public interface IotDeviceMessageService { @@ -35,8 +35,21 @@ public interface IotDeviceMessageService {
35 /** 35 /**
36 * 构建【设备上线】消息 36 * 构建【设备上线】消息
37 * 37 *
  38 + * @param productKey 产品 Key
  39 + * @param deviceName 设备名称
  40 + * @param serverId 设备连接的 serverId
38 * @return 消息 41 * @return 消息
39 */ 42 */
40 IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId); 43 IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId);
41 44
  45 + /**
  46 + * 构建【设备下线】消息
  47 + *
  48 + * @param productKey 产品 Key
  49 + * @param deviceName 设备名称
  50 + * @param serverId 设备连接的 serverId
  51 + * @return 消息
  52 + */
  53 + IotDeviceMessage buildDeviceMessageOfStateOffline(String productKey, String deviceName, String serverId);
  54 +
42 } 55 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java
@@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; @@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 7 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
8 import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; 8 import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec;
  9 +import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceCacheService;
  10 +import jakarta.annotation.Resource;
  11 +import lombok.extern.slf4j.Slf4j;
9 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
10 13
11 import java.time.LocalDateTime; 14 import java.time.LocalDateTime;
@@ -18,6 +21,7 @@ import java.util.Map; @@ -18,6 +21,7 @@ import java.util.Map;
18 * @author 芋道源码 21 * @author 芋道源码
19 */ 22 */
20 @Service 23 @Service
  24 +@Slf4j
21 public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { 25 public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
22 26
23 /** 27 /**
@@ -25,6 +29,9 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -25,6 +29,9 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
25 */ 29 */
26 private final Map<String, IotAlinkDeviceMessageCodec> codes; 30 private final Map<String, IotAlinkDeviceMessageCodec> codes;
27 31
  32 + @Resource
  33 + private IotDeviceCacheService deviceCacheService;
  34 +
28 public IotDeviceMessageServiceImpl(List<IotAlinkDeviceMessageCodec> codes) { 35 public IotDeviceMessageServiceImpl(List<IotAlinkDeviceMessageCodec> codes) {
29 this.codes = CollectionUtils.convertMap(codes, IotAlinkDeviceMessageCodec::type); 36 this.codes = CollectionUtils.convertMap(codes, IotAlinkDeviceMessageCodec::type);
30 } 37 }
@@ -32,51 +39,106 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { @@ -32,51 +39,106 @@ public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
32 @Override 39 @Override
33 public byte[] encodeDeviceMessage(IotDeviceMessage message, 40 public byte[] encodeDeviceMessage(IotDeviceMessage message,
34 String productKey, String deviceName) { 41 String productKey, String deviceName) {
35 - // TODO @芋艿:获取设备信息  
36 - String codecType = "alink";  
37 - return codes.get(codecType).encode(message); 42 + // 获取设备信息以确定编解码类型
  43 + IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName);
  44 + if (deviceInfo == null) {
  45 + log.warn("[encodeDeviceMessage][设备信息不存在][productKey: {}][deviceName: {}]",
  46 + productKey, deviceName);
  47 + return null;
  48 + }
  49 +
  50 + String codecType = "alink"; // 默认使用 alink 编解码器
  51 + IotAlinkDeviceMessageCodec codec = codes.get(codecType);
  52 + if (codec == null) {
  53 + log.error("[encodeDeviceMessage][编解码器不存在][codecType: {}]", codecType);
  54 + return null;
  55 + }
  56 +
  57 + return codec.encode(message);
38 } 58 }
39 59
40 @Override 60 @Override
41 public IotDeviceMessage decodeDeviceMessage(byte[] bytes, 61 public IotDeviceMessage decodeDeviceMessage(byte[] bytes,
42 String productKey, String deviceName, String serverId) { 62 String productKey, String deviceName, String serverId) {
43 - // TODO @芋艿:获取设备信息  
44 - String codecType = "alink";  
45 - IotDeviceMessage message = codes.get(codecType).decode(bytes); 63 + // 获取设备信息
  64 + IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName);
  65 + if (deviceInfo == null) {
  66 + log.warn("[decodeDeviceMessage][设备信息不存在][productKey: {}][deviceName: {}]",
  67 + productKey, deviceName);
  68 + return null;
  69 + }
  70 +
  71 + String codecType = "alink"; // 默认使用 alink 编解码器
  72 + IotAlinkDeviceMessageCodec codec = codes.get(codecType);
  73 + if (codec == null) {
  74 + log.error("[decodeDeviceMessage][编解码器不存在][codecType: {}]", codecType);
  75 + return null;
  76 + }
  77 +
  78 + IotDeviceMessage message = codec.decode(bytes);
  79 + if (message == null) {
  80 + log.warn("[decodeDeviceMessage][消息解码失败][productKey: {}][deviceName: {}]",
  81 + productKey, deviceName);
  82 + return null;
  83 + }
  84 +
46 // 补充后端字段 85 // 补充后端字段
47 - Long deviceId = 25L;  
48 - Long tenantId = 1L;  
49 - appendDeviceMessage(message, deviceId, tenantId, serverId);  
50 - return message; 86 + return appendDeviceMessage(message, deviceInfo, serverId);
51 } 87 }
52 88
53 @Override 89 @Override
54 public IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId) { 90 public IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId) {
  91 + // 获取设备信息
  92 + IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName);
  93 + if (deviceInfo == null) {
  94 + log.warn("[buildDeviceMessageOfStateOnline][设备信息不存在][productKey: {}][deviceName: {}]",
  95 + productKey, deviceName);
  96 + return null;
  97 + }
  98 +
55 IotDeviceMessage message = IotDeviceMessage.of(null, 99 IotDeviceMessage message = IotDeviceMessage.of(null,
56 IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(), null); 100 IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(), null);
57 - // 补充后端字段  
58 - Long deviceId = 25L;  
59 - Long tenantId = 1L;  
60 - return appendDeviceMessage(message, deviceId, tenantId, serverId); 101 +
  102 + return appendDeviceMessage(message, deviceInfo, serverId);
  103 + }
  104 +
  105 + @Override
  106 + public IotDeviceMessage buildDeviceMessageOfStateOffline(String productKey, String deviceName, String serverId) {
  107 + // 获取设备信息
  108 + IotDeviceCacheService.DeviceInfo deviceInfo = deviceCacheService.getDeviceInfo(productKey, deviceName);
  109 + if (deviceInfo == null) {
  110 + log.warn("[buildDeviceMessageOfStateOffline][设备信息不存在][productKey: {}][deviceName: {}]",
  111 + productKey, deviceName);
  112 + return null;
  113 + }
  114 +
  115 + IotDeviceMessage message = IotDeviceMessage.of(null,
  116 + IotDeviceMessageMethodEnum.STATE_OFFLINE.getMethod(), null);
  117 +
  118 + return appendDeviceMessage(message, deviceInfo, serverId);
61 } 119 }
62 120
63 /** 121 /**
64 * 补充消息的后端字段 122 * 补充消息的后端字段
65 * 123 *
66 - * @param message 消息  
67 - * @param deviceId 设备编号  
68 - * @param tenantId 租户编号  
69 - * @param serverId 设备连接的 serverId 124 + * @param message 消息
  125 + * @param deviceInfo 设备信息
  126 + * @param serverId 设备连接的 serverId
70 * @return 消息 127 * @return 消息
71 */ 128 */
72 private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message, 129 private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message,
73 - Long deviceId, Long tenantId, String serverId) { 130 + IotDeviceCacheService.DeviceInfo deviceInfo, String serverId) {
74 message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now()) 131 message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now())
75 - .setDeviceId(deviceId).setTenantId(tenantId).setServerId(serverId); 132 + .setDeviceId(deviceInfo.getDeviceId()).setTenantId(deviceInfo.getTenantId()).setServerId(serverId);
  133 +
76 // 特殊:如果设备没有指定 requestId,则使用 messageId 134 // 特殊:如果设备没有指定 requestId,则使用 messageId
77 if (StrUtil.isEmpty(message.getRequestId())) { 135 if (StrUtil.isEmpty(message.getRequestId())) {
78 message.setRequestId(message.getId()); 136 message.setRequestId(message.getId());
79 } 137 }
  138 +
  139 + log.debug("[appendDeviceMessage][消息字段补充完成][deviceId: {}][tenantId: {}]",
  140 + deviceInfo.getDeviceId(), deviceInfo.getTenantId());
  141 +
80 return message; 142 return message;
81 } 143 }
82 144