用户下订单业务逻辑

涉及到的基本对象有哪些?

答:用户类,订单主表类、订单详情类、枚举类、异常类、数据传输DTO类、前端视图VO类、对象转换类工具类等。

为什么需要这些类?

用户类:作为用户订单中的主角,不可省略。

订单主表类:主要存放一些关于订单的内置属性。例如:订单状态、支付状态、用户的各种信息、订单总价等。

订单详情表:主要存放在一个订单中用所购买的商品信息。例如:商品名称、商品单价、商品数量等。

关系:订单主表与订单详情表为一对多的关系,因为在一个订单中有多个商品。

枚举类:对于类似订单状态、支付状态等都存在多种状态,同时也要给前端返回与之对应的状态码,因此需要进行属性的分装,便于直接获取,同时后端抛出去的异常也是利用枚举的支付串形式。(只需要获取就行,不能被设置)。下面是模板:

@Getter
public enum OrderAmountEnum {
    NEW(0, "新订单"),
    FINISHED(1, "完结"),
    CANCEL(2, "已取消"),
    ;
    /**
     * 状态值
     */
    private Integer code;
    /**
     * 状态描述
     */
    private String message;

    OrderAmountEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

异常处理类:当程序遇到某个未知的错误,我们为了安全性着想,抛出运行时异常,终止这项请求,同时也可以利用异常提示,更换为自定义的异常,将自己想打印的异常打出。

@Data
public class SellException extends RuntimeException {

    private Integer code;

//用过调用枚举的异常描述
    public SellException(ResultEnum resultEnum){
        super(resultEnum.getMessage());
        this.code = resultEnum.getCode();
    }

    public SellException(Integer code, String messsage){
        super(messsage);
        this.code = code;
    }

}

DTO类:如果遇到实体一对多情况的时候,也就得在与数据库对应的实体中出现了数据库字段不存在属性(例如:List)那么如果我们给这个实体添加了一个该属性,那么必然导致后期出先字段为空或影响其他业务。同时数据不需要返回给前端只是在dao、service、controller之间做传输。采取这样的方式是将后端实体、前端请求实体、传输实体互相分开,便于分析业务逻辑。

From类:如果前端传入过来的是JSON字符对象,其中包含的属性比较多的时候,就需要表单类。经常用于实名认证、用户下订单等业务中。一般命名为xxfrom

Converter类:这个类就是用户对象的转换,以后拼接对象的时候尽量也选择拆分出去的方法。写在业务逻辑里面会使得业务庞大。

工具类:提供了业务接口所需的方法。时间类、UUID、JSON等。

常用到的接口有哪些?

答:常用到主体业务接口有根据用户id查询订单列表、创建订单、根据订单ID查询订单详情、取消订单、完结订单、支付订单。

业务接口的划分:使用SpringBoot的话基本不需要在Dao做过多的接口,不过需要编写测试方法。一般有条件的查询做处理的时候需要按照命名规则去编写业务接口。

创建一个订单的基本思路是什么?

当前端传入一个JSON对象给后台,后台可以直接接收字符串,也可以通过@Valid注解去检验传过来的JSON对象中的name与实体对象是否一致。同时需要利用bindingResult去判断JSON的参数是否正确。然后经过各种对象的转换,就可以直接调用创建订单的接口了,最后直接返回需要的数据给前端。那创建订单的基本流程是什么呢?首先需要我们通过前端用户订单里面的每一个商品去后台获取商品数据,这样做的原因就是因为前端传过来的数据可能存在问题,阻止篡改数据,然后去购买的可能。(这也是代码安全性的考验)。然后我们就可以去计算总价钱了(这个地方可能有优惠券、积分等成分)最后利用对象的拷贝方法(这个是根据名称来拷贝的)将后台查到的数据从新赋值给前端传过来的数据(这里也是为了保证数据的正确性),最终就可以创建一个订单详情了。(这里是因为购买的商品可能是一个List,因此一边遍历一边保存订单详情)。那订单主表我岂不也该创建?对的,下一步就是用来创建订单主表,这里与上面类似的方法。到目前为止订单都已经创建好了,但是为什么在没有看见扣库存的操作呀?其实在这有两种方式。一种是在创建详情的时候可以减掉库存,但是这样对于各种异常处理就都会分装在这个业务里面,不利于维护。第二种是利用购物车的方式,也就是创建一个订单详情购物车,将用户所购买的商品集合实例化到购物车对象中,然后将购物车传入扣库存的方法就好了,至于各种异常,他都将会在该方法中实现。那扣库存又是如何实现的?其实就是遍历购物车,首先查找数据库中该商品的信息,如果没有就不给予操作,如果有那么我们直接利用数据库库存-购买的库存,最后直接保存进去就行。

实例化代码:

//创建订单
@PostMapping("/create")
public ResultVo<Map<String, String>> create(@Valid OrderFrom orderFrom,
                                            BindingResult bindingResult) {
    //先校验,看传入参数是否正确
    if (bindingResult.hasErrors()) {
        log.error("【创建订单】 参数不正确,orderFrom={}", orderFrom);
        throw new SellException(ResultEnum.PARAM_ERROR.getCode(), bindingResult.getFieldError().getDefaultMessage());
    }
    OrderDto orderDto = OrderFrom2OrderDTOConverter.convert(orderFrom);
    //集合判断为空的方法
    if (CollectionUtils.isEmpty(orderDto.getOrderDetailList())) {
        log.error("【创建订单】 订单中不能没有商品,orderFrom={}", orderFrom);
        throw new SellException(ResultEnum.CART_ERROR);
    }
    //创建订单
    OrderDto createResult = orderService.create(orderDto);

    Map<String, String> map = new HashMap<>();
    map.put("orderId", createResult.getOrderId());

    return ResultUtil.Success(map);
}
Servrce:
@Override
    @Transactional
    public OrderDto create(OrderDto orderDto) {

        //默认价格为0.00
        BigDecimal bigDecimal = new BigDecimal(BigInteger.ZERO);

        //用户收购买的商品列表(这里为了实现一次性扣库存)
        //List<CartDto> cartDtoList = new ArrayList<>();

        //在创建订单的时候生成订单主表id
        String orderMasterId = KeyUtil.genUniquekey();

        //1.查询商品(数量,价格)
        for (OrderDetail orderDetail : orderDto.getOrderDetailList()) {
            ProductInfo productInfo = productService.findOne(orderDetail.getProductId());
            //判断商品是否存在,如果不存在就抛出一个异常
            if (productInfo == null) {
                throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
            }
            //3.计算订单总价(商品单价*(multiply)商品数量+(add)基础价格)
            bigDecimal = productInfo.getProductPrice().multiply(new BigDecimal(orderDetail.getProductQuantity())).add(bigDecimal);
            //4.订单详情入库(用的是数据库的数据,避免前端更改)
            BeanUtils.copyProperties(productInfo, orderDetail);
            orderDetail.setOrderId(orderMasterId);
            orderDetail.setDetailId(KeyUtil.genUniquekey());
            //保存订单详情表入库
            orderDetailRepository.save(orderDetail);

            //减库存(根据当前程序去扣库存,可以使用一次性扣多个商品库存的方法)
//            CartDto cartDto = new CartDto(orderDetail.getProductId(),orderDetail.getProductQuantity());
//            cartDtoList.add(cartDto);
        }
        //5.写入数据库 先拷贝在设置,不然orderDto里面的空数据会覆盖之前设置进去的值
        OrderMaster orderMaster = new OrderMaster();
        orderDto.setOrderId(orderMasterId);
        BeanUtils.copyProperties(orderDto, orderMaster);
        orderMaster.setOrderAmount(bigDecimal);
        orderMaster.setOrderStatus(OrderAmountEnum.NEW.getCode());
        orderMaster.setPayStatus(PayStatusEnum.WAIT.getCode());
        //保存订单主表入库
        orderMasterRepository.save(orderMaster);
        //4.减库存(一下减掉多个商品的库存)
        List<CartDto> cartDtoList = orderDto.getOrderDetailList().stream().map(e -> new CartDto(e.getProductId(), e.getProductQuantity())).collect(Collectors.toList());
        productService.decreaseStock(cartDtoList);

        //5.消息推送
        webSocket.sendMessage("有新的订单消息,注意查收!");
        return orderDto;
    }

实用的工具类

生成主键id:该方法经常用户生成主键uuid,为新添加的数据生成唯一主键

public class KeyUtil {

