package com.yaoyicloud.config; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; /** * Redis 计数器工具类 */ @Component @RequiredArgsConstructor public class RelationCounterRedisUtil { // Redis 键前缀(避免与其他缓存键冲突) private static final String COUNTER_KEY_PREFIX = "counter:"; // Redis 模板(注入 String 类型的模板,计数以字符串形式存储) private final RedisTemplate redisTemplate; private static final long DEF_REPORT_TTL = 1 * 60 * 60 * 1000; /** * 生成 Redis 计数器的完整 key * @param relationId 业务关联 ID * @return 格式:relation:counter:{relationId} */ private String getCounterKey(Long relationId) { return COUNTER_KEY_PREFIX + relationId; } /** * 初始化计数器(若不存在则设置初始值为 0) * 注意:通常无需主动调用,increment 方法会自动处理不存在的键 * @param relationId 业务关联 ID */ public void initCounter(Long relationId) { String key = getCounterKey(relationId); // 仅当键不存在时设置初始值为 0(避免覆盖已有计数) redisTemplate.opsForValue().setIfAbsent(key, "1", DEF_REPORT_TTL, TimeUnit.MILLISECONDS); } /** * 计数器自增 1,并返回自增后的值 * @param relationId 业务关联 ID * @return 自增后的计数(例如:初始为 0,调用后返回 1) */ public int increment(Long relationId) { return increment(relationId, 1); } /** * 计数器自增指定步长,并返回自增后的值 * @param relationId 业务关联 ID * @param step 自增步长(正整数) * @return 自增后的计数 */ public int increment(Long relationId, int step) { if (step <= 0) { throw new IllegalArgumentException("自增步长必须为正整数"); } String key = getCounterKey(relationId); // Redis 的 INCRBY 命令:原子性自增,返回自增后的值 Long newValue = redisTemplate.opsForValue().increment(key, step); return newValue.intValue(); } /** * 获取当前计数 * @param relationId 业务关联 ID * @return 当前计数(若键不存在,返回 0) */ public Integer getCount(Long relationId) { String key = getCounterKey(relationId); String value = redisTemplate.opsForValue().get(key); if (value == null) { initCounter(relationId); return 1; } return Integer.parseInt(value); } /** * 重置计数器为 0 * @param relationId 业务关联 ID */ public void reset(Long relationId) { String key = getCounterKey(relationId); redisTemplate.opsForValue().set(key, "1", DEF_REPORT_TTL, TimeUnit.MILLISECONDS); } /** * 删除计数器(适用于业务结束后清理资源) * @param relationId 业务关联 ID * @return 是否删除成功(true:键存在且被删除;false:键不存在) */ public boolean delete(Long relationId) { String key = getCounterKey(relationId); return Boolean.TRUE.equals(redisTemplate.delete(key)); } /** * 设置计数器过期时间(避免长期占用 Redis 内存) * @param relationId 业务关联 ID * @param timeout 过期时间 * @param unit 时间单位 */ public void setExpire(Long relationId, long timeout, TimeUnit unit) { String key = getCounterKey(relationId); redisTemplate.expire(key, timeout, unit); } /** * 检查计数器是否存在 * @param relationId 业务关联 ID * @return true:存在;false:不存在 */ public boolean exists(Long relationId) { String key = getCounterKey(relationId); return Boolean.TRUE.equals(redisTemplate.hasKey(key)); } }