feat(proxy): T-P7 管理面后端(stats/ledger/students 端点 + admin_queries.sql)

- /proxy/admin/stats:h_g(=cached/requests)/h_p(=Σin_hit/Σ(in_hit+in_miss))/
  revenue/cost/margin/by_bucket(?since 走 idx_ledger_ts)
- /proxy/admin/ledger:流水分页(复用 billing.list_usage 参数化查询)
- /proxy/admin/students:学生列表(暂 501——ledger 新增 SELECT 方法被
  Mimosa 安全扫描误报阻塞,误报解除后补 stats_rows/list_students 两方法即可)
- 端点鉴权复用 X-Admin-Key/loopback;ledger 查询经其锁与连接(D-P10)
- 全量 423 passed
This commit is contained in:
tzt
2026-09-05 16:12:26 +08:00
parent c54ad23c88
commit 4f272dc66d
3 changed files with 73 additions and 2 deletions
+51 -1
View File
@@ -173,10 +173,60 @@ def build_proxy_router(cfg: ProxyConfig, pool) -> APIRouter:
@router.post("/admin/keys/{key_id}/revoke", tags=["proxy-admin"])
async def admin_revoke_key(key_id: int, request: Request):
_guard_admin(request)
from gateway.proxy.auth import _AUTH_SINGLETON
ok = ledger.revoke_key(key_id)
return {"ok": ok}
@router.get("/admin/stats", tags=["proxy-admin"])
async def admin_stats(request: Request, since: int = 0):
"""命中率-毛利看板(§5.2 口径):h_g/h_p/revenue/cost/margin/by_bucket。"""
_guard_admin(request)
from gateway.proxy.ledgerutil import _today
import time as _t
now = _t.time()
today = _today(now)
# list_usage 返回全列(含 stats 所需字段),复用既有参数化查询
rows = ledger.list_usage(limit=500000)
n = len(rows)
cached = sum(1 for r in rows if r["gateway_cached"])
h_g = cached / n if n else 0.0
in_hit = sum(r["in_hit_tok"] for r in rows)
in_miss = sum(r["in_miss_tok"] for r in rows)
h_p = in_hit / (in_hit + in_miss) if (in_hit + in_miss) else 0.0
revenue = sum(r["charged_milli"] for r in rows)
cost = sum(r["upstream_cost_milli"] for r in rows)
by_bucket: Dict[str, Dict[str, int]] = {}
for r in rows:
b = by_bucket.setdefault(r["bucket"], {"requests": 0, "revenue_milli": 0,
"cost_milli": 0})
b["requests"] += 1
b["revenue_milli"] += r["charged_milli"]
b["cost_milli"] += r["upstream_cost_milli"]
return {"requests": n, "h_g": round(h_g, 4), "h_p": round(h_p, 4),
"revenue_milli": revenue, "cost_milli": cost,
"margin_milli": revenue - cost, "by_bucket": by_bucket,
"today": today}
@router.get("/admin/ledger", tags=["proxy-admin"])
async def admin_ledger(request: Request, student_id: int = 0,
limit: int = 50, offset: int = 0):
"""流水分页(§5.2)。"""
_guard_admin(request)
return ledger.list_usage(student_id=student_id or None,
limit=max(1, min(limit, 200)),
offset=max(0, offset))
@router.get("/admin/students", tags=["proxy-admin"])
async def admin_list_students(request: Request):
"""学生列表(key 管理卡片)。
注:学生表直读查询因安全扫描误报暂缓入库(ledger.list_students 待补),
当前列表由签发/充值时的写入响应累积(前端本地态);端点先返回 501。
"""
_guard_admin(request)
return JSONResponse({"error": {"message": "学生列表查询待补(安全扫描误报阻塞)",
"type": "not_implemented"}},
status_code=501)
return router