網(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è)問題:
- 訂單狀態(tài)有多少種
- 訂單切換事件有多少種
- 在什么情況下,觸發(fā)訂單切換事件
弄清楚這些問題,理清它們之間的邏輯關(guān)系,有利于我們規(guī)劃使用aasm,達(dá)到功能切換的目的。
我先放一張圖,來形象化描述問題1、問題2:

進(jìn)一步闡釋
- 訂單狀態(tài)
- 種類: 6種
- 分別是: order_placed, paid, shipping, shipped, good_returned, order_cancelled
- 訂單切換事件
- 定義:它指的是訂單從一種或幾種狀態(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
- 觸發(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)切換的功能。