Commit db27e8f0 authored by erio's avatar erio
Browse files

feat(usage): add account cost to breakdown sub-table and admin usage log

- UserBreakdownItem: add AccountCost field + SQL aggregation
- UserBreakdownSubTable: add orange account cost column
- Admin usage table: add account_cost column (after cost, default visible)
- Column settings: add account_cost toggle option
parent e0b12b75
...@@ -168,8 +168,9 @@ type UserBreakdownItem struct { ...@@ -168,8 +168,9 @@ type UserBreakdownItem struct {
Email string `json:"email"` Email string `json:"email"`
Requests int64 `json:"requests"` Requests int64 `json:"requests"`
TotalTokens int64 `json:"total_tokens"` TotalTokens int64 `json:"total_tokens"`
Cost float64 `json:"cost"` // 标准计费 Cost float64 `json:"cost"` // 标准计费
ActualCost float64 `json:"actual_cost"` // 实际扣除 ActualCost float64 `json:"actual_cost"` // 实际扣除
AccountCost float64 `json:"account_cost"` // 账号成本
} }
// UserBreakdownDimension specifies the dimension to filter for user breakdown. // UserBreakdownDimension specifies the dimension to filter for user breakdown.
......
...@@ -3157,7 +3157,8 @@ func (r *usageLogRepository) GetUserBreakdownStats(ctx context.Context, startTim ...@@ -3157,7 +3157,8 @@ func (r *usageLogRepository) GetUserBreakdownStats(ctx context.Context, startTim
COUNT(*) as requests, COUNT(*) as requests,
COALESCE(SUM(ul.input_tokens + ul.output_tokens + ul.cache_creation_tokens + ul.cache_read_tokens), 0) as total_tokens, COALESCE(SUM(ul.input_tokens + ul.output_tokens + ul.cache_creation_tokens + ul.cache_read_tokens), 0) as total_tokens,
COALESCE(SUM(ul.total_cost), 0) as cost, COALESCE(SUM(ul.total_cost), 0) as cost,
COALESCE(SUM(ul.actual_cost), 0) as actual_cost COALESCE(SUM(ul.actual_cost), 0) as actual_cost,
COALESCE(SUM(COALESCE(ul.account_stats_cost, ul.total_cost) * COALESCE(ul.account_rate_multiplier, 1)), 0) as account_cost
FROM usage_logs ul FROM usage_logs ul
LEFT JOIN users u ON u.id = ul.user_id LEFT JOIN users u ON u.id = ul.user_id
WHERE ul.created_at >= $1 AND ul.created_at < $2 WHERE ul.created_at >= $1 AND ul.created_at < $2
...@@ -3228,6 +3229,7 @@ func (r *usageLogRepository) GetUserBreakdownStats(ctx context.Context, startTim ...@@ -3228,6 +3229,7 @@ func (r *usageLogRepository) GetUserBreakdownStats(ctx context.Context, startTim
&row.TotalTokens, &row.TotalTokens,
&row.Cost, &row.Cost,
&row.ActualCost, &row.ActualCost,
&row.AccountCost,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
......
...@@ -160,6 +160,12 @@ ...@@ -160,6 +160,12 @@
</div> </div>
</template> </template>
<template #cell-account_cost="{ row }">
<span class="text-sm font-medium text-orange-500 dark:text-orange-400">
${{ accountBilled(row).toFixed(6) }}
</span>
</template>
<template #cell-first_token="{ row }"> <template #cell-first_token="{ row }">
<span v-if="row.first_token_ms != null" class="text-sm text-gray-600 dark:text-gray-400">{{ formatDuration(row.first_token_ms) }}</span> <span v-if="row.first_token_ms != null" class="text-sm text-gray-600 dark:text-gray-400">{{ formatDuration(row.first_token_ms) }}</span>
<span v-else class="text-sm text-gray-400 dark:text-gray-500">-</span> <span v-else class="text-sm text-gray-400 dark:text-gray-500">-</span>
......
...@@ -25,6 +25,9 @@ ...@@ -25,6 +25,9 @@
<td class="py-1 text-right text-green-600 dark:text-green-400"> <td class="py-1 text-right text-green-600 dark:text-green-400">
${{ formatCost(user.actual_cost) }} ${{ formatCost(user.actual_cost) }}
</td> </td>
<td class="py-1 text-right text-orange-500 dark:text-orange-400">
${{ formatCost(user.account_cost) }}
</td>
<td class="py-1 pr-1 text-right text-gray-400 dark:text-gray-500"> <td class="py-1 pr-1 text-right text-gray-400 dark:text-gray-500">
${{ formatCost(user.cost) }} ${{ formatCost(user.cost) }}
</td> </td>
......
...@@ -1245,6 +1245,7 @@ export interface UserBreakdownItem { ...@@ -1245,6 +1245,7 @@ export interface UserBreakdownItem {
total_tokens: number total_tokens: number
cost: number cost: number
actual_cost: number actual_cost: number
account_cost: number
} }
export interface UserUsageTrendPoint { export interface UserUsageTrendPoint {
......
...@@ -533,6 +533,7 @@ const allColumns = computed(() => [ ...@@ -533,6 +533,7 @@ const allColumns = computed(() => [
{ key: 'billing_mode', label: t('admin.usage.billingMode'), sortable: false }, { key: 'billing_mode', label: t('admin.usage.billingMode'), sortable: false },
{ key: 'tokens', label: t('usage.tokens'), sortable: false }, { key: 'tokens', label: t('usage.tokens'), sortable: false },
{ key: 'cost', label: t('usage.cost'), sortable: false }, { key: 'cost', label: t('usage.cost'), sortable: false },
{ key: 'account_cost', label: t('usage.accountCost'), sortable: false },
{ key: 'first_token', label: t('usage.firstToken'), sortable: false }, { key: 'first_token', label: t('usage.firstToken'), sortable: false },
{ key: 'duration', label: t('usage.duration'), sortable: false }, { key: 'duration', label: t('usage.duration'), sortable: false },
{ key: 'created_at', label: t('usage.time'), sortable: true }, { key: 'created_at', label: t('usage.time'), sortable: true },
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment