上传源代码版本

This commit is contained in:
Im-Jenisson
2026-06-01 16:30:29 +08:00
commit b2a9b7d3c2
462 changed files with 104365 additions and 0 deletions

View File

@@ -0,0 +1,244 @@
# 定时任务文档索引
本文件夹包含所有与 **PDF标签缓存定时任务** 相关的文档说明。
## 📚 文档清单
### 1. 核心文档
| 文档 | 说明 | 最后更新 |
|------|------|---------|
| [定时任务暂停指南](定时任务暂停指南.md) | 如何暂停、恢复定时任务以及API接口管理 | 2026-05-14 |
| [定时任务执行范围修改说明](定时任务执行范围修改说明_2026-05-14.md) | 限制定时任务仅处理2026-05-10之后订单的修改 | 2026-05-14 |
| [定时任务与API隔离修改](定时任务与API隔离修改_2026-05-14.md) | 时间限制条件仅应用于定时任务batch-parse接口无限制 | 2026-05-14 |
| [Background Task Scope Improvement](Background_Task_Scope_Improvement.md) | 定时任务执行范围优化总结 | 2026-05-13 |
### 2. 操作指南
#### 暂停与恢复定时任务
参考:[定时任务暂停指南](定时任务暂停指南.md)
**3种方法**
- 方法1配置文件方式生产环保境
- 方法2环境变量方式Docker
- 方法3API接口方式最灵活
#### 定时任务的执行时间限制
参考:[定时任务与API隔离修改](定时任务与API隔离修改_2026-05-14.md)
**关键点**
- 定时任务:仅处理 >= 2026-05-10 的新订单
- batch-parse 接口:处理所有订单,无时间限制
- 两者完全隔离,互不影响
### 3. 相关配置
#### 定时任务类
- **位置**: `src/CONTROLLER/BackgroundServices/LabelPdfCacheBackgroundService.cs`
- **执行间隔**: 5分钟
- **主要方法**: `ExecuteAsync()``ProcessPendingTasksAsync()`
#### 后台服务管理器
- **位置**: `src/CONTROLLER/BackgroundServices/BackgroundServiceManager.cs`
- **用途**: 提供暂停/恢复定时任务的功能
#### 缓存服务
- **位置**: `src/BLL/Services/LabelPdfCacheService.cs`
- **主要方法**: `ProcessPendingTasksAsync()`, `ProcessSingleCacheTaskAsync()`
---
## 🔄 定时任务工作流程
```
定时任务 (每5分钟)
├─ 第一步:处理失效缓存
│ └─ 获取 Status=3 的缓存记录
│ └─ 重新处理这些订单
├─ 第二步:处理待处理任务
│ └─ 获取 Status=0 或 Status=2 的缓存记录
│ └─ 尝试重新处理
└─ 第三步:处理新订单 ⭐ 时间限制在此
└─ 获取订单表中有标签但缓存表无记录的订单
└─ 仅处理创建时间 >= 2026-05-10 的订单
└─ 创建新的缓存记录
```
---
## ⚙️ 配置参数
### 定时任务执行间隔
**文件**: `src/CONTROLLER/BackgroundServices/LabelPdfCacheBackgroundService.cs` (第18行)
```csharp
private const int TaskIntervalMinutes = 5;
```
### 批处理大小
**文件**: `src/BLL/Services/LabelPdfCacheService.cs` (ProcessPendingTasksAsync方法参数)
```csharp
public async Task<int> ProcessPendingTasksAsync(int maxRetryCount = 3, int batchSize = 100)
```
### 时间截断日期
**文件**: `src/DAL/Repositories/LabelPdfCacheRepository.cs`
```csharp
var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);
```
---
## 🚀 常用操作
### 查看定时任务状态
```bash
curl -X GET http://localhost:5002/api/label/background-service/status
```
### 暂停定时任务
```bash
curl -X POST http://localhost:5002/api/label/background-service/pause
```
### 恢复定时任务
```bash
curl -X POST http://localhost:5002/api/label/background-service/resume
```
### 手动触发批量解析
```bash
# 处理所有订单(无时间限制)
curl -X POST http://localhost:5002/api/label/batch-parse \
-H "Content-Type: application/json" \
-d '{"mode":"all","limit":500}'
# 处理单个订单(包括旧订单)
curl -X POST http://localhost:5002/api/label/batch-parse \
-H "Content-Type: application/json" \
-d '{"mode":"single","waybillNumber":"ORDER_NUMBER"}'
```
---
## 📊 监控指标
### 缓存统计信息
```bash
curl -X GET http://localhost:5002/api/label/cache-statistics
```
**返回字段**:
- `totalRecords` - 总缓存记录数
- `successRecords` - 成功处理的记录
- `failedRecords` - 失败的记录
- `invalidRecords` - 无效的记录
- `pendingRecords` - 待处理的记录
- `withBarcodeRecords` - 包含条码的记录
- `averageParseDurationMs` - 平均解析时间
- `maxParseDurationMs` - 最大解析时间
- `minParseDurationMs` - 最小解析时间
---
## 🔧 故障排查
### 定时任务不执行
**可能原因**:
1. 服务未启动
2. 定时任务已暂停
3. 数据库连接失败
**解决方案**:
```bash
# 检查状态
curl -X GET http://localhost:5002/api/label/background-service/status
# 如果已暂停,恢复它
curl -X POST http://localhost:5002/api/label/background-service/resume
# 查看日志
tail -f logs/development_api_log-*.txt
```
### 定时任务处理缓慢
**可能原因**:
1. 待处理任务过多
2. 网络延迟
3. PDF渲染时间过长
**解决方案**:
```bash
# 查看缓存统计
curl -X GET http://localhost:5002/api/label/cache-statistics
# 手动处理部分任务
curl -X POST http://localhost:5002/api/label/batch-parse \
-H "Content-Type: application/json" \
-d '{"mode":"all","limit":100}'
```
### 新订单未被处理
**检查清单**:
1. ✅ 订单是否在 >= 2026-05-10 之后创建
2. ✅ 订单是否有标签数据
3. ✅ 定时任务是否正在运行
4. ✅ 缓存表中是否已有该订单的记录
**手动处理**:
```bash
curl -X POST http://localhost:5002/api/label/batch-parse \
-H "Content-Type: application/json" \
-d '{"mode":"single","waybillNumber":"WAYBILL_NUMBER"}'
```
---
## 📝 修改历史
| 日期 | 修改内容 | 文档 |
|------|--------|------|
| 2026-05-14 | 隔离定时任务和API的时间限制条件 | [定时任务与API隔离修改](定时任务与API隔离修改_2026-05-14.md) |
| 2026-05-14 | 添加定时任务暂停/恢复功能 | [定时任务暂停指南](定时任务暂停指南.md) |
| 2026-05-14 | 限制定时任务仅处理2026-05-10之后订单 | [定时任务执行范围修改说明](定时任务执行范围修改说明_2026-05-14.md) |
| 2026-05-13 | 优化定时任务执行范围 | [Background Task Scope Improvement](Background_Task_Scope_Improvement.md) |
---
## ❓ 常见问题
### Q: 定时任务多久执行一次?
A: 每5分钟执行一次。可以在 `LabelPdfCacheBackgroundService.cs` 中修改 `TaskIntervalMinutes` 常量来改变执行频率。
### Q: 如何手动处理2026-05-10之前的订单
A: 使用 `mode=single` 通过 batch-parse 接口手动处理单个订单,该模式不受时间限制。
### Q: 定时任务会处理失败的订单吗?
A: 会的。定时任务会重试失败的订单最多重试3次可配置
### Q: 能否改变定时任务的执行时间?
A: 可以。修改 `LabelPdfCacheBackgroundService.cs` 中的 `TaskIntervalMinutes` 常量。
### Q: 暂停定时任务后,待处理的任务会丢失吗?
A: 不会。暂停只是停止周期性执行,待处理的任务会保留在数据库中,恢复后继续处理。
---
## 🔗 相关资源
- **API文档**: 根目录下的 `API_Documentation_zh.md`
- **完整系统流程**: 根目录下的 `SystemFlowDocument.md`
- **项目README**: 根目录下的 `README.md`
---
**最后更新**: 2026-05-14
**维护者**: 开发团队
**状态**: ✅ 完整

