@@ -18,7 +18,8 @@ type TokenRefreshService struct {
1818 refreshers []TokenRefresher
1919 cfg * config.TokenRefreshConfig
2020 cacheInvalidator TokenCacheInvalidator
21- schedulerCache SchedulerCache // 用于同步更新调度器缓存,解决 token 刷新后缓存不一致问题
21+ schedulerCache SchedulerCache // 用于同步更新调度器缓存,解决 token 刷新后缓存不一致问题
22+ tempUnschedCache TempUnschedCache // 用于清除 Redis 中的临时不可调度缓存
2223
2324 stopCh chan struct {}
2425 wg sync.WaitGroup
@@ -34,12 +35,14 @@ func NewTokenRefreshService(
3435 cacheInvalidator TokenCacheInvalidator ,
3536 schedulerCache SchedulerCache ,
3637 cfg * config.Config ,
38+ tempUnschedCache TempUnschedCache ,
3739) * TokenRefreshService {
3840 s := & TokenRefreshService {
3941 accountRepo : accountRepo ,
4042 cfg : & cfg .TokenRefresh ,
4143 cacheInvalidator : cacheInvalidator ,
4244 schedulerCache : schedulerCache ,
45+ tempUnschedCache : tempUnschedCache ,
4346 stopCh : make (chan struct {}),
4447 }
4548
@@ -231,6 +234,26 @@ func (s *TokenRefreshService) refreshWithRetry(ctx context.Context, account *Acc
231234 slog .Info ("token_refresh.cleared_missing_project_id_error" , "account_id" , account .ID )
232235 }
233236 }
237+ // 刷新成功后清除临时不可调度状态(处理 OAuth 401 恢复场景)
238+ if account .TempUnschedulableUntil != nil && time .Now ().Before (* account .TempUnschedulableUntil ) {
239+ if clearErr := s .accountRepo .ClearTempUnschedulable (ctx , account .ID ); clearErr != nil {
240+ slog .Warn ("token_refresh.clear_temp_unschedulable_failed" ,
241+ "account_id" , account .ID ,
242+ "error" , clearErr ,
243+ )
244+ } else {
245+ slog .Info ("token_refresh.cleared_temp_unschedulable" , "account_id" , account .ID )
246+ }
247+ // 同步清除 Redis 缓存,避免调度器读到过期的临时不可调度状态
248+ if s .tempUnschedCache != nil {
249+ if clearErr := s .tempUnschedCache .DeleteTempUnsched (ctx , account .ID ); clearErr != nil {
250+ slog .Warn ("token_refresh.clear_temp_unsched_cache_failed" ,
251+ "account_id" , account .ID ,
252+ "error" , clearErr ,
253+ )
254+ }
255+ }
256+ }
234257 // 对所有 OAuth 账号调用缓存失效(InvalidateToken 内部根据平台判断是否需要处理)
235258 if s .cacheInvalidator != nil && account .Type == AccountTypeOAuth {
236259 if err := s .cacheInvalidator .InvalidateToken (ctx , account ); err != nil {
@@ -257,8 +280,8 @@ func (s *TokenRefreshService) refreshWithRetry(ctx context.Context, account *Acc
257280 return nil
258281 }
259282
260- // Antigravity 账户:不可重试错误直接标记 error 状态并返回
261- if account . Platform == PlatformAntigravity && isNonRetryableRefreshError (err ) {
283+ // 不可重试错误(invalid_grant/invalid_client 等)直接标记 error 状态并返回
284+ if isNonRetryableRefreshError (err ) {
262285 errorMsg := fmt .Sprintf ("Token refresh failed (non-retryable): %v" , err )
263286 if setErr := s .accountRepo .SetError (ctx , account .ID , errorMsg ); setErr != nil {
264287 slog .Error ("token_refresh.set_error_status_failed" ,
@@ -285,23 +308,13 @@ func (s *TokenRefreshService) refreshWithRetry(ctx context.Context, account *Acc
285308 }
286309 }
287310
288- // Antigravity 账户:其他错误仅记录日志,不标记 error(可能是临时网络问题)
289- // 其他平台账户:重试失败后标记 error
290- if account .Platform == PlatformAntigravity {
291- slog .Warn ("token_refresh.retry_exhausted_antigravity" ,
292- "account_id" , account .ID ,
293- "max_retries" , s .cfg .MaxRetries ,
294- "error" , lastErr ,
295- )
296- } else {
297- errorMsg := fmt .Sprintf ("Token refresh failed after %d retries: %v" , s .cfg .MaxRetries , lastErr )
298- if err := s .accountRepo .SetError (ctx , account .ID , errorMsg ); err != nil {
299- slog .Error ("token_refresh.set_error_status_failed" ,
300- "account_id" , account .ID ,
301- "error" , err ,
302- )
303- }
304- }
311+ // 可重试错误耗尽:仅记录日志,不标记 error(可能是临时网络问题,下个周期继续重试)
312+ slog .Warn ("token_refresh.retry_exhausted" ,
313+ "account_id" , account .ID ,
314+ "platform" , account .Platform ,
315+ "max_retries" , s .cfg .MaxRetries ,
316+ "error" , lastErr ,
317+ )
305318
306319 return lastErr
307320}
0 commit comments