Files
LabelChange-server/USPS自动集包接口文档.md
2026-06-01 16:30:29 +08:00

16 KiB
Raw Blame History

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 启动自动集包

请求 URLPOST /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 查询集包进度

请求 URLGET /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 取消集包任务

请求 URLPOST /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 获取集包结果

请求 URLGET /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. 注意事项

  1. 袋牌状态:只有状态为 "Opened" 的袋牌才能启动自动集包
  2. 筛选条件:系统会自动筛选 84 小时内换单成功的 USPS 包裹
  3. 处理速度:每单处理间隔 2-4 秒随机延迟,模拟人工操作
  4. 任务缓存:任务状态缓存 60 分钟,超时后无法查询
  5. 并发控制:目前支持多个任务同时运行
  6. 网络超时:建议设置合理的网络超时时间(如 30 秒)
  7. 错误处理:妥善处理 API 调用中的异常情况
  8. UI 响应:使用异步操作避免 UI 卡顿

9. 调试建议

  1. 服务地址:确保后端服务正常运行,地址配置正确
  2. 袋牌准备:测试前准备好状态为 "Opened" 的 USPS 袋牌
  3. 数据准备:确保有符合条件的 USPS 包裹数据
  4. 日志查看:后端服务日志可用于排查问题
  5. 状态监控:通过轮询接口实时监控任务进度

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 实现,如有接口变更请同步更新。