關於處理電商系統訂單狀態的流轉,分享下我的技術方案(附帶源碼)
前言
在設計電商系統訂單模塊時,訂單會涉及各種狀態以及狀態與狀態之間的流轉,
可擴展性
、可維護性
是我們需要關注的重點!本文分享一下我的技術方案。
如上圖,使用 golang
實現上圖的訂單流轉,同時當後續增加訂單狀態或訂單事件時,可以進行快速完成。
目的
關於訂單狀態的處理,使用統一入口,提高程序的 可擴展性
和 可維護性
。
邏輯分析
訂單狀態包括:默認
、已預訂
、已確認
、已鎖定
。
訂單事件包括:創建訂單
、確認訂單
、修改訂單
、支付訂單
。
通過上圖我們還知道了狀態與事件之間的關係,比如只有 已確認
的訂單才可以進行 修改訂單
。
需要考慮如下問題:
- 當訂單狀態增加時,如何儘可能少的改動或改動對歷史影響不大?
- 如果在同一入口調用,每個事件的處理方法需要的入參都有所不同,如何處理?
- 當某個事件完成後,有可能會進行發短訊或客戶端 Push 的操作,如何處理?
- 有可能某個事件,在不同平台(C端、商家後台、管理平台)的處理邏輯也有些不同,如何處理?
如何設計代碼能夠解決以上問題?
下面是我的一種代碼實現,供大家參考,實現了在 創建訂單
時,進行傳入參數和完成後給用戶發送短訊,其他事件的操作,同理就可以實現。
代碼實現
定義狀態
// 定義訂單狀態
const (
StatusDefault = State(0)
StatusReserved = State(10)
StatusConfirmed = State(20)
StatusLocked = State(30)
)
// statusText 定義訂單狀態文案
var statusText = map[State]string{
StatusDefault: "默認",
StatusReserved: "已預訂",
StatusConfirmed: "已確認",
StatusLocked: "已鎖定",
}
// statusEvent 定義訂單狀態對應的可操作事件
var statusEvent = map[State][]Event{
StatusDefault: {EventCreate},
StatusReserved: {EventConfirm},
StatusConfirmed: {EventModify, EventPay},
}
func StatusText(status State) string {
return statusText[status]
}
當有新訂單狀態的增加時,在此文件中增加相應狀態即可,同時維護好訂單狀態與訂單事件之間的關係。
定義事件
// 定義訂單事件
const (
EventCreate = Event("創建訂單")
EventConfirm = Event("確定訂單")
EventModify = Event("修改訂單")
EventPay = Event("支付訂單")
)
// 定義訂單事件對應的處理方法
var eventHandler = map[Event]Handler{
EventCreate: handlerCreate,
EventConfirm: handlerConfirm,
EventModify: handlerModify,
EventPay: handlerPay,
}
當有新訂單事件的增加時,在此文件中增加相應事件即可,同時維護好訂單事件與事件實現方法之間的關係。
定義事件的處理方法
var (
// handlerCreate 創建訂單
handlerCreate = Handler(func(opt *Opt) (State, error) {
message := fmt.Sprintf("正在處理創建訂單邏輯,訂單ID(%d), 訂單名稱(%s) ... 處理完畢!", opt.OrderId, opt.OrderName)
fmt.Println(message)
if opt.HandlerSendSMS != nil {
_ = opt.HandlerSendSMS("18888888888", "恭喜你預定成功了!")
}
return StatusReserved, nil
})
// handlerConfirm 確認訂單
handlerConfirm = Handler(func(opt *Opt) (State, error) {
return StatusConfirmed, nil
})
// handlerModify 修改訂單
handlerModify = Handler(func(opt *Opt) (State, error) {
return StatusReserved, nil
})
// handlerPay 支付訂單
handlerPay = Handler(func(opt *Opt) (State, error) {
return StatusLocked, nil
})
)
在此文件中維護具體的事件處理方法,如果邏輯比較複雜可以考慮拆分文件處理。
核心代碼
type State int // 狀態
type Event string // 事件
type Handler func(opt *Opt) (State, error) // 處理方法,並返回新的狀態
// FSM 有限狀態機
type FSM struct {
mu sync.Mutex // 排他鎖
state State // 當前狀態
handlers map[State]map[Event]Handler // 當前狀態可觸發的有限個事件
}
// 獲取當前狀態
func (f *FSM) getState() State {
return f.state
}
// 設置當前狀態
func (f *FSM) setState(newState State) {
f.state = newState
}
// addHandlers 添加事件和處理方法
func (f *FSM) addHandlers() (*FSM, error) {
...
return f, nil
}
// Call 事件處理
func (f *FSM) Call(event Event, opts ...Option) (State, error) {
f.mu.Lock()
defer f.mu.Unlock()
...
return f.getState(), nil
}
// NewFSM 實例化 FSM
func NewFSM(initState State) (fsm *FSM, err error) {
fsm = new(FSM)
fsm.state = initState
fsm.handlers = make(map[State]map[Event]Handler)
fsm, err = fsm.addHandlers()
if err != nil {
return
}
return
}
對訂單狀態的操作,只需要使用 Call
方法即可!
關於方法 addHandlers
和 Call
的代碼就不貼了,在文章後面我提供了源碼地址,供大家下載。
調用方式
例如當前狀態為 默認狀態
,依次進行如下操作:
創建訂單
,狀態變為已預訂
;修改訂單
,不可操作(已預訂狀態不可修改);確定訂單
,狀態變為已確認
;修改訂單
,狀態變為已預訂
;確定訂單
,狀態變為已確認
;支付訂單
,狀態變為已鎖定
;
// 通過訂單ID 或 其他信息查詢到訂單狀態
orderStatus := order.StatusDefault
orderMachine, err := order.NewFSM(orderStatus)
if err != nil {
fmt.Println(err.Error())
return
}
// 創建訂單,訂單創建成功後再給用戶發送短訊
if _, err = orderMachine.Call(order.EventCreate,
order.WithOrderId(1),
order.WithOrderName("測試訂單"),
order.WithHandlerSendSMS(sendSMS),
); err != nil {
fmt.Println(err.Error())
}
// 修改訂單
if _, err = orderMachine.Call(order.EventModify); err != nil {
fmt.Println(err.Error())
}
// 確認訂單
if _, err = orderMachine.Call(order.EventConfirm); err != nil {
fmt.Println(err.Error())
}
// 修改訂單
if _, err = orderMachine.Call(order.EventModify); err != nil {
fmt.Println(err.Error())
}
// 確認訂單
if _, err = orderMachine.Call(order.EventConfirm); err != nil {
fmt.Println(err.Error())
}
// 支付訂單
if _, err = orderMachine.Call(order.EventPay); err != nil {
fmt.Println(err.Error())
}
輸出:
正在處理創建訂單邏輯,訂單ID(1), 訂單名稱(測試訂單) ... 處理完畢!
發送短訊,給(18888888888)發送了(恭喜你預定成功了!)
操作[創建訂單],狀態從 [默認] 變成 [已預訂]
[警告] 狀態(已預訂)不允許操作(修改訂單)
操作[確定訂單],狀態從 [已預訂] 變成 [已確認]
操作[修改訂單],狀態從 [已確認] 變成 [已預訂]
操作[確定訂單],狀態從 [已預訂] 變成 [已確認]
操作[支付訂單],狀態從 [已確認] 變成 [已鎖定]
小結
以上就是我的技術方案,希望能對你有所幫助,感興趣的可以再進行封裝,上述代碼已提交到 github go-fsm-order,供下載使用。