| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- """超级管理员余额管理路由"""
- import logging
- from decimal import Decimal
- from fastapi import APIRouter, Depends, HTTPException, Query
- from pydantic import BaseModel
- from sqlalchemy.ext.asyncio import AsyncSession
- from app.database import get_db
- from app.services import sa_balance as svc
- logger = logging.getLogger(__name__)
- router = APIRouter(prefix="/api/sa-balance", tags=["超管余额"])
- public_router = APIRouter(prefix="/api/public/sa-balance", tags=["超管余额公开接口"])
- # ---------- 请求/响应 Schema ----------
- class RechargeRequest(BaseModel):
- sa_id: int
- amount: float
- remark: str = ""
- class DeductRequest(BaseModel):
- sa_id: int
- amount: float
- biz_order_no: str
- # ---------- 内部控制面板接口 ----------
- @router.post("/recharge", summary="给超管充值")
- async def handle_recharge(
- payload: RechargeRequest,
- db: AsyncSession = Depends(get_db),
- ):
- try:
- result = await svc.recharge(db, payload.sa_id, Decimal(str(payload.amount)), payload.remark)
- return {"code": 200, "data": result}
- except ValueError as e:
- raise HTTPException(status_code=400, detail=str(e))
- @router.get("/list", summary="所有超管余额列表")
- async def handle_list_sa_balance(
- db: AsyncSession = Depends(get_db),
- ):
- items = await svc.get_all_sa_balance(db)
- return {"code": 200, "data": items}
- @router.get("/{sa_id}", summary="查询超管余额详情")
- async def handle_get_sa_balance(
- sa_id: int,
- db: AsyncSession = Depends(get_db),
- ):
- try:
- info = await svc.get_sa_balance_info(db, sa_id)
- return {"code": 200, "data": info}
- except ValueError as e:
- raise HTTPException(status_code=404, detail=str(e))
- @router.get("/{sa_id}/logs", summary="查询超管余额变动日志")
- async def handle_get_sa_balance_logs(
- sa_id: int,
- page: int = Query(1, ge=1),
- size: int = Query(20, ge=1, le=100),
- db: AsyncSession = Depends(get_db),
- ):
- result = await svc.get_balance_logs(db, sa_id, page, size)
- return {"code": 200, "data": result}
- # ---------- AIGC-Space 调用的公开接口 ----------
- @public_router.post("/deduct", summary="扣减超管余额(AIGC-Space 调用)")
- async def handle_deduct(
- payload: DeductRequest,
- db: AsyncSession = Depends(get_db),
- ):
- try:
- ok, reason = await svc.deduct(db, payload.sa_id, Decimal(str(payload.amount)), payload.biz_order_no)
- return {"code": 200, "success": ok, "reason": reason}
- except Exception as e:
- # 扣减异常(DB 错误等),写入补偿记录,后台任务会重试
- logger.exception("超管扣减异常,写入补偿记录: sa_id=%d, amount=%s, order=%s",
- payload.sa_id, payload.amount, payload.biz_order_no)
- try:
- from app.services.compensation_service import record_pending
- await record_pending(
- db,
- target_type="sa",
- target_id=payload.sa_id,
- amount=Decimal(str(payload.amount)),
- biz_order_no=payload.biz_order_no,
- error_msg=str(e),
- )
- # 返回 success=True,告诉调用方"已接收,补偿任务会处理"
- return {"code": 200, "success": True, "reason": "已记录待补偿", "compensated": True}
- except Exception as record_err:
- logger.error("补偿记录写入失败: %s", record_err)
- return {"code": 200, "success": False, "reason": f"扣减异常且补偿记录失败: {e}"}
- class RecordPendingRequest(BaseModel):
- sa_id: int
- amount: float
- biz_order_no: str
- error_msg: str = ""
- @public_router.post("/record-pending", summary="记录待补偿扣减(AIGC-Space 网络失败时调用)")
- async def handle_record_pending(
- payload: RecordPendingRequest,
- db: AsyncSession = Depends(get_db),
- ):
- """当 AIGC-Space 调用 /deduct 失败(网络超时等)时,调用此端点记录待补偿"""
- try:
- from app.services.compensation_service import record_pending
- await record_pending(
- db,
- target_type="sa",
- target_id=payload.sa_id,
- amount=Decimal(str(payload.amount)),
- biz_order_no=payload.biz_order_no,
- error_msg=payload.error_msg,
- )
- return {"code": 200, "success": True, "reason": "已记录待补偿"}
- except Exception as e:
- logger.error("记录待补偿失败: %s", e)
- return {"code": 200, "success": False, "reason": str(e)}
- @public_router.get("/{sa_id}/status", summary="查询超管余额状态(AIGC-Space 调用)")
- async def handle_sa_balance_status(
- sa_id: int,
- db: AsyncSession = Depends(get_db),
- ):
- try:
- balance = await svc.get_balance(db, sa_id)
- return {
- "code": 200,
- "data": {
- "sa_id": sa_id,
- "balance": str(balance),
- "is_sufficient": balance > 0,
- },
- }
- except ValueError as e:
- raise HTTPException(status_code=404, detail=str(e))
|