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:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user