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',
|
||||
'--dest', "$buildDir/distributions",
|
||||
'--java-options', '-Dfile.encoding=UTF-8',
|
||||
'--java-options', '-Xmx512m',
|
||||
'--java-options', '-Xmx4G',
|
||||
'--java-options', '-Xms256m',
|
||||
'--verbose',
|
||||
'--icon', file('src/main/resources/img/logo256x.ico').absolutePath
|
||||
|
|
|
|||
|
|
@ -8,17 +8,24 @@ import top.r3944realms.docchecktoolrefactored.ui.SceneManager;
|
|||
import java.util.Objects;
|
||||
|
||||
public class JavaFxApplication extends Application {
|
||||
public Image logo = new Image(Objects.requireNonNull(getClass().getResourceAsStream("/img/icon.jpg")));
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
super.init();
|
||||
System.setVersion("1.0.0-beta");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
SceneManager.init(primaryStage);
|
||||
primaryStage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/img/icon.jpg"))));
|
||||
public void start(Stage primaryStage) {
|
||||
SceneManager.init(primaryStage, logo);
|
||||
SceneManager.switchLoginView();
|
||||
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 top.r3944realms.docchecktoolrefactored.cil.CliProcessor;
|
||||
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@ package top.r3944realms.docchecktoolrefactored;
|
|||
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.stage.FileChooser;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.r3944realms.docchecktoolrefactored.core.Setting;
|
||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||
|
||||
import java.io.*;
|
||||
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.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
|
|
@ -16,6 +19,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|||
public enum System {
|
||||
INSTANCE;
|
||||
private volatile Setting setting;
|
||||
@Getter
|
||||
private volatile String version;
|
||||
private volatile File lastModifiedFile;
|
||||
private static final String CONFIG_FILE_NAME = "config.ini";
|
||||
private static final Properties properties = new Properties();
|
||||
|
|
@ -106,8 +111,8 @@ public enum System {
|
|||
|
||||
/** 将Setting对象转换为Properties */
|
||||
private static void settingToProperties(Setting setting, Properties props) {
|
||||
props.setProperty("singleTimeout", String.valueOf(setting.getScanTimeout()));
|
||||
props.setProperty("totalTimeout", String.valueOf(setting.getTaskTimeout()));
|
||||
props.setProperty("scanTimeOutS", String.valueOf(setting.getScanTimeout()));
|
||||
props.setProperty("taskTimeOutS", String.valueOf(setting.getTaskTimeout()));
|
||||
props.setProperty("enableStep", String.valueOf(setting.isEnableStep()));
|
||||
}
|
||||
|
||||
|
|
@ -206,4 +211,10 @@ public enum System {
|
|||
public static Integer getAvailableProcessors() {
|
||||
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 org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.docchecktoolrefactored.System;
|
||||
import top.r3944realms.docchecktoolrefactored.ThreadPoolManager;
|
||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
|
@ -16,7 +17,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
|
@ -162,14 +163,16 @@ public class AddressFileComparator {
|
|||
}
|
||||
private final ExecutorService executor;
|
||||
public AddressFileComparator(int threadPoolSize) {
|
||||
this.executor = Executors.newFixedThreadPool(threadPoolSize);
|
||||
this.executor = ThreadPoolManager.createPool("address-comparator", threadPoolSize);
|
||||
}
|
||||
|
||||
public AddressFileComparator() {
|
||||
this.executor = Executors.newFixedThreadPool(System.getAvailableProcessors());
|
||||
this.executor = ThreadPoolManager.createPool("address-comparator", System.getAvailableProcessors());
|
||||
}
|
||||
@Setter
|
||||
private ProgressCallback progressCallback;
|
||||
|
||||
|
||||
// 安全调用回调方法
|
||||
private void safeOnPhaseStarted(Phase phase) {
|
||||
if (progressCallback != null) {
|
||||
|
|
@ -189,7 +192,25 @@ public class AddressFileComparator {
|
|||
}
|
||||
}
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
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.model.DuplicateGroup;
|
||||
import top.r3944realms.docchecktoolrefactored.model.FileMetadata;
|
||||
|
|
@ -13,7 +14,10 @@ import java.io.IOException;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
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.stream.Collectors;
|
||||
|
||||
|
|
@ -21,7 +25,6 @@ import java.util.stream.Collectors;
|
|||
/**
|
||||
* 重复文件查找核心类
|
||||
*/
|
||||
//TODO;代替DuplicateDocumentDetectionTask
|
||||
@Slf4j
|
||||
public class DuplicateFinder {
|
||||
private final FileScanner fileScanner;
|
||||
|
|
@ -49,7 +52,7 @@ public class DuplicateFinder {
|
|||
this.fileScanner = Objects.requireNonNull(fileScanner);
|
||||
this.hashCalculator = Objects.requireNonNull(hashCalculator);
|
||||
this.enableProgress = enableProgress;
|
||||
this.executorService = Executors.newFixedThreadPool(System.getAvailableProcessors());
|
||||
this.executorService = ThreadPoolManager.createPool("duplicate-finder-pool", System.getAvailableProcessors());
|
||||
}
|
||||
public DuplicateFinder(FileScanner fileScanner, FileHashCalculator hashCalculator) {
|
||||
this(fileScanner, hashCalculator, false);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package top.r3944realms.docchecktoolrefactored.core;
|
||||
|
||||
|
||||
import lombok.Setter;
|
||||
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.RobustParallelScanner;
|
||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||
|
|
@ -13,20 +15,49 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Setter
|
||||
@Slf4j
|
||||
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);
|
||||
}
|
||||
|
||||
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<>();
|
||||
|
||||
// 扫描所有目录中的文件
|
||||
for (Path directory : directories) {
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException("任务被取消");
|
||||
}
|
||||
|
||||
if (!Files.isDirectory(directory)) {
|
||||
throw new IllegalArgumentException("指定路径不是有效目录: " + directory);
|
||||
}
|
||||
|
|
@ -49,46 +80,115 @@ public class HashFileGenerator {
|
|||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否被中断
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException("任务被取消");
|
||||
}
|
||||
|
||||
// 计算每个文件的哈希值
|
||||
List<String[]> hashResults = new ArrayList<>();
|
||||
AtomicInteger processedFiles = new AtomicInteger(0);
|
||||
int totalFiles = allFiles.size();
|
||||
|
||||
allFiles.parallelStream().forEach(file -> {
|
||||
try {
|
||||
String hash = new MD5HashCalculator().calculatePartialHash(file);
|
||||
String[] result = {file.getFileName().toString(), hash};
|
||||
synchronized (hashResults) {
|
||||
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());
|
||||
}
|
||||
});
|
||||
processFilesInParallel(allFiles, hashResults, processedFiles, totalFiles);
|
||||
|
||||
// 检查是否被中断
|
||||
if (Thread.interrupted()) {
|
||||
throw new InterruptedException("任务被取消");
|
||||
}
|
||||
|
||||
// 写入结果到文件
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile.toFile()))) {
|
||||
writer.write("文件名,哈希值");
|
||||
writer.newLine();
|
||||
for (String[] result : hashResults) {
|
||||
writer.write(result[0] + "," + result[1]);
|
||||
writer.newLine();
|
||||
writeResultsToFile(outputFile, hashResults);
|
||||
}
|
||||
|
||||
private void processFilesInParallel(List<Path> files, List<String[]> results,
|
||||
AtomicInteger processedFiles, int totalFiles)
|
||||
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;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package top.r3944realms.docchecktoolrefactored.io.scanner;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* The interface File scanner.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package top.r3944realms.docchecktoolrefactored.ui;
|
|||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.input.KeyCode;
|
||||
|
|
@ -11,6 +12,7 @@ import javafx.scene.input.KeyCombination;
|
|||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.r3944realms.docchecktoolrefactored.System;
|
||||
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||
|
||||
|
|
@ -25,11 +27,12 @@ public class LoginStageController implements Initializable {
|
|||
/**
|
||||
* The Login button.
|
||||
*/
|
||||
public Button loginButton;
|
||||
@FXML private Button loginButton;
|
||||
/**
|
||||
* The Main pane.
|
||||
*/
|
||||
public BorderPane mainPane;
|
||||
@FXML private BorderPane mainPane;
|
||||
@FXML private Label versionL;
|
||||
@FXML private TextField usernameField;
|
||||
@FXML private PasswordField passwordField;
|
||||
|
||||
|
|
@ -43,7 +46,7 @@ public class LoginStageController implements Initializable {
|
|||
SceneManager.switchMainView();
|
||||
} else {
|
||||
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.KeyCombination;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.r3944realms.docchecktoolrefactored.System;
|
||||
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.List;
|
||||
|
|
@ -16,6 +19,7 @@ import java.util.List;
|
|||
/**
|
||||
* The type Main stage controller.
|
||||
*/
|
||||
@Slf4j
|
||||
public class MainStageController {
|
||||
|
||||
@FXML public Button nextB;
|
||||
|
|
@ -115,7 +119,34 @@ public class MainStageController {
|
|||
}
|
||||
|
||||
@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) {
|
||||
|
|
@ -123,9 +154,11 @@ public class MainStageController {
|
|||
}
|
||||
|
||||
@FXML void onOpenHelpDoc(ActionEvent actionEvent) {
|
||||
DialogUtil.showDetailedInformationDialog("未实现", "敬请期待","待完善文档后内置");
|
||||
}
|
||||
|
||||
@FXML void onAbout(ActionEvent actionEvent) {
|
||||
DialogUtil.showDetailedInformationDialog("版本", "版本信息","这里写些信息");
|
||||
}
|
||||
public void updateStepButtonsVisibility() {
|
||||
Setting setting = System.getSetting();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import javafx.fxml.FXMLLoader;
|
|||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Duration;
|
||||
|
|
@ -32,6 +33,9 @@ public class SceneManager {
|
|||
@Setter
|
||||
private static Stage primaryStage;
|
||||
@Getter
|
||||
@Setter
|
||||
public static Image logo;
|
||||
@Getter
|
||||
private static MainStageController mainController;
|
||||
@Getter
|
||||
private static final List<Stage> openStages = new ArrayList<>();
|
||||
|
|
@ -40,9 +44,12 @@ public class SceneManager {
|
|||
* Init.
|
||||
*
|
||||
* @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.logo = logo;
|
||||
primaryStage.getIcons().add(logo);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -87,6 +94,7 @@ public class SceneManager {
|
|||
try {
|
||||
Parent root = FXMLLoader.load(Objects.requireNonNull(Main.class.getResource("/fxml/setting-view.fxml")));
|
||||
Stage settingStage = new Stage();
|
||||
settingStage.getIcons().add(logo);
|
||||
settingStage.setTitle("数字化验收工具 - 设置");
|
||||
Scene scene = new Scene(root, 300, 206);
|
||||
settingStage.setScene(scene); // 默认大小可调
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import top.r3944realms.docchecktoolrefactored.System;
|
||||
import top.r3944realms.docchecktoolrefactored.ui.SceneManager;
|
||||
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.util.LoggerMarker;
|
||||
|
||||
|
|
@ -58,6 +59,7 @@ public class DuplicateDocumentPaneController {
|
|||
if (folderPath == null || folderPath.trim().isEmpty()) {
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "未选择文件夹,无法进行查重");
|
||||
result1TA.setText("请选择要检查的文件夹。");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "请选择要检查的文件夹");
|
||||
start1B.setDisable(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -88,6 +90,7 @@ public class DuplicateDocumentPaneController {
|
|||
// 绑定取消按钮 -> task.cancel()
|
||||
progressBar.setOnCancel(() -> {
|
||||
if (currentTask != null && currentTask.isRunning()) {
|
||||
cancel1B.setDisable(false);
|
||||
currentTask.cancel();
|
||||
}
|
||||
});
|
||||
|
|
@ -105,7 +108,10 @@ public class DuplicateDocumentPaneController {
|
|||
task.setOnFailed(e -> {
|
||||
progressBar.closeProgress();
|
||||
Throwable exception = task.getException();
|
||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||
result1TA.setText("检测过程中发生错误: " + exception.getMessage());
|
||||
DialogUtil.showDetailedErrorDialog("错误", "检测过程中发生错误", exception.getMessage());
|
||||
start1B.setDisable(false);
|
||||
cancel1B.setDisable(true);
|
||||
log.error(LoggerMarker.RELEASE_MARKER, "查重任务失败", exception);
|
||||
|
|
@ -114,11 +120,11 @@ public class DuplicateDocumentPaneController {
|
|||
// 处理任务取消情况
|
||||
task.setOnCancelled(e -> {
|
||||
progressBar.closeProgress();
|
||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||
result1TA.appendText("\n检测已取消");
|
||||
start1B.setDisable(false);
|
||||
cancel1B.setDisable(true);
|
||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||
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.task.AddressFileComparisonTask;
|
||||
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.util.LoggerMarker;
|
||||
|
||||
|
|
@ -75,9 +76,10 @@ public class PathCheckPaneController implements Initializable {
|
|||
loadCatalog2TF.setText(selectedFile.getAbsolutePath());
|
||||
System.setLastModifiedFile(selectedFile);
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "选择的目录文件路径为:{}", selectedFile.getAbsolutePath());
|
||||
}else{
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "用户未选择任何文件夹");
|
||||
result2TA.setText("未选择任何文件夹,请重新选择。");
|
||||
} else {
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "用户未选择任何文件");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "未选择任何文件夹,请重新选择");
|
||||
result2TA.setText("未选择载入目录文件,请重新选择。");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,6 +104,10 @@ public class PathCheckPaneController implements Initializable {
|
|||
if (selectedDirectory != null) {
|
||||
loadJPGFolder2TF.setText(selectedDirectory.getAbsolutePath());
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "选择的{}文件夹路径为:{}", selectedMode,selectedDirectory.getAbsolutePath());
|
||||
} else {
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "用户未选择任何文件夹");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "未选择任何文件夹,请重新选择");
|
||||
result2TA.setText("未选择载入文件夹,请重新选择。");
|
||||
}
|
||||
System.setLastModifiedFile(selectedDirectory);
|
||||
}
|
||||
|
|
@ -117,19 +123,23 @@ public class PathCheckPaneController implements Initializable {
|
|||
String filePath = loadCatalog2TF.getText();
|
||||
if (filePath.isEmpty()) {
|
||||
result2TA.setText("请先选择目录文件。");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "请先选择目录文件");
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "未选择目录文件");
|
||||
generateLogicalAddress2B.setDisable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Mode selectedMode = loadFolderType2CB.getValue();
|
||||
FileChooser fileChooser = System.getFileChooser();
|
||||
fileChooser.setTitle("选择保存逻辑地址文件的位置");
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||
fileChooser.setInitialFileName("逻辑地址文件.csv");
|
||||
fileChooser.setInitialFileName(selectedMode.toString() + "逻辑地址文件.csv");
|
||||
|
||||
File outputFile = fileChooser.showSaveDialog(generateLogicalAddress2B.getScene().getWindow());
|
||||
if (outputFile == null) {
|
||||
result2TA.setText("未选择保存位置");
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "用户未选择任何文件");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "未选择保存位置");
|
||||
generateLogicalAddress2B.setDisable(true);
|
||||
return;
|
||||
}
|
||||
|
|
@ -146,7 +156,7 @@ public class PathCheckPaneController implements Initializable {
|
|||
// 保存生成的文件路径
|
||||
logicalAddressFilePath = finalOutputFile.getAbsolutePath();
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "选择的输出文件路径: {}", logicalAddressFilePath);
|
||||
Mode selectedMode = loadFolderType2CB.getValue();
|
||||
|
||||
// 创建后台任务
|
||||
AddressFileGenerationTask task = new AddressFileGenerationTask(filePath, outputFile, selectedMode.number, true);
|
||||
// 绑定任务属性到UI
|
||||
|
|
@ -176,7 +186,10 @@ public class PathCheckPaneController implements Initializable {
|
|||
task.setOnFailed(e -> {
|
||||
progressBar.closeProgress();
|
||||
Throwable exception = task.getException();
|
||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||
result2TA.setText("检测过程中发生错误: " + exception.getMessage());
|
||||
DialogUtil.showDetailedErrorDialog("错误", "检测过程中发生错误: ", exception.getMessage());
|
||||
generateLogicalAddress2B.setDisable(false);
|
||||
log.error(LoggerMarker.RELEASE_MARKER, "生成逻辑路径 csv 文件任务失败", exception);
|
||||
});
|
||||
|
|
@ -184,10 +197,10 @@ public class PathCheckPaneController implements Initializable {
|
|||
// 处理任务取消情况
|
||||
task.setOnCancelled(e -> {
|
||||
progressBar.closeProgress();
|
||||
result2TA.appendText("\n检测已取消");
|
||||
generateLogicalAddress2B.setDisable(false);
|
||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||
result2TA.appendText("\n检测已取消");
|
||||
generateLogicalAddress2B.setDisable(false);
|
||||
log.info(LoggerMarker.RELEASE_MARKER, "生成逻辑路径 csv 文件任务已被取消");
|
||||
});
|
||||
Thread thread = new Thread(task);
|
||||
|
|
@ -205,25 +218,30 @@ public class PathCheckPaneController implements Initializable {
|
|||
String folderPath = loadJPGFolder2TF.getText();
|
||||
if (folderPath.isEmpty()) {
|
||||
result2TA.setText("请先选择文件夹。");
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "请先选择文件夹");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "请先选择文件夹");
|
||||
generatePhysicalAddress2B.setDisable(false);
|
||||
return;
|
||||
}
|
||||
File folder = new File(folderPath);
|
||||
if(!folder.exists() || !folder.isDirectory()) {
|
||||
result2TA.setText("所选路径不存在或不是一个有效的文件夹。");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "所选路径不存在或不是一个有效的文件夹");
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "所选路径不存在或不是一个有效的文件夹");
|
||||
generatePhysicalAddress2B.setDisable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Mode selectedMode = loadFolderType2CB.getValue();
|
||||
FileChooser fileChooser = System.getFileChooser();
|
||||
fileChooser.setTitle("选择保存物理地址文件的位置");
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("CSV Files", "*.csv"));
|
||||
fileChooser.setInitialFileName("物理地址文件.csv");
|
||||
fileChooser.setInitialFileName(selectedMode.toString() + "物理地址文件.csv");
|
||||
// 使用当前窗口作为父窗口显示文件选择对话框
|
||||
File outputFile = fileChooser.showSaveDialog(selectJPGFolder2B.getScene().getWindow());
|
||||
|
||||
if (outputFile == null) {
|
||||
result2TA.setText("未选择保存位置");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "未选择保存位置");
|
||||
generatePhysicalAddress2B.setDisable(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -240,8 +258,6 @@ public class PathCheckPaneController implements Initializable {
|
|||
// 保存生成的文件路径
|
||||
physicalAddressFilePath = finalOutputFile.getAbsolutePath();
|
||||
|
||||
//
|
||||
Mode selectedMode = loadFolderType2CB.getValue();
|
||||
// 创建后台任务
|
||||
AddressFileGenerationTask task = new AddressFileGenerationTask(folderPath, outputFile, selectedMode.number, false);
|
||||
// 保存到字段
|
||||
|
|
@ -273,7 +289,10 @@ public class PathCheckPaneController implements Initializable {
|
|||
task.setOnFailed(e -> {
|
||||
progressBar.closeProgress();
|
||||
Throwable exception = task.getException();
|
||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||
result2TA.setText("检测过程中发生错误: " + exception.getMessage());
|
||||
DialogUtil.showDetailedErrorDialog("错误", "检测过程中发生错误: ", exception.getMessage());
|
||||
generatePhysicalAddress2B.setDisable(false);
|
||||
log.error(LoggerMarker.RELEASE_MARKER, "生成物理路径 csv 文件任务失败", exception);
|
||||
});
|
||||
|
|
@ -281,10 +300,10 @@ public class PathCheckPaneController implements Initializable {
|
|||
// 处理任务取消情况
|
||||
task.setOnCancelled(e -> {
|
||||
progressBar.closeProgress();
|
||||
result2TA.appendText("\n检测已取消");
|
||||
generatePhysicalAddress2B.setDisable(false);
|
||||
currentTask.progressProperty().removeListener(progressChangeListener);
|
||||
currentTask.messageProperty().removeListener(messageChangeListener);
|
||||
result2TA.appendText("\n检测已取消");
|
||||
generatePhysicalAddress2B.setDisable(false);
|
||||
log.info(LoggerMarker.RELEASE_MARKER, "生成物理路径 csv 文件任务已被取消");
|
||||
});
|
||||
Thread thread = new Thread(task);
|
||||
|
|
@ -350,8 +369,9 @@ public class PathCheckPaneController implements Initializable {
|
|||
task.setOnFailed(event -> {
|
||||
cancelableProgressBar.closeProgress();
|
||||
Throwable exception = task.getException();
|
||||
result2TA.setText("文件比对失败: " + task.getException().getMessage());
|
||||
result2TA.setText("文件比对失败: " + exception.getMessage());
|
||||
start2B.setDisable(false);
|
||||
DialogUtil.showDetailedErrorDialog("错误", "文件比对失败:", exception.getMessage());
|
||||
log.error(LoggerMarker.RELEASE_MARKER, "查漏任务失败", exception);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package top.r3944realms.docchecktoolrefactored.ui.module;
|
|||
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Dialog;
|
||||
import javafx.scene.control.TextField;
|
||||
import top.r3944realms.docchecktoolrefactored.ui.utils.DialogUtil;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package top.r3944realms.docchecktoolrefactored.ui.module;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
|
|
@ -11,13 +12,14 @@ import javafx.stage.FileChooser;
|
|||
import javafx.stage.Stage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import top.r3944realms.docchecktoolrefactored.System;
|
||||
import top.r3944realms.docchecktoolrefactored.core.HashFileGenerator;
|
||||
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 java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
//TODO: 应该交给Platform:runLater;
|
||||
|
|
@ -48,9 +50,7 @@ public class StorageCarrierPaneController {
|
|||
@FXML
|
||||
private Button clearSelectedFoldersButton;
|
||||
|
||||
|
||||
|
||||
|
||||
private final top.r3944realms.docchecktoolrefactored.ui.utils.ProgressBar progressBar = new top.r3944realms.docchecktoolrefactored.ui.utils.ProgressBar(false);
|
||||
@FXML
|
||||
void onSelectLD(ActionEvent event) {
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "用户点击选择文件夹按钮");
|
||||
|
|
@ -117,11 +117,13 @@ public class StorageCarrierPaneController {
|
|||
|
||||
@FXML
|
||||
void onCaculateHash(ActionEvent event) {
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "开始计算RAR文件的MD5哈希值");
|
||||
generateHashFile7B.setDisable(true);
|
||||
String filePath = loadCompressedFile.getText();
|
||||
if (filePath == null || filePath.isEmpty()) {
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "未选择RAR文件,无法计算哈希值");
|
||||
result7TA.setText("请先选择一个 .rar 文件");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "请先选择一个 .rar 文件");
|
||||
generateHashFile7B.setDisable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -129,31 +131,38 @@ public class StorageCarrierPaneController {
|
|||
if (!file.exists() || !file.isFile() || !filePath.endsWith(".rar")) {
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "选择的文件无效或不是RAR文件: {}", filePath);
|
||||
result7TA.setText("所选文件不存在或不是一个有效的 .rar 文件");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "所选文件不存在或不是一个有效的 .rar 文件");
|
||||
generateHashFile7B.setDisable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "开始计算文件哈希值: {}", filePath);
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "开始计算RAR文件MD5哈希值: {}", filePath);
|
||||
MD5HashCalculator hashCalculator = new MD5HashCalculator();
|
||||
String hashResult = hashCalculator.calculateHash(file.toPath());
|
||||
result7TA.setText("计算结果:\n" + hashResult);
|
||||
generateHashFile7B.setDisable(false);
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "文件哈希值计算完成: {}", hashResult);
|
||||
} catch (IOException e) {
|
||||
log.error(LoggerMarker.DEBUG_MARKER, "计算文件哈希值时出错: {}", filePath, e);
|
||||
DialogUtil.showDetailedErrorDialog("错误", "生成哈希文件时出错:", e.getMessage());
|
||||
generateHashFile7B.setDisable(false);
|
||||
result7TA.setText("计算哈希值时出错: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
void onGenerateHF(ActionEvent event) {
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "开始生成哈希列表文件");
|
||||
caculateHash7B.setDisable(true);
|
||||
|
||||
String folderPathsText = loadDigitalOutcomes.getText();
|
||||
if (folderPathsText == null || folderPathsText.isEmpty()) {
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "未选择文件夹,无法生成哈希列表文件");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "请先选择一个文件夹");
|
||||
result7TA.setText("请先选择一个文件夹");
|
||||
caculateHash7B.setDisable(false);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "开始生成哈希列表文件");
|
||||
// 解析多个文件夹路径
|
||||
String[] folderPaths = folderPathsText.split(File.pathSeparator);
|
||||
List<File> folders = new ArrayList<>();
|
||||
|
|
@ -165,6 +174,8 @@ public class StorageCarrierPaneController {
|
|||
} else {
|
||||
log.warn(LoggerMarker.DEBUG_MARKER, "选择的路径无效或不是文件夹: {}", path);
|
||||
result7TA.setText("所选路径不存在或不是一个有效的文件夹: " + path);
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", ("所选路径不存在或不是一个有效的文件夹: " + path));
|
||||
caculateHash7B.setDisable(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -182,6 +193,7 @@ public class StorageCarrierPaneController {
|
|||
if (outputFile == null) {
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "用户取消了文件保存操作");
|
||||
result7TA.setText("未选择保存位置");
|
||||
DialogUtil.showWarningDialog("警告", "操作有误", "未选择保存位置");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -194,55 +206,51 @@ public class StorageCarrierPaneController {
|
|||
}
|
||||
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "选择的输出文件路径: {}", finalOutputFile.getAbsolutePath());
|
||||
|
||||
progressBar.showProgress(SceneManager.getPrimaryStage(), "生成哈希值列表文件", "正在初始化...");
|
||||
// 创建后台任务
|
||||
Task<String> task = new Task<>() {
|
||||
@Override
|
||||
protected String call() throws Exception {
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "开始执行哈希文件生成任务");
|
||||
updateMessage("开始生成哈希文件...");
|
||||
|
||||
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<String> task = new HashFileGenerationTask(finalOutputFile, folders);
|
||||
// 绑定任务属性到UI
|
||||
ChangeListener<Number> progressChangeListener = (obs, oldVal, newVal) -> {
|
||||
if (newVal != null) {
|
||||
if (task.getMessage() != null) {
|
||||
progressBar.updateProgress(newVal.doubleValue(), task.getMessage());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
task.progressProperty().addListener(progressChangeListener);
|
||||
// 绑定任务的消息到结果文本区域,实时显示进度
|
||||
task.messageProperty().addListener((observable, oldValue, newValue) -> {
|
||||
ChangeListener<String> messageChangeListener = (observable, oldValue, newValue) -> {
|
||||
result7TA.setText(newValue);
|
||||
});
|
||||
};
|
||||
task.messageProperty().addListener(messageChangeListener);
|
||||
|
||||
// 任务成功完成
|
||||
task.setOnSucceeded(e -> {
|
||||
progressBar.closeProgress();
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务成功完成");
|
||||
caculateHash7B.setDisable(false);
|
||||
result7TA.setText(task.getValue());
|
||||
});
|
||||
|
||||
// 任务失败处理
|
||||
task.setOnFailed(e -> {
|
||||
progressBar.closeProgress();
|
||||
Throwable exception = task.getException();
|
||||
task.progressProperty().removeListener(progressChangeListener);
|
||||
task.messageProperty().removeListener(messageChangeListener);
|
||||
caculateHash7B.setDisable(false);
|
||||
String errorMsg = "生成哈希文件时出错: " + (exception != null ? exception.getMessage() : "未知错误");
|
||||
DialogUtil.showDetailedErrorDialog("错误", "生成哈希文件时出错", errorMsg);
|
||||
log.error(LoggerMarker.RELEASE_MARKER, "哈希文件生成任务失败", exception);
|
||||
result7TA.setText(errorMsg);
|
||||
});
|
||||
|
||||
// 任务取消处理
|
||||
task.setOnCancelled(e -> {
|
||||
progressBar.closeProgress();
|
||||
task.progressProperty().removeListener(progressChangeListener);
|
||||
task.messageProperty().removeListener(messageChangeListener);
|
||||
caculateHash7B.setDisable(false);
|
||||
log.info(LoggerMarker.DEBUG_MARKER, "哈希文件生成任务被用户取消");
|
||||
result7TA.setText("哈希文件生成操作已取消");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class AddressFileComparisonTask extends Task<AddressFileComparator.Compar
|
|||
this.logicalFilePath = logicalFilePath;
|
||||
this.compareMode = compareMode;
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
comparator = new AddressFileComparator();
|
||||
this.comparator = new AddressFileComparator();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -75,7 +75,7 @@ public class AddressFileComparisonTask extends Task<AddressFileComparator.Compar
|
|||
.get(timeoutSeconds, TimeUnit.SECONDS);
|
||||
|
||||
} catch (TimeoutException e) {
|
||||
updateMessage("文件比对超时,请检查文件大小或电脑性能。");
|
||||
updateMessage("文件比对超时,请考虑在‘文件’-‘设置’里增加‘步骤任务超时时间’。");
|
||||
log.error("文件比对超时", e);
|
||||
throw e;
|
||||
} finally {
|
||||
|
|
@ -86,6 +86,9 @@ public class AddressFileComparisonTask extends Task<AddressFileComparator.Compar
|
|||
|
||||
@Override
|
||||
protected void cancelled() {
|
||||
super.cancelled();
|
||||
comparator.shutdown();
|
||||
updateMessage("文件比较任务已取消");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ 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.AddressFileGenerator;
|
||||
import top.r3944realms.docchecktoolrefactored.core.LogicalAddressFileGenerator;
|
||||
import top.r3944realms.docchecktoolrefactored.core.PhysicalAddressFileGenerator;
|
||||
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static java.util.concurrent.Executors.*;
|
||||
|
||||
@Slf4j
|
||||
public class AddressFileGenerationTask extends Task<String> {
|
||||
|
||||
|
|
@ -19,6 +19,9 @@ public class AddressFileGenerationTask extends Task<String> {
|
|||
private final File outputFile;
|
||||
private final int folderType;
|
||||
private final AddressFileGenerator generator;
|
||||
private ExecutorService executor;
|
||||
private Future<?> future;
|
||||
|
||||
|
||||
public AddressFileGenerationTask(String sourcePath,
|
||||
File outputFile,
|
||||
|
|
@ -31,60 +34,107 @@ public class AddressFileGenerationTask extends Task<String> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String call() {
|
||||
protected String call() throws Exception {
|
||||
updateMessage("初始化生成任务...");
|
||||
generator.setProgressCallback(new AddressFileGenerator.ProgressCallback() {
|
||||
@Override
|
||||
public void onPhaseStarted(AddressFileGenerator.Phase phase) {
|
||||
switch (phase) {
|
||||
case GENERATE_LOGICAL -> updateMessage("正在生成逻辑地址 CSV 文件 ...");
|
||||
case GENERATE_PHYSICAL -> updateMessage("正在生成物理地址 CSV 文件 ...");
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onPhaseStarted(AddressFileGenerator.Phase phase) {
|
||||
switch (phase) {
|
||||
case GENERATE_LOGICAL -> updateMessage("正在生成逻辑地址 CSV 文件 ...");
|
||||
case GENERATE_PHYSICAL -> updateMessage("正在生成物理地址 CSV 文件 ...");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhaseProgress(AddressFileGenerator.Phase phase, int current, int total) {
|
||||
if (total > 0) {
|
||||
updateProgress(current, total);
|
||||
switch (phase) {
|
||||
case GENERATE_LOGICAL -> updateMessage(String.format("在生成逻辑地址 CSV : %d/%d", current, total));
|
||||
case GENERATE_PHYSICAL -> updateMessage(String.format("在生成物理地址 CSV : %d/%d", current, total));
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onPhaseProgress(AddressFileGenerator.Phase phase, int current, int total) {
|
||||
if (total > 0) {
|
||||
updateProgress(current, total);
|
||||
switch (phase) {
|
||||
case GENERATE_LOGICAL -> updateMessage(String.format("在生成逻辑地址 CSV : %d/%d", current, total));
|
||||
case GENERATE_PHYSICAL -> updateMessage(String.format("在生成物理地址 CSV : %d/%d", current, total));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhaseCompleted(AddressFileGenerator.Phase phase) {
|
||||
switch (phase) {
|
||||
case GENERATE_LOGICAL -> updateMessage("已完成生成逻辑地址 CSV 文件任务");
|
||||
case GENERATE_PHYSICAL -> updateMessage("已完成生成物理地址 CSV 文件任务");
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
ExecutorService executor = newSingleThreadExecutor();
|
||||
Future<?> future = executor.submit(() -> {
|
||||
generator.generateAddressFile(sourcePath, outputFile, folderType);
|
||||
@Override
|
||||
public void onPhaseCompleted(AddressFileGenerator.Phase phase) {
|
||||
switch (phase) {
|
||||
case GENERATE_LOGICAL -> updateMessage("已完成生成逻辑地址 CSV 文件任务");
|
||||
case GENERATE_PHYSICAL -> updateMessage("已完成生成物理地址 CSV 文件任务");
|
||||
}
|
||||
}
|
||||
});
|
||||
executor = ThreadPoolManager.createPool("address-file-generate-pool", System.getAvailableProcessors());
|
||||
|
||||
// 提交任务到线程池
|
||||
future = executor.submit(() -> {
|
||||
try {
|
||||
generator.generateAddressFile(sourcePath, outputFile, folderType);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("地址文件生成失败", e);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// 等待执行完成或超时
|
||||
future.get(System.getSetting().getTaskTimeout(), TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
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();
|
||||
|
||||
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
|
||||
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.Paths;
|
||||
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.AtomicReference;
|
||||
|
||||
@Slf4j
|
||||
public class DuplicateDocumentDetectionTask extends Task<String>{
|
||||
private final String folderPath;
|
||||
private final MD5HashCalculator hashCalculator;
|
||||
private volatile RobustParallelScanner scanner;
|
||||
private final DuplicateFinder duplicateFinder;
|
||||
|
||||
public DuplicateDocumentDetectionTask(String 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)) {
|
||||
throw new IllegalArgumentException("指定路径不是有效目录: " + folderPath);
|
||||
}
|
||||
// 创建带进度更新的扫描器
|
||||
scanner = new RobustParallelScanner(10);
|
||||
|
||||
// 创建带有进度监听的 DuplicateFinder
|
||||
DuplicateFinder duplicateFinder = new DuplicateFinder(scanner, hashCalculator, true)
|
||||
.applySetting(System.getSetting());
|
||||
duplicateFinder.applySetting(System.getSetting());
|
||||
|
||||
// 用于统计文件总数
|
||||
AtomicInteger totalFiles = new AtomicInteger(0);
|
||||
|
|
@ -93,7 +93,7 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
|||
});
|
||||
|
||||
AtomicReference<List<DuplicateGroup>> resultRef = new AtomicReference<>();
|
||||
List<Exception> errors = new CopyOnWriteArrayList<>();
|
||||
AtomicReference<Throwable> errorRef = new AtomicReference<>();
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
// 在单独线程中执行查找
|
||||
|
|
@ -102,7 +102,7 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
|||
List<DuplicateGroup> duplicates = duplicateFinder.findDuplicates(rootPath);
|
||||
resultRef.set(duplicates);
|
||||
} catch (Exception e) {
|
||||
errors.add(e);
|
||||
errorRef.set(e);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
|
|
@ -111,42 +111,18 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
|||
findThread.setDaemon(true);
|
||||
findThread.start();
|
||||
|
||||
// 等待扫描完成,设置超时时间(例如5分钟)
|
||||
// 简单等待,Task取消时会自动中断
|
||||
long totalTimeout = System.getSetting().getTaskTimeout();
|
||||
if (!latch.await(totalTimeout, TimeUnit.SECONDS)) {
|
||||
duplicateFinder.shutdown();
|
||||
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 "操作被中断";
|
||||
throw new TimeoutException(String.format("任务超时(%d秒),请考虑在‘文件’-‘设置’里增加‘步骤任务超时时间’", totalTimeout));
|
||||
}
|
||||
|
||||
// 检查是否有错误
|
||||
if (!errors.isEmpty()) {
|
||||
throw new ScanningException(errors);
|
||||
if (errorRef.get() != null) {
|
||||
throw new RuntimeException(errorRef.get());
|
||||
} else if (!duplicateFinder.getErrors().isEmpty()) {
|
||||
throw new ScanningException(duplicateFinder.getErrors());
|
||||
}
|
||||
|
||||
List<DuplicateGroup> duplicateGroups = resultRef.get();
|
||||
|
|
@ -198,8 +174,8 @@ public class DuplicateDocumentDetectionTask extends Task<String>{
|
|||
@Override
|
||||
protected void 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;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.stage.Window;
|
||||
|
||||
import java.util.Optional;
|
||||
|
|
@ -44,8 +44,8 @@ public class DialogUtil {
|
|||
alert.setTitle(title);
|
||||
alert.setHeaderText(header);
|
||||
alert.setContentText(content);
|
||||
ButtonType yesButton = new ButtonType("Yes", ButtonBar.ButtonData.YES);
|
||||
ButtonType noButton = new ButtonType("No", ButtonBar.ButtonData.NO);
|
||||
ButtonType yesButton = new ButtonType("是", ButtonBar.ButtonData.YES);
|
||||
ButtonType noButton = new ButtonType("否", ButtonBar.ButtonData.NO);
|
||||
alert.getButtonTypes().setAll(yesButton, noButton);
|
||||
Optional<ButtonType> result = alert.showAndWait();
|
||||
return result.isPresent() && result.get() == yesButton;
|
||||
|
|
@ -60,10 +60,7 @@ public class DialogUtil {
|
|||
*/
|
||||
public static void showInformationDialog(String title, String header, String content) {
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(header);
|
||||
alert.setContentText(content);
|
||||
Alert alert = createAlert(Alert.AlertType.INFORMATION, title, header, content);
|
||||
alert.showAndWait();
|
||||
});
|
||||
}
|
||||
|
|
@ -77,10 +74,7 @@ public class DialogUtil {
|
|||
*/
|
||||
public static void showWarningDialog(String title, String header, String content) {
|
||||
Platform.runLater(() -> {
|
||||
Alert alert = new Alert(Alert.AlertType.WARNING);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(header);
|
||||
alert.setContentText(content);
|
||||
Alert alert = createAlert(Alert.AlertType.WARNING, title, header, content);
|
||||
alert.showAndWait();
|
||||
});
|
||||
}
|
||||
|
|
@ -93,11 +87,138 @@ public class DialogUtil {
|
|||
* @param content the 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(() -> {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(header);
|
||||
alert.setContentText(content);
|
||||
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 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();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
|
|
@ -71,4 +72,12 @@
|
|||
</rowConstraints>
|
||||
</GridPane>
|
||||
</center>
|
||||
<bottom>
|
||||
<HBox prefHeight="13.0" prefWidth="360.0" BorderPane.alignment="CENTER">
|
||||
<children>
|
||||
<Label text="版本号:" />
|
||||
<Label fx:id="versionL" text="<>" />
|
||||
</children>
|
||||
</HBox>
|
||||
</bottom>
|
||||
</BorderPane>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<?import javafx.scene.layout.VBox?>
|
||||
<?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>
|
||||
<MenuBar prefWidth="2558.0" VBox.vgrow="ALWAYS">
|
||||
<menus>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?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 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" />
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
<GridPane.margin>
|
||||
<Insets left="10.0" />
|
||||
</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>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?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 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" />
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?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>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?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>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?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>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@
|
|||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?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 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="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 />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints maxHeight="151.33334350585938" percentHeight="7.0" prefHeight="55.00001017252603" vgrow="NEVER" />
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<?import javafx.scene.layout.VBox?>
|
||||
<?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>
|
||||
<GridPane>
|
||||
<columnConstraints>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="10 seconds">
|
||||
|
||||
<!-- 属性定义保持不变 -->
|
||||
<property name="APP_NAME" value="DocCheckTool"/>
|
||||
<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="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"/>
|
||||
|
|
@ -18,61 +17,63 @@
|
|||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- RELEASE 文件日志 -->
|
||||
<!-- RELEASE 文件日志 - 只接收 RELEASE marker -->
|
||||
<appender name="RELEASE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/release.log</file>
|
||||
<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>
|
||||
</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>
|
||||
<pattern>${RELEASE_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- DEBUG 文件日志 -->
|
||||
<!-- DEBUG 文件日志 - 只接收 DEBUG marker -->
|
||||
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/debug/debug.log</file>
|
||||
<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>
|
||||
</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>
|
||||
<pattern>${DEBUG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- TRACKER 文件日志(默认不启用) -->
|
||||
<!-- TRACKER 文件日志 - 只接收 TRACE marker -->
|
||||
<appender name="TRACKER_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/debug/tracker.log</file>
|
||||
<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>
|
||||
</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>
|
||||
<pattern>${TRACKER_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</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 name="log.release" level="INFO" additivity="false">
|
||||
<appender-ref ref="RELEASE_FILE"/>
|
||||
|
|
@ -81,21 +82,17 @@
|
|||
|
||||
<logger name="log.debug" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="DEBUG_FILE"/>
|
||||
<appender-ref ref="RELEASE_FILE"/>
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</logger>
|
||||
|
||||
<logger name="log.tracker" level="TRACE" additivity="false">
|
||||
<appender-ref ref="TRACKER_FILE"/>
|
||||
<appender-ref ref="DEBUG_FILE"/>
|
||||
<appender-ref ref="RELEASE_FILE"/>
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</logger>
|
||||
|
||||
<!-- ROOT Logger -->
|
||||
<!-- ROOT Logger - 输出到控制台 -->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="RELEASE_FILE"/>
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
</configuration>
|
||||
Loading…
Reference in New Issue
Block a user