SearchAdd Request Create

更新日期:2026-08-05

程式 commit:region 8ad6ebc5;multi preserve 為尚未 commit 工作區

快速結論

這段在 actor 與建單前 guard 通過後建立 quote_requests。region 欄位可傳地區名稱或郵遞區號;multi preserve 只寫關聯表,不可反向改寫主表的單筆 preserve 欄位。

SearchAddHandler::handle()
-> QuoteRequestCreator::create()

新舊程式位置

Legacy root:

/Users/mattsu/Documents/Site/get-lancer-docker-2/get-lancer
職責LegacyPHP 8.2
API 入口QuoteRequestsController.php:1808Endpoint/V1/SearchAdd.php:53-119
guard 後建單QuoteRequestsController.php:2464-2478SearchAddHandler.php:205-223
組主表欄位並寫入QuoteRequestsController.php:2480-2524, 2579-2675QuoteRequestCreator.php:28-79
location fallbackQuoteRequestsController.php:2526-2577QuoteRequestCreator.php:101-143
active region field、地址與 zip 判斷QuoteRequestsController.php:2537-2573QuoteRequestCreator.php:146-189
empty_zip_code logQuoteRequestsController.php:2574-2576QuoteRequestCreator.php:194-235
phone fallbackQuoteRequestsController.php:2579-2595QuoteRequestCreator.php:295-307
match typeQuoteRequestsController.php:1929-1937QuoteRequestCreator.php:312-326
multi-request tagQuoteRequestsController.php:2665-2669QuoteRequestCreator.php:328-335
preserve 主表欄位QuoteRequestsController.php:2630,2645-2647QuoteRequestCreator.php:58-59

Preserve 主表邊界

  • quote_requests.has_preserve 只依單筆 preserve_to_quote_service_id;只有 multi 時保持 0
  • quote_requests.preserve_quote_service_id 只保存單筆 alias;只有 multi 時保持 NULL
  • multi list 另由 PreserveQuoteServiceWriter 寫入 quote_request_preserve_quote_service_ids,不新增主表欄位。
  • 直接預約仍只使用單筆指定專家與預約時間;multi list 不擴張直接預約語意。

Region / zip 規則

只有原始 QuoteRequest.zip_code 為空時,才讀 region form response。

SELECT id
FROM quote_form_fields
WHERE quote_category_id = ?
  AND type = 'region'
  AND is_active = 1;

依序判斷 active region 欄位:

  1. response 是地區名稱:由 address map 反查 zip,log note 為 area_name
  2. response 本身是 address map 內的 zip:直接採用,log note 為 area_code
  3. 都不符合:zip 保持空字串,log note 保持空字串。

取得 zip 後,以預設 AreaUtil 補 addressstate_idcity_id。不新增 Legacy 沒有的地區有效性條件。

empty_zip_code log

  • 觸發:原始 QuoteRequest.zip_code 為空;無論 region 是否成功解析都會寫。
  • channel / level:empty_zip_code / ERROR
  • message:{zip}|{area_name|area_code|空字串}|c_id:{category_id}
  • context:完整 request payload,並帶解析後的 QuoteRequest.zip_code/address/state_id/city_id
  • handler:DB log 固定寫入;Slack 只在 production 啟用,與 Legacy EnvFactory 條件一致。

風險:context 沿用 Legacy 的完整 request payload,可能包含 password、social token 或其他敏感 body 欄位,也會與 RouterV3 generic request log 重複。本次為 Legacy parity 未自行省略或過濾;若要縮減,需另做 migration decision。

流程對照

項目行為
guardcategory、form/category、actor、risk control 先於 QuoteRequestCreator 執行。
inputcategory、title、description、location、phone、travel、match、preserve 與 tracking 欄位由 SearchAddInput 提供。
DB read讀 category;原始 zip 為空時讀 active region form field。
DB write寫一列 quote_requests,再依 id reload。
payment本段沒有付款。
queue本段不寫 queue。
activity / notification本段不寫;由後續 writer 處理。
responsecreator 回傳 reload 後的 request row;外層成功 response 由 SearchAddHandler 組裝。

不可改變的契約

  • 建單必須發生在 actor 與 pre-create guard 成功之後。
  • quote_requests.user_id 必須來自已解析好的 actor。
  • match_type 必須依 category 規則正規化,不能只用 raw payload。
  • quote_service_idpreserve_quote_service_id 只有大於 0 才能落成有效 id。
  • multi preserve 不可把 has_preservepreserve_quote_service_id 改成多筆推導值。
  • 台灣手機 9xxxxxxxx 要補成 09xxxxxxxx
  • 原始 zip 為空時,region 地址名稱、region 直接 zip、invalid region 三種路徑都必須寫 empty_zip_code log。

測試

檔案:tests/SearchAddStep2IntegrationTest.php

Core:

  • testRegionFormResponseCanBeDirectZipCode()100 落表為 100 / 台北市,中正區 / state 5466 / city 360,message 為 area_code

Other:

  • testRegionFormAddressNameStillResolvesZipCode():既有地址名稱反查仍為 100,message 為 area_name
  • testInvalidRegionFormResponseKeepsEmptyLocationAndLogsIt():invalid response 保持空 location,message note 為空。

2026-08-04 PHP 8.2 container 結果:

  • Core:1 test, 10 assertions,通過。
  • Other:2 tests, 12 assertions,通過。
  • 既有明確 zip 建單 regression:2 tests, 33 assertions,通過;其中明確 zip 不會寫 empty_zip_code log。
  • 整檔另有 2 個既有固定 session 測試失敗:session 已失效,與 region 修改無關。
  • 測試建立的 region response parity test request 殘留數:0

Config / 部署

  • 沒有新增 config key,也不修改 ALB。
  • location 仍讀既有 area / sys_area 設定;台灣環境不可新增 area = tw
  • log 沿用既有 LogHelper / Slack 部署設定;本次沒有新增 webhook。

待確認 / 風險

  • Legacy 在建單前先計算 fee;PHP 8.2 拆到 request create 後的 RequestFeeUpdater,需以最終 DB 欄位維持 parity。
  • empty_zip_code context 的敏感資料與重複 logging 風險如上;未取得 reviewer 決定前保持 Legacy 行為。