View File

@@ -0,0 +1,335 @@
# 定时任务暂停指南
## 概述
项目中的PDF标签缓存定时任务是通过 `LabelPdfCacheBackgroundService` 实现的,它是一个 ASP.NET Core `BackgroundService`每5分钟执行一次。
---
## 定时任务信息
### 基本参数
- **服务类**: `LabelPdfCacheBackgroundService`
- **执行间隔**: 5分钟
- **功能**: 处理待处理的PDF缓存任务
- **执行方法**: `ProcessPendingTasksAsync()`
### 当前工作流程
1. 每5分钟检查一次待处理任务
2. 获取失效的缓存、未处理的任务、新订单
3. 批量处理这些任务
4. 同步保存PDF缓存异步识别条码
---
## 暂停定时任务的方法
### 方法1修改配置文件推荐用于生产环境
#### 步骤1修改 appsettings.json
`appsettings.json``appsettings.Production.json` 中添加一个配置开关:
```json
{
"BackgroundServices": {
"LabelPdfCacheServiceEnabled": false
}
}
```
#### 步骤2修改 Program.cs
修改Program.cs中的注册代码
```csharp
// 从这样:
builder.Services.AddHostedService<CONTROLLER.BackgroundServices.LabelPdfCacheBackgroundService>();
// 改为:
var enableLabelPdfCache = builder.Configuration.GetValue<bool>("BackgroundServices:LabelPdfCacheServiceEnabled", true);
if (enableLabelPdfCache)
{
builder.Services.AddHostedService<CONTROLLER.BackgroundServices.LabelPdfCacheBackgroundService>();
}
```
#### 优点
- 无需重新编译代码
- 支持配置热更新
- 适合生产环境
#### 缺点
- 需要修改两个文件
- 需要重启应用
---
### 方法2使用环境变量
#### 步骤1修改 Program.cs
```csharp
var enableLabelPdfCache =
!string.Equals(
Environment.GetEnvironmentVariable("DISABLE_LABEL_PDF_CACHE"),
"true",
StringComparison.OrdinalIgnoreCase);
if (enableLabelPdfCache)
{
builder.Services.AddHostedService<CONTROLLER.BackgroundServices.LabelPdfCacheBackgroundService>();
}
```
#### 步骤2设置环境变量
**Windows命令行**
```bash
set DISABLE_LABEL_PDF_CACHE=true
```
**Linux/Mac**
```bash
export DISABLE_LABEL_PDF_CACHE=true
```
**Docker**
```dockerfile
ENV DISABLE_LABEL_PDF_CACHE=true
```
#### 优点
- 不需要修改配置文件
- 容易在Docker容器中配置
- 支持运行时切换
#### 缺点
- 需要修改代码
- 需要重启应用
---
### 方法3创建暂停/恢复接口(最灵活)
#### 优点
- 可以在运行时动态控制
- 无需重启应用
- 最灵活,适合生产环境
#### 缺点
- 需要修改更多代码
- 状态只在内存中保存,重启后会重置
---
## 方法对比
| 方法 | 修改代码 | 重启应用 | 实时性 | 推荐场景 |
|------|--------|--------|-------|---------|
| 方法1配置文件 | 中等 | 是 | 低 | 生产环境固定配置 |
| 方法2环境变量 | 中等 | 是 | 低 | Docker容器部署 |
| 方法3管理接口 | 高 | 否 | 高 | 需要灵活控制 |
---
## 使用方法3的API调用示例
### JavaScript/Fetch
```javascript
// 暂停定时任务
async function pauseBackgroundService() {
const response = await fetch('http://localhost:5002/api/label/background-service/pause', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
console.log(data);
}
// 恢复定时任务
async function resumeBackgroundService() {
const response = await fetch('http://localhost:5002/api/label/background-service/resume', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
console.log(data);
}
// 获取定时任务状态
async function getBackgroundServiceStatus() {
const response = await fetch('http://localhost:5002/api/label/background-service/status');
const data = await response.json();
console.log(data);
}
```
### Python
```python
import requests
BASE_URL = "http://localhost:5002/api/label"
# 暂停定时任务
def pause_service():
response = requests.post(f"{BASE_URL}/background-service/pause")
print(response.json())
# 恢复定时任务
def resume_service():
response = requests.post(f"{BASE_URL}/background-service/resume")
print(response.json())
# 获取定时任务状态
def get_status():
response = requests.get(f"{BASE_URL}/background-service/status")
print(response.json())
```
### cURL
```bash
# 暂停定时任务
curl -X POST http://localhost:5002/api/label/background-service/pause
# 恢复定时任务
curl -X POST http://localhost:5002/api/label/background-service/resume
# 获取定时任务状态
curl -X GET http://localhost:5002/api/label/background-service/status
```
---
## 预期响应
### 成功暂停
```json
{
"status": "success",
"message": "后台定时任务已暂停",
"data": {
"isRunning": false
}
}
```
### 成功恢复
```json
{
"status": "success",
"message": "后台定时任务已恢复",
"data": {
"isRunning": true
}
}
```
### 获取状态
```json
{
"status": "success",
"message": "获取后台服务状态成功",
"data": {
"isRunning": true,
"service": "LabelPdfCacheBackgroundService",
"interval": "5 minutes"
}
}
```
---
## 注意事项
1. **数据不会丢失** - 暂停定时任务只是停止周期性执行,已有的待处理任务不会被删除
2. **可以手动处理** - 暂停后可以通过 `/batch-parse` 接口手动触发处理
3. **性能考虑** - 长时间暂停可能导致待处理任务堆积
4. **内存状态** - 如果使用方法3重启应用后服务会自动恢复运行
5. **监控建议** - 建议定期检查任务状态,确保没有任务堆积
---
## 常见场景
### 场景1维护期间暂停
如需进行数据库维护或其他重要操作,可以暂停定时任务避免并发冲突:
```bash
# 暂停
curl -X POST http://localhost:5002/api/label/background-service/pause
# 执行维护操作
# ...
# 恢复
curl -X POST http://localhost:5002/api/label/background-service/resume
```
### 场景2定点手动处理
暂停自动定时任务改为手动通过API按需处理
```bash
# 暂停自动任务
curl -X POST http://localhost:5002/api/label/background-service/pause
# 手动触发解析(当需要时)
curl -X POST http://localhost:5002/api/label/batch-parse \
-H "Content-Type: application/json" \
-d '{"mode":"all","limit":500}'
```
### 场景3监控异常时暂停
如发现定时任务出现异常,可以快速暂停避免继续出错:
```bash
# 检查状态
curl -X GET http://localhost:5002/api/label/background-service/status
# 如果出现异常,暂停
curl -X POST http://localhost:5002/api/label/background-service/pause
# 调查问题后恢复
curl -X POST http://localhost:5002/api/label/background-service/resume
```
---
## 故障排查
### Q: 暂停后定时任务仍在执行
**A:** 可能使用的是方法1或方法2需要重启应用才能生效
### Q: 暂停状态在重启后丢失
**A:** 这是正常的。如需持久化暂停状态,可以将状态保存到数据库
### Q: 恢复后任务堆积
**A:** 这是正常的。系统会逐个处理堆积的任务。可以调整 `ProcessPendingTasksAsync()` 的批处理大小来优化处理速度
---
## 推荐实施方案
根据部署环境选择:
- **开发环境**: 使用方法3API接口方便调试
- **生产环境单机**: 使用方法1配置文件稳定可靠
- **生产环境Docker**: 使用方法2环境变量配置灵活
- **生产环境微服务**: 使用方法3API接口需要分布式协调
---
## 参考信息
- **定时任务执行间隔**: 5分钟`LabelPdfCacheBackgroundService.cs` 第18行
- **处理方法**: `ProcessPendingTasksAsync()`
- **处理范围**: 失效缓存、待处理任务、新订单
- **错误处理**: 自动捕获异常,记录日志

