Spring Boot点餐系统实战(7)–买家订单service层(上)

本文介绍买家下单的service层的创建,并实现创建订单的方法。

 

本文关键词

DTO

@Transactional 注解

@Transient  注解

扣库存

随机数主键

异常

 

一、开发准备

1、数据库效果

order_master 表

Spring Boot点餐系统实战(7)–买家订单service层(上)

order_detail 表

Spring Boot点餐系统实战(7)–买家订单service层(上)

 

2、最终文件结构

Spring Boot点餐系统实战(7)–买家订单service层(上)

 

二、创建 DTO

DTO 全称数据传输对象,用于各个层的交互。而我们的 Entity 用于和数据库对接。

1、OrderDTO.java

  1. package com.liuyanzhao.sell.dto;
  2. import com.liuyanzhao.sell.entity.OrderDetail;
  3. import lombok.Data;
  4. import java.math.BigDecimal;
  5. import java.util.Date;
  6. import java.util.List;
  7. /**
  8.  * 订单
  9.  * @Author: 言曌
  10.  * @Date: 2017/11/13
  11.  * @Time: 上午11:23
  12.  */
  13. @Data
  14. public class OrderDTO {
  15.     //订单编号
  16.     private String orderId;
  17.     //买家名字
  18.     private String buyerName;
  19.     //买家电话
  20.     private String buyerPhone;
  21.     //买家地址
  22.     private String buyerAddress;
  23.     //买家微信Openid
  24.     private String buyerOpenid;
  25.     //订单总金额
  26.     private BigDecimal orderAmount;
  27.     //订单状态,默认为0新订单
  28.     private Integer orderStatus;
  29.     //支付状态,默认为0未支付
  30.     private Integer payStatus;
  31.     //订单创建时间
  32.     private Date createTime;
  33.     //订单更新时间
  34.     private Date updateTime;
  35.     private List<OrderDetail> orderDetailList;
  36. }

注意:第 50 行的 private List<OrderDetail> orderDetailList;  表示订单详情列表,因为一个订单通常可能会有多个东西(订单详情)。

如果我们不使用 DTO,直接在 Entity 里加这一句在 OrderMaster.java 中,导致数据库里没有这个字段会报错,需要加一个注解  @Transient ,让 orderDetailList 不被序列化

但是,为了管理方便,数据交互我们统一使用 DTO,而不用 Entity。只需要复制 Entity 里的内容到 DTO 中,然后添加我们需要的变量即可。

 

2、CartDTO.java

  1. package com.liuyanzhao.sell.dto;
  2. import lombok.Data;
  3. /**
  4.  * 购物车
  5.  * @Author: 言曌
  6.  * @Date: 2017/11/14
  7.  * @Time: 下午4:00
  8.  */
  9. @Data
  10. public class CartDTO {
  11.     //商品id
  12.     private String productId;
  13.     //数量
  14.     private Integer productQuantity;
  15.     public CartDTO(String productId, Integer productQuantity) {
  16.         this.productId = productId;
  17.         this.productQuantity = productQuantity;
  18.     }
  19. }

购物车里只需要存储两个主要信息,即商品变量和购买数量。

 

三、Service 创建

1、OrderService.java

  1. package com.liuyanzhao.sell.service;
  2. import com.liuyanzhao.sell.dto.OrderDTO;
  3. import org.springframework.data.domain.Page;
  4. import org.springframework.data.domain.Pageable;
  5. /**
  6.  * @Author: 言曌
  7.  * @Date: 2017/11/13
  8.  * @Time: 上午11:17
  9.  */
  10. public interface OrderService {
  11.     //创建订单
  12.     OrderDTO create(OrderDTO orderDTO);
  13.     //查询单个订单
  14.     OrderDTO findOne();
  15.     //查询订单列表
  16.     Page<OrderDTO> findList(String buyerOpenid, Pageable pageable);
  17.     //取消订单
  18.     OrderDTO cancel(OrderDTO orderDTO);
  19.     //完结订单
  20.     OrderDTO finish(OrderDTO orderDTO);
  21.     //支付订单
  22.     OrderDTO paid(OrderDTO orderDTO);
  23. }

