Skip to content

訂單狀態機思維實作開發筆記

概述

本文記錄了我們在電商後台管理系統中採用「狀態機思維」改進訂單狀態管理的設計決策、實作方法和 Trade-off 分析。

背景與問題

原始問題

  1. 管理界面不夠嚴謹:直接點擊下拉選單選擇任意狀態,缺乏業務規則驗證
  2. 缺乏操作指引:管理員不清楚當前狀態的下個合理步驟
  3. 狀態轉換混亂:沒有明確的狀態轉換規則和限制
  4. 錯誤處理不完善:狀態轉換失敗時缺乏友善的錯誤提示

考慮的解決方案

方案一:導入完整狀態機框架 (如 XState)

優點

  • 完整的狀態機功能
  • 視覺化狀態圖
  • 完善的事件處理
  • 強大的調試工具

缺點

  • 學習曲線陡峭
  • 增加 bundle size
  • 對簡單業務邏輯來說過度複雜
  • 維護成本高

方案二:狀態機思維 + 輕量級實作 ✅ (最終選擇)

優點

  • 概念清晰,易於理解
  • 最小化複雜度
  • 與現有架構無縫整合
  • 維護成本低
  • 按需擴展

缺點

  • 需要手動管理狀態轉換邏輯
  • 缺乏視覺化工具

Trade-off 分析

為什麼不用完整的狀態機框架?

  1. 業務複雜度評估

    • 訂單狀態相對簡單(9個狀態,15個轉換)
    • 狀態轉換規則已經在後端明確定義
    • 前端主要負責 UI 呈現和驗證
  2. 技術債務考量

    • 現有系統運作良好
    • 團隊熟悉 Vue 3 + Composables 架構
    • 引入新框架需要額外學習成本
  3. ROI 分析

    • 狀態機框架的核心價值在於複雜狀態管理
    • 我們的場景更偏向於 UI 增強而非業務邏輯控制
    • 輕量級方案能達到 80% 的效果,只需 20% 的複雜度

最終決策邏輯

狀態機概念很有用,但完整的狀態機框架是過殺

採用「狀態機思維」來指導設計,但用輕量級的實作方式,在複雜度和功能性之間找到最佳平衡點。

實作架構

1. 核心狀態管理 (types/order.ts)

typescript
// 完整的狀態定義
export enum OrderStatus {
  PENDING = 'pending',
  CONFIRMED = 'confirmed',
  PAID = 'paid',
  PROCESSING = 'processing',
  SHIPPED = 'shipped',
  DELIVERED = 'delivered',
  COMPLETED = 'completed',
  CANCELLED = 'cancelled',
  REFUNDED = 'refunded',
}

// 狀態轉換規則 - 基於資料庫觸發器邏輯
export const ORDER_TRANSITIONS = {
  [OrderStatus.PENDING]: [OrderStatus.CONFIRMED, OrderStatus.CANCELLED],
  [OrderStatus.CONFIRMED]: [OrderStatus.PAID, OrderStatus.CANCELLED],
  // ... 其他轉換規則
} as const

// 狀態轉換所需欄位
export const TRANSITION_REQUIRED_FIELDS = {
  [`${OrderStatus.PROCESSING}_${OrderStatus.SHIPPED}`]: ['trackingNumber'],
} as const

2. 狀態轉換邏輯 (composables/useOrder.ts)

typescript
// 狀態機思維的核心函數
export function getValidNextStates(currentStatus: OrderStatus): readonly OrderStatus[]
export function canTransitionTo(from: OrderStatus, to: OrderStatus): boolean
export function validateTransition(order: Order, newStatus: OrderStatus)
export function validateBulkTransition(orders: Order[], targetStatus: OrderStatus)

3. UI 組件增強

單一訂單操作 (DataTableRowActions.vue)

  • 智能狀態選擇:只顯示有效的下個狀態
  • 視覺化提示:當前狀態、可用轉換、錯誤訊息
  • 確認對話框:詳細的轉換預覽和驗證

批量操作 (BatchStatusTransitionDialog.vue)

  • 批量驗證:分析哪些訂單可以執行轉換
  • 統計報告:顯示可執行/無法執行的數量和原因
  • 智能過濾:只對有效的訂單執行操作

4. 錯誤處理與用戶體驗

