QuoteBids Add Consumer Survey
API:
POST /quote_bids/add_consumer_survey/quote_bid_id:{quote_bid_id}.jsonquote_bid_id 是 quote_bids.id。web-app 使用 consumer session token,body 是 FormData。
功能說明
consumer 在案件後續填寫實際支付 / 預算問卷時呼叫此 API。這支只新增或覆蓋 quote_consumer_surveys,不更新 bid 狀態,也不建立 activity、queue、transaction 或通知。
成功 response:
{"error":0,"status":"success"}快速結論
- actor:quote request owner consumer session。
- legacy API:
QuoteBidsController::add_consumer_survey()。 - legacy path:
/quote_bids/add_consumer_survey/quote_bid_id:{quote_bid_id}.json。 - web-app path:
/quote_bids/add_consumer_survey/quote_bid_id:${quoteId}.json。 - web-app body:
total_pay、comment、unit。 - new API:已補
Endpoint/V1/QuoteBids.php::add_consumer_survey()。 - routing:已補
Mapping.php,支援 legacy named path。 - 驗證狀態:staging response / DB side effect 已完成對表。
- 正式機流量:2026-06-02 查詢
ip-10-5-2-242access log,POST200 有 598 筆,OPTIONS200 有 104 筆;另有 1 筆GET401。
正式機 Access Log
2026-06-02 查詢機器:ip-10-5-2-242
normalized path:
| 筆數 | method / normalized path | status |
|---|---|---|
| 598 | POST /quote_bids/add_consumer_survey/quote_bid_id:{quote_bid_id}.json | 200 |
| 104 | OPTIONS /quote_bids/add_consumer_survey/quote_bid_id:{quote_bid_id}.json | 200 |
| 1 | GET /quote_bids/add_consumer_survey/quote_bid_id:{quote_bid_id}.json | 401 |
caller 來源包含 web https://www.pro360.com.tw/、iOS consumer app Alamofire 與 Android app okhttp。
Web-App 呼叫
web-app 檔案:
/Users/mattsu/Documents/Site/web-app/modules/utils/api-manager.js:827-841APIManager.postBudgetSurvey(quoteId, budget, comment, unit):
- 組 path:
/quote_bids/add_consumer_survey/quote_bid_id:${quoteId}.json。 - 使用
FormData。 - append:
total_pay = budgetcomment = commentunit = unit
- 使用
defaultPOSTConfig(data, session_token)。
同區塊的讀取 API 是 getBudgetSurvey(),path 為 /quote_bids/get_consumer_survey/quote_bid_id:${quoteId}.json。
Legacy Rule
legacy 檔案:
/Users/mattsu/Documents/Site/get-lancer-php56/app/Plugin/Quotes/Controller/QuoteBidsController.php:6097-6137
/Users/mattsu/Documents/Site/get-lancer-php56/app/Plugin/Quotes/Model/QuoteConsumerSurvey.php:83-109controller 規則:
- 非 JSON request 或 session invalid 時,回
{"error":1,"status":"invalid request"}。 - 從 named params 取
quote_bid_id,轉成 int。 QuoteConsumerSurvey::checkQuoteBidUser(user_id, quote_bid_id):- bid 不存在時,回
{"error":2,"status":"quote_bid_not_found"}。 - session user 不是 request owner 時,回
{"error":3,"status":"not_consumer"}。
- bid 不存在時,回
- 以
quote_bid_id查既有 survey:- 沒有 row:create。
- 已有 row:帶 existing
id,覆蓋同一筆。
- 寫入欄位:
quote_request_id = quote_bids.quote_request_idquote_bid_id = quote_bid_idtotal_pay = (float)getRequestData('total_pay')unit = getRequestData('unit')comment = getRequestData('comment')
- 成功回
{"error":0,"status":"success"}。
Side Effect
同步 DB:
- insert / update
quote_consumer_surveys
不做:
- 不更新
quote_bids。 - 不更新
quote_requests。 - 不建立
quote_activities/quote_activity_consumers。 - 不寫 transaction / wallet / subscription log。
- 不送 event queue / task event queue。
- 不送 notification。
Schema 對照
quote_consumer_surveys 欄位:
| 欄位 | legacy 寫入 | new 寫入 |
|---|---|---|
quote_request_id | quote_bids.quote_request_id | 同 legacy |
quote_bid_id | path named param | 同 legacy |
total_pay | (float)total_pay;沒送時是 0 | 同 legacy |
unit | request body;沒送時是 NULL | 同 legacy |
comment | request body;沒送時是 NULL | 同 legacy |
new update 使用明確 SQL 覆蓋欄位,讓 unit / comment 在 request 缺值時可被寫回 NULL。若使用一般 DO modify(),NULL 欄位可能不會更新,會偏離 legacy save($data)。
Config 對照
這支 API 沒有讀 legacy Configure::read(...)、settings、mail / SMS / notification template、URL、icon、fee threshold 或 payment threshold。
Staging 實測
2026-06-02 staging 以同一 consumer session 實測 legacy 與 new,response 與 DB side effect 一致。
| 版本 | quote_bid_id | quote_request_id | request user | response | quote_consumer_surveys |
|---|---|---|---|---|---|
| legacy | 2147690474 | 22472 | 8617 | {"error":0,"status":"success"} | total_pay=10.00、unit=單次計費、comment=哈囉 |
| new | 2147689089 | 22195 | 8617 | {"error":0,"status":"success"} | total_pay=10.00、unit=單次計費、comment=hello |
兩筆都只新增 / 覆蓋 quote_consumer_surveys;quote_request_id 取自 quote_bids.quote_request_id,且 session user 必須是 request owner。
判斷:staging response 與 quote_consumer_surveys 寫入語意已確認與 legacy 一致;這支不再列為待 staging 對表項目。
New PHP 8.2 對照
| new 檔案行號 | 對應 legacy | 職責 | 狀態 |
|---|---|---|---|
Lib/Common/RouterRule/Mapping.php:161-164 | web-app / access log path | 將 legacy named path route 到 QuoteBids::add_consumer_survey()。 | 已補 |
Endpoint/V1/QuoteBids.php:2644-2674 | QuoteBidsController.php:6099-6106 | session guard、quote_bid_id 解析、body 解析、response mapping。 | 已補 |
Lib/Model/QuoteConsumerSurvey.php:23-73 | QuoteBidsController.php:6108-6131 | 查既有 survey,新增或覆蓋 quote_consumer_surveys。 | 已補 |
Lib/Model/QuoteConsumerSurvey.php:79-105 | QuoteConsumerSurvey.php:83-109 | legacy bid 存在與 request owner guard;保留 error 2 / 3 response。 | 已補 |
tests/QuoteBidsAddConsumerSurveyTest.php:56-151 | success / overwrite / missing bid / non owner / invalid session | 覆蓋主要 DB side effect 與 guard。 | 已補 |
逐段行數對照
| Legacy 行號 | New 行號 | 對照內容 | 狀態 |
|---|---|---|---|
QuoteBidsController.php:6099-6103 | Endpoint/V1/QuoteBids.php:2647-2654 | session guard;invalid session 回 error=1/status=invalid request。 | 已對齊 |
QuoteBidsController.php:6105-6110 | Endpoint/V1/QuoteBids.php:2657-2667, QuoteConsumerSurvey.php:79-105 | named quote_bid_id 與 request owner guard。 | 已對齊 |
QuoteBidsController.php:6112-6121 | QuoteConsumerSurvey.php:27-44 | 以 quote_bid_id 查 existing survey;存在就覆蓋。 | 已對齊 |
QuoteBidsController.php:6123-6128 | QuoteConsumerSurvey.php:45-64 | 寫 quote_request_id、quote_bid_id、total_pay、unit、comment。 | 已對齊 |
QuoteBidsController.php:6130-6133 | Endpoint/V1/QuoteBids.php:2669 | 成功 response error=0/status=success。 | 已對齊 |
測試
docker exec -w /project-data cd63f9147e8d vendor/bin/phpunit tests/QuoteBidsAddConsumerSurveyTest.php結果:
OK (5 tests, 24 assertions)測試覆蓋:
- request owner 新增 survey。
- 同一
quote_bid_id重複呼叫會覆蓋同一筆 survey。 - body 缺
total_pay/unit/comment時,total_pay=0.00,unit/comment=NULL。 - bid 不存在回
error=2/status=quote_bid_not_found。 - 非 request owner 回
error=3/status=not_consumer。 - invalid session 回
error=1/status=invalid request。