users.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. from __future__ import annotations
  2. from fastapi import APIRouter, Depends, HTTPException, status
  3. from sqlalchemy import func
  4. from sqlmodel import select
  5. from sqlmodel.ext.asyncio.session import AsyncSession
  6. from app.db.session import get_async_session
  7. from app.models.users import User
  8. from app.schemas.users import UserCreate, UserRead, UserUpdate
  9. router = APIRouter(prefix="/users", tags=["users"])
  10. def _base_query():
  11. return select(User).where(User.is_active.is_(True))
  12. @router.get("/", response_model=list[UserRead])
  13. async def list_users(
  14. session: AsyncSession = Depends(get_async_session),
  15. ) -> list[User]:
  16. query = _base_query().order_by(User.created_at.desc())
  17. result = await session.exec(query)
  18. return result.all()
  19. @router.post("/", response_model=UserRead, status_code=status.HTTP_201_CREATED)
  20. async def create_user(
  21. payload: UserCreate,
  22. session: AsyncSession = Depends(get_async_session),
  23. ) -> User:
  24. # Check if username already exists
  25. existing = await session.exec(
  26. _base_query().where(func.lower(User.username) == payload.username.lower())
  27. )
  28. if existing.first():
  29. raise HTTPException(
  30. status_code=status.HTTP_409_CONFLICT,
  31. detail="Username already exists",
  32. )
  33. # Store password as plain text
  34. user = User(
  35. username=payload.username.strip(),
  36. password=payload.password,
  37. role=payload.role,
  38. )
  39. session.add(user)
  40. await session.commit()
  41. await session.refresh(user)
  42. return user
  43. async def _update_user_logic(
  44. id: int,
  45. payload: UserUpdate,
  46. session: AsyncSession,
  47. ) -> User:
  48. """更新用户的通用逻辑"""
  49. user = await _get_user(id, session)
  50. payload_dict = payload.model_dump(exclude_unset=True)
  51. if "username" in payload_dict:
  52. # Check if new username conflicts with existing user
  53. existing = await session.exec(
  54. _base_query()
  55. .where(func.lower(User.username) == payload.username.lower())
  56. .where(User.id != id)
  57. )
  58. if existing.first():
  59. raise HTTPException(
  60. status_code=status.HTTP_409_CONFLICT,
  61. detail="Username already exists",
  62. )
  63. user.username = payload.username.strip()
  64. if "password" in payload_dict:
  65. user.password = payload.password
  66. if "role" in payload_dict:
  67. user.role = payload.role
  68. await session.commit()
  69. await session.refresh(user)
  70. return user
  71. @router.put("/{id}", response_model=UserRead)
  72. async def update_user(
  73. id: int,
  74. payload: UserUpdate,
  75. session: AsyncSession = Depends(get_async_session),
  76. ) -> User:
  77. return await _update_user_logic(id, payload, session)
  78. @router.patch("/{id}", response_model=UserRead)
  79. async def patch_user(
  80. id: int,
  81. payload: UserUpdate,
  82. session: AsyncSession = Depends(get_async_session),
  83. ) -> User:
  84. return await _update_user_logic(id, payload, session)
  85. @router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT)
  86. async def delete_user(
  87. id: int,
  88. session: AsyncSession = Depends(get_async_session),
  89. ) -> None:
  90. user = await _get_user(id, session)
  91. user.is_active = False
  92. await session.commit()
  93. async def _get_user(id: int, session: AsyncSession) -> User:
  94. result = await session.exec(_base_query().where(User.id == id))
  95. user = result.one_or_none()
  96. if user is None:
  97. raise HTTPException(
  98. status_code=status.HTTP_404_NOT_FOUND,
  99. detail=f"User {id} not found",
  100. )
  101. return user