Nexora · ERP 架构现场审计 · 2026-06-09

两脉 ERP,只有一条真的会多租户。

本机 ~/repos/ 实证对比:pyerp 是唯一具备运行时多租户的 ERP;囍铺主线虽然代码里有大量 aid 字段,但控制器里那个开关从来没有打开。

实证来源:本机源码 + git log + md5 文档基线:Nexora 代码架构总览(已同步更新)

一、两脉对比

文档原本写"四套 ERP 同源",但实际只有两条不同源的代码主线,能力差距很大。

真 · 运行时多租户

pyerp 主线

单仓库 · 自带租户上下文 · 已在 prod 跑通多年

pyerp_php pyerp_php_api pyerp_web pyerp_app
  • aid(顶级租户)+ bid(业务方)双层注入,BaseController 自动从 URL/header 取,全局常量。
  • admin 表 = 租户表,带 endtime 订阅过期、status 启停、domain 多域名。
  • pt_type 三档行业版本:金属版 / 料部 / 企业版,菜单按版本切。
  • 独有贵金属子系统Ymetal* 系列 34 个 controller + 40+ 张表。

代码里约 2279 处 aid 引用 + 562 处 bid + 59 处 pt_type,业务表查询全部按 aid+bid 过滤。

伪多租户 · 字段空跑

囍铺主线

三仓库分叉 + 一仓库直接复用 · 每来一个客户拷一份代码

xipu_erp_php baijian_php xylferp_php hxerp(共用 xipu)
  • BaseController 不注入 aid / bid。代码里 1896 处 where('aid', aid) 全程拿到空常量,过滤等于没写。
  • ~前端有 $base.defaultPath.indexOf('xylf') 这种客户名硬编码 if,控制字段显隐——是想做多租户但只做到一半的证据。
  • "支持多客户"的现实方法:拷代码 + 拷 docker + 一个独立 MySQL 库(hxerp / 周锦记 / 张家界全是这模式)。
  • 无贵金属子系统,无订阅过期机制。

数据库给多租户留了字段位置,但代码里的开关没打开

二、囍铺脉真实血缘

md5 + git log 实证表明,xylferp 才是起源,xipu 和 baijian 都是后来一次性 squash 拷出来的。

xylferp_php · 仙游龙凤
2025-11-22 起源 · 113 commits
保留完整 git 历史,至今仍在迭代。前端定制最深,独有"产品标识码、模具号、证书编码"等字段。
xipu_erp_php · 囍铺
2026-02-09 squash · 11 commits
囍铺主仓 + 鸿星黄金共用此代码(仅换库)。新增 Aliyunimagesearch.php 是多客户改造起点。
baijian_php · 摆件
2026-02-09 squash · 4 commits
controller 全是 xipu/xylferp 的子集,没有自己独特的功能。

字节级证据:三个仓库的 BaseController.php md5 完全一致(b0c3ff0c…),74 个 controller 中 56 个三方字节相同,剩下 18 个核心业务 controller(Goods / Buy / Goodsoutbound / Retreat 等)已被各自改过。

三、客户独有定制

每个分叉沉淀了什么客户特有的东西。

起源仓 · 定制最深
xylferp_php

仙游龙凤

  • 独有 Config.php 系统参数 controller
  • 独有 ScoreConfig.php 积分配置 controller
  • 前端独有字段:产品标识码、模具号、证书编码、副编码
  • "标签价 / 预售价"称呼按 xylf 切换
特征:金属饰品定制业务,强模具+证书追溯。
主仓 · 多客户改造起点
xipu_erp_php

囍铺(+ 鸿星 + 周锦记)

  • 独有 Aliyunimagesearch.php:以图搜图按客户读 AK
  • 新增 xipunum_aliyun_imagesearch 配置表
  • 鸿星黄金不另开仓库,docker 直接挂这份代码连不同库
  • 周锦记/张家界部署同模式:拷代码 + 独立 docker + 独立库
特征:第一个真正在尝试"配置化多客户"。
子集仓 · 无独特功能
baijian_php

摆件

  • 无独有 controller
  • 是 xipu / xylferp 的子集
  • 同名 controller 也被微调过(Goods / Buy 行数都不同)
  • 合计只有 4 个 commit,几乎只做最小适配
特征:摆件无贵金属,业务最简,分叉收益最小。

四、三家客户从业务上加了什么

不看代码看业务——主线是"黄金珠宝零售"的标准 ERP,三家在各自实际经营中往里塞了哪些主线没有的功能。每条配上线时间,并标注来自哪个仓库。

主线 · 持续高强度迭代

