# 定时任务执行范围修改说明

## 修改内容

### 修改目标
将定时任务 `ProcessPendingTasksAsync()` 的执行范围限制为仅处理创建时间在 **2026-05-10** 之后的订单。

### 修改日期
- **修改时间**: 2026-05-14
- **截断日期**: 2026-05-10 00:00:00

---

## 涉及文件修改

### 1. LabelReplaceRepository.cs
**文件路径**: `src/DAL/Repositories/LabelReplaceRepository.cs`

#### 修改的方法：

**1.1 GetNewOrdersWithLabelsAsync()**
```csharp
public async Task<List<string>> GetNewOrdersWithLabelsAsync(int limit)
{
    var db = _provider.GetClient();
    // 定义截断日期：2026-05-10之后
    var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);
    
    return await db.Queryable<LabelReplaceEntity>()
        .Where(o => !string.IsNullOrEmpty(o.Label))
        .Where(o => o.CreatedAt >= cutoffDate)  // 新增
        .Where(o => !SqlFunc.Subqueryable<LabelPdfCache>()
            .Where(c => c.NeutralWaybillNumber == o.NeutralWaybillNumber)
            .Any())
        .Select(o => o.NeutralWaybillNumber)
        .Take(limit)
        .ToListAsync();
}
```

**说明**: 
- 添加了 `cutoffDate` 变量定义截断日期
- 添加了 `.Where(o => o.CreatedAt >= cutoffDate)` 条件过滤
- 该方法是定时任务获取新订单的主要入口

**1.2 GetAllOrdersWithLabelsAsync()**
```csharp
public async Task<List<LabelReplaceEntity>> GetAllOrdersWithLabelsAsync(int limit = 1000)
{
    var db = _provider.GetClient();
    var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);
    
    return await db.Queryable<LabelReplaceEntity>()
        .Where(lr => !string.IsNullOrEmpty(lr.Label))
        .Where(lr => lr.CreatedAt >= cutoffDate)  // 新增
        .Take(limit)
        .ToListAsync();
}
```

**说明**:
- 添加了创建时间过滤条件
- 影响批量解析接口中 `mode=all` 的查询结果

**1.3 GetOrdersWithLabelsByDateRangeAsync()**
```csharp
public async Task<List<LabelReplaceEntity>> GetOrdersWithLabelsByDateRangeAsync(DateTime startDate, DateTime endDate, int limit = 1000)
{
    var db = _provider.GetClient();
    var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);
    
    // 确保指定的日期范围不低于截断日期
    var finalStartDate = startDate < cutoffDate ? cutoffDate : startDate;
    
    return await db.Queryable<LabelReplaceEntity>()
        .Where(lr => !string.IsNullOrEmpty(lr.Label) 
            && lr.CreatedAt >= finalStartDate  // 修改
            && lr.CreatedAt <= endDate)
        .Take(limit)
        .ToListAsync();
}
```

**说明**:
- 添加了日期范围检查，确保开始日期不低于截断日期
- 影响批量解析接口中 `mode=range` 的查询结果

**1.4 GetOrdersWithLabelsByCustomerAsync()**
```csharp
public async Task<List<LabelReplaceEntity>> GetOrdersWithLabelsByCustomerAsync(int customerId, int limit = 1000)
{
    var db = _provider.GetClient();
    var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);
    
    return await db.Queryable<LabelReplaceEntity>()
        .Where(lr => !string.IsNullOrEmpty(lr.Label) 
            && lr.CustomerId == customerId
            && lr.CreatedAt >= cutoffDate)  // 新增
        .Take(limit)
        .ToListAsync();
}
```

**说明**:
- 添加了创建时间过滤条件
- 影响批量解析接口中 `mode=customer` 的查询结果

---

## 工作流程影响

### 定时任务处理流程

定时任务执行时流程如下：

```
ProcessPendingTasksAsync()
    ├─ GetInvalidCachesAsync()
    │  └─ 获取状态=3的无效缓存（结合订单表过滤）
    │
    ├─ GetPendingTasksAsync()  
    │  └─ 获取状态=0和状态=2的待处理缓存
    │
    └─ GetNewOrdersWithLabelsAsync()  ← 新增时间过滤
       └─ 获取订单表中有标签但缓存表无记录的订单
       └─ 过滤条件: CreatedAt >= 2026-05-10
```

### 受影响的查询

| 方法 | 影响 | 说明 |
|------|------|------|
| `GetNewOrdersWithLabelsAsync()` | ✅ 直接影响 | 定时任务的新订单获取方法 |
| `GetAllOrdersWithLabelsAsync()` | ✅ 直接影响 | 批量解析 `mode=all` |
| `GetOrdersWithLabelsByDateRangeAsync()` | ✅ 直接影响 | 批量解析 `mode=range` |
| `GetOrdersWithLabelsByCustomerAsync()` | ✅ 直接影响 | 批量解析 `mode=customer` |

---

## API 行为变化

### 批量解析接口 (`/batch-parse`)

**修改前**: 可以处理2026-05-10之前的订单

**修改后**: 只能处理2026-05-10及以后的订单

