feat: 优化仪表盘数据展示和用户体验

- 优化实时TPM显示格式,添加智能单位(K/M)
- 折线图tooltip添加token数单位格式化
- API Keys使用趋势图增加USD金额计算和显示
- 实现鼠标悬浮时数据倒序展示,前3名添加🥇🥈🥉标识
- 后端API添加费用计算支持
- 删除测试脚本,保持代码整洁

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shaw
2025-07-30 15:07:05 +08:00
parent 21461863af
commit 321be986a6
4 changed files with 102 additions and 84 deletions

View File

@@ -1073,7 +1073,7 @@ class RedisClient {
totalCacheReadTokens
};
logger.debug(`🔍 Realtime metrics - Final result:`, result);
logger.debug('🔍 Realtime metrics - Final result:', result);
return result;
} catch (error) {

View File

@@ -1986,15 +1986,27 @@ router.get('/api-keys-usage-trend', authenticateAdmin, async (req, res) => {
const data = await client.hgetall(key);
if (data && apiKeyMap.has(apiKeyId)) {
const totalTokens = (parseInt(data.inputTokens) || 0) +
(parseInt(data.outputTokens) || 0) +
(parseInt(data.cacheCreateTokens) || 0) +
(parseInt(data.cacheReadTokens) || 0);
const inputTokens = parseInt(data.inputTokens) || 0;
const outputTokens = parseInt(data.outputTokens) || 0;
const cacheCreateTokens = parseInt(data.cacheCreateTokens) || 0;
const cacheReadTokens = parseInt(data.cacheReadTokens) || 0;
const totalTokens = inputTokens + outputTokens + cacheCreateTokens + cacheReadTokens;
// 计算费用 - 使用默认模型价格,因为小时级别的数据没有模型信息
const usage = {
input_tokens: inputTokens,
output_tokens: outputTokens,
cache_creation_input_tokens: cacheCreateTokens,
cache_read_input_tokens: cacheReadTokens
};
const costResult = CostCalculator.calculateCost(usage, 'claude-3-5-haiku-20241022');
hourData.apiKeys[apiKeyId] = {
name: apiKeyMap.get(apiKeyId).name,
tokens: totalTokens,
requests: parseInt(data.requests) || 0
requests: parseInt(data.requests) || 0,
cost: costResult.costs.total,
formattedCost: costResult.formatted.total
};
}
}
@@ -2031,15 +2043,27 @@ router.get('/api-keys-usage-trend', authenticateAdmin, async (req, res) => {
const data = await client.hgetall(key);
if (data && apiKeyMap.has(apiKeyId)) {
const totalTokens = (parseInt(data.inputTokens) || 0) +
(parseInt(data.outputTokens) || 0) +
(parseInt(data.cacheCreateTokens) || 0) +
(parseInt(data.cacheReadTokens) || 0);
const inputTokens = parseInt(data.inputTokens) || 0;
const outputTokens = parseInt(data.outputTokens) || 0;
const cacheCreateTokens = parseInt(data.cacheCreateTokens) || 0;
const cacheReadTokens = parseInt(data.cacheReadTokens) || 0;
const totalTokens = inputTokens + outputTokens + cacheCreateTokens + cacheReadTokens;
// 计算费用 - 使用默认模型价格,因为日级别的汇总数据没有模型信息
const usage = {
input_tokens: inputTokens,
output_tokens: outputTokens,
cache_creation_input_tokens: cacheCreateTokens,
cache_read_input_tokens: cacheReadTokens
};
const costResult = CostCalculator.calculateCost(usage, 'claude-3-5-haiku-20241022');
dayData.apiKeys[apiKeyId] = {
name: apiKeyMap.get(apiKeyId).name,
tokens: totalTokens,
requests: parseInt(data.requests) || 0
requests: parseInt(data.requests) || 0,
cost: costResult.costs.total,
formattedCost: costResult.formatted.total
};
}
}