【CRMEB 社区版】二开第一问:这个需求该进 driver,还是进 modules?

二开第一问:这个需求该进 driver,还是进 modules?

二开最贵的错误往往不是写慢了,而是写错目录:支付 SDK 塞进订单服务、秒杀价写进商品类型、注册送礼焊在登录接口里。短期能跑,升级和协作都会疼。

这篇按开源项目 CRMEB 当前仓库的约定,把「选型」拆成可执行的判定,并标出那些看起来像插件、其实过不了零侵入的坑。路径相对 backend/。

两类目录,两套生命周期

落位 目录 解决什么 安装后关键表
驱动 driver/{type}/{Dir}/ 和第三方说话,业务规则不变 eb_driver
业务模块 modules/{领域}/{name}/ 卖什么、怎么卖、从哪登录、发什么奖 安装日志 + 自有表/菜单

驱动运行时不扫目录:拷进去但没走后台安装,DriverRegistry::scan($type) 读不到。业务模块靠 plugin.json 被发现,靠 Installer 写菜单和种子,靠 App::register() / boot() 挂注册中心与事件。

强制红线:插件不得改 app/、crmeb/、核心前端、public/install/、composer.json、config/、.env。需要改这些才能完成的需求,先停下来问清楚,不要假装已经零侵入。

判定顺序:五步,不要跳

flowchart TD A["新需求"] --> B{"只是换第三方通道?"} B -->|是| C["driver/{type}/"] B -->|否| D{"改变库存/履约/售后形态?"} D -->|是| E["modules/product"] D -->|否| F{"改变活动价/资格/限购且失败要阻断下单?"} F -->|是| G["modules/trade"] F -->|否| H{"卸掉后仍能原价成交,只是不再发奖?"} H -->|是| I["modules/marketing"] H -->|否| J{"新登录端或渠道 SDK?"} J -->|是| K["modules/channel"] J -->|否| L{"推广关系与佣金?"} L -->|是| M["modules/distribution"] L -->|否| N["读 domain-selection 或问清楚再动手"]

口诀可以再缩一句:去掉它之后,订单语义还在不在? 还在 → 多半是营销或通知;成交价/资格变了 → 交易模式;履约和售后形态变了 → 产品类型;只换供应商 → 驱动。

驱动十类(目前只有这些)

pay / sms / email / upload / logistics / express / invoice / bot / printer / copyproduct。

新增「第十一类驱动」要动 crmeb/ 与 app/,超出插件范围。接一家新短信商,是在已有 sms 类型下加目录,不是新造类型。

模块领域对照

领域 现役例子 典型扩展点
product normal / virtual / cardkey ProductTypeRegistry + 订单适配器 + 售后规则
trade seckill / combination / bargain / integral TradeModeRegistry,allowsOrderDiscount() 等
marketing newuser / signin / lottery event.php 监听字符串事件
channel h5 / wechat_mini / wechat_mp / app 渠道认证与登录
distribution 扁平 modules/distribution/ 关系快照、佣金、可挂 MCP 工具

驱动四个名字必须对齐

目录名 {Dir} 必须等于 Str::studly(driver_key)。例如 ali_pay → AliPay,yihaotong → Yihaotong。不对齐时门面抛 Driver [xxx] not supported.,在 Linux 上还可能直接 PSR-4 加载失败。

名字 规则
目录 {Dir} Str::studly(driver_key)
命名空间 driver\{type}\{Dir},类名通常 Driver
plugin.json 的 path 与真实目录完全一致
顶层 key 与 driver.driver_key 建议写成同一个值

可以用下面确认 studly 结果:

bash 复制代码
cd backend && php -r 'require "vendor/autoload.php"; echo \think\helper\Str::studly("your_key"), PHP_EOL;'

链路是:安装写 eb_driver → 业务取配置 → 门面按 key 拼类名 → 驱动类。目录存在 ≠ 已安装。

零侵入接得上,和接不上

按当前仓库文档核对过的结论:

相对好接(按 key 动态实例化):短信、邮件、上传存储、商品采集。适合作为第一个驱动二开练手题。

