增加后台支付列表,修改前台有问题也能支付成功,增加支付日志

This commit is contained in:
2026-06-10 14:47:06 +08:00
parent 94b84f288d
commit 9974fd5a2e
14 changed files with 688 additions and 10 deletions
+190
View File
@@ -440,6 +440,123 @@ async def batch_update_payment_configs(
return {"ok": True}
@router.get("/payment-stats")
async def get_payment_stats(
admin: User = Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
"""Return payment statistics for admin dashboard."""
from sqlalchemy import func
# Status breakdown
status_result = await db.execute(
select(
PaymentOrder.status,
func.count().label("count"),
func.coalesce(func.sum(PaymentOrder.amount), 0).label("amount"),
).group_by(PaymentOrder.status)
)
by_status = {}
for row in status_result.all():
by_status[row.status] = {"count": row.count, "amount": float(row.amount)}
# Today's stats
today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
today_result = await db.execute(
select(
func.count().label("paid_count"),
func.coalesce(func.sum(PaymentOrder.amount), 0).label("paid_amount"),
).where(
PaymentOrder.status == "paid",
PaymentOrder.paid_at >= today_start,
)
)
today_row = today_result.one()
# Recent 50 orders
recent_result = await db.execute(
select(PaymentOrder)
.order_by(PaymentOrder.created_at.desc())
.limit(50)
)
recent = recent_result.scalars().all()
return {
"by_status": by_status,
"today": {
"paid_count": today_row.paid_count,
"paid_amount": float(today_row.paid_amount),
},
"recent": [
{
"id": o.id,
"order_no": o.order_no,
"user_id": o.user_id,
"amount": o.amount,
"credits": o.credits,
"payment_method": o.payment_method,
"status": o.status,
"trade_no": o.trade_no,
"paid_at": o.paid_at.isoformat() if o.paid_at else None,
"created_at": o.created_at.isoformat() if o.created_at else None,
}
for o in recent
],
}
@router.get("/payment-orders")
async def get_admin_payment_orders(
method: str | None = None,
status: str | None = None,
page: int = 1,
page_size: int = 20,
admin: User = Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
"""Return paginated payment orders for admin."""
query = select(PaymentOrder)
if method:
query = query.where(PaymentOrder.payment_method == method)
if status:
query = query.where(PaymentOrder.status == status)
# Count total
count_result = await db.execute(
select(func.count()).select_from(query.subquery())
)
total = count_result.scalar() or 0
# Paginated results
result = await db.execute(
query.order_by(PaymentOrder.created_at.desc())
.offset((page - 1) * page_size)
.limit(page_size)
)
orders = result.scalars().all()
return {
"total": total,
"page": page,
"page_size": page_size,
"items": [
{
"id": o.id,
"order_no": o.order_no,
"user_id": o.user_id,
"amount": o.amount,
"credits": o.credits,
"payment_method": o.payment_method,
"status": o.status,
"trade_no": o.trade_no,
"paid_at": o.paid_at.isoformat() if o.paid_at else None,
"created_at": o.created_at.isoformat() if o.created_at else None,
}
for o in orders
],
}
@router.put("/payment-configs/{config_id}")
async def update_payment_config(
config_id: str,
@@ -1250,3 +1367,76 @@ async def admin_generate_video(
await db.flush()
return {"message": "ok", "record_id": record_id}
# ── Payment Stats ────────────────────────────────────────
@router.get("/payment-stats")
async def get_payment_stats(
admin: User = Depends(get_admin_user),
db: AsyncSession = Depends(get_db),
):
"""Payment statistics for admin dashboard."""
from app.models.payment_order import PaymentOrder
from datetime import datetime
# Count and revenue by status
rows = (await db.execute(
select(
PaymentOrder.status,
PaymentOrder.payment_method,
func.count(PaymentOrder.id).label("count"),
func.coalesce(func.sum(PaymentOrder.amount), 0).label("total_amount"),
).group_by(PaymentOrder.status, PaymentOrder.payment_method)
)).all()
by_status: dict[str, dict] = {}
for r in rows:
s = r.status
if s not in by_status:
by_status[s] = {"count": 0, "amount": 0.0}
by_status[s]["count"] += r.count
by_status[s]["amount"] += float(r.total_amount)
# Recent orders (last 50)
recent = (await db.execute(
select(PaymentOrder)
.order_by(PaymentOrder.created_at.desc())
.limit(50)
)).scalars().all()
# Today stats
today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
today_paid = (await db.execute(
select(
func.count(PaymentOrder.id),
func.coalesce(func.sum(PaymentOrder.amount), 0),
).where(
PaymentOrder.status == "paid",
PaymentOrder.paid_at >= today_start,
)
)).first()
today_count, today_amount = (today_paid or (0, 0))
return {
"by_status": by_status,
"today": {
"paid_count": int(today_count or 0),
"paid_amount": float(today_amount or 0),
},
"recent": [
{
"id": o.id,
"order_no": o.order_no,
"user_id": o.user_id,
"amount": o.amount,
"credits": o.credits,
"payment_method": o.payment_method,
"status": o.status,
"trade_no": o.trade_no,
"created_at": _iso(o.created_at),
"paid_at": _iso(o.paid_at),
}
for o in recent
],
}
+36 -2
View File
@@ -89,7 +89,10 @@ async def wechat_callback(request: Request, db: AsyncSession = Depends(get_db)):
async def alipay_callback(request: Request, db: AsyncSession = Depends(get_db)):
form_data = await request.form()
data = dict(form_data)
logger.info(f"Alipay callback received: {list(data.keys())}")
logger.info(
f"ALIPAY_CALLBACK order_no={data.get('out_trade_no')} "
f"trade_no={data.get('trade_no', '')} status={data.get('trade_status', '')}"
)
# Verify signature first
if not await verify_alipay_callback(data, db):
@@ -114,9 +117,40 @@ async def list_orders(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
# Auto-expire stale pending orders before returning
from app.services.payment import _check_and_expire_order
result = await db.execute(
select(PaymentOrder)
.where(PaymentOrder.user_id == current_user.id)
.order_by(PaymentOrder.created_at.desc())
)
return result.scalars().all()
orders = result.scalars().all()
for o in orders:
await _check_and_expire_order(db, o)
return orders
@router.post("/orders/{order_no}/cancel")
async def cancel_order(
order_no: str,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Cancel a pending order. Only the order owner can cancel, only if still pending."""
result = await db.execute(
select(PaymentOrder).where(
PaymentOrder.order_no == order_no,
PaymentOrder.user_id == current_user.id,
).limit(1)
)
order = result.scalar_one_or_none()
if not order:
raise HTTPException(status_code=404, detail="订单不存在")
if order.status != "pending":
raise HTTPException(status_code=400, detail=f"订单状态为{order.status},无法取消")
order.status = "cancelled"
await db.flush()
logger.info(
f"ORDER_CANCELLED order_no={order_no} user={current_user.id} amount={order.amount}"
)
return {"ok": True}