2026-06-24 f4bd1f3c89d906131495a0aca5aaf82966378510
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package cn.iocoder.yudao.module.iot.mq.consumer.device;
 
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;
import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService;
import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
 
import java.time.LocalDateTime;
import java.util.Objects;
 
/**
 * 针对 {@link IotDeviceMessage} 的业务处理器:调用 method 对应的逻辑。例如说:
 * 1. {@link IotDeviceMessageMethodEnum#PROPERTY_POST} 属性上报时,记录设备属性
 *
 * @author alwayssuper
 */
@Component
@Slf4j
public class IotDeviceMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
 
    @Resource
    private IotDeviceService deviceService;
    @Resource
    private IotDevicePropertyService devicePropertyService;
    @Resource
    private IotDeviceMessageService deviceMessageService;
 
    @Resource
    private IotMessageBus messageBus;
 
    @PostConstruct
    public void init() {
        messageBus.register(this);
    }
 
    @Override
    public String getTopic() {
        return IotDeviceMessage.MESSAGE_BUS_DEVICE_MESSAGE_TOPIC;
    }
 
    @Override
    public String getGroup() {
        return "iot_device_message_consumer";
    }
 
    @Override
    public void onMessage(IotDeviceMessage message) {
        if (!IotDeviceMessageUtils.isUpstreamMessage(message)) {
            log.error("[onMessage][message({}) 非上行消息,不进行处理]", message);
            return;
        }
 
        TenantUtils.execute(message.getTenantId(), () -> {
            // 1.1 更新设备的最后时间
            IotDeviceDO device = deviceService.validateDeviceExistsFromCache(message.getDeviceId());
            devicePropertyService.updateDeviceReportTimeAsync(device.getId(), LocalDateTime.now());
            // 1.2 更新设备的连接 server
            devicePropertyService.updateDeviceServerIdAsync(device.getId(), message.getServerId());
 
            // 2. 未上线的设备,强制上线
            forceDeviceOnline(message, device);
 
            // 3. 核心:处理消息
            deviceMessageService.handleUpstreamDeviceMessage(message, device);
        });
    }
 
    private void forceDeviceOnline(IotDeviceMessage message, IotDeviceDO device) {
        // 已经在线,无需处理
        if (ObjectUtil.equal(device.getState(), IotDeviceStateEnum.ONLINE.getState())) {
            return;
        }
        // 如果是 STATE 相关的消息,无需处理,不然就重复处理状态了
        if (Objects.equals(message.getMethod(), IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod())) {
            return;
        }
 
        // 特殊:设备非在线时,主动标记设备为在线
        // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志、规则引擎等等
        try {
            deviceMessageService.sendDeviceMessage(IotDeviceMessage.buildStateUpdateOnline().setDeviceId(device.getId()));
        } catch (Exception e) {
            // 注意:即使执行失败,也不影响主流程
            log.error("[forceDeviceOnline][message({}) device({}) 强制设备上线失败]", message, device, e);
        }
    }
 
}