POST /quote_feedback_comments/save/{feedback_id}.json
狀態:PHP 8 本地實作與測試完成,Staging 尚未驗證。
最後更新:2026-07-31
快速結論
- 功能是新增或更新 feedback comment;不是只新增。
- session user 必須是該 feedback 的
user_id或reviewed_user_id。 - 更新時還要是 comment owner,且 comment 必須屬於 URL 指定 feedback。
- 只用
trim()判斷空值;DB 保存原始comment,不可自行 trim、escape 或過濾 HTML。 - 不限制同一 feedback 的 comment 數量,也不檢查 feedback 狀態/公開狀態。
- 成功固定回
{"error":0,"status":"Success"};controller 內所有 business error 對外遮成error=1。
Route、caller 與流量
正式 caller 路徑:
POST /quote_feedback_comments/save/{feedback_id}.json- 案件聊天室入口:
web-app/modules/routes.js:669-674、web-app/modules/components/dashboard/works/ChatRoomVendor.js:345-355、web-app/modules/components/dashboard/ChatComponents.js:640-677、web-app/modules/components/dashboard/works/RequestDetail.js:887-897,943-945;專家在消費者評價卡片點「回覆評價/修改回覆」後送出。 - 專家服務評價入口:
web-app/modules/components/service/ServiceEditingContainer.js:575-584、web-app/modules/components/serviceProfileUnits/ReviewUnit.js:14-39、web-app/modules/containers/ServicesContainer.js:653-670、web-app/modules/components/service/ReviewList.js:238-290,405-420;只有「案件評價」且尚無 comment 時顯示回覆按鈕。 web-app/modules/components/dashboard/Review.js:47-54,204-213、VendorReviews.js:84-94最終呼叫api-manager.js:2172-2180,FormData只傳comment。- 現行 web caller 不傳 comment
id,因此 UI 只會走 create;update branch 需用 curl 或其他 client 驗證。「商品評價」改走/new/v1/nshop_order_feedbacks/add_comment。 - Production 2026-06-29~2026-07-28:751 POST calls,全部命中 Legacy。
- 2026-07-31 掃描 Legacy host
ip-10-5-2-19全部保留的get-lancer_access.log*:有實際 save流量,非.json為 0 筆;其他 instances/ALB 尚待確認。
PHP 8 已使用明確且 anchored mapping:
^quote_feedback_comments/save/([0-9]+)\.json$
→ QuoteFeedbackComments::save / feedback_id / ${1}Mapping 排在 generic File 前;unknown action、錯誤 path 與非數字 ID 已有 negative test。
程式位置
Legacy
Root:/Users/mattsu/Documents/Site/get-lancer-docker-2/get-lancer
app/Plugin/Quotes/Controller/QuoteFeedbackCommentsController.phpbeforeFilter():8-18。save():25-104。- session/request log:28-35。
- feedback、update ownership guard:39-64。
- customer/provider guard:66-72。
- save data:74-85。
- response/error log:87-102。
app/Plugin/Quotes/Model/QuoteFeedbackComment.php:2-18:只有belongsTo User,無 custom validation/callback。app/Plugin/Quotes/Model/QuoteFeedback.php:5-77:feedback association;本 action 只使用 feedback row 的 id 與雙方 user id。app/Controller/RestApiHelper.php- API key cache:12-43。
- session/Redis/auth side effect:75-153。
- user id 與 raw request info log:162-182。
app/Controller/AppController.php- JSON API key guard:145-181。
- request data:1233-1239。
- success JSON:1307-1311。
- API error response/warning log:1320-1343。
- Cake timestamp:
core/lib/Cake/Model/Model.php:697-753。
PHP 8
Endpoint/V1/QuoteFeedbackComments.phpsave():21-92。.json副檔名 guard:132-135。- Legacy session/cache/access side effect:137-249。
- auth/action error response:222-265。
- Legacy request/error log:266-318。
- feedback id、comment empty 語意:334-373。
Lib/Model/QuoteFeedbackComment.php- comment lookup:17-34。
- create/update:36-75。
Lib/Common/RouterRule/Mapping.php:156-159:anchored save mapping。tests/QuoteFeedbackCommentsSaveDeleteTest.php:105-559:save/delete 共用 integration tests。- 不需新增
QuoteFeedbackCommentsDO;本 action 不回 comment row。
逐段對照
| 階段 | Legacy | PHP 8 現況/要求 |
|---|---|---|
| guard | Legacy 可由 .json 或 Accept 判定 JSON;另驗 API key、session | PHP 8 只接受 .json;其餘 auth 已對齊 |
| input | URL feedback id;POST comment、optional id | 已對齊 PHP 5.6 cast/empty語意 |
| DB read | feedback;update時 comment | 已對齊;未加狀態條件 |
| DB write | create/update一筆 comment | 已對齊欄位與 timestamp |
| payment | 不適用 | 不可新增 |
| queue | endpoint無;auth cache miss可能 returnedQuoteService | 已保留 Legacy auth 時機 |
| activity | 不適用 | 不可新增 |
| notification | 不適用 | 不可新增 |
| log | request info;error時 debug/critical+warning | Legacy message/context 已保留;generic 差異待決策 |
| response | success固定;business error遮成 code 1 | 已對齊並測試型別 |
Migration decision(2026-07-31,reviewer 已接受):PHP 8 不搬 Legacy RequestHandler->prefers('json') 的 Accept negotiation,只接受 .json URL。Web caller 與 ip-10-5-2-19 retained logs 都只看到 .json;無副檔名即使帶 Accept: application/json 仍回 {"error":1,"status":"invalid request"},且不寫入 comment。
執行順序與 guard
- Request URL 必須以
.json結尾,且通過 API key/session。 RestApiHelper::getUserId()取得 session user,並先寫 endpoint request info log。comment經 PHP 5.6empty(trim(...))判斷;通過後仍保存原值。- 以 URL
feedback_id查 feedback;不加其他條件。 - POST
idtruthy 時走更新:comment 必須存在、屬於 user、屬於 feedback。 - user 是 feedback customer 時
is_customer=1;是 reviewed provider 時為0;其他人拒絕。 - create 或 update comment;不檢查 Cake
save()回傳值。 - 回 success response。
不可新增:feedback is_public/is_rejected/model/status guard、owner 以外 role guard、單筆 comment 限制、transaction、notification 或 queue。
Input contract
| Input | Legacy 行為 |
|---|---|
URL feedback_id | Cake positional argument;找不到即 business error |
missing/null/""/空白 comment | 拒絕 |
comment="0" 或 trim 後為 "0" | PHP empty("0"),拒絕 |
| 前後有空白但內容非空 | 接受,原始空白照存 |
array comment | PHP 5.6 warning 後落入空值;PHP 8 不可變 TypeError/999 |
POST id missing、null、""、"0"、0 | create |
POST id truthy | update guard;查詢時 (int) cast |
正常 Web caller不傳 id,但 migration 必須保留既有 update branch。
Insert/update 欄位分類
| 欄位 | create 來源 | update 來源 | 備註 |
|---|---|---|---|
id | auto increment | POST id | create 不可 explicit insert |
created | Cake auto timestamp | 保留原值 | PHP 8 create 必須補現在時間 |
modified | Cake auto timestamp | Cake auto timestamp | create/update 都更新 |
quote_feedback_id | 查得的 feedback id | 同一 feedback id | 不是直接信任 request body |
comment_user_id | session user id | session user id | update 已驗 owner |
comment | 原始 POST comment | 原始 POST comment | 不 trim/escape |
is_customer | 由 feedback 雙方判斷 | 重新判斷 | customer=1、provider=0 |
表中以外欄位不得加入 write。沒有 transaction;Cake save() 結果未被檢查,需以 source test 鎖定,不能自行改成失敗 response。
SQL 語意
SELECT id, user_id, reviewed_user_id
FROM quote_feedbacks
WHERE id = :feedback_id
LIMIT 1;
-- POST id truthy 時
SELECT id, quote_feedback_id, comment_user_id
FROM quote_feedback_comments
WHERE id = :comment_id
LIMIT 1;
INSERT INTO quote_feedback_comments
(created, modified, quote_feedback_id, comment_user_id, comment, is_customer)
VALUES
(NOW(), NOW(), :feedback_id, :user_id, :raw_comment, :is_customer);
-- update
UPDATE quote_feedback_comments
SET modified=NOW(), quote_feedback_id=:feedback_id,
comment_user_id=:user_id, comment=:raw_comment,
is_customer=:is_customer
WHERE id=:comment_id;目前 Docker DB 證據:413 comments/262 feedback ids;53 個 feedback 有多筆 comment,單一 feedback 最多 25 筆。因此不可補 uniqueness guard。
Response 與 error
成功:HTTP 200,JSON。
{"error":0,"status":"Success"}action 內 business error 2~7,以及 action 內 invalid request,全部被改寫為:
{"error":1,"status":"invalid request"}未知 exception:{"error":9,"status":"invalid request"}。Invalid API key 由全域 guard 回 HTTP 401、{"error":99999,"message":"INVALID API KEY"};blocked user 為 HTTP 403、{"error":5,"message":"Blocked User"}。
Invalid session 可能先被 Legacy ACL 攔截;最終 HTTP/body 必須以 Legacy Staging 實測為準,目前標 待確認。
Side effect、log 與 config
- Domain write:只新增或更新
quote_feedback_comments。 - 無 payment、activity、notification、endpoint queue。
- Legacy auth cache miss 可能更新
users.iphone_last_access/last_access_client,並可能新增task_queue.action=returnedQuoteService;cache hit 不做。 - Legacy request info log channel 是完整 path 加
::POST,message 是 user id;context 含原始 API key 與 requestdata/form。 - business error 另寫 Cake debug raw request;unknown error寫 critical raw request;
handleProApiException()再寫 controller warning。 - PHP 8 已保留 Legacy endpoint-specific request log、error warning,以及 save exception 的原始 message/context;
QuoteFeedbackCommentexception log 使用 PHP 8 application log sink,未啟用 PHP 8 Slack handler。 - Router generic log 仍會重複記錄 API key/comment,並可能寫
client_usage_users。Legacy 沒有這個 side effect,migration decision 尚待 reviewer 確認。留言可能含個資。
Config checklist:
- 觸發條件:URL 以
.json結尾、有效 API key、有效 session。 - 必要 key:沒有 endpoint 專用 ProConfig;依賴既有 DB、Redis、
api_keys、api_sessions。 - Legacy:action 沒有
Configure::read()/URL/template/payment config。 - Staging/Production:需確認 PHP 8 與 Legacy 使用相同 session DB/Redis
api_session_*;所有 PHP 8 instances 一致。 - 缺少或不同步:會被判 invalid API/session,或改變 cache-hit side effect時機。
PHP 5.6 → 8.2 必測
trim(null)、array comment 不可在 PHP 8 變 TypeError。empty("0")、空白、raw whitespace保存。- POST
id的 truthy 與(int)cast,例如0、abc、1abc。 - DB id 型別與 Legacy strict
===/!==ownership 比較。 - Cake timestamps與未檢查
save()result。 - 無
.json即使 Accept 偏好 JSON也拒絕;另測錯誤 action與 route negative case。
測試與待辦
- PHP 8.2 syntax:通過。
- 全域
core:3 tests / 36 assertions;save 核心覆蓋有效新增,以及 owner update/non-owner/wrong feedback guard。 - Other(
--exclude-group core):16 tests / 97 assertions;保留 provider、多筆、outsider、missing feedback、empty/0/array、API key/session/blocked、inactive/cache timing、raw log、response type與 route negative。專用測試合計19 / 133。 - Regression:
quote_feedbacks/sent10/76、quote_feedback_tags/index12/68,全部通過。 - 尚待:其他 Production instances/ALB 的非
.json流量、Legacy/Staging response baseline、Staging DB write/restore、前端「送出回應」smoke test、部署 Redis 核對、generic log/client_usage_users決策。Production ALB 不在本文件修改。