前言

上一篇,我们完成了商品的详情和商品的管理,这一篇我们来完成最后的一个购物车功能。

购物车,不外乎这几个功能:添加商品到购物车,删除购物车中的商品,对购物车中的商品进行结算。

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>

具体效果如下所示:

从购物车中删除商品

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

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

这里做的操作主要有三个:

  1. 把购物车中的商品插入到订单明细表中
  2. 更新订单主表的总金额
  3. 清空当前的购物车

到这里,我们的NancyMusicStore已经是到了收尾阶段。就差部署上线了啊!!

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

本文也已经同步到 Nancy之大杂烩

Nancy简单实战之NancyMusicStore(四):实现购物车的更多相关文章

  1. Nancy简单实战之NancyMusicStore(六):写在最后

    前言 由于公司搬家后,住的地方离上班的地方远了N倍,以前是走路十多分钟就可以到公司的,上班时间也从9:00提早到8:30 现在每天上班都是先坐公交,然后再坐地铁,在这段路上比较浪费时间而且每天都是要6 ...

  2. Nancy简单实战之NancyMusicStore(三):完善商品信息与管理

    前言 上一篇,我们做了不少准备,并且还把我们NancyFx音乐商城的首页打造好了.这一篇主要是完善我们在首页的商品浏览问题和添加对商品的管理. 下面开始正题: 商品详情 首先是查看单个商品的详情: 先 ...

  3. Nancy简单实战之NancyMusicStore(一):准备工作和搭建项目

    开发环境 OS : Windows 10 10.0.14393 IDE : Visual Studio 2015 Community With Update 3 Database : PostgreS ...

  4. Nancy简单实战之NancyMusicStore(二):打造首页

    前言 继上一篇搭建好项目之后,我们在这一篇中将把我们NancyMusicStore的首页打造出来. 布局 开始首页之前,我们要先为我们的整个应用添加一个通用的布局页面,WebForm中母版页的概念. ...

  5. Nancy简单实战之NancyMusicStore(五):部署上线

    前言 经过本系列前面四篇文章,NancyMusicStore已经开发完成了,下面就差部署上线了,我们会在两个不同的环境部署.其实之前的文章也有讲解在 Linux下部署的相关事宜.下面开始本文的内容. ...

  6. Python--Redis实战:第四章:数据安全与性能保障:第7节:非事务型流水线

    之前章节首次介绍multi和exec的时候讨论过它们的”事务“性质:被multi和exec包裹的命令在执行时不会被其他客户端打扰.而使用事务的其中一个好处就是底层的客户端会通过使用流水线来提高事务执行 ...

  7. AspNetCore-MVC实战系列(四)之结尾

    AspNetCore - MVC实战系列目录 . 爱留图网站诞生 . git源码:https://github.com/shenniubuxing3/LovePicture.Web . AspNetC ...

  8. SAS数据挖掘实战篇【四】

    SAS数据挖掘实战篇[四] 今天主要是介绍一下SAS的聚类案例,希望大家都动手做一遍,很多问题只有在亲自动手的过程中才会有发现有收获有心得. 1 聚类分析介绍 1.1 基本概念 聚类就是一种寻找数据之 ...

  9. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(四)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

随机推荐

  1. IT人常用的网站

    一.网页设计类 蓝色理想 http://www.blueidea.com 网页设计师联盟 http://www.68design.net 网页设计大本营 http://www.code-123.com ...

  2. (中等) POJ 2482 Stars in Your Window,静态二叉树。

    Description Here comes the problem: Assume the sky is a flat plane. All the stars lie on it with a l ...

  3. function $(id){ return document.getElementById(id); }导致所有的js不能用解决办法。。。。

    function $(id){ return document.getElementById(id); } document.getElementById(id) 是获得id这个元素的. 相当于定义了 ...

  4. 用PS给图标添加外发光效果

    最近在做app的时候用到了图标需要根据点击和非点击显示两种状态(原始状态和外发光状态). 如下图: 没办法,因为这是毕业设计的东西,总不能叫同事帮忙处理下.所以自己充当了回美工. 做法如下: 1.打开 ...

  5. phpmyadmin数据库导入大小限制的修改

    1.遇到导入过大文件时,首先检查php.ini 配置文件中的以下三个地方,upload_max_filesize, memory_limit 和post_max_size,并且推荐修改的值要稍大于导入 ...

  6. [转]解决Maven报错"Plugin execution not covered by lifecycle configuration"

    [转]解决Maven报错"Plugin execution not covered by lifecycle configuration" 导入Myabtis源码后,POM文件会报 ...

  7. 怎么把自己电脑上开发的项目发布到自己电脑IIS上面?

    windowsxp中: 步骤: 1.新建一个文件夹,把项目发布到里面(发布的时候需要注意的是: ) 注意:发布方法要选择:文件系统 目标位置选择:你新建来发布的那个文件夹名称 2.在IIS中新建网站 ...

  8. IOS9提示“不受信任的开发者”如何处理

    iPhone升级到IOS9版本后,发现部分APP在下载后首次运行时,都会提示“不受信任的应用程序开发者”,这是因为企业证书发布的APP,没有经过AppStore审核,于是iOS对用户做出一个安全性的提 ...

  9. iOS越狱包

    编译完了的程序是xxx.app文件夹,我们需要制作成ipa安装包,方便安装 找一个不大于500*500的png图片(程序icon图标即可),改名为:iTunesArtwork,注意不能有后缀名. 建立 ...

  10. iOS 发布流程 分类: ios相关 app相关 2015-05-22 14:50 186人阅读 评论(0) 收藏

    1.登陆苹果开发者中心http://developer.apple.com(99美元账号) 2.进入itunes connect 3.选择Manage Your Apps 4.选择Add New Ap ...