feat:1.以javafx SceneBuilder 17版本重新编辑了下FXML文件 2.添加线程池全局管理类(待完善:如何协作)3. 添加版本显示字段(非最终实现)
This commit is contained in:
parent
204b1f38bf
commit
4625d30105
|
|
@ -170,7 +170,7 @@ tasks.register('buildPortable', Exec) {
|
||||||
'--vendor', 'r3944realms',
|
'--vendor', 'r3944realms',
|
||||||
'--dest', "$buildDir/distributions",
|
'--dest', "$buildDir/distributions",
|
||||||
'--java-options', '-Dfile.encoding=UTF-8',
|
'--java-options', '-Dfile.encoding=UTF-8',
|
||||||
'--java-options', '-Xmx512m',
|
'--java-options', '-Xmx4G',
|
||||||
'--java-options', '-Xms256m',
|
'--java-options', '-Xms256m',
|
||||||
'--verbose',
|
'--verbose',
|
||||||
'--icon', file('src/main/resources/img/logo256x.ico').absolutePath
|
'--icon', file('src/main/resources/img/logo256x.ico').absolutePath
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,24 @@ import top.r3944realms.docchecktoolrefactored.ui.SceneManager;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class JavaFxApplication extends Application {
|
public class JavaFxApplication extends Application {
|
||||||
|
public Image logo = new Image(Objects.requireNonNull(getClass().getResourceAsStream("/img/icon.jpg")));
|
||||||
@Override
|
@Override
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
super.init();
|
super.init();
|
||||||
|
System.setVersion("1.0.0-beta");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) throws Exception {
|
public void start(Stage primaryStage) {
|
||||||
SceneManager.init(primaryStage);
|
SceneManager.init(primaryStage, logo);
|
||||||
primaryStage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/img/icon.jpg"))));
|
|
||||||
SceneManager.switchLoginView();
|
SceneManager.switchLoginView();
|
||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
// 关闭所有线程池
|
||||||
|
ThreadPoolManager.shutdownAll();
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package top.r3944realms.docchecktoolrefactored;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import top.r3944realms.docchecktoolrefactored.cil.CliProcessor;
|
import top.r3944realms.docchecktoolrefactored.cil.CliProcessor;
|
||||||
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,16 @@ 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.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
||||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.*;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
|
@ -16,6 +19,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
public enum System {
|
public enum System {
|
||||||
INSTANCE;
|
INSTANCE;
|
||||||
private volatile Setting setting;
|
private volatile Setting setting;
|
||||||
|
@Getter
|
||||||
|
private volatile String version;
|
||||||
private volatile File lastModifiedFile;
|
private volatile File lastModifiedFile;
|
||||||
private static final String CONFIG_FILE_NAME = "config.ini";
|
private static final String CONFIG_FILE_NAME = "config.ini";
|
||||||
private static final Properties properties = new Properties();
|
private static final Properties properties = new Properties();
|
||||||
|
|
@ -106,8 +111,8 @@ 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("singleTimeout", String.valueOf(setting.getScanTimeout()));
|
props.setProperty("scanTimeOutS", String.valueOf(setting.getScanTimeout()));
|
||||||
props.setProperty("totalTimeout", 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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,4 +211,10 @@ public enum System {
|
||||||
public static Integer getAvailableProcessors() {
|
public static Integer getAvailableProcessors() {
|
||||||
return Runtime.getRuntime().availableProcessors();
|
return Runtime.getRuntime().availableProcessors();
|
||||||
}
|
}
|
||||||
|
public static String version() {
|
||||||
|
return System.INSTANCE.getVersion();
|
||||||
|
}
|
||||||
|
public static void setVersion(String version) {
|
||||||
|
System.INSTANCE.version = version;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package top.r3944realms.docchecktoolrefactored;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
public class ThreadPoolManager {
|
||||||
|
private static final Map<String, ExecutorService> pools = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static ExecutorService createPool(String name, int size) {
|
||||||
|
return createPool(name, size, true); // 默认使用守护线程
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExecutorService createPool(String name, int size, boolean daemon) {
|
||||||
|
ThreadFactory factory = daemon ?
|
||||||
|
r -> {
|
||||||
|
Thread t = new Thread(r, name + "-thread");
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
} :
|
||||||
|
r -> new Thread(r, name + "-thread");
|
||||||
|
|
||||||
|
ExecutorService pool = Executors.newFixedThreadPool(size, factory);
|
||||||
|
pools.put(name, pool);
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void shutdownAll() {
|
||||||
|
pools.forEach((name, pool) -> {
|
||||||
|
if (!pool.isShutdown()) {
|
||||||
|
try {
|
||||||
|
pool.shutdown();
|
||||||
|
if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||||
|
pool.shutdownNow();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
pool.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pools.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import lombok.Setter;
|
||||||
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;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ThreadPoolManager;
|
||||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
|
@ -16,7 +17,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
@ -162,14 +163,16 @@ public class AddressFileComparator {
|
||||||
}
|
}
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
public AddressFileComparator(int threadPoolSize) {
|
public AddressFileComparator(int threadPoolSize) {
|
||||||
this.executor = Executors.newFixedThreadPool(threadPoolSize);
|
this.executor = ThreadPoolManager.createPool("address-comparator", threadPoolSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressFileComparator() {
|
public AddressFileComparator() {
|
||||||
this.executor = Executors.newFixedThreadPool(System.getAvailableProcessors());
|
this.executor = ThreadPoolManager.createPool("address-comparator", System.getAvailableProcessors());
|
||||||
}
|
}
|
||||||
@Setter
|
@Setter
|
||||||
private ProgressCallback progressCallback;
|
private ProgressCallback progressCallback;
|
||||||
|
|
||||||
|
|
||||||
// 安全调用回调方法
|
// 安全调用回调方法
|
||||||
private void safeOnPhaseStarted(Phase phase) {
|
private void safeOnPhaseStarted(Phase phase) {
|
||||||
if (progressCallback != null) {
|
if (progressCallback != null) {
|
||||||
|
|
@ -189,7 +192,25 @@ public class AddressFileComparator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
executor.shutdown();
|
executor.shutdown(); // 先尝试正常关闭
|
||||||
|
try {
|
||||||
|
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||||
|
executor.shutdownNow(); // 强制关闭
|
||||||
|
// 等待一段时间让任务响应中断
|
||||||
|
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||||
|
log.error(LoggerMarker.DEBUG_MARKER, "线程池无法正常关闭");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
executor.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void safeShutdown() {
|
||||||
|
if (!executor.isShutdown()) {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<ComparisonResult> compareFiles(String physicalFilePath, String logicalFilePath, CompareMode compareMode) {
|
public CompletableFuture<ComparisonResult> compareFiles(String physicalFilePath, String logicalFilePath, CompareMode compareMode) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import top.r3944realms.docchecktoolrefactored.System;
|
import top.r3944realms.docchecktoolrefactored.System;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ThreadPoolManager;
|
||||||
import top.r3944realms.docchecktoolrefactored.io.scanner.FileScanner;
|
import top.r3944realms.docchecktoolrefactored.io.scanner.FileScanner;
|
||||||
import top.r3944realms.docchecktoolrefactored.model.DuplicateGroup;
|
import top.r3944realms.docchecktoolrefactored.model.DuplicateGroup;
|
||||||
import top.r3944realms.docchecktoolrefactored.model.FileMetadata;
|
import top.r3944realms.docchecktoolrefactored.model.FileMetadata;
|
||||||
|
|
@ -13,7 +14,10 @@ import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
@ -21,7 +25,6 @@ import java.util.stream.Collectors;
|
||||||
/**
|
/**
|
||||||
* 重复文件查找核心类
|
* 重复文件查找核心类
|
||||||
*/
|
*/
|
||||||
//TODO;代替DuplicateDocumentDetectionTask
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DuplicateFinder {
|
public class DuplicateFinder {
|
||||||
private final FileScanner fileScanner;
|
private final FileScanner fileScanner;
|
||||||
|
|
@ -49,7 +52,7 @@ public class DuplicateFinder {
|
||||||
this.fileScanner = Objects.requireNonNull(fileScanner);
|
this.fileScanner = Objects.requireNonNull(fileScanner);
|
||||||
this.hashCalculator = Objects.requireNonNull(hashCalculator);
|
this.hashCalculator = Objects.requireNonNull(hashCalculator);
|
||||||
this.enableProgress = enableProgress;
|
this.enableProgress = enableProgress;
|
||||||
this.executorService = Executors.newFixedThreadPool(System.getAvailableProcessors());
|
this.executorService = ThreadPoolManager.createPool("duplicate-finder-pool", System.getAvailableProcessors());
|
||||||
}
|
}
|
||||||
public DuplicateFinder(FileScanner fileScanner, FileHashCalculator hashCalculator) {
|
public DuplicateFinder(FileScanner fileScanner, FileHashCalculator hashCalculator) {
|
||||||
this(fileScanner, hashCalculator, false);
|
this(fileScanner, hashCalculator, false);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package top.r3944realms.docchecktoolrefactored.core;
|
package top.r3944realms.docchecktoolrefactored.core;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.System;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ThreadPoolManager;
|
||||||
import top.r3944realms.docchecktoolrefactored.io.scanner.FileScanner;
|
import top.r3944realms.docchecktoolrefactored.io.scanner.FileScanner;
|
||||||
import top.r3944realms.docchecktoolrefactored.io.scanner.RobustParallelScanner;
|
import top.r3944realms.docchecktoolrefactored.io.scanner.RobustParallelScanner;
|
||||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
@ -13,20 +15,49 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@Setter
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class HashFileGenerator {
|
public class HashFileGenerator {
|
||||||
|
private ProgressCallback callback;
|
||||||
|
private final FileHashCalculator hashCalculator;
|
||||||
|
|
||||||
public interface ProgressListener {
|
public HashFileGenerator() {
|
||||||
|
this.hashCalculator = FileHashCalculator.defaultInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashFileGenerator(FileHashCalculator hashCalculator) {
|
||||||
|
this.hashCalculator = hashCalculator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ProgressCallback {
|
||||||
void onProgressUpdate(int current, int total);
|
void onProgressUpdate(int current, int total);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateHashFile(List<Path> directories, Path outputFile, ProgressListener listener) throws IOException, InterruptedException {
|
private void safeOnProgressUpdate(int current, int total) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onProgressUpdate(current, total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateHashFile(List<Path> directories, Path outputFile)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
|
||||||
|
// 开始时检查中断
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException("任务被取消");
|
||||||
|
}
|
||||||
|
|
||||||
List<Path> allFiles = new ArrayList<>();
|
List<Path> allFiles = new ArrayList<>();
|
||||||
|
|
||||||
// 扫描所有目录中的文件
|
// 扫描所有目录中的文件
|
||||||
for (Path directory : directories) {
|
for (Path directory : directories) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException("任务被取消");
|
||||||
|
}
|
||||||
|
|
||||||
if (!Files.isDirectory(directory)) {
|
if (!Files.isDirectory(directory)) {
|
||||||
throw new IllegalArgumentException("指定路径不是有效目录: " + directory);
|
throw new IllegalArgumentException("指定路径不是有效目录: " + directory);
|
||||||
}
|
}
|
||||||
|
|
@ -49,46 +80,115 @@ public class HashFileGenerator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Path path, Exception e) {
|
public void onError(Path path, Exception e) {
|
||||||
log.error(LoggerMarker.TRACE_MARKER, "Error scanning path: {} - {}", path, e.getMessage());
|
log.error(LoggerMarker.TRACE_MARKER, "扫描错误: {} - {}", path, e.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 等待扫描完成
|
// 使用带超时的等待,并响应中断
|
||||||
scanFuture.join();
|
try {
|
||||||
|
scanFuture.get(System.getSetting().getScanTimeout(), TimeUnit.SECONDS); // 设置合理的超时时间
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
throw new IOException("扫描超时: " + directory, e);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new IOException("扫描失败: " + directory, e);
|
||||||
|
}
|
||||||
|
|
||||||
allFiles.addAll(files);
|
allFiles.addAll(files);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否被中断
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException("任务被取消");
|
||||||
|
}
|
||||||
|
|
||||||
// 计算每个文件的哈希值
|
// 计算每个文件的哈希值
|
||||||
List<String[]> hashResults = new ArrayList<>();
|
List<String[]> hashResults = new ArrayList<>();
|
||||||
AtomicInteger processedFiles = new AtomicInteger(0);
|
AtomicInteger processedFiles = new AtomicInteger(0);
|
||||||
int totalFiles = allFiles.size();
|
int totalFiles = allFiles.size();
|
||||||
|
|
||||||
allFiles.parallelStream().forEach(file -> {
|
processFilesInParallel(allFiles, hashResults, processedFiles, totalFiles);
|
||||||
try {
|
|
||||||
String hash = new MD5HashCalculator().calculatePartialHash(file);
|
// 检查是否被中断
|
||||||
String[] result = {file.getFileName().toString(), hash};
|
if (Thread.interrupted()) {
|
||||||
synchronized (hashResults) {
|
throw new InterruptedException("任务被取消");
|
||||||
hashResults.add(result);
|
}
|
||||||
}
|
|
||||||
int processed = processedFiles.incrementAndGet();
|
|
||||||
if (listener != null) {
|
|
||||||
listener.onProgressUpdate(processed, totalFiles);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error(LoggerMarker.DEBUG_MARKER, "无法计算该文件哈希值: {} - {}", file, e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 写入结果到文件
|
// 写入结果到文件
|
||||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile.toFile()))) {
|
writeResultsToFile(outputFile, hashResults);
|
||||||
writer.write("文件名,哈希值");
|
}
|
||||||
writer.newLine();
|
|
||||||
for (String[] result : hashResults) {
|
private void processFilesInParallel(List<Path> files, List<String[]> results,
|
||||||
writer.write(result[0] + "," + result[1]);
|
AtomicInteger processedFiles, int totalFiles)
|
||||||
writer.newLine();
|
throws InterruptedException {
|
||||||
|
|
||||||
|
int processors = Runtime.getRuntime().availableProcessors();
|
||||||
|
ExecutorService executor = ThreadPoolManager.createPool("hash-generator", Runtime.getRuntime().availableProcessors());
|
||||||
|
|
||||||
|
try {
|
||||||
|
CountDownLatch latch = new CountDownLatch(files.size());
|
||||||
|
|
||||||
|
for (Path file : files) {
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
// 检查中断
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String hash = hashCalculator.calculatePartialHash(file);
|
||||||
|
String[] result = {file.getFileName().toString(), hash};
|
||||||
|
|
||||||
|
synchronized (results) {
|
||||||
|
results.add(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
int processed = processedFiles.incrementAndGet();
|
||||||
|
safeOnProgressUpdate(processed, totalFiles);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error(LoggerMarker.DEBUG_MARKER, "无法计算文件哈希值: {} - {}", file, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 等待所有任务完成,但响应中断
|
||||||
|
while (!latch.await(100, TimeUnit.MILLISECONDS)) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException("哈希计算被取消");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
executor.shutdownNow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private void writeResultsToFile(Path outputFile, List<String[]> results)
|
||||||
|
throws IOException, InterruptedException {
|
||||||
|
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile.toFile()))) {
|
||||||
|
writer.write("文件名,哈希值");
|
||||||
|
writer.newLine();
|
||||||
|
|
||||||
|
for (String[] result : results) {
|
||||||
|
// 检查中断
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException("文件写入被取消");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write(result[0] + "," + result[1]);
|
||||||
|
writer.newLine();
|
||||||
|
|
||||||
|
// 每写入100行检查一次中断,减少检查频率
|
||||||
|
if (results.indexOf(result) % 100 == 0) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException("文件写入被取消");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package top.r3944realms.docchecktoolrefactored.core;
|
package top.r3944realms.docchecktoolrefactored.core;
|
||||||
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package top.r3944realms.docchecktoolrefactored.io.scanner;
|
package top.r3944realms.docchecktoolrefactored.io.scanner;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface File scanner.
|
* The interface File scanner.
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package top.r3944realms.docchecktoolrefactored.ui;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.PasswordField;
|
import javafx.scene.control.PasswordField;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
|
|
@ -11,6 +12,7 @@ import javafx.scene.input.KeyCombination;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.System;
|
||||||
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
|
|
@ -25,11 +27,12 @@ public class LoginStageController implements Initializable {
|
||||||
/**
|
/**
|
||||||
* The Login button.
|
* The Login button.
|
||||||
*/
|
*/
|
||||||
public Button loginButton;
|
@FXML private Button loginButton;
|
||||||
/**
|
/**
|
||||||
* The Main pane.
|
* The Main pane.
|
||||||
*/
|
*/
|
||||||
public BorderPane mainPane;
|
@FXML private BorderPane mainPane;
|
||||||
|
@FXML private Label versionL;
|
||||||
@FXML private TextField usernameField;
|
@FXML private TextField usernameField;
|
||||||
@FXML private PasswordField passwordField;
|
@FXML private PasswordField passwordField;
|
||||||
|
|
||||||
|
|
@ -43,7 +46,7 @@ public class LoginStageController implements Initializable {
|
||||||
SceneManager.switchMainView();
|
SceneManager.switchMainView();
|
||||||
} else {
|
} else {
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "Invalid username or password");
|
log.info(LoggerMarker.DEBUG_MARKER, "Invalid username or password");
|
||||||
DialogUtil.showErrorDialog("错误", null, "用户名或密码错误!");
|
DialogUtil.showErrorDialog("错误", "输入错误", "用户名或密码错误!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,5 +62,6 @@ public class LoginStageController implements Initializable {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
versionL.setText(System.version());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,8 +7,11 @@ import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.KeyCodeCombination;
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
import top.r3944realms.docchecktoolrefactored.System;
|
import top.r3944realms.docchecktoolrefactored.System;
|
||||||
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -16,6 +19,7 @@ import java.util.List;
|
||||||
/**
|
/**
|
||||||
* The type Main stage controller.
|
* The type Main stage controller.
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class MainStageController {
|
public class MainStageController {
|
||||||
|
|
||||||
@FXML public Button nextB;
|
@FXML public Button nextB;
|
||||||
|
|
@ -115,7 +119,34 @@ public class MainStageController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML void onExit(ActionEvent actionEvent) {
|
@FXML void onExit(ActionEvent actionEvent) {
|
||||||
|
// 显示退出确认对话框
|
||||||
|
boolean confirmExit = DialogUtil.showExitConfirmation(tabPane.getScene().getWindow());
|
||||||
|
|
||||||
|
if (confirmExit) {
|
||||||
|
// 执行退出操作
|
||||||
|
exitApplication();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 退出应用程序
|
||||||
|
*/
|
||||||
|
private void exitApplication() {
|
||||||
|
try {
|
||||||
|
// 保存当前设置(如果需要)
|
||||||
|
System.saveSettingsNow();
|
||||||
|
log.info(LoggerMarker.DEBUG_MARKER, "应用程序正常退出");
|
||||||
|
|
||||||
|
// 关闭应用程序
|
||||||
|
SceneManager.getPrimaryStage().close();
|
||||||
|
|
||||||
|
// 强制退出JVM(确保所有线程都终止)
|
||||||
|
java.lang.System.exit(0);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 退出过程中发生错误,仍然强制退出
|
||||||
|
log.error(LoggerMarker.DEBUG_MARKER, "退出应用程序时发生错误", e);
|
||||||
|
java.lang.System.exit(1); // 非正常退出
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML void onOpenSetting(ActionEvent actionEvent) {
|
@FXML void onOpenSetting(ActionEvent actionEvent) {
|
||||||
|
|
@ -123,9 +154,11 @@ public class MainStageController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML void onOpenHelpDoc(ActionEvent actionEvent) {
|
@FXML void onOpenHelpDoc(ActionEvent actionEvent) {
|
||||||
|
DialogUtil.showDetailedInformationDialog("未实现", "敬请期待","待完善文档后内置");
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML void onAbout(ActionEvent actionEvent) {
|
@FXML void onAbout(ActionEvent actionEvent) {
|
||||||
|
DialogUtil.showDetailedInformationDialog("版本", "版本信息","这里写些信息");
|
||||||
}
|
}
|
||||||
public void updateStepButtonsVisibility() {
|
public void updateStepButtonsVisibility() {
|
||||||
Setting setting = System.getSetting();
|
Setting setting = System.getSetting();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
@ -32,6 +33,9 @@ public class SceneManager {
|
||||||
@Setter
|
@Setter
|
||||||
private static Stage primaryStage;
|
private static Stage primaryStage;
|
||||||
@Getter
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static Image logo;
|
||||||
|
@Getter
|
||||||
private static MainStageController mainController;
|
private static MainStageController mainController;
|
||||||
@Getter
|
@Getter
|
||||||
private static final List<Stage> openStages = new ArrayList<>();
|
private static final List<Stage> openStages = new ArrayList<>();
|
||||||
|
|
@ -40,9 +44,12 @@ public class SceneManager {
|
||||||
* Init.
|
* Init.
|
||||||
*
|
*
|
||||||
* @param primaryStage the primary stage
|
* @param primaryStage the primary stage
|
||||||
|
* @param logo the logo image
|
||||||
*/
|
*/
|
||||||
public static void init(Stage primaryStage) {
|
public static void init(Stage primaryStage, Image logo) {
|
||||||
SceneManager.primaryStage = primaryStage;
|
SceneManager.primaryStage = primaryStage;
|
||||||
|
SceneManager.logo = logo;
|
||||||
|
primaryStage.getIcons().add(logo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -87,6 +94,7 @@ public class SceneManager {
|
||||||
try {
|
try {
|
||||||
Parent root = FXMLLoader.load(Objects.requireNonNull(Main.class.getResource("/fxml/setting-view.fxml")));
|
Parent root = FXMLLoader.load(Objects.requireNonNull(Main.class.getResource("/fxml/setting-view.fxml")));
|
||||||
Stage settingStage = new Stage();
|
Stage settingStage = new Stage();
|
||||||
|
settingStage.getIcons().add(logo);
|
||||||
settingStage.setTitle("数字化验收工具 - 设置");
|
settingStage.setTitle("数字化验收工具 - 设置");
|
||||||
Scene scene = new Scene(root, 300, 206);
|
Scene scene = new Scene(root, 300, 206);
|
||||||
settingStage.setScene(scene); // 默认大小可调
|
settingStage.setScene(scene); // 默认大小可调
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import top.r3944realms.docchecktoolrefactored.System;
|
import top.r3944realms.docchecktoolrefactored.System;
|
||||||
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.ProgressBar;
|
import top.r3944realms.docchecktoolrefactored.ui.utils.ProgressBar;
|
||||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
|
|
@ -58,6 +59,7 @@ public class DuplicateDocumentPaneController {
|
||||||
if (folderPath == null || folderPath.trim().isEmpty()) {
|
if (folderPath == null || folderPath.trim().isEmpty()) {
|
||||||
log.warn(LoggerMarker.DEBUG_MARKER, "未选择文件夹,无法进行查重");
|
log.warn(LoggerMarker.DEBUG_MARKER, "未选择文件夹,无法进行查重");
|
||||||
result1TA.setText("请选择要检查的文件夹。");
|
result1TA.setText("请选择要检查的文件夹。");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "请选择要检查的文件夹");
|
||||||
start1B.setDisable(false);
|
start1B.setDisable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -88,6 +90,7 @@ public class DuplicateDocumentPaneController {
|
||||||
// 绑定取消按钮 -> task.cancel()
|
// 绑定取消按钮 -> task.cancel()
|
||||||
progressBar.setOnCancel(() -> {
|
progressBar.setOnCancel(() -> {
|
||||||
if (currentTask != null && currentTask.isRunning()) {
|
if (currentTask != null && currentTask.isRunning()) {
|
||||||
|
cancel1B.setDisable(false);
|
||||||
currentTask.cancel();
|
currentTask.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -105,7 +108,10 @@ public class DuplicateDocumentPaneController {
|
||||||
task.setOnFailed(e -> {
|
task.setOnFailed(e -> {
|
||||||
progressBar.closeProgress();
|
progressBar.closeProgress();
|
||||||
Throwable exception = task.getException();
|
Throwable exception = task.getException();
|
||||||
|
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||||
|
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||||
result1TA.setText("检测过程中发生错误: " + exception.getMessage());
|
result1TA.setText("检测过程中发生错误: " + exception.getMessage());
|
||||||
|
DialogUtil.showDetailedErrorDialog("错误", "检测过程中发生错误", exception.getMessage());
|
||||||
start1B.setDisable(false);
|
start1B.setDisable(false);
|
||||||
cancel1B.setDisable(true);
|
cancel1B.setDisable(true);
|
||||||
log.error(LoggerMarker.RELEASE_MARKER, "查重任务失败", exception);
|
log.error(LoggerMarker.RELEASE_MARKER, "查重任务失败", exception);
|
||||||
|
|
@ -114,11 +120,11 @@ public class DuplicateDocumentPaneController {
|
||||||
// 处理任务取消情况
|
// 处理任务取消情况
|
||||||
task.setOnCancelled(e -> {
|
task.setOnCancelled(e -> {
|
||||||
progressBar.closeProgress();
|
progressBar.closeProgress();
|
||||||
|
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||||
|
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||||
result1TA.appendText("\n检测已取消");
|
result1TA.appendText("\n检测已取消");
|
||||||
start1B.setDisable(false);
|
start1B.setDisable(false);
|
||||||
cancel1B.setDisable(true);
|
cancel1B.setDisable(true);
|
||||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
|
||||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
|
||||||
log.info(LoggerMarker.RELEASE_MARKER, "查重任务已被取消");
|
log.info(LoggerMarker.RELEASE_MARKER, "查重任务已被取消");
|
||||||
});
|
});
|
||||||
// 在新线程中执行任务
|
// 在新线程中执行任务
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import top.r3944realms.docchecktoolrefactored.core.AddressFileGenerator;
|
||||||
import top.r3944realms.docchecktoolrefactored.ui.SceneManager;
|
import top.r3944realms.docchecktoolrefactored.ui.SceneManager;
|
||||||
import top.r3944realms.docchecktoolrefactored.ui.task.AddressFileComparisonTask;
|
import top.r3944realms.docchecktoolrefactored.ui.task.AddressFileComparisonTask;
|
||||||
import top.r3944realms.docchecktoolrefactored.ui.task.AddressFileGenerationTask;
|
import top.r3944realms.docchecktoolrefactored.ui.task.AddressFileGenerationTask;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||||
import top.r3944realms.docchecktoolrefactored.ui.utils.ProgressBar;
|
import top.r3944realms.docchecktoolrefactored.ui.utils.ProgressBar;
|
||||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
|
|
@ -75,9 +76,10 @@ public class PathCheckPaneController implements Initializable {
|
||||||
loadCatalog2TF.setText(selectedFile.getAbsolutePath());
|
loadCatalog2TF.setText(selectedFile.getAbsolutePath());
|
||||||
System.setLastModifiedFile(selectedFile);
|
System.setLastModifiedFile(selectedFile);
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "选择的目录文件路径为:{}", selectedFile.getAbsolutePath());
|
log.info(LoggerMarker.DEBUG_MARKER, "选择的目录文件路径为:{}", selectedFile.getAbsolutePath());
|
||||||
}else{
|
} else {
|
||||||
log.warn(LoggerMarker.DEBUG_MARKER, "用户未选择任何文件夹");
|
log.warn(LoggerMarker.DEBUG_MARKER, "用户未选择任何文件");
|
||||||
result2TA.setText("未选择任何文件夹,请重新选择。");
|
DialogUtil.showWarningDialog("警告", "操作有误", "未选择任何文件夹,请重新选择");
|
||||||
|
result2TA.setText("未选择载入目录文件,请重新选择。");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,6 +104,10 @@ public class PathCheckPaneController implements Initializable {
|
||||||
if (selectedDirectory != null) {
|
if (selectedDirectory != null) {
|
||||||
loadJPGFolder2TF.setText(selectedDirectory.getAbsolutePath());
|
loadJPGFolder2TF.setText(selectedDirectory.getAbsolutePath());
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "选择的{}文件夹路径为:{}", selectedMode,selectedDirectory.getAbsolutePath());
|
log.info(LoggerMarker.DEBUG_MARKER, "选择的{}文件夹路径为:{}", selectedMode,selectedDirectory.getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
log.warn(LoggerMarker.DEBUG_MARKER, "用户未选择任何文件夹");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "未选择任何文件夹,请重新选择");
|
||||||
|
result2TA.setText("未选择载入文件夹,请重新选择。");
|
||||||
}
|
}
|
||||||
System.setLastModifiedFile(selectedDirectory);
|
System.setLastModifiedFile(selectedDirectory);
|
||||||
}
|
}
|
||||||
|
|
@ -117,19 +123,23 @@ public class PathCheckPaneController implements Initializable {
|
||||||
String filePath = loadCatalog2TF.getText();
|
String filePath = loadCatalog2TF.getText();
|
||||||
if (filePath.isEmpty()) {
|
if (filePath.isEmpty()) {
|
||||||
result2TA.setText("请先选择目录文件。");
|
result2TA.setText("请先选择目录文件。");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "请先选择目录文件");
|
||||||
|
log.warn(LoggerMarker.DEBUG_MARKER, "未选择目录文件");
|
||||||
generateLogicalAddress2B.setDisable(false);
|
generateLogicalAddress2B.setDisable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mode selectedMode = loadFolderType2CB.getValue();
|
||||||
FileChooser fileChooser = System.getFileChooser();
|
FileChooser fileChooser = System.getFileChooser();
|
||||||
fileChooser.setTitle("选择保存逻辑地址文件的位置");
|
fileChooser.setTitle("选择保存逻辑地址文件的位置");
|
||||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||||
fileChooser.setInitialFileName("逻辑地址文件.csv");
|
fileChooser.setInitialFileName(selectedMode.toString() + "逻辑地址文件.csv");
|
||||||
|
|
||||||
File outputFile = fileChooser.showSaveDialog(generateLogicalAddress2B.getScene().getWindow());
|
File outputFile = fileChooser.showSaveDialog(generateLogicalAddress2B.getScene().getWindow());
|
||||||
if (outputFile == null) {
|
if (outputFile == null) {
|
||||||
result2TA.setText("未选择保存位置");
|
result2TA.setText("未选择保存位置");
|
||||||
log.warn(LoggerMarker.DEBUG_MARKER, "用户未选择任何文件");
|
log.warn(LoggerMarker.DEBUG_MARKER, "用户未选择任何文件");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "未选择保存位置");
|
||||||
generateLogicalAddress2B.setDisable(true);
|
generateLogicalAddress2B.setDisable(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -146,7 +156,7 @@ public class PathCheckPaneController implements Initializable {
|
||||||
// 保存生成的文件路径
|
// 保存生成的文件路径
|
||||||
logicalAddressFilePath = finalOutputFile.getAbsolutePath();
|
logicalAddressFilePath = finalOutputFile.getAbsolutePath();
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "选择的输出文件路径: {}", logicalAddressFilePath);
|
log.info(LoggerMarker.DEBUG_MARKER, "选择的输出文件路径: {}", logicalAddressFilePath);
|
||||||
Mode selectedMode = loadFolderType2CB.getValue();
|
|
||||||
// 创建后台任务
|
// 创建后台任务
|
||||||
AddressFileGenerationTask task = new AddressFileGenerationTask(filePath, outputFile, selectedMode.number, true);
|
AddressFileGenerationTask task = new AddressFileGenerationTask(filePath, outputFile, selectedMode.number, true);
|
||||||
// 绑定任务属性到UI
|
// 绑定任务属性到UI
|
||||||
|
|
@ -176,7 +186,10 @@ public class PathCheckPaneController implements Initializable {
|
||||||
task.setOnFailed(e -> {
|
task.setOnFailed(e -> {
|
||||||
progressBar.closeProgress();
|
progressBar.closeProgress();
|
||||||
Throwable exception = task.getException();
|
Throwable exception = task.getException();
|
||||||
|
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||||
|
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||||
result2TA.setText("检测过程中发生错误: " + exception.getMessage());
|
result2TA.setText("检测过程中发生错误: " + exception.getMessage());
|
||||||
|
DialogUtil.showDetailedErrorDialog("错误", "检测过程中发生错误: ", exception.getMessage());
|
||||||
generateLogicalAddress2B.setDisable(false);
|
generateLogicalAddress2B.setDisable(false);
|
||||||
log.error(LoggerMarker.RELEASE_MARKER, "生成逻辑路径 csv 文件任务失败", exception);
|
log.error(LoggerMarker.RELEASE_MARKER, "生成逻辑路径 csv 文件任务失败", exception);
|
||||||
});
|
});
|
||||||
|
|
@ -184,10 +197,10 @@ public class PathCheckPaneController implements Initializable {
|
||||||
// 处理任务取消情况
|
// 处理任务取消情况
|
||||||
task.setOnCancelled(e -> {
|
task.setOnCancelled(e -> {
|
||||||
progressBar.closeProgress();
|
progressBar.closeProgress();
|
||||||
result2TA.appendText("\n检测已取消");
|
|
||||||
generateLogicalAddress2B.setDisable(false);
|
|
||||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||||
|
result2TA.appendText("\n检测已取消");
|
||||||
|
generateLogicalAddress2B.setDisable(false);
|
||||||
log.info(LoggerMarker.RELEASE_MARKER, "生成逻辑路径 csv 文件任务已被取消");
|
log.info(LoggerMarker.RELEASE_MARKER, "生成逻辑路径 csv 文件任务已被取消");
|
||||||
});
|
});
|
||||||
Thread thread = new Thread(task);
|
Thread thread = new Thread(task);
|
||||||
|
|
@ -205,25 +218,30 @@ public class PathCheckPaneController implements Initializable {
|
||||||
String folderPath = loadJPGFolder2TF.getText();
|
String folderPath = loadJPGFolder2TF.getText();
|
||||||
if (folderPath.isEmpty()) {
|
if (folderPath.isEmpty()) {
|
||||||
result2TA.setText("请先选择文件夹。");
|
result2TA.setText("请先选择文件夹。");
|
||||||
|
log.warn(LoggerMarker.DEBUG_MARKER, "请先选择文件夹");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "请先选择文件夹");
|
||||||
generatePhysicalAddress2B.setDisable(false);
|
generatePhysicalAddress2B.setDisable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
File folder = new File(folderPath);
|
File folder = new File(folderPath);
|
||||||
if(!folder.exists() || !folder.isDirectory()) {
|
if(!folder.exists() || !folder.isDirectory()) {
|
||||||
result2TA.setText("所选路径不存在或不是一个有效的文件夹。");
|
result2TA.setText("所选路径不存在或不是一个有效的文件夹。");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "所选路径不存在或不是一个有效的文件夹");
|
||||||
|
log.warn(LoggerMarker.DEBUG_MARKER, "所选路径不存在或不是一个有效的文件夹");
|
||||||
generatePhysicalAddress2B.setDisable(false);
|
generatePhysicalAddress2B.setDisable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Mode selectedMode = loadFolderType2CB.getValue();
|
||||||
FileChooser fileChooser = System.getFileChooser();
|
FileChooser fileChooser = System.getFileChooser();
|
||||||
fileChooser.setTitle("选择保存物理地址文件的位置");
|
fileChooser.setTitle("选择保存物理地址文件的位置");
|
||||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||||
fileChooser.setInitialFileName("物理地址文件.csv");
|
fileChooser.setInitialFileName(selectedMode.toString() + "物理地址文件.csv");
|
||||||
// 使用当前窗口作为父窗口显示文件选择对话框
|
// 使用当前窗口作为父窗口显示文件选择对话框
|
||||||
File outputFile = fileChooser.showSaveDialog(selectJPGFolder2B.getScene().getWindow());
|
File outputFile = fileChooser.showSaveDialog(selectJPGFolder2B.getScene().getWindow());
|
||||||
|
|
||||||
if (outputFile == null) {
|
if (outputFile == null) {
|
||||||
result2TA.setText("未选择保存位置");
|
result2TA.setText("未选择保存位置");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "未选择保存位置");
|
||||||
generatePhysicalAddress2B.setDisable(false);
|
generatePhysicalAddress2B.setDisable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -240,8 +258,6 @@ public class PathCheckPaneController implements Initializable {
|
||||||
// 保存生成的文件路径
|
// 保存生成的文件路径
|
||||||
physicalAddressFilePath = finalOutputFile.getAbsolutePath();
|
physicalAddressFilePath = finalOutputFile.getAbsolutePath();
|
||||||
|
|
||||||
//
|
|
||||||
Mode selectedMode = loadFolderType2CB.getValue();
|
|
||||||
// 创建后台任务
|
// 创建后台任务
|
||||||
AddressFileGenerationTask task = new AddressFileGenerationTask(folderPath, outputFile, selectedMode.number, false);
|
AddressFileGenerationTask task = new AddressFileGenerationTask(folderPath, outputFile, selectedMode.number, false);
|
||||||
// 保存到字段
|
// 保存到字段
|
||||||
|
|
@ -273,7 +289,10 @@ public class PathCheckPaneController implements Initializable {
|
||||||
task.setOnFailed(e -> {
|
task.setOnFailed(e -> {
|
||||||
progressBar.closeProgress();
|
progressBar.closeProgress();
|
||||||
Throwable exception = task.getException();
|
Throwable exception = task.getException();
|
||||||
|
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||||
|
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||||
result2TA.setText("检测过程中发生错误: " + exception.getMessage());
|
result2TA.setText("检测过程中发生错误: " + exception.getMessage());
|
||||||
|
DialogUtil.showDetailedErrorDialog("错误", "检测过程中发生错误: ", exception.getMessage());
|
||||||
generatePhysicalAddress2B.setDisable(false);
|
generatePhysicalAddress2B.setDisable(false);
|
||||||
log.error(LoggerMarker.RELEASE_MARKER, "生成物理路径 csv 文件任务失败", exception);
|
log.error(LoggerMarker.RELEASE_MARKER, "生成物理路径 csv 文件任务失败", exception);
|
||||||
});
|
});
|
||||||
|
|
@ -281,10 +300,10 @@ public class PathCheckPaneController implements Initializable {
|
||||||
// 处理任务取消情况
|
// 处理任务取消情况
|
||||||
task.setOnCancelled(e -> {
|
task.setOnCancelled(e -> {
|
||||||
progressBar.closeProgress();
|
progressBar.closeProgress();
|
||||||
result2TA.appendText("\n检测已取消");
|
|
||||||
generatePhysicalAddress2B.setDisable(false);
|
|
||||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||||
|
result2TA.appendText("\n检测已取消");
|
||||||
|
generatePhysicalAddress2B.setDisable(false);
|
||||||
log.info(LoggerMarker.RELEASE_MARKER, "生成物理路径 csv 文件任务已被取消");
|
log.info(LoggerMarker.RELEASE_MARKER, "生成物理路径 csv 文件任务已被取消");
|
||||||
});
|
});
|
||||||
Thread thread = new Thread(task);
|
Thread thread = new Thread(task);
|
||||||
|
|
@ -350,8 +369,9 @@ public class PathCheckPaneController implements Initializable {
|
||||||
task.setOnFailed(event -> {
|
task.setOnFailed(event -> {
|
||||||
cancelableProgressBar.closeProgress();
|
cancelableProgressBar.closeProgress();
|
||||||
Throwable exception = task.getException();
|
Throwable exception = task.getException();
|
||||||
result2TA.setText("文件比对失败: " + task.getException().getMessage());
|
result2TA.setText("文件比对失败: " + exception.getMessage());
|
||||||
start2B.setDisable(false);
|
start2B.setDisable(false);
|
||||||
|
DialogUtil.showDetailedErrorDialog("错误", "文件比对失败:", exception.getMessage());
|
||||||
log.error(LoggerMarker.RELEASE_MARKER, "查漏任务失败", exception);
|
log.error(LoggerMarker.RELEASE_MARKER, "查漏任务失败", exception);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package top.r3944realms.docchecktoolrefactored.ui.module;
|
||||||
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Dialog;
|
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package top.r3944realms.docchecktoolrefactored.ui.module;
|
package top.r3944realms.docchecktoolrefactored.ui.module;
|
||||||
|
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
|
@ -11,13 +12,14 @@ import javafx.stage.FileChooser;
|
||||||
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.HashFileGenerator;
|
|
||||||
import top.r3944realms.docchecktoolrefactored.core.MD5HashCalculator;
|
import top.r3944realms.docchecktoolrefactored.core.MD5HashCalculator;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ui.SceneManager;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ui.task.HashFileGenerationTask;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
//TODO: 应该交给Platform:runLater;
|
//TODO: 应该交给Platform:runLater;
|
||||||
|
|
@ -48,9 +50,7 @@ public class StorageCarrierPaneController {
|
||||||
@FXML
|
@FXML
|
||||||
private Button clearSelectedFoldersButton;
|
private Button clearSelectedFoldersButton;
|
||||||
|
|
||||||
|
private final top.r3944realms.docchecktoolrefactored.ui.utils.ProgressBar progressBar = new top.r3944realms.docchecktoolrefactored.ui.utils.ProgressBar(false);
|
||||||
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void onSelectLD(ActionEvent event) {
|
void onSelectLD(ActionEvent event) {
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "用户点击选择文件夹按钮");
|
log.info(LoggerMarker.DEBUG_MARKER, "用户点击选择文件夹按钮");
|
||||||
|
|
@ -117,11 +117,13 @@ public class StorageCarrierPaneController {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void onCaculateHash(ActionEvent event) {
|
void onCaculateHash(ActionEvent event) {
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "开始计算RAR文件的MD5哈希值");
|
generateHashFile7B.setDisable(true);
|
||||||
String filePath = loadCompressedFile.getText();
|
String filePath = loadCompressedFile.getText();
|
||||||
if (filePath == null || filePath.isEmpty()) {
|
if (filePath == null || filePath.isEmpty()) {
|
||||||
log.warn(LoggerMarker.DEBUG_MARKER, "未选择RAR文件,无法计算哈希值");
|
log.warn(LoggerMarker.DEBUG_MARKER, "未选择RAR文件,无法计算哈希值");
|
||||||
result7TA.setText("请先选择一个 .rar 文件");
|
result7TA.setText("请先选择一个 .rar 文件");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "请先选择一个 .rar 文件");
|
||||||
|
generateHashFile7B.setDisable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,31 +131,38 @@ public class StorageCarrierPaneController {
|
||||||
if (!file.exists() || !file.isFile() || !filePath.endsWith(".rar")) {
|
if (!file.exists() || !file.isFile() || !filePath.endsWith(".rar")) {
|
||||||
log.warn(LoggerMarker.DEBUG_MARKER, "选择的文件无效或不是RAR文件: {}", filePath);
|
log.warn(LoggerMarker.DEBUG_MARKER, "选择的文件无效或不是RAR文件: {}", filePath);
|
||||||
result7TA.setText("所选文件不存在或不是一个有效的 .rar 文件");
|
result7TA.setText("所选文件不存在或不是一个有效的 .rar 文件");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "所选文件不存在或不是一个有效的 .rar 文件");
|
||||||
|
generateHashFile7B.setDisable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "开始计算文件哈希值: {}", filePath);
|
log.info(LoggerMarker.DEBUG_MARKER, "开始计算RAR文件MD5哈希值: {}", filePath);
|
||||||
MD5HashCalculator hashCalculator = new MD5HashCalculator();
|
MD5HashCalculator hashCalculator = new MD5HashCalculator();
|
||||||
String hashResult = hashCalculator.calculateHash(file.toPath());
|
String hashResult = hashCalculator.calculateHash(file.toPath());
|
||||||
result7TA.setText("计算结果:\n" + hashResult);
|
result7TA.setText("计算结果:\n" + hashResult);
|
||||||
|
generateHashFile7B.setDisable(false);
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "文件哈希值计算完成: {}", hashResult);
|
log.info(LoggerMarker.DEBUG_MARKER, "文件哈希值计算完成: {}", hashResult);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error(LoggerMarker.DEBUG_MARKER, "计算文件哈希值时出错: {}", filePath, e);
|
log.error(LoggerMarker.DEBUG_MARKER, "计算文件哈希值时出错: {}", filePath, e);
|
||||||
|
DialogUtil.showDetailedErrorDialog("错误", "生成哈希文件时出错:", e.getMessage());
|
||||||
|
generateHashFile7B.setDisable(false);
|
||||||
result7TA.setText("计算哈希值时出错: " + e.getMessage());
|
result7TA.setText("计算哈希值时出错: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void onGenerateHF(ActionEvent event) {
|
void onGenerateHF(ActionEvent event) {
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "开始生成哈希列表文件");
|
caculateHash7B.setDisable(true);
|
||||||
|
|
||||||
String folderPathsText = loadDigitalOutcomes.getText();
|
String folderPathsText = loadDigitalOutcomes.getText();
|
||||||
if (folderPathsText == null || folderPathsText.isEmpty()) {
|
if (folderPathsText == null || folderPathsText.isEmpty()) {
|
||||||
log.warn(LoggerMarker.DEBUG_MARKER, "未选择文件夹,无法生成哈希列表文件");
|
log.warn(LoggerMarker.DEBUG_MARKER, "未选择文件夹,无法生成哈希列表文件");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "请先选择一个文件夹");
|
||||||
result7TA.setText("请先选择一个文件夹");
|
result7TA.setText("请先选择一个文件夹");
|
||||||
|
caculateHash7B.setDisable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
log.info(LoggerMarker.DEBUG_MARKER, "开始生成哈希列表文件");
|
||||||
// 解析多个文件夹路径
|
// 解析多个文件夹路径
|
||||||
String[] folderPaths = folderPathsText.split(File.pathSeparator);
|
String[] folderPaths = folderPathsText.split(File.pathSeparator);
|
||||||
List<File> folders = new ArrayList<>();
|
List<File> folders = new ArrayList<>();
|
||||||
|
|
@ -165,6 +174,8 @@ public class StorageCarrierPaneController {
|
||||||
} else {
|
} else {
|
||||||
log.warn(LoggerMarker.DEBUG_MARKER, "选择的路径无效或不是文件夹: {}", path);
|
log.warn(LoggerMarker.DEBUG_MARKER, "选择的路径无效或不是文件夹: {}", path);
|
||||||
result7TA.setText("所选路径不存在或不是一个有效的文件夹: " + path);
|
result7TA.setText("所选路径不存在或不是一个有效的文件夹: " + path);
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", ("所选路径不存在或不是一个有效的文件夹: " + path));
|
||||||
|
caculateHash7B.setDisable(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -182,6 +193,7 @@ public class StorageCarrierPaneController {
|
||||||
if (outputFile == null) {
|
if (outputFile == null) {
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "用户取消了文件保存操作");
|
log.info(LoggerMarker.DEBUG_MARKER, "用户取消了文件保存操作");
|
||||||
result7TA.setText("未选择保存位置");
|
result7TA.setText("未选择保存位置");
|
||||||
|
DialogUtil.showWarningDialog("警告", "操作有误", "未选择保存位置");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,55 +206,51 @@ public class StorageCarrierPaneController {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "选择的输出文件路径: {}", finalOutputFile.getAbsolutePath());
|
log.info(LoggerMarker.DEBUG_MARKER, "选择的输出文件路径: {}", finalOutputFile.getAbsolutePath());
|
||||||
|
progressBar.showProgress(SceneManager.getPrimaryStage(), "生成哈希值列表文件", "正在初始化...");
|
||||||
// 创建后台任务
|
// 创建后台任务
|
||||||
Task<String> task = new Task<>() {
|
Task<String> task = new HashFileGenerationTask(finalOutputFile, folders);
|
||||||
@Override
|
// 绑定任务属性到UI
|
||||||
protected String call() throws Exception {
|
ChangeListener<Number> progressChangeListener = (obs, oldVal, newVal) -> {
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "开始执行哈希文件生成任务");
|
if (newVal != null) {
|
||||||
updateMessage("开始生成哈希文件...");
|
if (task.getMessage() != null) {
|
||||||
|
progressBar.updateProgress(newVal.doubleValue(), task.getMessage());
|
||||||
HashFileGenerator generator = new HashFileGenerator();
|
}
|
||||||
|
|
||||||
// 传递多个文件夹路径
|
|
||||||
List<Path> folderPaths = folders.stream().map(File::toPath).collect(ArrayList::new,
|
|
||||||
ArrayList::add,
|
|
||||||
ArrayList::addAll);
|
|
||||||
|
|
||||||
generator.generateHashFile(folderPaths, finalOutputFile.toPath(), (current, total) -> {
|
|
||||||
updateProgress(current, total);
|
|
||||||
updateMessage("处理文件: " + current + "/" + total);
|
|
||||||
if (current % 500 == 0 || current == total) { // 每500个文件或完成时记录一次日志
|
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "处理进度: {}/{}", current, total);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务完成,输出文件: {}", finalOutputFile.getAbsolutePath());
|
|
||||||
return "哈希列表文件已生成: " + finalOutputFile.getAbsolutePath();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
task.progressProperty().addListener(progressChangeListener);
|
||||||
// 绑定任务的消息到结果文本区域,实时显示进度
|
// 绑定任务的消息到结果文本区域,实时显示进度
|
||||||
task.messageProperty().addListener((observable, oldValue, newValue) -> {
|
ChangeListener<String> messageChangeListener = (observable, oldValue, newValue) -> {
|
||||||
result7TA.setText(newValue);
|
result7TA.setText(newValue);
|
||||||
});
|
};
|
||||||
|
task.messageProperty().addListener(messageChangeListener);
|
||||||
|
|
||||||
// 任务成功完成
|
// 任务成功完成
|
||||||
task.setOnSucceeded(e -> {
|
task.setOnSucceeded(e -> {
|
||||||
|
progressBar.closeProgress();
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务成功完成");
|
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务成功完成");
|
||||||
|
caculateHash7B.setDisable(false);
|
||||||
result7TA.setText(task.getValue());
|
result7TA.setText(task.getValue());
|
||||||
});
|
});
|
||||||
|
|
||||||
// 任务失败处理
|
// 任务失败处理
|
||||||
task.setOnFailed(e -> {
|
task.setOnFailed(e -> {
|
||||||
|
progressBar.closeProgress();
|
||||||
Throwable exception = task.getException();
|
Throwable exception = task.getException();
|
||||||
|
task.progressProperty().removeListener(progressChangeListener);
|
||||||
|
task.messageProperty().removeListener(messageChangeListener);
|
||||||
|
caculateHash7B.setDisable(false);
|
||||||
String errorMsg = "生成哈希文件时出错: " + (exception != null ? exception.getMessage() : "未知错误");
|
String errorMsg = "生成哈希文件时出错: " + (exception != null ? exception.getMessage() : "未知错误");
|
||||||
|
DialogUtil.showDetailedErrorDialog("错误", "生成哈希文件时出错", errorMsg);
|
||||||
log.error(LoggerMarker.RELEASE_MARKER, "哈希文件生成任务失败", exception);
|
log.error(LoggerMarker.RELEASE_MARKER, "哈希文件生成任务失败", exception);
|
||||||
result7TA.setText(errorMsg);
|
result7TA.setText(errorMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 任务取消处理
|
// 任务取消处理
|
||||||
task.setOnCancelled(e -> {
|
task.setOnCancelled(e -> {
|
||||||
|
progressBar.closeProgress();
|
||||||
|
task.progressProperty().removeListener(progressChangeListener);
|
||||||
|
task.messageProperty().removeListener(messageChangeListener);
|
||||||
|
caculateHash7B.setDisable(false);
|
||||||
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务被用户取消");
|
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务被用户取消");
|
||||||
result7TA.setText("哈希文件生成操作已取消");
|
result7TA.setText("哈希文件生成操作已取消");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ public class AddressFileComparisonTask extends Task<AddressFileComparator.Compar
|
||||||
this.logicalFilePath = logicalFilePath;
|
this.logicalFilePath = logicalFilePath;
|
||||||
this.compareMode = compareMode;
|
this.compareMode = compareMode;
|
||||||
this.timeoutSeconds = timeoutSeconds;
|
this.timeoutSeconds = timeoutSeconds;
|
||||||
comparator = new AddressFileComparator();
|
this.comparator = new AddressFileComparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -75,7 +75,7 @@ public class AddressFileComparisonTask extends Task<AddressFileComparator.Compar
|
||||||
.get(timeoutSeconds, TimeUnit.SECONDS);
|
.get(timeoutSeconds, TimeUnit.SECONDS);
|
||||||
|
|
||||||
} catch (TimeoutException e) {
|
} catch (TimeoutException e) {
|
||||||
updateMessage("文件比对超时,请检查文件大小或电脑性能。");
|
updateMessage("文件比对超时,请考虑在‘文件’-‘设置’里增加‘步骤任务超时时间’。");
|
||||||
log.error("文件比对超时", e);
|
log.error("文件比对超时", e);
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -86,6 +86,9 @@ public class AddressFileComparisonTask extends Task<AddressFileComparator.Compar
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cancelled() {
|
protected void cancelled() {
|
||||||
|
super.cancelled();
|
||||||
comparator.shutdown();
|
comparator.shutdown();
|
||||||
|
updateMessage("文件比较任务已取消");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,15 @@ 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.System;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ThreadPoolManager;
|
||||||
import top.r3944realms.docchecktoolrefactored.core.AddressFileGenerator;
|
import top.r3944realms.docchecktoolrefactored.core.AddressFileGenerator;
|
||||||
import top.r3944realms.docchecktoolrefactored.core.LogicalAddressFileGenerator;
|
import top.r3944realms.docchecktoolrefactored.core.LogicalAddressFileGenerator;
|
||||||
import top.r3944realms.docchecktoolrefactored.core.PhysicalAddressFileGenerator;
|
import top.r3944realms.docchecktoolrefactored.core.PhysicalAddressFileGenerator;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
import static java.util.concurrent.Executors.*;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AddressFileGenerationTask extends Task<String> {
|
public class AddressFileGenerationTask extends Task<String> {
|
||||||
|
|
||||||
|
|
@ -19,6 +19,9 @@ public class AddressFileGenerationTask extends Task<String> {
|
||||||
private final File outputFile;
|
private final File outputFile;
|
||||||
private final int folderType;
|
private final int folderType;
|
||||||
private final AddressFileGenerator generator;
|
private final AddressFileGenerator generator;
|
||||||
|
private ExecutorService executor;
|
||||||
|
private Future<?> future;
|
||||||
|
|
||||||
|
|
||||||
public AddressFileGenerationTask(String sourcePath,
|
public AddressFileGenerationTask(String sourcePath,
|
||||||
File outputFile,
|
File outputFile,
|
||||||
|
|
@ -31,60 +34,107 @@ public class AddressFileGenerationTask extends Task<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String call() {
|
protected String call() throws Exception {
|
||||||
updateMessage("初始化生成任务...");
|
updateMessage("初始化生成任务...");
|
||||||
generator.setProgressCallback(new AddressFileGenerator.ProgressCallback() {
|
generator.setProgressCallback(new AddressFileGenerator.ProgressCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPhaseStarted(AddressFileGenerator.Phase phase) {
|
public void onPhaseStarted(AddressFileGenerator.Phase phase) {
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
case GENERATE_LOGICAL -> updateMessage("正在生成逻辑地址 CSV 文件 ...");
|
case GENERATE_LOGICAL -> updateMessage("正在生成逻辑地址 CSV 文件 ...");
|
||||||
case GENERATE_PHYSICAL -> updateMessage("正在生成物理地址 CSV 文件 ...");
|
case GENERATE_PHYSICAL -> updateMessage("正在生成物理地址 CSV 文件 ...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPhaseProgress(AddressFileGenerator.Phase phase, int current, int total) {
|
public void onPhaseProgress(AddressFileGenerator.Phase phase, int current, int total) {
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
updateProgress(current, total);
|
updateProgress(current, total);
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
case GENERATE_LOGICAL -> updateMessage(String.format("在生成逻辑地址 CSV : %d/%d", current, total));
|
case GENERATE_LOGICAL -> updateMessage(String.format("在生成逻辑地址 CSV : %d/%d", current, total));
|
||||||
case GENERATE_PHYSICAL -> updateMessage(String.format("在生成物理地址 CSV : %d/%d", current, total));
|
case GENERATE_PHYSICAL -> updateMessage(String.format("在生成物理地址 CSV : %d/%d", current, total));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPhaseCompleted(AddressFileGenerator.Phase phase) {
|
public void onPhaseCompleted(AddressFileGenerator.Phase phase) {
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
case GENERATE_LOGICAL -> updateMessage("已完成生成逻辑地址 CSV 文件任务");
|
case GENERATE_LOGICAL -> updateMessage("已完成生成逻辑地址 CSV 文件任务");
|
||||||
case GENERATE_PHYSICAL -> updateMessage("已完成生成物理地址 CSV 文件任务");
|
case GENERATE_PHYSICAL -> updateMessage("已完成生成物理地址 CSV 文件任务");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
executor = ThreadPoolManager.createPool("address-file-generate-pool", System.getAvailableProcessors());
|
||||||
ExecutorService executor = newSingleThreadExecutor();
|
|
||||||
Future<?> future = executor.submit(() -> {
|
// 提交任务到线程池
|
||||||
generator.generateAddressFile(sourcePath, outputFile, folderType);
|
future = executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
generator.generateAddressFile(sourcePath, outputFile, folderType);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("地址文件生成失败", e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 等待执行完成或超时
|
// 等待执行完成或超时
|
||||||
future.get(System.getSetting().getTaskTimeout(), TimeUnit.SECONDS);
|
future.get(System.getSetting().getTaskTimeout(), TimeUnit.SECONDS);
|
||||||
} catch (TimeoutException e) {
|
return outputFile.getAbsolutePath();
|
||||||
future.cancel(true); // 尝试中断
|
|
||||||
this.cancel(); // 取消 Task
|
|
||||||
throw new RuntimeException("生成任务超时 (>" + System.getSetting().getTaskTimeout() + "s)", e);
|
|
||||||
} catch (ExecutionException | InterruptedException e) {
|
|
||||||
throw new RuntimeException("生成任务失败", e.getCause());
|
|
||||||
} finally {
|
|
||||||
executor.shutdownNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
return outputFile.getAbsolutePath();
|
} catch (TimeoutException e) {
|
||||||
|
String errorMsg = "生成任务超时 (>" + System.getSetting().getTaskTimeout() + "s)";
|
||||||
|
updateMessage(errorMsg);
|
||||||
|
throw new RuntimeException(errorMsg, e);
|
||||||
|
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
String errorMsg = "生成任务失败: " + e.getCause().getMessage();
|
||||||
|
updateMessage(errorMsg);
|
||||||
|
throw new RuntimeException(errorMsg, e.getCause());
|
||||||
|
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
updateMessage("生成任务被取消");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw e;
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// 清理资源
|
||||||
|
if (executor != null) {
|
||||||
|
try {
|
||||||
|
executor.shutdown();
|
||||||
|
if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
|
||||||
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
executor.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cancelled() {
|
protected void cancelled() {
|
||||||
log.info("生成任务已取消: {}", outputFile.getAbsolutePath());
|
super.cancelled();
|
||||||
|
log.info(LoggerMarker.DEBUG_MARKER, "生成任务已取消: {}", outputFile.getAbsolutePath());
|
||||||
|
updateMessage("正在取消生成任务...");
|
||||||
|
|
||||||
|
// 先取消 Future
|
||||||
|
if (future != null && !future.isDone()) {
|
||||||
|
future.cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给任务一些时间响应中断
|
||||||
|
try {
|
||||||
|
if (executor != null) {
|
||||||
|
executor.shutdown(); // 先尝试正常关闭
|
||||||
|
if (!executor.awaitTermination(2, TimeUnit.SECONDS)) {
|
||||||
|
executor.shutdownNow(); // 强制关闭
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
executor.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMessage("生成任务已取消");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,24 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DuplicateDocumentDetectionTask extends Task<String>{
|
public class DuplicateDocumentDetectionTask extends Task<String>{
|
||||||
private final String folderPath;
|
private final String folderPath;
|
||||||
private final MD5HashCalculator hashCalculator;
|
private final DuplicateFinder duplicateFinder;
|
||||||
private volatile RobustParallelScanner scanner;
|
|
||||||
|
|
||||||
public DuplicateDocumentDetectionTask(String folderPath) {
|
public DuplicateDocumentDetectionTask(String folderPath) {
|
||||||
this.folderPath = folderPath;
|
this.folderPath = folderPath;
|
||||||
this.hashCalculator = new MD5HashCalculator();
|
// 创建带进度更新的扫描器
|
||||||
|
RobustParallelScanner scanner = new RobustParallelScanner(10);
|
||||||
|
MD5HashCalculator hashCalculator = new MD5HashCalculator();
|
||||||
|
// 进度监听的 DuplicateFinder
|
||||||
|
this.duplicateFinder = new DuplicateFinder(scanner, hashCalculator, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -39,12 +44,7 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
||||||
if (!Files.exists(rootPath) || !Files.isDirectory(rootPath)) {
|
if (!Files.exists(rootPath) || !Files.isDirectory(rootPath)) {
|
||||||
throw new IllegalArgumentException("指定路径不是有效目录: " + folderPath);
|
throw new IllegalArgumentException("指定路径不是有效目录: " + folderPath);
|
||||||
}
|
}
|
||||||
// 创建带进度更新的扫描器
|
duplicateFinder.applySetting(System.getSetting());
|
||||||
scanner = new RobustParallelScanner(10);
|
|
||||||
|
|
||||||
// 创建带有进度监听的 DuplicateFinder
|
|
||||||
DuplicateFinder duplicateFinder = new DuplicateFinder(scanner, hashCalculator, true)
|
|
||||||
.applySetting(System.getSetting());
|
|
||||||
|
|
||||||
// 用于统计文件总数
|
// 用于统计文件总数
|
||||||
AtomicInteger totalFiles = new AtomicInteger(0);
|
AtomicInteger totalFiles = new AtomicInteger(0);
|
||||||
|
|
@ -93,7 +93,7 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
||||||
});
|
});
|
||||||
|
|
||||||
AtomicReference<List<DuplicateGroup>> resultRef = new AtomicReference<>();
|
AtomicReference<List<DuplicateGroup>> resultRef = new AtomicReference<>();
|
||||||
List<Exception> errors = new CopyOnWriteArrayList<>();
|
AtomicReference<Throwable> errorRef = new AtomicReference<>();
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
// 在单独线程中执行查找
|
// 在单独线程中执行查找
|
||||||
|
|
@ -102,7 +102,7 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
||||||
List<DuplicateGroup> duplicates = duplicateFinder.findDuplicates(rootPath);
|
List<DuplicateGroup> duplicates = duplicateFinder.findDuplicates(rootPath);
|
||||||
resultRef.set(duplicates);
|
resultRef.set(duplicates);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
errors.add(e);
|
errorRef.set(e);
|
||||||
} finally {
|
} finally {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
|
|
@ -111,42 +111,18 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
||||||
findThread.setDaemon(true);
|
findThread.setDaemon(true);
|
||||||
findThread.start();
|
findThread.start();
|
||||||
|
|
||||||
// 等待扫描完成,设置超时时间(例如5分钟)
|
// 简单等待,Task取消时会自动中断
|
||||||
long totalTimeout = System.getSetting().getTaskTimeout();
|
long totalTimeout = System.getSetting().getTaskTimeout();
|
||||||
if (!latch.await(totalTimeout, TimeUnit.SECONDS)) {
|
if (!latch.await(totalTimeout, TimeUnit.SECONDS)) {
|
||||||
duplicateFinder.shutdown();
|
duplicateFinder.shutdown();
|
||||||
throw new TimeoutException(String.format("扫描超时(%d秒)", totalTimeout));
|
throw new TimeoutException(String.format("任务超时(%d秒),请考虑在‘文件’-‘设置’里增加‘步骤任务超时时间’", totalTimeout));
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否被取消
|
|
||||||
long start = java.lang.System.currentTimeMillis();
|
|
||||||
try {
|
|
||||||
boolean finished = false;
|
|
||||||
while (!finished) {
|
|
||||||
// 每 200ms 等待一次 latch,避免忙等待
|
|
||||||
finished = latch.await(200, TimeUnit.MILLISECONDS);
|
|
||||||
|
|
||||||
// 检查是否被取消
|
|
||||||
if (isCancelled()) {
|
|
||||||
duplicateFinder.shutdown();
|
|
||||||
return "操作已被取消";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否超时
|
|
||||||
if (java.lang.System.currentTimeMillis() - start > totalTimeout * 1000L) {
|
|
||||||
duplicateFinder.shutdown();
|
|
||||||
throw new TimeoutException(String.format("扫描超时(%d秒)", totalTimeout));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
duplicateFinder.shutdown();
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return "操作被中断";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有错误
|
// 检查是否有错误
|
||||||
if (!errors.isEmpty()) {
|
if (errorRef.get() != null) {
|
||||||
throw new ScanningException(errors);
|
throw new RuntimeException(errorRef.get());
|
||||||
|
} else if (!duplicateFinder.getErrors().isEmpty()) {
|
||||||
|
throw new ScanningException(duplicateFinder.getErrors());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DuplicateGroup> duplicateGroups = resultRef.get();
|
List<DuplicateGroup> duplicateGroups = resultRef.get();
|
||||||
|
|
@ -198,8 +174,8 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
||||||
@Override
|
@Override
|
||||||
protected void cancelled() {
|
protected void cancelled() {
|
||||||
super.cancelled();
|
super.cancelled();
|
||||||
if (scanner != null) {
|
// 清理资源
|
||||||
scanner.cancel();
|
duplicateFinder.shutdown();
|
||||||
}
|
updateMessage("操作已被取消");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
package top.r3944realms.docchecktoolrefactored.ui.task;
|
||||||
|
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.System;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.ThreadPoolManager;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.core.HashFileGenerator;
|
||||||
|
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class HashFileGenerationTask extends Task<String> {
|
||||||
|
private final HashFileGenerator generator;
|
||||||
|
private final File finalOutputFile;
|
||||||
|
private final List<File> folders;
|
||||||
|
private ExecutorService executor;
|
||||||
|
private Future<?> future;
|
||||||
|
|
||||||
|
public HashFileGenerationTask(File finalOutputFile, List<File> folders) {
|
||||||
|
this.finalOutputFile = finalOutputFile;
|
||||||
|
this.folders = folders;
|
||||||
|
this.generator = new HashFileGenerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String call() throws Exception {
|
||||||
|
log.info(LoggerMarker.DEBUG_MARKER, "开始执行哈希文件生成任务");
|
||||||
|
updateMessage("开始生成哈希文件...");
|
||||||
|
|
||||||
|
// 传递多个文件夹路径
|
||||||
|
List<Path> folderPaths = folders.stream().map(File::toPath).collect(ArrayList::new,
|
||||||
|
ArrayList::add,
|
||||||
|
ArrayList::addAll);
|
||||||
|
|
||||||
|
generator.setCallback((current, total) -> {
|
||||||
|
updateProgress(current, total);
|
||||||
|
updateMessage("处理文件: " + current + "/" + total);
|
||||||
|
if (current % 500 == 0 || current == total) {
|
||||||
|
log.info(LoggerMarker.DEBUG_MARKER, "处理进度: {}/{}", current, total);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 使用单独的线程执行生成任务,以便支持超时和取消
|
||||||
|
executor = ThreadPoolManager.createPool("hash-file-generate-pool", 1);
|
||||||
|
future = executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
generator.generateHashFile(folderPaths, finalOutputFile.toPath());
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 设置超时时间
|
||||||
|
long timeoutSeconds = System.getSetting().getTaskTimeout();
|
||||||
|
future.get(timeoutSeconds, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务完成,输出文件: {}", finalOutputFile.getAbsolutePath());
|
||||||
|
return "哈希列表文件已生成: " + finalOutputFile.getAbsolutePath();
|
||||||
|
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
String errorMsg = "哈希文件生成任务超时 (" + System.getSetting().getTaskTimeout() + "秒,请考虑在‘文件’-‘设置’里增加‘步骤任务超时时间’)";
|
||||||
|
updateMessage(errorMsg);
|
||||||
|
log.warn(LoggerMarker.DEBUG_MARKER, errorMsg);
|
||||||
|
throw new RuntimeException(errorMsg, e);
|
||||||
|
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
String errorMsg = "哈希文件生成失败: " + e.getCause().getMessage();
|
||||||
|
updateMessage(errorMsg);
|
||||||
|
log.error(LoggerMarker.DEBUG_MARKER, errorMsg, e);
|
||||||
|
throw new RuntimeException(errorMsg, e.getCause());
|
||||||
|
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
updateMessage("哈希文件生成任务被取消");
|
||||||
|
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务被取消");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw e;
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// 清理资源
|
||||||
|
if (executor != null) {
|
||||||
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cancelled() {
|
||||||
|
super.cancelled();
|
||||||
|
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务已取消");
|
||||||
|
updateMessage("正在取消哈希文件生成任务...");
|
||||||
|
|
||||||
|
// 先取消 Future
|
||||||
|
if (future != null && !future.isDone()) {
|
||||||
|
future.cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优雅关闭执行器
|
||||||
|
if (executor != null) {
|
||||||
|
try {
|
||||||
|
executor.shutdown(); // 先尝试正常关闭
|
||||||
|
if (!executor.awaitTermination(2, TimeUnit.SECONDS)) {
|
||||||
|
executor.shutdownNow(); // 强制关闭
|
||||||
|
executor.awaitTermination(1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
executor.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMessage("哈希文件生成任务已取消");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package top.r3944realms.docchecktoolrefactored.ui.utils;
|
package top.r3944realms.docchecktoolrefactored.ui.utils;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.ButtonBar;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -44,8 +44,8 @@ public class DialogUtil {
|
||||||
alert.setTitle(title);
|
alert.setTitle(title);
|
||||||
alert.setHeaderText(header);
|
alert.setHeaderText(header);
|
||||||
alert.setContentText(content);
|
alert.setContentText(content);
|
||||||
ButtonType yesButton = new ButtonType("Yes", ButtonBar.ButtonData.YES);
|
ButtonType yesButton = new ButtonType("是", ButtonBar.ButtonData.YES);
|
||||||
ButtonType noButton = new ButtonType("No", ButtonBar.ButtonData.NO);
|
ButtonType noButton = new ButtonType("否", ButtonBar.ButtonData.NO);
|
||||||
alert.getButtonTypes().setAll(yesButton, noButton);
|
alert.getButtonTypes().setAll(yesButton, noButton);
|
||||||
Optional<ButtonType> result = alert.showAndWait();
|
Optional<ButtonType> result = alert.showAndWait();
|
||||||
return result.isPresent() && result.get() == yesButton;
|
return result.isPresent() && result.get() == yesButton;
|
||||||
|
|
@ -60,10 +60,7 @@ public class DialogUtil {
|
||||||
*/
|
*/
|
||||||
public static void showInformationDialog(String title, String header, String content) {
|
public static void showInformationDialog(String title, String header, String content) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
Alert alert = createAlert(Alert.AlertType.INFORMATION, title, header, content);
|
||||||
alert.setTitle(title);
|
|
||||||
alert.setHeaderText(header);
|
|
||||||
alert.setContentText(content);
|
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -77,10 +74,7 @@ public class DialogUtil {
|
||||||
*/
|
*/
|
||||||
public static void showWarningDialog(String title, String header, String content) {
|
public static void showWarningDialog(String title, String header, String content) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
Alert alert = createAlert(Alert.AlertType.WARNING, title, header, content);
|
||||||
alert.setTitle(title);
|
|
||||||
alert.setHeaderText(header);
|
|
||||||
alert.setContentText(content);
|
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -93,11 +87,138 @@ public class DialogUtil {
|
||||||
* @param content the content
|
* @param content the content
|
||||||
*/
|
*/
|
||||||
public static void showErrorDialog(String title, String header, String content) {
|
public static void showErrorDialog(String title, String header, String content) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Alert alert = createAlert(Alert.AlertType.ERROR, title, header, content);
|
||||||
|
alert.showAndWait();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 创建支持长文本的对话框
|
||||||
|
*/
|
||||||
|
private static Alert createAlert(Alert.AlertType alertType, String title, String header, String content) {
|
||||||
|
Alert alert = new Alert(alertType);
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setHeaderText(header);
|
||||||
|
|
||||||
|
// 如果内容过长,使用 TextArea 来显示
|
||||||
|
if (content != null && content.length() > 100) {
|
||||||
|
// 创建可滚动的文本区域
|
||||||
|
TextArea textArea = new TextArea(content);
|
||||||
|
textArea.setEditable(false);
|
||||||
|
textArea.setWrapText(true);
|
||||||
|
textArea.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
textArea.setMaxHeight(Double.MAX_VALUE);
|
||||||
|
|
||||||
|
// 设置文本区域样式
|
||||||
|
textArea.setStyle("-fx-font-family: monospace; -fx-font-size: 12px;");
|
||||||
|
|
||||||
|
GridPane.setVgrow(textArea, Priority.ALWAYS);
|
||||||
|
GridPane.setHgrow(textArea, Priority.ALWAYS);
|
||||||
|
|
||||||
|
GridPane expContent = new GridPane();
|
||||||
|
expContent.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
expContent.add(new Label("详细信息:"), 0, 0);
|
||||||
|
expContent.add(textArea, 0, 1);
|
||||||
|
|
||||||
|
alert.getDialogPane().setContent(expContent);
|
||||||
|
|
||||||
|
// 设置对话框大小
|
||||||
|
alert.getDialogPane().setPrefSize(600, 400);
|
||||||
|
} else {
|
||||||
|
// 短文本直接显示
|
||||||
|
alert.setContentText(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return alert;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示带详细信息的错误对话框
|
||||||
|
*/
|
||||||
|
public static void showDetailedErrorDialog(String title, String summary, String details) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||||
alert.setTitle(title);
|
alert.setTitle(title);
|
||||||
alert.setHeaderText(header);
|
alert.setHeaderText(summary);
|
||||||
alert.setContentText(content);
|
|
||||||
|
// 创建可滚动的详细文本区域
|
||||||
|
TextArea textArea = new TextArea(details);
|
||||||
|
textArea.setEditable(false);
|
||||||
|
textArea.setWrapText(true);
|
||||||
|
textArea.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
textArea.setMaxHeight(Double.MAX_VALUE);
|
||||||
|
textArea.setStyle("-fx-font-family: monospace; -fx-font-size: 11px;");
|
||||||
|
|
||||||
|
GridPane.setVgrow(textArea, Priority.ALWAYS);
|
||||||
|
GridPane.setHgrow(textArea, Priority.ALWAYS);
|
||||||
|
|
||||||
|
GridPane expContent = new GridPane();
|
||||||
|
expContent.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
expContent.add(new Label("错误详情:"), 0, 0);
|
||||||
|
expContent.add(textArea, 0, 1);
|
||||||
|
|
||||||
|
alert.getDialogPane().setContent(expContent);
|
||||||
|
alert.getDialogPane().setPrefSize(700, 500);
|
||||||
|
alert.showAndWait();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示带详细信息的警告对话框
|
||||||
|
*/
|
||||||
|
public static void showDetailedWarningDialog(String title, String summary, String details) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setHeaderText(summary);
|
||||||
|
|
||||||
|
TextArea textArea = new TextArea(details);
|
||||||
|
textArea.setEditable(false);
|
||||||
|
textArea.setWrapText(true);
|
||||||
|
textArea.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
textArea.setMaxHeight(Double.MAX_VALUE);
|
||||||
|
textArea.setStyle("-fx-font-family: monospace; -fx-font-size: 11px;");
|
||||||
|
|
||||||
|
GridPane.setVgrow(textArea, Priority.ALWAYS);
|
||||||
|
GridPane.setHgrow(textArea, Priority.ALWAYS);
|
||||||
|
|
||||||
|
GridPane expContent = new GridPane();
|
||||||
|
expContent.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
expContent.add(new Label("警告详情:"), 0, 0);
|
||||||
|
expContent.add(textArea, 0, 1);
|
||||||
|
|
||||||
|
alert.getDialogPane().setContent(expContent);
|
||||||
|
alert.getDialogPane().setPrefSize(700, 500);
|
||||||
|
alert.showAndWait();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示带详细信息的信息对话框
|
||||||
|
*/
|
||||||
|
public static void showDetailedInformationDialog(String title, String summary, String details) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setHeaderText(summary);
|
||||||
|
|
||||||
|
TextArea textArea = new TextArea(details);
|
||||||
|
textArea.setEditable(false);
|
||||||
|
textArea.setWrapText(true);
|
||||||
|
textArea.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
textArea.setMaxHeight(Double.MAX_VALUE);
|
||||||
|
textArea.setStyle("-fx-font-family: monospace; -fx-font-size: 11px;");
|
||||||
|
|
||||||
|
GridPane.setVgrow(textArea, Priority.ALWAYS);
|
||||||
|
GridPane.setHgrow(textArea, Priority.ALWAYS);
|
||||||
|
|
||||||
|
GridPane expContent = new GridPane();
|
||||||
|
expContent.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
expContent.add(new Label("详细信息:"), 0, 0);
|
||||||
|
expContent.add(textArea, 0, 1);
|
||||||
|
|
||||||
|
alert.getDialogPane().setContent(expContent);
|
||||||
|
alert.getDialogPane().setPrefSize(700, 500);
|
||||||
alert.showAndWait();
|
alert.showAndWait();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
<?import javafx.scene.layout.BorderPane?>
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
<?import javafx.scene.layout.ColumnConstraints?>
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
<?import javafx.scene.layout.GridPane?>
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
|
@ -71,4 +72,12 @@
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
</GridPane>
|
</GridPane>
|
||||||
</center>
|
</center>
|
||||||
|
<bottom>
|
||||||
|
<HBox prefHeight="13.0" prefWidth="360.0" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<Label text="版本号:" />
|
||||||
|
<Label fx:id="versionL" text="<>" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</bottom>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="1000.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.MainStageController">
|
<VBox minHeight="-Infinity" minWidth="-Infinity" prefHeight="1000.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.MainStageController">
|
||||||
<children>
|
<children>
|
||||||
<MenuBar prefWidth="2558.0" VBox.vgrow="ALWAYS">
|
<MenuBar prefWidth="2558.0" VBox.vgrow="ALWAYS">
|
||||||
<menus>
|
<menus>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<GridPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.module.DuplicateDocumentPaneController">
|
<GridPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.module.DuplicateDocumentPaneController">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="288.0" minWidth="0.0" percentWidth="0.0" prefWidth="82.0" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="288.0" minWidth="0.0" percentWidth="0.0" prefWidth="82.0" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1263.9999633789064" minWidth="0.0" prefWidth="745.0" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1263.9999633789064" minWidth="0.0" prefWidth="745.0" />
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
<GridPane.margin>
|
<GridPane.margin>
|
||||||
<Insets left="10.0" />
|
<Insets left="10.0" />
|
||||||
</GridPane.margin></Label>
|
</GridPane.margin></Label>
|
||||||
<TextArea editable="false" maxWidth="1.7976931348623157E308" prefWidth="400.0" text="1.点击“选择文件夹”按钮,载入需要查找重复文件的数据(一般页面级文件和文件级文件分批载入检查)。 2.点击“开始检查”按钮,软件将对选定区域的数据批量计算文件哈希值,并对比查找重复文件,“结果反馈”区域将显示扫描文件数量、重复文件组和重复文件数量。 3.根据软件反馈的重复文件组,逐一核实确认是否为重复文件。 4.将确认后的检查结果填入查重登记表(附件1)。 ;" wrapText="true" GridPane.columnIndex="3" GridPane.columnSpan="2" GridPane.rowIndex="3">
|
<TextArea editable="false" maxWidth="1.7976931348623157E308" prefWidth="400.0" text="1.点击“选择文件夹”按钮,载入需要查找重复文件的数据(一般页面级文件和文件级文件分批载入检查)。 2.点击“开始检查”按钮,软件将对选定区域的数据批量计算文件哈希值,并对比查找重复文件,“结果反馈”区域将显示扫描文件数量、重复文件组和重复文件数量。 3.根据软件反馈的重复文件组,逐一核实确认是否为重复文件。 4.将确认后的检查结果填入查重登记表(附件1)。" 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>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<GridPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.module.PathCheckPaneController">
|
<GridPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.module.PathCheckPaneController">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="940.0" minWidth="0.0" percentWidth="0.0" prefWidth="104.0" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="940.0" minWidth="0.0" percentWidth="0.0" prefWidth="104.0" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="940.0" minWidth="10.0" percentWidth="0.0" prefWidth="104.0" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="940.0" minWidth="10.0" percentWidth="0.0" prefWidth="104.0" />
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<AnchorPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
<AnchorPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<children>
|
<children>
|
||||||
<TextArea editable="false" prefHeight="800.0" prefWidth="1000.0" scrollLeft="1.0" text="工作内容: 对照《元数据检查登记表》(附件4)检查并登记数字化项目信息、技术环境及技术参数的完整性等情况。" wrapText="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<TextArea editable="false" prefHeight="800.0" prefWidth="1000.0" scrollLeft="1.0" text="工作内容: 对照《元数据检查登记表》(附件4)检查并登记数字化项目信息、技术环境及技术参数的完整性等情况。" wrapText="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<font>
|
<font>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<AnchorPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
<AnchorPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<padding>
|
<padding>
|
||||||
<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" />
|
||||||
</padding>
|
</padding>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<AnchorPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
<AnchorPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<children>
|
<children>
|
||||||
<TextArea editable="false" prefHeight="800.0" prefWidth="1000.0" text="工作内容: 对照《工作记录检查登记表》(附件6)检查数字化工作台帐的规范性及与成果的一致性,并在表格中登记检查情况。" wrapText="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<TextArea editable="false" prefHeight="800.0" prefWidth="1000.0" text="工作内容: 对照《工作记录检查登记表》(附件6)检查数字化工作台帐的规范性及与成果的一致性,并在表格中登记检查情况。" wrapText="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<font>
|
<font>
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,13 @@
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<GridPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.module.StorageCarrierPaneController">
|
<GridPane prefHeight="800.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.module.StorageCarrierPaneController">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="226.33331298828125" minWidth="10.0" percentWidth="10.0" prefWidth="108.33333333333334" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="226.33331298828125" minWidth="10.0" percentWidth="10.0" prefWidth="108.33333333333334" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="559.3333511352539" minWidth="10.0" percentWidth="40.0" prefWidth="373.00002034505206" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="559.3333511352539" minWidth="10.0" percentWidth="40.0" prefWidth="373.00002034505206" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="345.3333435058594" minWidth="10.0" percentWidth="25.0" prefWidth="227.00004069010413" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="345.3333435058594" minWidth="10.0" percentWidth="25.0" prefWidth="227.00004069010413" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" maxWidth="500.0" minWidth="10.0" percentWidth="25.0" prefWidth="400.0" />
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="500.0" minWidth="10.0" percentWidth="25.0" prefWidth="400.0" />
|
||||||
|
<ColumnConstraints />
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
<rowConstraints>
|
<rowConstraints>
|
||||||
<RowConstraints maxHeight="151.33334350585938" percentHeight="7.0" prefHeight="55.00001017252603" vgrow="NEVER" />
|
<RowConstraints maxHeight="151.33334350585938" percentHeight="7.0" prefHeight="55.00001017252603" vgrow="NEVER" />
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
<VBox xmlns="http://javafx.com/javafx/23.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.SettingDialogController">
|
<VBox xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="top.r3944realms.docchecktoolrefactored.ui.SettingDialogController">
|
||||||
<children>
|
<children>
|
||||||
<GridPane>
|
<GridPane>
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration scan="true" scanPeriod="10 seconds">
|
<configuration scan="true" scanPeriod="10 seconds">
|
||||||
|
|
||||||
|
<!-- 属性定义保持不变 -->
|
||||||
<property name="APP_NAME" value="DocCheckTool"/>
|
<property name="APP_NAME" value="DocCheckTool"/>
|
||||||
<property name="LOG_HOME" value="${APP_NAME}/${log.dir:-logs}"/>
|
<property name="LOG_HOME" value="${APP_NAME}/${log.dir:-logs}"/>
|
||||||
|
|
||||||
<!-- 日志格式 -->
|
|
||||||
<property name="RELEASE_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%level] %msg%n"/>
|
<property name="RELEASE_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss} [%level] %msg%n"/>
|
||||||
<property name="DEBUG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n"/>
|
<property name="DEBUG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n"/>
|
||||||
<property name="TRACKER_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n"/>
|
<property name="TRACKER_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n"/>
|
||||||
|
|
@ -18,61 +17,63 @@
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<!-- RELEASE 文件日志 -->
|
<!-- RELEASE 文件日志 - 只接收 RELEASE marker -->
|
||||||
<appender name="RELEASE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="RELEASE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>${LOG_HOME}/release.log</file>
|
<file>${LOG_HOME}/release.log</file>
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<fileNamePattern>${LOG_HOME}/archive/release.%d{yyyy-MM-dd}.log</fileNamePattern>
|
<fileNamePattern>${LOG_HOME}/release.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
<maxHistory>7</maxHistory>
|
<maxHistory>7</maxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
|
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
|
||||||
|
<evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
|
||||||
|
<marker>RELEASE</marker>
|
||||||
|
</evaluator>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>${RELEASE_PATTERN}</pattern>
|
<pattern>${RELEASE_PATTERN}</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<!-- DEBUG 文件日志 -->
|
<!-- DEBUG 文件日志 - 只接收 DEBUG marker -->
|
||||||
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>${LOG_HOME}/debug/debug.log</file>
|
<file>${LOG_HOME}/debug/debug.log</file>
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<fileNamePattern>${LOG_HOME}/archive/debug.%d{yyyy-MM-dd}.log</fileNamePattern>
|
<fileNamePattern>${LOG_HOME}/debug/debug.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
<maxHistory>7</maxHistory>
|
<maxHistory>7</maxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
|
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
|
||||||
|
<evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
|
||||||
|
<marker>DEBUG</marker>
|
||||||
|
</evaluator>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>${DEBUG_PATTERN}</pattern>
|
<pattern>${DEBUG_PATTERN}</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<!-- TRACKER 文件日志(默认不启用) -->
|
<!-- TRACKER 文件日志 - 只接收 TRACE marker -->
|
||||||
<appender name="TRACKER_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="TRACKER_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<file>${LOG_HOME}/debug/tracker.log</file>
|
<file>${LOG_HOME}/debug/tracker.log</file>
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<fileNamePattern>${LOG_HOME}/archive/tracker.%d{yyyy-MM-dd}.log</fileNamePattern>
|
<fileNamePattern>${LOG_HOME}/debug/tracker.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||||
<maxHistory>7</maxHistory>
|
<maxHistory>7</maxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
|
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
|
||||||
|
<evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
|
||||||
|
<marker>TRACE</marker>
|
||||||
|
</evaluator>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>${TRACKER_PATTERN}</pattern>
|
<pattern>${TRACKER_PATTERN}</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<!-- 全局 Marker TurboFilter -->
|
|
||||||
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
|
|
||||||
<Marker>RELEASE</Marker>
|
|
||||||
<OnMatch>ACCEPT</OnMatch>
|
|
||||||
<OnMismatch>DENY</OnMismatch>
|
|
||||||
</turboFilter>
|
|
||||||
|
|
||||||
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
|
|
||||||
<Marker>DEBUG</Marker>
|
|
||||||
<OnMatch>ACCEPT</OnMatch>
|
|
||||||
<OnMismatch>NEUTRAL</OnMismatch>
|
|
||||||
</turboFilter>
|
|
||||||
|
|
||||||
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
|
|
||||||
<Marker>TRACKER</Marker>
|
|
||||||
<OnMatch>ACCEPT</OnMatch>
|
|
||||||
<OnMismatch>DENY</OnMismatch>
|
|
||||||
</turboFilter>
|
|
||||||
|
|
||||||
<!-- Logger 定义 -->
|
<!-- Logger 定义 -->
|
||||||
<logger name="log.release" level="INFO" additivity="false">
|
<logger name="log.release" level="INFO" additivity="false">
|
||||||
<appender-ref ref="RELEASE_FILE"/>
|
<appender-ref ref="RELEASE_FILE"/>
|
||||||
|
|
@ -81,21 +82,17 @@
|
||||||
|
|
||||||
<logger name="log.debug" level="DEBUG" additivity="false">
|
<logger name="log.debug" level="DEBUG" additivity="false">
|
||||||
<appender-ref ref="DEBUG_FILE"/>
|
<appender-ref ref="DEBUG_FILE"/>
|
||||||
<appender-ref ref="RELEASE_FILE"/>
|
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
<logger name="log.tracker" level="TRACE" additivity="false">
|
<logger name="log.tracker" level="TRACE" additivity="false">
|
||||||
<appender-ref ref="TRACKER_FILE"/>
|
<appender-ref ref="TRACKER_FILE"/>
|
||||||
<appender-ref ref="DEBUG_FILE"/>
|
|
||||||
<appender-ref ref="RELEASE_FILE"/>
|
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
<!-- ROOT Logger -->
|
<!-- ROOT Logger - 输出到控制台 -->
|
||||||
<root level="INFO">
|
<root level="INFO">
|
||||||
<appender-ref ref="RELEASE_FILE"/>
|
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</root>
|
</root>
|
||||||
|
|
||||||
</configuration>
|
</configuration>
|
||||||
Loading…
Reference in New Issue
Block a user