fix(monitor): fix statistics logic issues in monitor charts

Changes:
- Fix hourly token chart incorrectly counting failed request tokens
- Fix daily trend chart using UTC date causing cross-day statistics errors
- Daily trend chart now distinguishes success/failed request counts
- Token statistics only count successful requests for more accurate data

Modified files:
- src/components/monitor/DailyTrendChart.tsx (modified)
- src/components/monitor/HourlyTokenChart.tsx (modified)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kongkongyo
2026-01-14 17:34:37 +08:00
parent 76bfa26d3e
commit 5123d254f2
2 changed files with 31 additions and 6 deletions

View File

@@ -14,6 +14,8 @@ interface DailyTrendChartProps {
interface DailyStat {
date: string;
requests: number;
successRequests: number;
failedRequests: number;
inputTokens: number;
outputTokens: number;
reasoningTokens: number;
@@ -29,19 +31,33 @@ export function DailyTrendChart({ data, loading, isDark, timeRange }: DailyTrend
const dailyStats: Record<string, {
requests: number;
successRequests: number;
failedRequests: number;
inputTokens: number;
outputTokens: number;
reasoningTokens: number;
cachedTokens: number;
}> = {};
// 辅助函数:获取本地日期字符串 YYYY-MM-DD
const getLocalDateString = (timestamp: string): string => {
const date = new Date(timestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
Object.values(data.apis).forEach((apiData) => {
Object.values(apiData.models).forEach((modelData) => {
modelData.details.forEach((detail) => {
const date = new Date(detail.timestamp).toISOString().split('T')[0];
// 使用本地日期而非 UTC 日期
const date = getLocalDateString(detail.timestamp);
if (!dailyStats[date]) {
dailyStats[date] = {
requests: 0,
successRequests: 0,
failedRequests: 0,
inputTokens: 0,
outputTokens: 0,
reasoningTokens: 0,
@@ -49,10 +65,16 @@ export function DailyTrendChart({ data, loading, isDark, timeRange }: DailyTrend
};
}
dailyStats[date].requests++;
dailyStats[date].inputTokens += detail.tokens.input_tokens || 0;
dailyStats[date].outputTokens += detail.tokens.output_tokens || 0;
dailyStats[date].reasoningTokens += detail.tokens.reasoning_tokens || 0;
dailyStats[date].cachedTokens += detail.tokens.cached_tokens || 0;
if (detail.failed) {
dailyStats[date].failedRequests++;
} else {
dailyStats[date].successRequests++;
// 只统计成功请求的 Token
dailyStats[date].inputTokens += detail.tokens.input_tokens || 0;
dailyStats[date].outputTokens += detail.tokens.output_tokens || 0;
dailyStats[date].reasoningTokens += detail.tokens.reasoning_tokens || 0;
dailyStats[date].cachedTokens += detail.tokens.cached_tokens || 0;
}
});
});
});

View File

@@ -48,10 +48,13 @@ export function HourlyTokenChart({ data, loading, isDark }: HourlyTokenChartProp
hourlyStats[hour] = { total: 0, input: 0, output: 0, reasoning: 0, cached: 0 };
});
// 收集每小时的 Token 数据
// 收集每小时的 Token 数据(只统计成功请求)
Object.values(data.apis).forEach((apiData) => {
Object.values(apiData.models).forEach((modelData) => {
modelData.details.forEach((detail) => {
// 跳过失败请求,失败请求的 Token 数据不准确
if (detail.failed) return;
const timestamp = new Date(detail.timestamp);
if (timestamp < cutoffTime) return;