观影照片支持 DELETE 单删(暴露已存在的 Movie::deletePhoto)(#86)

用户:编辑观影记录的时候,那个图片应该可以被删除吧?现在没有删除按钮。

Movie::deletePhoto() 早就存在(连存储对象一起删,含视频 thumb_key),
网页端 movies/photo_del 也一直在用 —— 缺的只是 API 那一层。

- /movies/<id>/photos 从\"只 POST\"扩为 POST 追加 + DELETE 删除;
- photo_id 走 query:Api::dispatch() 只解析到 parts[2],parts[3] 会被
  静默丢弃;与 trips/<id>/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 通过。
This commit is contained in:
2026-11-06 11:10:07 +08:00
parent c6e4191bb3
commit 94a557cd2e
+45
View File
@@ -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/<id>/photos`),再往后会被丢掉。
同类做法见 `trips/<id>/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();