document
API test

锁定库存

POST

Description or Example

# 知识点 ## 锁定库存的基本思路 > 1. 首先先获取商品有库存的仓库 > 2. 然后继续判断该仓库是否有足够的库存 ## 为什么需要借助异常机制 > 因为并不是所有的商品都具有库存, 有可能前几个有, 后几个没有, 因此, 为了避免最终有些被扣除, 有些没有被扣除, 我们引入异常机制, 来保证整体的ACID特性, 要么全部能扣除, 要么全都不可以 ## 为什么需要工作单和工作单详情 > 因为我们采取了最终一致性+可靠消息来保证数据一致性, 因此, 我们需要自己回滚, 为了记录我们干过什么操作, 因此需要工作单和工作单详情 > 换而言之, 我们不能然一个失败的订单扣库存 # 核心代码 ```java @RequestMapping("/lock/sku/stock") public R lockSkuStock(@RequestBody WareLockTO wareLockTO) { try { Boolean isSuccess = wareInfoService.lockSkuStock(wareLockTO); if (isSuccess) return R.ok(); else throw new StockException("没有足够的库存"); } catch (StockException e) { // 没有库存 return R.error(WareConstant.StockEnum.STOCK_NOT_ENOUGH.getCode(), WareConstant.StockEnum.STOCK_NOT_ENOUGH.getMsg()); } } ``` ```java @Override @Transactional public Boolean lockSkuStock(WareLockTO wareLockTO) { // 0. 首先保存工作单 WareOrderTaskEntity orderTask = new WareOrderTaskEntity().setOrderSn(wareLockTO.getOrderSn()); wareOrderTaskService.save(orderTask); List<WareOrderTaskDetailEntity> detailEntities = new ArrayList<>(); List<WareLockTO.OrderItemInfo> orderItemInfoList = wareLockTO.getOrderItemInfoList(); if (orderItemInfoList != null && !orderItemInfoList.isEmpty()) { // 有订单需要锁定库存 // 1. 找各个商品有库存的仓库, 并封装对应的对象 List<SkuStockInfo> stockInfos = orderItemInfoList.stream().map(orderItemInfo -> { SkuStockInfo skuStockInfo = new SkuStockInfo(); Long skuId = orderItemInfo.getSkuId(); List<Long> wareIds = wareSkuService.getWareIdBySkuId(skuId); return skuStockInfo .setSkuId(skuId) .setWareIds(wareIds) .setNum(orderItemInfo.getNum()); }).collect(Collectors.toList()); for (SkuStockInfo stockInfo : stockInfos) { // 第一次遍历, 遍历每件商品 boolean isSuccess = false; // 布尔标识, 默认当前的商品没有库存 // 首先判断, 该商品是否有库存 if (stockInfo.wareIds == null || stockInfo.wareIds.isEmpty()) { // 符合条件, 说明当前商品并没有库存 throw new StockException("当前商品没有足够的库存"); } // 存在库存, 但是不知道是否足够, 继续判断 for (Long wareId : stockInfo.getWareIds()) { if(wareSkuService.judgeWareIsEnough(stockInfo.skuId, wareId, stockInfo.getNum())) { // 有足够的库存, 执行到这里, 锁定库存成功, 需要保存对应的工作单详情 detailEntities.add(new WareOrderTaskDetailEntity().setWareId(wareId) .setSkuId(stockInfo.skuId) .setSkuNum(stockInfo.num) .setTaskId(orderTask.getId()) .setLockStatus(WareConstant.LockEnum.LOCKED.getCode())); isSuccess = true; break; } // 没有足够的库存, 继续尝试去锁定 } if (!isSuccess) { // 当前商品真的没有库存 throw new StockException("当前商品没有足够的库存"); } } } // 最后保存工作单详情 wareOrderTaskDetailService.saveBatch(detailEntities); // 然后批量发消息 mqService.sendBatchMessage(detailEntities.stream().map(detailEntity -> { Long detailId = detailEntity.getId(); Long taskId = orderTask.getId(); return new WareTO().setTaskDetailId(detailId).setTaskId(taskId); }).collect(Collectors.toList())); return true; } ``` ```xml <select id="getHashStockWareBySkuId" resultType="java.lang.Long"> SELECT ware_id FROM wms_ware_sku WHERE sku_id = #{skuId} AND stock - stock_locked > 0 </select> <update id="judgeWareIsEnough"> UPDATE wms_ware_sku SET stock_locked = stock_locked + #{num} WHERE stock - stock_locked >= #{num} AND ware_id = #{wareId} AND sku_id = #{skuId} </update> ```