优化斗鱼充值
This commit is contained in:
@@ -6,7 +6,8 @@ import asyncio
|
||||
import threading
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, WebSocket, WebSocketDisconnect
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, WebSocket, WebSocketDisconnect
|
||||
from loguru import logger
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
|
||||
@@ -37,11 +38,85 @@ from ..services.douyu_service import (
|
||||
douyu_task_payload,
|
||||
ensure_douyu_config,
|
||||
)
|
||||
from core.douyu import FishFinRechargeClient, FishFinRechargeConfig, FishFinRechargeConfigError
|
||||
|
||||
|
||||
router = APIRouter(prefix="/api/douyu", tags=["斗鱼活动"])
|
||||
|
||||
|
||||
def _supplier_response_value(payload: dict, *keys: str):
|
||||
"""兼容供应商把订单字段放在根节点、data 或 result 中。"""
|
||||
data = payload.get("data") if isinstance(payload.get("data"), dict) else {}
|
||||
result = payload.get("result") if isinstance(payload.get("result"), dict) else {}
|
||||
for source in (payload, data, result):
|
||||
for key in keys:
|
||||
if source.get(key) is not None:
|
||||
return source[key]
|
||||
return None
|
||||
|
||||
|
||||
@router.post("/supplier-recharge/callback")
|
||||
async def supplier_recharge_callback(request: Request, db: Session = Depends(get_db)):
|
||||
"""接收供应商直充终态通知,验签后按 out_order_id 幂等更新任务。"""
|
||||
try:
|
||||
payload = await request.json()
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail="供应商回调不是 JSON") from exc
|
||||
if not isinstance(payload, dict):
|
||||
raise HTTPException(status_code=400, detail="供应商回调格式无效")
|
||||
try:
|
||||
client = FishFinRechargeClient(FishFinRechargeConfig.from_env())
|
||||
except FishFinRechargeConfigError as exc:
|
||||
logger.error("[douyu] 供应商直充回调配置无效: {}", exc)
|
||||
raise HTTPException(status_code=503, detail="供应商直充回调未配置") from exc
|
||||
if not client.verify_response_sign(payload, "POST"):
|
||||
logger.warning("[douyu] 供应商直充回调验签失败: keys={}", sorted(payload))
|
||||
raise HTTPException(status_code=401, detail="供应商回调签名无效")
|
||||
|
||||
out_order_id = str(_supplier_response_value(payload, "out_order_id") or "").strip()
|
||||
if not out_order_id:
|
||||
raise HTTPException(status_code=400, detail="供应商回调缺少 out_order_id")
|
||||
task = (
|
||||
db.query(DouyuTask)
|
||||
.filter(DouyuTask.supplier_out_order_id == out_order_id, DouyuTask.task_type == "create_gold_qr")
|
||||
.first()
|
||||
)
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="供应商回调订单不存在")
|
||||
|
||||
status = DouyuBatchRunner._to_int(_supplier_response_value(payload, "order_status", "orderStatus"))
|
||||
result = dict(task.result) if isinstance(task.result, dict) else {}
|
||||
result.update({
|
||||
"out_order_id": out_order_id,
|
||||
"order_id": _supplier_response_value(payload, "order_id", "orderId") or result.get("order_id"),
|
||||
"supplier_order_status": status,
|
||||
"supplier_order": DouyuBatchRunner._supplier_result(payload),
|
||||
"supplier_callback_received": True,
|
||||
})
|
||||
if task.status not in {"success", "failed", "stopped"}:
|
||||
if status == 2:
|
||||
task.status = "success"
|
||||
task.message = "供应商直充成功(异步通知)"
|
||||
task.finished_at = datetime.now(timezone.utc)
|
||||
if task.account:
|
||||
task.account.bind_status = "gold_recharged"
|
||||
task.account.updated_at = datetime.now(timezone.utc)
|
||||
elif status in {3, 4}:
|
||||
reason = str(_supplier_response_value(payload, "fail_reason", "message", "msg") or "供应商直充失败")
|
||||
task.status = "failed"
|
||||
task.message = reason[:512]
|
||||
task.finished_at = datetime.now(timezone.utc)
|
||||
else:
|
||||
task.status = "running"
|
||||
task.message = f"供应商直充订单处理中(状态 {status if status is not None else '-'})"
|
||||
task.result = result
|
||||
db.commit()
|
||||
logger.info("[douyu] 供应商直充回调已处理: out_order_id={} status={}", out_order_id, status)
|
||||
acknowledgement = {"code": 200, "message": "success"}
|
||||
acknowledgement["sign"] = client.sign(acknowledgement, "POST")
|
||||
return acknowledgement
|
||||
|
||||
|
||||
def _can_view_all(user: User) -> bool:
|
||||
return user_has_permission(user, "account:view_all")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user