package reward import ( "context" "crypto/rand" "encoding/hex" "strconv" "time" "github.com/redis/go-redis/v9" ) type tgConfig struct { botUser, channel, botToken, webhookSecret string } // SetTelegram 注入 TG 配置(经 Service.tg)。 func (s *Service) SetTelegram(botUser, channel, botToken, webhookSecret string) { s.tg = tgConfig{botUser: botUser, channel: channel, botToken: botToken, webhookSecret: webhookSecret} } // SetRedis 注入 redis 客户端(经 Service.rdb);为 nil 时 token 走内存兜底(测试/未配 redis)。 func (s *Service) SetRedis(rdb *redis.Client) { s.rdb = rdb } func randToken() string { b := make([]byte, 16) _, _ = rand.Read(b) return hex.EncodeToString(b) } // IssueTelegramToken 生成随机 token,Redis SETEX tg_bind: 600 ; // Redis 为 nil 时用内存 map 兜底(测试/未配 redis)。 func (s *Service) IssueTelegramToken(ctx context.Context, userID int64) (string, error) { tok := randToken() if s.rdb != nil { return tok, s.rdb.Set(ctx, "tg_bind:"+tok, userID, 10*time.Minute).Err() } s.memMu.Lock() if s.memTok == nil { s.memTok = map[string]int64{} } s.memTok[tok] = userID s.memMu.Unlock() return tok, nil } // ConsumeTelegramToken 一次性消费(GETDEL 语义)。 func (s *Service) ConsumeTelegramToken(ctx context.Context, token string) (int64, bool, error) { if s.rdb != nil { v, err := s.rdb.GetDel(ctx, "tg_bind:"+token).Result() if err == redis.Nil { return 0, false, nil } if err != nil { return 0, false, err } id, _ := strconv.ParseInt(v, 10, 64) return id, true, nil } s.memMu.Lock() defer s.memMu.Unlock() id, ok := s.memTok[token] if ok { delete(s.memTok, token) } return id, ok, nil }