RelationCounterRedisUtil.java 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package com.yaoyicloud.config;
  2. import lombok.RequiredArgsConstructor;
  3. import org.springframework.data.redis.core.RedisTemplate;
  4. import org.springframework.stereotype.Component;
  5. import java.util.concurrent.TimeUnit;
  6. /**
  7. * Redis 计数器工具类
  8. */
  9. @Component
  10. @RequiredArgsConstructor
  11. public class RelationCounterRedisUtil {
  12. // Redis 键前缀(避免与其他缓存键冲突)
  13. private static final String COUNTER_KEY_PREFIX = "counter:";
  14. // Redis 模板(注入 String 类型的模板,计数以字符串形式存储)
  15. private final RedisTemplate<String, String> redisTemplate;
  16. private static final long DEF_REPORT_TTL = 1 * 60 * 60 * 1000;
  17. /**
  18. * 生成 Redis 计数器的完整 key
  19. * @param relationId 业务关联 ID
  20. * @return 格式:relation:counter:{relationId}
  21. */
  22. private String getCounterKey(Long relationId) {
  23. return COUNTER_KEY_PREFIX + relationId;
  24. }
  25. /**
  26. * 初始化计数器(若不存在则设置初始值为 0)
  27. * 注意:通常无需主动调用,increment 方法会自动处理不存在的键
  28. * @param relationId 业务关联 ID
  29. */
  30. public void initCounter(Long relationId) {
  31. String key = getCounterKey(relationId);
  32. // 仅当键不存在时设置初始值为 0(避免覆盖已有计数)
  33. redisTemplate.opsForValue().setIfAbsent(key, "1", DEF_REPORT_TTL, TimeUnit.MILLISECONDS);
  34. }
  35. /**
  36. * 计数器自增 1,并返回自增后的值
  37. * @param relationId 业务关联 ID
  38. * @return 自增后的计数(例如:初始为 0,调用后返回 1)
  39. */
  40. public int increment(Long relationId) {
  41. return increment(relationId, 1);
  42. }
  43. /**
  44. * 计数器自增指定步长,并返回自增后的值
  45. * @param relationId 业务关联 ID
  46. * @param step 自增步长(正整数)
  47. * @return 自增后的计数
  48. */
  49. public int increment(Long relationId, int step) {
  50. if (step <= 0) {
  51. throw new IllegalArgumentException("自增步长必须为正整数");
  52. }
  53. String key = getCounterKey(relationId);
  54. // Redis 的 INCRBY 命令:原子性自增,返回自增后的值
  55. Long newValue = redisTemplate.opsForValue().increment(key, step);
  56. return newValue.intValue();
  57. }
  58. /**
  59. * 获取当前计数
  60. * @param relationId 业务关联 ID
  61. * @return 当前计数(若键不存在,返回 0)
  62. */
  63. public Integer getCount(Long relationId) {
  64. String key = getCounterKey(relationId);
  65. String value = redisTemplate.opsForValue().get(key);
  66. if (value == null) {
  67. initCounter(relationId);
  68. return 1;
  69. }
  70. return Integer.parseInt(value);
  71. }
  72. /**
  73. * 重置计数器为 0
  74. * @param relationId 业务关联 ID
  75. */
  76. public void reset(Long relationId) {
  77. String key = getCounterKey(relationId);
  78. redisTemplate.opsForValue().set(key, "1", DEF_REPORT_TTL, TimeUnit.MILLISECONDS);
  79. }
  80. /**
  81. * 删除计数器(适用于业务结束后清理资源)
  82. * @param relationId 业务关联 ID
  83. * @return 是否删除成功(true:键存在且被删除;false:键不存在)
  84. */
  85. public boolean delete(Long relationId) {
  86. String key = getCounterKey(relationId);
  87. return Boolean.TRUE.equals(redisTemplate.delete(key));
  88. }
  89. /**
  90. * 设置计数器过期时间(避免长期占用 Redis 内存)
  91. * @param relationId 业务关联 ID
  92. * @param timeout 过期时间
  93. * @param unit 时间单位
  94. */
  95. public void setExpire(Long relationId, long timeout, TimeUnit unit) {
  96. String key = getCounterKey(relationId);
  97. redisTemplate.expire(key, timeout, unit);
  98. }
  99. /**
  100. * 检查计数器是否存在
  101. * @param relationId 业务关联 ID
  102. * @return true:存在;false:不存在
  103. */
  104. public boolean exists(Long relationId) {
  105. String key = getCounterKey(relationId);
  106. return Boolean.TRUE.equals(redisTemplate.hasKey(key));
  107. }
  108. }