16 KiB
16 KiB
USPS 自动集包接口文档
1. 接口概述
本文档提供 USPS 自动集包功能的 API 接口说明,供 WinForm 前端调用。接口采用 RESTful 风格,返回 JSON 格式数据。
2. 接口列表
| 接口名称 | 请求方法 | 接口路径 | 功能描述 |
|---|---|---|---|
| 启动自动集包 | POST | /api/bagtag/auto-pack/start |
启动 USPS 自动集包任务 |
| 查询集包进度 | GET | /api/bagtag/auto-pack/progress/{taskId} |
查询自动集包任务进度 |
| 取消集包任务 | POST | /api/bagtag/auto-pack/cancel/{taskId} |
取消正在执行的集包任务 |
| 获取集包结果 | GET | /api/bagtag/auto-pack/result/{taskId} |
获取自动集包任务结果 |
3. 接口详情
3.1 启动自动集包
请求 URL: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
}
3.2 查询集包进度
请求 URL: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"
}
]
}
}
失败响应:
{
"code": 1002,
"message": "Task not found",
"data": null
}
3.3 取消集包任务
请求 URL:POST /api/bagtag/auto-pack/cancel/{taskId}
路径参数:
| 参数名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| taskId | string | 是 | 任务 ID |
成功响应:
{
"code": 0,
"message": "Task cancelled successfully",
"data": null
}
失败响应:
{
"code": 1002,
"message": "Task not found or already completed",
"data": null
}
3.4 获取集包结果
请求 URL: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"
}
]
}
}
失败响应:
{
"code": 1002,
"message": "Task not found",
"data": null
}
4. 响应状态码
| 状态码 | 说明 |
|---|---|
| 0 | 成功 |
| 1001 | 袋牌不存在或状态不正确 |
| 1002 | 任务不存在或已完成 |
| 1003 | 任务已取消 |
| 1004 | 任务已完成 |
| 1005 | 无符合条件的包裹 |
| 10031 | 袋牌不存在 |
| 10032 | 袋牌未打开 |
| 10033 | 渠道不匹配 |
| 10035 | 运单已关联到其他袋牌 |
| 9999 | 系统错误 |
5. 数据结构
5.1 StartAutoPackResponse
public class StartAutoPackResponse
{
public string TaskId { get; set; } // 任务ID
public string TagNumber { get; set; } // 袋牌号
public string Status { get; set; } // 任务状态
public int TotalCount { get; set; } // 包裹总数
public string Message { get; set; } // 消息
}
5.2 AutoPackProgressResponse
public class AutoPackProgressResponse
{
public string TaskId { get; set; } // 任务ID
public string TagNumber { get; set; } // 袋牌号
public string Status { get; set; } // 任务状态
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 int Progress { get; set; } // 进度百分比
public string Message { get; set; } // 消息
public DateTime StartTime { get; set; } // 开始时间
public DateTime? EstimatedEndTime { get; set; } // 预计完成时间
public List<AutoPackFailedItem> FailedItems { get; set; } // 失败列表
}
5.3 AutoPackResultResponse
public class AutoPackResultResponse
{
public string TaskId { get; set; } // 任务ID
public string TagNumber { get; set; } // 袋牌号
public string Status { get; set; } // 任务状态
public int TotalCount { get; set; } // 包裹总数
public int SuccessCount { get; set; } // 成功数量
public int FailedCount { get; set; } // 失败数量
public DateTime StartTime { get; set; } // 开始时间
public DateTime? EndTime { get; set; } // 结束时间
public int Duration { get; set; } // 持续时间(秒)
public List<AutoPackFailedItem> FailedItems { get; set; } // 失败列表
}
5.4 AutoPackFailedItem
public class AutoPackFailedItem
{
public string WaybillNumber { get; set; } // 运单号
public int ErrorCode { get; set; } // 错误码
public string ErrorMessage { get; set; } // 错误信息
}
6. WinForm 前端调用示例
6.1 启动自动集包
private async Task StartAutoPack()
{
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:5000"); // 替换为实际服务地址
var requestData = new {
tagNumber = "USPS202604031200010001",
creator = "admin"
};
var response = await httpClient.PostAsJsonAsync("/api/bagtag/auto-pack/start", requestData);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<StartAutoPackResponse>>();
if (result.code == 0)
{
// 保存任务ID用于后续查询
_currentTaskId = result.data.TaskId;
_totalPackages = result.data.TotalCount;
// 显示启动成功信息
MessageBox.Show($"自动集包任务已启动,共 {_totalPackages} 个包裹需要处理");
// 开始轮询进度
StartProgressPolling();
}
else
{
MessageBox.Show($"启动失败: {result.message}");
}
}
6.2 轮询进度
private System.Threading.Timer _progressTimer;
private void StartProgressPolling()
{
// 每1秒查询一次进度
_progressTimer = new System.Threading.Timer(async _ => {
await UpdateProgress();
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
}
private async Task UpdateProgress()
{
if (string.IsNullOrEmpty(_currentTaskId)) return;
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:5000");
var response = await httpClient.GetAsync($"/api/bagtag/auto-pack/progress/{_currentTaskId}");
var result = await response.Content.ReadFromJsonAsync<ApiResponse<AutoPackProgressResponse>>();
if (result.code == 0)
{
var progress = result.data;
// 更新UI(需要Invoke到UI线程)
this.Invoke((MethodInvoker)delegate {
// 更新进度条
progressBar.Value = progress.Progress;
// 更新状态标签
lblStatus.Text = $"状态: {progress.Status}";
lblProgress.Text = $"进度: {progress.ProcessedCount}/{progress.TotalCount} ({progress.Progress}%)";
lblSuccess.Text = $"成功: {progress.SuccessCount}";
lblFailed.Text = $"失败: {progress.FailedCount}";
lblCurrent.Text = $"当前: {progress.CurrentWaybill}";
// 更新预计完成时间
if (progress.EstimatedEndTime.HasValue)
{
lblEstimated.Text = $"预计完成: {progress.EstimatedEndTime.Value.ToString("yyyy-MM-dd HH:mm:ss")}";
}
// 检查任务是否完成
if (progress.Status == "completed" || progress.Status == "failed" || progress.Status == "cancelled")
{
_progressTimer?.Change(Timeout.Infinite, Timeout.Infinite);
MessageBox.Show($"任务已{progress.Status}!\n成功: {progress.SuccessCount}, 失败: {progress.FailedCount}");
}
});
}
}
6.3 取消任务
private async Task CancelTask()
{
if (string.IsNullOrEmpty(_currentTaskId)) return;
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:5000");
var response = await httpClient.PostAsync($"/api/bagtag/auto-pack/cancel/{_currentTaskId}", null);
var result = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
if (result.code == 0)
{
_progressTimer?.Change(Timeout.Infinite, Timeout.Infinite);
MessageBox.Show("任务已取消");
}
else
{
MessageBox.Show($"取消失败: {result.message}");
}
}
6.4 获取最终结果
private async Task GetTaskResult()
{
if (string.IsNullOrEmpty(_currentTaskId)) return;
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:5000");
var response = await httpClient.GetAsync($"/api/bagtag/auto-pack/result/{_currentTaskId}");
var result = await response.Content.ReadFromJsonAsync<ApiResponse<AutoPackResultResponse>>();
if (result.code == 0)
{
var taskResult = result.data;
// 显示结果
var message = $"任务结果:\n"
+ $"状态: {taskResult.Status}\n"
+ $"总数: {taskResult.TotalCount}\n"
+ $"成功: {taskResult.SuccessCount}\n"
+ $"失败: {taskResult.FailedCount}\n"
+ $"耗时: {taskResult.Duration} 秒\n\n";
if (taskResult.FailedItems != null && taskResult.FailedItems.Count > 0)
{
message += "失败明细:\n";
foreach (var item in taskResult.FailedItems)
{
message += $"- {item.WaybillNumber}: {item.ErrorMessage}\n";
}
}
MessageBox.Show(message, "任务结果");
}
else
{
MessageBox.Show($"获取结果失败: {result.message}");
}
}
7. 辅助类定义
// API 响应通用结构
public class ApiResponse<T>
{
public int code { get; set; }
public string message { get; set; }
public T data { get; set; }
}
// 启动请求
public class StartAutoPackRequest
{
public string TagNumber { get; set; }
public string Creator { get; set; } = "system";
}
// 响应模型(与后端一致)
public class StartAutoPackResponse
{
public string TaskId { get; set; }
public string TagNumber { get; set; }
public string Status { get; set; }
public int TotalCount { get; set; }
public string Message { get; set; }
}
public class AutoPackProgressResponse
{
public string TaskId { get; set; }
public string TagNumber { get; set; }
public string Status { get; set; }
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 int Progress { get; set; }
public string Message { get; set; }
public DateTime StartTime { get; set; }
public DateTime? EstimatedEndTime { get; set; }
public List<AutoPackFailedItem> FailedItems { get; set; }
}
public class AutoPackResultResponse
{
public string TaskId { get; set; }
public string TagNumber { get; set; }
public string Status { get; set; }
public int TotalCount { get; set; }
public int SuccessCount { get; set; }
public int FailedCount { get; set; }
public DateTime StartTime { get; set; }
public DateTime? EndTime { get; set; }
public int Duration { get; set; }
public List<AutoPackFailedItem> FailedItems { get; set; }
}
public class AutoPackFailedItem
{
public string WaybillNumber { get; set; }
public int ErrorCode { get; set; }
public string ErrorMessage { get; set; }
}
8. 注意事项
- 袋牌状态:只有状态为 "Opened" 的袋牌才能启动自动集包
- 筛选条件:系统会自动筛选 84 小时内换单成功的 USPS 包裹
- 处理速度:每单处理间隔 2-4 秒随机延迟,模拟人工操作
- 任务缓存:任务状态缓存 60 分钟,超时后无法查询
- 并发控制:目前支持多个任务同时运行
- 网络超时:建议设置合理的网络超时时间(如 30 秒)
- 错误处理:妥善处理 API 调用中的异常情况
- UI 响应:使用异步操作避免 UI 卡顿
9. 调试建议
- 服务地址:确保后端服务正常运行,地址配置正确
- 袋牌准备:测试前准备好状态为 "Opened" 的 USPS 袋牌
- 数据准备:确保有符合条件的 USPS 包裹数据
- 日志查看:后端服务日志可用于排查问题
- 状态监控:通过轮询接口实时监控任务进度
10. 常见问题
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 启动失败:Bag tag not found or not in opened status | 袋牌不存在或状态不是 Opened | 检查袋牌号是否正确,确保袋牌已打开 |
| 启动失败:No eligible waybills found | 无符合条件的包裹 | 检查是否有 84 小时内换单成功的 USPS 包裹 |
| 任务状态为 failed | 系统异常 | 查看后端日志,检查具体错误原因 |
| 进度查询返回 404 | 任务 ID 错误或任务已过期 | 检查任务 ID 是否正确,任务是否在 60 分钟内 |
| 取消任务失败 | 任务已完成或不存在 | 确认任务状态和 ID |
11. 版本信息
| 版本 | 日期 | 说明 |
|---|---|---|
| 1.0 | 2026-04-03 | 初始版本 |
注:本文档基于后端 API 实现,如有接口变更请同步更新。