feat(electron):优化应用启动和健康检查逻辑
- 修改 Spring Boot 配置启用懒加载初始化 - 优化主进程窗口打开逻辑,增加销毁状态检查 - 简化数据迁移函数中的条件判断 - 添加 JVM 参数 UseSerialGC优化内存使用- 移除 Spring 进程的标准输出和错误流监听- 改进健康检查机制,使用版本接口确认服务就绪 - 调整启动超时时间并优化重试间隔 - 延迟更新检查时机以提升启动速度
This commit is contained in:
@@ -20,31 +20,32 @@ let downloadedJarPath: string | null = null;
|
|||||||
let isQuitting = false;
|
let isQuitting = false;
|
||||||
let currentDownloadAbortController: AbortController | null = null;
|
let currentDownloadAbortController: AbortController | null = null;
|
||||||
function openAppIfNotOpened() {
|
function openAppIfNotOpened() {
|
||||||
if (appOpened) return;
|
if (appOpened || !mainWindow || mainWindow.isDestroyed()) {
|
||||||
appOpened = true;
|
!appOpened && setTimeout(openAppIfNotOpened, 50);
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
return;
|
||||||
isDev
|
|
||||||
? mainWindow.loadURL(`http://localhost:${process.argv[2] || 8083}`)
|
|
||||||
: mainWindow.loadFile(join(__dirname, '../renderer/index.html'));
|
|
||||||
|
|
||||||
mainWindow.webContents.once('did-finish-load', () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
||||||
const config = loadConfig();
|
|
||||||
const shouldMinimize = config.launchMinimized || false;
|
|
||||||
if (!shouldMinimize) {
|
|
||||||
mainWindow.show();
|
|
||||||
mainWindow.focus();
|
|
||||||
}
|
|
||||||
if (isDev) mainWindow.webContents.openDevTools();
|
|
||||||
}
|
|
||||||
if (splashWindow && !splashWindow.isDestroyed()) {
|
|
||||||
splashWindow.close();
|
|
||||||
splashWindow = null;
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appOpened = true;
|
||||||
|
isDev
|
||||||
|
? mainWindow.loadURL(`http://localhost:${process.argv[2] || 8083}`)
|
||||||
|
: mainWindow.loadFile(join(__dirname, '../renderer/index.html'));
|
||||||
|
|
||||||
|
mainWindow.webContents.once('did-finish-load', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
|
const shouldMinimize = loadConfig().launchMinimized || false;
|
||||||
|
if (!shouldMinimize) {
|
||||||
|
mainWindow.show();
|
||||||
|
mainWindow.focus();
|
||||||
|
}
|
||||||
|
if (isDev) mainWindow.webContents.openDevTools();
|
||||||
|
}
|
||||||
|
if (splashWindow && !splashWindow.isDestroyed()) {
|
||||||
|
splashWindow.close();
|
||||||
|
splashWindow = null;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通用资源路径获取函数
|
// 通用资源路径获取函数
|
||||||
@@ -127,21 +128,15 @@ function saveConfig(config: AppConfig) {
|
|||||||
|
|
||||||
function migrateDataFromPublic(): void {
|
function migrateDataFromPublic(): void {
|
||||||
if (!isDev) return;
|
if (!isDev) return;
|
||||||
|
|
||||||
const oldDataPath = join(__dirname, '../../public/data');
|
const oldDataPath = join(__dirname, '../../public/data');
|
||||||
if (!existsSync(oldDataPath)) return;
|
if (!existsSync(oldDataPath)) return;
|
||||||
|
|
||||||
const newDataPath = getDataDirectoryPath();
|
const newDataPath = getDataDirectoryPath();
|
||||||
try {
|
try {
|
||||||
readdirSync(oldDataPath).forEach(file => {
|
readdirSync(oldDataPath).forEach(file => {
|
||||||
const destFile = join(newDataPath, file);
|
const destFile = join(newDataPath, file);
|
||||||
if (!existsSync(destFile)) {
|
!existsSync(destFile) && copyFileSync(join(oldDataPath, file), destFile);
|
||||||
copyFileSync(join(oldDataPath, file), destFile);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch {}
|
||||||
console.log('数据迁移失败,使用默认配置');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -160,6 +155,7 @@ function startSpringBoot() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const springArgs = [
|
const springArgs = [
|
||||||
|
'-XX:+UseSerialGC',
|
||||||
'-jar', jarPath,
|
'-jar', jarPath,
|
||||||
`--spring.datasource.url=jdbc:sqlite:${dataDir}/erp-cache.db`,
|
`--spring.datasource.url=jdbc:sqlite:${dataDir}/erp-cache.db`,
|
||||||
`--server.port=8081`,
|
`--server.port=8081`,
|
||||||
@@ -169,54 +165,46 @@ function startSpringBoot() {
|
|||||||
springProcess = spawn(javaPath, springArgs, {
|
springProcess = spawn(javaPath, springArgs, {
|
||||||
cwd: dataDir,
|
cwd: dataDir,
|
||||||
detached: false,
|
detached: false,
|
||||||
stdio: ['ignore', 'pipe', 'pipe']
|
stdio: 'ignore'
|
||||||
});
|
});
|
||||||
|
|
||||||
let startupCompleted = false;
|
let startupCompleted = false;
|
||||||
|
|
||||||
springProcess.stdout?.on('data', (data) => {
|
springProcess.on('close', () => mainWindow ? mainWindow.close() : app.quit());
|
||||||
console.log('[Spring Boot]', data.toString().trim());
|
|
||||||
});
|
|
||||||
|
|
||||||
springProcess.stderr?.on('data', (data) => {
|
|
||||||
console.log('[Spring Boot]', data.toString().trim());
|
|
||||||
});
|
|
||||||
|
|
||||||
springProcess.on('close', (code) => {
|
|
||||||
mainWindow ? mainWindow.close() : app.quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
springProcess.on('error', (error) => {
|
springProcess.on('error', (error) => {
|
||||||
let errorMessage = '启动 Java 应用失败';
|
dialog.showErrorBox('启动失败', error.message.includes('ENOENT')
|
||||||
if (error.message.includes('ENOENT')) {
|
? '找不到 Java 运行环境'
|
||||||
errorMessage = '找不到 Java 运行环境\n请确保应用包含 JRE 或系统已安装 Java';
|
: '启动 Java 应用失败');
|
||||||
}
|
|
||||||
dialog.showErrorBox('启动失败', errorMessage);
|
|
||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
const checkHealth = () => {
|
const checkHealth = () => {
|
||||||
if (startupCompleted) return;
|
if (startupCompleted) return;
|
||||||
|
http.get('http://127.0.0.1:8081/api/system/version', (res) => {
|
||||||
http.get('http://127.0.0.1:8081', (res) => {
|
if (res.statusCode !== 200) {
|
||||||
if (!startupCompleted) {
|
setTimeout(checkHealth, 100);
|
||||||
startupCompleted = true;
|
return;
|
||||||
console.log('[Spring Boot] 服务已就绪');
|
|
||||||
openAppIfNotOpened();
|
|
||||||
}
|
}
|
||||||
}).on('error', () => {
|
let data = '';
|
||||||
setTimeout(checkHealth, 200);
|
res.on('data', chunk => data += chunk);
|
||||||
});
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(data);
|
||||||
|
if (json.success && json.currentVersion) {
|
||||||
|
startupCompleted = true;
|
||||||
|
openAppIfNotOpened();
|
||||||
|
} else {
|
||||||
|
setTimeout(checkHealth, 100);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setTimeout(checkHealth, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).on('error', () => setTimeout(checkHealth, 100));
|
||||||
};
|
};
|
||||||
|
|
||||||
setTimeout(checkHealth, 1000);
|
setTimeout(checkHealth, 100);
|
||||||
|
setTimeout(() => !startupCompleted && openAppIfNotOpened(), 60000);
|
||||||
setTimeout(() => {
|
|
||||||
if (!startupCompleted) {
|
|
||||||
console.log('[Spring Boot] 启动超时,强制打开窗口');
|
|
||||||
openAppIfNotOpened();
|
|
||||||
}
|
|
||||||
}, 15000);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dialog.showErrorBox('启动异常', `无法启动应用: ${error}`);
|
dialog.showErrorBox('启动异常', `无法启动应用: ${error}`);
|
||||||
app.quit();
|
app.quit();
|
||||||
@@ -296,7 +284,7 @@ function createWindow() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
mainWindow.webContents.once('did-finish-load', () => {
|
mainWindow.webContents.once('did-finish-load', () => {
|
||||||
setTimeout(() => checkPendingUpdate(), 500);
|
setTimeout(checkPendingUpdate, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,6 @@
|
|||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 拆分 hutool-all 为按需依赖,减少 JAR 包体积 -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-crypto</artifactId>
|
<artifactId>hutool-crypto</artifactId>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ javafx:
|
|||||||
|
|
||||||
spring:
|
spring:
|
||||||
main:
|
main:
|
||||||
lazy-initialization: false
|
lazy-initialization: true
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:sqlite:./data/erp-cache.db?journal_mode=WAL&synchronous=NORMAL&cache_size=10000&temp_store=memory&busy_timeout=30000
|
url: jdbc:sqlite:./data/erp-cache.db?journal_mode=WAL&synchronous=NORMAL&cache_size=10000&temp_store=memory&busy_timeout=30000
|
||||||
driver-class-name: org.sqlite.JDBC
|
driver-class-name: org.sqlite.JDBC
|
||||||
|
|||||||
Reference in New Issue
Block a user