|
@@ -0,0 +1,123 @@
|
|
|
+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<String, String> 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));
|
|
|
+ }
|
|
|
+}
|