天天看點

Nancy簡單實戰之NancyMusicStore(四):實作購物車

原文: 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 + ')');
    }
});           

這樣我們就徹底把布局頁完成了。下面是具體的效果

Nancy簡單實戰之NancyMusicStore(四):實作購物車

下面就專注購物車的其他實作了。

添加商品到購物車

添加商品到購物車,有這兩種情況:

  • 添加了一個購物車中沒有的商品(要向購物車中插一條記錄)
  • 添加了一個購物車中已經有了的商品(要向購物車中更新一條記錄)

是以我們就可以得到下面的實作(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】按鈕

Nancy簡單實戰之NancyMusicStore(四):實作購物車

當我們把加入購物車後,可以看到右上角的數量在改變,同時跳轉回了首頁。

Nancy簡單實戰之NancyMusicStore(四):實作購物車

購物車首頁

我們已經完成了添加商品到購物車,但是我們還看不到我們購物車裡面有些什麼商品,是以要有一個購物車首頁。

購物車的首頁,本質就是一個清單,這個清單所列了購物車内的所有商品,包含了這些商品的基本資訊和購物車的訂單總金額。

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>           

具體效果如下所示:

Nancy簡單實戰之NancyMusicStore(四):實作購物車

從購物車中删除商品

删除購物車中的商品也是同樣的有兩種情況

  • 一種是讓購物車中的商品數量減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>
}           

最後的話就是結算,下面進入我們的結算操作

購物車結算

購物車結算,也就是送出訂單,也就是填寫一些使用者的相關資訊,比如:姓名、位址、聯系電話等等這些資訊,見下圖。

Nancy簡單實戰之NancyMusicStore(四):實作購物車

我們在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"];
};           

先是建立了一張訂單,這張訂單隻包含了一些使用者資訊。訂單建立好了之後才去建立訂單明細,最後就是傳回訂單完成頁:

Nancy簡單實戰之NancyMusicStore(四):實作購物車

建立訂單明細的方法也是寫在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;
}           

這裡做的操作主要有三個:

  1. 把購物車中的商品插入到訂單明細表中
  2. 更新訂單主表的總金額
  3. 清空目前的購物車

到這裡,我們的NancyMusicStore已經是到了收尾階段。就差部署上線了啊!!

是以在下一篇,将是介紹Nancy的部署,分别在Windows和Linux下部署。

本文也已經同步到

Nancy之大雜燴