统一导出历史文件操作
This commit is contained in:
@@ -10,7 +10,6 @@ import com.example.workutils.biz.apidoc.parser.JavaSourceParser;
|
||||
import com.example.workutils.system.config.WorkUtilsProperties;
|
||||
import com.example.workutils.system.support.FileOperationHelper;
|
||||
import com.example.workutils.system.support.HistoryPersistence;
|
||||
import com.example.workutils.system.support.SystemOpenHelper;
|
||||
import com.example.workutils.system.ui.StyledButton;
|
||||
import com.example.workutils.system.ui.Theme;
|
||||
import com.example.workutils.system.support.AppExecutors;
|
||||
@@ -21,6 +20,9 @@ import com.example.workutils.system.ui.async.OperationStatusPane;
|
||||
import com.example.workutils.system.ui.history.ExportHistoryPane;
|
||||
import com.example.workutils.system.ui.history.HistoryFileCommands;
|
||||
import com.example.workutils.system.ui.history.HistoryItem;
|
||||
import com.example.workutils.system.ui.history.ResultFileActions;
|
||||
import com.example.workutils.system.ui.history.SystemFileActionGateway;
|
||||
import com.example.workutils.system.ui.notification.FxNotificationCenter;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -58,6 +60,7 @@ public class ApiDocWindow {
|
||||
private final ApiBaseClassManager apiBaseClassManager;
|
||||
private final RunApiDocExportUseCase exportUseCase;
|
||||
private final AsyncOperationController<RunApiDocExportUseCase.Result> exportOperation;
|
||||
private final HistoryFileCommands historyFileCommands;
|
||||
|
||||
// 模式选择
|
||||
private ToggleGroup modeGroup;
|
||||
@@ -107,6 +110,11 @@ public class ApiDocWindow {
|
||||
historyItems.setAll(historyRows.stream()
|
||||
.map(ApiDocWindow::toHistoryItem).toList()));
|
||||
this.stage = new Stage();
|
||||
this.historyFileCommands = new ResultFileActions(
|
||||
new SystemFileActionGateway(),
|
||||
new FxNotificationCenter(stage),
|
||||
message -> log("[INFO] " + message))
|
||||
.commands();
|
||||
buildUI();
|
||||
loadProjects();
|
||||
loadHistory();
|
||||
@@ -285,7 +293,7 @@ public class ApiDocWindow {
|
||||
|
||||
ExportHistoryPane historyPane = new ExportHistoryPane(
|
||||
"导出格式", "文件数", historyItems,
|
||||
historyFileCommands(), fsBody());
|
||||
historyFileCommands, fsBody());
|
||||
historyPane.setPrefHeight(210);
|
||||
historyWrapper.getChildren().add(historyPane);
|
||||
tabHistory.setContent(historyWrapper);
|
||||
@@ -760,13 +768,13 @@ public class ApiDocWindow {
|
||||
+ "-fx-background-radius: " + Theme.RADIUS_SM + "; -fx-padding: 2 6;"));
|
||||
|
||||
openBtn.setOnAction(e -> {
|
||||
openFile(currentFileName);
|
||||
historyFileCommands.openFile(currentFileName);
|
||||
});
|
||||
dirBtn.setOnAction(e -> {
|
||||
openFolder(currentFileName);
|
||||
historyFileCommands.openFolder(currentFileName);
|
||||
});
|
||||
copyBtn.setOnAction(e -> {
|
||||
copyFilePath(currentFileName);
|
||||
historyFileCommands.copyPath(currentFileName);
|
||||
});
|
||||
delBtn.setOnAction(e -> {
|
||||
if (currentRow != null) {
|
||||
@@ -793,63 +801,10 @@ public class ApiDocWindow {
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFilePath(String path) {
|
||||
if (path == null) return;
|
||||
javafx.scene.input.Clipboard.getSystemClipboard()
|
||||
.setContent(new javafx.scene.input.ClipboardContent() {{
|
||||
putString(path);
|
||||
}});
|
||||
log("[INFO] 已复制路径: " + path);
|
||||
}
|
||||
};
|
||||
return cell;
|
||||
}
|
||||
|
||||
private HistoryFileCommands historyFileCommands() {
|
||||
return new HistoryFileCommands(
|
||||
this::openFile,
|
||||
this::openWithNotepad,
|
||||
this::openFolder,
|
||||
path -> {
|
||||
javafx.scene.input.ClipboardContent content =
|
||||
new javafx.scene.input.ClipboardContent();
|
||||
content.putString(path);
|
||||
javafx.scene.input.Clipboard.getSystemClipboard().setContent(content);
|
||||
log("[INFO] 已复制路径: " + path);
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 文件打开工具方法 ====================
|
||||
|
||||
private void openFile(String path) {
|
||||
if (path == null) return;
|
||||
try {
|
||||
File f = new File(path);
|
||||
if (!f.exists()) return;
|
||||
SystemOpenHelper.openFile(f.toPath());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private void openWithNotepad(String path) {
|
||||
if (path == null) return;
|
||||
try {
|
||||
SystemOpenHelper.editFile(path);
|
||||
} catch (Exception e) {
|
||||
openFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void openFolder(String path) {
|
||||
if (path == null) return;
|
||||
try {
|
||||
File f = new File(path).getParentFile();
|
||||
if (f == null || !f.exists()) return;
|
||||
SystemOpenHelper.revealInFileManager(path);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 数据行 ====================
|
||||
|
||||
static HistoryItem toHistoryItem(HistoryRow row) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import com.example.workutils.biz.mybatisexport.service.MyBatisExportService;
|
||||
import com.example.workutils.system.config.WorkUtilsProperties;
|
||||
import com.example.workutils.system.support.AppExecutors;
|
||||
import com.example.workutils.system.support.HistoryPersistence;
|
||||
import com.example.workutils.system.support.SystemOpenHelper;
|
||||
import com.example.workutils.system.ui.StyledButton;
|
||||
import com.example.workutils.system.ui.Theme;
|
||||
import com.example.workutils.system.ui.async.AsyncOperationController;
|
||||
@@ -15,6 +14,9 @@ import com.example.workutils.system.ui.async.OperationStatusPane;
|
||||
import com.example.workutils.system.ui.history.ExportHistoryPane;
|
||||
import com.example.workutils.system.ui.history.HistoryFileCommands;
|
||||
import com.example.workutils.system.ui.history.HistoryItem;
|
||||
import com.example.workutils.system.ui.history.ResultFileActions;
|
||||
import com.example.workutils.system.ui.history.SystemFileActionGateway;
|
||||
import com.example.workutils.system.ui.notification.FxNotificationCenter;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -26,7 +28,6 @@ import javafx.scene.layout.*;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@@ -46,6 +47,7 @@ public class MyBatisExportWindow {
|
||||
private final WorkUtilsProperties properties;
|
||||
private final RunSqlExportUseCase runExportUseCase;
|
||||
private final AsyncOperationController<MyBatisExportService.ExportResult> exportOperation;
|
||||
private final HistoryFileCommands historyFileCommands;
|
||||
|
||||
// 过滤
|
||||
private ComboBox<String> filterModeCmb;
|
||||
@@ -81,6 +83,11 @@ public class MyBatisExportWindow {
|
||||
historyItems.setAll(historyRows.stream()
|
||||
.map(MyBatisExportWindow::toHistoryItem).toList()));
|
||||
this.stage = new Stage();
|
||||
this.historyFileCommands = new ResultFileActions(
|
||||
new SystemFileActionGateway(),
|
||||
new FxNotificationCenter(stage),
|
||||
message -> logArea.appendText("[INFO] " + message + "\n"))
|
||||
.commands();
|
||||
buildUI();
|
||||
loadHistory();
|
||||
}
|
||||
@@ -174,25 +181,12 @@ public class MyBatisExportWindow {
|
||||
tableTitle.setTextFill(Theme.TEXT_PRIMARY);
|
||||
|
||||
ExportHistoryPane historyPane = new ExportHistoryPane(
|
||||
"导出数", historyItems, historyFileCommands(), fsBody());
|
||||
"导出数", historyItems, historyFileCommands, fsBody());
|
||||
VBox.setVgrow(historyPane, Priority.ALWAYS);
|
||||
root.getChildren().addAll(filterRow, progressRow, logTitle, logArea, tableTitle, historyPane);
|
||||
stage.setScene(new Scene(root));
|
||||
}
|
||||
|
||||
private HistoryFileCommands historyFileCommands() {
|
||||
return new HistoryFileCommands(
|
||||
this::openFile,
|
||||
this::openWithNotepad,
|
||||
this::openFolder,
|
||||
path -> {
|
||||
javafx.scene.input.ClipboardContent content =
|
||||
new javafx.scene.input.ClipboardContent();
|
||||
content.putString(path);
|
||||
javafx.scene.input.Clipboard.getSystemClipboard().setContent(content);
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 历史持久化 ====================
|
||||
|
||||
/**
|
||||
@@ -275,37 +269,6 @@ public class MyBatisExportWindow {
|
||||
saveHistory();
|
||||
}
|
||||
|
||||
// ==================== 文件打开工具方法 ====================
|
||||
|
||||
private void openFile(String path) {
|
||||
if (path == null) return;
|
||||
try {
|
||||
File f = new File(path);
|
||||
if (!f.exists()) return;
|
||||
SystemOpenHelper.openFile(f.toPath());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private void openWithNotepad(String path) {
|
||||
if (path == null) return;
|
||||
try {
|
||||
SystemOpenHelper.editFile(path);
|
||||
} catch (Exception e) {
|
||||
openFile(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void openFolder(String path) {
|
||||
if (path == null) return;
|
||||
try {
|
||||
File f = new File(path).getParentFile();
|
||||
if (f == null || !f.exists()) return;
|
||||
SystemOpenHelper.revealInFileManager(path);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 数据行 ====================
|
||||
|
||||
static HistoryItem toHistoryItem(HistoryRow row) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.example.workutils.system.support.AppExecutors;
|
||||
import com.example.workutils.system.support.FileOperationHelper;
|
||||
import com.example.workutils.system.support.HistoryPersistence;
|
||||
import com.example.workutils.system.support.OutputHelper;
|
||||
import com.example.workutils.system.support.SystemOpenHelper;
|
||||
import com.example.workutils.system.ui.StyledButton;
|
||||
import com.example.workutils.system.ui.Theme;
|
||||
import com.example.workutils.system.ui.async.AsyncOperationController;
|
||||
@@ -17,6 +16,9 @@ import com.example.workutils.system.ui.async.OperationStatusPane;
|
||||
import com.example.workutils.system.ui.history.ExportHistoryPane;
|
||||
import com.example.workutils.system.ui.history.HistoryFileCommands;
|
||||
import com.example.workutils.system.ui.history.HistoryItem;
|
||||
import com.example.workutils.system.ui.history.ResultFileActions;
|
||||
import com.example.workutils.system.ui.history.SystemFileActionGateway;
|
||||
import com.example.workutils.system.ui.notification.FxNotificationCenter;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -28,7 +30,6 @@ import javafx.scene.layout.*;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@@ -49,6 +50,7 @@ public class ScannerWindow {
|
||||
private final OutputHelper outputHelper;
|
||||
private final RunScanUseCase runScanUseCase;
|
||||
private final AsyncOperationController<RunScanUseCase.Result> scanOperation;
|
||||
private final HistoryFileCommands historyFileCommands;
|
||||
|
||||
// UI
|
||||
private CheckBox regexCb;
|
||||
@@ -79,6 +81,11 @@ public class ScannerWindow {
|
||||
historyItems.setAll(historyRows.stream()
|
||||
.map(ScannerWindow::toHistoryItem).toList()));
|
||||
this.stage = new Stage();
|
||||
this.historyFileCommands = new ResultFileActions(
|
||||
new SystemFileActionGateway(),
|
||||
new FxNotificationCenter(stage),
|
||||
message -> logArea.appendText("[INFO] " + message + "\n"))
|
||||
.commands();
|
||||
buildUI();
|
||||
loadHistory();
|
||||
}
|
||||
@@ -158,7 +165,7 @@ public class ScannerWindow {
|
||||
tableTitle.setTextFill(Theme.TEXT_PRIMARY);
|
||||
|
||||
ExportHistoryPane historyPane = new ExportHistoryPane(
|
||||
"匹配数", historyItems, historyFileCommands(), fsBody());
|
||||
"匹配数", historyItems, historyFileCommands, fsBody());
|
||||
VBox.setVgrow(historyPane, Priority.ALWAYS);
|
||||
|
||||
// === 日志区 ===
|
||||
@@ -172,20 +179,6 @@ public class ScannerWindow {
|
||||
stage.setScene(new Scene(root));
|
||||
}
|
||||
|
||||
private HistoryFileCommands historyFileCommands() {
|
||||
return new HistoryFileCommands(
|
||||
this::openFile,
|
||||
this::openWithNotepad,
|
||||
this::openFolder,
|
||||
path -> {
|
||||
javafx.scene.input.ClipboardContent content =
|
||||
new javafx.scene.input.ClipboardContent();
|
||||
content.putString(path);
|
||||
javafx.scene.input.Clipboard.getSystemClipboard().setContent(content);
|
||||
logArea.appendText("[INFO] 已复制路径: " + path + "\n");
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 历史持久化 ====================
|
||||
|
||||
/**
|
||||
@@ -253,54 +246,6 @@ public class ScannerWindow {
|
||||
logArea.appendText("已导出: " + result.fileName() + "\n");
|
||||
}
|
||||
|
||||
// ==================== 文件打开工具方法 ====================
|
||||
|
||||
private void openFile(String path) {
|
||||
if (path == null) {
|
||||
logArea.appendText("[WARN] 路径为空\n");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
File f = new File(path);
|
||||
if (!f.exists()) {
|
||||
logArea.appendText("[WARN] 文件不存在: " + path + "\n");
|
||||
return;
|
||||
}
|
||||
// Delegate platform-specific open behavior to SystemOpenHelper.
|
||||
SystemOpenHelper.openFile(f.toPath());
|
||||
logArea.appendText("[OK] 已打开: " + path + "\n");
|
||||
} catch (Exception e) {
|
||||
logArea.appendText("[ERROR] 打开失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void openWithNotepad(String path) {
|
||||
try {
|
||||
SystemOpenHelper.editFile(path);
|
||||
logArea.appendText("[OK] 用系统编辑器打开: " + path + "\n");
|
||||
} catch (Exception e) {
|
||||
openFile(path); // fallback
|
||||
}
|
||||
}
|
||||
|
||||
private void openFolder(String path) {
|
||||
if (path == null) {
|
||||
logArea.appendText("[WARN] 路径为空\n");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
File f = new File(path).getParentFile();
|
||||
if (f == null || !f.exists()) {
|
||||
logArea.appendText("[WARN] 目录不存在: " + path + "\n");
|
||||
return;
|
||||
}
|
||||
SystemOpenHelper.revealInFileManager(path);
|
||||
logArea.appendText("[OK] 已打开目录: " + f.getAbsolutePath() + "\n");
|
||||
} catch (Exception e) {
|
||||
logArea.appendText("[ERROR] 打开目录失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 数据行 ====================
|
||||
|
||||
static HistoryItem toHistoryItem(HistoryRow row) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.example.workutils.system.ui.history;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface FileActionGateway {
|
||||
|
||||
void open(Path path) throws IOException;
|
||||
|
||||
void edit(Path path) throws IOException;
|
||||
|
||||
void reveal(Path path) throws IOException;
|
||||
|
||||
void copyPath(String path);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.example.workutils.system.ui.history;
|
||||
|
||||
import com.example.workutils.system.ui.notification.NotificationCenter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class ResultFileActions {
|
||||
|
||||
private final FileActionGateway gateway;
|
||||
private final NotificationCenter notifications;
|
||||
private final Consumer<String> successFeedback;
|
||||
|
||||
public ResultFileActions(
|
||||
FileActionGateway gateway,
|
||||
NotificationCenter notifications,
|
||||
Consumer<String> successFeedback) {
|
||||
this.gateway = Objects.requireNonNull(gateway, "gateway");
|
||||
this.notifications = Objects.requireNonNull(notifications, "notifications");
|
||||
this.successFeedback = Objects.requireNonNull(successFeedback, "successFeedback");
|
||||
}
|
||||
|
||||
public HistoryFileCommands commands() {
|
||||
return new HistoryFileCommands(
|
||||
path -> performExisting(path, "打开文件", gateway::open),
|
||||
path -> performExisting(path, "编辑文件", gateway::edit),
|
||||
path -> performExisting(path, "打开目录", gateway::reveal),
|
||||
this::copyPath);
|
||||
}
|
||||
|
||||
private void performExisting(String value, String action, PathAction operation) {
|
||||
Path path = parsePath(value);
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
if (!Files.isRegularFile(path)) {
|
||||
notifications.warning("文件操作失败", "文件不存在");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
operation.accept(path);
|
||||
} catch (IOException | RuntimeException error) {
|
||||
notifyFailure(action, error);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyPath(String value) {
|
||||
Path path = parsePath(value);
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
String normalized = path.toString();
|
||||
try {
|
||||
gateway.copyPath(normalized);
|
||||
successFeedback.accept(Files.exists(path)
|
||||
? "已复制路径: " + normalized
|
||||
: "已复制路径(文件已不存在): " + normalized);
|
||||
} catch (RuntimeException error) {
|
||||
notifyFailure("复制路径", error);
|
||||
}
|
||||
}
|
||||
|
||||
private Path parsePath(String value) {
|
||||
if (value == null || value.isBlank()) {
|
||||
notifications.warning("文件操作失败", "文件路径不能为空");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Path.of(value.trim()).toAbsolutePath().normalize();
|
||||
} catch (InvalidPathException error) {
|
||||
notifyFailure("解析路径", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyFailure(String action, Exception error) {
|
||||
String detail = error.getMessage();
|
||||
notifications.error("文件操作失败", action + "失败"
|
||||
+ (detail == null || detail.isBlank() ? "" : ": " + detail));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface PathAction {
|
||||
void accept(Path path) throws IOException;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.example.workutils.system.ui.history;
|
||||
|
||||
import com.example.workutils.system.support.SystemOpenHelper;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public final class SystemFileActionGateway implements FileActionGateway {
|
||||
|
||||
@Override
|
||||
public void open(Path path) throws IOException {
|
||||
SystemOpenHelper.openFile(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void edit(Path path) throws IOException {
|
||||
SystemOpenHelper.editFile(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reveal(Path path) throws IOException {
|
||||
SystemOpenHelper.revealInFileManager(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyPath(String path) {
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(path);
|
||||
Clipboard.getSystemClipboard().setContent(content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.example.workutils.system.ui.history;
|
||||
|
||||
import com.example.workutils.system.ui.notification.NotificationCenter;
|
||||
import com.example.workutils.system.ui.notification.NotificationMessage;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ResultFileActionsTest {
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
@Test
|
||||
void routesExistingFileActionsThroughGateway() throws Exception {
|
||||
Path file = Files.createFile(tempDir.resolve("result.csv"));
|
||||
RecordingGateway gateway = new RecordingGateway();
|
||||
RecordingNotifications notifications = new RecordingNotifications();
|
||||
ResultFileActions actions = new ResultFileActions(
|
||||
gateway, notifications, message -> { });
|
||||
|
||||
HistoryFileCommands commands = actions.commands();
|
||||
commands.openFile(file.toString());
|
||||
commands.openInEditor(file.toString());
|
||||
commands.openFolder(file.toString());
|
||||
|
||||
assertEquals(List.of(
|
||||
"open:" + file.toAbsolutePath().normalize(),
|
||||
"edit:" + file.toAbsolutePath().normalize(),
|
||||
"reveal:" + file.toAbsolutePath().normalize()), gateway.calls);
|
||||
assertTrue(notifications.messages.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void copiesNormalizedMissingPathAndReportsThatItNoLongerExists() {
|
||||
Path missing = tempDir.resolve("missing.csv");
|
||||
RecordingGateway gateway = new RecordingGateway();
|
||||
RecordingNotifications notifications = new RecordingNotifications();
|
||||
List<String> feedback = new ArrayList<>();
|
||||
ResultFileActions actions = new ResultFileActions(
|
||||
gateway, notifications, feedback::add);
|
||||
|
||||
actions.commands().copyPath(missing.toString());
|
||||
|
||||
String normalized = missing.toAbsolutePath().normalize().toString();
|
||||
assertEquals(List.of("copy:" + normalized), gateway.calls);
|
||||
assertEquals(List.of("已复制路径(文件已不存在): " + normalized), feedback);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsBlankAndMissingOpenTargetsWithoutCallingGateway() {
|
||||
RecordingGateway gateway = new RecordingGateway();
|
||||
RecordingNotifications notifications = new RecordingNotifications();
|
||||
ResultFileActions actions = new ResultFileActions(
|
||||
gateway, notifications, message -> { });
|
||||
|
||||
actions.commands().openFile(" ");
|
||||
actions.commands().openFolder(tempDir.resolve("gone.csv").toString());
|
||||
|
||||
assertTrue(gateway.calls.isEmpty());
|
||||
assertEquals(2, notifications.messages.size());
|
||||
assertEquals("文件路径不能为空", notifications.messages.get(0).message());
|
||||
assertEquals("文件不存在", notifications.messages.get(1).message());
|
||||
}
|
||||
|
||||
@Test
|
||||
void translatesPlatformFailureIntoUserFacingError() throws Exception {
|
||||
Path file = Files.createFile(tempDir.resolve("result.sql"));
|
||||
RecordingGateway gateway = new RecordingGateway();
|
||||
gateway.failure = new IllegalStateException("desktop unavailable");
|
||||
RecordingNotifications notifications = new RecordingNotifications();
|
||||
ResultFileActions actions = new ResultFileActions(
|
||||
gateway, notifications, message -> { });
|
||||
|
||||
actions.commands().openFile(file.toString());
|
||||
|
||||
assertEquals(1, notifications.messages.size());
|
||||
NotificationMessage message = notifications.messages.get(0);
|
||||
assertEquals("文件操作失败", message.title());
|
||||
assertTrue(message.message().contains("desktop unavailable"));
|
||||
}
|
||||
|
||||
private static final class RecordingGateway implements FileActionGateway {
|
||||
private final List<String> calls = new ArrayList<>();
|
||||
private RuntimeException failure;
|
||||
|
||||
@Override
|
||||
public void open(Path path) {
|
||||
record("open:" + path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void edit(Path path) {
|
||||
record("edit:" + path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reveal(Path path) {
|
||||
record("reveal:" + path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyPath(String path) {
|
||||
record("copy:" + path);
|
||||
}
|
||||
|
||||
private void record(String call) {
|
||||
if (failure != null) {
|
||||
throw failure;
|
||||
}
|
||||
calls.add(call);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingNotifications implements NotificationCenter {
|
||||
private final List<NotificationMessage> messages = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void show(NotificationMessage message) {
|
||||
messages.add(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean confirm(String title, String header, String message) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.example.workutils.system.ui.history;
|
||||
|
||||
import com.example.workutils.biz.apidoc.ApiDocWindow;
|
||||
import com.example.workutils.biz.mybatisexport.MyBatisExportWindow;
|
||||
import com.example.workutils.biz.scanner.ScannerWindow;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class WindowHistoryCommandsDependencyTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(classes = {
|
||||
ApiDocWindow.class,
|
||||
ScannerWindow.class,
|
||||
MyBatisExportWindow.class
|
||||
})
|
||||
void targetWindowOwnsOneSharedHistoryCommandBundle(Class<?> windowType) {
|
||||
long commandFields = Arrays.stream(windowType.getDeclaredFields())
|
||||
.filter(field -> field.getType() == HistoryFileCommands.class)
|
||||
.count();
|
||||
|
||||
assertEquals(1, commandFields,
|
||||
() -> windowType.getSimpleName()
|
||||
+ " must own one shared HistoryFileCommands instance");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user