## 订单接口-1

用户下单时调用的订单接口有两个：

1、确认订单[生成订单信息]

2、提交订单[此时默认支付成功]

### 1.确认订单

确认订单有以下几个步骤：

1、判断是购物车还是直接购买

2、组装购物项信息

3、根据店铺计算金额

4、计算总额

---

用户通过点击"**立即购买**"或进入购物车选择"**结算**"进入到"**确认订单**"页面，具体接口："`/a/order/confirm`"

由于有两种方式进入到确认订单界面，但本商城设计为一个接口处理，统一下单：

```java
public class OrderDTO {
    
    @Schema(description = "立即购买时提交的商品项,如果该值为空，则说明是从购物车进入，如果该值不为空则说明为立即购买" )
    private ShopCartItemDTO shopCartItem;

    @NotNull(message = "配送类型不能为空")
    @Schema(description = "配送类型3：无需快递" )
    private Integer dvyType;
}
```

通过购物车适配器来**获取购物项组装信息**

```java
public ServerResponseEntity<List<ShopCartItemVO>> getShopCartItems(ShopCartItemDTO shopCartItemParam) {
    ServerResponseEntity<List<ShopCartItemVO>> shopCartItemResponse;
    // 当立即购买时，没有提交的订单是没有购物车信息的
    if (shopCartItemParam != null) {
        shopCartItemResponse = conversionShopCartItem(shopCartItemParam);
    }
    // 从购物车提交订单
    else {
        shopCartItemResponse = shopCartFeignClient.getCheckedShopCartItems();
    }
    if (!shopCartItemResponse.isSuccess()) {
        return ServerResponseEntity.transform(shopCartItemResponse);
    }
    // 请选择您需要的商品加入购物车
    if (CollectionUtil.isEmpty(shopCartItemResponse.getData())) {
        return ServerResponseEntity.fail(ResponseEnum.SHOP_CART_NOT_EXIST);
    }
    // 返回购物车选择的商品信息
    return shopCartItemResponse;
}
```

当购物项`shopCartItem`为空时，则说明是从购物车进入，此时调用获取**用户的购物车信息**的方法`shopCartFeignClient.getCheckedShopCartItems()`：

```java
public ServerResponseEntity<List<ShopCartItemVO>> getCheckedShopCartItems() {
  //该方法从数据库查询购物车的商品
  List<ShopCartItemVO> checkedShopCartItems = shopCartService.getCheckedShopCartItems();
  if (CollectionUtil.isNotEmpty(checkedShopCartItems)) {
      for (ShopCartItemVO shopCartItem : checkedShopCartItems) {
          shopCartItem.setTotalAmount(shopCartItem.getCount() * shopCartItem.getSkuPriceFee());
      }
   }
   return ServerResponseEntity.success(checkedShopCartItems);
}
```

而若`shopCartItem`不为空，则是直接购买进入该页面，调用`shopCartAdapter.conversionShopCartItem()`方法，组装购物信息：

```java
public ServerResponseEntity<List<ShopCartItemVO>> conversionShopCartItem(ShopCartItemDTO shopCartItemParam){
        ServerResponseEntity<SpuAndSkuVO> spuAndSkuResponse = spuFeignClient.getSpuAndSkuById(shopCartItemParam.getSpuId(),shopCartItemParam.getSkuId());
        if (!spuAndSkuResponse.isSuccess()) {
            return ServerResponseEntity.transform(spuAndSkuResponse);
        }
        SkuVO sku = spuAndSkuResponse.getData().getSku();
        SpuVO spu = spuAndSkuResponse.getData().getSpu();
        // 拿到购物车的所有item
        ShopCartItemVO shopCartItem = new ShopCartItemVO();
        shopCartItem.setCartItemId(0L);
        shopCartItem.setSkuId(shopCartItemParam.getSkuId());
        shopCartItem.setCount(shopCartItemParam.getCount());
        shopCartItem.setSpuId(shopCartItemParam.getSpuId());
        shopCartItem.setSkuName(sku.getSkuName());
        shopCartItem.setSpuName(spu.getName());
        shopCartItem.setImgUrl(BooleanUtil.isTrue(spu.getHasSkuImg()) ? sku.getImgUrl() : spu.getMainImgUrl());
        shopCartItem.setSkuPriceFee(sku.getPriceFee());
        shopCartItem.setTotalAmount(shopCartItem.getCount() * shopCartItem.getSkuPriceFee());
        shopCartItem.setCreateTime(new Date());
        shopCartItem.setShopId(shopCartItemParam.getShopId());
        return ServerResponseEntity.success(Collections.singletonList(shopCartItem));
   }
```

