Browse Source

docs(spec): add post tags and list filtering design

Define a unified design for mixed-mode post tags and backend URL-persistent filtering across all post list pages, including OR/AND semantics, data model migration, API contracts, and rollout/testing strategy.

Made-with: Cursor
main
npmrun 2 weeks ago
parent
commit
c619d7bd86
  1. 269
      docs/superpowers/specs/2026-04-25-post-tags-and-list-filter-design.md

269
docs/superpowers/specs/2026-04-25-post-tags-and-list-filter-design.md

@ -0,0 +1,269 @@
# 文章标签与列表筛选设计
日期:2026-04-25
状态:已评审(待实现计划)
作者:Codex(与用户协作确认)
## 1. 背景与目标
当前系统已有文章能力与多个文章列表页面,但标签能力未形成完整闭环:
- 文章创建/编辑页未提供稳定的标签编辑体验。
- 各文章列表页缺乏统一标签筛选能力。
- 筛选状态无法通过 URL 持久化,导致刷新、分享、回退体验不完整。
本设计目标是为文章系统增加“标签 + 统一筛选”能力,并覆盖所有文章列表页面,满足以下要求:
- 标签体系:混合模式(可选已有标签 + 允许新建标签)。
- 筛选方式:支持多标签 OR/AND 切换。
- 筛选执行:后端查询筛选,前端通过 URL 参数持久化。
- 范围:覆盖“各个文章列表”页面,而非仅单页。
## 2. 范围定义
### 2.1 In Scope
- 文章新增/编辑支持标签输入与保存。
- 文章详情接口返回标准化标签列表用于回填。
- 各文章列表接口支持按标签筛选(含分页联动)。
- 各文章列表页面提供统一筛选交互(多选标签 + OR/AND)。
- URL 参数承载筛选状态,支持刷新/分享/前进后退。
- 数据模型升级为标准化标签关系模型,并提供迁移回填策略。
### 2.2 Out of Scope
- 独立“标签管理后台”(全量管理、批量操作)。
- 标签重命名、合并、别名管理工具。
- 标签统计分析与推荐系统。
## 3. 关键决策与取舍
### 3.1 方案选择
已确认采用“标准化关系模型”方案:
- 新增 `tags` + `post_tags` 多对多关系。
- `posts.tagsJson` 暂保留作兼容过渡,不作为长期主模型。
原因:
- 能稳定支撑 OR/AND、分页总数一致、标签池聚合等中长期需求。
- 优于继续在 `tagsJson` 上做字符串查询的性能与可维护性。
### 3.2 筛选语义
- `tagMode=or`:命中任一所选标签即返回。
- `tagMode=and`:必须同时命中全部所选标签才返回。
默认值为 `or`,非法值自动回退到 `or`
## 4. 数据模型设计
## 4.1 新增表
- `tags`
- `id`(PK)
- `userId`(所属用户)
- `name`(展示名)
- `slug`(规范化匹配键)
- `createdAt`
- `updatedAt`
- `post_tags`
- `postId`
- `tagId`
- `createdAt`
## 4.2 约束与索引
- `tags`:唯一约束 `(userId, slug)`,防止同一用户重复标签。
- `post_tags`:唯一约束 `(postId, tagId)`,防止重复关联。
- 索引:
- `post_tags(postId)`
- `post_tags(tagId)`
- 视实现需要可补充复合索引以优化 AND 查询。
## 4.3 兼容策略
- 过渡期保留 `posts.tagsJson`
- 写入以新关系表为主。
- 可选同步回写 `tagsJson` 供旧逻辑读取。
- 新接口返回 `tags: string[]`,逐步替代 `tagsJson` 在前端的使用。
- 稳定后再评估移除 `tagsJson` 的窗口与步骤。
## 5. 标签规范
## 5.1 输入规则
- 允许用户从“已有标签池”选择。
- 允许直接输入新标签并即时创建。
- 单篇文章可设置多个标签。
## 5.2 归一化规则
写入前统一归一化:
- 去除首尾空白。
- 合并中间连续空白为单空格。
- 生成匹配用 `slug`(用于唯一约束与筛选匹配)。
- 展示名保留用户输入风格(在不违反校验的前提下)。
## 5.3 校验规则
- 空标签忽略。
- 单标签长度上限:32 个字符。
- 单篇文章标签数量上限:10 个。
- 标签字符集:中文、英文字母、数字、空格、短横线(`-`)、下划线(`_`)。
## 6. API 设计
## 6.1 写接口
- `POST /api/me/posts`
- `PUT /api/me/posts/:id`
请求体增加:
- `tags: string[]`
服务端流程:
1. 归一化与校验 `tags`
2. 对标签执行用户维度 upsert(`tags` 表)。
3. 在事务内重建文章标签关系(`post_tags`)。
4. 返回标准化 `tags` 结果。
## 6.2 详情接口
- `GET /api/me/posts/:id`
返回补充:
- `tags: string[]`(用于编辑页回填)
## 6.3 列表接口(统一筛选)
- 我的文章:
- `GET /api/me/posts?page=...&tags=tag-a,tag-b&tagMode=or|and`
- 公开文章:
- `GET /api/public/profile/:publicSlug/posts?page=...&tags=...&tagMode=...`
返回补充:
- `availableTags: string[]`(当前主体可用标签池)
- `selectedTags: string[]`(本次生效筛选)
- `tagMode: "or" | "and"`(本次生效模式)
注意:
- `total``items` 必须共用完全一致的 where 条件,防止分页错位。
## 7. 查询逻辑
## 7.1 OR 查询
返回包含任一所选标签的文章。
## 7.2 AND 查询
返回同时包含全部所选标签的文章。推荐通过“按文章分组 + 命中标签数等于筛选标签数”实现。
## 7.3 空筛选
- `tags` 为空时不加标签过滤。
- 仅 `tagMode` 存在但无 `tags` 时,视同无筛选。
## 8. 前端交互设计
## 8.1 编辑页(新建/编辑文章)
- 增加标签输入区域:
- 可选择已有标签。
- 可输入并创建新标签。
- 保存时提交 `tags: string[]`
- 加载详情时回填标签。
## 8.2 各文章列表页
统一新增:
- 标签多选控件。
- OR/AND 模式切换控件。
- 清空筛选按钮。
- 当前筛选条件展示(可逐个移除)。
## 8.3 URL 状态管理
通过 query 持久化:
- `tags`: 逗号分隔的标签 slug 或约定编码值。
- `tagMode`: `or` / `and`
- 与 `page` 联动:筛选变化时可重置到第 1 页。
交互保证:
- 刷新后恢复筛选状态。
- 链接分享后可复现同筛选视图。
- 浏览器前进/后退可恢复历史筛选状态。
## 8.4 空态文案
区分两种空态:
- 真空:暂无任何文章。
- 筛选空:存在文章,但当前标签筛选无结果。
## 9. 错误处理与一致性
- 非法 `tagMode`:回退 `or`
- 非法 `tags`:返回 400(明确错误提示)。
- 写入采用事务,任一步失败全回滚。
- 接口返回的 `selectedTags/tagMode` 应是服务端最终生效值,前端直接据此回显。
## 10. 测试策略
## 10.1 后端单元/服务测试
- 标签归一化。
- 标签 upsert 去重(同用户维度)。
- 文章标签关系重建。
- OR/AND 筛选结果正确性。
## 10.2 API 测试
- query 参数解析(含非法输入)。
- 分页 + 筛选一致性(`items/total/page`)。
- 空筛选与边界筛选行为。
## 10.3 前端测试
- URL 参数回写与恢复。
- 切换 OR/AND 的行为与请求参数。
- 清空筛选与空态分支。
- 翻页后保持筛选条件。
## 11. 迁移与发布策略
1. 新增 migration:创建 `tags`、`post_tags`、索引与约束。
2. 增加回填脚本:从 `posts.tagsJson` 导入新关系表。
3. 上线后双读/双写过渡(按实现方案定具体比例和时长)。
4. 观察稳定性后,制定 `tagsJson` 下线计划。
## 12. 实施拆分建议(供计划阶段使用)
- Step 1:数据库迁移与模型定义。
- Step 2:服务层标签写入/读取与筛选查询。
- Step 3:`/api/me/posts*` 接口改造。
- Step 4:`/api/public/profile/:publicSlug/posts` 接口改造。
- Step 5:新建/编辑页标签输入。
- Step 6:各文章列表页筛选 UI + URL 同步。
- Step 7:测试补齐与回归。
---
本设计已满足当前确认项:
- 覆盖各文章列表页面。
- 标签模式为“混合(可选已有 + 可新建)”。
- 筛选支持 OR/AND 切换。
- 后端查询筛选并通过 URL 持久化。
Loading…
Cancel
Save