购物车模块的设计思想

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

封装一个高复用购物车核心方法

这个模块中封装了两个value object类;一个是CartProductVo,另一个是CartVo,这两个有什么联系呢?
首先我们回想一下京东或者淘宝的购物车,这个购物车显示给我的肯定含有商品的一些属性,例如名称,单价,主图,标题等,除了这些还有他自己的一些属性,像总的价格,目前是否勾选,商品的状态等等;这就是购物车与商品结合的VO,CartProductVo,而CartVo是对cartProductVo的大的包装,我们最后在渲染视图的时候,返回的对象就是CartVo;
CartProductVo类

public class CartProductVo {//结合Cart和product封装一个抽象对象private Integer id;private Integer userId;private Integer productId;private Integer quantity; //数量private String productName;private String productSubTitle;private String productMainImage;private BigDecimal productPrice;private Integer status;private BigDecimal productTotalPrice;private Integer productStock;   //库存private Integer productChecked;  //是否勾选private String limitQuantity;//限制数量的返回结果

CartVo类

public class CartVo {private List<CartProductVo> cartProductVoList;private BigDecimal cartTotalPrice;private Boolean allChecked;    //全选private String imageHost;//图片路径

在购物车操作的时候,用户对购物车进行一列的添加,删除,修改数量,而我们要显示最终的CartVo对象给他;知道要干什么之后,就是想实现方法了,我们可以获取这个用户id,从而知道用户以后的操作;我们根据用户id查找到这个用户的购物车,返回是一个集合,集合中的元素是购物车,然后判断一下这个集合是否为空且它的size是不是为0,如果不是,开始遍历这个集合,我们先把pojo中的Cart封装到CartProductVo对象中,再把CartProduct对象装到CartVo的list集合中一层一层的装载,最终返回我们想要的结果。当然中间因为含有产品的一些属性,所以我们要再次从数据库中找到这个商品,返回到service,从而将属性赋值给CartProductVo对象。在这个过程中,我们要注意的是,判断用户当前购买某个商品的数量是否超过库存量,如果超过,就不能再增加,库存量要更新值为当前数量,并且反回失败。计算这个商品的总金额就是数量x单价;调用BigDecimalUtil的乘法运算。
具体实现代码如下:

