Files
springboot-template/src/main/java/com/aisi/template/utils/RedisCache.java
shenjianZ 40c85c3c1f feat: 实现完整的 RBAC 权限管理系统与基础设施增强
在初始认证基础上,新增完整的 RBAC 权限模型(角色、权限、菜单三级管理),
  集成审计日志、接口限流、登录失败锁定、Refresh Token 机制、Redis 分布式缓存与锁、
  RocketMQ 消息队列,并引入 Flyway 数据库版本管理,同时补充项目文档与使用示例
2026-04-10 10:58:22 +08:00

763 lines
21 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.aisi.template.utils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Redis 缓存工具类
* 提供各种场景下的缓存操作方法
*
* 使用场景:
* 1. 对象缓存:缓存实体对象,减少数据库查询
* 2. 列表缓存:缓存列表数据
* 3. 集合缓存:缓存去重数据
* 4. 哈希缓存:缓存对象字段
* 5. 计数器:文章阅读数、点赞数等
*
* @author Claude
* @since 2024-04-09
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class RedisCache {
/**
* Redis 模板
*/
private final RedisTemplate<String, Object> redisTemplate;
// ==================== 1. String 类型操作 ====================
/**
* 设置缓存(永不过期)
* 注意:生产环境建议设置过期时间,防止内存溢出
*
* @param key 键
* @param value 值
*/
public void set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
log.debug("设置缓存成功 - key: {}", key);
} catch (Exception e) {
log.error("设置缓存失败 - key: {}", key, e);
}
}
/**
* 设置缓存(指定过期时间)
* 步骤:
* 1. 将值序列化后存入 Redis
* 2. 设置过期时间
*
* @param key 键
* @param value 值
* @param timeout 过期时间
* @param timeUnit 时间单位
*/
public void set(String key, Object value, long timeout, TimeUnit timeUnit) {
try {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
log.debug("设置缓存成功 - key: {}, timeout: {} {}", key, timeout, timeUnit);
} catch (Exception e) {
log.error("设置缓存失败 - key: {}", key, e);
}
}
/**
* 设置缓存(使用 Duration
*
* @param key 键
* @param value 值
* @param duration 过期时间
*/
public void set(String key, Object value, Duration duration) {
try {
redisTemplate.opsForValue().set(key, value, duration);
log.debug("设置缓存成功 - key: {}, duration: {}", key, duration);
} catch (Exception e) {
log.error("设置缓存失败 - key: {}", key, e);
}
}
/**
* 获取缓存
* 步骤:
* 1. 从 Redis 获取值
* 2. 反序列化为对象
*
* @param key 键
* @return 值,不存在返回 null
*/
public Object get(String key) {
try {
Object value = redisTemplate.opsForValue().get(key);
log.debug("获取缓存 - key: {}, found: {}", key, value != null);
return value;
} catch (Exception e) {
log.error("获取缓存失败 - key: {}", key, e);
return null;
}
}
/**
* 获取缓存(指定类型)
*
* @param key 键
* @param type 返回值类型
* @return 值
*/
@SuppressWarnings("unchecked")
public <T> T get(String key, Class<T> type) {
try {
Object value = redisTemplate.opsForValue().get(key);
if (value != null && type.isInstance(value)) {
return (T) value;
}
return null;
} catch (Exception e) {
log.error("获取缓存失败 - key: {}", key, e);
return null;
}
}
/**
* 删除缓存
*
* @param key 键
* @return 是否删除成功
*/
public boolean delete(String key) {
try {
Boolean result = redisTemplate.delete(key);
log.debug("删除缓存 - key: {}, result: {}", key, result);
return Boolean.TRUE.equals(result);
} catch (Exception e) {
log.error("删除缓存失败 - key: {}", key, e);
return false;
}
}
/**
* 批量删除缓存
* 步骤:
* 1. 遍历所有键
* 2. 一次性删除
*
* @param keys 键集合
* @return 删除的数量
*/
public long delete(Collection<String> keys) {
try {
Long count = redisTemplate.delete(keys);
log.debug("批量删除缓存 - count: {}", count);
return count != null ? count : 0;
} catch (Exception e) {
log.error("批量删除缓存失败", e);
return 0;
}
}
/**
* 判断键是否存在
*
* @param key 键
* @return 是否存在
*/
public boolean hasKey(String key) {
try {
Boolean result = redisTemplate.hasKey(key);
return Boolean.TRUE.equals(result);
} catch (Exception e) {
log.error("判断键是否存在失败 - key: {}", key, e);
return false;
}
}
/**
* 设置过期时间
*
* @param key 键
* @param timeout 过期时间
* @param unit 时间单位
* @return 是否设置成功
*/
public boolean expire(String key, long timeout, TimeUnit unit) {
try {
Boolean result = redisTemplate.expire(key, timeout, unit);
return Boolean.TRUE.equals(result);
} catch (Exception e) {
log.error("设置过期时间失败 - key: {}", key, e);
return false;
}
}
/**
* 获取过期时间
*
* @param key 键
* @param unit 时间单位
* @return 剩余过期时间,-1 表示永不过期,-2 表示键不存在
*/
public long getExpire(String key, TimeUnit unit) {
try {
Long expire = redisTemplate.getExpire(key, unit);
return expire != null ? expire : -2;
} catch (Exception e) {
log.error("获取过期时间失败 - key: {}", key, e);
return -2;
}
}
/**
* 自增操作(原子性)
* 使用场景:计数器、点赞数、阅读数等
* 步骤:
* 1. 原子性自增
* 2. 返回自增后的值
*
* @param key 键
* @param delta 增量
* @return 自增后的值
*/
public long increment(String key, long delta) {
try {
Long result = redisTemplate.opsForValue().increment(key, delta);
log.debug("自增操作 - key: {}, delta: {}, result: {}", key, delta, result);
return result != null ? result : 0;
} catch (Exception e) {
log.error("自增操作失败 - key: {}", key, e);
return 0;
}
}
/**
* 自减操作(原子性)
*
* @param key 键
* @param delta 减量
* @return 自减后的值
*/
public long decrement(String key, long delta) {
try {
Long result = redisTemplate.opsForValue().decrement(key, delta);
log.debug("自减操作 - key: {}, delta: {}, result: {}", key, delta, result);
return result != null ? result : 0;
} catch (Exception e) {
log.error("自减操作失败 - key: {}", key, e);
return 0;
}
}
// ==================== 2. Hash 类型操作 ====================
/**
* 设置哈希字段
* 使用场景:缓存对象的单个字段
* 步骤:
* 1. 将字段和值存入哈希表
* 2. 适合部分更新对象字段
*
* @param key 键
* @param field 字段
* @param value 值
*/
public void hSet(String key, String field, Object value) {
try {
redisTemplate.opsForHash().put(key, field, value);
log.debug("设置哈希字段 - key: {}, field: {}", key, field);
} catch (Exception e) {
log.error("设置哈希字段失败 - key: {}, field: {}", key, field, e);
}
}
/**
* 获取哈希字段
*
* @param key 键
* @param field 字段
* @return 值
*/
public Object hGet(String key, String field) {
try {
return redisTemplate.opsForHash().get(key, field);
} catch (Exception e) {
log.error("获取哈希字段失败 - key: {}, field: {}", key, field, e);
return null;
}
}
/**
* 批量设置哈希字段
*
* @param key 键
* @param map 字段-值映射
*/
public void hSetAll(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
log.debug("批量设置哈希字段 - key: {}", key);
} catch (Exception e) {
log.error("批量设置哈希字段失败 - key: {}", key, e);
}
}
/**
* 获取所有哈希字段
*
* @param key 键
* @return 字段-值映射
*/
public Map<Object, Object> hGetAll(String key) {
try {
return redisTemplate.opsForHash().entries(key);
} catch (Exception e) {
log.error("获取所有哈希字段失败 - key: {}", key, e);
return Map.of();
}
}
/**
* 删除哈希字段
*
* @param key 键
* @param fields 字段集合
* @return 删除的数量
*/
public long hDelete(String key, Object... fields) {
try {
return redisTemplate.opsForHash().delete(key, fields);
} catch (Exception e) {
log.error("删除哈希字段失败 - key: {}", key, e);
return 0;
}
}
/**
* 判断哈希字段是否存在
*
* @param key 键
* @param field 字段
* @return 是否存在
*/
public boolean hExists(String key, String field) {
try {
return redisTemplate.opsForHash().hasKey(key, field);
} catch (Exception e) {
log.error("判断哈希字段是否存在失败 - key: {}, field: {}", key, field, e);
return false;
}
}
/**
* 哈希字段自增
*
* @param key 键
* @param field 字段
* @param delta 增量
* @return 自增后的值
*/
public long hIncrement(String key, String field, long delta) {
try {
return redisTemplate.opsForHash().increment(key, field, delta);
} catch (Exception e) {
log.error("哈希字段自增失败 - key: {}, field: {}", key, field, e);
return 0;
}
}
// ==================== 3. List 类型操作 ====================
/**
* 从左侧推入列表
* 使用场景:消息队列、最新消息列表
* 步骤:
* 1. 将元素推入列表左侧
* 2. 返回当前列表长度
*
* @param key 键
* @param value 值
* @return 推入后的列表长度
*/
public long lLeftPush(String key, Object value) {
try {
Long size = redisTemplate.opsForList().leftPush(key, value);
log.debug("从左侧推入列表 - key: {}, size: {}", key, size);
return size != null ? size : 0;
} catch (Exception e) {
log.error("从左侧推入列表失败 - key: {}", key, e);
return 0;
}
}
/**
* 从右侧推入列表
*
* @param key 键
* @param value 值
* @return 推入后的列表长度
*/
public long lRightPush(String key, Object value) {
try {
Long size = redisTemplate.opsForList().rightPush(key, value);
log.debug("从右侧推入列表 - key: {}, size: {}", key, size);
return size != null ? size : 0;
} catch (Exception e) {
log.error("从右侧推入列表失败 - key: {}", key, e);
return 0;
}
}
/**
* 从左侧弹出列表元素
*
* @param key 键
* @return 弹出的元素
*/
public Object lLeftPop(String key) {
try {
return redisTemplate.opsForList().leftPop(key);
} catch (Exception e) {
log.error("从左侧弹出列表元素失败 - key: {}", key, e);
return null;
}
}
/**
* 从右侧弹出列表元素
*
* @param key 键
* @return 弹出的元素
*/
public Object lRightPop(String key) {
try {
return redisTemplate.opsForList().rightPop(key);
} catch (Exception e) {
log.error("从右侧弹出列表元素失败 - key: {}", key, e);
return null;
}
}
/**
* 获取列表范围
* 使用场景:分页查询列表数据
* 步骤:
* 1. 获取指定范围的元素
* 2. 支持负索引(-1 表示最后一个元素)
*
* @param key 键
* @param start 开始索引
* @param end 结束索引
* @return 元素列表
*/
public List<Object> lRange(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
log.error("获取列表范围失败 - key: {}", key, e);
return List.of();
}
}
/**
* 获取列表长度
*
* @param key 键
* @return 列表长度
*/
public long lSize(String key) {
try {
Long size = redisTemplate.opsForList().size(key);
return size != null ? size : 0;
} catch (Exception e) {
log.error("获取列表长度失败 - key: {}", key, e);
return 0;
}
}
/**
* 移除列表元素
*
* @param key 键
* @param count 移除数量(>0 从左往右,<0 从右往左,=0 全部)
* @param value 要移除的值
* @return 实际移除的数量
*/
public long lRemove(String key, long count, Object value) {
try {
Long removed = redisTemplate.opsForList().remove(key, count, value);
return removed != null ? removed : 0;
} catch (Exception e) {
log.error("移除列表元素失败 - key: {}", key, e);
return 0;
}
}
// ==================== 4. Set 类型操作 ====================
/**
* 添加到集合
* 使用场景:标签系统、共同好友、去重
* 步骤:
* 1. 添加元素到集合
* 2. 自动去重
*
* @param key 键
* @param values 值集合
* @return 添加的元素数量(不包含已存在的)
*/
public long sAdd(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
log.debug("添加到集合 - key: {}, count: {}", key, count);
return count != null ? count : 0;
} catch (Exception e) {
log.error("添加到集合失败 - key: {}", key, e);
return 0;
}
}
/**
* 获取集合所有元素
*
* @param key 键
* @return 元素集合
*/
public Set<Object> sMembers(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
log.error("获取集合元素失败 - key: {}", key, e);
return Set.of();
}
}
/**
* 判断元素是否在集合中
*
* @param key 键
* @param value 值
* @return 是否存在
*/
public boolean sIsMember(String key, Object value) {
try {
Boolean result = redisTemplate.opsForSet().isMember(key, value);
return Boolean.TRUE.equals(result);
} catch (Exception e) {
log.error("判断元素是否在集合中失败 - key: {}", key, e);
return false;
}
}
/**
* 获取集合大小
*
* @param key 键
* @return 集合大小
*/
public long sSize(String key) {
try {
Long size = redisTemplate.opsForSet().size(key);
return size != null ? size : 0;
} catch (Exception e) {
log.error("获取集合大小失败 - key: {}", key, e);
return 0;
}
}
/**
* 移除集合元素
*
* @param key 键
* @param values 要移除的值
* @return 实际移除的数量
*/
public long sRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count != null ? count : 0;
} catch (Exception e) {
log.error("移除集合元素失败 - key: {}", key, e);
return 0;
}
}
/**
* 集合交集
* 使用场景:共同好友、共同标签
* 步骤:
* 1. 获取多个集合的交集
* 2. 返回共有的元素
*
* @param key1 第一个集合键
* @param key2 第二个集合键
* @return 交集元素
*/
public Set<Object> sIntersect(String key1, String key2) {
try {
return redisTemplate.opsForSet().intersect(key1, key2);
} catch (Exception e) {
log.error("获取集合交集失败 - key1: {}, key2: {}", key1, key2, e);
return Set.of();
}
}
/**
* 集合并集
*
* @param key1 第一个集合键
* @param key2 第二个集合键
* @return 并集元素
*/
public Set<Object> sUnion(String key1, String key2) {
try {
return redisTemplate.opsForSet().union(key1, key2);
} catch (Exception e) {
log.error("获取集合并集失败 - key1: {}, key2: {}", key1, key2, e);
return Set.of();
}
}
// ==================== 5. ZSet 类型操作(有序集合)====================
/**
* 添加到有序集合
* 使用场景:排行榜、权重队列
* 步骤:
* 1. 添加元素及分数
* 2. 按分数自动排序
*
* @param key 键
* @param value 值
* @param score 分数
* @return 添加是否成功(新增返回 true更新返回 false
*/
public boolean zAdd(String key, Object value, double score) {
try {
Boolean result = redisTemplate.opsForZSet().add(key, value, score);
log.debug("添加到有序集合 - key: {}, value: {}, score: {}", key, value, score);
return Boolean.TRUE.equals(result);
} catch (Exception e) {
log.error("添加到有序集合失败 - key: {}", key, e);
return false;
}
}
/**
* 获取有序集合范围(按分数)
* 使用场景:排行榜分页查询
* 步骤:
* 1. 获取指定分数范围的元素
* 2. 按分数升序排列
*
* @param key 键
* @param min 最小分数
* @param max 最大分数
* @return 元素集合
*/
public Set<Object> zRangeByScore(String key, double min, double max) {
try {
return redisTemplate.opsForZSet().rangeByScore(key, min, max);
} catch (Exception e) {
log.error("获取有序集合范围失败 - key: {}", key, e);
return Set.of();
}
}
/**
* 获取有序集合排名(倒序)
* 使用场景:排行榜查询(分数高的排名靠前)
* 步骤:
* 1. 获取元素的排名从0开始
* 2. 按分数倒序排列
*
* @param key 键
* @param value 值
* @return 排名,不存在返回 null
*/
public Long zReverseRank(String key, Object value) {
try {
return redisTemplate.opsForZSet().reverseRank(key, value);
} catch (Exception e) {
log.error("获取有序集合排名失败 - key: {}", key, e);
return null;
}
}
/**
* 获取元素分数
*
* @param key 键
* @param value 值
* @return 分数
*/
public Double zScore(String key, Object value) {
try {
return redisTemplate.opsForZSet().score(key, value);
} catch (Exception e) {
log.error("获取元素分数失败 - key: {}", key, e);
return null;
}
}
/**
* 增加元素分数
*
* @param key 键
* @param value 值
* @param delta 增量
* @return 增加后的分数
*/
public Double zIncrementScore(String key, Object value, double delta) {
try {
return redisTemplate.opsForZSet().incrementScore(key, value, delta);
} catch (Exception e) {
log.error("增加元素分数失败 - key: {}", key, e);
return null;
}
}
/**
* 移除有序集合元素
*
* @param key 键
* @param values 要移除的值
* @return 实际移除的数量
*/
public long zRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForZSet().remove(key, values);
return count != null ? count : 0;
} catch (Exception e) {
log.error("移除有序集合元素失败 - key: {}", key, e);
return 0;
}
}
/**
* 获取有序集合大小
*
* @param key 键
* @return 集合大小
*/
public long zSize(String key) {
try {
Long size = redisTemplate.opsForZSet().size(key);
return size != null ? size : 0;
} catch (Exception e) {
log.error("获取有序集合大小失败 - key: {}", key, e);
return 0;
}
}
}