From 5b168f202e4baf504dedd12e1de285726af991ef Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Thu, 11 Jun 2026 16:12:56 +0800 Subject: [PATCH] =?UTF-8?q?ReportService=20=E6=B7=BB=E5=8A=A0=20ExportToFi?= =?UTF-8?q?le,=20=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E6=8A=A5=E5=91=8A=E6=96=87=E4=BB=B6=E8=BE=93=E5=87=BA,=20116?= =?UTF-8?q?=E6=B5=8B=E8=AF=955=E7=A7=92=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/IReportService.cs | 2 ++ .../Services/ReportService.cs | 18 ++++++++++++++++- .../FullPipelineTests.cs | 20 ++++++++++++++----- .../ReportServiceTests.cs | 2 +- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/CounterDrone.Core/Services/IReportService.cs b/src/CounterDrone.Core/Services/IReportService.cs index b43f160..2af12eb 100644 --- a/src/CounterDrone.Core/Services/IReportService.cs +++ b/src/CounterDrone.Core/Services/IReportService.cs @@ -11,5 +11,7 @@ namespace CounterDrone.Core.Services SimulationReport GetReport(string id); void DeleteReport(string id); PagedResult SearchReports(string keyword, string? dateFrom, string? dateTo, int page, int pageSize); + /// 导出报告为 Markdown 文件,返回文件路径 + string ExportToFile(string reportId, string outputDir); } } diff --git a/src/CounterDrone.Core/Services/ReportService.cs b/src/CounterDrone.Core/Services/ReportService.cs index eaf0b9b..7b51475 100644 --- a/src/CounterDrone.Core/Services/ReportService.cs +++ b/src/CounterDrone.Core/Services/ReportService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using CounterDrone.Core.Models; using SQLite; @@ -11,10 +12,12 @@ namespace CounterDrone.Core.Services { private readonly SQLiteConnection _db; private readonly ReportGenerator _generator = new(); + private readonly IPathProvider _paths; - public ReportService(SQLiteConnection db) + public ReportService(SQLiteConnection db, IPathProvider paths) { _db = db; + _paths = paths; } public SimulationReport Generate(string taskId, TaskFullConfig config, @@ -74,5 +77,18 @@ namespace CounterDrone.Core.Services Items = items, TotalCount = total, Page = page, PageSize = pageSize, }; } + + public string ExportToFile(string reportId, string outputDir) + { + var report = _db.Find(reportId); + if (report == null) throw new ArgumentException($"报告 {reportId} 不存在"); + + Directory.CreateDirectory(outputDir); + var safeTime = DateTime.UtcNow.ToString("yyyyMMdd_HHmmss"); + var fileName = $"{report.TaskName}_{safeTime}.md"; + var filePath = Path.Combine(outputDir, fileName); + File.WriteAllText(filePath, report.Content); + return filePath; + } } } diff --git a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs index f820c37..5d09a59 100644 --- a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs @@ -213,6 +213,19 @@ namespace CounterDrone.Core.Tests return rec; } + private void VerifyAndExportReport(SimulationEngine eng, DroneEntity drone) + { + var detail = _scenario.GetTaskDetail(_taskId)!; + var report = new ReportGenerator().Generate(detail, eng.Events.ToList(), drone.Status, eng.SimulationTime); + Assert.Contains("成功拦截", report); + Assert.Contains(detail.Task.Name, report); + + var svc = new ReportService(_mainDb, _paths); + var r = svc.Generate(_taskId, detail, eng.Events.ToList(), drone.Status.ToString(), eng.SimulationTime); + var path = svc.ExportToFile(r.Id, Path.Combine(_testDir, "reports")); + Assert.True(File.Exists(path)); + } + // ═══════════════════════════════════════════════ // 场景 1:无防御 — 无人机到达目标 // ═══════════════════════════════════════════════ @@ -347,6 +360,7 @@ namespace CounterDrone.Core.Tests Assert.True(launched > 0, msg); Assert.True(cloudsGenerated > 0, msg); Assert.True(drone.Status == DroneStatus.Destroyed, msg); + VerifyAndExportReport(eng, drone); } // ═══════════════════════════════════════════════ @@ -398,11 +412,7 @@ namespace CounterDrone.Core.Tests Assert.True(launched > 0, msg); Assert.True(cloudsGenerated > 0, msg); Assert.True(drone.Status == DroneStatus.Destroyed, msg); - - var detailJ = _scenario.GetTaskDetail(_taskId)!; - var report = new ReportGenerator().Generate(detailJ, eng.Events.ToList(), drone.Status, eng.SimulationTime); - Assert.Contains("成功拦截", report); - Assert.Contains(detailJ.Task.Name, report); + VerifyAndExportReport(eng, drone); } } } diff --git a/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs b/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs index 592afed..8ce8c3c 100644 --- a/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs +++ b/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs @@ -23,7 +23,7 @@ namespace CounterDrone.Core.Tests var paths = new TestPathProvider(_testDir); var dbm = new DatabaseManager(paths); _db = dbm.OpenMainDb(); - _service = new ReportService(_db); + _service = new ReportService(_db, paths); } public void Dispose()