Todo: 修改因exe_token错误导致的不断重连问题

Todo: 修改打包中.bat打包文件与测试打包脚本不一致问题
New: 新增installer安装包环境搭建数据
This commit is contained in:
2025-09-28 17:00:02 +08:00
parent 7f9894908d
commit c58cec750f
7 changed files with 640 additions and 55 deletions

106
installer/README.md Normal file
View File

@@ -0,0 +1,106 @@
# GUI 安装包构建工具
这个目录包含了使用 NSIS 构建 Windows 安装包的完整解决方案。
## 目录结构
```
installer/
├── build_installer.py # 主要的构建脚本
├── installer.nsi # 自动生成的 NSIS 脚本
├── assets/ # 安装包资源文件
│ ├── icon.png # 应用程序图标
│ └── license.txt # 软件许可协议
└── output/ # 生成的安装包输出目录
```
## 使用方法
### 方法一:使用批处理脚本(推荐)
```bash
# 在 GUI_main 目录下运行
build_installer.bat
```
### 方法二直接运行Python脚本
```bash
# 在 GUI_main/installer 目录下运行
python build_installer.py
```
## 前提条件
1. **NSIS 安装**
- 下载地址https://nsis.sourceforge.io/Download
- 安装后确保 `makensis` 命令在 PATH 中
2. **应用程序构建**
- 必须先运行 `build_production.py` 构建应用程序
- 确保 `dist/MultiPlatformGUI/` 目录存在
## 构建流程
1. **环境检查**:验证 NSIS 安装和应用程序构建
2. **准备资源**:复制图标文件和创建许可证
3. **生成脚本**:自动生成 NSIS 安装脚本
4. **编译安装包**:使用 makensis 编译最终的安装程序
## 输出结果
安装包将生成在 `installer/output/` 目录下,文件名格式:
```
ShuiDi_AI_Assistant_Setup_1.0.0_YYYYMMDD_HHMMSS.exe
```
## 安装包功能
- ✅ 完整的中文界面
- ✅ 自动创建开始菜单快捷方式
- ✅ 自动创建桌面快捷方式
- ✅ 注册表集成
- ✅ 完整的卸载功能
- ✅ 版本信息显示
## 自定义配置
可以修改 `build_installer.py` 中的以下配置:
```python
# 应用程序信息
self.app_name = "水滴AI客服智能助手"
self.app_name_en = "ShuiDi AI Assistant"
self.app_version = "1.0.0"
self.app_publisher = "水滴科技"
self.app_url = "https://www.shuidi.tech"
```
## 故障排除
### 常见问题
1. **找不到 makensis 命令**
- 确保 NSIS 已正确安装
- 将 NSIS 安装目录添加到 PATH 环境变量
2. **找不到 dist 目录**
- 先运行 `build_production.py` 构建应用程序
3. **图标文件问题**
- 确保 `static/` 目录下有图标文件
- 建议准备 ICO 格式的图标文件
### 日志和调试
构建过程中的详细信息会在控制台显示,包括:
- NSIS 版本信息
- 文件复制状态
- 编译过程输出
- 最终安装包信息
## 进一步优化
可以考虑添加以下功能:
- 数字签名支持
- 多语言支持
- 自定义安装选项
- 更新检查机制

View File

