from typing import Annotated from apscheduler.schedulers.asyncio import AsyncIOScheduler from fastapi import APIRouter, Depends, Path, Query from fastapi.responses import JSONResponse from app.api.v1.module_system.auth.schema import AuthSchema from app.common.response import ResponseSchema, SuccessResponse from app.core.dependencies import AuthPermission from app.core.logger import log from app.core.router_class import OperationLogRoute from .schema import ( FacetofaceApplySchema, FacetofaceOrderListOutSchema, FacetofaceOrderOutSchema, ) from .service import FacetofaceService FacetofaceRouter = APIRouter( route_class=OperationLogRoute, prefix="/facetoface", tags=["当面付开通"], ) @FacetofaceRouter.post( "/apply", summary="提交当面付开通申请", description="代商家提交当面付开通申请(自动完成 create → sign → confirm 三步)", response_model=ResponseSchema[FacetofaceOrderOutSchema], ) async def apply_controller( auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:facetoface:apply"]))], data: FacetofaceApplySchema, ) -> JSONResponse: result = await FacetofaceService.apply_service(auth=auth, data=data) log.info(f"当面付开通申请已提交: batch_no={result.batch_no}, merchant={data.merchant_name}") return SuccessResponse(data=result, msg="当面付开通申请已提交") @FacetofaceRouter.get( "", summary="查询申请单列表", description="分页查询当面付开通申请单列表", response_model=ResponseSchema[FacetofaceOrderListOutSchema], ) async def list_controller( auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:facetoface:list"]))], page_no: Annotated[int, Query(description="页码")] = 1, page_size: Annotated[int, Query(description="每页数量")] = 20, merchant_name: Annotated[str | None, Query(description="商户名称")] = None, shop_name: Annotated[str | None, Query(description="店铺名称")] = None, order_status: Annotated[str | None, Query(description="申请单状态")] = None, start_time: Annotated[str | None, Query(description="开始时间")] = None, end_time: Annotated[str | None, Query(description="结束时间")] = None, ) -> JSONResponse: search = {} if merchant_name: search["merchant_name"] = merchant_name if shop_name: search["shop_name"] = shop_name if order_status: search["order_status"] = order_status if start_time: search["start_time"] = start_time if end_time: search["end_time"] = end_time result = await FacetofaceService.list_service( auth=auth, page_no=page_no, page_size=page_size, search=search ) return SuccessResponse(data=result, msg="查询成功") @FacetofaceRouter.get( "/enterprises/status", summary="批量查询企业当面付状态", description="传入企业ID列表,返回各企业的当面付申请状态", ) async def batch_status_controller( auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:enterprise:list"]))], ids: Annotated[str, Query(description="企业ID列表,逗号分隔")], ) -> JSONResponse: enterprise_ids = [eid.strip() for eid in ids.split(",") if eid.strip()] result = await FacetofaceService.batch_status_service(auth=auth, enterprise_ids=enterprise_ids) return SuccessResponse(data=result, msg="查询成功") @FacetofaceRouter.get( "/enterprise/{enterprise_id}", summary="按企业ID查询当面付申请单", description="查询某个企业的当面付开通申请单", response_model=ResponseSchema[FacetofaceOrderOutSchema], ) async def get_by_enterprise_controller( enterprise_id: Annotated[str, Path(description="企业ID")], auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:facetoface:list"]))], ) -> JSONResponse: result = await FacetofaceService.get_by_enterprise_service(auth=auth, enterprise_id=enterprise_id) return SuccessResponse(data=result, msg="查询成功") @FacetofaceRouter.get( "/{order_id}", summary="查询申请单详情", description="查询单个当面付开通申请单详情", response_model=ResponseSchema[FacetofaceOrderOutSchema], ) async def detail_controller( order_id: Annotated[int, Path(description="申请单ID")], auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:facetoface:list"]))], ) -> JSONResponse: result = await FacetofaceService.detail_service(auth=auth, order_id=order_id) return SuccessResponse(data=result, msg="查询成功") @FacetofaceRouter.post( "/{order_id}/query", summary="手动查询申请单状态", description="手动触发查询支付宝申请单最新状态", response_model=ResponseSchema[FacetofaceOrderOutSchema], ) async def query_status_controller( order_id: Annotated[int, Path(description="申请单ID")], auth: Annotated[AuthSchema, Depends(AuthPermission(["module_payment:facetoface:query"]))], ) -> JSONResponse: result = await FacetofaceService.query_order_service(auth=auth, order_id=order_id) log.info(f"手动查询当面付申请单状态: id={order_id}, status={result.order_status}") return SuccessResponse(data=result, msg="查询成功") # ---------- 定时轮询任务 ---------- _poll_scheduler = AsyncIOScheduler() _poll_scheduler_started = False @FacetofaceRouter.on_event("startup") async def start_facetoface_poll_scheduler(): """启动时注册当面付申请单状态轮询任务""" global _poll_scheduler_started if _poll_scheduler_started: return _poll_scheduler_started = True _poll_scheduler.add_job( FacetofaceService.poll_pending_orders, "interval", minutes=30, max_instances=1, id="facetoface_poll_orders", replace_existing=True, ) _poll_scheduler.start() log.info("[当面付] 状态轮询定时任务已注册(每30分钟执行)") @FacetofaceRouter.on_event("shutdown") async def stop_facetoface_poll_scheduler(): if _poll_scheduler.running: _poll_scheduler.shutdown(wait=False) log.info("[当面付] 状态轮询定时任务已停止")