前言
在工贸业务微服务架构开发场景中,ERP 进销存属于数据一致性要求较高的核心业务模块,并发场景下库存超卖、多单据数据同步异常、多租户数据隔离失效,是开发过程中普遍存在的技术难点。
现有不少开源项目将库存业务逻辑与基础框架深度耦合,缺少标准化并发控制逻辑,在多租户 SaaS 架构中容易出现库存数据错乱问题。本文基于 EzCloud 开源项目中独立拆分的ez-cloud-erp插件模块,梳理采购、销售、库存、调拨、盘点全链路实现逻辑,模块内置分布式锁、事件驱动单据事务联动相关实现,全部源码可用于技术调试与方案参考。ERP 模块源码目录参考:gitee.com/tan-tianmin…
1、EzCloud ERP 模块工程分层结构
erp 模块内部三层业务拆分,各层级职责完全隔离:
- erp-purchase:承载采购申请、采购订单、采购入库、采购退货单据相关逻辑
- erp-sales:承载销售订单、销售出库、销售退货、客户对账相关逻辑
- erp-stock:库存核心底层服务,统一封装库存台账、批次管理、库存扣减、盘点、分布式锁能力
架构设计统一约束:所有库存数值变更逻辑仅允许在 erp-stock 服务内执行,采购、销售模块仅完成单据持久化,通过标准接口调用库存能力,避免多业务重复编写库存操作逻辑,降低代码冗余。
2、核心源码:分布式并发库存扣减实现(仓库 erp-stock 核心代码)
java
运行
@Service
public class StockServiceImpl implements StockService {
@Autowired
private StockMapper stockMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private TransactionStatus transactionStatus;
// 分布式锁Key前缀
private static final String STOCK_LOCK_PREFIX = "erp:stock:lock:";
@Override
@GlobalTransactional // Seata分布式事务注解
public void deductStock(Long goodsId, Integer num, Long tenantId) {
String lockKey = STOCK_LOCK_PREFIX + tenantId + ":" + goodsId;
// 获取Redis分布式锁,规避并发超卖问题
boolean lock = RedisLockUtil.tryLock(lockKey, 30, TimeUnit.SECONDS);
if (!lock) {
throw new BusinessException("当前操作人数过多,请稍后重试");
}
try {
// 查询当前租户商品可用库存
StockDO stock = stockMapper.selectByGoodsIdAndTenant(goodsId, tenantId);
if (stock.getAvailableNum() < num) {
throw new BusinessException("商品库存不足");
}
// 扣减可用库存,增加占用库存
stock.setAvailableNum(stock.getAvailableNum() - num);
stock.setOccupyNum(stock.getOccupyNum() + num);
stockMapper.updateById(stock);
// 新增库存变动流水记录
StockRecordDO record = buildStockRecord(goodsId, num, StockOperateType.SALE_OUT);
stockRecordMapper.insert(record);
} finally {
RedisLockUtil.unLock(lockKey);
}
}
}
代码设计技术特点
- 锁与查询条件携带租户 ID,天然实现不同租户库存数据逻辑隔离;
- 借助 Redis 分布式锁拦截同一商品并发扣减请求,解决并发超卖缺陷;
- 基于 Seata 分布式事务保障库存更新、流水记录操作原子执行,避免单表更新造成数据不一致;
- 拆分可用库存、占用库存双字段,锁定未完成出库订单对应库存,规避重复售卖逻辑漏洞。
3、单据自动联动机制(采购入库自动回写库存)
EzCloud ERP 模块采用事件监听模式解耦单据与库存逻辑,不存在硬编码依赖:
- 采购入库单审核流程完成后,系统发布入库业务事件;
- stock 服务统一监听对应事件,异步执行商品库存增量更新;
- 采购退货单据审核完成后,反向执行库存扣减逻辑;
整套事件通信基于 RocketMQ 异步实现,单据服务与库存服务完全解耦。后续拓展委外加工、门店调拨等新单据类型时,仅新增对应事件发布与监听逻辑,无需修改原有库存核心实现代码。
4、模块拓展设计特点(仅针对 ERP 模块独立设计,不重复全局架构内容)
- 库存操作逻辑统一封装,拓展行业单据时无需重复编写库存扣减、台账记录逻辑;
- 预留商品批次、序列号拓展字段,如需实现一物一码管理,仅扩展数据表字段,核心库存扣减代码无需改动;
- 内置库存预警、安全库存配置相关接口,可对接项目内置低代码表单、报表模块;
- ERP 属于独立插件工程,若业务场景无需进销存相关功能,可移除对应 Maven 依赖,不会干扰权限、流程等底层基础服务正常运行。
技术资源参考
- ERP 完整模块源码目录:gitee.com/tan-tianmin…
- 库存并发控制、单据事件相关说明文档:仓库 docs/erp-stock.md
- 开源协议标注为 Apache 2.0,协议内明确规定了源码使用约束,可供技术学习参考。