天天看點

關于處理電商系統訂單狀态的流轉,分享下我的技術方案(附帶源碼)

目錄

  • 前言
  • 目的
  • 邏輯分析
  • 代碼實作
    • 定義狀态
    • 定義事件
    • 定義事件的處理方法
    • 核心代碼
    • 調用方式
  • 小結

在設計電商系統訂單子產品時,訂單會涉及各種狀态以及狀态與狀态之間的流轉,

可擴充性

可維護性

是我們需要關注的重點!本文分享一下我的技術方案。
關于處理電商系統訂單狀态的流轉,分享下我的技術方案(附帶源碼)

如上圖,使用

golang

實作上圖的訂單流轉,同時當後續增加訂單狀态或訂單事件時,可以進行快速完成。

關于訂單狀态的處理,使用統一入口,提高程式的

可擴充性

可維護性

訂單狀态包括:

預設

已預訂

已确認

已鎖定

訂單事件包括:

建立訂單

确認訂單

修改訂單

支付訂單

通過上圖我們還知道了狀态與事件之間的關系,比如隻有

已确認

的訂單才可以進行

修改訂單

需要考慮如下問題:

  1. 當訂單狀态增加時,如何盡可能少的改動或改動對曆史影響不大?
  2. 如果在同一入口調用,每個事件的處理方法需要的入參都有所不同,如何處理?
  3. 當某個事件完成後,有可能會進行發短信或用戶端 Push 的操作,如何處理?
  4. 有可能某個事件,在不同平台(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

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

繼續閱讀