#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 平衡体态检测系统 - 后端服务 基于Flask的本地API服务 """ import os import sys import json import time import threading from datetime import datetime from flask import Flask, request, jsonify, send_file from flask_cors import CORS import sqlite3 import logging from pathlib import Path # 添加当前目录到Python路径 sys.path.append(os.path.dirname(os.path.abspath(__file__))) # 导入自定义模块 from database import DatabaseManager from device_manager import DeviceManager from detection_engine import DetectionEngine from data_processor import DataProcessor # 配置日志 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://localhost:3001', 'file://*']) # 全局变量 db_manager = None device_manager = None detection_engine = None data_processor = None current_detection = None detection_thread = None def init_app(): """初始化应用""" global db_manager, device_manager, detection_engine, data_processor try: # 创建必要的目录 os.makedirs('logs', exist_ok=True) os.makedirs('data', exist_ok=True) os.makedirs('exports', exist_ok=True) os.makedirs('videos', exist_ok=True) # 初始化数据库 db_manager = DatabaseManager('data/body_balance.db') db_manager.init_database() # 初始化设备管理器 device_manager = DeviceManager() # 初始化检测引擎 detection_engine = DetectionEngine() # 初始化数据处理器 data_processor = DataProcessor() logger.info('应用初始化完成') except Exception as e: logger.error(f'应用初始化失败: {e}') raise # ==================== 基础API ==================== @app.route('/health', methods=['GET']) def health_check(): """健康检查""" return jsonify({ 'status': 'ok', 'timestamp': datetime.now().isoformat(), 'version': '1.0.0' }) @app.route('/api/health', methods=['GET']) def api_health_check(): """API健康检查""" return jsonify({ 'status': 'ok', 'timestamp': datetime.now().isoformat(), 'version': '1.0.0' }) # ==================== 认证API ==================== @app.route('/api/auth/login', methods=['POST']) def login(): """用户登录""" try: data = request.get_json() username = data.get('username') password = data.get('password') remember = data.get('remember', False) # 简单的模拟登录验证 if username and password: # 这里可以添加真实的用户验证逻辑 # 目前使用模拟数据 user_data = { 'id': 1, 'username': username, 'name': '医生', 'role': 'doctor', 'avatar': '' } # 生成简单的token(实际项目中应使用JWT等安全token) token = f"token_{username}_{int(time.time())}" return jsonify({ 'success': True, 'data': { 'token': token, 'user': user_data }, 'message': '登录成功' }) else: return jsonify({ 'success': False, 'message': '用户名或密码不能为空' }), 400 except Exception as e: logger.error(f'登录失败: {e}') return jsonify({'success': False, 'message': '登录失败'}), 500 @app.route('/api/auth/register', methods=['POST']) def register(): """用户注册""" try: data = request.get_json() username = data.get('username') password = data.get('password') email = data.get('email') # 简单的模拟注册 if username and password: return jsonify({ 'success': True, 'message': '注册成功,请登录' }) else: return jsonify({ 'success': False, 'message': '用户名和密码不能为空' }), 400 except Exception as e: logger.error(f'注册失败: {e}') return jsonify({'success': False, 'message': '注册失败'}), 500 @app.route('/api/auth/logout', methods=['POST']) def logout(): """用户退出""" try: return jsonify({ 'success': True, 'message': '退出成功' }) except Exception as e: logger.error(f'退出失败: {e}') return jsonify({'success': False, 'message': '退出失败'}), 500 @app.route('/api/auth/verify', methods=['GET']) def verify_token(): """验证token""" try: # 简单的token验证(实际项目中应验证JWT等) auth_header = request.headers.get('Authorization') if auth_header and auth_header.startswith('Bearer '): token = auth_header.split(' ')[1] # 这里可以添加真实的token验证逻辑 return jsonify({ 'success': True, 'data': {'valid': True} }) else: return jsonify({ 'success': True, 'data': {'valid': True} # 暂时总是返回有效 }) except Exception as e: logger.error(f'验证token失败: {e}') return jsonify({'success': False, 'message': '验证失败'}), 500 @app.route('/api/auth/forgot-password', methods=['POST']) def forgot_password(): """忘记密码""" try: data = request.get_json() email = data.get('email') if email: return jsonify({ 'success': True, 'message': '重置密码邮件已发送' }) else: return jsonify({ 'success': False, 'message': '邮箱不能为空' }), 400 except Exception as e: logger.error(f'忘记密码处理失败: {e}') return jsonify({'success': False, 'message': '处理失败'}), 500 @app.route('/api/auth/reset-password', methods=['POST']) def reset_password(): """重置密码""" try: data = request.get_json() token = data.get('token') password = data.get('password') if token and password: return jsonify({ 'success': True, 'message': '密码重置成功' }) else: return jsonify({ 'success': False, 'message': '参数不完整' }), 400 except Exception as e: logger.error(f'重置密码失败: {e}') return jsonify({'success': False, 'message': '重置失败'}), 500 @app.route('/api/system/info', methods=['GET']) def get_system_info(): """获取系统信息""" try: info = { 'version': '1.0.0', 'start_time': datetime.now().isoformat(), 'database_status': 'connected' if db_manager else 'disconnected', 'device_count': len(device_manager.get_connected_devices()) if device_manager else 0 } return jsonify({'success': True, 'data': info}) except Exception as e: logger.error(f'获取系统信息失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 设备管理API ==================== @app.route('/api/devices/status', methods=['GET']) def get_device_status(): """获取设备状态""" try: if not device_manager: return jsonify({'camera': False, 'imu': False, 'pressure': False}) status = device_manager.get_device_status() return jsonify(status) except Exception as e: logger.error(f'获取设备状态失败: {e}') return jsonify({'camera': False, 'imu': False, 'pressure': False}) @app.route('/api/devices/refresh', methods=['POST']) def refresh_devices(): """刷新设备连接""" try: if device_manager: device_manager.refresh_devices() return jsonify({'success': True, 'message': '设备已刷新'}) except Exception as e: logger.error(f'刷新设备失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/devices/calibrate', methods=['POST']) def calibrate_devices(): """校准设备""" try: if device_manager: result = device_manager.calibrate_devices() return jsonify({'success': True, 'data': result}) return jsonify({'success': False, 'error': '设备管理器未初始化'}), 500 except Exception as e: logger.error(f'设备校准失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 患者管理API ==================== @app.route('/api/patients', methods=['GET']) def get_patients(): """获取患者列表""" try: page = int(request.args.get('page', 1)) size = int(request.args.get('size', 10)) keyword = request.args.get('keyword', '') patients = db_manager.get_patients(page, size, keyword) total = db_manager.get_patients_count(keyword) return jsonify({ 'success': True, 'data': { 'patients': patients, 'total': total, 'page': page, 'size': size } }) except Exception as e: logger.error(f'获取患者列表失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/patients', methods=['POST']) def create_patient(): """创建患者""" try: data = request.get_json() patient_id = db_manager.create_patient(data) return jsonify({ 'success': True, 'data': {'patient_id': patient_id}, 'message': '患者创建成功' }) except Exception as e: logger.error(f'创建患者失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/patients/', methods=['PUT']) def update_patient(patient_id): """更新患者信息""" try: data = request.get_json() db_manager.update_patient(patient_id, data) return jsonify({'success': True, 'message': '患者信息更新成功'}) except Exception as e: logger.error(f'更新患者信息失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/patients/', methods=['DELETE']) def delete_patient(patient_id): """删除患者""" try: db_manager.delete_patient(patient_id) return jsonify({'success': True, 'message': '患者删除成功'}) except Exception as e: logger.error(f'删除患者失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 检测管理API ==================== @app.route('/api/detection/start', methods=['POST']) def start_detection(): """开始检测""" global current_detection, detection_thread try: if current_detection and current_detection.get('status') == 'running': return jsonify({'success': False, 'error': '检测已在进行中'}), 400 data = request.get_json() patient_id = data.get('patientId') settings = data.get('settings', {}) # 创建检测会话 session_id = db_manager.create_detection_session(patient_id, settings) # 初始化检测状态 current_detection = { 'session_id': session_id, 'patient_id': patient_id, 'status': 'running', 'start_time': datetime.now(), 'settings': settings, 'data_points': 0 } # 启动检测线程 detection_thread = threading.Thread( target=run_detection, args=(session_id, settings) ) detection_thread.start() return jsonify({ 'success': True, 'data': {'session_id': session_id}, 'message': '检测已开始' }) except Exception as e: logger.error(f'开始检测失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/detection/stop', methods=['POST']) def stop_detection(): """停止检测""" global current_detection try: if not current_detection or current_detection.get('status') != 'running': return jsonify({'success': False, 'error': '没有正在进行的检测'}), 400 # 更新检测状态 current_detection['status'] = 'stopped' current_detection['end_time'] = datetime.now() # 等待检测线程结束 if detection_thread and detection_thread.is_alive(): detection_thread.join(timeout=5) return jsonify({'success': True, 'message': '检测已停止'}) except Exception as e: logger.error(f'停止检测失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/detection/status', methods=['GET']) def get_detection_status(): """获取检测状态""" try: if not current_detection: return jsonify({ 'success': True, 'data': {'status': 'idle'} }) # 计算运行时间 if current_detection.get('status') == 'running': elapsed = (datetime.now() - current_detection['start_time']).total_seconds() current_detection['elapsed_time'] = int(elapsed) return jsonify({ 'success': True, 'data': current_detection }) except Exception as e: logger.error(f'获取检测状态失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/detection/data', methods=['GET']) def get_realtime_data(): """获取实时检测数据""" try: if not current_detection or current_detection.get('status') != 'running': return jsonify({'success': False, 'error': '没有正在进行的检测'}) # 获取最新的检测数据 session_id = current_detection['session_id'] data = detection_engine.get_latest_data(session_id) return jsonify({ 'success': True, 'data': data }) except Exception as e: logger.error(f'获取实时数据失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 数据分析API ==================== @app.route('/api/analysis/session/', methods=['GET']) def analyze_session(session_id): """分析检测会话数据""" try: # 获取会话数据 session_data = db_manager.get_session_data(session_id) if not session_data: return jsonify({'success': False, 'error': '会话不存在'}), 404 # 进行数据分析 analysis_result = data_processor.analyze_session(session_data) # 保存分析结果 db_manager.save_analysis_result(session_id, analysis_result) return jsonify({ 'success': True, 'data': analysis_result }) except Exception as e: logger.error(f'分析会话数据失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/export/report/', methods=['GET']) def export_report(session_id): """导出检测报告""" try: # 生成报告 report_path = data_processor.generate_report(session_id) if not os.path.exists(report_path): return jsonify({'success': False, 'error': '报告生成失败'}), 500 return send_file( report_path, as_attachment=True, download_name=f'detection_report_{session_id}.pdf' ) except Exception as e: logger.error(f'导出报告失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 # ==================== 历史记录API ==================== @app.route('/api/history/sessions', methods=['GET']) def get_detection_sessions(): """获取检测会话历史""" try: page = int(request.args.get('page', 1)) size = int(request.args.get('size', 10)) patient_id = request.args.get('patient_id') sessions = db_manager.get_detection_sessions(page, size, patient_id) total = db_manager.get_sessions_count(patient_id) return jsonify({ 'success': True, 'data': { 'sessions': sessions, 'total': total, 'page': page, 'size': size } }) except Exception as e: logger.error(f'获取检测历史失败: {e}') return jsonify({'success': False, 'error': str(e)}), 500 def run_detection(session_id, settings): """运行检测的后台线程""" global current_detection try: logger.info(f'开始检测会话: {session_id}') # 检测循环 while (current_detection and current_detection.get('status') == 'running'): # 采集数据 if device_manager: data = device_manager.collect_data() if data: # 保存数据到数据库 db_manager.save_detection_data(session_id, data) current_detection['data_points'] += 1 # 根据采样频率控制循环间隔 frequency = settings.get('frequency', 60) time.sleep(1.0 / frequency) # 检查是否达到设定时长 duration = settings.get('duration', 0) if duration > 0: elapsed = (datetime.now() - current_detection['start_time']).total_seconds() if elapsed >= duration: current_detection['status'] = 'completed' break # 更新会话状态 if current_detection: db_manager.update_session_status( session_id, current_detection['status'], current_detection.get('data_points', 0) ) logger.info(f'检测会话完成: {session_id}') except Exception as e: logger.error(f'检测线程异常: {e}') if current_detection: current_detection['status'] = 'error' current_detection['error'] = str(e) # ==================== 错误处理 ==================== @app.errorhandler(404) def not_found(error): return jsonify({'success': False, 'error': 'API接口不存在'}), 404 @app.errorhandler(500) def internal_error(error): return jsonify({'success': False, 'error': '服务器内部错误'}), 500 if __name__ == '__main__': try: # 初始化应用 init_app() # 启动Flask服务 logger.info('启动后端服务...') app.run( host='127.0.0.1', port=5000, debug=False, threaded=True ) except KeyboardInterrupt: logger.info('服务被用户中断') except Exception as e: logger.error(f'服务启动失败: {e}') sys.exit(1) finally: logger.info('后端服务已停止')