feat(stats): add user group usage views

This commit is contained in:
wangpengxiang
2026-09-18 16:24:03 +08:00
parent fb25dde4c9
commit a95f0d2488
24 changed files with 1198 additions and 346 deletions
@@ -82,4 +82,35 @@ describe('adminApi analytics cache options', () => {
})
expect(getMock).toHaveBeenNthCalledWith(4, '/api/admin/stats/errors/distribution', { params })
})
it('requests the user group leaderboard with scoped cache parameters', async () => {
const groupParams = {
...params,
metric: 'cost' as const,
offset: 10,
limit: 10,
include_inactive: true,
}
getMock.mockResolvedValueOnce({
data: { items: [], total: 0, metric: 'cost', attribution: 'current_membership' },
})
await expect(adminApi.getLeaderboardUserGroups(groupParams)).resolves.toMatchObject({
attribution: 'current_membership',
})
expect(buildCacheKeyMock).toHaveBeenCalledWith(
'admin:stats:leaderboard:user-groups',
groupParams
)
expect(cachedRequestMock).toHaveBeenCalledWith(
'admin:stats:leaderboard:user-groups',
expect.any(Function),
20 * 1000
)
expect(getMock).toHaveBeenCalledWith('/api/admin/stats/leaderboard/user-groups', {
params: groupParams,
})
})
})
+34
View File
@@ -709,6 +709,8 @@ export interface LeaderboardItem {
requests: number
tokens: number
cost: number
member_count?: number
active_member_count?: number
}
export interface LeaderboardResponse {
@@ -717,6 +719,7 @@ export interface LeaderboardResponse {
metric: string
start_date?: string | null
end_date?: string | null
attribution?: 'current_membership'
}
export interface CostForecastResponse {
@@ -1298,6 +1301,7 @@ export const adminApi = {
model?: string
include_inactive?: boolean
exclude_admin?: boolean
user_group_id?: string
}): Promise<LeaderboardResponse> {
const cacheKey = buildCacheKey('admin:stats:leaderboard:users', params)
return cachedRequest(
@@ -1312,6 +1316,35 @@ export const adminApi = {
)
},
async getLeaderboardUserGroups(params?: {
start_date?: string
end_date?: string
preset?: string
timezone?: string
tz_offset_minutes?: number
metric?: 'requests' | 'tokens' | 'cost'
order?: 'asc' | 'desc'
limit?: number
offset?: number
provider_name?: string
model?: string
include_inactive?: boolean
exclude_admin?: boolean
}): Promise<LeaderboardResponse> {
const cacheKey = buildCacheKey('admin:stats:leaderboard:user-groups', params)
return cachedRequest(
cacheKey,
async () => {
const response = await apiClient.get<LeaderboardResponse>(
'/api/admin/stats/leaderboard/user-groups',
{ params }
)
return response.data
},
20 * 1000
)
},
async getLeaderboardApiKeys(params?: {
start_date?: string
end_date?: string
@@ -1595,6 +1628,7 @@ export const adminApi = {
timezone?: string
tz_offset_minutes?: number
user_id?: string
user_group_id?: string
model?: string
provider_name?: string
},
+1
View File
@@ -134,6 +134,7 @@ export interface UsageByApiFormat {
export interface UsageFilters {
user_id?: string // UUID
user_group_id?: string // UUID
provider_id?: string // UUID
model?: string
search?: string