from datetime import datetime, timezone import asyncio from fastapi.logger import logger from shared.enums import GENDER_LIST from shared.constants import PAGE_SIZE, DEFAULT_PASSWORD from modules.system.user.schemas import UserForm, UserStatusForm, UpdatePwdForm, UserInfoForm, ResetPwdForm from modules.system.dept.models import Dept from modules.system.level.models import Level from modules.system.position.models import Position from modules.system.user.models import User from modules.system.user.models import UserRole from modules.system.user.repository import user_repo from core.database import get_db_session from core import R from core import password from utils.rich_text import get_file_url, save_file from utils.request import like_contains from core.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 = user_repo.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 = 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 = user_repo.get_by_id(user_id) # 查询结果判空 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(): # 唯一性校验 if user_repo.filter(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_repo.create(**formData, create_user=get_user_id(request)) 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(): # 记录ID判空 user_id = data.id if not user_id or int(user_id) <= 0: return R.failed("记录ID不能为空"), None # 唯一性校验(排除自身) if user_repo.filter(User.username == data.username, User.id != data.id, User.is_delete == 0).first(): return R.failed("登录账号不能重复"), None # 根据ID查询记录 if not user_repo.get_by_id(user_id): 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 = user_repo.update(user_id, 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_repo.batch_delete_r(user_id) # 设置状态 def update_user_status(request, data: UserStatusForm): # 记录ID id = data.id # 状态值 status = data.status # 根据ID查询记录 if not user_repo.get_by_id(id): return R.failed("记录不存在") # 更新记录 result = user_repo.update(id, { "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): # 用户ID user_id = get_user_id(request) # 根据ID查询记录 if not user_repo.get_by_id(user_id): return R.failed("记录不存在") # 更新记录 updData = data.model_dump() result = user_repo.update(user_id, updData) # 提交数据 if not result: return R.failed("更新失败") # 返回结果 return R.ok(msg="更新成功") # 更新密码 def update_pwd(request, data: UpdatePwdForm): # 用户ID user_id = get_user_id(request) # 根据ID查询用户 user = user_repo.get_by_id(user_id) 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 = user_repo.update(user_id, { "password": new_hash, "salt": salt }) # 提交数据 if not result: return R.failed("更新失败") # 返回结果 return R.ok(msg="更新成功") # 重置密码 def reset_user_pwd(request, data: ResetPwdForm): # 用户ID user_id = data.id # 根据ID查询用户 if not user_repo.get_by_id(user_id): return R.failed("用户不存在") # 加密新密码 salt = password.generate_salt() new_hash = password.encrypt_password(DEFAULT_PASSWORD, salt) result = user_repo.update(user_id, { "password": new_hash, "salt": salt }) # 提交数据 if not result: return R.failed("重置密码失败") # 返回结果 return R.ok(msg="重置密码成功") from modules.system.user.models import UserRole from modules.system.role.models import Role from core.database import get_db_session # 根据用户ID查询角色列表 def get_user_role_list(userId): db = get_db_session() rows = ( db.query(Role.id, Role.name) .join(UserRole, UserRole.role_id == Role.id) .filter(UserRole.user_id == userId, Role.is_delete == 0) .all() ) role_list = [] if rows: for v in rows: item = { 'id': v.id, 'name': v.name, } role_list.append(item) return role_list # 根据用户ID列表批量查询角色,返回 {user_id: [{id, name}, ...]} 字典 def get_user_role_map(user_ids): if not user_ids: return {} db = get_db_session() # 用ORM查询替代原生SQL,避免tuple单元素/空列表的IN语法问题 from modules.system.user.models import UserRole from modules.system.role.models import Role rows = ( db.query(UserRole.user_id, Role.id.label('role_id'), Role.name.label('role_name')) .join(Role, Role.id == UserRole.role_id) .filter(UserRole.user_id.in_(user_ids), Role.is_delete == 0) .all() ) role_map = {} for row in rows: uid = row.user_id if uid not in role_map: role_map[uid] = [] role_map[uid].append({'id': row.role_id, 'name': row.role_name}) return role_map