61 lines
1.6 KiB
Go

package service
import (
"context"
"fmt"
"strconv"
"time"
"github.com/redis/go-redis/v9"
"hyapp/pkg/appcode"
"hyapp/pkg/idgen"
"hyapp/pkg/xerr"
)
const releaseLuckyGiftSendLockScript = `
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
end
return 0
`
type LuckyGiftSendLocker interface {
AcquireLuckyGiftSendLock(ctx context.Context, appCode string, userID int64, ttl time.Duration) (func(context.Context), error)
}
type RedisLuckyGiftSendLocker struct {
client *redis.Client
}
func NewRedisLuckyGiftSendLocker(client *redis.Client) *RedisLuckyGiftSendLocker {
return &RedisLuckyGiftSendLocker{client: client}
}
func (l *RedisLuckyGiftSendLocker) AcquireLuckyGiftSendLock(ctx context.Context, appCode string, userID int64, ttl time.Duration) (func(context.Context), error) {
if l == nil || l.client == nil {
return nil, nil
}
if userID <= 0 {
return nil, xerr.New(xerr.InvalidArgument, "lucky gift sender is invalid")
}
if ttl <= 0 {
ttl = 5 * time.Second
}
key := luckyGiftSendLockKey(appCode, userID)
token := idgen.New("lucky_gift_send_lock")
ok, err := l.client.SetNX(ctx, key, token, ttl).Result()
if err != nil {
return nil, err
}
if !ok {
return nil, xerr.New(xerr.Conflict, "lucky gift send is already processing")
}
return func(releaseCtx context.Context) {
_ = l.client.Eval(releaseCtx, releaseLuckyGiftSendLockScript, []string{key}, token).Err()
}, nil
}
func luckyGiftSendLockKey(rawAppCode string, userID int64) string {
return fmt.Sprintf("lucky_gift_send:%s:%s", appcode.Normalize(rawAppCode), strconv.FormatInt(userID, 10))
}