# 平台 Redis 使用规范

## 核心原则

- 优先使用 `DawnRedis`（`com.sie.iot.common.redis.DawnRedis`）
- 仅当 `DawnRedis` 无法满足需求时（如 Pipeline、Lua 脚本、Stream），才使用 `RedisTemplate`
- 通过 `DawnRedis.getRedisTemplate()` 获取底层 `RedisTemplate`，不要单独注入

## 注入方式

```java
@Autowired
private DawnRedis dawnRedis;
```

## Key 规范

- 格式：`模块:业务:标识`，如 `user:session:10001`
- 推荐通过 `ICacheKey` 接口定义枚举统一管理 key 前缀和过期时间：

```java
public enum UserCacheKey implements ICacheKey {
    USER_INFO("user:info:", Duration.ofMinutes(30)),
    USER_TOKEN("user:token:", Duration.ofHours(2));

    private final String prefix;
    private final Duration expire;

    @Override
    public String getPrefix() { return prefix; }
    @Override
    public Duration getExpire() { return expire; }
}

// 使用
CacheKey cacheKey = UserCacheKey.USER_INFO.getKey(userId);
dawnRedis.set(cacheKey, userInfo);
User user = dawnRedis.get(cacheKey);
```

- 禁止使用无过期时间的 key（除非明确需要永久缓存）
- 禁止使用 `keys()` 在生产环境大范围匹配，改用 `scanAll()`（已内置 scan 实现）

## 常用 API 速查

| 场景 | 方法 |
|------|------|
| 字符串读写 | `set(key, value)` / `setEx(key, value, duration)` / `get(key)` |
| 带 CacheKey 读写 | `set(cacheKey, value)` / `get(cacheKey)` |
| 缓存加载（防穿透） | `get(key, () -> loader)` / `get(cacheKey, () -> loader)` |
| 条件设置 | `setNx(key, value)` / `setNx(key, value, timeout, unit)` |
| 计数器 | `incr(key)` / `incrBy(key, n)` / `decr(key)` / `decrBy(key, n)` |
| 过期管理 | `expire(key, seconds)` / `expire(key, duration)` / `ttl(key)` |
| 删除 | `del(key)` / `del(cacheKey)` / `del(key1, key2, ...)` |
| 存在性 | `exists(key)` |
| Hash | `hSet` / `hGet` / `hMset` / `hGetAll` / `hDel` / `hExists` / `hKeys` / `hIncrBy` |
| List | `lPush` / `lPushRight` / `lPop` / `rPop` / `lRange` / `lLen` / `lTrim` |
| Set | `sAdd` / `sMembers` / `sIsMember` / `sRem` / `sInter` / `sUnion` / `sDiff` |
| ZSet | `zAdd` / `zRange` / `zRevrange` / `zRangeByScore` / `zRank` / `zRem` / `zScore` |
| 查询任意类型 | `query(key)` 返回 `RedisResult`（自动识别类型） |

## 禁止事项

1. 禁止直接 `new DawnRedis()`，必须通过 Spring 注入
2. 禁止存储大对象（单个 value > 1MB）
3. 禁止在循环中逐个操作，批量场景用 `mSet` / `mGet` 或 Pipeline
4. 禁止使用 `redisTemplate.keys()`，用 `dawnRedis.keys()` 或 `scanAll()`（内部已用 scan 实现）
5. 禁止存储敏感明文数据（密码、密钥等）

## 降级到 RedisTemplate 的场景

以下场景 `DawnRedis` 未封装，需通过 `dawnRedis.getRedisTemplate()` 操作：

- Pipeline 批量操作
- Lua 脚本执行（`execute(RedisScript, keys, args)`）
- Redis Stream
- Pub/Sub
- 事务（`multi/exec`）
