""" 企业后台管理 API(需 role=admin,与主站共用 JWT:/api/auth/login) """ import asyncpg from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from admin.schemas import ( AdminUserCreate, AdminUserListItem, AdminUserListResponse, AdminUserUpdate, DepartmentCreate, DepartmentResponse, DepartmentUpdate, EnterpriseResponse, EnterpriseUpdate, ) from core.dependencies import get_db, get_current_admin_user from models.user import User from services.admin_user_service import AdminUserService from services.department_service import DepartmentService from services.enterprise_service import EnterpriseService from utils.helpers import BaseResponse admin_router = APIRouter(prefix="/api/admin", tags=["后台管理"]) @admin_router.get("/enterprise", response_model=BaseResponse, summary="当前企业信息") async def get_enterprise( admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): if admin.enterprise_id is None: raise HTTPException(status_code=400, detail="用户未关联企业") row = await EnterpriseService.get_by_id(conn, admin.enterprise_id) if not row: raise HTTPException(status_code=404, detail="企业不存在") return BaseResponse( code=200, msg="ok", data=EnterpriseResponse(**row).model_dump(), ) @admin_router.put("/enterprise", response_model=BaseResponse, summary="更新企业信息") async def update_enterprise( body: EnterpriseUpdate, admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): if admin.enterprise_id is None: raise HTTPException(status_code=400, detail="用户未关联企业") row = await EnterpriseService.update_profile( conn, admin.enterprise_id, name=body.name, ai_display_name=body.ai_display_name, ) if not row: raise HTTPException(status_code=404, detail="企业不存在") return BaseResponse(code=200, msg="更新成功", data=EnterpriseResponse(**row).model_dump()) @admin_router.get("/departments", response_model=BaseResponse, summary="部门列表") async def list_departments( admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): rows = await DepartmentService.list_by_enterprise(conn, admin.enterprise_id) items = [ DepartmentResponse( id=r["id"], enterprise_id=r["enterprise_id"], name=r["name"], parent_id=r["parent_id"], created_at=r["created_at"], ).model_dump() for r in rows ] return BaseResponse(code=200, msg="ok", data={"items": items}) @admin_router.post("/departments", response_model=BaseResponse, summary="创建部门") async def create_department( body: DepartmentCreate, admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): if body.parent_id is not None: parent = await DepartmentService.get_by_id(conn, body.parent_id, admin.enterprise_id) if not parent: raise HTTPException(status_code=400, detail="上级部门不存在") try: row = await DepartmentService.create( conn, admin.enterprise_id, body.name, body.parent_id ) return BaseResponse( code=200, msg="创建成功", data=DepartmentResponse( id=row["id"], enterprise_id=row["enterprise_id"], name=row["name"], parent_id=row["parent_id"], created_at=row["created_at"], ).model_dump(), ) except asyncpg.UniqueViolationError: raise HTTPException(status_code=400, detail="同企业下部门名称已存在") @admin_router.put("/departments/{dept_id}", response_model=BaseResponse, summary="更新部门") async def update_department( dept_id: int, body: DepartmentUpdate, admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): if body.parent_id is not None: parent = await DepartmentService.get_by_id(conn, body.parent_id, admin.enterprise_id) if not parent: raise HTTPException(status_code=400, detail="上级部门不存在") row = await DepartmentService.update( conn, dept_id, admin.enterprise_id, name=body.name, parent_id=body.parent_id ) if not row: raise HTTPException(status_code=404, detail="部门不存在") return BaseResponse( code=200, msg="更新成功", data=DepartmentResponse( id=row["id"], enterprise_id=row["enterprise_id"], name=row["name"], parent_id=row["parent_id"], created_at=row["created_at"], ).model_dump(), ) @admin_router.delete("/departments/{dept_id}", response_model=BaseResponse, summary="删除部门") async def delete_department( dept_id: int, admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): err = await DepartmentService.delete(conn, dept_id, admin.enterprise_id) if err: raise HTTPException(status_code=400, detail=err) return BaseResponse(code=200, msg="删除成功", data=None) @admin_router.get("/users", response_model=BaseResponse, summary="用户列表") async def list_users( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), username: Optional[str] = Query(None, description="用户名(模糊)"), email: Optional[str] = Query(None, description="邮箱(模糊)"), phone: Optional[str] = Query(None, description="手机号(模糊)"), display_name: Optional[str] = Query(None, description="显示名(模糊)"), department_id: Optional[int] = Query(None, description="按部门 ID 精确筛选"), admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): if department_id is not None: d = await DepartmentService.get_by_id(conn, department_id, admin.enterprise_id) if not d: raise HTTPException(status_code=400, detail="部门不存在") rows, total = await AdminUserService.list_users( conn, admin.enterprise_id, page, page_size, username=username, email=email, phone=phone, display_name=display_name, department_id=department_id, ) items = [AdminUserListItem(**r).model_dump() for r in rows] return BaseResponse( code=200, msg="ok", data=AdminUserListResponse(total=total, items=items).model_dump(), ) @admin_router.post("/users", response_model=BaseResponse, summary="创建用户") async def create_user( body: AdminUserCreate, admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): if body.department_id is not None: d = await DepartmentService.get_by_id(conn, body.department_id, admin.enterprise_id) if not d: raise HTTPException(status_code=400, detail="部门不存在") try: row = await AdminUserService.create_user(conn, admin.enterprise_id, body) return BaseResponse( code=200, msg="创建成功", data=AdminUserListItem(**row).model_dump(), ) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @admin_router.get("/users/{user_id}", response_model=BaseResponse, summary="用户详情") async def get_user( user_id: int, admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): row = await AdminUserService.get_user(conn, admin.enterprise_id, user_id) if not row: raise HTTPException(status_code=404, detail="用户不存在") return BaseResponse( code=200, msg="ok", data=AdminUserListItem(**row).model_dump(), ) @admin_router.put("/users/{user_id}", response_model=BaseResponse, summary="更新用户") async def update_user( user_id: int, body: AdminUserUpdate, admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): unset = body.model_dump(exclude_unset=True) if "department_id" in unset and unset["department_id"] is not None: d = await DepartmentService.get_by_id(conn, unset["department_id"], admin.enterprise_id) if not d: raise HTTPException(status_code=400, detail="部门不存在") try: row = await AdminUserService.update_user(conn, admin, user_id, body) if not row: raise HTTPException(status_code=404, detail="用户不存在") return BaseResponse( code=200, msg="更新成功", data=AdminUserListItem(**row).model_dump(), ) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @admin_router.delete("/users/{user_id}", response_model=BaseResponse, summary="删除用户") async def delete_user( user_id: int, admin: User = Depends(get_current_admin_user), conn: asyncpg.Connection = Depends(get_db), ): try: ok = await AdminUserService.delete_user(conn, admin, user_id) if not ok: raise HTTPException(status_code=404, detail="用户不存在") return BaseResponse(code=200, msg="删除成功", data=None) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except asyncpg.ForeignKeyViolationError: raise HTTPException( status_code=400, detail="该用户仍存在关联数据(如会话、知识库归属等),无法直接删除,请先禁用账号", )