上传源代码版本
This commit is contained in:
257
.trae/documents/pdf_to_bitmap_rendering_plan.md
Normal file
257
.trae/documents/pdf_to_bitmap_rendering_plan.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# PDF页面渲染方案 - ConvertPdfFirstPageToBitmap改造
|
||||
|
||||
## 问题诊断
|
||||
当前`ConvertPdfFirstPageToBitmap`方法只生成纯白色Bitmap,未实现PDF页面的实际渲染,导致条码识别时无法识别到PDF中的内容(包括条码)。
|
||||
|
||||
## 关键需求更新 ⭐
|
||||
1. **充分利用现有能力**:项目代码中已有将PDF URL转换成字节流的功能,此方案应与之集成
|
||||
2. **确保缓存完整性**:只要验证了PDF有效性,不管是否成功提取条码,都必须将PDF字节流缓存到数据库
|
||||
3. **保障业务连续性**:缓存的目的是确保现场可以正常进行面单打印,条码识别是增强功能非必须功能
|
||||
|
||||
## 现有资源确认
|
||||
- ✅ 项目已集成`DinkToPdf`库(通过`PdfConverterSingleton`)
|
||||
- ✅ 项目已有`System.Drawing`和`System.Drawing.Imaging`
|
||||
- ✅ 项目已有PDF URL→字节流的转换能力(在下载/上传流程中)
|
||||
- ✅ 完整的CORE SDK支持
|
||||
|
||||
## 改进后的解决方案
|
||||
|
||||
### 核心逻辑调整
|
||||
|
||||
#### 原始流程(问题):
|
||||
```
|
||||
下载PDF → ConvertPdfFirstPageToBitmap(纯白) → 条码识别(失败) → 缓存PDF
|
||||
```
|
||||
|
||||
#### 改进流程(推荐):
|
||||
```
|
||||
下载PDF(已是字节流)
|
||||
↓
|
||||
验证PDF有效性 ✅ (关键点:必须执行)
|
||||
├─ 有效 → 立即缓存PDF字节流到数据库 ⭐ (优先级:高)
|
||||
└─ 无效 → 标记缓存失败,不阻塞业务
|
||||
↓
|
||||
条码识别(可选功能)
|
||||
├─ ConvertPdfFirstPageToBitmap(渲染)
|
||||
├─ RecognizeBarcodeAsync(识别)
|
||||
└─ 识别成功 → 更新缓存的条码字段(选择性)
|
||||
└─ 识别失败 → 不影响已缓存的PDF数据 ⭐
|
||||
```
|
||||
|
||||
### 关键设计原则
|
||||
|
||||
#### 原则1:缓存优先保障业务
|
||||
- **验证通过即缓存**:PDF验证成功→立即缓存PDF字节流
|
||||
- **条码是增强功能**:条码识别失败不影响PDF缓存
|
||||
- **现场打印保障**:确保缓存的PDF数据始终可用于打印
|
||||
|
||||
#### 原则2:充分利用现有能力
|
||||
- **复用字节流处理**:项目中已有的PDF URL→字节流转换逻辑
|
||||
- **整合现有DinkToPdf**:如使用DinkToPdf转换,复用已有的`PdfConverterSingleton`
|
||||
- **集成Ghostscript**:如采用Ghostscript方案,将PDF字节流直接传入
|
||||
|
||||
#### 原则3:非阻塞设计
|
||||
- **PDF缓存同步**:验证+缓存为关键路径,必须快速完成
|
||||
- **条码识别异步**:条码识别为后续异步操作,失败不影响主流程
|
||||
|
||||
## 实施方案详细设计
|
||||
|
||||
### 方案A:基于现有DinkToPdf(最小化改动) ✅ **推荐短期**
|
||||
**优点**:
|
||||
- 项目已集成DinkToPdf
|
||||
- 与现有代码体系一致
|
||||
- 改动最小
|
||||
|
||||
**流程**:
|
||||
```
|
||||
PDF字节流(已有)
|
||||
↓
|
||||
验证PDF + 缓存PDF字节流
|
||||
↓
|
||||
使用DinkToPdf或项目已有能力进行渲染
|
||||
↓
|
||||
条码识别(非必须)
|
||||
```
|
||||
|
||||
### 方案B:集成Ghostscript(生产级方案) ✅ **推荐长期**
|
||||
**优点**:
|
||||
- 渲染效果最佳
|
||||
- 专业PDF处理
|
||||
- 性能和质量兼优
|
||||
|
||||
**流程**:
|
||||
```
|
||||
PDF字节流(已有)
|
||||
↓
|
||||
验证PDF + 缓存PDF字节流
|
||||
↓
|
||||
使用Ghostscript渲染第一页→Bitmap
|
||||
↓
|
||||
条码识别(非必须)
|
||||
```
|
||||
|
||||
## 推荐实施流程
|
||||
|
||||
### 第一阶段:保障业务连续性(高优先级)
|
||||
1. **改造缓存逻辑**:确保PDF验证通过→立即缓存
|
||||
- 在`ProcessSingleCacheTask`中调整顺序
|
||||
- 先保存PDF字节流,再进行条码识别
|
||||
- 条码识别失败不回滚缓存
|
||||
|
||||
2. **缓存表结构确认**:确保能存储完整PDF数据
|
||||
- PdfBytes字段:存储PDF二进制数据 ✅ (已有)
|
||||
- Status字段:标记缓存状态 ✅ (已有)
|
||||
- 条码字段:可选,识别成功时更新 ✅ (已有)
|
||||
|
||||
### 第二阶段:改造ConvertPdfFirstPageToBitmap方法(中优先级)
|
||||
|
||||
#### 选项A1:使用DinkToPdf现有能力
|
||||
```
|
||||
输入:byte[] pdfBytes(已有字节流)
|
||||
处理流程:
|
||||
1. 使用PdfConverterSingleton进行转换
|
||||
2. 或复用项目中TagGenerationService的PDF处理逻辑
|
||||
3. 生成Bitmap用于条码识别
|
||||
输出:Bitmap对象
|
||||
```
|
||||
|
||||
#### 选项B1:集成Ghostscript.NET
|
||||
```bash
|
||||
dotnet add package Ghostscript.NET
|
||||
```
|
||||
|
||||
```
|
||||
输入:byte[] pdfBytes(已有字节流)
|
||||
处理流程:
|
||||
1. 保存PDF字节流到临时文件
|
||||
2. 使用GhostscriptRasterizer初始化
|
||||
3. 设置DPI为200
|
||||
4. 渲染第一页→Image
|
||||
5. 转换为Bitmap
|
||||
6. 清理临时文件
|
||||
输出:Bitmap对象
|
||||
```
|
||||
|
||||
### 第三阶段:异常处理和性能优化(低优先级)
|
||||
- 渲染超时处理(3-5秒)
|
||||
- 大文件内存管理
|
||||
- 缓存渲染结果
|
||||
|
||||
## 实现要点详解
|
||||
|
||||
### 关键修改1:缓存优先原则
|
||||
**在ProcessSingleCacheTask中**:
|
||||
```
|
||||
原始:下载PDF → 条码识别 → 成功则保存 → 失败则标记失败
|
||||
改进:下载PDF → 验证PDF → 立即保存PDF → 异步进行条码识别
|
||||
```
|
||||
|
||||
**新增逻辑**:
|
||||
```csharp
|
||||
// 1. 获取PDF字节流(现有能力)
|
||||
byte[] labelBytes = // ... 现有转换逻辑
|
||||
|
||||
// 2. 验证PDF有效性
|
||||
int pageCount = GetPdfPageCount(labelBytes); // 验证通过
|
||||
|
||||
// 3. 立即保存PDF到缓存(同步)
|
||||
await SaveCacheAsync(
|
||||
waybillNumber,
|
||||
labelBytes,
|
||||
pageCount,
|
||||
labelBytes.Length,
|
||||
finalMileTrackingNumber,
|
||||
customerId
|
||||
// 暂不传入条码信息
|
||||
);
|
||||
|
||||
// 4. 异步进行条码识别(不阻塞,失败不影响缓存)
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var (barcodeNumber, barcodeType, confidence) =
|
||||
await ExtractBarcodeFromPdfAsync(labelBytes);
|
||||
|
||||
if (!string.IsNullOrEmpty(barcodeNumber))
|
||||
{
|
||||
// 条码识别成功,更新缓存中的条码字段
|
||||
await UpdateCacheWithBarcodeAsync(waybillNumber, barcodeNumber, barcodeType, confidence);
|
||||
}
|
||||
}
|
||||
catch { /* 静默失败,不影响已缓存的PDF */ }
|
||||
});
|
||||
```
|
||||
|
||||
### 关键修改2:PDF字节流复用
|
||||
**充分利用现有能力**:
|
||||
```
|
||||
现有代码中的PDF URL → 字节流转换
|
||||
↓
|
||||
直接传给ConvertPdfFirstPageToBitmap
|
||||
↓
|
||||
直接传给SaveCacheAsync
|
||||
```
|
||||
|
||||
**好处**:
|
||||
- 无需重复转换
|
||||
- 一次转换多次使用
|
||||
- 性能最优
|
||||
|
||||
## 风险评估与对策
|
||||
|
||||
### 风险1:缓存失败导致无备份
|
||||
**风险**:如果缓存失败,可能没有PDF数据用于打印
|
||||
**对策**:
|
||||
- 重试机制:缓存失败重试3次
|
||||
- 业务降级:缓存失败时保留URL,现场可实时下载
|
||||
- 监控告警:记录所有缓存失败的case
|
||||
|
||||
### 风险2:条码识别阻塞缓存
|
||||
**风险**:条码识别耗时影响业务
|
||||
**对策**:
|
||||
- 使用异步Task(已设计)
|
||||
- 设置超时控制
|
||||
- 失败自动降级
|
||||
|
||||
### 风险3:Ghostscript部署依赖
|
||||
**风险**:Ghostscript需要系统配置
|
||||
**对策**:
|
||||
- 选择包含预编译二进制的NuGet版本
|
||||
- 统一部署文档和脚本
|
||||
- 开发环境提前测试
|
||||
|
||||
## 验证方案
|
||||
|
||||
### 验收标准
|
||||
- ✅ **业务保障**:无论条码识别成否,PDF都被成功缓存
|
||||
- ✅ **调试可见**:SaveDebugImage输出显示真实PDF内容(非空白)
|
||||
- ✅ **条码增强**:条码识别成功率>80%(失败不影响业务)
|
||||
- ✅ **性能指标**:
|
||||
- PDF验证+缓存:<1秒
|
||||
- PDF渲染:<3秒
|
||||
- 条码识别:<2秒
|
||||
- ✅ **异常处理**:所有异常都被捕获,不影响主流程
|
||||
|
||||
### 测试场景
|
||||
1. 正常PDF→成功缓存+条码识别成功
|
||||
2. 正常PDF→成功缓存+条码识别失败(验收:PDF仍被缓存)
|
||||
3. 包含二维码的PDF→成功缓存+二维码识别
|
||||
4. 包含一维码的PDF→成功缓存+一维码识别
|
||||
5. 损坏的PDF→缓存失败,异常处理,业务降级
|
||||
6. 超大PDF→性能和内存测试
|
||||
|
||||
## 实施优先级
|
||||
1. **高优先级**:修改缓存逻辑,确保验证通过→立即缓存
|
||||
2. **高优先级**:改造ConvertPdfFirstPageToBitmap进行真实渲染
|
||||
3. **中优先级**:条码识别异步化,失败自动降级
|
||||
4. **低优先级**:性能优化和缓存结果
|
||||
|
||||
## 预期效果
|
||||
完成此方案后:
|
||||
- ✅ 业务连续性保障:缓存的PDF数据可用于现场打印
|
||||
- ✅ 渲染效果提升:调试图像显示真实PDF内容
|
||||
- ✅ 条码识别增强:成功识别条码但不强制依赖
|
||||
- ✅ 系统鲁棒性:异常不影响核心缓存功能
|
||||
- ✅ 现有资源复用:充分利用已有的字节流转换能力
|
||||
|
||||
Reference in New Issue
Block a user