SearchAdd Async Fallbacks
更新日期:2026-08-07
快速結論
這段處理 search_add 同步主線結束後的 manual / related fallback。它不應被當成當次 response 的同步保證。
current 入口:
SearchAddHandler::handle()
-> RequestSecondLinkWriter::enqueuePreviousRequestTaskIfNeeded()
-> QuoteRequest::checkR1ManualMatchStartNoAutoQuote()
-> NewQuoteRequestHandler新舊程式對應
Current Legacy
主要入口:
/Users/mattsu/Documents/Site/get-lancer-docker-2/get-lancer/app/Plugin/Quotes/Model/QuoteRequest.php::checkR1ManualMatchStartNoAutoQuote()
/Users/mattsu/Documents/Site/get-lancer-docker-2/get-lancer/app/Plugin/Quotes/Model/QuoteService.php::_handleNewQuoteRequest()| legacy 區段 | 職責 |
|---|---|
checkR1ManualMatchStartNoAutoQuote() | R2 完成本輪 auto / narrow 後,必要時補排 R1。 |
_handleNewQuoteRequest() | manual / related fallback downstream。 |
QuoteRequestPreserveQuoteServiceId::getPreserveQuoteServiceIds() | 依 request id 讀完整指定專家清單。 |
| queue / worker | 後續通知、sent count、request-side downstream。 |
Current PHP 8.2
| current 位置 | 職責 |
|---|---|
Lib/SearchAdd/AutoQuoteDispatcher.php:41 | dispatch 後呼叫 checkR1ManualMatchStartNoAutoQuote()。 |
Lib/SearchAdd/SearchAddHandler.php:352-359 | handler 後段補 second-link task 與 checkR1ManualMatchStartNoAutoQuote()。 |
Lib/Model/QuoteRequest.php:1846-1908 | checkR1ManualMatchStartNoAutoQuote() current implementation。 |
Lib/Util/Task/NewQuoteRequestHandler.php:178-211,1160-1174 | 讀完整 preserve list,逐筆移到 manual candidate 前方;舊資料才退回 task 單筆參數。 |
Lib/Util/Task/NewQuoteRequestHandler.php:368-370 | 建 manual bid 時,is_preserve 仍只比較 task 單筆參數。 |
task queue newNarrowMatch | R1 是 direct reserve / narrow match type 時,排 narrow downstream worker。 |
行號對照
| new 行號 | 對應 legacy | 職責 |
|---|---|---|
AutoQuoteDispatcher.php:41 | QuoteService.php:1128 | auto quote dispatch 後檢查 R1/R2 manual fallback。 |
SearchAddHandler.php:352-359 | QuoteRequest.php:2199-2237 | search_add 主線末端補 second-link task 與 R1 fallback 檢查。 |
QuoteRequest.php:1846-1857 | QuoteRequest.php:2201-2206 | 確認目前 request 是否為 R2。 |
QuoteRequest.php:1859-1864 | QuoteRequest.php:2207-2215 | R2 已有 auto quote 時不補排。 |
QuoteRequest.php:1866-1872 | QuoteRequest.php:2216-2224 | R1 不存在或已有 auto quote 時不補排。 |
QuoteRequest.php:1874-1880 | QuoteRequest.php:2225-2229 | R1 已有 match start 時不補排。 |
QuoteRequest.php:1882-1908 | QuoteRequest.php:2230-2236 | 依 R1 match type 排 newQuoteRequest 或 newNarrowMatch,並依 action / value 做 idempotency。 |
NewQuoteRequestHandler.php:178-211,1160-1174 | QuoteService.php:3773-3801 | 依 request id 讀 multi preserve;每筆通過原有 referral/blocked/mute filter 後移到候選前方。 |
NewQuoteRequestHandler.php:346-372 | QuoteService.php:3967-3993 | 建立一般 manual / related bid;兩邊都不指定 pricing_unit、price_note,由 DB default 寫成 raw NULL。 |
NewQuoteRequestHandler.php:368-370 | QuoteService.php:3989-3991 | manual bid 的 is_preserve 保持單筆比較,不自行擴成 multi。 |
Side Effects
可能寫入:
task_queuetask_event_queueevent_queueevent_queue_log_1到event_queue_log_4task_event_queue_logquote_bids- downstream
quote_activities quote_requests.sent_count- notification / push / email 相關資料
Request_Match_Complete placeholder 對齊(reviewer 已同意)
程式 commit:b587ba7d
精確範圍
Current Legacy 的 consumer match-complete payload 位於:
app/Plugin/Quotes/Model/QuoteService.php:4880-4980- placeholder 實值:
QuoteService.php:4920-4932
PHP 8 shared worker 對應位置:
Lib/Util/Task/NewQuoteRequestHandler.php:751-821- 已調整欄位:
NewQuoteRequestHandler.php:782,785,787的ACTION_1_TEXT、STAT_1_TITLE、STAT_2_TITLE
三個欄位改為空字串;URL、報價數、平均回覆時間、template Complete_Match_Request 與 event Request_Match_Complete 都保持不變。
Shared caller 與邊界
TaskFactory.php:15-22 將 newQuoteRequest 導向 NewQuoteRequestHandler。目前會排入此 task 的 caller 包含:
BookingRequestProcessor.php:45-55RequestSecondLinkWriter.php:88-102CopyRequestHandler.php:495-502(HK copy)Endpoint/V1/QuoteRequests.php:1109-1117(HK edit)QuoteRequest.php:1882-1902QuoteRequestSecondLink.php:32-40
這次 reviewer 已同意以 shared payload 對齊 Current Legacy,因此所有 newQuoteRequest caller 一致生效,不做 search_add-only 分流。rematchQuoteRequest 雖繼承同一 handler,但 NewQuoteRequestHandler.php:725-738 的 !$re_match guard 不會送出 consumer match-complete event。
專家通知 Receive_Match 不在這次修改範圍:Current Legacy QuoteService.php:4541-4554 的 STAT_1_TITLE、STAT_2_TITLE 仍是中文,PHP 8 NewQuoteRequestHandler.php:636-721 必須保留相同行為。
Side effect / config
- 只改
Request_Match_Complete內 MoEngage attributes 的三個值。 - 不新增 DB write、queue、event、template、log、header、cookie 或 response。
- 不新增 config key;
Configuration.php與部署環境不需同步設定。
不可改變的契約
- 當次
search_addresponse 不保證 manual / related fallback 已完成。 - manual candidate 使用完整 multi preserve list;不可只使用 task 單筆參數。
- 一般 manual / related fallback bid 的
pricing_unit、price_noteraw DB contract 是NULL;測試必須使用assertNull(),不可先 cast 成 string。 - multi-only request 建立的 manual bid 仍不標
is_preserve;這是 current Legacy 行為,不可自行修成較合理版本。 NewQuoteRequestHandler是共用newQuoteRequestworker;只有 request 有關聯表資料時使用 multi list,沒有資料的既有 caller 仍退回 task 單筆參數。- queue row 可能已被 worker 搬到 log table。
- 判斷 notification 是否存在時,要查 queue 與 log。
- 不要把 downstream 欄位差異直接算成同步主線不一致。
Verification
主要測試:
tests/SearchAddStep9IntegrationTest.php2026-08-07 raw NULL parity:
- Canonical Legacy
QuoteService.php:3967-3993未指定pricing_unit、price_note;QuoteBid.php:4658-4699的 bulk INSERT 只包含 data set 實際出現的欄位。 - Canonical Legacy schema
app/Config/Schema/schema.php:1650的price_notedefault 是NULL;實際 stagingSHOW COLUMNS顯示price_note、pricing_unit都是 nullable 且 defaultNULL。 - 切換前 legacy request
22929的newQuoteRequesttask 為retry=0;其 75 筆一般/related manual bids,兩欄 raw value 全部都是NULL。 SearchAddStep9IntegrationTest已從(string)後比較空字串改成assertNull(),避免NULL/''被視為相同;targeted integration test1 test / 32 assertions,通過。
2026-08-05:
- Core multi preserve manual fallback:
1 test / 12 assertions;兩個已從一般 category pool 移除的指定專家仍建立 manual bid,is_preserve維持NULL。 - 既有 manual / related baseline:
1 test / 15 assertions,通過。 - Core payload parity:
SearchAddStep9IntegrationTest.php:444-571,Request_Match_Complete三欄為空、URL/數值仍有值;同時確認Receive_Match兩個中文標題保留。1 test / 32 assertions,通過。 - Step 9 Core:
2 tests / 44 assertions,通過。 - Step 9 完整 regression:
12 tests / 120 assertions,通過。 - 容器 PHP 8.2 syntax:
NewQuoteRequestHandler.php、SearchAddStep9IntegrationTest.php均通過。 - 程式已提交:
b587ba7d。
查 queue 規則:
event_queueevent_queue_logevent_queue_log_1event_queue_log_2event_queue_log_3event_queue_log_4task_event_queuetask_event_queue_log
待確認 / 風險
checkR1ManualMatchStartNoAutoQuote()目前在 dispatcher 與 handler 後段都有呼叫。legacy 在QuoteService.php:1128也會呼叫;current 以 match start guard 與 task action/value idempotency 避免重複排 task,已由SearchAddStep9IntegrationTest::testSecondLinkR1DirectReserveQueuesNewNarrowMatch()覆蓋newNarrowMatch分支。- 本次沒有新增 config key;staging/production 仍需確認
quote_request_preserve_quote_service_idsschema 與本地一致。