View File

@@ -0,0 +1,329 @@
# 定时任务执行范围修改说明
## 修改内容
### 修改目标
将定时任务 `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
**编译状态**: ✅ 成功
**部署状态**: 等待确认

View File

@@ -0,0 +1,193 @@
# 定时任务与批量解析接口隔离修改
## 修改概述
根据用户需求,将 **2026-05-10 之后的订单** 这个时间限制条件改为**仅应用于定时任务**,而 **batch-parse 接口不受此限制**
这样实现了两个不同的数据查询范围:
- **定时任务** (`ProcessPendingTasksAsync`) - 仅处理 >= 2026-05-10 的订单
- **batch-parse 接口** - 处理**所有**订单,无时间限制
---
## 修改详情
### 1. 新增方法
为了实现隔离,在两个 Repository 中都添加了**新的专用方法**
#### ILabelReplaceRepository 接口
```csharp
/// <summary>
/// 获取定时任务新订单仅2026-05-10之后的订单
/// </summary>
Task<List<string>> GetNewOrdersWithLabelsForBackgroundTaskAsync(int limit);
```
#### ILabelPdfCacheRepository 接口
```csharp
/// <summary>
/// 获取定时任务新订单仅2026-05-10之后的订单
/// </summary>
Task<List<string>> GetNewOrdersWithLabelsForBackgroundTaskAsync(int limit);
```
### 2. 原有方法恢复
以下方法已恢复为**无时间限制**的原始实现:
| 方法名 | 位置 | 修改 |
|--------|------|------|
| `GetNewOrdersWithLabelsAsync()` | LabelPdfCacheRepository | ✅ 移除时间过滤,恢复原始 |
| `GetAllOrdersWithLabelsAsync()` | LabelReplaceRepository | ✅ 移除时间过滤,恢复原始 |
| `GetOrdersWithLabelsByDateRangeAsync()` | LabelReplaceRepository | ✅ 移除时间过滤,恢复原始 |
| `GetOrdersWithLabelsByCustomerAsync()` | LabelReplaceRepository | ✅ 移除时间过滤,恢复原始 |
### 3. 定时任务调用修改
`LabelPdfCacheService.cs``ProcessPendingTasksAsync()` 方法中:
**修改前**:
```csharp
var newOrders = await _cacheRepository.GetNewOrdersWithLabelsAsync(newBatchSize);
```
**修改后**:
```csharp
// 仅处理创建时间>=2026-05-10的订单
var newOrders = await _cacheRepository.GetNewOrdersWithLabelsForBackgroundTaskAsync(newBatchSize);
```
---
## 工作流程对比
### batch-parse 接口行为
| 模式 | 支持范围 | 说明 |
|------|--------|------|
| `all` | **所有订单** | ✅ 不受时间限制 |
| `range` | **指定范围** | ✅ 按用户指定的日期范围处理 |
| `customer` | **指定客户订单** | ✅ 不受时间限制 |
| `single` | **单条订单** | ✅ 不受任何限制 |
### 定时任务行为
```
定时任务 (每5分钟)
├─ GetInvalidCachesAsync()
│ └─ 处理所有状态=3的无效缓存无时间限制
├─ GetPendingTasksAsync()
│ └─ 处理所有待处理缓存(无时间限制)
└─ GetNewOrdersWithLabelsForBackgroundTaskAsync() ← 【新增:仅>=2026-05-10】
└─ 只处理创建时间>=2026-05-10的新订单
```
---
## 使用场景
### 场景1使用批量解析处理历史订单
```bash
# 处理所有订单包括2026-05-10之前的
curl -X POST http://localhost:5002/api/label/batch-parse \
-H "Content-Type: application/json" \
-d '{"mode":"all","limit":500}'
```
**可以成功** - 返回所有有标签的订单
### 场景2定时任务自动处理
定时任务每5分钟自动执行
- ✅ 处理所有失效缓存
- ✅ 处理所有待处理缓存
- ✅ **仅处理**创建时间 >= 2026-05-10 的新订单
---
## 涉及文件修改
| 文件 | 修改内容 |
|------|---------|
| `src/DAL/Interfaces/ILabelReplaceRepository.cs` | 新增 `GetNewOrdersWithLabelsForBackgroundTaskAsync()` |
| `src/DAL/Repositories/LabelReplaceRepository.cs` | 实现新方法,恢复旧方法 |
| `src/DAL/Interfaces/ILabelPdfCacheRepository.cs` | 新增 `GetNewOrdersWithLabelsForBackgroundTaskAsync()` |
| `src/DAL/Repositories/LabelPdfCacheRepository.cs` | 实现新方法,恢复旧方法 |
| `src/BLL/Services/LabelPdfCacheService.cs` | 调用新方法 |
---
## 编译验证
**编译成功** - 整个项目编译无错误
---
## 方法汇总
### 查询范围明细
| 方法 | 作用 | 时间限制 | 使用场景 |
|------|------|---------|---------|
| `GetAllOrdersWithLabelsAsync()` | 查询所有有标签订单 | ❌ 无 | batch-parse mode=all |
| `GetOrdersWithLabelsByDateRangeAsync()` | 按日期范围查询 | ❌ 无 | batch-parse mode=range |
| `GetOrdersWithLabelsByCustomerAsync()` | 按客户查询 | ❌ 无 | batch-parse mode=customer |
| `GetNewOrdersWithLabelsAsync()` | 查询新订单 | ❌ 无 | batch-parse 补充查询 |
| `GetNewOrdersWithLabelsForBackgroundTaskAsync()` | 查询定时任务新订单 | ✅ >= 2026-05-10 | 定时任务专用 |
---
## 回滚方案
如需恢复到之前的行为定时任务和API都受时间限制只需
1.`ProcessPendingTasksAsync()` 中的调用改回:
```csharp
var newOrders = await _cacheRepository.GetNewOrdersWithLabelsAsync(newBatchSize);
```
2. 为通用方法添加时间限制条件
3. 移除专用的 `GetNewOrdersWithLabelsForBackgroundTaskAsync()` 方法
---
## 常见问题
### Q: 使用batch-parse接口处理2026-05-10之前的订单会成功吗
A: **是的,会成功**。现在batch-parse接口不受时间限制可以处理任何时间的订单。
### Q: 定时任务会处理2026-05-10之前的新订单吗
A: **不会**。定时任务仅处理 >= 2026-05-10 的新订单。如需处理旧订单,请使用 `mode=single` 手动处理。
### Q: 如何手动处理单个旧订单?
A: 使用 `mode=single` 模式:
```bash
curl -X POST http://localhost:5002/api/label/batch-parse \
-H "Content-Type: application/json" \
-d '{"mode":"single","waybillNumber":"OLD_WAYBILL_NUMBER"}'
```
### Q: 时间限制条件会影响定时任务的其他步骤吗?
A: **不会**。时间限制仅应用于获取新订单的步骤,不影响处理失效缓存和待处理任务的步骤。
---
## 测试验证清单
- [ ] 编译成功且无错误
- [ ] 使用 `mode=all` 查询到2026-05-10之前的订单
- [ ] 使用 `mode=range` 查询到指定日期范围的所有订单
- [ ] 使用 `mode=customer` 查询到该客户的所有订单(含旧订单)
- [ ] 定时任务仅处理>=2026-05-10的新订单
- [ ] 定时任务正常处理失效缓存(无时间限制)
- [ ] 定时任务正常处理待处理缓存(无时间限制)
- [ ] 缓存统计接口统计正确
---
**修改完成于**: 2026-05-14
**编译状态**: ✅ 成功
**部署状态**: 等待确认

View File

@@ -0,0 +1,241 @@
# 定时任务执行范围优化 - 总结
**日期**: 2026-05-13
**状态**: ✅ 完成并编译通过
---
## 🎯 问题分析
定时任务应该处理**订单表中有标签的数据**,但之前的实现只处理:
1. ❌ 缓存表中status=0待处理的记录
2. ❌ 缓存表中status=2失败且未超过重试次数的记录
**漏洞**:订单表中**新增的有标签订单**如果不在缓存表中,就永远不会被处理。
---
## ✅ 改进方案
现在定时任务按以下优先级处理:
```
第一步处理失效的缓存Status=3
└─ 订单表中有对应的有标签订单
第二步处理待处理的任务Status=0或2
└─ 订单表中有对应的有标签订单
第三步:处理订单表中新的有标签订单 ⭐ 新增
└─ 缓存表中不存在对应记录
└─ 订单表中该订单有标签数据
```
---
## 🔧 代码修改
### 1. 新增Repository方法
**文件**: `LabelPdfCacheRepository.cs`
```csharp
/// <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逻辑**
```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`
新增第三步处理流程:
```csharp
// 第三步:处理订单表中新的有标签订单
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. **订单标签有效性检查**
```csharp
.Where(o => !string.IsNullOrEmpty(o.Label)) // 确保Label不为空
```
2. **重复处理防护**
```csharp
.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. ✅ 提供详细的执行日志
---
**任务完成!定时任务现在能够正确处理订单表中所有有标签的数据。**

