From 94a557cd2e9a521e52aefc2a7362e71b7d2b55de Mon Sep 17 00:00:00 2001 From: laoyang Date: Fri, 6 Nov 2026 11:10:07 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=82=E5=BD=B1=E7=85=A7=E7=89=87=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20DELETE=20=E5=8D=95=E5=88=A0=EF=BC=88=E6=9A=B4?= =?UTF-8?q?=E9=9C=B2=E5=B7=B2=E5=AD=98=E5=9C=A8=E7=9A=84=20Movie::deletePh?= =?UTF-8?q?oto=EF=BC=89(#86)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户:编辑观影记录的时候,那个图片应该可以被删除吧?现在没有删除按钮。 Movie::deletePhoto() 早就存在(连存储对象一起删,含视频 thumb_key), 网页端 movies/photo_del 也一直在用 —— 缺的只是 API 那一层。 - /movies//photos 从\"只 POST\"扩为 POST 追加 + DELETE 删除; - photo_id 走 query:Api::dispatch() 只解析到 parts[2],parts[3] 会被 静默丢弃;与 trips//nodes/attach 同一口径; - 删除前显式校验归属(id + event_id 同查),不匹配就 404 —— deletePhoto 内部遇到错配是\"删 0 行 + 静默成功\",会让调用方误以为删掉了; - poster 仍只 POST:换海报是覆盖语义,清空走 PATCH 空 poster_key。 验证:event_id 错配查询返回 false(越权被拦);临时图入库后删除, 5→4 张、库行消失、Storage::exists true→false(驱动 OSS)。 已部署,md5 核对 b50db4dfff743c53c9e4988d694b202b,php -l 通过。 --- app/ApiHandler.php | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/app/ApiHandler.php b/app/ApiHandler.php index b992724..2843c1d 100644 --- a/app/ApiHandler.php +++ b/app/ApiHandler.php @@ -2261,6 +2261,51 @@ final class ApiHandler if (!$m) { Api::notFound('这条观影记录'); } if ($sub === 'poster' || $sub === 'photos') { + /* 🔴 2026-11-06:`photos` 现在同时支持 **POST(追加)** 与 + **DELETE(删一张)** —— 用户「编辑观影记录的时候,那个图片 + 应该可以被删除吧?现在没有删除按钮。」 + ⚠️ `poster` 只有 POST(换海报是覆盖语义,没有"删海报"这个操作 —— + 清空海报请用 PATCH 传空 `poster_key`,那条路由本来就支持)。 + ⚠️ DELETE 的 `photo_id` 走 query:`Api::dispatch()` 只解析到 + 路径第三段(`movies//photos`),再往后会被丢掉。 + 同类做法见 `trips//nodes/attach`。 */ + if ($sub === 'photos' && $method === 'DELETE') { + Api::requireWrite(); + $photoId = (int)Api::int('photo_id'); + if ($photoId <= 0) { + Api::fail('要说明删哪一张(photo_id)。', 'bad_request', 400); + } + /* ⚠️ 先确认这张照片确实属于这条记录,再删 —— + 否则传一个别的记录的照片 id 也能删掉(越权删)。 + `deletePhoto` 内部是 `WHERE id = ? AND event_id = ?`, + 错配时它删 0 行、什么也不做,但那样**静默成功**会误导调用方, + 所以这里显式校验一次,删不到就报 404。 */ + $st = Database::get()->prepare( + 'SELECT id FROM event_photos WHERE id = ? AND event_id = ?' + ); + $st->execute([$photoId, $id]); + if (!$st->fetchColumn()) { + Api::notFound('这张照片'); + } + Movie::deletePhoto($fam, $id, $photoId); + Api::ok([ + 'deleted' => $photoId, + 'photos' => array_map( + static fn(array $p): array => [ + 'id' => (int)$p['id'], + 'key' => (string)$p['key'], + 'kind' => (string)($p['kind'] ?? 'image'), + 'url' => Storage\Storage::url($fam, (string)$p['key']), + 'thumb' => !empty($p['thumb_key']) + ? Storage\Storage::url($fam, (string)$p['thumb_key']) + : '', + 'mime' => (string)($p['mime'] ?? ''), + ], + Movie::photos($id) + ), + ]); + } + if ($method !== 'POST') { Api::fail('不支持的方法。', 'method_not_allowed', 405); } Api::requireWrite();