Commit 4ea6e08f99da94b52b2ca8fdd3569c08c8c45613
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<IotDevi | @@ -24,6 +24,9 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<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<IotDevi | @@ -45,24 +48,24 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<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<IotDevi | @@ -75,24 +78,32 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<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<IotDevi | @@ -102,12 +113,23 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<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<IotDevi | @@ -118,11 +140,18 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<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<IotDevi | @@ -133,11 +162,18 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<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<IotDevi | @@ -151,7 +187,7 @@ public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<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 |