Improve gateway transport and usage runtime

This commit is contained in:
elky
2026-06-25 22:36:27 +08:00
parent d336d1a7fa
commit 6f00e9fc67
112 changed files with 12456 additions and 1387 deletions
@@ -65,6 +65,21 @@ function buildUsageRecord(overrides: Partial<UsageRecord> = {}): UsageRecord {
}
}
function createDeferred<T>() {
let resolve!: (value: T) => void
let reject!: (reason?: unknown) => void
const promise = new Promise<T>((resolvePromise, rejectPromise) => {
resolve = resolvePromise
reject = rejectPromise
})
return { promise, resolve, reject }
}
async function flushMicrotasks() {
await Promise.resolve()
await Promise.resolve()
}
describe('useUsageData', () => {
beforeEach(() => {
vi.clearAllMocks()
@@ -342,6 +357,45 @@ describe('useUsageData', () => {
expect(totalRecords.value).toBe(122101)
})
it('keeps the exact admin record total while a later page returns an estimate', 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(8650)
await loadRecords({ page: 1, pageSize: 20 }, undefined, dateRange)
await flushMicrotasks()
expect(totalRecords.value).toBe(8650)
const exactTotal = createDeferred<number>()
getAllUsageRecordsMock.mockResolvedValueOnce({
records: [buildUsageRecord({ id: 'usage-2' })],
total: 41,
total_is_estimated: true,
limit: 20,
offset: 20,
})
getAllUsageRecordTotalMock.mockReturnValueOnce(exactTotal.promise)
await loadRecords({ page: 2, pageSize: 20 }, undefined, dateRange)
expect(totalRecords.value).toBe(8650)
exactTotal.resolve(8651)
await flushMicrotasks()
expect(totalRecords.value).toBe(8651)
})
it('continues loading admin breakdowns when the summary request fails', async () => {
const isAdminPage = ref(true)
const {
@@ -62,6 +62,8 @@ export function useUsageData(options: UseUsageDataOptions) {
const currentDateRange = ref<DateRangeParams | undefined>(undefined)
let loadStatsRequestId = 0
let loadRecordsRequestId = 0
let currentAdminRecordTotalState: { key: string; exact: boolean } | null = null
const adminRecordExactTotalCache = new Map<string, number>()
// 可用的筛选选项(从统计数据获取,而不是从记录中)
const availableModels = ref<string[]>([])
@@ -77,6 +79,47 @@ export function useUsageData(options: UseUsageDataOptions) {
}))
})
function buildAdminRecordTotalKey(params: Record<string, unknown>): string {
return JSON.stringify(
Object.entries(params)
.filter(([key, value]) =>
!['limit', 'offset', 'include_total', 'total_only'].includes(key) &&
value !== undefined &&
value !== null &&
value !== ''
)
.sort(([left], [right]) => left.localeCompare(right))
)
}
function applyAdminRecordTotal(
totalKey: string,
total: number,
totalIsEstimated: boolean
) {
const normalizedTotal = Number.isFinite(total) && total > 0 ? total : 0
if (!totalIsEstimated) {
totalRecords.value = normalizedTotal
currentAdminRecordTotalState = { key: totalKey, exact: true }
adminRecordExactTotalCache.set(totalKey, normalizedTotal)
return
}
const cachedExactTotal = adminRecordExactTotalCache.get(totalKey)
if (cachedExactTotal !== undefined) {
totalRecords.value = cachedExactTotal
currentAdminRecordTotalState = { key: totalKey, exact: true }
return
}
const previousTotal = currentAdminRecordTotalState?.key === totalKey
? totalRecords.value
: 0
totalRecords.value = Math.max(previousTotal, normalizedTotal)
currentAdminRecordTotalState = { key: totalKey, exact: false }
}
// 加载统计数据(不加载记录)
async function loadStats(dateRange?: DateRangeParams, options: LoadStatsOptions = {}): Promise<boolean> {
const requestId = ++loadStatsRequestId
@@ -375,9 +418,10 @@ export function useUsageData(options: UseUsageDataOptions) {
}
const nextRecords = (response.records || []) as UsageRecord[]
currentRecords.value = mergeRecordStatus(currentRecords.value, nextRecords)
totalRecords.value = response.total || 0
const totalKey = buildAdminRecordTotalKey(params)
applyAdminRecordTotal(totalKey, response.total ?? 0, response.total_is_estimated === true)
if (response.total_is_estimated === true) {
void refreshAdminRecordTotal(params, requestId)
void refreshAdminRecordTotal(params, requestId, totalKey)
}
} else {
// 用户页面:使用用户 API
@@ -405,11 +449,14 @@ export function useUsageData(options: UseUsageDataOptions) {
async function refreshAdminRecordTotal(
params: Record<string, unknown>,
requestId: number
requestId: number,
totalKey: string
): Promise<void> {
try {
const total = await usageApi.getAllUsageRecordTotal(params)
if (requestId === loadRecordsRequestId) {
adminRecordExactTotalCache.set(totalKey, total)
currentAdminRecordTotalState = { key: totalKey, exact: true }
totalRecords.value = total
}
} catch (error) {