Selaa lähdekoodia

docs(specs): 知识中心按文章标签快速搜索 设计规格与变更记录

- 新增 docs/superpowers/specs/2026-11-10-article-tag-filter-design.md
- 记录需求概述/现状盘点/方案设计/兼容性约束/验证与入库(commit 6e9a511e2)/后续建议
Agnes Agent 2 päivää sitten
vanhempi
sitoutus
9e39128c6
1 muutettua tiedostoa jossa 94 lisäystä ja 0 poistoa
  1. 94 0
      docs/superpowers/specs/2026-11-10-article-tag-filter-design.md

+ 94 - 0
docs/superpowers/specs/2026-11-10-article-tag-filter-design.md

@@ -0,0 +1,94 @@
+# 知识中心按文章标签快速搜索 — 设计规格与变更记录
+
+> 日期:2026-11-10
+> 状态:**已实现并合入**(commit `6e9a511e2`)
+> 范围:cfc-backend + cfc-frontend(小程序知识中心页 `article-center`)
+
+## 1. 需求概述
+
+小程序知识中心页 `article-center` 顶部分类 Tab 从「五维(身/心/智/富/行)」改为「**按文章标签(articles.tags)动态筛选**」。顶部关键词搜索保留;文章卡片五维浮标保留(信息展示,非分类筛选)。
+
+## 2. 现状盘点
+
+| 环节 | 改造前 | 差距 |
+|------|--------|------|
+| 分类 Tab | `tabList` 硬编码五维(`body/mind/wisdom/action/wealth`) | 需改为动态文章标签 |
+| 列表筛选 | `/api/articles/list` 传 `dimensionCode` 做五维筛选 | 需改为传 `tag` 做标签筛选 |
+| 标签数据源 | 无聚合标签接口 | 缺 `GET/POST /api/articles/tags` |
+| 后端能力 | `ArticleService` 已有 `parseCommaOrJsonTags`(JSON 数组/逗号分隔兼容)+ `matchArticlesByTags` | 可复用解析,需新增 `listTopTags` |
+| 标签存储 | `articles.tags` 为 JSON 数组字符串(如 `["减脂","睡眠"]`),部分兼容逗号分隔 | 无需迁移,解析统一 |
+| 后台发布 | `AdminArticleController` 已支持写 tags + `/backfill-tags` 自动回填 | 无 |
+
+## 3. 方案设计
+
+```
+用户进入知识中心 onLoad
+   ├─ loadTags() → GET /api/articles/tags → 拼 tabList = [全部, 标签1, 标签2, ...](Top20 按频次降序)
+   │       失败 → 静默保留 [全部],页面仍可浏览
+   └─ loadArticles() → POST /api/articles/list { tag: currentTab, keyword, ... }
+            后端 getPublicList(..., tag, ...) 内存标签过滤(parseCommaOrJsonTags 解析后 contains)
+```
+
+### 3.1 后端
+
+**`ArticleController`**
+- `/list`(POST):新增读 `tag` 参数(`body.get("tag")`),转发到 10 参 `getPublicList`(位置 4)。
+- 新增 `/tags`(POST):`Result<List<String>>`,调 `articleService.listTopTags(20)`。
+
+**`ArticleService`**
+- 新增 10 参 `getPublicList(categoryId, keyword, dimensionCode, tag, page, size, userId, familyId, vendorType, memberId)`:`tag` 非空时用 `parseCommaOrJsonTags` 内存过滤(兼容 JSON 数组 / 逗号分隔),其余状态/权限/已读过滤逻辑不变。
+- 旧 8 参 / 9 参代理到 10 参(位置 4 传 `tag=null`),保持 `McpToolRegistry`(8 参调用)等既有调用方兼容。
+- 新增 `listTopTags(int limit)`:查 `status='published'` 且 tags 非空,`parseCommaOrJsonTags` 拆分统计频次,`merge` 去重、按频次降序(并列按标签字典序)返回 Top N(`limit<=0` 默认 20)。
+
+### 3.2 前端
+
+**`article-center/index.vue`**
+- `tabList` 初始仅 `[{ code:'', name:'全部' }]`;`onLoad` 新增 `loadTags()` 拉标签列表,拼到「全部」后(`{code:标签, name:标签, color:BrandColors.primary}`),失败静默。
+- `loadArticles()`:`params.dimensionCode = currentTab` → `params.tag = currentTab`;切 tab 时清词重载关键词搜索逻辑不变。
+- 外部 `options.dimension`(旧五维 code 入口)新语义:命中 tag 为空的可接受降级(注释说明)。
+
+**`utils/api.js`**
+- 新增 `export const getArticleTags = (data) => request('/api/articles/tags', 'POST', data || {})`。
+
+### 3.3 未改动(明确保留)
+
+- 顶部关键词搜索栏(keyword 搜索逻辑不变)。
+- 文章卡片五维浮标 `.ac-card-dimension-tags` / `parseDimensions()`(信息展示非筛选)。
+- `AdminArticleController`、`Article` 实体、`schema.sql`、`ArticleCategoryController`/`ArticleCommentController`。
+
+## 4. 兼容性与约束
+
+- **路由无冲突**:`/api/articles` base 下无重复 `POST /tags`;`FileRecordController` 的 `/tags` 属 `/api/admin/file-record` 不同 base。
+- **接口契约**:前端 `params.tag` ↔ 后端 `body.get("tag")`;`request('/api/articles/tags')` ↔ `@PostMapping("/tags")` 拼接一致。
+- **前端硬约束**(AGENTS.md):无 `?.`、无 CSS Grid、`:key` 为简单引用(`tab.code`/`n`/`item.id`/`dim.code`)、无 `new Date(string)`。
+- **后端硬约束**:新增 `/tags` 用 `@PostMapping`、返回 `Result<List<String>>`、`@Resource` 注入、LambdaQueryWrapper、无新依赖。
+- **测试同步**:`ArticleControllerTest` 4 处 stub 由 9 参改 10 参(位置 4 补 tag=null,解编译阻断);`GlobalSearchScP82ContractTest` 因走 `McpToolRegistry` 8 参路径(非 controller.list),保留 8 参并新增显式 `verify(articleService).getPublicList(8参)` 断言(改用 10 参 stub 会导致 mock NPE 变红,故不可行)。
+
+## 5. 验证与入库
+
+| 环节 | 结果 |
+|------|------|
+| 工程师 IS_PASS 全局一致性审查 | YES(参名一致、无残留五维、路由无冲突、代理签名修复) |
+| QA Round1 | 发现 2 处测试 Bug(`ArticleControllerTest` 9 参编译阻断 + `GlobalSearchScP82ContractTest` 失效契约)→ 判定 Engineer 回炉 |
+| 工程师修复 | Bug1 改 10 参 stub;Bug2 复核后**纠正 QA 建议**(8 参 verify 显式断言更正确,10 参 stub 会 NPE) |
+| QA Round2 | 采纳工程师复核,最终判定 **NoOne 全通过** |
+| 入库 | 6 文件 84+/15-,push commit `6e9a511e2`(FF 入链 `271f8921c..6e9a511e2`) |
+
+> ⚠ 本机无 mvn 依赖缓存,未实机编译;采用静态审查 + node 语法校验兜底。建议 CI/有缓存环境跑 `mvn -q test -Dtest=ArticleControllerTest,GlobalSearchScP82ContractTest` 最终确认。
+
+### 改动文件清单
+
+| 文件 | 动作 | 职责 |
+|------|------|------|
+| `cfc-backend/src/main/java/com/etotem/cfc/controller/content/ArticleController.java` | 修改 | `/list` 增 `tag` 参数;新增 `POST /tags` |
+| `cfc-backend/src/main/java/com/etotem/cfc/service/ArticleService.java` | 修改 | 新增 10 参 `getPublicList`(tag 过滤)+ `listTopTags`;旧 8/9 参代理 |
+| `cfc-frontend/pages/article-center/index.vue` | 修改 | Tab 动态化;`dimensionCode`→`tag`;新增 `loadTags()` |
+| `cfc-frontend/utils/api.js` | 修改 | 新增 `getArticleTags` |
+| `cfc-backend/src/test/java/com/etotem/cfc/integration/controller/ArticleControllerTest.java` | 修改 | 9 参 stub→10 参 |
+| `cfc-backend/src/test/java/com/etotem/cfc/unit/GlobalSearchScP82ContractTest.java` | 修改 | 增 8 参 `verify` 显式断言 |
+
+## 6. 后续建议
+
+1. 有依赖缓存环境跑 `mvn -q test -Dtest=ArticleControllerTest,GlobalSearchScP82ContractTest` 实机确认。
+2. 小程序真机验证:知识中心 Tab 显示文章标签、点击筛选、关键词搜索、五维浮标正常。
+3. 若后续需支持「标签 + 五维」组合筛选,可扩展 `/list` 同时接受 `tag` 与 `dimensionCode`(当前 `tag` 优先,二者互斥未实现)。