Files
LabelChange-server/.trae/specs/usps-auto-bag/spec.md
2026-06-01 16:30:29 +08:00

440 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# USPS 自动集包接口规格说明
## 1. 需求概述
根据 USPS自动集包需求文档实现 USPS 尾程包裹的自动集包功能。当用户在系统中创建 USPS 袋牌后,系统自动筛选符合条件的包裹并逐个关联到该袋牌。
**核心特点**
- 前端使用 WinForm需要实时反馈关联进度
- 采用异步任务 + 进度查询的设计模式
- 每次关联间隔 2-4 秒随机延迟,模拟人工操作
---
## 2. 接口设计
### 2.1 启动自动集包任务
**接口路径**`POST /api/bagtag/auto-pack/start`
**请求参数**
| 参数名 | 类型 | 必选 | 描述 |
|--------|------|------|------|
| tagNumber | string | 是 | USPS 袋牌号 |
| creator | string | 否 | 操作人,默认为 "system" |
**请求示例**
```json
{
"tagNumber": "USPS202604031200010001",
"creator": "admin"
}
```
**响应结构**
```json
{
"code": 0,
"message": "success",
"data": {
"taskId": "task_20260403120001_abc123",
"tagNumber": "USPS202604031200010001",
"status": "processing",
"totalCount": 50,
"message": "自动集包任务已启动"
}
}
```
**错误响应**
```json
{
"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 |
**响应结构**
```json
{
"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 |
**响应结构**
```json
{
"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 |
**响应结构**(任务完成后):
```json
{
"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包含
```csharp
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** 扩展:
```csharp
// 启动自动集包任务
Task<AutoPackTaskResult> StartAutoPackAsync(string tagNumber, string creator);
// 查询任务进度
Task<AutoPackTaskStatus> GetAutoPackProgressAsync(string taskId);
// 取消任务
Task<bool> CancelAutoPackAsync(string taskId);
// 获取任务结果
Task<AutoPackTaskStatus> GetAutoPackResultAsync(string taskId);
```
**IBagTagRepository** 扩展:
```csharp
// 查询符合条件的 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 查询符合条件的包裹
```sql
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 调用流程
```csharp
// 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. 日志记录
每个包裹关联操作记录订单日志:
```csharp
await _orderLogService.RecordOrderLogAsync(
neutralWaybillNumber: string.Empty,
finalMileTrackingNumber: waybillNumber,
operationType: OrderLogOperationType.PACK,
operationResult: success ? OrderLogOperationResult.SUCCESS : OrderLogOperationResult.FAILED,
operationDescription: $"自动集包到袋牌 {tagNumber}: {(success ? "成功" : errorMessage)}",
@operator: "system_auto"
);
```