Files
qihuaiflow/backend/python/app/api/admin/payment_admin.py
T
qyyfw 78ff261b5a 初始提交: Qihu-AIFlow v2.0.1 完整项目
- 前端 React+TypeScript (源码+构建产物)
- 后端 Python FastAPI
- 数据库迁移 001-015
- Docker部署配置
- 微信ClawBot API频道绑定
- 项目文档 README.md
2026-07-02 14:40:46 +08:00

227 lines
7.1 KiB
Python

"""
支付渠道配置 API(管理员)
"""
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session
from sqlalchemy import func
from pydantic import BaseModel
from typing import Optional
from ...database import get_db, SessionLocal
from ...models import PaymentConfig, Order
from ...auth import get_current_admin
from ...middleware.log_service import LogService
import uuid
import csv
import io
from datetime import datetime
router = APIRouter()
class PaymentConfigUpdate(BaseModel):
app_id: Optional[str] = None
merchant_id: Optional[str] = None
api_key: Optional[str] = None
api_v3_key: Optional[str] = None
cert_path: Optional[str] = None
cert_serial_no: Optional[str] = None
alipay_public_key: Optional[str] = None
app_private_key: Optional[str] = None
gateway_url: Optional[str] = None
notify_url: Optional[str] = None
return_url: Optional[str] = None
status: Optional[str] = None
# ============================================================
# 支付配置管理
# ============================================================
@router.get("/configs")
async def get_payment_configs(
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""获取支付配置列表"""
configs = db.query(PaymentConfig).order_by(
PaymentConfig.created_at.desc()
).all()
return [
{
"id": c.id,
"type": c.type,
"app_id": c.app_id,
"merchant_id": c.merchant_id,
"api_key": c.api_key,
"api_v3_key": c.api_v3_key,
"cert_path": c.cert_path,
"cert_serial_no": c.cert_serial_no,
"alipay_public_key": c.alipay_public_key,
"app_private_key": c.app_private_key,
"gateway_url": c.gateway_url,
"notify_url": c.notify_url,
"return_url": c.return_url,
"status": c.status,
"created_at": c.created_at.isoformat() if c.created_at else None,
"updated_at": c.updated_at.isoformat() if c.updated_at else None,
}
for c in configs
]
@router.put("/configs/{id}")
async def update_payment_config(
id: str,
data: PaymentConfigUpdate,
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""更新支付配置"""
config = db.query(PaymentConfig).filter(PaymentConfig.id == id).first()
if not config:
raise HTTPException(status_code=404, detail="支付配置不存在")
update_data = data.model_dump(exclude_unset=True)
for key, value in update_data.items():
if hasattr(config, key):
setattr(config, key, value)
config.updated_at = datetime.now()
db.commit()
LogService.log_audit(
admin_id=admin_user.id,
admin_name=admin_user.username,
action="update_payment_config",
target_type="payment_config",
target_id=id,
detail=f"更新支付配置 [{config.type}]",
)
return {"code": 0, "message": "支付配置已更新"}
# ============================================================
# 订单管理
# ============================================================
@router.get("/orders")
async def get_orders(
status: str = Query(None, description="订单状态筛选: pending/paid/refunded/cancelled"),
order_type: str = Query(None, description="订单类型筛选: membership/service/app"),
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""获取所有订单列表"""
query = db.query(Order)
if status:
query = query.filter(Order.status == status)
if order_type:
query = query.filter(Order.order_type == order_type)
total = query.count()
orders = query.order_by(Order.created_at.desc()).offset(
(page - 1) * page_size
).limit(page_size).all()
return {
"total": total,
"page": page,
"page_size": page_size,
"items": [
{
"id": o.id,
"user_id": o.user_id,
"order_type": o.order_type,
"item_id": o.item_id,
"item_name": o.item_name,
"amount": float(o.amount) if o.amount else 0,
"coupon_id": o.coupon_id,
"discount_amount": float(o.discount_amount) if o.discount_amount else 0,
"payment_method": o.payment_method,
"payment_id": o.payment_id,
"status": o.status,
"created_at": o.created_at.isoformat() if o.created_at else None,
"updated_at": o.updated_at.isoformat() if o.updated_at else None,
}
for o in orders
],
}
@router.put("/orders/{id}/refund")
async def refund_order(
id: str,
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""退款操作"""
order = db.query(Order).filter(Order.id == id).first()
if not order:
raise HTTPException(status_code=404, detail="订单不存在")
if order.status != "paid":
raise HTTPException(status_code=400, detail="只能对已支付的订单进行退款")
if order.status == "refunded":
raise HTTPException(status_code=400, detail="订单已退款")
order.status = "refunded"
order.updated_at = datetime.now()
db.commit()
LogService.log_audit(
admin_id=admin_user.id,
admin_name=admin_user.username,
action="refund_order",
target_type="order",
target_id=id,
detail=f"退款订单 {order.id}, 金额 {float(order.amount)}",
)
return {"message": "退款成功"}
@router.get("/orders/export")
async def export_orders(
status: str = Query(None, description="订单状态筛选"),
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""导出订单(CSV格式)"""
query = db.query(Order)
if status:
query = query.filter(Order.status == status)
orders = query.order_by(Order.created_at.desc()).all()
output = io.StringIO()
writer = csv.writer(output)
writer.writerow([
"订单ID", "用户ID", "订单类型", "商品ID", "商品名称",
"金额", "优惠券ID", "折扣金额", "支付方式", "支付ID",
"状态", "创建时间", "更新时间"
])
for o in orders:
writer.writerow([
o.id, o.user_id, o.order_type, o.item_id, o.item_name,
float(o.amount) if o.amount else 0,
o.coupon_id,
float(o.discount_amount) if o.discount_amount else 0,
o.payment_method, o.payment_id,
o.status,
o.created_at.isoformat() if o.created_at else "",
o.updated_at.isoformat() if o.updated_at else "",
])
output.seek(0)
return StreamingResponse(
iter([output.getvalue()]),
media_type="text/csv",
headers={
"Content-Disposition": f"attachment; filename=orders_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
},
)