訂單狀態(tài)切換功能: aasm狀態(tài)機(jī)

網(wǎng)上商店,消費(fèi)者下單后,形成了訂單,在不同情形下,訂單的狀態(tài)也會(huì)不同。因此,我們需要有一個(gè)功能,能使訂單從某種狀態(tài)切換(變化)成另一種狀態(tài)。這就是訂單狀態(tài)的切換功能,而aasm狀態(tài)機(jī)正是實(shí)現(xiàn)這一功能的工具。

邏輯梳理


在開始實(shí)現(xiàn)這種功能,使用aasm狀態(tài)機(jī)之前。我們需要先問自己幾個(gè)問題:

  1. 訂單狀態(tài)有多少種
  2. 訂單切換事件有多少種
  3. 在什么情況下,觸發(fā)訂單切換事件

弄清楚這些問題,理清它們之間的邏輯關(guān)系,有利于我們規(guī)劃使用aasm,達(dá)到功能切換的目的。

我先放一張圖,來形象化描述問題1、問題2:

進(jìn)一步闡釋

  1. 訂單狀態(tài)
  • 種類: 6種
  • 分別是: order_placed, paid, shipping, shipped, good_returned, order_cancelled
  1. 訂單切換事件
  • 定義:它指的是訂單從一種或幾種狀態(tài)切換/轉(zhuǎn)變成另一種狀態(tài)的過程
  • 種類:5種
  • 分別是:A, B, C, D, E
  • 舉例說明:比如A事件的發(fā)生,意味著訂單狀態(tài)從order_placed改變?yōu)閜aid,最終結(jié)果是訂單狀態(tài)為paid
  1. 觸發(fā)訂單切換事件的條件
  • 定義:它指的是,在滿足某種條件或情形下,我們就會(huì)去觸發(fā)訂單切換事件;從行為的先后順利角度看,執(zhí)行訂單切換事件行為的“前一個(gè)行為”就是“觸發(fā)訂單切換事件的條件”
  • 舉例說明:比如觸發(fā)A事件的條件,是消費(fèi)者下完訂單、點(diǎn)擊付款提交后

具體案例


Part I

解決問題1和問題2,即安裝aasm以及定義訂單狀態(tài)和訂單切換事件

Step1: 安裝aasm

gem 'aasm'

執(zhí)行bundle install rails s

Step2: 在orders table里,產(chǎn)生aasm_state欄位,用來記錄訂單狀態(tài)
執(zhí)行rails g migration add_aasm_state_to_orders
在剛才的migration 文件里,增加兩行

add_column :orders, :aasm_state, :string
add_index :orders, :aasm_state

執(zhí)行rake db:migrate

Step3: 在app/models/order.rb文件里,增加以下內(nèi)容

include AASM
aasm do
  state :order_placed
  state :paid
  state :shipping
  state :shipped
  state :good_returned
  state :order_cancelled

  event :make_payment do
    transitions from: :order_placed, to: :paid
  end

  event : ship do
    transitions from: :paid, to: :shipping
  end

  event :deliver do
    transitions from: :shipping, to: :shipped
  end

  event :return_good do
    transitions from: :shipped, to: :good_returned
  end 

  event :cancel_order do
    transitions from: [:order_placed, :paid], to: :order_cancelled
  end

end

說明:以上步驟,解決了問題1、問題2,

Part II

解決問題3,定義觸發(fā)訂單切換事件的條件,此處分別針對(duì)觸發(fā)事件A, B, C, D, E的條件

(一)定義觸發(fā)訂單切換事件A的條件

條件是:消費(fèi)者下定單且提交付款之后

Step1: 定義好routing,在member do下面加入兩行

resources :orders do
  member do
    post :pay_with_alipay
    post :pay_with_wechat
  end
end

Step2: 付款這個(gè)action,通常是在app/controllers/orders_controller.rb里定義的,比如是用支付寶或微信付款,則這個(gè)action可以加入一行: @order.make_payment

def pay_with_alipay
  @order = Order.find(params[:id])
  @order.set_payment_with("alipay")
  @order.make_payment
  flash[:notice] =  "已用支付寶完成付款"
  redirect_to order_path(@order)
end

def pay_with_wechat
  @order = Order.find(params[:id])
  @order.set_payment_with("wechat")
  @order.make_payment!
  flash[:notice] =  "已用微信完成付款"
  redirect_to order_path(@order)
end

Step3: 在app/models/order.rb里,分別定義和支付相關(guān)的兩個(gè)方法

