""" 审核任务模型 """ from typing import TYPE_CHECKING, Optional from datetime import datetime from sqlalchemy import String, Integer, Float, Text, 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 TaskStatus(str, enum.Enum): """任务状态""" PENDING = "pending" PROCESSING = "processing" COMPLETED = "completed" FAILED = "failed" APPROVED = "approved" REJECTED = "rejected" class Platform(str, enum.Enum): """投放平台""" DOUYIN = "douyin" XIAOHONGSHU = "xiaohongshu" BILIBILI = "bilibili" KUAISHOU = "kuaishou" class ReviewTask(Base, TimestampMixin): """审核任务表 (AI 自动审核)""" __tablename__ = "review_tasks" 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, ) # 视频信息 video_url: Mapped[str] = mapped_column(String(2048), nullable=False) platform: Mapped[Platform] = mapped_column( SQLEnum(Platform, name="platform_enum"), nullable=False, ) brand_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True) creator_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True) # 审核状态 status: Mapped[TaskStatus] = mapped_column( SQLEnum(TaskStatus, name="task_status_enum"), default=TaskStatus.PENDING, nullable=False, index=True, ) progress: Mapped[int] = mapped_column(Integer, default=0, nullable=False) current_step: Mapped[str] = mapped_column(String(100), default="等待处理", nullable=False) # 审核结果 score: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # 违规详情 (JSON 数组) # [{"type": "forbidden_word", "content": "最好", "severity": "high", ...}] violations: Mapped[Optional[list]] = mapped_column(JSONType, nullable=True) # 软性风控提示 (JSON 数组) soft_warnings: Mapped[Optional[list]] = mapped_column(JSONType, nullable=True) # 审核要求 (JSON) requirements: Mapped[Optional[dict]] = mapped_column(JSONType, nullable=True) # 竞品列表 competitors: Mapped[Optional[list]] = mapped_column(JSONType, nullable=True) # 错误信息 error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # 关联 tenant: Mapped["Tenant"] = relationship("Tenant", back_populates="review_tasks") manual_task: Mapped[Optional["ManualTask"]] = relationship( "ManualTask", back_populates="review_task", uselist=False, ) def __repr__(self) -> str: return f"" class ManualTask(Base, TimestampMixin): """人工审核任务表""" __tablename__ = "manual_tasks" 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, ) review_task_id: Mapped[Optional[str]] = mapped_column( String(64), ForeignKey("review_tasks.id", ondelete="SET NULL"), nullable=True, index=True, ) # 视频信息 (冗余存储,即使关联的 review_task 被删除也能查看) video_url: Mapped[Optional[str]] = mapped_column(String(2048), nullable=True) video_uploaded_at: Mapped[Optional[datetime]] = mapped_column( DateTime(timezone=True), nullable=True, ) platform: Mapped[Platform] = mapped_column( SQLEnum(Platform, name="platform_enum", create_type=False), nullable=False, ) creator_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True) # 脚本信息 script_content: Mapped[Optional[str]] = mapped_column(Text, nullable=True) script_file_url: Mapped[Optional[str]] = mapped_column(String(2048), nullable=True) script_uploaded_at: Mapped[Optional[datetime]] = mapped_column( DateTime(timezone=True), nullable=True, ) # 任务状态 status: Mapped[TaskStatus] = mapped_column( SQLEnum(TaskStatus, name="task_status_enum", create_type=False), default=TaskStatus.PENDING, nullable=False, index=True, ) # 审批结果 approve_comment: Mapped[Optional[str]] = mapped_column(Text, nullable=True) reject_reason: Mapped[Optional[str]] = mapped_column(Text, nullable=True) reject_violations: Mapped[Optional[list]] = mapped_column(JSONType, nullable=True) # 审批人 reviewer_id: Mapped[Optional[str]] = mapped_column(String(64), nullable=True) reviewed_at: Mapped[Optional[datetime]] = mapped_column( DateTime(timezone=True), nullable=True, ) # 关联 tenant: Mapped["Tenant"] = relationship("Tenant", back_populates="manual_tasks") review_task: Mapped[Optional["ReviewTask"]] = relationship( "ReviewTask", back_populates="manual_task", ) def __repr__(self) -> str: return f""