POST /quote_feedback_comments/save/{feedback_id}.json

狀態:PHP 8 本地實作與測試完成,Staging 尚未驗證。
最後更新:2026-07-31

快速結論

  • 功能是新增或更新 feedback comment;不是只新增。
  • session user 必須是該 feedback 的 user_idreviewed_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-674web-app/modules/components/dashboard/works/ChatRoomVendor.js:345-355web-app/modules/components/dashboard/ChatComponents.js:640-677web-app/modules/components/dashboard/works/RequestDetail.js:887-897,943-945;專家在消費者評價卡片點「回覆評價/修改回覆」後送出。
  • 專家服務評價入口:web-app/modules/components/service/ServiceEditingContainer.js:575-584web-app/modules/components/serviceProfileUnits/ReviewUnit.js:14-39web-app/modules/containers/ServicesContainer.js:653-670web-app/modules/components/service/ReviewList.js:238-290,405-420;只有「案件評價」且尚無 comment 時顯示回覆按鈕。
  • web-app/modules/components/dashboard/Review.js:47-54,204-213VendorReviews.js:84-94 最終呼叫 api-manager.js:2172-2180FormData 只傳 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.php
    • beforeFilter():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.php
    • save():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。

逐段對照

階段LegacyPHP 8 現況/要求
guardLegacy 可由 .jsonAccept 判定 JSON;另驗 API key、sessionPHP 8 只接受 .json;其餘 auth 已對齊
inputURL feedback id;POST comment、optional id已對齊 PHP 5.6 cast/empty語意
DB readfeedback;update時 comment已對齊;未加狀態條件
DB writecreate/update一筆 comment已對齊欄位與 timestamp
payment不適用不可新增
queueendpoint無;auth cache miss可能 returnedQuoteService已保留 Legacy auth 時機
activity不適用不可新增
notification不適用不可新增
logrequest info;error時 debug/critical+warningLegacy message/context 已保留;generic 差異待決策
responsesuccess固定;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

  1. Request URL 必須以 .json 結尾,且通過 API key/session。
  2. RestApiHelper::getUserId() 取得 session user,並先寫 endpoint request info log。
  3. comment 經 PHP 5.6 empty(trim(...)) 判斷;通過後仍保存原值。
  4. 以 URL feedback_id 查 feedback;不加其他條件。
  5. POST id truthy 時走更新:comment 必須存在、屬於 user、屬於 feedback。
  6. user 是 feedback customer 時 is_customer=1;是 reviewed provider 時為 0;其他人拒絕。
  7. create 或 update comment;不檢查 Cake save() 回傳值。
  8. 回 success response。

不可新增:feedback is_publicis_rejected/model/status guard、owner 以外 role guard、單筆 comment 限制、transaction、notification 或 queue。

Input contract

InputLegacy 行為
URL feedback_idCake positional argument;找不到即 business error
missing/null""/空白 comment拒絕
comment="0" 或 trim 後為 "0"PHP empty("0"),拒絕
前後有空白但內容非空接受,原始空白照存
array commentPHP 5.6 warning 後落入空值;PHP 8 不可變 TypeError/999
POST id missing、null"""0"0create
POST id truthyupdate guard;查詢時 (int) cast

正常 Web caller不傳 id,但 migration 必須保留既有 update branch。

Insert/update 欄位分類

欄位create 來源update 來源備註
idauto incrementPOST idcreate 不可 explicit insert
createdCake auto timestamp保留原值PHP 8 create 必須補現在時間
modifiedCake auto timestampCake auto timestampcreate/update 都更新
quote_feedback_id查得的 feedback id同一 feedback id不是直接信任 request body
comment_user_idsession user idsession user idupdate 已驗 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_accesslast_access_client,並可能新增 task_queue.action=returnedQuoteService;cache hit 不做。
  • Legacy request info log channel 是完整 path 加 ::POST,message 是 user id;context 含原始 API key 與 request dataform
  • 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;QuoteFeedbackComment exception 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_keysapi_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,例如 0abc1abc
  • DB id 型別與 Legacy strict ===!== ownership 比較。
  • Cake timestamps與未檢查 save() result。
  • .json 即使 Accept 偏好 JSON也拒絕;另測錯誤 action與 route negative case。

測試與待辦

  • PHP 8.2 syntax:通過。
  • 全域 core3 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/sent 10/76、quote_feedback_tags/index 12/68,全部通過。
  • 尚待:其他 Production instances/ALB 的非 .json 流量、Legacy/Staging response baseline、Staging DB write/restore、前端「送出回應」smoke test、部署 Redis 核對、generic log/client_usage_users 決策。Production ALB 不在本文件修改。