def set_payment_with(method)
  self.update_columns(payment_method: method)
end

def pay!
  self.update_columns(is_paid: true)
end

Step3: 事件A的觸發(fā)條件,包括兩個(gè):make_payment 以及 after_commit: :pay!
所以在order.rb里,重新定義事件A

  event :maket_payment, after_commit: :pay! do
    transitions from: :order_placed, to: :paid
  end


(二)定義觸發(fā)訂單切換事件B, C, D, E的條件

B: 管理員在后臺(tái)點(diǎn)擊“出貨”按鈕
C: 管理員在后臺(tái)點(diǎn)擊“已出貨”按鈕
D: 管理員在后臺(tái)點(diǎn)擊“退貨”按鈕
E: 管理員在后臺(tái)點(diǎn)擊“取消訂單”按鈕

Step1: 定義好routing, 在member do下面加入四行,這四行分別對(duì)應(yīng)以上“觸發(fā)訂單切換事件B, C, D, E 條件"的routing

namespace :admin do
  resources :orders do
    member do
      post :ship
      post :shipped
      post :return
      post :cancel
    end
  end
end

Step2: 定義好以上條件的action,在app/controllers/admin/orders_controller.rb文件里,寫以下代碼,讓action里包含了事件的執(zhí)行

def ship
  @order = Order.find(params[:id])
  @ordr.ship!
  redirect_to admin_order_path(@order)
end

def shipped
  @order = Order.find(params[:id])
  @ordr.deliver!
  redirect_to admin_order_path(@order)
end

def return
  @order = Order.find(params[:id])
  @ordr.return_good!
  redirect_to admin_order_path(@order)
end

def cancel
  @order = Order.find(params[:id])
  @ordr.cancel_order!
  redirect_to admin_order_path(@order)
end

Step3: 寫好view,讓每個(gè)訂單可以因其不同狀態(tài)被管理員在后臺(tái)點(diǎn)擊切換
新增一個(gè)文件touch app/views/admin/orders/_state.html.erb
在該文件里,寫入

<div style="padding: 10px; float: right; ">

<% case order.aasm_state do %>
<% when "order_placed" %>
  <%= link_to("取消訂單", cancel_admin_order_path(order), method: :post, class: "btn btn-default btn-sm") %>

<% when "paid" %>
  <%= link_to("取消訂單", cancel_admin_order_path(order), method: :post, class: "btn btn-default btn-sm") %>
  <%= link_to("出貨", ship_admin_order_path(order), method: :post, class: "btn btn-default btn-sm") %>

<% when "shipping" %>
  <%= link_to("已出貨", shipped_admin_order_path(order), method: :post, class: "btn btn-default btn-sm") %>

<% when "shipped" %>
  <%= link_to("退貨", return_admin_order_path(order), method: :post, class: "btn btn-default btn-sm") %>

<% when "good_returned" %>
  <span class="label label-default">已退貨</span>

<% when "order_cancelled" %>
  <span class="label label-default">已取消訂單</span>

<% end %>
<div>

在app/views/admin/orders/show.html.erb里,新增一行

<%= render "state", order: @order %>


總結(jié)


經(jīng)過邏輯梳理,理解運(yùn)作機(jī)制,能幫助我們事先規(guī)劃好,便于后續(xù)實(shí)作代碼。
通過具體案例,運(yùn)用上面的邏輯梳理,落到實(shí)處寫代碼,實(shí)現(xiàn)訂單狀態(tài)切換的功能。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • 2017/3/14 RDBMS:關(guān)系型數(shù)據(jù)庫管理系統(tǒng) 關(guān)系模型獨(dú)立于語言 SQL有幾種不同類型的語言:數(shù)據(jù)定義語言...
    ancherl閱讀 1,790評(píng)論 0 6
  • 轉(zhuǎn)載,覺得這篇寫 SQLAlchemy Core,寫得非常不錯(cuò)。不過后續(xù)他沒寫SQLAlchemy ORM... ...
    非夢(mèng)nj閱讀 5,591評(píng)論 1 14
  • 8 管理支付和訂單 在上一章中,你創(chuàng)建了一個(gè)包括商品目錄和訂單系統(tǒng)的在線商店。你還學(xué)習(xí)了如何用Celery啟動(dòng)異步...
    lakerszhy閱讀 2,442評(píng)論 1 4
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說閱讀 12,306評(píng)論 6 13

友情鏈接更多精彩內(nèi)容