# USPS 自动集包开发检查清单

## 开发前准备

- [ ] 已阅读 USPS自动集包需求文档.md
- [ ] 已阅读 spec.md 接口规范
- [ ] 已阅读 tasks.md 任务分解
- [ ] 已确认数据库表结构（label_replace_requests, bag_tag_waybills）
- [ ] 已确认 USPS 运单号识别规则

---

## 阶段一：模型定义

### 任务 1.1：创建任务状态模型
**文件**：`src/MDL/Models/AutoPackTaskStatus.cs`

- [ ] 创建 `AutoPackTaskStatus` 类
  - [ ] TaskId (string)
  - [ ] TagNumber (string)
  - [ ] Status (string)
  - [ ] TotalCount (int)
  - [ ] ProcessedCount (int)
  - [ ] SuccessCount (int)
  - [ ] FailedCount (int)
  - [ ] CurrentWaybill (string)
  - [ ] StartTime (DateTime)
  - [ ] EndTime (DateTime?)
  - [ ] FailedItems (List<AutoPackFailedItem>)
  - [ ] CancellationTokenSource (CancellationTokenSource)
- [ ] 创建 `AutoPackFailedItem` 类
  - [ ] WaybillNumber (string)
  - [ ] ErrorCode (int)
  - [ ] ErrorMessage (string)
- [ ] 添加必要的 using 语句
- [ ] 编译通过

### 任务 1.2：创建请求/响应 DTO
**文件**：`src/MDL/Models/AutoPackRequest.cs`

- [ ] 创建 `StartAutoPackRequest` 类
  - [ ] TagNumber (string)
  - [ ] Creator (string, 默认 "system")
- [ ] 创建 `StartAutoPackResponse` 类
  - [ ] TaskId (string)
  - [ ] TagNumber (string)
  - [ ] Status (string)
  - [ ] TotalCount (int)
  - [ ] Message (string)
- [ ] 创建 `AutoPackProgressResponse` 类
  - [ ] TaskId (string)
  - [ ] TagNumber (string)
  - [ ] Status (string)
  - [ ] TotalCount (int)
  - [ ] ProcessedCount (int)
  - [ ] SuccessCount (int)
  - [ ] FailedCount (int)
  - [ ] CurrentWaybill (string)
  - [ ] Progress (int)
  - [ ] Message (string)
  - [ ] StartTime (DateTime)
  - [ ] EstimatedEndTime (DateTime?)
  - [ ] FailedItems (List<AutoPackFailedItem>)
- [ ] 创建 `AutoPackResultResponse` 类
  - [ ] TaskId (string)
  - [ ] TagNumber (string)
  - [ ] Status (string)
  - [ ] TotalCount (int)
  - [ ] SuccessCount (int)
  - [ ] FailedCount (int)
  - [ ] StartTime (DateTime)
  - [ ] EndTime (DateTime?)
  - [ ] Duration (int)
  - [ ] FailedItems (List<AutoPackFailedItem>)
- [ ] 编译通过

---

## 阶段二：数据访问层

### 任务 2.1：扩展 Repository 接口
**文件**：`src/DAL/Interfaces/IBagTagRepository.cs`

- [ ] 添加 `GetEligibleUspsWaybillsAsync(DateTime cutoffTime)` 方法声明
- [ ] 添加 `GetEligibleUspsWaybillCountAsync(DateTime cutoffTime)` 方法声明
- [ ] 添加 XML 注释
- [ ] 编译通过

### 任务 2.2：实现 Repository 方法
**文件**：`src/DAL/Repositories/BagTagRepository.cs`

- [ ] 实现 `GetEligibleUspsWaybillsAsync` 方法
  - [ ] 使用 SqlSugar 执行 SQL 查询
  - [ ] 查询条件：ReplaceStatus = 'Y'
  - [ ] 查询条件：CreatedAt >= cutoffTime
  - [ ] 查询条件：FinalMileTrackingNumber IS NOT NULL AND != ''
  - [ ] 查询条件：未关联到 bag_tag_waybills（LEFT JOIN + IS NULL）
  - [ ] 查询条件：USPS 格式（REGEXP '^(92|93|94|95)'）
  - [ ] 查询条件：USPS 格式（REGEXP '^420[0-9]{5}(92|93|94|95)'）
  - [ ] 查询条件：USPS 格式（REGEXP '^420[0-9]{9}(92|93|94|95)'）
  - [ ] ORDER BY CreatedAt ASC
  - [ ] 返回 List<string>
- [ ] 实现 `GetEligibleUspsWaybillCountAsync` 方法
  - [ ] 使用 COUNT(*) 查询
  - [ ] 相同的 WHERE 条件
  - [ ] 返回 int
