package repository import ( "context" "fmt" "github.com/redis/go-redis/v9" "strconv" "time" ) // PlanListType 定义了要操作的套餐列表类型 type PlanListType string const ( // ClosedPlansList 代表已关闭的套餐列表 (使用 Redis Set) ClosedPlansList PlanListType = "closed" // ExpiringSoonPlansList 代表即将过期的套餐列表 (使用 Redis Sorted Set) ExpiringSoonPlansList PlanListType = "expiring_soon" ) // ExpiredRepository 定义了与过期套餐相关的操作接口 type ExpiredRepository interface { // AddPlans 将一个或多个套餐ID添加到指定的列表中 AddPlans(ctx context.Context, listType PlanListType, planIds ...int64) error // RemovePlans 从指定的列表中移除一个或多个套餐ID RemovePlans(ctx context.Context, listType PlanListType, planIds ...int64) error // GetAllPlanIds 获取指定列表中的所有套餐ID GetAllPlanIds(ctx context.Context, listType PlanListType) ([]int64, error) // IsPlanInList 检查一个套餐ID是否存在于指定的列表中 IsPlanInList(ctx context.Context, listType PlanListType, planId int64) (bool, error) } func NewExpiredRepository( repository *Repository, ) ExpiredRepository { return &expiredRepository{ Repository: repository, } } type expiredRepository struct { *Repository } // Key的前缀,用于标识所有已关闭套餐的Key // closePlansKey 用于存储所有已关闭套餐ID的Set const closePlansKey = "waf:closed_plans" // expiringSoonPlansKey 用于存储7天后过期套餐ID的Sorted Set // Score: 过期时间戳, Value: planId const expiringSoonPlansKey = "waf:expiring_soon_plans" // AddPlans 将一个或多个套餐ID添加到指定的列表中 func (r *expiredRepository) AddPlans(ctx context.Context, listType PlanListType, planIds ...int64) error { if len(planIds) == 0 { return nil } switch listType { case ClosedPlansList: members := make([]interface{}, len(planIds)) for i, id := range planIds { members[i] = id } return r.Rdb.SAdd(ctx, closePlansKey, members...).Err() case ExpiringSoonPlansList: // Score 代表套餐的实际过期时间戳,用于后续查询 expirationTimestamp := float64(time.Now().Add(7 * 24 * time.Hour).Unix()) members := make([]redis.Z, len(planIds)) for i, id := range planIds { members[i] = redis.Z{ Score: expirationTimestamp, Member: id, } } return r.Rdb.ZAdd(ctx, expiringSoonPlansKey, members...).Err() default: return fmt.Errorf("未知的列表类型: %s", listType) } } // RemovePlans 从指定的列表中移除一个或多个套餐ID func (r *expiredRepository) RemovePlans(ctx context.Context, listType PlanListType, planIds ...int64) error { if len(planIds) == 0 { return nil } members := make([]interface{}, len(planIds)) for i, id := range planIds { members[i] = id } switch listType { case ClosedPlansList: return r.Rdb.SRem(ctx, closePlansKey, members...).Err() case ExpiringSoonPlansList: return r.Rdb.ZRem(ctx, expiringSoonPlansKey, members...).Err() default: return fmt.Errorf("未知的列表类型: %s", listType) } } // GetAllPlanIds 获取指定列表中的所有套餐ID func (r *expiredRepository) GetAllPlanIds(ctx context.Context, listType PlanListType) ([]int64, error) { var members []string var err error switch listType { case ClosedPlansList: members, err = r.Rdb.SMembers(ctx, closePlansKey).Result() case ExpiringSoonPlansList: members, err = r.Rdb.ZRange(ctx, expiringSoonPlansKey, 0, -1).Result() default: return nil, fmt.Errorf("未知的列表类型: %s", listType) } if err != nil { return nil, err } planIds := make([]int64, len(members)) for i, memberStr := range members { id, err := strconv.ParseInt(memberStr, 10, 64) if err != nil { return nil, fmt.Errorf("无法解析套餐ID '%s': %w", memberStr, err) } planIds[i] = id } return planIds, nil } // IsPlanInList 检查一个套餐ID是否存在于指定的列表中 func (r *expiredRepository) IsPlanInList(ctx context.Context, listType PlanListType, planId int64) (bool, error) { switch listType { case ClosedPlansList: return r.Rdb.SIsMember(ctx, closePlansKey, planId).Result() case ExpiringSoonPlansList: _, err := r.Rdb.ZScore(ctx, expiringSoonPlansKey, strconv.FormatInt(planId, 10)).Result() if err == redis.Nil { return false, nil } return err == nil, err default: return false, fmt.Errorf("未知的列表类型: %s", listType) } }