2025-12-20 13:01:58 +08:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// TokenRefresher 定义平台特定的token刷新策略接口
|
|
|
|
|
|
// 通过此接口可以扩展支持不同平台(Anthropic/OpenAI/Gemini)
|
|
|
|
|
|
type TokenRefresher interface {
|
|
|
|
|
|
// CanRefresh 检查此刷新器是否能处理指定账号
|
2025-12-26 15:40:24 +08:00
|
|
|
|
CanRefresh(account *Account) bool
|
2025-12-20 13:01:58 +08:00
|
|
|
|
|
|
|
|
|
|
// NeedsRefresh 检查账号的token是否需要刷新
|
2025-12-26 15:40:24 +08:00
|
|
|
|
NeedsRefresh(account *Account, refreshWindow time.Duration) bool
|
2025-12-20 13:01:58 +08:00
|
|
|
|
|
|
|
|
|
|
// Refresh 执行token刷新,返回更新后的credentials
|
|
|
|
|
|
// 注意:返回的map应该保留原有credentials中的所有字段,只更新token相关字段
|
2025-12-26 15:40:24 +08:00
|
|
|
|
Refresh(ctx context.Context, account *Account) (map[string]any, error)
|
2025-12-20 13:01:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ClaudeTokenRefresher 处理Anthropic/Claude OAuth token刷新
|
|
|
|
|
|
type ClaudeTokenRefresher struct {
|
|
|
|
|
|
oauthService *OAuthService
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewClaudeTokenRefresher 创建Claude token刷新器
|
|
|
|
|
|
func NewClaudeTokenRefresher(oauthService *OAuthService) *ClaudeTokenRefresher {
|
|
|
|
|
|
return &ClaudeTokenRefresher{
|
|
|
|
|
|
oauthService: oauthService,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-16 01:31:54 +08:00
|
|
|
|
// CacheKey 返回用于分布式锁的缓存键
|
|
|
|
|
|
func (r *ClaudeTokenRefresher) CacheKey(account *Account) string {
|
|
|
|
|
|
return ClaudeTokenCacheKey(account)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-20 13:01:58 +08:00
|
|
|
|
// CanRefresh 检查是否能处理此账号
|
|
|
|
|
|
// 只处理 anthropic 平台的 oauth 类型账号
|
|
|
|
|
|
// setup-token 虽然也是OAuth,但有效期1年,不需要频繁刷新
|
2025-12-26 15:40:24 +08:00
|
|
|
|
func (r *ClaudeTokenRefresher) CanRefresh(account *Account) bool {
|
|
|
|
|
|
return account.Platform == PlatformAnthropic &&
|
|
|
|
|
|
account.Type == AccountTypeOAuth
|
2025-12-20 13:01:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NeedsRefresh 检查token是否需要刷新
|
|
|
|
|
|
// 基于 expires_at 字段判断是否在刷新窗口内
|
2025-12-26 15:40:24 +08:00
|
|
|
|
func (r *ClaudeTokenRefresher) NeedsRefresh(account *Account, refreshWindow time.Duration) bool {
|
2025-12-31 16:25:45 +08:00
|
|
|
|
expiresAt := account.GetCredentialAsTime("expires_at")
|
|
|
|
|
|
if expiresAt == nil {
|
2025-12-28 11:22:01 +08:00
|
|
|
|
return false
|
|
|
|
|
|
}
|
2025-12-31 16:25:45 +08:00
|
|
|
|
return time.Until(*expiresAt) < refreshWindow
|
2025-12-20 13:01:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Refresh 执行token刷新
|
|
|
|
|
|
// 保留原有credentials中的所有字段,只更新token相关字段
|
2025-12-26 15:40:24 +08:00
|
|
|
|
func (r *ClaudeTokenRefresher) Refresh(ctx context.Context, account *Account) (map[string]any, error) {
|
2025-12-20 13:01:58 +08:00
|
|
|
|
tokenInfo, err := r.oauthService.RefreshAccountToken(ctx, account)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-16 01:31:54 +08:00
|
|
|
|
newCredentials := BuildClaudeAccountCredentials(tokenInfo)
|
|
|
|
|
|
newCredentials = MergeCredentials(account.Credentials, newCredentials)
|
2025-12-20 13:01:58 +08:00
|
|
|
|
|
|
|
|
|
|
return newCredentials, nil
|
|
|
|
|
|
}
|
2025-12-22 22:58:31 +08:00
|
|
|
|
|
|
|
|
|
|
// OpenAITokenRefresher 处理 OpenAI OAuth token刷新
|
|
|
|
|
|
type OpenAITokenRefresher struct {
|
2026-01-31 21:46:28 +08:00
|
|
|
|
openaiOAuthService *OpenAIOAuthService
|
|
|
|
|
|
accountRepo AccountRepository
|
2025-12-22 22:58:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewOpenAITokenRefresher 创建 OpenAI token刷新器
|
2026-01-30 14:08:04 +08:00
|
|
|
|
func NewOpenAITokenRefresher(openaiOAuthService *OpenAIOAuthService, accountRepo AccountRepository) *OpenAITokenRefresher {
|
2025-12-22 22:58:31 +08:00
|
|
|
|
return &OpenAITokenRefresher{
|
|
|
|
|
|
openaiOAuthService: openaiOAuthService,
|
2026-01-30 14:08:04 +08:00
|
|
|
|
accountRepo: accountRepo,
|
2025-12-22 22:58:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-16 01:31:54 +08:00
|
|
|
|
// CacheKey 返回用于分布式锁的缓存键
|
|
|
|
|
|
func (r *OpenAITokenRefresher) CacheKey(account *Account) string {
|
|
|
|
|
|
return OpenAITokenCacheKey(account)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 22:58:31 +08:00
|
|
|
|
// CanRefresh 检查是否能处理此账号
|
2025-12-26 15:40:24 +08:00
|
|
|
|
func (r *OpenAITokenRefresher) CanRefresh(account *Account) bool {
|
2026-02-19 08:02:56 +08:00
|
|
|
|
return account.Platform == PlatformOpenAI && account.Type == AccountTypeOAuth
|
2025-12-22 22:58:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NeedsRefresh 检查token是否需要刷新
|
2026-04-02 20:44:12 +08:00
|
|
|
|
// expires_at 缺失且处于限流状态时需要刷新,防止限流期间 token 静默过期
|
2025-12-26 15:40:24 +08:00
|
|
|
|
func (r *OpenAITokenRefresher) NeedsRefresh(account *Account, refreshWindow time.Duration) bool {
|
2026-01-31 20:22:22 +08:00
|
|
|
|
expiresAt := account.GetCredentialAsTime("expires_at")
|
2025-12-22 22:58:31 +08:00
|
|
|
|
if expiresAt == nil {
|
2026-04-02 20:44:12 +08:00
|
|
|
|
return account.IsRateLimited()
|
2025-12-22 22:58:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return time.Until(*expiresAt) < refreshWindow
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Refresh 执行token刷新
|
|
|
|
|
|
// 保留原有credentials中的所有字段,只更新token相关字段
|
2025-12-26 15:40:24 +08:00
|
|
|
|
func (r *OpenAITokenRefresher) Refresh(ctx context.Context, account *Account) (map[string]any, error) {
|
2025-12-22 22:58:31 +08:00
|
|
|
|
tokenInfo, err := r.openaiOAuthService.RefreshAccountToken(ctx, account)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用服务提供的方法构建新凭证,并保留原有字段
|
|
|
|
|
|
newCredentials := r.openaiOAuthService.BuildAccountCredentials(tokenInfo)
|
2026-03-16 01:31:54 +08:00
|
|
|
|
newCredentials = MergeCredentials(account.Credentials, newCredentials)
|
2025-12-22 22:58:31 +08:00
|
|
|
|
|
|
|
|
|
|
return newCredentials, nil
|
|
|
|
|
|
}
|