繁体   English   中英

在一定时间后“过期”购物车的最佳技术? (导轨3)

[英]Best technique for “expiring” a shopping cart after a certain amount of time? (rails 3)

我正在用Rails开发一个简单的自定义购物车(是的,重新设计轮子)。 该购物车的功能必须与fab.com类似,因为用户最多只能将其物品存放在其购物车中12分钟。 向购物车中添加任何东西都会重新启动计时器,但是当时间到时,购物车将被清空。

我目前拥有此代码的前端方面的工作(使用简单的jQuery计时器插件)以及购物车上的过期时间戳记,每次添加项目时,时间戳记均已正确设置。 我的问题是:时间到后清空用户购物车的最佳方法是什么? 编辑为了清楚起见:我正在谈论服务器端解决方案,因为此方面的客户端工作正常。 我曾考虑过某种延迟的工作/任务任务,但这似乎过于复杂。 我想正确的解决方案可能是在购物车模型上进行某种回调,该回调在每次更新任何内容时都会检查到期日期?

下面是一些相关的代码片段。

推车型号

class Cart < ActiveRecord::Base
  has_many :cart_items
  has_many :products, :through => :cart_items

  def <<(cart_item)
    # add a cart_item to the cart, avoiding redundancy
    existing_cart_item = self.cart_items.select{ |ci| ci.product_id == cart_item.product_id }
    if existing_cart_item.size > 0
      existing_cart_item.first.quantity += cart_item.quantity
      existing_cart_item.first.save
    else
      self.cart_items << cart_item
    end
  end

  def reset_expiry(seconds)
    self.expires_at = Time.now + seconds
  end

end

购物车物品控制器

class CartItemsController < ApplicationController
  def create
    @cart_item = CartItem.new(params[:cart_item])
    @cart_item.unit_price = @cart_item.product.price
    current_cart << @cart_item
    current_cart.reset_expiry 720
    current_cart.save!
    respond_to do |format|
      format.html do
        flash[:notice] = "Added #{@cart_item.product.name} to cart."
        redirect_to current_cart
      end
      format.json do
        render :json => {
          :time => 720,
          :count => current_cart.total_quantity,
          :data => render_to_string(:partial => 'layouts/menu_cart_item.html.erb', :collection => current_cart.cart_items, :as => :item)
        }
      end
    end
  end
end

一个可行的解决方案! 看来我在最后一条评论中回答了自己的问题。 感谢@Catcall的想法。

最后,答案非常简单。 我在application_controller current_cart方法中添加了一行:

  def current_cart  
    if user_signed_in?
      if session[:cart_id]  
        @current_cart ||= current_user.carts.find(session[:cart_id])
        if @current_cart.purchased_at || @current_cart.expired?
          session[:cart_id] = nil
        end
      end  
      if session[:cart_id].nil?  
        @current_cart = current_user.carts.create!  
        session[:cart_id] ||= @current_cart.id  
      end
    end
    @current_cart  
  end

并为cart.rb模型添加了一些便捷的方法:

  def seconds_left
    if self.expires_at
      return (self.expires_at - Time.now).round
    else
      -1
    end
  end

  def expired?
    if self.expires_at
      return (self.expires_at < Time.now)
    else
      false
    end
  end

以及product.rb上的一些方法,这样我就可以准确地获取每个项目的最新数量:

  def remaining
    # starting inventory minus the total quantity of all
    # cart items in carts which haven't expired yet or have been purchased
    inventory - (cart_items.includes(:cart).where('carts.expires_at > ? OR carts.purchased_at IS NOT NULL',Time.now).to_a.sum(&:quantity))
  end

  def on_hold
    cart_items.includes(:cart).where('carts.expires_at > ? AND carts.purchased_at IS NULL',Time.now).to_a.sum(&:quantity)
  end

  def purchased
    cart_items.includes(:cart).where('carts.purchased_at IS NOT NULL',Time.now).to_a.sum(&:quantity)
  end

  def on_hold?
    (remaining < 1 && on_hold > 0)
  end

  def sold_out?
    (remaining < 1 && on_hold < 1)
  end

现在,如果用户当前的购物车已过期,只需为其创建一个新的购物车! 干净整洁。

由于用户可以关闭其浏览器,因此我认为您必须在服务器端执行此操作。 您也许可以从客户端更有效地执行此操作,但是仍然必须考虑封闭的浏览器。 IMO,尽管这会带来一些维护问题,但您可以为两者做一个很好的案例。

每当购物车发生变化时,您都可以尝试更新数据库中的购物车超时值。 然后使用服务器端定时过程,该过程将移动或删除过期购物车中的项目,然后更新(或删除)超时。

定时过程本身可能像单个SQL语句一样简单。 我可能会从类似这样的东西开始,以存储过程的形式编写。

DELETE FROM carts 
WHERE cart_expiry_ts < CURRENT_TIMESTAMP;

您必须检查目标平台的文档,才能准确地找到编写有效的存储过程所需的操作。 在PostgreSQL中,我将从这样的东西开始。

CREATE OR REPLACE FUNCTION expire_carts()
  RETURNS void AS 
    'DELETE FROM carts 
     WHERE cart_expiry_ts < CURRENT_TIMESTAMP;'
LANGUAGE SQL 

我将通过执行此语句来运行它。

SELECT expire_carts();

某些dbms平台包括自己的代理,用于在计时器上运行作业。 有些没有。 在没有的平台上,使用系统实用程序。 在Unix变体上通常是cron,在Windows下通常是“计划任务”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM