目錄
- 前言
- 目的
- 邏輯分析
- 代碼實作
- 定義狀态
- 定義事件
- 定義事件的處理方法
- 核心代碼
- 調用方式
- 小結
在設計電商系統訂單子產品時,訂單會涉及各種狀态以及狀态與狀态之間的流轉,、
可擴充性
是我們需要關注的重點!本文分享一下我的技術方案。
可維護性

如上圖,使用
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,供下載下傳使用。
作者:新亮筆記(關注公衆号,可申請添加微信好友)
出處:https://www.cnblogs.com/xinliangcoder
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。