zoukankan      html  css  js  c++  java
  • 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="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>
    

    具体效果如下所示:

    shoppingcart

    从购物车中删除商品

    删除购物车中的商品也是同样的有两种情况

    • 一种是让购物车中的商品数量减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>
    }
    

    最后的话就是结算,下面进入我们的结算操作

    购物车结算

    购物车结算,也就是提交订单,也就是填写一些用户的相关信息,比如:姓名、地址、联系电话等等这些信息,见下图。

    checkout

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

    先是创建了一张订单,这张订单只包含了一些用户信息。订单创建好了之后才去创建订单明细,最后就是返回订单完成页:

    complete

    创建订单明细的方法也是写在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之大杂烩

  • 相关阅读:
    线程的实现方式
    实现一个拷贝文件的工具类,要使用字符流还是字节流
    String&&StringBuilder&&StringBuffer
    面向对象的特征
    索引的选择
    TCP之Nagle算法&&延迟ACK
    通用套接字选项和TCP套接字选项
    TCP之非阻塞connect和accept
    TCP之种种连接异常
    TCP之listen&backlog
  • 原文地址:https://www.cnblogs.com/catcher1994/p/6308780.html
Copyright © 2011-2022 走看看