8fe58f4a42
- new POST /api/v1/coupon/step, JWT-authenticated, async httpx forwards body to pricebot - new PRICEBOT_BASE_URL / PRICEBOT_REQUEST_TIMEOUT_SEC settings (default localhost:8000) - error handling: pricebot unreachable / 5xx -> 502 with friendly message - tests: auth, passthrough, 5xx, unreachable, invalid json (5 cases, all pass) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
73 lines
2.1 KiB
Python
73 lines
2.1 KiB
Python
"""领券业务透传端点。
|
|
|
|
把客户端的 POST /api/v1/coupon/step 请求原样转发给 pricebot-backend
|
|
的 /api/coupon/step,只在外层加 JWT 鉴权。
|
|
|
|
pricebot 协议文档:
|
|
pricebot-backend/docs/projects/领券-客户端对接协议.md
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
import httpx
|
|
from fastapi import APIRouter, HTTPException, Request, status
|
|
|
|
from app.api.deps import CurrentUser
|
|
from app.core.config import settings
|
|
|
|
logger = logging.getLogger("shagua.coupon")
|
|
|
|
router = APIRouter(prefix="/api/v1/coupon", tags=["coupon"])
|
|
|
|
|
|
@router.post("/step", summary="领券任务步进 (透传到 pricebot)")
|
|
async def coupon_step(
|
|
request: Request,
|
|
user: CurrentUser,
|
|
) -> dict[str, Any]:
|
|
"""把请求体原样转发给 pricebot-backend 的 /api/coupon/step。
|
|
|
|
- 鉴权: Bearer access_token (get_current_user 隐式校验)
|
|
- 透传: 不做 schema 校验,pricebot 自己校验
|
|
- 失败: 网络不可达 / pricebot 5xx → 502 + 友好 message
|
|
"""
|
|
try:
|
|
body = await request.json()
|
|
except Exception as e:
|
|
raise HTTPException(status_code=400, detail=f"invalid json body: {e}") from e
|
|
|
|
url = f"{settings.PRICEBOT_BASE_URL.rstrip('/')}/api/coupon/step"
|
|
timeout = settings.PRICEBOT_REQUEST_TIMEOUT_SEC
|
|
|
|
logger.info(
|
|
"coupon_step user_id=%d trace_id=%s step=%s",
|
|
user.id,
|
|
body.get("trace_id"),
|
|
body.get("step"),
|
|
)
|
|
|
|
try:
|
|
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
resp = await client.post(url, json=body)
|
|
except httpx.RequestError as e:
|
|
logger.error("[pricebot] request failed: %s", e)
|
|
raise HTTPException(
|
|
status_code=status.HTTP_502_BAD_GATEWAY,
|
|
detail=f"pricebot upstream unreachable: {e}",
|
|
) from e
|
|
|
|
if resp.status_code >= 500:
|
|
logger.error(
|
|
"[pricebot] 5xx status=%d body=%s",
|
|
resp.status_code,
|
|
resp.text[:500],
|
|
)
|
|
raise HTTPException(
|
|
status_code=status.HTTP_502_BAD_GATEWAY,
|
|
detail=f"pricebot upstream returned {resp.status_code}",
|
|
)
|
|
|
|
return resp.json()
|