 private CartVo getCartVoLimit(Integer userId) {List<Cart> cartList = cartMapper.selectCartByUserId(userId);CartVo cartVo = new CartVo();List<CartProductVo> cartProductVoList = Lists.newArrayList();//处理计算精度缺失BigDecimal totalPrice = new BigDecimal("0");if (CollectionUtils.isNotEmpty(cartList)) {for (Cart cart : cartList) {CartProductVo cartProductVo = new CartProductVo();cartProductVo.setId(cart.getId());cartProductVo.setUserId(userId);cartProductVo.setProductId(cart.getProductId());Product product = productMapper.selectByPrimaryKey(cart.getProductId());if (product != null) {cartProductVo.setProductMainImage(product.getMainImage());cartProductVo.setProductName(product.getName());cartProductVo.setProductPrice(product.getPrice());cartProductVo.setProductSubTitle(product.getSubtitle());cartProductVo.setProductStock(product.getStock());cartProductVo.setStatus(product.getStatus());//判断库存int buyLimitCount = 0;if (product.getStock() >= cart.getQuantity()) {//如果购物车中的某件商品数量小于等于产品的总数,库存充足buyLimitCount = cart.getQuantity();cartProductVo.setLimitQuantity(Const.Cart.LIMIT_NUM_SUCCESS);} else {//如果买的数量大于库存总数,更新库存,并返回失败buyLimitCount = product.getStock();cartProductVo.setLimitQuantity(Const.Cart.LIMIT_NUM_FAIL);//更新有效库存Cart cart1 = new Cart();cart1.setId(cart.getId());cart1.setQuantity(buyLimitCount);cartMapper.updateByPrimaryKeySelective(cart);}cartProductVo.setQuantity(buyLimitCount);//计算总价格,单价 X 数量double num = cartProductVo.getQuantity();double unitPrice = product.getPrice().doubleValue();cartProductVo.setProductTotalPrice(BigDecimalUtil.mul(unitPrice, num));cartProductVo.setProductChecked(cart.getChecked());}//如果有一项被勾选就加入到总价中if (cart.getChecked() == Const.Cart.CHECKED) {//加他自己totalPrice = BigDecimalUtil.add(cartProductVo.getProductTotalPrice().doubleValue(), totalPrice.doubleValue());}//最后在把封装好的vo添加到list集合cartProductVoList.add(cartProductVo);}}cartVo.setAllChecked(this.getCheckedStatus(userId));cartVo.setCartProductVoList(cartProductVoList);cartVo.setCartTotalPrice(totalPrice);cartVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix"));return cartVo;}

解决浮点型商业运算中丢失精度的问题

因为对于java来讲并没有提供支付时所处理的元素方法,当我们直接使用浮点类型进行加减乘除运算的时候会出现以下情况

 public void test1(){System.out.println(1.2+3.15);//猜测是4.35System.out.println(2.9-1.12);//猜测是1.78System.out.println(1.3*3);//猜测是3.9System.out.println(36.6/3.0);//猜测是412.2}但是实际结果是4.351.77999999999999983.900000000000000412.200000000000001

因此如果直接使用浮点类型进行商品交易结算时这时会出大问题的;所以我们需要封装一个专门处理商业运算的工具类;选取BigDecimal类,使用它的含有字符串参数的构造方法创建对象是安全可靠的。

package cn.edu.mmall.util;import java.math.BigDecimal;public class BigDecimalUtil {private BigDecimalUtil() {}/***加法* @param v1* @param v2* @return*/public static BigDecimal add(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.add(b2);}/*** 减法* @param v1* @param v2* @return*/public static BigDecimal sub(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.subtract(b2);}/*** 乘法* @param v1* @param v2* @return*/public static BigDecimal mul(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));return b1.multiply(b2);}/*** 两数相除,需要保留小数点后两位,四舍五入* @param v1* @param v2* @return*/public static BigDecimal div(double v1,double v2){BigDecimal b1 = new BigDecimal(Double.toString(v1));BigDecimal b2 = new BigDecimal(Double.toString(v2));//四舍五入保留两位小数return b1.divide(b2,2,BigDecimal.ROUND_HALF_UP);}
}

BigDecimal类中还根据对应的参数设置了小数的处理方式,四舍五入,向上或向下取整,保留小数位等等,功能非常强大,但是重要的是,我们创建对象的时候一定要使用字符串参数的构造函数,只有这个才能确保数据计算过程中不会出现精度丢失。

功能的介绍

加入商品;更新商品、查询商品数、移除商品、单选/取消,全选/取消,购物车列表

加入商品:
如果商品id和数量又一个为空返回参数错误;然后根据用户id和商品id查询出这个购物车,如果购物ce为null,说明这个商品是第一加入,那就新建一个Cart对象,设置id,数量,选中,以及用户id,插入数据库即可,否则就是含有该商品,其余不动,商品的数量修改为传入的数据。

 public ServerResponse<CartVo> add(Integer userId, Integer productId, Integer count) {if (productId == null || count == null) {return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(),ResponseCode.ILLEGAL_ARGUMENT.getDesc());}Cart cart = cartMapper.selectByUserIdAndProductId(userId, productId);if (cart == null) {//这个产品不在这个购物车里,需要新增加一个产品记录Cart cartItem = new Cart();cartItem.setProductId(productId);cartItem.setQuantity(count);cartItem.setChecked(Const.Cart.CHECKED);cartItem.setUserId(userId);cartMapper.insert(cartItem);} else {//如果含有这个商品,数量加countcount = cart.getQuantity() + count;cart.setQuantity(count);cartMapper.updateByPrimaryKeySelective(cart);}return list(userId);}

list方法:

public ServerResponse<CartVo> list(Integer userId) {CartVo cartVoLimit = this.getCartVoLimit(userId);return ServerResponse.createBySuccessData(cartVoLimit);}

我们需要判断用户是不是全选所有商品,这时候的SQL语句就有技巧了,我们可以反着来处理,查询所有未选中的商品数量如果是0,就返回true,否则返回false;

private boolean getCheckedStatus(Integer userId) {if (userId == null) {return false;}//开始从数据库中查询当前用户购物车是否全选,全选返回true,否则false;int resultCount = cartMapper.selectCartProductCheckStatusByUserId(userId);return resultCount == 0;}

剩余代码:

 public ServerResponse<CartVo> update(Integer userId, Integer productId, Integer count) {if (productId == null || count == null) {return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(),ResponseCode.ILLEGAL_ARGUMENT.getDesc());}Cart cart = cartMapper.selectByUserIdAndProductId(userId, productId);if (cart == null) {cart.setQuantity(count);}cartMapper.updateByPrimaryKeySelective(cart);return list(userId);}@Overridepublic ServerResponse<CartVo> delete(Integer userId, String productIds) {List<String> productList = Splitter.on(",").splitToList(productIds);if (CollectionUtils.isEmpty(productList)) {return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(),ResponseCode.ILLEGAL_ARGUMENT.getDesc());}int i = cartMapper.deleteByUserIdAndProducts(userId, productList);return list(userId);}

根据用户id获取全购物车的商品数量

 <select id="selectCartProductCount" resultType="java.lang.Integer" parameterType="integer">select IFNULL(sum(quantity),0) as count from mmall_cart where user_id =#{userId}</select>

处理全选,全不选时候,商品id传null;单选,取消单选操作的时候,传的值不为null;
四种不同的请求对应一个处理方法,dao中是一条SQL语句;

  <update id="checkedOrUnCheckedProduct" parameterType="map">update mmall_cartset checked = #{checked},update_time=now()where user_id =#{userId}<if test="productId !=null">and product_id=#{productId}</if></update>
public ServerResponse<CartVo> selectOrUnSelect(Integer userId, Integer productId, Integer checkedId) {cartMapper.checkedOrUnCheckedProduct(userId, productId, checkedId);return list(userId);}

mmall商城购物车模块总结相关推荐

  1. Mvp快速搭建商城购物车模块

    代码地址如下: http://www.demodashi.com/demo/12834.html 前言: 说到MVP的时候其实大家都不陌生,但是涉及到实际项目中使用,还是有些无从下手.因此这里小编带着 ...

  2. jQuery实现PC端商城购物车模块基本功能(每个商品的小计和合计都会根据添加和删除的操作来动态计算)

    jQuery实现PC端商城购物车模块基本功能 先上效果图: 因为主要是想练习jQuery的使用,所以页面CSS部分比较简陋,有需要的话,大家在参考代码时,可以自己再完善下CSS部分的代码,让购物车页面 ...

  3. 淘淘商城---购物车模块

    一:模仿JD,在用户不登录的情况下,可以实现添加商品到购物车内.---将商品存放到cookie中 此处是商品详情页面-----点击加入购物车按钮的连接:端口:8090   参数:商品id  . 商品数 ...

  4. web day25 web day24 小项目练习图书商城, 购物车模块,订单模块,支付(易宝支付)

    购物车模块 购物车存储: 保存在session中(本次使用的) 保存在cookie中 保存在数据库中 购物车相关类 购物车结构 CartItem:包含图书和数量,小计 Cart:包含一个Map< ...

  5. Vue node.js商城-购物车模块

      一.渲染购物车列表页面 新建src/views/Cart.vue 获取cartList购物车列表数据就可以在页面中渲染出该用户的购物车列表数据 data(){   return {      ca ...

  6. mmall商城分类模块总结

    后台分类model的开发具体功能有:添加分类名称,修改分类名称,查询所有子分类,查询父分类以及它下面的子分类(递归) 需要注意的是,在后台管理进行操作的时候,都需要验证当前用户是否是管理员的角色,不管 ...

  7. mmall商城用户模块开发总结

    1.需要实现的功能介绍 注册 登录 用户名校验 忘记密码 提交问题答案 重置密码 获取用户信息 更新用户信息 退出登录 目标: 避免横向越权,纵向越权的安全漏洞 MD5明文加密级增加的salt值 Gu ...

  8. 【3D商城】使用Vuex状态管理完成购物车模块

    [3D商城]使用Vuex状态管理完成购物车模块 创建购物车的全局数据 添加产品到购物车 导航栏的购物车模块 结果 常见问题总结 创建购物车的全局数据 在store的index.js中 ,创建购物车变量 ...

  9. mmall购物车模块

    mmall购物车模块 cart数据库表设计 门户_购物车接口 业务需求 查询购物车list 方法复用getCartVoLimit cart数据库表设计 CREATE TABLE `mmall_cart ...

最新文章

  1. STM32电源框图解析(VDD、VSS、VDDA、VSSA、VREF+、VREF-、VBAT等的区别)
  2. Java入门学习注意事项有哪些?
  3. 基于matlab的64QAM,通信调制体制设计之64QAM性能分析MATLAB仿真及代码
  4. BT[2]-BLE初体验:心率计
  5. VTK:选择像素用法实战
  6. 浅谈 G1 GC 日志格式
  7. Echarts给坐标轴添加自定义属性
  8. Android之JNI ERROR (app bug): accessed stale global reference 0xb39533f2 (index 19708 in a table of s
  9. Spring-Boot + AOP实现多数据源动态切换
  10. ubuntu下gvim启动出现gtk warning Invalid input string
  11. 智能手机RAM和ROM的区别以及SD卡的作用
  12. minecraftjava版光追_我的世界:网易版终于更新狐狸生物?Java版光追技术已开始测试?...
  13. c语言小红今年12岁 他父亲比,书人2017秋季四年级期中复习题解析(1-60).pdf
  14. excel android 官网下载地址,excel手机版app下载-excel手机版(excel教程学习)下载v2.0 安卓版-西西软件下载...
  15. 小车yolo机械臂(四)python ros 和darknet_ros 使用launch文件启动脚本
  16. IDEA部署web项目Warning:No artifacts configured.
  17. @submit.prevent作用
  18. 老林学习笔记 :纯js 继承 闭包 与js实现继承原理 veu实现继承
  19. Elasticsearch的安装,以及Springboot整合Elasticsearch
  20. 在预装win8的UFI+GTP的pc上实现ubuntu和win8双系统启动

热门文章

  1. 公共CA与私有CA有何区别?
  2. tun/tap虚拟网卡收发机制解析
  3. 自动化运维和普通运维的区别是什么?哪个好?
  4. 迷你仓发展历程,深圳宝师傅上门存储引领迷你自助仓储行业新方向
  5. Flutter 输入密码
  6. 渡鸦MK60学习 【DMA直接存储器读取】
  7. JavaScript 国庆倒计时小案例
  8. 集和--迭代器Iterator
  9. 符号三角形 回溯法 pta
  10. 对大文件做CRC32校验