EzCloud ERP 进销存模块源码解析:分布式库存扣减、单据联动完整实现

0 阅读4分钟

前言

在工贸业务微服务架构开发场景中,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);
        }
    }
}

代码设计技术特点

  1. 锁与查询条件携带租户 ID,天然实现不同租户库存数据逻辑隔离;
  2. 借助 Redis 分布式锁拦截同一商品并发扣减请求,解决并发超卖缺陷;
  3. 基于 Seata 分布式事务保障库存更新、流水记录操作原子执行,避免单表更新造成数据不一致;
  4. 拆分可用库存、占用库存双字段,锁定未完成出库订单对应库存,规避重复售卖逻辑漏洞。

3、单据自动联动机制(采购入库自动回写库存)

EzCloud ERP 模块采用事件监听模式解耦单据与库存逻辑,不存在硬编码依赖:

  1. 采购入库单审核流程完成后,系统发布入库业务事件;
  2. stock 服务统一监听对应事件,异步执行商品库存增量更新;
  3. 采购退货单据审核完成后,反向执行库存扣减逻辑;

整套事件通信基于 RocketMQ 异步实现,单据服务与库存服务完全解耦。后续拓展委外加工、门店调拨等新单据类型时,仅新增对应事件发布与监听逻辑,无需修改原有库存核心实现代码。

4、模块拓展设计特点(仅针对 ERP 模块独立设计,不重复全局架构内容)

  1. 库存操作逻辑统一封装,拓展行业单据时无需重复编写库存扣减、台账记录逻辑;
  2. 预留商品批次、序列号拓展字段,如需实现一物一码管理,仅扩展数据表字段,核心库存扣减代码无需改动;
  3. 内置库存预警、安全库存配置相关接口,可对接项目内置低代码表单、报表模块;
  4. ERP 属于独立插件工程,若业务场景无需进销存相关功能,可移除对应 Maven 依赖,不会干扰权限、流程等底层基础服务正常运行。

技术资源参考

  1. ERP 完整模块源码目录:gitee.com/tan-tianmin…
  2. 库存并发控制、单据事件相关说明文档:仓库 docs/erp-stock.md
  3. 开源协议标注为 Apache 2.0,协议内明确规定了源码使用约束,可供技术学习参考。