@@ -0,0 +1,325 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
NSIS 安装包构建脚本
用于创建 GUI 应用程序的Windows安装包
"""
import os
import sys
import shutil
import subprocess
import datetime
from pathlib import Path
class NSISInstaller:
def __init__(self):
self.script_dir = Path(__file__).parent
self.project_root = self.script_dir.parent
self.dist_dir = self.project_root / "dist" / "MultiPlatformGUI"
self.output_dir = self.script_dir / "output"
self.assets_dir = self.script_dir / "assets"
self.nsis_script = self.script_dir / "installer.nsi"
# 应用程序信息
self.app_name = "水滴AI客服智能助手"
self.app_name_en = "ShuiDi AI Assistant"
self.app_version = "1.0.0"
self.app_publisher = "水滴智能科技"
self.app_url = "https://shuidrop.com/"
self.exe_name = "main.exe"
def check_prerequisites(self):
"""检查构建前提条件"""
print("🔍 检查构建前提条件...")
# 检查NSIS安装
try:
result = subprocess.run(['makensis', '/VERSION'],
capture_output=True, text=True, check=True)
nsis_version = result.stdout.strip()
print(f"✅ NSIS 版本: {nsis_version}")
except (subprocess.CalledProcessError, FileNotFoundError):
print("❌ 错误: 未找到NSIS或makensis命令")
print(" 请从 https://nsis.sourceforge.io/Download 下载并安装NSIS")
print(" 确保将NSIS添加到系统PATH环境变量")
return False
# 检查dist目录
if not self.dist_dir.exists():
print(f"❌ 错误: 未找到构建输出目录 {self.dist_dir}")
print(" 请先运行 build_production.py 构建应用程序")
return False
# 检查主程序文件
exe_path = self.dist_dir / self.exe_name
if not exe_path.exists():
print(f"❌ 错误: 未找到主程序文件 {exe_path}")
return False
print(f"✅ 找到主程序: {exe_path}")
return True
def prepare_assets(self):
"""准备安装包资源文件"""
print("📁 准备资源文件...")
# 创建assets目录
self.assets_dir.mkdir(exist_ok=True)
# 复制图标文件
icon_sources = [
self.project_root / "static" / "ai_assistant_icon_64.png",
self.project_root / "static" / "ai_assistant_icon_32.png",
self.project_root / "static" / "ai_assistant_icon_16.png"
]
# 寻找可用的图标文件
icon_found = False
for icon_source in icon_sources:
if icon_source.exists():
# 如果有PNG图标我们需要转换为ICO格式
# 这里暂时复制PNG实际项目中建议准备ICO格式图标
icon_dest = self.assets_dir / "icon.ico"
try:
# 尝试使用PIL转换PNG为ICO格式
from PIL import Image
img = Image.open(icon_source)
ico_path = self.assets_dir / "icon.ico"
img.save(ico_path, format='ICO', sizes=[(16,16), (32,32), (48,48), (64,64)])
print(f"✅ 转换图标: {icon_source.name} -> icon.ico")
icon_found = True
break
except ImportError:
print("⚠️ 未安装PIL库跳过图标转换")
break
except Exception as e:
print(f"⚠️ 图标转换失败: {e}")
continue
if not icon_found:
print("⚠️ 未找到图标文件,将使用默认图标")
return icon_found
def generate_nsis_script(self, has_icon=False):
"""生成NSIS安装脚本"""
print("📝 生成NSIS安装脚本...")
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
installer_name = f"{self.app_name_en}_Setup_{self.app_version}_{timestamp}.exe"
nsis_content = f'''# 水滴AI客服智能助手 NSIS 安装脚本
# 自动生成于 {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
Unicode True
# 定义应用程序信息
!define APP_NAME "{self.app_name}"
!define APP_NAME_EN "{self.app_name_en}"
!define APP_VERSION "{self.app_version}"
!define APP_PUBLISHER "{self.app_publisher}"
!define APP_URL "{self.app_url}"
!define APP_EXE "{self.exe_name}"
# 安装程序信息
Name "${{APP_NAME}} v${{APP_VERSION}}"
OutFile "output\\{installer_name}"
InstallDir "$PROGRAMFILES\\${{APP_NAME_EN}}"
InstallDirRegKey HKLM "Software\\${{APP_PUBLISHER}}\\${{APP_NAME_EN}}" "InstallPath"
# 界面设置
!include "MUI2.nsh"
!define MUI_ABORTWARNING
{f'!define MUI_ICON "assets\\\\icon.ico"' if has_icon else ''}
{f'!define MUI_UNICON "assets\\\\icon.ico"' if has_icon else ''}
# 页面配置
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "assets\\license.txt"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
# 语言
!insertmacro MUI_LANGUAGE "SimpChinese"
# 版本信息
VIProductVersion "{self.app_version}.0"
VIAddVersionKey /LANG=2052 "ProductName" "${{APP_NAME}}"
VIAddVersionKey /LANG=2052 "Comments" "水滴AI客服智能助手"
VIAddVersionKey /LANG=2052 "CompanyName" "${{APP_PUBLISHER}}"
VIAddVersionKey /LANG=2052 "FileDescription" "${{APP_NAME}} 安装程序"
VIAddVersionKey /LANG=2052 "FileVersion" "${{APP_VERSION}}"
VIAddVersionKey /LANG=2052 "ProductVersion" "${{APP_VERSION}}"
VIAddVersionKey /LANG=2052 "OriginalFilename" "{installer_name}"
VIAddVersionKey /LANG=2052 "LegalCopyright" "© 2024 ${{APP_PUBLISHER}}"
# 安装段
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
SetOverwrite on
# 复制所有文件
File /r "..\\dist\\MultiPlatformGUI\\*.*"
# 复制图标文件到安装目录(如果有的话)
{f'File "assets\\\\icon.ico"' if has_icon else ''}
# 创建开始菜单快捷方式
CreateDirectory "$SMPROGRAMS\\${{APP_NAME}}"
{f'CreateShortCut "$SMPROGRAMS\\\\${{APP_NAME}}\\\\${{APP_NAME}}.lnk" "$INSTDIR\\\\${{APP_EXE}}" "" "$INSTDIR\\\\icon.ico" 0' if has_icon else 'CreateShortCut "$SMPROGRAMS\\\\${{APP_NAME}}\\\\${{APP_NAME}}.lnk" "$INSTDIR\\\\${{APP_EXE}}"'}
{f'CreateShortCut "$SMPROGRAMS\\\\${{APP_NAME}}\\\\卸载${{APP_NAME}}.lnk" "$INSTDIR\\\\uninstall.exe" "" "$INSTDIR\\\\icon.ico" 0' if has_icon else 'CreateShortCut "$SMPROGRAMS\\\\${{APP_NAME}}\\\\卸载${{APP_NAME}}.lnk" "$INSTDIR\\\\uninstall.exe"'}
# 创建桌面快捷方式
{f'CreateShortCut "$DESKTOP\\\\${{APP_NAME}}.lnk" "$INSTDIR\\\\${{APP_EXE}}" "" "$INSTDIR\\\\icon.ico" 0' if has_icon else 'CreateShortCut "$DESKTOP\\\\${{APP_NAME}}.lnk" "$INSTDIR\\\\${{APP_EXE}}"'}
# 写入注册表
WriteRegStr HKLM "Software\\${{APP_PUBLISHER}}\\${{APP_NAME_EN}}" "InstallPath" "$INSTDIR"
WriteRegStr HKLM "Software\\${{APP_PUBLISHER}}\\${{APP_NAME_EN}}" "Version" "${{APP_VERSION}}"
# 写入卸载信息
WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${{APP_NAME_EN}}" "DisplayName" "${{APP_NAME}}"
WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${{APP_NAME_EN}}" "UninstallString" "$INSTDIR\\uninstall.exe"
WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${{APP_NAME_EN}}" "DisplayVersion" "${{APP_VERSION}}"
WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${{APP_NAME_EN}}" "Publisher" "${{APP_PUBLISHER}}"
WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${{APP_NAME_EN}}" "URLInfoAbout" "${{APP_URL}}"
WriteRegDWORD HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${{APP_NAME_EN}}" "NoModify" 1
WriteRegDWORD HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${{APP_NAME_EN}}" "NoRepair" 1
# 创建卸载程序
WriteUninstaller "$INSTDIR\\uninstall.exe"
SectionEnd
# 卸载段
Section "Uninstall"
# 删除快捷方式
Delete "$SMPROGRAMS\\${{APP_NAME}}\\${{APP_NAME}}.lnk"
Delete "$SMPROGRAMS\\${{APP_NAME}}\\卸载${{APP_NAME}}.lnk"
RMDir "$SMPROGRAMS\\${{APP_NAME}}"
Delete "$DESKTOP\\${{APP_NAME}}.lnk"
# 删除安装目录
RMDir /r "$INSTDIR"
# 删除注册表项
DeleteRegKey HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${{APP_NAME_EN}}"
DeleteRegKey HKLM "Software\\${{APP_PUBLISHER}}\\${{APP_NAME_EN}}"
SectionEnd
'''
# 写入NSIS脚本文件
# 使用UTF-8 BOM编码NSIS可以正确识别
with open(self.nsis_script, 'w', encoding='utf-8-sig') as f:
f.write(nsis_content)
print(f"✅ NSIS脚本已生成: {self.nsis_script}")
return installer_name
def create_license_file(self):
"""创建许可证文件"""
license_file = self.assets_dir / "license.txt"
license_content = f"""软件许可协议
{self.app_name} v{self.app_version}
版权所有 © 2025 {self.app_publisher}
使用本软件即表示您同意以下条款:
1. 本软件仅供合法用途使用
2. 不得对软件进行反向工程、反编译或反汇编
3. 软件按"原样"提供,不提供任何明示或暗示的保证
4. 在任何情况下,软件提供者均不对因使用本软件而产生的任何损害承担责任
如有疑问,请联系:{self.app_url}
"""
with open(license_file, 'w', encoding='utf-8-sig') as f:
f.write(license_content)
print(f"✅ 许可证文件已创建: {license_file}")
def build_installer(self):
"""构建安装包"""
print("🚀 开始构建NSIS安装包...")
# 创建输出目录
self.output_dir.mkdir(exist_ok=True)
# 执行NSIS编译
try:
cmd = ['makensis', str(self.nsis_script)]
result = subprocess.run(cmd, cwd=str(self.script_dir),
capture_output=True, text=True, check=True)
print("✅ NSIS编译成功")
if result.stdout:
print("NSIS输出:")
print(result.stdout)
return True
except subprocess.CalledProcessError as e:
print("❌ NSIS编译失败")
print(f"错误信息: {e.stderr}")
return False
def run(self):
"""执行完整的构建流程"""
print("=" * 60)
print(f"🔧 {self.app_name} 安装包构建工具")
print("=" * 60)
try:
# 1. 检查前提条件
if not self.check_prerequisites():
return False
# 2. 准备资源文件
has_icon = self.prepare_assets()
# 3. 创建许可证文件
self.create_license_file()
# 4. 生成NSIS脚本
installer_name = self.generate_nsis_script(has_icon)
# 5. 构建安装包
if not self.build_installer():
return False
# 6. 显示结果
installer_path = self.output_dir / installer_name
if installer_path.exists():
installer_size = installer_path.stat().st_size / (1024 * 1024)
print("\n" + "=" * 60)
print("🎉 安装包构建成功!")
print(f"📁 安装包位置: {installer_path}")
print(f"📏 文件大小: {installer_size:.1f} MB")
print("🚀 可以直接分发给用户使用")
print("=" * 60)
return True
else:
print("❌ 安装包文件未找到")
return False
except Exception as e:
print(f"❌ 构建过程出错: {e}")
return False
def main():
"""主函数"""
installer = NSISInstaller()
success = installer.run()
if not success:
sys.exit(1)
if __name__ == "__main__":
main()