明确进度与取消契约
This commit is contained in:
@@ -161,7 +161,7 @@ public class MyBatisExportWindow {
|
|||||||
modeLabel, filterModeCmb, filterValueField, filterHint, exportBtn);
|
modeLabel, filterModeCmb, filterValueField, filterHint, exportBtn);
|
||||||
|
|
||||||
OperationStatusPane progressRow = new OperationStatusPane(
|
OperationStatusPane progressRow = new OperationStatusPane(
|
||||||
exportOperation, fsBody() - 2);
|
exportOperation, fsBody() - 2, true);
|
||||||
|
|
||||||
// === 日志区域 ===
|
// === 日志区域 ===
|
||||||
Label logTitle = new Label("执行日志");
|
Label logTitle = new Label("执行日志");
|
||||||
|
|||||||
+8
-8
@@ -24,19 +24,19 @@ public final class RunSqlExportUseCase {
|
|||||||
new MyBatisExportService.ExportCallback() {
|
new MyBatisExportService.ExportCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onProjectStart(int current, int total, String projectName) {
|
public void onProjectStart(int current, int total, String projectName) {
|
||||||
context.report(current + "/" + total + " " + projectName,
|
SqlExportProgress progress = new SqlExportProgress(
|
||||||
total == 0 ? 0 : (double) (current - 1) / total);
|
current, total, projectName, 0);
|
||||||
progressConsumer.accept(new SqlExportProgress(
|
context.report(progress.message(), progress.fraction());
|
||||||
current, total, projectName, 0));
|
progressConsumer.accept(progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProjectDone(int current, int total,
|
public void onProjectDone(int current, int total,
|
||||||
String projectName, int recordCount) {
|
String projectName, int recordCount) {
|
||||||
context.report(current + "/" + total + " " + projectName,
|
SqlExportProgress progress = new SqlExportProgress(
|
||||||
total == 0 ? 0 : (double) current / total);
|
current, total, projectName, recordCount);
|
||||||
progressConsumer.accept(new SqlExportProgress(
|
context.report(progress.message(), progress.fraction());
|
||||||
current, total, projectName, recordCount));
|
progressConsumer.accept(progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 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() {
|
public double fraction() {
|
||||||
return total == 0 ? 0 : (double) current / total;
|
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 {
|
public Result execute(ScanRequest request, OperationContext context) throws Exception {
|
||||||
context.report("正在扫描项目", -1);
|
report(context, new ScanProgress("正在扫描项目", -1));
|
||||||
ScannerService.ScanResult scanResult = scannerService.scanAllProjects(
|
ScannerService.ScanResult scanResult = scannerService.scanAllProjects(
|
||||||
ScannerService.ScanType.KEYWORD, request.effectivePattern());
|
ScannerService.ScanType.KEYWORD, request.effectivePattern());
|
||||||
|
|
||||||
context.report("正在生成 CSV", -1);
|
report(context, new ScanProgress("正在生成 CSV", -1));
|
||||||
String fileName = outputHelper.generateOutputFileName("scan_result", "csv");
|
String fileName = outputHelper.generateOutputFileName("scan_result", "csv");
|
||||||
scannerService.exportScanResults(scanResult, fileName);
|
scannerService.exportScanResults(scanResult, fileName);
|
||||||
return new Result(scanResult, fileName);
|
return new Result(scanResult, fileName);
|
||||||
@@ -27,4 +27,8 @@ public final class RunScanUseCase {
|
|||||||
|
|
||||||
public record Result(ScannerService.ScanResult scanResult, String fileName) {
|
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;
|
package com.example.workutils.biz.scanner.application;
|
||||||
|
|
||||||
public record ScanProgress(String message, double progress) {
|
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