笔记图片块:补 API 上传子端点(图文混排的前半程)#23

用户原话(2026-10-28):「鸿蒙端对齐网页端」,笔记端上只支持纯文本,
图片块显示成「[图片]」,且编辑带图笔记会静默删光图片块。

这半程只改服务端、补一个缺口:
· POST /api/v1/notes/upload —— 笔记图片上传,返回附件 key。
  放在 notes() 的 $id>0 分支之前(上传是草稿态,不依赖笔记 id,
  新建笔记插图时还没有 id)。图片先挂 target_id=0,等 blocks 带
  key 提交时由 Note::claimUploads 认领(与 Web 端 notes/upload 同一套)。
  Attachment::save 只回 id 不回 key ⇒ 反查 Note::ATTACH=0 的 list 拿 key。
· dispatch 里 notes case 从 notes($id,$method) 改成 notes($id,$sub,$method),
  否则 $sub='upload' 根本传不进去(405)。

判据:
· _i23_note_image_api.py  11/0(上传返 key · 带 blocks 新建后 text+image
  两块都在 · PATCH 后图片块不被删 —— 这是 P0 数据丢失修复的核心证据)
· _i23_falsify.py 注掉 upload 分支 → ①② 变红(命中 2/2)
This commit is contained in:
2026-10-28 21:34:43 +08:00
parent 7c86859ab9
commit cd56a9d8fe
+47 -2
View File
@@ -111,7 +111,7 @@ final class ApiHandler
case 'ledger': $this->ledger($id, $sub, $method); return;
case 'resources': $this->resources($id, $method); return;
case 'books': $this->books($id, $sub, $method); return;
case 'notes': $this->notes($id, $method); return;
case 'notes': $this->notes($id, $sub, $method); return;
// 🔴 v45:登录(手机号 + 密码 → API 令牌)。
// 为什么必须走 API 而不是网页登录后复制 Cookie:
// App 是 Bearer 令牌那套,没有会话;而原先前端只能让人
@@ -2878,9 +2878,54 @@ final class ApiHandler
]));
}
private function notes(int $id, string $method): void
private function notes(int $id, string $sub, string $method): void
{
[$fam, $pid] = Api::me();
/* ═══════════════════════════════════════════════════════════
工单 #23(用户 2026-10-28「鸿蒙端对齐网页端」):笔记图片块。
端上要"图文混排"(往笔记里插图),缺两步:
· 上传图片拿附件 key → 这里补 `POST /api/v1/notes/upload`
· 拿到的 key 作为 `image` 块塞进 `blocks` 存 —— `noteBlocksIn()`
本来就认 `blocks` 数组(见 `cleanBlocks` 的 image 分支),
`Note::claimUploads` 也会按块里的 key 把"草稿附件"认领到笔记上。
🔴 上传是**草稿态**、**不依赖笔记 id**(新建笔记时还没有 id):
图片先挂在 target_id=0,等 `blocks` 带 key 提交时再认领——
与 Web 端 `notes/upload` 路由同一套语义。
⚐ 注意:`Notes/upload` 的 `$sub='upload'`、`$id=0`,所以这里必须
放在 `if ($id > 0)` **之前**(否则新建笔记插不了图)。
═══════════════════════════════════════════════════════════ */
if ($sub === 'upload') {
Api::requireWrite();
if ($method === 'POST') {
$file = $_FILES['file'] ?? null;
if (!is_array($file)) {
Api::fail('没有收到文件。', 'unprocessable', 422);
}
$r = \App\Attachment::save($fam, Note::ATTACH, 0, $file, $pid);
if (empty($r['ok'])) {
$msg = (string)($r['error'] ?? '');
Api::fail($msg !== '' ? $msg : '图片没存下来', 'unprocessable', 422);
}
// 🔴 `Attachment::save` 只回 id 不回 key ⇒ 反查一行拿 key
// (与 Web 端 `notes/upload` 路由同一招,见 index.php 那段)。
$key = '';
foreach (\App\Attachment::list(Note::ATTACH, 0) as $a) {
if ((int)$a['id'] === (int)($r['id'] ?? 0)) {
$key = (string)$a['key'];
break;
}
}
Api::ok([
'key' => $key,
'id' => (int)($r['id'] ?? 0),
], 201);
}
Api::fail('不支持的方法。', 'method_not_allowed', 405);
}
if ($id > 0) {
$n = Note::find($fam, $id, $pid);
if (!$n) { Api::notFound('这条笔记'); }