feat(compute): /v1中继鉴权重写与平台用户余额视图

- relay 鉴权重写:JWT→引擎 PAT 逐用户归属,拒绝匿名调用,余额<=0 预检 403
- compute_client 增 list_engine_users/get_engine_user/sync_user_mirror 等
- 运营端新增 /admin/compute/platform-users 平台用户视图(引擎真实余额/折扣/用量)与充值镜像回写

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Pine
2026-08-30 22:29:44 +08:00
parent f1147dced3
commit e44fea96ca
3 changed files with 156 additions and 21 deletions
+46 -2
View File
@@ -188,8 +188,8 @@ async def adjust_user_quota(engine_user_id: int, value: int, mode: str = "add")
- mode="add" 充值(增加余额)
- mode="subtract" 扣减
- mode="override" 直接设为该值
``value`` 为配额单位(见 /api/status 的 quota_per_unit500000 = $1)。
被充值用户的其他 PAT/会话会实时扣除,额度耗尽时引擎返回 403。
``value`` 为微元(人民币实际金额,1 元 = 1_000_000,见 compute-service meter.py)。
额度耗尽时引擎 /v1 返回 403。
"""
return await _request(
method="POST", path="/api/user/manage", headers=_admin_headers(),
@@ -197,6 +197,50 @@ async def adjust_user_quota(engine_user_id: int, value: int, mode: str = "add")
)
async def list_engine_users(page: int = 1, page_size: int = 100, status: int = 1) -> list[dict]:
"""分页拉取引擎用户(GET /api/user),返回 items 列表。"""
data = await _request(
method="GET",
path=f"/api/user?status={int(status)}&type=-1&p={int(page)}&page_size={int(page_size)}",
headers=_admin_headers(),
)
return (data.get("data") or {}).get("items") or []
async def get_engine_user(engine_user_id: int) -> dict | None:
"""按 id 取引擎用户(GET /api/user?id=),无则 None。"""
data = await _request(
method="GET", path=f"/api/user?id={int(engine_user_id)}&p=1&page_size=1",
headers=_admin_headers(),
)
items = (data.get("data") or {}).get("items") or []
return items[0] if items else None
async def sync_user_mirror(db, engine_user_id: int) -> bool:
"""把引擎用户余额回写到平台镜像(compute_quota/compute_used_quota)。
best-effort:引擎用户不存在或平台用户未映射时静默返回 False。
"""
try:
engine_user = await get_engine_user(engine_user_id)
username = str((engine_user or {}).get("username") or "")
if not username:
return False
balance = await user_balance(username)
platform_user = await db.users.get_by_username(username)
if platform_user is None:
return False
await db.users.set_compute_mirror(
platform_user["id"], provisioned=True, username=username,
quota=int(balance.get("quota") or 0),
used_quota=int(balance.get("used_quota") or 0),
)
return True
except Exception: # noqa: BLE001
return False
async def set_user_group_by_username(username: str, group: str) -> dict:
"""把引擎用户划到某个用户组(企业折扣凭 User.Group 触发组倍率)。