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", }