Your Name 4c9b2f1263 feat: Brief附件/项目平台/规则AI解析/消息中心修复 + 项目创建通知
- Brief 支持代理商附件上传 (迁移 007)
- 项目新增 platform 字段 (迁移 008),前端创建/展示平台信息
- 修复 AI 规则解析:处理中文引号导致 JSON 解析失败的问题
- 修复消息中心崩溃:补全后端消息类型映射 + fallback 保护
- 项目创建时自动发送消息通知
- .gitignore 排除 backend/data/ 数据库文件

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

65 lines
2.4 KiB
Python

"""
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+", "required": true}, ...]
selling_points: Mapped[Optional[list]] = mapped_column(JSONType, 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})>"