From b6dc60452a97544c55d04aac1b6ebc1c093c52d5 Mon Sep 17 00:00:00 2001 From: laoyang Date: Wed, 28 Oct 2026 22:29:55 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=B5=E5=BD=B1=E5=BD=B1=E8=AF=84+=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=EF=BC=9A=E4=BF=AE=E5=8D=8A=E6=88=90=E5=93=81=E5=B9=B6?= =?UTF-8?q?=E8=A1=A5=E5=85=A8=20/movies//{reviews,review,comments}=20#?= =?UTF-8?q?27?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户原话(2026-10-28):「鸿蒙端对齐网页端」。观影详情只能看平均分, 不能写影评/评论。盘点发现服务端其实有份**半成品且带 bug**: · comments GET 写死"只有我写过影评才返回" —— 错,改为独立 reviews 端点; · comments POST 调 addComment 参数**完全错位**(parent_id 传给了 feelings、 target_person 传给了 parentId)—— 评论实际存不进; · 缺影评/评论的删除端点。 现在对齐 Web 端 reviews 四条路由: · GET /movies//reviews 全部影评(含评论树)+ 我的影评 + 统计 + 感受目录 · POST /movies//review 写/改我的影评 · DELETE /movies//review 删我的影评 · POST /movies//comments 评论/回复(review_id/content/parent_id) · DELETE /movies//comments?comment_id=N 删我的评论 判据:_i27_movie_review_api.py 22/0(写/改影评 · 列表含 mine · 评论/回复 · 删评论级联 · 删影评 · 越权删别人影评被拒),_i27_falsify.py 注掉写分支变红。 --- app/ApiHandler.php | 115 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 26 deletions(-) diff --git a/app/ApiHandler.php b/app/ApiHandler.php index 1ed663d..6ea1ade 100644 --- a/app/ApiHandler.php +++ b/app/ApiHandler.php @@ -2220,35 +2220,98 @@ final class ApiHandler ]); } - if ($sub === 'comments') { + /* ──────────────────────────────────────────────────────────────── + 🔴 工单 #27(用户 2026-10-28「鸿蒙端对齐网页端」):影评 + 评论。 + + 这里原来是一份**半成品且带 bug**: + · `comments` GET 写死"只有我写过影评才返回"——错,应返回全部; + · `comments` POST 只能评论"我自己的影评",且 `addComment` 参数 + **完全错位**(`parent_id` 传给了 `feelings`、`target_person` + 传给了 `parentId`)——评论实际上存不进; + · 缺影评/评论的**删除**端点。 + + 现在对齐 Web 端 `movies/review_save / review_del / comment_add / + comment_del` 四条路由,端点是: + GET /movies//reviews 全部影评(含评论)+ 我的影评 + POST /movies//review 写/改我的影评(rating/content/feelings) + DELETE /movies//review 删我的影评 + POST /movies//comments 评论/回复某条影评(review_id/content/parent_id) + DELETE /movies//comments?comment_id=N 删我的评论 + + ⚠️ review_id / comment_id 走 body 或 query(`Api::param`) + —— 路由只解析三段,第 4 段会被丢(tasks 同款教训)。 + ⚠️ 评论归属校验:`addComment` 内部已判 review_id 归属, + 拒跨影评评论(见 MovieReview::addComment)。 + ──────────────────────────────────────────────────────────────── */ + if ($sub === 'reviews') { if ($method === 'GET') { - $rv = MovieReview::mine($id, $pid); - Api::ok($rv ? MovieReview::forEvent($id) : []); - } - if ($method === 'POST') { - Api::requireWrite(); - $rv = MovieReview::mine($id, $pid); - if (!$rv) { Api::fail('先写一条评价,才能评论。', 'unprocessable', 422); } - $txt = Api::str('content', '', 4000); - if ($txt === '') { Api::fail('评论不能是空的。', 'unprocessable', 422); } - $cid = MovieReview::addComment((int)$rv['id'], $pid, $txt, - Api::int('parent_id'), Api::str('target_person', '')); - if ($cid <= 0) { Api::fail('评论没保存下来(可能内容太长)。', 'unprocessable', 422); } - Api::ok(['id' => $cid], 201); + Api::ok([ + 'items' => MovieReview::forEvent($id), + 'mine' => MovieReview::mine($id, $pid), + 'stats' => MovieReview::stats($id), + 'catalog' => MovieReview::CATALOG, + ]); } } - if ($sub === 'review' && ($method === 'POST' || $method === 'PUT' || $method === 'PATCH')) { - Api::requireWrite(); - $my = MovieReview::mine($id, $pid); - $err = MovieReview::save( - $id, $fam, $pid, - Api::int('rating'), - Api::str('content', '', 8000), - array_values((array)Api::param('feelings', [])), - $my !== null - ); - if ($err !== '') { Api::fail($err, 'unprocessable', 422); } - Api::ok(MovieReview::mine($id, $pid)); + if ($sub === 'review') { + if ($method === 'GET') { + Api::ok(MovieReview::mine($id, $pid)); + } + if ($method === 'POST' || $method === 'PUT' || $method === 'PATCH') { + Api::requireWrite(); + $my = MovieReview::mine($id, $pid); + $err = MovieReview::save( + $id, $fam, $pid, + Api::int('rating'), + Api::str('content', '', 8000), + array_values((array)Api::param('feelings', [])), + $my !== null + ); + if ($err !== '') { Api::fail($err, 'unprocessable', 422); } + Api::ok(MovieReview::mine($id, $pid)); + } + if ($method === 'DELETE') { + Api::requireWrite(); + $my = MovieReview::mine($id, $pid); + if ($my === null) { + Api::fail('你没有这条记录的评价。', 'not_found', 404); + } + if (!MovieReview::deleteMine((int)$my['id'], $pid)) { + Api::fail('只能删除自己的评价。', 'forbidden', 403); + } + Api::ok(['deleted' => (int)$my['id']]); + } + Api::fail('不支持的方法。', 'method_not_allowed', 405); + } + if ($sub === 'comments') { + if ($method === 'POST') { + Api::requireWrite(); + $reviewId = Api::int('review_id'); + if ($reviewId <= 0) { + Api::fail('要评论哪条评价?缺 `review_id`。', 'unprocessable', 422); + } + $txt = Api::str('content', '', 4000); + if ($txt === '') { Api::fail('评论不能是空的。', 'unprocessable', 422); } + $cid = MovieReview::addComment( + $reviewId, $pid, $txt, + array_values((array)Api::param('feelings', [])), + Api::int('parent_id') + ); + if (is_string($cid)) { Api::fail($cid, 'unprocessable', 422); } + Api::ok(['id' => $cid], 201); + } + if ($method === 'DELETE') { + Api::requireWrite(); + $cid = Api::int('comment_id'); + if ($cid <= 0) { + Api::fail('要删哪条评论?缺 `comment_id`。', 'unprocessable', 422); + } + if (!MovieReview::deleteComment($cid, $pid)) { + Api::fail('只能删除自己的评论。', 'forbidden', 403); + } + Api::ok(['deleted' => $cid]); + } + Api::fail('不支持的方法。', 'method_not_allowed', 405); } if ($method === 'GET') { Api::ok($m); }