""" 特例审批模型 """ from typing import TYPE_CHECKING, Optional from datetime import datetime from sqlalchemy import String, Text, Boolean, ForeignKey, DateTime, Enum as SQLEnum from app.models.types import JSONType from sqlalchemy.orm import Mapped, mapped_column, relationship import enum from app.models.base import Base, TimestampMixin if TYPE_CHECKING: from app.models.tenant import Tenant class RiskTargetType(str, enum.Enum): """特例目标类型""" INFLUENCER = "influencer" ORDER = "order" CONTENT = "content" class RiskExceptionStatus(str, enum.Enum): """特例审批状态""" PENDING = "pending" APPROVED = "approved" REJECTED = "rejected" EXPIRED = "expired" REVOKED = "revoked" class RiskException(Base, TimestampMixin): """特例审批表""" __tablename__ = "risk_exceptions" id: Mapped[str] = mapped_column(String(64), primary_key=True) tenant_id: Mapped[str] = mapped_column( String(64), ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True, ) # 申请信息 applicant_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True) apply_time: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, ) # 目标信息 target_type: Mapped[RiskTargetType] = mapped_column( SQLEnum(RiskTargetType, name="risk_target_type_enum"), nullable=False, ) target_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True) risk_rule_id: Mapped[str] = mapped_column(String(64), nullable=False) # 状态 status: Mapped[RiskExceptionStatus] = mapped_column( SQLEnum(RiskExceptionStatus, name="risk_exception_status_enum"), default=RiskExceptionStatus.PENDING, nullable=False, index=True, ) # 有效期 valid_start_time: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, ) valid_end_time: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, ) # 申请原因 reason_category: Mapped[str] = mapped_column(String(100), nullable=False) justification: Mapped[str] = mapped_column(Text, nullable=False) attachment_url: Mapped[Optional[str]] = mapped_column(String(2048), nullable=True) # 审批信息 current_approver_id: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) # 审批流转日志 (JSON 数组) # [{"approver_id": "...", "action": "approve/reject", "comment": "...", "timestamp": "..."}] approval_chain_log: Mapped[list] = mapped_column(JSONType, default=list, nullable=False) # 驳回信息 auto_rejected: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) rejection_reason: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # 最近状态变更时间 last_status_at: Mapped[Optional[datetime]] = mapped_column( DateTime(timezone=True), nullable=True, ) # 关联 tenant: Mapped["Tenant"] = relationship("Tenant", back_populates="risk_exceptions") def __repr__(self) -> str: return f""