feat: 完善账户管理和仪表盘功能

- 修改使用记录API路由路径为 /dashboard/usage-records
- 增加对更多账户类型的支持(Bedrock、Azure、Droid、CCR等)
- 修复Codex模型识别逻辑,避免 gpt-5-codex 系列被错误归一化
- 在账户管理页面添加状态过滤器(正常/异常)
- 在账户管理页面添加限流时间过滤器(≤1h/5h/12h/1d)
- 增加账户统计汇总弹窗,按平台分类展示
- 完善仪表盘使用记录展示功能,支持分页加载
- 将 logs1/ 目录添加到 .gitignore

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
IanShaw027
2025-12-03 23:08:44 -08:00
committed by IanShaw027
parent 81971436e6
commit 3db268fff7
5 changed files with 374 additions and 206 deletions

View File

@@ -705,7 +705,7 @@ router.post('/cleanup', authenticateAdmin, async (req, res) => {
})
// 📊 获取最近的使用记录
router.get('/usage-records', authenticateAdmin, async (req, res) => {
router.get('/dashboard/usage-records', authenticateAdmin, async (req, res) => {
try {
const { limit = 100, offset = 0 } = req.query
const limitNum = Math.min(parseInt(limit) || 100, 500) // 最多500条
@@ -774,8 +774,44 @@ router.get('/usage-records', authenticateAdmin, async (req, res) => {
return
}
// 其他平台账户...
accountNameMap[accountId] = accountId // 降级显示ID
const bedrockAcc = await redis.getBedrockAccount(accountId)
if (bedrockAcc && bedrockAcc.name) {
accountNameMap[accountId] = bedrockAcc.name
return
}
const azureAcc = await redis.getAzureOpenaiAccount(accountId)
if (azureAcc && azureAcc.name) {
accountNameMap[accountId] = azureAcc.name
return
}
const openaiResponsesAcc = await redis.getOpenaiResponsesAccount(accountId)
if (openaiResponsesAcc && openaiResponsesAcc.name) {
accountNameMap[accountId] = openaiResponsesAcc.name
return
}
const droidAcc = await redis.getDroidAccount(accountId)
if (droidAcc && droidAcc.name) {
accountNameMap[accountId] = droidAcc.name
return
}
const ccrAcc = await redis.getCcrAccount(accountId)
if (ccrAcc && ccrAcc.name) {
accountNameMap[accountId] = ccrAcc.name
return
}
const openaiAcc = await redis.getOpenaiAccount(accountId)
if (openaiAcc && openaiAcc.name) {
accountNameMap[accountId] = openaiAcc.name
return
}
// 降级显示ID
accountNameMap[accountId] = accountId
} catch (error) {
accountNameMap[accountId] = accountId
}

View File

@@ -247,9 +247,11 @@ const handleResponses = async (req, res) => {
// 从请求体中提取模型和流式标志
let requestedModel = req.body?.model || null
const isCodexModel =
typeof requestedModel === 'string' && requestedModel.toLowerCase().includes('codex')
// 如果模型是 gpt-5 开头且后面还有内容(如 gpt-5-2025-08-07则覆盖为 gpt-5
if (requestedModel && requestedModel.startsWith('gpt-5-') && requestedModel !== 'gpt-5-codex') {
// 如果模型是 gpt-5 开头且后面还有内容(如 gpt-5-2025-08-07并且不是 Codex 系列,则覆盖为 gpt-5
if (requestedModel && requestedModel.startsWith('gpt-5-') && !isCodexModel) {
logger.info(`📝 Model ${requestedModel} detected, normalizing to gpt-5 for Codex API`)
requestedModel = 'gpt-5'
req.body.model = 'gpt-5' // 同时更新请求体中的模型