| 模式 | 说明 | 变化 |
|------|------|------|
| `all` | 处理所有有标签的订单 | ✅ 只处理>=2026-05-10的订单 |
| `range` | 按时间范围处理 | ✅ 开始日期自动调整至2026-05-10 |
| `customer` | 按客户处理 | ✅ 只处理>=2026-05-10的订单 |
| `single` | 单条订单处理 | ❌ 无变化（通过单号直接处理） |

### 示例

**请求 - 按时间范围处理**
```json
{
  "mode": "range",
  "startDate": "2026-05-01",  // 实际从2026-05-10开始
  "endDate": "2026-05-15"
}
```

**说明**: startDate 会被自动调整为 2026-05-10，因为这是截断日期

---

## 定时任务行为

### 定时任务的新特性

1. **自动时间过滤** - 所有查询都将受到创建时间的限制
2. **历史数据隔离** - 2026-05-10之前的订单不会被自动处理
3. **手动处理支持** - 通过 `mode=single` 可以手动处理任何订单

### 执行间隔
- **间隔**: 5分钟
- **处理范围**: 仅2026-05-10及以后的订单
- **批处理大小**: 100条（可配置）

---

## 配置参数

### 截断日期定义位置

所有截断日期都定义在数据访问层（Repository）中：

```csharp
var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);
```

### 修改截断日期的方法

如需修改截断日期，只需更改上述时间值，例如改为2026-06-01：

```csharp
var cutoffDate = new DateTime(2026, 6, 1, 0, 0, 0);
```

然后重新编译和部署项目。

---

## 数据库影响

### 查询变化

**修改前SQL**:
```sql
SELECT o.NeutralWaybillNumber 
FROM label_replace o
WHERE o.Label IS NOT NULL
  AND NOT EXISTS (
    SELECT 1 FROM label_pdf_cache c 
    WHERE c.NeutralWaybillNumber = o.NeutralWaybillNumber
  )
LIMIT 100;
```

**修改后SQL**:
```sql
SELECT o.NeutralWaybillNumber 
FROM label_replace o
WHERE o.Label IS NOT NULL
  AND o.CreatedAt >= '2026-05-10'        -- 新增条件
  AND NOT EXISTS (
    SELECT 1 FROM label_pdf_cache c 
    WHERE c.NeutralWaybillNumber = o.NeutralWaybillNumber
  )
LIMIT 100;
```

### 性能考虑

- 添加的 `CreatedAt >= cutoffDate` 条件可以利用现有的时间索引
- 预期查询性能无负面影响
- 可能会减少返回结果数量（因为过滤了旧数据）

---

## 兼容性

### 向后兼容性
- ✅ 单条处理模式 (`mode=single`) 不受影响
- ❌ 通过API手动处理时会受到时间限制

### 版本信息
- **修改版本**: v2.1
- **兼容版本**: v2.0（如需处理旧数据，需升级到v2.1后手动处理）

---

## 测试验证

### 编译验证
✅ 整个项目已成功编译，无新的编译错误

### 功能验证清单

- [ ] 定时任务成功运行（每5分钟一次）
- [ ] 只处理2026-05-10之后的订单
- [ ] 2026-05-10之前的订单不被处理
- [ ] 批量解析接口在 `mode=all` 时只返回新数据
- [ ] 批量解析接口在 `mode=range` 时正确调整日期范围
- [ ] 批量解析接口在 `mode=customer` 时只返回新客户订单
- [ ] 缓存统计接口统计正确

### 手动测试命令

```bash
# 测试批量解析 - all模式
curl -X POST http://localhost:5002/api/label/batch-parse \
  -H "Content-Type: application/json" \
  -d '{"mode":"all","limit":10}'

# 测试批量解析 - range模式（时间范围自动调整）
curl -X POST http://localhost:5002/api/label/batch-parse \
  -H "Content-Type: application/json" \
  -d '{"mode":"range","startDate":"2026-05-01","endDate":"2026-05-15","limit":10}'

# 测试定时任务状态
curl -X GET http://localhost:5002/api/label/background-service/status
```

---

## 回滚方案

如需恢复到修改前的行为，只需：

1. 移除所有的 `var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);` 定义
2. 移除所有的 `.Where(lr => lr.CreatedAt >= cutoffDate)` 条件
3. 重新编译和部署

---

## 常见问题

### Q: 如何处理2026-05-10之前的订单？
A: 使用 `mode=single` 通过单号进行手动处理：
```json
{"mode":"single","waybillNumber":"1Z999AA10123456784"}
```

### Q: 定时任务会处理旧订单吗？
A: 不会。定时任务 (`GetNewOrdersWithLabelsAsync`) 已被限制为仅处理2026-05-10及以后的订单。

### Q: 缓存统计会包含旧数据吗？
A: 是的。`cache-statistics` 接口统计的是缓存表中的所有数据，不受时间限制。

### Q: 如何修改截断日期？
A: 修改所有Repository中的 `new DateTime(2026, 5, 10, 0, 0, 0)` 为新的日期，然后重新编译部署。

---

## 相关文件

- **修改的数据库访问类**: `src/DAL/Repositories/LabelReplaceRepository.cs`
- **定时任务类**: `src/BLL/Services/LabelPdfCacheService.cs`
- **批量解析接口**: `src/CONTROLLER/Controllers/LabelController.cs`

---

**修改完成于**: 2026-05-14
**编译状态**: ✅ 成功
**部署状态**: 等待确认
