From c0d6ecefac648ac871e31e6f1ed2eed5121b78c9 Mon Sep 17 00:00:00 2001 From: shaw Date: Sun, 14 Sep 2025 12:20:10 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=99=90=E6=B5=81?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=88=A4=E6=96=AD=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E5=AF=B9=E8=B1=A1=E5=92=8C=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 cleanupOpenAIAccounts 方法中 rateLimitStatus 判断问题 - 修复 cleanupClaudeConsoleAccounts 方法中的判断逻辑 - 优化 unifiedOpenAIScheduler 的 _isRateLimited 辅助方法 - 保持原始服务层数据获取方式,通过判断逻辑适配不同数据格式 问题原因:服务层返回的 rateLimitStatus 是对象格式,但清理逻辑使用字符串比较, 导致限流账户无法被正确检测和自动恢复。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/services/rateLimitCleanupService.js | 39 ++++++++++++++++++------- src/services/unifiedOpenAIScheduler.js | 28 +++++++++++++++--- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/services/rateLimitCleanupService.js b/src/services/rateLimitCleanupService.js index b5f2325f..7a9cf618 100644 --- a/src/services/rateLimitCleanupService.js +++ b/src/services/rateLimitCleanupService.js @@ -134,11 +134,18 @@ class RateLimitCleanupService { */ async cleanupOpenAIAccounts(result) { try { + // 使用服务层获取账户数据 const accounts = await openaiAccountService.getAllAccounts() for (const account of accounts) { - // 只检查标记为限流的账号 - if (account.rateLimitStatus === 'limited') { + // 检查是否处于限流状态(兼容对象和字符串格式) + const isRateLimited = + account.rateLimitStatus === 'limited' || + (account.rateLimitStatus && + typeof account.rateLimitStatus === 'object' && + account.rateLimitStatus.status === 'limited') + + if (isRateLimited) { result.checked++ try { @@ -180,17 +187,20 @@ class RateLimitCleanupService { */ async cleanupClaudeAccounts(result) { try { - // 使用原始数据而不是处理过的数据,避免字段被转换 + // 使用 Redis 获取账户数据 const redis = require('../models/redis') const accounts = await redis.getAllClaudeAccounts() for (const account of accounts) { - // 检查所有可能处于限流状态的账号,包括自动停止的账号 - if ( + // 检查是否处于限流状态(兼容对象和字符串格式) + const isRateLimited = account.rateLimitStatus === 'limited' || - account.rateLimitedAt || - account.rateLimitAutoStopped === 'true' - ) { + (account.rateLimitStatus && + typeof account.rateLimitStatus === 'object' && + account.rateLimitStatus.status === 'limited') + + // 检查所有可能处于限流状态的账号,包括自动停止的账号 + if (isRateLimited || account.rateLimitedAt || account.rateLimitAutoStopped === 'true') { result.checked++ try { @@ -265,14 +275,21 @@ class RateLimitCleanupService { */ async cleanupClaudeConsoleAccounts(result) { try { + // 使用服务层获取账户数据 const accounts = await claudeConsoleAccountService.getAllAccounts() for (const account of accounts) { + // 检查是否处于限流状态(兼容对象和字符串格式) + const isRateLimited = + account.rateLimitStatus === 'limited' || + (account.rateLimitStatus && + typeof account.rateLimitStatus === 'object' && + account.rateLimitStatus.status === 'limited') + // 检查两种状态字段:rateLimitStatus 和 status - const hasRateLimitStatus = account.rateLimitStatus === 'limited' const hasStatusRateLimited = account.status === 'rate_limited' - if (hasRateLimitStatus || hasStatusRateLimited) { + if (isRateLimited || hasStatusRateLimited) { result.checked++ try { @@ -285,7 +302,7 @@ class RateLimitCleanupService { result.cleared++ // 如果 status 字段是 rate_limited,需要额外清理 - if (hasStatusRateLimited && !hasRateLimitStatus) { + if (hasStatusRateLimited && !isRateLimited) { await claudeConsoleAccountService.updateAccount(account.id, { status: 'active' }) diff --git a/src/services/unifiedOpenAIScheduler.js b/src/services/unifiedOpenAIScheduler.js index 5a158a10..efe08ac9 100644 --- a/src/services/unifiedOpenAIScheduler.js +++ b/src/services/unifiedOpenAIScheduler.js @@ -19,6 +19,26 @@ class UnifiedOpenAIScheduler { return schedulable !== false && schedulable !== 'false' } + // 🔧 辅助方法:检查账户是否被限流(兼容字符串和对象格式) + _isRateLimited(rateLimitStatus) { + if (!rateLimitStatus) { + return false + } + + // 兼容字符串格式(Redis 原始数据) + if (typeof rateLimitStatus === 'string') { + return rateLimitStatus === 'limited' + } + + // 兼容对象格式(getAllAccounts 返回的数据) + if (typeof rateLimitStatus === 'object') { + // 检查对象中的 status 字段 + return rateLimitStatus.status === 'limited' || rateLimitStatus.isRateLimited === true + } + + return false + } + // 🎯 统一调度OpenAI账号 async selectAccountForApiKey(apiKeyData, sessionHash = null, requestedModel = null) { try { @@ -63,7 +83,7 @@ class UnifiedOpenAIScheduler { } } else if ( accountType === 'openai-responses' && - boundAccount.rateLimitStatus === 'limited' + this._isRateLimited(boundAccount.rateLimitStatus) ) { // OpenAI-Responses 账户的限流检查 const isRateLimitCleared = await openaiResponsesAccountService.checkAndClearRateLimit( @@ -283,7 +303,7 @@ class UnifiedOpenAIScheduler { ) // 如果仍然处于限流状态,跳过 - if (account.rateLimitStatus === 'limited' && !isRateLimitCleared) { + if (this._isRateLimited(account.rateLimitStatus) && !isRateLimitCleared) { logger.debug(`⏭️ Skipping OpenAI-Responses account ${account.name} - rate limited`) continue } @@ -350,7 +370,7 @@ class UnifiedOpenAIScheduler { // 检查并清除过期的限流状态 const isRateLimitCleared = await openaiResponsesAccountService.checkAndClearRateLimit(accountId) - return account.rateLimitStatus !== 'limited' || isRateLimitCleared + return !this._isRateLimited(account.rateLimitStatus) || isRateLimitCleared } return false } catch (error) { @@ -504,7 +524,7 @@ class UnifiedOpenAIScheduler { return false } - if (account.rateLimitStatus === 'limited') { + if (this._isRateLimited(account.rateLimitStatus)) { // 如果有具体的重置时间,使用它 if (account.rateLimitResetAt) { const resetTime = new Date(account.rateLimitResetAt).getTime()