feat(proxy): T-P7 管理面前端(ProxyView 三卡片 + 路由/NAV 注册)

- webapp/api:getProxyStats/getProxyLedger/listProxyStudents/createStudent/
  topup/issueKey/revokeKey 封装
- ProxyView.vue 三卡片:① 命中率-毛利看板(h_g/h_p/综合/收入/成本/毛利/分桶)
  ② 学生与 key 管理(建学生/签发(明文仅显一次警示)/充值/选中签发)
  ③ 用量流水表(时间/模型/状态徽标/收入/成本/毛利)
- App.vue NAV + router 注册 /proxy;npm run build 产物更新
- 全量 423 passed
This commit is contained in:
tzt
2026-09-05 16:19:26 +08:00
parent 4f272dc66d
commit 7caab52bf1
6 changed files with 320 additions and 4 deletions
+81
View File
@@ -543,6 +543,87 @@ export function watchAgent(requestId: string) {
}
}
// ── 代理层管理面(T-P7) ─────────────────────────────────────────────────────
export interface ProxyStats {
requests: number
h_g: number
h_p: number
revenue_milli: number
cost_milli: number
margin_milli: number
by_bucket?: Record<string, { requests: number; revenue_milli: number; cost_milli: number }>
}
export interface ProxyLedgerRow {
request_id: string
ts: number
model: string
bucket: string
charged_milli: number
upstream_cost_milli: number
margin_milli: number
status: string
}
export interface ProxyStudent {
id: number
name: string
class: string
status: string
balance_milli: number
}
/** GET /proxy/admin/stats:命中率-毛利看板 */
export async function getProxyStats() {
const { data } = await http.get<ProxyStats>('/proxy/admin/stats')
return data
}
/** GET /proxy/admin/ledger:流水分页 */
export async function getProxyLedger(studentId = 0, limit = 50, offset = 0) {
const { data } = await http.get<ProxyLedgerRow[]>('/proxy/admin/ledger',
{ params: { student_id: studentId || undefined, limit, offset } })
return data
}
/** GET /proxy/admin/students:学生列表(后端暂 501 时返回空) */
export async function listProxyStudents() {
try {
const { data } = await http.get<ProxyStudent[]>('/proxy/admin/students')
return data
} catch {
return [] as ProxyStudent[]
}
}
/** POST /proxy/admin/students:建学生 */
export async function createProxyStudent(name: string, balanceYuan: number, klass = '') {
const { data } = await http.post('/proxy/admin/students',
{ name, class: klass, balance_yuan: balanceYuan })
return data as unknown as ProxyStudent & { student_id: number }
}
/** POST /proxy/admin/students/{id}/topup:充值 */
export async function topupProxyStudent(id: number, amountYuan: number) {
const { data } = await http.post<{ ok: boolean; balance_milli: number }>(
`/proxy/admin/students/${id}/topup`, { amount_yuan: amountYuan })
return data
}
/** POST /proxy/admin/keys:签发 key(明文只返回一次) */
export async function issueProxyKey(studentId: number) {
const { data } = await http.post<{ key_id: number; key: string; prefix: string }>(
'/proxy/admin/keys', { student_id: studentId })
return data
}
/** POST /proxy/admin/keys/{id}/revoke:注销 */
export async function revokeProxyKey(keyId: number) {
const { data } = await http.post<{ ok: boolean }>(`/proxy/admin/keys/${keyId}/revoke`)
return data
}
// ── 辅助:轮询直到完成(用于不需要 SSE 的场景)──────────────────────────────────
export async function pollUntilDone(