View File

@@ -0,0 +1,126 @@
# 定时任务快速参考
## 📌 核心信息
| 项目 | 内容 |
|------|------|
| **服务类** | `LabelPdfCacheBackgroundService` |
| **执行间隔** | 每5分钟 |
| **处理范围** | 失效缓存、待处理任务、新订单 |
| **时间限制** | 新订单仅处理 >= 2026-05-10 |
---
## 🚀 常用命令
### 查看状态
```bash
curl -X GET http://localhost:5002/api/label/background-service/status
```
### 暂停任务
```bash
curl -X POST http://localhost:5002/api/label/background-service/pause
```
### 恢复任务
```bash
curl -X POST http://localhost:5002/api/label/background-service/resume
```
### 查看缓存统计
```bash
curl -X GET http://localhost:5002/api/label/cache-statistics
```
---
## 📂 文件位置
| 文件 | 路径 |
|------|------|
| **服务主类** | `src/CONTROLLER/BackgroundServices/LabelPdfCacheBackgroundService.cs` |
| **服务管理器** | `src/CONTROLLER/BackgroundServices/BackgroundServiceManager.cs` |
| **业务逻辑** | `src/BLL/Services/LabelPdfCacheService.cs` |
| **数据访问** | `src/DAL/Repositories/LabelPdfCacheRepository.cs` |
---
## ⏱️ 配置参数
### 执行间隔
**文件**: `LabelPdfCacheBackgroundService.cs` 第18行
```csharp
private const int TaskIntervalMinutes = 5;
```
改为所需的分钟数
### 批处理大小
**文件**: `LabelPdfCacheService.cs` ProcessPendingTasksAsync方法
```csharp
public async Task<int> ProcessPendingTasksAsync(int maxRetryCount = 3, int batchSize = 100)
```
调整 `batchSize` 参数
### 时间限制日期
**文件**: `LabelPdfCacheRepository.cs` GetNewOrdersWithLabelsForBackgroundTaskAsync方法
```csharp
var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);
```
改为所需的日期
---
## 🔄 工作流程
```
每5分钟执行一次:
├─ 处理失效缓存 (Status=3)
├─ 处理待处理任务 (Status=0 或 Status=2)
└─ 处理新订单 (>= 2026-05-10 的有标签订单)
```
---
## ✅ 检查清单
- [ ] 定时任务是否正在运行? → 查看状态
- [ ] 是否有待处理任务堆积? → 查看缓存统计
- [ ] 新订单是否被自动处理? → 检查创建时间是否 >= 2026-05-10
- [ ] 定时任务是否遇到错误? → 查看日志文件
---
## 🆘 快速故障排查
| 问题 | 解决方案 |
|------|---------|
| 定时任务不执行 | 检查是否暂停,查看日志 |
| 新订单未被处理 | 检查创建时间,检查是否有标签 |
| 任务堆积 | 调整批处理大小或手动处理 |
| 错误重复发生 | 暂停任务,调查问题,恢复 |
---
## 📊 关键指标
`/api/label/cache-statistics` 获取:
- **totalRecords**: 总缓存数
- **successRecords**: 成功数
- **failedRecords**: 失败数
- **pendingRecords**: 待处理数
- **averageParseDurationMs**: 平均耗时
- **withBarcodeRecords**: 含条码数
---
## 🔗 相关文档
1. [定时任务暂停指南](01-定时任务暂停指南.md) - 如何控制定时任务
2. [执行范围修改说明](02-定时任务执行范围修改说明.md) - 时间限制详解
3. [定时任务与API隔离](03-定时任务与API隔离修改.md) - API与定时任务的区别
---
**最后更新**: 2026-05-14

View File

