417 lines
10 KiB
Markdown
417 lines
10 KiB
Markdown
# 实施检查清单与决策指南
|
||
|
||
**日期**: 2026-05-13
|
||
|
||
---
|
||
|
||
## 🎯 核心决策点
|
||
|
||
### 决策1:PDF渲染库选择
|
||
|
||
根据您项目特点(物流标签系统,PDF包含复杂图形和条码),以下是三种方案对比:
|
||
|
||
#### 方案A: GhostScript.NET (⭐ 推荐)
|
||
|
||
**参数**:
|
||
```csharp
|
||
dotnet add package Ghostscript.NET
|
||
// 需要系统预装 Ghostscript 或从nuget获取
|
||
```
|
||
|
||
**优点**:
|
||
- ✅ 行业标准,全球数百万用户
|
||
- ✅ 支持任何有效的PDF
|
||
- ✅ 渲染质量最高
|
||
- ✅ 性能稳定(50-200ms/页)
|
||
- ✅ 已被物流/打印行业广泛采用
|
||
|
||
**缺点**:
|
||
- ❌ 需要系统部署Ghostscript
|
||
- ❌ 首次安装配置较复杂
|
||
|
||
**适用场景**:生产环境、大规模部署
|
||
|
||
**示例代码**:
|
||
```csharp
|
||
private Bitmap? ConvertPdfFirstPageToBitmap(byte[] pdfBytes)
|
||
{
|
||
try
|
||
{
|
||
// 1. 将字节流写入临时文件
|
||
var tempPath = Path.Combine(Path.GetTempPath(), $"pdf_{Guid.NewGuid()}.pdf");
|
||
File.WriteAllBytes(tempPath, pdfBytes);
|
||
|
||
// 2. 使用GhostScript渲染
|
||
var rasterizer = new GhostscriptRasterizer();
|
||
rasterizer.Open(tempPath);
|
||
|
||
// 200 DPI, 获取第0页
|
||
Image image = rasterizer.GetPage(200, 200, 0);
|
||
var bitmap = new Bitmap(image);
|
||
|
||
rasterizer.Close();
|
||
File.Delete(tempPath);
|
||
|
||
return bitmap;
|
||
}
|
||
catch (Exception ex) { ... }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### 方案B: SelectPdf (用于快速验证)
|
||
|
||
**参数**:
|
||
```csharp
|
||
dotnet add package SelectPdf
|
||
// 个人开发免费许可
|
||
```
|
||
|
||
**优点**:
|
||
- ✅ 纯.NET库,无外部依赖
|
||
- ✅ API简单易用
|
||
- ✅ 个人开发免费
|
||
- ✅ 支持批量转换
|
||
|
||
**缺点**:
|
||
- ❌ 商业收费(企业)
|
||
- ❌ 某些高级功能需付费
|
||
|
||
**适用场景**:快速验证、开发测试
|
||
|
||
---
|
||
|
||
#### 方案C: iTextSharp (成本最低)
|
||
|
||
**参数**:
|
||
```csharp
|
||
dotnet add package iTextSharp
|
||
// 开源AGPL许可
|
||
```
|
||
|
||
**优点**:
|
||
- ✅ 完全开源
|
||
- ✅ 广泛使用
|
||
- ✅ 文档完善
|
||
|
||
**缺点**:
|
||
- ❌ 社区版不支持PDF渲染
|
||
- ❌ 专业版需商业许可
|
||
|
||
**适用场景**:不适合此项目(不支持渲染)
|
||
|
||
---
|
||
|
||
### 🔴 **选择建议**:
|
||
|
||
对于您的物流标签系统,**强烈推荐 GhostScript.NET**:
|
||
|
||
**理由**:
|
||
1. 物流行业事实标准(Ghostscript 是PDF打印业标准)
|
||
2. 标签PDF常包含高保真图形/二维码,需要高质量渲染
|
||
3. 性能满足实时需求(<200ms)
|
||
4. 可靠性已验证(全球工业级应用)
|
||
|
||
**风险最低** ✅
|
||
|
||
---
|
||
|
||
## ✅ 缓存流程确认
|
||
|
||
### 当前问题流程 ❌
|
||
```
|
||
下载 → 验证 → 条码识别❌ → 不缓存 → 失败
|
||
```
|
||
|
||
### 改进流程 ✅
|
||
|
||
**您的要求**:"只要验证了pdf的有效性,不管是否能够提取出二维码和一维码的内容都要将数据缓存"
|
||
|
||
**执行流程**:
|
||
```
|
||
下载PDF字节流
|
||
↓
|
||
验证(页数=1, 大小<900KB)
|
||
│
|
||
├─ 验证失败 → 记录失败,不缓存
|
||
│
|
||
▼ 验证成功
|
||
【立即缓存 ⭐】
|
||
└─ SaveCacheAsync(pdfBytes, Status=Success)
|
||
└─ 返回给用户 ✅
|
||
|
||
↓ (同时后台异步)
|
||
异步条码识别
|
||
├─ 成功 → 更新BarcodeNumber字段
|
||
└─ 失败 → 日志记录,PDF缓存保持有效 ✅
|
||
```
|
||
|
||
**确认要点**:
|
||
- [ ] ✅ 验证通过 → 立即缓存 (无需等待条码识别)
|
||
- [ ] ✅ 条码识别异步执行 (不阻塞用户请求)
|
||
- [ ] ✅ 识别失败 → PDF缓存仍然有效 (打印业务不中断)
|
||
|
||
---
|
||
|
||
## 📋 字段需求最终确认
|
||
|
||
### 必需字段(已确认)
|
||
|
||
```csharp
|
||
public long Id { get; set; }
|
||
public string NeutralWaybillNumber { get; set; } // ✅ 中性面单号
|
||
public byte[]? PdfBytes { get; set; } // ✅ PDF二进制内容
|
||
public int? PageCount { get; set; } // ✅ 页数(应该=1)
|
||
public int? FileSize { get; set; } // ✅ 文件大小
|
||
public byte Status { get; set; } // ✅ 缓存状态
|
||
public int RetryCount { get; set; } // ✅ 重试次数
|
||
public DateTime? LastRetryTime { get; set; } // ✅ 最后重试时间
|
||
public string? ErrorMessage { get; set; } // ✅ 错误信息
|
||
public DateTime CreatedTime { get; set; } // ✅ 创建时间
|
||
public DateTime UpdatedTime { get; set; } // ✅ 更新时间
|
||
```
|
||
|
||
### 标签关联字段(您要求添加)
|
||
|
||
```csharp
|
||
public string? FinalMileTrackingNumber { get; set; } // ✅ 尾程跟踪单号
|
||
public string? CustomerId { get; set; } // ✅ 客户ID
|
||
```
|
||
|
||
### 条码识别字段(新增,用于后续扩展)
|
||
|
||
```csharp
|
||
public string? BarcodeNumber { get; set; } // ✅ 识别的条码号
|
||
public byte BarcodeType { get; set; } // ✅ 条码类型(0=无, 1=1D, 2=2D)
|
||
public int? BarcodeConfidence { get; set; } // ✅ 识别置信度
|
||
public DateTime? BarcodeExtractTime { get; set; } // ✅ 识别时间
|
||
```
|
||
|
||
**需要添加其他字段吗**?比如:
|
||
- 订单ID?
|
||
- 原始标签URL?
|
||
- 渲染质量(DPI)记录?
|
||
|
||
---
|
||
|
||
## 🔧 实施步骤详细清单
|
||
|
||
### Phase 1: 环境准备 📦
|
||
|
||
- [ ] 1.1 - 在项目中添加 Ghostscript.NET 包
|
||
```bash
|
||
cd d:\EPproject\LabelReplaceServer
|
||
dotnet add package Ghostscript.NET
|
||
```
|
||
|
||
- [ ] 1.2 - 验证包安装成功
|
||
```bash
|
||
dotnet restore
|
||
```
|
||
|
||
- [ ] 1.3 - 开发机器验证 Ghostscript 依赖 (可选)
|
||
```bash
|
||
# 如果包提供了预编译的二进制文件,可能无需单独安装
|
||
# 若需要单独安装,访问: https://www.ghostscript.com/download/
|
||
```
|
||
|
||
---
|
||
|
||
### Phase 2: 代码修改 🔨
|
||
|
||
#### 修改集合A: LabelPdfCacheService.cs
|
||
|
||
- [ ] 2A.1 - 在文件头添加 GhostScript 命名空间
|
||
```csharp
|
||
using Ghostscript.NET;
|
||
using Ghostscript.NET.Rasterizer;
|
||
```
|
||
|
||
- [ ] 2A.2 - 修改 `ConvertPdfFirstPageToBitmap()` 方法
|
||
- 删除当前的纯白渲染逻辑(L461-472)
|
||
- 集成 GhostScript 进行真实渲染
|
||
- 参考长度:约30-40行代码
|
||
|
||
- [ ] 2A.3 - 修改 `ProcessSingleCacheTask()` 方法
|
||
- 调整缓存优先级:验证通过 → 立即缓存
|
||
- 条码识别改为异步非阻塞
|
||
- 预期修改:20-30行
|
||
|
||
- [ ] 2A.4 - 修改 `SaveCacheAsync()` 方法签名
|
||
- 让条码字段可选(默认值为null)
|
||
- 添加 `Status` 参数,区分成功/失败缓存
|
||
|
||
#### 修改集合B: ILabelPdfCacheRepository.cs & LabelPdfCacheRepository.cs
|
||
|
||
- [ ] 2B.1 - 添加 `UpdateBarcodeAsync()` 方法
|
||
```csharp
|
||
Task UpdateBarcodeAsync(string waybillNumber, string barcodeNumber,
|
||
byte barcodeType, int confidence);
|
||
```
|
||
|
||
#### 修改集合C: LabelController.cs
|
||
|
||
- [ ] 2C.1 - 调整下载接口缓存逻辑 (L597-621)
|
||
- 改为立即保存缓存(验证后)
|
||
- 条码识别移至后台异步任务
|
||
|
||
---
|
||
|
||
### Phase 3: 数据库验证 💾
|
||
|
||
- [ ] 3.1 - 检查 `label_pdf_cache` 表结构
|
||
```sql
|
||
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
|
||
WHERE TABLE_NAME = 'label_pdf_cache'
|
||
```
|
||
|
||
- [ ] 3.2 - 验证所有必需列
|
||
- [ ] NeutralWaybillNumber(唯一索引)
|
||
- [ ] PdfBytes(varbinary(max))
|
||
- [ ] Status(tinyint)
|
||
- [ ] BarcodeNumber, BarcodeType, BarcodeConfidence(可选字段)
|
||
|
||
- [ ] 3.3 - 如果缺少字段,执行迁移脚本
|
||
```sql
|
||
-- 示例,具体根据实际情况调整
|
||
ALTER TABLE label_pdf_cache
|
||
ADD Status TINYINT DEFAULT 1,
|
||
BarcodeNumber NVARCHAR(100) NULL,
|
||
BarcodeType TINYINT DEFAULT 0,
|
||
BarcodeConfidence INT NULL,
|
||
BarcodeExtractTime DATETIME2 NULL;
|
||
```
|
||
|
||
---
|
||
|
||
### Phase 4: 编译与验证 🔍
|
||
|
||
- [ ] 4.1 - 清理并重建项目
|
||
```bash
|
||
dotnet clean
|
||
dotnet build
|
||
```
|
||
|
||
- [ ] 4.2 - 检查编译错误
|
||
- [ ] 无错误
|
||
- [ ] 若有错误,根据报错信息修复
|
||
|
||
- [ ] 4.3 - 运行代码分析 (如已配置)
|
||
```bash
|
||
dotnet build /p:TreatWarningsAsErrors=true
|
||
```
|
||
|
||
---
|
||
|
||
### Phase 5: 单元测试 ✅
|
||
|
||
- [ ] 5.1 - 单页PDF测试
|
||
- 创建简单的单页PDF(无复杂图形)
|
||
- 验证缓存成功,条码识别可选
|
||
|
||
- [ ] 5.2 - 多页PDF测试
|
||
- 验证被正确拒绝(Status=Failed)
|
||
- 不应该保存到缓存
|
||
|
||
- [ ] 5.3 - 超大PDF测试
|
||
- >900KB的PDF文件
|
||
- 验证被正确拒绝
|
||
|
||
- [ ] 5.4 - 无效PDF测试
|
||
- 被破损的PDF文件
|
||
- 验证异常处理,不crash
|
||
|
||
- [ ] 5.5 - 条码识别测试
|
||
- 包含清晰条码的标签PDF
|
||
- 验证识别成功(可选,需要实际物流标签样本)
|
||
|
||
- [ ] 5.6 - 集成测试
|
||
- 测试完整的下载→验证→缓存→返回流程
|
||
- 验证性能指标(缓存延迟<100ms)
|
||
|
||
---
|
||
|
||
### Phase 6: 部署准备 🚀
|
||
|
||
- [ ] 6.1 - 准备部署文档
|
||
- Ghostscript系统依赖说明
|
||
- 数据库迁移脚本
|
||
|
||
- [ ] 6.2 - 灾难恢复计划
|
||
- 如何回滚到之前版本?
|
||
- 旧缓存数据如何处理?
|
||
|
||
- [ ] 6.3 - 监控配置
|
||
- 条码识别失败告警?
|
||
- 缓存命中率监控?
|
||
|
||
---
|
||
|
||
## 📊 验收标准
|
||
|
||
| 检查项 | 成功标准 | 验证方法 |
|
||
|--------|--------|--------|
|
||
| PDF验证功能 | 正确拒绝多页/超大PDF | 单元测试 |
|
||
| 缓存保存 | 验证通过后<100ms内保存 | 性能测试 |
|
||
| 条码识别 | 异步执行,失败不影响缓存 | 集成测试 |
|
||
| PDF渲染 | 不再返回纯白位图 | 视觉检查 |
|
||
| 系统可靠性 | 无新的运行时异常 | 负载测试 |
|
||
|
||
---
|
||
|
||
## ⚠️ 风险评估与缓解
|
||
|
||
### 风险1:Ghostscript依赖问题
|
||
|
||
**风险**:系统未安装或版本不匹配
|
||
|
||
**缓解**:
|
||
- [ ] 使用 nuget 提供的预编译版本(自动安装)
|
||
- [ ] 或在部署文档中明确说明系统要求
|
||
|
||
### 风险2:PDF渲染性能
|
||
|
||
**风险**:渲染大型复杂PDF超时
|
||
|
||
**缓解**:
|
||
- [ ] 设置渲染超时(建议5秒)
|
||
- [ ] 添加超时异常处理
|
||
- [ ] 异步执行,不阻塞主流程
|
||
|
||
### 风险3:数据库容量
|
||
|
||
**风险**:大量PDF字节流撑满数据库
|
||
|
||
**缓解**:
|
||
- [ ] 实施数据归档策略(>30天自动删除)
|
||
- [ ] 监控缓存表大小
|
||
- [ ] 定期清理过期缓存
|
||
|
||
### 风险4:条码识别准确性
|
||
|
||
**风险**:物流标签质量差导致识别失败
|
||
|
||
**缓解**:
|
||
- [ ] 这是预期行为,失败时日志记录即可
|
||
- [ ] PDF缓存仍然有效,业务继续进行
|
||
|
||
---
|
||
|
||
## 🎯 最后确认清单
|
||
|
||
**请在您同意以下内容后,回复"确认无误,请开始实施"**:
|
||
|
||
- [ ] PDF渲染方案:**GhostScript.NET** ✅
|
||
- [ ] 缓存流程:**验证通过 → 立即缓存 → 异步条码识别** ✅
|
||
- [ ] 条码失败:**不影响缓存,仅记录日志** ✅
|
||
- [ ] 数据库字段:**已确认上述所有字段** ✅
|
||
- [ ] 优先级:**缓存可用性 > 条码识别准确性** ✅
|
||
|
||
**如有任何调整或疑问,请在此指出**:
|
||
___________________________________________________________________________
|
||
|
||
---
|
||
|
||
**准备就绪!** 🚀
|