本文介绍买家下单的service层的创建,并实现创建订单的方法。
DTO
@Transactional 注解
@Transient 注解
扣库存
随机数主键
异常
1、数据库效果
order_master 表
order_detail 表
2、最终文件结构
DTO 全称数据传输对象,用于各个层的交互。而我们的 Entity 用于和数据库对接。
1、OrderDTO.java
注意:第 50 行的 private List<OrderDetail> orderDetailList; 表示订单详情列表,因为一个订单通常可能会有多个东西(订单详情)。
如果我们不使用 DTO,直接在 Entity 里加这一句在 OrderMaster.java 中,导致数据库里没有这个字段会报错,需要加一个注解 @Transient ,让 orderDetailList 不被序列化。
但是,为了管理方便,数据交互我们统一使用 DTO,而不用 Entity。只需要复制 Entity 里的内容到 DTO 中,然后添加我们需要的变量即可。
2、CartDTO.java
购物车里只需要存储两个主要信息,即商品变量和购买数量。
1、OrderService.java
2、OrderServiceImpl.java
注意:
① 第 50 行的 @Transactional 注解表示该方法需要事务管理,防止突然出现异常(机器坏了,停电了,断网啦啥的),这个 create() 方法执行一半,导致数据不全,从而出错。我们希望的是要么全部成功,要么全部失败。
② 第 51 行,我们使用 OrderDTO 类来储存订单信息,而不使用 OrderMaster 类。
③ 第 53 行的 KeyUtil.genUniqueKey() 还没有建立,用于生成 随机主键(当前时间毫秒数+六位随机整数)
④ 商品的价格一定要从数据库里查询,不能通过前台传过来
⑤ 一定要判断库存是否充足,不足抛异常
⑥ 第 61 行的抛出异常,该异常下文会写,枚举也是
⑦ 89-91 行是 lambda 表达式
枚举信息从下文中定义
ResultEnum.java
KeyUtil.java
注意:防止多线程异步导致订单号重复。
其实如果用户数量非常多,比如上亿级的,1ms 内还是可能会出现相同的随机数的,也就是订单号相同。这个时候我们可以再在订单号上加一个用户id。
我们目前只写了 create() 方法,下一节会继续写。
先把创建订单的方法测试下再说
OrderServiceImplTest.java
我们这个测试中,一个订单中有三种详情订单,第 21 行和第 61 行是日志相关内容
最终我们运行 Tomcat,查看数据库,可以查看到 OrderMaster 有一条记录,OrderDetail 一条记录。
即上面基本准备中的效果
本文地址:https://liuyanzhao.com/6720.html
本文关键词
DTO
@Transactional 注解
@Transient 注解
扣库存
随机数主键
异常
一、开发准备
1、数据库效果
order_master 表
order_detail 表
2、最终文件结构
二、创建 DTO
DTO 全称数据传输对象,用于各个层的交互。而我们的 Entity 用于和数据库对接。
1、OrderDTO.java
- package com.liuyanzhao.sell.dto;
- import com.liuyanzhao.sell.entity.OrderDetail;
- import lombok.Data;
- import java.math.BigDecimal;
- import java.util.Date;
- import java.util.List;
- /**
- * 订单
- * @Author: 言曌
- * @Date: 2017/11/13
- * @Time: 上午11:23
- */
- @Data
- public class OrderDTO {
- //订单编号
- private String orderId;
- //买家名字
- private String buyerName;
- //买家电话
- private String buyerPhone;
- //买家地址
- private String buyerAddress;
- //买家微信Openid
- private String buyerOpenid;
- //订单总金额
- private BigDecimal orderAmount;
- //订单状态,默认为0新订单
- private Integer orderStatus;
- //支付状态,默认为0未支付
- private Integer payStatus;
- //订单创建时间
- private Date createTime;
- //订单更新时间
- private Date updateTime;
- private List<OrderDetail> orderDetailList;
- }
注意:第 50 行的 private List<OrderDetail> orderDetailList; 表示订单详情列表,因为一个订单通常可能会有多个东西(订单详情)。
如果我们不使用 DTO,直接在 Entity 里加这一句在 OrderMaster.java 中,导致数据库里没有这个字段会报错,需要加一个注解 @Transient ,让 orderDetailList 不被序列化。
但是,为了管理方便,数据交互我们统一使用 DTO,而不用 Entity。只需要复制 Entity 里的内容到 DTO 中,然后添加我们需要的变量即可。
2、CartDTO.java
- package com.liuyanzhao.sell.dto;
- import lombok.Data;
- /**
- * 购物车
- * @Author: 言曌
- * @Date: 2017/11/14
- * @Time: 下午4:00
- */
- @Data
- public class CartDTO {
- //商品id
- private String productId;
- //数量
- private Integer productQuantity;
- public CartDTO(String productId, Integer productQuantity) {
- this.productId = productId;
- this.productQuantity = productQuantity;
- }
- }
购物车里只需要存储两个主要信息,即商品变量和购买数量。
三、Service 创建
1、OrderService.java
- package com.liuyanzhao.sell.service;
- import com.liuyanzhao.sell.dto.OrderDTO;
- import org.springframework.data.domain.Page;
- import org.springframework.data.domain.Pageable;
- /**
- * @Author: 言曌
- * @Date: 2017/11/13
- * @Time: 上午11:17
- */
- public interface OrderService {
- //创建订单
- OrderDTO create(OrderDTO orderDTO);
- //查询单个订单
- OrderDTO findOne();
- //查询订单列表
- Page<OrderDTO> findList(String buyerOpenid, Pageable pageable);
- //取消订单
- OrderDTO cancel(OrderDTO orderDTO);
- //完结订单
- OrderDTO finish(OrderDTO orderDTO);
- //支付订单
- OrderDTO paid(OrderDTO orderDTO);
- }
2、OrderServiceImpl.java
- package com.liuyanzhao.sell.service.impl;
- import com.liuyanzhao.sell.dao.OrderDetailDao;
- import com.liuyanzhao.sell.dao.OrderMasterDao;
- import com.liuyanzhao.sell.dto.CartDTO;
- import com.liuyanzhao.sell.dto.OrderDTO;
- import com.liuyanzhao.sell.entity.OrderDetail;
- import com.liuyanzhao.sell.entity.OrderMaster;
- import com.liuyanzhao.sell.entity.ProductInfo;
- import com.liuyanzhao.sell.enums.OrderStatusEnum;
- import com.liuyanzhao.sell.enums.PayStatusEnum;
- import com.liuyanzhao.sell.enums.ResultEnum;
- import com.liuyanzhao.sell.exception.SellException;
- import com.liuyanzhao.sell.service.OrderService;
- import com.liuyanzhao.sell.service.ProductService;
- import com.liuyanzhao.sell.utils.KeyUtil;
- import org.springframework.beans.BeanUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.domain.Page;
- import org.springframework.data.domain.Pageable;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import java.math.BigDecimal;
- import java.math.BigInteger;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.stream.Collectors;
- /**
- * @Author: 言曌
- * @Date: 2017/11/13
- * @Time: 上午11:29
- */
- @Service
- public class OrderServiceImpl implements OrderService{
- @Autowired
- private ProductService productService;
- @Autowired
- private OrderDetailDao orderDetailDao;
- @Autowired
- private OrderMasterDao orderMasterDao;
- @Override
- @Transactional //事务管理
- public OrderDTO create(OrderDTO orderDTO) {
- String orderId = KeyUtil.genUniqueKey();
- BigDecimal orderAmount = new BigDecimal(BigInteger.ZERO );
- //1、查询商品(数量,价格)
- List<OrderDetail> orderDetailList = new ArrayList<>();
- for(OrderDetail orderDetail:orderDTO.getOrderDetailList()) {
- ProductInfo productInfo = productService.findOne(orderDetail.getProductId());
- if(productInfo == null) {
- throw new SellException(ResultEnum.PRODUCT_NOT_EXISTS);
- }
- //2、计算订单总价
- orderAmount = productInfo.getProductPrice()
- .multiply(new BigDecimal(orderDetail.getProductQuantity()))
- .add(orderAmount);
- //订单详情入库
- orderDetail.setDetailId(KeyUtil.genUniqueKey());
- orderDetail.setOrderId(orderId);
- BeanUtils.copyProperties(productInfo,orderDetail);
- orderDetailDao.save(orderDetail);
- }
- //3、写入订单数据库(orderMaster和orderDetail)
- OrderMaster orderMaster = new OrderMaster();
- BeanUtils.copyProperties(orderDTO,orderMaster);
- orderMaster.setOrderId(orderId);
- orderMaster.setOrderAmount(orderAmount);
- orderMaster.setOrderStatus(OrderStatusEnum.NEW.getCode());
- orderMaster.setPayStatus(PayStatusEnum.WAIT.getCode());
- orderMasterDao.save(orderMaster);
- //4、扣库存
- List<CartDTO> cartDTOList = new ArrayList<>();
- orderDTO.getOrderDetailList().stream().map(e->
- new CartDTO(e.getProductId(),e.getProductQuantity())
- ).collect(Collectors.toList());
- productService.decreaseStock(cartDTOList);
- return orderDTO;
- }
- @Override
- public OrderDTO findOne() {
- return null;
- }
- @Override
- public Page<OrderDTO> findList(String buyerOpenid, Pageable pageable) {
- return null;
- }
- @Override
- public OrderDTO cancel(OrderDTO orderDTO) {
- return null;
- }
- @Override
- public OrderDTO finish(OrderDTO orderDTO) {
- return null;
- }
- @Override
- public OrderDTO paid(OrderDTO orderDTO) {
- return null;
- }
- }
注意:
① 第 50 行的 @Transactional 注解表示该方法需要事务管理,防止突然出现异常(机器坏了,停电了,断网啦啥的),这个 create() 方法执行一半,导致数据不全,从而出错。我们希望的是要么全部成功,要么全部失败。
② 第 51 行,我们使用 OrderDTO 类来储存订单信息,而不使用 OrderMaster 类。
③ 第 53 行的 KeyUtil.genUniqueKey() 还没有建立,用于生成 随机主键(当前时间毫秒数+六位随机整数)
④ 商品的价格一定要从数据库里查询,不能通过前台传过来
⑤ 一定要判断库存是否充足,不足抛异常
⑥ 第 61 行的抛出异常,该异常下文会写,枚举也是
⑦ 89-91 行是 lambda 表达式
四、新建异常
- package com.liuyanzhao.sell.exception;
- import com.liuyanzhao.sell.enums.ResultEnum;
- /**
- * @Author: 言曌
- * @Date: 2017/11/13
- * @Time: 下午12:04
- */
- public class SellException extends RuntimeException {
- private Integer code;
- public SellException(ResultEnum resultEnum) {
- super(resultEnum.getMessage());
- this.code = resultEnum.getCode();
- }
- }
枚举信息从下文中定义
五、新建异常信息的枚举
ResultEnum.java
- package com.liuyanzhao.sell.enums;
- import lombok.Getter;
- /**
- * 返回给前端提示
- * @Author: 言曌
- * @Date: 2017/11/13
- * @Time: 下午12:05
- */
- @Getter
- public enum ResultEnum {
- PRODUCT_NOT_EXISTS(10,"商品不存在"),
- PRODUCT_STOCK_ERROR(11, "库存不正确"),
- ;
- private Integer code;
- private String message;
- ResultEnum(Integer code, String message) {
- this.code = code;
- this.message = message;
- }
- }
六、新建 生成唯一索引的方法
KeyUtil.java
- package com.liuyanzhao.sell.utils;
- import java.util.Random;
- /**
- * 数据库索引,约束
- * @Author: 言曌
- * @Date: 2017/11/13
- * @Time: 下午12:28
- */
- public class KeyUtil {
- /**
- * 生成唯一主键
- * 格式:时间+六位随机数
- * @return
- */
- public static synchronized String genUniqueKey() {
- Random random = new Random();
- Integer number = random.nextInt(900000)+100000;//生成六位随机数
- return System.currentTimeMillis() + String.valueOf(number);
- }
- }
注意:防止多线程异步导致订单号重复。
其实如果用户数量非常多,比如上亿级的,1ms 内还是可能会出现相同的随机数的,也就是订单号相同。这个时候我们可以再在订单号上加一个用户id。
六、新建测试类
我们目前只写了 create() 方法,下一节会继续写。
先把创建订单的方法测试下再说
OrderServiceImplTest.java
- package com.liuyanzhao.sell.service.impl;
- import com.liuyanzhao.sell.dto.OrderDTO;
- import com.liuyanzhao.sell.entity.OrderDetail;
- import lombok.extern.slf4j.Slf4j;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.test.context.junit4.SpringRunner;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * @Author: 言曌
- * @Date: 2017/11/14
- * @Time: 下午4:26
- */
- @Slf4j
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class OrderServiceImplTest {
- @Autowired
- private OrderServiceImpl orderService;
- private final String BUYER_OPENID = "111111";
- @Test
- public void create() throws Exception {
- OrderDTO orderDTO = new OrderDTO();
- orderDTO.setBuyerAddress("地球村");
- orderDTO.setBuyerName("千小寻");
- orderDTO.setBuyerPhone("12345678912");
- orderDTO.setBuyerOpenid(BUYER_OPENID);
- //购物车
- List<OrderDetail> orderDetailList = new ArrayList<>();
- OrderDetail orderDetail = new OrderDetail();
- orderDetail.setProductId("123456");
- orderDetail.setProductQuantity(1);
- orderDetailList.add(orderDetail);
- OrderDetail orderDetail2 = new OrderDetail();
- orderDetail2.setProductId("123458");
- orderDetail2.setProductQuantity(2);
- orderDetailList.add(orderDetail2);
- OrderDetail orderDetail3 = new OrderDetail();
- orderDetail3.setProductId("123459");
- orderDetail3.setProductQuantity(3);
- orderDetailList.add(orderDetail3);
- orderDTO.setOrderDetailList(orderDetailList);
- OrderDTO result = orderService.create(orderDTO);
- log.info("【创建订单】result={}",result);
- }
- @Test
- public void findOne() throws Exception {
- }
- @Test
- public void findList() throws Exception {
- }
- @Test
- public void cancel() throws Exception {
- }
- @Test
- public void finish() throws Exception {
- }
- @Test
- public void paid() throws Exception {
- }
- }
我们这个测试中,一个订单中有三种详情订单,第 21 行和第 61 行是日志相关内容
最终我们运行 Tomcat,查看数据库,可以查看到 OrderMaster 有一条记录,OrderDetail 一条记录。
即上面基本准备中的效果
本文地址:https://liuyanzhao.com/6720.html
2017年11月20日 19:44:59
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); } 老哥抄来这段代码就没有疑问吗
2017年11月20日 19:57:29
好像没有耶