文档目录
物流插件开发

简介

本文档面向在 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:应推进完成、允许售后、解锁库存
  • 售后退货物流
    • 调用售后相关接口写入寄回物流,验证 aftersale_return 记录与状态推进
  • 第三方 API 模拟
    • 使用 Mock 或沙箱环境模拟物流查询接口,验证缓存命中与过期行为
添加日期:2026-10-05