|
|
const sqlite3 = require("sqlite3").verbose();
|
|
|
const path = require("path");
|
|
|
const { app } = require("electron");
|
|
|
const { spawn, exec } = require("child_process");
|
|
|
|
|
|
// 进程引用管理
|
|
|
const processes = {
|
|
|
LiveTalkingProcess: null,
|
|
|
gptsovitsProcess: null,
|
|
|
chatProcess: null
|
|
|
};
|
|
|
// 获取数据库路径
|
|
|
function getDatabasePath() {
|
|
|
if (process.env.NODE_ENV === "development") {
|
|
|
const dbPath = path.join(__dirname, "..", "..", "live_chat.db");
|
|
|
return dbPath;
|
|
|
} else {
|
|
|
const exePath = app.getPath("exe");
|
|
|
|
|
|
// 获取win-unpacked文件夹路径
|
|
|
const winUnpackedDir = path.dirname(exePath);
|
|
|
|
|
|
// 获取win-unpacked的上级目录(与win-unpacked同级的目录)
|
|
|
const parentDir = path.dirname(winUnpackedDir);
|
|
|
|
|
|
// 构建live_chat.db的完整路径
|
|
|
const dbPath = path.join(parentDir, "live_chat.db");
|
|
|
console.log("dbPath", dbPath);
|
|
|
return dbPath;
|
|
|
}
|
|
|
}
|
|
|
// 更新配置值
|
|
|
function updateConfig(tableName, key, value) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
const db = new sqlite3.Database(getDatabasePath(), err => {
|
|
|
if (err) {
|
|
|
console.error("数据库连接错误:", err.message);
|
|
|
return reject(err);
|
|
|
}
|
|
|
});
|
|
|
// 先尝试更新,如果影响行数为0则插入新记录
|
|
|
db.run(
|
|
|
`UPDATE ${tableName} SET value = ? WHERE key = ?`,
|
|
|
[value, key],
|
|
|
function (err) {
|
|
|
if (err) {
|
|
|
db.close();
|
|
|
return reject(err);
|
|
|
}
|
|
|
|
|
|
// 如果没有匹配的记录则插入
|
|
|
if (this.changes === 0) {
|
|
|
db.run(
|
|
|
`INSERT INTO ${tableName} (key, value) VALUES (?, ?)`,
|
|
|
[key, value],
|
|
|
function (err) {
|
|
|
db.close();
|
|
|
if (err) return reject(err);
|
|
|
resolve({
|
|
|
success: true,
|
|
|
action: "insert",
|
|
|
changes: this.changes
|
|
|
});
|
|
|
}
|
|
|
);
|
|
|
} else {
|
|
|
db.close();
|
|
|
resolve({ success: true, action: "update", changes: this.changes });
|
|
|
}
|
|
|
}
|
|
|
);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 获取配置值
|
|
|
function getConfigValue(tableName, key) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
const db = new sqlite3.Database(getDatabasePath(), err => {
|
|
|
if (err) {
|
|
|
return reject(err);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
db.get(
|
|
|
`SELECT value FROM ${tableName} WHERE key = ?`,
|
|
|
[key],
|
|
|
(err, row) => {
|
|
|
db.close();
|
|
|
if (err) return reject(err);
|
|
|
resolve(row ? row.value : null);
|
|
|
}
|
|
|
);
|
|
|
});
|
|
|
}
|
|
|
// 批量插入系统消息
|
|
|
const bulkInsertSystemMessages = messages => {
|
|
|
console.log("系统消息:", messages);
|
|
|
return new Promise((resolve, reject) => {
|
|
|
if (!Array.isArray(messages) || messages.length === 0) {
|
|
|
reject(new Error("请提供有效的消息数组"));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
const db = new sqlite3.Database(getDatabasePath(), err => {
|
|
|
if (err) {
|
|
|
reject(err);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 使用事务进行批量插入,提高性能
|
|
|
db.run("BEGIN TRANSACTION", err => {
|
|
|
if (err) {
|
|
|
reject(err);
|
|
|
db.close();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const stmt = db.prepare(
|
|
|
"INSERT INTO system_message (message) VALUES (?)"
|
|
|
);
|
|
|
let completed = 0;
|
|
|
let errorOccurred = null;
|
|
|
|
|
|
messages.forEach(message => {
|
|
|
stmt.run(message, function (err) {
|
|
|
if (err && !errorOccurred) {
|
|
|
errorOccurred = err;
|
|
|
}
|
|
|
|
|
|
completed++;
|
|
|
|
|
|
// 所有消息都处理完毕
|
|
|
if (completed === messages.length) {
|
|
|
stmt.finalize();
|
|
|
|
|
|
if (errorOccurred) {
|
|
|
db.run("ROLLBACK", () => {
|
|
|
reject(errorOccurred);
|
|
|
db.close();
|
|
|
});
|
|
|
} else {
|
|
|
db.run("COMMIT", err => {
|
|
|
if (err) {
|
|
|
reject(err);
|
|
|
} else {
|
|
|
resolve({
|
|
|
success: true,
|
|
|
count: messages.length
|
|
|
});
|
|
|
}
|
|
|
db.close();
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
});
|
|
|
});
|
|
|
} catch (error) {
|
|
|
reject(error);
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
// 清空系统消息表
|
|
|
const clearSystemMessages = () => {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
const db = new sqlite3.Database(getDatabasePath(), err => {
|
|
|
if (err) {
|
|
|
reject(err);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 使用TRUNCATE-like操作清空表,同时重置自增ID
|
|
|
db.run("DELETE FROM system_message", err => {
|
|
|
if (err) {
|
|
|
reject(err);
|
|
|
db.close();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 重置自增计数器(SQLite特定)
|
|
|
db.run(
|
|
|
"DELETE FROM sqlite_sequence WHERE name = 'system_message'",
|
|
|
err => {
|
|
|
if (err) {
|
|
|
reject(err);
|
|
|
} else {
|
|
|
resolve({ success: true });
|
|
|
}
|
|
|
db.close();
|
|
|
}
|
|
|
);
|
|
|
});
|
|
|
});
|
|
|
});
|
|
|
};
|
|
|
function getProcessExePath(fileName, exeName) {
|
|
|
let exePath;
|
|
|
let exeFolder;
|
|
|
// 判断是否为开发环境
|
|
|
if (process.env.NODE_ENV === "development") {
|
|
|
// process.cwd() 通常指向项目根目录
|
|
|
const projectRoot = process.cwd();
|
|
|
const parentDir = path.dirname(projectRoot);
|
|
|
exePath = path.join(parentDir, fileName, exeName);
|
|
|
exeFolder = path.join(parentDir, fileName);
|
|
|
} else {
|
|
|
// 生产环境: 与win-unpacked同级目录下的LiveTalking文件夹
|
|
|
exePath = app.getPath("exe");
|
|
|
// 获取win-unpacked文件夹路径
|
|
|
const winUnpackedDir = path.dirname(exePath);
|
|
|
// 获取win-unpacked的上级目录
|
|
|
const parentDir = path.dirname(winUnpackedDir);
|
|
|
exePath = path.join(parentDir, fileName, exeName);
|
|
|
exeFolder = path.join(parentDir, fileName);
|
|
|
}
|
|
|
console.log("exePath", exePath);
|
|
|
console.log("exeFolder", exeFolder);
|
|
|
// 标准化路径格式
|
|
|
return { exePath, exeFolder };
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 启动LiveTalking.exe
|
|
|
*/
|
|
|
async function startProcess(fileName, exeName) {
|
|
|
return new Promise(async (resolve, reject) => {
|
|
|
const processKey = `${fileName}Process`;
|
|
|
if (processes[processKey]) {
|
|
|
reject(`${fileName} is already running`);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 获取程序路径
|
|
|
const obj = getProcessExePath(fileName, exeName);
|
|
|
try {
|
|
|
// 启动进程时,通过wmic获取详细信息(仅Windows)
|
|
|
const newProcess = spawn("cmd.exe", ["/c", obj.exePath], {
|
|
|
// 直接运行bat,不使用start命令
|
|
|
cwd: obj.exeFolder,
|
|
|
windowsVerbatimArguments: true,
|
|
|
windowsHide: false,
|
|
|
detached: false // 不分离,便于跟踪子进程
|
|
|
});
|
|
|
|
|
|
// 记录进程ID(关键:保存实际PID)
|
|
|
processes[processKey] = {
|
|
|
process: newProcess,
|
|
|
pid: newProcess.pid // 保存PID用于后续终止
|
|
|
};
|
|
|
newProcess.on("error", err => {
|
|
|
processes[processKey] = null;
|
|
|
reject(`启动失败: ${err.message}`);
|
|
|
});
|
|
|
console.log(`spawned with PID ${newProcess.pid}`);
|
|
|
resolve(`${fileName} 启动成功 (PID: ${newProcess.pid})`);
|
|
|
} catch (err) {
|
|
|
reject(`启动异常: ${err.message}`);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
// 停止所有进程
|
|
|
async function stopAllProcesses() {
|
|
|
try {
|
|
|
// 清空所有属性,变成空对象
|
|
|
for (const key in processes) {
|
|
|
if (processes[key]) {
|
|
|
delete processes[key];
|
|
|
}
|
|
|
}
|
|
|
const projectRoot = process.cwd();
|
|
|
const parentDir = path.dirname(projectRoot);
|
|
|
const exPath = path.join(parentDir, "kill-live.bat");
|
|
|
const child = spawn("cmd.exe", ["/c", "start", '""', `"${exPath}"`], {
|
|
|
cwd: parentDir,
|
|
|
windowsVerbatimArguments: true,
|
|
|
windowsHide: false,
|
|
|
detached: true
|
|
|
});
|
|
|
child.unref();
|
|
|
} catch (err) {
|
|
|
console.error(err);
|
|
|
}
|
|
|
}
|
|
|
module.exports = {
|
|
|
updateConfig,
|
|
|
getConfigValue,
|
|
|
bulkInsertSystemMessages,
|
|
|
clearSystemMessages,
|
|
|
startProcess,
|
|
|
stopAllProcesses
|
|
|
};
|