Files
LabelChange-server/.trae/docs/BackgroundTasks/04-Background_Task_Scope_Improvement.md
2026-06-01 16:30:29 +08:00

5.9 KiB
Raw Blame History

定时任务执行范围优化 - 总结

日期: 2026-05-13
状态: 完成并编译通过


🎯 问题分析

定时任务应该处理订单表中有标签的数据,但之前的实现只处理:

  1. 缓存表中status=0待处理的记录
  2. 缓存表中status=2失败且未超过重试次数的记录

漏洞:订单表中新增的有标签订单如果不在缓存表中,就永远不会被处理。


改进方案

现在定时任务按以下优先级处理:

第一步处理失效的缓存Status=3
    └─ 订单表中有对应的有标签订单
    
第二步处理待处理的任务Status=0或2
    └─ 订单表中有对应的有标签订单
    
第三步:处理订单表中新的有标签订单 ⭐ 新增
    └─ 缓存表中不存在对应记录
    └─ 订单表中该订单有标签数据

🔧 代码修改

1. 新增Repository方法

文件: LabelPdfCacheRepository.cs

/// <summary>
/// 获取订单表中新的有标签订单(缓存表中不存在的)
/// </summary>
public async Task<List<string>> GetNewOrdersWithLabelsAsync(int limit)
{
    var db = _provider.GetClient();
    return await db.Queryable<LabelReplaceEntity>()
        .Where(o => !string.IsNullOrEmpty(o.Label))          // 订单有标签
        .Where(o => !SqlFunc.Subqueryable<LabelPdfCache>()
            .Where(c => c.NeutralWaybillNumber == o.NeutralWaybillNumber)
            .Any())                                           // 缓存表中不存在
        .Select(o => o.NeutralWaybillNumber)
        .Take(limit)
        .ToListAsync();
}

关键SQL逻辑

SELECT o.NeutralWaybillNumber
FROM LabelReplaceEntity o
WHERE o.Label IS NOT NULL AND o.Label != ''
  AND NOT EXISTS (
    SELECT 1 FROM label_pdf_cache c 
    WHERE c.NeutralWaybillNumber = o.NeutralWaybillNumber
  )

2. 更新接口定义

文件: ILabelPdfCacheRepository.cs

  • 添加 GetNewOrdersWithLabelsAsync 方法签名

3. 增强ProcessPendingTasksAsync逻辑

文件: LabelPdfCacheService.cs

新增第三步处理流程:

// 第三步:处理订单表中新的有标签订单
var newBatchSize = remainingBatchSize - pendingTasks.Count;
if (newBatchSize > 0)
{
    var newOrders = await _cacheRepository.GetNewOrdersWithLabelsAsync(newBatchSize);
    foreach (var waybillNumber in newOrders)
    {
        if (await ProcessSingleCacheTask(waybillNumber))
        {
            successCount++;
        }
    }
}

改进的日志

Completed processing PDF cache tasks, 
total processed: 45, 
invalid: 5, 
pending: 15, 
new orders: 25

📊 执行范围对比

修改前

订单状态 处理范围
订单有标签 仅处理缓存表中已存在的
新订单有标签 永远不会处理
缓存表无记录 跳过

修改后

订单状态 处理范围
失效缓存有标签 第一步处理
待处理缓存有标签 第二步处理
新订单有标签 第三步处理
缓存表无记录 新增处理

💡 工作流程示例

场景早上10:00定时任务执行batchSize=100

【第一步】处理失效缓存
  查询SELECT * FROM label_pdf_cache WHERE Status=3 AND OrderWithLabel LIMIT 100
  结果找到5条失效缓存
  操作重新处理这5条
  
【第二步】处理待处理任务
  查询SELECT * FROM label_pdf_cache 
        WHERE (Status=0 OR (Status=2 AND RetryCount<3)) 
        AND OrderWithLabel LIMIT 95
  结果找到15条待处理
  操作继续处理这15条
  
【第三步】处理新订单 ⭐ 新增
  查询SELECT o.NeutralWaybillNumber FROM LabelReplaceEntity o
        WHERE o.Label IS NOT NULL 
        AND NOT EXISTS (SELECT 1 FROM label_pdf_cache c 
                       WHERE c.NeutralWaybillNumber = o.NeutralWaybillNumber)
        LIMIT 80
  结果找到25条新订单有标签
  操作为这25条新订单创建缓存
  
【结果】
  本次执行处理了 45 条记录
  - 失效缓存5条
  - 待处理任务15条
  - 新订单25条

🛡️ 防护机制

  1. 订单标签有效性检查

    .Where(o => !string.IsNullOrEmpty(o.Label))  // 确保Label不为空
    
  2. 重复处理防护

    .Where(o => !SqlFunc.Subqueryable<LabelPdfCache>()
        .Where(c => c.NeutralWaybillNumber == o.NeutralWaybillNumber)
        .Any())  // 确保缓存表中不存在
    
  3. 批量处理限制

    • batchSize控制单次处理数量
    • 防止定时任务过度执行
  4. 完整的日志记录

    • 记录各阶段处理数量
    • 便于监控和调试

现在的覆盖场景

场景 处理方式 结果
新订单有标签 第三步 立即创建缓存
已缓存订单 第一、二步 重试或更新
订单无标签 过滤掉 跳过
缓存表无记录 第三步 创建新记录

📈 预期收益

  1. 完整覆盖

    • 不再有漏掉的新订单
    • 所有有标签订单都会被处理
  2. 及时处理

    • 新订单会在下个定时任务周期处理
    • 缩短缓存生成时间
  3. 可观测性

    • 详细的执行日志
    • 清晰的处理数量统计
  4. 性能平衡

    • batchSize限制单次处理量
    • 不会过度消耗资源

📝 编译验证

✅ 编译成功exit code = 0
✅ 零编译错误
✅ 新增方法已实现
✅ 接口已更新
✅ 逻辑已完善

🚀 现在的定时任务能够

  1. 处理订单表中所有有标签的订单
  2. 优先处理失效和待处理的缓存
  3. 自动发现新的有标签订单
  4. 为新订单创建缓存记录
  5. 提供详细的执行日志

任务完成!定时任务现在能够正确处理订单表中所有有标签的数据。