import { query } from "./db"; import { quotaToUsd } from "./metrics"; import { CACHE_CREATION, CACHE_READ, REAL_MODEL, cacheKey, cached, getChannelNames, getDisplayNames, timeWhere } from "./query-shared"; // ── 排名 ────────────────────────────────────────────────────── export interface RankingItem { rank: number; name: string; id?: number; calls: number; prompt_tokens: number; completion_tokens: number; cache_creation_tokens: number; cache_read_tokens: number; total_tokens: number; quota: number; quota_usd: number; } export function getUserRanking( startTs?: number, endTs?: number, limit = 50 ): Promise { return cached(cacheKey("userRank", startTs, endTs, limit), async () => { const params: (string | number | boolean | null)[] = []; const where = timeWhere(params, startTs, endTs); params.push(limit); const displayNames = await getDisplayNames(); const rows = await query( `SELECT user_id, username, COUNT(*)::int as calls, COALESCE(SUM(prompt_tokens), 0)::bigint as prompt, COALESCE(SUM(completion_tokens), 0)::bigint as completion, COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation, COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read, COALESCE(SUM(quota), 0)::bigint as quota FROM logs WHERE ${where} GROUP BY user_id, username ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) + COALESCE(SUM(${CACHE_CREATION}),0) + COALESCE(SUM(${CACHE_READ}),0) DESC LIMIT $${params.length}`, params ); return rows.map((r, i) => ({ rank: i + 1, name: displayNames[r.user_id] || r.username, username: r.username as string, id: Number(r.user_id), calls: Number(r.calls), prompt_tokens: Number(r.prompt), completion_tokens: Number(r.completion), cache_creation_tokens: Number(r.cache_creation), cache_read_tokens: Number(r.cache_read), total_tokens: Number(r.prompt) + Number(r.completion) + Number(r.cache_creation) + Number(r.cache_read), quota: Number(r.quota), quota_usd: quotaToUsd(Number(r.quota)), })); }); } export function getModelRanking( startTs?: number, endTs?: number, limit = 50 ): Promise { return cached(cacheKey("modelRank", startTs, endTs, limit), async () => { const params: (string | number | boolean | null)[] = []; const where = timeWhere(params, startTs, endTs); params.push(limit); const rows = await query( `SELECT ${REAL_MODEL} as model, COUNT(*)::int as calls, COALESCE(SUM(prompt_tokens), 0)::bigint as prompt, COALESCE(SUM(completion_tokens), 0)::bigint as completion, COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation, COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read, COALESCE(SUM(quota), 0)::bigint as quota FROM logs WHERE ${where} GROUP BY model ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) + COALESCE(SUM(${CACHE_CREATION}),0) + COALESCE(SUM(${CACHE_READ}),0) DESC LIMIT $${params.length}`, params ); return rows.map((r, i) => ({ rank: i + 1, name: r.model || "(unknown)", calls: Number(r.calls), prompt_tokens: Number(r.prompt), completion_tokens: Number(r.completion), cache_creation_tokens: Number(r.cache_creation), cache_read_tokens: Number(r.cache_read), total_tokens: Number(r.prompt) + Number(r.completion) + Number(r.cache_creation) + Number(r.cache_read), quota: Number(r.quota), quota_usd: quotaToUsd(Number(r.quota)), })); }); } export function getChannelRanking( startTs?: number, endTs?: number, limit = 50 ): Promise { return cached(cacheKey("channelRank", startTs, endTs, limit), async () => { const params: (string | number | boolean | null)[] = []; const where = timeWhere(params, startTs, endTs); params.push(limit); const channelNames = await getChannelNames(); const rows = await query( `SELECT channel_id, COUNT(*)::int as calls, COALESCE(SUM(prompt_tokens), 0)::bigint as prompt, COALESCE(SUM(completion_tokens), 0)::bigint as completion, COALESCE(SUM(${CACHE_CREATION}), 0)::bigint as cache_creation, COALESCE(SUM(${CACHE_READ}), 0)::bigint as cache_read, COALESCE(SUM(quota), 0)::bigint as quota FROM logs WHERE ${where} GROUP BY channel_id ORDER BY COALESCE(SUM(prompt_tokens),0) + COALESCE(SUM(completion_tokens),0) + COALESCE(SUM(${CACHE_CREATION}),0) + COALESCE(SUM(${CACHE_READ}),0) DESC LIMIT $${params.length}`, params ); return rows.map((r, i) => ({ rank: i + 1, name: channelNames[r.channel_id] || `已删除(${r.channel_id})`, id: Number(r.channel_id), calls: Number(r.calls), prompt_tokens: Number(r.prompt), completion_tokens: Number(r.completion), cache_creation_tokens: Number(r.cache_creation), cache_read_tokens: Number(r.cache_read), total_tokens: Number(r.prompt) + Number(r.completion) + Number(r.cache_creation) + Number(r.cache_read), quota: Number(r.quota), quota_usd: quotaToUsd(Number(r.quota)), })); }); }