""" 用户相关数据模型 """ from datetime import datetime from typing import List, Optional from sqlalchemy import ( Boolean, Column, DateTime, ForeignKey, Integer, String, Table, Text, UniqueConstraint, func, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from .base import Base, BaseModel, SoftDeleteMixin # 用户-团队关联表(用于多对多关系) user_teams = Table( "user_teams", BaseModel.metadata, Column( "user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True ), Column( "team_id", Integer, ForeignKey("teams.id", ondelete="CASCADE"), primary_key=True ), Column("role", String(50), default="member", nullable=False), # member, leader Column("joined_at", DateTime, server_default=func.now(), nullable=False), UniqueConstraint("user_id", "team_id", name="uq_user_team"), ) class UserTeam(Base): """用户团队关联模型(用于直接查询关联表)""" __allow_unmapped__ = True __table__ = user_teams # 重用已定义的表 # 定义列映射(不需要id,因为使用复合主键) user_id: Mapped[int] team_id: Mapped[int] role: Mapped[str] joined_at: Mapped[datetime] def __repr__(self) -> str: return f"" class User(BaseModel, SoftDeleteMixin): """用户模型""" __allow_unmapped__ = True __tablename__ = "users" # 基础信息 username: Mapped[str] = mapped_column(String(50), unique=True, nullable=False) email: Mapped[Optional[str]] = mapped_column(String(100), unique=True, nullable=True) phone: Mapped[Optional[str]] = mapped_column(String(20), unique=True, nullable=True) hashed_password: Mapped[str] = mapped_column( "password_hash", String(200), nullable=False ) # 个人信息 full_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) avatar_url: Mapped[Optional[str]] = mapped_column(String(500), nullable=True) bio: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # 性别: male/female(可扩展) gender: Mapped[Optional[str]] = mapped_column(String(10), nullable=True) # 学校 school: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) # 专业 major: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) # 企微员工userid(用于SCRM系统对接) wework_userid: Mapped[Optional[str]] = mapped_column(String(64), unique=True, nullable=True, comment="企微员工userid") # 系统角色:admin, manager, trainee role: Mapped[str] = mapped_column(String(20), default="trainee", nullable=False) # 账号状态 is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) is_verified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) # 时间记录 last_login_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) password_changed_at: Mapped[Optional[datetime]] = mapped_column( DateTime, nullable=True ) # 关联关系 teams: Mapped[List["Team"]] = relationship( "Team", secondary=user_teams, back_populates="members", lazy="selectin", ) exams = relationship("Exam", back_populates="user") # 岗位关系(通过关联表) position_memberships = relationship("PositionMember", back_populates="user", cascade="all, delete-orphan") def __repr__(self) -> str: return f"" class Team(BaseModel, SoftDeleteMixin): """团队模型""" __allow_unmapped__ = True __tablename__ = "teams" # 基础信息 name: Mapped[str] = mapped_column(String(100), unique=True, nullable=False) code: Mapped[str] = mapped_column(String(50), unique=True, nullable=False) description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # 团队类型:department, project, study_group team_type: Mapped[str] = mapped_column( String(50), default="department", nullable=False ) # 状态 is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) # 团队负责人 leader_id: Mapped[Optional[int]] = mapped_column( Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True ) # 父团队(支持层级结构) parent_id: Mapped[Optional[int]] = mapped_column( Integer, ForeignKey("teams.id", ondelete="CASCADE"), nullable=True ) # 关联关系 members: Mapped[List["User"]] = relationship( "User", secondary=user_teams, back_populates="teams", lazy="selectin", ) leader: Mapped[Optional["User"]] = relationship( "User", foreign_keys=[leader_id], lazy="selectin", ) parent: Mapped[Optional["Team"]] = relationship( "Team", remote_side="Team.id", foreign_keys=[parent_id], lazy="selectin", ) children: Mapped[List["Team"]] = relationship( "Team", back_populates="parent", lazy="selectin", ) def __repr__(self) -> str: return f""