from typing import Optional from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import func, or_, select from sqlalchemy.ext.asyncio import AsyncSession from backend.app.core.dependencies import get_current_user, require_any_permissions, require_permissions from backend.app.core.security import hash_password from backend.app.db.session import get_db from backend.app.models import RolePermission, User from backend.app.schemas.pagination import PaginatedResponse from backend.app.schemas.user import CurrentUserResponse, UserCreate, UserResponse, UserUpdate router = APIRouter(prefix="/users", tags=["users"]) def is_limited_scope(user: User) -> bool: return user.role and user.role.name not in {"管理员", "排班员"} @router.get("/me", response_model=CurrentUserResponse) async def get_current_user_profile( current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): result = await db.execute( select(RolePermission.permission_code).where(RolePermission.role_id == current_user.role_id) ) permissions = [row[0] for row in result.fetchall()] return { "id": current_user.id, "name": current_user.name, "account": current_user.account, "phone": current_user.phone, "title": current_user.title, "avatar": current_user.avatar, "role_id": current_user.role_id, "campus_id": current_user.campus_id, "dept_id": current_user.dept_id, "status": current_user.status, "permissions": permissions, } @router.get( "", response_model=list[UserResponse] | PaginatedResponse[UserResponse], ) async def list_users( db: AsyncSession = Depends(get_db), current_user: User = Depends(require_any_permissions(["users.view", "schedule.view", "duty.view"])), keyword: Optional[str] = Query(default=None), campus_id: Optional[UUID] = Query(default=None), dept_id: Optional[UUID] = Query(default=None), role_id: Optional[UUID] = Query(default=None), status: Optional[str] = Query(default=None), page: Optional[int] = Query(default=None, ge=1), page_size: Optional[int] = Query(default=None, ge=1, le=200, alias="pageSize"), size: Optional[int] = Query(default=None, ge=1, le=200), ): query = select(User) if is_limited_scope(current_user): if current_user.dept_id: dept_id = current_user.dept_id elif current_user.campus_id: campus_id = current_user.campus_id else: if page is None and page_size is None and size is None: return [] return {"items": [], "total": 0, "page": page or 1, "pageSize": page_size or size or 10} if keyword: like = f"%{keyword.strip()}%" query = query.where( or_( User.name.ilike(like), User.account.ilike(like), User.phone.ilike(like), User.title.ilike(like), ) ) if campus_id: query = query.where(User.campus_id == campus_id) if dept_id: query = query.where(User.dept_id == dept_id) if role_id: query = query.where(User.role_id == role_id) if status: query = query.where(User.status == status) if page is None and page_size is None and size is None: result = await db.execute(query.order_by(User.created_at.desc())) return result.scalars().all() page_value = page or 1 size_value = page_size or size or 10 total = await db.scalar(select(func.count()).select_from(query.subquery())) result = await db.execute( query.order_by(User.created_at.desc()) .offset((page_value - 1) * size_value) .limit(size_value) ) return { "items": result.scalars().all(), "total": total or 0, "page": page_value, "pageSize": size_value, } @router.post("", response_model=UserResponse, dependencies=[Depends(require_permissions(["users.create"]))]) async def create_user(payload: UserCreate, db: AsyncSession = Depends(get_db)): exists = await db.execute(select(User.id).where(User.account == payload.account)) if exists.scalar_one_or_none(): raise HTTPException(status_code=409, detail="账号已存在") user = User( name=payload.name, account=payload.account, phone=payload.phone, title=payload.title, avatar=payload.avatar, role_id=payload.role_id, campus_id=payload.campus_id, dept_id=payload.dept_id, status=payload.status, password_hash=hash_password(payload.password), token_version=1 ) db.add(user) await db.commit() await db.refresh(user) return user @router.put("/{user_id}", response_model=UserResponse, dependencies=[Depends(require_permissions(["users.edit"]))]) async def update_user(user_id: UUID, payload: UserUpdate, 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(status_code=404, detail="人员不存在") update_data = payload.model_dump(exclude_unset=True) bump_token = False if "password" in update_data: update_data["password_hash"] = hash_password(update_data.pop("password")) bump_token = True if "status" in update_data and update_data["status"] != user.status: bump_token = True if "role_id" in update_data and update_data["role_id"] != user.role_id: bump_token = True for key, value in update_data.items(): setattr(user, key, value) if bump_token: user.token_version = (user.token_version or 1) + 1 await db.commit() await db.refresh(user) return user @router.delete("/{user_id}", dependencies=[Depends(require_permissions(["users.delete"]))]) async def delete_user(user_id: UUID, 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(status_code=404, detail="人员不存在") await db.delete(user) await db.commit() return {"success": True}