订单微服务远程调用商品微服务和购物车微服务编码实践

按照前文步骤对订单微服务进行编码后,代码中依然会被标红。被标红的代码及注释如下:

@PostMapping("/saveOrder")
@ApiOperation(value = "生成订单接口", notes = "传入为地址 id 和待结算的购物项 id 数组")
public Result<String> saveOrder(@ApiParam(value = "订单参数") @RequestBody SaveOrderParam saveOrderParam, @TokenToMallUser MallUser loginMallUser) {
    int priceTotal = 0;
    if (saveOrderParam == null || saveOrderParam.getCartItemIds() == null ||
            saveOrderParam.getAddressId() == null) {
        NewBeeMallException.fail(ServiceResultEnum.PARAM_ERROR.getResult());
    }
    if (saveOrderParam.getCartItemIds().length < 1) {
        NewBeeMallException.fail(ServiceResultEnum.PARAM_ERROR.getResult());
    }
    // 根据购物项 id 列表查询购物车表并进行相关的逻辑校验
    List<NewBeeMallShoppingCartItemVO> itemsForSave =
            newBeeMallShoppingCartService.getCartItemsForSettle(Arrays.asList(saveOrderParam.getCartItemIds()), loginMallUser.getUserId());
    // 省略部分代码
    return ResultGenerator.genFailResult("生成订单失败");
}

在生成订单接口中,需要根据 cartItemIds 参数查询数据库中的一条或多条购物项数据进行基本的逻辑验证,之后才能执行后续的业务逻辑。而订单微服务未连接购物项表所在的数据库,无法直接查询对应的购物项数据,所以这部分代码会被标红,代码如下:

@Override
@Transactional
public String saveOrder(MallUser loginMallUser, MallUserAddress address,
                        List<NewBeeMallShoppingCartItemVO> myShoppingCartItems) {
    List<Long> itemIdList = myShoppingCartItems.stream().map
            (NewBeeMallShoppingCartItemVO::getCartItemId).collect(Collectors.toList());
    List<Long> goodsIds = myShoppingCartItems.stream().map(NewBeeMallShoppingCartItemVO::getGoodsId).collect(Collectors.toList());
    // 查询商品数据
    List<NewBeeMallGoods> newBeeMallGoods = newBeeMallGoodsMapper.selectByPrimaryKeys(goodsIds);
    // 检查是否存在已下架商品
    List<NewBeeMallGoods> goodsListNotSelling = newBeeMallGoods.stream()
            .filter(newBeeMallGoodsTemp ->
                    newBeeMallGoodsTemp.getGoodsSellStatus() != Constants.SELL_STATUS_UP)
            .collect(Collectors.toList());
    if (!CollectionUtils.isEmpty(goodsListNotSelling)) {
        if (!goodsListNotSelling.isEmpty()) {
            newBeeMallException.fail(goodsListNotSelling.get(0).getGoodsName() + " 已下架,无法生成订单");
        }
    }
    Map<Long, NewBeeMallGoods> newBeeMallGoodsMap = newBeeMallGoods.stream()
            .collect(Collectors.toMap(NewBeeMallGoods::getGoodsId, Function.identity(), (entity1, entity2) -> entity1));
    // 判断商品是否存在
    for (NewBeeMallShoppingCartItemVO shoppingCartItemVO : myShoppingCartItems) {
        // 查询的商品中不存在购物项中的该商品,无法生成订单
        if (!newBeeMallGoodsMap.containsKey(shoppingCartItemVO.getGoodsId())) {
            newBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult());
        }
        // 存在数量大于库存的情况,无法生成订单
        if (shoppingCartItemVO.getGoodsCount() > newBeeMallGoodsMap.get(shoppingCartItemVO.getGoodsId()).getStockNum()) {
            newBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult());
        }
    }
    // 删除购物项
    if (!CollectionUtils.isEmpty(itemIdList) && !CollectionUtils.isEmpty(goodsIds)
            && itemIdList.size() == goodsIds.size()) {
        if (newBeeMallShoppingCartItemMapper.deleteBatch(itemIdList) > 0) {
            List<StockNumDTO> stockNumDTOS = BeanUtil.copyList(myShoppingCartItems,
                    StockNumDTO.class);
            // 修改商品库存
            int updateStockNumResult = newBeeMallGoodsMapper.updateStockNum(stockNumDTOS);
            if (updateStockNumResult < 1) {
                newBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_COUNT_ERROR.getResult());
            }
            //生成订单号
            String orderNo = NumberUtil.genOrderNo();
            int priceTotal = 0;
            //保存订单
            NewBeeMallOrder newBeeMallOrder = new NewBeeMallOrder();
            newBeeMallOrder.setOrderNo(orderNo);
            newBeeMallOrder.setUserId(loginMallUser.getUserId());
            //总价
            for (NewBeeMallShoppingCartItemVO newBeeMallShoppingCartItemVO :
                    myShoppingCartItems) {
                priceTotal += newBeeMallShoppingCartItemVO.getGoodsCount() *
                        newBeeMallShoppingCartItemVO.getSellingPrice();
            }

            if (priceTotal < 1) {
                NewBeeMallException.fail(ServiceResultEnum.ORDER_PRICE_ERROR.getResult());
            }
            newBeeMallOrder.setTotalPrice(priceTotal);
            String extraInfo = "";
            //省略部分代码
            NewBeeMallException.fail(ServiceResultEnum.DB_ERROR.getResult());
            NewBeeMallException.fail(ServiceResultEnum.DB_ERROR.getResult());
            NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult());
            return ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult();
        }
    }
}

