"""领券业务透传端点。 把客户端的 POST /api/v1/coupon/step 请求原样转发给 pricebot-backend 的 /api/coupon/step。MVP 阶段**不鉴权**(见 docs/待办与技术债.md P1: device_id 透传给 pricebot 区分设备,待补 JWT + device_id↔user_id 绑定后 才能做用户级画像)。 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.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, ) -> dict[str, Any]: """把请求体原样转发给 pricebot-backend 的 /api/coupon/step。 - 鉴权: MVP 阶段不鉴权(待补 JWT,见 docs/待办与技术债.md P1) - 透传: 不做 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 device_id=%s trace_id=%s step=%s", body.get("device_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()