Express 域覆盖物流轨迹查询、寄件下单、电子面单、运费预估等通用场景,外加顺丰函证通的法律函件寄递能力。和别的域不同,这里有两个接口,分别服务两套业务语义。本页解释双接口的职责边界与关键流程;精确的方法签名和 DTO 见 Express 抽象参考。
域结构
Bitzsoft.Integrations.Express ← 抽象层(双接口 + 异常 + 模型 + 枚举)├── Bitzsoft.Integrations.Express.Cainiao ← 菜鸟├── Bitzsoft.Integrations.Express.ChinaPost ← 中国邮政├── Bitzsoft.Integrations.Express.Kdniao ← 快递鸟├── Bitzsoft.Integrations.Express.Kuaidi100 ← 快递100├── Bitzsoft.Integrations.Express.SfLegal ← 顺丰函证通(法律函件)├── Bitzsoft.Integrations.Express.ShowApi ← 聚合数据 ShowApi├── Bitzsoft.Integrations.Express.Zto ← 中通└── Bitzsoft.Integrations.Express.All ← 聚合包为什么是两个接口
快递域里有两类业务,操作对象和语义完全不同:
- 通用物流——运单轨迹查询、寄件下单、电子面单、运费时效。菜鸟、快递鸟、快递100 这些聚合平台或单一快递公司都做这件事,操作对象是”运单”。
- 法律函件寄递——顺丰函证通面向律所、会计师事务所,提供法律函件的创建、寄递、签收、归档全生命周期管理。操作对象是”法律函件”,有签收确认和全流程记录这类法律语义。
如果把两者塞进一个 IExpressProvider,调用方会面对一堆对它毫无意义的方法——查物流的用不到”确认签收函件”,寄函件的用不到”识别快递公司”。强行统一只会稀释接口的语义清晰度。
所以这里拆成两个接口:通用物流走 IExpressProvider,法律函件走 ISfLegalDocumentProvider。顺丰函证通只实现后者,其余 6 家只实现前者,互不污染。两者的供应商集合完全不重叠——顺丰函证通的 Descriptor limitations 明确记录:“仅提供法律函件寄递生命周期,不注册通用 IExpressProvider。“
IExpressProvider:通用物流
8 个方法覆盖四类操作:
public interface IExpressProvider{ string ProviderName { get; }
// ① 轨迹查询(实时 / 批量 / 订阅推送) Task<ExpressTrackingInfo> TrackAsync(string trackingNumber, string? carrierCode = null, string? phoneSuffix = null, CancellationToken ct = default); Task<IReadOnlyList<ExpressTrackingInfo>> BatchTrackAsync(IEnumerable<string> trackingNumbers, string? carrierCode = null, CancellationToken ct = default); Task<ExpressSubscriptionResult> SubscribeAsync(string trackingNumber, string callbackUrl, string? carrierCode = null, CancellationToken ct = default);
// ② 寄件下单 Task<ExpressShipmentResult> CreateShipmentAsync(ExpressShipmentOrder order, CancellationToken ct = default); Task CancelShipmentAsync(string orderId, CancellationToken ct = default);
// ③ 运费与面单 Task<IReadOnlyList<ExpressQuoteResult>> GetQuoteAsync(ExpressQuoteRequest request, CancellationToken ct = default); Task<ExpressWaybillResult> GenerateWaybillAsync(ExpressShipmentOrder order, CancellationToken ct = default);
// ④ 快递公司识别 Task<ExpressCarrierInfo> DetectCarrierAsync(string trackingNumber, CancellationToken ct = default);}承运商识别
各家快递公司用不同的编码体系(快递鸟的 YTO、快递100 的 yuantong)。Express 域用统一的 ExpressCarrierCode 枚举(SF / YTO / ZTO / STO / YD / HTKY / JTSD / EMS / ChinaPost / JD / DBL / HHTT / ZJS / Cainiao)作为映射目标,各 Provider 内部维护平台编码到此枚举的映射。
DetectCarrierAsync 根据运单号自动识别快递公司,返回 ExpressCarrierInfo:
| 字段 | 说明 |
|---|---|
CarrierCode | 供应商侧编码 |
CarrierName | 快递公司名称 |
UnifiedCode | 统一枚举 ExpressCarrierCode |
Confidence | 匹配置信度(0-1) |
TrackAsync 的 carrierCode 参数可选——部分供应商支持仅凭单号自动识别承运商。
轨迹订阅模型
实时查询(TrackAsync / BatchTrackAsync)每次调用都向供应商拉取一次。当需要持续跟踪时,用 SubscribeAsync 注册订阅:供应商在运单状态变化时回调 callbackUrl,应用被动接收推送,无需轮询。
ExpressSubscriptionResult 含 Success、SubscriptionId(供应商返回)、失败时的 FailureReason。订阅成功后,应用需在 callbackUrl 实现 HTTP 接收端点解析供应商推送的轨迹数据。
电子面单
GenerateWaybillAsync 接收 ExpressShipmentOrder(与寄件下单同结构),返回 ExpressWaybillResult,含面单的 HTML 内容(HtmlContent)或 Base64 编码的 PDF(PdfBase64),以及打印尺寸描述(PrintSize)。
ISfLegalDocumentProvider:法律函件
顺丰函证通的法律函件寄递有独特语义——它不只是”寄个快递”,而是管理一份法律函件从创建到归档的完整记录:
public interface ISfLegalDocumentProvider{ string ProviderName { get; }
// ① 创建法律函件寄递任务 Task<LegalDocumentShipmentResult> CreateDocumentShipmentAsync(LegalDocumentShipmentRequest request, CancellationToken ct = default); // ② 查询寄递状态 Task<LegalDocumentTrackingResult> TrackDocumentAsync(string documentId, CancellationToken ct = default); // ③ 确认签收(法律函件特有的回执环节) Task ConfirmReceiptAsync(string documentId, CancellationToken ct = default); // ④ 全流程记录(含创建、寄递、签收、归档) Task<LegalDocumentFullRecord> GetFullRecordAsync(string documentId, CancellationToken ct = default);}法律函件生命周期
LegalDocumentShipmentRequest 相比通用寄件多了法律语义字段:
| 字段 | 说明 |
|---|---|
DocumentType | 函件类型(律师函、催收函、仲裁通知书等,必填) |
Title | 函件标题 |
CaseNumber | 关联案件编号 |
Sender / Receiver | 寄件人/收件人(ExpressContact) |
LegalDocumentFullRecord 是完整档案,含创建时间(CreatedTime)、寄出时间(SentTime)、签收时间(SignedTime)、签收人(SignedBy)、物流轨迹(Traces)——这是法律函件区别于普通快递的核心价值:可追溯的全流程证据链。
ConfirmReceiptAsync 是法律函件特有的”确认签收”环节,对应 LegalDocumentStatus.Signed 状态。状态枚举覆盖 Created / Pending / Sent / InTransit / Signed / Rejected / Returned / Exception 八种。
错误处理:ExpressException
失败时抛 ExpressException,沿用其他域的异常封装风格:
public sealed class ExpressException : IntegrationException{ public string ProviderName => Provider ?? string.Empty; public new string? ErrorCode => base.ErrorCode; public new string? ProviderMessage => base.ProviderMessage;
public ExpressException( string providerName, string message, string? errorCode = null, string? providerMessage = null, Exception? innerException = null) : base(domain: "Express", message: $"[{providerName}] {message}", ...);}消息格式 [ProviderName] message。Express 域用异常风格,不用 Result 包装。这与 TeamWork 一致,但与新域(EnterpriseInfo / ElectronicSignature 的 Result 模式)不同。
厂商清单
| 厂商 | Provider ID | 实现接口 | 能力定位 |
|---|---|---|---|
| 快递鸟 Kdniao | kdniao | IExpressProvider | 轨迹 + 寄件 + 面单 |
| 快递100 Kuaidi100 | kuaidi100 | IExpressProvider | 轨迹 + 订阅推送 |
| 菜鸟 Cainiao | cainiao | IExpressProvider | 轨迹 + 电子面单 |
| 中国邮政 ChinaPost | chinapost | IExpressProvider | 邮政轨迹与寄件 |
| 中通 Zto | zto | IExpressProvider | 中通轨迹与寄件 |
| 聚合数据 ShowApi | showapi | IExpressProvider | 聚合轨迹查询 |
| 顺丰函证通 SfLegal | sflegal | ISfLegalDocumentProvider | 法律函件全生命周期 |
两个接口的供应商集合完全不重叠。
注册模式
单厂商
// ① 通用物流——快递鸟builder.Services.AddBitzsoftKdniaoExpress(builder.Configuration.GetSection("Express:Kdniao"));
// ② 法律函件——顺丰函证通builder.Services.AddBitzsoftSfLegal(builder.Configuration.GetSection("Express:SfLegal"));两个接口互不依赖,可以只注册通用物流,也可以只注册函件服务。
多厂商聚合
builder.Services.AddBitzsoftExpressAll(builder.Configuration, "Express");All 聚合包按配置节存在性按需注册:
{ "Express": { "Kdniao": { "EBusinessID": "...", "AppKey": "..." }, "Kuaidi100": { "Customer": "...", "Key": "..." }, "SfLegal": { "PartnerId": "...", "Checkword": "..." } }}消费
通用物流
public class TrackingService(IExpressProvider express){ // 实时查轨迹 public async Task<ExpressTrackingInfo> TrackAsync(string trackingNumber) => await express.TrackAsync(trackingNumber);
// 寄件下单 public async Task<ExpressShipmentResult> ShipAsync(ExpressShipmentOrder order) => await express.CreateShipmentAsync(order);
// 订阅轨迹推送(被动接收,无需轮询) public async Task SubscribeAsync(string trackingNumber, string callbackUrl) => await express.SubscribeAsync(trackingNumber, callbackUrl);}法律函件
public class LegalDocumentService(ISfLegalDocumentProvider sfLegal){ public async Task<string> DispatchAsync(LegalDocumentShipmentRequest request) { var result = await sfLegal.CreateDocumentShipmentAsync(request); return result.DocumentId; // 函件 ID,后续用于查状态和签收 }
public async Task ConfirmAsync(string documentId) { await sfLegal.ConfirmReceiptAsync(documentId); // 法律函件特有的签收确认 }
public async Task<LegalDocumentFullRecord> ArchiveAsync(string documentId) { // 全流程记录作为法律证据归档 return await sfLegal.GetFullRecordAsync(documentId); }}多厂商场景分别用 IIntegrationProviderResolver<IExpressProvider> 和 IIntegrationProviderResolver<ISfLegalDocumentProvider> 按 Provider ID 解析。
相关
- Express 抽象参考:双接口全部方法签名与 DTO 字段表
- 三层包结构
- 结构变体
- 错误处理约定