""" RBAC 角色相關 Models - UserRole: 租戶層級角色 (不綁定部門) - RoleRight: 角色對系統功能的 CRUD 權限 - UserRoleAssignment: 使用者角色分配 (直接對人,跨部門有效) """ from datetime import datetime from sqlalchemy import Column, Integer, String, Boolean, DateTime, Text, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import relationship from app.db.base import Base class UserRole(Base): """角色表 (租戶層級,不綁定部門)""" __tablename__ = "tenant_user_roles" __table_args__ = ( UniqueConstraint("tenant_id", "seq_no", name="uq_tenant_role_seq"), UniqueConstraint("tenant_id", "role_code", name="uq_tenant_role_code"), Index("idx_roles_tenant", "tenant_id"), ) id = Column(Integer, primary_key=True, index=True) tenant_id = Column(Integer, ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, comment="租戶 ID") seq_no = Column(Integer, nullable=False, comment="租戶內序號 (觸發器自動生成)") role_code = Column(String(100), nullable=False, comment="角色代碼 (租戶內唯一,例如 HR_ADMIN)") role_name = Column(String(200), nullable=False, comment="角色名稱") description = Column(Text, nullable=True, comment="角色說明") # 通用欄位 is_active = Column(Boolean, default=True, nullable=False, comment="是否啟用") edit_by = Column(String(100), nullable=True, comment="最後編輯者") created_at = Column(DateTime, default=datetime.utcnow, nullable=False, comment="建立時間") updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False, comment="更新時間") # 關聯 tenant = relationship("Tenant", back_populates="user_roles") rights = relationship("RoleRight", back_populates="role", cascade="all, delete-orphan", lazy="selectin") user_assignments = relationship("UserRoleAssignment", back_populates="role", cascade="all, delete-orphan", lazy="dynamic") def __repr__(self): return f"" class RoleRight(Base): """角色功能權限表 (Role and System Right)""" __tablename__ = "tenant_role_rights" __table_args__ = ( UniqueConstraint("role_id", "function_id", name="uq_role_function"), Index("idx_role_rights_role", "role_id"), Index("idx_role_rights_function", "function_id"), ) id = Column(Integer, primary_key=True, index=True) role_id = Column(Integer, ForeignKey("tenant_user_roles.id", ondelete="CASCADE"), nullable=False, comment="角色 ID") function_id = Column(Integer, ForeignKey("system_functions_cache.id", ondelete="CASCADE"), nullable=False, comment="系統功能 ID") can_read = Column(Boolean, default=False, nullable=False, comment="查詢權限") can_create = Column(Boolean, default=False, nullable=False, comment="新增權限") can_update = Column(Boolean, default=False, nullable=False, comment="修改權限") can_delete = Column(Boolean, default=False, nullable=False, comment="刪除權限") # 通用欄位 is_active = Column(Boolean, default=True, nullable=False, comment="是否啟用") edit_by = Column(String(100), nullable=True, comment="最後編輯者") created_at = Column(DateTime, default=datetime.utcnow, nullable=False, comment="建立時間") updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False, comment="更新時間") # 關聯 role = relationship("UserRole", back_populates="rights") function = relationship("SystemFunctionCache") def __repr__(self): perms = [] if self.can_read: perms.append("R") if self.can_create: perms.append("C") if self.can_update: perms.append("U") if self.can_delete: perms.append("D") return f"" class UserRoleAssignment(Base): """使用者角色分配表 (直接對人,跨部門有效)""" __tablename__ = "tenant_user_role_assignments" __table_args__ = ( UniqueConstraint("keycloak_user_id", "role_id", name="uq_user_role"), Index("idx_user_roles_tenant", "tenant_id"), Index("idx_user_roles_keycloak", "keycloak_user_id"), ) id = Column(Integer, primary_key=True, index=True) tenant_id = Column(Integer, ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, comment="租戶 ID") keycloak_user_id = Column(String(36), nullable=False, comment="Keycloak User UUID (永久識別碼)") role_id = Column(Integer, ForeignKey("tenant_user_roles.id", ondelete="CASCADE"), nullable=False, comment="角色 ID") # 審計欄位(完整記錄) assigned_at = Column(DateTime, default=datetime.utcnow, nullable=False, comment="分配時間") assigned_by = Column(String(36), nullable=True, comment="分配者 keycloak_user_id") revoked_at = Column(DateTime, nullable=True, comment="撤銷時間(軟刪除)") revoked_by = Column(String(36), nullable=True, comment="撤銷者 keycloak_user_id") # 通用欄位 is_active = Column(Boolean, default=True, nullable=False, comment="是否啟用") edit_by = Column(String(36), nullable=True, comment="最後編輯者 keycloak_user_id") created_at = Column(DateTime, default=datetime.utcnow, nullable=False, comment="建立時間") updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False, comment="更新時間") # 關聯 role = relationship("UserRole", back_populates="user_assignments") def __repr__(self): return f""