折扣公式只是促銷系統的一部分。完整的模型還需要回答:誰持有這張券、目前是否有效、什麼狀態算一次使用,以及取消後應恢復什麼。
我使用共用的優惠券定義,並將帳號持有的權益與訂單使用紀錄分開。以下說明模型的責任,不重現客戶的促銷規則。
共用限制放在關聯欄位
需要篩選、關聯或限制的資訊,使用明確欄位,例如啟用狀態、有效期間、類型、持有者與使用上限。只由特定計算器解讀的參數,則可放在 JSONB。
| 責任 | 表示方式 |
|---|---|
| 促銷提供什麼 | 定義與計算參數 |
| 誰能使用已發放的權益 | 帳號持有紀錄 |
| 這次使用是否仍計入上限 | 訂單狀態與使用政策 |
| 客戶實際取得多少折扣 | 訂單保存的金額與折扣快照 |
JSONB 適合容納不同參數,仍需要驗證結構。新增計算器時,必須知道接受哪些輸入,以及哪些組合應被拒絕。
使用次數取決於業務狀態
取消的訂單與已完成購買,不應消耗相同的權益。未完成結帳也需要明確的預留期限。目前實作依符合條件的訂單推算使用次數,個人上限與後台總數共用相同的篩選規則。
這讓取消與退款不必再維護另一個計數器,但仍需一致定義哪些訂單狀態算一次使用。
將上限檢查與建單放進同一個決策
兩個請求可能同時讀到最後一次可用額度。如果讀完後各自建單,查詢結果正確也無法保證不超額。
結帳流程會取得這張券的交易層級 advisory lock,在同一交易內檢查使用量並建立訂單。同一張券的請求會在消耗權益的決策點依序處理,不同券則使用不同的鎖鍵。
已指派給帳號的優惠券另外驗證持有權。知道一個有效券碼,不代表可以使用發給其他人的權益。
保留訂單接受的金額
營運人員可以在建單後調整促銷。訂單保存當時接受的價格與折扣結果,不依目前的優惠券設定重算歷史金額。
取消與退款也必須協調訂單狀態及權益狀態。使用與恢復的路徑都明確,這個資料模型才足以支援完整流程。