"""admin 角色 → 可见页面(权限)映射表。 RBAC 的「角色」侧:每个角色持有一组「页面 key」(= 左侧导航项),决定该角色登录后台后左边能看到 哪些页。super_admin 是内建全权角色(is_builtin=True),恒可见全部页(effective_pages 里特判, 不依赖本表存的 pages)、不可编辑/删除。其余角色(含 operator/finance)可由 super_admin 增删改。 admin_user.role 存的是本表的 name(字符串弱引用,不建外键——与既有 require_role 字符串口径一致; 删除在用角色由业务层拦截,见 routers/roles.py)。页面 key 清单见 app/admin/permissions.py。 """ from __future__ import annotations from datetime import datetime from sqlalchemy import JSON, Boolean, DateTime, Integer, String, func from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.orm import Mapped, mapped_column from app.db.base import Base _JSON = JSON().with_variant(JSONB(), "postgresql") class AdminRole(Base): __tablename__ = "admin_role" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) # 角色标识 key(不可变,承重):admin_user.role 引用它、require_role 按它鉴权。 # 内建 = super_admin/operator/finance/tech(英文,勿改);自定义 = 创建时的名称。 name: Mapped[str] = mapped_column(String(32), unique=True, index=True, nullable=False) # 展示名(可改):内建 = 管理员/运营/财务/技术;自定义默认 = name。UI 一律展示 label。 label: Mapped[str] = mapped_column(String(32), nullable=False, default="") # 该角色可见页面 key 列表(= 左侧导航项),见 app/admin/permissions.py 的 PERMISSION_CATALOG pages: Mapped[list] = mapped_column(_JSON, nullable=False, default=list) # 内建角色(当前仅 super_admin):不可编辑/删除,恒全权 is_builtin: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) def __repr__(self) -> str: # pragma: no cover return f""