    /**
     * 生成唯一主键  (synchronized,防止多线程的时候时间一致)
     * 时间(毫秒)+随机数(6位)
     */
    public static synchronized String genUniquekey(){

        Random random = new Random();
        Integer number =  random.nextInt(900000)+100000;
        return System.currentTimeMillis()+String.valueOf(number);
    }
}

JSON解析器:JSON解析器中用于解析前端传过来的json数据对象,然后将此数据存放到后端需要改数据的实体。经常用于解析list数据。

@Slf4j
public class OrderFrom2OrderDTOConverter {

    public static OrderDto convert(OrderFrom orderFrom) {
        //json转换器
        Gson gson = new Gson();

        OrderDto orderDto = new OrderDto();

        orderDto.setBuyerName(orderFrom.getName());
        orderDto.setBuyerPhone(orderFrom.getPhone());
        orderDto.setBuyerOpenid(orderFrom.getOpenid());
        orderDto.setBuyerAddress(orderFrom.getAddress());

        List<OrderDetail> orderDetailList = new ArrayList<>();
        //json转换
        try {
//            获取json里面list数据的谷歌json获取方式
            orderDetailList = gson.fromJson(orderFrom.getItems(), new TypeToken<List<OrderDetail>>() {}.getType());
        } catch (Exception e) {
            log.error("【对象转换】错误,strig={}", orderFrom.getItems());
            throw new SellException(ResultEnum.PARAM_ERROR);
        }
        orderDto.setOrderDetailList(orderDetailList);
        return orderDto;
    }

}

JSON格式化工具:该工具经常用于日志的输出,显示效果。如果不使用,那么出来的JSON是经过压缩的,看起来非常的不方便。

public class JsonUtil {

    public static String toJson(Object object){
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setPrettyPrinting();
        Gson gson = gsonBuilder.create();
        return gson.toJson(object);
    }

}

 

 

 

  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值