Your Name 0ef7650c09 feat: 审核体系全面改造 — 多维度评分 + 卖点优先级 + AI 语义匹配 + 品牌方 AI 状态通知
后端:
- 审核结果拆分为 4 个独立维度 (法规合规/平台规则/品牌安全/Brief匹配度)
- 卖点优先级从 required:bool 改为三级 (core/recommended/reference)
- AI 语义匹配卖点覆盖 + AI 整体 Brief 匹配度分析
- BriefMatchDetail 评分详情 (覆盖率+亮点+问题点)
- min_selling_points 代理商可配置最少卖点数 + Alembic 迁移
- AI 语境复核过滤误报
- Brief AI 解析 + 规则 AI 解析
- AI 未配置/异常时通知品牌方
- 种子数据更新 (新格式审核结果+brief_match_detail)

前端:
- 三端审核页面展示四维度评分卡片
- 卖点编辑改为三级优先级选择器
- BriefMatchDetail 展示 (覆盖率进度条+亮点+问题)
- min_selling_points 配置 UI
- AI 配置页未配置时静默处理
- 文件预览/下载/签名 URL 优化

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 19:11:54 +08:00

68 lines
2.5 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.

"""
Brief 模型
"""
from typing import TYPE_CHECKING, Optional
from sqlalchemy import String, Text, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import Base, TimestampMixin
from app.models.types import JSONType
if TYPE_CHECKING:
from app.models.project import Project
class Brief(Base, TimestampMixin):
"""Brief 文档表"""
__tablename__ = "briefs"
id: Mapped[str] = mapped_column(String(64), primary_key=True)
project_id: Mapped[str] = mapped_column(
String(64),
ForeignKey("projects.id", ondelete="CASCADE"),
unique=True,
nullable=False,
index=True,
)
# 原始文件
file_url: Mapped[Optional[str]] = mapped_column(String(2048), nullable=True)
file_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
# 解析后的结构化内容
# 卖点要求: [{"content": "SPF50+", "priority": "core"}, ...]
selling_points: Mapped[Optional[list]] = mapped_column(JSONType, nullable=True)
# 代理商要求至少体现的卖点条数0 或 None 表示不限制)
min_selling_points: Mapped[Optional[int]] = mapped_column(nullable=True)
# 违禁词: [{"word": "最好", "reason": "绝对化用语"}, ...]
blacklist_words: Mapped[Optional[list]] = mapped_column(JSONType, nullable=True)
# 竞品: ["竞品A", "竞品B", ...]
competitors: Mapped[Optional[list]] = mapped_column(JSONType, nullable=True)
# 品牌调性要求
brand_tone: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# 时长要求(秒)
min_duration: Mapped[Optional[int]] = mapped_column(nullable=True)
max_duration: Mapped[Optional[int]] = mapped_column(nullable=True)
# 其他要求(自由文本)
other_requirements: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# 附件文档(品牌方上传的参考资料)
# [{"id": "af1", "name": "达人拍摄指南.pdf", "url": "...", "size": "1.5MB"}, ...]
attachments: Mapped[Optional[list]] = mapped_column(JSONType, nullable=True)
# 代理商附件(代理商上传的补充资料,与品牌方 attachments 分开存储)
# [{"id": "af1", "name": "达人拍摄指南.pdf", "url": "...", "size": "1.5MB"}, ...]
agency_attachments: Mapped[Optional[list]] = mapped_column(JSONType, nullable=True)
# 关联
project: Mapped["Project"] = relationship("Project", back_populates="brief")
def __repr__(self) -> str:
return f"<Brief(id={self.id}, project_id={self.project_id})>"