mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-19 14:24:45 +08:00
Merge pull request #960 from 0xObjc/codex/user-spending-ranking
feat(admin): add user spending ranking dashboard view
This commit is contained in:
@@ -1993,6 +1993,10 @@ type ModelStat = usagestats.ModelStat
|
||||
// UserUsageTrendPoint represents user usage trend data point
|
||||
type UserUsageTrendPoint = usagestats.UserUsageTrendPoint
|
||||
|
||||
// UserSpendingRankingItem represents a user spending ranking row.
|
||||
type UserSpendingRankingItem = usagestats.UserSpendingRankingItem
|
||||
type UserSpendingRankingResponse = usagestats.UserSpendingRankingResponse
|
||||
|
||||
// APIKeyUsageTrendPoint represents API key usage trend data point
|
||||
type APIKeyUsageTrendPoint = usagestats.APIKeyUsageTrendPoint
|
||||
|
||||
@@ -2109,6 +2113,78 @@ func (r *usageLogRepository) GetUserUsageTrend(ctx context.Context, startTime, e
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetUserSpendingRanking returns user spending ranking aggregated within the time range.
|
||||
func (r *usageLogRepository) GetUserSpendingRanking(ctx context.Context, startTime, endTime time.Time, limit int) (result *UserSpendingRankingResponse, err error) {
|
||||
if limit <= 0 {
|
||||
limit = 12
|
||||
}
|
||||
|
||||
query := `
|
||||
WITH user_spend AS (
|
||||
SELECT
|
||||
u.user_id,
|
||||
COALESCE(us.email, '') as email,
|
||||
COALESCE(SUM(u.actual_cost), 0) as actual_cost,
|
||||
COUNT(*) as requests,
|
||||
COALESCE(SUM(u.input_tokens + u.output_tokens + u.cache_creation_tokens + u.cache_read_tokens), 0) as tokens
|
||||
FROM usage_logs u
|
||||
LEFT JOIN users us ON u.user_id = us.id
|
||||
WHERE u.created_at >= $1 AND u.created_at < $2
|
||||
GROUP BY u.user_id, us.email
|
||||
),
|
||||
ranked AS (
|
||||
SELECT
|
||||
user_id,
|
||||
email,
|
||||
actual_cost,
|
||||
requests,
|
||||
tokens,
|
||||
COALESCE(SUM(actual_cost) OVER (), 0) as total_actual_cost
|
||||
FROM user_spend
|
||||
ORDER BY actual_cost DESC, tokens DESC, user_id ASC
|
||||
LIMIT $3
|
||||
)
|
||||
SELECT
|
||||
user_id,
|
||||
email,
|
||||
actual_cost,
|
||||
requests,
|
||||
tokens,
|
||||
total_actual_cost
|
||||
FROM ranked
|
||||
ORDER BY actual_cost DESC, tokens DESC, user_id ASC
|
||||
`
|
||||
|
||||
rows, err := r.sql.QueryContext(ctx, query, startTime, endTime, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := rows.Close(); closeErr != nil && err == nil {
|
||||
err = closeErr
|
||||
result = nil
|
||||
}
|
||||
}()
|
||||
|
||||
ranking := make([]UserSpendingRankingItem, 0)
|
||||
totalActualCost := 0.0
|
||||
for rows.Next() {
|
||||
var row UserSpendingRankingItem
|
||||
if err = rows.Scan(&row.UserID, &row.Email, &row.ActualCost, &row.Requests, &row.Tokens, &totalActualCost); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ranking = append(ranking, row)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &UserSpendingRankingResponse{
|
||||
Ranking: ranking,
|
||||
TotalActualCost: totalActualCost,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UserDashboardStats 用户仪表盘统计
|
||||
type UserDashboardStats = usagestats.UserDashboardStats
|
||||
|
||||
|
||||
Reference in New Issue
Block a user