2、OrderServiceImpl.java

  1. package com.liuyanzhao.sell.service.impl;
  2. import com.liuyanzhao.sell.dao.OrderDetailDao;
  3. import com.liuyanzhao.sell.dao.OrderMasterDao;
  4. import com.liuyanzhao.sell.dto.CartDTO;
  5. import com.liuyanzhao.sell.dto.OrderDTO;
  6. import com.liuyanzhao.sell.entity.OrderDetail;
  7. import com.liuyanzhao.sell.entity.OrderMaster;
  8. import com.liuyanzhao.sell.entity.ProductInfo;
  9. import com.liuyanzhao.sell.enums.OrderStatusEnum;
  10. import com.liuyanzhao.sell.enums.PayStatusEnum;
  11. import com.liuyanzhao.sell.enums.ResultEnum;
  12. import com.liuyanzhao.sell.exception.SellException;
  13. import com.liuyanzhao.sell.service.OrderService;
  14. import com.liuyanzhao.sell.service.ProductService;
  15. import com.liuyanzhao.sell.utils.KeyUtil;
  16. import org.springframework.beans.BeanUtils;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.data.domain.Page;
  19. import org.springframework.data.domain.Pageable;
  20. import org.springframework.stereotype.Service;
  21. import org.springframework.transaction.annotation.Transactional;
  22. import java.math.BigDecimal;
  23. import java.math.BigInteger;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. import java.util.stream.Collectors;
  27. /**
  28.  * @Author: 言曌
  29.  * @Date: 2017/11/13
  30.  * @Time: 上午11:29
  31.  */
  32. @Service
  33. public class OrderServiceImpl implements OrderService{
  34.     @Autowired
  35.     private ProductService productService;
  36.     @Autowired
  37.     private OrderDetailDao orderDetailDao;
  38.     @Autowired
  39.     private OrderMasterDao orderMasterDao;
  40.     @Override
  41.     @Transactional //事务管理
  42.     public OrderDTO create(OrderDTO orderDTO) {
  43.         String orderId = KeyUtil.genUniqueKey();
  44.         BigDecimal orderAmount = new BigDecimal(BigInteger.ZERO );
  45.         //1、查询商品(数量,价格)
  46.         List<OrderDetail> orderDetailList = new ArrayList<>();
  47.         for(OrderDetail orderDetail:orderDTO.getOrderDetailList()) {
  48.             ProductInfo productInfo = productService.findOne(orderDetail.getProductId());
  49.             if(productInfo == null) {
  50.                 throw new SellException(ResultEnum.PRODUCT_NOT_EXISTS);
  51.             }
  52.             //2、计算订单总价
  53.             orderAmount = productInfo.getProductPrice()
  54.                     .multiply(new BigDecimal(orderDetail.getProductQuantity()))
  55.                     .add(orderAmount);
  56.             //订单详情入库
  57.             orderDetail.setDetailId(KeyUtil.genUniqueKey());
  58.             orderDetail.setOrderId(orderId);
  59.             BeanUtils.copyProperties(productInfo,orderDetail);
  60.             orderDetailDao.save(orderDetail);
  61.         }
  62.         //3、写入订单数据库(orderMaster和orderDetail)
  63.         OrderMaster orderMaster = new OrderMaster();
  64.         BeanUtils.copyProperties(orderDTO,orderMaster);
  65.         orderMaster.setOrderId(orderId);
  66.         orderMaster.setOrderAmount(orderAmount);
  67.         orderMaster.setOrderStatus(OrderStatusEnum.NEW.getCode());
  68.         orderMaster.setPayStatus(PayStatusEnum.WAIT.getCode());
  69.         orderMasterDao.save(orderMaster);
  70.         //4、扣库存
  71.         List<CartDTO> cartDTOList = new ArrayList<>();
  72.         orderDTO.getOrderDetailList().stream().map(e->
  73.             new CartDTO(e.getProductId(),e.getProductQuantity())
  74.         ).collect(Collectors.toList());
  75.         productService.decreaseStock(cartDTOList);
  76.         return orderDTO;
  77.     }
  78.     @Override
  79.     public OrderDTO findOne() {
  80.         return null;
  81.     }
  82.     @Override
  83.     public Page<OrderDTO> findList(String buyerOpenid, Pageable pageable) {
  84.         return null;
  85.     }
  86.     @Override
  87.     public OrderDTO cancel(OrderDTO orderDTO) {
  88.         return null;
  89.     }
  90.     @Override
  91.     public OrderDTO finish(OrderDTO orderDTO) {
  92.         return null;
  93.     }
  94.     @Override
  95.     public OrderDTO paid(OrderDTO orderDTO) {
  96.         return null;
  97.     }
  98. }

 

