拆解芋道 ERP(一):开篇——为什么我从 33 张表开始拆

第 1 / 10 章
拆解芋道 ERP(一):开篇——为什么我从 33 张表开始拆

本系列拆解的是芋道开源 ruoyi-vue-pro 里的 yudao-module-erp 模块,底座是芋道开源项目,这一点我会全程如实标注。我的目标不是教你"用"这套 ERP,而是把它当作一台商用软件的解剖标本,看清一张进销存系统是怎么长出来的。

我为什么要拆它

去年我接手过一个进销存二开需求,甲方预算不高,时间只有六周。当时我在选型阶段反复对比了几套方案,最后深刻体会到一件事:与其凭空设计库存模型,不如先读懂一套被大量项目验证过的现成实现。

芋道的 ERP 模块就是这么一个标本。它不大——整个模块 33 张业务表,比 CRM、商城这些明星模块低调得多;它也不小——采购、销售、库存、财务四大流程闭环齐全,单据流转、审核状态、库存台账、收付核销一样不少。更关键的是,它的代码是典型 Java 后端面试高频知识点的"实战汇编":

  • 主子表单据的事务一致性怎么写?
  • 库存扣减怎么防止超卖和负库存?
  • 单据号怎么在分布式环境下不重复?
  • 反审核(把已审核单据退回去)怎么保证不产生脏数据?

这些问题的答案不在八股文里,而在这 33 张表和对应的 Service 代码里。我把它们一层层拆开给你看。

33 张表的全景地图

先看全景。整个模块的表分成五个分组,我用官网逆向文档的分组方式给你列出来:

分组表数量核心表
产品3erp_product(产品)、erp_product_category(分类)、erp_product_unit(单位)
采购6erp_purchase_order(订单)、erp_purchase_in(入库)、erp_purchase_return(退货),各有配套 items 子表
销售6erp_sale_order(订单)、erp_sale_out(出库)、erp_sale_return(退货),同样各带 items
库存10erp_stock(余额)、erp_stock_record(台账流水)、erp_warehouse(仓库),加其它出入库、调拨、盘点四类单据
财务5erp_account(结算账户)、erp_finance_receipt(收款单)、erp_finance_payment(付款单),加配套 items

把这张表在脑子里过一遍,你会发现它其实就是一个贸易公司的完整业务骨架:

  1. 先有主数据——卖什么(产品)、卖给谁(客户)、从谁那买(供应商)、在哪存(仓库)、用什么钱(结算账户);
  2. 然后有单据流——采购侧"订单 → 入库 → 付款",销售侧"订单 → 出库 → 收款",任何一环不满意就退货;
  3. 底下垫着库存账——所有单据的出入库动作最终都落到 erp_stock 改余额、往 erp_stock_record 记流水;
  4. 最后是钱——收款单、付款单把前面的单据核销掉。

这个"主数据 → 单据流 → 台账 → 核销"的四层结构,是所有 ERP 类系统的通用骨架。你看懂了这 33 张表,以后再看金蝶、用友的表结构,甚至自己设计一套轻量进销存,都只是在同一个骨架上添肉。

我在拆解中特别注意的三个设计

第一个是**"余额 + 流水"的库存模型**。erp_stock 表只有 4 个字段(产品、仓库、数量、主键),它只记"现在还有多少";而每一次增减都会在 erp_stock_record 里留下一条带业务类型、带单据引用的流水。这个组合我在后面第 7 章会专门拆——它的精妙之处在于"改余额失败就别想记流水,记流水失败余额也回滚",两件事被锁在同一个事务里。

第二个是审核状态机的位置。几乎所有单据表都有 status 字段,取值只有两个:10(未审核)、20(已审核),对应 ErpAuditStatus 枚举。库存的增减不发生在"保存单据"时,而是发生在"审核单据"时——也就是说未审核的单据对库存零影响。这个设计让"录错了还没审"的单据可以随意改,也让我能拆出"反审核时校验下游单据"的一整套防御逻辑。

第三个是BigDecimal 的使用密度。所有金额字段都是 decimal(24,6),所有金额计算都走 MoneyUtils 工具类,统一保留 2 位小数、四舍五入。我在第 3 章会专门讲这个:金额精度是 ERP 和普通 CRUD 系统拉开差距的地方,double 一下去,钱就算不回来了。

声明:它不是会计级 ERP

为了避免误导,我先把丑话说在前面:这套模块是进销存级别的 ERP,不是会计级。它没有成本核算(erp_stock 里没有成本字段)、没有批次/序列号管理、没有总账凭证,财务只有收付款单。芋道作者自己在代码里也留了不少 TODO,比如 ErpAuditStatus 上写着"未来接入工作流后,会丰富状态"。

但这恰恰是它适合被拆解的原因:它把一个 ERP 最核心、最通用的部分用最少的表实现了,而缺的那些部分,正好是我第 10 章"二开方向清单"的内容——读完前面 9 章,你会很清楚离商用还差哪几块,以及每一块该怎么补。

这个系列怎么读

后面 9 章的顺序,就是一次贸易公司业务的自然展开:

  • 第 2 章:主数据层,5 张基础表和"删除被引用的数据"的防御;
  • 第 3 章:33 张表共同的阅读规律——主子表、单号生成、金额精度、双状态字段;
  • 第 4、5 章:采购侧,从订单到入库退货,看单据怎么联动;
  • 第 6 章:销售侧,你会发现它和采购互为镜像,镜像里藏着对称之美;
  • 第 7 章:库存台账,全模块最核心的 4 字段余额表;
  • 第 8 章:调拨与盘点,一个动作生成两条流水的写法;
  • 第 9 章:财务收付核销,用 SUM 回写和 negate 冲销完成闭环;
  • 第 10 章:收尾,离商用还差的五件事,以及每一件的二开思路。

我不假设你读过芋道框架的其它部分,所有拆解都基于 ERP 模块自身的代码。代码我会贴关键片段,但建议你打开源码对照着看——ruoyi-vue-pro 是 MIT 协议开源的,个人与企业可以 100% 免费使用,拿来学习没有任何法律障碍。

准备好了就翻第 2 章。我们先从一张产品表看起,看看最"简单"的主数据层,藏着什么不简单的防御逻辑。