更新版本(1.0->1.1):

1. 步骤任务超时终止配置
2. 步骤辅助关闭后主界面自动调整尺寸
3. 扫描超时会显示最后扫描的文件
4. track日志实时记录扫描文件
5. 一些不影响主功能的,细微调整(如FXML UI文件)
This commit is contained in:
叁玖领域 2026-01-09 11:16:06 +08:00
parent d1608666db
commit 96f7acb2a9
19 changed files with 210 additions and 44 deletions

View File

@ -4,7 +4,9 @@
<component name="GradleSettings"> <component name="GradleSettings">
<option name="linkedExternalProjectsSettings"> <option name="linkedExternalProjectsSettings">
<GradleProjectSettings> <GradleProjectSettings>
<option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$PROJECT_DIR$/../../projEnv/gradle/gradle-8.6" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />

View File

@ -1,3 +1,3 @@
project_group =top.r3944realms.docchecktoolrefacored project_group =top.r3944realms.docchecktoolrefacored
project_name = doc-check-tool project_name = doc-check-tool
project_version = 1.0 project_version = 1.1

View File

@ -12,7 +12,7 @@ public class JavaFxApplication extends Application {
@Override @Override
public void init() throws Exception { public void init() throws Exception {
super.init(); super.init();
System.setVersion("1.0"); System.setVersion("1.1");
} }
@Override @Override

View File

@ -3,7 +3,6 @@ package top.r3944realms.docchecktoolrefactored;
import javafx.stage.DirectoryChooser; import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import top.r3944realms.docchecktoolrefactored.core.Setting; import top.r3944realms.docchecktoolrefactored.core.Setting;
import top.r3944realms.docchecktoolrefactored.ui.module.ProjectInfoPaneController; import top.r3944realms.docchecktoolrefactored.ui.module.ProjectInfoPaneController;
@ -28,6 +27,7 @@ public enum System {
private static final Properties properties = new Properties(); private static final Properties properties = new Properties();
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 默认值 // 默认值
private static final boolean DEFAULT_ENABLE_TASK_TIMEOUT = false;
private static final long DEFAULT_SINGLE_TIMEOUT = 30; private static final long DEFAULT_SINGLE_TIMEOUT = 30;
private static final long DEFAULT_TOTAL_TIMEOUT = 12600; private static final long DEFAULT_TOTAL_TIMEOUT = 12600;
private static final boolean DEFAULT_ENABLE_STEP = false; private static final boolean DEFAULT_ENABLE_STEP = false;
@ -116,6 +116,7 @@ public enum System {
/** 将Setting对象转换为Properties */ /** 将Setting对象转换为Properties */
private static void settingToProperties(Setting setting, Properties props) { private static void settingToProperties(Setting setting, Properties props) {
props.setProperty("scanTimeOutS", String.valueOf(setting.getScanTimeout())); props.setProperty("scanTimeOutS", String.valueOf(setting.getScanTimeout()));
props.setProperty("enableTaskTimeout", String.valueOf(setting.isEnableTaskTimeout()));
props.setProperty("taskTimeOutS", String.valueOf(setting.getTaskTimeout())); props.setProperty("taskTimeOutS", String.valueOf(setting.getTaskTimeout()));
props.setProperty("enableStep", String.valueOf(setting.isEnableStep())); props.setProperty("enableStep", String.valueOf(setting.isEnableStep()));
} }
@ -123,7 +124,12 @@ public enum System {
/** 将Properties转换为Setting对象 */ /** 将Properties转换为Setting对象 */
private static Setting propertiesToSetting(Properties props) { private static Setting propertiesToSetting(Properties props) {
Setting s = new Setting(); Setting s = new Setting();
try {
s.setEnableTaskTimeout((Boolean.parseBoolean(props.getProperty("enableTaskTimeout", String.valueOf(DEFAULT_ENABLE_TASK_TIMEOUT)))));
} catch (Exception e) {
s.setScanTimeout(DEFAULT_SINGLE_TIMEOUT);
log.error(LoggerMarker.DEBUG_MARKER, "enableTaskTimeout格式错误使用默认值{}", DEFAULT_ENABLE_TASK_TIMEOUT);
}
try { try {
s.setScanTimeout(Long.parseLong(props.getProperty("scanTimeOutS", String.valueOf(DEFAULT_SINGLE_TIMEOUT)))); s.setScanTimeout(Long.parseLong(props.getProperty("scanTimeOutS", String.valueOf(DEFAULT_SINGLE_TIMEOUT))));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
@ -150,6 +156,7 @@ public enum System {
/** 获取默认Setting */ /** 获取默认Setting */
private static Setting defaultSetting() { private static Setting defaultSetting() {
Setting s = new Setting(); Setting s = new Setting();
s.setEnableStep(DEFAULT_ENABLE_TASK_TIMEOUT);
s.setScanTimeout(DEFAULT_SINGLE_TIMEOUT); s.setScanTimeout(DEFAULT_SINGLE_TIMEOUT);
s.setTaskTimeout(DEFAULT_TOTAL_TIMEOUT); s.setTaskTimeout(DEFAULT_TOTAL_TIMEOUT);
return s; return s;

View File

@ -82,7 +82,7 @@ public class HashFileGenerator {
public void onError(Path path, Exception e) { public void onError(Path path, Exception e) {
log.error(LoggerMarker.TRACE_MARKER, "扫描错误: {} - {}", path, e.getMessage()); log.error(LoggerMarker.TRACE_MARKER, "扫描错误: {} - {}", path, e.getMessage());
} }
}); }, System.getSetting().getScanTimeout());
// 使用带超时的等待并响应中断 // 使用带超时的等待并响应中断
try { try {

View File

@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class Setting { public class Setting {
private long scanTimeout = 30; private long scanTimeout = 30;
private boolean enableTaskTimeout = false;
private long taskTimeout = 60 * 5; private long taskTimeout = 60 * 5;
private boolean enableStep = false; private boolean enableStep = false;
} }

View File

@ -9,16 +9,25 @@ import java.nio.file.AccessDeniedException;
import java.nio.file.DirectoryStream; import java.nio.file.DirectoryStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.concurrent.ForkJoinPool; import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit; import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeoutException; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j @Slf4j
public class RobustParallelScanner implements FileScanner { public class RobustParallelScanner implements FileScanner {
private final ForkJoinPool forkJoinPool; private final ForkJoinPool forkJoinPool;
private volatile boolean cancelled = false; private volatile boolean cancelled = false;
// 用于记录当前正在扫描的路径
private final AtomicReference<Path> currentScanningPath = new AtomicReference<>();
private final AtomicReference<String> lastProcessedFile = new AtomicReference<>();
private final AtomicInteger totalFilesScanned = new AtomicInteger(0);
private final AtomicInteger totalDirectoriesScanned = new AtomicInteger(0);
private volatile LocalDateTime scanStartTime;
private final int maxDepth; private final int maxDepth;
public RobustParallelScanner(int maxDepth) { public RobustParallelScanner(int maxDepth) {
this(Runtime.getRuntime().availableProcessors(), maxDepth); this(Runtime.getRuntime().availableProcessors(), maxDepth);
@ -67,45 +76,117 @@ public class RobustParallelScanner implements FileScanner {
scanInternal(rootPath, listener, totalFiles, 30); scanInternal(rootPath, listener, totalFiles, 30);
} }
private void scanInternal(Path rootPath, FileScanListener listener, AtomicLong totalFiles, long timeout) { private void scanInternal(Path rootPath, FileScanListener listener, AtomicLong totalFiles, long timeout) {
resetScanState();
scanStartTime = LocalDateTime.now();
currentScanningPath.set(rootPath);
try { try {
validateDirectory(rootPath); validateDirectory(rootPath);
forkJoinPool.submit(() -> { ForkJoinTask<?> submit = forkJoinPool.submit(() -> {
try { try {
AtomicInteger processedFiles = new AtomicInteger(0); AtomicInteger processedFiles = new AtomicInteger(0);
scanDirectory(rootPath, listener, processedFiles, totalFiles, 0); scanDirectory(rootPath, listener, processedFiles, totalFiles, 0);
if (!cancelled) { if (!cancelled) {
listener.onScanComplete(); listener.onScanComplete();
logScanStatistics();
} }
} catch (Exception e) { } catch (Exception e) {
listener.onError(rootPath, e); listener.onError(rootPath, e);
} }
}).get(timeout, TimeUnit.SECONDS); });
} catch (TimeoutException e) { handleTimeout(submit, rootPath, listener, timeout);
log.error(LoggerMarker.TRACE_MARKER, "Scan timeout: {}", rootPath, e);
forkJoinPool.shutdownNow();
listener.onError(rootPath, new TimeoutException("扫描超时30秒"));
} catch (Exception e) { } catch (Exception e) {
listener.onError(rootPath, e); listener.onError(rootPath, e);
} }
} }
private void handleTimeout(Future<?> scanFuture, Path rootPath,
FileScanListener listener, long timeout) throws TimeoutException {
try {
scanFuture.get(timeout, TimeUnit.SECONDS);
} catch (TimeoutException e) {
// 获取详细的超时信息
String timeoutInfo = buildTimeoutInfo(rootPath);
log.error(LoggerMarker.RELEASE_MARKER, "扫描超时 - {}", timeoutInfo, e);
// 取消扫描任务
cancelled = true;
scanFuture.cancel(true);
forkJoinPool.shutdownNow();
log.info("扫描超时 - {}", timeoutInfo);
// 创建详细的超时异常
throw new TimeoutException(
String.format("扫描超时: 已执行 %d 秒\n%s", timeout, timeoutInfo)
);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
listener.onError(rootPath, new InterruptedException("扫描被中断"));
} catch (ExecutionException e) {
listener.onError(rootPath, e);
}
}
private void resetScanState() {
cancelled = false;
currentScanningPath.set(null);
lastProcessedFile.set(null);
totalFilesScanned.set(0);
totalDirectoriesScanned.set(0);
}
private String buildTimeoutInfo(Path rootPath) {
return "扫描开始时间: " + formatDateTime(scanStartTime) + "\n" +
"扫描根目录: " + rootPath.toAbsolutePath() + "\n" +
"当前扫描路径: " + currentScanningPath.get() + "\n" +
"最后处理的文件: " + lastProcessedFile.get() + "\n" +
"已扫描文件数: " + totalFilesScanned.get() + "\n" +
"已扫描目录数: " + totalDirectoriesScanned.get() + "\n"
;
}
private void logScanStatistics() {
LocalDateTime endTime = LocalDateTime.now();
log.info(LoggerMarker.DEBUG_MARKER, """
扫描统计信息:
开始时间: {}
结束时间: {}
总扫描时间: {}
扫描文件数: {}
扫描目录数: {}
最后扫描路径: {}
""",
formatDateTime(scanStartTime),
formatDateTime(endTime),
java.time.Duration.between(scanStartTime, endTime).getSeconds(),
totalFilesScanned.get(),
totalDirectoriesScanned.get(),
currentScanningPath.get()
);
}
private void scanDirectory(Path dir, FileScanListener listener, private void scanDirectory(Path dir, FileScanListener listener,
AtomicInteger processedFiles, AtomicLong totalFiles, int currentDepth) { AtomicInteger processedFiles, AtomicLong totalFiles, int currentDepth) {
if (cancelled || currentDepth > maxDepth) return; if (cancelled || currentDepth > maxDepth) return;
currentScanningPath.set(dir);
totalDirectoriesScanned.incrementAndGet();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
for (Path path : stream) { for (Path path : stream) {
if (cancelled) break; if (cancelled) break;
if (Files.isDirectory(path)) { if (Files.isDirectory(path)) {
// 记录目录扫描开始
log.trace(LoggerMarker.TRACE_MARKER, "扫描目录: {}, 深度: {}", path, currentDepth + 1);
scanDirectory(path, listener, processedFiles, totalFiles, currentDepth + 1); scanDirectory(path, listener, processedFiles, totalFiles, currentDepth + 1);
} else if (Files.isRegularFile(path)) { } else if (Files.isRegularFile(path)) {
lastProcessedFile.set(path.toAbsolutePath().toString());
processFile(path, listener, processedFiles, totalFiles); processFile(path, listener, processedFiles, totalFiles);
} }
} }
} catch (IOException e) { } catch (IOException e) {
log.warn(LoggerMarker.TRACE_MARKER, "无法访问目录: {}", dir, e);
listener.onError(dir, e); listener.onError(dir, e);
} }
} }
@ -115,14 +196,20 @@ public class RobustParallelScanner implements FileScanner {
try { try {
listener.onFileFound(file); listener.onFileFound(file);
totalFilesScanned.incrementAndGet();
// 进度更新处理 // 进度更新处理
if (listener instanceof ProgressAwareListener progressListener && totalFiles != null) { if (totalFilesScanned.get() % 1000 == 0) {
log.debug(LoggerMarker.TRACE_MARKER, "已扫描 {} 个文件,当前文件: {}",
totalFilesScanned.get(), file);
}
if (listener instanceof ProgressAwareListener progressListener) {
int processed = processedFiles.incrementAndGet(); int processed = processedFiles.incrementAndGet();
long total = totalFiles.get(); long total = totalFiles.get();
progressListener.onProgressUpdate(processed, (int)total); progressListener.onProgressUpdate(processed, (int)total);
} }
} catch (Exception e) { } catch (Exception e) {
log.warn(LoggerMarker.TRACE_MARKER, "处理文件失败: {}", file, e);
listener.onError(file, e); listener.onError(file, e);
} }
} }
@ -139,6 +226,16 @@ public class RobustParallelScanner implements FileScanner {
path.toString().contains("$")) { path.toString().contains("$")) {
throw new IOException("系统目录禁止访问: " + path); throw new IOException("系统目录禁止访问: " + path);
} }
long freeSpace = Files.getFileStore(path).getUsableSpace();
if (freeSpace < 1024 * 1024 * 10) { // 小于10MB
log.warn(LoggerMarker.TRACE_MARKER, "磁盘空间不足: {},可用空间: {} MB",
path, freeSpace / (1024 * 1024));
}
}
private String formatDateTime(LocalDateTime dateTime) {
if (dateTime == null) return "N/A";
return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} }
@Override @Override

View File

@ -11,6 +11,9 @@ import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import top.r3944realms.docchecktoolrefactored.System; import top.r3944realms.docchecktoolrefactored.System;
@ -53,6 +56,7 @@ public class MainStageController {
@FXML private MenuItem exitMI; @FXML private MenuItem exitMI;
@FXML private MenuItem logoutMI; @FXML private MenuItem logoutMI;
@FXML private MenuItem settingMI; @FXML private MenuItem settingMI;
@FXML private HBox stepAssistant;
private List<Tab> tabs; private List<Tab> tabs;
private int currentIndex = 0; private int currentIndex = 0;
@ -257,8 +261,24 @@ public class MainStageController {
} }
public void updateStepButtonsVisibility() { public void updateStepButtonsVisibility() {
Setting setting = System.getSetting(); Setting setting = System.getSetting();
boolean visible = setting.isEnableStep(); // 由enableStep控制 boolean visible = setting.isEnableStep();
// 设置容器可见性和管理状态
stepAssistant.setVisible(visible);
stepAssistant.setManaged(visible);
// 同时设置按钮的可见性可选因为容器已控制
nextB.setVisible(visible); nextB.setVisible(visible);
prevB.setVisible(visible); prevB.setVisible(visible);
// 如果隐藏按钮区域调整布局
if (!visible) {
// 确保TabPane占据剩余空间
VBox.setVgrow(tabPane, Priority.ALWAYS);
} else {
// 恢复默认的垂直增长策略
VBox.setVgrow(tabPane, Priority.ALWAYS);
VBox.setVgrow(stepAssistant, Priority.NEVER);
}
} }
} }

View File

@ -18,7 +18,7 @@ import java.util.ResourceBundle;
public class SettingDialogController implements Initializable { public class SettingDialogController implements Initializable {
@FXML private CheckBox enableStepCB; @FXML private CheckBox enableStepCB, enableTaskTimeoutCB;
@FXML private Button resetB, saveB, cancelB; @FXML private Button resetB, saveB, cancelB;
@FXML private Spinner<Long> scanTimeOutS, taskTimeOutS; @FXML private Spinner<Long> scanTimeOutS, taskTimeOutS;
@ -35,11 +35,19 @@ public class SettingDialogController implements Initializable {
setting = System.getSetting(); setting = System.getSetting();
// 初始化 Spinner // 初始化 Spinner
enableTaskTimeoutCB.setSelected(setting.isEnableTaskTimeout());
scanTimeOutS.setValueFactory(new LongSpinnerValueFactory(1, 3600, setting.getScanTimeout())); scanTimeOutS.setValueFactory(new LongSpinnerValueFactory(1, 3600, setting.getScanTimeout()));
scanTimeOutS.setEditable(true); scanTimeOutS.setEditable(true);
if (setting.isEnableTaskTimeout()) {
taskTimeOutS.setEditable(true);
taskTimeOutS.setDisable(false);
} else {
taskTimeOutS.setDisable(true);
}
taskTimeOutS.setValueFactory(new LongSpinnerValueFactory(1, 3600 * 24, setting.getTaskTimeout())); taskTimeOutS.setValueFactory(new LongSpinnerValueFactory(1, 3600 * 24, setting.getTaskTimeout()));
taskTimeOutS.setEditable(true); taskTimeOutS.setEditable(true);
// 添加焦点离开时校验 // 添加焦点离开时校验
addSpinnerValidation(scanTimeOutS, SINGLE_MIN, SINGLE_MAX); addSpinnerValidation(scanTimeOutS, SINGLE_MIN, SINGLE_MAX);
addSpinnerValidation(taskTimeOutS, TOTAL_MIN, TOTAL_MAX); addSpinnerValidation(taskTimeOutS, TOTAL_MIN, TOTAL_MAX);
@ -53,6 +61,7 @@ public class SettingDialogController implements Initializable {
setting.setScanTimeout(scanTimeOutS.getValue()); setting.setScanTimeout(scanTimeOutS.getValue());
setting.setTaskTimeout(taskTimeOutS.getValue()); setting.setTaskTimeout(taskTimeOutS.getValue());
setting.setEnableStep(enableStepCB.isSelected()); setting.setEnableStep(enableStepCB.isSelected());
setting.setEnableTaskTimeout(enableTaskTimeoutCB.isSelected());
// 保存到配置文件 // 保存到配置文件
System.saveSettingsNow(); System.saveSettingsNow();
// 通知主界面刷新按钮状态 // 通知主界面刷新按钮状态
@ -69,6 +78,7 @@ public class SettingDialogController implements Initializable {
scanTimeOutS.getValueFactory().setValue(30L); // 默认单次超时 scanTimeOutS.getValueFactory().setValue(30L); // 默认单次超时
taskTimeOutS.getValueFactory().setValue(300L); // 默认总超时 taskTimeOutS.getValueFactory().setValue(300L); // 默认总超时
enableStepCB.setSelected(false); enableStepCB.setSelected(false);
enableTaskTimeoutCB.setSelected(false);
} }
/** 取消修改 */ /** 取消修改 */
@ -83,14 +93,19 @@ public class SettingDialogController implements Initializable {
} }
@FXML @FXML
void onCheckTwo(MouseDragEvent mouseDragEvent) { void onCheckThree(MouseDragEvent mouseDragEvent) {
validateSpinnerValue(taskTimeOutS, 60, 3600 * 24); validateSpinnerValue(taskTimeOutS, 60, 3600 * 24);
} }
@FXML @FXML
void onSettingThree(ActionEvent actionEvent) { void onSettingTwo(ActionEvent actionEvent) {
taskTimeOutS.setDisable(!enableTaskTimeoutCB.isSelected());
}
@FXML
void onSettingFour(ActionEvent actionEvent) {
} }
/** 给 Spinner 添加离开焦点校验 */ /** 给 Spinner 添加离开焦点校验 */
private void addSpinnerValidation(Spinner<Long> spinner, long min, long max) { private void addSpinnerValidation(Spinner<Long> spinner, long min, long max) {
spinner.getEditor().focusedProperty().addListener((obs, oldVal, newVal) -> { spinner.getEditor().focusedProperty().addListener((obs, oldVal, newVal) -> {

View File

@ -11,6 +11,7 @@ import javafx.stage.DirectoryChooser;
import javafx.stage.Stage; import javafx.stage.Stage;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import top.r3944realms.docchecktoolrefactored.System; import top.r3944realms.docchecktoolrefactored.System;
import top.r3944realms.docchecktoolrefactored.core.ScanningException;
import top.r3944realms.docchecktoolrefactored.ui.SceneManager; import top.r3944realms.docchecktoolrefactored.ui.SceneManager;
import top.r3944realms.docchecktoolrefactored.ui.task.DuplicateDocumentDetectionTask; import top.r3944realms.docchecktoolrefactored.ui.task.DuplicateDocumentDetectionTask;
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil; import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
@ -18,6 +19,8 @@ import top.r3944realms.docchecktoolrefactored.ui.utils.ProgressBar;
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker; import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
import java.io.File; import java.io.File;
import java.util.List;
/** /**
* The type Duplicate document pane controller. * The type Duplicate document pane controller.
*/ */
@ -119,10 +122,18 @@ public class DuplicateDocumentPaneController {
Throwable exception = task.getException(); Throwable exception = task.getException();
currentTask.progressProperty().removeListener(progressChangeListener); currentTask.progressProperty().removeListener(progressChangeListener);
// currentTask.messageProperty().removeListener(messageChangeListener); // currentTask.messageProperty().removeListener(messageChangeListener);
result1TA.setText("检测过程中发生错误: " + exception.getMessage());
// 调用提取的样式方法添加结果文本域错误态 // 调用提取的样式方法添加结果文本域错误态
addResultErrorStyle(); addResultErrorStyle();
DialogUtil.showDetailedErrorDialog("错误", "检测过程中发生错误", exception.getMessage()); if (exception instanceof ScanningException e1) {
List<String> list = e1.exceptions.stream().map(Throwable::getMessage).toList();
String message = String.join("\n", list);
DialogUtil.showDetailedErrorDialog("错误", "检测过程中发生错误", message);
result1TA.setText("检测过程中发生错误: " + message);
} else {
DialogUtil.showDetailedErrorDialog("错误", "检测过程中发生错误", exception.getMessage());
result1TA.setText("检测过程中发生错误: " + exception.getMessage());
}
start1B.setDisable(false); start1B.setDisable(false);
// 调用提取的样式方法移除开始按钮加载态 // 调用提取的样式方法移除开始按钮加载态
removeStartButtonLoadingStyle(); removeStartButtonLoadingStyle();

View File

@ -2,8 +2,10 @@ package top.r3944realms.docchecktoolrefactored.ui.task;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import top.r3944realms.docchecktoolrefactored.System;
import top.r3944realms.docchecktoolrefactored.core.AddressFileComparator; import top.r3944realms.docchecktoolrefactored.core.AddressFileComparator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -71,9 +73,10 @@ public class AddressFileComparisonTask extends Task<AddressFileComparator.Compar
try { try {
// 构建显示文本 // 构建显示文本
return comparator.compareFiles(physicalFilePath, logicalFilePath, compareMode) CompletableFuture<AddressFileComparator.ComparisonResult> comparisonResult = comparator.compareFiles(physicalFilePath, logicalFilePath, compareMode);
.get(timeoutSeconds, TimeUnit.SECONDS); if (System.getSetting().isEnableTaskTimeout()) {
return comparisonResult.get(timeoutSeconds, TimeUnit.SECONDS);
} else return comparisonResult.get();
} catch (TimeoutException e) { } catch (TimeoutException e) {
updateMessage("文件比对超时,请考虑在‘文件’-‘设置’里增加‘步骤任务超时时间’。"); updateMessage("文件比对超时,请考虑在‘文件’-‘设置’里增加‘步骤任务超时时间’。");
log.error("文件比对超时", e); log.error("文件比对超时", e);

View File

@ -90,7 +90,9 @@ public class AddressFileGenerationTask extends Task<String> {
try { try {
// 等待执行完成或超时 // 等待执行完成或超时
future.get(System.getSetting().getTaskTimeout(), TimeUnit.SECONDS); if (System.getSetting().isEnableTaskTimeout()){
future.get(System.getSetting().getTaskTimeout(), TimeUnit.SECONDS);
} else future.get();
return outputFile.getAbsolutePath(); return outputFile.getAbsolutePath();
} catch (TimeoutException e) { } catch (TimeoutException e) {

View File

@ -114,11 +114,14 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
findThread.start(); findThread.start();
// 简单等待Task取消时会自动中断 // 简单等待Task取消时会自动中断
long totalTimeout = System.getSetting().getTaskTimeout(); boolean isEnableTaskTimout = System.getSetting().isEnableTaskTimeout();
if (!latch.await(totalTimeout, TimeUnit.SECONDS)) { if (isEnableTaskTimout) {
duplicateFinder.shutdown(); long totalTimeout = System.getSetting().getTaskTimeout();
throw new TimeoutException(String.format("任务超时(%d秒请考虑在文件-‘设置’里增加‘步骤任务超时时间’", totalTimeout)); if (!latch.await(totalTimeout, TimeUnit.SECONDS)) {
} duplicateFinder.shutdown();
throw new TimeoutException(String.format("任务超时(%d秒请考虑在文件-‘设置’里增加‘步骤任务超时时间’", totalTimeout));
}
} else latch.await();
// 检查是否有错误 // 检查是否有错误
if (errorRef.get() != null) { if (errorRef.get() != null) {

View File

@ -57,9 +57,12 @@ public class HashFileGenerationTask extends Task<String> {
}); });
try { try {
// 设置超时时间 boolean enableTaskTimeout = System.getSetting().isEnableTaskTimeout();
long timeoutSeconds = System.getSetting().getTaskTimeout(); if (enableTaskTimeout) {
future.get(timeoutSeconds, TimeUnit.SECONDS); // 设置超时时间
long timeoutSeconds = System.getSetting().getTaskTimeout();
future.get(timeoutSeconds, TimeUnit.SECONDS);
} else future.get();
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务完成,输出文件: {}", finalOutputFile.getAbsolutePath()); log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务完成,输出文件: {}", finalOutputFile.getAbsolutePath());
return "哈希列表文件已生成: " + finalOutputFile.getAbsolutePath(); return "哈希列表文件已生成: " + finalOutputFile.getAbsolutePath();

View File

@ -76,7 +76,7 @@
</Tab> </Tab>
</tabs> </tabs>
</TabPane> </TabPane>
<HBox nodeOrientation="RIGHT_TO_LEFT" prefHeight="100.0" prefWidth="200.0" VBox.vgrow="ALWAYS"> <HBox fx:id="stepAssistant" nodeOrientation="RIGHT_TO_LEFT" prefHeight="100.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
<children> <children>
<Button fx:id="nextB" mnemonicParsing="false" onAction="#onNext" prefHeight="100.0" prefWidth="500.0" text="下一步"> <Button fx:id="nextB" mnemonicParsing="false" onAction="#onNext" prefHeight="100.0" prefWidth="500.0" text="下一步">
<HBox.margin> <HBox.margin>

View File

@ -20,10 +20,10 @@
<ColumnConstraints /> <ColumnConstraints />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="151.33334350585938" percentHeight="7.0" prefHeight="55.00001017252603" vgrow="NEVER" /> <RowConstraints maxHeight="151.33334350585938" prefHeight="55.00001017252603" vgrow="SOMETIMES" />
<RowConstraints maxHeight="508.33333333333326" percentHeight="7.0" prefHeight="73.99999491373697" vgrow="NEVER" /> <RowConstraints maxHeight="508.33333333333326" percentHeight="7.0" prefHeight="73.99999491373697" vgrow="SOMETIMES" />
<RowConstraints maxHeight="592.6666666666666" percentHeight="7.0" prefHeight="581.3333536783855" vgrow="NEVER" /> <RowConstraints maxHeight="592.6666666666666" prefHeight="51.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="1.7976931348623157E308" prefHeight="581.3333536783855" vgrow="SOMETIMES" /> <RowConstraints maxHeight="1.7976931348623157E308" minHeight="-Infinity" prefHeight="605.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
<children> <children>
<Label prefHeight="23.0" prefWidth="141.0" text="载入数字化成果:"> <Label prefHeight="23.0" prefWidth="141.0" text="载入数字化成果:">
@ -66,7 +66,7 @@
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" /> <Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
</padding> </padding>
</TextField> </TextField>
<TextArea fx:id="result7TA" editable="false" prefWidth="400.0" GridPane.columnSpan="3" GridPane.rowIndex="3"> <TextArea fx:id="result7TA" editable="false" prefHeight="450.0" prefWidth="400.0" GridPane.columnSpan="3" GridPane.rowIndex="3">
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</GridPane.margin> </GridPane.margin>
@ -118,7 +118,7 @@
<Font size="14.0" /> <Font size="14.0" />
</font> </font>
</Button> </Button>
<TextArea editable="false" maxWidth="1.7976931348623157E308" prefWidth="400.0" text="1.对照《存储载体检查登记表》附件7检查并记录存储载体的类型/数量/内容/可读性情况;&#10;&#10;2.计算数字化成果(包括单页、多页文件)的MD5码生成列表文件保存在目录所在文件夹&#10;&#10;3.将列表文件、目录文件、检测过程文件第2步生成的逻辑地址、物理地址等csv文件打包生成&quot;数字化验收检测包.rar&quot;;&#10;&#10;4.计算并验证压缩包的MD5码或哈希值;&#10;5.结果填入《存储载体检查登记表》。附件7" wrapText="true" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="3"> <TextArea editable="false" prefHeight="450.0" prefWidth="400.0" text="1.对照《存储载体检查登记表》附件7检查并记录存储载体的类型/数量/内容/可读性情况;&#10;&#10;2.计算数字化成果(包括单页、多页文件)的MD5码生成列表文件保存在目录所在文件夹&#10;&#10;3.将列表文件、目录文件、检测过程文件第2步生成的逻辑地址、物理地址等csv文件打包生成&quot;数字化验收检测包.rar&quot;;&#10;&#10;4.计算并验证压缩包的MD5码或哈希值;&#10;5.结果填入《存储载体检查登记表》。附件7" wrapText="true" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="3">
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</GridPane.margin> </GridPane.margin>

View File

@ -23,6 +23,7 @@
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="ALWAYS" />
</rowConstraints> </rowConstraints>
@ -37,12 +38,12 @@
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</GridPane.margin> </GridPane.margin>
</Spinner> </Spinner>
<Label prefHeight="19.0" prefWidth="167.0" text="步骤任务超时时间:" GridPane.rowIndex="2"> <Label prefHeight="19.0" prefWidth="167.0" text="步骤任务超时时间:" GridPane.rowIndex="3">
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</GridPane.margin> </GridPane.margin>
</Label> </Label>
<Spinner fx:id="taskTimeOutS" onMouseDragExited="#onCheckTwo" GridPane.columnIndex="1" GridPane.rowIndex="2"> <Spinner fx:id="taskTimeOutS" onMouseDragExited="#onCheckThree" GridPane.columnIndex="1" GridPane.rowIndex="3">
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</GridPane.margin> </GridPane.margin>
@ -53,8 +54,9 @@
</font> </font>
</Label> </Label>
<Label text="秒" GridPane.columnIndex="2" GridPane.rowIndex="1" /> <Label text="秒" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<Label text="秒" GridPane.columnIndex="2" GridPane.rowIndex="2" /> <Label text="秒" GridPane.columnIndex="2" GridPane.rowIndex="3" />
<CheckBox fx:id="enableStepCB" mnemonicParsing="false" onAction="#onSettingThree" text="启用步骤辅助" GridPane.columnSpan="3" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" /> <CheckBox fx:id="enableStepCB" mnemonicParsing="false" onAction="#onSettingFour" text="启用步骤辅助" GridPane.columnSpan="3" GridPane.halignment="CENTER" GridPane.rowIndex="4" GridPane.valignment="CENTER" />
<CheckBox fx:id="enableTaskTimeoutCB" mnemonicParsing="false" onAction="#onSettingTwo" text="启用步骤任务超时终止" GridPane.columnSpan="3" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="CENTER" />
</children> </children>
</GridPane> </GridPane>
<HBox alignment="CENTER"> <HBox alignment="CENTER">

Binary file not shown.

Binary file not shown.