import uuid from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.database import get_db from app.middleware.auth import get_current_user, require_director from app.models.user import User from app.schemas.customer import UserOut router = APIRouter(prefix="/users", tags=["Users"]) class UpdateUserRoleRequest(BaseModel): role: str # manager / director / leader department: str = "" require_report: bool = True color: Optional[str] = None # hex color for manager tags @router.get("/", response_model=list[UserOut]) async def list_users( role: Optional[str] = Query(None), current_user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """List all users. Used for companion selection, assignment, etc.""" query = select(User) if role: query = query.where(User.role == role) query = query.order_by(User.name) result = await db.execute(query) return result.scalars().all() @router.get("/me") async def get_current_user_info( current_user: dict = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """Get current user's full profile.""" result = await db.execute(select(User).where(User.id == uuid.UUID(current_user["user_id"]))) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") return { "id": str(user.id), "casdoor_id": user.casdoor_id, "name": user.name, "role": user.role, "department": user.department, "wecom_userid": user.wecom_userid, } @router.put("/{user_id}/role") async def update_user_role( user_id: str, data: UpdateUserRoleRequest, current_user: dict = Depends(require_director), db: AsyncSession = Depends(get_db), ): """Director updates a user's role. Only director can do this.""" if data.role not in ("manager", "director", "leader"): raise HTTPException(status_code=400, detail="Invalid role. Must be manager/director/leader") result = await db.execute(select(User).where(User.id == uuid.UUID(user_id))) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") user.role = data.role if data.department: user.department = data.department user.require_report = data.require_report if data.color is not None: user.color = data.color await db.commit() await db.refresh(user) return { "id": str(user.id), "name": user.name, "role": user.role, "department": user.department, "require_report": user.require_report, "color": user.color, } class UpdateWecomRequest(BaseModel): wecom_userid: Optional[str] = None # None or "" to unbind @router.put("/{user_id}/wecom") async def update_user_wecom( user_id: str, data: UpdateWecomRequest, current_user: dict = Depends(require_director), db: AsyncSession = Depends(get_db), ): """Director binds or unbinds a user's WeChat Work account.""" result = await db.execute(select(User).where(User.id == uuid.UUID(user_id))) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") new_id = (data.wecom_userid or "").strip() # Check duplicate if new_id: dup = await db.execute( select(User).where(User.wecom_userid == new_id, User.id != uuid.UUID(user_id)) ) existing = dup.scalar_one_or_none() if existing: raise HTTPException(status_code=400, detail=f"该企微ID已被「{existing.name}」绑定") user.wecom_userid = new_id if new_id else None await db.commit() await db.refresh(user) return { "id": str(user.id), "name": user.name, "wecom_userid": user.wecom_userid, } @router.delete("/{user_id}") async def delete_user( user_id: str, current_user: dict = Depends(require_director), db: AsyncSession = Depends(get_db), ): """Director deletes a user. Cannot delete self.""" if user_id == current_user["user_id"]: raise HTTPException(status_code=400, detail="不能删除自己") result = await db.execute(select(User).where(User.id == uuid.UUID(user_id))) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") await db.delete(user) await db.commit() return {"detail": "deleted", "name": user.name}