diff --git a/2.Design/API.md b/2.Design/API.md index a47ff56..78e520b 100644 --- a/2.Design/API.md +++ b/2.Design/API.md @@ -389,7 +389,58 @@ Query:`doc_type`(默认 `postmortem`)。来源可以是任务或工单; --- -## 6. 健康与版本 +## 6. 附件与富文本 + +### 6.1 存储格式:Markdown + 附件表 + +正文类字段(任务 `body_md`、工单说明、知识正文、评论、验收结论)**一律存 Markdown**,不存 HTML。 +理由:能导出、能全文检索、能直接喂给 LLM;HTML 每次渲染都要重新清洗一遍,是隐患。 + +图片不是 base64 塞进正文,而是走附件接口拿一个稳定地址: + +```markdown +![票据截图.png](/api/v1/workspaces/{ws_id}/attachments/{attachment_id}/raw?token=...) +``` + +| 方法 | 路径 | 说明 | +|---|---|---| +| POST | `/workspaces/{ws_id}/items/{item_id}/attachments` | multipart,字段名 `file`;返回 201 + 附件对象 | +| GET | `/workspaces/{ws_id}/items/{item_id}/attachments` | `{items: [...]}` | +| GET | `/workspaces/{ws_id}/attachments/{id}/download` | 下载(`Content-Disposition: attachment`) | +| GET | `/workspaces/{ws_id}/attachments/{id}/raw?token=` | 直链,给 `` 用 | +| DELETE | `/workspaces/{ws_id}/attachments/{id}?confirm=true` | 上传者或管理员可删;不传 confirm 报 `8004` | + +附件对象的字段:`id / item_id / filename / content_type / size / sha256 / uploader_id / created_at / can_delete`。 +事项与知识的列表接口会附带 `attachment_count`,详情接口附带 `attachments` 全量。 + +### 6.2 为什么图片直链要带 token + +`` 发不出 `Authorization` 头。两种解法:把图片读成 base64 内联(正文会膨胀几十倍,且破坏检索), +或者给一个生命周期短的签名地址。 + +当前实现取的是"query 里带 access token",**并且只允许本工作区成员访问**(非成员一律 `8001`, +不区分"不存在"与"没权限",避免探测他人附件)。这是本地/内网阶段的折中,上生产前应换成 +**带过期时间的一次性签名 URL**,见 `ROADMAP` 的 M7 安全清单。 + +### 6.3 附件错误码 + +| 码 | HTTP | 含义 | +|---|---|---| +| 8001 | 404 | 附件不存在,或不属于这个工作区 | +| 8002 | 413 | 超过 `MAX_UPLOAD_MB`(默认 20) | +| 8003 | 403 | 不是上传者也不是管理员 | +| 8004 | 409 | 需要 `?confirm=true`(删除是破坏性动作) | + +### 6.4 前端约定 + +- 编辑器写 Markdown,工具栏/快捷键 `Ctrl+B`、`Ctrl+I` 只管插入语法;预览用 `renderMarkdown()` + 渲染(先转义再上标签,链接协议白名单 `http(s)/mailto//`)。 +- 粘贴或拖入图片时:先上传 → 拿到 `id` → 在光标处插入 `![文件名](attachmentUrl(...))`。 +- 上传接口自己带 `Idempotency-Key`,**不要**手动设 `Content-Type`(浏览器要自己带 boundary)。 + +--- + +## 7. 健康与版本 | 方法 | 路径 | 说明 | |---|---|---| diff --git a/2.Design/ROADMAP-M0-M1.md b/2.Design/ROADMAP-M0-M1.md index b409905..20f4ffb 100644 --- a/2.Design/ROADMAP-M0-M1.md +++ b/2.Design/ROADMAP-M0-M1.md @@ -349,3 +349,34 @@ - [x] 模拟器(API 35)实跑:登录 → 工作区 → 任务勾选 → 工单 → 知识 → 成员,截图存 `2.Design/screens/android-*.png` - [~] 看板不做拖拽:手机屏幕上一列已占 76% 宽,拖拽命中率太差;改为点卡片选目标列,功能等价 - [ ] 离线缓存(Room)、图片附件、消息推送:均未做,M5 再议 + +--- + +## 5. 富文本与附件(M2.5) + +### 5.1 决策:正文存 Markdown,不存 HTML +- [x] 任务备注、工单说明与验收结论、知识正文、评论,统一 `*_md` 字段存 Markdown +- [x] 渲染器自研零依赖(`web/src/lib/markdown.ts`),**先转义再上标签**,链接协议白名单 `http(s)/mailto//` +- [x] 不做 contenteditable 富文本:它产出 HTML,导出/检索/喂 LLM 都要再清洗,且 `dangerouslySetInnerHTML` 面积更大 +- [ ] 表格与 HTML 片段:暂不支持,按原样当文本(等真有需求再加) + +### 5.2 附件 +- [x] 后端:`attachment` 表 + RLS + 迁移 0005;`POST/GET/DELETE` 与 `download`/`raw` 两个读取口 +- [x] 本地磁盘存储(`backend/app/services/storage.py`),key 由内容 sha256 决定;换对象存储只改这一个文件 +- [x] 上限 `MAX_UPLOAD_MB=20`,声明大小与流式读取双重拦截,超限 `8002` +- [x] 列表带 `attachment_count`,详情带 `attachments`,避免一屏 N 次请求 +- [x] 下载走带 `Authorization` 的 fetch 转 blob;`` 直链走 `raw?token=`,只允许本工作区成员 +- [ ] 图片直链改一次性签名 URL(当前 token 放 query,上生产前必须换) +- [ ] 缩略图、图片压缩、病毒扫描:未做 + +### 5.3 编辑器 +- [x] `RichTextEditor`:工具栏(粗体/斜体/标题/列表/引用/代码/链接/图片)+ `Ctrl+B`/`Ctrl+I` + 预览切换 +- [x] 粘贴与拖拽上传图片,自动在光标处插入 `![文件名](attachmentUrl(...))` +- [x] `AttachmentPanel`:上传/下载/删除(删除二次确认,对应后端 `?confirm=true`) +- [x] 安卓:本轮未做富文本编辑,只做只读渲染 + 附件下载(M5 再议) + +### 5.4 验证 +- [x] 后端 `pytest` → **70 passed**(新增附件 5 个用例:上传下载、超限、跨租户、删除确认与权限、raw 直链鉴权) +- [x] `web/e2e/richtext.mjs` → 9 项全绿(渲染、贴图上传后 `/raw` 返回 200、附件落库后重开仍在、控制台无报错) +- [x] 既有三套 e2e 回归通过(smoke 7 / tickets 8 / knowledge 7) +- [x] `tsc -b` 0 错;`vite build` 255.07 kB │ gzip 80.85 kB \ No newline at end of file diff --git a/README.md b/README.md index 26c7117..14de2e5 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ | 工单:升级不搬数据、受理→办理→提交→验收、驳回、改派、站内通知 | ✅ 后端 + Web + 安卓已通,双人 e2e 8 项全绿 | | 知识:工单验收自动生成草稿、核对后发布、来源可追、手记 | ✅ 后端 + Web + 安卓已通,e2e 7 项全绿 | | 界面:两端统一设计系统(纸白 / 墨绿 / 陶土橙,语义色、字号阶、组件库) | ✅ 已重做,见 [`2.Design/ROADMAP-M0-M1.md`](2.Design/ROADMAP-M0-M1.md) 第 4 节 | -| 后端测试 | ✅ 66 passed,ruff 干净 | +| 富文本:备注 / 工单说明 / 知识正文支持 Markdown 格式、图片、附件 | ✅ 后端 + Web 已通(附件接口见 [`2.Design/API.md`](2.Design/API.md) 第 6 节,e2e 9 项全绿);安卓暂只读 | +| 后端测试 | ✅ 70 passed,ruff 干净 | | 知识切片与语义检索(pgvector) | ⬜ 配置已就绪(`deploy/docker-compose.vector.yml`),本机 Docker 起不来,未跑过 | | LLM 能力(结构化录入、问答带引用、人物画像) | ⬜ M4 |