两个路径的购物信息统一组装完毕后，不能在这一步删除购物车信息，万一用户点击返回后，发现购物车的商品虽然还没提交，但是已经消失，会造成信息差。

下一步，根据不同店铺来划分购物项，调用`shopCartAdapter.conversionShopCart()`：

```java
public List<ShopCartVO> conversionShopCart(List<ShopCartItemVO> shopCartItems){

        // 根据店铺ID划分item
        Map<Long, List<ShopCartItemVO>> shopCartMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemVO::getShopId));

        // 返回一个店铺的所有信息
        List<ShopCartVO> shopCarts = Lists.newArrayList();
        for (Long shopId : shopCartMap.keySet()) {
            // 构建每个店铺的购物车信息
            ShopCartVO shopCart = buildShopCart(shopId,shopCartMap.get(shopId));
            shopCart.setShopId(shopId);
            shopCart.setShopCartItemVOS(shopCartMap.get(shopId));
            // 店铺信息
            ServerResponseEntity<String> shopNameResponse = shopDetailFeignClient.getShopNameByShopId(shopId);
            if (!shopNameResponse.isSuccess()) {
                throw new mall4cloudException(shopNameResponse.getMsg());
            }
            shopCart.setShopName(shopNameResponse.getData());
            shopCarts.add(shopCart);
        }
        return shopCarts;
    }
```

在`buildShopCart(shopId,shopCartMap.get(shopId))`的时候，根据每个店铺来划分商品，再计算每个店铺商品的全部金额：

```java
private ShopCartVO buildShopCart(Long shopId, List<ShopCartItemVO> shopCartItems) {
        ShopCartVO shopCart = new ShopCartVO();
        shopCart.setShopId(shopId);
        long total = 0L;
        int totalCount = 0;
        for (ShopCartItemVO shopCartItem : shopCartItems) {
            total += shopCartItem.getTotalAmount();
            totalCount += shopCartItem.getCount();
        }
        shopCart.setTotal(total);
        shopCart.setTotalCount(totalCount);
        return shopCart;
}
```

此时根据店铺计算完金额后，在确认订单的时候，会重新计算一次总金额，返回给前端

```java
private void recalculateAmountWhenFinishingCalculateShop(ShopCartOrderMergerVO shopCartOrderMerger, List<ShopCartVO> shopCarts) {
        // 所有店铺的订单信息
        List<ShopCartOrderVO> shopCartOrders = new ArrayList<>();
        long total = 0;
        int totalCount = 0;
        // 所有店铺所有的商品item
        for (ShopCartVO shopCart : shopCarts) {
            // 每个店铺的订单信息
            ShopCartOrderVO shopCartOrder = new ShopCartOrderVO();
            shopCartOrder.setShopId(shopCart.getShopId());
            shopCartOrder.setShopName(shopCart.getShopName());
            total += shopCart.getTotal();
            totalCount += shopCart.getTotalCount();
            shopCartOrder.setTotal(shopCart.getTotal());
            shopCartOrder.setTotalCount(shopCart.getTotalCount());
            shopCartOrder.setShopCartItemVO(shopCart.getShopCartItemVOS());
            shopCartOrders.add(shopCartOrder);
        }
        shopCartOrderMerger.setTotal(total);
        shopCartOrderMerger.setTotalCount(totalCount);
        shopCartOrderMerger.setShopCartOrders(shopCartOrders);
    }
```

最后，使用工具类对重复提交的订单做判断，并且将结果存入缓存中，提交订单时可取出使用：

```java
// 防止重复提交
RedisUtil.STRING_REDIS_TEMPLATE.opsForValue().set(OrderCacheNames.ORDER_CONFIRM_UUID_KEY + CacheNames.UNION + userId, String.valueOf(userId));
// 保存订单计算结果缓存，省得重新计算并且用户确认的订单金额与提交的一致
cacheManagerUtil.putCache(OrderCacheNames.ORDER_CONFIRM_KEY,String.valueOf(userId),shopCartOrderMerger);
```

提交订单的接口设计请看下节：**订单接口-2**

