1订单设计-确认订单.md 8.2 KB

下单简单地分成几个步骤:

  1. 用户点击“立即购买”或“购物车-结算”进入到“确认订单”页面
  2. 在“确认订单”页面选择收货地址,优惠券等,重新计算运费、订单价格
  3. 提交订单,选择支付方式进行支付
  4. 支付完毕

第一步:

  1. 用户点击“立即购买”或“购物车-结算”进入到“确认订单”页面,相关url/p/order/confirm

我们希望能够有个统一下单的接口,不太希望“立即购买”和“购物车-结算”两个不同的接口影响到后面所有的流程,毕竟谁也不想一个差不多一样的接口,要写两遍,所以我们看下我们的系统是如何做的。

public class OrderParam {
	@ApiModelProperty(value = "立即购买时提交的商品项")
	private OrderItemParam orderItem;
}

这里主要判断orderItem的情况:

  • 假设orderItem 不为空,则说明是从立即购买进入

通过shopCartItemAdapter.getShopCartItemsByOrderItems(orderParam.getOrderItem(), userId) 这个方法对orderItem的情况进行判断,组装获取用户提交的购物车商品项,此时并不能将购物车商品删除,因为删除购物车中的商品,是在第三步提交订单的时候进行的,不然用户点击返回键,看到购物车里面的东西还没提交订单,东西就消失了,会感觉很奇怪。

我们重新回到controller层,经过一系列对活动商品的判断以及配送方式的筛选之后,我们看到了一行代码shopCartAdapter.getShopCarts(shopCartItems)

    @PostMapping("/confirm")
    @ApiOperation(value = "结算,生成订单信息", notes = "传入下单所需要的参数进行下单")
    public ResponseEntity<ShopCartOrderMergerDto> confirm(@Valid @RequestBody OrderParam orderParam) {
    // 根据店铺组装购车中的商品信息,返回每个店铺中的购物车商品信息
    List<ShopCartDto> shopCarts = shopCartAdapter.getShopCarts(shopCartItems);
    }

这行代码我们再《购物车的设计》这篇已经着重讲过了,但是我们在这为什么还需要这个东西呢?

很简单,无论是点击“立即购买”或“购物车-结算”,事实上都是通过用户计算过一遍金额了,而且甚至有满减满折,套餐之类的活动,都是通过了统一的计算的。而这一套计算的流程,我们并不希望重新写一遍。所以当然是能够使用之前计算的金额,那是最好的咯。

第二步:

  1. 在“确认订单”页面选择收货地址,优惠券等,重新计算运费、订单价格

我们知道无论是在第一步还是第二步,本质上还是在确认订单的页面,其中订单页面的数据结构并没有发生任何的变化,所以其实第一步第二步是可以写在一起的。所以我们可以看到OrderParam 还多了几个参数

public class OrderParam {
	@ApiModelProperty(value = "地址ID,0为默认地址",required=true)
	@NotNull(message = "地址不能为空")
	private Long addrId;
	
	@ApiModelProperty(value = "用户是否改变了优惠券的选择,如果用户改变了优惠券的选择,则完全根据传入参数进行优惠券的选择")
	private Integer userChangeCoupon;

	@ApiModelProperty(value = "优惠券id数组")
	private List<Long> couponIds;
}

但是有个问题,就是在于用户点击立即购买的时候,没有地址,那样如何计算运费呢?答案就是使用默认地址进行计算呀~

