主要更新: - 更新代理商端文档,明确项目由品牌方分配流程 - 新增Brief配置详情页(已配置)设计稿 - 完善工作台紧急待办中品牌新任务功能 - 整理Pencil设计文件中代理商端页面顺序 - 新增后端FastAPI框架及核心API - 新增前端Next.js页面和组件库 - 添加.gitignore排除构建和缓存文件 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
165 lines
5.4 KiB
Python
165 lines
5.4 KiB
Python
"""
|
|
审核任务模型
|
|
"""
|
|
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"<ReviewTask(id={self.id}, status={self.status})>"
|
|
|
|
|
|
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"<ManualTask(id={self.id}, status={self.status})>"
|