| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- from typing import Annotated, Any
- from fastapi import APIRouter, Depends, Header, Request, Response, status
- from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
- from pydantic import BaseModel, Field
- from app.api.responses import success_response
- from app.core.config import Settings
- from app.core.errors import AppError
- from app.domains.enrollment.service import EnrollmentService
- from app.domains.identity.models import H5User
- from app.domains.identity.service import H5AuthService
- enrollment_bearer_scheme = HTTPBearer(auto_error=False)
- class InsuredInput(BaseModel):
- age: int = Field(ge=0, le=120)
- region_code: str = Field(min_length=6, max_length=12)
- occupation_code: str = Field(min_length=1, max_length=32)
- class CreateQuoteRequest(BaseModel):
- product_id: str = Field(min_length=26, max_length=26)
- plan_id: str = Field(min_length=26, max_length=26)
- insured: InsuredInput
- relationship: str = Field(pattern="^(SELF|PARENT|SPOUSE|CHILD)$")
- class PersonInput(BaseModel):
- name: str = Field(min_length=1, max_length=64)
- id_no: str = Field(min_length=6, max_length=32)
- class ContactInput(BaseModel):
- mobile: str = Field(min_length=11, max_length=11)
- class CreateDraftRequest(BaseModel):
- quote_id: str = Field(min_length=26, max_length=26)
- applicant: PersonInput
- insured: PersonInput
- contact: ContactInput
- class CreateOrderRequest(BaseModel):
- draft_id: str = Field(min_length=26, max_length=26)
- confirmation_token: str = Field(min_length=32, max_length=128)
- class MockPaymentCallbackRequest(BaseModel):
- callback_no: str = Field(min_length=1, max_length=64)
- provider_transaction_no: str = Field(min_length=1, max_length=64)
- payment_no: str = Field(min_length=1, max_length=40)
- status: str = Field(pattern="^(SUCCEEDED|FAILED)$")
- amount_cents: int = Field(gt=0)
- occurred_at: str = Field(min_length=10, max_length=64)
- def create_enrollment_router(
- service: EnrollmentService,
- h5_auth_service: H5AuthService,
- settings: Settings,
- ) -> APIRouter:
- router = APIRouter(tags=["enrollment"])
- def current_h5_user(
- credentials: Annotated[
- HTTPAuthorizationCredentials | None,
- Depends(enrollment_bearer_scheme),
- ],
- ) -> H5User:
- if credentials is None:
- raise AppError("AUTH_REQUIRED", "请先登录", 401)
- return h5_auth_service.authenticate_access(credentials.credentials)
- @router.post("/h5/quotes", status_code=status.HTTP_201_CREATED)
- def create_quote(
- payload: CreateQuoteRequest,
- request: Request,
- user: Annotated[H5User, Depends(current_h5_user)],
- ) -> dict[str, Any]:
- return success_response(
- request,
- service.create_quote(
- user,
- product_id=payload.product_id,
- plan_id=payload.plan_id,
- age=payload.insured.age,
- region_code=payload.insured.region_code,
- occupation_code=payload.insured.occupation_code,
- relationship=payload.relationship,
- ),
- )
- @router.post("/h5/enrollment-drafts", status_code=status.HTTP_201_CREATED)
- def create_draft(
- payload: CreateDraftRequest,
- request: Request,
- user: Annotated[H5User, Depends(current_h5_user)],
- ) -> dict[str, Any]:
- return success_response(
- request,
- service.create_draft(
- user,
- quote_id=payload.quote_id,
- applicant=payload.applicant.model_dump(),
- insured=payload.insured.model_dump(),
- contact=payload.contact.model_dump(),
- ),
- )
- @router.post(
- "/h5/enrollment-drafts/{draft_id}/confirmation",
- status_code=status.HTTP_201_CREATED,
- )
- def confirm_draft(
- draft_id: str,
- request: Request,
- user: Annotated[H5User, Depends(current_h5_user)],
- ) -> dict[str, Any]:
- return success_response(request, service.confirm_draft(user, draft_id))
- @router.post("/h5/orders", status_code=status.HTTP_201_CREATED)
- def create_order(
- payload: CreateOrderRequest,
- request: Request,
- response: Response,
- user: Annotated[H5User, Depends(current_h5_user)],
- idempotency_key: Annotated[
- str,
- Header(alias="Idempotency-Key", min_length=1, max_length=64),
- ],
- ) -> dict[str, Any]:
- data, created = service.create_order(
- user,
- draft_id=payload.draft_id,
- confirmation_token=payload.confirmation_token,
- idempotency_key=idempotency_key,
- )
- response.status_code = status.HTTP_201_CREATED if created else status.HTTP_200_OK
- return success_response(request, data)
- @router.post(
- "/h5/orders/{order_id}/payments",
- status_code=status.HTTP_201_CREATED,
- )
- def create_payment(
- order_id: str,
- request: Request,
- response: Response,
- user: Annotated[H5User, Depends(current_h5_user)],
- idempotency_key: Annotated[
- str,
- Header(alias="Idempotency-Key", min_length=1, max_length=64),
- ],
- ) -> dict[str, Any]:
- data, created = service.create_payment(
- user,
- order_id=order_id,
- idempotency_key=idempotency_key,
- )
- response.status_code = status.HTTP_201_CREATED if created else status.HTTP_200_OK
- return success_response(request, data)
- @router.post("/dev/mock-payments/{payment_id}/complete")
- def complete_mock_payment(
- payment_id: str,
- request: Request,
- user: Annotated[H5User, Depends(current_h5_user)],
- ) -> dict[str, Any]:
- if not settings.dev_tools_enabled or settings.app_env == "production":
- raise AppError("DEV_TOOL_DISABLED", "本地模拟支付未启用", 404)
- return success_response(
- request,
- service.complete_mock_payment(user, payment_id=payment_id),
- )
- @router.post("/callbacks/mock-payment")
- def mock_payment_callback(
- payload: MockPaymentCallbackRequest,
- request: Request,
- signature: Annotated[
- str,
- Header(alias="X-Mock-Pay-Signature", min_length=1, max_length=128),
- ],
- ) -> dict[str, Any]:
- return success_response(
- request,
- service.handle_mock_callback(payload.model_dump(), signature),
- )
- @router.get("/h5/policies")
- def list_policies(
- request: Request,
- user: Annotated[H5User, Depends(current_h5_user)],
- ) -> dict[str, Any]:
- return success_response(request, service.list_policies(user))
- @router.get("/h5/orders")
- def list_orders(
- request: Request,
- user: Annotated[H5User, Depends(current_h5_user)],
- ) -> dict[str, Any]:
- return success_response(request, service.list_orders(user.id))
- return router
|