明确进度与取消契约
This commit is contained in:
@@ -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("执行日志");
|
||||
|
||||
+8
-8
@@ -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
|
||||
|
||||
+17
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -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());
|
||||
}
|
||||
}
|
||||
+34
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user