订单模块
功能介绍:
前台功能:
1.创建订单
controller层实现:
传入userId和收货地址shippingId,由后台自动创建订单。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @RequestMapping("create.do") 2 @ResponseBody 3 public ServerResponse create(HttpServletRequest request, Integer shippingId) { 4 // User user = (User)session.getAttribute(Const.CURRENT_USER); 5 6 String loginToken = CookieUtil.readLoginToken(request); 7 if(StringUtils.isEmpty(loginToken)) { 8 return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户的信息"); 9 } 10 String userJsonStr = RedisShardedPoolUtil.get(loginToken); 11 User user = JsonUtil.string2Obj(userJsonStr, User.class); 12 13 if(user ==null){ 14 return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),ResponseCode.NEED_LOGIN.getDesc()); 15 } 16 //将userId和shippingId传入,创建订单 17 return iOrderService.createOrder(user.getId(), shippingId); 18 }
service层实现:
1)根据userId从购物车表CART中将已勾选的商品取出来,由cartList保存,然后根据这个cartList,由getCartOrderItem()生成订单详情orderItemList。
2)由订单详情orderItemList可以计算出整个订单的总价,由getOrderTotalPrice()实现。
3)根据以上计算出来的订单总价、收货地址shipingId、userId生成最后的订单order,由assembleOrder()实现,并持久化到数据库中。
4)由assembleOrder()生成的订单,是带有最后的订单号的orderNo,所以将这个订单号更新到之前的订单详情orderItemList中,由mybatis的批量插入实现。
5)根据订单详情orderItemList,去对应的商品表中减库存,由reduceProductStock()实现。
6)清空当前购物车,由cleanCart()实现。
7)将生成的最后的订单order和订单详情orderItemList,组合成vo后返回给前台。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public ServerResponse createOrder(Integer userId, Integer shippingId) { 2 //从购物车中获取数据 3 //将购物车中已勾选的物品获取出来,这些物品会用来生成订单 4 List<Cart> cartList = cartMapper.selectCheckedCartByUserId(userId); 5 6 //根据这些物品,即cartList,生成订单详情List 7 ServerResponse serverResponse = this.getCartOrderItem(userId, cartList); 8 if(!serverResponse.isSuccess()) { 9 return serverResponse; 10 } 11 List<OrderItem> orderItemList = (List<OrderItem>)serverResponse.getData(); 12 //根据订单详情list,计算订单总价 13 BigDecimal payment = this.getOrderTotalPrice(orderItemList); 14 15 //生成订单 16 Order order = this.assembleOrder(userId, shippingId, payment); 17 if(order == null) { 18 return ServerResponse.createByErrorMessage("生成订单错误"); 19 } 20 if(CollectionUtils.isEmpty(orderItemList)) { 21 return ServerResponse.createByErrorMessage("购物车为空"); 22 } 23 //将订单号逐一的更新到当前订单详情中,也就是一条订单数据可以对应多条订单详情 24 for(OrderItem orderItem : orderItemList) { 25 orderItem.setOrderNo(order.getOrderNo()); 26 } 27 //mybatis批量插入 28 //由于订单详情,一次性可能有多个商品,多个订单详情,也就是多条数据,所以这里就要使用一次性批量插入 29 orderItemMapper.batchInsert(orderItemList); 30 //生成成功,减少产品的库存 31 this.reduceProductStock(orderItemList); 32 33 //清空购物车 34 this.cleanCart(cartList); 35 36 //将订单明细返回给前端,返回给前端 37 //pojo->vo 38 //组装vo,传给前端的数据 39 OrderVo orderVo = assembleOrderVo(order, orderItemList); 40 return ServerResponse.createBySuccess(orderVo); 41 }
getCartOrderItem()由购物车勾选状态生成订单详情实现:
从购物车表中获取productId,然后从product表中获取当前商品的详细信息,校验商品的在售和库存状态,然后组装订单详情,一种商品对应一个订单详情,一个订单详情由orderNo唯一标识,多个订单详情对应一个订单,最后将组装完成的订单详情cartItemList返回。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private ServerResponse getCartOrderItem(Integer userId,List<Cart> cartList){ 2 List<OrderItem> orderItemList = Lists.newArrayList(); 3 if(CollectionUtils.isEmpty(cartList)){ 4 return ServerResponse.createByErrorMessage("购物车为空"); 5 } 6 7 //校验购物车的数据,包括产品的状态和数量 8 for(Cart cartItem : cartList){ 9 OrderItem orderItem = new OrderItem(); 10 //从购物车中获取productId,然后从product表中获取当前产品的详细信息 11 Product product = productMapper.selectByPrimaryKey(cartItem.getProductId()); 12 //查看当前产品是否在售 13 if(Const.ProductStatusEnum.ON_SALE.getCode() != product.getStatus()){ 14 return ServerResponse.createByErrorMessage("产品"+product.getName()+"不是在线售卖状态"); 15 } 16 17 //校验库存,校验当前产品选购的数量是否超过产品本身库存 18 if(cartItem.getQuantity() > product.getStock()){ 19 return ServerResponse.createByErrorMessage("产品"+product.getName()+"库存不足"); 20 } 21 22 //组装订单详情,一种商品,一个订单详情,而订单详情又由orderNo唯一标识,所以可能多种商品有同样的orderNo,而orderNo又对应订单order中的一个订单,也由orderNo唯一标识 23 24 orderItem.setUserId(userId); 25 orderItem.setProductId(product.getId()); 26 orderItem.setProductName(product.getName()); 27 orderItem.setProductImage(product.getMainImage()); 28 orderItem.setCurrentUnitPrice(product.getPrice()); 29 orderItem.setQuantity(cartItem.getQuantity()); 30 // 这里将当前商品的总价:单价*数量,存进数据表 31 orderItem.setTotalPrice(BigDecimalUtil.mul(product.getPrice().doubleValue(),cartItem.getQuantity())); 32 //将当前订单详情加入订单详情List中 33 orderItemList.add(orderItem); 34 } 35 //将订单详情list返回 36 return ServerResponse.createBySuccess(orderItemList); 37 }
getOrderTotalPrice()由订单详情计算出整个订单的总价:
由于在生成订单详情的时候,已经对每种商品单独计算过对应的总价,存储在订单详情中了,所以这里只需要将订单中的所有商品价值相加即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private BigDecimal getOrderTotalPrice(List<OrderItem> orderItemList) { 2 BigDecimal payment = new BigDecimal("0"); 3 //计算当前订单总计,逐一相加 4 for(OrderItem orderItem : orderItemList) { 5 payment = BigDecimalUtil.add(payment.doubleValue(), orderItem.getTotalPrice().doubleValue()); 6 } 7 return payment; 8 }
assembleOrder()由userId、订单详情、总价组装最后的订单:
由generateOrderNo()生成订单号,然后添加订单所需要的字段,比如订单号、订单状态、运费、支付类型、订单总价等,然后将订单持久化到数据库中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private Order assembleOrder(Integer userId, Integer shippingId, BigDecimal payment) { 2 Order order = new Order(); 3 //生成订单号orderNo,很重要 4 long orderNo = this.generateOrderNo(); 5 //设置订单号 6 order.setOrderNo(orderNo); 7 //设置订单状态 8 order.setStatus(Const.OrderStatusEnum.NO_PAY.getCode()); 9 //设置运费 10 order.setPostage(0); 11 //设置支付类型 12 order.setPaymentType(Const.PaymentTypeEnum.ONLINE_PAY.getCode()); 13 //设置订单总价 14 order.setPayment(payment); 15 order.setUserId(userId); 16 order.setShippingId(shippingId); 17 18 //将这个订单持久化到数据库中 19 int rowCount = orderMapper.insert(order); 20 if(rowCount > 0) { 21 return order; 22 } 23 return null; 24 }
reduceProductStock()由订单详情去对应的商品表中减库存:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private void reduceProductStock(List<OrderItem> orderItemList) { 2 //根据订单详情list,逐一针对商品,去product表中减库存 3 for(OrderItem orderItem : orderItemList) { 4 //根据productId,从product表中拿到当前商品 5 Product product = productMapper.selectByPrimaryKey(orderItem.getProductId()); 6 //减库存 7 product.setStock(product.getStock() - orderItem.getQuantity()); 8 //将库存信息更新到product表中 9 productMapper.updateByPrimaryKeySelective(product); 10 } 11 }
cleanCart()清空购物车:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private void cleanCart(List<Cart> cartList) { 2 for(Cart cart : cartList) { 3 //更新cart购物车表 4 cartMapper.deleteByPrimaryKey(cart.getId()); 5 } 6 }
2.获取购物车中已经选中的商品信息
service层实现:
根据userId从购物车表中,将当前用户选中的商品拿出来,由cartList记录。根据cartList,由getCartOrderItem()得到订单详情orderItemList,从订单详情orderItemList中计算订单总价。最后将订单详情、订单总价等组装成对应的vo对象返回给前端。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public ServerResponse getOrderCartProduct(Integer userId) { 2 OrderProductVo orderProductVo = new OrderProductVo(); 3 //从购物车中获取数据 4 //根据userId,从购物车表中,将当前用户选中的商品拿出来 5 List<Cart> cartList = cartMapper.selectCheckedCartByUserId(userId); 6 //根据选中的商品list,得到订单详情 7 ServerResponse serverResponse = this.getCartOrderItem(userId,cartList); 8 if(!serverResponse.isSuccess()){ 9 return serverResponse; 10 } 11 List<OrderItem> orderItemList =( List<OrderItem> ) serverResponse.getData(); 12 13 List<OrderItemVo> orderItemVoList = Lists.newArrayList(); 14 //从订单详情list中,计算订单总价 15 BigDecimal payment = new BigDecimal("0"); 16 for(OrderItem orderItem : orderItemList){ 17 //计算订单总价 18 payment = BigDecimalUtil.add(payment.doubleValue(),orderItem.getTotalPrice().doubleValue()); 19 //将订单详情,组装成订单详情vo对象,放在voList中 20 orderItemVoList.add(assembleOrderItemVo(orderItem)); 21 } 22 //将订单总价放在orderProductVo中 23 orderProductVo.setProductTotalPrice(payment); 24 //将订单详情voList放在orderProductVo中 25 orderProductVo.setOrderItemVoList(orderItemVoList); 26 //将图片服务器地址放在orderProductVo中 27 orderProductVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix")); 28 //将orderProductVo返回 29 return ServerResponse.createBySuccess(orderProductVo); 30 }
3.订单列表
4.订单详情
5.取消订单
controller层实现:
传入userId,orderNo参数,通过service层实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @RequestMapping("cancel.do") 2 @ResponseBody 3 public ServerResponse cancel(HttpServletRequest request , Long orderNo) { 4 // User user = (User)session.getAttribute(Const.CURRENT_USER); 5 6 String loginToken = CookieUtil.readLoginToken(request); 7 if(StringUtils.isEmpty(loginToken)) { 8 return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户的信息"); 9 } 10 String userJsonStr = RedisShardedPoolUtil.get(loginToken); 11 User user = JsonUtil.string2Obj(userJsonStr, User.class); 12 13 if(user ==null){ 14 return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),ResponseCode.NEED_LOGIN.getDesc()); 15 } 16 return iOrderService.cancel(user.getId(), orderNo); 17 }
service层实现:
通过userId,orderNo查到对应的订单,如果订单状态是未付款,则置订单状态为CANCELED,更行到数据库即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public ServerResponse<String> cancel(Integer userId, Long orderNo) { 2 //根据userId和orderNo拿到订单 3 Order order = orderMapper.selectByUserIdAndOrderNo(userId, orderNo); 4 if(order == null) { 5 return ServerResponse.createByErrorMessage("该用户此订单不存在"); 6 } 7 //判断订单状态 8 if(order.getStatus() != Const.OrderStatusEnum.NO_PAY.getCode()) { 9 return ServerResponse.createByErrorMessage("已付款,无法取消订单"); 10 } 11 Order updateOrder = new Order(); 12 updateOrder.setId(order.getId()); 13 //更新订单状态 14 updateOrder.setStatus(Const.OrderStatusEnum.CANCELED.getCode()); 15 //将更新状态更新到数据库中 16 int row = orderMapper.updateByPrimaryKeySelective(updateOrder); 17 if(row > 0) { 18 return ServerResponse.createBySuccess(); 19 } 20 return ServerResponse.createByError(); 21 }
后台功能:
1.订单列表
2.订单搜索
3.订单详情
4.订单发货
学习目标:
1.设计实用、安全、扩展性强大的常量、枚举类
2.订单号生成规则、订单严谨性判断
3.POJO和VO之间的实际操练
4.mybatis批量插入