SearchAdd Request Side Writers

更新日期:2026-08-05

狀態:gap(Legacy NewStaging inner_text 尚未搬到 PHP 8)

完整差異見 newstaging_delta_audit_2026-08-05.md

快速結論

這段在 quote_requests 建立後、配對前執行。它補齊 preserve 關聯表、form、landing、log、summary、tracking、abuse、abnormal scan 等 request-side side effects。

current 入口:

SearchAddHandler::handle()
-> request-side writer classes

新舊程式對應

Current Legacy

主要入口:

/Users/mattsu/Documents/Site/get-lancer-docker-2/get-lancer/app/Plugin/Quotes/Controller/QuoteRequestsController.php::search_add()
legacy 區段職責
form submission writes寫入 form submission 與 field responses。
landing / tracking writeslanding page、tracking、request log。
summary / form time writesrequest summary、FormTime。
abuse / abnormal scan風險記錄與異常掃描。
preserve service idsQuoteRequestsController.php:1985-2006,2689-2698 驗證、合併、去重後逐筆寫關聯表。

Current PHP 8.2

current 位置職責
Lib/SearchAdd/SearchAddHandler.php:225-296request-side writers 執行順序。
Lib/SearchAdd/PreserveQuoteServiceWriter.php:12-31單筆/多筆指定專家關聯表。
Lib/SearchAdd/FormSubmissionWriter.phpform submission / field responses。
待新增 inner_text writerinner_text 另寫 quote_request_inners,並從後續 Form payload 移除。
Lib/SearchAdd/LandingPageWriter.phplanding page history。
Lib/SearchAdd/RequestLogWriter.phprequest log。
Lib/SearchAdd/RequestActivityWriter.phprequest activity。
Lib/SearchAdd/RequestSecondLinkWriter.phpsecond link。
Lib/SearchAdd/RequestNotificationWriter.phprequest notification marker。
Lib/SearchAdd/RequestSummaryUpdater.phpsummary 更新。
Lib/SearchAdd/FormTimeWriter.phpFormTime。
Lib/SearchAdd/RequestFeeUpdater.phprequest fee。
Lib/SearchAdd/ExternalIntegrationWriter.php外部整合。
Lib/SearchAdd/RequestTrackingWriter.phptracking。
Lib/SearchAdd/AbuseCheckWriter.phpabuse check。
Lib/SearchAdd/AbnormalScanWriter.phpabnormal scan 與 warning response 分支。

行號對照

new 行號對應 legacy職責
SearchAddHandler.php:225-296QuoteRequestsController.php:2639-3219request 建好後補所有 request-side writers,abnormal scan 命中時提前回 response。
PreserveQuoteServiceWriter.php:12-31QuoteRequestsController.php:1985-2006,2689-2698multi 全組驗證、合併單筆、原始值去重後,每個 id 寫一列。
FormSubmissionWriter.php:18-74QuoteFormSubmission.php:42-203, QuoteRequestsController.php:2925-2931建立 quote_form_submissionsquote_form_submission_fields
待新增QuoteRequestsController.php:2896-2918active inner_text 另寫 quote_request_inners,寫後從 Form unset。
FormSubmissionWriter.php:81-147QuoteFormSubmission.php:97-197依 DB field type 正規化 response;travelpref JSON string-array 使用 decode 後原字串。
RequestSummaryUpdater.php:20-30QuoteRequestsController.php:2890-2966, 3120-3127根據已寫入 form fields 回補 request/profile 欄位,reload summary,寫 limit id。
RequestSummaryUpdater.php:64-88QuoteRequestsController.php:2890-2954travelpref、due_date、region profile address、single travel preference。
RequestSummaryUpdater.php:102-137QuoteRequestsController.php:2904-2931object JSON、單一字串、JSON 字串陣列轉三個 travel flags,object 保留 customer note。
RequestSummaryUpdater.php:142-155QuoteRequestsController.php:2946-2954category 只有單一 travel preference 時補預設 travel flags。
RequestFeeUpdater.php:28-43QuoteRequestsController.php:2559-2594category fee set 寫回 request fee 欄位。
RequestFeeUpdater.php:45-89QuoteRequestsController.php:3051-3056variable fee / campaign / booking fee / high value rule 後補。
AbnormalScanWriter.php:56-61QuoteRequestsController.php:3077-3120海外電話 / Seon 風險掃描。
AbnormalScanWriter.php:63-77QuoteRequestsController.php:3122-3167abnormal new user / list keyword,命中 disable match 時寫 abnormal item。
AbnormalScanWriter.php:79-82QuoteRequestsController.php:3169-3180legacy old keyword scan,命中時後續回 warning。
AbnormalScanWriter.php:84-101QuoteRequestsController.php:3182-3207category keyword scan。
AbnormalScanWriter.php:103-107, 155-176QuoteRequestsController.php:3210-3219, 3313-3319掃描命中時寫 pending params,並把 request 改成 abnormal check / 停止配對。

