初始提交: Qihu-AIFlow v2.0.1 完整项目
- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
This commit is contained in:
commit
78ff261b5a
199 files changed
+69126
No files matched your search
@@ -0,0 +1,608 @@
|
||||
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",
|
||||
}
|
||||
Reference in new issue
Block a user