10 KiB
10 KiB
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)
- 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)
- 创建
AutoPackResultResponse类- TaskId (string)
- TagNumber (string)
- Status (string)
- TotalCount (int)
- SuccessCount (int)
- FailedCount (int)
- StartTime (DateTime)
- EndTime (DateTime?)
- Duration (int)
- FailedItems (List)
- 编译通过
阶段二:数据访问层
任务 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
- 实现
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 前端正常调用
- 接口响应格式符合规范
- 错误码定义清晰