跳到主要內容

AI 內容生成演示系列 - 訂單狀態機

已生成 1

訂單系統最常見的 bug,不是某個畫面寫錯,而是**狀態流轉沒有被嚴格約束**:已出貨的訂單又被改回待付款、已退款的訂單再次扣款、取消後仍然發貨。這些都源自把「狀態」當成一個可任意覆寫的欄位,而不是一台**只能依規則轉移的狀態機**。

更新於 2026/06/18

為什麼訂單要用「狀態機」來建模?

訂單系統最常見的 bug,不是某個畫面寫錯,而是狀態流轉沒有被嚴格約束:已出貨的訂單又被改回待付款、已退款的訂單再次扣款、取消後仍然發貨。這些都源自把「狀態」當成一個可任意覆寫的欄位,而不是一台只能依規則轉移的狀態機。

狀態機的核心是三件事:

  • 狀態(State):訂單在某一刻的處境,例如「待付款」。
  • 事件(Event):外部觸發,例如「使用者完成付款」「金流回拋成功」。
  • 轉移(Transition):在某狀態下收到某事件,才允許走到下一個狀態;其餘一律拒絕。

核心狀態與流轉

一筆訂單從建立到結束,會經過以下主要狀態:

  1. 待付款(Pending) —— 訂單已建立、尚未收款。可因逾時自動轉「已取消」。
  2. 已付款(Paid) —— 金流確認收款,等待出貨。
  3. 已出貨(Shipped) —— 商品交付物流,進入配送。
  4. 已完成(Completed) —— 買家簽收或鑑賞期結束,交易定案。
  5. 已取消(Cancelled) —— 付款前由買家或系統終止。
  6. 已退款(Refunded) —— 付款後退回款項,交易反向結束。

關鍵設計原則

  • 終態不可逆:已完成、已取消、已退款 為終態,不允許再轉出任何狀態。
  • 取消有時間邊界:只有 待付款/已付款(未出貨)能取消;一旦 已出貨,就只能走退貨退款流程而非直接取消。
  • 退款需先有收款:已退款 只能從 已付款 或 已出貨 進入,不可能從 待付款 直接退款。
  • 每次轉移都要留痕:狀態變更應寫入事件日誌(誰、何時、因何事件),便於對帳與爭議處理。
示意圖 · diagram generated/order-state-machine.tsx
完成付款逾時/買家取消出貨買家取消簽收/鑑賞期結束退貨待付款已付款已出貨已完成已取消已退款
起始狀態終態(完成)終態(取消)終態(退款)
點擊狀態節點以探索可達轉移

延伸思考

  • 並發與冪等:金流回拋可能重送,狀態機要保證「已付款 → 已付款」是冪等的、不會重複出貨。
  • 逾時的自動轉移:待付款 的取消多半由排程觸發,屬於系統事件而非使用者事件。
  • 退貨流程的展開:實務上 已出貨 → 已退款 之間常還有「退貨申請中/退貨運送中」等子狀態,可視需求再細分。
讀完這篇了嗎?
建立於 2026/06/18 ai-內容生成演示系列-訂單狀態機.mdx