兼容性优化
This commit is contained in:
@@ -269,7 +269,6 @@
|
|||||||
<directory>${basedir}/scripts</directory>
|
<directory>${basedir}/scripts</directory>
|
||||||
<includes>
|
<includes>
|
||||||
<include>start.bat</include>
|
<include>start.bat</include>
|
||||||
<include>start-jar.bat</include>
|
|
||||||
</includes>
|
</includes>
|
||||||
</resource>
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
+96
-45
@@ -1,6 +1,6 @@
|
|||||||
@echo off
|
@echo off
|
||||||
chcp 65001 >nul
|
chcp 65001 >nul
|
||||||
setlocal
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
:: ============================================
|
:: ============================================
|
||||||
:: WorkUtils 生产打包脚本(免安装版)
|
:: WorkUtils 生产打包脚本(免安装版)
|
||||||
@@ -8,7 +8,7 @@ setlocal
|
|||||||
:: (含 exe + 内嵌JRE + jar + 启动脚本)
|
:: (含 exe + 内嵌JRE + jar + 启动脚本)
|
||||||
:: ============================================
|
:: ============================================
|
||||||
|
|
||||||
::: 切换到项目根目录
|
:: 切换到项目根目录
|
||||||
cd /d "%~dp0.."
|
cd /d "%~dp0.."
|
||||||
set "PROJECT_DIR=%CD%"
|
set "PROJECT_DIR=%CD%"
|
||||||
set "TARGET=%PROJECT_DIR%\target"
|
set "TARGET=%PROJECT_DIR%\target"
|
||||||
@@ -20,26 +20,55 @@ echo ========== WorkUtils 生产构建 ==========
|
|||||||
echo 项目目录: %PROJECT_DIR%
|
echo 项目目录: %PROJECT_DIR%
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
::: 步骤 1:清理残留进程
|
:: 步骤 1:清理残留进程 & 解锁删除旧产物
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
echo [1/5] Clean stale processes...
|
echo [1/5] Clean stale processes and files...
|
||||||
taskkill /f /im java.exe 2>nul
|
taskkill /f /im java.exe 2>nul
|
||||||
taskkill /f /im workutils.exe 2>nul
|
taskkill /f /im workutils.exe 2>nul
|
||||||
|
:: 等进程完全退出,文件锁释放
|
||||||
|
echo 等待进程退出...
|
||||||
|
ping 127.0.0.1 -n 4 >nul
|
||||||
|
|
||||||
::: ============================================================
|
:: --- 清理 target/dist ---
|
||||||
::: 步骤 2:Maven 打包
|
|
||||||
::: ============================================================
|
|
||||||
echo.
|
|
||||||
echo [2/5] Maven build...
|
|
||||||
if exist "%TARGET%" (
|
if exist "%TARGET%" (
|
||||||
echo Cleaning old target...
|
:: 策略1:直接 rd(文件锁已释放时应成功)
|
||||||
rd /s /q "%TARGET%" 2>nul
|
rd /s /q "%TARGET%" 2>nul
|
||||||
|
:: 策略2:如果 rd 失败,改名后重建
|
||||||
if exist "%TARGET%" (
|
if exist "%TARGET%" (
|
||||||
echo [WARN] Cannot delete target, retrying with Maven clean...
|
echo [WARN] 直接删除失败,尝试改名清理...
|
||||||
call "%PROJECT_DIR%\mvnw.cmd" clean -q 2>nul
|
:: 先删 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
|
call "%PROJECT_DIR%\mvnw.cmd" package -DskipTests -q
|
||||||
if %errorlevel% neq 0 (
|
if %errorlevel% neq 0 (
|
||||||
echo [ERROR] Maven build failed!
|
echo [ERROR] Maven build failed!
|
||||||
@@ -47,22 +76,22 @@ if %errorlevel% neq 0 (
|
|||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
::: 步骤 3:提取版本号(PowerShell 解析 pom.xml)
|
:: 步骤 3:提取版本号
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
echo.
|
echo.
|
||||||
echo [3/5] Read version from pom.xml...
|
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"
|
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"
|
if "%VERSION%"=="" set "VERSION=0.0.1-SNAPSHOT"
|
||||||
echo 版本: %VERSION%
|
echo 版本: %VERSION%
|
||||||
|
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
::: 步骤 4:生成精简 JRE(jlink 内嵌 Java 17 + JavaFX)
|
:: 步骤 4:生成精简 JRE(jlink 内嵌 Java 17 + JavaFX)
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
echo.
|
echo.
|
||||||
echo [4/5] Generate bundled JRE...
|
echo [4/5] Generate bundled JRE...
|
||||||
|
|
||||||
::: 4.1 查找 JDK 17(环境变量 JDK17_HOME > 默认路径)
|
:: 4.1 查找 JDK 17
|
||||||
if not defined JDK17_HOME (
|
if not defined JDK17_HOME (
|
||||||
for %%d in (
|
for %%d in (
|
||||||
"C:\Program Files\Java\jdk-17"
|
"C:\Program Files\Java\jdk-17"
|
||||||
@@ -81,7 +110,7 @@ if not defined JDK17_HOME (
|
|||||||
)
|
)
|
||||||
:jdk17_found
|
:jdk17_found
|
||||||
|
|
||||||
::: 4.2 查找 JavaFX SDK(环境变量 JFX_SDK_HOME > 默认路径)
|
:: 4.2 查找 JavaFX SDK
|
||||||
if not defined JFX_SDK_HOME (
|
if not defined JFX_SDK_HOME (
|
||||||
for %%d in (
|
for %%d in (
|
||||||
"C:\Program Files\Java\javafx-sdk-17.0.15"
|
"C:\Program Files\Java\javafx-sdk-17.0.15"
|
||||||
@@ -102,10 +131,10 @@ if not defined JFX_SDK_HOME (
|
|||||||
echo JDK : %JDK17_HOME%
|
echo JDK : %JDK17_HOME%
|
||||||
echo JavaFX: %JFX_SDK_HOME%
|
echo JavaFX: %JFX_SDK_HOME%
|
||||||
|
|
||||||
::: 4.3 清理旧 JRE
|
:: 4.3 清理旧 JRE
|
||||||
if exist "%TARGET%\jre" rd /s /q "%TARGET%\jre"
|
if exist "%TARGET%\jre" rd /s /q "%TARGET%\jre" 2>nul
|
||||||
|
|
||||||
::: 4.4 执行 jlink
|
:: 4.4 执行 jlink
|
||||||
echo Running jlink, please wait...
|
echo Running jlink, please wait...
|
||||||
"%JDK17_HOME%\bin\jlink.exe" ^
|
"%JDK17_HOME%\bin\jlink.exe" ^
|
||||||
--module-path "%JFX_SDK_HOME%\lib;%JDK17_HOME%\jmods" ^
|
--module-path "%JFX_SDK_HOME%\lib;%JDK17_HOME%\jmods" ^
|
||||||
@@ -119,7 +148,7 @@ if %errorlevel% neq 0 (
|
|||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
::: 4.5 补充 JavaFX 原生 DLL(jlink 不会自动从 SDK bin/ 复制)
|
:: 4.5 补充 JavaFX 原生 DLL
|
||||||
echo Copy JavaFX native DLLs...
|
echo Copy JavaFX native DLLs...
|
||||||
if exist "%JFX_SDK_HOME%\bin\glass.dll" (
|
if exist "%JFX_SDK_HOME%\bin\glass.dll" (
|
||||||
xcopy /y /q "%JFX_SDK_HOME%\bin\*.dll" "%TARGET%\jre\bin\" >nul
|
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
|
echo [OK] JRE generated : %TARGET%\jre
|
||||||
|
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
::: 步骤 5:组装发布目录 & 打包 ZIP
|
:: 步骤 5:组装发布目录 & 打包 ZIP
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
echo.
|
echo.
|
||||||
echo [5/5] Assemble distribution...
|
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
|
mkdir "%DIST%\workutils" 2>nul
|
||||||
|
|
||||||
::: 复制 EXE
|
:: 复制 EXE
|
||||||
if exist "%TARGET%\workutils.exe" (
|
if exist "%TARGET%\workutils.exe" (
|
||||||
copy /y "%TARGET%\workutils.exe" "%DIST%\workutils\" >nul
|
copy /y "%TARGET%\workutils.exe" "%DIST%\workutils\" >nul
|
||||||
echo [OK] workutils.exe
|
echo [OK] workutils.exe
|
||||||
@@ -148,44 +181,61 @@ if exist "%TARGET%\workutils.exe" (
|
|||||||
echo [WARN] workutils.exe not found!
|
echo [WARN] workutils.exe not found!
|
||||||
)
|
)
|
||||||
|
|
||||||
::: 复制 JAR
|
:: 复制 JAR
|
||||||
for %%j in ("%TARGET%\workutils-*.jar") do (
|
for %%j in ("%TARGET%\workutils-*.jar") do (
|
||||||
copy /y "%%j" "%DIST%\workutils\" >nul
|
copy /y "%%j" "%DIST%\workutils\" >nul
|
||||||
echo [OK] %%~nxj
|
echo [OK] %%~nxj
|
||||||
)
|
)
|
||||||
|
|
||||||
::: 复制内嵌 JRE
|
:: 复制内嵌 JRE
|
||||||
if exist "%TARGET%\jre\bin\java.exe" (
|
if exist "%TARGET%\jre\bin\java.exe" (
|
||||||
|
echo 复制内嵌 JRE...
|
||||||
xcopy /e /i /q "%TARGET%\jre" "%DIST%\workutils\jre\" >nul
|
xcopy /e /i /q "%TARGET%\jre" "%DIST%\workutils\jre\" >nul
|
||||||
echo [OK] jre/ ^(bundled Java 17 + JavaFX^)
|
echo [OK] jre/ ^(bundled Java 17 + JavaFX^)
|
||||||
) else (
|
) else (
|
||||||
echo [WARN] jre not found in target!
|
echo [WARN] jre not found in target!
|
||||||
)
|
)
|
||||||
|
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
::: 生成 start.bat(备选启动方式)
|
:: 生成 start.bat
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
(
|
(
|
||||||
echo @echo off
|
echo @echo off
|
||||||
echo chcp 65001 ^>nul
|
echo chcp 65001 ^>nul
|
||||||
echo title WorkUtils
|
echo title WorkUtils
|
||||||
echo java -jar "%%~dp0workutils-%VERSION%.jar" %%*
|
echo.
|
||||||
echo pause
|
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"
|
) > "%DIST%\workutils\start.bat"
|
||||||
echo [OK] start.bat
|
echo [OK] start.bat
|
||||||
|
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
::: 打包 ZIP
|
:: 打包 ZIP
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
set "ZIPNAME=workutils-v%VERSION%"
|
set "ZIPNAME=workutils-v%VERSION%"
|
||||||
echo 正在压缩 %ZIPNAME%.zip ...
|
echo 正在压缩 %ZIPNAME%.zip ...
|
||||||
powershell -Command ^
|
powershell -Command ^
|
||||||
"Compress-Archive -Path '%DIST%\workutils' -DestinationPath '%DIST%\%ZIPNAME%.zip' -Force"
|
"Compress-Archive -Path '%DIST%\workutils' -DestinationPath '%DIST%\%ZIPNAME%.zip' -Force"
|
||||||
echo [OK] %ZIPNAME%.zip
|
echo [OK] %ZIPNAME%.zip
|
||||||
|
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
::: 完成
|
:: 完成
|
||||||
::: ============================================================
|
:: ============================================================
|
||||||
echo.
|
echo.
|
||||||
echo ============================================
|
echo ============================================
|
||||||
echo Build Complete ^! v%VERSION%
|
echo Build Complete ^! v%VERSION%
|
||||||
@@ -196,3 +246,4 @@ echo %DIST%\%ZIPNAME%.zip
|
|||||||
echo ============================================
|
echo ============================================
|
||||||
echo.
|
echo.
|
||||||
pause
|
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
|
@echo off
|
||||||
chcp 65001 > nul
|
chcp 65001 >nul
|
||||||
title workutils
|
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 ====================================
|
||||||
echo workutils 系统工具集
|
echo WorkUtils v%VERSION%
|
||||||
echo 控制台编码: UTF-8 (65001)
|
echo Java: %JAVA_VER% ^( %JAVA_EXE% ^)
|
||||||
echo ====================================
|
echo ====================================
|
||||||
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.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
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.*;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
@@ -48,6 +50,37 @@ public class WorkUtilsApplication implements CommandLineRunner {
|
|||||||
this.settingsService = settingsService;
|
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) {
|
public static void main(String[] args) {
|
||||||
WinConsole.ensureUtf8();
|
WinConsole.ensureUtf8();
|
||||||
SpringApplication.run(WorkUtilsApplication.class, args);
|
SpringApplication.run(WorkUtilsApplication.class, args);
|
||||||
|
|||||||
@@ -3,12 +3,15 @@ package com.example.workutils.system.config;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -20,6 +23,7 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
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) {
|
private void ensureDataFile(Path target, String classpathResource, String label) {
|
||||||
try {
|
try {
|
||||||
if (Files.exists(target)) return;
|
// 文件存在且非空 → 跳过;不存在或为空(0字节)→ 重新复制模板
|
||||||
|
if (Files.exists(target) && Files.size(target) > 0) {
|
||||||
|
log.debug("{} 已存在且非空,跳过复制: {}", label, target.toAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
Files.createDirectories(target.getParent());
|
Files.createDirectories(target.getParent());
|
||||||
ClassPathResource cpr = new ClassPathResource(classpathResource);
|
|
||||||
if (cpr.exists()) {
|
// 多 ClassLoader 回退,兼容 fat JAR / thin JAR / IDE 环境
|
||||||
try (InputStream in = cpr.getInputStream()) {
|
InputStream in = openClasspathStream(classpathResource);
|
||||||
|
if (in != null) {
|
||||||
|
try (in) {
|
||||||
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
log.info("已从模板复制{}: {} ({} bytes)", label, target.toAbsolutePath(), Files.size(target));
|
||||||
}
|
}
|
||||||
log.info("已从模板复制{}: {}", label, target.toAbsolutePath());
|
|
||||||
} else {
|
} else {
|
||||||
Files.createFile(target);
|
// 所有 ClassLoader 都未找到模板,保留空文件防下次重试
|
||||||
log.info("模板未找到,已创建空{}: {}", label, target.toAbsolutePath());
|
if (!Files.exists(target)) Files.createFile(target);
|
||||||
|
log.warn("模板未找到[classpath:{}],已创建空{}: {}", classpathResource, label, target.toAbsolutePath());
|
||||||
}
|
}
|
||||||
} catch (java.io.IOException e) {
|
} catch (java.io.IOException e) {
|
||||||
log.warn("创建{}失败: {}", label, e.getMessage());
|
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() {
|
public Path getExportBasePath() {
|
||||||
return Paths.get(exportDirectory);
|
return Paths.get(exportDirectory);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ import javafx.scene.paint.Color;
|
|||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import javafx.scene.text.FontWeight;
|
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
|
* 工集 UI 设计系统 —— AtlantaFX 主题管理 + 字体/间距 Token
|
||||||
* <p>
|
* <p>
|
||||||
@@ -25,6 +31,9 @@ public final class Theme {
|
|||||||
private static volatile Mode currentMode = Mode.LIGHT;
|
private static volatile Mode currentMode = Mode.LIGHT;
|
||||||
private static volatile String currentCustomCss = "css/light-theme.css";
|
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; }
|
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) {
|
public static void applyCustomCss(Scene scene) {
|
||||||
var cssUrl = Theme.class.getClassLoader().getResource(currentCustomCss);
|
String dataUri = cssFileCache.computeIfAbsent(currentCustomCss, Theme::loadCssAsDataUri);
|
||||||
if (cssUrl == null) {
|
if (dataUri == null) {
|
||||||
System.err.println("[Theme] 警告: 找不到 CSS 资源 " + currentCustomCss + ",将使用默认样式");
|
System.err.println("[Theme] 警告: 找不到 CSS 资源 " + currentCustomCss + ",将使用默认样式");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String css = cssUrl.toExternalForm();
|
|
||||||
scene.getStylesheets().removeIf(s -> s.contains("light-theme.css") || s.contains("dark-theme.css"));
|
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