from sqlalchemy import Boolean, Column, ForeignKey, String, Table, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base from app.models.base import TimestampMixin role_permission = Table( "core_role_permission", Base.metadata, Column("role_id", String(32), ForeignKey("core_role.id"), primary_key=True), Column("permission_id", String(32), ForeignKey("core_permission.id"), primary_key=True), ) user_role = Table( "core_user_role", Base.metadata, Column("user_id", String(32), ForeignKey("core_user.id"), primary_key=True), Column("role_id", String(32), ForeignKey("core_role.id"), primary_key=True), ) class User(TimestampMixin, Base): __tablename__ = "core_user" email: Mapped[str] = mapped_column(String(120), unique=True, index=True) username: Mapped[str] = mapped_column(String(80), unique=True, index=True) password_hash: Mapped[str] = mapped_column(String(200)) nickname: Mapped[str] = mapped_column(String(80), default="") status: Mapped[str] = mapped_column(String(20), default="enabled") is_superuser: Mapped[bool] = mapped_column(Boolean, default=False) roles: Mapped[list["Role"]] = relationship("Role", secondary=user_role, back_populates="users") class Role(TimestampMixin, Base): __tablename__ = "core_role" name: Mapped[str] = mapped_column(String(80), unique=True) code: Mapped[str] = mapped_column(String(80), unique=True, index=True) description: Mapped[str] = mapped_column(Text, default="") status: Mapped[str] = mapped_column(String(20), default="enabled") users: Mapped[list[User]] = relationship("User", secondary=user_role, back_populates="roles") permissions: Mapped[list["Permission"]] = relationship("Permission", secondary=role_permission) class Permission(TimestampMixin, Base): __tablename__ = "core_permission" name: Mapped[str] = mapped_column(String(100)) code: Mapped[str] = mapped_column(String(120), unique=True, index=True) resource: Mapped[str] = mapped_column(String(80), default="") action: Mapped[str] = mapped_column(String(40), default="") class Menu(TimestampMixin, Base): __tablename__ = "core_menu" parent_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True) title: Mapped[str] = mapped_column(String(100)) path: Mapped[str] = mapped_column(String(200), default="") icon: Mapped[str] = mapped_column(String(100), default="") permission_code: Mapped[str] = mapped_column(String(120), default="") visible: Mapped[bool] = mapped_column(Boolean, default=True) class Announcement(TimestampMixin, Base): __tablename__ = "core_announcement" title: Mapped[str] = mapped_column(String(160)) content: Mapped[str] = mapped_column(Text, default="") status: Mapped[str] = mapped_column(String(20), default="draft")