From d812af915976ae739b2cea568d6fc545171f69d8 Mon Sep 17 00:00:00 2001 From: "jett.gao" Date: Fri, 23 Jan 2026 18:19:34 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Claude=20Console=20=E9=85=8D=E9=A2=9D?= =?UTF-8?q?=E8=B6=85=E9=99=90=E7=8A=B6=E6=80=81=E4=BC=98=E5=8C=96=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=B8=BB=E5=8A=A8=E8=87=AA=E5=8A=A8=E6=81=A2?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 rateLimitCleanupService 配额超限恢复检查(每5分钟) - 调度器预检查配额超限账户,到达重置时间自动恢复 - 前端显示"余额不足"替代默认的"手动停止调度" Co-Authored-By: Claude Opus 4.5 --- src/services/rateLimitCleanupService.js | 66 +++++++++++++++++++++++- src/services/unifiedClaudeScheduler.js | 17 ++++++ web/admin-spa/src/views/AccountsView.vue | 4 ++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/services/rateLimitCleanupService.js b/src/services/rateLimitCleanupService.js index 0775b650..d3b90df6 100644 --- a/src/services/rateLimitCleanupService.js +++ b/src/services/rateLimitCleanupService.js @@ -73,6 +73,7 @@ class RateLimitCleanupService { openai: { checked: 0, cleared: 0, errors: [] }, claude: { checked: 0, cleared: 0, errors: [] }, claudeConsole: { checked: 0, cleared: 0, errors: [] }, + quotaExceeded: { checked: 0, cleared: 0, errors: [] }, tokenRefresh: { checked: 0, refreshed: 0, errors: [] } } @@ -85,13 +86,22 @@ class RateLimitCleanupService { // 清理 Claude Console 账号 await this.cleanupClaudeConsoleAccounts(results.claudeConsole) + // 清理 Claude Console 配额超限状态 + await this.cleanupClaudeConsoleQuotaExceeded(results.quotaExceeded) + // 主动刷新等待重置的 Claude 账户 Token(防止 5小时/7天 等待期间 Token 过期) await this.proactiveRefreshClaudeTokens(results.tokenRefresh) const totalChecked = - results.openai.checked + results.claude.checked + results.claudeConsole.checked + results.openai.checked + + results.claude.checked + + results.claudeConsole.checked + + results.quotaExceeded.checked const totalCleared = - results.openai.cleared + results.claude.cleared + results.claudeConsole.cleared + results.openai.cleared + + results.claude.cleared + + results.claudeConsole.cleared + + results.quotaExceeded.cleared const duration = Date.now() - startTime if (totalCleared > 0 || results.tokenRefresh.refreshed > 0) { @@ -103,6 +113,9 @@ class RateLimitCleanupService { logger.info( ` Claude Console: ${results.claudeConsole.cleared}/${results.claudeConsole.checked}` ) + logger.info( + ` Quota Exceeded: ${results.quotaExceeded.cleared}/${results.quotaExceeded.checked}` + ) if (results.tokenRefresh.checked > 0 || results.tokenRefresh.refreshed > 0) { logger.info( ` Token Refresh: ${results.tokenRefresh.refreshed}/${results.tokenRefresh.checked} refreshed` @@ -124,6 +137,7 @@ class RateLimitCleanupService { ...results.openai.errors, ...results.claude.errors, ...results.claudeConsole.errors, + ...results.quotaExceeded.errors, ...results.tokenRefresh.errors ] if (allErrors.length > 0) { @@ -358,6 +372,54 @@ class RateLimitCleanupService { } } + /** + * 检查并恢复 Claude Console 账号的配额超限状态 + */ + async cleanupClaudeConsoleQuotaExceeded(result) { + try { + const accounts = await claudeConsoleAccountService.getAllAccounts() + + for (const account of accounts) { + // 检查是否处于配额超限状态 + if (account.status === 'quota_exceeded' || account.quotaStoppedAt) { + result.checked++ + + try { + // 使用 isAccountQuotaExceeded 方法,它会自动触发恢复 + const isStillExceeded = await claudeConsoleAccountService.isAccountQuotaExceeded( + account.id + ) + + if (!isStillExceeded) { + result.cleared++ + logger.info( + `🧹 Auto-recovered quota exceeded for Claude Console account: ${account.name} (${account.id})` + ) + + // 记录已恢复的账户信息 + this.clearedAccounts.push({ + platform: 'Claude Console', + accountId: account.id, + accountName: account.name, + previousStatus: 'quota_exceeded', + currentStatus: 'active' + }) + } + } catch (error) { + result.errors.push({ + accountId: account.id, + accountName: account.name, + error: error.message + }) + } + } + } + } catch (error) { + logger.error('Failed to cleanup Claude Console quota exceeded accounts:', error) + result.errors.push({ error: error.message }) + } + } + /** * 主动刷新 Claude 账户 Token(防止等待重置期间 Token 过期) * 仅对因限流/配额限制而等待重置的账户执行刷新: diff --git a/src/services/unifiedClaudeScheduler.js b/src/services/unifiedClaudeScheduler.js index 0d39ad68..f56e4e7c 100644 --- a/src/services/unifiedClaudeScheduler.js +++ b/src/services/unifiedClaudeScheduler.js @@ -673,6 +673,23 @@ class UnifiedClaudeScheduler { } } + // 主动检查配额超限状态并尝试恢复(在过滤之前执行,确保可以恢复配额超限的账户) + if (currentAccount.status === 'quota_exceeded') { + // 触发配额检查,如果已到重置时间会自动恢复账户 + const isStillExceeded = await claudeConsoleAccountService.isAccountQuotaExceeded( + currentAccount.id + ) + if (!isStillExceeded) { + // 重新获取账户最新状态 + const refreshedAccount = await claudeConsoleAccountService.getAccount(currentAccount.id) + if (refreshedAccount) { + // 更新当前循环中的账户数据 + currentAccount = refreshedAccount + logger.info(`✅ Account ${currentAccount.name} recovered from quota_exceeded status`) + } + } + } + logger.info( `🔍 Checking Claude Console account: ${currentAccount.name} - isActive: ${currentAccount.isActive}, status: ${currentAccount.status}, accountType: ${currentAccount.accountType}, schedulable: ${currentAccount.schedulable}` ) diff --git a/web/admin-spa/src/views/AccountsView.vue b/web/admin-spa/src/views/AccountsView.vue index 7147b3fd..fcfcf94b 100644 --- a/web/admin-spa/src/views/AccountsView.vue +++ b/web/admin-spa/src/views/AccountsView.vue @@ -4119,6 +4119,10 @@ const getSchedulableReason = (account) => { if (account.status === 'unauthorized') { return 'API Key无效或已过期(401错误)' } + // 检查配额超限状态 + if (account.status === 'quota_exceeded') { + return '余额不足' + } if (account.overloadStatus === 'overloaded') { return '服务过载(529错误)' }