up:: SpringBoot电商项目购物车模块购物车列表接口

说明:

(1) 本篇博客内容:开发【添加商品到购物车】接口;

(2) 本篇博客需要注意的点:

● 如果接口返回的数据格式不符合接口的要求(接口要求返回JSON格式,而接口没有做相应的设置),也是会报404错误的;

● 一些复杂的、可以单独拉出来的逻辑,可以单独写一个方法;(以防止单个方法规模过于庞大)

● 对于,比如【不同的数字代表不同的含义】的地方,我们最好在Constant这个盛放常量的类中,予以说明;以防程序后来的维护者、接盘者,一时不知不同的数据到底代表什么含义;

**(3)本篇博客出于安全考虑:而采用的一种广泛采用的做法: 接口不能传入用户id,而是要在程序内部获取;否则,如果接口参数中有用户id的话,这很可能会被黑客利用,黑客就可以通过这个接口,给任何人的购物车中随意添加商品了;

其实,由此,也能感受到【接口的设计能力】,这个能力也是需要慢慢锻炼的;


一:【添加商品到购物车】接口;

1.【添加商品到购物车】接口文档;

接口返回的是“购物车列表”;

说明:

(1)这个接口为什么要返回购物车列表?: 我们每次向购物车添加商品后,购物车就会发生变化;即,我们的购物车肯定是需要刷新的;那么与其让其前端再去调用接口来获取最新的购物车列表,不如我们在这个接口就直接返回最新的购物车列表,这可以减少延迟,提高性能;(PS:购物车模块的其他接口,也是如此)

2.【添加商品到购物车】接口,在界面上的表现;

待写,项目上线后,再回来补充……


二:正式开发;

1.在CartController中,创建【添加商品到购物车】的方法:add()方法;

 
         /**
          * 购物车模块:添加商品到购物车;
          * @param productId
          * @param count
          * @return
          */
         @ApiOperation("添加商品到购物车")
         @PostMapping("/add")
         public ApiRestResponse add(@RequestParam("productId") Integer productId, @RequestParam("count") Integer count) {
             List<CartVO> cartVOList =  cartService.add(UserFilter.currentUser.getId(), productId, count);
             return ApiRestResponse.success(cartVOList);
         }

说明:

(1) url,请求方式,参数,要符合接口文档的要求;

(2) 因为,这个接口会被在【SpringBoot电商项目购物车模块统一校验当前是否有用户登录】中编写的过滤器给处理;所以,我们可以通过UserFilter来获取当前登录用户;

(5) Service层的添加商品到购物车的方法,在下一部分介绍;

2.在CartServiceImpl实现类中编写添加商品到购物车的方法:add()方法;

 
     /**
          * 添加商品到购物车
          * @param userId
          * @param productId
          * @param count
          * @return
          */
         @Override
         public List<CartVO> add(Integer userId, Integer productId, Integer count) {
             //调用工具方法,先看下【我们传的这个productId,在数据库中究竟有没有这个id的商品】;
             //然后,再看下商品是否是上架状态、商品库存是否足够;
             validProduct(productId, count);
 
 
             //然后,在看下购物车中,是否已经添加过了这个商品
             Cart cart = cartMapper.selectCartByUserIdAndProductId(userId,productId);
             if (cart == null) {//如果该商品之前,不在购物车里;那么,我们就创建一个cart记录,添加到cart表中;
                 cart = new Cart();
                 cart.setProductId(productId);
                 cart.setUserId(userId);
                 cart.setQuantity(count);
									cart.setSelected(Constant.CartIsSelected.CHECKED);//默认情况下,商品被添加到购物车后,就是选中状态
                 cartMapper.insertSelective(cart);
             } else {//如果该商品之前,就在购物车里了;那么,我们就去更新购物车中的这个商品,主要是对商品数量的叠加
                 int countNew = cart.getQuantity() + count;
                 Cart cartNew = new Cart();
                 cartNew.setQuantity(countNew);
                 cartNew.setId(cart.getId());
                 cartNew.setProductId(cart.getProductId());
                 cartNew.setUserId(cart.getUserId());
                 //至于商品是否设为选中,需要看具体的项目业务要求;
                 // 我们这儿的策略,只要我们增加购物车的某个商品的数量了,就认为用户想买了,就把其设为选中状态;
                 cartNew.setSelected(Constant.CartIsSelected.CHECKED);
                 cartMapper.updateByPrimaryKeySelective(cartNew);
             }
 
 
             return this.list(userId);
         }
 
         /**
          * 工具方法;判断商品是否存在、商品是否是上架状态、商品库存是否足够;
          * @param productId
          * @param count
          */
         private void validProduct(Integer productId, Integer count) {
             //判断商品是否存在、商品是否是上架状态;如果不行,就抛出“商品不可售”的异常;
             Product product = productMapper.selectByPrimaryKey(productId);
             if (product == null || !product.getStatus().equals(Constant.SaleStatus.SALE)) {
                 throw new  ImoocMallException(ImoocMallExceptionEnum.NOT_SALE);
             }
             //判断商品库存,如果库存不足,也不能把商品添加到购物车,抛出“商品库存不足异常;
             if (count > product.getStock()) {
                 throw new  ImoocMallException(ImoocMallExceptionEnum.NOT_ENOUGH);
             }
         }

说明:

(1.1)编写工具方法validProduct()方法,去检查:【前端传过来的商品id,商品表中是否有这个商品】、【商品是否是上架状态】、【商品的库存是否足够】;


(1.2)程序上下架的状态,我们为了【开发时,更加明确含义】、【有利于后期扩展】:把其定义在了Constant类中;(这是个很重要的开发习惯!!!)


(2.1)如果商品存在,商品是上架状态,商品库存足够时,这人需要根据购物车中是否有这个商品,来分情况处理;


(2.2)调用在【5.在CartMapper中定义【根据userId和ProductId,从Cart表中,查询数据】的方法:selectCartByUserIdAndProductId()方法;并在CartMapper.xml中编写实现SQL;】中定义的方法,判断在购物车中,是否添加过这个商品;


(2.3)如果购物车中没有这个商品,那么我们就向购物车中添加这个商品;


(2.4)如果购物车中,已经有了这个商品;那么,我们就在原有的基础上,对商品的数量进行更新;

PS:我们在更新的时候,需要设置id;


(2.5)和商品上下架同理,购物车中商品的选中状态,我们也在Constant类中定义了;


(3)因为,这个接口要求返回购物车列表,所以,这个方法调用了在开发【购物车列表】接口时,开发的list()方法;

4.在CartService接口中反向生成方法的声明;

5.在CartMapper中定义【根据userId和ProductId,从Cart表中,查询数据】的方法:selectCartByUserIdAndProductId()方法;并在CartMapper.xml中编写实现SQL;

(1)在CartMapper中定义【根据userId和ProductId,从Cart表中,查询数据】的方法:selectCartByUserIdAndProductId()方法;

很显然,通过userId和productId可以唯一确定cart表的一条数据;


(2)在CartMapper.xml中编写实现SQL;

  <select id="selectCartByUserIdAndProductId" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from imooc_mall_cart
    where user_id = #{userId,jdbcType=INTEGER} and product_id = #{productId,jdbcType=INTEGER};
  </select>

这个SQL没什么好说的,就是根据userId和productId,去cart表中看看,购物车中是否有添加过这个商品;


三:测试:

1.一个容易犯错的点:报404错误,也可能是因为接口没有返回JSON格式的数据;

所以,对此,我们可以在类上改用@RestController注解,或者在方法上使用@ResponseBody注解; (PS:我们这儿的选择是,在类上改用@RestController注解)

2.正常测试;