同样,在生成订单的业务层代码中,也有针对购物项表和商品表的操作,如删除购物项数据、查询商品数据、修改商品库存,之后才能执行后续生成订单的业务逻辑。而订单微服务未连接购物项表和商品表所在的数据库,无法直接通过 NewBeeMallShoppingCartItemMapperGoodsMapper 完成对应的功能,所以这部分代码会被标红。

因此,想要完成订单微服务的代码改造,必须进行购物项数据查询、购物项数据删除、商品数据查询和商品库存修改部分代码的改造,由原本直接操作购物项表和商品表改为远程调用购物车微服务和商品微服务中的接口来完成这个逻辑。订单微服务不仅要与用户微服务通信,还要与购物车微服务和商品微服务通信,代码改造步骤如下。

第一步,在商品微服务中增加修改库存的逻辑代码和对外暴露接口。

当前所需的查询购物项数据、删除购物项数据、查询商品数据和修改商品库存这4个远程调用操作,有 3 个功能都已经完成编码,只有修改商品库存还未完成编码。

打开 newbee-mall-cloud-goods-web 工程,在 NewBeeAdminGoodsInfoController 类中新增接口,代码如下:

/**
 * 修改商品库存
 */
@PutMapping("/updateStock")
@ApiOperation(value = "修改库存", notes = "")
public Result updateStock(@RequestBody UpdateStockNumDTO updateStockNumDTO) {
    return ResultGenerator.genSuccessResult(newBeeMallGoodsService.updateStockNum(updateStockNumDTO.getStockNumDTOS()));
}

打开 newbee-mall-cloud-goods-api 工程,在 NewBeeCloudGoodsServiceFeign 类中定义该接口,使其可以被其他微服务调用,新增代码如下:

@PutMapping(value = "/admin/updateStock")
Result<Boolean> updateStock(@RequestBody UpdateStockNumDTO updateStockNumDTO);

第二步,引入 goods-apishop-cart-api 依赖。

打开 newbee-mall-cloud-order-web 工程下的 pom.xml 文件,增加与购物车微服务和商品微服务远程通信所需的 newbee-mall-cloud-shop-cart-api 模块和 newbee-mall-cloud-goods-api 模块,新增依赖配置如下:

<dependency>
    <groupId>ltd.goods.newbee.cloud</groupId>
    <artifactId>newbee-mall-cloud-goods-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>ltd.shopcart.newbee.cloud</groupId>
    <artifactId>newbee-mall-cloud-shop-cart-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

第三步,增加关于购物车微服务和商品微服务中 FeignClient 的配置。

打开 newbee-mall-cloud-goods-web 工程,对启动类 NewBeeMallCloudOrderServiceApplication 中的 @EnableFeignClients 注解进行修改,增加对 NewBeeCloudShopCartServiceFeign 类和 NewBeeCloudGoodsServiceFeign 类的声明,代码如下:

@EnableFeignClients(basePackageClasses = {ltd.goods.cloud.newbee.openfeign.NewBeeCloudGoodsServiceFeign.class, ltd.shopcart.cloud.newbee.openfeign.NewBeeCloudShopCartServiceFeign.class, ltd.user.cloud.newbee.openfeign.NewBeeCloudUserServiceFeign.class})

接下来就可以直接使用 NewBeeCloudShopCartServiceFeign 类和 NewBeeCloudGoodsServiceFeign 类与购物车微服务、商品微服务进行远程通信了。

第四步,修改对商品表和购物项表操作的代码。

打开 newbee-mall-cloud-order-web 工程,修改 NewBeeMallOrderServiceImpl 类中 saveOrder() 方法的逻辑代码。删除原本直接操作购物项表和商品表的代码,之后依次注入 NewBeeCloudShopCartServiceFeign 类和 NewBeeCloudGoodsServiceFeign 类,并通过调用购物车微服务和商品微服务来完成查询购物项数据、删除购物项数据、查询商品数据和修改商品库存 4 个逻辑操作。这里,笔者把购物项数据的查询和前置判断也转移到业务方法中了。修改后的代码及相应的注释如下:

