expired.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package repository
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/redis/go-redis/v9"
  6. "strconv"
  7. "time"
  8. )
  9. // PlanListType 定义了要操作的套餐列表类型
  10. type PlanListType string
  11. const (
  12. // ClosedPlansList 代表已关闭的套餐列表 (使用 Redis Set)
  13. ClosedPlansList PlanListType = "closed"
  14. // ExpiringSoonPlansList 代表即将过期的套餐列表 (使用 Redis Sorted Set)
  15. ExpiringSoonPlansList PlanListType = "expiring_soon"
  16. )
  17. // ExpiredRepository 定义了与过期套餐相关的操作接口
  18. type ExpiredRepository interface {
  19. // AddPlans 将一个或多个套餐ID添加到指定的列表中
  20. AddPlans(ctx context.Context, listType PlanListType, planIds ...int64) error
  21. // RemovePlans 从指定的列表中移除一个或多个套餐ID
  22. RemovePlans(ctx context.Context, listType PlanListType, planIds ...int64) error
  23. // GetAllPlanIds 获取指定列表中的所有套餐ID
  24. GetAllPlanIds(ctx context.Context, listType PlanListType) ([]int64, error)
  25. // IsPlanInList 检查一个套餐ID是否存在于指定的列表中
  26. IsPlanInList(ctx context.Context, listType PlanListType, planId int64) (bool, error)
  27. }
  28. func NewExpiredRepository(
  29. repository *Repository,
  30. ) ExpiredRepository {
  31. return &expiredRepository{
  32. Repository: repository,
  33. }
  34. }
  35. type expiredRepository struct {
  36. *Repository
  37. }
  38. // Key的前缀,用于标识所有已关闭套餐的Key
  39. // closePlansKey 用于存储所有已关闭套餐ID的Set
  40. const closePlansKey = "waf:closed_plans"
  41. // expiringSoonPlansKey 用于存储7天后过期套餐ID的Sorted Set
  42. // Score: 过期时间戳, Value: planId
  43. const expiringSoonPlansKey = "waf:expiring_soon_plans"
  44. // AddPlans 将一个或多个套餐ID添加到指定的列表中
  45. func (r *expiredRepository) AddPlans(ctx context.Context, listType PlanListType, planIds ...int64) error {
  46. if len(planIds) == 0 {
  47. return nil
  48. }
  49. switch listType {
  50. case ClosedPlansList:
  51. members := make([]interface{}, len(planIds))
  52. for i, id := range planIds {
  53. members[i] = id
  54. }
  55. return r.Rdb.SAdd(ctx, closePlansKey, members...).Err()
  56. case ExpiringSoonPlansList:
  57. // Score 代表套餐的实际过期时间戳,用于后续查询
  58. expirationTimestamp := float64(time.Now().Add(7 * 24 * time.Hour).Unix())
  59. members := make([]redis.Z, len(planIds))
  60. for i, id := range planIds {
  61. members[i] = redis.Z{
  62. Score: expirationTimestamp,
  63. Member: id,
  64. }
  65. }
  66. return r.Rdb.ZAdd(ctx, expiringSoonPlansKey, members...).Err()
  67. default:
  68. return fmt.Errorf("未知的列表类型: %s", listType)
  69. }
  70. }
  71. // RemovePlans 从指定的列表中移除一个或多个套餐ID
  72. func (r *expiredRepository) RemovePlans(ctx context.Context, listType PlanListType, planIds ...int64) error {
  73. if len(planIds) == 0 {
  74. return nil
  75. }
  76. members := make([]interface{}, len(planIds))
  77. for i, id := range planIds {
  78. members[i] = id
  79. }
  80. switch listType {
  81. case ClosedPlansList:
  82. return r.Rdb.SRem(ctx, closePlansKey, members...).Err()
  83. case ExpiringSoonPlansList:
  84. return r.Rdb.ZRem(ctx, expiringSoonPlansKey, members...).Err()
  85. default:
  86. return fmt.Errorf("未知的列表类型: %s", listType)
  87. }
  88. }
  89. // GetAllPlanIds 获取指定列表中的所有套餐ID
  90. func (r *expiredRepository) GetAllPlanIds(ctx context.Context, listType PlanListType) ([]int64, error) {
  91. var members []string
  92. var err error
  93. switch listType {
  94. case ClosedPlansList:
  95. members, err = r.Rdb.SMembers(ctx, closePlansKey).Result()
  96. case ExpiringSoonPlansList:
  97. members, err = r.Rdb.ZRange(ctx, expiringSoonPlansKey, 0, -1).Result()
  98. default:
  99. return nil, fmt.Errorf("未知的列表类型: %s", listType)
  100. }
  101. if err != nil {
  102. return nil, err
  103. }
  104. planIds := make([]int64, len(members))
  105. for i, memberStr := range members {
  106. id, err := strconv.ParseInt(memberStr, 10, 64)
  107. if err != nil {
  108. return nil, fmt.Errorf("无法解析套餐ID '%s': %w", memberStr, err)
  109. }
  110. planIds[i] = id
  111. }
  112. return planIds, nil
  113. }
  114. // IsPlanInList 检查一个套餐ID是否存在于指定的列表中
  115. func (r *expiredRepository) IsPlanInList(ctx context.Context, listType PlanListType, planId int64) (bool, error) {
  116. switch listType {
  117. case ClosedPlansList:
  118. return r.Rdb.SIsMember(ctx, closePlansKey, planId).Result()
  119. case ExpiringSoonPlansList:
  120. _, err := r.Rdb.ZScore(ctx, expiringSoonPlansKey, strconv.FormatInt(planId, 10)).Result()
  121. if err == redis.Nil {
  122. return false, nil
  123. }
  124. return err == nil, err
  125. default:
  126. return false, fmt.Errorf("未知的列表类型: %s", listType)
  127. }
  128. }