You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
10 KiB
Python
276 lines
10 KiB
Python
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Body, Depends, Path, Query, Request
|
|
from fastapi.exceptions import HTTPException
|
|
from fastapi.responses import JSONResponse
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.common.response import ResponseSchema, SuccessResponse
|
|
from app.core.base_params import PaginationQueryParam
|
|
from app.core.base_schema import AuthSchema, PageResultSchema
|
|
from app.core.dependencies import AuthPermission, db_getter
|
|
from app.core.exceptions import CustomException
|
|
from app.core.logger import logger
|
|
from app.core.router_class import OperationLogRoute
|
|
|
|
from .schema import (
|
|
OrderCreateSchema,
|
|
OrderOutSchema,
|
|
OrderQueryParam,
|
|
OrderStatusMessage,
|
|
PaymentCreateOut,
|
|
PaymentRecordOutSchema,
|
|
PaymentStatusOut,
|
|
RefundApplySchema,
|
|
RefundOutSchema,
|
|
RefundReviewSchema,
|
|
)
|
|
from .service import OrderService, PaymentService, RefundService
|
|
|
|
OrderRouter = APIRouter(route_class=OperationLogRoute, prefix="/order", tags=["平台管理", "订单管理"])
|
|
PaymentRouter = APIRouter(route_class=OperationLogRoute, prefix="/payment", tags=["平台管理", "支付管理"])
|
|
RefundRouter = APIRouter(route_class=OperationLogRoute, prefix="/refund", tags=["平台管理", "退款管理"])
|
|
TenantOrderRouter = APIRouter(route_class=OperationLogRoute, prefix="/order", tags=["平台管理", "租户订单"])
|
|
|
|
@OrderRouter.post(
|
|
"/create",
|
|
summary="创建订单",
|
|
response_model=ResponseSchema[OrderOutSchema],
|
|
)
|
|
async def order_create_controller(
|
|
data: Annotated[OrderCreateSchema, Body()],
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_platform:order:create"]))],
|
|
) -> JSONResponse:
|
|
result = await OrderService.create_order(auth=auth, data=data)
|
|
return SuccessResponse(data=result, msg="订单创建成功")
|
|
|
|
@OrderRouter.get(
|
|
"/detail/{order_id}",
|
|
summary="订单详情",
|
|
response_model=ResponseSchema[OrderOutSchema],
|
|
)
|
|
async def order_detail_controller(
|
|
order_id: Annotated[int, Path(ge=1)],
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_platform:order:query"]))],
|
|
) -> JSONResponse:
|
|
order = await OrderService.get_detail(auth=auth, order_id=order_id)
|
|
if not order:
|
|
raise HTTPException(status_code=404, detail="订单不存在")
|
|
return SuccessResponse(data=order)
|
|
|
|
@OrderRouter.get(
|
|
"/list",
|
|
summary="订单列表",
|
|
response_model=ResponseSchema[PageResultSchema[OrderOutSchema]],
|
|
)
|
|
async def order_list_controller(
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_platform:order:query"]))],
|
|
page: Annotated[PaginationQueryParam, Depends()],
|
|
search: Annotated[OrderQueryParam, Depends()],
|
|
) -> JSONResponse:
|
|
items, total = await OrderService.get_list(
|
|
auth=auth,
|
|
page_no=page.page_no,
|
|
page_size=page.page_size,
|
|
order_by=page.order_by,
|
|
search=search,
|
|
)
|
|
offset = (page.page_no - 1) * page.page_size
|
|
result = PageResultSchema(
|
|
page_no=page.page_no,
|
|
page_size=page.page_size,
|
|
total=total,
|
|
has_next=offset + page.page_size < total,
|
|
items=items,
|
|
)
|
|
return SuccessResponse(data=result)
|
|
|
|
@OrderRouter.post(
|
|
"/cancel/{order_id}",
|
|
summary="取消订单",
|
|
response_model=ResponseSchema[OrderStatusMessage],
|
|
)
|
|
async def order_cancel_controller(
|
|
order_id: Annotated[int, Path(ge=1)],
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_platform:order:update"]))],
|
|
) -> JSONResponse:
|
|
result = await OrderService.cancel_order(auth=auth, order_id=order_id)
|
|
return SuccessResponse(data=result, msg=result["message"])
|
|
|
|
@PaymentRouter.post(
|
|
"/pay/{order_id}",
|
|
summary="创建支付(获取支付 URL/二维码)",
|
|
response_model=ResponseSchema[PaymentCreateOut],
|
|
)
|
|
async def payment_create_controller(
|
|
order_id: Annotated[int, Path(ge=1)],
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_platform:order:update"]))],
|
|
request: Request,
|
|
method: Annotated[str, Query(description="支付渠道: alipay / wxpay(留空=自动)")] = "",
|
|
) -> JSONResponse:
|
|
base_url = str(request.base_url).rstrip("/")
|
|
result = await PaymentService.create_payment(auth=auth, order_id=order_id, method=method, notify_base_url=base_url)
|
|
return SuccessResponse(data=result, msg="支付信息已生成")
|
|
|
|
@PaymentRouter.get(
|
|
"/status/{order_id}",
|
|
summary="查询支付状态(供前端轮询)",
|
|
response_model=ResponseSchema[PaymentStatusOut],
|
|
)
|
|
async def payment_status_controller(
|
|
order_id: Annotated[int, Path(ge=1)],
|
|
db: Annotated[AsyncSession, Depends(db_getter)],
|
|
) -> JSONResponse:
|
|
auth = AuthSchema(db=db, check_data_scope=False)
|
|
result = await OrderService.check_payment_status(auth=auth, order_id=order_id)
|
|
return SuccessResponse(data=result)
|
|
|
|
@PaymentRouter.post(
|
|
"/callback/{method}",
|
|
summary="支付回调(统一入口)",
|
|
response_model=ResponseSchema[dict],
|
|
)
|
|
async def payment_callback_controller(
|
|
method: Annotated[str, Path(description="支付渠道: alipay / wxpay / mock")],
|
|
data: Annotated[dict, Body()],
|
|
db: Annotated[AsyncSession, Depends(db_getter)],
|
|
) -> JSONResponse:
|
|
try:
|
|
auth = AuthSchema(db=db, check_data_scope=False)
|
|
result = await PaymentService.handle_callback(auth=auth, method=method, callback_data=data)
|
|
logger.info(f"支付回调处理成功: {result}")
|
|
return SuccessResponse(data=result)
|
|
except CustomException as e:
|
|
logger.warning(f"支付回调处理失败: {e}")
|
|
return SuccessResponse(data={"message": str(e)}, code=400)
|
|
|
|
@PaymentRouter.post(
|
|
"/mock/callback",
|
|
summary="Mock 支付回调(开发环境触发模拟支付)",
|
|
response_model=ResponseSchema[dict],
|
|
)
|
|
async def payment_mock_callback_controller(
|
|
order_id: Annotated[int, Body(ge=1, description="订单 ID")],
|
|
db: Annotated[AsyncSession, Depends(db_getter)],
|
|
) -> JSONResponse:
|
|
from app.utils.payment import get_mock_gateway
|
|
|
|
from .crud import OrderCRUD
|
|
|
|
auth = AuthSchema(db=db, check_data_scope=False)
|
|
order = await OrderCRUD(auth).get_by_id(order_id)
|
|
if not order:
|
|
raise HTTPException(status_code=404, detail="订单不存在")
|
|
|
|
mock_gw = get_mock_gateway()
|
|
callback_data = mock_gw.get_mock_callback_data(order.id, order.order_no)
|
|
result = await PaymentService.handle_callback(auth=auth, method="mock", callback_data=callback_data)
|
|
logger.info(f"Mock 支付回调触发: order_id={order_id}")
|
|
return SuccessResponse(data=result, msg="模拟支付成功")
|
|
|
|
@PaymentRouter.get(
|
|
"/record/list",
|
|
summary="支付记录列表",
|
|
response_model=ResponseSchema[PageResultSchema[PaymentRecordOutSchema]],
|
|
)
|
|
async def payment_record_list_controller(
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_platform:order:query"]))],
|
|
page: Annotated[int, Query(ge=1)] = 1,
|
|
page_size: Annotated[int, Query(ge=1, le=100)] = 20,
|
|
) -> JSONResponse:
|
|
offset = (page - 1) * page_size
|
|
items, total = await PaymentService.get_records(auth=auth, offset=offset, limit=page_size)
|
|
result = PageResultSchema(
|
|
page_no=page,
|
|
page_size=page_size,
|
|
total=total,
|
|
has_next=offset + page_size < total,
|
|
items=items,
|
|
)
|
|
return SuccessResponse(data=result)
|
|
|
|
@RefundRouter.get(
|
|
"/list",
|
|
summary="退款审核列表",
|
|
response_model=ResponseSchema[PageResultSchema[RefundOutSchema]],
|
|
)
|
|
async def refund_list_controller(
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_platform:order:query"]))],
|
|
status: Annotated[int | None, Query(description="状态筛选")] = None,
|
|
page: Annotated[int, Query(ge=1)] = 1,
|
|
page_size: Annotated[int, Query(ge=1, le=100)] = 20,
|
|
) -> JSONResponse:
|
|
offset = (page - 1) * page_size
|
|
items, total = await RefundService.get_list(auth=auth, status=status, offset=offset, limit=page_size)
|
|
result = PageResultSchema(
|
|
page_no=page,
|
|
page_size=page_size,
|
|
total=total,
|
|
has_next=offset + page_size < total,
|
|
items=items,
|
|
)
|
|
return SuccessResponse(data=result)
|
|
|
|
@RefundRouter.put(
|
|
"/approve/{refund_id}",
|
|
summary="批准退款",
|
|
response_model=ResponseSchema[OrderStatusMessage],
|
|
)
|
|
async def refund_approve_controller(
|
|
refund_id: Annotated[int, Path(ge=1)],
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_platform:order:update"]))],
|
|
) -> JSONResponse:
|
|
result = await RefundService.approve(
|
|
auth=auth,
|
|
refund_id=refund_id,
|
|
reviewer_id=auth.user.id if auth.user else 0,
|
|
operator_name=auth.user.name if auth.user else "",
|
|
)
|
|
return SuccessResponse(data=result, msg=result["message"])
|
|
|
|
@RefundRouter.put(
|
|
"/reject/{refund_id}",
|
|
summary="驳回退款",
|
|
response_model=ResponseSchema[OrderStatusMessage],
|
|
)
|
|
async def refund_reject_controller(
|
|
refund_id: Annotated[int, Path(ge=1)],
|
|
data: Annotated[RefundReviewSchema, Body()],
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["module_platform:order:update"]))],
|
|
) -> JSONResponse:
|
|
result = await RefundService.reject(
|
|
auth=auth,
|
|
refund_id=refund_id,
|
|
reviewer_id=auth.user.id if auth.user else 0,
|
|
data=data,
|
|
operator_name=auth.user.name if auth.user else "",
|
|
)
|
|
return SuccessResponse(data=result, msg=result["message"])
|
|
|
|
@TenantOrderRouter.post(
|
|
"/create",
|
|
summary="租户端创建订单",
|
|
response_model=ResponseSchema[OrderOutSchema],
|
|
)
|
|
async def tenant_order_create_controller(
|
|
data: Annotated[OrderCreateSchema, Body()],
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["tenant:order:create"]))],
|
|
) -> JSONResponse:
|
|
if data.tenant_id != auth.tenant_id:
|
|
raise HTTPException(status_code=403, detail="无权操作")
|
|
result = await OrderService.create_order(auth=auth, data=data)
|
|
return SuccessResponse(data=result, msg="订单创建成功")
|
|
|
|
@TenantOrderRouter.post(
|
|
"/refund/apply/{order_id}",
|
|
summary="申请退款",
|
|
response_model=ResponseSchema[RefundOutSchema],
|
|
)
|
|
async def tenant_refund_apply_controller(
|
|
order_id: Annotated[int, Path(ge=1)],
|
|
data: Annotated[RefundApplySchema, Body()],
|
|
auth: Annotated[AuthSchema, Depends(AuthPermission(["tenant:order:refund"]))],
|
|
) -> JSONResponse:
|
|
result = await RefundService.apply(auth=auth, data=data, order_id=order_id)
|
|
return SuccessResponse(data=result, msg="退款申请已提交")
|