""" 应用市场审核 API(管理员) """ from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from ...database import get_db, SessionLocal from ...models import MarketApp, Coupon, UserCoupon, User from ...auth import get_current_admin from ...middleware.log_service import LogService import uuid from datetime import datetime router = APIRouter() # ============================================================ # 应用审核管理 # ============================================================ @router.get("/apps/pending") async def get_pending_apps( admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """获取待审核应用列表""" apps = db.query(MarketApp).filter( MarketApp.status == "pending" ).order_by(MarketApp.created_at.desc()).limit(1000).all() return [ { "id": a.id, "name": a.name, "type": a.type, "source": a.source, "publisher_id": a.publisher_id, "publisher_name": a.publisher_name, "description": a.description, "icon": a.icon, "tutorial": a.tutorial, "price": float(a.price) if a.price else 0, "is_member_free": a.is_member_free, "join_promotion": a.join_promotion, "status": a.status, "rating_avg": float(a.rating_avg) if a.rating_avg else 0, "review_count": a.review_count, "install_count": a.install_count, "skill_content": a.skill_content, "mcp_transport": a.mcp_transport, "agent_endpoint": a.agent_endpoint, "created_at": a.created_at.isoformat() if a.created_at else None, "updated_at": a.updated_at.isoformat() if a.updated_at else None, } for a in apps ] @router.put("/apps/{id}/approve") async def approve_app( id: str, admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """审核通过应用""" app = db.query(MarketApp).filter(MarketApp.id == id).first() if not app: raise HTTPException(status_code=404, detail="应用不存在") app.status = "published" app.updated_at = datetime.now() db.commit() LogService.log_audit( admin_id=admin_user.id, admin_name=admin_user.username, action="approve_app", target_type="market_app", target_id=id, detail=f"审核通过应用 {app.name}", ) return {"message": "应用审核通过"} @router.put("/apps/{id}/reject") async def reject_app( id: str, admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """审核拒绝应用""" app = db.query(MarketApp).filter(MarketApp.id == id).first() if not app: raise HTTPException(status_code=404, detail="应用不存在") app.status = "removed" app.updated_at = datetime.now() db.commit() LogService.log_audit( admin_id=admin_user.id, admin_name=admin_user.username, action="reject_app", target_type="market_app", target_id=id, detail=f"审核拒绝应用 {app.name}", ) return {"message": "应用审核已拒绝"} @router.get("/apps") async def get_all_apps( status: str = Query(None, description="状态筛选: pending/published/removed"), app_type: str = Query(None, description="类型筛选: skill/agent/mcp"), 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(MarketApp) if status: query = query.filter(MarketApp.status == status) if app_type: query = query.filter(MarketApp.type == app_type) total = query.count() apps = query.order_by(MarketApp.created_at.desc()).offset( (page - 1) * page_size ).limit(page_size).all() return { "total": total, "page": page, "page_size": page_size, "items": [ { "id": a.id, "name": a.name, "type": a.type, "source": a.source, "publisher_id": a.publisher_id, "publisher_name": a.publisher_name, "description": a.description, "icon": a.icon, "tutorial": a.tutorial, "price": float(a.price) if a.price else 0, "is_member_free": a.is_member_free, "join_promotion": a.join_promotion, "status": a.status, "rating_avg": float(a.rating_avg) if a.rating_avg else 0, "review_count": a.review_count, "install_count": a.install_count, "created_at": a.created_at.isoformat() if a.created_at else None, "updated_at": a.updated_at.isoformat() if a.updated_at else None, } for a in apps ], } @router.put("/apps/{id}") async def admin_edit_app( id: str, name: str = None, description: str = None, icon: str = None, price: float = None, is_member_free: bool = None, join_promotion: bool = None, tutorial: str = None, admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """管理员编辑应用""" app = db.query(MarketApp).filter(MarketApp.id == id).first() if not app: raise HTTPException(status_code=404, detail="应用不存在") if name is not None: app.name = name if description is not None: app.description = description if icon is not None: app.icon = icon if price is not None: app.price = price if is_member_free is not None: app.is_member_free = is_member_free if join_promotion is not None: app.join_promotion = join_promotion if tutorial is not None: app.tutorial = tutorial app.updated_at = datetime.now() db.commit() LogService.log_audit( admin_id=admin_user.id, admin_name=admin_user.username, action="edit_market_app", target_type="market_app", target_id=id, detail=f"管理员编辑应用 {app.name}", ) return {"message": "应用已更新"} # ============================================================ # 优惠券管理 # ============================================================ @router.get("/coupons") async def get_coupons( status: str = Query(None, description="状态筛选: active/expired/disabled"), admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """获取优惠券列表""" query = db.query(Coupon) if status: query = query.filter(Coupon.status == status) coupons = query.order_by(Coupon.created_at.desc()).limit(1000).all() return [ { "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, "valid_from": c.valid_from.isoformat() if c.valid_from else None, "valid_to": c.valid_to.isoformat() if c.valid_to else None, "total_count": c.total_count, "used_count": c.used_count, "status": c.status, "created_at": c.created_at.isoformat() if c.created_at else None, } for c in coupons ] @router.post("/coupons") async def create_coupon( code: str, coupon_type: str, value: float, valid_from: str, valid_to: str, min_amount: float = 0, total_count: int = None, status: str = "active", admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """创建优惠券""" # 检查code是否已存在 existing = db.query(Coupon).filter(Coupon.code == code).first() if existing: raise HTTPException(status_code=400, detail="优惠券码已存在") coupon = Coupon( id=str(uuid.uuid4()), code=code, type=coupon_type, value=value, min_amount=min_amount, valid_from=datetime.fromisoformat(valid_from), valid_to=datetime.fromisoformat(valid_to), total_count=total_count, status=status, ) db.add(coupon) db.commit() LogService.log_audit( admin_id=admin_user.id, admin_name=admin_user.username, action="create_coupon", target_type="coupon", target_id=coupon.id, detail=f"创建优惠券 {code}", ) return {"message": "优惠券已创建", "id": coupon.id} @router.put("/coupons/{id}") async def update_coupon( id: str, code: str = None, value: float = None, min_amount: float = None, valid_from: str = None, valid_to: str = None, total_count: int = None, status: str = None, admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """更新优惠券""" coupon = db.query(Coupon).filter(Coupon.id == id).first() if not coupon: raise HTTPException(status_code=404, detail="优惠券不存在") if code is not None: # 检查新code是否重复 if code != coupon.code: dup = db.query(Coupon).filter(Coupon.code == code).first() if dup: raise HTTPException(status_code=400, detail="优惠券码已存在") coupon.code = code if value is not None: coupon.value = value if min_amount is not None: coupon.min_amount = min_amount if valid_from is not None: coupon.valid_from = datetime.fromisoformat(valid_from) if valid_to is not None: coupon.valid_to = datetime.fromisoformat(valid_to) if total_count is not None: coupon.total_count = total_count if status is not None: coupon.status = status db.commit() LogService.log_audit( admin_id=admin_user.id, admin_name=admin_user.username, action="update_coupon", target_type="coupon", target_id=id, detail=f"更新优惠券 {coupon.code}", ) return {"message": "优惠券已更新"} @router.delete("/coupons/{id}") async def delete_coupon( id: str, admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """删除优惠券""" coupon = db.query(Coupon).filter(Coupon.id == id).first() if not coupon: raise HTTPException(status_code=404, detail="优惠券不存在") db.delete(coupon) db.commit() LogService.log_audit( admin_id=admin_user.id, admin_name=admin_user.username, action="delete_coupon", target_type="coupon", target_id=id, detail=f"删除优惠券 {coupon.code}", ) return {"message": "优惠券已删除"} @router.post("/coupons/{id}/assign") async def assign_coupon( id: str, user_id: str, admin_user=Depends(get_current_admin), db: Session = Depends(get_db), ): """发放优惠券给用户""" coupon = db.query(Coupon).filter(Coupon.id == id).first() if not coupon: raise HTTPException(status_code=404, detail="优惠券不存在") if coupon.status != "active": raise HTTPException(status_code=400, detail="优惠券非活跃状态") user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException(status_code=404, detail="用户不存在") # 检查是否已发放 existing = db.query(UserCoupon).filter( UserCoupon.user_id == user_id, UserCoupon.coupon_id == id, ).first() if existing: raise HTTPException(status_code=400, detail="该用户已拥有此优惠券") user_coupon = UserCoupon( id=str(uuid.uuid4()), user_id=user_id, coupon_id=id, ) db.add(user_coupon) db.commit() LogService.log_audit( admin_id=admin_user.id, admin_name=admin_user.username, action="assign_coupon", target_type="coupon", target_id=id, detail=f"发放优惠券 {coupon.code} 给用户 {user.username}", ) return {"message": "优惠券已发放"}