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