Side Effects

同步可能寫入:

  • quote_request_preserve_quote_service_ids
  • quote_form_submissions
  • quote_form_submission_fields
  • quote_request_inners(NewStaging pending)
  • quote_request_landing_pages
  • request log / activity 相關資料
  • second link 相關資料
  • tracking / abuse / abnormal scan 相關資料
  • quote_requests.form_summary
  • quote_requests.is_provider_travel
  • quote_requests.is_customer_travel
  • quote_requests.is_remote
  • quote_requests.customer_travel_note(只有 object 格式且有非空 comment)
  • request fee 相關欄位

不可改變的契約

  • writers 必須在 request create 後、auto quote 前執行。
  • multi preserve 必須是 array 且每項 is_numeric();任一 invalid 清空 multi,再合併單筆。
  • array_unique() 依輸入原始值去重;不能先 cast 成 int 再去重。
  • 關聯表包含單筆 preserve id,但 quote_requests.has_preserve/preserve_quote_service_id 仍只依單筆輸入。
  • abnormal scan 命中時可能提前回 success / warning,不應再進 match。
  • form response normalization 要維持 legacy 對 checkbox / multi-select / travel preference 的處理;legacy form value contract 以純值字串為主。
  • current 額外支援 {value: "..."} / [{value: "..."}] 攤平成純值,是為了相容新版 client payload shape,避免寫入 quote_form_submission_fields.responses 後污染 form_summaryjob_center_summarylanguage_summary。這不是 legacy 原本支援的資料格式。
  • travelpref{travel_vendor:{value:...}, ...} 是既有 JSON object 語意,不能套用 {value} 攤平規則。
  • travelpref 也接受精確單值 travel_vendortravel_customerremote 與 JSON string-array;比對使用 strict in_array,大小寫不同或未知值不啟用 flag。
  • JSON string-array 必須原樣寫進 quote_form_submission_fields.responses;不能寫成 normalizer 的逗號字串。
  • request-side writers 不應建立 provider bid。
  • inner_text 只在同 category 且 is_active=1 時分流;missing / NULL 不寫,空字串與字串 "0" 仍寫。
  • inner_text 不可寫入 quote_form_submission_fields,也不可再參與 variable fee、abnormal scan、special flow 或 matching。
  • request create 與 first-field category guard 仍要先看到完整 Form;已提供的 FormSummary 不因 inner field 分流而重算。

SQL 與 response

  • 寫入:INSERT INTO quote_request_preserve_quote_service_ids (created, quote_request_id, quote_service_id) VALUES (NOW(), ?, ?),每個合併後 id 一列。
  • 寫入:INSERT INTO quote_form_submissions ...INSERT INTO quote_form_submission_fields (... responses, type)
  • NewStaging pending:INSERT INTO quote_request_inners (created, quote_category_id, quote_request_id, quote_form_field_id, input_value) VALUES (NOW(), ?, ?, ?, ?)
  • 更新:UPDATE quote_requests SET is_provider_travel = ?, is_customer_travel = ?, is_remote = ? ... WHERE id = ?
  • API success response shape 不變;reload 後三個 travel flag 反映 DB 最終值,欄位型別仍由既有 response formatter 決定。