typescript
// 友善的錯誤訊息
const validation = validateTransition(order, newStatus)
if (!validation.valid) {
  showErrorDialog(validation.error, validation.missingFields)
}

關鍵設計決策

1. 資料來源的真理性 (Single Source of Truth)

決策:資料庫觸發器是狀態轉換的最終仲裁者 原因

  • 確保資料一致性
  • 前端只負責 UI 驗證和使用者體驗
  • 避免前後端邏輯不一致的問題

2. 狀態轉換規則的定義位置

決策:在前端 TypeScript 中定義常數 原因

  • 提供 TypeScript 類型安全
  • 便於 UI 組件使用
  • 與後端資料庫觸發器保持同步

3. 對話框確認機制

決策:每次狀態轉換都顯示確認對話框 原因

  • 防止誤操作
  • 提供業務邏輯說明
  • 收集必要的額外資訊(如物流單號)

4. 批量操作的智能處理

決策:預先驗證並只對有效訂單執行操作 原因

  • 避免部分失敗的困擾
  • 提供清晰的操作預覽
  • 減少 API 調用次數

實作細節

狀態轉換驗證流程

  1. UI 層驗證:檢查轉換是否符合業務規則
  2. 必填欄位檢查:確保所需資料完整
  3. 確認對話框:顯示轉換詳情和影響
  4. API 調用:執行實際的狀態變更
  5. 錯誤處理:友善的錯誤訊息和恢復建議

批量操作處理邏輯

typescript
// 批量驗證示例
const bulkValidation = validateBulkTransition(selectedOrders, targetStatus)
// 結果:{ validOrders: [], invalidOrders: [], canProceed: boolean }

// 只對有效訂單執行操作
if (bulkValidation.canProceed) {
  await batchUpdateStatus(bulkValidation.validOrders, targetStatus)
}

性能考量

1. 計算快取

  • 狀態轉換規則使用 computed 快取
  • 避免重複的驗證計算

2. 組件優化

  • 使用 readonly 類型避免不必要的響應式監聽
  • 按需載入對話框組件

3. API 調用優化

  • 批量操作減少網路請求
  • 樂觀更新提升使用者體驗

維護指南

新增狀態

  1. 更新 OrderStatus enum
  2. 修改 ORDER_TRANSITIONS 規則
  3. 更新 field-config.ts 中的顯示設定
  4. 測試所有相關的 UI 組件

新增轉換規則

  1. 更新 ORDER_TRANSITIONS 常數
  2. 如需額外欄位,更新 TRANSITION_REQUIRED_FIELDS
  3. 更新確認對話框的業務邏輯說明
  4. 確保與後端觸發器邏輯一致

除錯指南

  1. 檢查瀏覽器控制台的驗證錯誤
  2. 確認資料庫觸發器的執行結果
  3. 使用 Vue DevTools 查看狀態變化
  4. 驗證 API 調用的參數和回應

測試策略

單元測試

  • 狀態轉換函數的邏輯測試
  • 邊界條件和錯誤情況測試

整合測試

  • 完整的狀態轉換流程測試
  • 批量操作的各種情境測試

E2E 測試

  • 管理員操作流程測試
  • 錯誤處理和恢復流程測試

未來擴展可能性

短期改進

  1. 狀態變更歷史:實作 order_status_logs
  2. 自動化轉換:基於時間或事件的自動狀態轉換
  3. 通知機制:狀態變更時的即時通知

中期改進

  1. 工作流設計器:視覺化的狀態流程編輯
  2. 條件轉換:基於訂單屬性的動態轉換規則
  3. 角色權限:不同角色的狀態轉換權限控制

長期考量

  1. 完整狀態機:如果業務複雜度增加,考慮導入 XState
  2. 機器學習:預測最佳的狀態轉換路徑
  3. 多租戶支援:不同商戶的自定義狀態流程

結論

透過「狀態機思維」的輕量級實作,我們成功改善了訂單管理的使用者體驗,同時保持了系統的簡潔性和可維護性。這個方案證明了在適當的場景下,概念導向的設計比工具導向的設計更有價值。

關鍵成功因素

  1. 準確評估業務複雜度
  2. 選擇合適的技術方案
  3. 重視使用者體驗設計
  4. 保持與現有架構的一致性

這個實作為未來的狀態管理提供了一個可擴展的基礎,可以根據業務需求的變化靈活調整和增強。