本文讲什么
可以看到,购物车这样一个功能模块,在各种购物类APP或者web应用中绝对是必不可少的东西.不论在大学中的课程设计,还是在实际的项目开发中,绝对非常重要且有些复杂的内容. 在实际操作中,身边有很多的小伙伴遇到编写购物车的代码的时候,有时候真的是一脸懵逼,总是搞不明白设计的思路,这就是本文写作的原因. 所以,本文适合搞不清楚购物车实现原理,知道原理但是实际编码不知道如何下手的小伙伴,我将给出一个思路以及实际的代码供大家参考. 在本文中,我将会用尽可能简单的句子,表达出我想表达的意思.废话不多说,开始我们的购物车实战!
购物车的几种实现方式
购物车的实现方式有很多,但是最常见的就三种:Cookie,Session,数据库.三种方法各有优劣,适合的场景各不相同.
- Cookie方法:通过把购物车中的商品数据写入Cookie中,再通过浏览器进行读取.这个方法,适合在用户没有登录的情况下使用,但是有个非常严重的缺点,即在用户禁用了Cookie的时候是无法使用的.
- Session方法:通过Session来保存商品信息,这确实是个好的方法,适合用户已经登录的情况,将数据放在Session中,用户就能读取购物车中的商品信息,而且速度十分的快.但是缺点也很明显,由于Session是建立在用户客户端和服务器之间的,在Session中保存数据,无疑会增加服务器的负担.
- 数据库(Redis):数据库无疑是一种非常棒的保存购物车中信息的有效途径,且能够持久化保存,但是问题也很明显,那就是读取速度会差强人意.
好了,下面来说一下几种实现方式的应用场景.
- 当用户没有登录的情况下,用户将商品加入购物车,此时的商品信息是写入了Cookie中,并且会设置一个保存时间,即使关闭浏览器过一段时间访问仍能看到购物车中的信息.(或者将购物数据写入Session中,但是关闭浏览器,购物车中的信息也就不见了)
- 用户登陆后,如果在Session中存储了商品信息且没有关闭浏览器(如果在Cookie中存储了商品信息且没有过期),将会读取其中的商品信息,并且将这些信息写入数据库中进行持久保存.
本文的行文方式说明
经过上面的讲解,我想你一定对购物车有所了解,为了使读者更加清晰的明白购物车的实现,我们省去了在未结算的状态下的持久化数据库. 也就是说,在文章中,我将使用Session来实现购物车,并且当用户没有登录的情况下,禁止用户将商品加入购物车.当然你不必为此担忧,即使我这样做,我的代码已经包括了整个购物操作的绝大多数步骤.请耐心向下看. 此外,本文使用SSM框架作为行文代码. 如果你是初学者也不必担心,我将为你提供一套项目的源代码,可以在我的GitHub中获取---->,这套系统是基于servlet+jsp+mysql开发的,注释非常完善,当然最重要的模块也就是下单模块肯定是有的,而且非常完善,欢迎下载.
购物车模块的实现
数据库设计
-
用户表 | 字段 | 意义 | |--------|--------| |id|用户id| |userName|用户名| |password|用户密码|
-
商品表 | 字段 | 意义 | |--------|--------| |id|商品id| |commName|商品名称| |price|商品价格|
-
订单表 | 字段 | 意义 | |--------|--------| |id| 订单id| |commName|商品名称| |count|商品数量| |subtotal|商品小计| |total|总价|
用户登录
为了实现我上述的思路,肯定是要求用户先行登录.代码如下.
- LoginController
@Controllerpublic class LoginController { private LoginService loginService; private CommonService commonService; @Autowired public LoginController(LoginService loginService, CommonService commonService) { this.loginService = loginService; this.commonService = commonService; } @RequestMapping("/login") public String login(User user, HttpSession session, Model model) { //登录验证 if (loginService.isUser(user)) { Listcommons = commonService.selectAllCommons(); model.addAttribute("commons", commons); model.addAttribute("username", user.getUsername()); //把用户信息保存到session中 session.setAttribute("user", user); return "shopping"; } else { model.addAttribute("message", "用户名或密码错误"); return "index"; } }}复制代码
这是最常规的用户登录的代码,思路就是拿着用户名到数据库里面查询,如果能查到,则说明用户名无误,如果查不到则说明没有此用户,提示用户注册.如果用户名存在,再比对密码,一般密码不会像我们这样直接在数据库里面使用明文密码,都是会加盐的(MD5算法).如果比对密码的结果为true,则用户可以登录.比对过程isUser的代码如下.
@Servicepublic class LoginService { private UserMapper userMapper; @Autowired public LoginService(UserMapper userMapper) { this.userMapper = userMapper; } /** * 判断用户名或密码是否正确 * @param user * @return */ public boolean isUser(User user){ UserExample example = new UserExample(); UserExample.Criteria criteria = example.createCriteria(); criteria.andUsernameEqualTo(user.getUsername()); ListeqUser = userMapper.selectByExample(example); //如果没有查询到user,则返回false if (eqUser == null){ return false; } return eqUser.get(0).getPassword().equals(user.getPassword()); }}复制代码
确认用户登录以后,需要把用户信息放在session中以便后续过程使用.
###用户购物模块 当用户登录以后,展示在用户眼前的界面是这样的(页面模板来自菜鸟教程,经过改编):
左栏中的数据来自登录代码中的
Listcommons = commonService.selectAllCommons();model.addAttribute("commons", commons);复制代码
当点击加入购物车以后,触发onlick事件:onclick="javascript:joinCart(${common.id})"
详细代码如下:
function joinCart(id) { $.ajax({ url: "${pageContext.request.contextPath}/shop/joinCart", data: "id=" + id, type: "POST", success: function (result) { alert("加入购物车成功!"); //清空购物车列表 $("#shop_cart tbody").empty(); //动态构建购物车列表 var obj = result; $.each(obj,function (index,item) { var emptyTd = $("").append("#"); var commnameTd = $("").append(item.commname); var countTd = $("").append(item.count); var subtotalTd = $("").append(item.subtotal); $("") .append(emptyTd) .append(commnameTd) .append(countTd) .append(subtotalTd) .appendTo("#shop_cart tbody"); //设置总金额 var totalSpan = document.getElementById("subtotal"); totalSpan.innerHTML = item.total; }); } }) }复制代码
可以看到,当触发这个方法时,实际上是使用了异步请求的方式向服务端发送数据,服务端做相应处理以后,封装购物车列表,然后把购物车商品列表以JSON格式传回,也就是封装在result中,利用js,动态构建购物车列表.于是就出现下面这种情况. 当将商品加入购物车以后:
首先提示用户已经加入购物车,然后在利用异步请求构建整个购物车,如果你对前端的了解并不是很深,不必担心,这部分内容实际上很简单,你可以随便百度一下这个知识点,记住就好了.实际上就是利用js操作json数据而已.
其实对于初学者来说,感觉上面的过程挺神奇的,其实不然.前端的代码看完了,我就来给你详细的解释一下后端的代码. 首先,可以将后段代码分成两部分,一部分是判断,各种判断,另外一部分是封装数据,相对简单.
//标识符:判断是否存在此商品boolean flag = false;//通过id查询商品信息Common common = shopService.selectCommById(Integer.parseInt(id));//从session中获取购物车信息Listshopcart = (List ) session.getAttribute("shopcart");//获取用户信息User user = (User) session.getAttribute("user");if (user == null) { //如果用户为空,则直接返回,让用户登录 throw new RuntimeException("用户未登录");}//判断购物车列表是否为空if (shopcart == null) { //new 一个集合 shopcart = new ArrayList<>();}复制代码
先判断用户是否已经登录,如果用户已经登录,则执行后续流程,如果用户没有登录,则直接抛出异常,交给异步请求的error处理,提示用户登录即可. 如果用户已经登录,则继续下面的步骤,判断购物车是否为空,为空也就是说明用户没有将任何商品加入购物车,则需要创建一个新的ArrayList集合,同时利用商品id查询出的商品信息,封装订单对象.
else { for (Order order : shopcart) { //判断是否存在此商品,存在则数量+1 if (order.getCommname().equals(common.getCommname())) { flag = true; order.setCount(order.getCount() + 1); order.setSubtotal(order.getCount() * common.getPrice()); } }}复制代码
在遍历的过程中,如果遍历的数据的commName和查到的name相同的话,则证明是同一件商品,则将此商品的数量+1,然后再设置小计价格即可.同时将flag设置为true,表示遍历到了同样的数据. 如果没有遍历到名称相同的商品,则直接新建一个对象,封装数据,加入集合.
//如果购物车中没有当前商品的信息,则新增商品if (!flag) { Order order = new Order(); order.setCommname(common.getCommname()); order.setCount(1); order.setSubtotal(common.getPrice()); //把商品加入集合 shopcart.add(order);}复制代码
最后一步就是计算总价格,封装数据即可.
//计算总价格Double total = 0d;for (Order order : shopcart) { total += order.getSubtotal();}for (Order order : shopcart) { order.setTotal(total);}//设置sessionsession.setAttribute("shopcart", shopcart);//返回listreturn shopcart;复制代码
购物车全部代码如下:
Controller@RequestMapping("/shop")public class ShopController { private ShopService shopService; public ShopController(ShopService shopService) { this.shopService = shopService; } @RequestMapping("/joinCart") @ResponseBody public ListjoinCart(String id, HttpSession session, Model model) { //标识符:判断是否存在此商品 boolean flag = false; //通过id查询商品信息 Common common = shopService.selectCommById(Integer.parseInt(id)); //从session中获取购物车信息 List shopcart = (List ) session.getAttribute("shopcart"); //获取用户信息 User user = (User) session.getAttribute("user"); if (user == null) { //如果用户为空,则直接返回,让用户登录 throw new RuntimeException("用户未登录"); } //判断购物车列表是否为空 if (shopcart == null) { //new 一个集合 shopcart = new ArrayList<>(); } else { for (Order order : shopcart) { //判断是否存在此商品,存在则数量+1 if (order.getCommname().equals(common.getCommname())) { flag = true; order.setCount(order.getCount() + 1); order.setSubtotal(order.getCount() * common.getPrice()); } } } //如果购物车中没有当前商品的信息,则新增商品 if (!flag) { Order order = new Order(); order.setCommname(common.getCommname()); order.setCount(1); order.setSubtotal(common.getPrice()); //把商品加入集合 shopcart.add(order); } //计算总价格 Double total = 0d; for (Order order : shopcart) { total += order.getSubtotal(); } for (Order order : shopcart) { order.setTotal(total); } //设置session session.setAttribute("shopcart", shopcart); //返回list return shopcart; }}复制代码
关于操作购物车商品数量及结算
首先说操作购物车商品数量,既然我们能够按照通过id加商品的数量,肯定也是能够按照商品id减商品的数量,这部分无需多说,相信按照上面的代码,以你的聪明才智,肯定是能够做出来的. 至于结算操作,就更加单了. 用户点击结算按钮以后,跳转到后台,通过后台代码,先把session中保存的数据取出,然后遍历将数据写入数据库,进行持久化的操作即可. 以上. 获取文中项目代码: 如果您的积分不够,欢迎关注我的微信公众号:最高权限比特流,回复"购物车源代码"进行下载.
结语
感谢您的阅读,如果您对文章有任何问题,欢迎留言,欢迎联系我:roobtyan@outlook.com. 也欢迎您关注我的微信公众号:最高权限比特流. 以及我的个人博客:www.roobtyan.cn