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:41dispatch 後呼叫 checkR1ManualMatchStartNoAutoQuote()
Lib/SearchAdd/SearchAddHandler.php:352-359handler 後段補 second-link task 與 checkR1ManualMatchStartNoAutoQuote()
Lib/Model/QuoteRequest.php:1846-1908checkR1ManualMatchStartNoAutoQuote() 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 newNarrowMatchR1 是 direct reserve / narrow match type 時,排 narrow downstream worker。

行號對照

new 行號對應 legacy職責
AutoQuoteDispatcher.php:41QuoteService.php:1128auto quote dispatch 後檢查 R1/R2 manual fallback。
SearchAddHandler.php:352-359QuoteRequest.php:2199-2237search_add 主線末端補 second-link task 與 R1 fallback 檢查。
QuoteRequest.php:1846-1857QuoteRequest.php:2201-2206確認目前 request 是否為 R2。
QuoteRequest.php:1859-1864QuoteRequest.php:2207-2215R2 已有 auto quote 時不補排。
QuoteRequest.php:1866-1872QuoteRequest.php:2216-2224R1 不存在或已有 auto quote 時不補排。
QuoteRequest.php:1874-1880QuoteRequest.php:2225-2229R1 已有 match start 時不補排。
QuoteRequest.php:1882-1908QuoteRequest.php:2230-2236依 R1 match type 排 newQuoteRequestnewNarrowMatch,並依 action / value 做 idempotency。
NewQuoteRequestHandler.php:178-211,1160-1174QuoteService.php:3773-3801依 request id 讀 multi preserve;每筆通過原有 referral/blocked/mute filter 後移到候選前方。
NewQuoteRequestHandler.php:346-372QuoteService.php:3967-3993建立一般 manual / related bid;兩邊都不指定 pricing_unitprice_note,由 DB default 寫成 raw NULL
NewQuoteRequestHandler.php:368-370QuoteService.php:3989-3991manual bid 的 is_preserve 保持單筆比較,不自行擴成 multi。

Side Effects

可能寫入:

  • task_queue
  • task_event_queue
  • event_queue
  • event_queue_log_1event_queue_log_4
  • task_event_queue_log
  • quote_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,787ACTION_1_TEXTSTAT_1_TITLESTAT_2_TITLE

三個欄位改為空字串;URL、報價數、平均回覆時間、template Complete_Match_Request 與 event Request_Match_Complete 都保持不變。

Shared caller 與邊界

TaskFactory.php:15-22newQuoteRequest 導向 NewQuoteRequestHandler。目前會排入此 task 的 caller 包含:

  • BookingRequestProcessor.php:45-55
  • RequestSecondLinkWriter.php:88-102
  • CopyRequestHandler.php:495-502(HK copy)
  • Endpoint/V1/QuoteRequests.php:1109-1117(HK edit)
  • QuoteRequest.php:1882-1902
  • QuoteRequestSecondLink.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-4554STAT_1_TITLESTAT_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_add response 不保證 manual / related fallback 已完成。
  • manual candidate 使用完整 multi preserve list;不可只使用 task 單筆參數。
  • 一般 manual / related fallback bid 的 pricing_unitprice_note raw DB contract 是 NULL;測試必須使用 assertNull(),不可先 cast 成 string。
  • multi-only request 建立的 manual bid 仍不標 is_preserve;這是 current Legacy 行為,不可自行修成較合理版本。
  • NewQuoteRequestHandler 是共用 newQuoteRequest worker;只有 request 有關聯表資料時使用 multi list,沒有資料的既有 caller 仍退回 task 單筆參數。
  • queue row 可能已被 worker 搬到 log table。
  • 判斷 notification 是否存在時,要查 queue 與 log。
  • 不要把 downstream 欄位差異直接算成同步主線不一致。

Verification

主要測試:

tests/SearchAddStep9IntegrationTest.php

2026-08-07 raw NULL parity:

  • Canonical Legacy QuoteService.php:3967-3993 未指定 pricing_unitprice_noteQuoteBid.php:4658-4699 的 bulk INSERT 只包含 data set 實際出現的欄位。
  • Canonical Legacy schema app/Config/Schema/schema.php:1650price_note default 是 NULL;實際 staging SHOW COLUMNS 顯示 price_notepricing_unit 都是 nullable 且 default NULL
  • 切換前 legacy request 22929newQuoteRequest task 為 retry=0;其 75 筆一般/related manual bids,兩欄 raw value 全部都是 NULL
  • SearchAddStep9IntegrationTest 已從 (string) 後比較空字串改成 assertNull(),避免 NULL'' 被視為相同;targeted integration test 1 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-571Request_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.phpSearchAddStep9IntegrationTest.php 均通過。
  • 程式已提交:b587ba7d

查 queue 規則:

  • event_queue
  • event_queue_log
  • event_queue_log_1
  • event_queue_log_2
  • event_queue_log_3
  • event_queue_log_4
  • task_event_queue
  • task_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_ids schema 與本地一致。