- [ ] 添加异常处理
- [ ] 编译通过

---

## 阶段三：业务逻辑层

### 任务 3.1：扩展 Service 接口
**文件**：`src/BLL/Interfaces/IBagTagService.cs`

- [ ] 添加 `StartAutoPackAsync(string tagNumber, string creator)` 方法声明
- [ ] 添加 `GetAutoPackProgressAsync(string taskId)` 方法声明
- [ ] 添加 `CancelAutoPackAsync(string taskId)` 方法声明
- [ ] 添加 `GetAutoPackResultAsync(string taskId)` 方法声明
- [ ] 添加 XML 注释
- [ ] 编译通过

### 任务 3.2：实现任务启动逻辑
**文件**：`src/BLL/Services/BagTagService.cs`

- [ ] 注入 ICacheService（如未注入）
- [ ] 实现 `StartAutoPackAsync` 方法
  - [ ] 验证袋牌存在（GetByTagNumberAsync）
  - [ ] 验证袋牌状态为 "Opened"
  - [ ] 计算 cutoffTime（DateTime.UtcNow.AddHours(-84)）
  - [ ] 调用 GetEligibleUspsWaybillsAsync 获取包裹列表
  - [ ] 检查包裹数量 > 0
  - [ ] 生成唯一 TaskId（格式：task_{timestamp}_{guid}）
  - [ ] 创建 AutoPackTaskStatus 对象
  - [ ] 存入缓存（key: "autopack:{taskId}", 过期时间 60 分钟）
  - [ ] 启动后台任务（Task.Run）
  - [ ] 返回 StartAutoPackResponse
- [ ] 添加异常处理
- [ ] 编译通过

### 任务 3.3：实现自动集包执行逻辑
**文件**：`src/BLL/Services/BagTagService.cs`

- [ ] 创建 `ExecuteAutoPackAsync` 私有方法
  - [ ] 参数：taskId, tagNumber, waybills, creator
  - [ ] 创建 Random 对象
  - [ ] 遍历 waybills
    - [ ] 从缓存获取任务状态
    - [ ] 检查 CancellationTokenSource.IsCancellationRequested
    - [ ] 更新 CurrentWaybill
    - [ ] 调用 AssociateWaybillAsync 进行关联
    - [ ] 更新 SuccessCount 或 FailedCount
    - [ ] 记录失败信息到 FailedItems
    - [ ] 更新 ProcessedCount
    - [ ] 保存任务状态到缓存
    - [ ] 随机延迟 2-4 秒（最后一个不延迟）
  - [ ] 任务完成处理
    - [ ] 设置 Status（completed/cancelled）
    - [ ] 设置 EndTime
    - [ ] 清空 CurrentWaybill
    - [ ] 保存最终状态到缓存
- [ ] 实现 `GetAutoPackProgressAsync` 方法
  - [ ] 从缓存获取任务状态
  - [ ] 计算 Progress 百分比
  - [ ] 计算 EstimatedEndTime
  - [ ] 映射到 AutoPackProgressResponse
  - [ ] 返回响应
- [ ] 实现 `CancelAutoPackAsync` 方法
  - [ ] 从缓存获取任务状态
  - [ ] 检查任务是否存在且状态为 processing
  - [ ] 调用 CancellationTokenSource.Cancel()
  - [ ] 更新状态为 cancelled
  - [ ] 保存到缓存
  - [ ] 返回 bool
- [ ] 实现 `GetAutoPackResultAsync` 方法
  - [ ] 从缓存获取任务状态
  - [ ] 映射到 AutoPackResultResponse
  - [ ] 计算 Duration
  - [ ] 返回响应
- [ ] 添加异常处理
- [ ] 编译通过

---

## 阶段四：接口层

### 任务 4.1：实现控制器方法
**文件**：`src/CONTROLLER/Controllers/BagTagController.cs`

- [ ] 添加 `StartAutoPack` 方法
  - [ ] HTTP POST 路由："auto-pack/start"
  - [ ] 参数：[FromBody] StartAutoPackRequest
  - [ ] 调用 _bagTagService.StartAutoPackAsync
  - [ ] 返回统一响应格式 { code, message, data }
  - [ ] 异常处理
- [ ] 添加 `GetAutoPackProgress` 方法
  - [ ] HTTP GET 路由："auto-pack/progress/{taskId}"
  - [ ] 参数：string taskId
  - [ ] 调用 _bagTagService.GetAutoPackProgressAsync
  - [ ] 处理任务不存在的情况
  - [ ] 返回统一响应格式
  - [ ] 异常处理
