This commit is contained in:
+52
-1
@@ -389,7 +389,58 @@ Query:`doc_type`(默认 `postmortem`)。来源可以是任务或工单;
|
||||
|
||||
---
|
||||
|
||||
## 6. 健康与版本
|
||||
## 6. 附件与富文本
|
||||
|
||||
### 6.1 存储格式:Markdown + 附件表
|
||||
|
||||
正文类字段(任务 `body_md`、工单说明、知识正文、评论、验收结论)**一律存 Markdown**,不存 HTML。
|
||||
理由:能导出、能全文检索、能直接喂给 LLM;HTML 每次渲染都要重新清洗一遍,是隐患。
|
||||
|
||||
图片不是 base64 塞进正文,而是走附件接口拿一个稳定地址:
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|---|---|---|
|
||||
| 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=<access>` | 直链,给 `<img src>` 用 |
|
||||
| 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
|
||||
|
||||
`<img>` 发不出 `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` → 在光标处插入 `)`。
|
||||
- 上传接口自己带 `Idempotency-Key`,**不要**手动设 `Content-Type`(浏览器要自己带 boundary)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 健康与版本
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|---|---|---|
|
||||
|
||||
@@ -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;`<img>` 直链走 `raw?token=`,只允许本工作区成员
|
||||
- [ ] 图片直链改一次性签名 URL(当前 token 放 query,上生产前必须换)
|
||||
- [ ] 缩略图、图片压缩、病毒扫描:未做
|
||||
|
||||
### 5.3 编辑器
|
||||
- [x] `RichTextEditor`:工具栏(粗体/斜体/标题/列表/引用/代码/链接/图片)+ `Ctrl+B`/`Ctrl+I` + 预览切换
|
||||
- [x] 粘贴与拖拽上传图片,自动在光标处插入 `)`
|
||||
- [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
|
||||
Reference in New Issue
Block a user