quote_activities
文件狀態:已整理 / 待持續補實例
最後驗證:2026-06-01
來源:new Lib/Model/QuoteActivity.php、Endpoint/V1/QuoteActivities.php、Lib/Constant/ConstQuoteActivityType.php、legacy QuoteActivity.php、DB schema
目的
quote_activities 是 quote flow 的事件時間線表。它不是 queue,也不是 application log;它記錄已發生的使用者或系統事件,供聊天室、案件 inbox、my work / my request 列表、後台 activity 查詢、統計與部分 unread 判斷使用。
常見例子:
- consumer 建立需求:
CreateRequest - provider 送出報價:
SubmitQuote - consumer / provider 進聊天室讀取:
ViewBid - 聊天訊息:
SendChat - consumer 聯絡 provider:
AutoQuoteContact - consumer 邀請 provider 報價:
WantProviderQuote - provider 接受 / 拒絕 narrow match:
NarrowProviderAccept/NarrowProviderReject - provider 取得電話:
ProReceivePhone - 付款、儲值、refund、第三方付款、voice contact、Google reserve、Zendesk 等流程事件
不是什麼
- 不是非同步 queue。通知或統計工作仍看
event_queue/task_event_queue。 - 不是金流真實交易紀錄。扣款與付款狀態要看
transactions、payment_orders、third_party_payment_logs等。 - 不是唯一 unread 來源。部分列表會搭配
quote_bids.*_last_read_on、is_*_readed、quote_activity_consumers、Redis unread count。 - 不是每次 view 都一定新增
quote_activitiesrow。ViewBid有去重邏輯,重複 view 可能寫入quote_activity_views。
主要欄位
| 欄位 | 用途 |
|---|---|
id | activity 主鍵,也是多數列表排序與引用依據。 |
created / modified | activity 建立與更新時間。inbox 常用最新 modified 排序。 |
quote_activity_type_id | 事件類型,對應 ConstQuoteActivityType 與 quote_activity_types。 |
provider_user_id | 此 activity 所屬 provider。某些 user-level activity 可為 NULL。 |
requestor_user_id | 此 activity 所屬 requestor / consumer。某些 user-level activity 可為 NULL。 |
receiver_user_id | 事件發起者或接收呈現的主體。程式常用它判斷這筆 activity 對哪個 user 顯示。 |
model / foreign_id | 主關聯。quote flow 通常是 QuoteRequest + quote_requests.id。user-level activity 會是 User + users.id。 |
secondary_model / secondary_foreign_id | 次關聯。quote bid 相關事件通常是 QuoteBid + quote_bids.id。 |
is_viewed | 舊欄位,實際 read/unread 判斷不能只看它。 |
param1 / param2 | 類型相依參數,例如 message、reason、狀態、金額、付款 id、reserve event id。不可跨 type 套同一語意。 |
寫入入口
主要 helper:
Lib/Model/QuoteActivity.php| helper | 寫入形狀 | 用途 |
|---|---|---|
createActivity(...) | 完整指定 model、foreign_id、actor、secondary、param | 最底層寫入。 |
createQuoteRequestActivityById(...) | model = QuoteRequest,無 secondary | request-level event,例如建立需求。 |
createQuoteBidActivityById(...) | model = QuoteRequest,secondary_model = QuoteBid | bid / chat / contact / review / reserve 事件主入口。 |
createUserActivity(...) | model = User | user-level event,例如儲值、付款失敗、每日 access。 |
createQuoteBidActivityById() 寫入的典型結構:
model = QuoteRequest
foreign_id = quote_request_id
secondary_model = QuoteBid
secondary_foreign_id = quote_bid_id
provider_user_id = quote_bids.provider_user_id
requestor_user_id = quote_requests.user_id
receiver_user_id = 本次事件的動作者或顯示主體
quote_activity_type_id = ConstQuoteActivityType::<type>
param1 / param2 = type-specific payload關聯表
quote_activity_consumers
QuoteActivity::createActivity() 會在特定 consumer-facing activity 發生時 upsert quote_activity_consumers。
白話理解:
quote_activities是完整歷史紀錄。每發生一次 activity,就新增一筆。quote_activity_consumers是 consumer 列表用的「每個 bid 最新一筆摘要」。- 同一個
quote_bid_id在quote_activity_consumers只會有一筆;新的 consumer-facing activity 發生時,這筆會被更新到最新 activity。 - 所以查歷史要看
quote_activities,查 consumer 案件列表最後狀態 / 排序 / unread 相關索引,才看quote_activity_consumers。
例子:
quote_activities
1. SubmitQuote
2. SendChat
3. ProviderInviteReview
4. AddReview
quote_activity_consumers
同一個 quote_bid_id 只保留最新一筆,例如 AddReview。用途:
- 記錄 consumer 每個 quote bid 最新的 activity。
Endpoint/V1/QuoteActivities.php::inbox()用它快速組 consumer inbox / my request 列表。- key concept 是「每個 consumer + quote bid 的最新可見活動」,不是完整 activity history。
觸發條件:
- activity type 在
QuoteBid::$myRequestConsumerActivities - 或
UserReportIssue且receiver_user_id = requestor_user_id
quote_activity_views
ViewBid 有去重邏輯:
- 如果同一 receiver / provider / request / empty params 已存在
ViewBidactivity,新的 view 不再新增quote_activities。 - 這種重複 view 會寫到
quote_activity_views。
因此查「是否曾讀取」時不能只看 quote_activities 新增筆數,也要理解去重行為。
讀取入口
quote_bid chat room
Endpoint/V1/QuoteActivities.php::quote_bid()用途:
- 回傳單一 quote bid 的聊天室 / timeline。
- 可在
is_update_last_read_on=1時建立ViewBidactivity。 - 同步更新:
quote_bids.provider_last_read_on/requestor_last_read_onquote_bids.is_provider_readed/is_requestor_readedquote_requests.provider_last_read_on/requestor_last_read_on- unread count / badge queue
它讀取:
quote_activities
WHERE secondary_model = 'QuoteBid'
AND secondary_foreign_id = quote_bid_id
AND quote_activity_type_id IN QuoteBid::$chatRoomActivities並依 actor 做過濾與轉換,例如:
- provider 看不到部分 closed / hidden activity。
- blocked chat 之後的 activity 不顯示。
- auto quote 的重複
ViewBid不顯示。 - Zendesk event 會把
param1轉成顯示文字。 - third-party payment logs 會被合併成 timeline row。
consumer inbox
Endpoint/V1/QuoteActivities.php::inbox()用途:
- 依
quote_activity_consumers.modified排序,組 consumer 案件 / quote bid 列表。 - 不是直接掃整張
quote_activities。
常見 activity type
完整定義在:
Lib/Constant/ConstQuoteActivityType.php常見 quote flow type:
| type | id | 用途 |
|---|---|---|
CreateRequest | 1 | 建立需求。 |
SubmitQuote | 2 | provider 送出報價。 |
ViewBid | 4 | requestor / provider 查看 bid 或聊天室。 |
BidStatusChange | 5 | bid 狀態變更,例如 reject / undo reject。 |
AddReview / UpdateReview | 12 / 13 | 評價新增 / 更新。 |
CreateQuoteBid | 21 | quote bid 建立,chat room 可能補一筆 synthetic row。 |
SendChat | 22 | 聊天訊息。 |
HireVendor | 35 | hired / self hire。 |
PurchaseCredit / PurchaseCreditAutoRefill / PurchaseCreditFailure | 38 / 39 / 40 | 儲值與付款失敗 user activity。 |
NoReadRefund / AdminRefund | 43 / 44 | refund 事件。 |
AutoQuoteContact | 50 | consumer 正式聯絡 auto quote provider。 |
ProviderInviteReview | 51 | provider 邀請 consumer review。 |
AutoQuoteRestoreBalance | 52 | auto quote restore balance。 |
AutoQuotePayDebt | 53 | auto quote debt payment。 |
SwitchToManualQuote | 54 | auto quote 轉手動報價。 |
ProReceivePhone | 55 | provider 取得 consumer phone。 |
WantProviderQuote | 56 | consumer 邀請 provider 報價。 |
NarrowProviderAccept / NarrowProviderReject | 57 / 58 | provider 回應 narrow match invitation。 |
HireVendorReminder | 59 | 洽談後提醒 hire。 |
TP* | 60-75 | third-party payment timeline。 |
VoiceContact* | 76-86 | 語音接通流程。 |
ZendeskTicketEventPro / ZendeskTicketEventConsumer | 87 / 88 | Zendesk ticket 顯示事件。 |
BidReserveTimeAdd/Update/Remove | 89 / 90 / 91 | 已配對 provider 預約時間變更。 |
UserAccess | 92 | 使用者每日 access。 |
Nshop* | 93-98 | N-shop 商品 / order / review consumer event。 |
GReserve* | 99-101 | Google reservation event。 |
常見流程關係
| 流程 | 常見 activity | 說明 |
|---|---|---|
| search_add / 建單 | RequestorSignup, CreateRequest, SubmitQuote, CreateQuoteBid | 建立 request / bid 及初始 timeline。 |
| consumer 聯絡 provider | SendChat, AutoQuoteContact | 訊息與正式聯絡分開記錄。 |
| narrow match | WantProviderQuote, NarrowProviderAccept, NarrowProviderReject | invitation 與 provider 回應。 |
| provider 送手動報價 | SubmitQuote, SwitchToManualQuote | 手動報價與 auto quote 轉手動。 |
| 聊天室讀取 | ViewBid | 會搭配 read flags / last_read_on 更新。 |
| review / hire | ProviderInviteReview, AddReview, UpdateReview, HireVendor | 評價與成交狀態。 |
| refund / payment | PurchaseCredit*, AutoQuotePayDebt, NoReadRefund, AdminRefund, TP* | activity 只是 timeline,交易事實要看 payment / transaction 表。 |
| reserve / voice / Zendesk | BidReserveTime*, GReserve*, VoiceContact*, ZendeskTicketEvent* | 對外流程事件顯示。 |
判讀規則
- 先用
secondary_model = 'QuoteBid' AND secondary_foreign_id = quote_bid_id查 bid 相關 timeline。 - request-level activity 用
model = 'QuoteRequest' AND foreign_id = quote_request_id查。 - user-level payment / access activity 用
model = 'User' AND foreign_id = user_id查。 - 不要單看
param1/param2。必須先看quote_activity_type_id,再判斷 param 意義。 - 不要把 activity 當成唯一事實來源。狀態仍以核心表為主:
- bid 狀態看
quote_bids - request 狀態看
quote_requests - 扣款看
transactions - queue / notification 看
event_queue/task_event_queue
- bid 狀態看
- 同一流程可能先寫 message activity,再因 guard / classification 失敗而沒有後續正式 activity。例如 consumer contact message 被拒時,可能有
SendChat,但不會有AutoQuoteContact。 ViewBid可能因去重寫到quote_activity_views,不要用「沒有新增 quote_activities row」推論沒有查看。
常用查詢
查單一 bid timeline:
SELECT *
FROM quote_activities
WHERE secondary_model = 'QuoteBid'
AND secondary_foreign_id = :quote_bid_id
ORDER BY id ASC;查單一 request activity:
SELECT *
FROM quote_activities
WHERE model = 'QuoteRequest'
AND foreign_id = :quote_request_id
ORDER BY id ASC;查某個 type:
SELECT *
FROM quote_activities
WHERE secondary_model = 'QuoteBid'
AND secondary_foreign_id = :quote_bid_id
AND quote_activity_type_id = :type_id
ORDER BY id ASC;查 consumer inbox 索引:
SELECT *
FROM quote_activity_consumers
WHERE user_id = :user_id
ORDER BY modified DESC;Migration 注意事項
- 搬移 API 時,不能只確認核心 table 狀態,也要比對是否建立正確 activity type。
receiver_user_id要跟 legacy 對齊;它會影響聊天室、inbox、unread、後台顯示。model/foreign_id與secondary_model/secondary_foreign_id不能反放。quote bid event 通常仍以QuoteRequest為主 model,QuoteBid放 secondary。- 若 legacy 會避免重複 activity,新版也要確認是否有 existence check,例如
AutoQuoteContact、reserve、ViewBid。 - 測試 downstream timeline 時,不要只 assert
quote_activities,也要看流程是否同步更新 read flags、quote_activity_consumers、queue 或核心表狀態。