2026-06-30 24681c81c09022f584a57006f2534b5f74723414
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package cn.iocoder.yudao.module.iot.service.rule.data.action;
 
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkRedisConfig;
import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotRedisDataStructureEnum;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.stream.ObjectRecord;
import org.springframework.data.redis.connection.stream.StreamRecords;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
 
import java.util.Map;
 
/**
 * Redis 的 {@link IotDataRuleAction} 实现类
 * 支持多种 Redis 数据结构:Stream、Hash、List、Set、ZSet、String
 *
 * @author HUIHUI
 */
@Component
@Slf4j
public class IotRedisRuleAction extends
        IotDataRuleCacheableAction<IotDataSinkRedisConfig, RedisTemplate<String, Object>> {
 
    @Override
    public Integer getType() {
        return IotDataSinkTypeEnum.REDIS.getType();
    }
 
    @Override
    public void execute(IotDeviceMessage message, IotDataSinkRedisConfig config) throws Exception {
        // 1. 获取 RedisTemplate
        RedisTemplate<String, Object> redisTemplate = getProducer(config);
 
        // 2. 根据数据结构类型执行不同的操作
        String messageJson = JsonUtils.toJsonString(message);
        IotRedisDataStructureEnum dataStructure = getDataStructureByType(config.getDataStructure());
        switch (dataStructure) {
            case STREAM:
                executeStream(redisTemplate, config, messageJson);
                break;
            case HASH:
                executeHash(redisTemplate, config, message, messageJson);
                break;
            case LIST:
                executeList(redisTemplate, config, messageJson);
                break;
            case SET:
                executeSet(redisTemplate, config, messageJson);
                break;
            case ZSET:
                executeZSet(redisTemplate, config, message, messageJson);
                break;
            case STRING:
                executeString(redisTemplate, config, messageJson);
                break;
            default:
                throw new IllegalArgumentException("不支持的 Redis 数据结构类型: " + dataStructure);
        }
 
        log.info("[execute][消息发送成功] dataStructure: {}, config: {}", dataStructure.getName(), config);
    }
 
    /**
     * 执行 Stream 操作
     */
    private void executeStream(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {
        ObjectRecord<String, ?> record = StreamRecords.newRecord()
                .ofObject(messageJson).withStreamKey(config.getTopic());
        redisTemplate.opsForStream().add(record);
    }
 
    /**
     * 执行 Hash 操作
     */
    private void executeHash(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config,
                             IotDeviceMessage message, String messageJson) {
        String hashField = StrUtil.isNotBlank(config.getHashField()) ?
                config.getHashField() : String.valueOf(message.getDeviceId());
        redisTemplate.opsForHash().put(config.getTopic(), hashField, messageJson);
    }
 
    /**
     * 执行 List 操作
     */
    private void executeList(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {
        redisTemplate.opsForList().rightPush(config.getTopic(), messageJson);
    }
 
    /**
     * 执行 Set 操作
     */
    private void executeSet(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {
        redisTemplate.opsForSet().add(config.getTopic(), messageJson);
    }
 
    /**
     * 执行 ZSet 操作
     */
    private void executeZSet(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config,
                             IotDeviceMessage message, String messageJson) {
        double score;
        if (StrUtil.isNotBlank(config.getScoreField())) {
            // 尝试从消息中获取分数字段
            try {
                Map<String, Object> messageMap = JsonUtils.parseObject(messageJson, Map.class);
                Object scoreValue = messageMap.get(config.getScoreField());
                score = scoreValue instanceof Number ? ((Number) scoreValue).doubleValue() : System.currentTimeMillis();
            } catch (Exception e) {
                score = System.currentTimeMillis();
            }
        } else {
            // 使用当前时间戳作为分数
            score = System.currentTimeMillis();
        }
        redisTemplate.opsForZSet().add(config.getTopic(), messageJson, score);
    }
 
    /**
     * 执行 String 操作
     */
    private void executeString(RedisTemplate<String, Object> redisTemplate, IotDataSinkRedisConfig config, String messageJson) {
        redisTemplate.opsForValue().set(config.getTopic(), messageJson);
    }
 
    @Override
    protected RedisTemplate<String, Object> initProducer(IotDataSinkRedisConfig config) {
        // 1.1 创建 Redisson 配置
        Config redissonConfig = new Config();
        SingleServerConfig serverConfig = redissonConfig.useSingleServer()
                .setAddress("redis://" + config.getHost() + ":" + config.getPort())
                .setDatabase(config.getDatabase());
        // 1.2 设置密码(如果有)
        if (StrUtil.isNotBlank(config.getPassword())) {
            serverConfig.setPassword(config.getPassword());
        }
 
        // 2.1 创建 RedisTemplate 并配置
        RedissonClient redisson = Redisson.create(redissonConfig);
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(new RedissonConnectionFactory(redisson));
        // 2.2 设置序列化器
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        template.setValueSerializer(RedisSerializer.json());
        template.setHashValueSerializer(RedisSerializer.json());
        template.afterPropertiesSet();
        return template;
    }
 
    @Override
    protected void closeProducer(RedisTemplate<String, Object> producer) throws Exception {
        RedisConnectionFactory factory = producer.getConnectionFactory();
        if (factory != null) {
            ((RedissonConnectionFactory) factory).destroy();
        }
    }
 
    /**
     * 根据类型值获取数据结构枚举
     */
    private IotRedisDataStructureEnum getDataStructureByType(Integer type) {
        for (IotRedisDataStructureEnum dataStructure : IotRedisDataStructureEnum.values()) {
            if (dataStructure.getType().equals(type)) {
                return dataStructure;
            }
        }
        throw new IllegalArgumentException("不支持的 Redis 数据结构类型: " + type);
    }
 
}