package task import ( "context" "github.com/redis/go-redis/v9" "go.uber.org/zap" "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/constants" ) // priorityIndexAbsent 判断活动优先项索引能否确认该卡该任务类型没有活动优先项。 // 索引未建立、读取失败或成员存在时返回 false,调用方照常查库。 func (b *PollingBase) priorityIndexAbsent(ctx context.Context, cardID uint, taskType string) bool { if b == nil || b.redis == nil { return false } pipe := b.redis.Pipeline() ready := pipe.Exists(ctx, constants.RedisPollingPriorityReadyKey) member := pipe.SIsMember(ctx, constants.RedisPollingPriorityActiveKey, constants.PollingPriorityIndexMember(cardID, taskType)) if _, err := pipe.Exec(ctx); err != nil { return false } return ready.Val() > 0 && !member.Val() } // PriorityIndexRebuilder 定期按库内活动优先项重建索引(并入最近入队的项),使已终态的项退出索引。 type PriorityIndexRebuilder struct { store *postgres.PollingPriorityItemStore redis *redis.Client logger *zap.Logger } // NewPriorityIndexRebuilder 创建活动优先项索引重建器。 func NewPriorityIndexRebuilder(store *postgres.PollingPriorityItemStore, redisClient *redis.Client, logger *zap.Logger) *PriorityIndexRebuilder { if logger == nil { logger = zap.NewNop() } return &PriorityIndexRebuilder{store: store, redis: redisClient, logger: logger} } // Rebuild 重建一次索引;失败时清除就绪标记,轮询步骤回退为查库。 func (r *PriorityIndexRebuilder) Rebuild(ctx context.Context) { if r == nil || r.store == nil || r.redis == nil { return } keys, err := r.store.ListActiveKeys(ctx) if err != nil { r.logger.Warn("重建活动优先项索引失败,轮询步骤回退查库", zap.Error(err)) r.redis.Del(ctx, constants.RedisPollingPriorityReadyKey) return } members := make([]any, 0, len(keys)) for _, key := range keys { members = append(members, constants.PollingPriorityIndexMember(key.CardID, key.TaskType)) } tmp := constants.RedisPollingPriorityRebuildKey pipe := r.redis.TxPipeline() pipe.Del(ctx, tmp) if len(members) > 0 { pipe.SAdd(ctx, tmp, members...) } pipe.SUnionStore(ctx, tmp, tmp, constants.RedisPollingPriorityRecentKey) pipe.Del(ctx, constants.RedisPollingPriorityActiveKey) pipe.SUnionStore(ctx, constants.RedisPollingPriorityActiveKey, tmp) pipe.Del(ctx, tmp) pipe.Set(ctx, constants.RedisPollingPriorityReadyKey, "1", constants.PollingPriorityIndexReadyTTL) if _, err := pipe.Exec(ctx); err != nil { r.logger.Warn("写入活动优先项索引失败,轮询步骤回退查库", zap.Error(err)) r.redis.Del(ctx, constants.RedisPollingPriorityReadyKey) } }