diff --git a/packages/admin-client/src/features/conversations/application/useConversations.ts b/packages/admin-client/src/features/conversations/application/useConversations.ts
index 3d7669e..2e5bb74 100644
--- a/packages/admin-client/src/features/conversations/application/useConversations.ts
+++ b/packages/admin-client/src/features/conversations/application/useConversations.ts
@@ -64,4 +64,6 @@ export type {
ConversationStatistics,
ConversationQueryParams,
TokenDetails,
+ GlobalTokenStats,
+ TodayTokenStats,
} from '../infrastructure/conversations.api';
diff --git a/packages/admin-client/src/features/conversations/infrastructure/conversations.api.ts b/packages/admin-client/src/features/conversations/infrastructure/conversations.api.ts
index 269ddb1..4176b85 100644
--- a/packages/admin-client/src/features/conversations/infrastructure/conversations.api.ts
+++ b/packages/admin-client/src/features/conversations/infrastructure/conversations.api.ts
@@ -78,6 +78,24 @@ export interface PaginatedConversations {
totalPages: number;
}
+export interface GlobalTokenStats {
+ totalInputTokens: number;
+ totalOutputTokens: number;
+ totalCacheCreationTokens: number;
+ totalCacheReadTokens: number;
+ totalTokens: number;
+ totalEstimatedCost: number;
+ totalApiCalls: number;
+}
+
+export interface TodayTokenStats {
+ totalInputTokens: number;
+ totalOutputTokens: number;
+ totalTokens: number;
+ totalEstimatedCost: number;
+ totalApiCalls: number;
+}
+
export interface ConversationStatistics {
total: number;
active: number;
@@ -85,6 +103,8 @@ export interface ConversationStatistics {
converted: number;
todayCount: number;
conversionRate: string;
+ tokenStats: GlobalTokenStats;
+ todayTokenStats: TodayTokenStats;
}
export interface ConversationQueryParams {
diff --git a/packages/admin-client/src/features/conversations/presentation/pages/ConversationsPage.tsx b/packages/admin-client/src/features/conversations/presentation/pages/ConversationsPage.tsx
index 4829850..a16b30c 100644
--- a/packages/admin-client/src/features/conversations/presentation/pages/ConversationsPage.tsx
+++ b/packages/admin-client/src/features/conversations/presentation/pages/ConversationsPage.tsx
@@ -22,6 +22,9 @@ import {
GlobalOutlined,
LaptopOutlined,
BarChartOutlined,
+ DollarOutlined,
+ ApiOutlined,
+ ThunderboltOutlined,
} from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import dayjs from 'dayjs';
@@ -196,7 +199,7 @@ export function ConversationsPage() {
对话管理
- {/* Statistics Cards */}
+ {/* Statistics Cards - Conversations */}
@@ -251,6 +254,76 @@ export function ConversationsPage() {
+ {/* Token Usage Statistics */}
+
+
+
+
+ }
+ valueStyle={{ color: '#13c2c2', fontSize: 20 }}
+ formatter={(value) => formatNumber(value as number)}
+ />
+
+ 输入: {formatNumber(stats?.tokenStats?.totalInputTokens ?? 0)} |
+ 输出: {formatNumber(stats?.tokenStats?.totalOutputTokens ?? 0)}
+
+
+
+
+
+
+
+ }
+ precision={2}
+ valueStyle={{ color: '#eb2f96', fontSize: 20 }}
+ />
+
+ API 调用: {formatNumber(stats?.tokenStats?.totalApiCalls ?? 0)} 次
+
+
+
+
+
+
+
+ }
+ valueStyle={{ color: '#1890ff', fontSize: 20 }}
+ formatter={(value) => formatNumber(value as number)}
+ />
+
+ 输入: {formatNumber(stats?.todayTokenStats?.totalInputTokens ?? 0)} |
+ 输出: {formatNumber(stats?.todayTokenStats?.totalOutputTokens ?? 0)}
+
+
+
+
+
+
+
+ }
+ precision={4}
+ valueStyle={{ color: '#fa8c16', fontSize: 20 }}
+ />
+
+ API 调用: {formatNumber(stats?.todayTokenStats?.totalApiCalls ?? 0)} 次
+
+
+
+
+
+
{/* Filters */}
@@ -542,3 +615,14 @@ function parseUserAgent(ua: string): string {
return `${browser} / ${os}`;
}
+
+// Helper function to format large numbers (e.g., 1234567 -> 1.23M)
+function formatNumber(num: number): string {
+ if (num >= 1000000) {
+ return (num / 1000000).toFixed(2) + 'M';
+ }
+ if (num >= 1000) {
+ return (num / 1000).toFixed(1) + 'K';
+ }
+ return num.toString();
+}
diff --git a/packages/services/conversation-service/src/adapters/inbound/admin-conversation.controller.ts b/packages/services/conversation-service/src/adapters/inbound/admin-conversation.controller.ts
index ec4bb3d..4d05189 100644
--- a/packages/services/conversation-service/src/adapters/inbound/admin-conversation.controller.ts
+++ b/packages/services/conversation-service/src/adapters/inbound/admin-conversation.controller.ts
@@ -323,7 +323,7 @@ export class AdminConversationController {
}
/**
- * 获取对话统计
+ * 获取对话统计(包含 Token 汇总)
*/
@Get('statistics/overview')
async getStatistics(@Headers('authorization') auth: string) {
@@ -342,6 +342,33 @@ export class AdminConversationController {
.where('c.created_at >= :today', { today })
.getCount();
+ // 全局 Token 统计
+ const allTokenStats = await this.tokenUsageRepo
+ .createQueryBuilder('t')
+ .select([
+ 'SUM(t.input_tokens) as "totalInputTokens"',
+ 'SUM(t.output_tokens) as "totalOutputTokens"',
+ 'SUM(t.cache_creation_tokens) as "totalCacheCreationTokens"',
+ 'SUM(t.cache_read_tokens) as "totalCacheReadTokens"',
+ 'SUM(t.total_tokens) as "totalTokens"',
+ 'SUM(t.estimated_cost) as "totalEstimatedCost"',
+ 'COUNT(*) as "totalApiCalls"',
+ ])
+ .getRawOne();
+
+ // 今日 Token 统计
+ const todayTokenStats = await this.tokenUsageRepo
+ .createQueryBuilder('t')
+ .select([
+ 'SUM(t.input_tokens) as "totalInputTokens"',
+ 'SUM(t.output_tokens) as "totalOutputTokens"',
+ 'SUM(t.total_tokens) as "totalTokens"',
+ 'SUM(t.estimated_cost) as "totalEstimatedCost"',
+ 'COUNT(*) as "totalApiCalls"',
+ ])
+ .where('t.created_at >= :today', { today })
+ .getRawOne();
+
return {
success: true,
data: {
@@ -351,6 +378,23 @@ export class AdminConversationController {
converted,
todayCount,
conversionRate: total > 0 ? ((converted / total) * 100).toFixed(1) : '0',
+ // Token 汇总统计
+ tokenStats: {
+ totalInputTokens: parseInt(allTokenStats?.totalInputTokens || '0'),
+ totalOutputTokens: parseInt(allTokenStats?.totalOutputTokens || '0'),
+ totalCacheCreationTokens: parseInt(allTokenStats?.totalCacheCreationTokens || '0'),
+ totalCacheReadTokens: parseInt(allTokenStats?.totalCacheReadTokens || '0'),
+ totalTokens: parseInt(allTokenStats?.totalTokens || '0'),
+ totalEstimatedCost: parseFloat(allTokenStats?.totalEstimatedCost || '0'),
+ totalApiCalls: parseInt(allTokenStats?.totalApiCalls || '0'),
+ },
+ todayTokenStats: {
+ totalInputTokens: parseInt(todayTokenStats?.totalInputTokens || '0'),
+ totalOutputTokens: parseInt(todayTokenStats?.totalOutputTokens || '0'),
+ totalTokens: parseInt(todayTokenStats?.totalTokens || '0'),
+ totalEstimatedCost: parseFloat(todayTokenStats?.totalEstimatedCost || '0'),
+ totalApiCalls: parseInt(todayTokenStats?.totalApiCalls || '0'),
+ },
},
};
}