前言
上一篇,我们完成了商品的详情和商品的管理,这一篇我们来完成最后的一个购物车功能。
购物车,不外乎这几个功能:添加商品到购物车,删除购物车中的商品,对购物车中的商品进行结算。
MVC MusicStore中,在Models文件夹中添加了一个ShoppingCart类来处理这一块的内容
这个类就类似我们的业务逻辑层,所以这里也采用了和它一样的做法。
取购物车
首先来看一下取购物车这个静态方法:
public static ShoppingCart GetCart(NancyContext context)
{var cart = new ShoppingCart();cart.ShoppingCartId = cart.GetCartId(context);return cart;
}
取购物车,其实只是给购物车类里面的ShoppingCartId赋值,而ShoppingCartId值是来自GetCartId方法:
public string GetCartId(NancyContext context)
{if (context.Request.Session[CartSessionKey] == null){if (context.CurrentUser != null){context.Request.Session[CartSessionKey] = context.CurrentUser.UserName;}else{Guid tempCartId = Guid.NewGuid();context.Request.Session[CartSessionKey] = tempCartId.ToString();}}return context.Request.Session[CartSessionKey].ToString();
}
在MVC MusicStrore中,这个方法的参数用的是HttpContextBase,而在Nancy中,Nancy有自己的Context
所以自然就是直接用Nancy自带的Context。这里是每次都会为新用户创建一个guid存储在session中
并用这个session作为购物车的唯一标识。
在Nancy中,用到了session的话,需要在启动器中启用Session,不然Session会一直是空的。
我们在CustomerBootstrapper类的ApplicationStartup
方法中添加启动Cookie的代码,具体如下:
protected override void ApplicationStartup(TinyIoCContainer container,IPipelines pipelines)
{//enable the cookieCookieBasedSessions.Enable(pipelines);//Prevent errors on LinuxStaticConfiguration.DisableErrorTraces = false;
}
购物车商品数量
还记得我们在布局_Layout.cshtml里面还有一个购物车中的商品数量还没有实现。我们现在把这个功能补上。
在ShoppingCart中添加下面取数的方法,这个方法是根据购物车的id去数据取出相应的数据。
public int GetCount()
{string cmd = "public.get_total_count_by_cartid";var res = DBHelper.ExecuteScalar(cmd, new{cid = ShoppingCartId}, null, null, CommandType.StoredProcedure);return Convert.ToInt32(res);
}
然后我们新建一个ShopCartModule.cs,并在构造函数中添加取数的方法。
Get["/cartsummary"] = _ =>
{var cart = ShoppingCart.GetCart(this.Context);return Response.AsJson(cart.GetCount());
};
最后在_Layout.cshtml中用ajax调用这个方法即可:
$.ajax({url: "/shoppingcart/cartsummary",method: "get",dataType: "json",success: function (res) {$("#cart-status").text('Cart (' + res + ')');}
});
这样我们就彻底把布局页完成了。下面是具体的效果
下面就专注购物车的其他实现了。
添加商品到购物车
添加商品到购物车,有这两种情况:
添加了一个购物车中没有的商品(要向购物车中插一条记录)
添加了一个购物车中已经有了的商品(要向购物车中更新一条记录)
所以我们就可以得到下面的实现(ShoppingCart):
public void AddToCart(Album album)
{string getItemCmd = "public.get_cart_item_by_cartid_and_albumid";var cartItem = DBHelper.QueryFirstOrDefault<Cart>(getItemCmd, new{cid = ShoppingCartId,aid = album.AlbumId}, null, null, CommandType.StoredProcedure);string addToCartCmd = string.Empty;if (cartItem == null){// Create a new cart item if no cart item existsAddCartItem(cartItem, album.AlbumId);}else{UpdateCartItem(cartItem);}
}
在添加之前都要向根据购物车标识和专辑(商品)标识去判断。此时我们在Module中的实现就比较简单了
Get["/addtocart/{id:int}"] = _ =>
{int id = 0;if (int.TryParse(_.id, out id)){string cmd = "public.get_album_by_aid";var addedAlbum = DBHelper.QueryFirstOrDefault<Album>(cmd, new{aid = id}, null, null, CommandType.StoredProcedure);var cart = ShoppingCart.GetCart(this.Context);cart.AddToCart(addedAlbum);}return Response.AsRedirect("~/");
};
后台逻辑处理好了,我们把商品加入购物车的入口在那呢?入口就在商品详情页下面的【Add to cart】按钮
当我们把加入购物车后,可以看到右上角的数量在改变,同时跳转回了首页。
购物车首页
我们已经完成了添加商品到购物车,但是我们还看不到我们购物车里面有些什么商品,所以要有一个购物车首页。
购物车的首页,本质就是一个列表,这个列表所列了购物车内的所有商品,包含了这些商品的基本信息和购物车的订单总金额。
Get["/index"] = _ =>
{var cart = ShoppingCart.GetCart(this.Context);// Set up our ViewModelvar viewModel = new ShoppingCartViewModel{CartItems = cart.GetCartItems(),CartTotal = cart.GetTotal()};// Return the viewreturn View["Index", viewModel];
};
视图如下 :
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<NancyMusicStore.ViewModels.ShoppingCartViewModel>
@{ViewBag.Title = "Shopping Cart";
}
<h3><em>Review</em> your cart:
</h3>
<p class="button"><a href="javascript:;">Checkout >></a>
</p>
<div id="update-message">
</div>
<table><tr><th>Album Name</th><th>Price (each)</th><th>Quantity</th><th></th></tr>@foreach (var item in Model.CartItems){<tr id="row-@item.RecordId"><td><a href="/store/details/@item.AlbumId">@item.Title</a></td><td>@item.Price</td><td id="item-count-@item.RecordId">@item.Count</td><td><a href="javascript:void(0);" class="RemoveLink" data-id="@item.RecordId">Remove from cart</a></td></tr>}<tr><td>Total</td><td></td><td></td><td id="cart-total">@Model.CartTotal</td></tr>
</table>
具体效果如下所示:
从购物车中删除商品
删除购物车中的商品也是同样的有两种情况
一种是让购物车中的商品数量减1
一种是从购物车中直接删掉商品,不同的是删除的同时返回了商品的数量,这个数量用于在页面展示。
public int RemoveFromCart(int id)
{string getItemCmd = "public.get_cart_item_by_cartid_and_recordid";var cartItem = DBHelper.QueryFirstOrDefault<Cart>(getItemCmd, new{cid = ShoppingCartId,rid = id}, null, null, CommandType.StoredProcedure);int itemCount = 0;if (cartItem != null){ if (cartItem.Count > 1){UpdateCartItemCount(cartItem, itemCount); }else{RemoveCartItem(cartItem.RecordId);}}return itemCount;
}
同时还要在购物车列表页面添加相应的JS处理
@section scripts{<script type="text/javascript">$(function () {$(".RemoveLink").click(function () {var recordToDelete = $(this).attr("data-id");if (recordToDelete != '') {$.post("/shoppingcart/removefromcart", { "id": recordToDelete },function (data) {if (data.ItemCount == 0) {$('#row-' + data.deleteid).fadeOut('slow');} else {$('#item-count-' + data.deleteId).text(data.itemCount);}$('#cart-total').text(data.cartTotal);$('#update-message').text(data.message);$('#cart-status').text('Cart (' + data.cartCount + ')');});}});});</script>
}
最后的话就是结算,下面进入我们的结算操作
购物车结算
购物车结算,也就是提交订单,也就是填写一些用户的相关信息,比如:姓名、地址、联系电话等等这些信息,见下图。
我们在Modules文件夹中添加一个CheckOutModule.cs用来处理结算相关的功能。
要结算,必须要登录,所以我们要首先添加需要授权的这句代码this.RequiresAuthentication();
然后再考虑其他事情。
提交订单的后台操作如下:
Post["/addressandpayment"] = _ =>
{var order = this.Bind<Order>();order.Username = this.Context.CurrentUser.UserName;order.OrderDate = DateTime.UtcNow;string cmd = "public.add_order";var res = DBHelper.ExecuteScalar(cmd, new{odate = order.OrderDate,uname = order.Username,fname = order.FirstName,lname = order.LastName,adr = order.Address,cn = order.City,sn = order.State,pcode = order.PostalCode,cname = order.Country,ph = order.Phone,ea = order.Email,t = order.Total}, null, null, CommandType.StoredProcedure);if (Convert.ToInt32(res) != 0){order.OrderId = Convert.ToInt32(res);var cart = ShoppingCart.GetCart(this.Context);cart.CreateOrder(order);string redirectUrl = string.Format("/checkout/complete/{0}", res.ToString());return Response.AsRedirect(redirectUrl);}return View["AddressAndPayment"];
};
先是创建了一张订单,这张订单只包含了一些用户信息。订单创建好了之后才去创建订单明细,最后就是返回订单完成页:
创建订单明细的方法也是写在Models下面的ShoppingCart中,具体如下:
public int CreateOrder(Order order)
{decimal orderTotal = 0;var cartItems = GetCartItems(); foreach (var item in cartItems){ AddOrderDetails(new OrderDetail{AlbumId = item.AlbumId,OrderId = order.OrderId,UnitPrice = item.Price,Quantity = item.Count});// Set the order total of the shopping cartorderTotal += (item.Count * item.Price);}UpdateOrderTotal(order.OrderId, orderTotal); // Empty the shopping cartEmptyCart();// Return the OrderId as the confirmation numberreturn order.OrderId;
}
这里做的操作主要有三个:
- 把购物车中的商品插入到订单明细表中
- 更新订单主表的总金额
- 清空当前的购物车
到这里,我们的NancyMusicStore已经是到了收尾阶段。就差部署上线了啊!!
所以在下一篇,将是介绍Nancy的部署,分别在Windows和Linux下部署。
本文也已经同步到 Nancy之大杂烩