1
This commit is contained in:
@@ -77,7 +77,7 @@ router = APIRouter(prefix="/admin", tags=["admin"])
|
||||
|
||||
# ── User Management ──────────────────────────────────────
|
||||
|
||||
@router.get("/users", response_model=list[AdminUserOut])
|
||||
@router.get("/users")
|
||||
async def list_users(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
@@ -86,13 +86,15 @@ async def list_users(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
query = select(User).order_by(User.created_at.desc())
|
||||
count_query = select(func.count(User.id))
|
||||
if search:
|
||||
query = query.where(
|
||||
(User.username.ilike(f"%{search}%"))
|
||||
| (User.email.ilike(f"%{search}%"))
|
||||
)
|
||||
search_filter = (User.username.ilike(f"%{search}%")) | (User.email.ilike(f"%{search}%"))
|
||||
query = query.where(search_filter)
|
||||
count_query = count_query.where(search_filter)
|
||||
total = (await db.execute(count_query)).scalar() or 0
|
||||
result = await db.execute(query.offset((page - 1) * page_size).limit(page_size))
|
||||
return result.scalars().all()
|
||||
items = result.scalars().all()
|
||||
return {"items": [AdminUserOut.model_validate(u) for u in items], "total": total}
|
||||
|
||||
|
||||
@router.post("/users", response_model=AdminUserOut)
|
||||
@@ -555,24 +557,6 @@ async def get_payment_stats(
|
||||
)
|
||||
month_row = month_result.one()
|
||||
|
||||
# Recent orders with filters
|
||||
recent_filters = []
|
||||
if payment_method:
|
||||
recent_filters.append(PaymentOrder.payment_method == payment_method)
|
||||
if status:
|
||||
recent_filters.append(PaymentOrder.status == status)
|
||||
recent_filters.append(PaymentOrder.created_at >= query_start)
|
||||
recent_filters.append(PaymentOrder.created_at < query_end)
|
||||
|
||||
recent_result = await db.execute(
|
||||
select(PaymentOrder, User)
|
||||
.join(User, PaymentOrder.user_id == User.id)
|
||||
.where(*recent_filters)
|
||||
.order_by(PaymentOrder.created_at.desc())
|
||||
.limit(50)
|
||||
)
|
||||
recent_data = recent_result.all()
|
||||
|
||||
return {
|
||||
"by_status": by_status,
|
||||
"today": {
|
||||
@@ -583,76 +567,68 @@ async def get_payment_stats(
|
||||
"paid_count": month_row.paid_count,
|
||||
"paid_amount": round(float(month_row.paid_amount), 2),
|
||||
},
|
||||
"recent": [
|
||||
{
|
||||
"id": o.id,
|
||||
"order_no": o.order_no,
|
||||
"user_id": o.user_id,
|
||||
"username": u.username,
|
||||
"amount": round(o.amount, 2),
|
||||
"credits": round(o.credits, 2),
|
||||
"payment_method": o.payment_method,
|
||||
"status": o.status if o.status in ("pending", "paid", "cancelled", "refunded") else "cancelled",
|
||||
"trade_no": o.trade_no,
|
||||
"paid_at": _iso(o.paid_at),
|
||||
"created_at": _iso(o.created_at),
|
||||
}
|
||||
for o, u in recent_data
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@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,
|
||||
async def list_payment_orders(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
payment_method: str | None = Query(None),
|
||||
status: str | None = Query(None),
|
||||
start_date: str | None = Query(None),
|
||||
end_date: str | None = Query(None),
|
||||
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)
|
||||
"""Return paginated payment orders for admin dashboard."""
|
||||
query = select(PaymentOrder, User.username).join(User, PaymentOrder.user_id == User.id)
|
||||
count_query = select(func.count(PaymentOrder.id))
|
||||
|
||||
filters = []
|
||||
if payment_method:
|
||||
filters.append(PaymentOrder.payment_method == payment_method)
|
||||
if status:
|
||||
query = query.where(PaymentOrder.status == status)
|
||||
filters.append(PaymentOrder.status == status)
|
||||
if start_date:
|
||||
filters.append(PaymentOrder.created_at >= datetime.fromisoformat(start_date).replace(tzinfo=CST))
|
||||
if end_date:
|
||||
filters.append(PaymentOrder.created_at < (datetime.fromisoformat(end_date) + timedelta(days=1)).replace(tzinfo=CST))
|
||||
|
||||
# Count total
|
||||
count_result = await db.execute(
|
||||
select(func.count()).select_from(query.subquery())
|
||||
)
|
||||
total = count_result.scalar() or 0
|
||||
for f in filters:
|
||||
query = query.where(f)
|
||||
count_query = count_query.where(f)
|
||||
|
||||
# Paginated results
|
||||
total = (await db.execute(count_query)).scalar() or 0
|
||||
result = await db.execute(
|
||||
query.order_by(PaymentOrder.created_at.desc())
|
||||
.offset((page - 1) * page_size)
|
||||
.limit(page_size)
|
||||
query.order_by(PaymentOrder.created_at.desc()).offset((page - 1) * page_size).limit(page_size)
|
||||
)
|
||||
orders = result.scalars().all()
|
||||
rows = result.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": round(o.amount, 2),
|
||||
"credits": round(o.credits, 2),
|
||||
"payment_method": o.payment_method,
|
||||
"status": o.status if o.status in ("pending", "paid", "cancelled", "refunded") else "cancelled",
|
||||
"trade_no": o.trade_no,
|
||||
"paid_at": _iso(o.paid_at),
|
||||
"created_at": _iso(o.created_at),
|
||||
}
|
||||
for o in orders
|
||||
],
|
||||
}
|
||||
items = [
|
||||
{
|
||||
"id": o.id,
|
||||
"orderNo": o.order_no,
|
||||
"order_no": o.order_no,
|
||||
"userId": o.user_id,
|
||||
"user_id": o.user_id,
|
||||
"username": username,
|
||||
"amount": round(float(o.amount), 2),
|
||||
"credits": round(float(o.credits), 2),
|
||||
"paymentMethod": o.payment_method,
|
||||
"payment_method": o.payment_method,
|
||||
"status": o.status,
|
||||
"tradeNo": o.trade_no,
|
||||
"trade_no": o.trade_no,
|
||||
"paidAt": o.paid_at.isoformat() if o.paid_at else None,
|
||||
"paid_at": o.paid_at.isoformat() if o.paid_at else None,
|
||||
"createdAt": o.created_at.isoformat() if o.created_at else None,
|
||||
"created_at": o.created_at.isoformat() if o.created_at else None,
|
||||
}
|
||||
for o, username in rows
|
||||
]
|
||||
|
||||
return {"items": items, "total": total, "page": page, "page_size": page_size}
|
||||
|
||||
@router.put("/payment-configs/{config_id}")
|
||||
async def update_payment_config(
|
||||
|
||||
@@ -15,7 +15,7 @@ from app.services.credits import get_records
|
||||
router = APIRouter(prefix="/credits", tags=["credits"])
|
||||
|
||||
|
||||
@router.get("", response_model=CreditBalanceOut)
|
||||
@router.get("")
|
||||
async def get_credits(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
@@ -23,12 +23,11 @@ async def get_credits(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
records, total = await get_records(db, current_user.id, page, page_size)
|
||||
from fastapi.responses import JSONResponse
|
||||
return JSONResponse(content={
|
||||
return {
|
||||
"credits": round(current_user.credits, 2),
|
||||
"records": [CreditRecordOut.model_validate(r) for r in records],
|
||||
"total": total,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@router.get(
|
||||
|
||||
@@ -288,7 +288,7 @@ async def alipay_callback(request: Request, db: AsyncSession = Depends(get_db)):
|
||||
return "success"
|
||||
|
||||
|
||||
@router.get("/orders", response_model=list[PaymentOrderOut])
|
||||
@router.get("/orders")
|
||||
async def list_orders(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
@@ -296,10 +296,10 @@ async def list_orders(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
from app.services.payment import _check_and_expire_order
|
||||
|
||||
|
||||
count_query = select(func.count(PaymentOrder.id)).where(PaymentOrder.user_id == current_user.id)
|
||||
total = (await db.execute(count_query)).scalar() or 0
|
||||
|
||||
|
||||
result = await db.execute(
|
||||
select(PaymentOrder)
|
||||
.where(PaymentOrder.user_id == current_user.id)
|
||||
@@ -310,9 +310,8 @@ async def list_orders(
|
||||
orders = result.scalars().all()
|
||||
for o in orders:
|
||||
await _check_and_expire_order(db, o)
|
||||
|
||||
from fastapi.responses import JSONResponse
|
||||
return JSONResponse(content={"items": orders, "total": total})
|
||||
|
||||
return {"items": [PaymentOrderOut.model_validate(o) for o in orders], "total": total}
|
||||
|
||||
|
||||
@router.get("/orders/{order_no}", response_model=PaymentOrderOut)
|
||||
|
||||
Reference in New Issue
Block a user