From 454f366c5059aa88a836bc6043c3f74e526113ca Mon Sep 17 00:00:00 2001 From: litongtongxue Date: Tue, 7 Oct 2025 11:27:29 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=97=A5=E5=9D=87?= =?UTF-8?q?=E8=B4=B9=E7=94=A8=E8=AE=A1=E7=AE=97=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题描述: - 之前的日均费用计算是基于固定的30天窗口,而不是账户实际使用的天数 - 这导致新创建的账户显示的日均费用不准确 修复方案: - 获取账户的创建时间(createdAt字段) - 计算从账户创建到当前时间的实际天数 - 使用实际天数来计算日均费用(30天总费用 / 实际天数) - 在前端显示实际使用天数,让用户了解计算基准 修改内容: - 后端:在 /accounts/:accountId/usage-history 端点中添加实际天数计算逻辑 - 前端:在详情弹窗中显示基于实际使用天数的提示信息 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/routes/admin.js | 51 +++++++++++++++++-- .../accounts/AccountUsageDetailModal.vue | 7 ++- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/routes/admin.js b/src/routes/admin.js index cd79b76f..f55d092d 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -4162,6 +4162,36 @@ router.get('/accounts/:accountId/usage-history', authenticateAdmin, async (req, gemini: 'gemini-1.5-flash' } + // 获取账户信息以获取创建时间 + let accountData = null + let accountCreatedAt = null + + try { + switch (platform) { + case 'claude': + accountData = await claudeAccountService.getAccount(accountId) + break + case 'claude-console': + accountData = await claudeConsoleAccountService.getAccount(accountId) + break + case 'openai': + accountData = await openaiAccountService.getAccount(accountId) + break + case 'openai-responses': + accountData = await openaiResponsesAccountService.getAccount(accountId) + break + case 'gemini': + accountData = await geminiAccountService.getAccount(accountId) + break + } + + if (accountData && accountData.createdAt) { + accountCreatedAt = new Date(accountData.createdAt) + } + } catch (error) { + logger.warn(`Failed to get account data for avgDailyCost calculation: ${error.message}`) + } + const client = redis.getClientSafe() const fallbackModel = fallbackModelMap[platform] || 'unknown' const daysCount = Math.min(Math.max(parseInt(days, 10) || 30, 1), 60) @@ -4281,9 +4311,22 @@ router.get('/accounts/:accountId/usage-history', authenticateAdmin, async (req, }) } - const avgDailyCost = daysCount > 0 ? totalCost / daysCount : 0 - const avgDailyRequests = daysCount > 0 ? totalRequests / daysCount : 0 - const avgDailyTokens = daysCount > 0 ? totalTokens / daysCount : 0 + // 计算实际使用天数(从账户创建到现在) + let actualDaysForAvg = daysCount + if (accountCreatedAt) { + const now = new Date() + const diffTime = Math.abs(now - accountCreatedAt) + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + // 使用实际使用天数,但不超过请求的天数范围 + actualDaysForAvg = Math.min(diffDays, daysCount) + // 至少为1天,避免除零 + actualDaysForAvg = Math.max(actualDaysForAvg, 1) + } + + // 使用实际天数计算日均值 + const avgDailyCost = actualDaysForAvg > 0 ? totalCost / actualDaysForAvg : 0 + const avgDailyRequests = actualDaysForAvg > 0 ? totalRequests / actualDaysForAvg : 0 + const avgDailyTokens = actualDaysForAvg > 0 ? totalTokens / actualDaysForAvg : 0 const todayData = history.length > 0 ? history[history.length - 1] : null @@ -4293,6 +4336,8 @@ router.get('/accounts/:accountId/usage-history', authenticateAdmin, async (req, history, summary: { days: daysCount, + actualDaysUsed: actualDaysForAvg, // 实际使用的天数(用于计算日均值) + accountCreatedAt: accountCreatedAt ? accountCreatedAt.toISOString() : null, totalCost, totalCostFormatted: CostCalculator.formatCost(totalCost), totalRequests, diff --git a/web/admin-spa/src/components/accounts/AccountUsageDetailModal.vue b/web/admin-spa/src/components/accounts/AccountUsageDetailModal.vue index 58c2e75a..0b136972 100644 --- a/web/admin-spa/src/components/accounts/AccountUsageDetailModal.vue +++ b/web/admin-spa/src/components/accounts/AccountUsageDetailModal.vue @@ -38,6 +38,9 @@

近 {{ summary?.days || 30 }} 天内的费用与请求趋势 + + (日均基于实际使用 {{ summary.actualDaysUsed }} 天) +

@@ -443,7 +446,9 @@ const primaryMetrics = computed(() => [ key: 'avgCost', label: '日均费用', value: props.summary?.avgDailyCostFormatted || formatCost(props.summary?.avgDailyCost || 0), - subtitle: '平均每日成本', + subtitle: props.summary?.actualDaysUsed && props.summary?.actualDaysUsed < props.summary?.days + ? `基于 ${props.summary.actualDaysUsed} 天实际使用` + : '平均每日成本', icon: 'fa-wave-square', iconClass: 'text-purple-500' },