兼容性优化
This commit is contained in:
@@ -269,7 +269,6 @@
|
||||
<directory>${basedir}/scripts</directory>
|
||||
<includes>
|
||||
<include>start.bat</include>
|
||||
<include>start-jar.bat</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
+96
-45
@@ -1,6 +1,6 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: ============================================
|
||||
:: WorkUtils 生产打包脚本(免安装版)
|
||||
@@ -8,7 +8,7 @@ setlocal
|
||||
:: (含 exe + 内嵌JRE + jar + 启动脚本)
|
||||
:: ============================================
|
||||
|
||||
::: 切换到项目根目录
|
||||
:: 切换到项目根目录
|
||||
cd /d "%~dp0.."
|
||||
set "PROJECT_DIR=%CD%"
|
||||
set "TARGET=%PROJECT_DIR%\target"
|
||||
@@ -20,26 +20,55 @@ echo ========== WorkUtils 生产构建 ==========
|
||||
echo 项目目录: %PROJECT_DIR%
|
||||
echo.
|
||||
|
||||
::: ============================================================
|
||||
::: 步骤 1:清理残留进程
|
||||
::: ============================================================
|
||||
echo [1/5] Clean stale processes...
|
||||
:: ============================================================
|
||||
:: 步骤 1:清理残留进程 & 解锁删除旧产物
|
||||
:: ============================================================
|
||||
echo [1/5] Clean stale processes and files...
|
||||
taskkill /f /im java.exe 2>nul
|
||||
taskkill /f /im workutils.exe 2>nul
|
||||
:: 等进程完全退出,文件锁释放
|
||||
echo 等待进程退出...
|
||||
ping 127.0.0.1 -n 4 >nul
|
||||
|
||||
::: ============================================================
|
||||
::: 步骤 2:Maven 打包
|
||||
::: ============================================================
|
||||
echo.
|
||||
echo [2/5] Maven build...
|
||||
:: --- 清理 target/dist ---
|
||||
if exist "%TARGET%" (
|
||||
echo Cleaning old target...
|
||||
:: 策略1:直接 rd(文件锁已释放时应成功)
|
||||
rd /s /q "%TARGET%" 2>nul
|
||||
:: 策略2:如果 rd 失败,改名后重建
|
||||
if exist "%TARGET%" (
|
||||
echo [WARN] Cannot delete target, retrying with Maven clean...
|
||||
call "%PROJECT_DIR%\mvnw.cmd" clean -q 2>nul
|
||||
echo [WARN] 直接删除失败,尝试改名清理...
|
||||
:: 先删 dist/ 子目录的文件(规避 JRE 文件锁)
|
||||
if exist "%DIST%" (
|
||||
del /f /s /q "%DIST%\*" 2>nul
|
||||
rd /s /q "%DIST%" 2>nul
|
||||
)
|
||||
:: 再删 jre/
|
||||
if exist "%TARGET%\jre" (
|
||||
del /f /s /q "%TARGET%\jre\*" 2>nul
|
||||
rd /s /q "%TARGET%\jre" 2>nul
|
||||
)
|
||||
:: 删 target/ 下其余文件
|
||||
del /f /s /q "%TARGET%\*" 2>nul
|
||||
for /d %%d in ("%TARGET%\*") do rd /s /q "%%d" 2>nul
|
||||
rd /s /q "%TARGET%" 2>nul
|
||||
)
|
||||
:: 策略3:改名旧目录,让后续步骤创建新的
|
||||
if exist "%TARGET%" (
|
||||
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /value') do set "TS=%%I"
|
||||
set "OLD_TARGET=%PROJECT_DIR%\target_old_!TS:~0,14!"
|
||||
echo [WARN] 无法删除,重命名为 !OLD_TARGET! ...
|
||||
move "%TARGET%" "!OLD_TARGET!" >nul 2>&1
|
||||
if exist "!OLD_TARGET!" (
|
||||
echo [INFO] 旧产物已移至 !OLD_TARGET!,稍后请手动删除
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
:: ============================================================
|
||||
:: 步骤 2:Maven 打包
|
||||
:: ============================================================
|
||||
echo.
|
||||
echo [2/5] Maven build...
|
||||
call "%PROJECT_DIR%\mvnw.cmd" package -DskipTests -q
|
||||
if %errorlevel% neq 0 (
|
||||
echo [ERROR] Maven build failed!
|
||||
@@ -47,22 +76,22 @@ if %errorlevel% neq 0 (
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
::: ============================================================
|
||||
::: 步骤 3:提取版本号(PowerShell 解析 pom.xml)
|
||||
::: ============================================================
|
||||
:: ============================================================
|
||||
:: 步骤 3:提取版本号
|
||||
:: ============================================================
|
||||
echo.
|
||||
echo [3/5] Read version from pom.xml...
|
||||
for /f "usebackq delims=" %%a in (`powershell -NoProfile -NonInteractive -Command "([xml](Get-Content -Encoding UTF8 '%PROJECT_DIR%\pom.xml')).project.version"`) do set "VERSION=%%a"
|
||||
if "%VERSION%"=="" set "VERSION=0.0.1-SNAPSHOT"
|
||||
echo 版本: %VERSION%
|
||||
|
||||
::: ============================================================
|
||||
::: 步骤 4:生成精简 JRE(jlink 内嵌 Java 17 + JavaFX)
|
||||
::: ============================================================
|
||||
:: ============================================================
|
||||
:: 步骤 4:生成精简 JRE(jlink 内嵌 Java 17 + JavaFX)
|
||||
:: ============================================================
|
||||
echo.
|
||||
echo [4/5] Generate bundled JRE...
|
||||
|
||||
::: 4.1 查找 JDK 17(环境变量 JDK17_HOME > 默认路径)
|
||||
:: 4.1 查找 JDK 17
|
||||
if not defined JDK17_HOME (
|
||||
for %%d in (
|
||||
"C:\Program Files\Java\jdk-17"
|
||||
@@ -81,7 +110,7 @@ if not defined JDK17_HOME (
|
||||
)
|
||||
:jdk17_found
|
||||
|
||||
::: 4.2 查找 JavaFX SDK(环境变量 JFX_SDK_HOME > 默认路径)
|
||||
:: 4.2 查找 JavaFX SDK
|
||||
if not defined JFX_SDK_HOME (
|
||||
for %%d in (
|
||||
"C:\Program Files\Java\javafx-sdk-17.0.15"
|
||||
@@ -102,10 +131,10 @@ if not defined JFX_SDK_HOME (
|
||||
echo JDK : %JDK17_HOME%
|
||||
echo JavaFX: %JFX_SDK_HOME%
|
||||
|
||||
::: 4.3 清理旧 JRE
|
||||
if exist "%TARGET%\jre" rd /s /q "%TARGET%\jre"
|
||||
:: 4.3 清理旧 JRE
|
||||
if exist "%TARGET%\jre" rd /s /q "%TARGET%\jre" 2>nul
|
||||
|
||||
::: 4.4 执行 jlink
|
||||
:: 4.4 执行 jlink
|
||||
echo Running jlink, please wait...
|
||||
"%JDK17_HOME%\bin\jlink.exe" ^
|
||||
--module-path "%JFX_SDK_HOME%\lib;%JDK17_HOME%\jmods" ^
|
||||
@@ -119,7 +148,7 @@ if %errorlevel% neq 0 (
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
::: 4.5 补充 JavaFX 原生 DLL(jlink 不会自动从 SDK bin/ 复制)
|
||||
:: 4.5 补充 JavaFX 原生 DLL
|
||||
echo Copy JavaFX native DLLs...
|
||||
if exist "%JFX_SDK_HOME%\bin\glass.dll" (
|
||||
xcopy /y /q "%JFX_SDK_HOME%\bin\*.dll" "%TARGET%\jre\bin\" >nul
|
||||
@@ -130,17 +159,21 @@ if exist "%JFX_SDK_HOME%\bin\glass.dll" (
|
||||
|
||||
echo [OK] JRE generated : %TARGET%\jre
|
||||
|
||||
::: ============================================================
|
||||
::: 步骤 5:组装发布目录 & 打包 ZIP
|
||||
::: ============================================================
|
||||
:: ============================================================
|
||||
:: 步骤 5:组装发布目录 & 打包 ZIP
|
||||
:: ============================================================
|
||||
echo.
|
||||
echo [5/5] Assemble distribution...
|
||||
|
||||
::: 清理旧发布目录
|
||||
if exist "%DIST%" rd /s /q "%DIST%"
|
||||
:: 清理旧发布目录
|
||||
if exist "%DIST%" (
|
||||
del /f /s /q "%DIST%\*" 2>nul
|
||||
for /d %%d in ("%DIST%\*") do rd /s /q "%%d" 2>nul
|
||||
rd /s /q "%DIST%" 2>nul
|
||||
)
|
||||
mkdir "%DIST%\workutils" 2>nul
|
||||
|
||||
::: 复制 EXE
|
||||
:: 复制 EXE
|
||||
if exist "%TARGET%\workutils.exe" (
|
||||
copy /y "%TARGET%\workutils.exe" "%DIST%\workutils\" >nul
|
||||
echo [OK] workutils.exe
|
||||
@@ -148,44 +181,61 @@ if exist "%TARGET%\workutils.exe" (
|
||||
echo [WARN] workutils.exe not found!
|
||||
)
|
||||
|
||||
::: 复制 JAR
|
||||
:: 复制 JAR
|
||||
for %%j in ("%TARGET%\workutils-*.jar") do (
|
||||
copy /y "%%j" "%DIST%\workutils\" >nul
|
||||
echo [OK] %%~nxj
|
||||
)
|
||||
|
||||
::: 复制内嵌 JRE
|
||||
:: 复制内嵌 JRE
|
||||
if exist "%TARGET%\jre\bin\java.exe" (
|
||||
echo 复制内嵌 JRE...
|
||||
xcopy /e /i /q "%TARGET%\jre" "%DIST%\workutils\jre\" >nul
|
||||
echo [OK] jre/ ^(bundled Java 17 + JavaFX^)
|
||||
) else (
|
||||
echo [WARN] jre not found in target!
|
||||
)
|
||||
|
||||
::: ============================================================
|
||||
::: 生成 start.bat(备选启动方式)
|
||||
::: ============================================================
|
||||
:: ============================================================
|
||||
:: 生成 start.bat
|
||||
:: ============================================================
|
||||
(
|
||||
echo @echo off
|
||||
echo chcp 65001 ^>nul
|
||||
echo title WorkUtils
|
||||
echo java -jar "%%~dp0workutils-%VERSION%.jar" %%*
|
||||
echo pause
|
||||
echo.
|
||||
echo :: 优先用内嵌 JRE,否则查系统 Java 17+
|
||||
echo set "JAVA_BIN=%%~dp0jre\bin\java.exe"
|
||||
echo if exist "%%JAVA_BIN%%" goto :run
|
||||
echo.
|
||||
echo set "JAVA_BIN=java"
|
||||
echo for /f "tokens=3" %%%%v in ^('"%%JAVA_BIN%%" -version 2^>^&1 ^| findstr /i "version"'^) do set "JAVA_VER=%%%%~v"
|
||||
echo for /f "tokens=1 delims=." %%%%m in ^("%%JAVA_VER%%"^) do set "MAJOR=%%%%m"
|
||||
echo if "%%MAJOR%%"=="1" for /f "tokens=2 delims=." %%%%m in ^("%%JAVA_VER%%"^) do set "MAJOR=%%%%m"
|
||||
echo if %%MAJOR%% LSS 17 ^(
|
||||
echo echo [ERROR] Java %%JAVA_VER%% found. WorkUtils requires Java 17+.
|
||||
echo echo Download JDK 17+ from https://adoptium.net/
|
||||
echo pause ^& exit /b 1
|
||||
echo ^)
|
||||
echo.
|
||||
echo :run
|
||||
echo "%%JAVA_BIN%%" -jar "%%~dp0workutils-%VERSION%.jar" %%*
|
||||
echo if %%errorlevel%% neq 0 pause
|
||||
) > "%DIST%\workutils\start.bat"
|
||||
echo [OK] start.bat
|
||||
|
||||
::: ============================================================
|
||||
::: 打包 ZIP
|
||||
::: ============================================================
|
||||
:: ============================================================
|
||||
:: 打包 ZIP
|
||||
:: ============================================================
|
||||
set "ZIPNAME=workutils-v%VERSION%"
|
||||
echo 正在压缩 %ZIPNAME%.zip ...
|
||||
powershell -Command ^
|
||||
"Compress-Archive -Path '%DIST%\workutils' -DestinationPath '%DIST%\%ZIPNAME%.zip' -Force"
|
||||
echo [OK] %ZIPNAME%.zip
|
||||
|
||||
::: ============================================================
|
||||
::: 完成
|
||||
::: ============================================================
|
||||
:: ============================================================
|
||||
:: 完成
|
||||
:: ============================================================
|
||||
echo.
|
||||
echo ============================================
|
||||
echo Build Complete ^! v%VERSION%
|
||||
@@ -196,3 +246,4 @@ echo %DIST%\%ZIPNAME%.zip
|
||||
echo ============================================
|
||||
echo.
|
||||
pause
|
||||
exit /b 0
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
title WorkUtils
|
||||
|
||||
java -Dfile.encoding=UTF-8 -jar "%~dp0target\workutils-0.0.2-SNAPSHOT.jar" %*
|
||||
|
||||
pause
|
||||
+52
-6
@@ -1,10 +1,56 @@
|
||||
@echo off
|
||||
chcp 65001 > nul
|
||||
title workutils
|
||||
chcp 65001 >nul
|
||||
title WorkUtils
|
||||
|
||||
:: 切到脚本所在目录的父目录(即项目根目录)
|
||||
:: 当脚本在 scripts/ 时 → 项目根;在 target/ 时 → 项目根
|
||||
cd /d "%~dp0.."
|
||||
|
||||
:: 从 pom.xml 读取版本号
|
||||
set "VERSION="
|
||||
for /f "usebackq delims=" %%a in (`powershell -NoProfile -NonInteractive -Command "([xml](Get-Content -Encoding UTF8 '%CD%\pom.xml')).project.version"`) do set "VERSION=%%a"
|
||||
if "%VERSION%"=="" set "VERSION=0.0.1-SNAPSHOT"
|
||||
|
||||
:: 优先匹配版本号精确的 JAR,找不到回退通配符
|
||||
set "JAR="
|
||||
if exist "target\workutils-%VERSION%.jar" set "JAR=target\workutils-%VERSION%.jar"
|
||||
if "%JAR%"=="" for %%j in ("target\workutils-*.jar") do set "JAR=%%j"
|
||||
if "%JAR%"=="" (
|
||||
echo [ERROR] JAR not found in target\ Run build_release.bat first.
|
||||
pause & exit /b 1
|
||||
)
|
||||
|
||||
:: ======== 查找可用的 Java 17+ ========
|
||||
set "JAVA_EXE="
|
||||
|
||||
:: 1) 优先用 jlink 生成的内嵌 JRE(运行过 build_release.bat 后产生)
|
||||
if exist "target\jre\bin\java.exe" set "JAVA_EXE=target\jre\bin\java.exe"
|
||||
|
||||
:: 2) 检查 JDK17_HOME 环境变量
|
||||
if "%JAVA_EXE%"=="" if defined JDK17_HOME if exist "%JDK17_HOME%\bin\java.exe" set "JAVA_EXE=%JDK17_HOME%\bin\java.exe"
|
||||
|
||||
:: 3) 检查 JAVA_HOME(可能是 JDK 17)
|
||||
if "%JAVA_EXE%"=="" if defined JAVA_HOME if exist "%JAVA_HOME%\bin\java.exe" set "JAVA_EXE=%JAVA_HOME%\bin\java.exe"
|
||||
|
||||
:: 4) 回退到 PATH 上的 java
|
||||
if "%JAVA_EXE%"=="" set "JAVA_EXE=java"
|
||||
|
||||
:: 检查 Java 版本是否 ≥ 17
|
||||
for /f "tokens=3" %%v in ('"%JAVA_EXE%" -version 2^>^&1 ^| findstr /i "version"') do set "JAVA_VER=%%~v"
|
||||
for /f "tokens=1 delims=." %%m in ("%JAVA_VER%") do set "MAJOR=%%m"
|
||||
if "%MAJOR%"=="1" for /f "tokens=2 delims=." %%m in ("%JAVA_VER%") do set "MAJOR=%%m"
|
||||
if %MAJOR% LSS 17 (
|
||||
echo [ERROR] Java %JAVA_VER% found. WorkUtils requires Java 17+.
|
||||
echo Path: %JAVA_EXE%
|
||||
echo Download JDK 17+ from https://adoptium.net/
|
||||
pause & exit /b 1
|
||||
)
|
||||
|
||||
echo ====================================
|
||||
echo workutils 系统工具集
|
||||
echo 控制台编码: UTF-8 (65001)
|
||||
echo WorkUtils v%VERSION%
|
||||
echo Java: %JAVA_VER% ^( %JAVA_EXE% ^)
|
||||
echo ====================================
|
||||
echo.
|
||||
cd /d "%~dp0"
|
||||
java -jar target\workutils-0.0.2-SNAPSHOT.jar
|
||||
|
||||
"%JAVA_EXE%" -Dfile.encoding=UTF-8 -jar "%JAR%" %*
|
||||
if %errorlevel% neq 0 pause
|
||||
|
||||
@@ -17,6 +17,8 @@ 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;
|
||||
@@ -48,6 +50,37 @@ public class WorkUtilsApplication implements CommandLineRunner {
|
||||
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);
|
||||
|
||||
@@ -3,12 +3,15 @@ package com.example.workutils.system.config;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -20,6 +23,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* 工具集配置属性
|
||||
@@ -301,23 +305,74 @@ public class WorkUtilsProperties {
|
||||
|
||||
private void ensureDataFile(Path target, String classpathResource, String label) {
|
||||
try {
|
||||
if (Files.exists(target)) return;
|
||||
Files.createDirectories(target.getParent());
|
||||
ClassPathResource cpr = new ClassPathResource(classpathResource);
|
||||
if (cpr.exists()) {
|
||||
try (InputStream in = cpr.getInputStream()) {
|
||||
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
// 文件存在且非空 → 跳过;不存在或为空(0字节)→ 重新复制模板
|
||||
if (Files.exists(target) && Files.size(target) > 0) {
|
||||
log.debug("{} 已存在且非空,跳过复制: {}", label, target.toAbsolutePath());
|
||||
return;
|
||||
}
|
||||
Files.createDirectories(target.getParent());
|
||||
|
||||
// 多 ClassLoader 回退,兼容 fat JAR / thin JAR / IDE 环境
|
||||
InputStream in = openClasspathStream(classpathResource);
|
||||
if (in != null) {
|
||||
try (in) {
|
||||
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
log.info("已从模板复制{}: {} ({} bytes)", label, target.toAbsolutePath(), Files.size(target));
|
||||
}
|
||||
log.info("已从模板复制{}: {}", label, target.toAbsolutePath());
|
||||
} else {
|
||||
Files.createFile(target);
|
||||
log.info("模板未找到,已创建空{}: {}", label, target.toAbsolutePath());
|
||||
// 所有 ClassLoader 都未找到模板,保留空文件防下次重试
|
||||
if (!Files.exists(target)) Files.createFile(target);
|
||||
log.warn("模板未找到[classpath:{}],已创建空{}: {}", classpathResource, label, target.toAbsolutePath());
|
||||
}
|
||||
} catch (java.io.IOException e) {
|
||||
log.warn("创建{}失败: {}", label, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多 ClassLoader 回退打开 classpath 资源流。
|
||||
* 优先级:ContextClassLoader → 当前类的 ClassLoader → 系统 ClassLoader → 直读 JAR
|
||||
*/
|
||||
private static InputStream openClasspathStream(String path) {
|
||||
InputStream in;
|
||||
|
||||
// 1) Thread ContextClassLoader(Spring Boot fat JAR 标准)
|
||||
ClassLoader ctxCl = Thread.currentThread().getContextClassLoader();
|
||||
if (ctxCl != null) {
|
||||
in = ctxCl.getResourceAsStream(path);
|
||||
if (in != null) return in;
|
||||
}
|
||||
|
||||
// 2) 当前类的 ClassLoader
|
||||
in = WorkUtilsProperties.class.getClassLoader().getResourceAsStream(path);
|
||||
if (in != null) return in;
|
||||
|
||||
// 3) 系统 ClassLoader(兜底)
|
||||
in = ClassLoader.getSystemResourceAsStream(path);
|
||||
if (in != null) return in;
|
||||
|
||||
// 4) 直接从 JAR 文件读取(终极回退,绕过所有 ClassLoader 问题)
|
||||
try {
|
||||
URL jarUrl = WorkUtilsProperties.class.getProtectionDomain().getCodeSource().getLocation();
|
||||
if (jarUrl != null) {
|
||||
String jarPath = jarUrl.getPath();
|
||||
String entryName = "BOOT-INF/classes/" + path;
|
||||
try (JarFile jar = new JarFile(jarPath)) {
|
||||
java.util.jar.JarEntry entry = jar.getJarEntry(entryName);
|
||||
if (entry != null) {
|
||||
byte[] bytes = jar.getInputStream(entry).readAllBytes();
|
||||
log.info("从 JAR 直接读取模板: {} ({} bytes)", entryName, bytes.length);
|
||||
return new ByteArrayInputStream(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 不是 JAR 环境或读取失败,忽略,前面 ClassLoader 方式已尽力
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Path getExportBasePath() {
|
||||
return Paths.get(exportDirectory);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@ import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 工集 UI 设计系统 —— AtlantaFX 主题管理 + 字体/间距 Token
|
||||
* <p>
|
||||
@@ -25,6 +31,9 @@ public final class Theme {
|
||||
private static volatile Mode currentMode = Mode.LIGHT;
|
||||
private static volatile String currentCustomCss = "css/light-theme.css";
|
||||
|
||||
/** 缓存已加载的 CSS data: URI,避免重复读取 */
|
||||
private static final Map<String, String> cssFileCache = new ConcurrentHashMap<>();
|
||||
|
||||
public static Mode getCurrentMode() { return currentMode; }
|
||||
|
||||
/**
|
||||
@@ -44,17 +53,36 @@ public final class Theme {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将自定义 CSS 样式表添加到 Scene
|
||||
* 将自定义 CSS 样式表添加到 Scene。
|
||||
* <p>
|
||||
* 兼容 Spring Boot fat JAR:使用 data: URI 内联 CSS,不依赖文件系统 URL。
|
||||
*/
|
||||
public static void applyCustomCss(Scene scene) {
|
||||
var cssUrl = Theme.class.getClassLoader().getResource(currentCustomCss);
|
||||
if (cssUrl == null) {
|
||||
String dataUri = cssFileCache.computeIfAbsent(currentCustomCss, Theme::loadCssAsDataUri);
|
||||
if (dataUri == null) {
|
||||
System.err.println("[Theme] 警告: 找不到 CSS 资源 " + currentCustomCss + ",将使用默认样式");
|
||||
return;
|
||||
}
|
||||
String css = cssUrl.toExternalForm();
|
||||
scene.getStylesheets().removeIf(s -> s.contains("light-theme.css") || s.contains("dark-theme.css"));
|
||||
scene.getStylesheets().add(css);
|
||||
scene.getStylesheets().add(dataUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 classpath 读取 CSS 文件,转成 data: URI(Base64),JavaFX 原生支持。
|
||||
*/
|
||||
private static String loadCssAsDataUri(String classpath) {
|
||||
try (InputStream in = Theme.class.getClassLoader().getResourceAsStream(classpath)) {
|
||||
if (in == null) {
|
||||
System.err.println("[Theme] 错误: classpath 中找不到 " + classpath);
|
||||
return null;
|
||||
}
|
||||
String css = new String(in.readAllBytes(), StandardCharsets.UTF_8);
|
||||
String b64 = Base64.getEncoder().encodeToString(css.getBytes(StandardCharsets.UTF_8));
|
||||
return "data:text/css;base64," + b64;
|
||||
} catch (Exception e) {
|
||||
System.err.println("[Theme] 错误: 读取 CSS 失败 " + classpath + " - " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user