From 2d05024e82ac0c16eff30625195486f7c6862ea8 Mon Sep 17 00:00:00 2001 From: Gitea Actions Bot Date: Sat, 11 Oct 2025 16:04:35 +0800 Subject: [PATCH] =?UTF-8?q?[patch]=20=E8=A7=A3=E5=86=B3=E4=B9=B1=E7=A0=81b?= =?UTF-8?q?ug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build_production.py | 1 - config.py | 2 +- exe_file_logger.py | 256 +++++++++++++++++++++---------------------- quick_build.py | 124 ++++++++++----------- version_history.json | 16 +++ 5 files changed, 207 insertions(+), 192 deletions(-) diff --git a/build_production.py b/build_production.py index 8ba93e8..fda513d 100644 --- a/build_production.py +++ b/build_production.py @@ -112,7 +112,6 @@ def main(): restore_logging() print("\n" + "=" * 60) - input("Press Enter to exit...") if __name__ == "__main__": main() \ No newline at end of file diff --git a/config.py b/config.py index da84c96..6a17b9a 100644 --- a/config.py +++ b/config.py @@ -42,7 +42,7 @@ VERSION = "1.0" WINDOW_TITLE = "AI回复连接入口-V1.0" # 应用版本号(用于版本检查) -APP_VERSION = "1.5.0" +APP_VERSION = "1.5.1" # 平台特定配置 PLATFORMS = { diff --git a/exe_file_logger.py b/exe_file_logger.py index f712cfe..5fb038a 100644 --- a/exe_file_logger.py +++ b/exe_file_logger.py @@ -1,129 +1,129 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -EXE文件日志器 - 将所有输出重定向到文件 -适用于 pyinstaller -w 打包的exe -""" - -import sys -import os -from datetime import datetime -import threading - -class FileLogger: - """文件日志器类""" - - def __init__(self): - # 确定日志文件路径 - if getattr(sys, 'frozen', False): - # 打包环境 - self.log_dir = os.path.dirname(sys.executable) - else: - # 开发环境 - self.log_dir = os.getcwd() - - # 创建日志文件 - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - self.log_file = os.path.join(self.log_dir, f"MultiPlatformGUI_{timestamp}.log") - - # 线程锁,确保多线程写入安全 - self.lock = threading.Lock() - - # 初始化日志文件 - self.write_log("=" * 80) - self.write_log("多平台客服GUI日志开始") - self.write_log(f"Python版本: {sys.version}") - self.write_log(f"是否打包环境: {getattr(sys, 'frozen', False)}") - self.write_log(f"日志文件: {self.log_file}") - self.write_log("=" * 80) - - def write_log(self, message): - """写入日志到文件""" - try: - with self.lock: - timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] - log_entry = f"[{timestamp}] {message}\n" - - with open(self.log_file, 'a', encoding='utf-8') as f: - f.write(log_entry) - f.flush() - os.fsync(f.fileno()) # 强制写入磁盘 - except Exception as e: - # 如果写入失败,至少尝试输出到stderr - try: - sys.stderr.write(f"LOG_ERROR: {message} | Error: {e}\n") - sys.stderr.flush() - except: - pass - - def write(self, text): - """实现write方法以支持作为sys.stdout使用""" - if text.strip(): # 只记录非空内容 - self.write_log(text.strip()) - - def flush(self): - """实现flush方法""" - pass - -class TeeOutput: - """同时输出到原始输出和文件的类""" - - def __init__(self, original, file_logger): - self.original = original - self.file_logger = file_logger - - def write(self, text): - # 写入原始输出(如果存在) - if self.original: - try: - self.original.write(text) - self.original.flush() - except: - pass - - # 写入文件日志 - if text.strip(): - self.file_logger.write_log(text.strip()) - - def flush(self): - if self.original: - try: - self.original.flush() - except: - pass - -# 全局文件日志器实例 -_file_logger = None - -def setup_file_logging(): - """设置文件日志记录""" - global _file_logger - - if _file_logger is None: - _file_logger = FileLogger() - - # 保存原始的stdout和stderr - original_stdout = sys.stdout - original_stderr = sys.stderr - - # 创建Tee输出,同时输出到原始输出和文件 - sys.stdout = TeeOutput(original_stdout, _file_logger) - sys.stderr = TeeOutput(original_stderr, _file_logger) - - _file_logger.write_log("文件日志系统已设置完成") - - return _file_logger - -def log_to_file(message): - """直接写入文件日志的函数""" - global _file_logger - if _file_logger: - _file_logger.write_log(message) - else: - # 如果还没有初始化,先初始化 - setup_file_logging() - _file_logger.write_log(message) - -# 注释掉自动初始化,改为手动调用 -# if getattr(sys, 'frozen', False): +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +EXE文件日志器 - 将所有输出重定向到文件 +适用于 pyinstaller -w 打包的exe +""" + +import sys +import os +from datetime import datetime +import threading + +class FileLogger: + """文件日志器类""" + + def __init__(self): + # 确定日志文件路径 + if getattr(sys, 'frozen', False): + # 打包环境 + self.log_dir = os.path.dirname(sys.executable) + else: + # 开发环境 + self.log_dir = os.getcwd() + + # 创建日志文件 + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + self.log_file = os.path.join(self.log_dir, f"MultiPlatformGUI_{timestamp}.log") + + # 线程锁,确保多线程写入安全 + self.lock = threading.Lock() + + # 初始化日志文件 + self.write_log("=" * 80) + self.write_log("多平台客服GUI日志开始") + self.write_log(f"Python版本: {sys.version}") + self.write_log(f"是否打包环境: {getattr(sys, 'frozen', False)}") + self.write_log(f"日志文件: {self.log_file}") + self.write_log("=" * 80) + + def write_log(self, message): + """写入日志到文件""" + try: + with self.lock: + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] + log_entry = f"[{timestamp}] {message}\n" + + with open(self.log_file, 'a', encoding='utf-8') as f: + f.write(log_entry) + f.flush() + os.fsync(f.fileno()) # 强制写入磁盘 + except Exception as e: + # 如果写入失败,至少尝试输出到stderr + try: + sys.stderr.write(f"LOG_ERROR: {message} | Error: {e}\n") + sys.stderr.flush() + except: + pass + + def write(self, text): + """实现write方法以支持作为sys.stdout使用""" + if text.strip(): # 只记录非空内容 + self.write_log(text.strip()) + + def flush(self): + """实现flush方法""" + pass + +class TeeOutput: + """同时输出到原始输出和文件的类""" + + def __init__(self, original, file_logger): + self.original = original + self.file_logger = file_logger + + def write(self, text): + # 写入原始输出(如果存在) + if self.original: + try: + self.original.write(text) + self.original.flush() + except: + pass + + # 写入文件日志 + if text.strip(): + self.file_logger.write_log(text.strip()) + + def flush(self): + if self.original: + try: + self.original.flush() + except: + pass + +# 全局文件日志器实例 +_file_logger = None + +def setup_file_logging(): + """设置文件日志记录""" + global _file_logger + + if _file_logger is None: + _file_logger = FileLogger() + + # 保存原始的stdout和stderr + original_stdout = sys.stdout + original_stderr = sys.stderr + + # 创建Tee输出,同时输出到原始输出和文件 + sys.stdout = TeeOutput(original_stdout, _file_logger) + sys.stderr = TeeOutput(original_stderr, _file_logger) + + _file_logger.write_log("文件日志系统已设置完成") + + return _file_logger + +def log_to_file(message): + """直接写入文件日志的函数""" + global _file_logger + if _file_logger: + _file_logger.write_log(message) + else: + # 如果还没有初始化,先初始化 + setup_file_logging() + _file_logger.write_log(message) + +# 注释掉自动初始化,改为手动调用 +# if getattr(sys, 'frozen', False): # setup_file_logging() \ No newline at end of file diff --git a/quick_build.py b/quick_build.py index 9677739..2a611ee 100644 --- a/quick_build.py +++ b/quick_build.py @@ -12,44 +12,44 @@ from pathlib import Path def clean_build(): """清理构建目录""" - print("🧹 清理旧的构建文件...") + print("Cleaning old build files...") # 跳过进程结束步骤,避免影响当前脚本运行 - print("🔄 跳过进程结束步骤(避免脚本自终止)...") + print("Skipping process termination step...") # 等待少量时间确保文件句柄释放 import time - print("⏳ 等待文件句柄释放...") + print("Waiting for file handles to release...") time.sleep(0.5) dirs_to_clean = ['dist', 'build'] for dir_name in dirs_to_clean: - print(f"🔍 检查目录: {dir_name}") + print(f"Checking directory: {dir_name}") if os.path.exists(dir_name): try: - print(f"🗑️ 正在删除: {dir_name}") + print(f"Deleting: {dir_name}") shutil.rmtree(dir_name) - print(f"✅ 已删除: {dir_name}") + print(f"Deleted: {dir_name}") except PermissionError as e: - print(f"⚠️ {dir_name} 被占用,尝试强制删除... 错误: {e}") + print(f"WARNING: {dir_name} is in use, attempting force delete... Error: {e}") try: subprocess.run(['rd', '/s', '/q', dir_name], shell=True, check=True) - print(f"✅ 强制删除成功: {dir_name}") + print(f"Force delete successful: {dir_name}") except Exception as e2: - print(f"❌ 无法删除 {dir_name}: {e2}") - print("💡 请手动删除或运行 force_clean_dist.bat") + print(f"ERROR: Cannot delete {dir_name}: {e2}") + print("TIP: Please delete manually or run force_clean_dist.bat") except Exception as e: - print(f"❌ 删除 {dir_name} 时出错: {e}") + print(f"ERROR: Failed to delete {dir_name}: {e}") else: - print(f"ℹ️ {dir_name} 不存在,跳过") + print(f"INFO: {dir_name} does not exist, skipping") - print("✅ 清理阶段完成") + print("Cleaning phase completed") def build_with_command(): """使用命令行参数直接打包""" - print("🚀 开始打包...") + print("Starting build...") cmd = [ 'pyinstaller', @@ -95,8 +95,8 @@ def build_with_command(): result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8') if result.returncode == 0: - print("✅ 打包成功!") - print("📁 打包结果: dist/MultiPlatformGUI/") + print("Build successful!") + print("Build result: dist/MultiPlatformGUI/") # 验证输出目录(不需要重命名了) if os.path.exists('dist/MultiPlatformGUI/MultiPlatformGUI.exe'): @@ -110,18 +110,18 @@ def build_with_command(): os.remove(new_exe) os.rename(old_exe, new_exe) - print("✅ 主程序已重命名: MultiPlatformGUI.exe → main.exe") + print("Main executable renamed: MultiPlatformGUI.exe -> main.exe") exe_size = os.path.getsize(new_exe) / 1024 / 1024 - print(f"✅ 主程序验证通过: main.exe ({exe_size:.1f} MB)") + print(f"Main executable verified: main.exe ({exe_size:.1f} MB)") except Exception as e: - print(f"⚠️ 重命名主程序失败: {e}") + print(f"WARNING: Failed to rename main executable: {e}") elif os.path.exists('dist/MultiPlatformGUI/main.exe'): # 如果已经是 main.exe,直接验证 exe_size = os.path.getsize('dist/MultiPlatformGUI/main.exe') / 1024 / 1024 - print(f"✅ 主程序验证通过: main.exe ({exe_size:.1f} MB)") + print(f"Main executable verified: main.exe ({exe_size:.1f} MB)") else: - print("⚠️ 未找到主程序文件") + print("WARNING: Main executable file not found") # 创建使用说明 try: @@ -131,14 +131,14 @@ def build_with_command(): return True else: - print("❌ 打包失败") + print("ERROR: Build failed") if result.stderr: - print("错误信息:") + print("Error messages:") print(result.stderr) return False except Exception as e: - print(f"❌ 打包过程出错: {e}") + print(f"ERROR: Build process error: {e}") return False @@ -197,9 +197,9 @@ MultiPlatformGUI/ try: with open('dist/MultiPlatformGUI/使用说明.txt', 'w', encoding='utf-8') as f: f.write(guide_content) - print("✅ 已生成使用说明文件") + print("Usage guide file generated") except: - print("⚠️ 使用说明生成失败") + print("WARNING: Usage guide generation failed") def create_usage_guide_fallback(): @@ -227,14 +227,14 @@ cd dist/main try: with open('dist/main/使用说明.txt', 'w', encoding='utf-8') as f: f.write(guide_content) - print("✅ 已生成使用说明文件 (备用路径)") + print("Usage guide file generated (fallback path)") except: - print("⚠️ 使用说明生成失败") + print("WARNING: Usage guide generation failed") def verify_result(): """验证打包结果""" - print("\n🔍 验证打包结果...") + print("\nVerifying build result...") # 检查MultiPlatformGUI目录(统一输出目录) target_dir = "dist/MultiPlatformGUI" @@ -242,8 +242,8 @@ def verify_result(): if os.path.exists(target_dir): return _verify_directory(target_dir) else: - print("❌ 未找到打包输出目录: dist/MultiPlatformGUI") - print("💡 请检查 PyInstaller 执行是否成功") + print("ERROR: Build output directory not found: dist/MultiPlatformGUI") + print("TIP: Please check if PyInstaller executed successfully") return False def _verify_directory(base_dir): @@ -257,105 +257,105 @@ def _verify_directory(base_dir): dll_path = f"{base_dir}/_internal/Utils/PythonNew32/SaiNiuApi.dll" static_dir = f"{base_dir}/_internal/static" - print(f"📁 验证目录: {base_dir}") + print(f"Verifying directory: {base_dir}") # 检查主程序 if os.path.exists(exe_path): size = os.path.getsize(exe_path) / 1024 / 1024 # MB - print(f"✅ 主程序: main.exe ({size:.1f} MB)") + print(f"Main executable: main.exe ({size:.1f} MB)") else: - print("❌ 主程序 main.exe 不存在") + print("ERROR: Main executable main.exe does not exist") return False # 检查_internal目录 if os.path.exists(internal_dir): file_count = len([f for f in os.listdir(internal_dir) if os.path.isfile(os.path.join(internal_dir, f))]) dir_count = len([d for d in os.listdir(internal_dir) if os.path.isdir(os.path.join(internal_dir, d))]) - print(f"✅ 依赖目录: _internal/ ({file_count} 个文件, {dir_count} 个子目录)") + print(f"Dependencies directory: _internal/ ({file_count} files, {dir_count} subdirectories)") else: - print("❌ _internal 目录不存在") + print("ERROR: _internal directory does not exist") return False # 检查关键DLL(可选) if os.path.exists(dll_path): dll_size = os.path.getsize(dll_path) / 1024 # KB - print(f"✅ 千牛DLL: SaiNiuApi.dll ({dll_size:.1f} KB)") + print(f"QianNiu DLL: SaiNiuApi.dll ({dll_size:.1f} KB)") else: - print("⚠️ 千牛DLL不存在(可能正常)") + print("WARNING: QianNiu DLL does not exist (may be normal)") # 检查静态资源 if os.path.exists(static_dir): icon_files = list(Path(static_dir).glob("*.png")) - print(f"✅ 静态资源: static/ ({len(icon_files)} 个图标文件)") + print(f"Static resources: static/ ({len(icon_files)} icon files)") else: - print("⚠️ static 目录不存在") + print("WARNING: static directory does not exist") - print(f"✅ 目录 {base_dir} 验证通过") + print(f"Directory {base_dir} verification passed") return True def main(): """主函数""" - print("🔥 多平台客服GUI快速打包工具") + print("Multi-Platform Customer Service GUI Quick Build Tool") print("=" * 60) try: # 检查依赖 - print("🔍 检查打包依赖...") + print("Checking build dependencies...") try: import subprocess result = subprocess.run(['pyinstaller', '--version'], capture_output=True, text=True, check=False) if result.returncode == 0: - print(f"✅ PyInstaller 版本: {result.stdout.strip()}") + print(f"PyInstaller version: {result.stdout.strip()}") else: - print("❌ PyInstaller 未安装或不可用") - print("💡 请运行: pip install pyinstaller") + print("ERROR: PyInstaller not installed or unavailable") + print("TIP: Run: pip install pyinstaller") return False except Exception as e: - print(f"❌ 检查PyInstaller时出错: {e}") + print(f"ERROR: Failed to check PyInstaller: {e}") return False # 清理 - print("\n📍 开始清理阶段...") + print("\nCleaning phase started...") clean_build() - print("📍 清理阶段完成") + print("Cleaning phase completed") # 打包 - print("\n📍 开始打包阶段...") + print("\nBuild phase started...") if not build_with_command(): - print("❌ 打包阶段失败") + print("ERROR: Build phase failed") return False - print("📍 打包阶段完成") + print("Build phase completed") # 验证 - print("\n📍 开始验证阶段...") + print("\nVerification phase started...") if not verify_result(): - print("❌ 验证阶段失败") + print("ERROR: Verification phase failed") return False - print("📍 验证阶段完成") + print("Verification phase completed") print("\n" + "=" * 60) - print("🎉 打包完成!") + print("Build completed successfully!") # 显示打包结果 if os.path.exists("dist/MultiPlatformGUI"): - print("📁 打包结果: dist/MultiPlatformGUI/") - print("🚀 运行方式: cd dist/MultiPlatformGUI && .\\main.exe") - print("📦 安装包构建: python installer/build_installer.py") + print("Build result: dist/MultiPlatformGUI/") + print("Run command: cd dist/MultiPlatformGUI && .\\main.exe") + print("Create installer: python installer/build_installer.py") print("=" * 60) return True except Exception as e: - print(f"❌ 打包失败: {e}") + print(f"ERROR: Build failed: {e}") return False if __name__ == "__main__": success = main() if success: - print("\n✅ 可以开始测试了!") + print("\nReady for testing!") else: - print("\n❌ 请检查错误信息并重试") \ No newline at end of file + print("\nERROR: Please check error messages and retry") \ No newline at end of file diff --git a/version_history.json b/version_history.json index 80cd56d..03eb70e 100644 --- a/version_history.json +++ b/version_history.json @@ -1,4 +1,20 @@ [ + { + "version": "1.5.1", + "update_type": "patch", + "content": "[patch] 添加develop用于测试", + "author": "Gitea Actions Bot", + "commit_hash": "2dddcc9804ad4a6966439f2139110ab639ca1452", + "commit_short_hash": "2dddcc98", + "branch": "develop", + "release_time": "2025-10-11 15:56:40", + "download_url": "https://ks3-cn-guangzhou.ksyuncs.com/shuidrop-chat-server/installers/ShuiDi_AI_Assistant_Setup_v1.5.1.exe", + "stats": { + "files_changed": 1, + "lines_added": 1, + "lines_deleted": 1 + } + }, { "version": "1.5.0", "update_type": "minor",