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 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 get(String key, Class 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 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 map) { try { redisTemplate.opsForHash().putAll(key, map); log.debug("批量设置哈希字段 - key: {}", key); } catch (Exception e) { log.error("批量设置哈希字段失败 - key: {}", key, e); } } /** * 获取所有哈希字段 * * @param key 键 * @return 字段-值映射 */ public Map 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 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 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 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 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 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; } } }