feat(usage): expose request timing details

This commit is contained in:
elky
2026-06-10 09:16:15 +08:00
parent 84f41dae77
commit 8edcbdcb29
41 changed files with 1884 additions and 335 deletions
@@ -3,6 +3,7 @@ import { ref } from 'vue'
const {
getAllUsageRecordsMock,
getAllUsageRecordTotalMock,
getUsageStatsMock,
getUsageByModelMock,
getUsageByProviderMock,
@@ -10,6 +11,7 @@ const {
meGetUsageMock,
} = vi.hoisted(() => ({
getAllUsageRecordsMock: vi.fn(),
getAllUsageRecordTotalMock: vi.fn(),
getUsageStatsMock: vi.fn(),
getUsageByModelMock: vi.fn(),
getUsageByProviderMock: vi.fn(),
@@ -20,6 +22,7 @@ const {
vi.mock('@/api/usage', () => ({
usageApi: {
getAllUsageRecords: getAllUsageRecordsMock,
getAllUsageRecordTotal: getAllUsageRecordTotalMock,
getUsageStats: getUsageStatsMock,
getUsageByModel: getUsageByModelMock,
getUsageByProvider: getUsageByProviderMock,
@@ -72,6 +75,7 @@ describe('useUsageData', () => {
limit: 20,
offset: 0,
})
getAllUsageRecordTotalMock.mockResolvedValue(1)
getUsageStatsMock.mockRejectedValue({
response: { status: 500 },
message: 'stats failed',
@@ -143,6 +147,35 @@ describe('useUsageData', () => {
})
})
it('refreshes exact admin record totals after an estimated first page', async () => {
const isAdminPage = ref(true)
const { loadRecords, totalRecords } = useUsageData({ isAdminPage })
const dateRange = { preset: 'last7days', tz_offset_minutes: 0 }
getAllUsageRecordsMock.mockResolvedValueOnce({
records: [buildUsageRecord()],
total: 21,
total_is_estimated: true,
limit: 20,
offset: 0,
})
getAllUsageRecordTotalMock.mockResolvedValueOnce(122101)
await loadRecords({ page: 1, pageSize: 20 }, undefined, dateRange)
expect(getAllUsageRecordsMock).toHaveBeenCalledWith(expect.objectContaining({
include_total: false,
}))
await Promise.resolve()
await Promise.resolve()
expect(getAllUsageRecordTotalMock).toHaveBeenCalledWith(expect.objectContaining({
preset: 'last7days',
tz_offset_minutes: 0,
}))
expect(totalRecords.value).toBe(122101)
})
it('continues loading admin breakdowns when the summary request fails', async () => {
const isAdminPage = ref(true)
const {
@@ -366,13 +366,19 @@ export function useUsageData(options: UseUsageDataOptions) {
params.hide_unknown = true
}
const response = await usageApi.getAllUsageRecords(params)
const response = await usageApi.getAllUsageRecords({
...params,
include_total: false,
})
if (requestId !== loadRecordsRequestId) {
return
}
const nextRecords = (response.records || []) as UsageRecord[]
currentRecords.value = mergeRecordStatus(currentRecords.value, nextRecords)
totalRecords.value = response.total || 0
if (response.total_is_estimated === true) {
void refreshAdminRecordTotal(params, requestId)
}
} else {
// 用户页面:使用用户 API
const userData = await meApi.getUsage(params)
@@ -397,6 +403,20 @@ export function useUsageData(options: UseUsageDataOptions) {
}
}
async function refreshAdminRecordTotal(
params: Record<string, unknown>,
requestId: number
): Promise<void> {
try {
const total = await usageApi.getAllUsageRecordTotal(params)
if (requestId === loadRecordsRequestId) {
totalRecords.value = total
}
} catch (error) {
log.warn('加载使用记录总数失败:', error)
}
}
function mergePositiveDurationMs(
existingValue: number | null | undefined,
nextValue: number | null | undefined
@@ -513,6 +533,8 @@ export function useUsageData(options: UseUsageDataOptions) {
actual_cost: existing.actual_cost ?? record.actual_cost,
response_time_ms: mergePositiveDurationMs(existing.response_time_ms, record.response_time_ms),
first_byte_time_ms: mergePositiveDurationMs(existing.first_byte_time_ms, record.first_byte_time_ms),
updated_at: existing.updated_at ?? record.updated_at,
response_time_updated_at: existing.response_time_updated_at ?? record.response_time_updated_at,
status_code: existing.status_code ?? record.status_code,
error_message: existing.error_message ?? record.error_message,
image_progress: existing.image_progress ?? record.image_progress,