报告XP.ReportEngine:新增 IReportService接口封装完整报告生成流程,支持外部类库直接调用;新增 ReportRequest、ReportServiceResult 请求/响应模型;新增引擎预热机制;PDF 生成改为 Task.Run 后台线程执行,解决进度窗口和主窗口卡死问题;完善文档。

This commit is contained in:
QI Mingxuan
2026-05-14 19:29:11 +08:00
parent 11c69d03fb
commit 29bc8576af
13 changed files with 1485 additions and 318 deletions
+121 -116
View File
@@ -2,26 +2,34 @@
## 概述
XP.ReportEngine 是 XplorePlane 平面 CT 检测系统的 PDF 报告生成模块。负责将 XP.ImageProcessing 的检测分析结果(距离测量、BGA 气泡率、空隙测量、通孔填锡率)转换为结构化的 PDF 检测报告。
XP.ReportEngine 是 XplorePlane 平面 CT 检测系统的 PDF 报告生成模块。负责将检测分析结果(距离测量、BGA 气泡率、空隙测量、通孔填锡率)转换为结构化的 PDF 检测报告。
模块采用管线式架构(Pipeline),将报告生成过程分解为五个独立阶段:
**模板加载 → 数据适配 → 数据绑定 → 排版计算 → PDF 渲染**
对外提供 `IReportService` 门面接口,外部模块只需构建 `ReportRequest` 即可一行调用完成报告生成。
## 技术栈
| 依赖 | 版本 | 用途 |
|------|------|------|
| .NET 8.0 | net8.0-windows7.0 | 运行时 |
| itext7 | 8.0.5 | PDF 文档生成核心库 |
| itext7.bouncy-castle-adapter | 8.0.5 | iText 7 加密支持 |
| iText7 | 8.0.5 | PDF 文档生成核心库 |
| itext7.bouncy-castle-adapter | 8.0.5 | iText7 加密支持 |
| Newtonsoft.Json | 13.0.3 | JSON 模板反序列化 |
| Prism.Wpf | 9.0.537 | 模块化框架与 DI |
| Telerik UI for WPF | 2024.1.408 | UI 控件(演示窗口) |
| XP.Common | — | 日志、本地化、通用窗体、PDF 阅读器 |
## 项目结构
```
XP.ReportEngine/
├── Configs/ # 配置加载
│ ├── ConfigLoader.cs
│ └── ReportConfig.cs
├── Interfaces/ # 核心接口定义
│ ├── IReportService.cs ★ 门面接口(外部调用入口)
│ ├── IReportGenerator.cs
│ ├── IReportGeneratorFactory.cs
│ ├── ITemplateEngine.cs
@@ -30,24 +38,28 @@ XP.ReportEngine/
│ ├── IPdfRenderer.cs
│ └── IReportDataAdapter.cs
├── Models/ # 数据模型
│ ├── ReportRequest.cs ★ 报告生成请求
│ ├── ReportServiceResult.cs ★ 报告服务结果
│ ├── ReportContext.cs
│ ├── ReportMetadata.cs
│ ├── ImageData.cs
│ ├── ReportTemplate.cs
│ ├── TemplateElement.cs
│ ├── LayoutPage.cs
│ ├── ReportResult.cs
│ ├── ReportGenerationOptions.cs
── ProcessorOutput.cs
── ProcessorOutput.cs
│ └── TemplateValidationResult.cs
├── Services/ # 服务实现
│ ├── ReportService.cs ★ 门面服务实现
│ ├── PdfReportGenerator.cs # 管线协调器
│ ├── ReportGeneratorFactory.cs # 工厂
│ ├── JsonTemplateEngine.cs # JSON 模板加载与验证
│ ├── ExpressionDataBinder.cs # ${} 表达式数据绑定
│ ├── PageLayoutEngine.cs # 分页与排版
│ ├── ITextPdfRenderer.cs # iText 7 PDF 渲染
│ ├── ITextPdfRenderer.cs # iText7 PDF 渲染
│ ├── ProcessorDataAdapter.cs # 处理器数据适配
│ └── ReportIdGenerator.cs # 报告编号生成
├── Fonts/ # 字体目录(当前使用系统字体,目录保留备用)
│ └── ReportIdGenerator.cs # 报告编号生成RPT-yyyyMMdd-NNN
├── Templates/ # JSON 报告模板
│ └── StandardReportTemplate.json
├── Resources/ # 多语言资源文件
@@ -55,112 +67,99 @@ XP.ReportEngine/
│ ├── Resources.zh-CN.resx
│ ├── Resources.zh-TW.resx
│ └── Resources.en-US.resx
├── ViewModels/ # 演示窗口 ViewModel
│ └── ReportDemoViewModel.cs
├── Views/ # 演示窗口 View
│ ├── ReportDemoWindow.xaml
│ └── ReportDemoWindow.xaml.cs
├── Documents/ # 项目文档
│ ├── README.md ← 本文件
│ ├── Guidance.md # 使用指南
│ ├── App.config.example # 配置示例
│ ├── FontFilesGuidance.md # 字体方案说明
│ ├── XP.ReportEngineDesign.md # 架构设计文档
│ └── XP.ReportEngineModelDefine.md # 模型定义文档
├── ReportEngineModule.cs # Prism 模块入口
└── XP.ReportEngine.csproj
```
## 快速开始
### 1. 字体说明
模块使用 Windows 系统自带字体(微软雅黑 + Arial),无需额外添加字体文件。详见 `Documents/FontFilesGuidance.md`
### 2. 通过 DI 容器调用
模块通过 `ReportEngineModule` 自动注册到 Prism DI 容器,其他模块可直接注入 `IReportGenerator` 使用:
```csharp
public class MyService
{
private readonly IReportGenerator _reportGenerator;
public MyService(IReportGenerator reportGenerator)
{
_reportGenerator = reportGenerator;
}
public async Task GenerateReport()
{
var context = new ReportContext
{
Metadata = new ReportMetadata
{
ReportId = "RPT-20250511-001",
InspectionDate = DateTime.Now,
SampleName = "PCB-001",
OperatorName = "张三"
},
Properties = new Dictionary<string, object>
{
["sampleName"] = "PCB-001"
}
};
var options = new ReportGenerationOptions
{
TemplatePath = "Templates/StandardReportTemplate.json",
OutputFilePath = @"D:\Reports\report.pdf",
Format = ReportOutputFormat.Pdf
};
var result = await _reportGenerator.GenerateAsync(context, options);
if (result.IsSuccess)
{
// PDF 已保存到 OutputFilePath
}
else
{
// result.ErrorMessage 包含错误信息
}
}
}
```
### 3. 使用数据适配器
将 XP.ImageProcessing 处理器输出转换为 ReportContext
```csharp
var adapter = container.Resolve<IReportDataAdapter>();
var processorOutputs = new List<ProcessorOutput>
{
new ProcessorOutput
{
ProcessorType = "BgaVoidRateProcessor",
OutputData = new Dictionary<string, object>
{
["BgaCount"] = 120,
["VoidRate"] = 0.035,
["Classification"] = "Pass"
}
}
};
var metadata = new ReportMetadata
{
ReportId = "RPT-20250511-001",
InspectionDate = DateTime.Now,
SampleName = "PCB-001",
OperatorName = "张三"
};
ReportContext context = adapter.Adapt(processorOutputs, metadata);
```
## 核心接口
| 接口 | 实现类 | 职责 |
|------|--------|------|
| **`IReportService`** | **`ReportService`** | **门面接口,外部模块调用入口** |
| `IReportGenerator` | `PdfReportGenerator` | 协调管线各阶段,生成 PDF |
| `IReportGeneratorFactory` | `ReportGeneratorFactory` | 根据格式创建对应生成器 |
| `ITemplateEngine` | `JsonTemplateEngine` | JSON 模板加载、反序列化、验证 |
| `IDataBinder` | `ExpressionDataBinder` | `${}` 表达式解析与数据绑定 |
| `ILayoutEngine` | `PageLayoutEngine` | 分页、元素定位、表格跨页 |
| `IPdfRenderer` | `ITextPdfRenderer` | 使用 iText 7 渲染 PDF |
| `IReportDataAdapter` | `ProcessorDataAdapter` | OutputData → ReportContext 转换 |
| `IPdfRenderer` | `ITextPdfRenderer` | 使用 iText7 渲染 PDF |
| `IReportDataAdapter` | `ProcessorDataAdapter` | ProcessorOutput → ReportContext 转换 |
## 架构分层
```
┌─────────────────────────────────────────────────────┐
│ 外部调用方(XP.App、其他模块) │
│ 注入 IReportService,传入 ReportRequest │
└──────────────────────┬──────────────────────────────┘
┌──────────────────────▼──────────────────────────────┐
│ ReportService(门面层) │
│ 生成报告ID → 解析输出路径 → 数据适配 → 注入配置数据 │
└──────────────────────┬──────────────────────────────┘
┌──────────────────────▼──────────────────────────────┐
│ PdfReportGenerator(管线协调层) │
│ 模板加载 → 数据绑定 → 排版计算 → PDF 渲染 → 保存 │
└─────────────────────────────────────────────────────┘
```
## 快速开始
详细使用指南请参阅 `Documents/Guidance.md`
### 最简调用(通过 IReportService
```csharp
public class MyService
{
private readonly IReportService _reportService;
public MyService(IReportService reportService)
{
_reportService = reportService;
}
public async Task GenerateReport(List<ProcessorOutput> outputs)
{
var request = new ReportRequest
{
ProcessorOutputs = outputs,
Metadata = new ReportMetadata
{
SampleName = "PCB-001",
OperatorName = "张三",
InspectionDate = DateTime.Now
}
};
var result = await _reportService.GenerateAsync(request);
if (result.IsSuccess)
{
// result.OutputFilePath — 生成的 PDF 路径
// result.ReportId — 报告编号
}
}
}
```
## 预热机制
模块在 Prism 初始化时自动在后台执行一次预热(`WarmUpAsync`),触发 iText7 初始化、字体加载、JIT 编译等一次性开销,避免用户首次生成报告时卡顿。
预热与正式生成通过 `SemaphoreSlim` 互斥锁串行执行,不会出现并发冲突。
## 数据绑定表达式
@@ -174,35 +173,32 @@ ReportContext context = adapter.Adapt(processorOutputs, metadata);
| `${functionName(param)}` | 格式化函数 | `${formatDate(inspectionDate)}` |
| `${loc:ResourceKey}` | 本地化键解析 | `${loc:Report_Title}` |
内置格式化函数:
- `formatDate(value)` — 根据当前语言格式化日期
- `formatNumber(value, decimals)` — 根据当前语言格式化数字
- `formatPercent(value)` — 格式化百分比
## 多语言支持
支持三种语言:简体中文(zh-CN)、繁体中文(zh-TW)、英文(en-US)。
通过 `ILocalizationService` 自动解析 `${loc:Key}` 表达式为当前语言文本。模块在初始化时注册资源源到 Fallback Chain。
## 模板定义
## 模板页面类型
标准模板位于 `Templates/StandardReportTemplate.json`包含以下页面类型:
- `homepage` — 报告首页(元数据 + 摘要表格)
- `metricData` — 距离测量数据页
- `bgaInspection` — BGA 焊球检测页(含数据表格)
- `voidInspection` — 空隙检测页(含数据表格)
- `viaFillInspection` — 通孔填锡检测页
标准模板 `StandardReportTemplate.json` 包含以下页面类型:
模板 JSON 结构需包含三个必需顶层字段:`document``pages``styles`
| 页面类型 | 说明 |
|---------|------|
| `homepage` | 报告首页(公司信息 + 元数据 + 汇总表格) |
| `summary` | 检测结果汇总页 |
| `metricData` | 距离测量数据页 |
| `bgaInspection` | BGA 焊球检测页(含数据表格) |
| `voidInspection` | 空隙检测页(含数据表格) |
| `viaFillInspection` | 通孔填锡检测页 |
## 错误处理
模块采用结果对象模式(Result Pattern,所有公共方法返回 `ReportResult` 而非抛出异常
- `ReportResult.Success(stream)` — 成功,包含 PDF MemoryStream
- `ReportResult.Failure(message, ex)` — 失败,包含错误信息和可选异常
模块采用结果对象模式(Result Pattern):
- `ReportServiceResult.Success(path, reportId)` — 成功
- `ReportServiceResult.Failure(message, ex)` — 失败
非致命性问题(缺失属性、未定义样式、图像缺失)会记录警告日志并继续执行,不会中断报告生成。
非致命性问题(缺失属性、图像缺失)会记录警告日志并继续执行,不会中断报告生成。
## 构建
@@ -210,3 +206,12 @@ ReportContext context = adapter.Adapt(processorOutputs, metadata);
cd XplorePlane
dotnet build XP.ReportEngine/XP.ReportEngine.csproj
```
## 相关文档
- `Documents/Guidance.md` — 详细使用指南(IReportService 调用方式)
- `Documents/TemplateDevelopment.md` — 模板开发指南(新增/自定义模板)
- `Documents/App.config.example` — 配置文件示例
- `Documents/FontFilesGuidance.md` — 字体方案说明
- `Documents/XP.ReportEngineDesign.md` — 架构设计文档
- `Documents/XP.ReportEngineModelDefine.md` — 模型定义文档