package repository import ( "context" "errors" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) const imageCardDailyFree = 3 // ImageCardRepo tracks daily draw quotas. type ImageCardRepo struct { Pool *pgxpool.Pool } // UsedToday returns how many free draws used today. func (r *ImageCardRepo) UsedToday(ctx context.Context, userID uuid.UUID, day time.Time) (int, error) { var n int err := r.Pool.QueryRow(ctx, ` SELECT used FROM image_card_quotas WHERE user_id=$1 AND day=$2::date`, userID, day.Format("2006-01-02"), ).Scan(&n) if errors.Is(err, pgx.ErrNoRows) { return 0, nil } return n, err } // TryConsume increments today's used count when under free limit. // Returns remaining after consume. ErrQuotaExhausted when free tier is out. func (r *ImageCardRepo) TryConsume(ctx context.Context, userID uuid.UUID, day time.Time) (remaining int, err error) { dayStr := day.Format("2006-01-02") var used int err = r.Pool.QueryRow(ctx, ` INSERT INTO image_card_quotas(user_id, day, used) VALUES ($1, $2::date, 1) ON CONFLICT (user_id, day) DO UPDATE SET used = image_card_quotas.used + 1 WHERE image_card_quotas.used < $3 RETURNING used`, userID, dayStr, imageCardDailyFree, ).Scan(&used) if errors.Is(err, pgx.ErrNoRows) { return 0, ErrQuotaExhausted } if err != nil { return 0, err } return imageCardDailyFree - used, nil } // RemainingToday without consuming. func (r *ImageCardRepo) RemainingToday(ctx context.Context, userID uuid.UUID, day time.Time, unlimited bool) (int, error) { if unlimited { return 99, nil } used, err := r.UsedToday(ctx, userID, day) if err != nil { return 0, err } left := imageCardDailyFree - used if left < 0 { left = 0 } return left, nil } // DailyFreeLimit is the free-tier cap. func DailyFreeLimit() int { return imageCardDailyFree } // ErrQuotaExhausted means free daily draws are used up. var ErrQuotaExhausted = errors.New("今日免费次数已用完")