Files
beige/backend/app/models/product.py
yangqianqian 6a2632da70 baseline: Clover 独立仓库首次基线提交
将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。
当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包),
M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:30:22 +08:00

111 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
app/models/product.py — products / benchmark_notes / banned_words
Alembic 003 业务主体1/3
products.category 是纯数据字段禁止任何品类枚举基石A
"""
from datetime import datetime
from typing import Optional
from sqlalchemy import (
BigInteger, DateTime, Enum, ForeignKey,
Index, Integer, String, Text, func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.constants.enums import BannedWordLevel, ProductSource
from app.core.database import Base
class Product(Base):
"""产品档案(卖点/违禁词/风格/调性/文案角度/可调prompt/source"""
__tablename__ = "products"
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
)
name: Mapped[str] = mapped_column(String(128), nullable=False)
# category 是纯数据字段不在代码里做枚举基石A
category: Mapped[str | None] = mapped_column(String(64))
source: Mapped[str] = mapped_column(
Enum(ProductSource, values_callable=lambda x: [e.value for e in x]),
default=ProductSource.CUSTOM, nullable=False,
)
selling_points: Mapped[str | None] = mapped_column(Text) # JSON数组
style_tone: Mapped[str | None] = mapped_column(String(128))
text_angles: Mapped[str | None] = mapped_column(Text) # JSON数组用户设定
custom_prompt: Mapped[str | None] = mapped_column(Text) # 等北哥方案注入
image_path: Mapped[str | None] = mapped_column(String(512)) # 产品参考图路径(前端上传后写入)
# 008: 品牌词(客户输入,植入文案每条+生图特写图第2/6张第5环
brand_keyword: Mapped[str | None] = mapped_column(String(64), comment="品牌词,客户录入,随产品固定")
# 012: 目标人群(客户输入,透传进文案/生图 promptstoryboard.py:52 原恒空现可填)
target_audience: Mapped[str | None] = mapped_column(String(128), comment="目标人群,客户输入,透传进文案/生图prompt")
is_active: Mapped[bool] = mapped_column(default=True, nullable=False)
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
)
benchmark_notes: Mapped[list["BenchmarkNote"]] = relationship(
back_populates="product", lazy="noload"
)
__table_args__ = (
Index("idx_products_workspace_id", "workspace_id"),
)
class BenchmarkNote(Base):
"""标杆笔记(截图+手填亮点为主通道)"""
__tablename__ = "benchmark_notes"
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", ondelete="CASCADE"), nullable=False
)
screenshot_url: Mapped[str | None] = mapped_column(String(512))
highlights: Mapped[str | None] = mapped_column(Text) # 手填亮点
link_url: Mapped[str | None] = mapped_column(String(512))
# 009: 第2环标杆分析字段
features_json: Mapped[str | None] = mapped_column(Text, comment="爆款8特征分析结果JSONAI解析后写入")
analyze_status: Mapped[str] = mapped_column(String(20), default="pending", nullable=False, comment="AI分析状态: pending/analyzing/done/failed")
created_at: Mapped[datetime] = mapped_column(
DateTime, server_default=func.now(), nullable=False
)
product: Mapped["Product"] = relationship(back_populates="benchmark_notes")
__table_args__ = (
Index("idx_benchmark_notes_product_id", "product_id"),
)
class BannedWord(Base):
"""违禁词库三级auto_fix/soft_warn/hard_block"""
__tablename__ = "banned_words"
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
)
word: Mapped[str] = mapped_column(String(64), nullable=False)
level: Mapped[str] = mapped_column(
Enum(BannedWordLevel, values_callable=lambda x: [e.value for e in x]),
nullable=False,
)
replacement: Mapped[str | None] = mapped_column(String(128))
updatable: Mapped[bool] = mapped_column(default=True, nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime, server_default=func.now(), nullable=False
)
__table_args__ = (
Index("idx_banned_words_workspace_id", "workspace_id"),
)