baseline: Clover 独立仓库首次基线提交
将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。 当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包), M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
151
backend/app/models/task.py
Normal file
151
backend/app/models/task.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
app/models/task.py — generation_tasks / text_candidates / image_candidates / delivery_packages
|
||||
Alembic 003 业务主体(2/3)
|
||||
任务主键:自增 BIGINT + mongo_trace_id VARCHAR(24)
|
||||
eval_score 留 NULL,不接 banana 假评分(基石)
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import (
|
||||
BigInteger, Boolean, DateTime, Enum, Float, ForeignKey,
|
||||
Index, Integer, String, Text, func,
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.constants.enums import (
|
||||
BannedWordStatus, CandidateSource, ImageRole,
|
||||
PackageStatus, ReviewStatus, TaskStatus,
|
||||
)
|
||||
from app.core.database import Base
|
||||
|
||||
|
||||
class GenerationTask(Base):
|
||||
"""生产任务,审核字段平铺(不建独立审核表)。"""
|
||||
__tablename__ = "generation_tasks"
|
||||
|
||||
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
|
||||
workspace_id: Mapped[int] = mapped_column(
|
||||
BigInteger, ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
product_id: Mapped[int] = mapped_column(
|
||||
BigInteger, ForeignKey("products.id"), nullable=False
|
||||
)
|
||||
operator_id: Mapped[int] = mapped_column(
|
||||
BigInteger, ForeignKey("users.id"), nullable=False
|
||||
)
|
||||
theme: Mapped[str | None] = mapped_column(String(256))
|
||||
text_count: Mapped[int] = mapped_column(Integer, default=5, nullable=False)
|
||||
image_count: Mapped[int] = mapped_column(Integer, default=3, nullable=False)
|
||||
track: Mapped[str] = mapped_column(String(16), default="ai", nullable=False)
|
||||
# 本次产品是否入镜:True=必须用产品参考图(无图禁生成,不降级纯文生图);False=允许纯文生图
|
||||
need_product_image: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
status: Mapped[str] = mapped_column(
|
||||
Enum(TaskStatus, values_callable=lambda x: [e.value for e in x]),
|
||||
default=TaskStatus.PENDING, nullable=False,
|
||||
)
|
||||
mongo_trace_id: Mapped[str | None] = mapped_column(String(24)) # MongoDB trace
|
||||
# ── 审核字段(平铺)──────────────────────────────
|
||||
review_status: Mapped[str | None] = mapped_column(
|
||||
Enum(ReviewStatus, values_callable=lambda x: [e.value for e in x])
|
||||
)
|
||||
reviewer_id: Mapped[int | None] = mapped_column(BigInteger, ForeignKey("users.id"))
|
||||
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime)
|
||||
reject_reason: Mapped[str | None] = mapped_column(Text)
|
||||
approved_at: Mapped[datetime | None] = mapped_column(DateTime)
|
||||
archived_at: Mapped[datetime | None] = mapped_column(DateTime)
|
||||
# 011: 第2环S12+第11环裂变
|
||||
benchmark_ids: Mapped[str | None] = mapped_column(Text, comment="关联标杆笔记ID列表(JSON list),第2环引用")
|
||||
source_fission_id: Mapped[int | None] = mapped_column(Integer, comment="裂变来源fission_task ID;NULL=普通任务")
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, server_default=func.now(), nullable=False
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, server_default=func.now(), onupdate=func.now(), nullable=False
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_generation_tasks_workspace_status", "workspace_id", "status"),
|
||||
)
|
||||
|
||||
|
||||
class TextCandidate(Base):
|
||||
"""文案候选(source=ai/import 区分双轨)。eval_score 留 NULL。"""
|
||||
__tablename__ = "text_candidates"
|
||||
|
||||
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
|
||||
workspace_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||||
task_id: Mapped[int] = mapped_column(
|
||||
BigInteger, ForeignKey("generation_tasks.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
source: Mapped[str] = mapped_column(
|
||||
Enum(CandidateSource, values_callable=lambda x: [e.value for e in x]),
|
||||
default=CandidateSource.AI, nullable=False,
|
||||
)
|
||||
angle_label: Mapped[str | None] = mapped_column(String(64))
|
||||
content: Mapped[str | None] = mapped_column(Text)
|
||||
score_json: Mapped[str | None] = mapped_column(Text) # 五维分 JSON
|
||||
banned_word_status: Mapped[str] = mapped_column(
|
||||
Enum(BannedWordStatus, values_callable=lambda x: [e.value for e in x]),
|
||||
default=BannedWordStatus.PASS, nullable=False,
|
||||
)
|
||||
eval_score: Mapped[float | None] = mapped_column(Float) # 一期留 NULL
|
||||
is_selected: Mapped[bool] = mapped_column(default=False, nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, server_default=func.now(), nullable=False
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_text_candidates_task_id", "task_id"),
|
||||
)
|
||||
|
||||
|
||||
class ImageCandidate(Base):
|
||||
"""图片候选。eval_score 留 NULL。"""
|
||||
__tablename__ = "image_candidates"
|
||||
|
||||
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
|
||||
workspace_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||||
task_id: Mapped[int] = mapped_column(
|
||||
BigInteger, ForeignKey("generation_tasks.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
# role 用 varchar 不用 enum:storyboard 角色名仍在演进,约束放应用层 ImageRole
|
||||
role: Mapped[str] = mapped_column(
|
||||
String(32), default=ImageRole.HOOK.value, nullable=False,
|
||||
)
|
||||
url: Mapped[str | None] = mapped_column(String(512))
|
||||
strategy: Mapped[str | None] = mapped_column(String(4)) # A/B/C(二期)
|
||||
seq: Mapped[int] = mapped_column(Integer, default=1) # 分镜序号
|
||||
is_selected: Mapped[bool] = mapped_column(default=False, nullable=False)
|
||||
eval_score: Mapped[float | None] = mapped_column(Float) # 一期留 NULL
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, server_default=func.now(), nullable=False
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_image_candidates_task_id", "task_id"),
|
||||
)
|
||||
|
||||
|
||||
class DeliveryPackage(Base):
|
||||
"""达人素材交付包。"""
|
||||
__tablename__ = "delivery_packages"
|
||||
|
||||
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
|
||||
workspace_id: Mapped[int] = mapped_column(
|
||||
BigInteger, ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
task_id: Mapped[int] = mapped_column(
|
||||
BigInteger, ForeignKey("generation_tasks.id"), nullable=False
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
Enum(PackageStatus, values_callable=lambda x: [e.value for e in x]),
|
||||
default=PackageStatus.PENDING, nullable=False,
|
||||
)
|
||||
package_path: Mapped[str | None] = mapped_column(String(512))
|
||||
download_url: Mapped[str | None] = mapped_column(String(512))
|
||||
expires_at: Mapped[datetime | None] = mapped_column(DateTime)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, server_default=func.now(), nullable=False
|
||||
)
|
||||
Reference in New Issue
Block a user