"""身份与权限模型(平台层,三种部署形态共用)。 从旧 shared/models/database.py 拆出(D3,2026-09-17)。 原 User.stp_files ↔ STPFile.user 跨模块 relationship 已删除(无使用方): 用户与 STP 文件的关联走 STPFile.user_id 裸 FK,查询由 moldinsight 侧显式 select。 """ from sqlalchemy import Column, Integer, String, Text, DateTime, Boolean, ForeignKey, JSON from sqlalchemy.sql import func from sqlalchemy.orm import relationship from shared.models.base import Base class User(Base): """用户表""" __tablename__ = "users" __excluded_fields__ = {'hashed_password'} id = Column(Integer, primary_key=True, index=True) username = Column(String(50), unique=True, index=True, nullable=False) email = Column(String(255), unique=True, index=True, nullable=False) hashed_password = Column(String(255), nullable=False) full_name = Column(String(100)) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=func.now()) last_login = Column(DateTime, nullable=True) user_roles = relationship("UserRole", back_populates="user", cascade="all, delete-orphan") @property def roles(self): return [ur.role for ur in self.user_roles] @property def is_superuser(self): return any(r.code == 'admin' for r in self.roles) def has_permission(self, permission_code: str) -> bool: if self.is_superuser: return True for role in self.roles: for perm in role.permissions: if perm.code == permission_code: return True return False def safe_dict(self): return {k: v for k, v in self.__dict__.items() if not k.startswith('_') and k not in self.__excluded_fields__} def __repr__(self): return f"" class Role(Base): """角色表""" __tablename__ = "roles" id = Column(Integer, primary_key=True, index=True) code = Column(String(50), unique=True, index=True, nullable=False) name = Column(String(100), nullable=False) description = Column(Text, nullable=True) is_system = Column(Boolean, default=False) created_at = Column(DateTime, default=func.now()) user_roles = relationship("UserRole", back_populates="role", cascade="all, delete-orphan") role_permissions = relationship("RolePermission", back_populates="role", cascade="all, delete-orphan") @property def permissions(self): return [rp.permission for rp in self.role_permissions] def __repr__(self): return f"" class Permission(Base): """权限表""" __tablename__ = "permissions" id = Column(Integer, primary_key=True, index=True) code = Column(String(100), unique=True, index=True, nullable=False) name = Column(String(100), nullable=False) module = Column(String(50), nullable=True) description = Column(Text, nullable=True) created_at = Column(DateTime, default=func.now()) role_permissions = relationship("RolePermission", back_populates="permission", cascade="all, delete-orphan") def __repr__(self): return f"" class UserRole(Base): """用户角色关联表""" __tablename__ = "user_roles" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True) role_id = Column(Integer, ForeignKey("roles.id"), nullable=False, index=True) created_at = Column(DateTime, default=func.now()) user = relationship("User", back_populates="user_roles") role = relationship("Role", back_populates="user_roles") def __repr__(self): return f"" class RolePermission(Base): """角色权限关联表""" __tablename__ = "role_permissions" id = Column(Integer, primary_key=True, index=True) role_id = Column(Integer, ForeignKey("roles.id"), nullable=False, index=True) permission_id = Column(Integer, ForeignKey("permissions.id"), nullable=False, index=True) created_at = Column(DateTime, default=func.now()) role = relationship("Role", back_populates="role_permissions") permission = relationship("Permission", back_populates="role_permissions") def __repr__(self): return f"" class UserActivity(Base): """用户活动日志表""" __tablename__ = "user_activities" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True) # 活动信息 activity_type = Column(String(50), nullable=False, index=True) # upload, view, download, delete, export resource_type = Column(String(50), nullable=True) # stp_file, geometry_data, mold_cavity resource_id = Column(Integer, nullable=True) # 活动详情 description = Column(Text, nullable=True) meta_data = Column(JSON, nullable=True) # 时间戳 created_at = Column(DateTime, default=func.now(), index=True) # IP和设备信息 ip_address = Column(String(45), nullable=True) user_agent = Column(String(500), nullable=True) def __repr__(self): return f"" class SystemLog(Base): """系统日志表(重要操作和错误)""" __tablename__ = "system_logs" id = Column(Integer, primary_key=True, index=True) # 日志级别 level = Column(String(20), nullable=False, index=True) # INFO, WARNING, ERROR, CRITICAL # 日志信息 message = Column(Text, nullable=False) module = Column(String(100), nullable=True) # 模块名 function_name = Column(String(100), nullable=True) # 时间戳 created_at = Column(DateTime, default=func.now(), index=True) # 用户信息(如果有关联用户) user_id = Column(Integer, ForeignKey("users.id"), nullable=True) # 额外信息 request_id = Column(String(100), nullable=True) # 关联的请求ID execution_time_ms = Column(Integer, nullable=True) # 执行时间 # 关联数据 resource_type = Column(String(50), nullable=True) resource_id = Column(Integer, nullable=True) def __repr__(self): return f""