mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-29 08:11:32 +00:00
feat: 优化分组用量统计
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { get } = vi.hoisted(() => ({
|
||||
get: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/api/client', () => ({
|
||||
apiClient: { get },
|
||||
}))
|
||||
|
||||
import { getUsageSummary } from '@/api/admin/groups'
|
||||
|
||||
describe('admin group usage summary API', () => {
|
||||
beforeEach(() => {
|
||||
get.mockReset()
|
||||
get.mockResolvedValue({ data: [] })
|
||||
})
|
||||
|
||||
it('does not send browser timezone parameters', async () => {
|
||||
const summary = [
|
||||
{ group_id: 1, today_cost: 1.25, yesterday_cost: 2.5, total_cost: 9.75 },
|
||||
]
|
||||
get.mockResolvedValue({ data: summary })
|
||||
|
||||
await expect(getUsageSummary()).resolves.toEqual(summary)
|
||||
|
||||
expect(get).toHaveBeenCalledWith('/admin/groups/usage-summary')
|
||||
})
|
||||
})
|
||||
@@ -446,18 +446,15 @@ export async function clearGroupRPMOverrides(id: number): Promise<{ message: str
|
||||
}
|
||||
|
||||
/**
|
||||
* Get usage summary (today + cumulative cost) for all groups
|
||||
* @param timezone - IANA timezone string (e.g. "Asia/Shanghai")
|
||||
* Get usage summary (today + yesterday + cumulative cost) for all groups
|
||||
* @returns Array of group usage summaries
|
||||
*/
|
||||
export async function getUsageSummary(
|
||||
timezone?: string
|
||||
): Promise<{ group_id: number; today_cost: number; total_cost: number }[]> {
|
||||
export async function getUsageSummary(): Promise<
|
||||
{ group_id: number; today_cost: number; yesterday_cost: number; total_cost: number }[]
|
||||
> {
|
||||
const { data } = await apiClient.get<
|
||||
{ group_id: number; today_cost: number; total_cost: number }[]
|
||||
>('/admin/groups/usage-summary', {
|
||||
params: timezone ? { timezone } : undefined
|
||||
})
|
||||
{ group_id: number; today_cost: number; yesterday_cost: number; total_cost: number }[]
|
||||
>('/admin/groups/usage-summary')
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
@@ -821,6 +821,7 @@ export default {
|
||||
userStatus: 'Status'
|
||||
},
|
||||
usageToday: 'Today',
|
||||
usageYesterday: 'Yesterday',
|
||||
usageTotal: 'Total',
|
||||
accountsAvailable: 'Avail:',
|
||||
accountsRateLimited: 'Limited:',
|
||||
|
||||
@@ -814,6 +814,7 @@ export default {
|
||||
userStatus: '状态'
|
||||
},
|
||||
usageToday: '今日',
|
||||
usageYesterday: '昨日',
|
||||
usageTotal: '累计',
|
||||
accountsAvailable: '可用:',
|
||||
accountsRateLimited: '限流:',
|
||||
|
||||
@@ -333,6 +333,16 @@
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
<div class="text-gray-500 dark:text-gray-400">
|
||||
<span class="text-gray-400 dark:text-gray-500">{{
|
||||
t("admin.groups.usageYesterday")
|
||||
}}</span>
|
||||
<span class="ml-1 font-medium text-gray-700 dark:text-gray-300"
|
||||
>${{
|
||||
formatCost(usageMap.get(row.id)?.yesterday_cost ?? 0)
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
<div class="text-gray-500 dark:text-gray-400">
|
||||
<span class="text-gray-400 dark:text-gray-500">{{
|
||||
t("admin.groups.usageTotal")
|
||||
@@ -4894,6 +4904,7 @@ const groups = ref<AdminGroup[]>([]);
|
||||
const loading = ref(false);
|
||||
type GroupUsageSummary = {
|
||||
today_cost: number;
|
||||
yesterday_cost: number;
|
||||
total_cost: number;
|
||||
};
|
||||
|
||||
@@ -5739,12 +5750,12 @@ const loadUsageSummary = async () => {
|
||||
}
|
||||
usageLoading.value = true;
|
||||
try {
|
||||
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
const data = await adminAPI.groups.getUsageSummary(tz);
|
||||
const data = await adminAPI.groups.getUsageSummary();
|
||||
const map = new Map<number, GroupUsageSummary>();
|
||||
for (const item of data) {
|
||||
map.set(item.group_id, {
|
||||
today_cost: item.today_cost,
|
||||
yesterday_cost: item.yesterday_cost,
|
||||
total_cost: item.total_cost,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ const messages: Record<string, string> = {
|
||||
'admin.groups.columns.usage': 'Usage',
|
||||
'admin.groups.columns.status': 'Status',
|
||||
'admin.groups.columns.actions': 'Actions',
|
||||
'admin.groups.usageToday': 'Today',
|
||||
'admin.groups.usageYesterday': 'Yesterday',
|
||||
'admin.groups.usageTotal': 'Total',
|
||||
}
|
||||
|
||||
vi.mock('@/api/admin', () => ({
|
||||
@@ -151,6 +154,9 @@ const DataTableStub = {
|
||||
<div>
|
||||
<div data-test="columns">{{ columns.map((col) => col.key).join(',') }}</div>
|
||||
<div data-test="rows">{{ data.map((row) => row.name).join(',') }}</div>
|
||||
<div v-if="data.length" data-test="usage-cell">
|
||||
<slot name="cell-usage" :row="data[0]" />
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
}
|
||||
@@ -378,10 +384,26 @@ describe('admin GroupsView column settings', () => {
|
||||
await openColumnSettings(wrapper)
|
||||
await clickColumnToggle(wrapper, 'Usage')
|
||||
expect(getUsageSummary).toHaveBeenCalledTimes(1)
|
||||
expect(getUsageSummary).toHaveBeenCalledWith()
|
||||
expect(getCapacitySummary).not.toHaveBeenCalled()
|
||||
|
||||
await clickColumnToggle(wrapper, 'Capacity')
|
||||
expect(getUsageSummary).toHaveBeenCalledTimes(1)
|
||||
expect(getCapacitySummary).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('renders yesterday usage between today and total', async () => {
|
||||
getUsageSummary.mockResolvedValue([
|
||||
{ group_id: 1, today_cost: 1.25, yesterday_cost: 2.5, total_cost: 9.75 },
|
||||
])
|
||||
|
||||
const wrapper = await mountView()
|
||||
const text = wrapper.get('[data-test="usage-cell"]').text()
|
||||
|
||||
expect(text).toContain('Today$1.25')
|
||||
expect(text).toContain('Yesterday$2.50')
|
||||
expect(text).toContain('Total$9.75')
|
||||
expect(text.indexOf('Today')).toBeLessThan(text.indexOf('Yesterday'))
|
||||
expect(text.indexOf('Yesterday')).toBeLessThan(text.indexOf('Total'))
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user