前言:
现在兄弟们对“用java编写购物车”可能比较关切,看官们都需要了解一些“用java编写购物车”的相关文章。那么小编也在网上搜集了一些对于“用java编写购物车””的相关知识,希望各位老铁们能喜欢,朋友们一起来学习一下吧!写在前面
本章节我们来介绍购物车模块相关功能的开发,具体包括加入商品;更新商品数;查询商品数;移除商品;单选/取消;全选/取消和购物车列表等这几个功能。本篇主要介绍如何将商品加入到购物车,这里面会定义一些可以复用的购物车代码,还有一些功能:如字段校验,数量校验,价格的动态变化等。
技能掌握
1、购物车模块的设计思想;
2、如何封装一个高复用的购物车核心方法;
3、解决浮点型商业运算中丢失精度的问题;
数据表设计:
商品添加到购物车
首先我们打开controller这个包下面的portal包,在里面新建一个CartController.java文件,里面写入以下代码:
package top.store.controller.portal;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import top.store.common.Const;import top.store.common.ResponseCode;import top.store.common.ServerResponse;import top.store.pojo.User;import top.store.service.ICartService;import top.store.vo.CartVo;import javax.servlet.http.HttpSession;@Controller@RequestMapping("/cart/") //这是我们对整个前台商品功能配置的一个类似于namespace的东西public class CartController { @Autowired private ICartService iCartService; /*** * 前台往购物车添加商品 * @author lenovo * */ @RequestMapping(value = "add.do") //这里就是具体的每个方法的url链接 @ResponseBody //自动序列化json功能 public ServerResponse<CartVo> add(HttpSession session, Integer productId, Integer count){ //验证用户是否登录 User user =(User) session.getAttribute(Const.CURRENT_USER); //未登录需要用户强制登录 if(user ==null){ return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),ResponseCode.NEED_LOGIN.getDesc()); } //实现我们购物车添加商品的逻辑 return iCartService.add(user.getId(),productId,count); }}
然后打开service这个包,在里面新建ICartService接口,里面写入以下代码:
package top.store.service;import top.store.common.ServerResponse;import top.store.vo.CartVo;public interface ICartService { ServerResponse<CartVo> add(Integer userId, Integer productId, Integer count); //前台购物车添加商品}
以及在Impl包里面新建一个CartServiceImpl.java文件,里面写入以下代码:
package top.store.service.impl;import com.google.common.collect.Lists;import org.apache.commons.collections.CollectionUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import top.store.common.Const;import top.store.common.ResponseCode;import top.store.common.ServerResponse;import top.store.dao.CartMapper;import top.store.dao.ProductMapper;import top.store.pojo.Cart;import top.store.pojo.Product;import top.store.service.ICartService;import top.store.util.BigDecimalUtil;import top.store.util.PropertiesUtil;import top.store.vo.CartProductVo;import top.store.vo.CartVo;import java.math.BigDecimal;import java.util.List;@Service("iCartService")public class CartServiceImpl implements ICartService { @Autowired private CartMapper cartMapper; @Autowired private ProductMapper productMapper; /*** * 前台往购物车添加商品 * @author lenovo * */ //注意此处的add里面必须包含userId,因为这个必须是精确到个人进行购物车的商品添加 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.selectCartByUserIdAndProductId(userId,productId); //自定义的sql语句 if(cart ==null){ //该产品不在这个购物车里面,因此需要新增一个这个产品的记录 Cart cartItem =new Cart(); cartItem.setQuantity(count); //往购物车里面添加count数量的商品 cartItem.setChecked(Const.Cart.CHECKED); //设置该商品被选中状态(放入购物车的产品是自动被选中的) cartItem.setProductId(productId); //设置商品id cartItem.setUserId(userId); //商品属于用户 cartMapper.insert(cartItem); //将商品插入到购物车里面 }else{ //说明该商品已经存在于购物车里. //商品已存在,我们就要进行数量相加 count =cart.getQuantity() +count; cart.setQuantity(count); cartMapper.updateByPrimaryKeySelective(cart); } //接下来是核心的功能,如数量判断,值校验,购物车里面的商品也有图片,因此需要组装一个“新的”vo对象。 CartVo cartVo = this.getCartVoLimit(userId); return ServerResponse.createBySuccess(cartVo); } //创建一个用于实现购物车信息的函数 private CartVo getCartVoLimit(Integer userId){ CartVo cartVo =new CartVo(); List<Cart> cartList =cartMapper.selectCartByUserId(userId); //自定义的sql语句 List<CartProductVo> cartProductVoList = Lists.newArrayList(); //记住这里一定要使用它的String构造器,否则会出现更大的错误 BigDecimal cartTotalPrice = new BigDecimal("0"); if(CollectionUtils.isNotEmpty(cartList)){ for(Cart cartItem:cartList){ CartProductVo cartProductVo =new CartProductVo(); cartProductVo.setId(cartItem.getId()); cartProductVo.setUserId(cartItem.getUserId()); cartProductVo.setProductId(cartItem.getProductId()); //根据购物车里面的商品Id来获取商品的信息 Product product =productMapper.selectByPrimaryKey(cartItem.getProductId()); //如果该商品存在的话,我们就使用前面的CartProductVo进行商品的组装(因为CartProductVo本身是一个对象,但是具体需要什么我们需要进行设置) if(product != null){ //我们这里之所以不直接从商品对象里获取信息,是因为后面有些是存在于购物车的,有些存在于商品,很容易造成混乱,因此我们这里就使用了一个组装的购物车商品对象,你需要的时候调用它就可以 cartProductVo.setProductName(product.getName()); cartProductVo.setProductSubtitle(product.getSubtitle()); cartProductVo.setProductMainImage(product.getMainImage()); cartProductVo.setProductStatus(product.getStatus()); cartProductVo.setProductStock(product.getStock()); //判断库存数量 int buyLimitCount = 0; if(product.getStock() >= cartItem.getQuantity()){ //库存充足的时候,系统会提示LIMIT_NUM_SUCCESS buyLimitCount = cartItem.getQuantity(); cartProductVo.setLimitQuantity(Const.Cart.LIMIT_NUM_SUCCESS); }else{ //库存不足的时候,系统提示LIMIT_NUM_FAIL buyLimitCount =product.getStock(); cartProductVo.setLimitQuantity(Const.Cart.LIMIT_NUM_FAIL); //自动更新购物车的有效库存 Cart cartForQuantity =new Cart(); cartForQuantity.setId(cartItem.getId()); cartForQuantity.setQuantity(buyLimitCount); cartMapper.updateByPrimaryKeySelective(cartForQuantity); } //现在更新cartProductVo对象的其他信息 cartProductVo.setQuantity(buyLimitCount); //计算该商品的总价,仅仅针对该商品 //进行乘法运算:价格*数量,注意这里的单价因为是BigDecimal类型,所以需要转换成double类型,最后再利用工具类的toString调用String构造器 //cartProductVo.setProductTotalPrice(BigDecimalUtil.mut(cartProductVo.getProductPrice().doubleValue(),cartProductVo.getQuantity())); //这样会引发空指针异常 cartProductVo.setProductTotalPrice(BigDecimalUtil.mut(product.getPrice().doubleValue(),cartProductVo.getQuantity())); cartProductVo.setProductChecked(cartItem.getChecked()); //选中状态使用购物车自带的属性,不用商品自己设置属性 } //如果不判断是否有商品就进行添加购物车操作,会报空指针异常。(其实就是判断购物车是否是处于被选中状态) if(cartItem.getChecked() ==Const.Cart.CHECKED){ //如果已经勾选,就把之前的商品价格添加到整个购物车总价中进行结算 //这里面有2个参数,一个是我们在初始化的时候设置的0,另一个就是上面计算出来的ProductTotalPrice,这两个都需要进行double转化 cartTotalPrice =BigDecimalUtil.add(cartTotalPrice.doubleValue(),cartProductVo.getProductTotalPrice().doubleValue()); } //把我们上面设置的cartProductVo添加到cartProductVoList里面 cartProductVoList.add(cartProductVo); } } //把CartProductVo放入到我们的CartVo里面 cartVo.setCartTotalPrice(cartTotalPrice); cartVo.setCartProductVoList(cartProductVoList); cartVo.setAllChecked(this.getAllCheckedStatus(userId)); cartVo.setImageHost(PropertiesUtil.getProperty("")); //这里需要直接从服务器中获取 return cartVo; } //根据用户id来判断购物车是否处于选中状态 public boolean getAllCheckedStatus (Integer userId){ if(userId ==null){ return false; } //如果用户不为空 //这里sql语句结果之所以没有返回boolean是因为我们去数据库里面一般返回的都是int,所以需要进行二次判断 return cartMapper.selectCartProductCheckedStatusByUserId(userId) ==0; }}
上面那个是我们的核心代码,所以我依旧啰嗦几句:
1、首先判断传入的参数是否正确:
if(productId ==null ||count ==null){ return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(),ResponseCode.ILLEGAL_ARGUMENT.getDesc()); }
2、看到Cart cart =cartMapper.selectCartByUserIdAndProductId(userId,productId); //自定义的sql语句就知道需要去dao层的CartMapper.java文件,新增一行代码:
//注意在mybatis中使用多个参数时,需要使用param注解 Cart selectCartByUserIdAndProductId(@Param(value ="userId" )Integer userId,@Param(value = "productId") Integer productId);
接着打开mappers包的CartMapper.xml文件,里面新增以下代码:
<select id="selectCartByUserIdAndProductId" resultMap="BaseResultMap" parameterType="map"> select <include refid="Base_Column_List"/> from store_cart where user_id=#{userId} and product_id =#{productId} </select>
3、继续回到CartServiceImpl.java文件,我们发现该产品不在这个购物车里面,因此需要新增一个这个产品的记录,而且商品的选中是自动的,也就是需要设置一个常量。我们能打开common包下面的const文件,在里面新增以下代码:
public interface Cart{ int CHECKED = 1;//购物车选中状态 int UN_CHECKED = 0;//购物车中未选中状态 }
4、接下来是核心的功能,如数量判断,值校验,购物车里面的商品也有图片,因此需要组装一个“新的”vo对象。因此,我们需要在Vo这个包里面,新建两个java类,一个是CartProductVo,它是结合了购物车和商品的对象,因此你可以去购物车和商品对象中选择你所需要的字段,里面的代码如下:
package top.store.vo;import java.math.BigDecimal;public class CartProductVo { //本vo是结合了购物车和商品的对象,因此你可以去购物车和商品对象中选择你所需要的字段 private Integer id; //购物车id private Integer userId; private Integer productId; private Integer quantity; //购物车中商品的数量 private String productName; //商品名称 private String productSubtitle; //商品子标题 private String productMainImage; //商品主图 private BigDecimal productPrice; //商品价格 private Integer productStatus; //商品状态 private BigDecimal productTotalPrice; //商品总价 private Integer productStock; //商品库存 private Integer productChecked;//商品是否勾选 private String limitQuantity;//用于限制数量的一个返回结果 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public Integer getProductId() { return productId; } public void setProductId(Integer productId) { this.productId = productId; } public Integer getQuantity() { return quantity; } public void setQuantity(Integer quantity) { this.quantity = quantity; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getProductSubtitle() { return productSubtitle; } public void setProductSubtitle(String productSubtitle) { this.productSubtitle = productSubtitle; } public String getProductMainImage() { return productMainImage; } public void setProductMainImage(String productMainImage) { this.productMainImage = productMainImage; } public BigDecimal getProductPrice() { return productPrice; } public void setProductPrice(BigDecimal productPrice) { this.productPrice = productPrice; } public Integer getProductStatus() { return productStatus; } public void setProductStatus(Integer productStatus) { this.productStatus = productStatus; } public BigDecimal getProductTotalPrice() { return productTotalPrice; } public void setProductTotalPrice(BigDecimal productTotalPrice) { this.productTotalPrice = productTotalPrice; } public Integer getProductStock() { return productStock; } public void setProductStock(Integer productStock) { this.productStock = productStock; } public Integer getProductChecked() { return productChecked; } public void setProductChecked(Integer productChecked) { this.productChecked = productChecked; } public String getLimitQuantity() { return limitQuantity; } public void setLimitQuantity(String limitQuantity) { this.limitQuantity = limitQuantity; }}
另一个文件是CartVo,里面的代码如下:
package top.store.vo;import java.math.BigDecimal;import java.util.List;public class CartVo { //购物车Vo对象 private List<CartProductVo> cartProductVoList; //商品对象 private BigDecimal cartTotalPrice; //购物车所有商品总价 private boolean allChecked; //购物车是否处于选中状态 private String imageHost; //购物车对象的主图 public List<CartProductVo> getCartProductVoList() { return cartProductVoList; } public void setCartProductVoList(List<CartProductVo> cartProductVoList) { this.cartProductVoList = cartProductVoList; } public BigDecimal getCartTotalPrice() { return cartTotalPrice; } public void setCartTotalPrice(BigDecimal cartTotalPrice) { this.cartTotalPrice = cartTotalPrice; } public boolean isAllChecked() { return allChecked; } public void setAllChecked(boolean allChecked) { this.allChecked = allChecked; } public String getImageHost() { return imageHost; } public void setImageHost(String imageHost) { this.imageHost = imageHost; }}
5、我们继续回到CartServiceImpl.java文件,看到这行代码List<Cart> cartList =cartMapper.selectCartByUserId(userId); //自定义的sql语句就知道需要去dao层的CartMapper.java文件,新增一行代码:
List<Cart> selectCartByUserId(Integer UserId);
接着打开mappers包的CartMapper.xml文件,里面新增以下代码:
<select id="selectCartByUserId" resultMap="BaseResultMap" parameterType="int"> select <include refid="Base_Column_List"/> from store_cart where user_id =#{userId} </select>
6、Java中丢失精度的问题是一个值得注意的问题,因此我们需要进行设置。float和double只能用于科学计算和工程计算,商业运算必须使用BigDecimal。通过查看BigDecimal的String构造器,我们发现:
When a {@code double} must be used as a source for a* {@code BigDecimal}, note that this constructor provides an* exact conversion; it does not give the same result as* converting the {@code double} to a {@code String} using the* {@link Double#toString(double)} method and then using the* {@link #BigDecimal(String)} constructor. To get that result,* use the {@code static} {@link #valueOf(double)} method.
从上面的源码中我们也发现了这一情况,因此需要使用它的String构造器。但是呢,我们的数据库中存放的都是int类型,因此我们需要创建一个用于转换类型的工具类,打开Util这个包,我们在里面新建一个BigDecimalUtil.java的文件,在里面写入以下代码:
package top.store.util;import java.math.BigDecimal;public class BigDecimalUtil { //我们不能使我们的工具类在外部进行实例化,因此将构造方法私有化 private BigDecimalUtil(){ } //加法 public static BigDecimal add(double a1,double a2){ BigDecimal b1 =new BigDecimal(Double.toString(a1)); BigDecimal b2 =new BigDecimal(Double.toString(a2)); return b1.add(b2); } //减法 public static BigDecimal sub(double a1,double a2){ BigDecimal b1 =new BigDecimal(Double.toString(a1)); BigDecimal b2 =new BigDecimal(Double.toString(a2)); return b1.subtract(b2); } //乘法 public static BigDecimal mut(double a1,double a2){ BigDecimal b1 =new BigDecimal(Double.toString(a1)); BigDecimal b2 =new BigDecimal(Double.toString(a2)); return b1.multiply(b2); } //除法,注意使用四舍五入 public static BigDecimal div(double a1,double a2){ BigDecimal b1 =new BigDecimal(Double.toString(a1)); BigDecimal b2 =new BigDecimal(Double.toString(a2)); return b1.divide(b2,2,BigDecimal.ROUND_HALF_UP); //四舍五入模式,保留2位小数 }}
你可以使用Ctrl+O(字母O)来查看该类的不同类型的构造方法,我们使用下面的构造方法,scale是指你保留的位数,roundingMode是指什么模式:ROUND_HALF_UP是四舍五入模式。
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
具体的可以参看这篇文章:BigDecimal.ROUND_HALF_XXX的各种用法
7、我们这里之所以不直接从商品对象里获取信息,是因为后面有些是存在于购物车的,有些存在于商品,很容易造成混乱,因此我们这里就使用了一个组装的购物车商品对象,你需要的时候调用它就可以。
8、在判断库存数量的时候,我们需要注意设置一个常量,用于提示前端最低库存,继续打开common包下面的const文件,在里面新增以下代码:
String LIMIT_NUM_FAIL = "LIMIT_NUM_FAIL";String LIMIT_NUM_SUCCESS = "LIMIT_NUM_SUCCESS";
把之前的购物车状态代码合在一块就是:
public interface Cart{ int CHECKED = 1;//购物车选中状态 int UN_CHECKED = 0;//购物车中未选中状态 String LIMIT_NUM_FAIL = "LIMIT_NUM_FAIL"; String LIMIT_NUM_SUCCESS = "LIMIT_NUM_SUCCESS"; }
9、然后就是cartVo.setImageHost(PropertiesUtil.getProperty("")); //这里需要直接从服务器中获取这个很多注意事项我都在代码里面作了详细的说明。
10、看到这行代码,你同样需要
//如果用户不为空//这里sql语句结果之所以没有返回boolean是因为我们去数据库里面一般返回的都是int,所以需要进行二次判断return cartMapper.selectCartProductCheckedStatusByUserId(userId) ==0;
去dao层的CartMapper.java文件,新增一行代码:
int selectCartProductCheckedStatusByUserId(Integer userId);
接着打开mappers包的CartMapper.xml文件,里面新增以下代码:
<select id="selectCartProductCheckedStatusByUserId" resultType="int" parameterType="int"> <!--这里就是说里面有没有选中的就意味着不是全选--> SELECT count(1) from store_cart where checked = 0 and user_id = #{userId} </select></mapper>
精度测试
对了,还有前面的精度测试,我们打开test包下面的java包,里面新建一个包top.licheetools.test,名字随意,我这里就是这个样子,然后新建BigDecimalTest.java文件:
package top.licheetools.test;import org.junit.Test;import java.math.BigDecimal;//这里因为是专门的测试包,所以不需要使用junit,也不需要加载Spring容器public class BigDecimalTest { @Test public void test1(){ System.out.println(0.08+0.05); System.out.println(2.01-0.58); System.out.println(0.08*100); System.out.println(6.89/100); } @Test public void test2(){ //使用BigDecimal的Integer构造函数 BigDecimal b1 =new BigDecimal(0.06); BigDecimal b2 =new BigDecimal(0.09); System.out.println(b1.add(b2)); } @Test public void test3(){ //使用BigDecimal的String构造函数 BigDecimal b1 =new BigDecimal("0.06"); BigDecimal b2 =new BigDecimal("0.09"); System.out.println(b1.add(b2)); }}
运行结果我这里就只附上截图:
这样我们本篇关于如何将商品加入到购物车的介绍就到此为止了,这里因为需要构造一个复用的购物车模型,所以需要很多时间,但是后面功能的实现就能直接借助它了,开发起来还是快速的,感谢你的赏阅!