users.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. from typing import Optional
  2. from uuid import UUID
  3. from fastapi import APIRouter, Depends, HTTPException, Query
  4. from sqlalchemy import func, or_, select
  5. from sqlalchemy.ext.asyncio import AsyncSession
  6. from backend.app.core.dependencies import get_current_user, require_any_permissions, require_permissions
  7. from backend.app.core.security import hash_password
  8. from backend.app.db.session import get_db
  9. from backend.app.models import RolePermission, User
  10. from backend.app.schemas.pagination import PaginatedResponse
  11. from backend.app.schemas.user import CurrentUserResponse, UserCreate, UserResponse, UserUpdate
  12. router = APIRouter(prefix="/users", tags=["users"])
  13. def is_limited_scope(user: User) -> bool:
  14. return user.role and user.role.name not in {"管理员", "排班员"}
  15. @router.get("/me", response_model=CurrentUserResponse)
  16. async def get_current_user_profile(
  17. current_user: User = Depends(get_current_user),
  18. db: AsyncSession = Depends(get_db),
  19. ):
  20. result = await db.execute(
  21. select(RolePermission.permission_code).where(RolePermission.role_id == current_user.role_id)
  22. )
  23. permissions = [row[0] for row in result.fetchall()]
  24. return {
  25. "id": current_user.id,
  26. "name": current_user.name,
  27. "account": current_user.account,
  28. "phone": current_user.phone,
  29. "title": current_user.title,
  30. "avatar": current_user.avatar,
  31. "role_id": current_user.role_id,
  32. "campus_id": current_user.campus_id,
  33. "dept_id": current_user.dept_id,
  34. "status": current_user.status,
  35. "permissions": permissions,
  36. }
  37. @router.get(
  38. "",
  39. response_model=list[UserResponse] | PaginatedResponse[UserResponse],
  40. )
  41. async def list_users(
  42. db: AsyncSession = Depends(get_db),
  43. current_user: User = Depends(require_any_permissions(["users.view", "schedule.view", "duty.view"])),
  44. keyword: Optional[str] = Query(default=None),
  45. campus_id: Optional[UUID] = Query(default=None),
  46. dept_id: Optional[UUID] = Query(default=None),
  47. role_id: Optional[UUID] = Query(default=None),
  48. status: Optional[str] = Query(default=None),
  49. page: Optional[int] = Query(default=None, ge=1),
  50. page_size: Optional[int] = Query(default=None, ge=1, le=200, alias="pageSize"),
  51. size: Optional[int] = Query(default=None, ge=1, le=200),
  52. ):
  53. query = select(User)
  54. if is_limited_scope(current_user):
  55. if current_user.dept_id:
  56. dept_id = current_user.dept_id
  57. elif current_user.campus_id:
  58. campus_id = current_user.campus_id
  59. else:
  60. if page is None and page_size is None and size is None:
  61. return []
  62. return {"items": [], "total": 0, "page": page or 1, "pageSize": page_size or size or 10}
  63. if keyword:
  64. like = f"%{keyword.strip()}%"
  65. query = query.where(
  66. or_(
  67. User.name.ilike(like),
  68. User.account.ilike(like),
  69. User.phone.ilike(like),
  70. User.title.ilike(like),
  71. )
  72. )
  73. if campus_id:
  74. query = query.where(User.campus_id == campus_id)
  75. if dept_id:
  76. query = query.where(User.dept_id == dept_id)
  77. if role_id:
  78. query = query.where(User.role_id == role_id)
  79. if status:
  80. query = query.where(User.status == status)
  81. if page is None and page_size is None and size is None:
  82. result = await db.execute(query.order_by(User.created_at.desc()))
  83. return result.scalars().all()
  84. page_value = page or 1
  85. size_value = page_size or size or 10
  86. total = await db.scalar(select(func.count()).select_from(query.subquery()))
  87. result = await db.execute(
  88. query.order_by(User.created_at.desc())
  89. .offset((page_value - 1) * size_value)
  90. .limit(size_value)
  91. )
  92. return {
  93. "items": result.scalars().all(),
  94. "total": total or 0,
  95. "page": page_value,
  96. "pageSize": size_value,
  97. }
  98. @router.post("", response_model=UserResponse, dependencies=[Depends(require_permissions(["users.create"]))])
  99. async def create_user(payload: UserCreate, db: AsyncSession = Depends(get_db)):
  100. exists = await db.execute(select(User.id).where(User.account == payload.account))
  101. if exists.scalar_one_or_none():
  102. raise HTTPException(status_code=409, detail="账号已存在")
  103. user = User(
  104. name=payload.name,
  105. account=payload.account,
  106. phone=payload.phone,
  107. title=payload.title,
  108. avatar=payload.avatar,
  109. role_id=payload.role_id,
  110. campus_id=payload.campus_id,
  111. dept_id=payload.dept_id,
  112. status=payload.status,
  113. password_hash=hash_password(payload.password),
  114. token_version=1
  115. )
  116. db.add(user)
  117. await db.commit()
  118. await db.refresh(user)
  119. return user
  120. @router.put("/{user_id}", response_model=UserResponse, dependencies=[Depends(require_permissions(["users.edit"]))])
  121. async def update_user(user_id: UUID, payload: UserUpdate, db: AsyncSession = Depends(get_db)):
  122. result = await db.execute(select(User).where(User.id == user_id))
  123. user = result.scalar_one_or_none()
  124. if not user:
  125. raise HTTPException(status_code=404, detail="人员不存在")
  126. update_data = payload.model_dump(exclude_unset=True)
  127. bump_token = False
  128. if "password" in update_data:
  129. update_data["password_hash"] = hash_password(update_data.pop("password"))
  130. bump_token = True
  131. if "status" in update_data and update_data["status"] != user.status:
  132. bump_token = True
  133. if "role_id" in update_data and update_data["role_id"] != user.role_id:
  134. bump_token = True
  135. for key, value in update_data.items():
  136. setattr(user, key, value)
  137. if bump_token:
  138. user.token_version = (user.token_version or 1) + 1
  139. await db.commit()
  140. await db.refresh(user)
  141. return user
  142. @router.delete("/{user_id}", dependencies=[Depends(require_permissions(["users.delete"]))])
  143. async def delete_user(user_id: UUID, db: AsyncSession = Depends(get_db)):
  144. result = await db.execute(select(User).where(User.id == user_id))
  145. user = result.scalar_one_or_none()
  146. if not user:
  147. raise HTTPException(status_code=404, detail="人员不存在")
  148. await db.delete(user)
  149. await db.commit()
  150. return {"success": True}