文章目录
- 一,锁定库存的基本逻辑
- 二,具体实现
创建订单时,有一个非常重要的步骤,就是锁定库存,或者称之为预占库存。
尽管还没有卖出去,但是因为订单已经创建,所以要确保这个订单对应商品是有库存的,就需要提前告知库存服务,需要准备多少库存,库存服务就会在当前库存基础上减去一部分库存,避免超卖。
一,锁定库存的基本逻辑
根据图片,锁定库存的逻辑可以概括为以下几个步骤:
- 商品尝试锁库存:这是库存锁定流程的开始,系统尝试对商品进行库存锁定。
- 增强版逻辑:增加了一些额外的判断或处理步骤。
- 撤销:在库存锁定过程中,如果遇到某些条件不满足,系统会执行撤销操作,释放已锁定的库存。
- 全部锁定:在某些情况下,系统可能会执行全部库存的锁定操作。
- 全部回滚:如果全部锁定操作失败或遇到问题,系统会执行全部回滚,即撤销所有已执行的锁定操作。
- 失败:在尝试锁定库存的过程中,如果遇到问题导致无法完成锁定,系统会标记为失败。
- 成功:如果库存锁定操作成功完成,系统会标记为成功。
- 修改锁定状态:在锁定过程中,系统可能需要根据实际情况修改库存的锁定状态。
- 返回:完成锁定或解锁操作后,系统会返回到上一步或初始状态。
这个过程看起来是一个自动化的库存管理流程,涉及到库存的锁定、解锁、状态修改等操作,并在操作过程中会有成功或失败的反馈。
二,具体实现
public boolean orderLockStock(WareSkuLockVo vo) {
/**
* 保存库存工作单详情信息
* 追溯
*/
WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
wareOrderTaskEntity.setOrderSn(vo.getOrderSn());
wareOrderTaskEntity.setCreateTime(new Date());
wareOrderTaskService.save(wareOrderTaskEntity);
//1、按照下单的收货地址,找到一个就近仓库,锁定库存
//2、找到每个商品在哪个仓库都有库存
List<OrderItemVo> locks = vo.getLocks();
List<SkuWareHasStock> collect = locks.stream().map((item) -> {
SkuWareHasStock stock = new SkuWareHasStock();
Long skuId = item.getSkuId();
stock.setSkuId(skuId);
stock.setNum(item.getCount());
//查询这个商品在哪个仓库有库存
List<Long> wareIdList = wareSkuDao.listWareIdHasSkuStock(skuId);
stock.setWareId(wareIdList);
return stock;
}).collect(Collectors.toList());
//2、锁定库存
for (SkuWareHasStock hasStock : collect) {
boolean skuStocked = false;
Long skuId = hasStock.getSkuId();
List<Long> wareIds = hasStock.getWareId();
if (org.springframework.util.StringUtils.isEmpty(wareIds)) {
//没有任何仓库有这个商品的库存
throw new NoStockException(skuId);
}
//1、如果每一个商品都锁定成功,将当前商品锁定了几件的工作单记录发给MQ
//2、锁定失败。前面保存的工作单信息都回滚了。发送出去的消息,即使要解锁库存,由于在数据库查不到指定的id,所有就不用解锁
for (Long wareId : wareIds) {
//锁定成功就返回1,失败就返回0
Long count = wareSkuDao.lockSkuStock(skuId,wareId,hasStock.getNum());
if (count == 1) {
skuStocked = true;
WareOrderTaskDetailEntity taskDetailEntity = WareOrderTaskDetailEntity.builder()
.skuId(skuId)
.skuName("")
.skuNum(hasStock.getNum())
.taskId(wareOrderTaskEntity.getId())
.wareId(wareId)
.lockStatus(1)
.build();
wareOrderTaskDetailService.save(taskDetailEntity);
//TODO 告诉MQ库存锁定成功
StockLockedTo lockedTo = new StockLockedTo();
lockedTo.setId(wareOrderTaskEntity.getId());
StockDetailTo detailTo = new StockDetailTo();
BeanUtils.copyProperties(taskDetailEntity,detailTo);
lockedTo.setDetailTo(detailTo);
// rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",lockedTo);
break;
} else {
//当前仓库锁失败,重试下一个仓库
}
}
if (skuStocked == false) {
//当前商品所有仓库都没有锁住
throw new NoStockException(skuId);
}
}
//3、肯定全部都是锁定成功的
return true;
}
orderLockStock
的方法为订单锁定库存。
-
保存库存工作单信息:
- 创建一个新的
WareOrderTaskEntity
实体,并设置订单号 (orderSn
) 和创建时间。 - 保存这个实体到数据库。
- 创建一个新的
-
查询并准备锁定库存的信息:
- 获取传入的
WareSkuLockVo
对象中的locks
列表,该列表包含需要锁定的商品信息。 - 遍历
locks
列表,为每个商品创建一个SkuWareHasStock
对象,其中包含商品 ID (skuId
)、需要锁定的数量 (num
) 以及拥有该商品库存的仓库列表 (wareIdList
)。
- 获取传入的
-
锁定库存:
- 遍历准备好的
SkuWareHasStock
对象列表。 - 对于每个商品,检查是否有任何仓库有此商品的库存。如果没有,则抛出
NoStockException
异常。 - 尝试在每个有库存的仓库中锁定商品:
- 如果锁定成功(返回值为 1),则设置
skuStocked
为true
,并保存一条库存锁定详情记录至数据库。 - 创建一个消息对象 (
StockLockedTo
) 并填充相关信息,准备发送给消息队列以通知其他服务库存已锁定。 - 如果所有尝试锁定的仓库都失败,则最终抛出
NoStockException
异常。
- 如果锁定成功(返回值为 1),则设置
- 遍历准备好的
-
返回结果:
- 如果所有商品的库存都成功锁定,则方法返回
true
。
- 如果所有商品的库存都成功锁定,则方法返回
这个方法实现了从接收库存锁定请求开始,经过库存锁定和状态记录,到最后通过消息队列通知其他服务库存锁定成功的一系列流程。
同时,它还处理了异常情况,如当商品没有库存时会抛出异常来确保系统的健壮性和一致性。