SearchAdd API 契約

對應入口:

Endpoint

  • POST /quote_requests/search_add.json

Request 組成

公開 request 主要分成四塊:

  • User[...]
    • requestor 身分相關欄位
    • 這一塊負責讓系統判斷:
      • 是既有 user
      • 要登入既有帳號
      • 還是要走 auto signup
    • 常見內容包含:
      • email
      • phone
      • password
      • name
      • device_id
      • verify_code
    • 這一塊的結果會直接影響:
      • 最終 user_id
      • session_id
      • request 掛到哪個 user 底下
  • QuoteRequest[...]
    • request 主體資料
    • 主要描述:
      • 這張單是什麼需求
      • 要服務哪個 category
      • 客戶的基本聯絡與地區資訊
      • 是否指定特殊配對模式
    • 常見內容包含:
      • quote_category_id
      • title
      • description
      • phone_no
      • address
      • zip_code
      • match_type
      • quote_service_id
      • landing_page_url
    • 這一塊也會影響:
      • 是否進一般 auto match
      • 是否進 preserve / authorized / direct reserve / direct user 等特殊流程
  • Form[...]
    • 各 category form 的實際答案
    • 這一塊承載的是需求細節,不同 category 會完全不同
    • 例如:
      • 服務地區
      • 上課方式
      • 時段
      • 規格
      • 預算或補充條件
    • 這些答案後續會影響:
      • form submission 寫表
      • request summary
      • language summary
      • auto quote match 條件
  • FormTime[...] / FormSummary
    • form 的顯示順序與摘要資訊
    • FormTime[...]
      • 比較偏前端題目順序、顯示上下文、保留原始表單脈絡
    • FormSummary
      • 比較偏 legacy 相容摘要,讓 request 建立後可回填可讀 summary
    • 這一塊不是主要業務判斷入口
    • 但它會影響:
      • request 建立後的摘要欄位
      • 後台與 legacy 流程使用的 summary payload

Request 範例

一般建單

這是一個最小可讀的 request 範例,重點是讓人看懂 payload 是如何攤平成 User[...]QuoteRequest[...]Form[...]

{
  "User[email]": "demo@example.com",
  "User[phone]": "0912345678",
  "User[name]": "測試使用者",
  "User[device_id]": "demo-device-001",
  "QuoteRequest[quote_category_id]": "280",
  "QuoteRequest[title]": "英文家教",
  "QuoteRequest[description]": "需要成人英文口說課程",
  "QuoteRequest[phone_no]": "0912345678",
  "QuoteRequest[address]": "台北市大安區",
  "QuoteRequest[zip_code]": "106",
  "QuoteRequest[match_type]": "1",
  "QuoteRequest[landing_page_url]": "/course-landing",
  "Form[6766]": "成人英文",
  "Form[6767]": "每週一次",
  "Form[6768]": "一對一",
  "FormSummary": "{\"需求類型\":\"成人英文\",\"頻率\":\"每週一次\",\"上課方式\":\"一對一\"}"
}

這種 request 通常代表:

  • User[...] 先確定 requestor
  • QuoteRequest[...] 建 request 主體
  • Form[...] 補 category-specific 細節
  • match_type = 1 代表走一般主線,不是特殊指定流程

指定 provider 的 Authorized request

這個範例的重點是:

  • match_type = Authorize
  • quote_service_id

表示這張 request 不是走一般 auto match,而是指定某位 provider / service。

{
  "User[email]": "demo@example.com",
  "User[phone]": "0912345678",
  "User[name]": "測試使用者",
  "QuoteRequest[quote_category_id]": "280",
  "QuoteRequest[title]": "英文家教",
  "QuoteRequest[description]": "希望直接由指定老師接案",
  "QuoteRequest[phone_no]": "0912345678",
  "QuoteRequest[address]": "台北市信義區",
  "QuoteRequest[zip_code]": "110",
  "QuoteRequest[match_type]": "2",
  "QuoteRequest[quote_service_id]": "3301",
  "Form[6766]": "成人英文",
  "Form[6767]": "每週一次",
  "FormSummary": "{\"需求類型\":\"成人英文\",\"頻率\":\"每週一次\"}"
}

這種 request 通常代表:

  • request 主體仍然照一般方式建立
  • 但後續不走一般 provider pool match
  • 而是先對指定的 quote_service_id 建 bid,再進 authorized 後處理

authorized 同步後處理目前對齊的重點是:

  • 入口改走 QuoteBid::processWantToContactProvider()
  • 同步完成 bid contact state
  • 同步完成付款判斷
    • wallet
    • debt only
    • immediate charge
    • auto refill
  • 同步補:
    • quote_user_subscription_log_id
    • AutoQuoteContact
    • Contact_Pro
    • Auto_Charge_Fail
    • receipt event
    • fail-switch to new leads

Success Response

成功時主體為:

{
  "error": 0,
  "message": "Success",
  "user_id": 123,
  "username": "user@example.com",
  "session_id": "token",
  "QuoteRequest": { "...": "..." },
  "QuoteBid": [{ "...": "..." }]
}

username 欄位不應視為 search_add 的穩定契約欄位。

  • 舊版 controller 實作實際回的是 request 流程中的 $user_email
  • 因此會依登入路徑與 request payload 而變動
  • 既有 session 路徑若未補到 email,可能回 null
  • credential / signup 路徑則可能回 email

做 legacy 對照時,不要單獨以 username 是否為 null 當成主流程 parity 判斷依據;優先看:

  • user_id
  • session_id
  • QuoteRequest
  • QuoteBid

Error Response

對外仍維持 legacy 風格:

  • error
  • message

內部錯誤鍵值不作為 public contract 對外保證。

同步與非同步邊界

這是 search_add 最容易被誤解的地方。

同步 response 只保證:

  • request 已建立
  • request-side writers 已完成
  • 本輪 auto / narrow / finalize 已完成
  • 部分特殊流程已補寫

同步 response 不保證:

  • legacy manual / related fallback 已經跑完
  • newQuoteRequest task 已經完成
  • 所有後續 downstream task 都已反映在 QuoteBid

如果要確認 manual / related flow,請看 async_fallbacks.md

對串接端的重要提醒

  • 不要用「當次 response 沒有 manual bid」直接判定整體配對失敗
  • QuoteBid 代表的是同步主線當下可見結果
  • 真正的 legacy parity 需要連同後續 task 一起看