完成了基本功能。

This commit is contained in:
chenxudong 2025-08-08 14:24:06 +08:00
parent d616f686e3
commit 4528e81211
4 changed files with 229 additions and 118 deletions

View File

@ -1,5 +1,6 @@
cd src
pyinstaller --onefile --add-data "install/Common.py:install" .\install\InstallMariaDB.py
pyinstaller --onefile --add-data "install/Common.py:install" --add-data "datas/nssm.exe:datas" --add-data "datas/electromagnetic.jar:datas" --add-data "datas/init.sql:datas" .\install\InstallOrUpgradeComacDB.py
pyinstaller --onefile --add-data "install/Common.py:install" --add-data "datas/electromagnetic.jar:datas" --add-data "datas/init.sql:datas" --hidden-import win32timezone .\install\InstallOrUpgradeComacDB.py
pyinstaller --onefile .\install\Uninstall.py
pyinstaller --onefile .\install\SetFile.py
pyinstaller --onefile --add-data "install/Common.py:install" --hidden-import win32timezone .\install\ManagerService.py

Binary file not shown.

View File

@ -1,135 +1,72 @@
from datetime import datetime
import shutil
from Common import *
current_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(
os.path.abspath(__file__))
manage_service_exe = os.path.join(current_dir, "ManagerService.exe")
app_log_dir = os.path.join(current_dir, "logs")
sql_path = get_resource_path(os.path.join("datas", "init.sql"))
jar_path = get_resource_path(os.path.join("datas", "electromagnetic.jar"))
class InstallOrUpgradeComacDb:
def set_sql():
with open(sql_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
modified = False
new_lines = []
for line in lines:
if 'CREATE TABLE' in line and 'IF NOT EXISTS' not in line:
new_line = line.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS ')
new_lines.append(new_line)
modified = True
else:
new_lines.append(line)
def __init__(self, current_dir):
# 如果有修改,则写回文件
if modified:
with open(sql_path, 'w', encoding='utf-8') as file:
file.writelines(new_lines)
self.run_dir = current_dir
self.app_log_dir = os.path.join(current_dir, "logs")
self.comac_db_running_port = 12396
self.jar_path = get_resource_path(os.path.join("datas", "electromagnetic.jar"))
self.url = f'http://127.0.0.1:{self.comac_db_running_port}/index'
self.service_name = "ComacDatabase"
self.service_description = "数据库组件服务"
# 路径配置
self.datas_dir = get_resource_path(os.path.join('datas'))
# NSSM可执行文件路径
self.nssm_exe = get_resource_path(os.path.join('datas', 'nssm.exe'))
# Java和JAR文件路径
self.java_path = os.path.join(current_dir, "jdk", "bin", "java.exe")
self.sql_path = get_resource_path(os.path.join("datas", "init.sql"))
pass
command2 = fr'{current_dir}\mariadb\bin\mysql --no-defaults -u root -p{mariadb_passowrd} -P {mariadb_port} {mariadb_init_schema} < {sql_path}'
with os.popen(command2) as stream:
res2 = stream.read()
logger.info(res2)
logger.info("sql设置成功")
pass
def start_comac_db(self):
delete_old_files(self.app_log_dir, 2)
self.__remove_pre_service()
self.__set_sql()
self.__register_and_start_service()
logger.info("运行完成10秒钟后自动退出")
time.sleep(10)
pass
def set_jar():
dest_jar_path = os.path.join(current_dir, "electromagnetic.jar")
shutil.copy(jar_path, dest_jar_path)
logger.info("jar设置成功")
pass
def __set_sql(self):
self.__set_replace()
logger.info("设置数据")
command2 = fr'{self.run_dir}\mariadb\bin\mysql --no-defaults -u root -p{mariadb_passowrd} -P {mariadb_port} {mariadb_init_schema} < {self.sql_path}'
with os.popen(command2) as stream:
res2 = stream.read()
logger.info(res2)
pass
def update_service(cmd):
command = [manage_service_exe, cmd]
result = subprocess.run(command, capture_output=True, text=True)
def __register_and_start_service(self):
# 服务配置
# 构建NSSM安装命令
formatted_time = datetime.now().strftime("%Y%m%d%H%M%S")
cmd = [
str(self.nssm_exe),
"install",
self.service_name,
self.java_path,
f"-jar {self.jar_path} "
f"--logging.file.path={self.app_log_dir} "
f"--logging.file.name={self.app_log_dir}/app_{formatted_time}.log "
f"--winPrefix={self.run_dir} "
f"--spring.datasource.username={mariadb_user} "
f"--spring.datasource.password={mariadb_passowrd} "
f"--server.port={comac_db_port} "
f"--spring.datasource.url=jdbc:mariadb://127.0.0.1:{mariadb_port}/{mariadb_init_schema}?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&sslModel=true&serverTimezone=GMT%2B8&allowMultiQueries=true&rewriteBatchedStatements=true"
]
# 执行安装命令
logger.info("Installing service...")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
logger.info("Error installing service:")
logger.info(result.stderr)
return False
logger.info("Service installed successfully!")
# 设置服务显示名称和描述
subprocess.run([str(self.nssm_exe), "set", self.service_name, "DisplayName", self.service_name])
subprocess.run([str(self.nssm_exe), "set", self.service_name, "Description", self.service_description])
# 设置启动目录(可选,但推荐)
# subprocess.run([str(nssm_exe), "set", service_name, "AppDirectory", str(datas_dir)])
# 设置启动类型为自动(可选)
subprocess.run([str(self.nssm_exe), "set", self.service_name, "Start", "SERVICE_AUTO_START"])
logger.info("Service configuration completed.")
subprocess.run(rf"net start {self.service_name}")
time.sleep(10)
def __remove_pre_service(self):
exist = check_service_exist(self.service_name)
if not exist:
return
logger.info("清理历史服务")
stop_command = ["sc", "stop", self.service_name]
delete_command = ["sc", "delete", self.service_name]
res1 = subprocess.run(stop_command, capture_output=True, text=True)
logger.info(res1.stdout)
res2 = subprocess.run(delete_command, capture_output=True, text=True)
logger.info(res2.stdout)
time.sleep(5)
pass
def __set_replace(self):
with open(self.sql_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
modified = False
new_lines = []
for line in lines:
if 'CREATE TABLE' in line and 'IF NOT EXISTS' not in line:
new_line = line.replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS ')
new_lines.append(new_line)
modified = True
else:
new_lines.append(line)
# 如果有修改,则写回文件
if modified:
with open(self.sql_path, 'w', encoding='utf-8') as file:
file.writelines(new_lines)
if result.returncode != 0:
logger.warning(f"操作失败cmd is {cmd}")
else:
logger.info(f"操作成功cmd is {cmd}")
pass
if __name__ == '__main__':
current_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
log_dir = os.path.join(current_dir, "logs")
ensure_dir(log_dir)
ensure_dir(app_log_dir)
logger.add(
sink=os.path.join(log_dir, "InstallComacDB_{time}.log"), # 文件路径模板
sink=os.path.join(app_log_dir, "InstallOrUpgradeComacDB.log"), # 文件路径模板
rotation="10 MB", # 文件大小达到10MB时轮转
retention="1 days", # 保留最近1天的日志
compression="zip", # 压缩旧日志节省空间
enqueue=True, # 线程安全写入
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}" # 自定义格式
)
InstallOrUpgradeComacDb(current_dir).start_comac_db()
set_sql()
set_jar()
update_service("stop")
update_service("remove")
update_service("install")
update_service("start")
pass

View File

@ -0,0 +1,173 @@
from datetime import datetime
import servicemanager
import win32event
import win32service
import win32serviceutil
from Common import *
current_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(
os.path.abspath(__file__))
service_name = "ComacDatabase"
service_description = "Comac数据库组件服务"
java_exe = os.path.join(current_dir, "jdk", "bin", "java.exe")
jar_path = os.path.join(current_dir, "electromagnetic.jar")
app_log_dir = os.path.join(current_dir, "logs")
class ComacDBService(win32serviceutil.ServiceFramework):
_svc_name_ = service_name # 服务名称
_svc_display_name_ = service_name # 显示名称
_svc_description_ = service_description # 服务描述
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
self.process = None
self.service_ready = False
def SvcDoRun(self):
try:
# 报告服务正在启动
self.ReportServiceStatus(win32service.SERVICE_START_PENDING, waitHint=10000)
formatted_time = datetime.now().strftime("%Y%m%d%H%M%S")
cmd = [
java_exe,
f"-jar",
jar_path,
f"--logging.file.path={app_log_dir}",
f"--logging.file.name={app_log_dir}/app_{formatted_time}.log",
f"--winPrefix={current_dir}",
f"--spring.datasource.username={mariadb_user}",
f"--spring.datasource.password={mariadb_passowrd}",
f"--server.port={comac_db_port}",
f"--spring.datasource.url=jdbc:mariadb://127.0.0.1:{mariadb_port}/{mariadb_init_schema}?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&sslModel=true&serverTimezone=GMT%2B8&allowMultiQueries=true&rewriteBatchedStatements=true"
]
# 添加完整的错误处理
env = os.environ.copy()
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = 0 # 隐藏窗口
# 启动Java进程使用subprocess.Popen捕获输出
try:
self.process = subprocess.Popen(
cmd,
cwd=app_log_dir,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE,
creationflags=subprocess.CREATE_NO_WINDOW,
env=env,
startupinfo=startupinfo
)
logger.info(f"Java process started with PID: {self.process.pid}")
# 检查进程是否存活
time.sleep(2) # 给Java启动时间
if self.process.poll() is not None:
# 读取输出以诊断问题
output, _ = self.process.communicate(timeout=2)
output = output.decode('gbk', errors='ignore')
logger.error(f"Java process exited prematurely. Output: {output}")
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
return
except Exception as e:
logger.error(f"Failed to start Java process: {str(e)}")
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
return
# 服务完全启动
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
self.service_ready = True
logger.info("Service fully started and running")
# 启动线程监控Java进程
self.monitor_java_process()
# 等待停止信号
win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
except Exception as e:
logger.error(f"Service failed: {str(e)}", exc_info=True)
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
finally:
# 确保清理资源
if self.process and self.process.poll() is None:
self.process.terminate()
self.process = None
def monitor_java_process(self):
"""在后台线程中监控Java进程"""
import threading
def monitor():
try:
if self.process:
# 持续读取输出
while self.process.poll() is None and self.service_ready:
line = self.process.stdout.readline()
if line:
line = line.decode('gbk', errors='ignore').strip()
logger.info(f"JAVA: {line}")
else:
time.sleep(0.5)
# 检查进程退出原因
if self.process.poll() is not None:
logger.warning(f"Java process exited with code: {self.process.poll()}")
except Exception as e:
logger.error(f"Monitor thread error: {str(e)}")
# 启动监控线程
threading.Thread(target=monitor, daemon=True).start()
def SvcStop(self):
# 报告服务正在停止
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# 停止Java进程如果存在
if self.process:
try:
# 更优雅的关闭方式
logger.info("Terminating Java process...")
self.process.terminate()
# 等待最多10秒
for _ in range(10):
if self.process.poll() is not None:
break
time.sleep(1)
else:
logger.warning("Java process did not exit, using force kill")
self.process.kill()
except Exception as e:
logger.error(f"Error stopping Java process: {str(e)}")
# 通知主线程停止
win32event.SetEvent(self.hWaitStop)
self.service_ready = False
# 报告服务已停止
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
logger.info("Service stopped successfully")
pass
if __name__ == '__main__':
ensure_dir(app_log_dir)
logger.add(
sink=os.path.join(app_log_dir, "ManagerService.log"), # 文件路径模板
rotation="10 MB", # 文件大小达到10MB时轮转
retention="1 days", # 保留最近1天的日志
compression="zip", # 压缩旧日志节省空间
enqueue=True, # 线程安全写入
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}" # 自定义格式
)
if len(sys.argv) == 1:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(ComacDBService)
servicemanager.StartServiceCtrlDispatcher()
else:
err = win32serviceutil.HandleCommandLine(ComacDBService)