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

时间:2011-08-06 02:46:17

标签: ruby-on-rails ruby ruby-on-rails-3

我正在开发一个简单的自定义购物车(是的,重新发明轮子)。购物车必须与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

2 个答案:

答案 0 :(得分:2)

一个有效的解决方案!我似乎用最后的评论回答了我自己的问题。感谢@Catcall的想法。

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

  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

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

答案 1 :(得分:1)

由于用户可以关闭浏览器,我认为您必须在服务器端执行此操作。您可以从客户端更有效地执行此操作,但您仍需要考虑已关闭的浏览器。 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下的“计划任务”。