注意:

① 第 50 行的 @Transactional  注解表示该方法需要事务管理,防止突然出现异常(机器坏了,停电了,断网啦啥的),这个 create() 方法执行一半,导致数据不全,从而出错。我们希望的是要么全部成功,要么全部失败。

② 第 51 行,我们使用 OrderDTO 类来储存订单信息,而不使用 OrderMaster 类。

③ 第 53 行的 KeyUtil.genUniqueKey() 还没有建立,用于生成 随机主键(当前时间毫秒数+六位随机整数)

商品的价格一定要从数据库里查询,不能通过前台传过来

⑤ 一定要判断库存是否充足,不足抛异常

⑥ 第 61 行的抛出异常,该异常下文会写,枚举也是

⑦ 89-91 行是 lambda 表达式

 

四、新建异常

  1. package com.liuyanzhao.sell.exception;
  2. import com.liuyanzhao.sell.enums.ResultEnum;
  3. /**
  4.  * @Author: 言曌
  5.  * @Date: 2017/11/13
  6.  * @Time: 下午12:04
  7.  */
  8. public class SellException extends RuntimeException {
  9.     private Integer code;
  10.     public SellException(ResultEnum resultEnum) {
  11.         super(resultEnum.getMessage());
  12.         this.code = resultEnum.getCode();
  13.     }
  14. }

枚举信息从下文中定义

 

五、新建异常信息的枚举

ResultEnum.java

  1. package com.liuyanzhao.sell.enums;
  2. import lombok.Getter;
  3. /**
  4.  * 返回给前端提示
  5.  * @Author: 言曌
  6.  * @Date: 2017/11/13
  7.  * @Time: 下午12:05
  8.  */
  9. @Getter
  10. public enum ResultEnum {
  11.     PRODUCT_NOT_EXISTS(10,"商品不存在"),
  12.     PRODUCT_STOCK_ERROR(11"库存不正确"),
  13.     ;
  14.     private Integer code;
  15.     private String message;
  16.     ResultEnum(Integer code, String message) {
  17.         this.code = code;
  18.         this.message = message;
  19.     }
  20. }

 

六、新建 生成唯一索引的方法

KeyUtil.java

  1. package com.liuyanzhao.sell.utils;
  2. import java.util.Random;
  3. /**
  4.  * 数据库索引,约束
  5.  * @Author: 言曌
  6.  * @Date: 2017/11/13
  7.  * @Time: 下午12:28
  8.  */
  9. public class KeyUtil {
  10.     /**
  11.      * 生成唯一主键
  12.      * 格式:时间+六位随机数
  13.      * @return
  14.      */
  15.     public static synchronized String genUniqueKey() {
  16.         Random random = new Random();
  17.         Integer number = random.nextInt(900000)+100000;//生成六位随机数
  18.         return System.currentTimeMillis() + String.valueOf(number);
  19.     }
  20. }

注意:防止多线程异步导致订单号重复。

其实如果用户数量非常多,比如上亿级的,1ms 内还是可能会出现相同的随机数的,也就是订单号相同。这个时候我们可以再在订单号上加一个用户id。

 

六、新建测试类

