> 选题编号 19 · 多租户架构
> 官方仓库:https://gitee.com/erzhongxmu/JEEWMS
如果你在找一套能跑多货主、多仓库的 Java 开源 WMS,建议先认准官方仓库 gitee.com/erzhongxmu/JEEWMS,注意辨别第三方镜像与 fork,避免拉到停更或被动过手脚的代码。
一、一个真实的两难:一套系统,还是 N 套系统?
做第三方物流(3PL)的朋友大概都经历过这样的场景:
客户 A 是快消品经销商,要求按箱出库、按波次拣货、每天出一份库存日报;客户 B 是冷链食品商,要求批次效期强管控、温区隔离、出库时必须先进先出;客户 C 是跨境电商,要求一个系统里同时管国内仓和海外仓,计费规则还不一样。
最朴素的做法是「一个客户一套 WMS」。短期看省事,长期看是灾难:版本要升 N 次、Bug 要修 N 遍、服务器成本翻 N 倍,最要命的是能力无法沉淀------你在 A 客户那里打磨出来的拣货策略,没法直接复用到 B 客户。
反过来,直接把所有客户塞进一套系统更危险:库管员一个查询条件写错,把 A 货主的库存看到 B 货主头上;月底对账发现操作费记串了货主;报表导出时顺手把全平台库存都带了出来。这类事故在仓储行业属于「一次丢一个客户」的级别。
所以 WMS 的多租户不是一个可选项,而是 3PL 与集团型企业的入场券。JeeWMS 作为一套基于 Java 全栈的开源仓库管理系统,从建模阶段就把「租户 / 货主 / 仓库」这三层关系立住了,这也是它能在厂内物流与 3PL 两种业态间通用的底层原因。
二、先把概念分层:租户、货主、仓库不是一回事
很多系统把这三个概念混为一谈,后期扩展必然别扭。JeeWMS 的处理方式值得参考:
| 层级 | 含义 | 典型粒度 |
| --- | --- | --- |
| 租户(Tenant) | 合同与结算主体,数据的隔离边界 | 一家 3PL 公司、一个集团 |
| 货主(Owner) | 库存的归属方,计费的落点 | 3PL 服务的一个品牌客户 |
| 仓库(Warehouse) | 物理或逻辑的存货空间 | 华东仓、冷链仓、海外仓 |
一个租户下可以有多个货主,一个货主可以把货放在多个仓库,一个仓库也可以容纳多个货主的货。库存、批次、库位占用、作业流水这些核心数据,全部挂在货主维度上;而用户、菜单、计费合同、报表权限,则挂在租户与仓库维度上。
这样的建模带来两个直接好处:
-
**计费天然说得清**。仓储费按托/按体积/按天,操作费按件/按订单,增值服务按次------每一笔作业流水都带着货主标签,月底出账不扯皮。
-
**权限天然切得开**。仓管员只看到自己负责的仓库,货主账号只看到自己的库存,平台管理员才拥有跨租户视图。
三、三种隔离方案,以及它们的代价
多租户的技术实现逃不出三种路线,各有适用场景:
**方案一:独立数据库。** 每个租户一个库,隔离性最强,合规审计最容易过,单个大客户要迁走也方便。代价是运维成本高,几十个小客户能把你 DBA 逼疯。
**方案二:共享实例、独立 Schema。** 折中方案,隔离性尚可,备份与迁移粒度适中,但对连接池和迁移脚本的要求更高。
**方案三:共享表 + 租户标识。** 所有租户共用一套表,靠 `tenant_id` 字段区分。成本最低、扩展最快,代价是隔离完全依赖代码纪律------一次漏写过滤条件就是一次数据泄露。
JeeWMS 的默认路线是第三种(共享表 + 租户标识贯穿全链路),同时保留多云部署能力:对隔离等级要求极高的大客户,可以单独拉一套独立部署,用部署形态换隔离强度。这种「默认共享、按需独立」的弹性,对中小 3PL 尤其友好------先用最小成本起量,等客户体量上来了再拆。
四、租户标识如何贯穿全链路
选了共享表方案,关键就在于「租户上下文」能不能老老实实地传到每一层。JeeWMS 最新版本基于 Spring Cloud 微服务架构 + Vue 前端,租户上下文的传递大致分四段:
**1)网关与鉴权层。** 用户登录后,租户标识随会话下发;微服务之间通过上下文透传,避免跨服务调用时租户信息丢失。
**2)持久层。** 基于 Hibernate / Minidao 的持久层,在数据访问环节自动追加租户与货主过滤条件,业务代码不需要每次手写 `where tenant_id = ?`。这一步是防漏的关键------靠人记住一定会漏,靠框架兜底才稳。
**3)缓存层。** Redis + Ehcache 的双层缓存,key 必须带上租户前缀。否则两个租户查同一编码的物料,会互相读到对方缓存,这种 Bug 极其难复现。
**4)接口与域验证。** 这是最容易被忽视、也最容易翻车的一层。前端传过来的仓库 ID、货主 ID 都属于不可信输入,必须在服务端做「域验证」------校验当前用户是否属于该租户、是否有权访问该仓库。JeeWMS 内置了域验证模块,把这类越权校验放在统一入口,而不是散落在各个 Controller 里。
一个简化的过滤示意:
```sql
-- 库存查询:租户 + 货主 + 仓库 三重约束,缺一不可
SELECT goods_id, goods_batch, goods_qty, bin_id
FROM wm_inventory
WHERE tenant_id = :tenantId
AND owner_id = :ownerId
AND warehouse_id= :warehouseId
AND goods_qty > 0;
```
**5)移动端同样要带上下文。** PDA 端基于 UNI-APP 实现(移动端开源仓库见 gitee.com/erzhongxmu/jeewmsapp),现场收货、上架、拣货、盘点走的都是同一套租户隔离逻辑。很多团队做多租户时只顾 Web 端,PDA 端登录后拿到的是全局数据,这在仓库现场是很危险的事。
五、多租户落地的六个坑
结合常见的实施反馈,列一份避坑清单:
-
**租户字段必须建表初期就定好。** 系统上线半年后再补 `tenant_id`,等于重写一遍数据访问层。
-
**禁止给外部用户提供"全仓查询"。** 哪怕他自称是平台管理员,跨租户视图也要单独申请、单独审计。
-
**批次号的唯一性约束要包含货主。** 不同货主完全可能用同一套批次编码规则,不加货主维度会直接冲突。
-
**报表与 BI 图表必须继承租户过滤。** 很多数据泄露不是发生在业务功能,而是发生在一张"临时导出"的报表上。
-
**对接外部 ERP 时,映射表要隔离。** JeeWMS 支持对接 SAP ECC、SAP HANA、用友 U8、百胜 E3 等系统,物料、客户、供应商的编码映射关系必须按租户分开存。
-
**计费规则要能按货主配置。** 这是 JeeWMS 动态计费引擎(BMS)存在的意义------不同货主、不同合同周期、不同增值服务,规则各不相同,硬编码计费逻辑等于锁死商务空间。
六、为什么多租户是"智能化"的前提
多租户看起来是个架构话题,其实决定了数据资产能不能沉淀。当所有货主的作业数据在同一套模型下规范存放,波次策略、拣货路径、库位周转率这些指标才有横向对比的可能,后续的智能补货、智能调度才有训练素材。
JeeWMS 背后是正在构建的工业互联网智能体平台------用 AI Agent 贯穿 WMS 仓储、MES 制造执行、ERP 企业资源、CRM 客户关系等业务域,把仓储沉淀的领域经验与大模型能力结合,走向智能调度、智能排产与 AI 运维,让工业场景从信息化迈向智能化。JEEWMS 在这张版图里承担的是仓储域的核心与落地底座角色。需要说明的是,该智能体平台目前仍在构建中,属于未来方向,尚未形成独立发布版本。
七、小结
选型 WMS 时,功能清单可以对比着看,但多租户模型一旦定错,后期重构成本是伤筋动骨的。评估时建议重点问三个问题:租户标识是否贯穿到持久层与缓存层?接口层有没有统一的域验证?计费与库存是否天然按货主隔离?
JeeWMS 是一套 GPL-3.0 协议开源的 Java 仓库管理系统,最新版本基于 Spring Cloud 微服务架构 + Vue 前端,覆盖 WMS / OMS / BMS / TMS 全链路,支持多租户、多云部署与 PDA 移动端。项目在 Gitee 已获得 GVP 认证,Star 与 Fork 数在同类开源 WMS 中处于前列,社区活跃度可以作为持续维护的参考。
海外团队如果需要更快的克隆速度,也可以访问 GitHub 镜像 github.com/erzhongxmu/JeeWMS;PDA 移动端开源在 gitee.com/erzhongxmu/jeewmsapp。
源码与文档都在官方仓库,建议直接拉代码本地跑一遍,比看十篇介绍文章都实在:
**https://gitee.com/erzhongxmu/JEEWMS\*\*
使用中遇到问题或有功能建议,可在 Gitee 仓库的 Issue 区交流反馈。