- [ ] 添加 `CancelAutoPack` 方法
  - [ ] HTTP POST 路由："auto-pack/cancel/{taskId}"
  - [ ] 参数：string taskId
  - [ ] 调用 _bagTagService.CancelAutoPackAsync
  - [ ] 处理取消失败的情况
  - [ ] 返回统一响应格式
  - [ ] 异常处理
- [ ] 添加 `GetAutoPackResult` 方法
  - [ ] HTTP GET 路由："auto-pack/result/{taskId}"
  - [ ] 参数：string taskId
  - [ ] 调用 _bagTagService.GetAutoPackResultAsync
  - [ ] 处理任务不存在的情况
  - [ ] 返回统一响应格式
  - [ ] 异常处理
- [ ] 编译通过

### 任务 4.2：注册依赖注入
**文件**：`src/CONTROLLER/Program.cs`

- [ ] 检查 ICacheService 是否已注册
- [ ] 检查 IBagTagService 是否已注册
- [ ] 确认无需额外注册

---

## 阶段五：测试与优化

### 任务 5.1：编写单元测试

- [ ] 测试启动任务 - 袋牌不存在
- [ ] 测试启动任务 - 袋牌未打开
- [ ] 测试启动任务 - 无符合条件的包裹
- [ ] 测试启动任务 - 成功启动
- [ ] 测试查询进度 - 任务不存在
- [ ] 测试查询进度 - 正常查询
- [ ] 测试取消任务 - 任务不存在
- [ ] 测试取消任务 - 任务已完成
- [ ] 测试取消任务 - 正常取消
- [ ] 测试自动集包执行 - 全部成功
- [ ] 测试自动集包执行 - 部分失败
- [ ] 测试自动集包执行 - 取消操作
- [ ] 所有测试通过

### 任务 5.2：性能优化与联调

- [ ] SQL 查询性能优化
  - [ ] 检查 label_replace_requests 表索引（CreatedAt, ReplaceStatus, FinalMileTrackingNumber）
  - [ ] 检查 bag_tag_waybills 表索引（FinalMileTrackingNumber）
- [ ] 缓存配置优化
  - [ ] 确认缓存过期时间合理（60分钟）
- [ ] 并发控制
  - [ ] 确认同时只能有一个自动集包任务在运行（可选）
- [ ] 异常处理完善
  - [ ] 数据库连接异常
  - [ ] 缓存访问异常
  - [ ] 关联操作异常
- [ ] WinForm 联调
  - [ ] 前端能正常调用启动接口
  - [ ] 前端能正常轮询进度
  - [ ] 进度条实时更新
  - [ ] 取消功能正常
  - [ ] 大数据量测试（100+ 包裹）

---

## 代码审查清单

### 代码规范

- [ ] 命名规范符合项目标准
- [ ] 方法添加 XML 注释
- [ ] 复杂逻辑添加行内注释
- [ ] 无死代码
- [ ] 无 Console.WriteLine（使用 ILogger）

### 异常处理

- [ ] 所有异步方法有 try-catch
- [ ] 异常信息不暴露敏感信息
- [ ] 异常正确记录日志

### 性能

- [ ] 数据库查询使用参数化 SQL
- [ ] 避免 N+1 查询问题
- [ ] 缓存使用合理

### 安全

- [ ] 输入参数验证
- [ ] 防止 SQL 注入
- [ ] 权限检查（如需要）

---

## 部署检查清单

- [ ] 代码编译通过
- [ ] 单元测试全部通过
- [ ] 数据库迁移脚本（如需要）
- [ ] 配置文件更新（如需要）
- [ ] API 文档更新
- [ ] 部署到测试环境
- [ ] 测试环境验证通过
- [ ] 部署到生产环境
- [ ] 生产环境验证通过

---

## 文档检查清单

- [ ] spec.md 已更新（如有变更）
- [ ] tasks.md 已更新（如有变更）
- [ ] 接口文档已更新
- [ ] 前端联调文档已提供

---

## 验收标准

### 功能验收

- [ ] 创建 USPS 袋牌后能自动触发集包（或手动触发接口）
- [ ] 正确筛选符合条件的 USPS 包裹（84小时内、未集包、已换单）
- [ ] 逐个关联包裹，间隔 2-4 秒
- [ ] 实时反馈进度（总数、已处理数、成功数、失败数）
- [ ] 支持取消操作
- [ ] 记录操作日志

### 性能验收

- [ ] 100 个包裹处理时间 < 10 分钟
- [ ] 前端轮询响应时间 < 100ms
- [ ] 内存占用稳定，无内存泄漏

### 兼容性验收

- [ ] WinForm 前端正常调用
- [ ] 接口响应格式符合规范
- [ ] 错误码定义清晰