@@ -0,0 +1,284 @@
# 定时任务源代码参考
## 文件位置总览
### 后台服务相关
#### 1. LabelPdfCacheBackgroundService.cs
**路径**: `src/CONTROLLER/BackgroundServices/LabelPdfCacheBackgroundService.cs`
**作用**: 定时任务的主服务类
**关键内容**:
- `ExecuteAsync()` - 后台服务的主方法每5分钟执行一次
- `TaskIntervalMinutes = 5` - 执行间隔设置
- 依赖注入了 `ILabelPdfCacheService`
**示例代码位置**:
- 第18行: 执行间隔定义
- 第25-40行: 构造函数和依赖注入
- 第42-70行: ExecuteAsync主方法实现
#### 2. BackgroundServiceManager.cs
**路径**: `src/CONTROLLER/BackgroundServices/BackgroundServiceManager.cs`
**作用**: 管理后台服务的暂停/恢复
**关键内容**:
- `PauseService()` - 暂停服务
- `ResumeService()` - 恢复服务
- `IsRunning()` - 检查运行状态
- `GetCancellationToken()` - 获取取消令牌
---
### 业务逻辑相关
#### 3. LabelPdfCacheService.cs
**路径**: `src/BLL/Services/LabelPdfCacheService.cs`
**作用**: PDF缓存的业务逻辑处理
**关键方法**:
- `ProcessPendingTasksAsync()` (第235-290行)
- 处理失效缓存
- 处理待处理任务
- 处理新订单(仅>=2026-05-10
- `ProcessSingleCacheTaskAsync()` (第291-350行)
- 处理单个订单
- 获取PDF字节流
- 同步保存缓存
- 异步识别条码
- `RecognizeBarcodeAsync()` (第450-550行)
- 条码识别逻辑
- 支持一维码和二维码
- `GetCacheStatisticsAsync()` (第580-620行)
- 获取统计信息
#### 4. LabelPdfCacheBackgroundService.cs 中的调用
**关键代码位置**: 第50-65行
```csharp
var successCount = await cacheService.ProcessPendingTasksAsync();
```
---
### 数据访问相关
#### 5. LabelPdfCacheRepository.cs
**路径**: `src/DAL/Repositories/LabelPdfCacheRepository.cs`
**关键方法**:
1. **GetNewOrdersWithLabelsAsync()** (第142-152行)
- 获取新订单(无时间限制)
- 用于 batch-parse 接口
2. **GetNewOrdersWithLabelsForBackgroundTaskAsync()** (第183-200行)
- 获取新订单(仅>=2026-05-10
- **仅用于定时任务**
- 关键代码位置: 第186行
```csharp
var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);
```
3. **GetInvalidCachesAsync()** (第53-65行)
- 获取失效缓存
4. **GetPendingTasksAsync()** (第67-85行)
- 获取待处理任务
---
### 接口/API相关
#### 6. LabelController.cs
**路径**: `src/CONTROLLER/Controllers/LabelController.cs`
**定时任务相关接口**:
1. **batch-parse** (第2510-2640行)
- POST `/api/label/batch-parse`
- 支持4种模式: all, range, customer, single
- 不受时间限制
2. **cache-statistics** (第2652-2670行)
- GET `/api/label/cache-statistics`
- 获取缓存统计信息
3. **background-service/pause** (需要实现)
- POST `/api/label/background-service/pause`
- 暂停定时任务
4. **background-service/resume** (需要实现)
- POST `/api/label/background-service/resume`
- 恢复定时任务
5. **background-service/status** (需要实现)
- GET `/api/label/background-service/status`
- 查看定时任务状态
---
## 关键代码片段
### 定时任务执行流程
**文件**: `LabelPdfCacheService.cs` 第235-290行
```csharp
public async Task<int> ProcessPendingTasksAsync(int maxRetryCount = 3, int batchSize = 100)
{
var successCount = 0;
try
{
// 第一步:处理失效的缓存
var invalidCaches = await _cacheRepository.GetInvalidCachesAsync(batchSize);
// 第二步:处理待处理的任务
var remainingBatchSize = batchSize - invalidCaches.Count;
var pendingTasks = await _cacheRepository.GetPendingTasksAsync(maxRetryCount, remainingBatchSize);
// 第三步:处理订单表中新的有标签订单(仅>=2026-05-10
var newBatchSize = remainingBatchSize - pendingTasks.Count;
if (newBatchSize > 0)
{
// 使用专用方法获取新订单(受时间限制)
var newOrders = await _cacheRepository.GetNewOrdersWithLabelsForBackgroundTaskAsync(newBatchSize);
foreach (var waybillNumber in newOrders)
{
if (await ProcessSingleCacheTask(waybillNumber))
{
successCount++;
}
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in ProcessPendingTasksAsync");
}
return successCount;
}
```
### 定时任务新订单查询(受时间限制)
**文件**: `LabelPdfCacheRepository.cs` 第183-200行
```csharp
public async Task<List<string>> GetNewOrdersWithLabelsForBackgroundTaskAsync(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();
}
```
### 通用新订单查询(无时间限制)
**文件**: `LabelPdfCacheRepository.cs` 第142-152行
```csharp
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();
}
```
---
## 配置参数修改位置
### 1. 执行间隔 (5分钟)
**文件**: `LabelPdfCacheBackgroundService.cs` 第18行
```csharp
private const int TaskIntervalMinutes = 5;
```
**改为**: 所需的分钟数
### 2. 批处理大小 (100条)
**文件**: `LabelPdfCacheService.cs` ProcessPendingTasksAsync方法参数
```csharp
public async Task<int> ProcessPendingTasksAsync(int maxRetryCount = 3, int batchSize = 100)
```
**改为**: 所需的批处理大小
### 3. 重试次数 (3次)
**文件**: `LabelPdfCacheService.cs` ProcessPendingTasksAsync方法参数
```csharp
public async Task<int> ProcessPendingTasksAsync(int maxRetryCount = 3, int batchSize = 100)
```
**改为**: 所需的重试次数
### 4. 时间限制日期 (2026-05-10)
**文件**: `LabelPdfCacheRepository.cs` GetNewOrdersWithLabelsForBackgroundTaskAsync方法
```csharp
var cutoffDate = new DateTime(2026, 5, 10, 0, 0, 0);
```
**改为**: 所需的日期
---
## 依赖关系
```
LabelPdfCacheBackgroundService
└─→ ILabelPdfCacheService
├─→ ILabelPdfCacheRepository
│ ↓
│ └─→ SqlSugar (数据库)
└─→ ILogger
```
---
## 调试技巧
### 打断点位置
1. **定时任务执行**: `LabelPdfCacheBackgroundService.ExecuteAsync()`
2. **处理逻辑**: `LabelPdfCacheService.ProcessPendingTasksAsync()`
3. **单个订单处理**: `LabelPdfCacheService.ProcessSingleCacheTaskAsync()`
4. **查询新订单**: `LabelPdfCacheRepository.GetNewOrdersWithLabelsForBackgroundTaskAsync()`
### 查看日志
日志文件位置: `logs/` 目录
关键日志关键字:
- "Label PDF Cache Background Service is starting"
- "Starting label PDF cache processing task"
- "Completed label PDF cache processing task"
- "Error occurred in label PDF cache background service"
---
**最后更新**: 2026-05-14

View File

@@ -0,0 +1,406 @@
# 定时任务管理模块 - 项目待办清单
## 📋 项目概述
**项目名称**: PDF标签缓存定时任务管理模块
**优先级**: 高
**目标**: 建立完整的定时任务管理系统,包括任务执行、监控、告警和控制面板
**预期周期**: 中期4-6周
---
## 🎯 核心目标
- ✅ 完整的定时任务管理API
- ✅ 后台任务执行监控
- ✅ 定时任务配置管理
- ✅ 任务执行日志和审计
- ✅ 告警和异常处理
- ✅ 管理后台Dashboard
---
## 📋 需求分析
### 当前系统现状
-**已实现**:
- `LabelPdfCacheBackgroundService` - 后台服务主类
- `ProcessPendingTasksAsync()` - 核心处理方法
- 基本的5分钟定时执行
- 三步处理流程(失效缓存、待处理任务、新订单)
- 时间限制条件(>=2026-05-10
- 基本的暂停/恢复功能BackgroundServiceManager
- ⚠️ **部分实现**:
- 暂停/恢复API接口需要完整实现在LabelController
- 基本的状态查询(需要增强)
-**未实现**:
- 定时任务配置管理界面
- 详细的执行日志记录
- 任务执行历史查询
- 告警和通知机制
- 性能监控和分析
- 错误重试策略可视化
- 定时任务管理Dashboard
- 任务调度的可视化配置
---
## 🔨 任务分解
### 阶段1: API层完善 (优先级: ⭐⭐⭐)
#### 1.1 完整的控制接口
- [ ] **Task**: 在LabelController中完整实现暂停/恢复API
- [ ] `POST /api/label/background-service/pause` - 暂停定时任务
- [ ] `POST /api/label/background-service/resume` - 恢复定时任务
- [ ] `GET /api/label/background-service/status` - 获取定时任务状态
- [ ] 返回详细的状态信息(运行状态、最后执行时间、下次执行时间等)
- **预计工作量**: 2小时
- **相关文件**: `src/CONTROLLER/Controllers/LabelController.cs`
#### 1.2 配置管理接口
- [ ] **Task**: 创建定时任务配置管理API
- [ ] `GET /api/label/background-service/config` - 获取当前配置
- [ ] `POST /api/label/background-service/config` - 更新配置
- [ ] 支持配置项:
- 执行间隔(分钟)
- 批处理大小
- 重试次数
- 时间限制日期
- 是否启用
- **预计工作量**: 4小时
- **相关文件**:
- `src/CONTROLLER/Controllers/LabelController.cs`
- `src/BLL/Services/LabelPdfCacheService.cs`
#### 1.3 日志查询接口
- [ ] **Task**: 实现执行日志查询API
- [ ] `GET /api/label/background-service/logs` - 查询执行日志
- [ ] 支持筛选条件:
- 日期范围
- 执行状态(成功/失败)
- 关键词搜索
- [ ] 分页支持
- **预计工作量**: 3小时
- **相关文件**:
- `src/CONTROLLER/Controllers/LabelController.cs`
- `src/DAL/Repositories/LabelPdfCacheRepository.cs`
### 阶段2: 数据存储层 (优先级: ⭐⭐⭐)
#### 2.1 创建定时任务日志表
- [ ] **Task**: 设计和创建 `background_task_logs`
- 表结构:
```sql
CREATE TABLE background_task_logs (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
task_name VARCHAR(100), -- 任务名称
execution_time DATETIME, -- 执行时间
status TINYINT, -- 状态: 0=进行中, 1=成功, 2=失败
processed_count INT, -- 处理数量
success_count INT, -- 成功数
error_count INT, -- 错误数
duration_ms INT, -- 执行耗时(毫秒)
error_message TEXT, -- 错误信息
created_at DATETIME,
updated_at DATETIME,
INDEX idx_execution_time (execution_time),
INDEX idx_status (status)
)
```
- **预计工作量**: 1小时
- **相关文件**: `src/DB/Scripts/CreateBackgroundTaskLogsTable.sql`
#### 2.2 创建定时任务配置表
- [ ] **Task**: 设计和创建 `background_task_config` 表
- 表结构:
```sql
CREATE TABLE background_task_config (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
config_key VARCHAR(100) UNIQUE, -- 配置键
config_value VARCHAR(500), -- 配置值
description TEXT, -- 描述
is_editable BOOLEAN, -- 是否可编辑
created_at DATETIME,
updated_at DATETIME
)
```
- **预计工作量**: 1小时
- **相关文件**: `src/DB/Scripts/CreateBackgroundTaskConfigTable.sql`
#### 2.3 创建日志数据访问层
- [ ] **Task**: 为日志表创建Repository
- [ ] `IBackgroundTaskLogRepository` 接口
- [ ] `BackgroundTaskLogRepository` 实现
- [ ] 关键方法:
- `AddLogAsync()` - 添加日志
- `GetLogsAsync()` - 查询日志
- `GetLatestExecutionAsync()` - 获取最后一次执行信息
- **预计工作量**: 3小时
- **相关文件**:
- `src/DAL/Interfaces/IBackgroundTaskLogRepository.cs`
- `src/DAL/Repositories/BackgroundTaskLogRepository.cs`
#### 2.4 创建配置数据访问层
- [ ] **Task**: 为配置表创建Repository
- [ ] `IBackgroundTaskConfigRepository` 接口
- [ ] `BackgroundTaskConfigRepository` 实现
- [ ] 关键方法:
- `GetConfigAsync()` - 获取配置
- `UpdateConfigAsync()` - 更新配置
- `GetAllConfigsAsync()` - 获取所有配置
- **预计工作量**: 2小时
- **相关文件**:
- `src/DAL/Interfaces/IBackgroundTaskConfigRepository.cs`
- `src/DAL/Repositories/BackgroundTaskConfigRepository.cs`
### 阶段3: 业务逻辑层 (优先级: ⭐⭐⭐)
#### 3.1 任务执行日志记录
- [ ] **Task**: 在LabelPdfCacheService中添加日志记录
- [ ] `ProcessPendingTasksAsync()` 方法添加日志记录
- 记录开始时间
- 记录处理数量
- 记录成功/失败数
- 记录执行耗时
- 记录错误信息
- [ ] 创建 `LogExecutionAsync()` 辅助方法
- **预计工作量**: 2小时
- **相关文件**: `src/BLL/Services/LabelPdfCacheService.cs`
#### 3.2 配置管理服务
- [ ] **Task**: 创建配置管理服务
- [ ] `IBackgroundTaskConfigService` 接口
- [ ] `BackgroundTaskConfigService` 实现
- [ ] 关键方法:
- `GetTaskIntervalAsync()` - 获取执行间隔
- `GetBatchSizeAsync()` - 获取批处理大小
- `GetRetryCountAsync()` - 获取重试次数
- `GetCutoffDateAsync()` - 获取时间截断日期
- `UpdateConfigAsync()` - 更新配置
- [ ] 配置缓存机制(内存缓存)
- **预计工作量**: 3小时
- **相关文件**:
- `src/BLL/Interfaces/IBackgroundTaskConfigService.cs`
- `src/BLL/Services/BackgroundTaskConfigService.cs`
#### 3.3 任务执行统计服务
- [ ] **Task**: 创建执行统计服务
- [ ] 关键方法:
- `GetExecutionStatsAsync()` - 获取执行统计
- `GetRecentExecutionsAsync()` - 获取最近执行记录
- `GetFailureRateAsync()` - 获取失败率
- `GetAverageDurationAsync()` - 获取平均耗时
- **预计工作量**: 2小时
- **相关文件**: `src/BLL/Services/LabelPdfCacheService.cs`
#### 3.4 动态配置加载
- [ ] **Task**: 实现运行时动态配置
- [ ] 定时刷新配置缓存(每分钟)
- [ ] 配置变更事件通知
- [ ] `LabelPdfCacheBackgroundService` 支持动态间隔
- **预计工作量**: 3小时
- **相关文件**:
- `src/CONTROLLER/BackgroundServices/LabelPdfCacheBackgroundService.cs`
- `src/BLL/Services/BackgroundTaskConfigService.cs`
### 阶段4: 控制器和API (优先级: ⭐⭐)
#### 4.1 扩展LabelController
- [ ] **Task**: 添加定时任务管理相关API端点
- [ ] 暂停/恢复/状态接口(✅ 已规划)
- [ ] 配置管理接口POST/GET
- [ ] 日志查询接口
- [ ] 执行统计接口
- [ ] 手动触发接口
- **预计工作量**: 4小时
- **相关文件**: `src/CONTROLLER/Controllers/LabelController.cs`
#### 4.2 API文档更新
- [ ] **Task**: 更新API文档
- [ ] 添加新接口文档
- [ ] 请求/响应示例
- [ ] 错误码说明
- [ ] 使用场景说明
- **预计工作量**: 2小时
- **相关文件**: `API_Documentation_zh.md`
### 阶段5: 告警和监控 (优先级: ⭐⭐)
#### 5.1 告警机制
- [ ] **Task**: 实现定时任务告警
- [ ] 失败告警
- [ ] 长时间未执行告警
- [ ] 执行超时告警
- [ ] 处理数量异常告警
- [ ] 告警通知方式:
- 邮件通知
- 系统消息
- Webhook回调
- **预计工作量**: 5小时
- **相关文件**:
- `src/BLL/Services/LabelPdfCacheService.cs`
- `src/BLL/Services/AlertService.cs` (新建)
#### 5.2 性能监控
- [ ] **Task**: 添加性能指标监控
- [ ] 执行耗时分析
- [ ] 处理速度分析
- [ ] 失败率分析
- [ ] 资源使用率监控
- **预计工作量**: 3小时
- **相关文件**: `src/BLL/Services/PerformanceMetricsService.cs` (新建)
### 阶段6: 管理后台 (优先级: ⭐)
#### 6.1 Dashboard设计
- [ ] **Task**: 创建定时任务管理Dashboard页面
- [ ] 实时运行状态展示
- [ ] 近期执行记录列表
- [ ] 执行统计图表
- [ ] 配置管理界面
- [ ] 控制按钮(暂停/恢复/手动执行)
- **预计工作量**: 8小时前端
- **相关文件**: 前端项目
#### 6.2 配置管理页面
- [ ] **Task**: 创建配置管理界面
- [ ] 执行间隔配置
- [ ] 批处理大小配置
- [ ] 重试策略配置
- [ ] 告警规则配置
- [ ] 配置历史记录
- **预计工作量**: 6小时前端
#### 6.3 日志查询页面
- [ ] **Task**: 创建日志查询界面
- [ ] 日志列表
- [ ] 高级筛选
- [ ] 详情查看
- [ ] 日志导出
- **预计工作量**: 4小时前端
### 阶段7: 测试和文档 (优先级: ⭐⭐)
#### 7.1 单元测试
- [ ] **Task**: 编写定时任务相关单元测试
- [ ] `BackgroundTaskConfigService` 测试
- [ ] `BackgroundTaskLogRepository` 测试
- [ ] `LabelPdfCacheService` 日志记录测试
- [ ] 测试覆盖率 >= 80%
- **预计工作量**: 4小时
- **相关文件**: `src/Tests/`
#### 7.2 集成测试
- [ ] **Task**: 编写集成测试
- [ ] API接口测试
- [ ] 数据库操作测试
- [ ] 定时任务执行测试
- **预计工作量**: 3小时
- **相关文件**: `src/Tests/`
#### 7.3 文档完善
- [ ] **Task**: 完善定时任务管理文档
- [ ] 部署和配置文档
- [ ] API使用指南
- [ ] 故障排查指南
- [ ] 性能优化指南
- **预计工作量**: 3小时
- **相关文件**: `.trae/docs/BackgroundTasks/`
#### 7.4 用户手册
- [ ] **Task**: 编写最终用户手册
- [ ] 功能说明
- [ ] 操作流程
- [ ] 常见问题解答
- **预计工作量**: 2小时
---
## 📊 工作量统计
| 阶段 | 任务数 | 预计工作量 | 优先级 |
|------|-------|----------|-------|
| 阶段1: API层完善 | 3 | 9小时 | ⭐⭐⭐ |
| 阶段2: 数据存储层 | 4 | 7小时 | ⭐⭐⭐ |
| 阶段3: 业务逻辑层 | 4 | 10小时 | ⭐⭐⭐ |
| 阶段4: 控制器和API | 2 | 6小时 | ⭐⭐ |
| 阶段5: 告警和监控 | 2 | 8小时 | ⭐⭐ |
| 阶段6: 管理后台 | 3 | 18小时 | ⭐ |
| 阶段7: 测试和文档 | 4 | 12小时 | ⭐⭐ |
| **总计** | **22** | **70小时** | - |
---
## 🔄 执行顺序
建议按以下顺序执行:
1. **第1周**: 阶段2 (数据存储层) + 阶段1 (API层)
2. **第2周**: 阶段3 (业务逻辑层)
3. **第3周**: 阶段4 (控制器) + 阶段7 (测试)
4. **第4周**: 阶段5 (告警监控)
5. **第5-6周**: 阶段6 (管理后台) + 文档完善
---
## 🎓 技术栈
- **后端**: ASP.NET Core, C#
- **数据库**: MySQL, SqlSugar ORM
- **日志**: Serilog, ILogger
- **缓存**: 内存缓存
- **前端**: Vue.js / React (待确定)
- **图表**: ECharts / Chart.js
---
## ✅ 验收标准
### 功能完整性
- [ ] 所有API接口都已实现并测试通过
- [ ] 定时任务配置可动态修改
- [ ] 执行日志完整记录
- [ ] 告警机制正常工作
### 性能要求
- [ ] API响应时间 < 500ms
- [ ] 日志查询 < 2s (1000条数据)
- [ ] 内存占用 < 100MB
### 用户体验
- [ ] Dashboard直观易用
- [ ] 错误信息清晰易懂
- [ ] 支持中文界面
### 文档完整性
- [ ] API文档完整
- [ ] 用户手册完整
- [ ] 部署文档完整
---
## 🚀 后续计划
- [ ] 考虑微服务化部署
- [ ] 支持分布式定时任务
- [ ] 任务执行链
- [ ] 自适应调度算法
- [ ] 支持Cron表达式配置
---
## 📞 联系方式
**项目经理**: TBD
**技术主管**: TBD
**前端负责人**: TBD
---
**文档创建日期**: 2026-05-14
**最后更新**: 2026-05-14
**版本**: 1.0
**状态**: 📋 待审批

View File

@@ -0,0 +1,514 @@
# 面单标签PDF缓存系统 API 文档
## 概述
本文档描述了面单标签PDF缓存系统的API接口。该系统用于存储和管理物流面单的PDF标签字节流支持批量解析、条码识别和缓存统计功能。
---
## 基础信息
### API基地址
```
http://[服务器地址]:[端口]/api/label
```
### 支持的HTTP方法
- `GET` - 获取数据
- `POST` - 创建或提交数据
### 响应格式
所有API响应都是JSON格式包含以下顶层字段
- `status` - 状态标识 (`success``error`)
- `message` - 状态消息
- `data` - 响应数据(成功时)或 `errorDetails` - 错误详情(失败时)
---
## API 接口列表
### 1. 批量解析标签数据
#### 接口信息
- **路由**: `/batch-parse`
- **方法**: `POST`
- **URL**: `/api/label/batch-parse`
- **描述**: 批量解析订单标签数据,支持多种模式。可用于补充解析已有的订单标签。
#### 请求参数
| 参数名 | 类型 | 必需 | 说明 |
|--------|------|------|------|
| Mode | string | 是 | 解析模式,必须是以下值之一:`all``range``customer``single` |
| WaybillNumber | string | 否 | 中性面单单号。在 `single` 模式下必需 |
| CustomerId | int | 否 | 客户ID。在 `customer` 模式下必需 |
| StartDate | datetime | 否 | 开始日期。在 `range` 模式下必需,格式:`YYYY-MM-DD` 或 ISO 8601 |
| EndDate | datetime | 否 | 结束日期。在 `range` 模式下必需,格式:`YYYY-MM-DD` 或 ISO 8601 |
| Limit | int | 否 | 限制返回的最大数量。默认值1000 |
#### 模式说明
| 模式 | 说明 | 必需参数 |
|------|------|---------|
| `all` | 处理所有有标签的订单 | 无 |
| `range` | 按时间范围处理 | StartDate, EndDate |
| `customer` | 按指定客户处理 | CustomerId |
| `single` | 处理单条订单 | WaybillNumber |
#### 请求示例
**模式1: 处理所有有标签的订单**
```json
{
"mode": "all",
"limit": 500
}
```
**模式2: 按时间范围处理**
```json
{
"mode": "range",
"startDate": "2024-01-01",
"endDate": "2024-01-31",
"limit": 1000
}
```
**模式3: 按客户处理**
```json
{
"mode": "customer",
"customerId": 123,
"limit": 500
}
```
**模式4: 处理单条订单**
```json
{
"mode": "single",
"waybillNumber": "1Z999AA10123456784"
}
```
#### 成功响应示例
```json
{
"status": "success",
"message": "批量解析完成",
"data": {
"totalProcessed": 100,
"successCount": 98,
"errorCount": 2,
"mode": "all"
}
}
```
#### 失败响应示例
**参数验证失败**
```json
{
"status": "error",
"message": "请提供有效的请求参数"
}
```
**模式参数缺失**
```json
{
"status": "error",
"message": "时间范围模式需要 StartDate 和 EndDate 参数"
}
```
```json
{
"status": "error",
"message": "客户模式需要 CustomerId 参数"
}
```
```json
{
"status": "error",
"message": "单条模式需要 WaybillNumber 参数"
}
```
**无效的处理模式**
```json
{
"status": "error",
"message": "无效的处理模式,请使用: all, range, customer, single"
}
```
**系统异常**
```json
{
"status": "error",
"message": "批量解析失败",
"errorDetails": "[具体错误信息]"
}
```
#### 响应字段说明
**成功响应 (data 字段)**
| 字段 | 类型 | 说明 |
|------|------|------|
| totalProcessed | int | 处理的总订单数量 |
| successCount | int | 成功处理的订单数量 |
| errorCount | int | 处理失败的订单数量 |
| mode | string | 使用的解析模式 |
#### HTTP状态码
- `200` - 请求成功处理即使业务逻辑返回error状态也是200
- `400` - 请求参数错误
---
### 2. 查看缓存统计信息
#### 接口信息
- **路由**: `/cache-statistics`
- **方法**: `GET`
- **URL**: `/api/label/cache-statistics`
- **描述**: 获取PDF标签缓存的统计信息包括总数、成功数、失败数、性能指标等。
#### 请求参数
#### 成功响应示例
```json
{
"status": "success",
"message": "缓存统计信息",
"data": {
"totalRecords": 5000,
"successRecords": 4950,
"failedRecords": 30,
"invalidRecords": 15,
"pendingRecords": 5,
"withBarcodeRecords": 4890,
"averageParseDurationMs": 245.5,
"maxParseDurationMs": 1200,
"minParseDurationMs": 50
}
}
```
#### 失败响应示例
```json
{
"status": "error",
"message": "获取统计信息失败",
"errorDetails": "[具体错误信息]"
}
```
#### 响应字段说明
**data 字段**
| 字段 | 类型 | 说明 |
|------|------|------|
| totalRecords | int | 缓存表中的总记录数 |
| successRecords | int | 处理成功的记录数Status=1 |
| failedRecords | int | 处理失败的记录数Status=2 |
| invalidRecords | int | 无效的记录数Status=3 |
| pendingRecords | int | 待处理的记录数Status=0 |
| withBarcodeRecords | int | 成功识别条码的记录数 |
| averageParseDurationMs | double | 平均PDF解析耗时毫秒 |
| maxParseDurationMs | int | 最大PDF解析耗时毫秒 |
| minParseDurationMs | int | 最小PDF解析耗时毫秒 |
#### 缓存记录状态说明
| 状态值 | 说明 |
|--------|------|
| 0 | 待处理 - 刚创建或待重试的记录 |
| 1 | 成功 - PDF已缓存且处理成功 |
| 2 | 失败 - 处理失败,超过重试次数 |
| 3 | 无效 - 缓存已失效或过期 |
#### HTTP状态码
- `200` - 请求成功处理
---
## 数据模型
### BatchParseLabelRequest
批量解析请求模型
```typescript
{
mode: string; // 必需all | range | customer | single
waybillNumber?: string; // 可选:单条模式下的面单号
customerId?: number; // 可选客户ID
startDate?: string; // 可选:开始日期 (YYYY-MM-DD)
endDate?: string; // 可选:结束日期 (YYYY-MM-DD)
limit?: number; // 可选最大数量默认1000
}
```
### CacheStatistics
缓存统计数据模型
```typescript
{
totalRecords: number; // 总记录数
successRecords: number; // 成功记录数
failedRecords: number; // 失败记录数
invalidRecords: number; // 无效记录数
pendingRecords: number; // 待处理记录数
withBarcodeRecords: number; // 包含条码的记录数
averageParseDurationMs: number; // 平均解析时间(毫秒)
maxParseDurationMs: number; // 最大解析时间(毫秒)
minParseDurationMs: number; // 最小解析时间(毫秒)
}
```
---
## 使用示例
### JavaScript/TypeScript
#### 使用Fetch API
```javascript
// 1. 批量解析 - 处理所有有标签的订单
const batchParseAllOrders = async () => {
const response = await fetch('http://localhost:8080/api/label/batch-parse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
mode: 'all',
limit: 500
})
});
const data = await response.json();
console.log(data);
};
// 2. 批量解析 - 按时间范围
const batchParseByDateRange = async () => {
const response = await fetch('http://localhost:8080/api/label/batch-parse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
mode: 'range',
startDate: '2024-01-01',
endDate: '2024-01-31',
limit: 1000
})
});
const data = await response.json();
console.log(data);
};
// 3. 批量解析 - 按客户
const batchParseByCustomer = async () => {
const response = await fetch('http://localhost:8080/api/label/batch-parse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
mode: 'customer',
customerId: 123,
limit: 500
})
});
const data = await response.json();
console.log(data);
};
// 4. 批量解析 - 单条订单
const batchParseSingle = async () => {
const response = await fetch('http://localhost:8080/api/label/batch-parse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
mode: 'single',
waybillNumber: '1Z999AA10123456784'
})
});
const data = await response.json();
console.log(data);
};
// 5. 获取缓存统计
const getCacheStatistics = async () => {
const response = await fetch('http://localhost:8080/api/label/cache-statistics');
const data = await response.json();
console.log(data);
};
```
#### 使用Axios
```javascript
import axios from 'axios';
const baseURL = 'http://localhost:8080/api/label';
// 1. 批量解析 - 处理所有有标签的订单
const batchParseAll = async () => {
try {
const response = await axios.post(`${baseURL}/batch-parse`, {
mode: 'all',
limit: 500
});
console.log(response.data);
} catch (error) {
console.error('Error:', error);
}
};
// 2. 获取缓存统计
const getStatistics = async () => {
try {
const response = await axios.get(`${baseURL}/cache-statistics`);
console.log(response.data);
} catch (error) {
console.error('Error:', error);
}
};
```
### Python
```python
import requests
import json
from datetime import datetime
BASE_URL = "http://localhost:8080/api/label"
# 1. 批量解析 - 处理所有有标签的订单
def batch_parse_all():
payload = {
"mode": "all",
"limit": 500
}
response = requests.post(f"{BASE_URL}/batch-parse", json=payload)
print(json.dumps(response.json(), indent=2))
# 2. 批量解析 - 按时间范围
def batch_parse_by_date_range():
payload = {
"mode": "range",
"startDate": "2024-01-01",
"endDate": "2024-01-31",
"limit": 1000
}
response = requests.post(f"{BASE_URL}/batch-parse", json=payload)
print(json.dumps(response.json(), indent=2))
# 3. 批量解析 - 按客户
def batch_parse_by_customer():
payload = {
"mode": "customer",
"customerId": 123,
"limit": 500
}
response = requests.post(f"{BASE_URL}/batch-parse", json=payload)
print(json.dumps(response.json(), indent=2))
# 4. 批量解析 - 单条订单
def batch_parse_single():
payload = {
"mode": "single",
"waybillNumber": "1Z999AA10123456784"
}
response = requests.post(f"{BASE_URL}/batch-parse", json=payload)
print(json.dumps(response.json(), indent=2))
# 5. 获取缓存统计
def get_cache_statistics():
response = requests.get(f"{BASE_URL}/cache-statistics")
print(json.dumps(response.json(), indent=2))
# 使用示例
if __name__ == "__main__":
# batch_parse_all()
# batch_parse_by_date_range()
# batch_parse_by_customer()
batch_parse_single()
# get_cache_statistics()
```
---
## 错误处理
### 常见错误及解决方案
| 错误信息 | 原因 | 解决方案 |
|---------|------|---------|
| 请提供有效的请求参数 | 请求体为空或Mode字段缺失 | 检查请求JSON格式确保Mode字段存在 |
| 时间范围模式需要 StartDate 和 EndDate 参数 | range模式缺少日期参数 | 添加StartDate和EndDate参数 |
| 客户模式需要 CustomerId 参数 | customer模式缺少客户ID | 添加CustomerId参数 |
| 单条模式需要 WaybillNumber 参数 | single模式缺少面单号 | 添加WaybillNumber参数 |
| 无效的处理模式 | Mode值不是允许的四种之一 | 使用 all、range、customer、single 之一 |
| 批量解析失败 | 服务器内部错误 | 查看errorDetails字段检查服务器日志 |
| 获取统计信息失败 | 服务器内部错误 | 查看errorDetails字段检查服务器日志 |
---
## 性能建议
1. **批量大小**: 建议Limit不要超过5000避免单次请求处理过多数据
2. **日期范围**: 时间范围模式时建议不要跨越太长的时间跨度如超过90天
3. **请求频率**: 避免频繁发送相同的请求建议间隔至少5秒
4. **缓存更新**: 定时任务会自动处理待处理订单,无需频繁手动调用
---
## FAQ
**Q: 批量解析后多久能看到结果?**
A: 批量解析是异步处理的。解析请求返回后,系统会在后台处理。通常需要几秒到几分钟,取决于数据量和系统负载。
**Q: 可以同时发送多个批量解析请求吗?**
A: 可以但建议不要同时发送超过10个请求避免系统过载。
**Q: 如何判断某个订单是否已被缓存?**
A: 调用cache-statistics接口查看successRecords字段。或者查询订单表中对应订单的缓存状态。
**Q: 缓存数据会被清理吗?**
A: 缓存数据会根据业务规则进行清理。无效的缓存会被标记为Status=3并可能在定期维护时删除。
**Q: 如何处理解析失败的订单?**
A: 系统会自动重试失败的订单最多3次。重试都失败后会标记为Status=2。可以通过single模式重新尝试解析单个订单。
---
## 更新历史
| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0 | 2024-01-01 | 初版发布包含batch-parse和cache-statistics接口 |
---
## 联系方式
如有任何问题或建议,请联系技术支持团队。