Files
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

609 lines
20 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import and_, desc, asc
from pydantic import BaseModel, Field
from typing import Optional
import uuid
from datetime import datetime, date
from ..database import get_db
from ..models import (
User, MembershipPlan, Order, Coupon, ValueAddedService,
CustomServiceOrder, CustomServiceRequest, MembershipSubscription,
gen_uuid
)
from ..auth import get_current_user
from ..middleware.log_service import LogService
router = APIRouter()
class CreateOrderRequest(BaseModel):
item_id: str
coupon_id: Optional[str] = None
order_type: str = "membership"
amount: Optional[float] = None
class UseCouponRequest(BaseModel):
code: str
order_id: Optional[str] = None
class CustomServiceOrderRequest(BaseModel):
service_id: str
description: Optional[str] = None
budget: Optional[float] = None
contact_info: Optional[str] = None
class ServiceRequestModel(BaseModel):
"""增值服务工单请求模型(数据定制/私有化/平台代理)"""
service_id: str
title: str
description: Optional[str] = None
requirements: Optional[dict] = None # 需求详情
@router.get("/plans")
def list_plans(db: Session = Depends(get_db)):
plans = db.query(MembershipPlan).filter(MembershipPlan.is_active == True).order_by(asc(MembershipPlan.level)).all()
result = []
for p in plans:
result.append({
"id": p.id,
"name": p.name,
"level": p.level,
"duration_type": p.duration_type,
"price": float(p.price) if p.price else 0,
"daily_free_tokens": p.daily_free_tokens or 0,
"features": p.features or [],
"is_active": p.is_active,
})
return {"code": 0, "data": result, "message": "ok"}
@router.post("/orders")
def create_order(
req: CreateOrderRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
plan = db.query(MembershipPlan).filter(
and_(MembershipPlan.id == req.item_id, MembershipPlan.is_active == True)
).first()
if not plan:
raise HTTPException(status_code=404, detail="会员计划不存在或已下线")
amount = req.amount or float(plan.price or 0)
order = Order(
id=gen_uuid(),
user_id=current_user.id,
order_type=req.order_type if hasattr(req, 'order_type') and req.order_type else "membership",
item_id=req.item_id,
item_name=plan.name,
amount=amount,
status="pending",
coupon_id=req.coupon_id if hasattr(req, 'coupon_id') and req.coupon_id else None,
created_at=datetime.now(),
updated_at=datetime.now(),
)
db.add(order)
db.commit()
db.refresh(order)
LogService.log_operation(
user_id=current_user.id, username=current_user.username, action="create_order",
target="payment_order", target_id=order.id,
detail=f"创建订单 #{order.id},金额 {amount}",
)
return {
"code": 0,
"data": {
"id": order.id,
"item_id": order.item_id,
"plan_name": plan.name,
"amount": float(order.amount),
"status": order.status,
"created_at": str(order.created_at),
},
"message": "订单创建成功",
}
@router.get("/orders")
def list_orders(
status: Optional[str] = Query(None),
page: int = Query(default=1, ge=1),
page_size: int = Query(default=20, ge=1, le=100),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
query = db.query(Order).filter(Order.user_id == current_user.id)
if status:
query = query.filter(Order.status == status)
total = query.count()
orders = query.order_by(desc(Order.created_at)).offset((page - 1) * page_size).limit(page_size).all()
result = []
for o in orders:
plan = db.query(MembershipPlan).filter(MembershipPlan.id == o.item_id).first()
result.append({
"id": o.id,
"item_id": o.item_id,
"plan_name": plan.name if plan else "unknown",
"amount": float(o.amount),
"status": o.status,
"coupon_id": o.coupon_id,
"paid_at": str(o.paid_at) if o.paid_at else None,
"created_at": str(o.created_at),
})
return {
"code": 0,
"data": {"total": total, "page": page, "page_size": page_size, "items": result},
"message": "ok",
}
@router.get("/orders/{order_id}")
def get_order(
order_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
order = db.query(Order).filter(
and_(Order.id == order_id, Order.user_id == current_user.id)
).first()
if not order:
raise HTTPException(status_code=404, detail="订单不存在")
plan = db.query(MembershipPlan).filter(MembershipPlan.id == order.item_id).first()
return {
"code": 0,
"data": {
"id": order.id,
"item_id": order.item_id,
"plan_name": plan.name if plan else "unknown",
"amount": float(order.amount),
"status": order.status,
"coupon_id": order.coupon_id,
"paid_at": str(order.paid_at) if order.paid_at else None,
"created_at": str(order.created_at),
"updated_at": str(order.updated_at),
},
"message": "ok",
}
@router.post("/orders/{order_id}/pay")
def pay_order(
order_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
order = db.query(Order).filter(
and_(Order.id == order_id, Order.user_id == current_user.id)
).first()
if not order:
raise HTTPException(status_code=404, detail="订单不存在")
if order.status != "pending":
raise HTTPException(status_code=400, detail="订单状态不允许支付")
# 模拟支付成功
order.status = "paid"
order.paid_at = datetime.now()
order.updated_at = datetime.now()
# 激活会员
plan = db.query(MembershipPlan).filter(MembershipPlan.id == order.item_id).first()
if plan:
# 查找或创建会员记录
membership = db.query(MembershipSubscription).filter(
MembershipSubscription.user_id == current_user.id
).first()
if membership:
# 如果已有会员,延长有效期
if membership.end_at and membership.end_at > datetime.now():
# 从当前到期日开始延长
membership.end_at = membership.end_at + __import__("datetime").timedelta(days=30)
else:
# 已过期,从现在开始
membership.end_at = datetime.now() + __import__("datetime").timedelta(days=30)
membership.plan_id = plan.id
membership.status = "active"
membership.updated_at = datetime.now()
else:
membership = MembershipSubscription(
id=gen_uuid(),
user_id=current_user.id,
plan_id=plan.id,
status="active",
start_at=datetime.now(),
end_at=datetime.now() + __import__("datetime").timedelta(days=30),
created_at=datetime.now(),
updated_at=datetime.now(),
)
db.add(membership)
LogService.log_operation(
user_id=current_user.id, username=current_user.username, action="pay_order",
target="payment_order", target_id=order.id,
detail=f"支付订单 #{order.id},金额 {order.amount}",
)
db.commit()
return {
"code": 0,
"data": {
"order_id": order.id,
"status": order.status,
"paid_at": str(order.paid_at),
},
"message": "支付成功",
}
@router.get("/coupons")
def list_coupons(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
from ..models import UserCoupon
now = datetime.now()
# 先获取用户的user_coupons
user_coupons = db.query(UserCoupon).filter(
and_(
UserCoupon.user_id == current_user.id,
UserCoupon.status == "unused",
)
).all()
result = []
coupon_ids = [uc.coupon_id for uc in user_coupons]
if coupon_ids:
coupons = db.query(Coupon).filter(
and_(
Coupon.id.in_(coupon_ids),
Coupon.status == "active",
Coupon.valid_to > now,
)
).order_by(asc(Coupon.valid_to)).all()
for c in coupons:
result.append({
"id": c.id,
"code": c.code,
"type": c.type,
"value": float(c.value) if c.value else 0,
"min_amount": float(c.min_amount) if c.min_amount else 0,
"expire_at": str(c.valid_to) if c.valid_to else None,
"status": c.status,
})
return {"code": 0, "data": result, "message": "ok"}
@router.post("/coupons/use")
def use_coupon(
req: UseCouponRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
from ..models import UserCoupon
coupon = db.query(Coupon).filter(
and_(
Coupon.code == req.code,
Coupon.status == "active",
)
).first()
if not coupon:
raise HTTPException(status_code=404, detail="优惠券不存在或已失效")
# 检查用户是否持有该优惠券
user_coupon = db.query(UserCoupon).filter(
and_(
UserCoupon.coupon_id == coupon.id,
UserCoupon.user_id == current_user.id,
UserCoupon.status == "unused",
)
).first()
if not user_coupon:
raise HTTPException(status_code=404, detail="优惠券不存在或已使用")
if coupon.valid_to and coupon.valid_to < datetime.now():
user_coupon.status = "expired"
db.commit()
raise HTTPException(status_code=400, detail="优惠券已过期")
# 标记为已使用
user_coupon.status = "used"
user_coupon.used_at = datetime.now()
if req.order_id:
order = db.query(Order).filter(
and_(Order.id == req.order_id, Order.user_id == current_user.id)
).first()
if order and order.status == "pending":
# 应用优惠券折扣
discount = 0
if coupon.type == "fixed":
discount = float(coupon.value or 0)
elif coupon.type == "percent":
discount = float(order.amount) * float(coupon.value or 0) / 100
order.amount = max(0, float(order.amount) - discount)
order.coupon_id = coupon.id
order.discount_amount = discount
db.commit()
return {"code": 0, "data": {"code": coupon.code, "status": user_coupon.status}, "message": "优惠券使用成功"}
@router.get("/services")
def list_services(db: Session = Depends(get_db)):
services = db.query(ValueAddedService).filter(
ValueAddedService.status == "active"
).order_by(asc(ValueAddedService.created_at)).all()
result = []
for s in services:
result.append({
"id": s.id,
"name": s.name,
"description": s.description,
"type": s.type,
"price": float(s.price) if s.price else 0,
"features": s.features or [],
"assigned_agent_id": s.assigned_agent_id,
})
return {"code": 0, "data": result, "message": "ok"}
@router.post("/custom-services")
def create_custom_service_order(
req: CustomServiceOrderRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
service = db.query(ValueAddedService).filter(
and_(ValueAddedService.id == req.service_id, ValueAddedService.status == "active")
).first()
if not service:
raise HTTPException(status_code=404, detail="服务不存在或已下线")
order = CustomServiceOrder(
id=gen_uuid(),
user_id=current_user.id,
service_id=req.service_id,
service_type=service.type,
description=req.description,
price=req.budget or float(service.price or 0),
status="pending",
created_at=datetime.now(),
updated_at=datetime.now(),
)
db.add(order)
db.commit()
db.refresh(order)
LogService.log_operation(
user_id=current_user.id, username=current_user.username, action="create_custom_service",
target="custom_service_order", target_id=order.id,
detail=f"创建定制服务订单 #{order.id},服务: {service.name}",
)
return {
"code": 0,
"data": {
"id": order.id,
"service_id": order.service_id,
"service_name": service.name,
"price": float(order.price) if order.price else 0,
"status": order.status,
"created_at": str(order.created_at),
},
"message": "定制服务订单已提交",
}
@router.post("/service-requests")
def create_service_request(
req: ServiceRequestModel,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""创建增值服务工单(平台代理/数据定制/私有化项目)"""
service = db.query(ValueAddedService).filter(
and_(ValueAddedService.id == req.service_id, ValueAddedService.status == "active")
).first()
if not service:
raise HTTPException(status_code=404, detail="服务不存在或已下线")
# 验证服务类型是否为工单性质
if service.type not in ("platform_proxy", "data_custom", "private_project"):
raise HTTPException(status_code=400, detail="此服务不支持工单提交")
request_obj = CustomServiceRequest(
id=gen_uuid(),
user_id=current_user.id,
service_id=req.service_id,
request_type=service.type,
title=req.title,
description=req.description,
requirements=req.requirements,
status="pending",
created_at=datetime.now(),
updated_at=datetime.now(),
)
db.add(request_obj)
db.commit()
db.refresh(request_obj)
LogService.log_operation(
user_id=current_user.id, username=current_user.username,
action="create_service_request",
target="custom_service_request", target_id=request_obj.id,
detail=f"创建服务工单 #{request_obj.id},类型: {service.type}, 标题: {req.title}",
)
return {
"code": 0,
"data": {
"id": request_obj.id,
"service_id": request_obj.service_id,
"service_name": service.name,
"request_type": service.type,
"title": request_obj.title,
"status": request_obj.status,
"created_at": str(request_obj.created_at),
},
"message": "服务工单已提交",
}
# ============================================================
# 电子凭证开具 API
# ============================================================
class ReceiptRequest(BaseModel):
receipt_type: str = Field(default="electronic") # electronic / invoice
title: Optional[str] = None # 发票抬头(公司名称或个人姓名)
tax_number: Optional[str] = None # 税号
@router.post("/orders/{order_id}/receipt")
def generate_receipt(
order_id: str,
req: ReceiptRequest = Depends(),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""为已支付订单开具电子凭证"""
order = db.query(Order).filter(
and_(Order.id == order_id, Order.user_id == current_user.id)
).first()
if not order:
raise HTTPException(status_code=404, detail="订单不存在")
if order.status != "paid":
raise HTTPException(status_code=400, detail="仅已支付订单可开具电子凭证")
# 检查是否已开过凭证
if order.receipt_number:
return {
"code": 0,
"data": {
"receipt_number": order.receipt_number,
"receipt_type": order.receipt_type or "electronic",
"title": order.receipt_title or current_user.username,
"amount": float(order.amount),
"issued_at": order.receipt_issued_at.strftime("%Y-%m-%d %H:%M:%S") if order.receipt_issued_at else "",
"status": "issued",
"message": "该订单已开具电子凭证",
},
"message": "电子凭证已存在",
}
# 生成凭证编号:RCT + 年月日 + 随机6位
now = datetime.now()
receipt_number = f"RCT{now.strftime('%Y%m%d')}{uuid.uuid4().hex[:6].upper()}"
# 保存凭证信息到订单
order.receipt_number = receipt_number
order.receipt_type = req.receipt_type
order.receipt_title = req.title or current_user.username
order.receipt_tax_number = req.tax_number
order.receipt_issued_at = now
order.updated_at = now
# 记录日志
LogService.log_operation(
user_id=current_user.id, username=current_user.username, action="generate_receipt",
target="payment_order", target_id=order.id,
detail=f"为订单 #{order.id} 开具电子凭证 {receipt_number}",
)
db.commit()
# 构建凭证详情
receipt_data = {
"receipt_number": receipt_number,
"receipt_type": req.receipt_type,
"title": req.title or current_user.username,
"tax_number": req.tax_number,
"order_id": order.id,
"amount": float(order.amount),
"order_type": order.order_type or "membership",
"item_name": order.item_name or "会员订阅",
"payment_method": order.payment_method or "微信支付",
"payment_id": order.payment_id or f"PAY{now.strftime('%Y%m%d%H%M%S')}",
"paid_at": order.paid_at.strftime("%Y-%m-%d %H:%M:%S") if hasattr(order, 'paid_at') and order.paid_at else now.strftime("%Y-%m-%d %H:%M:%S"),
"issued_at": now.strftime("%Y-%m-%d %H:%M:%S"),
"customer_name": current_user.username,
"customer_email": current_user.email or "",
"customer_phone": current_user.phone or "",
"status": "issued",
"platform_name": "AIFlow智能自动化PaaS平台",
"remarks": "此电子凭证由AIFlow平台自动生成,与纸质凭证具有同等效力。如需帮助,请联系客服。",
}
return {
"code": 0,
"data": receipt_data,
"message": "电子凭证开具成功",
}
@router.get("/orders/{order_id}/receipt")
def get_receipt(
order_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""获取订单已有的电子凭证"""
order = db.query(Order).filter(
and_(Order.id == order_id, Order.user_id == current_user.id)
).first()
if not order:
raise HTTPException(status_code=404, detail="订单不存在")
if not order.receipt_number:
return {
"code": 0,
"data": None,
"message": "该订单尚未开具电子凭证",
}
return {
"code": 0,
"data": {
"receipt_number": order.receipt_number,
"receipt_type": order.receipt_type or "electronic",
"title": order.receipt_title or current_user.username,
"tax_number": order.receipt_tax_number,
"order_id": order.id,
"amount": float(order.amount),
"order_type": order.order_type or "membership",
"item_name": order.item_name or "会员订阅",
"payment_method": order.payment_method or "微信支付",
"issued_at": order.receipt_issued_at.strftime("%Y-%m-%d %H:%M:%S") if order.receipt_issued_at else "",
"status": "issued" if order.receipt_number else "not_issued",
"platform_name": "AIFlow智能自动化PaaS平台",
},
"message": "ok",
}