#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 数据库管理模块 负责SQLite数据库的创建、连接和数据操作 """ import sqlite3 import json import uuid from datetime import datetime, timezone, timedelta from typing import List, Dict, Optional, Any import logging logger = logging.getLogger(__name__) class DatabaseManager: """数据库管理器""" def __init__(self, db_path: str): self.db_path = db_path self.connection = None # 设置中国上海时区 (UTC+8) self.china_tz = timezone(timedelta(hours=8)) def get_china_time(self) -> str: """获取中国时区的当前时间字符串""" return datetime.now(self.china_tz).strftime('%Y-%m-%d %H:%M:%S') def get_connection(self) -> sqlite3.Connection: """获取数据库连接""" if not self.connection: self.connection = sqlite3.connect( self.db_path, check_same_thread=False, timeout=30.0 ) self.connection.row_factory = sqlite3.Row return self.connection def init_database(self): """初始化数据库表结构""" conn = self.get_connection() cursor = conn.cursor() try: # 创建患者表 cursor.execute(''' CREATE TABLE IF NOT EXISTS patients ( id TEXT PRIMARY KEY, name TEXT NOT NULL, gender TEXT, age INTEGER, birth_date TIMESTAMP, nationality TEXT, height REAL, weight REAL, phone TEXT, shoe_size TEXT, medical_history TEXT, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # 创建检测会话表 cursor.execute(''' CREATE TABLE IF NOT EXISTS detection_sessions ( id TEXT PRIMARY KEY, patient_id TEXT NOT NULL, start_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, end_time TIMESTAMP, duration INTEGER, frequency INTEGER, status TEXT DEFAULT 'created', settings TEXT, data_points INTEGER DEFAULT 0, video_path TEXT, notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (patient_id) REFERENCES patients (id) ) ''') # 创建检测数据表 cursor.execute(''' CREATE TABLE IF NOT EXISTS detection_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, data_type TEXT NOT NULL, data_value TEXT NOT NULL, FOREIGN KEY (session_id) REFERENCES detection_sessions (id) ) ''') # 创建分析结果表 cursor.execute(''' CREATE TABLE IF NOT EXISTS analysis_results ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, analysis_type TEXT NOT NULL, result_data TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (session_id) REFERENCES detection_sessions (id) ) ''') # 创建系统设置表 cursor.execute(''' CREATE TABLE IF NOT EXISTS system_settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL, description TEXT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # 创建用户表 cursor.execute(''' CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, name TEXT NOT NULL, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, register_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, is_active BOOLEAN DEFAULT 0, user_type TEXT DEFAULT 'user', phone TEXT DEFAULT '', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # 创建索引 cursor.execute('CREATE INDEX IF NOT EXISTS idx_patients_name ON patients (name)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_sessions_patient ON detection_sessions (patient_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_sessions_time ON detection_sessions (start_time)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_data_session ON detection_data (session_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_data_timestamp ON detection_data (timestamp)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_users_username ON users (username)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_users_type ON users (user_type)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_users_phone ON users (phone)') # 插入默认管理员账户(如果不存在) cursor.execute('SELECT COUNT(*) FROM users WHERE username = ?', ('admin',)) admin_exists = cursor.fetchone()[0] if admin_exists == 0: admin_id = str(uuid.uuid4()) # 默认密码为 admin123,明文存储 admin_password = 'admin123' cursor.execute(''' INSERT INTO users (id, name, username, password, is_active, user_type) VALUES (?, ?, ?, ?, ?, ?) ''', (admin_id, '系统管理员', 'admin', admin_password, 1, 'admin')) logger.info('创建默认管理员账户: admin/admin123') conn.commit() logger.info('数据库初始化完成') except Exception as e: conn.rollback() logger.error(f'数据库初始化失败: {e}') raise # ==================== 患者管理 ==================== def create_patient(self, patient_data: Dict[str, Any]) -> str: """创建患者记录""" conn = self.get_connection() cursor = conn.cursor() try: patient_id = str(uuid.uuid4()) # 使用中国时区时间 china_time = self.get_china_time() cursor.execute(''' INSERT INTO patients ( id, name, gender, age, birth_date, height, weight, phone, shoe_size, medical_history, notes, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( patient_id, patient_data.get('name'), patient_data.get('gender'), patient_data.get('age'), patient_data.get('birth_date'), patient_data.get('height'), patient_data.get('weight'), patient_data.get('phone'), patient_data.get('shoe_size'), patient_data.get('medical_history'), patient_data.get('notes'), china_time, china_time )) conn.commit() logger.info(f'创建患者记录: {patient_id}') return patient_id except Exception as e: conn.rollback() logger.error(f'创建患者记录失败: {e}') raise def get_patients(self, page: int = 1, size: int = 10, keyword: str = '') -> List[Dict]: """获取患者列表""" conn = self.get_connection() cursor = conn.cursor() try: offset = (page - 1) * size if keyword: cursor.execute(''' SELECT * FROM patients WHERE name LIKE ? OR phone LIKE ? ORDER BY created_at DESC LIMIT ? OFFSET ? ''', (f'%{keyword}%', f'%{keyword}%', size, offset)) else: cursor.execute(''' SELECT * FROM patients ORDER BY created_at DESC LIMIT ? OFFSET ? ''', (size, offset)) rows = cursor.fetchall() return [dict(row) for row in rows] except Exception as e: logger.error(f'获取患者列表失败: {e}') raise def get_patients_count(self, keyword: str = '') -> int: """获取患者总数""" conn = self.get_connection() cursor = conn.cursor() try: if keyword: cursor.execute(''' SELECT COUNT(*) FROM patients WHERE name LIKE ? OR phone LIKE ? ''', (f'%{keyword}%', f'%{keyword}%')) else: cursor.execute('SELECT COUNT(*) FROM patients') return cursor.fetchone()[0] except Exception as e: logger.error(f'获取患者总数失败: {e}') return 0 def get_patient(self, patient_id: str) -> Optional[Dict]: """获取单个患者信息""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute('SELECT * FROM patients WHERE id = ?', (patient_id,)) row = cursor.fetchone() return dict(row) if row else None except Exception as e: logger.error(f'获取患者信息失败: {e}') return None def update_patient(self, patient_id: str, patient_data: Dict[str, Any]): """更新患者信息""" conn = self.get_connection() cursor = conn.cursor() try: # 使用中国时区时间 china_time = self.get_china_time() cursor.execute(''' UPDATE patients SET name = ?, gender = ?, age = ?, birth_date = ?, height = ?, weight = ?, phone = ?, shoe_size = ?, medical_history = ?, notes = ?, updated_at = ? WHERE id = ? ''', ( patient_data.get('name'), patient_data.get('gender'), patient_data.get('age'), patient_data.get('birth_date'), patient_data.get('height'), patient_data.get('weight'), patient_data.get('phone'), patient_data.get('shoe_size'), patient_data.get('medical_history'), patient_data.get('notes'), china_time, patient_id )) conn.commit() logger.info(f'更新患者信息: {patient_id}') except Exception as e: conn.rollback() logger.error(f'更新患者信息失败: {e}') raise def delete_patient(self, patient_id: str): """删除患者记录""" conn = self.get_connection() cursor = conn.cursor() try: # 删除相关的检测数据 cursor.execute(''' DELETE FROM detection_data WHERE session_id IN ( SELECT id FROM detection_sessions WHERE patient_id = ? ) ''', (patient_id,)) # 删除相关的分析结果 cursor.execute(''' DELETE FROM analysis_results WHERE session_id IN ( SELECT id FROM detection_sessions WHERE patient_id = ? ) ''', (patient_id,)) # 删除检测会话 cursor.execute('DELETE FROM detection_sessions WHERE patient_id = ?', (patient_id,)) # 删除患者记录 cursor.execute('DELETE FROM patients WHERE id = ?', (patient_id,)) conn.commit() logger.info(f'删除患者记录: {patient_id}') except Exception as e: conn.rollback() logger.error(f'删除患者记录失败: {e}') raise # ==================== 检测会话管理 ==================== def create_detection_session(self, patient_id: str, settings: Dict[str, Any]) -> str: """创建检测会话""" conn = self.get_connection() cursor = conn.cursor() try: session_id = str(uuid.uuid4()) # 使用中国时区时间 china_time = self.get_china_time() cursor.execute(''' INSERT INTO detection_sessions ( id, patient_id, duration, frequency, settings, status, start_time, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ''', ( session_id, patient_id, settings.get('duration', 60), settings.get('frequency', 60), json.dumps(settings), 'created', china_time, china_time )) conn.commit() logger.info(f'创建检测会话: {session_id}') return session_id except Exception as e: conn.rollback() logger.error(f'创建检测会话失败: {e}') raise def update_session_status(self, session_id: str, status: str, data_points: int = 0): """更新会话状态""" conn = self.get_connection() cursor = conn.cursor() try: if status in ['completed', 'stopped', 'error']: # 使用中国时区时间 china_time = self.get_china_time() cursor.execute(''' UPDATE detection_sessions SET status = ?, data_points = ?, end_time = ? WHERE id = ? ''', (status, data_points, china_time, session_id)) else: cursor.execute(''' UPDATE detection_sessions SET status = ?, data_points = ? WHERE id = ? ''', (status, data_points, session_id)) conn.commit() logger.info(f'更新会话状态: {session_id} -> {status}') except Exception as e: conn.rollback() logger.error(f'更新会话状态失败: {e}') raise def update_session_duration(self, session_id: str, duration: int): """更新会话持续时间""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute(''' UPDATE detection_sessions SET duration = ? WHERE id = ? ''', (duration, session_id)) conn.commit() logger.info(f'更新会话持续时间: {session_id} -> {duration}秒') except Exception as e: conn.rollback() logger.error(f'更新会话持续时间失败: {e}') raise def update_session_video_path(self, session_id: str, video_path: str): """更新会话视频路径""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute(''' UPDATE detection_sessions SET video_path = ? WHERE id = ? ''', (video_path, session_id)) conn.commit() logger.info(f'更新会话视频路径: {session_id} -> {video_path}') except Exception as e: conn.rollback() logger.error(f'更新会话视频路径失败: {e}') raise def get_detection_sessions(self, page: int = 1, size: int = 10, patient_id: str = None) -> List[Dict]: """获取检测会话列表""" conn = self.get_connection() cursor = conn.cursor() try: offset = (page - 1) * size if patient_id: cursor.execute(''' SELECT s.*, p.name as patient_name FROM detection_sessions s LEFT JOIN patients p ON s.patient_id = p.id WHERE s.patient_id = ? ORDER BY s.start_time DESC LIMIT ? OFFSET ? ''', (patient_id, size, offset)) else: cursor.execute(''' SELECT s.*, p.name as patient_name FROM detection_sessions s LEFT JOIN patients p ON s.patient_id = p.id ORDER BY s.start_time DESC LIMIT ? OFFSET ? ''', (size, offset)) rows = cursor.fetchall() sessions = [] for row in rows: session = dict(row) # 解析设置JSON if session['settings']: try: session['settings'] = json.loads(session['settings']) except: session['settings'] = {} sessions.append(session) return sessions except Exception as e: logger.error(f'获取检测会话列表失败: {e}') raise def get_sessions_count(self, patient_id: str = None) -> int: """获取会话总数""" conn = self.get_connection() cursor = conn.cursor() try: if patient_id: cursor.execute('SELECT COUNT(*) FROM detection_sessions WHERE patient_id = ?', (patient_id,)) else: cursor.execute('SELECT COUNT(*) FROM detection_sessions') return cursor.fetchone()[0] except Exception as e: logger.error(f'获取会话总数失败: {e}') return 0 def get_session_data(self, session_id: str) -> Optional[Dict]: """获取会话详细数据""" conn = self.get_connection() cursor = conn.cursor() try: # 获取会话基本信息 cursor.execute(''' SELECT s.*, p.name as patient_name FROM detection_sessions s LEFT JOIN patients p ON s.patient_id = p.id WHERE s.id = ? ''', (session_id,)) session_row = cursor.fetchone() if not session_row: return None session = dict(session_row) # 解析设置JSON if session['settings']: try: session['settings'] = json.loads(session['settings']) except: session['settings'] = {} # 获取检测数据 cursor.execute(''' SELECT * FROM detection_data WHERE session_id = ? ORDER BY timestamp ''', (session_id,)) data_rows = cursor.fetchall() session['data'] = [] for row in data_rows: data_point = dict(row) # 解析数据JSON try: data_point['data_value'] = json.loads(data_point['data_value']) except: pass session['data'].append(data_point) return session except Exception as e: logger.error(f'获取会话数据失败: {e}') return None # ==================== 检测数据管理 ==================== def save_detection_data(self, session_id: str, data: Dict[str, Any]): """保存检测数据""" conn = self.get_connection() cursor = conn.cursor() try: # 使用中国时区时间 china_time = self.get_china_time() # 保存不同类型的数据 for data_type, data_value in data.items(): cursor.execute(''' INSERT INTO detection_data (session_id, data_type, data_value, timestamp) VALUES (?, ?, ?, ?) ''', (session_id, data_type, json.dumps(data_value), china_time)) conn.commit() except Exception as e: conn.rollback() logger.error(f'保存检测数据失败: {e}') raise def get_latest_detection_data(self, session_id: str, limit: int = 10) -> List[Dict]: """获取最新的检测数据""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute(''' SELECT * FROM detection_data WHERE session_id = ? ORDER BY timestamp DESC LIMIT ? ''', (session_id, limit)) rows = cursor.fetchall() data_points = [] for row in rows: data_point = dict(row) try: data_point['data_value'] = json.loads(data_point['data_value']) except: pass data_points.append(data_point) return data_points except Exception as e: logger.error(f'获取最新检测数据失败: {e}') return [] # ==================== 分析结果管理 ==================== def save_analysis_result(self, session_id: str, analysis_result: Dict[str, Any]): """保存分析结果""" conn = self.get_connection() cursor = conn.cursor() try: for analysis_type, result_data in analysis_result.items(): cursor.execute(''' INSERT INTO analysis_results (session_id, analysis_type, result_data) VALUES (?, ?, ?) ''', (session_id, analysis_type, json.dumps(result_data))) conn.commit() logger.info(f'保存分析结果: {session_id}') except Exception as e: conn.rollback() logger.error(f'保存分析结果失败: {e}') raise def get_analysis_results(self, session_id: str) -> Dict[str, Any]: """获取分析结果""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute(''' SELECT * FROM analysis_results WHERE session_id = ? ORDER BY created_at DESC ''', (session_id,)) rows = cursor.fetchall() results = {} for row in rows: analysis_type = row['analysis_type'] try: result_data = json.loads(row['result_data']) results[analysis_type] = result_data except: results[analysis_type] = row['result_data'] return results except Exception as e: logger.error(f'获取分析结果失败: {e}') return {} # ==================== 系统设置管理 ==================== def get_setting(self, key: str, default_value: Any = None) -> Any: """获取系统设置""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute('SELECT value FROM system_settings WHERE key = ?', (key,)) row = cursor.fetchone() if row: try: return json.loads(row['value']) except: return row['value'] return default_value except Exception as e: logger.error(f'获取系统设置失败: {e}') return default_value def set_setting(self, key: str, value: Any, description: str = ''): """设置系统设置""" conn = self.get_connection() cursor = conn.cursor() try: value_str = json.dumps(value) if not isinstance(value, str) else value # 使用中国时区时间 china_time = self.get_china_time() cursor.execute(''' INSERT OR REPLACE INTO system_settings (key, value, description, updated_at) VALUES (?, ?, ?, ?) ''', (key, value_str, description, china_time)) conn.commit() logger.info(f'设置系统设置: {key}') except Exception as e: conn.rollback() logger.error(f'设置系统设置失败: {e}') raise # ==================== 用户管理 ==================== def register_user(self, user_data: Dict[str, Any]) -> Dict[str, Any]: """用户注册""" conn = self.get_connection() cursor = conn.cursor() try: # 检查手机号是否已存在(如果提供了手机号) if user_data.get('phone'): cursor.execute('SELECT COUNT(*) FROM users WHERE phone = ?', (user_data['phone'],)) if cursor.fetchone()[0] > 0: return { 'success': False, 'message': '手机号已存在' } user_id = str(uuid.uuid4()) # 密码明文存储 password = user_data['password'] # 使用中国时区时间 china_time = self.get_china_time() cursor.execute(''' INSERT INTO users (id, name, username, password, phone, is_active, user_type, register_date, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( user_id, user_data['name'], user_data['username'], password, user_data.get('phone'), # 手机号可选 1, # 新注册用户默认激活 'user', china_time, china_time, china_time )) conn.commit() logger.info(f'用户注册成功: {user_data["username"]}') return { 'success': True, 'user_id': user_id, 'message': '注册成功' } except sqlite3.IntegrityError: conn.rollback() logger.error(f'用户名已存在: {user_data["username"]}') return { 'success': False, 'message': '用户名已存在' } except Exception as e: conn.rollback() logger.error(f'用户注册失败: {e}') return { 'success': False, 'message': '注册失败' } def authenticate_user(self, username: str, password: str) -> Optional[Dict]: """用户登录验证""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute(''' SELECT * FROM users WHERE username = ? AND password = ? AND is_active = 1 ''', (username, password)) row = cursor.fetchone() if row: user = dict(row) # 不返回密码 del user['password'] logger.info(f'用户登录成功: {username}') return user else: logger.warning(f'用户登录失败: {username}') return None except Exception as e: logger.error(f'用户验证失败: {e}') return None def get_user_by_phone(self, phone: str) -> Optional[Dict]: """根据手机号查询用户""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute('SELECT * FROM users WHERE phone = ?', (phone,)) row = cursor.fetchone() if row: user = dict(row) # 不返回密码 del user['password'] return user return None except Exception as e: logger.error(f'根据手机号查询用户失败: {e}') return None def get_users(self, page: int = 1, size: int = 10, status: str = 'all') -> List[Dict]: """获取用户列表""" conn = self.get_connection() cursor = conn.cursor() try: offset = (page - 1) * size if status == 'pending': cursor.execute(''' SELECT id, name, username, phone, register_date, is_active, user_type, created_at FROM users WHERE is_active = 0 AND user_type = 'user' ORDER BY created_at DESC LIMIT ? OFFSET ? ''', (size, offset)) elif status == 'active': cursor.execute(''' SELECT id, name, username, phone, register_date, is_active, user_type, created_at FROM users WHERE is_active = 1 ORDER BY created_at DESC LIMIT ? OFFSET ? ''', (size, offset)) else: cursor.execute(''' SELECT id, name, username, phone, register_date, is_active, user_type, created_at FROM users ORDER BY created_at DESC LIMIT ? OFFSET ? ''', (size, offset)) rows = cursor.fetchall() return [dict(row) for row in rows] except Exception as e: logger.error(f'获取用户列表失败: {e}') return [] def get_users_count(self, status: str = 'all') -> int: """获取用户总数""" conn = self.get_connection() cursor = conn.cursor() try: if status == 'pending': cursor.execute('SELECT COUNT(*) FROM users WHERE is_active = 0 AND user_type = "user"') elif status == 'active': cursor.execute('SELECT COUNT(*) FROM users WHERE is_active = 1') else: cursor.execute('SELECT COUNT(*) FROM users') return cursor.fetchone()[0] except Exception as e: logger.error(f'获取用户总数失败: {e}') return 0 def approve_user(self, user_id: str, approved: bool = True): """审核用户""" conn = self.get_connection() cursor = conn.cursor() try: # 使用中国时区时间 china_time = self.get_china_time() cursor.execute(''' UPDATE users SET is_active = ?, updated_at = ? WHERE id = ? ''', (1 if approved else 0, china_time, user_id)) conn.commit() status = '通过' if approved else '拒绝' logger.info(f'用户审核{status}: {user_id}') except Exception as e: conn.rollback() logger.error(f'用户审核失败: {e}') raise def get_user(self, user_id: str) -> Optional[Dict]: """获取单个用户信息""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute(''' SELECT id, name, username, phone, register_date, is_active, user_type, created_at, updated_at FROM users WHERE id = ? ''', (user_id,)) row = cursor.fetchone() return dict(row) if row else None except Exception as e: logger.error(f'获取用户信息失败: {e}') return None def update_user(self, user_id: str, user_data: Dict[str, Any]): """更新用户信息""" conn = self.get_connection() cursor = conn.cursor() try: # 密码明文存储,无需加密处理 # 构建更新语句 fields = [] values = [] for key, value in user_data.items(): if key in ['name', 'username', 'password', 'is_active', 'user_type','phone']: fields.append(f'{key} = ?') values.append(value) if fields: # 使用中国时区时间 china_time = self.get_china_time() fields.append('updated_at = ?') values.append(china_time) values.append(user_id) sql = f"UPDATE users SET {', '.join(fields)} WHERE id = ?" cursor.execute(sql, values) conn.commit() logger.info(f'更新用户信息: {user_id}') except Exception as e: conn.rollback() logger.error(f'更新用户信息失败: {e}') raise def delete_user(self, user_id: str): """删除用户""" conn = self.get_connection() cursor = conn.cursor() try: cursor.execute('DELETE FROM users WHERE id = ?', (user_id,)) conn.commit() logger.info(f'删除用户: {user_id}') except Exception as e: conn.rollback() logger.error(f'删除用户失败: {e}') raise def close(self): """关闭数据库连接""" if self.connection: self.connection.close() self.connection = None logger.info('数据库连接已关闭')