fix(admin): 修复 usage 观测全表扫描并下推聚合查询

- 收紧 admin usage/stats 默认时间范围和 active 轮询查询
- 将 summary/records/aggregation/time-series/leaderboard 下推到 SQL 侧
- 统一前端时间参数并补齐 admin usage/stats 回归测试
This commit is contained in:
fawney19
2026-04-16 17:41:32 +08:00
parent 10605d9fb0
commit 65cd9dc3e5
28 changed files with 3085 additions and 381 deletions

View File

@@ -284,7 +284,10 @@ export const usageApi = {
* 获取活跃请求的状态(轻量级接口,用于轮询更新)
* @param ids 可选,逗号分隔的请求 ID 列表
*/
async getActiveRequests(ids?: string[]): Promise<{
async getActiveRequests(
ids?: string[],
timeRange?: Pick<UsageFilters, 'start_date' | 'end_date' | 'preset' | 'timezone' | 'tz_offset_minutes'>
): Promise<{
requests: Array<{
id: string
status: 'pending' | 'streaming' | 'completed' | 'failed' | 'cancelled'
@@ -307,7 +310,25 @@ export const usageApi = {
target_model?: string | null
}>
}> {
const params = ids?.length ? { ids: ids.join(',') } : {}
const params: Record<string, string | number> = {}
if (ids?.length) {
params.ids = ids.join(',')
}
if (timeRange?.start_date) {
params.start_date = timeRange.start_date
}
if (timeRange?.end_date) {
params.end_date = timeRange.end_date
}
if (timeRange?.preset) {
params.preset = timeRange.preset
}
if (timeRange?.timezone) {
params.timezone = timeRange.timezone
}
if (typeof timeRange?.tz_offset_minutes === 'number') {
params.tz_offset_minutes = timeRange.tz_offset_minutes
}
const response = await apiClient.get('/api/admin/usage/active', { params })
return response.data
},

View File

@@ -1,15 +1,5 @@
import type { PeriodValue, DateRangeParams } from '../types'
/**
* 格式化日期为 ISO 格式(不带毫秒,兼容 FastAPI datetime 解析)
*/
function formatDateForApi(date: Date): string {
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}`
}
function getTimezoneParams() {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
const tz_offset_minutes = -new Date().getTimezoneOffset()
@@ -20,37 +10,19 @@ function getTimezoneParams() {
* 根据时间段值计算日期范围
*/
export function getDateRangeFromPeriod(period: PeriodValue): DateRangeParams {
const now = new Date()
let startDate: Date
let endDate = new Date(now)
switch (period) {
case 'today':
startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate())
break
case 'yesterday':
startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1)
endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate())
break
case 'last7days':
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
break
case 'last30days':
startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000)
break
case 'last90days':
startDate = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000)
break
return {
preset: period,
...getTimezoneParams()
}
default:
return {} // 返回空对象表示不过滤时间
}
return {
start_date: formatDateForApi(startDate),
end_date: formatDateForApi(endDate),
preset: period,
...getTimezoneParams()
}
}
/**

View File

@@ -347,7 +347,7 @@ const discoveredActiveRequestIds = new Set<string>()
async function loadActiveRequestUpdates(ids?: string[]) {
if (isAdminPage.value) {
return usageApi.getActiveRequests(ids)
return usageApi.getActiveRequests(ids, timeRange.value)
}
const idsParam = ids?.length ? ids.join(',') : undefined
return meApi.getActiveRequests(idsParam)