我的修正提交
This commit is contained in:
parent
1822b750da
commit
e6a1ad7d8b
@ -1,165 +0,0 @@
|
|||||||
# 身体平衡评估系统 - 后端打包说明
|
|
||||||
|
|
||||||
本文档说明如何将后端Python应用程序打包成独立的exe可执行文件。
|
|
||||||
|
|
||||||
## 📋 打包前准备
|
|
||||||
|
|
||||||
### 1. 环境要求
|
|
||||||
- Python 3.8+ (推荐 3.9-3.11)
|
|
||||||
- Windows 10/11 操作系统
|
|
||||||
- 至少 2GB 可用磁盘空间
|
|
||||||
|
|
||||||
### 2. 依赖检查
|
|
||||||
确保以下核心依赖已安装:
|
|
||||||
```bash
|
|
||||||
pip install Flask Flask-CORS Flask-SocketIO
|
|
||||||
pip install numpy opencv-python psutil
|
|
||||||
pip install PyInstaller
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 快速打包
|
|
||||||
|
|
||||||
### 方法一:使用批处理脚本(推荐)
|
|
||||||
1. 在backend目录下双击运行 `build_backend.bat`
|
|
||||||
2. 脚本会自动完成所有打包步骤
|
|
||||||
3. 等待打包完成
|
|
||||||
|
|
||||||
### 方法二:手动打包
|
|
||||||
1. 打开命令提示符,切换到backend目录
|
|
||||||
2. 运行打包脚本:
|
|
||||||
```bash
|
|
||||||
python build_exe.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方法三:直接使用PyInstaller
|
|
||||||
```bash
|
|
||||||
# 生成spec文件
|
|
||||||
pyinstaller --onefile --windowed --name=BodyBalanceBackend app.py
|
|
||||||
|
|
||||||
# 或使用自定义spec文件
|
|
||||||
pyinstaller backend.spec --clean --noconfirm
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📁 输出文件结构
|
|
||||||
|
|
||||||
打包完成后,在 `dist/` 目录下会生成:
|
|
||||||
|
|
||||||
```
|
|
||||||
dist/
|
|
||||||
├── BodyBalanceBackend.exe # 主可执行文件
|
|
||||||
├── start_backend.bat # 启动脚本
|
|
||||||
├── config.ini # 配置文件
|
|
||||||
├── config.json # JSON配置文件
|
|
||||||
└── logs/ # 日志目录(运行时创建)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 配置说明
|
|
||||||
|
|
||||||
### PyInstaller配置 (backend.spec)
|
|
||||||
- **--onefile**: 打包成单个exe文件
|
|
||||||
- **--windowed**: 隐藏控制台窗口(可选)
|
|
||||||
- **--add-data**: 包含数据文件
|
|
||||||
- **--add-binary**: 包含二进制文件(如DLL)
|
|
||||||
- **--hidden-import**: 包含隐式导入的模块
|
|
||||||
|
|
||||||
### 包含的文件
|
|
||||||
- 配置文件:`config.ini`, `config.json`
|
|
||||||
- 动态库:`dll/k4a.dll`(如果存在)
|
|
||||||
- 数据目录:`data/`, `captured_images/`, `tests/`
|
|
||||||
|
|
||||||
## 🚀 部署和运行
|
|
||||||
|
|
||||||
### 1. 部署到目标机器
|
|
||||||
1. 将整个 `dist/` 文件夹复制到目标机器
|
|
||||||
2. 确保目标机器安装了必要的运行时库
|
|
||||||
|
|
||||||
### 2. 运行方式
|
|
||||||
|
|
||||||
**方式一:直接运行**
|
|
||||||
```bash
|
|
||||||
BodyBalanceBackend.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
**方式二:使用启动脚本**
|
|
||||||
```bash
|
|
||||||
start_backend.bat
|
|
||||||
```
|
|
||||||
|
|
||||||
**方式三:命令行运行**
|
|
||||||
```bash
|
|
||||||
cd dist
|
|
||||||
BodyBalanceBackend.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 访问服务
|
|
||||||
- 服务器地址:`http://localhost:5000`
|
|
||||||
- 健康检查:`http://localhost:5000/health`
|
|
||||||
- API文档:`http://localhost:5000/api/health`
|
|
||||||
|
|
||||||
## 🔍 故障排除
|
|
||||||
|
|
||||||
### 常见问题
|
|
||||||
|
|
||||||
**1. 打包失败**
|
|
||||||
- 检查Python版本是否兼容
|
|
||||||
- 确保所有依赖已正确安装
|
|
||||||
- 检查磁盘空间是否充足
|
|
||||||
|
|
||||||
**2. exe运行失败**
|
|
||||||
- 检查是否缺少Visual C++ Redistributable
|
|
||||||
- 查看logs目录下的错误日志
|
|
||||||
- 确保配置文件存在且格式正确
|
|
||||||
|
|
||||||
**3. 模块导入错误**
|
|
||||||
- 在spec文件中添加缺失的模块到 `hiddenimports`
|
|
||||||
- 检查第三方库的兼容性
|
|
||||||
|
|
||||||
**4. 文件路径问题**
|
|
||||||
- 确保所有相对路径正确
|
|
||||||
- 检查数据文件是否正确包含
|
|
||||||
|
|
||||||
### 调试方法
|
|
||||||
|
|
||||||
**1. 启用控制台输出**
|
|
||||||
修改spec文件中的 `console=True`
|
|
||||||
|
|
||||||
**2. 查看详细日志**
|
|
||||||
```bash
|
|
||||||
BodyBalanceBackend.exe --debug
|
|
||||||
```
|
|
||||||
|
|
||||||
**3. 测试模式运行**
|
|
||||||
```bash
|
|
||||||
python app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📝 自定义打包
|
|
||||||
|
|
||||||
### 修改打包配置
|
|
||||||
编辑 `build_exe.py` 文件,可以自定义:
|
|
||||||
- 输出文件名
|
|
||||||
- 包含的文件和目录
|
|
||||||
- 隐式导入的模块
|
|
||||||
- 图标文件
|
|
||||||
|
|
||||||
### 添加新的依赖
|
|
||||||
1. 在 `requirements_build.txt` 中添加新依赖
|
|
||||||
2. 在spec文件的 `hiddenimports` 中添加模块名
|
|
||||||
3. 重新打包
|
|
||||||
|
|
||||||
## 🔒 安全注意事项
|
|
||||||
|
|
||||||
1. **配置文件安全**:确保配置文件中不包含敏感信息
|
|
||||||
2. **网络安全**:默认绑定所有接口(0.0.0.0),生产环境建议修改
|
|
||||||
3. **文件权限**:确保exe文件有适当的执行权限
|
|
||||||
|
|
||||||
## 📞 技术支持
|
|
||||||
|
|
||||||
如果遇到问题,请检查:
|
|
||||||
1. 日志文件:`logs/backend.log`
|
|
||||||
2. 系统要求是否满足
|
|
||||||
3. 依赖版本是否兼容
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**注意**:首次打包可能需要较长时间,请耐心等待。建议在虚拟环境中进行打包以避免依赖冲突。
|
|
@ -7,18 +7,19 @@ a = Analysis(
|
|||||||
['app.py'],
|
['app.py'],
|
||||||
pathex=[],
|
pathex=[],
|
||||||
binaries=[
|
binaries=[
|
||||||
('dll/femtobolt/bin/k4a.dll', '.'), # K4A动态库
|
('dll/femtobolt/bin/k4a.dll', 'dll/femtobolt/bin'), # K4A动态库
|
||||||
('dll/femtobolt/bin/k4arecord.dll', '.'), # K4A录制库
|
('dll/femtobolt/bin/k4arecord.dll', 'dll/femtobolt/bin'), # K4A录制库
|
||||||
('dll/femtobolt/bin/depthengine_2_0.dll', '.'), # 深度引擎
|
('dll/femtobolt/bin/depthengine_2_0.dll', 'dll/femtobolt/bin'), # 深度引擎
|
||||||
('dll/femtobolt/bin/OrbbecSDK.dll', '.'), # Orbbec SDK
|
('dll/femtobolt/bin/OrbbecSDK.dll', 'dll/femtobolt/bin'), # Orbbec SDK
|
||||||
('dll/femtobolt/bin/ob_usb.dll', '.'), # Orbbec USB库
|
('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库
|
||||||
('dll/femtobolt/bin/live555.dll', '.'), # Live555库
|
('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库
|
||||||
('dll/smitsense/SMiTSenseUsb-F3.0.dll', '.'), # SMiTSense传感器库
|
('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库
|
||||||
('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', '.'), # Orbbec配置文件
|
('dll/smitsense/SMiTSenseUsbWrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类
|
||||||
],
|
],
|
||||||
datas=[
|
datas=[
|
||||||
('config.ini', '.'), # 配置文件
|
('config.ini', '.'), # 配置文件
|
||||||
('data', 'data'), # 数据文件夹
|
('data', 'data'), # 数据文件夹
|
||||||
|
('dll', 'dll'), # DLL目录结构
|
||||||
],
|
],
|
||||||
hiddenimports=[
|
hiddenimports=[
|
||||||
'flask',
|
'flask',
|
||||||
|
@ -1,242 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
平衡体态检测系统 - 后端服务(最小版本)
|
|
||||||
基于Flask的本地API服务,暂时移除SocketIO功能
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import threading
|
|
||||||
from datetime import datetime
|
|
||||||
from flask import Flask, jsonify, send_file
|
|
||||||
from flask import request as flask_request
|
|
||||||
from flask_cors import CORS
|
|
||||||
import sqlite3
|
|
||||||
import logging
|
|
||||||
from pathlib import Path
|
|
||||||
import cv2
|
|
||||||
import configparser
|
|
||||||
|
|
||||||
# 添加当前目录到Python路径
|
|
||||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
# 导入自定义模块
|
|
||||||
from database import DatabaseManager
|
|
||||||
from device_manager import DeviceManager
|
|
||||||
from utils import config as app_config
|
|
||||||
|
|
||||||
# 配置日志
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
||||||
handlers=[
|
|
||||||
logging.FileHandler('logs/backend.log', encoding='utf-8'),
|
|
||||||
logging.StreamHandler()
|
|
||||||
]
|
|
||||||
)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# 创建Flask应用
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config['SECRET_KEY'] = 'body-balance-detection-system-2024'
|
|
||||||
|
|
||||||
# 启用CORS
|
|
||||||
CORS(app, origins=['http://localhost:3000', 'http://127.0.0.1:3000', 'http://192.168.1.58:3000'])
|
|
||||||
|
|
||||||
# 全局变量
|
|
||||||
db_manager = None
|
|
||||||
device_manager = None
|
|
||||||
socketio = None # 暂时禁用SocketIO
|
|
||||||
|
|
||||||
def init_app():
|
|
||||||
"""初始化应用"""
|
|
||||||
global db_manager, device_manager
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 创建必要的目录
|
|
||||||
os.makedirs('logs', exist_ok=True)
|
|
||||||
os.makedirs('data', exist_ok=True)
|
|
||||||
|
|
||||||
# 从配置文件读取数据库路径
|
|
||||||
db_path_config = app_config.get('DATABASE', 'path', 'data/body_balance.db')
|
|
||||||
|
|
||||||
# 对于打包后的exe,确保数据库路径基于exe所在目录
|
|
||||||
if getattr(sys, 'frozen', False):
|
|
||||||
# 如果是打包后的exe,使用exe所在目录
|
|
||||||
exe_dir = os.path.dirname(sys.executable)
|
|
||||||
if not os.path.isabs(db_path_config):
|
|
||||||
db_path = os.path.join(exe_dir, db_path_config)
|
|
||||||
else:
|
|
||||||
db_path = db_path_config
|
|
||||||
else:
|
|
||||||
# 如果是开发环境,使用脚本所在目录
|
|
||||||
if not os.path.isabs(db_path_config):
|
|
||||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
if db_path_config.startswith('backend/'):
|
|
||||||
db_path_config = db_path_config[8:] # 去掉 'backend/' 前缀
|
|
||||||
db_path = os.path.join(current_dir, db_path_config)
|
|
||||||
else:
|
|
||||||
db_path = db_path_config
|
|
||||||
|
|
||||||
# 确保数据库目录存在
|
|
||||||
db_dir = os.path.dirname(db_path)
|
|
||||||
os.makedirs(db_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# 打印数据库路径信息用于调试
|
|
||||||
logger.info(f'数据库配置路径: {db_path_config}')
|
|
||||||
logger.info(f'数据库实际路径: {db_path}')
|
|
||||||
logger.info(f'数据库目录: {db_dir}')
|
|
||||||
logger.info(f'当前工作目录: {os.getcwd()}')
|
|
||||||
|
|
||||||
# 初始化数据库
|
|
||||||
db_manager = DatabaseManager(db_path)
|
|
||||||
db_manager.init_database()
|
|
||||||
|
|
||||||
# 初始化设备管理器(不使用SocketIO)
|
|
||||||
device_manager = DeviceManager(db_manager)
|
|
||||||
|
|
||||||
logger.info('应用初始化完成(最小版本,无SocketIO)')
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'应用初始化失败: {e}')
|
|
||||||
logger.warning('部分功能可能不可用,但服务将继续运行')
|
|
||||||
|
|
||||||
# ==================== 基础API ====================
|
|
||||||
|
|
||||||
@app.route('/health', methods=['GET'])
|
|
||||||
def health_check():
|
|
||||||
"""健康检查"""
|
|
||||||
return jsonify({
|
|
||||||
'status': 'ok',
|
|
||||||
'timestamp': datetime.now().isoformat(),
|
|
||||||
'version': '1.0.0-minimal',
|
|
||||||
'socketio_enabled': False
|
|
||||||
})
|
|
||||||
|
|
||||||
@app.route('/api/health', methods=['GET'])
|
|
||||||
def api_health_check():
|
|
||||||
"""API健康检查"""
|
|
||||||
return jsonify({
|
|
||||||
'status': 'ok',
|
|
||||||
'timestamp': datetime.now().isoformat(),
|
|
||||||
'version': '1.0.0-minimal',
|
|
||||||
'socketio_enabled': False
|
|
||||||
})
|
|
||||||
|
|
||||||
@app.route('/api/test', methods=['GET'])
|
|
||||||
def test_endpoint():
|
|
||||||
"""测试端点"""
|
|
||||||
return jsonify({
|
|
||||||
'message': 'Backend is running!',
|
|
||||||
'timestamp': datetime.now().isoformat(),
|
|
||||||
'database_status': 'connected' if db_manager else 'not_connected',
|
|
||||||
'device_manager_status': 'initialized' if device_manager else 'not_initialized'
|
|
||||||
})
|
|
||||||
|
|
||||||
# ==================== 认证API ====================
|
|
||||||
|
|
||||||
@app.route('/api/auth/login', methods=['POST'])
|
|
||||||
def login():
|
|
||||||
"""用户登录"""
|
|
||||||
try:
|
|
||||||
data = flask_request.get_json()
|
|
||||||
username = data.get('username')
|
|
||||||
password = data.get('password')
|
|
||||||
remember = data.get('remember', False)
|
|
||||||
|
|
||||||
if not username or not password:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'message': '用户名或密码不能为空'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
# 简单的测试登录逻辑
|
|
||||||
if username == 'admin' and password == 'admin':
|
|
||||||
# 生成token(实际项目中应使用JWT等安全token)
|
|
||||||
token = f"token_{username}_{int(time.time())}"
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
'success': True,
|
|
||||||
'message': '登录成功',
|
|
||||||
'data': {
|
|
||||||
'token': token,
|
|
||||||
'user': {
|
|
||||||
'id': 1,
|
|
||||||
'username': username,
|
|
||||||
'role': 'admin'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'message': '用户名或密码错误'
|
|
||||||
}), 401
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'登录失败: {e}')
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'message': '登录过程中发生错误'
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
@app.route('/api/auth/logout', methods=['POST'])
|
|
||||||
def logout():
|
|
||||||
"""用户登出"""
|
|
||||||
return jsonify({
|
|
||||||
'success': True,
|
|
||||||
'message': '登出成功'
|
|
||||||
})
|
|
||||||
|
|
||||||
# ==================== 设备API ====================
|
|
||||||
|
|
||||||
@app.route('/api/devices/status', methods=['GET'])
|
|
||||||
def get_device_status():
|
|
||||||
"""获取设备状态"""
|
|
||||||
try:
|
|
||||||
if device_manager:
|
|
||||||
status = {
|
|
||||||
'cameras': device_manager.get_camera_status(),
|
|
||||||
'sensors': device_manager.get_sensor_status()
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
status = {
|
|
||||||
'cameras': {},
|
|
||||||
'sensors': {},
|
|
||||||
'error': '设备管理器未初始化'
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
'success': True,
|
|
||||||
'data': status
|
|
||||||
})
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'获取设备状态失败: {e}')
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'message': f'获取设备状态失败: {str(e)}'
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
# ==================== 错误处理 ====================
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
|
||||||
def not_found(error):
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'message': '接口不存在'
|
|
||||||
}), 404
|
|
||||||
|
|
||||||
@app.errorhandler(500)
|
|
||||||
def internal_error(error):
|
|
||||||
return jsonify({
|
|
||||||
'success': False,
|
|
||||||
'message': '服务器内部错误'
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
init_app()
|
|
||||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
|
@ -1,71 +0,0 @@
|
|||||||
|
|
||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
block_cipher = None
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['app_minimal.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[
|
|
||||||
('dll/smitsense/SMiTSenseUsb-F3.0.dll', '.'), # SMiTSense传感器库
|
|
||||||
],
|
|
||||||
datas=[
|
|
||||||
('config.ini', '.'), # 配置文件
|
|
||||||
('data', 'data'), # 数据文件夹
|
|
||||||
],
|
|
||||||
hiddenimports=[
|
|
||||||
'flask',
|
|
||||||
'flask_cors',
|
|
||||||
'sqlite3',
|
|
||||||
'configparser',
|
|
||||||
'logging',
|
|
||||||
'threading',
|
|
||||||
'queue',
|
|
||||||
'base64',
|
|
||||||
'psutil',
|
|
||||||
'database',
|
|
||||||
'device_manager',
|
|
||||||
'utils',
|
|
||||||
'cv2',
|
|
||||||
'numpy',
|
|
||||||
],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[
|
|
||||||
'flask_socketio',
|
|
||||||
'socketio',
|
|
||||||
'engineio',
|
|
||||||
'eventlet',
|
|
||||||
'gevent',
|
|
||||||
],
|
|
||||||
win_no_prefer_redirects=False,
|
|
||||||
win_private_assemblies=False,
|
|
||||||
cipher=block_cipher,
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='BodyBalanceBackend',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=True,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon=None
|
|
||||||
)
|
|
@ -1,202 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
身体平衡评估系统 - 后端服务器 (简化版本)
|
|
||||||
专门用于PyInstaller打包的版本,移除了SocketIO功能
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import threading
|
|
||||||
from datetime import datetime
|
|
||||||
from flask import Flask, jsonify, send_file
|
|
||||||
from flask import request as flask_request
|
|
||||||
from flask_cors import CORS
|
|
||||||
import sqlite3
|
|
||||||
import logging
|
|
||||||
from pathlib import Path
|
|
||||||
import cv2
|
|
||||||
import configparser
|
|
||||||
|
|
||||||
# 添加当前目录到Python路径
|
|
||||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
# 导入自定义模块
|
|
||||||
from database import DatabaseManager
|
|
||||||
from device_manager import DeviceManager, VideoStreamManager
|
|
||||||
from utils import config as app_config
|
|
||||||
|
|
||||||
# 配置日志
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
||||||
handlers=[
|
|
||||||
logging.FileHandler('logs/backend.log', encoding='utf-8'),
|
|
||||||
logging.StreamHandler()
|
|
||||||
]
|
|
||||||
)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
# 创建Flask应用
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config['SECRET_KEY'] = 'body-balance-detection-system-2024'
|
|
||||||
|
|
||||||
# 启用CORS支持
|
|
||||||
CORS(app, origins='*', supports_credentials=True, allow_headers=['Content-Type', 'Authorization'], methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
|
|
||||||
|
|
||||||
# 读取RTSP配置
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config_path = os.path.join(os.path.dirname(__file__), 'config.ini')
|
|
||||||
if not os.path.exists(config_path):
|
|
||||||
# 如果当前目录没有config.ini,尝试上级目录
|
|
||||||
config_path = os.path.join(os.path.dirname(__file__), '..', 'config.ini')
|
|
||||||
config.read(config_path, encoding='utf-8')
|
|
||||||
|
|
||||||
# 全局变量
|
|
||||||
db_manager = None
|
|
||||||
device_manager = None
|
|
||||||
current_detection = None
|
|
||||||
detection_thread = None
|
|
||||||
video_stream_manager = None
|
|
||||||
|
|
||||||
def init_app():
|
|
||||||
"""初始化应用"""
|
|
||||||
global db_manager, device_manager, video_stream_manager
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 初始化数据库管理器
|
|
||||||
db_path = os.path.join(os.path.dirname(__file__), 'data', 'body_balance.db')
|
|
||||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
|
||||||
db_manager = DatabaseManager(db_path)
|
|
||||||
# 初始化数据库表结构
|
|
||||||
db_manager.init_database()
|
|
||||||
logger.info("数据库管理器初始化成功")
|
|
||||||
|
|
||||||
# 初始化设备管理器
|
|
||||||
device_manager = DeviceManager()
|
|
||||||
logger.info("设备管理器初始化成功")
|
|
||||||
|
|
||||||
# 初始化视频流管理器
|
|
||||||
video_stream_manager = VideoStreamManager(device_manager=device_manager)
|
|
||||||
logger.info("视频流管理器初始化成功")
|
|
||||||
|
|
||||||
logger.info("应用初始化完成")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"应用初始化失败: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
@app.route('/health', methods=['GET'])
|
|
||||||
def health_check():
|
|
||||||
"""健康检查接口"""
|
|
||||||
return jsonify({
|
|
||||||
'status': 'ok',
|
|
||||||
'timestamp': datetime.now().isoformat(),
|
|
||||||
'message': '身体平衡评估系统后端服务正常运行'
|
|
||||||
})
|
|
||||||
|
|
||||||
@app.route('/api/health', methods=['GET'])
|
|
||||||
def api_health_check():
|
|
||||||
"""API健康检查接口"""
|
|
||||||
return jsonify({
|
|
||||||
'status': 'success',
|
|
||||||
'data': {
|
|
||||||
'service': '身体平衡评估系统',
|
|
||||||
'version': '1.0.0',
|
|
||||||
'timestamp': datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
@app.route('/api/auth/login', methods=['POST'])
|
|
||||||
def login():
|
|
||||||
"""用户登录接口"""
|
|
||||||
try:
|
|
||||||
data = flask_request.get_json()
|
|
||||||
username = data.get('username')
|
|
||||||
password = data.get('password')
|
|
||||||
|
|
||||||
if not username or not password:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'error',
|
|
||||||
'message': '用户名和密码不能为空'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
# 验证用户凭据
|
|
||||||
user = db_manager.authenticate_user(username, password)
|
|
||||||
if user:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'success',
|
|
||||||
'message': '登录成功',
|
|
||||||
'data': {
|
|
||||||
'user_id': user['id'],
|
|
||||||
'username': user['username'],
|
|
||||||
'role': user['role'],
|
|
||||||
'token': 'simple_token_' + str(user['id'])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'error',
|
|
||||||
'message': '用户名或密码错误'
|
|
||||||
}), 401
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"登录失败: {e}")
|
|
||||||
return jsonify({'status': 'error', 'message': '登录失败'}), 500
|
|
||||||
|
|
||||||
@app.route('/api/auth/register', methods=['POST'])
|
|
||||||
def register():
|
|
||||||
"""用户注册接口"""
|
|
||||||
try:
|
|
||||||
data = flask_request.get_json()
|
|
||||||
username = data.get('username')
|
|
||||||
password = data.get('password')
|
|
||||||
email = data.get('email', '')
|
|
||||||
|
|
||||||
if not username or not password:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'error',
|
|
||||||
'message': '用户名和密码不能为空'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
# 创建用户
|
|
||||||
user_id = db_manager.create_user(username, password, email)
|
|
||||||
if user_id:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'success',
|
|
||||||
'message': '注册成功',
|
|
||||||
'data': {'user_id': user_id}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
return jsonify({
|
|
||||||
'status': 'error',
|
|
||||||
'message': '注册失败,用户名可能已存在'
|
|
||||||
}), 400
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"注册失败: {e}")
|
|
||||||
return jsonify({'status': 'error', 'message': '注册失败'}), 500
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
try:
|
|
||||||
# 初始化应用
|
|
||||||
init_app()
|
|
||||||
|
|
||||||
# 启动服务器
|
|
||||||
logger.info("启动身体平衡评估系统后端服务器...")
|
|
||||||
logger.info("服务器地址: http://localhost:5000")
|
|
||||||
|
|
||||||
app.run(
|
|
||||||
host='0.0.0.0',
|
|
||||||
port=5000,
|
|
||||||
debug=False,
|
|
||||||
threaded=True
|
|
||||||
)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logger.info("服务器已停止")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"服务器启动失败: {e}")
|
|
||||||
sys.exit(1)
|
|
@ -1,76 +0,0 @@
|
|||||||
|
|
||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['main_exe.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[
|
|
||||||
# 只包含确实存在的DLL文件
|
|
||||||
],
|
|
||||||
datas=[
|
|
||||||
('config.ini', '.'), # 配置文件
|
|
||||||
('data', 'data'), # 数据文件夹
|
|
||||||
('tests', 'tests'), # 测试文件夹
|
|
||||||
('app_minimal.py', '.'), # 应用入口文件
|
|
||||||
],
|
|
||||||
hiddenimports=[
|
|
||||||
'flask',
|
|
||||||
'flask_socketio',
|
|
||||||
'flask_cors',
|
|
||||||
'socketio',
|
|
||||||
'engineio',
|
|
||||||
'engineio.async_drivers.threading',
|
|
||||||
'socketio.namespace',
|
|
||||||
'cv2',
|
|
||||||
'numpy',
|
|
||||||
'psutil',
|
|
||||||
'sqlite3',
|
|
||||||
'configparser',
|
|
||||||
'logging',
|
|
||||||
'threading',
|
|
||||||
'queue',
|
|
||||||
'base64',
|
|
||||||
'requests',
|
|
||||||
'click',
|
|
||||||
'colorama',
|
|
||||||
'database',
|
|
||||||
'device_manager',
|
|
||||||
'utils',
|
|
||||||
'dns',
|
|
||||||
'dns.resolver',
|
|
||||||
'dns.asyncresolver'
|
|
||||||
],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[
|
|
||||||
'eventlet',
|
|
||||||
'gevent',
|
|
||||||
'gevent_uwsgi'
|
|
||||||
],
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='BodyBalanceBackend',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=True,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon=None
|
|
||||||
)
|
|
@ -35,18 +35,19 @@ a = Analysis(
|
|||||||
['app.py'],
|
['app.py'],
|
||||||
pathex=[],
|
pathex=[],
|
||||||
binaries=[
|
binaries=[
|
||||||
('dll/femtobolt/bin/k4a.dll', '.'), # K4A动态库
|
('dll/femtobolt/bin/k4a.dll', 'dll/femtobolt/bin'), # K4A动态库
|
||||||
('dll/femtobolt/bin/k4arecord.dll', '.'), # K4A录制库
|
('dll/femtobolt/bin/k4arecord.dll', 'dll/femtobolt/bin'), # K4A录制库
|
||||||
('dll/femtobolt/bin/depthengine_2_0.dll', '.'), # 深度引擎
|
('dll/femtobolt/bin/depthengine_2_0.dll', 'dll/femtobolt/bin'), # 深度引擎
|
||||||
('dll/femtobolt/bin/OrbbecSDK.dll', '.'), # Orbbec SDK
|
('dll/femtobolt/bin/OrbbecSDK.dll', 'dll/femtobolt/bin'), # Orbbec SDK
|
||||||
('dll/femtobolt/bin/ob_usb.dll', '.'), # Orbbec USB库
|
('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库
|
||||||
('dll/femtobolt/bin/live555.dll', '.'), # Live555库
|
('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库
|
||||||
('dll/smitsense/SMiTSenseUsb-F3.0.dll', '.'), # SMiTSense传感器库
|
('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库
|
||||||
('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', '.'), # Orbbec配置文件
|
('dll/smitsense/SMiTSenseUsbWrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类
|
||||||
],
|
],
|
||||||
datas=[
|
datas=[
|
||||||
('config.ini', '.'), # 配置文件
|
('config.ini', '.'), # 配置文件
|
||||||
('data', 'data'), # 数据文件夹
|
('data', 'data'), # 数据文件夹
|
||||||
|
('dll', 'dll'), # DLL目录结构
|
||||||
],
|
],
|
||||||
hiddenimports=[
|
hiddenimports=[
|
||||||
'flask',
|
'flask',
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
echo ================================================
|
|
||||||
echo 身体平衡评估系统 - 后端打包工具
|
|
||||||
echo ================================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: 检查是否在正确的目录
|
|
||||||
if not exist "app.py" (
|
|
||||||
echo ❌ 错误:请在backend目录下运行此脚本
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
:: 检查Python环境
|
|
||||||
echo 🔍 检查Python环境...
|
|
||||||
python --version
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo ❌ 错误:Python未安装或未添加到PATH
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
:: 安装打包依赖
|
|
||||||
echo.
|
|
||||||
echo 📦 安装打包依赖...
|
|
||||||
pip install pyinstaller
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo ❌ 错误:PyInstaller安装失败
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
:: 清理之前的构建
|
|
||||||
echo.
|
|
||||||
echo 🧹 清理之前的构建文件...
|
|
||||||
if exist "build" rmdir /s /q "build"
|
|
||||||
if exist "dist" rmdir /s /q "dist"
|
|
||||||
if exist "*.spec" del /q "*.spec"
|
|
||||||
echo ✓ 清理完成
|
|
||||||
|
|
||||||
:: 运行打包脚本
|
|
||||||
echo.
|
|
||||||
echo 🚀 开始打包...
|
|
||||||
python build_exe.py
|
|
||||||
if errorlevel 1 (
|
|
||||||
echo ❌ 打包失败
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo 🎉 打包完成!
|
|
||||||
echo.
|
|
||||||
echo 📁 输出目录:dist/
|
|
||||||
echo 🚀 可执行文件:dist/BodyBalanceBackend.exe
|
|
||||||
echo 📋 启动脚本:dist/start_backend.bat
|
|
||||||
echo.
|
|
||||||
echo 使用说明:
|
|
||||||
echo 1. 将整个dist文件夹复制到目标机器
|
|
||||||
echo 2. 确保目标机器有必要的运行时库(Visual C++ Redistributable)
|
|
||||||
echo 3. 运行 BodyBalanceBackend.exe 或 start_backend.bat
|
|
||||||
echo.
|
|
||||||
pause
|
|
@ -1,233 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
身体平衡评估系统后端打包脚本
|
|
||||||
基于最小测试框架的成功经验,简化打包配置
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def check_dependencies():
|
|
||||||
"""检查必需的依赖"""
|
|
||||||
print("检查依赖模块...")
|
|
||||||
required_modules = [
|
|
||||||
'flask',
|
|
||||||
'flask_socketio',
|
|
||||||
'flask_cors',
|
|
||||||
'socketio',
|
|
||||||
'engineio',
|
|
||||||
'numpy',
|
|
||||||
'cv2',
|
|
||||||
'psutil'
|
|
||||||
]
|
|
||||||
|
|
||||||
missing_modules = []
|
|
||||||
for module in required_modules:
|
|
||||||
try:
|
|
||||||
__import__(module)
|
|
||||||
print(f"✓ {module}")
|
|
||||||
except ImportError:
|
|
||||||
print(f"✗ {module} (缺失)")
|
|
||||||
missing_modules.append(module)
|
|
||||||
|
|
||||||
if missing_modules:
|
|
||||||
print(f"\n缺失模块: {', '.join(missing_modules)}")
|
|
||||||
print("请运行: pip install -r requirements_build.txt")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print("✓ 所有依赖模块检查通过")
|
|
||||||
return True
|
|
||||||
|
|
||||||
def create_spec_file():
|
|
||||||
"""创建PyInstaller spec文件"""
|
|
||||||
spec_content = '''
|
|
||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['main_exe.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[
|
|
||||||
# 只包含确实存在的DLL文件
|
|
||||||
],
|
|
||||||
datas=[
|
|
||||||
('config.ini', '.'), # 配置文件
|
|
||||||
('data', 'data'), # 数据文件夹
|
|
||||||
('tests', 'tests'), # 测试文件夹
|
|
||||||
('app_minimal.py', '.'), # 应用入口文件
|
|
||||||
],
|
|
||||||
hiddenimports=[
|
|
||||||
'flask',
|
|
||||||
'flask_socketio',
|
|
||||||
'flask_cors',
|
|
||||||
'socketio',
|
|
||||||
'engineio',
|
|
||||||
'engineio.async_drivers.threading',
|
|
||||||
'socketio.namespace',
|
|
||||||
'cv2',
|
|
||||||
'numpy',
|
|
||||||
'psutil',
|
|
||||||
'sqlite3',
|
|
||||||
'configparser',
|
|
||||||
'logging',
|
|
||||||
'threading',
|
|
||||||
'queue',
|
|
||||||
'base64',
|
|
||||||
'requests',
|
|
||||||
'click',
|
|
||||||
'colorama',
|
|
||||||
'database',
|
|
||||||
'device_manager',
|
|
||||||
'utils',
|
|
||||||
'dns',
|
|
||||||
'dns.resolver',
|
|
||||||
'dns.asyncresolver'
|
|
||||||
],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[
|
|
||||||
'eventlet',
|
|
||||||
'gevent',
|
|
||||||
'gevent_uwsgi'
|
|
||||||
],
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='BodyBalanceBackend',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=True,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon=None
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open('backend.spec', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(spec_content)
|
|
||||||
|
|
||||||
print("✓ 已创建 backend.spec 文件")
|
|
||||||
|
|
||||||
def build_exe():
|
|
||||||
"""构建exe文件"""
|
|
||||||
print("开始构建exe文件...")
|
|
||||||
|
|
||||||
# 检查依赖模块
|
|
||||||
print("\n检查依赖模块...")
|
|
||||||
if not check_dependencies():
|
|
||||||
print("\n❌ 依赖检查失败,请先安装缺失的模块")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 检查PyInstaller是否安装
|
|
||||||
try:
|
|
||||||
import PyInstaller
|
|
||||||
print(f"\n✓ PyInstaller版本: {PyInstaller.__version__}")
|
|
||||||
except ImportError:
|
|
||||||
print("❌ PyInstaller未安装,正在安装...")
|
|
||||||
subprocess.run([sys.executable, '-m', 'pip', 'install', 'pyinstaller>=6.10.0'])
|
|
||||||
|
|
||||||
# 清理之前的构建
|
|
||||||
print("\n清理构建目录...")
|
|
||||||
if os.path.exists('build'):
|
|
||||||
shutil.rmtree('build')
|
|
||||||
print("✓ 清理build目录")
|
|
||||||
|
|
||||||
if os.path.exists('dist'):
|
|
||||||
shutil.rmtree('dist')
|
|
||||||
print("✓ 清理dist目录")
|
|
||||||
|
|
||||||
# 创建spec文件
|
|
||||||
create_spec_file()
|
|
||||||
|
|
||||||
# 使用spec文件构建
|
|
||||||
print("\n开始打包...")
|
|
||||||
cmd = [sys.executable, '-m', 'PyInstaller', 'backend.spec', '--clean', '--noconfirm']
|
|
||||||
print(f"执行命令: {' '.join(cmd)}")
|
|
||||||
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("\n✓ 构建成功!")
|
|
||||||
print("exe文件位置: dist/BodyBalanceBackend.exe")
|
|
||||||
|
|
||||||
# 复制额外的文件
|
|
||||||
dist_path = Path('dist')
|
|
||||||
if dist_path.exists():
|
|
||||||
# 复制配置文件(如果存在)
|
|
||||||
config_files = ['config.ini', 'config.json']
|
|
||||||
for config_file in config_files:
|
|
||||||
if os.path.exists(config_file):
|
|
||||||
shutil.copy2(config_file, dist_path)
|
|
||||||
print(f"✓ 复制配置文件: {config_file}")
|
|
||||||
|
|
||||||
# 创建启动脚本
|
|
||||||
start_script = dist_path / 'start_backend.bat'
|
|
||||||
with open(start_script, 'w', encoding='utf-8') as f:
|
|
||||||
f.write('@echo off\n')
|
|
||||||
f.write('chcp 65001 >nul\n')
|
|
||||||
f.write('echo Starting Body Balance Backend...\n')
|
|
||||||
f.write('BodyBalanceBackend.exe\n')
|
|
||||||
f.write('pause\n')
|
|
||||||
print("✓ 创建启动脚本: start_backend.bat")
|
|
||||||
|
|
||||||
print("\n🎉 打包完成!")
|
|
||||||
print("\n测试方法:")
|
|
||||||
print("1. 直接运行: dist/BodyBalanceBackend.exe")
|
|
||||||
print("2. 使用脚本: dist/start_backend.bat")
|
|
||||||
print("3. 在浏览器中访问: http://localhost:5000")
|
|
||||||
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print("\n❌ 构建失败")
|
|
||||||
print(f"错误信息: {result.stderr}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主函数"""
|
|
||||||
print("================================================")
|
|
||||||
print("身体平衡评估系统 - 后端打包工具 (简化版)")
|
|
||||||
print("基于最小测试框架的成功经验")
|
|
||||||
print("================================================")
|
|
||||||
print()
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 检查是否在正确的目录
|
|
||||||
if not os.path.exists('main_exe.py'):
|
|
||||||
print("❌ 错误:请在backend目录下运行此脚本")
|
|
||||||
print("当前目录应包含 main_exe.py 文件")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 构建exe
|
|
||||||
if build_exe():
|
|
||||||
print("\n✅ 所有操作完成!")
|
|
||||||
else:
|
|
||||||
print("\n❌ 构建过程中出现错误")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 错误: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
input("\n按回车键退出...")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,524 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
后端应用程序打包脚本
|
|
||||||
使用PyInstaller将Flask应用程序打包成独立的exe文件
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def create_spec_file():
|
|
||||||
"""创建PyInstaller spec文件"""
|
|
||||||
spec_content = '''
|
|
||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
block_cipher = None
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['main_exe.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[
|
|
||||||
('dll/femtobolt/bin/k4a.dll', '.'), # K4A动态库
|
|
||||||
('dll/femtobolt/bin/k4arecord.dll', '.'), # K4A录制库
|
|
||||||
('dll/femtobolt/bin/depthengine_2_0.dll', '.'), # 深度引擎
|
|
||||||
('dll/femtobolt/bin/OrbbecSDK.dll', '.'), # Orbbec SDK
|
|
||||||
('dll/femtobolt/bin/ob_usb.dll', '.'), # Orbbec USB库
|
|
||||||
('dll/femtobolt/bin/live555.dll', '.'), # Live555库
|
|
||||||
('dll/smitsense/SMiTSenseUsb-F3.0.dll', '.'), # SMiTSense传感器库
|
|
||||||
('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', '.'), # Orbbec配置文件
|
|
||||||
],
|
|
||||||
datas=[
|
|
||||||
('..\\config.ini', '.'), # 配置文件
|
|
||||||
('..\\config.json', '.'), # JSON配置文件
|
|
||||||
('tests', 'tests'), # 测试文件夹
|
|
||||||
('data', 'data'), # 数据文件夹
|
|
||||||
],
|
|
||||||
hiddenimports=[
|
|
||||||
'flask',
|
|
||||||
'flask_socketio',
|
|
||||||
'flask_cors',
|
|
||||||
'cv2',
|
|
||||||
'numpy',
|
|
||||||
'pandas',
|
|
||||||
'scipy',
|
|
||||||
'matplotlib',
|
|
||||||
'seaborn',
|
|
||||||
'sklearn',
|
|
||||||
'PIL',
|
|
||||||
'reportlab',
|
|
||||||
'sqlite3',
|
|
||||||
'configparser',
|
|
||||||
'logging',
|
|
||||||
'threading',
|
|
||||||
'queue',
|
|
||||||
'base64',
|
|
||||||
'psutil',
|
|
||||||
'pykinect_azure',
|
|
||||||
'pyserial',
|
|
||||||
'requests',
|
|
||||||
'yaml',
|
|
||||||
'click',
|
|
||||||
'colorama',
|
|
||||||
'tqdm',
|
|
||||||
'database',
|
|
||||||
'device_manager',
|
|
||||||
'utils',
|
|
||||||
],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[],
|
|
||||||
win_no_prefer_redirects=False,
|
|
||||||
win_private_assemblies=False,
|
|
||||||
cipher=block_cipher,
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='BodyBalanceBackend',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=True,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon=None, # 图标文件不存在,暂时禁用
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open('backend.spec', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(spec_content)
|
|
||||||
print("✓ 已创建 backend.spec 文件")
|
|
||||||
|
|
||||||
def create_debug_spec():
|
|
||||||
"""创建诊断脚本的spec文件"""
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
spec_content = f'''# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
block_cipher = None
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['test_socketio_debug.py'],
|
|
||||||
pathex=['{os.getcwd()}'],
|
|
||||||
binaries=[],
|
|
||||||
datas=[],
|
|
||||||
hiddenimports=[
|
|
||||||
'flask',
|
|
||||||
'flask_socketio',
|
|
||||||
'eventlet',
|
|
||||||
'threading',
|
|
||||||
'engineio.async_drivers.threading',
|
|
||||||
'engineio.async_drivers.eventlet',
|
|
||||||
'engineio.async_eventlet',
|
|
||||||
'socketio.async_eventlet',
|
|
||||||
'greenlet',
|
|
||||||
],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={{}},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[],
|
|
||||||
win_no_prefer_redirects=False,
|
|
||||||
win_private_assemblies=False,
|
|
||||||
cipher=block_cipher,
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='test_socketio_debug',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=True,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open('debug.spec', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(spec_content)
|
|
||||||
print("✓ 已创建 debug.spec 文件")
|
|
||||||
|
|
||||||
# 直接构建诊断exe
|
|
||||||
print("开始构建诊断exe文件...")
|
|
||||||
result = subprocess.run([sys.executable, '-m', 'PyInstaller', 'debug.spec'],
|
|
||||||
capture_output=True, text=True, encoding='utf-8')
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✓ 诊断exe构建成功!")
|
|
||||||
print("运行诊断: dist/test_socketio_debug.exe")
|
|
||||||
else:
|
|
||||||
print(f"❌ 诊断exe构建失败: {result.stderr}")
|
|
||||||
|
|
||||||
def create_minimal_spec():
|
|
||||||
"""创建最小化测试脚本的spec文件"""
|
|
||||||
print("创建最小化测试脚本的spec文件...")
|
|
||||||
|
|
||||||
spec_content = '''# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
block_cipher = None
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['test_socketio_minimal.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[],
|
|
||||||
datas=[],
|
|
||||||
hiddenimports=[
|
|
||||||
'flask',
|
|
||||||
'flask_socketio',
|
|
||||||
'eventlet',
|
|
||||||
'eventlet.hubs',
|
|
||||||
'eventlet.hubs.epolls',
|
|
||||||
'eventlet.hubs.kqueue',
|
|
||||||
'eventlet.hubs.selects',
|
|
||||||
'threading',
|
|
||||||
'engineio',
|
|
||||||
'engineio.async_drivers',
|
|
||||||
'engineio.async_drivers.threading',
|
|
||||||
'engineio.async_drivers.eventlet',
|
|
||||||
'engineio.async_drivers.gevent',
|
|
||||||
'socketio',
|
|
||||||
'socketio.namespace'
|
|
||||||
],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[],
|
|
||||||
win_no_prefer_redirects=False,
|
|
||||||
win_private_assemblies=False,
|
|
||||||
cipher=block_cipher,
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='test_socketio_minimal',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=True,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open('minimal.spec', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(spec_content)
|
|
||||||
|
|
||||||
print("✓ 已创建 minimal.spec 文件")
|
|
||||||
|
|
||||||
# 直接构建
|
|
||||||
print("开始构建最小化测试exe...")
|
|
||||||
result = subprocess.run([sys.executable, '-m', 'PyInstaller', 'minimal.spec', '--clean'],
|
|
||||||
capture_output=True, text=True, encoding='utf-8')
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✓ 最小化测试exe构建完成: dist/test_socketio_minimal.exe")
|
|
||||||
else:
|
|
||||||
print(f"❌ 最小化测试exe构建失败: {result.stderr}")
|
|
||||||
|
|
||||||
def create_main_entry():
|
|
||||||
"""创建主入口文件"""
|
|
||||||
main_content = '''
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
身体平衡评估系统 - 后端服务入口
|
|
||||||
独立运行的exe版本
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# 设置工作目录
|
|
||||||
if getattr(sys, 'frozen', False):
|
|
||||||
# 如果是打包后的exe
|
|
||||||
application_path = os.path.dirname(sys.executable)
|
|
||||||
else:
|
|
||||||
# 如果是开发环境
|
|
||||||
application_path = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
|
|
||||||
os.chdir(application_path)
|
|
||||||
sys.path.insert(0, application_path)
|
|
||||||
|
|
||||||
# 创建必要的目录
|
|
||||||
os.makedirs('logs', exist_ok=True)
|
|
||||||
os.makedirs('data', exist_ok=True)
|
|
||||||
|
|
||||||
# 配置日志
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
||||||
handlers=[
|
|
||||||
logging.FileHandler('logs/backend.log', encoding='utf-8'),
|
|
||||||
logging.StreamHandler()
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主函数"""
|
|
||||||
try:
|
|
||||||
logger.info("启动身体平衡评估系统后端服务...")
|
|
||||||
logger.info(f"工作目录: {os.getcwd()}")
|
|
||||||
|
|
||||||
# 导入并启动Flask应用
|
|
||||||
from app import app, socketio, init_app
|
|
||||||
|
|
||||||
# 初始化应用
|
|
||||||
init_app()
|
|
||||||
|
|
||||||
# 启动服务器
|
|
||||||
logger.info("后端服务器启动在 http://localhost:5000")
|
|
||||||
socketio.run(
|
|
||||||
app,
|
|
||||||
host='0.0.0.0',
|
|
||||||
port=5000,
|
|
||||||
debug=False
|
|
||||||
)
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logger.info("用户中断,正在关闭服务器...")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"启动失败: {e}")
|
|
||||||
input("按回车键退出...")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open('main_exe.py', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(main_content)
|
|
||||||
print("✓ 已创建 main_exe.py 入口文件")
|
|
||||||
|
|
||||||
def check_dependencies():
|
|
||||||
"""检查必需的依赖模块"""
|
|
||||||
required_modules = [
|
|
||||||
'flask', 'flask_cors', 'flask_socketio', 'numpy', 'pandas',
|
|
||||||
'cv2', 'matplotlib', 'seaborn', 'PIL', 'reportlab',
|
|
||||||
'serial', 'requests', 'sqlite3', 'configparser', 'scipy', 'eventlet'
|
|
||||||
]
|
|
||||||
|
|
||||||
missing_modules = []
|
|
||||||
for module in required_modules:
|
|
||||||
try:
|
|
||||||
__import__(module)
|
|
||||||
print(f"✓ {module}")
|
|
||||||
except ImportError:
|
|
||||||
missing_modules.append(module)
|
|
||||||
print(f"❌ {module} - 未安装")
|
|
||||||
|
|
||||||
if missing_modules:
|
|
||||||
print(f"\n警告: 发现 {len(missing_modules)} 个缺失的依赖模块")
|
|
||||||
print("建议运行: pip install -r requirements.txt")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print("✓ 所有必需依赖模块检查通过")
|
|
||||||
return True
|
|
||||||
|
|
||||||
def build_exe():
|
|
||||||
"""构建exe文件"""
|
|
||||||
print("开始构建exe文件...")
|
|
||||||
|
|
||||||
# 检查依赖模块
|
|
||||||
print("\n检查依赖模块...")
|
|
||||||
if not check_dependencies():
|
|
||||||
print("\n❌ 依赖检查失败,请先安装缺失的模块")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 检查PyInstaller是否安装
|
|
||||||
try:
|
|
||||||
import PyInstaller
|
|
||||||
print(f"\n✓ PyInstaller版本: {PyInstaller.__version__}")
|
|
||||||
except ImportError:
|
|
||||||
print("❌ PyInstaller未安装,正在安装...")
|
|
||||||
os.system('pip install pyinstaller')
|
|
||||||
|
|
||||||
# 清理之前的构建
|
|
||||||
if os.path.exists('build'):
|
|
||||||
shutil.rmtree('build')
|
|
||||||
print("✓ 清理build目录")
|
|
||||||
|
|
||||||
if os.path.exists('dist'):
|
|
||||||
shutil.rmtree('dist')
|
|
||||||
print("✓ 清理dist目录")
|
|
||||||
|
|
||||||
# 使用spec文件构建
|
|
||||||
cmd = 'python -m PyInstaller backend.spec --clean --noconfirm'
|
|
||||||
print(f"执行命令: {cmd}")
|
|
||||||
result = os.system(cmd)
|
|
||||||
|
|
||||||
if result == 0:
|
|
||||||
print("\n✓ 构建成功!")
|
|
||||||
print("exe文件位置: dist/BodyBalanceBackend.exe")
|
|
||||||
|
|
||||||
# 复制额外的文件
|
|
||||||
dist_path = Path('dist')
|
|
||||||
if dist_path.exists():
|
|
||||||
# 复制配置文件
|
|
||||||
config_files = ['../config.ini', '../config.json']
|
|
||||||
for config_file in config_files:
|
|
||||||
if os.path.exists(config_file):
|
|
||||||
shutil.copy2(config_file, dist_path)
|
|
||||||
print(f"✓ 复制配置文件: {config_file}")
|
|
||||||
|
|
||||||
# 创建启动脚本
|
|
||||||
start_script = dist_path / 'start_backend.bat'
|
|
||||||
with open(start_script, 'w', encoding='utf-8') as f:
|
|
||||||
f.write('@echo off\n')
|
|
||||||
f.write('echo 启动身体平衡评估系统后端服务...\n')
|
|
||||||
f.write('BodyBalanceBackend.exe\n')
|
|
||||||
f.write('pause\n')
|
|
||||||
print("✓ 创建启动脚本: start_backend.bat")
|
|
||||||
|
|
||||||
print("\n🎉 打包完成!")
|
|
||||||
print("运行方式:")
|
|
||||||
print("1. 直接运行: dist/BodyBalanceBackend.exe")
|
|
||||||
print("2. 使用脚本: dist/start_backend.bat")
|
|
||||||
else:
|
|
||||||
print("❌ 构建失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def check_required_files():
|
|
||||||
"""检查必需的文件是否存在"""
|
|
||||||
required_files = {
|
|
||||||
'app.py': '主应用文件',
|
|
||||||
'database.py': '数据库模块',
|
|
||||||
'device_manager.py': '设备管理模块',
|
|
||||||
'utils.py': '工具模块',
|
|
||||||
'../config.ini': '配置文件',
|
|
||||||
'../config.json': 'JSON配置文件'
|
|
||||||
}
|
|
||||||
|
|
||||||
required_dlls = {
|
|
||||||
'dll/femtobolt/bin/k4a.dll': 'K4A动态库',
|
|
||||||
'dll/femtobolt/bin/k4arecord.dll': 'K4A录制库',
|
|
||||||
'dll/femtobolt/bin/depthengine_2_0.dll': '深度引擎',
|
|
||||||
'dll/femtobolt/bin/OrbbecSDK.dll': 'Orbbec SDK',
|
|
||||||
'dll/femtobolt/bin/ob_usb.dll': 'Orbbec USB库',
|
|
||||||
'dll/femtobolt/bin/live555.dll': 'Live555库',
|
|
||||||
'dll/smitsense/SMiTSenseUsb-F3.0.dll': 'SMiTSense传感器库',
|
|
||||||
'dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml': 'Orbbec配置文件'
|
|
||||||
}
|
|
||||||
|
|
||||||
missing_files = []
|
|
||||||
|
|
||||||
print("检查必需文件...")
|
|
||||||
for file_path, description in required_files.items():
|
|
||||||
if os.path.exists(file_path):
|
|
||||||
print(f"✓ {description}: {file_path}")
|
|
||||||
else:
|
|
||||||
missing_files.append((file_path, description))
|
|
||||||
print(f"❌ {description}: {file_path} - 文件不存在")
|
|
||||||
|
|
||||||
print("\n检查DLL文件...")
|
|
||||||
for dll_path, description in required_dlls.items():
|
|
||||||
if os.path.exists(dll_path):
|
|
||||||
print(f"✓ {description}: {dll_path}")
|
|
||||||
else:
|
|
||||||
missing_files.append((dll_path, description))
|
|
||||||
print(f"❌ {description}: {dll_path} - 文件不存在")
|
|
||||||
|
|
||||||
if missing_files:
|
|
||||||
print(f"\n警告: 发现 {len(missing_files)} 个缺失文件")
|
|
||||||
print("缺失的文件可能导致打包失败或运行时错误")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print("\n✓ 所有必需文件检查通过")
|
|
||||||
return True
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主函数"""
|
|
||||||
print("=" * 50)
|
|
||||||
print("身体平衡评估系统 - 后端打包工具")
|
|
||||||
print("=" * 50)
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 检查当前目录
|
|
||||||
if not os.path.exists('app.py'):
|
|
||||||
print("❌ 请在backend目录下运行此脚本")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 检查必需文件
|
|
||||||
print("\n检查必需文件...")
|
|
||||||
if not check_required_files():
|
|
||||||
print("\n⚠️ 文件检查发现问题,但将继续执行打包")
|
|
||||||
print("如果打包失败,请检查上述缺失的文件")
|
|
||||||
input("按回车键继续...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 创建配置文件
|
|
||||||
create_spec_file()
|
|
||||||
# create_main_entry() # 注释掉以保留现有的main_exe.py修改
|
|
||||||
|
|
||||||
# 临时:为诊断脚本创建spec文件
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == 'debug':
|
|
||||||
create_debug_spec()
|
|
||||||
return
|
|
||||||
elif len(sys.argv) > 1 and sys.argv[1] == 'minimal':
|
|
||||||
create_minimal_spec()
|
|
||||||
return
|
|
||||||
|
|
||||||
# 构建exe
|
|
||||||
if build_exe():
|
|
||||||
print("\n✅ 所有操作完成!")
|
|
||||||
else:
|
|
||||||
print("\n❌ 构建过程中出现错误")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 错误: {e}")
|
|
||||||
|
|
||||||
input("\n按回车键退出...")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,244 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
app_minimal.py 打包脚本
|
|
||||||
使用PyInstaller将最小版本Flask应用程序打包成独立的exe文件
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def clean_build_dirs():
|
|
||||||
"""清理构建目录"""
|
|
||||||
print("清理构建目录...")
|
|
||||||
dirs_to_clean = ['build', 'dist', '__pycache__']
|
|
||||||
|
|
||||||
for dir_name in dirs_to_clean:
|
|
||||||
if os.path.exists(dir_name):
|
|
||||||
try:
|
|
||||||
shutil.rmtree(dir_name)
|
|
||||||
print(f"✓ 已删除 {dir_name}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"⚠️ 删除 {dir_name} 失败: {e}")
|
|
||||||
|
|
||||||
def create_minimal_spec():
|
|
||||||
"""创建app_minimal.py的PyInstaller spec文件"""
|
|
||||||
spec_content = '''
|
|
||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
|
|
||||||
block_cipher = None
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['app_minimal.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[
|
|
||||||
('dll/smitsense/SMiTSenseUsb-F3.0.dll', '.'), # SMiTSense传感器库
|
|
||||||
],
|
|
||||||
datas=[
|
|
||||||
('config.ini', '.'), # 配置文件
|
|
||||||
('data', 'data'), # 数据文件夹
|
|
||||||
],
|
|
||||||
hiddenimports=[
|
|
||||||
'flask',
|
|
||||||
'flask_cors',
|
|
||||||
'sqlite3',
|
|
||||||
'configparser',
|
|
||||||
'logging',
|
|
||||||
'threading',
|
|
||||||
'queue',
|
|
||||||
'base64',
|
|
||||||
'psutil',
|
|
||||||
'database',
|
|
||||||
'device_manager',
|
|
||||||
'utils',
|
|
||||||
'cv2',
|
|
||||||
'numpy',
|
|
||||||
],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[
|
|
||||||
'flask_socketio',
|
|
||||||
'socketio',
|
|
||||||
'engineio',
|
|
||||||
'eventlet',
|
|
||||||
'gevent',
|
|
||||||
],
|
|
||||||
win_no_prefer_redirects=False,
|
|
||||||
win_private_assemblies=False,
|
|
||||||
cipher=block_cipher,
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='BodyBalanceBackend',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=True,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch=None,
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon=None
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
|
|
||||||
with open('app_minimal.spec', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(spec_content)
|
|
||||||
|
|
||||||
print("✓ 已创建 app_minimal.spec 文件")
|
|
||||||
|
|
||||||
def build_exe():
|
|
||||||
"""构建exe文件"""
|
|
||||||
print("\n开始构建exe文件...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 使用PyInstaller构建
|
|
||||||
cmd = [sys.executable, '-m', 'PyInstaller', 'app_minimal.spec', '--clean', '--noconfirm']
|
|
||||||
print(f"执行命令: {' '.join(cmd)}")
|
|
||||||
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
print("✓ 构建成功!")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"✗ 构建失败")
|
|
||||||
print(f"错误输出: {result.stderr}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"✗ 构建过程出错: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def create_startup_script():
|
|
||||||
"""创建启动脚本"""
|
|
||||||
startup_script = '''@echo off
|
|
||||||
echo 启动身体平衡评估系统后端服务(最小版本)...
|
|
||||||
echo.
|
|
||||||
echo 服务信息:
|
|
||||||
echo - HTTP API: http://localhost:5000
|
|
||||||
echo - 管理界面: http://localhost:5000
|
|
||||||
echo.
|
|
||||||
echo 按Ctrl+C停止服务
|
|
||||||
echo.
|
|
||||||
"BodyBalanceBackend.exe"
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo.
|
|
||||||
echo 服务启动失败,请检查错误信息
|
|
||||||
pause
|
|
||||||
)
|
|
||||||
'''
|
|
||||||
|
|
||||||
dist_dir = 'dist'
|
|
||||||
if not os.path.exists(dist_dir):
|
|
||||||
os.makedirs(dist_dir)
|
|
||||||
|
|
||||||
with open(os.path.join(dist_dir, 'start_backend.bat'), 'w', encoding='utf-8') as f:
|
|
||||||
f.write(startup_script)
|
|
||||||
|
|
||||||
print("✓ 创建启动脚本: dist/start_backend.bat")
|
|
||||||
|
|
||||||
def copy_config_files():
|
|
||||||
"""复制配置文件到dist目录"""
|
|
||||||
print("复制配置文件...")
|
|
||||||
|
|
||||||
config_files = ['config.ini']
|
|
||||||
dist_dir = 'dist'
|
|
||||||
|
|
||||||
if not os.path.exists(dist_dir):
|
|
||||||
os.makedirs(dist_dir)
|
|
||||||
|
|
||||||
for config_file in config_files:
|
|
||||||
if os.path.exists(config_file):
|
|
||||||
try:
|
|
||||||
shutil.copy2(config_file, dist_dir)
|
|
||||||
print(f"✓ 已复制 {config_file}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"⚠️ 复制 {config_file} 失败: {e}")
|
|
||||||
else:
|
|
||||||
print(f"⚠️ 配置文件不存在: {config_file}")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主函数"""
|
|
||||||
print("=" * 60)
|
|
||||||
print("身体平衡评估系统 - app_minimal.py 打包工具")
|
|
||||||
print("=" * 60)
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 检查当前目录
|
|
||||||
if not os.path.exists('app_minimal.py'):
|
|
||||||
print("✗ 错误: 找不到 app_minimal.py 文件")
|
|
||||||
print("请确保在backend目录下运行此脚本")
|
|
||||||
input("按回车键退出...")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 清理构建目录
|
|
||||||
clean_build_dirs()
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 创建spec文件
|
|
||||||
print("创建PyInstaller配置...")
|
|
||||||
create_minimal_spec()
|
|
||||||
print()
|
|
||||||
|
|
||||||
# 构建exe
|
|
||||||
if build_exe():
|
|
||||||
print()
|
|
||||||
print("后处理...")
|
|
||||||
|
|
||||||
# 检查生成的exe文件
|
|
||||||
exe_path = 'dist/BodyBalanceBackend.exe'
|
|
||||||
if os.path.exists(exe_path):
|
|
||||||
print(f"✓ exe文件位置: {exe_path}")
|
|
||||||
|
|
||||||
# 复制配置文件
|
|
||||||
copy_config_files()
|
|
||||||
|
|
||||||
# 创建启动脚本
|
|
||||||
create_startup_script()
|
|
||||||
|
|
||||||
print()
|
|
||||||
print("🎉 打包完成!")
|
|
||||||
print()
|
|
||||||
print("输出文件:")
|
|
||||||
print(f"- 可执行文件: {exe_path}")
|
|
||||||
print("- 启动脚本: dist/start_backend.bat")
|
|
||||||
print("- 配置文件: dist/config.ini")
|
|
||||||
print()
|
|
||||||
print("使用方式:")
|
|
||||||
print("1. 直接运行: dist/BodyBalanceBackend.exe")
|
|
||||||
print("2. 使用脚本: dist/start_backend.bat")
|
|
||||||
print()
|
|
||||||
print("服务地址: http://localhost:5000")
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("✗ 错误: 未找到生成的exe文件")
|
|
||||||
else:
|
|
||||||
print("\n✗ 打包失败")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\n✗ 打包过程出错: {e}")
|
|
||||||
|
|
||||||
print()
|
|
||||||
input("按回车键退出...")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -0,0 +1,60 @@
|
|||||||
|
[08/15 11:08:00.007828][debug][1996][Context.cpp:30] Context creating, work_dir=D:\Trae_space\BodyBalanceEvaluation\backend\dist
|
||||||
|
[08/15 11:08:00.007990][debug][1996][Context.cpp:49] Config file version=1.1
|
||||||
|
[08/15 11:08:00.008057][debug][1996][FrameBufferManager.cpp:23] Max global frame buffer size updated! size=2048.000MB
|
||||||
|
[08/15 11:08:00.008095][info][1996][Context.cpp:68] Context created with config: default config!
|
||||||
|
[08/15 11:08:00.008265][info][1996][Context.cpp:73] Work directory=D:\Trae_space\BodyBalanceEvaluation\backend\dist, SDK version=v1.10.11-20240724-aeaa107e5
|
||||||
|
[08/15 11:08:00.008455][debug][1996][DeviceManager.cpp:30] DeviceManager init ...
|
||||||
|
[08/15 11:08:00.008674][info][1996][MfPal.cpp:105] createObPal: create WinPal!
|
||||||
|
[08/15 11:08:00.008795][debug][1996][MfPal.cpp:110] WmfPal init ...
|
||||||
|
[08/15 11:08:00.039058][debug][1996][MfPal.cpp:117] WmfPal created!
|
||||||
|
[08/15 11:08:00.039105][debug][1996][DeviceManager.cpp:34] Enable USB Device Enumerator ...
|
||||||
|
[08/15 11:08:00.066384][debug][1996][EnumeratorLibusb.cpp:321] queryDevicesInfo done!
|
||||||
|
[08/15 11:08:00.066623][debug][1996][MfPal.cpp:216] Create WinEventDeviceWatcher!
|
||||||
|
[08/15 11:08:00.066791][debug][1996][UsbDeviceEnumerator.cpp:78] No matched usb device found!
|
||||||
|
[08/15 11:08:00.066833][info][1996][DeviceManager.cpp:15] Current found device(s): (0)
|
||||||
|
[08/15 11:08:00.066975][debug][1996][DeviceManager.cpp:52] DeviceManager construct done!
|
||||||
|
[08/15 11:08:00.067012][debug][1996][Context.cpp:81] Context destroying ...
|
||||||
|
[08/15 11:08:00.067024][debug][1996][DeviceManager.cpp:56] DeviceManager destroy ...
|
||||||
|
[08/15 11:08:00.067033][debug][1996][DeviceManager.cpp:64] DeviceManager Destructors done
|
||||||
|
[08/15 11:08:00.214042][debug][1996][MfPal.cpp:128] WmfPal destroyed!
|
||||||
|
[08/15 11:08:00.214700][info][1996][Context.cpp:84] Context destroyed
|
||||||
|
[08/15 11:11:00.761881][debug][12088][Context.cpp:30] Context creating, work_dir=D:\Trae_space\BodyBalanceEvaluation\backend\dist
|
||||||
|
[08/15 11:11:00.762009][debug][12088][Context.cpp:49] Config file version=1.1
|
||||||
|
[08/15 11:11:00.762057][debug][12088][FrameBufferManager.cpp:23] Max global frame buffer size updated! size=2048.000MB
|
||||||
|
[08/15 11:11:00.762081][info][12088][Context.cpp:68] Context created with config: default config!
|
||||||
|
[08/15 11:11:00.762166][info][12088][Context.cpp:73] Work directory=D:\Trae_space\BodyBalanceEvaluation\backend\dist, SDK version=v1.10.11-20240724-aeaa107e5
|
||||||
|
[08/15 11:11:00.762526][debug][12088][DeviceManager.cpp:30] DeviceManager init ...
|
||||||
|
[08/15 11:11:00.762542][info][12088][MfPal.cpp:105] createObPal: create WinPal!
|
||||||
|
[08/15 11:11:00.763193][debug][12088][MfPal.cpp:110] WmfPal init ...
|
||||||
|
[08/15 11:11:00.790457][debug][12088][MfPal.cpp:117] WmfPal created!
|
||||||
|
[08/15 11:11:00.790495][debug][12088][DeviceManager.cpp:34] Enable USB Device Enumerator ...
|
||||||
|
[08/15 11:11:00.815795][debug][12088][EnumeratorLibusb.cpp:321] queryDevicesInfo done!
|
||||||
|
[08/15 11:11:00.816035][debug][12088][MfPal.cpp:216] Create WinEventDeviceWatcher!
|
||||||
|
[08/15 11:11:00.816179][debug][12088][UsbDeviceEnumerator.cpp:78] No matched usb device found!
|
||||||
|
[08/15 11:11:00.816215][info][12088][DeviceManager.cpp:15] Current found device(s): (0)
|
||||||
|
[08/15 11:11:00.816652][debug][12088][DeviceManager.cpp:52] DeviceManager construct done!
|
||||||
|
[08/15 11:11:00.816690][debug][12088][Context.cpp:81] Context destroying ...
|
||||||
|
[08/15 11:11:00.816703][debug][12088][DeviceManager.cpp:56] DeviceManager destroy ...
|
||||||
|
[08/15 11:11:00.816712][debug][12088][DeviceManager.cpp:64] DeviceManager Destructors done
|
||||||
|
[08/15 11:11:00.960762][debug][12088][MfPal.cpp:128] WmfPal destroyed!
|
||||||
|
[08/15 11:11:00.961818][info][12088][Context.cpp:84] Context destroyed
|
||||||
|
[08/15 11:17:16.080184][debug][27992][Context.cpp:30] Context creating, work_dir=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend
|
||||||
|
[08/15 11:17:16.080326][debug][27992][Context.cpp:49] Config file version=1.1
|
||||||
|
[08/15 11:17:16.080380][debug][27992][FrameBufferManager.cpp:23] Max global frame buffer size updated! size=2048.000MB
|
||||||
|
[08/15 11:17:16.080407][info][27992][Context.cpp:68] Context created with config: default config!
|
||||||
|
[08/15 11:17:16.080471][info][27992][Context.cpp:73] Work directory=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend, SDK version=v1.10.11-20240724-aeaa107e5
|
||||||
|
[08/15 11:17:16.080501][debug][27992][DeviceManager.cpp:30] DeviceManager init ...
|
||||||
|
[08/15 11:17:16.080518][info][27992][MfPal.cpp:105] createObPal: create WinPal!
|
||||||
|
[08/15 11:17:16.080537][debug][27992][MfPal.cpp:110] WmfPal init ...
|
||||||
|
[08/15 11:17:16.106317][debug][27992][MfPal.cpp:117] WmfPal created!
|
||||||
|
[08/15 11:17:16.106358][debug][27992][DeviceManager.cpp:34] Enable USB Device Enumerator ...
|
||||||
|
[08/15 11:17:16.132353][debug][27992][EnumeratorLibusb.cpp:321] queryDevicesInfo done!
|
||||||
|
[08/15 11:17:16.132619][debug][27992][MfPal.cpp:216] Create WinEventDeviceWatcher!
|
||||||
|
[08/15 11:17:16.132822][debug][27992][UsbDeviceEnumerator.cpp:78] No matched usb device found!
|
||||||
|
[08/15 11:17:16.132867][info][27992][DeviceManager.cpp:15] Current found device(s): (0)
|
||||||
|
[08/15 11:17:16.132889][debug][27992][DeviceManager.cpp:52] DeviceManager construct done!
|
||||||
|
[08/15 11:17:16.132917][debug][27992][Context.cpp:81] Context destroying ...
|
||||||
|
[08/15 11:17:16.132929][debug][27992][DeviceManager.cpp:56] DeviceManager destroy ...
|
||||||
|
[08/15 11:17:16.132938][debug][27992][DeviceManager.cpp:64] DeviceManager Destructors done
|
||||||
|
[08/15 11:17:16.274031][debug][27992][MfPal.cpp:128] WmfPal destroyed!
|
||||||
|
[08/15 11:17:16.274987][info][27992][Context.cpp:84] Context destroyed
|
Binary file not shown.
@ -0,0 +1,16 @@
|
|||||||
|
@echo off
|
||||||
|
echo 启动身体平衡评估系统后端服务(完整版)...
|
||||||
|
echo.
|
||||||
|
echo 服务信息:
|
||||||
|
echo - HTTP API: http://localhost:5000
|
||||||
|
echo - SocketIO: ws://localhost:5000
|
||||||
|
echo - 管理界面: http://localhost:5000
|
||||||
|
echo.
|
||||||
|
echo 按Ctrl+C停止服务
|
||||||
|
echo.
|
||||||
|
"BodyBalanceBackend.exe"
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo.
|
||||||
|
echo 服务启动失败,请检查错误信息
|
||||||
|
pause
|
||||||
|
)
|
50
frontend/src/renderer/package-lock.json
generated
50
frontend/src/renderer/package-lock.json
generated
@ -12,6 +12,7 @@
|
|||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"element-plus": "^2.3.9",
|
"element-plus": "^2.3.9",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
"pinia": "^2.1.6",
|
"pinia": "^2.1.6",
|
||||||
"socket.io-client": "^4.7.2",
|
"socket.io-client": "^4.7.2",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
@ -1728,6 +1729,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-arraybuffer": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@ -2330,6 +2340,15 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-line-break": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
||||||
@ -3872,6 +3891,19 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html2canvas": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"css-line-break": "^2.1.0",
|
||||||
|
"text-segmentation": "^1.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/http-cache-semantics": {
|
"node_modules/http-cache-semantics": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
|
||||||
@ -5749,6 +5781,15 @@
|
|||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/text-segmentation": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.2.5",
|
"version": "0.2.5",
|
||||||
"resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.5.tgz",
|
"resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.5.tgz",
|
||||||
@ -5888,6 +5929,15 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/utrie": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-arraybuffer": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/validate-npm-package-license": {
|
"node_modules/validate-npm-package-license": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
"resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"element-plus": "^2.3.9",
|
"element-plus": "^2.3.9",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
"pinia": "^2.1.6",
|
"pinia": "^2.1.6",
|
||||||
"socket.io-client": "^4.7.2",
|
"socket.io-client": "^4.7.2",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
@ -53,7 +54,9 @@
|
|||||||
{
|
{
|
||||||
"from": "../../../backend/dist",
|
"from": "../../../backend/dist",
|
||||||
"to": "resources/backend",
|
"to": "resources/backend",
|
||||||
"filter": ["**/*"]
|
"filter": [
|
||||||
|
"**/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"win": {
|
"win": {
|
||||||
|
Loading…
Reference in New Issue
Block a user