""" app/models/workspace.py — workspaces / workspace_members / user_api_keys Alembic 002:多租户基础,全新建。 matrix_accounts 二期预留,一期不建。 """ from datetime import datetime from sqlalchemy import ( BigInteger, DateTime, Enum, ForeignKey, Index, String, UniqueConstraint, func, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from app.constants.enums import UserRole from app.core.database import Base class Workspace(Base): __tablename__ = "workspaces" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(128), nullable=False) slug: Mapped[str] = mapped_column(String(64), unique=True, nullable=False) is_active: Mapped[bool] = mapped_column(default=True, nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), nullable=False ) members: Mapped[list["WorkspaceMember"]] = relationship( back_populates="workspace", lazy="noload" ) class WorkspaceMember(Base): __tablename__ = "workspace_members" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) workspace_id: Mapped[int] = mapped_column( BigInteger, ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False ) user_id: Mapped[int] = mapped_column( BigInteger, ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) role: Mapped[str] = mapped_column( Enum(UserRole, values_callable=lambda x: [e.value for e in x]), nullable=False, ) created_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), nullable=False ) workspace: Mapped["Workspace"] = relationship(back_populates="members") __table_args__ = ( UniqueConstraint("workspace_id", "user_id", name="uq_workspace_member"), ) class UserApiKey(Base): """ 个人 API Key(Fernet 加密)。 encrypted_key 只存密文,不存 url(token站固定自家站)。 UNIQUE(user_id, workspace_id, provider)。 """ __tablename__ = "user_api_keys" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) user_id: Mapped[int] = mapped_column( BigInteger, ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) workspace_id: Mapped[int] = mapped_column( BigInteger, ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False ) provider: Mapped[str] = mapped_column(String(32), nullable=False) encrypted_key: Mapped[str] = mapped_column(String(512), nullable=False) key_last4: Mapped[str] = mapped_column(String(4), nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), nullable=False ) __table_args__ = ( UniqueConstraint( "user_id", "workspace_id", "provider", name="uq_user_workspace_provider", ), Index("idx_user_api_keys_workspace_id", "workspace_id"), )