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

0 阅读5分钟

二开第一问:这个需求该进 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 类型下加目录,不是新造类型。

模块领域对照

领域现役例子典型扩展点
productnormal / virtual / cardkeyProductTypeRegistry + 订单适配器 + 售后规则
tradeseckill / combination / bargain / integralTradeModeRegistry,allowsOrderDiscount() 等
marketingnewuser / signin / lotteryevent.php 监听字符串事件
channelh5 / 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 结果:

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 注册。

选型对了,再用工具链

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,不要关检查硬合并。