# labelBytes 为空时记录缓存失败修改

## 修改说明

当 PDF 字节流 (`labelBytes`) 为 `null` 或长度为 0 时（表示源文件有问题），直接将缓存状态记录为**失败** (`Status = 2`)，而不再尝试进行条码识别。

---

## 修改位置

**文件**: `src/BLL/Services/LabelPdfCacheService.cs`  
**方法**: `ProcessSingleCacheTaskAsync()`  
**行号**: 324-330

---

## 修改前后对比

### 修改前
```csharp
byte[] labelBytes;
// 解析Label内容
// ... 省略解析代码 ...

// 校验PDF页数（直接进行校验，没有检查labelBytes是否为空）
int pageCount = GetPdfPageCount(labelBytes);
```

### 修改后
```csharp
byte[] labelBytes;
// 解析Label内容
// ... 省略解析代码 ...

// 当labelBytes为null或为空时，标记为失败（源文件有问题）
if (labelBytes == null || labelBytes.Length == 0)
{
    var duration = (int)(DateTime.UtcNow - startTime).TotalMilliseconds;
    _logger.LogError("labelBytes is null or empty for waybill: {number}, duration: {duration}ms - 源文件有问题", waybillNumber, duration);
    var newRetryCount = (existingCache?.RetryCount ?? 0) + 1;
    await UpdateCacheStatus(existingCache, waybillNumber, 2, "源文件为空或无效，无法提取条码", newRetryCount, duration);
    return false;
}

// 校验PDF页数
int pageCount = GetPdfPageCount(labelBytes);
```

---

## 关键改动

### 1. 新增空值检查
```csharp
if (labelBytes == null || labelBytes.Length == 0)
```

### 2. 标记为失败状态
```csharp
await UpdateCacheStatus(
    existingCache, 
    waybillNumber, 
    2,  // ✅ Status = 2 (失败)
    "源文件为空或无效，无法提取条码", 
    newRetryCount, 
    duration
);
```

### 3. 日志记录为ERROR级别
```csharp
_logger.LogError("labelBytes is null or empty for waybill: {number}, duration: {duration}ms - 源文件有问题", waybillNumber, duration);
```

### 4. 增加重试计数
```csharp
var newRetryCount = (existingCache?.RetryCount ?? 0) + 1;
```

---

## 处理流程

### 修改前流程
```
获取labelBytes
    ↓
检查PDF页数 ← 可能失败 (如果labelBytes为空)
    ↓
提取条码信息 ← 可能失败
    ↓
保存缓存
```

### 修改后流程
```
获取labelBytes
    ↓
【新增】检查labelBytes是否为空或null
    ├─ YES: 标记为失败(Status=2)→ 增加重试计数 → 返回false
    └─ NO: 继续
            ↓
        检查PDF页数
            ↓
        提取条码信息
            ↓
        保存缓存
```

---

## 缓存状态

当 labelBytes 为空时（源文件有问题），缓存记录将被保存为：

```
Status: 2 (失败)
ErrorMessage: "源文件为空或无效，无法提取条码"
BarcodeNumber: NULL (无法提取)
BarcodeType: 0 (无条码)
BarcodeConfidence: NULL (无置信度)
RetryCount: 前次重试次数 + 1
解析耗时: 记录实际耗时（毫秒）
```

### 重试机制

- 首次失败: `RetryCount = 1`
- 第二次失败: `RetryCount = 2`
- 第三次失败: `RetryCount = 3` → 如果 `RetryCount >= MaxRetryCount (3)`，则标记为最终失败

---

## 错误信息

缓存表中 `error_message` 字段将记录：
```
源文件为空或无效，无法提取条码
```

日志中将记录：
```
labelBytes is null or empty for waybill: {number}, duration: {duration}ms - 源文件有问题
```

---

## 编译验证

✅ **编译成功** - 整个项目编译无错误

---

## 测试验证清单

- [ ] labelBytes 为 null 时，缓存状态为 2（失败）
- [ ] labelBytes.Length 为 0 时，缓存状态为 2（失败）
- [ ] 缓存记录中 `status` 字段为 2
- [ ] 缓存记录中 `error_message` 包含 "源文件为空或无效"
- [ ] `RetryCount` 正确增加
- [ ] 日志级别为 ERROR
- [ ] 不会尝试进行条码识别
- [ ] 返回 false（处理失败）

---

## 影响范围

### 直接影响
- `ProcessSingleCacheTaskAsync()` 方法
- 缓存表中新插入的记录（status = 2）

### 间接影响
- 定时任务执行流程
- batch-parse API 处理流程
- 缓存统计查询（failedRecords 增加）

---

## 业务含义

### 缓存状态说明

| 状态值 | 含义 | 原因 | 是否重试 |
|--------|------|------|---------|
| 0 | 待处理 | 刚创建或待重试 | ✅ 会重试 |
| 1 | 成功 | 成功缓存并识别条码 | ❌ 不重试 |
| 2 | 失败 | 源文件问题或超过重试次数 | ✅ 最多3次 |
| 3 | 无效 | 缓存过期或被清理 | ❌ 不重试 |

### 源文件问题的情况

当出现以下情况时会被记录为失败：
- 源文件为 null
- 源文件字节长度为 0
- 源文件损坏（无法解析）
- 源文件格式不正确

---

**修改日期**: 2026-05-14  
**版本**: 1.1（更新为失败状态）  
**编译状态**: ✅ 成功  
**测试状态**: 待测试
