deep-risk/backend/app/models/user.py
2025-12-14 20:08:27 +08:00

265 lines
7.9 KiB
Python
Raw Permalink 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.

"""
用户相关模型
"""
from datetime import datetime
from typing import List, Optional, TYPE_CHECKING
from sqlalchemy import (
Boolean,
Column,
DateTime,
Integer,
String,
Table,
Text,
ForeignKey,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import BaseModel
# 避免循环导入
if TYPE_CHECKING:
from .role import Role
# 用户角色关联表(多对多)
user_roles_table = Table(
"sys_user_role",
BaseModel.metadata,
Column("user_id", Integer, ForeignKey("sys_user.id"), primary_key=True),
Column("role_id", Integer, ForeignKey("sys_role.id"), primary_key=True),
)
class User(BaseModel):
"""
用户表
"""
__tablename__ = "sys_user"
username: Mapped[str] = mapped_column(
String(50), unique=True, index=True, nullable=False, comment="用户名"
)
password: Mapped[str] = mapped_column(String(255), nullable=False, comment="密码哈希")
nickname: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True, comment="昵称"
)
email: Mapped[Optional[str]] = mapped_column(
String(100), unique=True, index=True, nullable=True, comment="邮箱"
)
phone: Mapped[Optional[str]] = mapped_column(
String(20), nullable=True, comment="手机号"
)
avatar: Mapped[Optional[str]] = mapped_column(
String(255), nullable=True, comment="头像URL"
)
status: Mapped[bool] = mapped_column(
Boolean, default=True, comment="状态0-禁用1-正常"
)
is_superuser: Mapped[bool] = mapped_column(
Boolean, default=False, comment="是否超级用户"
)
last_login_time: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True, comment="最后登录时间"
)
last_login_ip: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True, comment="最后登录IP"
)
# 关联的企业实体IDMCN机构ID或主播ID
entity_id: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True, comment="关联的企业实体IDMCN机构或主播"
)
# 实体类型mcn-机构streamer-主播
entity_type: Mapped[Optional[str]] = mapped_column(
String(20), nullable=True, comment="关联实体类型mcn-机构streamer-主播"
)
# 关系
roles: Mapped[List["Role"]] = relationship(
"Role", secondary=user_roles_table, back_populates="users"
)
async def check_password(self, plain_password: str) -> bool:
"""
检查密码
"""
from app.utils.helpers import verify_password
return verify_password(plain_password, self.password)
async def set_password(self, plain_password: str) -> None:
"""
设置密码
"""
from app.utils.helpers import get_password_hash
self.password = get_password_hash(plain_password)
@classmethod
async def get_by_username(
cls, session, username: str
) -> Optional["User"]:
"""
根据用户名获取用户
"""
from sqlalchemy import select
result = await session.execute(select(cls).where(cls.username == username))
return result.scalar_one_or_none()
@classmethod
async def get_by_email(cls, session, email: str) -> Optional["User"]:
"""
根据邮箱获取用户
"""
from sqlalchemy import select
result = await session.execute(select(cls).where(cls.email == email))
return result.scalar_one_or_none()
async def has_role(self, role_name: str) -> bool:
"""
检查是否拥有特定角色
"""
return any(role.role_code == role_name for role in self.roles)
async def has_permission(self, permission: str) -> bool:
"""
检查是否拥有特定权限
"""
for role in self.roles:
for perm in role.permissions:
if perm.permission_code == permission:
return True
return False
class Role(BaseModel):
"""
角色表
"""
__tablename__ = "sys_role"
role_name: Mapped[str] = mapped_column(
String(50), nullable=False, comment="角色名称"
)
role_code: Mapped[str] = mapped_column(
String(50), unique=True, nullable=False, comment="角色编码"
)
description: Mapped[Optional[str]] = mapped_column(
Text, nullable=True, comment="角色描述"
)
status: Mapped[bool] = mapped_column(
Boolean, default=True, comment="状态0-禁用1-正常"
)
# 关系
users: Mapped[List[User]] = relationship(
"User", secondary=user_roles_table, back_populates="roles"
)
class Permission(BaseModel):
"""
权限表
"""
__tablename__ = "sys_permission"
permission_name: Mapped[str] = mapped_column(
String(50), nullable=False, comment="权限名称"
)
permission_code: Mapped[str] = mapped_column(
String(100), unique=True, nullable=False, comment="权限编码"
)
resource_type: Mapped[str] = mapped_column(
String(20), nullable=False, comment="资源类型menu-菜单button-按钮"
)
resource_path: Mapped[Optional[str]] = mapped_column(
String(100), nullable=True, comment="资源路径"
)
description: Mapped[Optional[str]] = mapped_column(
Text, nullable=True, comment="权限描述"
)
status: Mapped[bool] = mapped_column(
Boolean, default=True, comment="状态0-禁用1-正常"
)
class UserLoginLog(BaseModel):
"""
用户登录日志表
"""
__tablename__ = "sys_user_login_log"
user_id: Mapped[int] = mapped_column(
Integer, nullable=False, comment="用户ID"
)
username: Mapped[str] = mapped_column(
String(50), nullable=False, comment="用户名"
)
login_time: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=datetime.utcnow, comment="登录时间"
)
login_ip: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True, comment="登录IP"
)
user_agent: Mapped[Optional[str]] = mapped_column(
Text, nullable=True, comment="用户代理"
)
status: Mapped[bool] = mapped_column(
Boolean, default=True, comment="登录状态0-失败1-成功"
)
failure_reason: Mapped[Optional[str]] = mapped_column(
String(255), nullable=True, comment="失败原因"
)
class UserOperationLog(BaseModel):
"""
用户操作日志表
"""
__tablename__ = "sys_user_operation_log"
user_id: Mapped[int] = mapped_column(
Integer, nullable=False, comment="用户ID"
)
username: Mapped[str] = mapped_column(
String(50), nullable=False, comment="用户名"
)
module: Mapped[str] = mapped_column(
String(50), nullable=False, comment="模块名称"
)
operation: Mapped[str] = mapped_column(
String(100), nullable=False, comment="操作名称"
)
method: Mapped[Optional[str]] = mapped_column(
String(10), nullable=True, comment="请求方法"
)
path: Mapped[Optional[str]] = mapped_column(
String(200), nullable=True, comment="请求路径"
)
params: Mapped[Optional[Text]] = mapped_column(
Text, nullable=True, comment="请求参数"
)
result: Mapped[Optional[Text]] = mapped_column(
Text, nullable=True, comment="执行结果"
)
operation_time: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=datetime.utcnow, comment="操作时间"
)
ip_address: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True, comment="IP地址"
)
user_agent: Mapped[Optional[str]] = mapped_column(
Text, nullable=True, comment="用户代理"
)
status: Mapped[bool] = mapped_column(
Boolean, default=True, comment="操作状态0-失败1-成功"
)