You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

215 lines
7.8 KiB

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,
)