@Autowired
private NewBeeCloudGoodsServiceFeign goodsService;

@Autowired
private NewBeeCloudShoppingCartServiceFeign shopCartService;

@Override
@Transactional
public String saveOrder(Long mallUserId, MallUserAddress address, List<Long> cartItemIds) {
    //调用购物车微服务 Feign 获取数据
    Result<List<NewBeeMallShoppingCartItemDTO>> cartItemDTOListResult =
            shopCartService.listByCartItemIds(cartItemIds);
    if (cartItemDTOListResult == null || cartItemDTOListResult.getResultCode() != 200) {
        NewBeeMallException.fail("参数异常");
    }
    List<NewBeeMallShoppingCartItemDTO> itemsForSave =
            cartItemDTOListResult.getData();
    if (CollectionUtils.isEmpty(itemsForSave)) {
        //无数据
        NewBeeMallException.fail("参数异常");
    }
    List<Long> itemIdList = itemsForSave.stream().map(NewBeeMallShoppingCartItemDTO::getCartItemId).collect(Collectors.toList());
    List<Long> goodsIds = itemsForSave.stream().map(NewBeeMallShoppingCartItemDTO::getGoodsId).collect(Collectors.toList());
    //调用商品微服务 Feign 获取数据
    Result<List<NewBeeMallGoodsDTO>> goodsDTOListResult = goodsService.listByGoodsIds(goodsIds);

    if (goodsIds != null && goodsDTOListResult != null && goodsDTOListResult.getResultCode() != 200) {
        NewBeeMallException.fail("参数异常");
    }
    //检查是否包含已下架商品
    List<NewBeeMallGoodsDTO> newBeeMallGoods = goodsDTOListResult.getData();
    List<NewBeeMallGoodsDTO> goodsListNotSelling = newBeeMallGoods.stream()
            .filter(newBeeMallGoodsTemp -> newBeeMallGoodsTemp.getGoodsSellStatus() != 0)
            .collect(Collectors.toList());

    if (!CollectionUtils.isEmpty(goodsListNotSelling)) {
        //goodsListNotSelling 对象非空表示有下架商品
        NewBeeMallException.fail(goodsListNotSelling.get(0).getGoodsName() + " 已下架,无法生成订单");
    }

    Map<Long, NewBeeMallGoodsDTO> newBeeMallGoodsMap = newBeeMallGoods.stream()
            .collect(Collectors.toMap(NewBeeMallGoodsDTO::getGoodsId, entity -> entity));
    //判断商品库存
    for (NewBeeMallShoppingCartItemDTO cartItemDTO : itemsForSave) {
        //查询的商品中不存在购物车中的这条关联商品数据,直接返回错误提示
        if (!newBeeMallGoodsMap.containsKey(cartItemDTO.getGoodsId())) {
            NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult());
        }
        //存在数量大于库存的情况,直接返回错误提示
        if (cartItemDTO.getGoodsCount() > newBeeMallGoodsMap.get(cartItemDTO.getGoodsId()).getStockNum()) {
            NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_COUNT_ERROR.getResult());
        }
    }

    //删除购物项
    if (!CollectionUtils.isEmpty(itemIdList) && !CollectionUtils.isEmpty(goodsIds)
            && !CollectionUtils.isEmpty(newBeeMallGoods)) {
        //调用购物车微服务 Feign 删除数据
        Result<Boolean> deleteByCartItemIdsResult = shopCartService.deleteByIds(itemIdList);

        if (deleteByCartItemIdsResult != null && deleteByCartItemIdsResult.getResultCode() == 200) {
            List<StockNumDTO> stockNumDTOs = BeanUtil.copyList(itemsForSave,
                    StockNumDTO.class);
            UpdateStockNumDTO updateStockNumDTO = new UpdateStockNumDTO();
            updateStockNumDTO.setStockNumDTOs(stockNumDTOs);

            // 调用商品微服务 Feign 修改库存数据
            Result<Boolean> updateStockResult = goodsService.updateStock(updateStockNumDTO);
            if (updateStockResult == null || updateStockResult.getResultCode() != 200) {
                NewBeeMallException.fail(ServiceResultEnum.PARAM_ERROR.getResult());
            }
            if (!updateStockResult.getData()) {
                NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_COUNT_
                        ERROR.getResult());
            }
            // 省略部分代码
            NewBeeMallException.fail(ServiceResultEnum.DB_ERROR.getResult());
        }
        NewBeeMallException.fail(ServiceResultEnum.DB_ERROR.getResult());
        NewBeeMallException.fail(ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult());
        return ServiceResultEnum.SHOPPING_ITEM_ERROR.getResult();
    }
}