From a9e9d1cc3e343b4cc74a6c18ac0fe8b4ec871269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=B6=E8=AE=AD?= Date: Fri, 31 Jul 2026 19:44:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=98=8E=E7=A1=AE=E8=BF=9B=E5=BA=A6=E4=B8=8E?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mybatisexport/MyBatisExportWindow.java | 2 +- .../application/RunSqlExportUseCase.java | 16 ++++----- .../application/SqlExportProgress.java | 17 +++++++++ .../scanner/application/RunScanUseCase.java | 8 +++-- .../biz/scanner/application/ScanProgress.java | 10 ++++++ .../RunSqlExportCancellationTest.java | 35 +++++++++++++++++++ .../application/SqlExportProgressTest.java | 34 ++++++++++++++++++ .../scanner/application/ScanProgressTest.java | 24 +++++++++++++ .../ui/async/OperationContextTestSupport.java | 13 +++++++ 9 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 src/test/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportCancellationTest.java create mode 100644 src/test/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgressTest.java create mode 100644 src/test/java/com/example/workutils/biz/scanner/application/ScanProgressTest.java create mode 100644 src/test/java/com/example/workutils/system/ui/async/OperationContextTestSupport.java diff --git a/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java b/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java index 6a6311c..8d3a452 100644 --- a/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java +++ b/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java @@ -161,7 +161,7 @@ public class MyBatisExportWindow { modeLabel, filterModeCmb, filterValueField, filterHint, exportBtn); OperationStatusPane progressRow = new OperationStatusPane( - exportOperation, fsBody() - 2); + exportOperation, fsBody() - 2, true); // === 日志区域 === Label logTitle = new Label("执行日志"); diff --git a/src/main/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportUseCase.java b/src/main/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportUseCase.java index 43b878e..a1f589f 100644 --- a/src/main/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportUseCase.java +++ b/src/main/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportUseCase.java @@ -24,19 +24,19 @@ public final class RunSqlExportUseCase { new MyBatisExportService.ExportCallback() { @Override public void onProjectStart(int current, int total, String projectName) { - context.report(current + "/" + total + " " + projectName, - total == 0 ? 0 : (double) (current - 1) / total); - progressConsumer.accept(new SqlExportProgress( - current, total, projectName, 0)); + SqlExportProgress progress = new SqlExportProgress( + current, total, projectName, 0); + context.report(progress.message(), progress.fraction()); + progressConsumer.accept(progress); } @Override public void onProjectDone(int current, int total, String projectName, int recordCount) { - context.report(current + "/" + total + " " + projectName, - total == 0 ? 0 : (double) current / total); - progressConsumer.accept(new SqlExportProgress( - current, total, projectName, recordCount)); + SqlExportProgress progress = new SqlExportProgress( + current, total, projectName, recordCount); + context.report(progress.message(), progress.fraction()); + progressConsumer.accept(progress); } @Override diff --git a/src/main/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgress.java b/src/main/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgress.java index 3da1ec8..e3a645b 100644 --- a/src/main/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgress.java +++ b/src/main/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgress.java @@ -2,7 +2,24 @@ package com.example.workutils.biz.mybatisexport.application; public record SqlExportProgress(int current, int total, String projectName, int recordCount) { + public SqlExportProgress { + projectName = projectName == null ? "" : projectName.trim(); + if (current < 0 || total < 0 || recordCount < 0) { + throw new IllegalArgumentException("progress counts must not be negative"); + } + if (current > total) { + throw new IllegalArgumentException("current must not exceed total"); + } + if (projectName.isEmpty()) { + throw new IllegalArgumentException("projectName must not be blank"); + } + } + public double fraction() { return total == 0 ? 0 : (double) current / total; } + + public String message() { + return current + "/" + total + " " + projectName; + } } diff --git a/src/main/java/com/example/workutils/biz/scanner/application/RunScanUseCase.java b/src/main/java/com/example/workutils/biz/scanner/application/RunScanUseCase.java index 0fab926..3f39848 100644 --- a/src/main/java/com/example/workutils/biz/scanner/application/RunScanUseCase.java +++ b/src/main/java/com/example/workutils/biz/scanner/application/RunScanUseCase.java @@ -15,11 +15,11 @@ public final class RunScanUseCase { } public Result execute(ScanRequest request, OperationContext context) throws Exception { - context.report("正在扫描项目", -1); + report(context, new ScanProgress("正在扫描项目", -1)); ScannerService.ScanResult scanResult = scannerService.scanAllProjects( ScannerService.ScanType.KEYWORD, request.effectivePattern()); - context.report("正在生成 CSV", -1); + report(context, new ScanProgress("正在生成 CSV", -1)); String fileName = outputHelper.generateOutputFileName("scan_result", "csv"); scannerService.exportScanResults(scanResult, fileName); return new Result(scanResult, fileName); @@ -27,4 +27,8 @@ public final class RunScanUseCase { public record Result(ScannerService.ScanResult scanResult, String fileName) { } + + private void report(OperationContext context, ScanProgress progress) { + context.report(progress.message(), progress.progress()); + } } diff --git a/src/main/java/com/example/workutils/biz/scanner/application/ScanProgress.java b/src/main/java/com/example/workutils/biz/scanner/application/ScanProgress.java index 32857bc..2e930e5 100644 --- a/src/main/java/com/example/workutils/biz/scanner/application/ScanProgress.java +++ b/src/main/java/com/example/workutils/biz/scanner/application/ScanProgress.java @@ -1,4 +1,14 @@ package com.example.workutils.biz.scanner.application; public record ScanProgress(String message, double progress) { + + public ScanProgress { + message = message == null ? "" : message.trim(); + if (message.isEmpty()) { + throw new IllegalArgumentException("message must not be blank"); + } + if (Double.isNaN(progress) || progress < -1 || progress > 1) { + throw new IllegalArgumentException("progress must be -1 or between 0 and 1"); + } + } } diff --git a/src/test/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportCancellationTest.java b/src/test/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportCancellationTest.java new file mode 100644 index 0000000..8e23e4d --- /dev/null +++ b/src/test/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportCancellationTest.java @@ -0,0 +1,35 @@ +package com.example.workutils.biz.mybatisexport.application; + +import com.example.workutils.biz.mybatisexport.service.MyBatisExportService; +import com.example.workutils.system.ui.async.OperationContext; +import com.example.workutils.system.ui.async.OperationContextTestSupport; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class RunSqlExportCancellationTest { + + @Test + void exposesOperationCancellationToServiceCallback() throws Exception { + AtomicBoolean cancellationObserved = new AtomicBoolean(); + MyBatisExportService service = new MyBatisExportService() { + @Override + public ExportResult exportWithCallback( + String prefix, String mode, String value, ExportCallback callback) { + cancellationObserved.set(callback.isCancelled()); + return null; + } + }; + OperationContext context = OperationContextTestSupport.withCancellation(() -> true); + RunSqlExportUseCase useCase = new RunSqlExportUseCase(service); + + useCase.execute( + SqlExportRequest.create(SqlExportRequest.FilterMode.ALL, ""), + context, + progress -> { }); + + assertTrue(cancellationObserved.get()); + } +} diff --git a/src/test/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgressTest.java b/src/test/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgressTest.java new file mode 100644 index 0000000..5debd4e --- /dev/null +++ b/src/test/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgressTest.java @@ -0,0 +1,34 @@ +package com.example.workutils.biz.mybatisexport.application; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class SqlExportProgressTest { + + @Test + void derivesStableMessageAndFraction() { + SqlExportProgress preparing = new SqlExportProgress(0, 0, " 准备 ", 0); + SqlExportProgress done = new SqlExportProgress(2, 2, "project", 5); + + assertEquals("0/0 准备", preparing.message()); + assertEquals(0, preparing.fraction()); + assertEquals("2/2 project", done.message()); + assertEquals(1, done.fraction()); + } + + @Test + void rejectsInvalidCountsAndProjectName() { + assertThrows(IllegalArgumentException.class, + () -> new SqlExportProgress(-1, 1, "project", 0)); + assertThrows(IllegalArgumentException.class, + () -> new SqlExportProgress(2, 1, "project", 0)); + assertThrows(IllegalArgumentException.class, + () -> new SqlExportProgress(0, -1, "project", 0)); + assertThrows(IllegalArgumentException.class, + () -> new SqlExportProgress(0, 0, " ", 0)); + assertThrows(IllegalArgumentException.class, + () -> new SqlExportProgress(0, 0, "project", -1)); + } +} diff --git a/src/test/java/com/example/workutils/biz/scanner/application/ScanProgressTest.java b/src/test/java/com/example/workutils/biz/scanner/application/ScanProgressTest.java new file mode 100644 index 0000000..513259f --- /dev/null +++ b/src/test/java/com/example/workutils/biz/scanner/application/ScanProgressTest.java @@ -0,0 +1,24 @@ +package com.example.workutils.biz.scanner.application; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ScanProgressTest { + + @Test + void acceptsAndNormalizesSupportedProgressValues() { + assertEquals("扫描中", new ScanProgress(" 扫描中 ", -1).message()); + assertEquals(0, new ScanProgress("开始", 0).progress()); + assertEquals(1, new ScanProgress("完成", 1).progress()); + } + + @Test + void rejectsBlankMessageAndInvalidProgress() { + assertThrows(IllegalArgumentException.class, () -> new ScanProgress(" ", 0)); + assertThrows(IllegalArgumentException.class, () -> new ScanProgress("扫描", Double.NaN)); + assertThrows(IllegalArgumentException.class, () -> new ScanProgress("扫描", -1.1)); + assertThrows(IllegalArgumentException.class, () -> new ScanProgress("扫描", 1.1)); + } +} diff --git a/src/test/java/com/example/workutils/system/ui/async/OperationContextTestSupport.java b/src/test/java/com/example/workutils/system/ui/async/OperationContextTestSupport.java new file mode 100644 index 0000000..69758b0 --- /dev/null +++ b/src/test/java/com/example/workutils/system/ui/async/OperationContextTestSupport.java @@ -0,0 +1,13 @@ +package com.example.workutils.system.ui.async; + +import java.util.function.BooleanSupplier; + +public final class OperationContextTestSupport { + + private OperationContextTestSupport() { + } + + public static OperationContext withCancellation(BooleanSupplier cancellationRequested) { + return new OperationContext((message, progress) -> { }, cancellationRequested); + } +}