refactor: 统一账户过期时间字段映射和检查逻辑

主要改进:
1. 创建 mapExpiryField() 工具函数统一处理前后端字段映射(expiresAt -> subscriptionExpiresAt)
2. 统一 subscriptionExpiresAt 初始值为 null(替代空字符串)
3. 规范过期检查方法名为 isSubscriptionExpired(),返回 true 表示已过期
4. 优化过期检查条件判断,只检查 null 而非空字符串
5. 补充 OpenAI-Responses 和调度器中缺失的过期检查逻辑
6. 添加代码评审文档记录未修复问题

影响范围:
- 所有 9 种账户服务的过期字段处理
- admin.js 中所有账户更新路由
- 统一调度器的过期账户过滤逻辑

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
mrlitong
2025-10-14 08:04:05 +00:00
parent ba60a2dcbb
commit cbc3a83f11
13 changed files with 410 additions and 82 deletions

View File

@@ -32,6 +32,26 @@ const ProxyHelper = require('../utils/proxyHelper')
const router = express.Router()
// 🛠️ 工具函数:映射前端字段名到后端字段名
/**
* 映射前端的 expiresAt 字段到后端的 subscriptionExpiresAt 字段
* @param {Object} updates - 更新对象
* @param {string} accountType - 账户类型 (如 'Claude', 'OpenAI' 等)
* @param {string} accountId - 账户 ID
* @returns {Object} 映射后的更新对象
*/
function mapExpiryField(updates, accountType, accountId) {
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
logger.info(
`Mapping expiresAt to subscriptionExpiresAt for ${accountType} account ${accountId}`
)
}
return mappedUpdates
}
// 👥 用户管理
// 获取所有用户列表用于API Key分配
@@ -2399,11 +2419,7 @@ router.put('/claude-accounts/:accountId', authenticateAdmin, async (req, res) =>
}
// 映射字段名前端的expiresAt -> 后端的subscriptionExpiresAt
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
}
const mappedUpdates = mapExpiryField(updates, 'Claude', accountId)
await claudeAccountService.updateAccount(accountId, mappedUpdates)
@@ -2746,14 +2762,7 @@ router.put('/claude-console-accounts/:accountId', authenticateAdmin, async (req,
const updates = req.body
// ✅ 【新增】映射字段名:前端的 expiresAt -> 后端的 subscriptionExpiresAt
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
logger.info(
`Mapping expiresAt to subscriptionExpiresAt for Claude Console account ${accountId}`
)
}
const mappedUpdates = mapExpiryField(updates, 'Claude Console', accountId)
// 验证priority的有效性1-100
if (
@@ -3172,12 +3181,7 @@ router.put('/ccr-accounts/:accountId', authenticateAdmin, async (req, res) => {
const updates = req.body
// ✅ 【新增】映射字段名:前端的 expiresAt -> 后端的 subscriptionExpiresAt
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
logger.info(`Mapping expiresAt to subscriptionExpiresAt for CCR account ${accountId}`)
}
const mappedUpdates = mapExpiryField(updates, 'CCR', accountId)
// 验证priority的有效性1-100
if (
@@ -3575,12 +3579,7 @@ router.put('/bedrock-accounts/:accountId', authenticateAdmin, async (req, res) =
const updates = req.body
// ✅ 【新增】映射字段名:前端的 expiresAt -> 后端的 subscriptionExpiresAt
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
logger.info(`Mapping expiresAt to subscriptionExpiresAt for Bedrock account ${accountId}`)
}
const mappedUpdates = mapExpiryField(updates, 'Bedrock', accountId)
// 验证priority的有效性1-100
if (
@@ -4047,12 +4046,7 @@ router.put('/gemini-accounts/:accountId', authenticateAdmin, async (req, res) =>
}
// ✅ 【新增】映射字段名:前端的 expiresAt -> 后端的 subscriptionExpiresAt
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
logger.info(`Mapping expiresAt to subscriptionExpiresAt for Gemini account ${accountId}`)
}
const mappedUpdates = mapExpiryField(updates, 'Gemini', accountId)
// 处理分组的变更
if (mappedUpdates.accountType !== undefined) {
@@ -7430,12 +7424,7 @@ router.put('/openai-accounts/:id', authenticateAdmin, async (req, res) => {
const updates = req.body
// ✅ 【新增】映射字段名:前端的 expiresAt -> 后端的 subscriptionExpiresAt
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
logger.info(`Mapping expiresAt to subscriptionExpiresAt for OpenAI account ${id}`)
}
const mappedUpdates = mapExpiryField(updates, 'OpenAI', id)
const { needsImmediateRefresh, requireRefreshSuccess } = mappedUpdates
@@ -7988,12 +7977,7 @@ router.put('/azure-openai-accounts/:id', authenticateAdmin, async (req, res) =>
const updates = req.body
// ✅ 【新增】映射字段名:前端的 expiresAt -> 后端的 subscriptionExpiresAt
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
logger.info(`Mapping expiresAt to subscriptionExpiresAt for Azure OpenAI account ${id}`)
}
const mappedUpdates = mapExpiryField(updates, 'Azure OpenAI', id)
const account = await azureOpenaiAccountService.updateAccount(id, mappedUpdates)
@@ -8357,12 +8341,7 @@ router.put('/openai-responses-accounts/:id', authenticateAdmin, async (req, res)
const updates = req.body
// ✅ 【新增】映射字段名:前端的 expiresAt -> 后端的 subscriptionExpiresAt
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
logger.info(`Mapping expiresAt to subscriptionExpiresAt for OpenAI-Responses account ${id}`)
}
const mappedUpdates = mapExpiryField(updates, 'OpenAI-Responses', id)
// 验证priority的有效性1-100
if (mappedUpdates.priority !== undefined) {
@@ -8838,12 +8817,7 @@ router.put('/droid-accounts/:id', authenticateAdmin, async (req, res) => {
const updates = { ...req.body }
// ✅ 【新增】映射字段名:前端的 expiresAt -> 后端的 subscriptionExpiresAt
const mappedUpdates = { ...updates }
if ('expiresAt' in mappedUpdates) {
mappedUpdates.subscriptionExpiresAt = mappedUpdates.expiresAt
delete mappedUpdates.expiresAt
logger.info(`Mapping expiresAt to subscriptionExpiresAt for Droid account ${id}`)
}
const mappedUpdates = mapExpiryField(updates, 'Droid', id)
const { accountType: rawAccountType, groupId, groupIds } = mappedUpdates