订单状态切换功能: aasm状态机

网上商店,消费者下单后,形成了订单,在不同情形下,订单的状态也会不同。因此,我们需要有一个功能,能使订单从某种状态切换(变化)成另一种状态。这就是订单状态的切换功能,而aasm状态机正是实现这一功能的工具。

逻辑梳理


在开始实现这种功能,使用aasm状态机之前。我们需要先问自己几个问题:

  1. 订单状态有多少种
  2. 订单切换事件有多少种
  3. 在什么情况下,触发订单切换事件

弄清楚这些问题,理清它们之间的逻辑关系,有利于我们规划使用aasm,达到功能切换的目的。

我先放一张图,来形象化描述问题1、问题2:

进一步阐释

  1. 订单状态
  • 种类: 6种
  • 分别是: order_placed, paid, shipping, shipped, good_returned, order_cancelled
  1. 订单切换事件
  • 定义:它指的是订单从一种或几种状态切换/转变成另一种状态的过程
  • 种类:5种
  • 分别是:A, B, C, D, E
  • 举例说明:比如A事件的发生,意味着订单状态从order_placed改变为paid,最终结果是订单状态为paid
  1. 触发订单切换事件的条件
  • 定义:它指的是,在满足某种条件或情形下,我们就会去触发订单切换事件;从行为的先后顺利角度看,执行订单切换事件行为的“前一个行为”就是“触发订单切换事件的条件”
  • 举例说明:比如触发A事件的条件,是消费者下完订单、点击付款提交后

具体案例


Part I

解决问题1和问题2,即安装aasm以及定义订单状态和订单切换事件

Step1: 安装aasm

gem 'aasm'

执行bundle install rails s

Step2: 在orders table里,产生aasm_state栏位,用来记录订单状态
执行rails g migration add_aasm_state_to_orders
在刚才的migration 文件里,增加两行

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

执行rake db:migrate

Step3: 在app/models/order.rb文件里,增加以下内容

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,定义触发订单切换事件的条件,此处分别针对触发事件A, B, C, D, E的条件

(一)定义触发订单切换事件A的条件

条件是:消费者下定单且提交付款之后

Step1: 定义好routing,在member do下面加入两行

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

Step2: 付款这个action,通常是在app/controllers/orders_controller.rb里定义的,比如是用支付宝或微信付款,则这个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里,分别定义和支付相关的两个方法

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

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

Step3: 事件A的触发条件,包括两个:make_payment 以及 after_commit: :pay!
所以在order.rb里,重新定义事件A

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


(二)定义触发订单切换事件B, C, D, E的条件

B: 管理员在后台点击“出货”按钮
C: 管理员在后台点击“已出货”按钮
D: 管理员在后台点击“退货”按钮
E: 管理员在后台点击“取消订单”按钮

Step1: 定义好routing, 在member do下面加入四行,这四行分别对应以上“触发订单切换事件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里包含了事件的执行

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,让每个订单可以因其不同状态被管理员在后台点击切换
新增一个文件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 %>


总结


经过逻辑梳理,理解运作机制,能帮助我们事先规划好,便于后续实作代码。
通过具体案例,运用上面的逻辑梳理,落到实处写代码,实现订单状态切换的功能。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • 2017/3/14 RDBMS:关系型数据库管理系统 关系模型独立于语言 SQL有几种不同类型的语言:数据定义语言...
    ancherl阅读 1,605评论 0 6
  • 转载,觉得这篇写 SQLAlchemy Core,写得非常不错。不过后续他没写SQLAlchemy ORM... ...
    非梦nj阅读 5,385评论 1 14
  • 8 管理支付和订单 在上一章中,你创建了一个包括商品目录和订单系统的在线商店。你还学习了如何用Celery启动异步...
    lakerszhy阅读 2,349评论 1 4
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,934评论 6 13