囍铺 完整生态 7 个仓库

客户:囍铺 + 鸿星黄金 + 周锦记 + 张家界等多家黄金珠宝零售连锁,每家独立部署。

完整生态包含:PC 后端 xipu_erp_php · PC 前端 xipu_erp_web (734 commits) · Kotlin 主后端 sprin-boot-kotlin219 (76 commits) · 销售员 App app_xiao_shou_kotlin (605 commits) · ERP 移动端 kotlinapi_uniapp · 桌面收银 electron_exe · 自动打印 python-auto-print-desktop

最后更新 2026-06-09
业务上比主线多出来的功能 (过去 8 个月,按业务模块归类)
财务报表 · 多门店化
  1. 2026-06-07
    财务报表查询加速,筛选条件 + 样式重做(销售员 App
  2. 2026-06-05
    财务页面整体改版,连续 5 天 12 次提交(Kotlin 后端 + 销售员 App
  3. 2026-06-01
    多门店财务报表,原来只能单店看,现在按门店汇总(Kotlin 后端
  4. 2026-05-31
    多门店导购业绩汇总,跨门店导购排名(Kotlin 后端 + 销售员 App
  5. 2026-04-03 → 04-08
    订单财务数据后台定时汇总,从手动算改后台跑批,首页直接看(Kotlin 后端
单据打印 · 全链路改造
  1. 2026-05-27
    云打印机门店级配置,判断门店有没配云打印机决定打印路径(Kotlin 后端
  2. 2026-05-27
    打印 PDF 操作日志,记录谁在哪台打印什么(Kotlin 后端
  3. 2026-05-22 → 05-28
    自定义单据模板系统,连续一周 8 次提交做完整个模板引擎(Kotlin 后端
  4. 2026-05-24 → 05-25
    手机端单据打印 + 导出 PDF + 预览,移动端打印从无到有(销售员 App
  5. 2025-10-27
    批量打印标签,支持正序倒序(PC 前端
销售 / 置换 / 回收 三大单据扩展
  1. 2026-05-13
    新增"是否开发票"字段,单据带发票标记(Kotlin 后端
  2. 2026-05-11 / 05-12
    销售订单"优惠金额"字段,公式 = 应收 − 实收(Kotlin 后端
  3. 2026-05-09
    销售/置换/回收 操作日志,单据级日志(Kotlin 后端
  4. 2026-05-08
    销售/回收单"其他费用"字段,应对杂费场景(Kotlin 后端
  5. 2026-05-07
    置换/回收"折旧费/克 + 折旧金额"字段,旧金抵换计价(Kotlin 后端
  6. 2025-11-09
    回收并入销售单子菜单,工作流重组(PC 前端
盘点 · 入库审批 · 库存
  1. 2026-06-02
    手机盘点按盘点员筛选,门店主管做盘点责任追溯(PC 前端 + PC 后端
  2. 2026-05-30 / 05-31
    临时盘点单 + 盘点货品问题修复Kotlin 后端 + 销售员 App
  3. 2026-05-15
    入库订单审批流,订单要审核才入库(Kotlin 后端
  4. 2026-04-15
    多库房经营改造,商品/调拨/销售全链路加库房筛选(PC 后端 + PC 前端
会员 · 销售员 App · 收银
  1. 2026-04-22
    会员"结料 / 结款 / 充值"功能,会员账户从只读变可操作(销售员 App
  2. 2026-04-22
    销售价/回收价拆开维护,金价波动期灵活定价(PC 前端
  3. 2026-06-07
    销售员 App 上线 H5 版,容器化部署,#ifdef H5 隔离不影响原生 APP(销售员 App
  4. 2025-11-17
    用户级开启微信小程序码,给会员发个人专属码(PC 前端
  5. 2025-11-17
    收银输入框点击全选,小优化提速收银(PC 前端
安全 · 多客户基建(最近开始的方向)
  1. 2026-06-09
    按客户走以图搜图配置,每家客户用自己的阿里云图像搜索账号 —— 是囍铺脉真正第一次尝试"配置化多客户"PC 前端 + PC 后端
  2. 2025-12-23
    用户类型加"股东"身份,对应不同查看权限范围(PC 前端
  3. 2025-11-25
    登录加谷歌验证器,二次验证(PC 前端
  4. 2026-05-29
    清除 ThinkPHP webshell 后门,三家都做了同一动作(PC 后端
空壳分叉 · 没真正用

摆件 baijian_php / baijian_web

客户:原计划做摆件 / 家居装饰品业务,跟黄金珠宝无关。

仅 2 个仓库,前端 1 commit,后端 4 commits 全是安全 + CI。

最后更新 2026-05-29
业务上比主线多出来的功能

无。从 2026-02-09 拷出来到今天一个业务功能都没加 —— 前端仓 4 个月零提交,后端只有"清后门"和"配 CI"这种维护性 commit。

推断:当年想给摆件业务留代码位置,但实际项目没启动或被搁置。这份代码相当于一份过期半年的囍铺骨架快照,如果以后真要做摆件 ERP,应该基于最新 xipu 重新分一份,或直接走多租户复用。

起源仓 · 已停更

仙游龙凤 xylferp_php / xylferp_web

客户:金属饰品 OEM 代加工厂——不是零售店,是给品牌方代工生产首饰的工厂。

2 个仓库 + 自己的 Kotlin 后端 sprin-boot-kotlin219-xylf。2025-11 起源,2026-01 后冻结。

最后更新 2026-05-29
业务上比主线多出来的功能 (2025-11 → 2026-01 开发期,按业务模块归类)
工厂体系 · 仙游龙凤最大特色
  1. 2025-12-09 / 12-10
    工厂管理上线,连续两天 4 次提交,把"工厂"做成独立维度(PC 前端 + PC 后端
  2. 2026-01-07
    修复工厂相关逻辑PC 后端
  3. 起源就有
    以工厂为核心的代加工链路。整个采购 / 结算 / 对账以"工厂"为维度:维护工厂档案、工厂金价成色、工厂简写、工厂分类、欠款情况、按工厂筛选所有单据。这是零售 ERP 没有的工厂端能力。
一物一码追溯
  1. 2026-01-08
    副编码功能,给商品加流转辅助标识(PC 前端 + PC 后端
  2. 起源就有
    追溯四件套:产品标识码 (出厂) + 模具号 (生产模具) + 证书编码 (鉴定证书) + 副编码 (流转)。证书号支持检索,可反查到生产工厂和模具批次。
  3. 起源就有
    镶嵌作为独立业务字段。"是否镶嵌"开关 + 主石数量 / 主石重量 / 纯度,参与计价和品质追溯。零售 ERP 里这些只是商品备注。
回收 / 销售 / 积分
  1. 2025-12-01
    回收名称改用 id,回收业务字段规范化(PC 前端
  2. 2026-01-07
    回收统计页,专门的回收数据汇总(PC 前端
  3. 2025-11-23 / 11-24
    积分系统起步,按时间段查询积分规则(PC 前端
  4. 2025-12-12 / 12-13
    积分配置后台,连续 2 天 3 次提交完成(PC 前端
  5. 2025-12-16
    分类层级支持积分配置,按品类设积分(PC 前端
  6. 2025-12-10
    销售价改造PC 前端
  7. 2025-12-30 / 12-31
    计算公式更新(金价计价相关)(PC 前端
  8. 2026-01-12
    计算逻辑更新PC 后端
  9. 起源就有
    历史采购老数据导入,专门的"老数据"页面把上一代系统的采购单导进新系统。
基础配置 · 权限 · 安全
  1. 2025-11-23
    店铺、岗位管理PC 前端
  2. 2025-11-23
    商品分类增删改PC 前端
  3. 2025-12-07
    分类排序PC 前端
  4. 2025-12-01
    登录加谷歌验证器,比囍铺早 25 天(囍铺 2025-12-26 才上)(PC 前端
  5. 2025-12-10
    权限认证改造PC 前端
  6. 2025-12-27
    统一"导购"和"业务员"概念,组织角色简化(PC 后端
  7. 2026-05-29
    清除 ThinkPHP webshell 后门,与囍铺 / 摆件同步做的安全补丁(PC 后端
打印 · 入库 · 商品图片
  1. 2025-11-23
    商品图片上传 + 以图搜图。2025 年 11 月就做过一版"上传图片找相似商品"。注意——比囍铺 2026-06 才做的同名功能早 7 个月,但这套实现没回流到囍铺,囍铺 6 月又从头做了一遍。
  2. 2025-11-29
    打印模块上线PC 前端
  3. 2025-12-02
    更新打印 + 更新入库PC 后端
  4. 2025-12-15
    打印细节调整PC 前端
  5. 2026-01-09
    点击聚焦优化PC 前端
  6. 2026-01-21
    显示保存按钮PC 前端

📌 业务视角下的核心问题

囍铺生态过去 8 个月真的很活跃 —— 仅 Kotlin 后端 + 销售员 App 就 100+ 个业务 commit,覆盖财务、打印、销售单据、盘点、会员、移动端 6 大模块。但这些好功能没回流到仙游龙凤 / 摆件。反过来仙游龙凤在 2025-11 ~ 2026-01 那 2.5 个月密集开发期沉淀的"工厂体系 / 追溯四件套 / 回收业务 / 积分系统"对囍铺接 OEM 客户也用得上,也没回流过来。最戏剧的是以图搜图——仙游龙凤 2025-11 就做过,囍铺 2026-06 又从头做了一遍。谷歌验证器也是仙游龙凤 12-01 先上,囍铺 12-26 又自己做了一版。三个分叉之间谁都拿不到对方的好东西,这是"拷代码"模式最致命的副作用。

五、多租户演进路线

给囍铺主线接上真正的多租户能力,按风险从低到高分四档。每一档都可以独立交付。

P0

通管道

把 pyerp 那 8 行 aid/bid 注入逻辑搬进囍铺主线 BaseController。不传 aid 时默认 1,单租户用户行为完全不变,但拿到了往下走的基础。

1 天 · 零风险
P1

配置按租户

OSS、ImageSearch 等配置表加 aid 字段,按 Host 头自动选对应客户的配置。解决"每装一个客户改一处代码"的问题。

1 周 · 低风险
P2

同库加客户

用户、菜单、角色三张表的 aid 引用接通。新增客户从此不再需要新开机器和新建库,控制台 INSERT 一行 admin 即可。

2–3 周 · 中风险
P3

业务全隔离

单据、库存、商品、客户等业务表全部按 aid 隔离,完全等同 pyerp 已经跑通的方案。等真有客户规模再做。

数月 · 大改

⚠ 风险提示

pyerp 现有的 8000+ 处 where('aid', aid) 是行级隔离的反面教材——一旦某处漏写就会跨租户串数据。囍铺主线接多租户时应该用 ThinkPHP model 全局 scope 一次性挂上 aid 过滤,不要靠人肉守纪律每处都写。

⚠ 配套建议

不要再继续"每来一个客户就拷一份代码"。三个分叉已经分化到 56/74 一致率,再分下去同步成本爆炸——昨天周锦记以图搜图的补丁就只打到了 xipu 一个仓库,baijian / xylferp 没动。

六、手机端多租户

PC 后台是同源相对路径,装哪个域名就连哪个,天然适配多客户。手机端不一样——原生 APK 的后端候选清单硬编码在前端,永远只能连这 3 个囍铺地址,所以周锦记目前只能走 H5 浏览器,发原生 APK 给周锦记是连不上的。

🔍 现状(app_xiao_shou_kotlin/src/uilts/login/loginUtils.js:7

export const LOGIN_HOST_CANDIDATES = [
  'erp.xipugold.com',
  'rjerp.xipugold.com',
  '47.119.139.162:25231',
];

最终方案 · 登录时选择服务器

已定方案

服务器清单配置化 + 登录页下拉选择

用户在登录页下拉选择「囍铺集团 / 周锦记 / 张家界…」即可。不让用户手填地址。一份 APK 适配所有客户,新增客户零代码改动。

第一阶段 半天
实现步骤
  1. 阶段 1 · 前端
    服务器清单先写死在前端 JSON 数组里(因为后端还没分多租户,做接口不顺手)。直接在 loginUtils.js 里维护一份 SERVER_LIST = [{name:'囍铺集团',host:'erp.xipugold.com'},{name:'周锦记',host:'zjj.py99999.com'},...],登录页拿来填下拉框。
  2. 阶段 1 · 前端
    登录页加下拉「选择服务器」,用户选完把 host 写本地存储 (uni.storage),后续 LOGIN_HOST_CANDIDATES 直接读本地存储里那一项。老用户升级 App 后没选过 → 默认走囍铺第一个地址,行为不变。
  3. 阶段 1 · 运维
    新增客户走代码改动:在 SERVER_LIST 里加一行 → 重发 APK。虽然要重打包,但每个客户共用同一个 APK,比"每家定制打包"省得多。
  4. 阶段 2 · 等多租户后端
    将来后端按本文档第五节走完 P0/P1 后,把写死的 JSON 改成调后端 /api/servers 接口,由后端返回当前可用客户清单。新增客户从此后端配一行即可,APK 不动。

📌 与第五节多租户路线的关系

这套手机端方案跟后端多租户改造解耦推进——阶段 1 (前端写死) 今天就能落,不依赖后端任何改动;阶段 2 接后端接口要等 P1「配置按租户」做完。所以可以马上把周锦记的原生 APK 这条路打通,不卡后端进度。