From 07688881b48c27f699196b3c89c565998b918ff6 Mon Sep 17 00:00:00 2001 From: dadao Date: Wed, 28 Oct 2026 14:03:10 +0800 Subject: [PATCH] =?UTF-8?q?docs(m3):=20=E8=A1=A5=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=A5=91=E7=BA=A6=E3=80=81M3=20=E6=B8=85?= =?UTF-8?q?=E5=8D=95=E4=B8=8E=E7=8A=B6=E6=80=81=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - API.md 新增第 5 节:列表/详情/手记/改内容/状态动作/从事项沉淀 + 7xxx 错误码 - ROADMAP 增 M3 全节与 DOD,验收用例补 11–13,明确 3.8 向量索引未跑 - README 状态表:知识已通、pgvector 只有配置 - DEVELOPMENT 补三条 e2e 命令与「先想一条能失败的断言」 --- 2.Design/API.md | 92 ++++++++++++++++++++++++++++++++++++++- 2.Design/DEVELOPMENT.md | 7 ++- 2.Design/ROADMAP-M0-M1.md | 39 +++++++++++++++-- README.md | 8 ++-- 4 files changed, 136 insertions(+), 10 deletions(-) diff --git a/2.Design/API.md b/2.Design/API.md index a2c18a3..a47ff56 100644 --- a/2.Design/API.md +++ b/2.Design/API.md @@ -1,4 +1,4 @@ -# 接口约定(M0–M2 已实现) +# 接口约定(M0–M3 已实现) > 只写**已经在跑**的接口。写完没验证的东西不进这份文档。 > 前缀 `/api/v1`。所有响应都是同一个信封,**HTTP 200 不代表业务成功**。 @@ -301,7 +301,95 @@ canceled→ todo (恢复) --- -## 5. 健康与版本 +## 5. 知识 `/workspaces/{ws_id}` + +### 5.1 `GET /knowledge` + +| 参数 | 说明 | +|---|---| +| `status` | 可重复;`draft`/`in_review`/`published`/`archived`,不传为全部 | +| `doc_type` | `note`/`howto`/`faq`/`postmortem`/`decision` | +| `source_item_id` | 按来源事项筛 | +| `q` | 标题 / 正文 / 摘要子串 | +| `limit` | 1–100,默认 30 | +| `cursor` | 上一页的 `next_cursor` | + +返回 `{items, next_cursor, has_more}`。每条是 `item` 字段 + `knowledge` + `source`: + +```json +{ + "id": "…", "kind": "knowledge", "title": "发票原件必须一起寄", + "body_md": "…", "status": "published", "tags": [], + "knowledge": { + "doc_type": "howto", "summary": "…", "status_hint": "published", + "source_kind": "task", "source_item_id": "…", "generated_by": "human", + "published_at": "2026-10-28T05:54:44Z", "archived_at": null, + "chunk_count": 0, "author_id": "…" + }, + "source": {"id": "…", "kind": "task", "title": "发票原件必须一起寄", "status": "done"} +} +``` + +> `status` 与 `knowledge.status_hint` 是同一件事:前者在 `item` 上(复用事项表), +> 后者在 `knowledge_doc` 上(列表按知识口径筛时不必回连 `item`)。**两者必须同步**, +> 状态动作接口会一起写。 + +### 5.2 `GET /knowledge/{item_id}` + +详情,附最近 50 条 `activities`。不存在或跨租户统一 `7001`。 + +### 5.3 `POST /knowledge` — 手记或指定来源 + +```json +{"title": "报销单要贴发票原件", "body_md": "…", "doc_type": "note", + "source_item_id": "可选的来源事项 id"} +``` + +- 不带 `source_item_id` → `source_kind=manual`(手记),允许没有来源。 +- 带 → 来源必须是同工作区的 `task`/`ticket`,否则 `7005`。 +- 新知识一律 `draft`,不会直接对外可见。 + +### 5.4 `PATCH /knowledge/{item_id}` — 修改内容 + +必须带 `version`(乐观锁,冲突 `4009`)。可改 `title`/`body_md`/`summary`/`doc_type`。 +**已发布的不许直接改**(`7003`,先撤回审议或归档),避免"发布后被悄悄改写"。 + +### 5.5 `POST /knowledge/{item_id}/status` — 状态动作 + +| action | 允许的前置状态 | 结果 | 谁能做 | +|---|---|---|---| +| `submit_review` | draft | in_review | 成员 | +| `withdraw` | in_review | draft | 成员 | +| `publish` | draft / in_review | published | owner / admin | +| `archive` | draft / in_review / published | archived | 成员 | +| `restore` | archived | draft | 成员 | + +非法动作返回 `7003`,`details.allowed_from` 是动作要求的前置状态,`details.allowed_actions` +是当前状态下还能做什么——前端据此渲染按钮,不用自己维护一份状态机。 + +**发布要 owner/admin 的理由**:发布等于团队为这条结论背书,不能谁都能推。 + +### 5.6 `POST /items/{item_id}/knowledge` — 把一件事沉淀成知识 + +Query:`doc_type`(默认 `postmortem`)。来源可以是任务或工单;工单会带出验收结论。 +返回与 5.2 相同的知识对象;同一来源重复沉淀 → `7002`。 + +**工单验收通过时,后端会自动调用这条逻辑**(`generated_by=rule`,`doc_type=postmortem`, +正文开头固定一句"这份草稿由系统根据来源自动整理,请先核对再发布"),所以用户不需要记得点。 + +### 5.7 知识的错误码 + +| 码 | HTTP | 含义 | +|---|---|---| +| 7001 | 404 | 知识不存在,或不是知识 | +| 7002 | 409 | 这个来源已经有知识了 | +| 7003 | 409 | 当前状态不允许这个动作 | +| 7004 | 403 | 权限不足(如非管理员发布) | +| 7005 | 409 | 来源事项无效或缺失 | + +--- + +## 6. 健康与版本 | 方法 | 路径 | 说明 | |---|---|---| diff --git a/2.Design/DEVELOPMENT.md b/2.Design/DEVELOPMENT.md index ceeaf79..390539c 100644 --- a/2.Design/DEVELOPMENT.md +++ b/2.Design/DEVELOPMENT.md @@ -84,11 +84,16 @@ ruff check . && ruff format --check . ```bash cd web -node e2e/smoke.mjs # 登录→建任务→详情保存→看板拖拽→非法流转被拒,7 项断言 +node e2e/smoke.mjs # 登录→建任务→详情保存→看板拖拽→非法流转被拒,7 项断言 +node e2e/tickets.mjs # 双人:升级工单→受理→提交→验收→原任务回流,8 项断言 +node e2e/knowledge.mjs # 沉淀成知识→待核对→发布,7 项断言 ``` 用本机缓存的 Chromium(`playwright-core`,不下载浏览器),截图落在 `web/e2e/artifacts/`。 +> e2e 抓的都是"手动点几下看不出来"的问题:字段缺失导致整页白屏、请求竞态导致的空列表、 +> 点错按钮的 404。加功能时**先想一条能失败的断言**,再动手写页面。 + ### 1000 条下的首屏耗时 ```bash diff --git a/2.Design/ROADMAP-M0-M1.md b/2.Design/ROADMAP-M0-M1.md index 7f70110..a8a21c3 100644 --- a/2.Design/ROADMAP-M0-M1.md +++ b/2.Design/ROADMAP-M0-M1.md @@ -261,7 +261,36 @@ --- -## 关键验收用例(M0–M2 必须通过) +## M3 · 知识库(第 7–8 周,10 人日) + +| # | 任务 | 人日 | 状态 | +|---|---|---|---| +| 3.1 | `knowledge_doc`(1:1 于 item,`source_kind` + `source_item_id` 必填约束)+ 迁移 0004 + RLS | 1.0 | [x] 已跑通,隔离测试覆盖 | +| 3.2 | 状态机 `draft / in_review / published / archived`,发布要 owner/admin | 0.5 | [x] 5 个动作,非法动作回 `allowed_from` + `allowed_actions` | +| 3.3 | 结构化编辑:标题 / 正文 / 摘要 / 类型,乐观锁,已发布禁改 | 1.0 | [x] 含 `4009` 冲突用例 | +| 3.4 | **闭环沉淀**:工单验收通过自动生成草稿(`generated_by=rule`),同一来源只生成一次 | 1.5 | [x] 只转述不推断,正文带"请先核对再发布" | +| 3.5 | 手动沉淀:任务/工单详情里"沉淀成知识" | 0.5 | [x] 有 e2e | +| 3.6 | 手记:没有来源的知识(唯一允许 `source_kind=manual` 的入口) | 0.5 | [x] 手记页 + 列表 | +| 3.7 | 检索:标题/正文/摘要子串 + 类型/状态筛选 + 游标分页 | 1.0 | [x] trigram 索引在 0002 已建 | +| 3.8 | 切片与向量索引(`chunk` 表、pgvector HNSW) | 2.0 | [ ] 见下方说明 | +| 3.9 | 安卓知识列表 | 1.0 | [ ] 与工单一起延后到编译链就绪 | +| 3.10 | 文档与 e2e | 1.0 | [x] `web/e2e/knowledge.mjs` 7 项断言绿 | + +### M3 完成定义(DOD) +- [x] 一件事办完 → 自动出现一份草稿 → 核对 → 发布,全程不用手动复制粘贴 +- [x] 每条知识都能追到来源(除手记),点开能看到来源事项 +- [x] 非管理员发布被拒;跨租户看不到别人的知识 +- [ ] 向量切片与语义检索(需要独立 pgvector 实例真正跑起来) +- [ ] 真实用两周:新同事靠知识库自己解决过一次问题(需要真实团队) + +**关于 3.8 的说明**:本机 Docker 起不来、fnsvr-01 上也没有 pgvector 扩展, +所以 `deploy/docker-compose.vector.yml` 与 `VECTOR_DATABASE_URL` 只有配置,**没有跑过**。 +按"写完不算完成"的规矩,它在真跑起来之前保持未勾。切片表设计要点已写进 +`2.Design/PRODUCT-PLAN.md` 的 D8,落地时不要改 `EMBEDDING_DIM`(改了要重算全量)。 + +--- + +## 关键验收用例(M0–M3 必须通过) | # | 用例 | 期望 | 现状 | |---|---|---|---| @@ -275,14 +304,16 @@ | 8 | 部署后 `/version` | commit 与仓库 HEAD 一致 | [~] 机制已有,等部署验证 | | 9 | 办理人验收自己的工单 | 403 `6004`,不能自己给自己过 | ✅ 已覆盖 | | 10 | 工单验收通过后原任务 | 自动从"被阻塞"回到"待办",事件流留痕 | ✅ 已覆盖(含 e2e) | +| 11 | 工单验收通过后知识库 | 自动出现一份草稿,且只生成一次 | ✅ 已覆盖 | +| 12 | 成员发布知识 | 403 `7004`;owner 发布成功且记录发布时间 | ✅ 已覆盖 | +| 13 | 知识有没有来源 | 非手记必须有 `source_item_id`,否则 7005 / 库级约束拒绝 | ✅ 已覆盖 | --- ## 下一步排期建议 ``` -已完成 0.1–0.9 骨架与认证 + 1.1–1.7 任务域 + 2.1–2.6 工单协同(含双人 e2e) -待补 0.11 部署 / 0.12 契约 / 0.14 微信 spike / CI 在 Gitea 实跑 / 安卓编译链 -第7–8周 M3 知识:knowledge_doc + chunk、切片与索引、工单关闭生成草稿、带引用问答 +已完成 0.1–0.9 骨架与认证 + 1.1–1.7 任务域 + 2.1–2.6 工单协同 + 3.1–3.7、3.10 知识库 +待补 0.11 部署 / 0.12 契约 / 0.14 微信 spike / CI 在 Gitea 实跑 / 安卓编译链 / 3.8 向量索引 第9–10周 M4 AI:结构化录入、人物工作侧写、主动提醒、20 条黄金用例回归 ``` diff --git a/README.md b/README.md index 85c0fc9..9d94207 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,15 @@ |---|---| | 手机号验证码登录、刷新令牌、幂等请求 | ✅ 已实现并有测试 | | 工作区 / 成员 / 邀请码加入,跨租户隔离(RLS) | ✅ 同上 | -| 任务:收件箱→待办→进行中→完成、截止时间、标签、评论、事件流、乐观锁 | ✅ 后端 + Web 已通,57 个测试全绿 | +| 任务:收件箱→待办→进行中→完成、截止时间、标签、评论、事件流、乐观锁 | ✅ 后端 + Web 已通 | | Web 列表 / 看板拖拽 / 详情侧栏编辑 / 键盘流 / 快速录入语法 | ✅ e2e 7 项断言全绿,构建通过 | | 1000 条任务下的首屏性能 | ✅ 实测 P50 38–55ms(目标 < 300ms) | | 安卓端任务列表与详情 | ⚠️ 代码已写,**本机无 JDK/Gradle,未编译验证** | | 工单:升级不搬数据、受理→办理→提交→验收、驳回、改派、站内通知 | ✅ 后端 + Web 已通,双人 e2e 8 项全绿 | -| 后端测试 | ✅ 57 passed,ruff 干净 | -| 知识库、LLM 能力 | ⬜ M3–M4 | +| 知识:工单验收自动生成草稿、核对后发布、来源可追、手记 | ✅ 后端 + Web 已通,e2e 7 项全绿 | +| 后端测试 | ✅ 65 passed,ruff 干净 | +| 知识切片与语义检索(pgvector) | ⬜ 配置已就绪(`deploy/docker-compose.vector.yml`),本机 Docker 起不来,未跑过 | +| 安卓端知识页 / LLM 能力 | ⬜ M3 收尾 / M4 | ## 快速开始