mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-22 05:02:57 +00:00
- 移除原监控仪表盘独立模块,将相关功能合并到在线监控模块 - 重构租户配置字段名,统一使用logo_url和name替代tenant_logo/tenant_name - 优化搜索工具函数,移除重复导入 - 调整参数配置模型字段长度限制,移除config_value的max_length约束 - 清理冗余的常量定义和导入语句 - 修复批量状态设置接口的redis依赖注入 - 增强OAuth登录安全性,添加租户默认归属和state一次性消费 - 优化资源目录缓存逻辑,减少重复计算 - 新增API Token模块基础框架 - 完善用户token版本管理,支持主动失效JWT - 调整AI模型配置缓存过期时间 - 修复菜单类型字段索引,提升查询性能 - 简化前端刷新token调用逻辑 - 新增滑块验证完成接口和忘记密码验证码校验 - 调整系统配置默认值,添加操作日志保留天数和接口白名单配置 - 限制Mock支付回调仅在开发环境可用 - 重构websocket认证方式,支持更安全的subprotocol传参
121 lines
4.1 KiB
Python
121 lines
4.1 KiB
Python
from datetime import datetime
|
||
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from app.api.v1.module_system.position.crud import PositionCRUD
|
||
from app.api.v1.module_system.role.crud import RoleCRUD
|
||
from app.core.base_crud import CRUDBase
|
||
from app.core.base_schema import AuthSchema
|
||
from app.core.exceptions import CustomException
|
||
|
||
from .model import UserModel
|
||
from .schema import (
|
||
UserCreateSchema,
|
||
UserUpdateSchema,
|
||
)
|
||
|
||
|
||
class UserCRUD(CRUDBase[UserModel, UserCreateSchema, UserUpdateSchema]):
|
||
"""用户模块数据层"""
|
||
|
||
def __init__(self, auth: AuthSchema, db: AsyncSession) -> None:
|
||
super().__init__(model=UserModel, auth=auth, db=db)
|
||
|
||
async def update_last_login(self, id: int) -> None:
|
||
"""更新用户最后登录时间
|
||
|
||
参数:
|
||
- id (int): 用户ID
|
||
"""
|
||
await self.set([id], last_login=datetime.now())
|
||
|
||
async def set_user_roles(self, user_ids: list[int], role_ids: list[int]) -> None:
|
||
"""批量设置用户角色(带租户隔离验证)
|
||
|
||
参数:
|
||
- user_ids (list[int]): 用户ID列表
|
||
- role_ids (list[int]): 角色ID列表
|
||
|
||
返回:
|
||
- None
|
||
"""
|
||
user_objs = await self.get_list(search={"id": ("in", user_ids)})
|
||
if role_ids:
|
||
role_objs = await RoleCRUD(self.auth, self.db).get_list(search={"id": ("in", role_ids)})
|
||
auth_user = self.auth.user
|
||
if auth_user and not auth_user.is_superuser:
|
||
for role in role_objs:
|
||
if role.tenant_id != auth_user.tenant_id:
|
||
raise CustomException(msg=f"角色 {role.name} 不属于当前租户")
|
||
else:
|
||
role_objs = []
|
||
|
||
for obj in user_objs:
|
||
relationship = obj.roles
|
||
relationship.clear()
|
||
relationship.extend(role_objs)
|
||
await self.db.flush()
|
||
|
||
async def set_user_positions(self, user_ids: list[int], position_ids: list[int]) -> None:
|
||
"""批量设置用户岗位(带租户隔离验证)
|
||
|
||
参数:
|
||
- user_ids (list[int]): 用户ID列表
|
||
- position_ids (list[int]): 岗位ID列表
|
||
|
||
返回:
|
||
- None
|
||
"""
|
||
user_objs = await self.get_list(search={"id": ("in", user_ids)})
|
||
if position_ids:
|
||
position_objs = await PositionCRUD(self.auth, self.db).get_list(search={"id": ("in", position_ids)})
|
||
auth_user = self.auth.user
|
||
if auth_user and not auth_user.is_superuser:
|
||
for position in position_objs:
|
||
if position.tenant_id != auth_user.tenant_id:
|
||
raise CustomException(msg=f"岗位 {position.name} 不属于当前租户")
|
||
else:
|
||
position_objs = []
|
||
|
||
for obj in user_objs:
|
||
relationship = obj.positions
|
||
relationship.clear()
|
||
relationship.extend(position_objs)
|
||
await self.db.flush()
|
||
|
||
async def change_password(self, id: int, password_hash: str) -> UserModel:
|
||
"""修改用户密码
|
||
|
||
参数:
|
||
- id (int): 用户ID
|
||
- password_hash (str): 密码哈希值
|
||
|
||
返回:
|
||
- UserModel: 更新后的用户信息
|
||
"""
|
||
return await self.update(id=id, data=UserUpdateSchema(password=password_hash))
|
||
|
||
async def forget_password(self, id: int, password_hash: str) -> UserModel:
|
||
"""重置密码(与 change_password 逻辑相同)"""
|
||
return await self.change_password(id=id, password_hash=password_hash)
|
||
|
||
async def bump_token_version(self, user_id: int) -> None:
|
||
"""递增指定用户的 token_version 字段,使所有现有 JWT 立即失效。
|
||
|
||
配合 invalidate_user_sessions(service 层调用)可在用户改密/重置/禁用时
|
||
同时清掉 Redis 中的活跃会话。
|
||
|
||
参数:
|
||
- user_id (int): 用户ID
|
||
"""
|
||
from sqlalchemy import update as sa_update
|
||
|
||
from .model import UserModel
|
||
|
||
await self.db.execute(
|
||
sa_update(UserModel)
|
||
.where(UserModel.id == user_id)
|
||
.values(token_version=UserModel.token_version + 1)
|
||
)
|
||
await self.db.flush()
|