| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- package repository
- import (
- "context"
- "fmt"
- "time"
- "github.com/Wei-Shaw/sub2api/internal/service"
- "github.com/redis/go-redis/v9"
- )
- const (
- redeemRateLimitKeyPrefix = "redeem:ratelimit:v2:"
- redeemLockKeyPrefix = "redeem:lock:"
- redeemRateLimitWindow = 10 * time.Minute
- )
- var incrementRedeemAttemptScript = redis.NewScript(`
- local current = redis.call('INCR', KEYS[1])
- local ttl = redis.call('PTTL', KEYS[1])
- if current == 1 or ttl == -1 then
- redis.call('PEXPIRE', KEYS[1], ARGV[1])
- end
- return current
- `)
- // redeemRateLimitKey generates the Redis key for redeem attempt rate limiting.
- func redeemRateLimitKey(userID int64) string {
- return fmt.Sprintf("%s%d", redeemRateLimitKeyPrefix, userID)
- }
- // redeemLockKey generates the Redis key for redeem code locking.
- func redeemLockKey(code string) string {
- return redeemLockKeyPrefix + code
- }
- type redeemCache struct {
- rdb *redis.Client
- }
- func NewRedeemCache(rdb *redis.Client) service.RedeemCache {
- return &redeemCache{rdb: rdb}
- }
- func (c *redeemCache) GetRedeemAttemptCount(ctx context.Context, userID int64) (int, error) {
- key := redeemRateLimitKey(userID)
- count, err := c.rdb.Get(ctx, key).Int()
- if err == redis.Nil {
- return 0, nil
- }
- return count, err
- }
- func (c *redeemCache) IncrementRedeemAttemptCount(ctx context.Context, userID int64) error {
- key := redeemRateLimitKey(userID)
- return incrementRedeemAttemptScript.Run(
- ctx,
- c.rdb,
- []string{key},
- redeemRateLimitWindow.Milliseconds(),
- ).Err()
- }
- func (c *redeemCache) AcquireRedeemLock(ctx context.Context, code string, ttl time.Duration) (bool, error) {
- key := redeemLockKey(code)
- return c.rdb.SetNX(ctx, key, 1, ttl).Result()
- }
- func (c *redeemCache) ReleaseRedeemLock(ctx context.Context, code string) error {
- key := redeemLockKey(code)
- return c.rdb.Del(ctx, key).Err()
- }
|