原文: Nancy簡單實戰之NancyMusicStore(四):實作購物車
前言
上一篇,我們完成了商品的詳情和商品的管理,這一篇我們來完成最後的一個購物車功能。
購物車,不外乎這幾個功能:添加商品到購物車,删除購物車中的商品,對購物車中的商品進行結算。
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 cookie
CookieBasedSessions.Enable(pipelines);
//Prevent errors on Linux
StaticConfiguration.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 exists
AddCartItem(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 ViewModel
var viewModel = new ShoppingCartViewModel
{
CartItems = cart.GetCartItems(),
CartTotal = cart.GetTotal()
};
// Return the view
return 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="[email protected]">
<td>
<a href="/store/details/@item.AlbumId">@item.Title</a>
</td>
<td>
@item.Price
</td>
<td id="[email protected]">
@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 cart
orderTotal += (item.Count * item.Price);
}
UpdateOrderTotal(order.OrderId, orderTotal);
// Empty the shopping cart
EmptyCart();
// Return the OrderId as the confirmation number
return order.OrderId;
}
這裡做的操作主要有三個:
- 把購物車中的商品插入到訂單明細表中
- 更新訂單主表的總金額
- 清空目前的購物車
到這裡,我們的NancyMusicStore已經是到了收尾階段。就差部署上線了啊!!
是以在下一篇,将是介紹Nancy的部署,分别在Windows和Linux下部署。
本文也已經同步到
Nancy之大雜燴