From c10267ce2be8ad0668daabcae8a6ab8dc585cf2e Mon Sep 17 00:00:00 2001 From: cagedbird043 Date: Tue, 24 Feb 2026 20:01:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=88=B7=E6=96=B0=E7=94=A8=E9=87=8F?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E5=90=8E=E8=87=AA=E5=8A=A8=E6=B8=85=E7=90=86?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E5=8F=AF=E6=81=A2=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../internal/service/account_usage_service.go | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/backend/internal/service/account_usage_service.go b/backend/internal/service/account_usage_service.go index 7698223e..a363a790 100644 --- a/backend/internal/service/account_usage_service.go +++ b/backend/internal/service/account_usage_service.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "strings" "sync" "time" @@ -217,12 +218,20 @@ func (s *AccountUsageService) GetUsage(ctx context.Context, accountID int64) (*U } if account.Platform == PlatformGemini { - return s.getGeminiUsage(ctx, account) + usage, err := s.getGeminiUsage(ctx, account) + if err == nil { + s.tryClearRecoverableAccountError(ctx, account) + } + return usage, err } // Antigravity 平台:使用 AntigravityQuotaFetcher 获取额度 if account.Platform == PlatformAntigravity { - return s.getAntigravityUsage(ctx, account) + usage, err := s.getAntigravityUsage(ctx, account) + if err == nil { + s.tryClearRecoverableAccountError(ctx, account) + } + return usage, err } // 只有oauth类型账号可以通过API获取usage(有profile scope) @@ -256,6 +265,7 @@ func (s *AccountUsageService) GetUsage(ctx context.Context, accountID int64) (*U // 4. 添加窗口统计(有独立缓存,1 分钟) s.addWindowStats(ctx, account, usage) + s.tryClearRecoverableAccountError(ctx, account) return usage, nil } @@ -486,6 +496,32 @@ func parseTime(s string) (time.Time, error) { return time.Time{}, fmt.Errorf("unable to parse time: %s", s) } +func (s *AccountUsageService) tryClearRecoverableAccountError(ctx context.Context, account *Account) { + if account == nil || account.Status != StatusError { + return + } + + msg := strings.ToLower(strings.TrimSpace(account.ErrorMessage)) + if msg == "" { + return + } + + if !strings.Contains(msg, "token refresh failed") && + !strings.Contains(msg, "invalid_client") && + !strings.Contains(msg, "missing_project_id") && + !strings.Contains(msg, "unauthenticated") { + return + } + + if err := s.accountRepo.ClearError(ctx, account.ID); err != nil { + log.Printf("[usage] failed to clear recoverable account error for account %d: %v", account.ID, err) + return + } + + account.Status = StatusActive + account.ErrorMessage = "" +} + // buildUsageInfo 构建UsageInfo func (s *AccountUsageService) buildUsageInfo(resp *ClaudeUsageResponse, updatedAt *time.Time) *UsageInfo { info := &UsageInfo{