Warhummer/migrate_to_sqlite.py
2025-06-25 09:35:26 +08:00

306 lines
9.9 KiB
Python

import json
import os
import sqlite3
import datetime
import logging
# 配置日志
logging.basicConfig(
filename='migration.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('migration')
# 数据文件路径
DATA_DIR = 'data'
DB_PATH = os.path.join(DATA_DIR, 'warhammer.db')
USERS_FILE = os.path.join(DATA_DIR, 'users.json')
GAME_STATES_FILE = os.path.join(DATA_DIR, 'game_states.json')
CARDS_FILE = os.path.join(DATA_DIR, 'cards.json')
CARD_VOTES_FILE = os.path.join(DATA_DIR, 'card_votes.json')
CUSTOM_CARDS_FILE = os.path.join(DATA_DIR, 'custom_cards.json')
SESSIONS_FILE = os.path.join(DATA_DIR, 'sessions.json')
def create_tables(conn):
"""创建数据库表结构"""
cursor = conn.cursor()
# 用户表
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
created_at TEXT NOT NULL,
high_score INTEGER DEFAULT 0,
total_games INTEGER DEFAULT 0,
last_year INTEGER DEFAULT 41000,
last_game_time TEXT
)
''')
# 游戏状态表
cursor.execute('''
CREATE TABLE IF NOT EXISTS game_states (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
game_data TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
)
''')
# 自定义卡牌表
cursor.execute('''
CREATE TABLE IF NOT EXISTS custom_cards (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
character_data TEXT NOT NULL,
description TEXT NOT NULL,
option_a TEXT NOT NULL,
option_b TEXT NOT NULL,
creator_id TEXT NOT NULL,
creator_name TEXT NOT NULL,
created_at TEXT NOT NULL,
FOREIGN KEY (creator_id) REFERENCES users(id)
)
''')
# 卡牌投票表
cursor.execute('''
CREATE TABLE IF NOT EXISTS card_votes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
card_id TEXT NOT NULL,
user_id TEXT NOT NULL,
vote_type TEXT NOT NULL,
created_at TEXT NOT NULL,
FOREIGN KEY (card_id) REFERENCES custom_cards(id),
FOREIGN KEY (user_id) REFERENCES users(id),
UNIQUE(card_id, user_id)
)
''')
# 卡牌投票统计表
cursor.execute('''
CREATE TABLE IF NOT EXISTS card_vote_stats (
card_id TEXT PRIMARY KEY,
upvotes INTEGER DEFAULT 0,
downvotes INTEGER DEFAULT 0,
FOREIGN KEY (card_id) REFERENCES custom_cards(id)
)
''')
# 会话表
cursor.execute('''
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
login_time TEXT NOT NULL,
last_activity TEXT NOT NULL,
ip_address TEXT,
status TEXT,
FOREIGN KEY (user_id) REFERENCES users(id)
)
''')
# 卡牌数据表 - 只存储一条记录,因为这是游戏基础数据
cursor.execute('''
CREATE TABLE IF NOT EXISTS game_cards (
id INTEGER PRIMARY KEY,
cards_data TEXT NOT NULL
)
''')
conn.commit()
logger.info("数据库表创建完成")
def load_json_data(file_path, default_value=None):
"""从JSON文件安全地加载数据"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as e:
logger.warning(f"加载文件 {file_path} 失败: {str(e)}")
return default_value if default_value is not None else {}
def migrate_users(conn, users_data):
"""迁移用户数据"""
cursor = conn.cursor()
for user in users_data:
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.utcnow().isoformat()),
user.get('high_score', 0),
user.get('total_games', 0),
user.get('last_year', 41000),
user.get('last_game_time', None)
)
)
conn.commit()
logger.info(f"用户数据迁移完成,共 {len(users_data)} 条记录")
def migrate_game_states(conn, game_states_data):
"""迁移游戏状态数据"""
cursor = conn.cursor()
for state in game_states_data:
cursor.execute(
"INSERT OR REPLACE INTO game_states (id, user_id, game_data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
(
state['id'],
state['user_id'],
json.dumps(state['game_data'], ensure_ascii=False),
state.get('created_at', datetime.datetime.utcnow().isoformat()),
state.get('updated_at', datetime.datetime.utcnow().isoformat())
)
)
conn.commit()
logger.info(f"游戏状态数据迁移完成,共 {len(game_states_data)} 条记录")
def migrate_custom_cards(conn, custom_cards_data):
"""迁移自定义卡牌数据"""
cursor = conn.cursor()
cards = custom_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.utcnow().isoformat())
)
)
conn.commit()
logger.info(f"自定义卡牌数据迁移完成,共 {len(cards)} 条记录")
def migrate_card_votes(conn, card_votes_data):
"""迁移卡牌投票数据"""
cursor = conn.cursor()
# 迁移用户投票记录
user_votes = card_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.utcnow().isoformat()
)
)
# 迁移卡牌投票统计
card_votes = 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()
logger.info(f"卡牌投票数据迁移完成")
def migrate_sessions(conn, sessions_data):
"""迁移会话数据"""
cursor = conn.cursor()
active_sessions = sessions_data.get('active_sessions', [])
for session in active_sessions:
cursor.execute(
"INSERT OR REPLACE INTO sessions (id, user_id, login_time, last_activity, ip_address, status) VALUES (?, ?, ?, ?, ?, ?)",
(
str(hash(session['user_id'] + session.get('login_time', ''))), # 创建唯一ID
session['user_id'],
session.get('login_time', datetime.datetime.utcnow().isoformat()),
session.get('last_activity', datetime.datetime.utcnow().isoformat()),
session.get('ip_address', ''),
session.get('status', 'idle')
)
)
conn.commit()
logger.info(f"会话数据迁移完成,共 {len(active_sessions)} 条记录")
def migrate_cards_data(conn, cards_data):
"""迁移卡牌基础数据"""
cursor = conn.cursor()
cursor.execute(
"INSERT OR REPLACE INTO game_cards (id, cards_data) VALUES (?, ?)",
(1, json.dumps(cards_data, ensure_ascii=False))
)
conn.commit()
logger.info(f"卡牌基础数据迁移完成")
def main():
"""主迁移函数"""
logger.info("开始数据迁移")
# 检查数据库文件路径
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
# 创建/连接到数据库
conn = sqlite3.connect(DB_PATH)
try:
# 创建表结构
create_tables(conn)
# 加载并迁移用户数据
users_data = load_json_data(USERS_FILE, [])
migrate_users(conn, users_data)
# 加载并迁移游戏状态数据
game_states_data = load_json_data(GAME_STATES_FILE, [])
migrate_game_states(conn, game_states_data)
# 加载并迁移自定义卡牌数据
custom_cards_data = load_json_data(CUSTOM_CARDS_FILE, {"cards": []})
migrate_custom_cards(conn, custom_cards_data)
# 加载并迁移卡牌投票数据
card_votes_data = load_json_data(CARD_VOTES_FILE, {"user_votes": {}, "card_votes": {}})
migrate_card_votes(conn, card_votes_data)
# 加载并迁移会话数据
sessions_data = load_json_data(SESSIONS_FILE, {"active_sessions": []})
migrate_sessions(conn, sessions_data)
# 加载并迁移卡牌基础数据
cards_data = load_json_data(CARDS_FILE, {})
migrate_cards_data(conn, cards_data)
logger.info("数据迁移成功完成")
except Exception as e:
logger.error(f"迁移失败: {str(e)}")
raise
finally:
conn.close()
if __name__ == "__main__":
main()