mirror of
https://github.com/crmeb/CRMEB.git
synced 2026-10-28 21:40:50 +00:00
127 lines
6.9 KiB
Markdown
127 lines
6.9 KiB
Markdown
# 第三方驱动底座(driver/)
|
||
|
||
## 一、定位
|
||
|
||
`driver/` 是三层架构的最底层,只封装**与第三方服务的通信细节**(协议、鉴权、SDK 适配),不承载任何业务决策:
|
||
|
||
```
|
||
modules/(业务扩展层)→ app/(统一管理中台)→ crmeb/(核心底层)→ driver/(第三方驱动底座)
|
||
```
|
||
|
||
- **只能被 `crmeb/services` 调用**,不可反向引用 `app/`、`modules/`
|
||
- 驱动包内禁止访问数据库、禁止调用业务 Services,配置由上层注入(`initialize(array $config)`)
|
||
- 每个驱动包 = 一个自包含目录(`Driver.php` + `plugin.json`,可选 `Installer.php` + `extend/` SDK 子目录)
|
||
|
||
## 二、驱动类型总览
|
||
|
||
| 类型 | 目录 | 职责 | 基类 | 开发指南 |
|
||
|------|------|------|------|---------|
|
||
| 短信 | `driver/sms/` | 短信发送/模板/记录 | `crmeb\services\sms\BaseSms` | [sms/DEVELOPMENT.md](sms/DEVELOPMENT.md) |
|
||
| 支付 | `driver/pay/` | 收款/退款/对账 | `crmeb\services\pay\BasePay` + `PayInterface` | [pay/DEVELOPMENT.md](pay/DEVELOPMENT.md) |
|
||
| 上传存储 | `driver/upload/` | 文件存储/域名/区域 | `crmeb\services\upload\BaseUpload` | [upload/DEVELOPMENT.md](upload/DEVELOPMENT.md) |
|
||
| 邮件 | `driver/email/` | 邮件发送 | `crmeb\services\email\BaseEmail` + `EmailInterface` | [email/DEVELOPMENT.md](email/DEVELOPMENT.md) |
|
||
| 物流查询 | `driver/logistics/` | 快递轨迹查询 | `crmeb\services\logistics\BaseExpress` | [logistics/DEVELOPMENT.md](logistics/DEVELOPMENT.md) |
|
||
| 电子面单 | `driver/express/` | 面单打印 | `crmeb\services\express\BaseExpress` | [express/DEVELOPMENT.md](express/DEVELOPMENT.md) |
|
||
| 发票 | `driver/invoice/` | 发票开具/下载 | `crmeb\services\invoice\BaseInvoice` | [invoice/DEVELOPMENT.md](invoice/DEVELOPMENT.md) |
|
||
| 机器人通知 | `driver/bot/` | 群机器人消息推送 | `crmeb\services\bot\BaseBot` | [bot/DEVELOPMENT.md](bot/DEVELOPMENT.md) |
|
||
| 打印 | `driver/printer/` | 云打印机打印 | `crmeb\services\printer\BasePrinter` | [printer/DEVELOPMENT.md](printer/DEVELOPMENT.md) |
|
||
| 商品采集 | `driver/copyproduct/` | 第三方平台商品采集 | `crmeb\services\copyproduct\CopyProduct` | [copyproduct/DEVELOPMENT.md](copyproduct/DEVELOPMENT.md) |
|
||
|
||
## 三、统一扩展框架
|
||
|
||
### 3.1 驱动注册中心
|
||
|
||
所有驱动类型通过 `crmeb\services\driver\DriverRegistry` 统一注册(收口原先分散在各 Services 中的重复扫描逻辑):
|
||
|
||
```php
|
||
use crmeb\services\driver\DriverRegistry;
|
||
|
||
$drivers = DriverRegistry::scan('sms'); // [driver_key => 驱动信息数组]
|
||
$driver = DriverRegistry::get('sms', 'aliyun'); // 单驱动信息,不存在返回 null
|
||
$class = DriverRegistry::getDriverClass('sms', 'aliyun'); // driver\sms\Aliyun\Driver
|
||
```
|
||
|
||
`scan()` 返回的驱动信息数组包含:`plugin.json` 的 `driver` 子对象全量字段 + 归一化默认值 + `dir_name`(目录名)+ `namespace`(完整命名空间)。同请求内有缓存,不重复扫描磁盘。
|
||
|
||
### 3.2 plugin.json 清单规范
|
||
|
||
每个驱动包必须携带 `plugin.json`(双层结构):
|
||
|
||
```json
|
||
{
|
||
"key": "driver_key",
|
||
"name": "驱动名称",
|
||
"description": "驱动功能简述",
|
||
"version": "1.0.0",
|
||
"author": "CRMEB",
|
||
"type": "driver",
|
||
"path": "driver/{type}/{DriverName}",
|
||
"require": { "crmeb": "^6.0", "php": "^8.0" },
|
||
"driver": {
|
||
"name": "驱动显示名称",
|
||
"driver_key": "driver_key",
|
||
"class_name": "Driver",
|
||
"icon": "icon-xxx",
|
||
"is_builtin": true,
|
||
"sort": 100,
|
||
"capabilities": [],
|
||
"config_fields": [],
|
||
"help_doc": {}
|
||
}
|
||
}
|
||
```
|
||
|
||
- **顶层**:插件元信息,由 Installer 生命周期消费(安装/卸载/升级展示)
|
||
- **`driver` 子对象**:驱动全量描述字段,Registry 全量透传不做白名单;标准键(`name`/`class_name`/`icon`/`capabilities`/`config_fields`/`help_doc`/`sort`/`is_builtin`)缺省时由注册中心补默认值;各类型可有扩展字段(如 pay 的 `channel_key`/`pay_type_map`,upload 的 `domain_template`/`bucket_fields`,copyproduct 的 `platforms`)
|
||
- `key`(顶层)与 `driver_key`(driver 子对象)必须一致
|
||
- 完整字段说明见各类型 DEVELOPMENT.md,通用顶层字段见[短信驱动开发指南](sms/DEVELOPMENT.md#pluginjson-字段说明)
|
||
|
||
### 3.3 Installer 生命周期(可选)
|
||
|
||
驱动包需要执行安装态动作(建表、写入字典/配置)时,在包内提供 `Installer.php`,**原位生效**,系统不复制驱动文件到其他目录。
|
||
|
||
## 四、选型标准
|
||
|
||
新增第三方对接能力时,按以下顺序判定落位:
|
||
|
||
### 判定 1:是否属于既有 10 类驱动类型?
|
||
|
||
**是** → 在对应 `driver/{type}/` 下新增驱动包(如新增聚合支付通道 → `driver/pay/NewPay/`)。
|
||
|
||
判定特征:
|
||
- 能力本质是「换一个服务商做同一件事」(换短信通道、换 OSS 厂商、换支付渠道)
|
||
- 上层业务流程不变,只有通信细节变化
|
||
- 配置形态为「后台可切换 + 动态表单」(`config_fields` 渲染)
|
||
|
||
### 判定 2:不属于既有类型,是纯通信对接吗?
|
||
|
||
**是** → 评估新增驱动类型目录 `driver/{type}/`:
|
||
- 仅当它是对接「外部服务协议」且被多个业务方复用时才值得开新类型(如未来的 OCR、风控)
|
||
- 单一业务专用、无复用场景 → 不开新类型,放到 `crmeb/services/` 对应服务内实现
|
||
|
||
**否(涉及业务规则/流程编排)** → 不放 driver 层,改用业务扩展点:
|
||
|
||
| 需求本质 | 扩展点 | 落位 |
|
||
|---------|--------|------|
|
||
| 卖什么/库存/运费/履约流程 | 产品类型 | `modules/product/{type}/` |
|
||
| 怎么卖/定价/验证/售后规则 | 交易模式 | `modules/trade/{type}/` |
|
||
| 用户从哪来/如何识别登录 | 用户渠道 | `modules/channel/{端}/` |
|
||
| 抽奖/直播/签到等运营活动 | 营销玩法 | `modules/marketing/{type}/` |
|
||
|
||
> 简单判据:**写的是"怎么和第三方说话" → driver 层;写的是"业务上怎么办" → modules 扩展点。**
|
||
|
||
### 判定 3:驱动包内部的取舍
|
||
|
||
- **SDK 体积大**(如 COS/OBS 扩展)→ 放驱动包内 `extend/` 子目录,与主工程隔离
|
||
- **需要安装态动作** → 提供 `Installer.php`(原位生效,禁止文件复制)
|
||
- **配置字段** → 全部声明在 `plugin.json` 的 `config_fields`,后台动态渲染;密钥类字段用 `password` 类型;从其他配置自动读取的字段标记 `readonly`
|
||
- **能力差异化** → 通过 `capabilities` 声明(如短信的 `template`/`record`,机器人的 `send_markdown`/`send_card`),上层按能力降级而非硬编码判断驱动
|
||
|
||
## 五、开发流程
|
||
|
||
1. 在 `driver/{type}/{DriverName}/` 创建目录
|
||
2. 编写 `plugin.json`(先抄同类型现有驱动的结构再改)
|
||
3. 继承对应基类实现 `Driver.php`
|
||
4. 用 `DriverRegistry::scan()` 验证注册
|
||
5. 参考各类型 [DEVELOPMENT.md](#二驱动类型总览) 的接口规范与参考实现
|