fix: 修复限流状态判断逻辑,兼容对象和字符串格式

- 修复 cleanupOpenAIAccounts 方法中 rateLimitStatus 判断问题
- 修复 cleanupClaudeConsoleAccounts 方法中的判断逻辑
- 优化 unifiedOpenAIScheduler 的 _isRateLimited 辅助方法
- 保持原始服务层数据获取方式,通过判断逻辑适配不同数据格式

问题原因:服务层返回的 rateLimitStatus 是对象格式,但清理逻辑使用字符串比较,
导致限流账户无法被正确检测和自动恢复。

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shaw
2025-09-14 12:20:10 +08:00
parent 158a9b9a31
commit c0d6ecefac
2 changed files with 52 additions and 15 deletions

View File

@@ -134,11 +134,18 @@ class RateLimitCleanupService {
*/ */
async cleanupOpenAIAccounts(result) { async cleanupOpenAIAccounts(result) {
try { try {
// 使用服务层获取账户数据
const accounts = await openaiAccountService.getAllAccounts() const accounts = await openaiAccountService.getAllAccounts()
for (const account of accounts) { 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++ result.checked++
try { try {
@@ -180,17 +187,20 @@ class RateLimitCleanupService {
*/ */
async cleanupClaudeAccounts(result) { async cleanupClaudeAccounts(result) {
try { try {
// 使用原始数据而不是处理过的数据,避免字段被转换 // 使用 Redis 获取账户数据
const redis = require('../models/redis') const redis = require('../models/redis')
const accounts = await redis.getAllClaudeAccounts() const accounts = await redis.getAllClaudeAccounts()
for (const account of accounts) { for (const account of accounts) {
// 检查所有可能处于限流状态的账号,包括自动停止的账号 // 检查是否处于限流状态(兼容对象和字符串格式)
if ( const isRateLimited =
account.rateLimitStatus === 'limited' || account.rateLimitStatus === 'limited' ||
account.rateLimitedAt || (account.rateLimitStatus &&
account.rateLimitAutoStopped === 'true' typeof account.rateLimitStatus === 'object' &&
) { account.rateLimitStatus.status === 'limited')
// 检查所有可能处于限流状态的账号,包括自动停止的账号
if (isRateLimited || account.rateLimitedAt || account.rateLimitAutoStopped === 'true') {
result.checked++ result.checked++
try { try {
@@ -265,14 +275,21 @@ class RateLimitCleanupService {
*/ */
async cleanupClaudeConsoleAccounts(result) { async cleanupClaudeConsoleAccounts(result) {
try { try {
// 使用服务层获取账户数据
const accounts = await claudeConsoleAccountService.getAllAccounts() const accounts = await claudeConsoleAccountService.getAllAccounts()
for (const account of accounts) { for (const account of accounts) {
// 检查是否处于限流状态(兼容对象和字符串格式)
const isRateLimited =
account.rateLimitStatus === 'limited' ||
(account.rateLimitStatus &&
typeof account.rateLimitStatus === 'object' &&
account.rateLimitStatus.status === 'limited')
// 检查两种状态字段rateLimitStatus 和 status // 检查两种状态字段rateLimitStatus 和 status
const hasRateLimitStatus = account.rateLimitStatus === 'limited'
const hasStatusRateLimited = account.status === 'rate_limited' const hasStatusRateLimited = account.status === 'rate_limited'
if (hasRateLimitStatus || hasStatusRateLimited) { if (isRateLimited || hasStatusRateLimited) {
result.checked++ result.checked++
try { try {
@@ -285,7 +302,7 @@ class RateLimitCleanupService {
result.cleared++ result.cleared++
// 如果 status 字段是 rate_limited需要额外清理 // 如果 status 字段是 rate_limited需要额外清理
if (hasStatusRateLimited && !hasRateLimitStatus) { if (hasStatusRateLimited && !isRateLimited) {
await claudeConsoleAccountService.updateAccount(account.id, { await claudeConsoleAccountService.updateAccount(account.id, {
status: 'active' status: 'active'
}) })

View File

@@ -19,6 +19,26 @@ class UnifiedOpenAIScheduler {
return schedulable !== false && schedulable !== 'false' 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账号 // 🎯 统一调度OpenAI账号
async selectAccountForApiKey(apiKeyData, sessionHash = null, requestedModel = null) { async selectAccountForApiKey(apiKeyData, sessionHash = null, requestedModel = null) {
try { try {
@@ -63,7 +83,7 @@ class UnifiedOpenAIScheduler {
} }
} else if ( } else if (
accountType === 'openai-responses' && accountType === 'openai-responses' &&
boundAccount.rateLimitStatus === 'limited' this._isRateLimited(boundAccount.rateLimitStatus)
) { ) {
// OpenAI-Responses 账户的限流检查 // OpenAI-Responses 账户的限流检查
const isRateLimitCleared = await openaiResponsesAccountService.checkAndClearRateLimit( 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`) logger.debug(`⏭️ Skipping OpenAI-Responses account ${account.name} - rate limited`)
continue continue
} }
@@ -350,7 +370,7 @@ class UnifiedOpenAIScheduler {
// 检查并清除过期的限流状态 // 检查并清除过期的限流状态
const isRateLimitCleared = const isRateLimitCleared =
await openaiResponsesAccountService.checkAndClearRateLimit(accountId) await openaiResponsesAccountService.checkAndClearRateLimit(accountId)
return account.rateLimitStatus !== 'limited' || isRateLimitCleared return !this._isRateLimited(account.rateLimitStatus) || isRateLimitCleared
} }
return false return false
} catch (error) { } catch (error) {
@@ -504,7 +524,7 @@ class UnifiedOpenAIScheduler {
return false return false
} }
if (account.rateLimitStatus === 'limited') { if (this._isRateLimited(account.rateLimitStatus)) {
// 如果有具体的重置时间,使用它 // 如果有具体的重置时间,使用它
if (account.rateLimitResetAt) { if (account.rateLimitResetAt) {
const resetTime = new Date(account.rateLimitResetAt).getTime() const resetTime = new Date(account.rateLimitResetAt).getTime()