Change Status Reviews
對應 legacy API:
/quote_bids/change_status/reviews/{quote_bid_id}.json快速結論
- 用途:新增 / 更新 quote feedback。
- actor:consumer 可評 provider;provider 可評 consumer。
- legacy controller:
/Users/mattsu/Documents/Site/get-lancer-php56/app/Plugin/Quotes/Controller/QuoteBidsController.php的change_status($filter, $quote_bid_id),$filter == 'reviews'分支。 - legacy review 寫入:同 controller 內呼叫
save_review($user_id, $quote_bid)。 - new API:
Endpoint/V1/QuoteBids.php::change_status_reviews() - helper:
PRO360\ActionHelper\QuoteBid\ChangeStatusReviewsHelper - routing:
Lib/Common/RouterRule/Mapping.php - 狀態:一般 review 流程已補齊;
is_completed/is_closed狀態流仍拆到 completed_closed.md。 - 2026-05-12 正式機
get-lancer_access.log*查詢:reviews/153 筆;其中is_completed=10 筆,is_closed=10 筆。
Request
POST /quote_bids/change_status/reviews/{quote_bid_id}.json常用欄位:
| 欄位 | 說明 |
|---|---|
is_satisfied | 評分,通常 1 到 5;一般 review 流程必填 |
feedback | 評價文字 |
tag | 評價 tag array;低分評價會過濾合法 tag 後寫入負評通知內容 |
FeedbackAttachment | 評價附件 URL array;最多取前 3 筆,且只接受 legacy allowlist 內的 CloudFront URL |
Request / Response 範例
Consumer 評 provider
curl 'https://api-staging.pro360.com.tw/quote_bids/change_status/reviews/{quote_bid_id}.json' \
-H 'x-pro360-rest-api-key: {api_key}' \
-H 'x-pro360-user-session-token: {consumer_session_token}' \
-F 'is_satisfied=5' \
-F 'feedback=服務很好,溝通清楚' \
-F 'FeedbackAttachment[]=https://dfv9n9rpncj8c.cloudfront.net/review/a.jpg'response:
{
"status": "Success"
}主要結果:
quote_feedbacks.user_id = quote_requests.user_idquote_feedbacks.reviewed_user_id = quote_bids.provider_user_idquote_feedbacks.is_creator = 1- 寫入 / 同步
quote_feedback_attachments - 建立
AddReviewactivity - 插入
event_queue.New_Review,或已被 worker 搬到event_queue_log_1到event_queue_log_4
Provider 評 consumer
curl 'https://api-staging.pro360.com.tw/quote_bids/change_status/reviews/{quote_bid_id}.json' \
-H 'x-pro360-rest-api-key: {api_key}' \
-H 'x-pro360-user-session-token: {provider_session_token}' \
-F 'is_satisfied=4' \
-F 'feedback=客戶需求明確,溝通順利'response:
{
"status": "Success"
}主要結果:
quote_feedbacks.user_id = quote_bids.provider_user_idquote_feedbacks.reviewed_user_id = quote_requests.user_idquote_feedbacks.is_creator = 0- 建立
AddReviewactivity - 插入
event_queue.New_Review,或已被 worker 搬到event_queue_log_1到event_queue_log_4
更新既有評價
同一個 actor 對同一筆 quote_bid_id 再次送出評價時,更新原本的 quote_feedbacks row。
curl 'https://api-staging.pro360.com.tw/quote_bids/change_status/reviews/{quote_bid_id}.json' \
-H 'x-pro360-rest-api-key: {api_key}' \
-H 'x-pro360-user-session-token: {session_token}' \
-F 'is_satisfied=3' \
-F 'feedback=更新後的評價內容'response:
{
"status": "Success"
}主要結果:
quote_feedbacks不新增第二筆,改更新既有 row- 建立
UpdateReviewactivity - 插入
event_queue.Update_Review,或已被 worker 搬到event_queue_log_1到event_queue_log_4
低分評價
curl 'https://api-staging.pro360.com.tw/quote_bids/change_status/reviews/{quote_bid_id}.json' \
-H 'x-pro360-rest-api-key: {api_key}' \
-H 'x-pro360-user-session-token: {consumer_session_token}' \
-F 'is_satisfied=1' \
-F 'feedback=沒有依約完成' \
-F 'tag[]=late'response:
{
"status": "Success"
}額外結果:
- 寄送 badReview admin email
- 寫入
user_mute_matches - 寫入
user_mute_match_logs
未聯繫 provider 前評價
quote_bids.is_want_to_contact_provider = 0 時拒絕。
{
"error": 19,
"status": "您必須先與專家聯繫後才能評價專家"
}缺少 is_satisfied
一般 review 流程沒有帶 is_satisfied 時拒絕。
{
"status": "error"
}Actor / Guard
允許:
quote_requests.user_id == session user id:consumer 評 provider。quote_bids.provider_user_id == session user id:provider 評 consumer。
拒絕:
| 條件 | response |
|---|---|
| actor 不是 consumer / provider | error=1,status=發生錯誤,請稍後再試 |
quote_bids.is_want_to_contact_provider = 0 | error=19,status=您必須先與專家聯繫後才能評價專家 |
is_satisfied 空值 | status=error |
成功流程
- 更新 read timestamp:
- consumer 評 provider:
quote_bids.requestor_last_read_on = now - provider 評 consumer:
quote_bids.provider_last_read_on = now
- consumer 評 provider:
- 建立或更新
quote_feedbacks:- consumer 評 provider:
user_id = requestor,reviewed_user_id = provider,is_creator = 1 - provider 評 consumer:
user_id = provider,reviewed_user_id = requestor,is_creator = 0 - 同一組
user_id + reviewed_user_id + foreign_id已存在時更新原 row。
- consumer 評 provider:
- 同步
quote_feedback_attachments:- 最多取
FeedbackAttachment前 3 筆。 - 只接受 legacy allowlist 內的 CloudFront URL。
- 已存在但本次未傳入的附件會改
is_active = 0。
- 最多取
- 更新統計:
- user feedback stats
- service feedback stats
- quote service profile score
- 寫
quote_activities:- 新增評價:
AddReview - 更新評價:
UpdateReview
- 新增評價:
- 若 provider 已封鎖 consumer,保留 feedback / stats / activity,但不送 review notification event。
- 送 review notification event:
- 新增評價:
event_queue.New_Review - 更新評價:
event_queue.Update_Review - queue 可能已被 worker 搬到
event_queue_log_1到event_queue_log_4,測試與排查要接受 queue / log 任一存在。
- 新增評價:
- 送 review auto message:
- 1 星:
review1+review0 - 5 星:
review5+review0 - 其他分數:
review0
- 1 星:
低分評價
is_satisfied = 1 或 2 時,額外 side effect:
- 寄送 badReview admin email。
- 依
QuoteFeedbackTag::getValidTags($is_satisfied)過濾 tag 後放入 email 內容。 - 寫入
user_mute_matches:provider_user_id = quote_bids.provider_user_idconsumer_user_id = quote_requests.user_idis_pro = 0
- 寫入
user_mute_match_logs。
設定來源 / 環境差異檢查
這支 API 不走報價付款流程,因此不需要檢查 TapPay / Stripe / wallet 相關設定。
會用到的設定來源:
| 設定 | 用途 | 新專案來源 | staging 參考值 |
|---|---|---|---|
site.url | review email / web push target URL | ProConfig::get('site.url'),通常在 Lib/Common/Configuration.php | https://staging.pro360.com.tw |
api.url | ShortUrl::addShortUrl() 產生 /click/{hash} 短網址;##CHATROOM_PROVIDER_URL## 會依此產生 {api.url}/click/{hash} | ProConfig::get('api.url') | https://api-staging.pro360.com.tw |
EmailTemplate.from_email | badReview admin email 預設寄件者 | ProConfig::get('EmailTemplate.from_email') | notification@pro360.email |
site_environment | EmailQueue::sendEmail() 在非 production subject 前加環境名稱 | Lib/Common/Configuration.php define / ConstantUtil::get('site_environment') | staging |
email_admin_lists | badReview admin email 收件人 | DB:EmailAdminList::getEmailLists('badReview') | email_type=badReview 目前有 mei-te@pro360.com.tw |
2026-05-13 staging 確認:
Lib/Common/Configuration.php:site.url=https://staging.pro360.com.tw、api.url=https://api-staging.pro360.com.tw、EmailTemplate.from_email=notification@pro360.email、site_environment=staging。- 本機容器可為了 local web 測試覆寫
site.url=http://localhost:12347;migration 對 staging / production 驗證時以部署環境設定為準。 email_templates.name = 340 Feedback Received Notification存在且is_active=1。push_templates.name IN (push.content.review.new, push.content.review.update)存在且is_active=1。
若 staging / production 行為不同,優先確認:
Lib/Common/Configuration.php是否有正確的site.url/api.url/EmailTemplate.from_email。- badReview 收件名單來源是
EmailAdminList::getEmailLists('badReview'),不是寫死在此 helper。 - notification template key 由 event worker 後段解析;API 只負責插入
New_Review/Update_Reviewevent。 Endpoint/V1/EventQueue.php::index()會依時間分片寫event_queue_log_1到event_queue_log_4,不要只查舊的event_queue_log主表。
新舊程式對應
以下行號是本次 migration 實作後的定位,用來追 code review / 後續補差異;若檔案再改動,行號可能會漂移,仍以區塊職責為準。
Legacy PHP 5.6
主要入口:
/Users/mattsu/Documents/Site/get-lancer-php56/app/Plugin/Quotes/Controller/QuoteBidsController.phpchange_status(..., reviews, ...) 分支:
| legacy 行號 | 職責 |
|---|---|
3959-3965 | 進入 filter == reviews;初始化 is_new / is_updated,reviewed_user_id = other_user_id。 |
3967-3975 | JSON request 若沒有 is_completed 且沒有 is_satisfied,直接回 {"status":"error"}。 |
3978-3983 | is_want_to_contact_provider = 0 時回 error=19,禁止未聯繫先評價。 |
3986-3997 | 取 feedback / is_satisfied,並依 actor 更新 requestor_last_read_on 或 provider_last_read_on。 |
3999-4013 | 查既有 quote_feedbacks,判斷這次是新增或更新。 |
4015-4022 | 一般 review 主線:is_satisfied 有值時呼叫 save_review($user_id, $quote_bid)。 |
4024-4056 | 1 / 2 星低分評價:過濾合法 tag、寄 badReview admin email、寫 UserMuteMatch。 |
4087-4096 | 依星等送 review auto message:1 星 / 5 星加專屬 key,最後都加 review0。 |
4098+ | is_completed / is_closed 狀態流;這批不混在一般 review helper,另見 completed_closed.md。 |
save_review($user_id, $quote_bid):
| legacy 行號 | 職責 |
|---|---|
5232-5244 | 判斷 provider / consumer actor,決定 reviewed_user_id 與 is_creator。 |
5246-5256 | 用 user_id + reviewed_user_id + foreign_id 查既有評價。 |
5258-5275 | 載入附件 model、取 feedback、取並過濾合法 tag。 |
5280-5309 | 新增或更新 quote_feedbacks;同一 actor 對同一 bid 不新增第二筆。 |
5310-5313 | 更新 user / service feedback 統計。 |
5314-5346 | 同步 quote_feedback_attachments:最多 3 張、UploadUtil::filterByValidUrl() allowlist、未出現在本次 request 的舊附件設 is_active = 0。 |
5348-5350 | 更新 quote service profile score。 |
5352-5369 | 建立 AddReview 或 UpdateReview activity;consumer 評 provider 時也更新 provider unread。 |
5372-5377 | provider 已封鎖 consumer 時,評價仍保存,但不送 review notification event。 |
5379-5433 | 組 email / push / web push 內容;新增用 New_Review,更新用 Update_Review。 |
5435-5493 | 建立 notification task payload,寫入 EventQueue::insertEvent($event_type, ...)。 |
New PHP 8.2
route / endpoint:
| new 檔案行號 | 職責 |
|---|---|
Lib/Common/RouterRule/Mapping.php:157-160 | explicit route:quote_bids/change_status/reviews/{id}.json -> change_status_reviews。 |
Endpoint/V1/QuoteBids.php:2926-2941 | API action:驗 session、取 quote_bid_id / post body,呼叫 ChangeStatusReviewsHelper,回 JSON。 |
helper:
Lib/ActionHelper/QuoteBid/ChangeStatusReviewsHelper.php| new 行號 | 對應 legacy | 職責 |
|---|---|---|
41-64 | 3959-3965, actor guard | 讀 bid / request,確認 actor 是 consumer 或 provider;不符合回 error=1。 |
65-69 | 3978-3983 | 未聯繫 provider 前回 error=19。 |
70-72 | 3967-3975 | 一般 review 缺 is_satisfied 時回 {"status":"error"}。 |
80-97 | 3986-4022 | 更新 read timestamp,呼叫新版 save_review()。 |
100-126 | 4024-4056 | 1 / 2 星低分評價:合法 tag、badReview email、UserMuteMatch。 |
128-136 | 4087-4096 | review auto message。 |
140 | legacy reviews success response | 成功 response 必須維持 {"status":"Success"},不可自行加 error:0。 |
142-145 | legacy error branches | 錯誤 response 維持 error + status。 |
save_review() 對照:
| new 行號 | 對應 legacy | 職責 |
|---|---|---|
150-160 | 5232-5244 | 判斷 provider / consumer actor,決定 reviewed_user_id 與 is_creator。 |
162-168 | 5246-5256 | 用 user_id + reviewed_user_id + foreign_id 查既有評價。 |
170-182 | 5261-5275 | 正規化 feedback / star / tag。 |
184-218 | 5280-5309 | 新增或更新 quote_feedbacks,並取得 quote_feedback_id。 |
218, 374-423 | 5314-5346 | 同步 feedback attachments。 |
220-224 | 5310-5313, 5348-5350 | 更新 user / service feedback stats 與 profile score。 |
226-279 | 5352-5369 | 建立 AddReview / UpdateReview activity;consumer 評 provider 時處理 provider archived 與 unread。 |
281-283 | 5372-5377 | blocked chat 時保留評價與 activity,但跳過 review notification event。 |
285-329 | 5379-5433 | 組 email / push / web push replace content,決定 New_Review 或 Update_Review。 |
332-371 | 5435-5493 | 建立 task payload,寫入 EventQueue::insertEvent($event_type, ...)。 |
374-423 | 5314-5346 | 附件同步:新增不存在的 URL,未傳入的既有 URL 設 is_active = 0。 |
425-450 | UploadUtil::filterByValidUrl() | 新版以 helper 內 allowlist 重建 legacy 可接受 CloudFront 網域。 |
Response contract 注意
這支 API 和 self_hire 不同:legacy 一般成功只回:
{
"status": "Success"
}不要因為其他 change_status filter 會回 {"status":"Success","error":0} 就套到 reviews。測試必須鎖住「成功不含 error」。
PHPUnit
docker exec -w /project-data cd63f9147e8d vendor/bin/phpunit tests/QuoteBidsChangeStatusReviewsTest.php覆蓋:
- consumer 評 provider
- provider 評 consumer
- 未聯繫 provider 前拒絕評價
- 更新既有評價
- 低分評價的 mute match 與 badReview email
- feedback attachment 同步
- blocked chat user 不送 review event
test 對照:
| test 行號 | 職責 |
|---|---|
tests/QuoteBidsChangeStatusReviewsTest.php:142-178 | consumer 評 provider:response 不含 error、feedback 欄位、read timestamp、activity、New_Review。 |
tests/QuoteBidsChangeStatusReviewsTest.php:180-236 | feedback attachment:最多 3 張、allowlist、更新時停用未傳入附件。 |
tests/QuoteBidsChangeStatusReviewsTest.php:238-261 | blocked chat:仍保存 feedback,但不送 review event。 |
tests/QuoteBidsChangeStatusReviewsTest.php:263-297 | provider 評 consumer:actor / reviewed user / read timestamp / activity / event。 |
tests/QuoteBidsChangeStatusReviewsTest.php:299-318 | 未聯繫 provider 前回 error=19,不寫 feedback。 |
tests/QuoteBidsChangeStatusReviewsTest.php:320-354 | 更新既有評價:維持一筆 feedback,建立 UpdateReview 與 Update_Review event。 |
tests/QuoteBidsChangeStatusReviewsTest.php:356-392 | 低分評價:建立 user_mute_matches 與 user_mute_match_logs。 |
Legacy gap
legacy reviews 分支還包含狀態流,但這批不混在一般 review helper:
is_completed = 1的 work completed 狀態流is_closed = 1的 payment completed / closed 狀態流- Wallet / Sudopay 完工或結案付款處理
completed / is_closed 詳細見 completed_closed.md。