13 KiB
13 KiB
USPS 自动集包接口规格说明
1. 需求概述
根据 USPS自动集包需求文档,实现 USPS 尾程包裹的自动集包功能。当用户在系统中创建 USPS 袋牌后,系统自动筛选符合条件的包裹并逐个关联到该袋牌。
核心特点:
- 前端使用 WinForm,需要实时反馈关联进度
- 采用异步任务 + 进度查询的设计模式
- 每次关联间隔 2-4 秒随机延迟,模拟人工操作
2. 接口设计
2.1 启动自动集包任务
接口路径:POST /api/bagtag/auto-pack/start
请求参数:
| 参数名 | 类型 | 必选 | 描述 |
|---|---|---|---|
| tagNumber | string | 是 | USPS 袋牌号 |
| creator | string | 否 | 操作人,默认为 "system" |
请求示例:
{
"tagNumber": "USPS202604031200010001",
"creator": "admin"
}
响应结构:
{
"code": 0,
"message": "success",
"data": {
"taskId": "task_20260403120001_abc123",
"tagNumber": "USPS202604031200010001",
"status": "processing",
"totalCount": 50,
"message": "自动集包任务已启动"
}
}
错误响应:
{
"code": 1001,
"message": "Bag tag not found or not in opened status",
"data": null
}
2.2 查询自动集包进度
接口路径:GET /api/bagtag/auto-pack/progress/{taskId}
路径参数:
| 参数名 | 类型 | 必选 | 描述 |
|---|---|---|---|
| taskId | string | 是 | 任务ID |
响应结构:
{
"code": 0,
"message": "success",
"data": {
"taskId": "task_20260403120001_abc123",
"tagNumber": "USPS202604031200010001",
"status": "processing",
"totalCount": 50,
"processedCount": 25,
"successCount": 24,
"failedCount": 1,
"currentWaybill": "9201234567890123456789",
"progress": 50,
"message": "正在处理第 25/50 个包裹",
"startTime": "2026-04-03T12:00:01Z",
"estimatedEndTime": "2026-04-03T12:03:30Z",
"failedItems": [
{
"waybillNumber": "9201234567890123456788",
"errorCode": 10035,
"errorMessage": "Waybill is already associated with another bag tag"
}
]
}
}
状态说明:
| 状态值 | 说明 |
|---|---|
| pending | 等待处理 |
| processing | 处理中 |
| completed | 已完成 |
| failed | 失败/异常终止 |
| cancelled | 已取消 |
2.3 取消自动集包任务
接口路径:POST /api/bagtag/auto-pack/cancel/{taskId}
路径参数:
| 参数名 | 类型 | 必选 | 描述 |
|---|---|---|---|
| taskId | string | 是 | 任务ID |
响应结构:
{
"code": 0,
"message": "Task cancelled successfully",
"data": {
"taskId": "task_20260403120001_abc123",
"status": "cancelled",
"processedCount": 25,
"successCount": 24,
"failedCount": 1
}
}
2.4 获取任务结果
接口路径:GET /api/bagtag/auto-pack/result/{taskId}
路径参数:
| 参数名 | 类型 | 必选 | 描述 |
|---|---|---|---|
| taskId | string | 是 | 任务ID |
响应结构(任务完成后):
{
"code": 0,
"message": "success",
"data": {
"taskId": "task_20260403120001_abc123",
"tagNumber": "USPS202604031200010001",
"status": "completed",
"totalCount": 50,
"successCount": 48,
"failedCount": 2,
"startTime": "2026-04-03T12:00:01Z",
"endTime": "2026-04-03T12:03:45Z",
"duration": 224,
"failedItems": [
{
"waybillNumber": "9201234567890123456788",
"errorCode": 10035,
"errorMessage": "Waybill is already associated with another bag tag"
},
{
"waybillNumber": "9201234567890123456787",
"errorCode": 10033,
"errorMessage": "Channel does not match"
}
]
}
}
3. 业务逻辑
3.1 筛选条件
系统仅自动关联同时满足以下条件的包裹:
-
尾程渠道为 USPS
- 通过运单号识别渠道(92/93/94/95 开头,或 420+邮编+92/93/94/95)
-
当前尚未被集包
- 未关联到任何袋牌(bag_tag_waybills 表中不存在)
-
已完成换单
- label_replace_requests 表中存在对应记录
- 换单状态为 "Y"(正常换单)
-
换单时间在前 3 天内(84小时)
- 以创建袋牌时间为基准
- CreatedAt >= 当前时间 - 84小时
3.2 处理流程
┌─────────────────────────────────────────────────────────────┐
│ 1. 接收启动请求 (POST /api/bagtag/auto-pack/start) │
│ - 验证袋牌存在且状态为 Opened │
│ - 生成唯一任务ID │
│ - 查询符合条件的包裹总数 │
│ - 返回任务ID给前端 │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 异步执行自动集包任务 │
│ - 创建后台任务 │
│ - 逐个关联包裹 │
│ - 每单间隔 2-4 秒随机延迟 │
│ - 实时更新任务进度状态 │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. 前端轮询进度 (GET /api/bagtag/auto-pack/progress/{id}) │
│ - 建议轮询间隔:1-2 秒 │
│ - 显示进度条、当前处理单号、成功/失败数量 │
│ - 可实时取消任务 │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. 任务完成 │
│ - 返回最终结果 │
│ - 记录操作日志 │
└─────────────────────────────────────────────────────────────┘
3.3 关联处理逻辑
对于每个符合条件的包裹:
- 调用
AssociateWaybillAsync方法进行关联 - 记录关联结果(成功/失败)
- 更新任务进度状态
- 生成 2-4 秒随机延迟
- 处理下一个包裹
3.4 任务状态管理
任务状态存储在内存缓存中(ICacheService),包含:
public class AutoPackTaskStatus
{
public string TaskId { get; set; }
public string TagNumber { get; set; }
public string Status { get; set; } // pending/processing/completed/failed/cancelled
public int TotalCount { get; set; }
public int ProcessedCount { get; set; }
public int SuccessCount { get; set; }
public int FailedCount { get; set; }
public string CurrentWaybill { get; set; }
public DateTime StartTime { get; set; }
public DateTime? EndTime { get; set; }
public List<FailedItem> FailedItems { get; set; }
public CancellationTokenSource CancellationTokenSource { get; set; }
}
4. 技术实现
4.1 新增模型
AutoPackTaskStatus(任务状态模型)
- 位置:
MDL/Models/AutoPackTaskStatus.cs
AutoPackRequest(启动请求模型)
- 位置:
MDL/Models/BagTagRequest.cs(扩展)
4.2 接口定义
IBagTagService 扩展:
// 启动自动集包任务
Task<AutoPackTaskResult> StartAutoPackAsync(string tagNumber, string creator);
// 查询任务进度
Task<AutoPackTaskStatus> GetAutoPackProgressAsync(string taskId);
// 取消任务
Task<bool> CancelAutoPackAsync(string taskId);
// 获取任务结果
Task<AutoPackTaskStatus> GetAutoPackResultAsync(string taskId);
IBagTagRepository 扩展:
// 查询符合条件的 USPS 包裹
Task<List<string>> GetEligibleUspsWaybillsAsync(string tagNumber, DateTime cutoffTime);
// 获取符合条件的包裹数量
Task<int> GetEligibleUspsWaybillCountAsync(string tagNumber, DateTime cutoffTime);
4.3 控制器实现
在 BagTagController 中添加:
POST /api/bagtag/auto-pack/startGET /api/bagtag/auto-pack/progress/{taskId}POST /api/bagtag/auto-pack/cancel/{taskId}GET /api/bagtag/auto-pack/result/{taskId}
5. 数据库查询
5.1 查询符合条件的包裹
SELECT DISTINCT
l.FinalMileTrackingNumber
FROM label_replace_requests l
LEFT JOIN bag_tag_waybills b ON l.FinalMileTrackingNumber = b.FinalMileTrackingNumber
WHERE
l.ReplaceStatus = 'Y'
AND l.CreatedAt >= @CutoffTime
AND l.FinalMileTrackingNumber IS NOT NULL
AND l.FinalMileTrackingNumber != ''
AND b.Id IS NULL -- 未关联到任何袋牌
AND (
-- USPS 运单号格式:92/93/94/95 开头
l.FinalMileTrackingNumber REGEXP '^(92|93|94|95)'
-- 或 420+邮编+92/93/94/95 开头
OR l.FinalMileTrackingNumber REGEXP '^420[0-9]{5}(92|93|94|95)'
OR l.FinalMileTrackingNumber REGEXP '^420[0-9]{9}(92|93|94|95)'
)
ORDER BY l.CreatedAt ASC
6. 错误码定义
| 错误码 | 说明 |
|---|---|
| 0 | 成功 |
| 1001 | 袋牌不存在或状态不正确 |
| 1002 | 任务不存在 |
| 1003 | 任务已取消 |
| 1004 | 任务已完成 |
| 1005 | 无符合条件的包裹 |
| 10031 | 袋牌不存在 |
| 10032 | 袋牌未打开 |
| 10033 | 渠道不匹配 |
| 10035 | 运单已关联到其他袋牌 |
| 9999 | 系统错误 |
7. 前端集成建议
7.1 WinForm 调用流程
// 1. 启动自动集包
var response = await httpClient.PostAsJsonAsync("/api/bagtag/auto-pack/start",
new { tagNumber = "USPS202604031200010001", creator = "admin" });
var result = await response.Content.ReadFromJsonAsync<ApiResponse<AutoPackResult>>();
var taskId = result.Data.TaskId;
// 2. 轮询进度
timer = new Timer(async _ => {
var progressResponse = await httpClient.GetAsync($"/api/bagtag/auto-pack/progress/{taskId}");
var progress = await progressResponse.Content.ReadFromJsonAsync<ApiResponse<AutoPackProgress>>();
// 更新UI:进度条、当前单号、成功/失败数
UpdateUI(progress.Data);
if (progress.Data.Status == "completed" || progress.Data.Status == "failed")
{
timer.Stop();
ShowResult(progress.Data);
}
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
// 3. 取消任务(用户点击取消按钮)
await httpClient.PostAsync($"/api/bagtag/auto-pack/cancel/{taskId}", null);
7.2 UI 展示建议
- 进度条:显示
processedCount / totalCount百分比 - 当前处理:显示
currentWaybill单号 - 统计信息:成功数、失败数、剩余数
- 预计完成时间:根据当前速度计算
- 失败列表: expandable 面板显示失败明细
8. 性能考虑
- 异步处理:使用
Task.Run或后台服务执行集包任务 - 缓存进度:使用
ICacheService存储任务状态,避免数据库压力 - 批量查询:一次性查询所有符合条件的包裹,避免多次数据库访问
- 延迟控制:2-4 秒随机延迟,避免对系统造成过大压力
9. 安全考虑
- 袋牌状态验证:确保袋牌处于 Opened 状态才能自动集包
- 渠道匹配:自动识别运单号渠道,确保与袋牌渠道一致
- 重复关联检查:避免将已关联的包裹再次关联
- 任务超时:设置任务最大执行时间(如 30 分钟),超时自动终止
10. 日志记录
每个包裹关联操作记录订单日志:
await _orderLogService.RecordOrderLogAsync(
neutralWaybillNumber: string.Empty,
finalMileTrackingNumber: waybillNumber,
operationType: OrderLogOperationType.PACK,
operationResult: success ? OrderLogOperationResult.SUCCESS : OrderLogOperationResult.FAILED,
operationDescription: $"自动集包到袋牌 {tagNumber}: {(success ? "成功" : errorMessage)}",
@operator: "system_auto"
);