import uuid from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from database import get_db from models import Department, User, UserRole, Role from schemas import ( DepartmentCreate, DepartmentUpdate, DepartmentOut, UserCreate, UserUpdate, UserOut, ) from modules.auth.router import hash_password, get_user_roles router = APIRouter(prefix="/api/org", tags=["org"]) @router.get("/departments", response_model=list[DepartmentOut]) async def get_departments(request: Request, db: AsyncSession = Depends(get_db)): result = await db.execute( select(Department).where(Department.parent_id.is_(None)).order_by(Department.sort_order) ) roots = result.scalars().all() return [await _build_department_tree(db, d) for d in roots] async def _build_department_tree(db: AsyncSession, dept: Department) -> DepartmentOut: children_result = await db.execute( select(Department).where(Department.parent_id == dept.id).order_by(Department.sort_order) ) children = children_result.scalars().all() return DepartmentOut( id=dept.id, name=dept.name, parent_id=dept.parent_id, path=dept.path, level=dept.level, sort_order=dept.sort_order, children=[await _build_department_tree(db, c) for c in children], ) @router.post("/departments", response_model=DepartmentOut) async def create_department( req: DepartmentCreate, request: Request, db: AsyncSession = Depends(get_db) ): parent_path = "/" level = 0 if req.parent_id: parent_result = await db.execute(select(Department).where(Department.id == req.parent_id)) parent = parent_result.scalar_one_or_none() if not parent: raise HTTPException(404, "父部门不存在") parent_path = parent.path level = parent.level + 1 dept = Department( name=req.name, parent_id=req.parent_id, path=f"{parent_path}/{req.name}".replace("//", "/"), level=level, sort_order=req.sort_order, ) db.add(dept) await db.flush() return DepartmentOut( id=dept.id, name=dept.name, parent_id=dept.parent_id, path=dept.path, level=dept.level, sort_order=dept.sort_order, children=[], ) @router.put("/departments/{dept_id}", response_model=DepartmentOut) async def update_department( dept_id: uuid.UUID, req: DepartmentUpdate, request: Request, db: AsyncSession = Depends(get_db), ): result = await db.execute(select(Department).where(Department.id == dept_id)) dept = result.scalar_one_or_none() if not dept: raise HTTPException(404, "部门不存在") if req.name is not None: dept.name = req.name if req.parent_id is not None: dept.parent_id = req.parent_id if req.sort_order is not None: dept.sort_order = req.sort_order return DepartmentOut( id=dept.id, name=dept.name, parent_id=dept.parent_id, path=dept.path, level=dept.level, sort_order=dept.sort_order, children=[], ) @router.delete("/departments/{dept_id}") async def delete_department(dept_id: uuid.UUID, request: Request, db: AsyncSession = Depends(get_db)): result = await db.execute(select(Department).where(Department.id == dept_id)) dept = result.scalar_one_or_none() if not dept: raise HTTPException(404, "部门不存在") await db.delete(dept) return {"code": 200, "message": "删除成功"} @router.get("/users", response_model=list[UserOut]) async def get_users(request: Request, db: AsyncSession = Depends(get_db)): user_ctx = request.state.user result = await db.execute(select(User)) users = result.scalars().all() if user_ctx["data_scope"] == "self_only": users = [u for u in users if str(u.id) == user_ctx["id"]] elif user_ctx["data_scope"] == "subordinate_only": sub_ids = await _get_subordinate_ids(db, uuid.UUID(user_ctx["id"])) sub_ids.add(uuid.UUID(user_ctx["id"])) users = [u for u in users if u.id in sub_ids] return [await _user_to_out(db, u) for u in users] @router.get("/users/{user_id}", response_model=UserOut) async def get_user(user_id: uuid.UUID, request: Request, db: AsyncSession = Depends(get_db)): result = await db.execute(select(User).where(User.id == user_id)) user = result.scalar_one_or_none() if not user: raise HTTPException(404, "用户不存在") return await _user_to_out(db, user) @router.post("/users", response_model=UserOut) async def create_user(req: UserCreate, request: Request, db: AsyncSession = Depends(get_db)): existing = await db.execute(select(User).where(User.username == req.username)) if existing.scalar_one_or_none(): raise HTTPException(400, "用户名已存在") user = User( username=req.username, password_hash=hash_password(req.password), display_name=req.display_name, email=req.email, phone=req.phone, wecom_user_id=req.wecom_user_id, department_id=req.department_id, position=req.position, manager_id=req.manager_id, ) db.add(user) await db.flush() if req.role_ids: for role_id in req.role_ids: db.add(UserRole(user_id=user.id, role_id=role_id)) await db.flush() return await _user_to_out(db, user) @router.put("/users/{user_id}", response_model=UserOut) async def update_user( user_id: uuid.UUID, req: UserUpdate, request: Request, db: AsyncSession = Depends(get_db), ): result = await db.execute(select(User).where(User.id == user_id)) user = result.scalar_one_or_none() if not user: raise HTTPException(404, "用户不存在") if req.display_name is not None: user.display_name = req.display_name if req.email is not None: user.email = req.email if req.phone is not None: user.phone = req.phone if req.department_id is not None: user.department_id = req.department_id if req.position is not None: user.position = req.position if req.manager_id is not None: user.manager_id = req.manager_id if req.status is not None: user.status = req.status if req.role_ids is not None: await db.execute(select(UserRole).where(UserRole.user_id == user.id)) existing_urs = (await db.execute( select(UserRole).where(UserRole.user_id == user.id) )).scalars().all() for ur in existing_urs: await db.delete(ur) for role_id in req.role_ids: db.add(UserRole(user_id=user.id, role_id=role_id)) return await _user_to_out(db, user) @router.get("/subordinates", response_model=list[UserOut]) async def get_subordinates(request: Request, db: AsyncSession = Depends(get_db)): user_ctx = request.state.user manager_id = uuid.UUID(user_ctx["id"]) sub_ids = await _get_subordinate_ids(db, manager_id) result = await db.execute(select(User).where(User.id.in_(sub_ids))) users = result.scalars().all() return [await _user_to_out(db, u) for u in users] async def _get_subordinate_ids(db: AsyncSession, manager_id: uuid.UUID) -> set[uuid.UUID]: result = await db.execute(select(User).where(User.manager_id == manager_id)) direct = result.scalars().all() ids = {u.id for u in direct} for sub in direct: ids.update(await _get_subordinate_ids(db, sub.id)) return ids async def _user_to_out(db: AsyncSession, user: User) -> UserOut: roles = await get_user_roles(db, user.id) return UserOut( id=user.id, username=user.username, display_name=user.display_name, email=user.email, phone=user.phone, wecom_user_id=user.wecom_user_id, department_id=user.department_id, position=user.position, manager_id=user.manager_id, status=user.status, roles=roles, created_at=user.created_at, )