我们目前只写了 create() 方法,下一节会继续写。

先把创建订单的方法测试下再说

OrderServiceImplTest.java

  1. package com.liuyanzhao.sell.service.impl;
  2. import com.liuyanzhao.sell.dto.OrderDTO;
  3. import com.liuyanzhao.sell.entity.OrderDetail;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.boot.test.context.SpringBootTest;
  9. import org.springframework.test.context.junit4.SpringRunner;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. /**
  13.  * @Author: 言曌
  14.  * @Date: 2017/11/14
  15.  * @Time: 下午4:26
  16.  */
  17. @Slf4j
  18. @RunWith(SpringRunner.class)
  19. @SpringBootTest
  20. public class OrderServiceImplTest {
  21.     @Autowired
  22.     private OrderServiceImpl orderService;
  23.     private final String BUYER_OPENID = "111111";
  24.     @Test
  25.     public void create() throws Exception {
  26.         OrderDTO orderDTO = new OrderDTO();
  27.         orderDTO.setBuyerAddress("地球村");
  28.         orderDTO.setBuyerName("千小寻");
  29.         orderDTO.setBuyerPhone("12345678912");
  30.         orderDTO.setBuyerOpenid(BUYER_OPENID);
  31.         //购物车
  32.         List<OrderDetail> orderDetailList = new ArrayList<>();
  33.         OrderDetail orderDetail = new OrderDetail();
  34.         orderDetail.setProductId("123456");
  35.         orderDetail.setProductQuantity(1);
  36.         orderDetailList.add(orderDetail);
  37.         OrderDetail orderDetail2 = new OrderDetail();
  38.         orderDetail2.setProductId("123458");
  39.         orderDetail2.setProductQuantity(2);
  40.         orderDetailList.add(orderDetail2);
  41.         OrderDetail orderDetail3 = new OrderDetail();
  42.         orderDetail3.setProductId("123459");
  43.         orderDetail3.setProductQuantity(3);
  44.         orderDetailList.add(orderDetail3);
  45.         orderDTO.setOrderDetailList(orderDetailList);
  46.         OrderDTO result = orderService.create(orderDTO);
  47.         log.info("【创建订单】result={}",result);
  48.     }
  49.     @Test
  50.     public void findOne() throws Exception {
  51.     }
  52.     @Test
  53.     public void findList() throws Exception {
  54.     }
  55.     @Test
  56.     public void cancel() throws Exception {
  57.     }
  58.     @Test
  59.     public void finish() throws Exception {
  60.     }
  61.     @Test
  62.     public void paid() throws Exception {
  63.     }
  64. }

我们这个测试中,一个订单中有三种详情订单,第 21 行和第 61 行是日志相关内容

 

最终我们运行 Tomcat,查看数据库,可以查看到 OrderMaster 有一条记录,OrderDetail 一条记录。

即上面基本准备中的效果

 

本文地址:https://liuyanzhao.com/6720.html

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
言曌

发表评论

:?::razz::sad::evil::!::smile::oops::grin::eek::shock::???::cool::lol::mad::twisted::roll::wink::idea::arrow::neutral::cry::mrgreen:

目前评论:2   其中:访客  1   博主  1

    • avatar sss

      for (OrderDetail orderDetail : orderDTO.getOrderDetailList()) {
      ProductInfo productInfo = productService.findOne(orderDetail.getProductId());
      if (productInfo == null) {
      throw new SellException(ResultEnum.PRODUCT_NOT_EXI);
      }
      //2. 计算订单总价
      orderAmount = productInfo.getProductPrice()
      .multiply(new BigDecimal(orderDetail.getProductQuantity()))
      .add(orderAmount);

      orderDetail.setDetailId(KeyUtil.genUniqueKey());
      orderDetail.setOrderId(orderId);
      BeanUtils.copyProperties(productInfo, orderDetail);
      orderDetailRepository.save(orderDetail);
      }

      老哥抄来这段代码就没有疑问吗

        • avatar 言曌  博主

          @sss 好像没有耶