C端打包修复不能在中文路径下启动的问题
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
|
||||
REM 获取当前批处理文件所在目录
|
||||
cd /d "%~dp0"
|
||||
|
||||
REM 检查是否有管理员权限
|
||||
net session >nul 2>&1
|
||||
if %errorlevel% == 0 (
|
||||
REM 已有管理员权限,直接启动
|
||||
start "" "NPQS9100.exe"
|
||||
exit
|
||||
) else (
|
||||
REM 没有管理员权限,使用 PowerShell 以管理员身份启动(隐藏窗口)
|
||||
powershell -WindowStyle Hidden -Command "Start-Process '%~dp0NPQS9100.exe' -Verb RunAs"
|
||||
exit
|
||||
)
|
||||
|
||||
@@ -4,27 +4,34 @@ NPQS-9100 升级与回滚说明
|
||||
|
||||
一、升级步骤(超简单!)
|
||||
----------------------------------------
|
||||
1. 双击 upgrade.bat(首次会自动创建 upgrade 文件夹)
|
||||
2. 将升级文件放入 upgrade/ 目录:
|
||||
- app.asar (前端升级包 - 文件)
|
||||
- app.asar.unpacked\ (前端升级包 - 文件夹)
|
||||
- entrance.jar (后端升级包)
|
||||
3. 再次双击 upgrade.bat 开始升级
|
||||
4. 等待完成后重启应用
|
||||
1. 先使用 Navicat 或其他工具手动导出数据库 SQL 备份
|
||||
2. 双击 upgrade.bat(首次会自动创建 upgrade 文件夹)
|
||||
3. 将升级文件放入 upgrade/ 目录:
|
||||
- app.asar + app.asar.unpacked\ (前端升级包,必须成套放入)
|
||||
- entrance.jar (后端升级包)
|
||||
4. 再次双击 upgrade.bat 开始升级
|
||||
5. 等待完成后重启应用
|
||||
|
||||
补充说明:
|
||||
- 可以只升级后端
|
||||
- 可以只升级前端,但前端升级时必须同时提供:
|
||||
app.asar
|
||||
app.asar.unpacked\
|
||||
- 数据库不由 upgrade.bat 自动备份,请务必提前手动导出 SQL
|
||||
|
||||
二、回滚步骤
|
||||
----------------------------------------
|
||||
如果升级后出现问题:
|
||||
1. 双击 rollback.bat
|
||||
2. 选择是否回滚数据库(谨慎!)
|
||||
3. 等待完成后重启应用
|
||||
1. 双击 rollback.bat(仅回滚前后端程序文件)
|
||||
2. 等待完成后重启应用
|
||||
3. 如需恢复数据库,请手动执行升级前导出的 SQL
|
||||
|
||||
三、重要提示
|
||||
----------------------------------------
|
||||
✓ 升级前会自动备份到 backup/ 目录
|
||||
✓ 数据库会自动备份到 mysql/data_backup/
|
||||
✓ 升级前会自动备份前后端程序文件到 backup/ 目录
|
||||
✓ 升级日志保存在 logs/upgrade.log
|
||||
✓ 多次升级时,backup/ 保存最后一次升级前的版本
|
||||
✓ 数据库备份与恢复由人工处理,不再由脚本自动执行
|
||||
|
||||
四、紧急情况
|
||||
----------------------------------------
|
||||
@@ -32,15 +39,15 @@ NPQS-9100 升级与回滚说明
|
||||
|
||||
【恢复前端】
|
||||
copy /Y backup\app.asar resources\app.asar
|
||||
rmdir /s /q resources\app.asar.unpacked
|
||||
xcopy backup\app.asar.unpacked resources\app.asar.unpacked\ /E /I /Y
|
||||
|
||||
【恢复后端】
|
||||
copy /Y backup\entrance.jar resources\extraResources\java\entrance.jar
|
||||
|
||||
【恢复数据库】(慎用!)
|
||||
xcopy mysql\data_backup mysql\data\ /E /I /Y
|
||||
【恢复数据库】
|
||||
请使用 Navicat 或其他工具执行升级前导出的 SQL 备份
|
||||
|
||||
========================================
|
||||
详细文档请参考:doc/绿色包升级指南.md
|
||||
如需完整技术文档,请联系交付方提供开发文档。
|
||||
========================================
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="*"
|
||||
name="NPQS9100"
|
||||
type="win32"
|
||||
/>
|
||||
<description>NPQS-9100自动检测平台</description>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 and Windows 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
@@ -52,7 +52,8 @@ echo.
|
||||
|
||||
echo ========================================
|
||||
echo 打包完成!
|
||||
echo 输出目录: out\win-unpacked\
|
||||
echo 最终交付目录: out\NPQS-9100\
|
||||
echo 原始输出目录: out\win-unpacked\ (已自动重命名)
|
||||
echo ========================================
|
||||
echo.
|
||||
pause
|
||||
|
||||
@@ -63,7 +63,8 @@ echo.
|
||||
|
||||
echo ========================================
|
||||
echo ✓ 打包完成!
|
||||
echo 输出目录: out\win-unpacked\
|
||||
echo 最终交付目录: out\NPQS-9100\
|
||||
echo 原始输出目录: out\win-unpacked\ (已自动重命名)
|
||||
echo ========================================
|
||||
echo.
|
||||
pause
|
||||
|
||||
@@ -53,13 +53,13 @@ if exist "%LOGDIR%" (
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [4] 检查 MySQL 服务...
|
||||
sc query mysql9100 >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo MySQL 服务存在
|
||||
sc query mysql9100 | findstr "STATE"
|
||||
echo [4] 检查当前运行模式...
|
||||
echo 当前版本为绿色包进程模式,不使用 MySQL Windows 服务
|
||||
if exist "mysql\my.ini" (
|
||||
echo 检测到 MySQL 配置文件:
|
||||
findstr /i "^port=" "mysql\my.ini"
|
||||
) else (
|
||||
echo MySQL 服务不存在
|
||||
echo 尚未检测到 mysql\my.ini(可能应用还未完整启动)
|
||||
)
|
||||
echo.
|
||||
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal
|
||||
echo ========================================
|
||||
echo NPQS9100 回滚工具
|
||||
echo ========================================
|
||||
echo.
|
||||
echo 【重要提示】
|
||||
echo 本工具用于回滚上次升级的内容
|
||||
echo 回滚后将恢复到升级前的状态
|
||||
echo 本工具仅回滚前后端程序文件
|
||||
echo 数据库不会由本脚本自动恢复
|
||||
echo 如需恢复数据库,请手动执行之前导出的 SQL 备份
|
||||
echo.
|
||||
pause
|
||||
echo.
|
||||
|
||||
echo [1/5] 停止 NPQS9100 进程...
|
||||
echo [1/4] 停止 NPQS9100 进程...
|
||||
taskkill /F /IM NPQS9100.exe 2>nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo NPQS9100 已停止
|
||||
@@ -21,7 +23,7 @@ if %errorlevel% equ 0 (
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [2/5] 检查备份文件...
|
||||
echo [2/4] 检查备份文件...
|
||||
set hasBackup=0
|
||||
|
||||
if exist backup\app.asar (
|
||||
@@ -39,23 +41,17 @@ if exist backup\entrance.jar (
|
||||
set hasBackup=1
|
||||
)
|
||||
|
||||
if exist mysql\data_backup (
|
||||
echo 发现数据库备份
|
||||
set hasBackup=1
|
||||
)
|
||||
|
||||
if %hasBackup%==0 (
|
||||
echo 未发现任何备份文件!
|
||||
echo 未发现任何前后端备份文件!
|
||||
echo 无法执行回滚操作
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [3/5] 回滚前端...
|
||||
echo [3/4] 回滚前端...
|
||||
set frontendRollback=0
|
||||
|
||||
REM 回滚 app.asar
|
||||
if exist backup\app.asar (
|
||||
echo 正在恢复 app.asar...
|
||||
copy /Y backup\app.asar resources\app.asar >nul 2>&1
|
||||
@@ -69,7 +65,6 @@ if exist backup\app.asar (
|
||||
)
|
||||
)
|
||||
|
||||
REM 回滚 app.asar.unpacked
|
||||
if exist backup\app.asar.unpacked (
|
||||
echo 正在恢复 app.asar.unpacked...
|
||||
if exist resources\app.asar.unpacked (
|
||||
@@ -91,7 +86,7 @@ if %frontendRollback%==0 (
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [4/5] 回滚后端...
|
||||
echo [4/4] 回滚后端...
|
||||
if exist backup\entrance.jar (
|
||||
echo 正在恢复 JAR 文件...
|
||||
copy /Y backup\entrance.jar resources\extraResources\java\entrance.jar >nul 2>&1
|
||||
@@ -107,37 +102,11 @@ if exist backup\entrance.jar (
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [5/5] 回滚数据库...
|
||||
if exist mysql\data_backup (
|
||||
echo 是否回滚数据库?(数据库回滚会丢失升级后的数据!)
|
||||
echo [Y] 是 [N] 否
|
||||
choice /C YN /N /M "请选择:"
|
||||
if errorlevel 2 (
|
||||
echo 已跳过数据库回滚
|
||||
) else (
|
||||
echo 正在回滚数据库...
|
||||
if exist mysql\data (
|
||||
rmdir /s /q mysql\data 2>nul
|
||||
)
|
||||
xcopy mysql\data_backup mysql\data\ /E /I /Y /Q >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo 数据库已回滚
|
||||
) else (
|
||||
echo 数据库回滚失败
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
) else (
|
||||
echo 无数据库备份,跳过
|
||||
)
|
||||
echo.
|
||||
|
||||
echo ========================================
|
||||
echo 回滚完成!
|
||||
echo ========================================
|
||||
echo.
|
||||
echo 如需恢复数据库,请手动执行之前导出的 SQL 备份。
|
||||
echo 您现在可以启动 NPQS9100 了
|
||||
echo.
|
||||
pause
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal
|
||||
echo ========================================
|
||||
echo NPQS9100 升级工具
|
||||
echo ========================================
|
||||
@@ -11,9 +12,8 @@ if not exist upgrade (
|
||||
echo 【首次使用】已自动创建 upgrade 目录
|
||||
echo.
|
||||
echo 请将升级文件放入 upgrade 目录:
|
||||
echo - app.asar (前端升级包 - 文件)
|
||||
echo - app.asar.unpacked\ (前端升级包 - 文件夹)
|
||||
echo - entrance.jar (后端升级包)
|
||||
echo - app.asar + app.asar.unpacked\ (前端升级包,必须成套放入)
|
||||
echo - entrance.jar (后端升级包)
|
||||
echo.
|
||||
echo 放置完成后,重新运行本脚本即可升级
|
||||
echo.
|
||||
@@ -21,19 +21,51 @@ if not exist upgrade (
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
REM 检查是否有升级文件
|
||||
set hasUpgrade=0
|
||||
if exist upgrade\app.asar set hasUpgrade=1
|
||||
if exist upgrade\app.asar.unpacked set hasUpgrade=1
|
||||
if exist upgrade\entrance.jar set hasUpgrade=1
|
||||
REM 检查升级文件状态
|
||||
set "hasFrontendAsar=0"
|
||||
set "hasFrontendUnpacked=0"
|
||||
set "upgradeFrontend=0"
|
||||
set "upgradeBackend=0"
|
||||
set "frontendUpgraded=0"
|
||||
|
||||
if %hasUpgrade%==0 (
|
||||
if exist upgrade\app.asar set "hasFrontendAsar=1"
|
||||
if exist upgrade\app.asar.unpacked set "hasFrontendUnpacked=1"
|
||||
if exist upgrade\entrance.jar set "upgradeBackend=1"
|
||||
|
||||
if %hasFrontendAsar%==1 if %hasFrontendUnpacked%==1 (
|
||||
set "upgradeFrontend=1"
|
||||
)
|
||||
|
||||
if %hasFrontendAsar%==1 if %hasFrontendUnpacked%==0 (
|
||||
echo 【错误】前端升级包不完整!
|
||||
echo.
|
||||
echo 当前仅检测到:upgrade\app.asar
|
||||
echo 前端升级必须同时提供以下两个内容:
|
||||
echo - upgrade\app.asar
|
||||
echo - upgrade\app.asar.unpacked\
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if %hasFrontendAsar%==0 if %hasFrontendUnpacked%==1 (
|
||||
echo 【错误】前端升级包不完整!
|
||||
echo.
|
||||
echo 当前仅检测到:upgrade\app.asar.unpacked\
|
||||
echo 前端升级必须同时提供以下两个内容:
|
||||
echo - upgrade\app.asar
|
||||
echo - upgrade\app.asar.unpacked\
|
||||
echo.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if %upgradeFrontend%==0 if %upgradeBackend%==0 (
|
||||
echo 【提示】upgrade 目录为空!
|
||||
echo.
|
||||
echo 请将升级文件放入 upgrade 目录:
|
||||
echo - app.asar (前端升级包 - 文件)
|
||||
echo - app.asar.unpacked\ (前端升级包 - 文件夹)
|
||||
echo - entrance.jar (后端升级包)
|
||||
echo - app.asar + app.asar.unpacked\ (前端升级包,必须成套放入)
|
||||
echo - entrance.jar (后端升级包)
|
||||
echo.
|
||||
echo 放置完成后,重新运行本脚本即可升级
|
||||
echo.
|
||||
@@ -42,19 +74,22 @@ if %hasUpgrade%==0 (
|
||||
)
|
||||
|
||||
echo 【检测到升级文件】
|
||||
if exist upgrade\app.asar echo - 前端升级(asar文件): upgrade\app.asar
|
||||
if exist upgrade\app.asar.unpacked echo - 前端升级(unpacked文件夹): upgrade\app.asar.unpacked\
|
||||
if exist upgrade\entrance.jar echo - 后端升级: upgrade\entrance.jar
|
||||
if %upgradeFrontend%==1 (
|
||||
echo - 前端升级: upgrade\app.asar + upgrade\app.asar.unpacked\
|
||||
)
|
||||
if %upgradeBackend%==1 (
|
||||
echo - 后端升级: upgrade\entrance.jar
|
||||
)
|
||||
echo.
|
||||
echo 【重要提示】
|
||||
echo 1. 升级前会自动备份当前版本
|
||||
echo 2. 如升级失败可运行 rollback.bat 回滚
|
||||
echo 3. 数据库会自动备份到 mysql\data_backup\
|
||||
echo 1. 升级前会自动备份当前前后端程序文件
|
||||
echo 2. 如升级失败可运行 rollback.bat 回滚前后端程序文件
|
||||
echo 3. 数据库请先使用 Navicat 或其他工具手动导出 SQL 备份
|
||||
echo.
|
||||
pause
|
||||
echo.
|
||||
|
||||
echo [1/6] 停止 NPQS9100 进程...
|
||||
echo [1/5] 停止 NPQS9100 进程...
|
||||
taskkill /F /IM NPQS9100.exe 2>nul
|
||||
if %errorlevel% equ 0 (
|
||||
echo NPQS9100 已停止
|
||||
@@ -64,63 +99,54 @@ if %errorlevel% equ 0 (
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [2/6] 备份当前版本(用于回滚)...
|
||||
echo [2/5] 备份当前版本(用于回滚)...
|
||||
if not exist backup mkdir backup
|
||||
|
||||
REM 备份前端(app.asar 和 app.asar.unpacked)
|
||||
if exist resources\app.asar (
|
||||
echo 正在备份前端 app.asar...
|
||||
if not exist backup mkdir backup
|
||||
copy /Y resources\app.asar backup\app.asar >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo app.asar 备份完成
|
||||
) else (
|
||||
echo app.asar 备份失败
|
||||
REM 备份前端(仅在执行前端升级时)
|
||||
if %upgradeFrontend%==1 (
|
||||
if exist resources\app.asar (
|
||||
echo 正在备份前端 app.asar...
|
||||
copy /Y resources\app.asar backup\app.asar >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo app.asar 备份完成
|
||||
) else (
|
||||
echo app.asar 备份失败
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
if exist resources\app.asar.unpacked (
|
||||
echo 正在备份前端 app.asar.unpacked...
|
||||
if exist backup\app.asar.unpacked (
|
||||
rmdir /s /q backup\app.asar.unpacked 2>nul
|
||||
)
|
||||
xcopy resources\app.asar.unpacked backup\app.asar.unpacked\ /E /I /Y /Q >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo app.asar.unpacked 备份完成
|
||||
) else (
|
||||
echo app.asar.unpacked 备份失败
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if exist resources\app.asar.unpacked (
|
||||
echo 正在备份前端 app.asar.unpacked...
|
||||
if exist backup\app.asar.unpacked (
|
||||
rmdir /s /q backup\app.asar.unpacked 2>nul
|
||||
)
|
||||
xcopy resources\app.asar.unpacked backup\app.asar.unpacked\ /E /I /Y /Q >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo app.asar.unpacked 备份完成
|
||||
) else (
|
||||
echo app.asar.unpacked 备份失败
|
||||
)
|
||||
)
|
||||
|
||||
REM 备份后端
|
||||
if exist resources\extraResources\java\entrance.jar (
|
||||
REM 备份后端(仅在执行后端升级时)
|
||||
if %upgradeBackend%==1 if exist resources\extraResources\java\entrance.jar (
|
||||
echo 正在备份后端...
|
||||
copy /Y resources\extraResources\java\entrance.jar backup\entrance.jar >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo 后端备份完成
|
||||
) else (
|
||||
echo 后端备份失败
|
||||
)
|
||||
)
|
||||
|
||||
REM 备份数据库
|
||||
if exist mysql\data (
|
||||
echo 正在备份数据库...
|
||||
if exist mysql\data_backup (
|
||||
rmdir /s /q mysql\data_backup 2>nul
|
||||
)
|
||||
xcopy mysql\data mysql\data_backup\ /E /I /Y /Q >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo 数据库备份完成
|
||||
) else (
|
||||
echo 数据库备份失败,请手动备份后继续
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [3/6] 记录版本信息...
|
||||
echo [3/5] 记录版本信息...
|
||||
if not exist backup\version.txt (
|
||||
echo 备份时间: %date% %time% > backup\version.txt
|
||||
echo 升级前版本备份 >> backup\version.txt
|
||||
@@ -130,29 +156,22 @@ if not exist backup\version.txt (
|
||||
echo 版本信息已记录
|
||||
echo.
|
||||
|
||||
echo [4/6] 升级前端...
|
||||
set frontendUpgraded=0
|
||||
|
||||
REM 升级 app.asar
|
||||
if exist upgrade\app.asar (
|
||||
echo [4/5] 升级前端...
|
||||
if %upgradeFrontend%==1 (
|
||||
echo 正在替换 app.asar...
|
||||
copy /Y upgrade\app.asar resources\app.asar >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo app.asar 升级完成
|
||||
set frontendUpgraded=1
|
||||
) else (
|
||||
echo app.asar 升级失败,正在回滚...
|
||||
if exist backup\app.asar (
|
||||
copy /Y backup\app.asar resources\app.asar >nul 2>&1
|
||||
echo 已回滚到升级前版本
|
||||
)
|
||||
echo 已回滚到升级前版本
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
REM 升级 app.asar.unpacked
|
||||
if exist upgrade\app.asar.unpacked (
|
||||
echo 正在替换 app.asar.unpacked...
|
||||
if exist resources\app.asar.unpacked (
|
||||
rmdir /s /q resources\app.asar.unpacked 2>nul
|
||||
@@ -160,26 +179,27 @@ if exist upgrade\app.asar.unpacked (
|
||||
xcopy upgrade\app.asar.unpacked resources\app.asar.unpacked\ /E /I /Y /Q >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo app.asar.unpacked 升级完成
|
||||
set frontendUpgraded=1
|
||||
set "frontendUpgraded=1"
|
||||
) else (
|
||||
echo app.asar.unpacked 升级失败,正在回滚...
|
||||
echo app.asar.unpacked 升级失败,正在回滚整个前端...
|
||||
if exist backup\app.asar (
|
||||
copy /Y backup\app.asar resources\app.asar >nul 2>&1
|
||||
)
|
||||
if exist backup\app.asar.unpacked (
|
||||
rmdir /s /q resources\app.asar.unpacked 2>nul
|
||||
xcopy backup\app.asar.unpacked resources\app.asar.unpacked\ /E /I /Y /Q >nul 2>&1
|
||||
echo 已回滚到升级前版本
|
||||
)
|
||||
echo 已回滚到升级前版本
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
if %frontendUpgraded%==0 (
|
||||
) else (
|
||||
echo 无前端升级包,跳过
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [5/6] 升级后端...
|
||||
if exist upgrade\entrance.jar (
|
||||
echo [5/5] 升级后端...
|
||||
if %upgradeBackend%==1 (
|
||||
echo 正在替换 JAR 文件...
|
||||
copy /Y upgrade\entrance.jar resources\extraResources\java\entrance.jar >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
@@ -188,8 +208,17 @@ if exist upgrade\entrance.jar (
|
||||
echo 后端升级失败,正在回滚...
|
||||
if exist backup\entrance.jar (
|
||||
copy /Y backup\entrance.jar resources\extraResources\java\entrance.jar >nul 2>&1
|
||||
echo 已回滚到升级前版本
|
||||
)
|
||||
if %frontendUpgraded%==1 (
|
||||
if exist backup\app.asar (
|
||||
copy /Y backup\app.asar resources\app.asar >nul 2>&1
|
||||
)
|
||||
if exist backup\app.asar.unpacked (
|
||||
rmdir /s /q resources\app.asar.unpacked 2>nul
|
||||
xcopy backup\app.asar.unpacked resources\app.asar.unpacked\ /E /I /Y /Q >nul 2>&1
|
||||
)
|
||||
)
|
||||
echo 已回滚到升级前版本
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
@@ -198,19 +227,17 @@ if exist upgrade\entrance.jar (
|
||||
)
|
||||
echo.
|
||||
|
||||
echo [6/6] 记录升级日志...
|
||||
if not exist logs mkdir logs
|
||||
echo ========================================== >> logs\upgrade.log
|
||||
echo 升级时间: %date% %time% >> logs\upgrade.log
|
||||
if exist upgrade\dist (
|
||||
if %upgradeFrontend%==1 (
|
||||
echo 升级内容: 前端 >> logs\upgrade.log
|
||||
)
|
||||
if exist upgrade\entrance.jar (
|
||||
if %upgradeBackend%==1 (
|
||||
echo 升级内容: 后端 >> logs\upgrade.log
|
||||
)
|
||||
echo 数据库处理: 请手动导出/导入 SQL >> logs\upgrade.log
|
||||
echo ========================================== >> logs\upgrade.log
|
||||
echo 升级日志已记录
|
||||
echo.
|
||||
|
||||
echo ========================================
|
||||
echo 升级完成!
|
||||
@@ -218,10 +245,9 @@ echo ========================================
|
||||
echo.
|
||||
echo 【提示】
|
||||
echo 1. 如需回滚,请运行 rollback.bat
|
||||
echo 2. 升级文件已使用,可删除 upgrade 目录
|
||||
echo 2. 数据库如需恢复,请手动执行之前导出的 SQL
|
||||
echo 3. 升级文件已使用,可删除 upgrade 目录
|
||||
echo.
|
||||
echo 您现在可以启动 NPQS9100 应用。
|
||||
echo.
|
||||
pause
|
||||
|
||||
|
||||
|
||||
@@ -3,18 +3,21 @@
|
||||
## 使用方法
|
||||
|
||||
1. 将升级文件放入此目录:
|
||||
- app.asar (前端升级包 - 文件)
|
||||
- app.asar.unpacked/ (前端升级包 - 文件夹)
|
||||
- app.asar + app.asar.unpacked/ (前端升级包,必须成套放入)
|
||||
- entrance.jar (后端升级包)
|
||||
|
||||
2. 双击运行根目录的 upgrade.bat 脚本
|
||||
2. 升级前先手动导出数据库 SQL 备份
|
||||
|
||||
3. 升级完成后重启应用
|
||||
3. 双击运行根目录的 upgrade.bat 脚本
|
||||
|
||||
4. 升级完成后重启应用
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 可以只放前端或只放后端,支持单独升级
|
||||
- 可以只放后端,或放完整前端包,支持单独升级
|
||||
- 如果升级前端,必须同时放入 app.asar 和 app.asar.unpacked/
|
||||
- 升级前会自动备份到 backup/ 目录
|
||||
- 如果升级失败,运行 rollback.bat 可回滚
|
||||
- 数据库不由脚本自动备份,请手动导出/导入 SQL
|
||||
- 如果升级失败,运行 rollback.bat 可回滚前后端程序文件
|
||||
|
||||
详细说明请参考:README-升级回滚.txt
|
||||
详细说明请参考根目录 README-升级回滚.txt
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
],
|
||||
"win": {
|
||||
"icon": "build/icons/icon.ico",
|
||||
"requestedExecutionLevel": "requireAdministrator",
|
||||
"requestedExecutionLevel": "asInvoker",
|
||||
"signAndEditExecutable": false,
|
||||
"verifyUpdateCodeSignature": false,
|
||||
"artifactName": "${productName}-${os}-${version}-${arch}.${ext}",
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
- 自动更新配置文件
|
||||
|
||||
#### **2. 盘符自动识别**
|
||||
- 应用在 C 盘 → 数据目录:`C:\NPQS9100_Data\`
|
||||
- 应用在 D 盘 → 数据目录:`D:\NPQS9100_Data\`
|
||||
- 程序目录为纯英文/ASCII路径时:数据目录位于应用目录内的 `NPQS9100_Data\`
|
||||
- 程序目录包含中文或其他非 ASCII 字符时:自动切换到当前盘符根目录下的 `NPQS9100_Runtime\`
|
||||
- 自动创建所有必要的子目录
|
||||
|
||||
#### **3. Loading 界面**
|
||||
@@ -65,18 +65,21 @@
|
||||
|
||||
### 📁 目录结构
|
||||
|
||||
交付口径说明:
|
||||
- `electron-builder` 原始输出目录:`out/win-unpacked`
|
||||
- `npm run build-w` 脚本最终交付目录:`out/NPQS-9100`
|
||||
|
||||
```
|
||||
win-unpacked/ # 绿色版目录(约 650-800 MB)
|
||||
NPQS-9100/ # 绿色版目录(约 650-800 MB)
|
||||
├── NPQS9100.exe # 主程序(双击运行)⭐
|
||||
├── NPQS9100-启动器.bat # 管理员启动器(备用)
|
||||
├── upgrade.bat # 升级工具 ⭐
|
||||
├── rollback.bat # 回滚工具 ⭐
|
||||
├── uninstall-mysql-service.bat # MySQL 服务卸载工具
|
||||
├── 使用说明.txt # 使用手册
|
||||
├── README-升级回滚.txt # 升级说明
|
||||
├── upgrade/ # 升级包目录(首次使用自动创建)
|
||||
├── backup/ # 自动备份目录
|
||||
│ ├── dist/ # 前端备份
|
||||
│ ├── app.asar # 前端主包备份
|
||||
│ ├── app.asar.unpacked/ # 前端展开目录备份
|
||||
│ ├── entrance.jar # 后端备份
|
||||
│ └── version.txt # 版本记录
|
||||
├── resources/
|
||||
@@ -93,13 +96,12 @@ win-unpacked/ # 绿色版目录(约 650-800 MB)
|
||||
├── scripts/ # 启动管理脚本(⚠️ 不要修改)
|
||||
│ ├── config-generator.js
|
||||
│ ├── java-runner.js
|
||||
│ ├── mysql-service-manager.js
|
||||
│ ├── mysql-process-manager.js
|
||||
│ ├── port-checker.js
|
||||
│ └── startup-manager.js
|
||||
├── mysql/ # MySQL 数据库
|
||||
│ ├── bin/
|
||||
│ ├── data/ # 数据库数据 ⚠️ 不要动
|
||||
│ ├── data_backup/ # 自动备份
|
||||
│ ├── my.ini # 运行时生成
|
||||
│ └── README.txt
|
||||
├── jre/ # Java 运行环境(⚠️ 不要修改)
|
||||
@@ -110,7 +112,12 @@ win-unpacked/ # 绿色版目录(约 650-800 MB)
|
||||
└── upgrade.log
|
||||
|
||||
用户数据目录(首次运行自动创建):
|
||||
X:\NPQS9100_Data\ (X 是应用所在盘符)
|
||||
- 程序目录为纯英文/ASCII路径时:
|
||||
`应用目录\NPQS9100_Data\`
|
||||
- 程序目录包含中文或其他非 ASCII 字符时:
|
||||
`X:\NPQS9100_Runtime\data\` (X 是应用所在盘符)
|
||||
|
||||
用户数据目录内包含:
|
||||
├── logs/ # Spring Boot 日志
|
||||
├── template/ # 报告模板
|
||||
├── report/ # 生成的报告
|
||||
@@ -126,10 +133,8 @@ X:\NPQS9100_Data\ (X 是应用所在盘符)
|
||||
| 文件 | 功能 | 使用场景 |
|
||||
|------|------|---------|
|
||||
| **NPQS9100.exe** | 主程序 | 日常启动 |
|
||||
| **NPQS9100-启动器.bat** | 管理员启动器 | 首次运行或权限问题时 |
|
||||
| **upgrade.bat** | 升级工具 | 收到升级包时使用 |
|
||||
| **rollback.bat** | 回滚工具 | 升级后出问题时使用 |
|
||||
| **uninstall-mysql-service.bat** | MySQL 服务卸载 | 完全卸载应用时使用 |
|
||||
| **使用说明.txt** | 使用手册 | 遇到问题时参考 |
|
||||
| **README-升级回滚.txt** | 升级说明 | 升级前阅读 |
|
||||
|
||||
@@ -151,9 +156,9 @@ X:\NPQS9100_Data\ (X 是应用所在盘符)
|
||||
|
||||
| 路径 | 说明 | 备份建议 |
|
||||
|------|------|---------|
|
||||
| **mysql/data/** | 数据库数据 | ⚠️ 定期备份! |
|
||||
| **应用目录\\mysql\\data/** 或 **X:\\NPQS9100_Runtime\\mysql\\data/** | 数据库数据 | ⚠️ 定期备份! |
|
||||
| **backup/** | 升级备份 | 自动管理,无需手动操作 |
|
||||
| **X:\NPQS9100_Data/** | 用户数据目录 | 包含日志、模板、报告 |
|
||||
| **应用目录\\NPQS9100_Data/** 或 **X:\\NPQS9100_Runtime\\data/** | 用户数据目录 | 包含日志、模板、报告 |
|
||||
|
||||
---
|
||||
|
||||
@@ -174,7 +179,7 @@ clean-and-build.bat
|
||||
3. 构建前端代码(Vue 3)
|
||||
4. 编译 Electron 主进程代码
|
||||
5. 复制 MySQL、JRE、Java 资源
|
||||
6. 生成 win-unpacked 绿色版目录
|
||||
6. 先生成 `out/win-unpacked`,再自动重命名为最终交付目录 `out/NPQS-9100`
|
||||
|
||||
**等待时间**:约 5-15 分钟
|
||||
|
||||
@@ -183,7 +188,7 @@ clean-and-build.bat
|
||||
#### **步骤 2:测试运行**
|
||||
|
||||
```bash
|
||||
cd out\win-unpacked
|
||||
cd out\NPQS-9100
|
||||
NPQS9100.exe
|
||||
```
|
||||
|
||||
@@ -200,7 +205,7 @@ NPQS9100.exe
|
||||
|
||||
```powershell
|
||||
cd out
|
||||
Compress-Archive -Path win-unpacked -DestinationPath "NPQS9100-v1.0.0-绿色版.zip" -Force
|
||||
Compress-Archive -Path NPQS-9100 -DestinationPath "NPQS9100-v1.0.0-绿色版.zip" -Force
|
||||
```
|
||||
|
||||
**压缩后大小**:约 350-450 MB
|
||||
@@ -237,7 +242,8 @@ Compress-Archive -Path win-unpacked -DestinationPath "NPQS9100-v1.0.0-绿色版.
|
||||
├─ MySQL 连接:localhost:3307
|
||||
├─ MySQL 密码:njcnpqs ⭐ 自动写入
|
||||
├─ Java 端口:18093
|
||||
├─ 数据路径:C:\NPQS9100_Data\
|
||||
├─ 数据路径:纯英文路径时为 `应用目录\NPQS9100_Data\`
|
||||
├─ 数据路径:中文路径时为 `X:\NPQS9100_Runtime\data\`
|
||||
├─ 创建所有必要目录(logs、template、report、data)
|
||||
↓
|
||||
[80%] 启动 Spring Boot 后端
|
||||
@@ -274,7 +280,7 @@ Compress-Archive -Path win-unpacked -DestinationPath "NPQS9100-v1.0.0-绿色版.
|
||||
↓
|
||||
停止 Spring Boot 进程(通过PID和端口精确清理)
|
||||
↓
|
||||
停止 MySQL 服务(如果是服务模式)
|
||||
停止 MySQL 进程(随应用启动的 mysqld.exe)
|
||||
↓
|
||||
清理资源
|
||||
↓
|
||||
@@ -289,17 +295,19 @@ Compress-Archive -Path win-unpacked -DestinationPath "NPQS9100-v1.0.0-绿色版.
|
||||
|
||||
#### **升级流程**
|
||||
```
|
||||
1. 双击 upgrade.bat(首次自动创建 upgrade/ 目录)
|
||||
2. 将升级包放入 upgrade/ 目录
|
||||
3. 再次运行 upgrade.bat(自动备份 + 升级)
|
||||
4. 重启应用测试
|
||||
1. 先手动导出数据库 SQL 备份
|
||||
2. 双击 upgrade.bat(首次自动创建 upgrade/ 目录)
|
||||
3. 将升级包放入 upgrade/ 目录
|
||||
4. 再次运行 upgrade.bat(自动备份前后端程序文件 + 升级)
|
||||
5. 重启应用测试
|
||||
```
|
||||
|
||||
#### **回滚流程**
|
||||
```
|
||||
1. 双击 rollback.bat
|
||||
2. 自动从 backup/ 恢复旧版本
|
||||
3. 重启应用
|
||||
2. 自动从 backup/ 恢复前后端程序文件
|
||||
3. 如需恢复数据库,手动执行之前导出的 SQL
|
||||
4. 重启应用
|
||||
```
|
||||
|
||||
---
|
||||
@@ -310,23 +318,27 @@ Compress-Archive -Path win-unpacked -DestinationPath "NPQS9100-v1.0.0-绿色版.
|
||||
|
||||
**第 1 步:准备升级包**
|
||||
|
||||
1. 双击 `upgrade.bat`
|
||||
2. 脚本自动创建 `upgrade/` 文件夹并提示
|
||||
3. 将升级文件放入 `upgrade/` 目录:
|
||||
- `dist/` - 前端升级包(可选)
|
||||
1. 先使用 Navicat 或其他工具手动导出数据库 SQL 备份
|
||||
2. 双击 `upgrade.bat`
|
||||
3. 脚本自动创建 `upgrade/` 文件夹并提示
|
||||
4. 将升级文件放入 `upgrade/` 目录:
|
||||
- `app.asar` + `app.asar.unpacked/` - 前端升级包(可选,必须成套)
|
||||
- `entrance.jar` - 后端升级包(可选)
|
||||
4. 再次双击 `upgrade.bat` 开始升级
|
||||
5. 再次双击 `upgrade.bat` 开始升级
|
||||
|
||||
**第 2 步:自动升级**
|
||||
|
||||
脚本会自动:
|
||||
- ✅ 停止 NPQS9100 进程
|
||||
- ✅ **自动备份当前版本到 `backup/` 目录**
|
||||
- ✅ 备份数据库到 `mysql/data_backup/`
|
||||
- ✅ **自动备份当前前后端程序文件到 `backup/` 目录**
|
||||
- ✅ 替换前端文件(如果有)
|
||||
- ✅ 替换后端 JAR(如果有)
|
||||
- ✅ 记录升级日志到 `logs/upgrade.log`
|
||||
|
||||
数据库说明:
|
||||
- ⚠️ 数据库不再由 `upgrade.bat` 自动备份
|
||||
- ⚠️ 升级前请务必手动导出 SQL 备份
|
||||
|
||||
**第 3 步:重启应用**
|
||||
|
||||
升级完成后,双击 `NPQS9100.exe` 启动应用。
|
||||
@@ -340,8 +352,9 @@ Compress-Archive -Path win-unpacked -DestinationPath "NPQS9100-v1.0.0-绿色版.
|
||||
**升级前端**:
|
||||
```batch
|
||||
taskkill /F /IM NPQS9100.exe
|
||||
rmdir /s /q resources\app.asar.unpacked\public\dist
|
||||
xcopy upgrade\dist resources\app.asar.unpacked\public\dist\ /E /I /Y
|
||||
copy /Y upgrade\app.asar resources\app.asar
|
||||
rmdir /s /q resources\app.asar.unpacked
|
||||
xcopy upgrade\app.asar.unpacked resources\app.asar.unpacked\ /E /I /Y
|
||||
```
|
||||
|
||||
**升级后端**:
|
||||
@@ -360,23 +373,23 @@ copy /Y upgrade\entrance.jar resources\extraResources\java\entrance.jar
|
||||
|
||||
脚本会自动:
|
||||
- ✅ 停止 NPQS9100
|
||||
- ✅ 恢复前端(从 `backup/dist/`)
|
||||
- ✅ 恢复前端(从 `backup/app.asar` 和 `backup/app.asar.unpacked/`)
|
||||
- ✅ 恢复后端(从 `backup/entrance.jar`)
|
||||
- ✅ 询问是否恢复数据库(⚠️ 会丢失升级后的数据)
|
||||
- ✅ 数据库由人工恢复,不再由脚本自动处理
|
||||
|
||||
#### **方法 2:手动回滚**
|
||||
|
||||
```batch
|
||||
# 恢复前端
|
||||
rmdir /s /q resources\app.asar.unpacked\public\dist
|
||||
xcopy backup\dist resources\app.asar.unpacked\public\dist\ /E /I /Y
|
||||
copy /Y backup\app.asar resources\app.asar
|
||||
rmdir /s /q resources\app.asar.unpacked
|
||||
xcopy backup\app.asar.unpacked resources\app.asar.unpacked\ /E /I /Y
|
||||
|
||||
# 恢复后端
|
||||
copy /Y backup\entrance.jar resources\extraResources\java\entrance.jar
|
||||
|
||||
# 恢复数据库(⚠️ 谨慎!)
|
||||
rmdir /s /q mysql\data
|
||||
xcopy mysql\data_backup mysql\data\ /E /I /Y
|
||||
# 恢复数据库
|
||||
# 使用 Navicat 或其他工具执行升级前导出的 SQL 备份
|
||||
```
|
||||
|
||||
---
|
||||
@@ -385,9 +398,9 @@ xcopy mysql\data_backup mysql\data\ /E /I /Y
|
||||
|
||||
| 升级类型 | 升级包来源 | 客户放置位置 | 最终替换位置 |
|
||||
|---------|-----------|-------------|-------------|
|
||||
| **前端** | `out/win-unpacked/resources/app.asar` + `app.asar.unpacked/` | `upgrade/app.asar` + `upgrade/app.asar.unpacked/` | `resources/app.asar` + `resources/app.asar.unpacked/` |
|
||||
| **后端** | `out/win-unpacked/resources/extraResources/java/entrance.jar` | `upgrade/entrance.jar` | `resources/extraResources/java/entrance.jar` |
|
||||
| **数据库** | ⚠️ **不升级,自动保留** | - | `mysql/data/` |
|
||||
| **前端** | `out/NPQS-9100/resources/app.asar` + `app.asar.unpacked/` | `upgrade/app.asar` + `upgrade/app.asar.unpacked/` | `resources/app.asar` + `resources/app.asar.unpacked/` |
|
||||
| **后端** | `out/NPQS-9100/resources/extraResources/java/entrance.jar` | `upgrade/entrance.jar` | `resources/extraResources/java/entrance.jar` |
|
||||
| **数据库** | 手动导出的 SQL 备份 | 不放入升级包 | 需要时人工导入 |
|
||||
|
||||
---
|
||||
|
||||
@@ -412,10 +425,11 @@ npm run build-frontend
|
||||
|
||||
# 4️⃣ 打包应用
|
||||
npm run build-w
|
||||
# 输出: out/win-unpacked/
|
||||
# 最终交付目录: out/NPQS-9100/
|
||||
# 原始输出目录: out/win-unpacked/(脚本会自动重命名)
|
||||
|
||||
# 5️⃣ 准备升级包
|
||||
# 从 out/win-unpacked/resources/ 复制以下文件:
|
||||
# 从 out/NPQS-9100/resources/ 复制以下文件:
|
||||
# - app.asar (文件)
|
||||
# - app.asar.unpacked/ (整个文件夹)
|
||||
#
|
||||
@@ -435,7 +449,7 @@ npm run build-w
|
||||
# - app.asar.unpacked/
|
||||
|
||||
# 2️⃣ 复制到升级目录
|
||||
# 将两个文件都放到:win-unpacked/upgrade/
|
||||
# 将两个文件都放到:NPQS-9100/upgrade/
|
||||
# - upgrade/app.asar
|
||||
# - upgrade/app.asar.unpacked/
|
||||
|
||||
@@ -478,7 +492,7 @@ npm run build-w
|
||||
|
||||
# 2️⃣ 复制到升级目录
|
||||
# 将 entrance.jar 复制到:
|
||||
# win-unpacked/upgrade/entrance.jar
|
||||
# NPQS-9100/upgrade/entrance.jar
|
||||
|
||||
# 3️⃣ 运行升级脚本
|
||||
# 双击: upgrade.bat
|
||||
@@ -497,7 +511,7 @@ NPQS9100-升级-v1.1.0.zip
|
||||
└── entrance.jar # 后端升级包
|
||||
|
||||
# 客户侧:复制到
|
||||
win-unpacked/upgrade/
|
||||
NPQS-9100/upgrade/
|
||||
├── app.asar
|
||||
├── app.asar.unpacked/
|
||||
└── entrance.jar
|
||||
@@ -511,13 +525,11 @@ win-unpacked/upgrade/
|
||||
|
||||
### ✅ 打包验证
|
||||
|
||||
- [ ] `out/win-unpacked` 目录存在
|
||||
- [ ] `out/NPQS-9100` 目录存在
|
||||
- [ ] 目录大小约 650-800 MB
|
||||
- [ ] NPQS9100.exe 存在
|
||||
- [ ] NPQS9100-启动器.bat 存在
|
||||
- [ ] upgrade.bat 存在
|
||||
- [ ] rollback.bat 存在
|
||||
- [ ] uninstall-mysql-service.bat 存在
|
||||
- [ ] 使用说明.txt 存在
|
||||
- [ ] README-升级回滚.txt 存在
|
||||
- [ ] mysql/ 目录完整
|
||||
@@ -602,10 +614,12 @@ win-unpacked/upgrade/
|
||||
- [ ] upgrade.bat 首次运行自动创建 upgrade/ 目录
|
||||
- [ ] upgrade.bat 检测到升级文件后正常升级
|
||||
- [ ] 升级前自动备份到 backup/ 目录
|
||||
- [ ] 升级前已手动导出数据库 SQL
|
||||
- [ ] 升级后应用正常运行
|
||||
- [ ] 升级后数据库数据保留
|
||||
- [ ] rollback.bat 能正确回滚前端
|
||||
- [ ] rollback.bat 能正确回滚后端
|
||||
- [ ] 如需恢复数据库,能手动导入 SQL
|
||||
- [ ] 回滚后应用恢复正常
|
||||
|
||||
---
|
||||
@@ -710,7 +724,7 @@ C:\Users\[用户名]\AppData\Roaming\NQPS9100\logs\
|
||||
|
||||
### Q8: 升级后数据丢失?
|
||||
|
||||
**A**: 检查 `mysql/data/` 目录是否完整,如有备份,从 `mysql/data_backup/` 恢复
|
||||
**A**: 检查升级前是否已手动导出数据库 SQL 备份。如需恢复数据库,请使用 Navicat 或其他工具执行该 SQL 备份。
|
||||
|
||||
---
|
||||
|
||||
@@ -728,7 +742,8 @@ C:\Users\[用户名]\AppData\Roaming\NQPS9100\logs\
|
||||
**A**:
|
||||
1. 检查 `backup/` 目录是否有备份文件
|
||||
2. 查看 `backup/version.txt` 确认备份版本
|
||||
3. 手动执行回滚步骤(参考本文档)
|
||||
3. 手动执行前后端回滚步骤(参考本文档)
|
||||
4. 如问题与数据库有关,手动导入升级前导出的 SQL
|
||||
|
||||
---
|
||||
|
||||
@@ -767,27 +782,32 @@ VITE_API_URL=http://192.168.1.100:18092
|
||||
|
||||
---
|
||||
|
||||
### Q14: MySQL 服务如何卸载?
|
||||
### Q14: 现在还需要安装或卸载 MySQL 服务吗?
|
||||
|
||||
**A**:
|
||||
**方法 1:使用卸载脚本(推荐)**
|
||||
```batch
|
||||
双击: uninstall-mysql-service.bat
|
||||
```
|
||||
**A**:
|
||||
|
||||
**方法 2:手动卸载**
|
||||
```batch
|
||||
# 停止服务
|
||||
net stop mysql9100
|
||||
**当前版本不需要。**
|
||||
|
||||
# 删除服务
|
||||
sc delete mysql9100
|
||||
```
|
||||
当前版本已经放弃“首次使用时把 MySQL 安装成 Windows 服务并设置开机自启”的方案,改为**绿色包 + 进程模式**:
|
||||
|
||||
**注意**:
|
||||
- 卸载服务不会删除数据库数据
|
||||
- 数据保存在 `mysql/data/` 目录
|
||||
- 下次启动应用会重新安装服务
|
||||
- 双击 `NPQS9100.exe` 时,应用会自动启动 `mysqld.exe`
|
||||
- 退出应用时,MySQL 进程会自动停止
|
||||
- **无需管理员权限**
|
||||
- **不会注册 Windows 服务**
|
||||
- **不会依赖开机自启**
|
||||
|
||||
**为什么改成这样?**
|
||||
|
||||
- 旧方案需要管理员权限
|
||||
- 用户可能直接双击使用,导致服务安装失败后应用启动失败
|
||||
- 也可能出现授权失败、赋权不完整等问题
|
||||
- 对 C 端用户来说不稳定,因此改为随应用启停的绿色包进程模式
|
||||
|
||||
**数据说明**:
|
||||
|
||||
- 程序目录为纯英文/ASCII路径时,MySQL 数据位于 `应用目录\mysql\data\`
|
||||
- 程序目录包含中文或其他非 ASCII 字符时,MySQL 数据位于 `X:\NPQS9100_Runtime\mysql\data\`
|
||||
- 当前版本不依赖任何 MySQL Windows 服务
|
||||
|
||||
---
|
||||
|
||||
@@ -800,8 +820,7 @@ sc delete mysql9100
|
||||
**开发者文档**(doc/ 目录):
|
||||
- `doc/NPQS-9100绿色包完整指南.md` - 本文档(完整指南)
|
||||
- `doc/打包前检查清单.md` - 逐项检查
|
||||
- `doc/管理员权限说明.md` - 权限问题处理
|
||||
- `doc/MySQL服务化方案说明.md` - MySQL 服务管理
|
||||
- `doc/管理员权限说明.md` - 历史服务模式与当前进程模式说明
|
||||
|
||||
---
|
||||
|
||||
@@ -812,7 +831,7 @@ sc delete mysql9100
|
||||
- `scripts/port-checker.js` - 端口检测工具
|
||||
- `scripts/startup-manager.js` - 启动状态管理
|
||||
- `scripts/config-generator.js` - 配置文件生成
|
||||
- `scripts/mysql-service-manager.js` - MySQL 服务管理器
|
||||
- `scripts/mysql-process-manager.js` - MySQL 进程管理器
|
||||
- `scripts/java-runner.js` - Java 运行器
|
||||
- `scripts/log-window-manager.js` - 日志窗口管理
|
||||
- `electron/preload/lifecycle.js` - 生命周期管理
|
||||
@@ -827,7 +846,7 @@ StartupManager (显示 Loading)
|
||||
↓ 调用
|
||||
PortChecker (检测端口)
|
||||
↓ 调用
|
||||
MySQLServiceManager (管理 MySQL 服务)
|
||||
MySQLProcessManager (管理 MySQL 进程)
|
||||
↓ 调用
|
||||
ConfigGenerator (生成配置)
|
||||
↓ 调用
|
||||
@@ -840,14 +859,14 @@ JavaRunner (启动 Spring Boot)
|
||||
|
||||
### 用户体验提升
|
||||
- ✅ **任务栏只显示1个图标** - Loading 窗口不在任务栏显示
|
||||
- ✅ **纯绿色版** - 只生成 win-unpacked 目录,压缩成 zip 即可发布
|
||||
- ✅ **纯绿色版** - 最终交付目录统一为 `out/NPQS-9100`,压缩成 zip 即可发布
|
||||
- ✅ **一键解压即用** - 用户解压后双击即可运行
|
||||
- ✅ **热更新机制** - 前后端可独立升级,支持一键回滚
|
||||
|
||||
### 技术改进
|
||||
- ✅ **MySQL 密码自动配置** - 配置生成器自动写入密码
|
||||
- ✅ **权限自动授权** - 支持 localhost 和 127.0.0.1 访问
|
||||
- ✅ **MySQL 服务化** - 使用 Windows 服务管理,开机自启
|
||||
- ✅ **MySQL 进程模式** - 随应用启动和退出自动管理,无需管理员权限
|
||||
- ✅ **窗口管理优化** - 使用 destroy() 确保窗口完全释放
|
||||
- ✅ **精确进程清理** - 不会误杀其他 Java 进程
|
||||
- ✅ **自动备份机制** - 升级前自动备份,支持回滚
|
||||
|
||||
@@ -96,7 +96,7 @@ gen.generateConfig({ mysqlPort: 3307 }).then(console.log);
|
||||
|
||||
---
|
||||
|
||||
#### `scripts/mysql-service-manager.js` - MySQL 进程管理器(绿色包 - 进程模式)
|
||||
#### `scripts/mysql-process-manager.js` - MySQL 进程管理器(绿色包 - 进程模式)
|
||||
**作用**:以进程模式管理 MySQL,**无需管理员权限**,随应用启动/关闭
|
||||
**核心方法**:
|
||||
```javascript
|
||||
@@ -141,8 +141,8 @@ this.mysqlProcess = spawn(mysqldPath, [
|
||||
**调试方法**:
|
||||
```javascript
|
||||
// 单独测试
|
||||
const MySQLServiceManager = require('./scripts/mysql-service-manager');
|
||||
const mysql = new MySQLServiceManager();
|
||||
const MySQLProcessManager = require('./scripts/mysql-process-manager');
|
||||
const mysql = new MySQLProcessManager();
|
||||
mysql.ensureServiceRunning(
|
||||
(startPort, maxAttempts) => startPort, // Mock port checker
|
||||
(port, timeout) => Promise.resolve(true)
|
||||
@@ -363,7 +363,7 @@ await this.checkEnvironment(); // 你的自定义方法
|
||||
|
||||
### 场景4:修改MySQL进程配置(绿色包 - 进程模式)
|
||||
|
||||
**文件**:`scripts/mysql-service-manager.js`
|
||||
**文件**:`scripts/mysql-process-manager.js`
|
||||
|
||||
**修改 my.ini 配置**(推荐方式):
|
||||
```javascript
|
||||
@@ -502,7 +502,7 @@ electron/preload/lifecycle.js (启动入口)
|
||||
│
|
||||
├── scripts/port-checker.js (检测端口)
|
||||
│
|
||||
├── scripts/mysql-service-manager.js (MySQL服务管理)
|
||||
├── scripts/mysql-process-manager.js (MySQL进程管理)
|
||||
│
|
||||
├── scripts/config-generator.js (生成配置)
|
||||
│ ↓ 读取
|
||||
@@ -555,7 +555,7 @@ electron/preload/lifecycle.js (启动入口)
|
||||
|------|------|---------|
|
||||
| 端口检测 | `scripts/port-checker.js` | 全文 |
|
||||
| 启动管理 | `scripts/startup-manager.js` | 全文 |
|
||||
| MySQL服务管理 | `scripts/mysql-service-manager.js` | 核心方法 ensureServiceRunning |
|
||||
| MySQL进程管理 | `scripts/mysql-process-manager.js` | 核心方法 ensureServiceRunning |
|
||||
| Java启动 | `scripts/java-runner.js` | 152-198 |
|
||||
| 配置生成 | `scripts/config-generator.js` | 38-83 |
|
||||
| 启动流程 | `electron/preload/lifecycle.js` | 27-159 |
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
- [ ] `scripts/port-checker.js` 存在
|
||||
- [ ] `scripts/startup-manager.js` 存在
|
||||
- [ ] `scripts/config-generator.js` 存在
|
||||
- [ ] `scripts/mysql-service-manager.js` 存在
|
||||
- [ ] `scripts/mysql-process-manager.js` 存在
|
||||
- [ ] `scripts/log-window-manager.js` 存在
|
||||
- [ ] `scripts/java-runner.js` 存在
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
### MySQL 进程(绿色包 - 进程模式)
|
||||
- [ ] MySQL 进程能正常启动(通过 spawn mysqld.exe)
|
||||
- [ ] ~~MySQL 服务配置为开机自启~~(已废弃,使用进程模式)
|
||||
- [ ] 确认当前版本为进程模式,不依赖 MySQL Windows 服务
|
||||
- [ ] MySQL 数据库首次启动时自动初始化
|
||||
- [ ] MySQL 连接密码正确(njcnpqs)
|
||||
- [ ] MySQL 进程随应用退出而自动关闭
|
||||
@@ -98,9 +98,9 @@ npm run build-w # 打包 Windows 便携版
|
||||
### 基础测试(绿色包 - 进程模式)
|
||||
- [ ] **无需管理员权限**,普通用户双击 exe 能正常启动 ✅
|
||||
- [ ] Loading 界面正常显示,显示启动步骤
|
||||
- [ ] ~~MySQL 服务自动安装并启动~~(已废弃)
|
||||
- [ ] 不会尝试安装 MySQL Windows 服务
|
||||
- [ ] **MySQL 进程自动启动**(任务管理器可见 mysqld.exe)
|
||||
- [ ] ~~MySQL 服务配置为开机自启~~(已废弃,进程模式随应用启动)
|
||||
- [ ] MySQL 随应用启动,退出后自动停止,不依赖开机自启
|
||||
- [ ] Spring Boot 自动启动(任务管理器可见 java.exe)
|
||||
- [ ] 主界面正常显示
|
||||
- [ ] 应用退出后,MySQL 和 Java 进程自动关闭 ✅
|
||||
@@ -147,8 +147,8 @@ C:\Users\[用户名]\AppData\Roaming\NQPS9100\logs\
|
||||
|
||||
### 检查内容
|
||||
- [ ] 端口检测日志正确
|
||||
- [ ] MySQL 服务安装/启动日志正常
|
||||
- [ ] 日志中显示"服务已配置为开机自启"
|
||||
- [ ] MySQL 进程启动日志正常
|
||||
- [ ] 日志中显示 mysqld.exe 启动与退出清理信息
|
||||
- [ ] Spring Boot 启动日志正常
|
||||
- [ ] 无严重错误
|
||||
|
||||
@@ -185,7 +185,6 @@ C:\Users\[用户名]\AppData\Roaming\NQPS9100\logs\
|
||||
- 检查端口是否被占用(应自动切换到其他端口)
|
||||
- 查看应用日志:`%APPDATA%/NQPS9100/logs/9100.log`
|
||||
- 查看 MySQL 错误日志:`mysql/data/*.err`
|
||||
- ~~使用 sc query mysql9100 检查服务状态~~(已废弃,不再使用服务)
|
||||
- 检查是否有残留的 mysqld.exe 进程(任务管理器)
|
||||
|
||||
### Spring Boot 启动失败
|
||||
|
||||
@@ -1,58 +1,27 @@
|
||||
# 管理员权限说明
|
||||
|
||||
> ⚠️ **文档已过期** - 本文档描述的是旧版服务模式,自 2025-12-01 起已改用**进程模式(绿色包)**。
|
||||
>
|
||||
> 🎉 **新版本不再需要管理员权限!**
|
||||
>
|
||||
> 参考最新文档:`MySQL进程模式改造方案.md` 和 `NPQS-9100绿色包完整指南.md`
|
||||
当前版本采用**绿色包 + MySQL 进程模式**,**不需要管理员权限**。
|
||||
|
||||
---
|
||||
## 当前结论
|
||||
|
||||
## ~~🔐 为什么需要管理员权限?~~(已废弃)
|
||||
1. 双击 `NPQS9100.exe` 即可启动
|
||||
2. 无需管理员权限
|
||||
3. 无 UAC 弹窗
|
||||
4. 解压即用,完全绿色
|
||||
5. 仅在应用运行期间启动 MySQL
|
||||
|
||||
~~NPQS9100 需要管理 MySQL Windows 服务,执行以下操作:~~
|
||||
- ~~✅ 安装 MySQL 服务 (`mysql9100`)~~
|
||||
- ~~✅ 启动/停止服务~~
|
||||
- ~~✅ 删除服务(重新安装时)~~
|
||||
## 说明
|
||||
|
||||
~~这些操作都需要 **Windows 管理员权限**。~~
|
||||
- MySQL 和 Spring Boot 会在应用启动时自动启动
|
||||
- 退出应用时自动清理
|
||||
- 不注册 Windows 服务
|
||||
- 不依赖开机自启
|
||||
|
||||
**现状**:已改用进程模式,直接启动 `mysqld.exe` 进程,无需注册 Windows 服务,**完全不需要管理员权限**。
|
||||
## 为什么这样设计
|
||||
|
||||
## 🎉 新版启动方式(进程模式)
|
||||
|
||||
**当前版本启动非常简单**:
|
||||
|
||||
1. ✅ 双击 `NPQS9100.exe` 即可启动
|
||||
2. ✅ 无需管理员权限
|
||||
3. ✅ 无 UAC 弹窗
|
||||
4. ✅ 解压即用,完全绿色
|
||||
|
||||
**MySQL 和 Spring Boot 会在应用启动时自动启动,退出时自动清理。**
|
||||
|
||||
---
|
||||
|
||||
## ~~以下内容已废弃(保留作为历史参考)~~
|
||||
|
||||
<details>
|
||||
<summary>点击展开查看旧版(服务模式)说明</summary>
|
||||
|
||||
### ~~两种启动方式(已废弃)~~
|
||||
|
||||
#### ~~方式 1:使用启动器(推荐)~~
|
||||
|
||||
~~打包后的目录中有一个 `NPQS9100-启动器.bat` 文件。~~
|
||||
|
||||
#### ~~方式 2:手动以管理员身份运行~~
|
||||
|
||||
~~右键点击 `NPQS9100.exe` 选择"以管理员身份运行"~~
|
||||
|
||||
### ~~常见问题(已废弃)~~
|
||||
|
||||
~~Q: MySQL 服务安装后还需要管理员权限吗?~~
|
||||
A: **新版本不使用 Windows 服务,因此完全不需要管理员权限。**
|
||||
|
||||
</details>
|
||||
- 需要管理员权限的启动方式对 C 端用户不稳定
|
||||
- 用户直接双击使用时,提权、授权或服务安装失败会导致启动失败
|
||||
- 因此当前版本统一采用无需授权的绿色包进程模式
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -45,11 +45,11 @@ function getScriptsPath(scriptName) {
|
||||
}
|
||||
|
||||
// 延迟加载 scripts
|
||||
let MySQLServiceManager, JavaRunner, ConfigGenerator, PortChecker, StartupManager, LogWindowManager;
|
||||
let MySQLProcessManager, JavaRunner, ConfigGenerator, PortChecker, StartupManager, LogWindowManager;
|
||||
|
||||
function loadScripts() {
|
||||
if (!MySQLServiceManager) {
|
||||
MySQLServiceManager = require(getScriptsPath('mysql-service-manager'));
|
||||
if (!MySQLProcessManager) {
|
||||
MySQLProcessManager = require(getScriptsPath('mysql-process-manager'));
|
||||
JavaRunner = require(getScriptsPath('java-runner'));
|
||||
ConfigGenerator = require(getScriptsPath('config-generator'));
|
||||
PortChecker = require(getScriptsPath('port-checker'));
|
||||
@@ -60,7 +60,7 @@ function loadScripts() {
|
||||
|
||||
class Lifecycle {
|
||||
constructor() {
|
||||
this.mysqlServiceManager = null;
|
||||
this.mysqlProcessManager = null;
|
||||
this.javaRunner = null;
|
||||
this.startupManager = null;
|
||||
this.logWindowManager = null;
|
||||
@@ -94,26 +94,26 @@ class Lifecycle {
|
||||
this.startupManager.updateProgress('init');
|
||||
await this.sleep(500);
|
||||
|
||||
// 步骤2-4: 确保 MySQL 服务运行
|
||||
// 步骤2-4: 确保 MySQL 进程运行
|
||||
this.logWindowManager.addLog('system', '▶ 步骤4: 检查 MySQL 配置...');
|
||||
logger.info('[lifecycle] MySQL config check - enable:', config.mysql?.enable, 'autoStart:', config.mysql?.autoStart);
|
||||
|
||||
if (config.mysql && config.mysql.enable && config.mysql.autoStart) {
|
||||
this.startupManager.updateProgress('check-mysql-port');
|
||||
this.logWindowManager.addLog('system', '▶ 步骤5: 启动 MySQL 服务管理器...');
|
||||
this.logWindowManager.addLog('system', '▶ 步骤5: 启动 MySQL 进程管理器...');
|
||||
|
||||
this.mysqlServiceManager = new MySQLServiceManager(this.logWindowManager);
|
||||
this.logWindowManager.addLog('system', '正在检查 MySQL 服务状态...');
|
||||
this.mysqlProcessManager = new MySQLProcessManager(this.logWindowManager);
|
||||
this.logWindowManager.addLog('system', '正在检查 MySQL 进程状态...');
|
||||
|
||||
try {
|
||||
// 使用服务管理器确保MySQL服务运行
|
||||
this.logWindowManager.addLog('system', '▶ 步骤6: 确保 MySQL 服务运行中...');
|
||||
this.mysqlPort = await this.mysqlServiceManager.ensureServiceRunning(
|
||||
// 使用进程管理器确保 MySQL 进程运行
|
||||
this.logWindowManager.addLog('system', '▶ 步骤6: 确保 MySQL 进程运行中...');
|
||||
this.mysqlPort = await this.mysqlProcessManager.ensureServiceRunning(
|
||||
PortChecker.findAvailablePort.bind(PortChecker),
|
||||
PortChecker.waitForPort.bind(PortChecker)
|
||||
);
|
||||
|
||||
logger.info(`[lifecycle] MySQL service running on port: ${this.mysqlPort}`);
|
||||
logger.info(`[lifecycle] MySQL process running on port: ${this.mysqlPort}`);
|
||||
this.logWindowManager.addLog('success', `✓ MySQL 服务已就绪,端口: ${this.mysqlPort}`);
|
||||
this.startupManager.updateProgress('wait-mysql', { mysqlPort: this.mysqlPort });
|
||||
await this.sleep(500);
|
||||
@@ -201,7 +201,7 @@ class Lifecycle {
|
||||
dataPath: dataPath
|
||||
});
|
||||
|
||||
await this.startSpringBoot(configPath);
|
||||
await this.startSpringBoot(configPath, dataPath);
|
||||
|
||||
// 步骤8: 等待 Spring Boot 就绪
|
||||
this.logWindowManager.addLog('system', '▶ 步骤11: 等待 Spring Boot 就绪(最多60秒)...');
|
||||
@@ -376,14 +376,14 @@ class Lifecycle {
|
||||
}
|
||||
|
||||
// 停止 MySQL 进程(进程模式)
|
||||
if (this.mysqlServiceManager) {
|
||||
if (this.mysqlProcessManager) {
|
||||
try {
|
||||
logger.info('[lifecycle] Stopping MySQL process...');
|
||||
if (this.logWindowManager && this.logWindowManager.logWindow && !this.logWindowManager.logWindow.isDestroyed()) {
|
||||
this.logWindowManager.addLog('system', '正在停止 MySQL...');
|
||||
}
|
||||
|
||||
await this.mysqlServiceManager.stopMySQLProcess();
|
||||
await this.mysqlProcessManager.stopMySQLProcess();
|
||||
|
||||
logger.info('[lifecycle] MySQL process stopped');
|
||||
if (this.logWindowManager && this.logWindowManager.logWindow && !this.logWindowManager.logWindow.isDestroyed()) {
|
||||
@@ -419,7 +419,7 @@ class Lifecycle {
|
||||
/**
|
||||
* 启动 Spring Boot 应用
|
||||
*/
|
||||
async startSpringBoot(configPath) {
|
||||
async startSpringBoot(configPath, dataPath) {
|
||||
try {
|
||||
logger.info('[lifecycle] Starting Spring Boot application...');
|
||||
this.logWindowManager.addLog('java', '正在启动 Spring Boot 应用...');
|
||||
@@ -434,12 +434,6 @@ class Lifecycle {
|
||||
? path.join(__dirname, '..', 'build', 'extraResources', 'java', 'entrance.jar')
|
||||
: path.join(process.resourcesPath, 'extraResources', 'java', 'entrance.jar');
|
||||
|
||||
// 获取日志路径(与 config-generator.js 中的 dataPath 保持一致)
|
||||
const isDev2 = !process.resourcesPath;
|
||||
const baseDir = isDev2
|
||||
? path.join(__dirname, '..', '..')
|
||||
: path.dirname(process.resourcesPath);
|
||||
const dataPath = path.join(baseDir, 'NPQS9100_Data');
|
||||
const logPath = path.join(dataPath, 'logs');
|
||||
|
||||
const javaProcess = this.javaRunner.runSpringBoot(jarPath, configPath, {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { resolveRuntimeStrategy } = require('./path-utils');
|
||||
|
||||
/**
|
||||
* 配置文件生成器
|
||||
@@ -8,11 +9,12 @@ const path = require('path');
|
||||
class ConfigGenerator {
|
||||
constructor() {
|
||||
// 开发环境:项目根目录
|
||||
// 打包后:应用根目录(win-unpacked)
|
||||
// 打包后:应用根目录(最终交付目录)
|
||||
const isDev = !process.resourcesPath;
|
||||
const baseDir = isDev
|
||||
? path.join(__dirname, '..')
|
||||
: path.dirname(process.resourcesPath);
|
||||
const pathStrategy = resolveRuntimeStrategy(baseDir);
|
||||
|
||||
// 开发环境:build/extraResources/java
|
||||
// 打包后:resources/extraResources/java
|
||||
@@ -21,9 +23,12 @@ class ConfigGenerator {
|
||||
: path.join(process.resourcesPath, 'extraResources', 'java');
|
||||
this.templatePath = path.join(this.javaPath, 'application.yml.template');
|
||||
this.configPath = path.join(this.javaPath, 'application.yml');
|
||||
this.pathStrategy = pathStrategy;
|
||||
|
||||
// 数据目录(使用应用所在盘符的根目录下的data文件夹)
|
||||
this.dataPath = this.getDataPath(baseDir);
|
||||
// 数据目录:
|
||||
// - 安全路径:使用应用目录内的 NPQS9100_Data
|
||||
// - 非 ASCII 路径:切到英文安全运行根目录下,避免在盘符根目录创建多个文件夹
|
||||
this.dataPath = this.getDataPath(baseDir, pathStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,8 +36,11 @@ class ConfigGenerator {
|
||||
* @param {string} baseDir 应用基础目录
|
||||
* @returns {string} 数据目录路径
|
||||
*/
|
||||
getDataPath(baseDir) {
|
||||
// 数据目录设置在应用目录内的 NPQS9100_Data 文件夹
|
||||
getDataPath(baseDir, pathStrategy = resolveRuntimeStrategy(baseDir)) {
|
||||
if (pathStrategy.usesSafePaths) {
|
||||
return path.join(pathStrategy.safeRuntimeRoot, 'data');
|
||||
}
|
||||
|
||||
return path.join(baseDir, 'NPQS9100_Data');
|
||||
}
|
||||
|
||||
@@ -85,6 +93,7 @@ class ConfigGenerator {
|
||||
this.createDirectories();
|
||||
|
||||
console.log('[ConfigGenerator] Configuration file generated successfully');
|
||||
console.log('[ConfigGenerator] Path mode:', this.pathStrategy.usesSafePaths ? 'safe-data-root' : 'app-local-data');
|
||||
console.log('[ConfigGenerator] Data path:', this.dataPath);
|
||||
console.log('[ConfigGenerator] MySQL port:', options.mysqlPort || 3306);
|
||||
console.log('[ConfigGenerator] MySQL password:', options.mysqlPassword || 'njcnpqs');
|
||||
|
||||
@@ -9,7 +9,7 @@ class JavaRunner {
|
||||
constructor() {
|
||||
// 在开发与打包后均可解析到应用根目录下的 jre 目录
|
||||
// 开发环境:项目根目录
|
||||
// 打包后:应用根目录(win-unpacked)
|
||||
// 打包后:应用根目录(最终交付目录)
|
||||
const isDev = !process.resourcesPath;
|
||||
const baseDir = isDev
|
||||
? path.join(__dirname, '..')
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
const { spawn, exec } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { resolveRuntimeStrategy } = require('./path-utils');
|
||||
|
||||
/**
|
||||
* MySQL 进程管理器
|
||||
* 使用进程模式启动 MySQL,无需管理员权限
|
||||
*/
|
||||
class MySQLServiceManager {
|
||||
class MySQLProcessManager {
|
||||
constructor(logWindowManager = null) {
|
||||
const isDev = !process.resourcesPath;
|
||||
const baseDir = isDev
|
||||
? path.join(__dirname, '..')
|
||||
: path.dirname(process.resourcesPath);
|
||||
const pathStrategy = resolveRuntimeStrategy(baseDir);
|
||||
|
||||
this.mysqlPath = path.join(baseDir, 'mysql');
|
||||
this.baseDir = baseDir;
|
||||
this.pathStrategy = pathStrategy;
|
||||
this.sourceMysqlPath = path.join(baseDir, 'mysql');
|
||||
this.mysqlPath = pathStrategy.usesSafePaths
|
||||
? path.join(pathStrategy.safeRuntimeRoot, 'mysql')
|
||||
: this.sourceMysqlPath;
|
||||
this.binPath = path.join(this.mysqlPath, 'bin');
|
||||
this.dataPath = path.join(this.mysqlPath, 'data');
|
||||
this.configFile = path.join(this.mysqlPath, 'my.ini');
|
||||
@@ -37,6 +44,106 @@ class MySQLServiceManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归复制目录。
|
||||
* 仅当文件不存在、大小变化或源文件时间更新时才覆盖,避免每次启动全量重写。
|
||||
*/
|
||||
copyDirectorySync(sourceDir, targetDir, options = {}) {
|
||||
const {
|
||||
excludeTopLevelDirs = new Set(),
|
||||
excludeFileNames = new Set(),
|
||||
excludeFileExtensions = new Set()
|
||||
} = options;
|
||||
|
||||
if (!fs.existsSync(sourceDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
fs.mkdirSync(targetDir, { recursive: true });
|
||||
}
|
||||
|
||||
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
||||
|
||||
entries.forEach((entry) => {
|
||||
const sourcePath = path.join(sourceDir, entry.name);
|
||||
const targetPath = path.join(targetDir, entry.name);
|
||||
const isTopLevelEntry = sourceDir === this.sourceMysqlPath;
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
if (isTopLevelEntry && excludeTopLevelDirs.has(entry.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.copyDirectorySync(sourcePath, targetPath, options);
|
||||
return;
|
||||
}
|
||||
|
||||
const extension = path.extname(entry.name).toLowerCase();
|
||||
if (excludeFileNames.has(entry.name) || excludeFileExtensions.has(extension)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let shouldCopy = !fs.existsSync(targetPath);
|
||||
if (!shouldCopy) {
|
||||
const sourceStat = fs.statSync(sourcePath);
|
||||
const targetStat = fs.statSync(targetPath);
|
||||
shouldCopy = sourceStat.size !== targetStat.size || sourceStat.mtimeMs > targetStat.mtimeMs;
|
||||
}
|
||||
|
||||
if (shouldCopy) {
|
||||
fs.copyFileSync(sourcePath, targetPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 非 ASCII 路径下,将 MySQL 运行环境同步到英文安全目录。
|
||||
*/
|
||||
ensureRuntimeEnvironment() {
|
||||
if (!this.pathStrategy.usesSafePaths) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log('system', '检测到应用路径包含非 ASCII 字符,启用 MySQL 英文安全运行目录');
|
||||
this.log('system', `MySQL 源目录: ${this.sourceMysqlPath}`);
|
||||
this.log('system', `MySQL 运行目录: ${this.mysqlPath}`);
|
||||
this.log('system', `MySQL 数据目录: ${this.dataPath}`);
|
||||
|
||||
fs.mkdirSync(this.mysqlPath, { recursive: true });
|
||||
fs.mkdirSync(path.dirname(this.dataPath), { recursive: true });
|
||||
|
||||
this.copyDirectorySync(this.sourceMysqlPath, this.mysqlPath, {
|
||||
excludeTopLevelDirs: new Set(['data', 'data_backup']),
|
||||
excludeFileNames: new Set(['my.ini', 'mysql_error.log', 'mysql_slow.log']),
|
||||
excludeFileExtensions: new Set(['.pid', '.err'])
|
||||
});
|
||||
|
||||
this.ensureSeedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 首次进入英文安全路径模式时,将打包内预置数据库复制到安全数据目录。
|
||||
*/
|
||||
ensureSeedData() {
|
||||
const hasTargetData = fs.existsSync(this.dataPath) && fs.readdirSync(this.dataPath).length > 0;
|
||||
if (hasTargetData) {
|
||||
this.log('system', '检测到已有 MySQL 安全数据目录,直接复用');
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceDataPath = path.join(this.sourceMysqlPath, 'data');
|
||||
if (fs.existsSync(sourceDataPath) && fs.readdirSync(sourceDataPath).length > 0) {
|
||||
this.log('system', '首次启动,正在复制预置 MySQL 数据到安全数据目录...');
|
||||
this.copyDirectorySync(sourceDataPath, this.dataPath);
|
||||
this.log('success', '预置 MySQL 数据复制完成');
|
||||
return;
|
||||
}
|
||||
|
||||
fs.mkdirSync(this.dataPath, { recursive: true });
|
||||
this.log('warn', '未找到预置 MySQL 数据目录,后续将按空库初始化');
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行命令并返回Promise
|
||||
*/
|
||||
@@ -352,7 +459,10 @@ default-character-set=utf8mb4
|
||||
* @returns {Promise<number>} 返回 MySQL 运行的端口
|
||||
*/
|
||||
async ensureServiceRunning(findAvailablePort, waitForPort) {
|
||||
this.ensureRuntimeEnvironment();
|
||||
|
||||
this.log('system', '开始 MySQL 进程检查流程(进程模式)');
|
||||
this.log('system', `路径策略: ${this.pathStrategy.usesSafePaths ? '英文安全路径模式' : '应用目录直启模式'}`);
|
||||
this.log('system', `MySQL 路径: ${this.mysqlPath}`);
|
||||
this.log('system', `配置文件: ${this.configFile}`);
|
||||
|
||||
@@ -401,4 +511,4 @@ default-character-set=utf8mb4
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MySQLServiceManager;
|
||||
module.exports = MySQLProcessManager;
|
||||
41
scripts/path-utils.js
Normal file
41
scripts/path-utils.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* 判断路径中是否包含非 ASCII 字符。
|
||||
* 这里不只判断中文,其他非 ASCII 字符同样视为不安全路径。
|
||||
*/
|
||||
function hasNonAscii(targetPath = '') {
|
||||
return /[^\x00-\x7F]/.test(targetPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前路径所在盘符根目录,例如 D:\
|
||||
*/
|
||||
function getDriveRoot(targetPath = '') {
|
||||
const resolvedPath = path.resolve(targetPath || process.cwd());
|
||||
return path.parse(resolvedPath).root;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析运行期路径策略。
|
||||
* - 安全路径:继续直接使用应用目录
|
||||
* - 非 ASCII 路径:切到英文安全路径
|
||||
*/
|
||||
function resolveRuntimeStrategy(baseDir) {
|
||||
const normalizedBaseDir = path.resolve(baseDir);
|
||||
const driveRoot = getDriveRoot(normalizedBaseDir);
|
||||
const usesSafePaths = hasNonAscii(normalizedBaseDir);
|
||||
|
||||
return {
|
||||
baseDir: normalizedBaseDir,
|
||||
driveRoot,
|
||||
usesSafePaths,
|
||||
safeRuntimeRoot: path.join(driveRoot, 'NPQS9100_Runtime')
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hasNonAscii,
|
||||
getDriveRoot,
|
||||
resolveRuntimeStrategy
|
||||
};
|
||||
Reference in New Issue
Block a user