"""租户管理路由""" from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel from typing import Optional, List from datetime import date from sqlalchemy.orm import Session from sqlalchemy import func from ..database import get_db from ..models.tenant import Tenant, Subscription from ..models.stats import TenantUsageDaily from .auth import get_current_user, require_operator from ..models.user import User router = APIRouter(prefix="/tenants", tags=["租户管理"]) # Schemas class TenantCreate(BaseModel): code: str name: str contact_info: Optional[dict] = None status: str = "active" expired_at: Optional[date] = None class TenantUpdate(BaseModel): name: Optional[str] = None contact_info: Optional[dict] = None status: Optional[str] = None expired_at: Optional[date] = None class SubscriptionCreate(BaseModel): tenant_id: int app_code: str start_date: Optional[date] = None end_date: Optional[date] = None quota: Optional[dict] = None status: str = "active" class SubscriptionUpdate(BaseModel): start_date: Optional[date] = None end_date: Optional[date] = None quota: Optional[dict] = None status: Optional[str] = None # API Endpoints @router.get("") async def list_tenants( page: int = Query(1, ge=1), size: int = Query(20, ge=1, le=100), status: Optional[str] = None, keyword: Optional[str] = None, user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """获取租户列表""" query = db.query(Tenant) if status: query = query.filter(Tenant.status == status) if keyword: query = query.filter( (Tenant.code.contains(keyword)) | (Tenant.name.contains(keyword)) ) total = query.count() tenants = query.order_by(Tenant.id.desc()).offset((page - 1) * size).limit(size).all() return { "total": total, "page": page, "size": size, "items": [ { "id": t.id, "code": t.code, "name": t.name, "contact_info": t.contact_info, "status": t.status, "expired_at": t.expired_at, "created_at": t.created_at } for t in tenants ] } @router.get("/{tenant_id}") async def get_tenant( tenant_id: int, user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """获取租户详情""" tenant = db.query(Tenant).filter(Tenant.id == tenant_id).first() if not tenant: raise HTTPException(status_code=404, detail="租户不存在") # 获取订阅 subscriptions = db.query(Subscription).filter( Subscription.tenant_id == tenant_id ).all() # 获取用量统计(最近30天) usage = db.query( func.sum(TenantUsageDaily.ai_calls).label('total_calls'), func.sum(TenantUsageDaily.ai_tokens).label('total_tokens'), func.sum(TenantUsageDaily.ai_cost).label('total_cost') ).filter( TenantUsageDaily.tenant_id == tenant_id ).first() return { "id": tenant.id, "code": tenant.code, "name": tenant.name, "contact_info": tenant.contact_info, "status": tenant.status, "expired_at": tenant.expired_at, "created_at": tenant.created_at, "updated_at": tenant.updated_at, "subscriptions": [ { "id": s.id, "app_code": s.app_code, "start_date": s.start_date, "end_date": s.end_date, "quota": s.quota, "status": s.status } for s in subscriptions ], "usage_summary": { "total_calls": int(usage.total_calls or 0), "total_tokens": int(usage.total_tokens or 0), "total_cost": float(usage.total_cost or 0) } } @router.post("") async def create_tenant( data: TenantCreate, user: User = Depends(require_operator), db: Session = Depends(get_db) ): """创建租户""" # 检查 code 是否重复 exists = db.query(Tenant).filter(Tenant.code == data.code).first() if exists: raise HTTPException(status_code=400, detail="租户代码已存在") tenant = Tenant( code=data.code, name=data.name, contact_info=data.contact_info, status=data.status, expired_at=data.expired_at ) db.add(tenant) db.commit() db.refresh(tenant) return {"success": True, "id": tenant.id} @router.put("/{tenant_id}") async def update_tenant( tenant_id: int, data: TenantUpdate, user: User = Depends(require_operator), db: Session = Depends(get_db) ): """更新租户""" tenant = db.query(Tenant).filter(Tenant.id == tenant_id).first() if not tenant: raise HTTPException(status_code=404, detail="租户不存在") update_data = data.model_dump(exclude_unset=True) for key, value in update_data.items(): setattr(tenant, key, value) db.commit() return {"success": True} @router.delete("/{tenant_id}") async def delete_tenant( tenant_id: int, user: User = Depends(require_operator), db: Session = Depends(get_db) ): """删除租户""" tenant = db.query(Tenant).filter(Tenant.id == tenant_id).first() if not tenant: raise HTTPException(status_code=404, detail="租户不存在") # 删除关联的订阅 db.query(Subscription).filter(Subscription.tenant_id == tenant_id).delete() db.delete(tenant) db.commit() return {"success": True} # 订阅管理 @router.get("/{tenant_id}/subscriptions") async def list_subscriptions( tenant_id: int, user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """获取租户订阅列表""" subscriptions = db.query(Subscription).filter( Subscription.tenant_id == tenant_id ).all() return [ { "id": s.id, "tenant_id": s.tenant_id, "app_code": s.app_code, "start_date": s.start_date, "end_date": s.end_date, "quota": s.quota, "status": s.status, "created_at": s.created_at } for s in subscriptions ] @router.post("/{tenant_id}/subscriptions") async def create_subscription( tenant_id: int, data: SubscriptionCreate, user: User = Depends(require_operator), db: Session = Depends(get_db) ): """创建订阅""" tenant = db.query(Tenant).filter(Tenant.id == tenant_id).first() if not tenant: raise HTTPException(status_code=404, detail="租户不存在") subscription = Subscription( tenant_id=tenant_id, app_code=data.app_code, start_date=data.start_date, end_date=data.end_date, quota=data.quota, status=data.status ) db.add(subscription) db.commit() db.refresh(subscription) return {"success": True, "id": subscription.id} @router.put("/subscriptions/{subscription_id}") async def update_subscription( subscription_id: int, data: SubscriptionUpdate, user: User = Depends(require_operator), db: Session = Depends(get_db) ): """更新订阅""" subscription = db.query(Subscription).filter(Subscription.id == subscription_id).first() if not subscription: raise HTTPException(status_code=404, detail="订阅不存在") update_data = data.model_dump(exclude_unset=True) for key, value in update_data.items(): setattr(subscription, key, value) db.commit() return {"success": True} @router.delete("/subscriptions/{subscription_id}") async def delete_subscription( subscription_id: int, user: User = Depends(require_operator), db: Session = Depends(get_db) ): """删除订阅""" subscription = db.query(Subscription).filter(Subscription.id == subscription_id).first() if not subscription: raise HTTPException(status_code=404, detail="订阅不存在") db.delete(subscription) db.commit() return {"success": True}