178 lines
6.8 KiB
Python
178 lines
6.8 KiB
Python
"""用户CRUD"""
|
||
from typing import Optional, List, Tuple
|
||
from sqlalchemy.orm import Session
|
||
from sqlalchemy import and_
|
||
|
||
from app.models.models import SysUser, SysUserHis
|
||
from app.crud.base import CRUDBase
|
||
from app.core.security import get_password_hash
|
||
from app.core.utils import generate_random_password
|
||
|
||
# 运营端「重置密码」专用明文:仅服务端使用,禁止由 HTTP 请求传入
|
||
DEFAULT_ADMIN_RESET_PLAIN_PASSWORD = "111111"
|
||
|
||
|
||
class UserCRUD(CRUDBase[SysUser]):
|
||
"""用户CRUD操作"""
|
||
|
||
def get_by_username(self, db: Session, username: str) -> Optional[SysUser]:
|
||
"""根据用户名获取用户"""
|
||
return db.query(SysUser).filter(
|
||
and_(SysUser.username == username, SysUser.is_del == 0)
|
||
).first()
|
||
|
||
def get_by_phone(self, db: Session, phone: str) -> Optional[SysUser]:
|
||
"""根据手机号获取用户"""
|
||
return db.query(SysUser).filter(
|
||
and_(SysUser.phone == phone, SysUser.is_del == 0)
|
||
).first()
|
||
|
||
def get_by_wx_mini_openid(self, db: Session, openid: str) -> Optional[SysUser]:
|
||
"""根据微信小程序 openid 获取用户"""
|
||
if not openid:
|
||
return None
|
||
return db.query(SysUser).filter(
|
||
and_(SysUser.wx_mini_openid == openid, SysUser.is_del == 0)
|
||
).first()
|
||
|
||
def get_multi_by_role(self, db: Session, role: str, skip: int = 0, limit: int = 100) -> List[SysUser]:
|
||
"""根据角色获取用户列表"""
|
||
return db.query(SysUser).filter(
|
||
and_(SysUser.role == role, SysUser.is_del == 0)
|
||
).offset(skip).limit(limit).all()
|
||
|
||
def get_multi_by_region(self, db: Session, region_id: str, skip: int = 0, limit: int = 100) -> List[SysUser]:
|
||
"""根据区域获取用户列表"""
|
||
return db.query(SysUser).filter(
|
||
and_(SysUser.region_id == region_id, SysUser.is_del == 0)
|
||
).offset(skip).limit(limit).all()
|
||
|
||
def get_multi_with_filters(
|
||
self,
|
||
db: Session,
|
||
role: str = None,
|
||
roles: Optional[List[str]] = None,
|
||
region_id: str = None,
|
||
keyword: str = None,
|
||
is_active: bool = None,
|
||
skip: int = 0,
|
||
limit: int = 10
|
||
) -> tuple[List[SysUser], int]:
|
||
"""多条件查询用户"""
|
||
query = db.query(SysUser).filter(SysUser.is_del == 0)
|
||
|
||
if role:
|
||
query = query.filter(SysUser.role == role)
|
||
elif roles:
|
||
role_values = [str(x).strip() for x in roles if str(x).strip()]
|
||
if role_values:
|
||
query = query.filter(SysUser.role.in_(role_values))
|
||
if region_id:
|
||
query = query.filter(SysUser.region_id == region_id)
|
||
if is_active is not None:
|
||
query = query.filter(SysUser.is_active == is_active)
|
||
if keyword:
|
||
query = query.filter(
|
||
(SysUser.username.like(f"%{keyword}%")) |
|
||
(SysUser.real_name.like(f"%{keyword}%")) |
|
||
(SysUser.phone.like(f"%{keyword}%"))
|
||
)
|
||
|
||
total = query.count()
|
||
users = query.order_by(SysUser.create_at.desc()).offset(skip).limit(limit).all()
|
||
return users, total
|
||
|
||
def create_with_password(self, db: Session, obj_in: dict, creator: dict = None) -> Tuple[SysUser, Optional[str]]:
|
||
"""创建用户。
|
||
|
||
若请求体包含非空 password(运营端表单),则使用该密码写入 password_hash;
|
||
否则生成随机密码,并在返回值中给出明文 initialPassword(仅此种情况返回,供管理员告知用户)。
|
||
"""
|
||
obj_in = dict(obj_in)
|
||
raw_pwd = obj_in.pop("password", None)
|
||
plain: Optional[str] = None
|
||
if isinstance(raw_pwd, str):
|
||
plain = raw_pwd.strip()
|
||
if plain:
|
||
if len(plain) < 6:
|
||
raise ValueError("密码至少6位")
|
||
obj_in["password_hash"] = get_password_hash(plain)
|
||
initial_reveal: Optional[str] = None
|
||
else:
|
||
initial_reveal = generate_random_password()
|
||
obj_in["password_hash"] = get_password_hash(initial_reveal)
|
||
obj_in["is_active"] = obj_in.get("is_active", True)
|
||
user = self.create(db, obj_in, creator)
|
||
return user, initial_reveal
|
||
|
||
def reset_password_to_admin_default(self, db: Session, id: str) -> bool:
|
||
"""将密码重置为服务端固定默认(DEFAULT_ADMIN_RESET_PLAIN_PASSWORD),不由请求传入。"""
|
||
user = self.get(db, id)
|
||
if not user:
|
||
return False
|
||
plain = DEFAULT_ADMIN_RESET_PLAIN_PASSWORD
|
||
if len(plain) < 6:
|
||
raise ValueError("默认重置密码配置无效:至少6位")
|
||
user.password_hash = get_password_hash(plain)
|
||
db.commit()
|
||
return True
|
||
|
||
def update_with_history(self, db: Session, id: str, obj_in: dict, updater: dict = None) -> Optional[SysUser]:
|
||
"""更新用户并记录历史"""
|
||
original = self.get(db, id)
|
||
if not original:
|
||
return None
|
||
|
||
his_data = {
|
||
"id": original.id,
|
||
"username": original.username,
|
||
"password_hash": original.password_hash,
|
||
"real_name": original.real_name,
|
||
"phone": original.phone,
|
||
"avatar_url": getattr(original, "avatar_url", None),
|
||
"wx_mini_openid": getattr(original, "wx_mini_openid", None),
|
||
"role": original.role,
|
||
"region_id": original.region_id,
|
||
"is_active": original.is_active,
|
||
"create_at": original.create_at,
|
||
"create_by": original.create_by,
|
||
"create_name": original.create_name,
|
||
"update_at": original.update_at,
|
||
"update_by": original.update_by,
|
||
}
|
||
his = SysUserHis(**his_data)
|
||
db.add(his)
|
||
|
||
return self.update(db, id, obj_in, updater)
|
||
|
||
def soft_delete_with_history(self, db: Session, id: str) -> bool:
|
||
"""删除用户并记录历史"""
|
||
original = self.get(db, id)
|
||
if not original:
|
||
return False
|
||
|
||
his_data = {
|
||
"id": original.id,
|
||
"username": original.username,
|
||
"password_hash": original.password_hash,
|
||
"real_name": original.real_name,
|
||
"phone": original.phone,
|
||
"avatar_url": getattr(original, "avatar_url", None),
|
||
"wx_mini_openid": getattr(original, "wx_mini_openid", None),
|
||
"role": original.role,
|
||
"region_id": original.region_id,
|
||
"is_active": original.is_active,
|
||
"create_at": original.create_at,
|
||
"create_by": original.create_by,
|
||
"create_name": original.create_name,
|
||
"update_at": original.update_at,
|
||
"update_by": original.update_by,
|
||
}
|
||
his = SysUserHis(**his_data)
|
||
db.add(his)
|
||
|
||
return self.delete(db, id)
|
||
|
||
|
||
user_crud = UserCRUD(SysUser)
|