feat(admin): 计费 & 用量合成一页 —— 顶部配规则、下方看结果(闭环)
按"配置和观测放一个页面"收口:删掉重复的「计价 & 预算」页(其预算区块是 mock), 把真·计价配置并入「计费 & 用量」,形成 规则→扣费→观测 一页闭环。 - 新组件 BillingRules:全局 token→积分汇率 + 每模型 单价/币种/积分权重(读写 /admin/pricing + /admin/billing-config),改完对后续任务实时生效。 - UsagePage:顶部「计费规则」(配置端) + 下方「用量观测」(按规则折算后的实际消耗)。 - 删 PricingPage + /pricing 路由;api 加 credit_weight / getBillingConfig / saveBillingConfig。 live 验证(preview):改汇率保存→持久化→新任务按新汇率扣→用量观测反映; tsc + 41 vitest 全过;无 PricingPage 残引。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -125,6 +125,7 @@ export interface Pricing {
|
||||
model_id: string;
|
||||
input_per_1k: number;
|
||||
output_per_1k: number;
|
||||
credit_weight: number; // 每模型积分权重(0/缺省=1.0,不加权)
|
||||
currency: string;
|
||||
}
|
||||
|
||||
@@ -142,6 +143,24 @@ export async function savePricing(p: Pricing): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// —— 全局计费规则(token→积分汇率)——
|
||||
export async function getBillingConfig(): Promise<{ tokens_per_credit: number }> {
|
||||
const res = guard(await fetch(`${ADMIN}/billing-config`, { headers: authHeaders() }));
|
||||
if (!res.ok) throw new Error(`billing config failed: ${res.status}`);
|
||||
const d = (await res.json()) as { tokens_per_credit?: string };
|
||||
return { tokens_per_credit: Number(d.tokens_per_credit) || 1000 }; // 空/未设 → 回退默认 1000
|
||||
}
|
||||
|
||||
export async function saveBillingConfig(tokensPerCredit: number): Promise<void> {
|
||||
const res = guard(
|
||||
await fetch(`${ADMIN}/billing-config`, { method: "PUT", headers: authHeaders(true), body: JSON.stringify({ tokens_per_credit: tokensPerCredit }) }),
|
||||
);
|
||||
if (!res.ok) {
|
||||
const d = (await res.json().catch(() => ({}))) as { error?: string };
|
||||
throw new Error(d.error ?? `save billing config failed: ${res.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
// gatewayOnline 用公开的 /healthz 探活(不受鉴权影响)。
|
||||
export async function gatewayOnline(): Promise<boolean> {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user