Preserve 關聯表欄位分類:

欄位來源
createdLegacy Cake timestamp callback,PHP 8 使用 NOW()
quote_request_id新建 quote_requests.id
quote_service_idSearchAddInput::getPreserveQuoteServiceIds() 的驗證/合併/去重結果。

本地 PHP 8.2 SHOW COLUMNSid int auto_incrementcreated datetime NULLquote_request_id int NOT NULLquote_service_id int NOT NULL

quote_request_inners 欄位分類:

欄位來源
createdDB NOW()
quote_category_idrequest category id。
quote_request_id新建 request id。
quote_form_field_idactive inner_text field id。
input_valueLegacy 開頭已 raw-url-decode 的 Form[field_id]

本地 PHP 8.2 已用 DBFactory 核對 quote_request_inners schema;目前 active fixture 為 field 14293 / category 614。此 id 只供本地測試,不當成 staging / production 固定值。

Verification

主要測試:

tests/SearchAddStep4IntegrationTest.php
tests/SearchAddTravelPreferenceTest.php
tests/SearchAddTravelPreferenceIntegrationTest.php
tests/SearchAddInputTest.php
tests/FormSubmissionWriterTest.php
tests/SearchAddStep7IntegrationTest.php
tests/SearchAddStep9IntegrationTest.php

2026-08-04:Core 5 tests / 20 assertions、Other 4 tests / 8 assertions;三種格式、negative cases、DB 原值與 flags 均通過,DB 測試使用 transaction 並 rollback。既有 Step4 category 280 測試因固定 session 失效,在 writer 前回 error 11,與本次調整無關。

2026-08-05 preserve 專用結果:

  • Input:2 tests / 3 assertions
  • Step 7 Core:2 tests / 30 assertions;單筆與 multi-only 寫表,指定專家留在 auto pool。
  • Step 7 Other:1 test / 10 assertions;invalid multi 整組不寫,一般 bid 的 is_preserve/reason/requestor_noteNULL
  • Regression:SearchAddHandlerTest3 tests / 9 assertionsSearchAddInputTest 全檔為 7 tests / 18 assertions
  • Step 9 Core:1 test / 12 assertions;manual worker 能從本表讀出兩筆 multi preserve。既有 manual / related baseline 1 test / 15 assertions 亦通過。
  • 兩個既有 Step 5 narrow reflection tests 在進 matcher 前由 request validation 回 error 11(Email 格式),因此不列為本次通過結果。

待確認 / 風險

  • 本次已用 PHP 8.2 容器 SHOW COLUMNS 核對 quote_form_fieldsquote_form_submissionsquote_form_submission_fieldsquote_requests;field 3082 為 category 280travelpref
  • 本次沒有新增 config key,也沒有 queue、payment、notification 或額外 response 欄位。
  • staging/production 的關聯表 schema 尚未在本輪核對;部署前必須確認與上述四欄一致。
  • FormSubmissionWriter.php:18-74 目前會把 inner_text 當一般 field 寫入,是已確認的 parity gap。
  • 不能只在 FormSubmissionWriter skip;RequestFeeUpdaterAbnormalScanWriter、special flow 與 matcher 也會讀 SearchAddInput::getFormPayload()。實作應在 request create 後建立 filtered input,讓所有下游使用移除後的 Form。
  • inner_text Core test:assert quote_request_inners 有一列、quote_form_submission_fields 無該 field、一般 field仍存在,且 pricing / match 不受 inner value 影響。
  • Other test:missing / NULL、空字串 / "0"、inactive / other-category、FormSummary 不被重算。