购物车原理以及实现

本文讲什么, 本文其实就是项目3的精华。

可以看到,购物车这样一个功能模块,在各种购物类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

@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 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 eqUser = userMapper.selectByExample(example);

        //如果没有查询到user,则返回false

        if (eqUser == null){

            return false;

        }

        return eqUser.get(0).getPassword().equals(user.getPassword());

    }

}


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

###用户购物模块

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

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

List 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 = $("").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中获取购物车信息

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<>();

}


先判断用户是否已经登录,如果用户已经登录,则执行后续流程,如果用户没有登录,则直接抛出异常,交给异步请求的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 joinCart(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中保存的数据取出,然后遍历将数据写入数据库,进行持久化的操作即可.

购物车的源代码:我发群里

你可能感兴趣的:(购物车原理以及实现)