import sqlite3 import json import logging import os import threading import datetime import uuid import random # 配置日志 logging.basicConfig( filename='db_adapter.log', level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger('db_adapter') # 数据库文件路径 DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data", "warhammer.db") # 线程本地存储,确保每个线程使用独立的数据库连接 _thread_local = threading.local() def get_db_connection(): """获取数据库连接(每个线程一个)""" if not hasattr(_thread_local, 'connection'): _thread_local.connection = sqlite3.connect(DB_PATH) # 启用外键约束 _thread_local.connection.execute("PRAGMA foreign_keys = ON") # 配置连接返回行为字典格式 _thread_local.connection.row_factory = sqlite3.Row return _thread_local.connection def close_db_connection(): """关闭当前线程的数据库连接""" if hasattr(_thread_local, 'connection'): _thread_local.connection.close() delattr(_thread_local, 'connection') # 用户数据操作 def get_users(): """获取所有用户数据""" conn = get_db_connection() cursor = conn.cursor() try: cursor.execute("SELECT * FROM users") users = [dict(row) for row in cursor.fetchall()] return users except Exception as e: logger.error(f"获取用户数据失败: {str(e)}") return [] def save_users(users): """保存用户数据(批量更新)""" conn = get_db_connection() cursor = conn.cursor() try: # 开始事务 conn.execute("BEGIN TRANSACTION") for user in users: cursor.execute( "INSERT OR REPLACE INTO users (id, username, password, created_at, high_score, total_games, last_year, last_game_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", ( user['id'], user['username'], user['password'], user.get('created_at', datetime.datetime.now().isoformat()), user.get('high_score', 0), user.get('total_games', 0), user.get('last_year', 41000), user.get('last_game_time', None) ) ) # 提交事务 conn.commit() return True except Exception as e: # 回滚事务 conn.rollback() logger.error(f"保存用户数据失败: {str(e)}") return False # 游戏状态操作 def get_game_states(): """获取所有游戏状态数据""" conn = get_db_connection() cursor = conn.cursor() try: cursor.execute("SELECT * FROM game_states") rows = cursor.fetchall() game_states = [] for row in rows: state = dict(row) # 将JSON字符串转换为字典 state['game_data'] = json.loads(state['game_data']) game_states.append(state) return game_states except Exception as e: logger.error(f"获取游戏状态数据失败: {str(e)}") return [] def save_game_states(states): """保存游戏状态数据(批量更新)""" conn = get_db_connection() cursor = conn.cursor() try: # 开始事务 conn.execute("BEGIN TRANSACTION") for state in states: # 将游戏数据转换为JSON字符串 game_data_json = json.dumps(state['game_data'], ensure_ascii=False) cursor.execute( "INSERT OR REPLACE INTO game_states (id, user_id, game_data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)", ( state['id'], state['user_id'], game_data_json, state.get('created_at', datetime.datetime.now().isoformat()), state.get('updated_at', datetime.datetime.now().isoformat()) ) ) # 提交事务 conn.commit() return True except Exception as e: # 回滚事务 conn.rollback() logger.error(f"保存游戏状态数据失败: {str(e)}") return False # 卡牌数据操作 def get_cards(): """获取卡牌基础数据""" conn = get_db_connection() cursor = conn.cursor() try: cursor.execute("SELECT cards_data FROM game_cards WHERE id = 1") row = cursor.fetchone() if row: return json.loads(row['cards_data']) else: logger.warning("未找到卡牌数据,返回空字典") return {} except Exception as e: logger.error(f"获取卡牌数据失败: {str(e)}") return {} # 自定义卡牌操作 def get_custom_cards(): """获取自定义卡牌数据""" conn = get_db_connection() cursor = conn.cursor() try: cursor.execute(""" SELECT c.*, vs.upvotes, vs.downvotes FROM custom_cards c LEFT JOIN card_vote_stats vs ON c.id = vs.card_id """) rows = cursor.fetchall() cards = [] for row in rows: card = { 'id': row['id'], 'title': row['title'], 'character': json.loads(row['character_data']), 'description': row['description'], 'option_a': json.loads(row['option_a']), 'option_b': json.loads(row['option_b']), 'creator_id': row['creator_id'], 'creator_name': row['creator_name'], 'created_at': row['created_at'], 'upvotes': row['upvotes'] or 0, 'downvotes': row['downvotes'] or 0 } cards.append(card) return {'cards': cards} except Exception as e: logger.error(f"获取自定义卡牌数据失败: {str(e)}") return {'cards': []} def save_custom_cards(data): """保存自定义卡牌数据""" conn = get_db_connection() cursor = conn.cursor() try: # 开始事务 conn.execute("BEGIN TRANSACTION") # 清空现有数据(可选,根据需要修改) # cursor.execute("DELETE FROM custom_cards") # 插入新数据 cards = data.get('cards', []) for card in cards: cursor.execute( """INSERT OR REPLACE INTO custom_cards (id, title, character_data, description, option_a, option_b, creator_id, creator_name, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", ( card['id'], card['title'], json.dumps(card['character'], ensure_ascii=False), card['description'], json.dumps(card['option_a'], ensure_ascii=False), json.dumps(card['option_b'], ensure_ascii=False), card['creator_id'], card['creator_name'], card.get('created_at', datetime.datetime.now().isoformat()) ) ) # 确保有对应的投票统计记录 cursor.execute( "INSERT OR IGNORE INTO card_vote_stats (card_id, upvotes, downvotes) VALUES (?, ?, ?)", (card['id'], card.get('upvotes', 0), card.get('downvotes', 0)) ) # 提交事务 conn.commit() return True except Exception as e: # 回滚事务 conn.rollback() logger.error(f"保存自定义卡牌数据失败: {str(e)}") return False # 卡牌投票操作 def get_card_votes(): """获取卡牌投票数据""" conn = get_db_connection() cursor = conn.cursor() try: # 获取用户投票 cursor.execute("SELECT user_id, card_id, vote_type FROM card_votes") vote_rows = cursor.fetchall() user_votes = {} for row in vote_rows: user_id = row['user_id'] if user_id not in user_votes: user_votes[user_id] = {} user_votes[user_id][row['card_id']] = row['vote_type'] # 获取投票统计 cursor.execute("SELECT card_id, upvotes, downvotes FROM card_vote_stats") stat_rows = cursor.fetchall() card_votes = {} for row in stat_rows: card_id = row['card_id'] card_votes[card_id] = { 'upvotes': row['upvotes'] or 0, 'downvotes': row['downvotes'] or 0, 'users': {} # 这部分可能需要额外填充,取决于原来的数据结构 } return {'user_votes': user_votes, 'card_votes': card_votes} except Exception as e: logger.error(f"获取卡牌投票数据失败: {str(e)}") return {'user_votes': {}, 'card_votes': {}} def save_card_votes(data): """保存卡牌投票数据""" conn = get_db_connection() cursor = conn.cursor() try: # 开始事务 conn.execute("BEGIN TRANSACTION") # 更新用户投票 user_votes = data.get('user_votes', {}) for user_id, votes in user_votes.items(): for card_id, vote_type in votes.items(): cursor.execute( """INSERT OR REPLACE INTO card_votes (card_id, user_id, vote_type, created_at) VALUES (?, ?, ?, ?)""", ( card_id, user_id, vote_type, datetime.datetime.now().isoformat() ) ) # 更新投票统计 card_votes = data.get('card_votes', {}) for card_id, votes in card_votes.items(): cursor.execute( "INSERT OR REPLACE INTO card_vote_stats (card_id, upvotes, downvotes) VALUES (?, ?, ?)", ( card_id, votes.get('upvotes', 0), votes.get('downvotes', 0) ) ) # 提交事务 conn.commit() return True except Exception as e: # 回滚事务 conn.rollback() logger.error(f"保存卡牌投票数据失败: {str(e)}") return False # 会话操作 def get_sessions(): """获取会话数据""" conn = get_db_connection() cursor = conn.cursor() try: cursor.execute("SELECT * FROM sessions") rows = cursor.fetchall() active_sessions = [] for row in rows: session = { 'user_id': row['user_id'], 'login_time': row['login_time'], 'last_activity': row['last_activity'], 'ip_address': row['ip_address'], 'status': row['status'] } active_sessions.append(session) return {'active_sessions': active_sessions} except Exception as e: logger.error(f"获取会话数据失败: {str(e)}") return {'active_sessions': []} def save_sessions(data): """保存会话数据""" conn = get_db_connection() cursor = conn.cursor() try: # 开始事务 conn.execute("BEGIN TRANSACTION") # 清空旧会话 cursor.execute("DELETE FROM sessions") # 插入新会话 active_sessions = data.get('active_sessions', []) for session in active_sessions: # 生成会话ID session_id = str(uuid.uuid4()) cursor.execute( """INSERT INTO sessions (id, user_id, login_time, last_activity, ip_address, status) VALUES (?, ?, ?, ?, ?, ?)""", ( session_id, session['user_id'], session.get('login_time', datetime.datetime.now().isoformat()), session.get('last_activity', datetime.datetime.now().isoformat()), session.get('ip_address', ''), session.get('status', 'idle') ) ) # 提交事务 conn.commit() return True except Exception as e: # 回滚事务 conn.rollback() logger.error(f"保存会话数据失败: {str(e)}") return False # 投票卡牌的实用函数 def vote_card(card_id, user_id, vote_type): """对卡牌进行投票或取消投票""" conn = get_db_connection() cursor = conn.cursor() try: # 开始事务 conn.execute("BEGIN TRANSACTION") # 检查是否已存在投票 cursor.execute("SELECT vote_type FROM card_votes WHERE card_id = ? AND user_id = ?", (card_id, user_id)) existing_vote = cursor.fetchone() # 检查卡牌投票统计 cursor.execute("SELECT upvotes, downvotes FROM card_vote_stats WHERE card_id = ?", (card_id,)) vote_stats = cursor.fetchone() upvotes = 0 downvotes = 0 if vote_stats: upvotes = vote_stats['upvotes'] or 0 downvotes = vote_stats['downvotes'] or 0 new_vote = None if vote_type == 'none' or (existing_vote and existing_vote['vote_type'] == vote_type): # 取消投票 if existing_vote: if existing_vote['vote_type'] == 'upvote': upvotes = max(0, upvotes - 1) else: downvotes = max(0, downvotes - 1) # 删除投票记录 cursor.execute("DELETE FROM card_votes WHERE card_id = ? AND user_id = ?", (card_id, user_id)) else: # 如果之前投过票,先取消之前的投票 if existing_vote: if existing_vote['vote_type'] == 'upvote': upvotes = max(0, upvotes - 1) else: downvotes = max(0, downvotes - 1) # 添加新投票 if vote_type == 'upvote': upvotes += 1 new_vote = 'upvote' else: downvotes += 1 new_vote = 'downvote' # 更新或添加投票记录 cursor.execute( """INSERT OR REPLACE INTO card_votes (card_id, user_id, vote_type, created_at) VALUES (?, ?, ?, ?)""", (card_id, user_id, vote_type, datetime.datetime.now().isoformat()) ) # 更新投票统计 cursor.execute( """INSERT OR REPLACE INTO card_vote_stats (card_id, upvotes, downvotes) VALUES (?, ?, ?)""", (card_id, upvotes, downvotes) ) # 提交事务 conn.commit() return { 'success': True, 'upvotes': upvotes, 'downvotes': downvotes, 'newVote': new_vote } except Exception as e: # 回滚事务 conn.rollback() logger.error(f"投票失败: {str(e)}") return { 'success': False, 'error': str(e) } # 死亡场景操作 def get_death_scenarios(death_type=None): """获取死亡场景数据,可选按死亡类型筛选""" conn = get_db_connection() cursor = conn.cursor() try: if death_type: cursor.execute("SELECT * FROM death_scenarios WHERE death_type = ?", (death_type,)) else: cursor.execute("SELECT * FROM death_scenarios") rows = cursor.fetchall() scenarios = [] for row in rows: scenario = { 'id': row['id'], 'death_type': row['death_type'], 'name': row['name'], 'weight': row['weight'] or 100, 'first_card': json.loads(row['first_card']), 'second_card': json.loads(row['second_card']) } scenarios.append(scenario) return scenarios except Exception as e: logger.error(f"获取死亡场景数据失败: {str(e)}") return [] def get_random_death_scenario(death_type): """根据死亡类型获取随机死亡场景,考虑权重""" conn = get_db_connection() cursor = conn.cursor() try: # 获取指定类型的所有死亡场景 cursor.execute("SELECT id, weight FROM death_scenarios WHERE death_type = ?", (death_type,)) scenarios = cursor.fetchall() if not scenarios: return None # 计算权重总和 total_weight = sum(s['weight'] or 100 for s in scenarios) # 随机选择一个场景,考虑权重 rand_val = random.randint(1, total_weight) # 根据权重选择 current_weight = 0 selected_id = None for scenario in scenarios: current_weight += scenario['weight'] or 100 if rand_val <= current_weight: selected_id = scenario['id'] break # 获取选中的场景详情 if selected_id: cursor.execute("SELECT * FROM death_scenarios WHERE id = ?", (selected_id,)) row = cursor.fetchone() if row: return { 'id': row['id'], 'death_type': row['death_type'], 'name': row['name'], 'weight': row['weight'] or 100, 'first_card': json.loads(row['first_card']), 'second_card': json.loads(row['second_card']) } return None except Exception as e: logger.error(f"获取随机死亡场景失败: {str(e)}") return None def save_death_scenarios(scenarios): """保存死亡场景数据""" conn = get_db_connection() cursor = conn.cursor() try: # 开始事务 conn.execute("BEGIN TRANSACTION") for scenario in scenarios: cursor.execute( """INSERT OR REPLACE INTO death_scenarios (id, death_type, name, weight, first_card, second_card) VALUES (?, ?, ?, ?, ?, ?)""", ( scenario['id'], scenario['death_type'], scenario['name'], scenario.get('weight', 100), json.dumps(scenario['first_card'], ensure_ascii=False), json.dumps(scenario['second_card'], ensure_ascii=False) ) ) # 提交事务 conn.commit() return True except Exception as e: # 回滚事务 conn.rollback() logger.error(f"保存死亡场景数据失败: {str(e)}") return False # 成就系统操作 def get_achievements(): """获取所有成就数据""" conn = get_db_connection() cursor = conn.cursor() try: cursor.execute("SELECT * FROM achievements") rows = cursor.fetchall() achievements = [] for row in rows: achievement = { 'id': row['id'], 'name': row['name'], 'description': row['description'], 'icon': row['icon'], 'death_scenario_id': row['death_scenario_id'], 'hidden': row['hidden'] } achievements.append(achievement) return achievements except Exception as e: logger.error(f"获取成就数据失败: {str(e)}") return [] def get_user_achievements(user_id): """获取用户已解锁的成就""" conn = get_db_connection() cursor = conn.cursor() try: cursor.execute(""" SELECT a.*, ua.unlock_time FROM achievements a JOIN user_achievements ua ON a.id = ua.achievement_id WHERE ua.user_id = ? """, (user_id,)) rows = cursor.fetchall() unlocked = [] for row in rows: achievement = { 'id': row['id'], 'name': row['name'], 'description': row['description'], 'icon': row['icon'], 'death_scenario_id': row['death_scenario_id'], 'unlock_time': row['unlock_time'] } unlocked.append(achievement) return unlocked except Exception as e: logger.error(f"获取用户成就数据失败: {str(e)}") return [] def unlock_achievement(user_id, achievement_id): """解锁用户成就""" conn = get_db_connection() cursor = conn.cursor() try: # 检查成就是否存在 cursor.execute("SELECT id FROM achievements WHERE id = ?", (achievement_id,)) if not cursor.fetchone(): logger.error(f"成就不存在: {achievement_id}") return False # 检查用户是否已解锁该成就 cursor.execute( "SELECT * FROM user_achievements WHERE user_id = ? AND achievement_id = ?", (user_id, achievement_id) ) if cursor.fetchone(): # 已解锁,无需重复操作 return True # 解锁新成就 cursor.execute( """INSERT INTO user_achievements (user_id, achievement_id, unlock_time) VALUES (?, ?, ?)""", ( user_id, achievement_id, datetime.datetime.now().isoformat() ) ) conn.commit() return True except Exception as e: conn.rollback() logger.error(f"解锁成就失败: {str(e)}") return False def save_achievements(achievements): """保存成就数据""" conn = get_db_connection() cursor = conn.cursor() try: # 开始事务 conn.execute("BEGIN TRANSACTION") for achievement in achievements: cursor.execute( """INSERT OR REPLACE INTO achievements (id, name, description, icon, death_scenario_id, hidden) VALUES (?, ?, ?, ?, ?, ?)""", ( achievement['id'], achievement['name'], achievement['description'], achievement['icon'], achievement.get('death_scenario_id'), achievement.get('hidden', 0) ) ) # 提交事务 conn.commit() return True except Exception as e: # 回滚事务 conn.rollback() logger.error(f"保存成就数据失败: {str(e)}") return False