Sync Match 與 Finalize
這份文件裡的用語先固定:
match- 指 request 建立後,如何決定配給哪個 provider,以及建立哪一種 bid
finalize- 指 bid 建立後,接著補做的收尾執行
- 例如 contact、charge、activity、notify、receipt、reserve 後續處理
對應程式:
- AutoQuoteDispatcher.php
- AutoQuoteMatcher.php
- NarrowMatchBuilder.php
- QuoteBidFinalizer.php
- BidPricingResolver.php
要分三層看
1. dispatcher
回答:
- request 是否真的進 downstream
- 特殊模式是否切去別的流程
2. matcher
回答:
- provider 候選池從哪來
- 哪些 provider 被保留或淘汰
- 初始
quote_bids旗標怎麼決定
3. finalizer
回答:
quote_status_idprovider_status_idquote_sent_onis_want_to_contact_provideris_contact_chargeprice_notepricing_unitquote_amount
Auto Quote Charge Sync
一般 auto quote 的同步收費鏈目前在 AutoQuoteMatcher 完成,不是在 QuoteBidFinalizer。
同步階段的責任順序是:
- 建立
quote_bids - 寫入
quote_bids.total_site_fee - 扣
quote_service_categories.balance - 寫入
transactionstransaction_type_id = 40class = QuoteBidforeign_id = quote_bid_idreal_pay_date = nullreal_pay_amount = amountis_paid_user
- 補
users.first_purchase_time(首次 auto quote 付款時)
這條同步鏈對齊的 legacy 概念是:
QuoteBid::createAutoQuoteBid()QuoteBid::logConsumeUserWallet()AppModel::logQuoteBidTxn()
因此,如果看到:
- bid 建立了
total_site_fee也有值
還要再確認:
quote_service_categories.balance是否同步扣除transactions(40)是否存在is_paid_user/first_purchase_time是否正確補上
Authorized Contact / Charge Sync
authorized 不是一般 auto quote loop 的尾巴,而是指定專家建 bid 後,直接進 QuoteBid::processWantToContactProvider()。
前提限制:
authorized不是只看 request 傳了QuoteRequest[match_type]=2- request 建立時,
match_type會先依 category 規則正規化 - 只有 category
match_type = Both(3)或 category 本身就是Authorize(2),authorized才是合法輸入 - 如果 category
match_type = 1,舊版會先把 request 壓回Multiple(1),不應再進authorizedspecial flow - staging 手動驗證時,若看到 request row 最終是
match_type = 1,但後續仍跑了authorizedcontact / payment,代表 current flow 仍有分流來源不一致的風險
目前同步對齊的責任順序是:
AuthorizedMatchProcessor先建立指定quote_service_id的 bidAuthorizedContactProcessor交由QuoteBid::processWantToContactProvider()做後續- 同步補 bid contact state
is_want_to_contact_providercontact_provider_onis_paid_for_subscription
- 同步做付款判斷
- wallet 直接付款
- debt only
- immediate charge
- auto refill package
- 同步建立付款紀錄與 subscription log
- wallet path 通常新增
transactions.transaction_type_id = 33 - auto quote card / debt path 可能新增
transactions.transaction_type_id = 40 quote_bids.quote_user_subscription_log_id必須回填quote_user_subscription_logs.id- 不可把
transactions.id當成quote_user_subscription_log_id
- wallet path 通常新增
- 同步補:
auto_quote_contact_countAutoQuoteContactContact_ProAuto_Charge_FailAuto_Charge_ProPurchase_Credit_Pro- 若 request 有
quote_request_life_tokens,QuoteBid::processWantToContactProvider()需送 Life55688EVENT_EXPERT_QUOTING
- charge fail 時,必要時改走 fail-switch new leads
RetryCreditCard若還有下一張 valid auto quote card,先 invalid 當前卡並重試下一張;全部失敗才送Auto_Charge_Fail/ fallback
這條鏈目前是 Step7 的主要驗證目標,不應再把 side effects 分散回 processor 內手補。
注意:Step8 只負責 search_add 建立 / 綁定 life_token 與 quote_request_life_tokens;contact provider 成功後的 Life55688 expert quoting event 屬於 shared contact/payment method 的 side effect,完整規則以 API 路由與遷移/API Migration/pro360_api_82/api_migration/details/quote_bids/want_to_contact_provider.md 為準。
補充:
QuoteRequest[reason]不決定authorized/preserve/direct reserve等 special flow 分流- special flow 仍由
match_type與 request 結構決定 - special flow 判斷應以 request 正規化後的
match_type為準,不應只看原始 payload reason是 contact provider 階段的情境值,主要影響 downstream notify 行為- 例如
GET_PHONE_NUMBER_OF_VENDOER會讓Contact_Pro額外補 app push / webpush task
Special Flow 白話表
這幾條不要混成同一種「特殊 auto quote」。
authorized
- 指的是:request 明確指定某個
quote_service_id - 前提是:該 category 允許
Authorize - 主要入口:
AuthorizedMatchProcessorAuthorizedContactProcessorQuoteBid::processWantToContactProvider()
- 語意:
- 建指定 provider 的 bid
- 直接進 contact / payment / notify / receipt 主線
- 不走一般 broad auto quote / narrow fallback
- 若 category 本身只允許
Multiple(1),request 即使硬送QuoteRequest[match_type]=2,legacy 也會先正規化回1
preserve
- 指的是:request 要優先保留某個 provider
- 語意:
- 不是獨立 contact flow
- 仍在 auto/narrow 主線內處理
- 只是優先保住指定
quote_service_id
- 最終可能變成:
is_auto_quote = 1- 或
is_narrow_match = 1
direct reserve
- 指的是:對某位 provider 發起直接預約,帶預約時段
- 主要入口:
DirectReserveProcessor
- 語意不是固定只建一筆 reserve invitation,而是兩段分流:
- direct match success
- 建 auto quote bid
- finalize 後直接 contact provider
- fail to new leads
- 轉成 narrow / new leads
- reserve pause event 改成
WAITING_RESERVE
- direct match success
- 這條 flow 自帶:
- reserve row
- pause event
- reserve activity / message / notify
轉成新客源 bid 後的通知副作用判讀:
DirectReserveProcessorfallback 到 new leads 時,主流程成立的核心是:switchToProSearchNewLeads()成功建立 / 切換 bidquote_bids進入is_narrow_match = 1、narrow_status_id = WAITINGDirect_Reserve_To_New_Leads_Proevent 成功送出
- 這裡的「新客源 bid / new leads」是專案內部流程名詞,指的是:
- bid 已轉成
is_narrow_match = 1 narrow_status_id = WAITING- 專家等待是否接受這筆案件
- bid 已轉成
setMyWorkUnreadCount()與sendConsumerWantProviderQuoteMail()屬於新客源 bid 建立後的通知副作用- 這兩個通知副作用失敗時,現在會記
error_in_switchToProSearchNewLeadslog,但不應再把已成功建立的新客源 bid 一起判成失敗 - 所以看到:
- bid / activity / narrow 狀態已成立
- 但
error_in_switchToProSearchNewLeads有紀錄
代表應先判讀為「主流程成功、通知副作用失敗」
direct user
- 指的是:provider 先被放進
direct_users - 主要入口:
DirectUserProcessor
- 語意:
- 不是 requestor 手動指定 provider
- 是平台依 monthly fee / match rate 規則決定是否直接送
- 命中時會:
- 改寫 request fee
- 建 auto quote bid
- 寫
direct_user_quote_request_matches - 再進 contact 主線
booking
- 指的是:booking 店家或 booking admin 送出的 request
- 主要入口:
BookingRequestProcessor
- 語意:
- 先改寫 request-side booking 資料
- 再回一般 matcher 主線
auto_quote_sent_count應由 matcher 主線直接更新,不應再靠 booking 後補COUNT(*)
Narrow Match Pool 邊界
這次對齊過程已確認:
- 不是所有被 auto filter 排除的 provider 都會轉成 narrow
- legacy 只有特定來源會進 narrow pool
- 部分 filter 在 legacy 會直接排除 provider,不會建立 narrow bid
如果看到新舊差異是「新版 narrow 數量比 legacy 多」,優先檢查:
AutoQuoteMatcherNarrowMatchBuilder