zoukankan      html  css  js  c++  java
  • 购物车原理以及实现

    本文讲什么

    可以看到,购物车这样一个功能模块,在各种购物类APP或者web应用中绝对是必不可少的东西.不论在大学中的课程设计,还是在实际的项目开发中,绝对非常重要且有些复杂的内容.
    在实际操作中,身边有很多的小伙伴遇到编写购物车的代码的时候,有时候真的是一脸懵逼,总是搞不明白设计的思路,这就是本文写作的原因.
    所以,本文适合搞不清楚购物车实现原理,知道原理但是实际编码不知道如何下手的小伙伴,我将给出一个思路以及实际的代码供大家参考.
    在本文中,我将会用尽可能简单的句子,表达出我想表达的意思.废话不多说,开始我们的购物车实战!

    购物车的几种实现方式

    购物车的实现方式有很多,但是最常见的就三种:Cookie,Session,数据库.三种方法各有优劣,适合的场景各不相同.

    • Cookie方法:通过把购物车中的商品数据写入Cookie中,再通过浏览器进行读取.这个方法,适合在用户没有登录的情况下使用,但是有个非常严重的缺点,即在用户禁用了Cookie的时候是无法使用的.
    • Session方法:通过Session来保存商品信息,这确实是个好的方法,适合用户已经登录的情况,将数据放在Session中,用户就能读取购物车中的商品信息,而且速度十分的快.但是缺点也很明显,由于Session是建立在用户客户端和服务器之间的,在Session中保存数据,无疑会增加服务器的负担.
    • 数据库(Redis):数据库无疑是一种非常棒的保存购物车中信息的有效途径,且能够持久化保存,但是问题也很明显,那就是读取速度会差强人意.

    好了,下面来说一下几种实现方式的应用场景.

    • 当用户没有登录的情况下,用户将商品加入购物车,此时的商品信息是写入了Cookie中,并且会设置一个保存时间,即使关闭浏览器过一段时间访问仍能看到购物车中的信息.(或者将购物数据写入Session中,但是关闭浏览器,购物车中的信息也就不见了)
    • 用户登陆后,如果在Session中存储了商品信息且没有关闭浏览器(如果在Cookie中存储了商品信息且没有过期),将会读取其中的商品信息,并且将这些信息写入数据库中进行持久保存.

    本文的行文方式说明

    经过上面的讲解,我想你一定对购物车有所了解,为了使读者更加清晰的明白购物车的实现,我们省去了在未结算的状态下的持久化数据库.
    也就是说,在文章中,我将使用Session来实现购物车,并且当用户没有登录的情况下,禁止用户将商品加入购物车.当然你不必为此担忧,即使我这样做,我的代码已经包括了整个购物操作的绝大多数步骤.请耐心向下看.
    此外,本文使用SSM框架作为行文代码.
    如果你是初学者也不必担心,我将为你提供一套项目的源代码,可以在我的GitHub中获取—->https://github.com/roobtyan/dinner” target=”_blank”>餐厅点餐系统,这套系统是基于servlet+jsp+mysql开发的,注释非常完善,当然最重要的模块也就是下单模块肯定是有的,而且非常完善,欢迎下载.

    购物车模块的实现

    数据库设计

    • 用户表
    字段 意义
    id 用户id
    userName 用户名
    password 用户密码

    - 商品表

    字段 意义
    id 商品id
    commName 商品名称
    price 商品价格

    - 订单表

    字段 意义
    id 订单id
    commName 商品名称
    count 商品数量
    subtotal 商品小计
    total 总价

    用户登录

    为了实现我上述的思路,肯定是要求用户先行登录.代码如下.
    - LoginController

    @Controller
    public 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)) {
                List<Common> commons = 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的代码如下.

    @Service
    public 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());
            List<User> eqUser = userMapper.selectByExample(example);
            //如果没有查询到user,则返回false
            if (eqUser == null){
                return false;
            }
            return eqUser.get(0).getPassword().equals(user.getPassword());
        }
    
    }

    确认用户登录以后,需要把用户信息放在session中以便后续过程使用.

    用户购物模块

    当用户登录以后,展示在用户眼前的界面是这样的(页面模板来自菜鸟教程,经过改编):

    左栏中的数据来自登录代码中的

    List<Common> commons = 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 = $("<td></td>").append("#");
                       var commnameTd = $("<td></td>").append(item.commname);
                       var countTd = $("<td></td>").append(item.count);
                       var subtotalTd = $("<td></td>").append(item.subtotal);
    
                       $("<tr></tr>")
                           .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中获取购物车信息
    List<Order> shopcart = (List<Order>) 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);
    }
    //设置session
    session.setAttribute("shopcart", shopcart);
    //返回list
    return shopcart;

    购物车全部代码如下:

    Controller
    @RequestMapping("/shop")
    public class ShopController {
        private ShopService shopService;
    
        public ShopController(ShopService shopService) {
            this.shopService = shopService;
        }
    
        @RequestMapping("/joinCart")
        @ResponseBody
        public List<Order> joinCart(String id, HttpSession session, Model model) {
            //标识符:判断是否存在此商品
            boolean flag = false;
            //通过id查询商品信息
            Common common = shopService.selectCommById(Integer.parseInt(id));
            //从session中获取购物车信息
            List<Order> shopcart = (List<Order>) 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中保存的数据取出,然后遍历将数据写入数据库,进行持久化的操作即可.
    以上.
    获取文中项目代码:https://download.csdn.net/download/yanmiao0715/10570386
    如果您的积分不够,欢迎关注我的微信公众号:最高权限比特流,回复”购物车源代码”进行下载.

    结语

    感谢您的阅读,如果您对文章有任何问题,欢迎留言,欢迎联系我:roobtyan@outlook.com.
    也欢迎您关注我的微信公众号:最高权限比特流.
    以及我的个人博客:www.roobtyan.cn

  • 相关阅读:
    arcgis python 把多个MXD批量导出一个PDF
    arcgis python pdf合并
    arcgis python 列出一个表所有字段
    arcgis python 随机取部分数据
    arcgis python 删除一个数据库所有数据
    python将py文件转换为pyc
    从 10.x 到 ArcGIS Pro 的 Python 迁移
    Entity Framework 6.X实现记录执行的SQL功能
    Entity Framework 5.0系列之EF概览-三种编程方式
    Entity Framework 全面教程详解(转)
  • 原文地址:https://www.cnblogs.com/roobtyan/p/9576684.html
Copyright © 2011-2022 走看看