Files
2026-06-01 16:30:29 +08:00

13 KiB
Raw Permalink Blame History

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 筛选条件

系统仅自动关联同时满足以下条件的包裹:

  1. 尾程渠道为 USPS

    • 通过运单号识别渠道92/93/94/95 开头,或 420+邮编+92/93/94/95
  2. 当前尚未被集包

    • 未关联到任何袋牌bag_tag_waybills 表中不存在)
  3. 已完成换单

    • label_replace_requests 表中存在对应记录
    • 换单状态为 "Y"(正常换单)
  4. 换单时间在前 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 关联处理逻辑

对于每个符合条件的包裹:

  1. 调用 AssociateWaybillAsync 方法进行关联
  2. 记录关联结果(成功/失败)
  3. 更新任务进度状态
  4. 生成 2-4 秒随机延迟
  5. 处理下一个包裹

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/start
  • GET /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. 性能考虑

  1. 异步处理:使用 Task.Run 或后台服务执行集包任务
  2. 缓存进度:使用 ICacheService 存储任务状态,避免数据库压力
  3. 批量查询:一次性查询所有符合条件的包裹,避免多次数据库访问
  4. 延迟控制2-4 秒随机延迟,避免对系统造成过大压力

9. 安全考虑

  1. 袋牌状态验证:确保袋牌处于 Opened 状态才能自动集包
  2. 渠道匹配:自动识别运单号渠道,确保与袋牌渠道一致
  3. 重复关联检查:避免将已关联的包裹再次关联
  4. 任务超时:设置任务最大执行时间(如 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"
);