using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using XP.Common.Logging.Interfaces;
using XP.ReportEngine.Configs;
using XP.ReportEngine.Interfaces;
using XP.ReportEngine.Models;
namespace XP.ReportEngine.Services
{
///
/// 报告服务实现(门面)| Report service implementation (Facade)
/// 协调报告生成的完整流程,将 UI 无关的业务逻辑封装为可复用服务
/// Orchestrates the complete report generation workflow, encapsulating UI-independent business logic as a reusable service
///
public class ReportService : IReportService
{
private readonly IReportGenerator _reportGenerator;
private readonly IReportDataAdapter _dataAdapter;
private readonly ILoggerService _logger;
private readonly ReportIdGenerator _reportIdGenerator;
private readonly ReportConfig _reportConfig;
///
/// 生成互斥锁,防止并发渲染导致 iText7 字体对象跨文档引用错误
/// Generation mutex to prevent concurrent rendering causing iText7 cross-document font reference errors
///
private readonly SemaphoreSlim _generateLock = new(1, 1);
///
/// 构造函数 | Constructor
///
/// 报告生成器 | Report generator
/// 数据适配器 | Data adapter
/// 日志服务 | Logger service
/// 报告编号生成器 | Report ID generator
/// 报告配置 | Report config
public ReportService(
IReportGenerator reportGenerator,
IReportDataAdapter dataAdapter,
ILoggerService logger,
ReportIdGenerator reportIdGenerator,
ReportConfig reportConfig)
{
_reportGenerator = reportGenerator ?? throw new ArgumentNullException(nameof(reportGenerator));
_dataAdapter = dataAdapter ?? throw new ArgumentNullException(nameof(dataAdapter));
_logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
_reportIdGenerator = reportIdGenerator ?? throw new ArgumentNullException(nameof(reportIdGenerator));
_reportConfig = reportConfig ?? throw new ArgumentNullException(nameof(reportConfig));
}
///
/// 生成报告 | Generate report
///
public async Task GenerateAsync(ReportRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
// 获取互斥锁,防止并发渲染 | Acquire mutex to prevent concurrent rendering
await _generateLock.WaitAsync();
try
{
_logger.Info("报告服务开始生成 | Report service starting generation");
// 步骤 1:生成报告编号 | Step 1: Generate report ID
var reportId = GenerateReportId(request);
_logger.Info("报告编号: {ReportId} | Report ID: {ReportId}", reportId);
// 步骤 2:确定输出路径 | Step 2: Determine output path
var outputPath = ResolveOutputPath(request, reportId);
_logger.Info("输出路径: {Path} | Output path: {Path}", outputPath);
// 步骤 3:数据适配 | Step 3: Data adaptation
var metadata = request.Metadata ?? new ReportMetadata();
if (string.IsNullOrEmpty(metadata.ReportId))
{
metadata.ReportId = reportId;
}
if (metadata.InspectionDate == default)
{
metadata.InspectionDate = DateTime.Now;
}
var context = _dataAdapter.Adapt(request.ProcessorOutputs ?? new List(), metadata);
_logger.Info("数据适配完成 | Data adaptation completed");
// 步骤 4:注入额外图像 | Step 4: Inject additional images
InjectAdditionalImages(context, request);
// 步骤 5:注入配置中的 Logo 和公司信息 | Step 5: Inject logo and company info from config
InjectConfigData(context);
// 步骤 6:注入自定义属性 | Step 6: Inject custom properties
InjectCustomProperties(context, request);
// 步骤 7:生成首页汇总表 | Step 7: Generate homepage summary table
context.Properties["summaryTable"] = CreateSummaryTableData(context);
// 步骤 8:调用管线生成 PDF | Step 8: Call pipeline to generate PDF
var templatePath = _reportConfig.GetResolvedTemplatePath();
var options = new ReportGenerationOptions
{
TemplatePath = templatePath,
OutputFilePath = outputPath,
Format = ReportOutputFormat.Pdf
};
var genResult = await _reportGenerator.GenerateAsync(context, options);
// 步骤 9:处理结果 | Step 9: Handle result
if (genResult.IsSuccess)
{
_logger.Info("报告生成成功: {Path} | Report generated successfully: {Path}", outputPath);
return ReportServiceResult.Success(outputPath, reportId);
}
else
{
_logger.Error(null, "报告生成失败: {Message} | Report generation failed: {Message}", genResult.ErrorMessage);
return ReportServiceResult.Failure(genResult.ErrorMessage, genResult.Exception);
}
}
catch (Exception ex)
{
_logger.Error(ex, "报告服务异常: {Message} | Report service exception: {Message}", ex.Message);
return ReportServiceResult.Failure($"报告生成过程中发生异常: {ex.Message}", ex);
}
finally
{
_generateLock.Release();
}
}
///
/// 预热报告引擎 | Warm up report engine
/// 通过生成一个最小化的空白 PDF 来触发所有一次性初始化:
/// iText7 程序集加载、BouncyCastle 注册、字体子系统初始化、JIT 编译
///
public async Task WarmUpAsync()
{
// 获取互斥锁,确保预热与正式生成不并发 | Acquire mutex to ensure warm-up doesn't overlap with generation
await _generateLock.WaitAsync();
try
{
_logger.Info("报告引擎预热开始 | Report engine warm-up started");
await Task.Run(() =>
{
// 加载模板(触发 JSON 反序列化 JIT)| Load template (triggers JSON deserialization JIT)
var templatePath = _reportConfig.GetResolvedTemplatePath();
if (!File.Exists(templatePath))
{
_logger.Warn("预热跳过:模板文件不存在 {Path} | Warm-up skipped: template not found {Path}", templatePath);
return;
}
// 构建最小上下文 | Build minimal context
var context = new ReportContext
{
Metadata = new ReportMetadata
{
ReportId = "WARMUP",
InspectionDate = DateTime.Now,
SampleName = "WarmUp",
OperatorName = "System"
},
ResultGroups = new List(),
Images = new Dictionary(),
Properties = new Dictionary
{
["summaryTable"] = new List>()
}
};
var options = new ReportGenerationOptions
{
TemplatePath = templatePath,
OutputFilePath = null, // 不保存文件 | Don't save file
Format = ReportOutputFormat.Pdf
};
// 执行完整管线(触发 iText7 初始化 + 字体加载 + JIT)
// Execute full pipeline (triggers iText7 init + font loading + JIT)
var result = _reportGenerator.GenerateAsync(context, options).GetAwaiter().GetResult();
// 释放预热产生的流 | Dispose warm-up stream
result.PdfStream?.Dispose();
});
_logger.Info("报告引擎预热完成 | Report engine warm-up completed");
}
catch (Exception ex)
{
// 预热失败不影响正常功能 | Warm-up failure doesn't affect normal functionality
_logger.Warn("报告引擎预热失败(不影响正常使用)| Report engine warm-up failed (doesn't affect normal usage): {Message}", ex.Message);
}
finally
{
_generateLock.Release();
}
}
#region 私有方法 | Private Methods
///
/// 生成报告编号 | Generate report ID
/// 优先使用请求中已有的 ReportId,否则自动生成
/// Prefer existing ReportId from request, otherwise auto-generate
///
private string GenerateReportId(ReportRequest request)
{
// 如果 Metadata 中已有 ReportId,直接使用 | If Metadata already has ReportId, use it directly
if (request.Metadata != null && !string.IsNullOrEmpty(request.Metadata.ReportId))
{
return request.Metadata.ReportId;
}
// 如果 FileNameParameters 中已有 ReportId,直接使用 | If FileNameParameters already has ReportId, use it
if (request.FileNameParameters != null &&
request.FileNameParameters.TryGetValue("ReportId", out var existingId) &&
!string.IsNullOrEmpty(existingId))
{
return existingId;
}
// 自动生成 | Auto-generate
return _reportIdGenerator.GenerateNext();
}
///
/// 解析输出文件路径 | Resolve output file path
///
private string ResolveOutputPath(ReportRequest request, string reportId)
{
// 如果请求中指定了输出路径,直接使用 | If output path specified in request, use it directly
if (!string.IsNullOrEmpty(request.OutputFilePath))
{
// 确保目录存在 | Ensure directory exists
var dir = Path.GetDirectoryName(request.OutputFilePath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
return request.OutputFilePath;
}
// 使用配置和参数自动生成路径 | Auto-generate path using config and parameters
var fileNameParams = request.FileNameParameters ?? new Dictionary();
// 确保 ReportId 在参数中 | Ensure ReportId is in parameters
if (!fileNameParams.ContainsKey("ReportId"))
{
fileNameParams["ReportId"] = reportId;
}
return _reportConfig.ResolveOutputFilePath(fileNameParams);
}
///
/// 注入额外图像到上下文 | Inject additional images into context
///
private void InjectAdditionalImages(ReportContext context, ReportRequest request)
{
if (request.AdditionalImages == null || request.AdditionalImages.Count == 0)
return;
foreach (var kvp in request.AdditionalImages)
{
if (kvp.Value != null)
{
context.Images[kvp.Key] = kvp.Value;
_logger.Debug("注入额外图像: {Key} | Injected additional image: {Key}", kvp.Key);
}
}
}
///
/// 注入配置中的 Logo 和公司信息 | Inject logo and company info from config
///
private void InjectConfigData(ReportContext context)
{
// 注入公司名称 | Inject company name
if (!string.IsNullOrEmpty(_reportConfig.CompanyName))
{
context.Properties["CompanyName"] = _reportConfig.CompanyName;
}
// 注入软件名称 | Inject software name
if (!string.IsNullOrEmpty(_reportConfig.SoftwareName))
{
context.Properties["SoftwareName"] = _reportConfig.SoftwareName;
}
// 注入公司 Logo | Inject company logo
if (!string.IsNullOrEmpty(_reportConfig.CompanyLogo))
{
var logoPath = Path.IsPathRooted(_reportConfig.CompanyLogo)
? _reportConfig.CompanyLogo
: Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _reportConfig.CompanyLogo);
if (File.Exists(logoPath))
{
context.Images["companyLogo"] = new ImageData
{
SourceType = ImageSourceType.FilePath,
FilePath = logoPath
};
_logger.Info("公司 Logo 已加载: {Path} | Company logo loaded: {Path}", logoPath);
}
else
{
_logger.Warn("公司 Logo 文件不存在: {Path} | Company logo file not found: {Path}", logoPath);
}
}
// 注入软件 Logo | Inject software logo
if (!string.IsNullOrEmpty(_reportConfig.SoftwareLogo))
{
var softwareLogoPath = Path.IsPathRooted(_reportConfig.SoftwareLogo)
? _reportConfig.SoftwareLogo
: Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _reportConfig.SoftwareLogo);
if (File.Exists(softwareLogoPath))
{
context.Images["softwareLogo"] = new ImageData
{
SourceType = ImageSourceType.FilePath,
FilePath = softwareLogoPath
};
_logger.Info("软件 Logo 已加载: {Path} | Software logo loaded: {Path}", softwareLogoPath);
}
else
{
_logger.Warn("软件 Logo 文件不存在: {Path} | Software logo file not found: {Path}", softwareLogoPath);
}
}
}
///
/// 注入自定义属性 | Inject custom properties
///
private void InjectCustomProperties(ReportContext context, ReportRequest request)
{
if (request.CustomProperties == null || request.CustomProperties.Count == 0)
return;
foreach (var kvp in request.CustomProperties)
{
context.Properties[kvp.Key] = kvp.Value;
}
}
///
/// 根据 ReportContext 中的结果分组生成首页汇总表数据
/// Generate homepage summary table data from ReportContext result groups
///
private List> CreateSummaryTableData(ReportContext context)
{
var rows = new List>();
foreach (var group in context.ResultGroups)
{
var inspectionType = group.ProcessorType switch
{
"LineMeasurementProcessor" => "线测量 | Line Measurement",
"BgaVoidRateProcessor" => "BGA 气泡率检测 | BGA Void Rate",
"VoidMeasurementProcessor" => "空隙测量 | Void Measurement",
"FillRateProcessor" => "通孔填锡率 | Via Fill Rate",
_ => group.ProcessorType
};
var classification = string.IsNullOrEmpty(group.Classification) ? "N/A" : group.Classification;
var status = classification == "Pass" ? "[PASS] 合格" : classification == "Fail" ? "[FAIL] 不合格" : "—";
rows.Add(new Dictionary
{
["inspectionType"] = inspectionType,
["classification"] = classification,
["status"] = status
});
}
return rows;
}
#endregion
}
}