简介
本文档面向在 DouPHP 框架中开发“物流插件”的工程师,围绕以下目标展开:
- 说明物流查询接口的实现方法与数据流转
- 讲解快递单号跟踪、发货状态推进机制
- 以 EMS、快递配送等现有插件为例,演示如何集成第三方物流服务
- 覆盖 API 调用、数据格式转换、错误处理、缓存策略等关键技术点
- 提供可复用的插件开发模板与测试方法
项目结构
DouPHP 将“物流能力”以插件形式组织,位于 plugin 目录下。每个物流插件包含:
- Provider:对外暴露插件元信息与可用方法
- Service:业务逻辑(如运费计算、方法列表)
- manifest.php:插件注册信息(插件组、Provider 类名)
同时,订单模块提供了统一的“发货/更新物流”入口与校验规则,以及售后退货物流记录能力。
graph TB
subgraph "插件层"
EMS["EMS 插件<br/>EmsProvider / EmsService"]
EXP["快递配送插件<br/>ExpressProvider / ExpressService"]
end
subgraph "订单模块"
WC["工作区控制器<br/>WorkController::tracking"]
WOS["工作台服务<br/>WorkOrderService::updateWorkTracking"]
REQ["请求校验<br/>OrderTrackingFormRequest"]
end
subgraph "售后模块"
AS["售后退货物流<br/>AftersaleService"]
end
WC --> WOS
WOS --> DB[("订单表 order")]
AS --> DB
EMS --> WC
EXP --> WC
核心组件
- 物流插件接口与实现
- Provider:声明插件 ID、名称、描述、配置项与方法列表
- Service:读取插件配置并返回可用的配送方式(含费用、描述)
- 订单发货/物流更新
- 控制器接收 shipping_id、tracking_no 并调用服务更新
- 服务写入订单表并处理首次发货的状态推进与库存解锁
- 售后退货物流
- 记录寄回物流公司、运单号、发货时间,并推进售后状态
架构总览
下图展示了从后台或工作台发起“发货/更新物流”到订单持久化与状态推进的完整流程,并体现物流插件如何参与配送方式选择。
sequenceDiagram
participant Admin as "后台/工作台"
participant WC as "WorkController"
participant WOS as "WorkOrderService"
participant DB as "数据库(order)"
participant Plugin as "物流插件(EMS/快递)"
Admin->>WC : POST tracking(order_id, shipping_id, tracking_no)
WC->>WOS : updateWorkTracking(...)
WOS->>DB : 写入 shipping_id/tracking_no/shipped_at
alt 首次发货且无运单号
WOS->>DB : 标记完成、允许售后、解锁库存
else 有运单号
WOS-->>Admin : 仅更新物流信息
end
Note over WC,Plugin : 配送方式由物流插件 methods() 提供
详细组件分析
物流插件:EMS
- 职责
- 声明插件 ID、名称、描述、配置项(运费、包邮门槛)
- 通过 Service 返回可用配送方式(id/name/price/desc)
- 关键点
- 使用 plugin() 获取已启用插件及其配置
- 价格来源于配置字段 fee,未配置时默认 0
classDiagram
class EmsProvider {
+pluginId() string
+meta() array
+methods() array
}
class EmsService {
+methods() array
}
EmsProvider --> EmsService : "委托 methods()"
物流插件:快递配送
- 职责
- 与 EMS 类似,提供“快递配送”方式的元信息与费用
- 关键点
- 同样通过 plugin() 读取配置,支持 free 包邮阈值
订单发货与物流更新
- 入口
- 后台/工作台调用 WorkController::tracking,参数包括 order_id、shipping_id、tracking_no
- 处理逻辑
- 若已有运单号:仅更新物流三字段(物流公司、运单号、发货时间)
- 若无运单号:视为直接确认收货,推进订单完成、允许售后、解锁库存
- 输入校验
- OrderTrackingFormRequest 对 order_id、shipping_id、tracking_no 进行长度与类型校验
flowchart TD
Start(["开始"]) --> Validate["校验参数<br/>order_id/shipping_id/tracking_no"]
Validate --> CheckTrack{"是否已有运单号?"}
CheckTrack -- 是 --> UpdateOnly["更新物流公司/运单号/发货时间"]
CheckTrack -- 否 --> Complete["标记完成/允许售后/解锁库存"]
UpdateOnly --> End(["结束"])
Complete --> End
售后退货物流记录
- 功能
- 记录买家寄回的物流公司、运单号、发货时间
- 推进售后状态为“已寄回”,并附加备注
- 展示
- 后台售后详情页渲染 return_info 中的公司、单号、时间
后台订单页与物流表单
- 页面元素
- 下拉选择物流公司(来自插件提供的配送方式)
- 输入框填写运单号
- 提交后调用后台接口更新物流
- 交互
- 若已有运单号,显示取消按钮;否则显示提交按钮
依赖关系分析
- 插件与订单模块解耦
- 订单模块不关心具体物流商实现,只依赖“配送方式列表”和“发货/更新物流”的统一接口
- 插件内部依赖
- Provider 依赖 Service 获取方法列表
- Service 通过 plugin() 读取运行时配置(费用、包邮门槛)
- 数据流向
- 控制器 -> 服务 -> 数据库
- 售后模块独立维护退货物流信息
graph LR
WC["WorkController"] --> WOS["WorkOrderService"]
WOS --> DB[("order 表")]
Plugin["物流插件(EMS/快递)"] --> WC
Plugin --> DB
AS["AftersaleService"] --> DB
性能与缓存策略
- 插件配置读取
- Service 通过 plugin() 获取配置,建议在高频场景下结合系统缓存减少重复 IO
- 物流查询接口
- 建议对第三方物流查询结果按“运单号+物流公司”做短期缓存,避免频繁拉取
- 并发与幂等
- 发货/更新物流接口需保证幂等:同一运单号多次提交应产生一致结果
- 超时与重试
- 对第三方 API 调用设置合理超时与重试退避,失败时降级为本地记录
故障排查指南
- 参数校验失败
- 检查 order_id、shipping_id、tracking_no 是否符合规则(类型、长度)
- 状态推进异常
- 若首次发货且无运单号,会推进完成并解锁库存;请核对订单当前状态与模块类型
- 售后退货物流未生效
- 确认 AftersaleService 是否正确写入 aftersale_return 并推进售后状态
- 第三方 API 异常
- 增加日志记录请求与响应,区分网络错误、签名错误、业务拒绝等
结论
- DouPHP 通过“插件 + 统一接口”的方式实现了物流能力的可扩展性
- 订单发货/更新物流流程清晰,支持“仅更新物流”和“直接完成”两种路径
- 售后退货物流独立记录,便于追踪逆向物流
- 基于现有 EMS、快递配送插件,可快速扩展更多第三方物流服务商
附录:开发模板与测试方法
新增物流插件步骤
- 创建插件目录与文件
- manifest.php:声明插件组与 Provider 类
- XxxProvider.php:实现 ShippingPluginProviderInterface,提供 pluginId、meta、methods
- XxxService.php:读取插件配置,返回配送方式列表(id/name/price/desc)
- 接入订单发货
- 确保插件 methods() 返回的 id 与后端 shipping_id 对应
- 后台/工作台调用 WorkController::tracking 即可更新物流
- 可选:对接第三方物流查询
- 在 Service 中封装查询方法,按“运单号+物流公司”查询轨迹
- 将轨迹结果缓存并按需刷新
测试方法
- 单元测试
- 验证 Provider.meta() 与 methods() 返回值结构正确
- 验证 Service 读取配置后的 price/name 符合预期
- 集成测试
- 调用 WorkController::tracking,传入不同组合参数:
- 仅有 shipping_id:应仅更新物流公司
- shipping_id + tracking_no:更新物流公司、运单号、发货时间
- 无 tracking_no:应推进完成、允许售后、解锁库存
- 调用 WorkController::tracking,传入不同组合参数:
- 售后退货物流
- 调用售后相关接口写入寄回物流,验证 aftersale_return 记录与状态推进
- 第三方 API 模拟
- 使用 Mock 或沙箱环境模拟物流查询接口,验证缓存命中与过期行为