user.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. roles: Optional[List[str]] = None,
  45. region_id: str = None,
  46. keyword: str = None,
  47. is_active: bool = None,
  48. skip: int = 0,
  49. limit: int = 10
  50. ) -> tuple[List[SysUser], int]:
  51. """多条件查询用户"""
  52. query = db.query(SysUser).filter(SysUser.is_del == 0)
  53. if role:
  54. query = query.filter(SysUser.role == role)
  55. elif roles:
  56. role_values = [str(x).strip() for x in roles if str(x).strip()]
  57. if role_values:
  58. query = query.filter(SysUser.role.in_(role_values))
  59. if region_id:
  60. query = query.filter(SysUser.region_id == region_id)
  61. if is_active is not None:
  62. query = query.filter(SysUser.is_active == is_active)
  63. if keyword:
  64. query = query.filter(
  65. (SysUser.username.like(f"%{keyword}%")) |
  66. (SysUser.real_name.like(f"%{keyword}%")) |
  67. (SysUser.phone.like(f"%{keyword}%"))
  68. )
  69. total = query.count()
  70. users = query.order_by(SysUser.create_at.desc()).offset(skip).limit(limit).all()
  71. return users, total
  72. def create_with_password(self, db: Session, obj_in: dict, creator: dict = None) -> Tuple[SysUser, Optional[str]]:
  73. """创建用户。
  74. 若请求体包含非空 password(运营端表单),则使用该密码写入 password_hash;
  75. 否则生成随机密码,并在返回值中给出明文 initialPassword(仅此种情况返回,供管理员告知用户)。
  76. """
  77. obj_in = dict(obj_in)
  78. raw_pwd = obj_in.pop("password", None)
  79. plain: Optional[str] = None
  80. if isinstance(raw_pwd, str):
  81. plain = raw_pwd.strip()
  82. if plain:
  83. if len(plain) < 6:
  84. raise ValueError("密码至少6位")
  85. obj_in["password_hash"] = get_password_hash(plain)
  86. initial_reveal: Optional[str] = None
  87. else:
  88. initial_reveal = generate_random_password()
  89. obj_in["password_hash"] = get_password_hash(initial_reveal)
  90. obj_in["is_active"] = obj_in.get("is_active", True)
  91. user = self.create(db, obj_in, creator)
  92. return user, initial_reveal
  93. def reset_password_to_admin_default(self, db: Session, id: str) -> bool:
  94. """将密码重置为服务端固定默认(DEFAULT_ADMIN_RESET_PLAIN_PASSWORD),不由请求传入。"""
  95. user = self.get(db, id)
  96. if not user:
  97. return False
  98. plain = DEFAULT_ADMIN_RESET_PLAIN_PASSWORD
  99. if len(plain) < 6:
  100. raise ValueError("默认重置密码配置无效:至少6位")
  101. user.password_hash = get_password_hash(plain)
  102. db.commit()
  103. return True
  104. def update_with_history(self, db: Session, id: str, obj_in: dict, updater: dict = None) -> Optional[SysUser]:
  105. """更新用户并记录历史"""
  106. original = self.get(db, id)
  107. if not original:
  108. return None
  109. his_data = {
  110. "id": original.id,
  111. "username": original.username,
  112. "password_hash": original.password_hash,
  113. "real_name": original.real_name,
  114. "phone": original.phone,
  115. "avatar_url": getattr(original, "avatar_url", None),
  116. "wx_mini_openid": getattr(original, "wx_mini_openid", None),
  117. "role": original.role,
  118. "region_id": original.region_id,
  119. "is_active": original.is_active,
  120. "create_at": original.create_at,
  121. "create_by": original.create_by,
  122. "create_name": original.create_name,
  123. "update_at": original.update_at,
  124. "update_by": original.update_by,
  125. }
  126. his = SysUserHis(**his_data)
  127. db.add(his)
  128. return self.update(db, id, obj_in, updater)
  129. def soft_delete_with_history(self, db: Session, id: str) -> bool:
  130. """删除用户并记录历史"""
  131. original = self.get(db, id)
  132. if not original:
  133. return False
  134. his_data = {
  135. "id": original.id,
  136. "username": original.username,
  137. "password_hash": original.password_hash,
  138. "real_name": original.real_name,
  139. "phone": original.phone,
  140. "avatar_url": getattr(original, "avatar_url", None),
  141. "wx_mini_openid": getattr(original, "wx_mini_openid", None),
  142. "role": original.role,
  143. "region_id": original.region_id,
  144. "is_active": original.is_active,
  145. "create_at": original.create_at,
  146. "create_by": original.create_by,
  147. "create_name": original.create_name,
  148. "update_at": original.update_at,
  149. "update_by": original.update_by,
  150. }
  151. his = SysUserHis(**his_data)
  152. db.add(his)
  153. return self.delete(db, id)
  154. user_crud = UserCRUD(SysUser)