|
2 | 2 |
|
3 | 3 | 本文档用于指导 agent 通过后台接口完成文章新增、编辑与相关 AI 辅助操作。 |
4 | 4 |
|
| 5 | +## 前置:Markdown 渲染接口 |
| 6 | + |
| 7 | +新增或编辑文章正文时,必须先调用前端提供的 Markdown 渲染接口,拿到与后台编辑器一致的 HTML 和目录 JSON,再提交给后端。 |
| 8 | + |
| 9 | +**渲染接口地址:** |
| 10 | +``` |
| 11 | +POST {frontendBaseUrl}/api/render-markdown |
| 12 | +Content-Type: application/json |
| 13 | +``` |
| 14 | + |
| 15 | +**请求体:** |
| 16 | +```json |
| 17 | +{ "markdown": "# 标题\n\n正文内容" } |
| 18 | +``` |
| 19 | + |
| 20 | +**响应体:** |
| 21 | +```json |
| 22 | +{ |
| 23 | + "html": "<h1 id=\"...\">标题</h1>\n<p>正文内容</p>\n", |
| 24 | + "toc": [{ "id": "...", "text": "标题", "level": 1 }], |
| 25 | + "tocJson": "[{\"id\":\"...\",\"text\":\"标题\",\"level\":1}]", |
| 26 | + "plainText": "标题 正文内容", |
| 27 | + "readingMinutes": 1 |
| 28 | +} |
| 29 | +``` |
| 30 | + |
| 31 | +**说明:** |
| 32 | +- `html` → 对应后端接口的 `contentHtml` 字段 |
| 33 | +- `tocJson` → 对应后端接口的 `contentTocJson` 字段 |
| 34 | +- `frontendBaseUrl` 从本地连接文件中读取;如果不存在,可向用户索要 |
| 35 | +- 前端渲染接口与后台编辑器使用完全相同的 `markdown-it` + Shiki 管线,保证渲染结果一致** |
| 36 | + |
| 37 | +**完整流程:** |
| 38 | + |
| 39 | +```text |
| 40 | +1. 取文章本地 Markdown 内容 |
| 41 | + │ |
| 42 | + ▼ |
| 43 | +2. POST {frontendBaseUrl}/api/render-markdown |
| 44 | + body: { markdown: "..." } |
| 45 | + │ |
| 46 | + ▼ |
| 47 | +3. 拿到 html + tocJson |
| 48 | + │ |
| 49 | + ▼ |
| 50 | +4. POST /api/admin/posts(创建)或 PUT /api/admin/posts/{id}(编辑) |
| 51 | + body: { contentMarkdown, contentHtml, contentTocJson, ...其他字段 } |
| 52 | +
|
| 53 | +⚠️ 创建文章时,html + tocJson 必须和基础字段一起在 POST 中提交, |
| 54 | + 不要先创建再 PUT 补全。一次完成,不要分两步。 |
| 55 | +``` |
| 56 | + |
5 | 57 | ## 零、统一前置步骤 |
6 | 58 |
|
7 | 59 | 在查询或写入文章相关后台接口前,先处理连接信息: |
|
20 | 72 | 建议按以下顺序操作: |
21 | 73 |
|
22 | 74 | 1. 先确认用户是否已经提供文章标题和正文。 |
23 | | -2. 如果用户没有指定分类或标签,先查询可用列表: |
| 75 | +2. 如果在创建前需要生成正文的 HTML 和目录,先调用渲染接口: |
| 76 | + - `POST {frontendBaseUrl}/api/render-markdown` |
| 77 | + - 传入正文 Markdown,拿到 `html`、`tocJson` 等字段 |
| 78 | + - 后续提交时一并传给后端 |
| 79 | +3. 如果用户没有指定分类或标签,先查询可用列表: |
24 | 80 | - `GET /api/admin/categories` |
25 | 81 | - `GET /api/admin/tags` |
26 | | -3. 根据文章标题和正文语义,从已有分类、标签里挑选最合适的项: |
| 82 | +4. 根据文章标题和正文语义,从已有分类、标签里挑选最合适的项: |
27 | 83 | - 若确实存在明显匹配项,应自动选中对应分类 / 标签 |
28 | 84 | - 若没有明显匹配项,可以留空,不要为了凑字段乱选 |
29 | | -4. 专题默认视为“不设置”: |
| 85 | +5. 专题默认视为"不设置": |
30 | 86 | - 只有用户明确说本次也要挂专题时,才查询 `GET /api/admin/topics` |
31 | 87 | - 用户未指定时,不调用专题相关接口 |
32 | | -5. `slug`、`summary`、`seoTitle`、`seoDescription`: |
| 88 | +6. `slug`、`summary`、`seoTitle`、`seoDescription`: |
33 | 89 | - 如果用户已提供,优先使用用户提供值。 |
34 | 90 | - 如果用户未提供,优先由当前 AI agent 根据标题和正文直接生成。 |
35 | 91 | - 默认不要为了补这些字段再额外调用 `POST /api/admin/posts/ai/meta/generate`。 |
36 | 92 | - 只有当用户明确要求使用后台 AI 元信息生成能力,或需要和后台内置生成结果保持一致时,才考虑调用该接口。 |
37 | 93 | - 调用该接口时优先传 `title` 与 `contentMarkdown`;后端会统一组装提示词并返回 OpenAI Chat 风格结果。 |
38 | | -6. 文章状态默认建议: |
| 94 | +7. 文章状态默认建议: |
39 | 95 | - 若用户没有明确要求立即发布,默认传 `DRAFT` |
40 | | - - 若用户明确说“直接发布”,传 `PUBLISHED` |
41 | | - - 若用户明确说“保存但先下线”,传 `OFFLINE` |
42 | | -7. 布尔默认建议: |
| 96 | + - 若用户明确说"直接发布",传 `PUBLISHED` |
| 97 | + - 若用户明确说"保存但先下线",传 `OFFLINE` |
| 98 | +8. 布尔默认建议: |
43 | 99 | - `isTop=false` |
44 | 100 | - `isRecommend=false` |
45 | 101 | - `allowComment=true` |
46 | | -8. 内容权限默认不启用。 |
47 | | - - 应主动提示用户:“当前默认不启用内容权限,要不要开启文章访问控制或隐藏内容权限?” |
48 | | -9. 若用户不启用内容权限: |
| 102 | +9. 内容权限默认不启用。 |
| 103 | + - 应主动提示用户:"当前默认不启用内容权限,要不要开启文章访问控制或隐藏内容权限?" |
| 104 | +10. 若用户不启用内容权限: |
49 | 105 | - 仍建议提交默认的禁用结构,保持与当前后台编辑器一致。 |
50 | | -10. 若用户启用整篇文章访问控制: |
| 106 | +11. 若用户启用整篇文章访问控制: |
51 | 107 | - 至少选择一个规则:`LOGIN` / `WECHAT_ACCESS_CODE` / `ACCESS_CODE` |
52 | 108 | - 若包含 `ACCESS_CODE`,必须同时填写: |
53 | 109 | - `articleAccessCode` |
54 | 110 | - `articleAccessCodeHint` |
55 | | -11. 若用户启用尾部隐藏内容: |
56 | | - - 必须填写: |
57 | | - - `tailHiddenAccess.enabled=true` |
58 | | - - `tailHiddenAccess.title` |
59 | | - - `tailHiddenAccess.ruleTypes` |
60 | | - - `tailHiddenContentMarkdown` |
61 | | -12. 创建文章最终调用: |
| 111 | +12. 若用户启用尾部隐藏内容: |
| 112 | + - 必须填写: |
| 113 | + - `tailHiddenAccess.enabled=true` |
| 114 | + - `tailHiddenAccess.title` |
| 115 | + - `tailHiddenAccess.ruleTypes` |
| 116 | + - `tailHiddenContentMarkdown` |
| 117 | +13. 创建文章最终调用: |
62 | 118 | - `POST /api/admin/posts` |
| 119 | + - 请求体中应包含从渲染接口获取的 `contentHtml` 和 `contentTocJson` |
| 120 | + - **⚠️ 关键约束:渲染结果(contentHtml、contentTocJson)必须一次性带入 POST 请求,禁止先创建再 PUT 补全。** |
| 121 | + 创建时只传基础字段后续再 PUT 补充渲染结果属于冗余请求,既浪费 token 也无必要;POST 创建本就支持所有字段同时提交。 |
63 | 122 |
|
64 | 123 | ## 二、编辑文章 |
65 | 124 |
|
|
70 | 129 | 1. 先定位目标文章。 |
71 | 130 | 2. 用 `GET /api/admin/posts/{postId}` 读取当前完整详情。 |
72 | 131 | 3. 在原详情基础上合并本次修改。 |
73 | | -4. 因为更新接口要求很多字段保留完整语义,不能只传一个零散字段然后丢掉其他字段。 |
74 | | -5. 若用户只说“改摘要”或“改标题”: |
| 132 | +4. 如果本次修改涉及正文内容,先调用渲染接口获取新的 HTML 和目录: |
| 133 | + - `POST {frontendBaseUrl}/api/render-markdown` |
| 134 | + - 传入新的正文 Markdown,拿到 `html` 和 `tocJson` |
| 135 | + - 在最终提交时替换 `contentHtml` 和 `contentTocJson` |
| 136 | +5. 如果本次修改不涉及正文(只改状态、封面、标签等),**不需要调用渲染接口**。 |
| 137 | + - `contentMarkdown` 设置为 `null` 即可,后端不会清空已有正文(详见后端增量更新逻辑) |
| 138 | +6. 因为更新接口要求很多字段保留完整语义,不能只传一个零散字段然后丢掉其他字段。 |
| 139 | +7. 若用户只说"改摘要"或"改标题": |
75 | 140 | - 也应该先获取原文详情 |
76 | 141 | - 再仅修改目标字段 |
77 | 142 | - 其余字段保持原值 |
78 | | -6. 更新文章最终调用: |
| 143 | +8. 更新文章最终调用: |
79 | 144 | - `PUT /api/admin/posts/{postId}` |
80 | 145 |
|
81 | 146 | ## 三、文章创建与编辑时的重点字段 |
|
0 commit comments