mmall商城购物车模块总结
购物车模块的设计思想
购物车的实现方式有很多,但是最常见的就三种: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.35
System.out.println(2.9-1.12);//猜测是1.78
System.out.println(1.3*3);//猜测是3.9
System.out.println(36.6/3.0);//猜测是412.2
}
但是实际结果是
4.35
1.7799999999999998
3.9000000000000004
12.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 {
//如果含有这个商品,数量加count
count = 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);
}
@Override
public 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_cart
set 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商城购物车模块总结的更多相关文章
- Mvp快速搭建商城购物车模块
代码地址如下:http://www.demodashi.com/demo/12834.html 前言: 说到MVP的时候其实大家都不陌生,但是涉及到实际项目中使用,还是有些无从下手.因此这里小编带着大 ...
- 用JSP实现的商城购物车模块
这两天,在学习JSP,正好找个小模块来练练手: 下面就是实现购物车模块的页面效果截图: 图1. 产品显示页面 通过此页面进行产品选择,增加到购物车 图2 .购物车页面 图3 . 商品数量设置 好了,先 ...
- Vue node.js商城-购物车模块
一.渲染购物车列表页面 新建src/views/Cart.vue获取cartList购物车列表数据就可以在页面中渲染出该用户的购物车列表数据 data(){ return { car ...
- mmall商城分类模块总结
后台分类model的开发具体功能有:添加分类名称,修改分类名称,查询所有子分类,查询父分类以及它下面的子分类(递归) 需要注意的是,在后台管理进行操作的时候,都需要验证当前用户是否是管理员的角色,不管 ...
- mmall商城用户模块开发总结
1.需要实现的功能介绍 注册 登录 用户名校验 忘记密码 提交问题答案 重置密码 获取用户信息 更新用户信息 退出登录 目标: 避免横向越权,纵向越权的安全漏洞 MD5明文加密级增加的salt值 Gu ...
- 走进Vue时代进阶篇(01):重构电商购物车模块
前言 从这篇文章开始,我准备给大家分享一些关于Vue.js这门框架的技巧性系列文章,正好我们公司项目中也用到了Vue.所以,教是最好的学.进阶篇比较适合于二三线城市,还在小厂打拼的童鞋们.欢迎你们跟着 ...
- Java开源生鲜电商平台-购物车模块的设计与架构(源码可下载)
ava开源生鲜电商平台-购物车模块的设计与架构(源码可下载) 说明:任何一个电商无论是B2C还是B2B都有一个购物车模块,其中最重要的原因就是客户需要的东西放在一起,形成一个购物清单,确认是否有问题, ...
- 3_python之路之商城购物车
python之路之商城购物车 1.程序说明:Readme.txt 1.程序文件:storeapp_new.py userinfo.py 2.程序文件说明:storeapp_new.py-主程序 use ...
- 基于vue2.0打造移动商城页面实践 vue实现商城购物车功能 基于Vue、Vuex、Vue-router实现的购物商城(原生切换动画)效果
基于vue2.0打造移动商城页面实践 地址:https://www.jianshu.com/p/2129bc4d40e9 vue实现商城购物车功能 地址:http://www.jb51.net/art ...
随机推荐
- 题解-CF1140E Palindrome-less Arrays
CF1140E Palindrome-less Arrays \(n\) 和 \(k\) 和 \(n\) 个数的序列 \(a\).把 \(a\) 中的 \(-1\) 替换成 \([1,k]\) 之间的 ...
- RDD、DF和DS的共性与区别
共性: 1.都是spark平台下的分布式弹性数据集 2.都有惰性机制,创建.转换如map操作时不会立即执行,遇到foreach等Action算子时才开始运算. 3.都会自动缓存计算 4.都有parti ...
- 安卓基于谷歌串口api进行串口开发
准备材料 AndroidStudio 谷歌android-serialport-api 前情提要 网上提供很多基于c语言对安卓串口开发,有jni.cmake等等,不过都太高深,谷歌提供的api已经可以 ...
- windows 远程连接报错
在windows7上或者windows10上远程连接服务器报错("连接错误"),试了网上的方法,发现是服务器安装ssl证书关闭了ssh服务,开启ssh服务后,重启电脑就可以解决这个 ...
- 使用tkinter打造一个小说下载器,想看什么小说,就下什么
前言 今天教大家用户Python GUI编程--tkinter 打造一个小说下载器,想看什么小说,就下载什么小说 先看下效果图 Tkinter 是使用 python 进行窗口视窗设计的模块.Tkint ...
- Python轻松入门到项目实战-实用教程
本课程完全基于Python3讲解,针对广大的Python爱好者与同学录制.通过本课程的学习,可以让同学们在学习Python的过程中少走弯路.整个课程以实例教学为核心,通过对大量丰富的经典实例的讲解.让 ...
- js--数组的map()方法的使用
javaScript中Array.map()的用法 前言 作为一个刚刚踏入前端世界的小白,工作中看到身边同事大佬写的代码就像古诗一样简介整齐,而我的代码如同一堆散沙,看上去毫无段落感,而且简单的功能需 ...
- java.lang.NoClassDefFoundError: org/I0Itec/zkclient/IZkStateListener
spark streaming 读 kafka 报错 java.lang.NoClassDefFoundError: org/I0Itec/zkclient/IZkStateListener java ...
- MybatisPlus_01
目录 1.1 简介 1.1.1 特性 1.1.2 框架结构 2.1 快速开始 2.1.1 2.1.2 yaml文件配置 2.1.3 编码 2.1.4 测试 3.1 思考 1.1 简介 MyBatis- ...
- APEX-数据导出/打印
前言: 由于公司使用了Oracle APEX构建应用,且在APEX新版本v20.2版本中增强了相关报表导出数据相关功能:正好现在做的事情也需要类似的功能,就先来学习一下Oracle的APEX相关功能及 ...