Files
meida_backend/apps/api/routers/quota.py
T
xsl 2009aa938c fix(billing): make quota seed and column names match the product SSOT
The product plan is the only source for quota numbers, but the 0001 seed
used a second set (agent_chat 20/50, tryon hard 10) and omitted hairstyle,
hd_export and report regen. Migration 0003 replaces the seed and renames
points_reserved → quota_reserved and bonus_value → bonus_granted so the
docs and the schema stop contradicting each other.

Effective limit now clips at hard_cap. CI runs the integration slice
against the Postgres/Redis services it already starts.
2026-08-14 11:28:35 +08:00

62 lines
2.0 KiB
Python
Executable File

"""``GET /quota`` and ``GET /quota/ledger`` (架构文档 §9.3 配额).
FR-BILL-07: 额度相关文案中**不得出现任何价格、购买、会员、升级字样**。
Only two recovery paths may ever be surfaced: 等待恢复 and 做任务换额度.
"""
from __future__ import annotations
from fastapi import APIRouter, Query
from apps.api.deps import BillingServiceDep, CurrentUserId
from apps.api.schemas import LedgerEntry, QuotaResponse
from packages.agent.service import QUOTA_KEY_AGENT_CHAT
from packages.shared.response import ok
router = APIRouter(prefix="/quota", tags=["quota"])
@router.get("", summary="Current period quota for a feature")
async def get_quota(
user_id: CurrentUserId,
billing: BillingServiceDep,
quota_key: str = Query(default=QUOTA_KEY_AGENT_CHAT, max_length=64),
) -> dict[str, object]:
snapshot = await billing.get_quota(user_id=user_id, quota_key=quota_key)
return ok(
QuotaResponse(
quota_key=snapshot.quota_key,
period_key=snapshot.period_key,
used=snapshot.used,
limit_value=snapshot.limit_value,
bonus_granted=snapshot.bonus_granted,
remaining=snapshot.remaining,
reset_at=snapshot.reset_at,
).model_dump()
)
@router.get("/ledger", summary="Quota ledger — 流水是真相,余额是缓存")
async def get_ledger(
user_id: CurrentUserId,
billing: BillingServiceDep,
limit: int = Query(default=50, ge=1, le=200),
) -> dict[str, object]:
rows = await billing.list_ledger(user_id=user_id, limit=limit)
return ok(
[
LedgerEntry(
tx_id=str(row.tx_id),
delta=row.delta,
tx_type=row.tx_type,
status=row.status,
reason=row.reason,
quota_key=row.quota_key,
period_key=row.period_key,
balance_after=row.balance_after,
created_at=row.created_at,
).model_dump()
for row in rows
]
)