package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; import cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils; import io.vertx.mqtt.messages.MqttPublishMessage; import lombok.extern.slf4j.Slf4j; /** * IoT 网关 EMQX 上行消息处理器 * * @author 芋道源码 */ @Slf4j public class IotEmqxUpstreamHandler { private final IotDeviceMessageService deviceMessageService; private final String serverId; public IotEmqxUpstreamHandler(String serverId) { this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); this.serverId = serverId; } /** * 处理 MQTT 发布消息 */ public void handle(MqttPublishMessage mqttMessage) { log.debug("[handle][收到 MQTT 消息, topic: {}, payload: {}]", mqttMessage.topicName(), mqttMessage.payload()); String topic = mqttMessage.topicName(); byte[] payload = mqttMessage.payload().getBytes(); try { // 1. 解析主题,一次性获取所有信息 String[] topicParts = topic.split("/"); String productKey = ArrayUtil.get(topicParts, 2); String deviceName = ArrayUtil.get(topicParts, 3); if (topicParts.length < 4 || StrUtil.hasBlank(productKey, deviceName)) { log.warn("[handle][topic({}) 格式不正确,无法解析有效的 productKey 和 deviceName]", topic); return; } // 2.1 反序列化消息 IotDeviceMessage message = deviceMessageService.deserializeDeviceMessage(payload, productKey, deviceName); if (message == null) { log.warn("[handle][topic({}) payload({}) 消息解码失败]", topic, new String(payload)); return; } // 2.2 标准化回复消息的 method(MQTT 协议中,设备回复消息的 method 会携带 _reply 后缀) IotMqttTopicUtils.normalizeReplyMethod(message); // 3. 发送消息到队列 deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId); } catch (Exception e) { log.error("[handle][topic({}) payload({}) 处理异常]", topic, new String(payload), e); } } }