using System; using System.IO; using System.Threading.Tasks; using XP.Common.Logging.Interfaces; using XP.ReportEngine.Interfaces; using XP.ReportEngine.Models; namespace XP.ReportEngine.Services { /// /// PDF 报告生成器实现 | PDF report generator implementation /// 协调管线各阶段:模板加载 → 数据绑定 → 排版 → 渲染 → 保存 /// Orchestrates pipeline phases: template loading → data binding → layout → rendering → saving /// public class PdfReportGenerator : IReportGenerator { private readonly ILoggerService _logger; private readonly ITemplateEngine _templateEngine; private readonly IDataBinder _dataBinder; private readonly ILayoutEngine _layoutEngine; private readonly IPdfRenderer _pdfRenderer; /// /// 构造函数 | Constructor /// /// 日志服务 | Logger service /// 模板引擎 | Template engine /// 数据绑定器 | Data binder /// 排版引擎 | Layout engine /// PDF 渲染器 | PDF renderer public PdfReportGenerator( ILoggerService logger, ITemplateEngine templateEngine, IDataBinder dataBinder, ILayoutEngine layoutEngine, IPdfRenderer pdfRenderer) { _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger)); _templateEngine = templateEngine ?? throw new ArgumentNullException(nameof(templateEngine)); _dataBinder = dataBinder ?? throw new ArgumentNullException(nameof(dataBinder)); _layoutEngine = layoutEngine ?? throw new ArgumentNullException(nameof(layoutEngine)); _pdfRenderer = pdfRenderer ?? throw new ArgumentNullException(nameof(pdfRenderer)); } /// /// 异步生成 PDF 报告 | Generate PDF report asynchronously /// 执行完整管线:模板加载 → 验证 → 数据绑定 → 排版计算 → PDF 渲染 → 文件保存(可选) /// Executes full pipeline: template load → validate → data bind → layout → PDF render → save (optional) /// /// 报告上下文数据 | Report context data /// 生成选项 | Generation options /// 生成结果 | Generation result public async Task GenerateAsync(ReportContext context, ReportGenerationOptions options) { try { _logger.Info("报告生成管线开始 | Report generation pipeline started"); // 阶段 1:加载模板 | Phase 1: Load template _logger.Info("阶段 1:加载模板 | Phase 1: Loading template"); var template = _templateEngine.LoadTemplate(options.TemplatePath); if (template == null) { var errorMsg = $"模板文件未找到: {options.TemplatePath}"; _logger.Error(null, "模板加载失败: {Path} | Template loading failed: {Path}", options.TemplatePath); return ReportResult.Failure(errorMsg); } var validation = _templateEngine.Validate(template); if (!validation.IsValid) { var errorMsg = $"模板验证失败: {validation.ErrorMessage}"; _logger.Error(null, "模板验证失败: {Message} | Template validation failed: {Message}", validation.ErrorMessage); return ReportResult.Failure(errorMsg); } _logger.Info("阶段 1 完成:模板加载成功 | Phase 1 completed: Template loaded successfully"); // 阶段 2:数据绑定 | Phase 2: Data binding _logger.Info("阶段 2:数据绑定 | Phase 2: Data binding"); var boundTemplate = _dataBinder.Bind(template, context); _logger.Info("阶段 2 完成:数据绑定成功 | Phase 2 completed: Data binding successful"); // 阶段 3:排版计算 | Phase 3: Layout calculation _logger.Info("阶段 3:排版计算 | Phase 3: Layout calculation"); var pages = _layoutEngine.CalculateLayout(boundTemplate, options); _logger.Info("阶段 3 完成:排版计算成功,共 {PageCount} 页 | Phase 3 completed: Layout calculated, {PageCount} pages", pages.Count); // 阶段 4:PDF 渲染 | Phase 4: PDF rendering _logger.Info("阶段 4:PDF 渲染 | Phase 4: PDF rendering"); var stream = _pdfRenderer.Render(pages, options, boundTemplate); _logger.Info("阶段 4 完成:PDF 渲染成功 | Phase 4 completed: PDF rendering successful"); // 阶段 5:保存文件(可选)| Phase 5: Save file (optional) if (!string.IsNullOrEmpty(options.OutputFilePath)) { _logger.Info("阶段 5:保存文件 | Phase 5: Saving file"); await SaveToFileAsync(stream, options.OutputFilePath); _logger.Info("阶段 5 完成:文件保存成功 {Path} | Phase 5 completed: File saved successfully {Path}", options.OutputFilePath); } _logger.Info("报告生成管线完成 | Report generation pipeline completed"); return ReportResult.Success(stream); } catch (Exception ex) { _logger.Error(ex, "报告生成失败 | Report generation failed: {Message}", ex.Message); return ReportResult.Failure($"报告生成过程中发生错误: {ex.Message}", ex); } } /// /// 将 MemoryStream 保存到文件 | Save MemoryStream to file /// /// PDF 内存流 | PDF memory stream /// 输出文件路径 | Output file path private async Task SaveToFileAsync(MemoryStream stream, string filePath) { // 确保输出目录存在 | Ensure output directory exists var directory = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // 重置流位置后写入文件 | Reset stream position before writing to file stream.Position = 0; using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None); await stream.CopyToAsync(fileStream); // 重置流位置以便后续使用 | Reset stream position for subsequent use stream.Position = 0; } } }