feat(accounts): show estimated OpenAI weekly cost

This commit is contained in:
wucm667
2026-09-07 18:37:15 +08:00
parent 909b8c7ef4
commit 35e69af41b
5 changed files with 113 additions and 2 deletions
@@ -134,6 +134,7 @@
:utilization="usageInfo.seven_day.utilization"
:resets-at="usageInfo.seven_day.resets_at"
:window-stats="usageInfo.seven_day.window_stats"
:estimated-total-cost="openAISevenDayEstimatedTotalCost"
:show-now-when-idle="true"
color="emerald"
/>
@@ -784,6 +785,25 @@ const hasOpenAIUsageFallback = computed(() => {
return !!usageInfo.value?.five_hour || !!usageInfo.value?.seven_day
})
const openAISevenDayEstimatedTotalCost = computed(() => {
const sevenDay = usageInfo.value?.seven_day
const utilization = sevenDay?.utilization
const currentCost = sevenDay?.window_stats?.cost
if (
typeof utilization !== 'number' ||
typeof currentCost !== 'number' ||
!Number.isFinite(utilization) ||
!Number.isFinite(currentCost) ||
utilization <= 0 ||
currentCost <= 0
) {
return null
}
const estimate = (currentCost * 100) / utilization
return Number.isFinite(estimate) && estimate > 0 ? estimate : null
})
const openAIUsageRefreshKey = computed(() => buildOpenAIUsageRefreshKey(props.account))
const shouldAutoLoadUsageOnMount = computed(() => {
@@ -22,6 +22,14 @@
>
U ${{ formatUserCost }}
</span>
<span
v-if="estimatedTotalCost != null"
data-test="estimated-total-cost"
class="rounded bg-gray-100 px-1.5 py-0.5 dark:bg-gray-800"
:title="t('admin.accounts.usageWindow.estimatedTotalCostTooltip')"
>
{{ t('admin.accounts.usageWindow.estimatedTotalCost', { cost: estimatedTotalCost.toFixed(2) }) }}
</span>
</div>
</div>
@@ -67,6 +75,7 @@ const props = withDefaults(
resetsAt?: string | null
color: 'indigo' | 'emerald' | 'purple' | 'amber'
windowStats?: WindowStats | null
estimatedTotalCost?: number | null
showNowWhenIdle?: boolean
remainingCapacity?: boolean
/** fixed: 定宽居中徽章(账号页纵向对齐);auto: 限宽截断左对齐(监控页组合标签) */
@@ -441,6 +441,84 @@ describe('AccountUsageCell', () => {
expect(wrapper.text()).toContain('7d|36|900')
})
it('仅为 OpenAI OAuth 7d 窗口计算预计总费用', async () => {
getUsage.mockResolvedValue({
five_hour: {
utilization: 25,
resets_at: null,
remaining_seconds: 0,
window_stats: { requests: 1, tokens: 100, cost: 2 }
},
seven_day: {
utilization: 40,
resets_at: null,
remaining_seconds: 0,
window_stats: { requests: 2, tokens: 200, cost: 12 }
}
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({ id: 6752, platform: 'openai', type: 'oauth' })
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'estimatedTotalCost'],
template: '<div class="usage-bar">{{ label }}|{{ estimatedTotalCost ?? "none" }}</div>'
},
AccountQuotaInfo: true
}
}
})
await flushPromises()
expect(wrapper.text()).toContain('5h|none')
expect(wrapper.text()).toContain('7d|30')
})
it.each([
{ id: 6801, utilization: 0, cost: 12 },
{ id: 6802, utilization: -1, cost: 12 },
{ id: 6803, utilization: Number.NaN, cost: 12 },
{ id: 6804, utilization: 40, cost: 0 },
{ id: 6805, utilization: 40, cost: Number.POSITIVE_INFINITY }
])('OpenAI OAuth 7d 输入无效时不显示预计总费用 (%o)', async ({ id, utilization, cost }) => {
getUsage.mockResolvedValue({
seven_day: {
utilization,
resets_at: null,
remaining_seconds: 0,
window_stats: { requests: 1, tokens: 100, cost }
}
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({
id,
platform: 'openai',
type: 'oauth'
})
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'estimatedTotalCost'],
template: '<div class="usage-bar">{{ label }}|{{ estimatedTotalCost ?? "none" }}</div>'
},
AccountQuotaInfo: true
}
}
})
await flushPromises()
expect(wrapper.text()).toContain('7d|none')
expect(wrapper.text()).not.toMatch(/Infinity|NaN/)
})
it('OpenAI OAuth 有现成快照时,手动刷新信号会触发 usage 重拉', async () => {
getUsage.mockResolvedValue({
five_hour: {
@@ -1534,7 +1534,9 @@ export default {
grokLastProbe: 'Probe {time}',
grokLastHeadersSeen: 'Headers {time}',
passiveSampled: 'Passive',
activeQuery: 'Query'
activeQuery: 'Query',
estimatedTotalCost: 'Est. total ${cost}',
estimatedTotalCostTooltip: 'Estimated total cost at 100% utilization, based on current window cost and utilization'
},
openaiQuotaReset: {
count: 'Credits',
@@ -479,7 +479,9 @@ export default {
grokLastProbe: '探测 {time}',
grokLastHeadersSeen: '响应头 {time}',
passiveSampled: '被动采样',
activeQuery: '查询'
activeQuery: '查询',
estimatedTotalCost: '预计总费用 ${cost}',
estimatedTotalCostTooltip: '根据当前窗口费用和使用率估算达到 100% 使用率时的总费用'
},
openaiQuotaReset: {
count: '次数',