装了驱动也不够(核心有写死点):

类型 现象
pay 支付方式列表与端上 channel_key 分支有白名单;只装驱动,后台/端上可能看不到或调不起
logistics 查询驱动白名单,新 key 可能直接「不支持」
printer 出票服务按名称放行与拼配置
invoice 调用方固定一号通
bot 通知渠道写死钉钉/企微路径
express 面单类命名空间固定落在 driver\logistics\

遇到上表,交付方案里要写明:是改核心(需同意)、是只支持已有通道、还是另做业务适配层。不要在客户现场才发现「驱动装上了但发不了」。

业务模块:选型错了的典型症状

  • 秒杀价写在 marketing 监听器里改应付金额 → 禁用模块后用户仍能以活动价下单,或监听顺序导致价算乱。应落 trade,并声明 allowsOrderDiscount()。
  • 卡密/网盘履约写在支付成功监听器里,却不注册产品类型 → 订单中台看不到正确的 source_type、售后规则、详情路由。
  • 渠道登录逻辑塞进 app/api → 升级必冲突;应 modules/channel/{name} + 认证器契约。
  • 分销工具写进 modules/service/mcp/tools/ 并 use modules\distribution\... → arch:check 跨模块互引直接报错。工具应放在分销模块自己的 mcp/ 里,经 McpToolsRegister 注册。

选型对了,再用工具链

text 复制代码
php think make:module <name> --label=中文名 --type=...
php think arch:check
php think plugin:manifest-check

make:module 生成合规模块骨架;arch:check 拦三层依赖与跨模块互引;清单检查拦 path / DIY 声明悬空。事件用字符串 key,模块在自己的 event.php 追加,随安装状态加载。

代价

1. 选型没有编译器。 目录能编译、后台能安装,不代表领域对。验收要加一条:禁用/卸载后,成交语义是否仍符合预期。

2. 驱动「装上」和「业务调用到」是两件事。 白名单类驱动必须在方案里单独验证调用链。

3. 跨模块互引会被架构检查拦住。 共享只能走 crmeb 契约、字符串事件、或把代码放到拥有数据的那个模块里。

排障

  1. 驱动报不支持:先对 studly 目录名、再查是否已安装写入 eb_driver、最后查业务侧是否白名单。
  2. 模块菜单没有:是否跑过 Installer,是否加入 model_checkbox,角色是否包含新菜单 ID。
  3. 事件没触发:模块是否启用、event.php 是否在 boot() 里 loadEvent、事件名是否与触发端字符串一致。
  4. arch:check 红:按报错搬 use,不要关检查硬合并。
相关推荐
新*15 分钟前
Elasticsearch 7.17 的 SQL 到底能查到什么?一次踩坑记录
后端·sql·elasticsearch·查询
卷无止境39 分钟前
SALib敏感性分析的python库学习笔记
后端·python
ServBay41 分钟前
从 Val Town 停服看,代码能下载不代表项目能搬家
后端·sqlite
我不会起名字3222 小时前
seq 空洞排查:Mongo 的 51 只是预留上界,Redis CURR 才是已消耗边界
redis·后端·mongodb·go·即时通讯·im·故障排查
特立独行的猫A2 小时前
少 3 个环节、少 1 次 ABI、少 3500 行代码:cj-tauri 与 Ionic/Capacitor 的鸿蒙桥接机制对比
java·后端·python
学长毕设2 小时前
基于SpringBoot的高校后勤报修管理系统-附源码
java·spring boot·后端
特立独行的猫A2 小时前
cj-tauri vs Rust/Tauri 同款播放器实测:编译 21 秒 vs 11 分钟,磁盘 12 MB vs 1.2 GB
java·后端·python
乐橙开放平台3 小时前
海康和大华摄像头能统一管理吗?可以通过 GB28181 或视频融合平台接入
网络·后端·物联网·音视频
一条Go6 小时前
OnCall 时段路由:跨午夜、左闭右开与「Local 不是时区」
后端·架构·github