user.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. """用户CRUD"""
  2. from typing import Optional, List, Tuple
  3. from sqlalchemy.orm import Session
  4. from sqlalchemy import and_
  5. from app.models.models import SysUser, SysUserHis
  6. from app.crud.base import CRUDBase
  7. from app.core.security import get_password_hash
  8. from app.core.utils import generate_random_password
  9. # 运营端「重置密码」专用明文:仅服务端使用,禁止由 HTTP 请求传入
  10. DEFAULT_ADMIN_RESET_PLAIN_PASSWORD = "111111"
  11. class UserCRUD(CRUDBase[SysUser]):
  12. """用户CRUD操作"""
  13. def get_by_username(self, db: Session, username: str) -> Optional[SysUser]:
  14. """根据用户名获取用户"""
  15. return db.query(SysUser).filter(
  16. and_(SysUser.username == username, SysUser.is_del == 0)
  17. ).first()
  18. def get_by_phone(self, db: Session, phone: str) -> Optional[SysUser]:
  19. """根据手机号获取用户"""
  20. return db.query(SysUser).filter(
  21. and_(SysUser.phone == phone, SysUser.is_del == 0)
  22. ).first()
  23. def get_by_wx_mini_openid(self, db: Session, openid: str) -> Optional[SysUser]:
  24. """根据微信小程序 openid 获取用户"""
  25. if not openid:
  26. return None
  27. return db.query(SysUser).filter(
  28. and_(SysUser.wx_mini_openid == openid, SysUser.is_del == 0)
  29. ).first()
  30. def get_multi_by_role(self, db: Session, role: str, skip: int = 0, limit: int = 100) -> List[SysUser]:
  31. """根据角色获取用户列表"""
  32. return db.query(SysUser).filter(
  33. and_(SysUser.role == role, SysUser.is_del == 0)
  34. ).offset(skip).limit(limit).all()
  35. def get_multi_by_region(self, db: Session, region_id: str, skip: int = 0, limit: int = 100) -> List[SysUser]:
  36. """根据区域获取用户列表"""
  37. return db.query(SysUser).filter(
  38. and_(SysUser.region_id == region_id, SysUser.is_del == 0)
  39. ).offset(skip).limit(limit).all()
  40. def get_multi_with_filters(
  41. self,
  42. db: Session,
  43. role: str = None,
  44. region_id: str = None,
  45. keyword: str = None,
  46. is_active: bool = None,
  47. skip: int = 0,
  48. limit: int = 10
  49. ) -> tuple[List[SysUser], int]:
  50. """多条件查询用户"""
  51. query = db.query(SysUser).filter(SysUser.is_del == 0)
  52. if role:
  53. query = query.filter(SysUser.role == role)
  54. if region_id:
  55. query = query.filter(SysUser.region_id == region_id)
  56. if is_active is not None:
  57. query = query.filter(SysUser.is_active == is_active)
  58. if keyword:
  59. query = query.filter(
  60. (SysUser.username.like(f"%{keyword}%")) |
  61. (SysUser.real_name.like(f"%{keyword}%")) |
  62. (SysUser.phone.like(f"%{keyword}%"))
  63. )
  64. total = query.count()
  65. users = query.order_by(SysUser.create_at.desc()).offset(skip).limit(limit).all()
  66. return users, total
  67. def create_with_password(self, db: Session, obj_in: dict, creator: dict = None) -> Tuple[SysUser, Optional[str]]:
  68. """创建用户。
  69. 若请求体包含非空 password(运营端表单),则使用该密码写入 password_hash;
  70. 否则生成随机密码,并在返回值中给出明文 initialPassword(仅此种情况返回,供管理员告知用户)。
  71. """
  72. obj_in = dict(obj_in)
  73. raw_pwd = obj_in.pop("password", None)
  74. plain: Optional[str] = None
  75. if isinstance(raw_pwd, str):
  76. plain = raw_pwd.strip()
  77. if plain:
  78. if len(plain) < 6:
  79. raise ValueError("密码至少6位")
  80. obj_in["password_hash"] = get_password_hash(plain)
  81. initial_reveal: Optional[str] = None
  82. else:
  83. initial_reveal = generate_random_password()
  84. obj_in["password_hash"] = get_password_hash(initial_reveal)
  85. obj_in["is_active"] = obj_in.get("is_active", True)
  86. user = self.create(db, obj_in, creator)
  87. return user, initial_reveal
  88. def reset_password_to_admin_default(self, db: Session, id: str) -> bool:
  89. """将密码重置为服务端固定默认(DEFAULT_ADMIN_RESET_PLAIN_PASSWORD),不由请求传入。"""
  90. user = self.get(db, id)
  91. if not user:
  92. return False
  93. plain = DEFAULT_ADMIN_RESET_PLAIN_PASSWORD
  94. if len(plain) < 6:
  95. raise ValueError("默认重置密码配置无效:至少6位")
  96. user.password_hash = get_password_hash(plain)
  97. db.commit()
  98. return True
  99. def update_with_history(self, db: Session, id: str, obj_in: dict, updater: dict = None) -> Optional[SysUser]:
  100. """更新用户并记录历史"""
  101. original = self.get(db, id)
  102. if not original:
  103. return None
  104. his_data = {
  105. "id": original.id,
  106. "username": original.username,
  107. "password_hash": original.password_hash,
  108. "real_name": original.real_name,
  109. "phone": original.phone,
  110. "avatar_url": getattr(original, "avatar_url", None),
  111. "wx_mini_openid": getattr(original, "wx_mini_openid", None),
  112. "role": original.role,
  113. "region_id": original.region_id,
  114. "is_active": original.is_active,
  115. "create_at": original.create_at,
  116. "create_by": original.create_by,
  117. "create_name": original.create_name,
  118. "update_at": original.update_at,
  119. "update_by": original.update_by,
  120. }
  121. his = SysUserHis(**his_data)
  122. db.add(his)
  123. return self.update(db, id, obj_in, updater)
  124. def soft_delete_with_history(self, db: Session, id: str) -> bool:
  125. """删除用户并记录历史"""
  126. original = self.get(db, id)
  127. if not original:
  128. return False
  129. his_data = {
  130. "id": original.id,
  131. "username": original.username,
  132. "password_hash": original.password_hash,
  133. "real_name": original.real_name,
  134. "phone": original.phone,
  135. "avatar_url": getattr(original, "avatar_url", None),
  136. "wx_mini_openid": getattr(original, "wx_mini_openid", None),
  137. "role": original.role,
  138. "region_id": original.region_id,
  139. "is_active": original.is_active,
  140. "create_at": original.create_at,
  141. "create_by": original.create_by,
  142. "create_name": original.create_name,
  143. "update_at": original.update_at,
  144. "update_by": original.update_by,
  145. }
  146. his = SysUserHis(**his_data)
  147. db.add(his)
  148. return self.delete(db, id)
  149. user_crud = UserCRUD(SysUser)