feat(monitor): 作品标签 —— 词表在设置里维护,作品上贴
Deploy VitePress site to Pages / build (push) Canceled after 0s
Deploy VitePress site to Pages / Deploy (push) Canceled after 0s

和「作品备注」刻意并存,不是一回事:备注是一句话的自由文字,标签是**从一份固定
词表里选的分类**。分类必须封闭,否则「重点 / 重要 / 优先」各写各的,筛就没法用了。

* `monitor_tag`:词表,全局一套(不按平台分)—— 「重点」是给人自己用的心智,不该在
  小红书和抖音各定义一遍。颜色存的是**调色板里的名字**,不是色值:Tailwind 的类名是
  静态提取的,拼出来的 `text-${color}` 它看不见,线上会静默变无色。
* `monitor_note_tag`:作品↔标签,**多对多**。一条作品可以既是重点又是竞品;只能贴一个
  的话人就会跑去备注里写自由文字,这份词表就白建了。全部替换式提交,不是逐个增删。
* 删除标签时**显式删关联**,不靠外键级联 —— 测试跑 SQLite,它默认不开外键约束。
* 组头上的筛选做成平铺开关片,多选是「或」。

顺带:/monitor/notes 现在支持按 tag_id 筛。
This commit is contained in:
2026-10-11 09:16:17 +08:00
parent 5c7577cab0
commit b9aa590814
15 changed files with 1147 additions and 10 deletions
+44
View File
@@ -405,6 +405,50 @@ class MonitorNoteAlias(MonitorBase):
updated_at: Mapped[int] = mapped_column(BigInteger, nullable=False)
class MonitorTag(MonitorBase):
"""作品标签的**词表** —— 在设置里维护,贴标签时从这里选。
刻意做成一张表而不是一个设置项:设置里那个注册表存的是标量(一个数、一段文字),
而这是一组能增能删能改名的条目。
**全局一套,不按平台分。** 「重点」「竞品」这种分类是给人自己用的心智,不该在小红书
和抖音各定义一遍;而且同一个词在两个平台上含义不同的话,反而不好比。
"""
__tablename__ = "monitor_tag"
__table_args__ = (UniqueConstraint("name", name="uq_tag_name"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String(32), nullable=False)
# 一个**调色板里的名字**(cyan/pink/green/…),不是颜色值。Tailwind 的类名是静态
# 提取的,拼不出 `text-${color}` 那种动态类,所以这里存的是查表用的键。
color: Mapped[str] = mapped_column(String(16), nullable=False, default="")
sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
created_at: Mapped[int] = mapped_column(BigInteger, nullable=False)
class MonitorNoteTag(MonitorBase):
"""作品 ↔ 标签。
键取 ``(platform, note_id)``,和作品备注同一个口径:作品 id 本身带平台语义,但显式
带上 platform 才能和别处用同一套查询形状。
**多对多**:一条作品可以既是「重点」又是「竞品」—— 标签本来就是可以叠加的分类,
一个不够用的话人就会去备注里写自由文字,那这个功能就白做了。
"""
__tablename__ = "monitor_note_tag"
__table_args__ = (
UniqueConstraint("platform", "note_id", "tag_id", name="uq_note_tag"),
)
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
platform: Mapped[str] = mapped_column(String(16), nullable=False, index=True)
note_id: Mapped[str] = mapped_column(String(128), nullable=False, index=True)
tag_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
created_at: Mapped[int] = mapped_column(BigInteger, nullable=False)
class MonitorSetting(MonitorBase):
"""Key/value store. Holds the XHS cookie for unattended runs."""