我们看下计算订单的事件,事实上有很多营销活动的时候,订单的计算也是非常的复杂的,所以我们和购物车一样,采用对每个活动进行单独的方法请求,一个接一个的对订单进行“装饰”,最后生成ShopCartOrderMergerDto一个合并的对象

    @PostMapping("/confirm")
    @Operation(summary = "结算,生成普通订单信息" , description = "传入下单所需要的参数进行下单")
    public ServerResponseEntity<ShopCartOrderMergerDto> confirm(@Valid @RequestBody OrderParam orderParam) throws ExecutionException, InterruptedException {
        ...
        // 计算满减,并重新组合购物车
        if (discountShopCartManager != null) {
            shopCarts = discountShopCartManager.calculateDiscountAndMakeUpShopCart(shopCarts);
        }
        // 计算套餐,并重新组合购物车
        if (comboShopCartManager != null) {
            shopCarts = comboShopCartManager.calculateDiscountAndMakeUpShopCart(shopCarts);
        }
        // 异步计算运费,运费暂时和优惠券没啥关联,可以与优惠券异步计算,获取用户地址,自提信息
        CompletableFuture<UserDeliveryInfoVO> deliveryFuture = null;
        if (!Objects.equals(mold, 1)) {
            deliveryFuture = CompletableFuture.supplyAsync(
                    () -> deliveryOrderManager.calculateAndGetDeliverInfo(shopCartItems, userDeliveryInfoVO),
                    orderThreadPoolExecutor);
        }

        // 运费用异步计算,最后要等运费出结果
        UserDeliveryInfoVO userDeliveryInfo = new UserDeliveryInfoVO();
        if (Objects.nonNull(deliveryFuture)) {
            userDeliveryInfo = deliveryFuture.get();
        }
        // 计算优惠券,并返回优惠券信息
        ChooseCouponParam chooseCouponParam = new ChooseCouponParam(orderParam.getUserChangeCoupon(), orderParam.getCouponUserIds(), shopCarts);
        if (couponConfirmOrderManager != null) {
            shopCarts = couponConfirmOrderManager.chooseShopCoupon(chooseCouponParam);
        }

        // 店铺会员等级优惠计算
        if (userLevelOrderManager != null) {
            userLevelOrderManager.calShopLevelDiscount(shopCartOrderMerger);
        }

        // ===============================================开始平台优惠的计算==================================================
        // 计算平台优惠券,并返回平台优惠券信息
        if (couponConfirmOrderManager != null) {
            shopCartOrderMerger = couponConfirmOrderManager.choosePlatformCoupon(new PlatformChooseCouponParam(orderParam.getUserChangeCoupon(),
                    orderParam.getCouponUserIds(), shopCartOrderMerger, chooseCouponParam.getPlatformCoupons()));
        }
        // 等级折扣
        if (userLevelOrderManager != null) {
            userLevelOrderManager.calculateLevelDiscount(shopCartOrderMerger, extension);
        }
        // ===============================================结束平台优惠的计算==================================================
        // 计算订单积分抵扣金额
        if (orderUseScoreManager != null) {
            orderUseScoreManager.orderUseScore(shopCartOrderMerger, orderParam, shopCartItems, extension);
        }
        shopCartOrderMerger.setOrderShopReduce(orderShopReduce);
        // 计算平台佣金
        confirmOrderManager.calculatePlatformCommission(shopCartOrderMerger);
        return ServerResponseEntity.success(shopCartOrderMerger);
    }

我们看看返回给前端的订单信息:

@Data
public class ShopCartOrderMergerDto implements Serializable{

    @ApiModelProperty(value = "实际总值", required = true)
    private Double actualTotal;

    @ApiModelProperty(value = "商品总值", required = true)
    private Double total;

    @ApiModelProperty(value = "商品总数", required = true)
    private Integer totalCount;

    @ApiModelProperty(value = "订单优惠金额(所有店铺优惠金额相加)", required = true)
    private Double orderReduce;

    @ApiModelProperty(value = "地址Dto", required = true)
    private UserAddrDto userAddr;

    @ApiModelProperty(value = "每个店铺的购物车信息", required = true)
    private List<ShopCartOrderDto> shopCartOrders;

    @ApiModelProperty(value = "整个订单可以使用的优惠券列表", required = true)
    private List<CouponOrderDto> coupons;
}

这里又有一段我们熟悉的代码:

@ApiModelProperty(value = "每个店铺的购物车信息", required = true)
private List<ShopCartOrderDto> shopCartOrders;

没错这里返回的数据格式,和购物车的格式是一样的,因为第一步当中已经说明,订单来自于购物车的计算,所以会在基础上添加新的数据,基本上就是返回给前端的数据了。