hxf/backend/th_agenter/models/user.py

121 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""User model."""
from sqlalchemy import String, Boolean, Text
from sqlalchemy.orm import relationship, Mapped, mapped_column
from typing import List, Optional
from loguru import logger
from ..db.base import BaseModel
class User(BaseModel):
"""User model."""
__tablename__ = "users"
username: Mapped[str] = mapped_column(String(50), unique=True, index=True, nullable=False)
email: Mapped[str] = mapped_column(String(100), unique=True, index=True, nullable=False)
hashed_password: Mapped[str] = mapped_column(String(255), nullable=False)
full_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
avatar_url: Mapped[str | None] = mapped_column(String(255), nullable=True)
bio: Mapped[str | None] = mapped_column(Text, nullable=True)
# 关系 - 只保留角色关系
roles = relationship("Role", secondary="user_roles", back_populates="users")
def __repr__(self):
return f"<User(id={self.id}, username='{self.username}', email='{self.email}', full_name='{self.full_name}', is_active={self.is_active}, avatar_url='{self.avatar_url}', bio='{self.bio}', is_superuser={self.is_admin})>"
def to_dict(self, include_sensitive=False, include_roles=False):
"""Convert to dictionary, optionally excluding sensitive data."""
data = super().to_dict()
data.update({
'username': self.username,
'email': self.email,
'full_name': self.full_name,
'is_active': self.is_active,
'avatar_url': self.avatar_url,
'bio': self.bio,
'is_superuser': self.is_admin # 使用同步的 is_admin 属性代替异步的 is_superuser 方法
})
if not include_sensitive:
data.pop('hashed_password', None)
if include_roles:
try:
# 安全访问roles关系属性
data['roles'] = [role.to_dict() for role in self.roles if role.is_active]
except Exception:
# 如果角色关系未加载或访问出错,返回空列表
data['roles'] = []
return data
async def has_role(self, role_code: str) -> bool:
"""检查用户是否拥有指定角色."""
try:
# 在异步环境中,需要先加载关系属性
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import object_session
from sqlalchemy import select
from .permission import Role, UserRole
session = object_session(self)
if isinstance(session, AsyncSession):
# 如果是异步会话使用await加载关系
await session.refresh(self, ['roles'])
return any(role.code == role_code and role.is_active for role in self.roles)
except Exception:
# 如果对象已分离或加载关系失败,使用数据库查询
from sqlalchemy.orm import object_session
from sqlalchemy import select
from .permission import Role, UserRole
session = object_session(self)
if session is None:
# 如果没有会话返回False
return False
else:
from sqlalchemy.ext.asyncio import AsyncSession
if isinstance(session, AsyncSession):
# 如果是异步会话,使用异步查询
user_role = await session.execute(
select(UserRole).join(Role).filter(
UserRole.user_id == self.id,
Role.code == role_code,
Role.is_active == True
)
)
return user_role.scalar_one_or_none() is not None
else:
# 如果是同步会话,使用同步查询
user_role = session.query(UserRole).join(Role).filter(
UserRole.user_id == self.id,
Role.code == role_code,
Role.is_active == True
).first()
return user_role is not None
async def is_superuser(self) -> bool:
"""检查用户是否为超级管理员."""
return await self.has_role('SUPER_ADMIN')
async def is_admin_user(self) -> bool:
"""检查用户是否为管理员(兼容性方法)."""
return await self.is_superuser()
# 注意:属性方式的 is_admin 无法是异步的,所以我们改为同步方法并简化实现
@property
def is_admin(self) -> bool:
"""检查用户是否为管理员(属性方式)."""
# 同步属性无法使用 await所以我们只能检查已加载的角色
# 使用try-except捕获可能的MissingGreenlet错误
try:
# 检查角色关系是否已经加载
# 如果roles属性是一个InstrumentedList且已经加载那么它应该有__iter__方法
return any(role.code == 'SUPER_ADMIN' and role.is_active for role in self.roles)
except Exception:
# 如果角色关系未加载或访问出错返回False
return False