288 lines
10 KiB
Java
288 lines
10 KiB
Java
package com.example.workutils;
|
|
|
|
import com.example.workutils.system.config.*;
|
|
import com.example.workutils.system.plugin.Plugin;
|
|
import com.example.workutils.system.plugin.PluginDescriptor;
|
|
import com.example.workutils.system.plugin.PluginManager;
|
|
import com.example.workutils.system.plugin.VisualPluginFactory;
|
|
import com.example.workutils.system.support.JfxBootstrap;
|
|
import com.example.workutils.system.support.WinConsole;
|
|
import com.example.workutils.system.ui.MainWindow;
|
|
import com.example.workutils.system.ui.Theme;
|
|
import javafx.application.Platform;
|
|
import javafx.scene.Parent;
|
|
import javafx.scene.control.Alert;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.boot.CommandLineRunner;
|
|
import org.springframework.boot.SpringApplication;
|
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
|
import org.springframework.context.event.EventListener;
|
|
|
|
import java.util.*;
|
|
import java.util.concurrent.CountDownLatch;
|
|
|
|
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
|
@Slf4j
|
|
public class WorkUtilsApplication implements CommandLineRunner {
|
|
|
|
static {
|
|
// 消除 JavaFX classpath 运行时的 WARNING: Unsupported JavaFX configuration
|
|
java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger("");
|
|
rootLogger.setFilter(record ->
|
|
!record.getMessage().contains("Unsupported JavaFX configuration"));
|
|
}
|
|
|
|
private final PluginManager pluginManager;
|
|
private final WorkUtilsProperties properties;
|
|
private final SettingsService settingsService;
|
|
|
|
/** 主窗口引用,供内嵌插件面板使用 */
|
|
private MainWindow mainWindow;
|
|
/** 系统设置窗口缓存(内嵌模式复用) */
|
|
private SettingsWindow settingsWindow;
|
|
|
|
public WorkUtilsApplication(PluginManager pluginManager, WorkUtilsProperties properties,
|
|
SettingsService settingsService) {
|
|
this.pluginManager = pluginManager;
|
|
this.properties = properties;
|
|
this.settingsService = settingsService;
|
|
}
|
|
|
|
// ==================== 启动诊断 ====================
|
|
|
|
/**
|
|
* 应用就绪后验证所有 data/ 配置文件状态
|
|
*/
|
|
@EventListener(ApplicationReadyEvent.class)
|
|
public void onReady() {
|
|
String[] files = {
|
|
"project_paths.txt", "datasources.json",
|
|
"damaged_repos.cache", "mybatis-generator.properties", "workutils.config"
|
|
};
|
|
java.nio.file.Path dataDir = java.nio.file.Paths.get(
|
|
System.getProperty("user.dir"), "data");
|
|
StringBuilder sb = new StringBuilder("data 目录文件状态:\n");
|
|
for (String f : files) {
|
|
java.nio.file.Path p = dataDir.resolve(f);
|
|
try {
|
|
if (java.nio.file.Files.exists(p)) {
|
|
long size = java.nio.file.Files.size(p);
|
|
sb.append(String.format(" %-35s %s%n", f,
|
|
size > 0 ? size + " bytes ✓" : "0 bytes ✗ 空文件!"));
|
|
} else {
|
|
sb.append(String.format(" %-35s 不存在 ✗%n", f));
|
|
}
|
|
} catch (Exception e) {
|
|
sb.append(String.format(" %-35s 读取失败: %s%n", f, e.getMessage()));
|
|
}
|
|
}
|
|
log.info(sb.toString());
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
WinConsole.ensureUtf8();
|
|
SpringApplication.run(WorkUtilsApplication.class, args);
|
|
}
|
|
|
|
@Override
|
|
public void run(String... args) throws Exception {
|
|
if (args.length == 0) {
|
|
showMainWindow();
|
|
} else if ("--console".equals(args[0])) {
|
|
showInteractiveMenu();
|
|
} else {
|
|
processCommand(args);
|
|
}
|
|
}
|
|
|
|
// ==================== 主窗口入口 ====================
|
|
|
|
private void showMainWindow() throws InterruptedException {
|
|
JfxBootstrap.await();
|
|
if (JfxBootstrap.isFailed()) {
|
|
System.err.println("JavaFX 初始化失败,回退到控制台模式");
|
|
showInteractiveMenu();
|
|
return;
|
|
}
|
|
|
|
// 始终使用亮色主题
|
|
Theme.Mode mode = Theme.Mode.LIGHT;
|
|
Platform.runLater(() -> Theme.applyTheme(mode));
|
|
|
|
List<MainWindow.NavItem> navItems = buildNavItems();
|
|
|
|
CountDownLatch windowClosed = new CountDownLatch(1);
|
|
Platform.runLater(() -> {
|
|
try {
|
|
mainWindow = new MainWindow(navItems, properties);
|
|
mainWindow.showAndWait();
|
|
} catch (Exception e) {
|
|
log.error("主窗口创建失败", e);
|
|
System.err.println("主窗口创建失败: " + e.getMessage());
|
|
} finally {
|
|
windowClosed.countDown();
|
|
}
|
|
});
|
|
|
|
windowClosed.await();
|
|
log.info("主窗口已关闭");
|
|
}
|
|
|
|
// ==================== 导航项构建 ====================
|
|
|
|
private List<MainWindow.NavItem> buildNavItems() {
|
|
List<MainWindow.NavItem> items = new ArrayList<>();
|
|
|
|
for (Plugin plugin : pluginManager.getEnabledPlugins()) {
|
|
PluginDescriptor desc = plugin.getDescriptor();
|
|
VisualPluginFactory factory = plugin.getVisualFactory();
|
|
String pluginId = desc.getId();
|
|
|
|
items.add(new MainWindow.NavItem(
|
|
desc.getIcon(),
|
|
desc.getName(),
|
|
desc.getDescription(),
|
|
factory != null ? () -> openPluginEmbedded(pluginId, plugin, factory) : null,
|
|
pluginId
|
|
));
|
|
}
|
|
|
|
// 系统设置 — 内嵌为 Tab
|
|
items.add(new MainWindow.NavItem(
|
|
"⚙️", "系统设置",
|
|
"配置系统路径、数据库连接、项目路径、多数据源、字体大小等",
|
|
this::openSettingsEmbedded,
|
|
"__settings__"
|
|
));
|
|
|
|
return items;
|
|
}
|
|
|
|
// ==================== 内嵌优先打开 ====================
|
|
|
|
/**
|
|
* 优先尝试内嵌模式;若插件不支持嵌入,则退回到独立窗口。
|
|
* <p>
|
|
* 在 JFX 线程上调用(来自导航按钮事件)。
|
|
*/
|
|
private void openPluginEmbedded(String pluginId, Plugin plugin, VisualPluginFactory factory) {
|
|
// 配置检查
|
|
String configError = plugin.checkConfig();
|
|
if (configError != null) {
|
|
showConfigAlert(plugin.getName(), configError);
|
|
return;
|
|
}
|
|
|
|
// 尝试获取内嵌面板
|
|
Parent embeddedPane = factory.getEmbeddedPane();
|
|
if (embeddedPane != null && mainWindow != null) {
|
|
PluginDescriptor desc = plugin.getDescriptor();
|
|
mainWindow.embedPlugin(pluginId, desc.getIcon(), desc.getName(), embeddedPane);
|
|
return;
|
|
}
|
|
|
|
// 回退:独立窗口模式
|
|
openPluginStandalone(plugin, factory);
|
|
}
|
|
|
|
// ==================== 独立窗口模式(弹出或回退) ====================
|
|
|
|
/**
|
|
* 以独立窗口形式打开插件。用于:
|
|
* <ul>
|
|
* <li>插件不支持内嵌时的回退</li>
|
|
* <li>用户点击「弹出窗口」按钮</li>
|
|
* </ul>
|
|
*/
|
|
private void openPluginStandalone(Plugin plugin, VisualPluginFactory factory) {
|
|
new Thread(() -> {
|
|
try {
|
|
JfxBootstrap.await();
|
|
} catch (InterruptedException ie) {
|
|
Thread.currentThread().interrupt();
|
|
return;
|
|
}
|
|
|
|
CountDownLatch windowClosed = new CountDownLatch(1);
|
|
Platform.runLater(() -> {
|
|
try {
|
|
factory.open(windowClosed::countDown);
|
|
} catch (Exception e) {
|
|
log.error("独立窗口创建失败: {}", plugin.getName(), e);
|
|
windowClosed.countDown();
|
|
}
|
|
});
|
|
try {
|
|
windowClosed.await();
|
|
} catch (InterruptedException e) {
|
|
Thread.currentThread().interrupt();
|
|
}
|
|
}, plugin.getId() + "-Window").start();
|
|
}
|
|
|
|
// ==================== 配置提示 ====================
|
|
|
|
private void showConfigAlert(String pluginName, String message) {
|
|
Alert alert = new Alert(Alert.AlertType.WARNING);
|
|
alert.setTitle("配置检查");
|
|
alert.setHeaderText(pluginName + " 配置不完整");
|
|
alert.setContentText(message);
|
|
alert.showAndWait();
|
|
}
|
|
|
|
// ==================== 系统设置 ====================
|
|
|
|
private void openSettingsEmbedded() {
|
|
if (settingsWindow == null) {
|
|
settingsWindow = settingsService.createSettingsWindow(null);
|
|
}
|
|
if (mainWindow != null) {
|
|
Parent pane = settingsWindow.getContentPane();
|
|
mainWindow.embedPlugin("__settings__", "⚙️", "系统设置", pane);
|
|
}
|
|
}
|
|
|
|
private void openSettingsStandalone() {
|
|
if (settingsWindow == null) {
|
|
settingsWindow = settingsService.createSettingsWindow(null);
|
|
}
|
|
javafx.stage.Stage s = settingsWindow.getStage();
|
|
if (s != null) {
|
|
s.show();
|
|
}
|
|
}
|
|
|
|
/** @deprecated 保留兼容旧路径 */
|
|
private void openSettingsWindow() {
|
|
new Thread(() -> settingsService.showSettings(null, null),
|
|
"SettingsWindow-Thread").start();
|
|
}
|
|
|
|
// ==================== 控制台模式(降级) ====================
|
|
|
|
private void showInteractiveMenu() {
|
|
Scanner scanner = new Scanner(System.in);
|
|
pluginManager.runMainMenu(scanner);
|
|
}
|
|
|
|
private void processCommand(String... args) throws Exception {
|
|
String command = args[0].toLowerCase();
|
|
Optional<Plugin> toolOpt = pluginManager.getPluginByCommand(command);
|
|
if (toolOpt.isPresent()) {
|
|
Plugin plugin = toolOpt.get();
|
|
try {
|
|
String[] toolArgs = args.length > 1
|
|
? Arrays.copyOfRange(args, 1, args.length) : new String[0];
|
|
plugin.executeCommand(toolArgs);
|
|
} catch (Exception e) {
|
|
log.error("命令执行失败: {} [{}]", plugin.getName(), plugin.getId(), e);
|
|
System.err.println("命令执行失败: " + e.getMessage());
|
|
}
|
|
} else {
|
|
System.err.println("未知命令: " + command);
|
|
}
|
|
}
|
|
}
|