app_launcher.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. """应用启动器 - 适配backend结构的统一启动文件"""
  2. import os
  3. import sys
  4. import time
  5. import threading
  6. import webbrowser
  7. import signal
  8. import atexit
  9. from pathlib import Path
  10. # 设置工作目录和模块路径
  11. if getattr(sys, 'frozen', False):
  12. # 打包后的环境
  13. if hasattr(sys, '_MEIPASS'):
  14. base_dir = Path(sys._MEIPASS)
  15. backend_dir = base_dir / "backend"
  16. else:
  17. base_dir = Path(sys.executable).parent
  18. backend_dir = base_dir / "backend"
  19. else:
  20. # 开发环境
  21. base_dir = Path(__file__).parent
  22. backend_dir = base_dir / "backend"
  23. if backend_dir.exists():
  24. os.chdir(backend_dir)
  25. sys.path.insert(0, str(backend_dir))
  26. else:
  27. print(f"ERROR: backend目录不存在: {backend_dir}")
  28. input("按回车键退出...")
  29. # 全局变量用于进程管理
  30. uvicorn_server = None
  31. server_thread = None
  32. server_should_stop = False
  33. def cleanup_server():
  34. """清理服务器进程"""
  35. global uvicorn_server, server_thread, server_should_stop
  36. print("正在关闭服务器...")
  37. server_should_stop = True
  38. # 尝试优雅关闭uvicorn服务器
  39. if uvicorn_server:
  40. try:
  41. uvicorn_server.should_exit = True
  42. print("服务器已设置关闭标志")
  43. except:
  44. pass
  45. # 强制终止占用8000端口的进程
  46. try:
  47. import subprocess
  48. # 查找占用8000端口的进程
  49. result = subprocess.run(['netstat', '-ano'], capture_output=True, text=True)
  50. for line in result.stdout.split('\n'):
  51. if ':8000' in line and 'LISTENING' in line:
  52. parts = line.split()
  53. if len(parts) >= 5:
  54. pid = parts[-1]
  55. try:
  56. subprocess.run(['taskkill', '/F', '/PID', pid],
  57. capture_output=True, check=True)
  58. print(f"已终止占用8000端口的进程 PID: {pid}")
  59. except:
  60. pass
  61. except:
  62. pass
  63. def signal_handler(signum, frame):
  64. """信号处理器"""
  65. print(f"\n收到信号 {signum},正在关闭服务...")
  66. cleanup_server()
  67. sys.exit(0)
  68. def main():
  69. """主函数"""
  70. global uvicorn_server, server_thread, server_should_stop
  71. print("="*50)
  72. print("AI写标书助手 - 启动中...")
  73. print("="*50)
  74. # 注册信号处理器
  75. if hasattr(signal, 'SIGTERM'):
  76. signal.signal(signal.SIGTERM, signal_handler)
  77. if hasattr(signal, 'SIGINT'):
  78. signal.signal(signal.SIGINT, signal_handler)
  79. # 注册退出处理器
  80. atexit.register(cleanup_server)
  81. try:
  82. print("OK: 切换到backend目录")
  83. print("启动服务器...")
  84. def start_server():
  85. global uvicorn_server, server_should_stop
  86. try:
  87. import uvicorn
  88. # 动态导入app.main模块
  89. try:
  90. from app.main import app
  91. except ImportError as ie:
  92. print(f"ERROR: 无法导入app.main: {ie}")
  93. print(f"当前工作目录: {os.getcwd()}")
  94. print(f"Python路径: {sys.path[:3]}")
  95. raise ie
  96. # 创建uvicorn配置
  97. config = uvicorn.Config(app, host="127.0.0.1", port=8000, log_level="warning")
  98. uvicorn_server = uvicorn.Server(config)
  99. # 运行服务器
  100. uvicorn_server.run()
  101. except Exception as e:
  102. if not server_should_stop:
  103. print(f"ERROR: 服务启动失败: {e}")
  104. import traceback
  105. traceback.print_exc()
  106. # 创建非守护线程,但添加退出处理
  107. server_thread = threading.Thread(target=start_server, daemon=False)
  108. server_thread.start()
  109. print("等待服务启动...")
  110. time.sleep(5)
  111. def open_browser():
  112. if not server_should_stop:
  113. time.sleep(2)
  114. try:
  115. webbrowser.open('http://localhost:8000')
  116. print("浏览器已打开")
  117. except Exception as e:
  118. print(f"打开浏览器失败: {e}")
  119. browser_thread = threading.Thread(target=open_browser, daemon=True)
  120. browser_thread.start()
  121. print("\n" + "="*50)
  122. print("服务启动完成!")
  123. print("访问地址: http://localhost:8000")
  124. print("API文档: http://localhost:8000/docs")
  125. print("健康检查: http://localhost:8000/health")
  126. print("="*50)
  127. print("\n完整功能已集成,关闭此窗口会自动停止服务")
  128. print("按 Ctrl+C 可以安全退出")
  129. print("="*50)
  130. # 等待服务器线程,或者直到收到退出信号
  131. try:
  132. while server_thread.is_alive() and not server_should_stop:
  133. time.sleep(1)
  134. except KeyboardInterrupt:
  135. print("\n收到退出信号...")
  136. except KeyboardInterrupt:
  137. print("\n服务已关闭")
  138. except Exception as e:
  139. print(f"运行时错误: {e}")
  140. import traceback
  141. traceback.print_exc()
  142. finally:
  143. cleanup_server()
  144. print("程序已退出")
  145. if __name__ == "__main__":
  146. main()