from datetime import datetime, timezone import asyncio from fastapi.logger import logger from sqlalchemy import and_ from apps.constants.constants import GENDER_LIST from apps.constants.message import PAGE_SIZE, DEFAULT_PASSWORD from apps.forms.user import UserForm, UserStatusForm, UpdatePwdForm, UserInfoForm, ResetPwdForm from apps.models.dept import Dept from apps.models.level import Level from apps.models.position import Position from apps.models.user import User from apps.models.user_role import UserRole from apps.services import user_role from extensions import get_db_session from utils import R from utils import password from utils.rich_text import get_file_url, save_file from utils.request import like_contains from utils.security import get_user_id from utils.perm_cache import invalidate_user_perms # 查询用户分页数据 def get_user_page(request): db = get_db_session() # 页码 page = int(request.query_params.get("pageNo", 1)) # 每页数 limit = int(request.query_params.get("pageSize", PAGE_SIZE)) # 实例化查询对象 query = db.query(User).filter(User.is_delete == 0) # 用户账号 username = request.query_params.get('username') if username: query = query.filter(User.username.like(like_contains(username), escape='\\')) # 性别 gender = request.query_params.get('gender') if gender: query = query.filter(User.gender == gender) # 用户状态 status = request.query_params.get('status') if status: query = query.filter(User.status == status) # 排序 query = query.order_by(User.id.asc()) # 记录总数 count = query.count() # 分页查询 user_list = query.limit(limit).offset((page - 1) * limit).all() # 若当前页超出末页(数据被并发删除),自动回退到第一页 if page > 1 and not user_list: page = 1 count = query.count() user_list = query.limit(limit).offset(0).all() # 实例化结果 result = [] # 遍历数据源 if len(user_list) > 0: # 仅查询当前页用到的部门ID dept_ids = {item.dept_id for item in user_list if item.dept_id} deptList = {} if dept_ids: for dept in db.query(Dept).filter(Dept.id.in_(dept_ids), Dept.is_delete == 0).all(): deptList[dept.id] = dept.name # 仅查询当前页用到的职级ID level_ids = {item.level_id for item in user_list if item.level_id} levelList = {} if level_ids: for level in db.query(Level).filter(Level.id.in_(level_ids), Level.is_delete == 0).all(): levelList[level.id] = level.name # 仅查询当前页用到的岗位ID position_ids = {item.position_id for item in user_list if item.position_id} positionList = {} if position_ids: for position in db.query(Position).filter(Position.id.in_(position_ids), Position.is_delete == 0).all(): positionList[position.id] = position.name # 批量查询用户角色 user_ids = [item.id for item in user_list] roleMap = user_role.get_user_role_map(user_ids) for item in user_list: # 城市 city_list = [] # 省份编码 city_list.append(item.province_code) # 城市编码 city_list.append(item.city_code) # 县区编码 city_list.append(item.district_code) # 对象转字典 data = item.to_dict() # 性别 data['gender_name'] = GENDER_LIST.get(item.gender) # 头像 data['avatar'] = get_file_url(item.avatar) if item.avatar else "" # 部门名称 data['dept_name'] = deptList.get(item.dept_id) if deptList else None # 职级名称 data['level_name'] = levelList.get(item.level_id) if levelList else None # 岗位名称 data['position_name'] = positionList.get(item.position_id) if positionList else None # 角色列表 data['roleList'] = roleMap.get(item.id, []) # 行政区划 data['city'] = city_list # 加入列表 result.append(data) # 返回结果 return R.ok(data=result, count=count) # 根据ID查询用户详情 def get_user_detail(user_id): db = get_db_session() # 根据ID查询用户 user = db.query(User).filter(and_(User.id == user_id, User.is_delete == 0)).first() # 查询结果判空 if not user: return None # 获取用户角色数据 roleList = db.query(UserRole).filter(UserRole.user_id == user.id).all() roles = [] for role in roleList: roles.append(role.role_id) # 城市编码 cityList = [] # 省份编号 cityList.append(user.province_code) # 城市编码 cityList.append(user.city_code) # 县区编码 cityList.append(user.district_code) # 对象转字典 data = user.to_dict() # 头像 data['avatar'] = get_file_url(user.avatar) if user.avatar else "" # 角色 data['roles'] = roles # 行政区划 data['city'] = cityList # 返回结果 return data # 添加用户 async def add_user(request, data: UserForm): def _add(): db = get_db_session() # 唯一性校验 if db.query(User).filter(and_(User.username == data.username, User.is_delete == 0)).first(): return R.failed("登录账号不能重复"), None # 获取表单数据(排除非数据列) formData = data.model_dump(exclude={'city', 'roles'}) # 图片处理 avatar = data.avatar if avatar: formData['avatar'] = save_file(avatar, "user") # 密码加密:未传密码时使用默认密码 pwd = data.password or DEFAULT_PASSWORD salt = password.generate_salt() formData['password'] = password.encrypt_password(pwd, salt) formData['salt'] = salt # 行政区划处理 citys = data.city if citys and len(citys) == 3: formData['province_code'] = citys[0] formData['city_code'] = citys[1] formData['district_code'] = citys[2] # 表单数据赋值给对象 user = User(**formData) user.create_user = get_user_id(request) # 插入数据 user.save() return None, user.id # 同步DB/文件操作放入线程池,避免阻塞事件循环 err, user_id = await asyncio.to_thread(_add) if err: return err # 创建用户角色数据 + 清理权限缓存 await add_user_role(user_id, data.roles) # 返回结果 return R.ok(msg="添加成功") # 更新用户 async def update_user(request, data: UserForm): def _update(): db = get_db_session() # 记录ID判空 user_id = data.id if not user_id or int(user_id) <= 0: return R.failed("记录ID不能为空"), None # 唯一性校验(排除自身) if db.query(User).filter(and_(User.username == data.username, User.id != data.id, User.is_delete == 0)).first(): return R.failed("登录账号不能重复"), None # 根据ID查询记录 user = db.query(User).filter(and_(User.id == user_id, User.is_delete == 0)).first() # 查询结果判空 if not user: return R.failed("记录不存在"), None # 获取表单数据(排除敏感字段和非数据列) formData = data.model_dump(exclude={'id', 'password', 'city', 'roles'}) # 图片处理 avatar = data.avatar if avatar: formData['avatar'] = save_file(avatar, "user") # 密码加密:仅传入新密码时才更新,否则保留原值 pwd = data.password if pwd: salt = password.generate_salt() formData['password'] = password.encrypt_password(pwd, salt) formData['salt'] = salt # 行政区划处理 citys = data.city if citys and len(citys) == 3: formData['province_code'] = citys[0] formData['city_code'] = citys[1] formData['district_code'] = citys[2] updData = formData updData['update_user'] = get_user_id(request) updData['update_time'] = datetime.now(tz=timezone.utc) # 更新数据 result = db.query(User).filter_by(id=user_id).update(updData) if not result: return R.failed("更新失败"), None return None, user_id # 同步DB/文件操作放入线程池,避免阻塞事件循环 err, user_id = await asyncio.to_thread(_update) if err: return err # 创建用户角色数据 + 清理权限缓存 await add_user_role(user_id, data.roles) # 返回结果 return R.ok(msg="更新成功") # 创建用户角色信息 async def add_user_role(user_id, roleIdList): def _save(): db = get_db_session() # 删除用户角色关系数据 db.query(UserRole).filter(UserRole.user_id == user_id).delete() # 创建新的用户角色关系 if roleIdList: new_roles = [] for roleId in roleIdList: if not roleId: continue new_roles.append(UserRole(user_id=user_id, role_id=roleId)) db.add_all(new_roles) # 同步DB操作放入线程池,避免阻塞事件循环 await asyncio.to_thread(_save) # 清理该用户权限缓存 await invalidate_user_perms(user_id) # 删除用户 def delete_user(user_id): return User.batch_delete_r(user_id) # 设置状态 def update_user_status(request, data: UserStatusForm): db = get_db_session() # 记录ID id = data.id # 状态值 status = data.status # 根据ID查询记录 user = db.query(User).filter(and_(User.id == id, User.is_delete == 0)).first() # 查询结果判空 if not user: return R.failed("记录不存在") # 更新记录 result = db.query(User).filter_by(id=id).update({ "status": status, "update_user": get_user_id(request), "update_time": datetime.now(tz=timezone.utc) }) # 提交数据 if not result: return R.failed("设置失败") # 返回结果 return R.ok(msg="设置成功") # 更新用户信息 def update_user_info(request, data: UserInfoForm): db = get_db_session() # 用户ID user_id = get_user_id(request) # 根据ID查询记录 user = db.query(User).filter(and_(User.id == user_id, User.is_delete == 0)).first() # 查询结果判空 if not user: return R.failed("记录不存在") # 更新记录 updData = data.model_dump() result = db.query(User).filter_by(id=user_id).update(updData) # 提交数据 if not result: return R.failed("更新失败") # 返回结果 return R.ok(msg="更新成功") # 更新密码 def update_pwd(request, data: UpdatePwdForm): db = get_db_session() # 用户ID user_id = get_user_id(request) # 根据ID查询用户 user = db.query(User).filter(and_(User.is_delete == 0, User.id == user_id)).first() if not user: return R.failed("用户不存在") # 验证原始密码 if not password.verify_password(data.oldPassword, user.salt or '', user.password or ''): return R.failed("原始密码不正确") # 加密新密码 salt = password.generate_salt() new_hash = password.encrypt_password(data.newPassword, salt) result = db.query(User).filter_by(id=user_id).update({ "password": new_hash, "salt": salt }) # 提交数据 if not result: return R.failed("更新失败") # 返回结果 return R.ok(msg="更新成功") # 重置密码 def reset_user_pwd(request, data: ResetPwdForm): db = get_db_session() # 用户ID user_id = data.id # 根据ID查询用户 user = db.query(User).filter(and_(User.is_delete == 0, User.id == user_id)).first() if not user: return R.failed("用户不存在") # 加密新密码 salt = password.generate_salt() new_hash = password.encrypt_password(DEFAULT_PASSWORD, salt) result = db.query(User).filter_by(id=user_id).update({ "password": new_hash, "salt": salt }) # 提交数据 if not result: return R.failed("重置密码失败") # 返回结果 return R.ok(msg="重置密码成功")