Files
group_xinghuo_jinrong/app/api/simulate.py
T

54 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""模拟交易网关路由(PRD FR-1 · 薄路由,不含业务)。
鉴权:`Depends(get_auth_context)`(B6 回挂,评审 P2-2)——一期接受
risk_demo 演示账号或客户本人(auth.customer_id == 请求 customer_id,
PRD FR-1 §鉴权);越权经 deps.deny 审计后 403。T-01 后工厂内部换 JWT。
trace:main 中间件贯通(B7),响应头 X-Trace-Id 回写;service 层 ensure_trace
仍兜底脚本/测试直调场景。
挂载:main.py include(B7)。错误体统一 ApiError → 手册 §10 结构(挂账④),
convert 400 / 资源 404 不变 HTTP 语义。
"""
from __future__ import annotations
from decimal import Decimal
from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from app.api.deps import AuthContext, deny, get_auth_context
from app.gateway.trade_gateway import UnsupportedTradeType, submit_trade
from app.repository.risk_repository import RiskRepository
from app.utils.exceptions import ApiError
router = APIRouter(prefix="/api/simulate", tags=["simulate"])
def _repo() -> RiskRepository:
"""审计仓储(deny 留痕用;测试 monkeypatch 点)。"""
return RiskRepository()
class TradeRequest(BaseModel):
customer_id: str = Field(..., min_length=1)
product_id: str = Field(..., min_length=1)
trade_type: str = Field(..., max_length=16, description="subscribe | redeem;convert 显式拒绝")
amount: Decimal = Field(..., gt=0, description="交易金额(元),必须为正数")
@router.post("/trade")
def submit_trade_api(req: TradeRequest, auth: AuthContext = Depends(get_auth_context)) -> dict:
"""模拟交易(FR-1):适当性阻断或放行+引擎判定,返回 blocked + trade_id。"""
if not (auth.has_role("risk_demo") or (auth.is_customer() and auth.customer_id == req.customer_id)):
deny(
auth, "AUTH_403_ROLE", _repo(),
customer_id=req.customer_id, message="risk_demo or owner customer only",
agent_type="platform", # 网关越权与放行审计同口径(复审 P3)
)
try:
return submit_trade(req.model_dump())
except UnsupportedTradeType as exc:
raise ApiError(400, "BAD_REQUEST", str(exc)) from exc
except LookupError as exc: # NotFoundError 亦为其子类;已统一(B6 评审 P3-5)
raise ApiError(404, "NOT_FOUND", str(exc)) from exc