From abd3154540e33afaeb2f60e11c82ac6051301c90 Mon Sep 17 00:00:00 2001 From: JusterZhu Date: Wed, 20 May 2026 23:06:35 +0800 Subject: [PATCH] feat: add ReportGeneratorService and wire up in SimulateViewModel - Generates simulation_report.md with config table, result, notes, full timeline - Wired into SimulateViewModel after successful simulation - Completes the Simulate Update feature (#31) --- src/Services/ReportGeneratorService.cs | 64 ++++++++++++++++++++++++++ src/ViewModels/SimulateViewModel.cs | 5 +- 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/Services/ReportGeneratorService.cs diff --git a/src/Services/ReportGeneratorService.cs b/src/Services/ReportGeneratorService.cs new file mode 100644 index 0000000..899cb92 --- /dev/null +++ b/src/Services/ReportGeneratorService.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using GeneralUpdate.Tools.Models; + +namespace GeneralUpdate.Tools.Services; + +/// +/// Generates simulation_report.md after a simulation run. +/// +public class ReportGeneratorService +{ + public async Task GenerateAsync( + SimulateConfigModel config, + SimulationResult result, + string outputDir) + { + var sb = new StringBuilder(); + + sb.AppendLine("# Update Simulation Report"); + sb.AppendLine(); + sb.AppendLine("## Configuration"); + sb.AppendLine(); + sb.AppendLine("| Field | Value |"); + sb.AppendLine("|-------|-------|"); + sb.AppendLine($"| Patch | {EscapeMd(config.PatchFilePath)} |"); + sb.AppendLine($"| App Directory | {EscapeMd(config.AppDirectory)} |"); + sb.AppendLine($"| Platform | {config.Platform} |"); + sb.AppendLine($"| AppType | {config.AppType} |"); + sb.AppendLine($"| Version | {config.CurrentVersion} → {config.TargetVersion} |"); + sb.AppendLine($"| Server Port | {config.ServerPort} |"); + sb.AppendLine($"| Simulation Time | {DateTime.Now:yyyy-MM-dd HH:mm:ss} |"); + sb.AppendLine(); + + sb.AppendLine("## Result"); + sb.AppendLine(); + sb.AppendLine($"**{(result.Success ? "✅ PASS" : "❌ FAIL")}** — {result.Elapsed.TotalSeconds:F1}s"); + if (!string.IsNullOrEmpty(result.ErrorMessage)) + sb.AppendLine($"\nError: `{result.ErrorMessage}`"); + sb.AppendLine(); + + if (result.Notes.Count > 0) + { + sb.AppendLine("## Notes"); + sb.AppendLine(); + foreach (var note in result.Notes) + sb.AppendLine($"- {note}"); + sb.AppendLine(); + } + + sb.AppendLine("## Timeline"); + sb.AppendLine(); + sb.AppendLine("```"); + sb.Append(result.FullLog); + sb.AppendLine("```"); + + var reportPath = Path.Combine(outputDir, "simulation_report.md"); + await File.WriteAllTextAsync(reportPath, sb.ToString(), Encoding.UTF8); + return reportPath; + } + + private static string EscapeMd(string s) => s.Replace(@"\", @"\\").Replace("|", "\\|"); +} diff --git a/src/ViewModels/SimulateViewModel.cs b/src/ViewModels/SimulateViewModel.cs index 600be07..bb2605f 100644 --- a/src/ViewModels/SimulateViewModel.cs +++ b/src/ViewModels/SimulateViewModel.cs @@ -13,6 +13,7 @@ public partial class SimulateViewModel : ViewModelBase { private readonly LocalizationService _loc = LocalizationService.Instance; private readonly SimulationService _sim = new(); + private readonly ReportGeneratorService _report = new(); public SimulateConfigModel Config { get; } = new(); @@ -85,8 +86,8 @@ async Task StartSimulation() L($" Note: {note}"); // Generate report - var reportPath = Path.Combine(Config.OutputDirectory, "simulation_report.md"); - // report generation will be in next PR + var reportPath = await _report.GenerateAsync(Config, result, Config.OutputDirectory); + L($"Report: {reportPath}"); } else {