mirror of
https://github.com/zhinianboke/xianyu-auto-reply.git
synced 2025-08-02 12:37:35 +08:00
318 lines
11 KiB
Python
318 lines
11 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
完整的多用户数据隔离修复脚本
|
||
"""
|
||
|
||
import sqlite3
|
||
import json
|
||
import time
|
||
from loguru import logger
|
||
|
||
def backup_database():
|
||
"""备份数据库"""
|
||
try:
|
||
import shutil
|
||
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
||
backup_file = f"xianyu_data_backup_{timestamp}.db"
|
||
shutil.copy2("xianyu_data.db", backup_file)
|
||
logger.info(f"数据库备份完成: {backup_file}")
|
||
return backup_file
|
||
except Exception as e:
|
||
logger.error(f"数据库备份失败: {e}")
|
||
return None
|
||
|
||
def add_user_id_columns():
|
||
"""为相关表添加user_id字段"""
|
||
conn = sqlite3.connect('xianyu_data.db')
|
||
cursor = conn.cursor()
|
||
|
||
try:
|
||
# 检查并添加user_id字段到cards表
|
||
cursor.execute("PRAGMA table_info(cards)")
|
||
columns = [column[1] for column in cursor.fetchall()]
|
||
|
||
if 'user_id' not in columns:
|
||
logger.info("为cards表添加user_id字段...")
|
||
cursor.execute('ALTER TABLE cards ADD COLUMN user_id INTEGER REFERENCES users(id)')
|
||
logger.info("✅ cards表user_id字段添加成功")
|
||
else:
|
||
logger.info("cards表已有user_id字段")
|
||
|
||
# 检查并添加user_id字段到delivery_rules表
|
||
cursor.execute("PRAGMA table_info(delivery_rules)")
|
||
columns = [column[1] for column in cursor.fetchall()]
|
||
|
||
if 'user_id' not in columns:
|
||
logger.info("为delivery_rules表添加user_id字段...")
|
||
cursor.execute('ALTER TABLE delivery_rules ADD COLUMN user_id INTEGER REFERENCES users(id)')
|
||
logger.info("✅ delivery_rules表user_id字段添加成功")
|
||
else:
|
||
logger.info("delivery_rules表已有user_id字段")
|
||
|
||
# 检查并添加user_id字段到notification_channels表
|
||
cursor.execute("PRAGMA table_info(notification_channels)")
|
||
columns = [column[1] for column in cursor.fetchall()]
|
||
|
||
if 'user_id' not in columns:
|
||
logger.info("为notification_channels表添加user_id字段...")
|
||
cursor.execute('ALTER TABLE notification_channels ADD COLUMN user_id INTEGER REFERENCES users(id)')
|
||
logger.info("✅ notification_channels表user_id字段添加成功")
|
||
else:
|
||
logger.info("notification_channels表已有user_id字段")
|
||
|
||
conn.commit()
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"添加user_id字段失败: {e}")
|
||
conn.rollback()
|
||
return False
|
||
finally:
|
||
conn.close()
|
||
|
||
def create_user_settings_table():
|
||
"""创建用户设置表"""
|
||
conn = sqlite3.connect('xianyu_data.db')
|
||
cursor = conn.cursor()
|
||
|
||
try:
|
||
# 检查表是否已存在
|
||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='user_settings'")
|
||
if cursor.fetchone():
|
||
logger.info("user_settings表已存在")
|
||
return True
|
||
|
||
logger.info("创建user_settings表...")
|
||
cursor.execute('''
|
||
CREATE TABLE user_settings (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
user_id INTEGER NOT NULL,
|
||
key TEXT NOT NULL,
|
||
value TEXT,
|
||
description TEXT,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||
UNIQUE(user_id, key)
|
||
)
|
||
''')
|
||
|
||
conn.commit()
|
||
logger.info("✅ user_settings表创建成功")
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"创建user_settings表失败: {e}")
|
||
conn.rollback()
|
||
return False
|
||
finally:
|
||
conn.close()
|
||
|
||
def migrate_existing_data():
|
||
"""迁移现有数据到admin用户"""
|
||
conn = sqlite3.connect('xianyu_data.db')
|
||
cursor = conn.cursor()
|
||
|
||
try:
|
||
# 获取admin用户ID
|
||
cursor.execute("SELECT id FROM users WHERE username = 'admin'")
|
||
admin_result = cursor.fetchone()
|
||
|
||
if not admin_result:
|
||
logger.error("未找到admin用户,请先创建admin用户")
|
||
return False
|
||
|
||
admin_id = admin_result[0]
|
||
logger.info(f"找到admin用户,ID: {admin_id}")
|
||
|
||
# 迁移cards表数据
|
||
cursor.execute("SELECT COUNT(*) FROM cards WHERE user_id IS NULL")
|
||
unbound_cards = cursor.fetchone()[0]
|
||
|
||
if unbound_cards > 0:
|
||
logger.info(f"迁移 {unbound_cards} 个未绑定的卡券到admin用户...")
|
||
cursor.execute("UPDATE cards SET user_id = ? WHERE user_id IS NULL", (admin_id,))
|
||
logger.info("✅ 卡券数据迁移完成")
|
||
|
||
# 迁移delivery_rules表数据
|
||
cursor.execute("SELECT COUNT(*) FROM delivery_rules WHERE user_id IS NULL")
|
||
unbound_rules = cursor.fetchone()[0]
|
||
|
||
if unbound_rules > 0:
|
||
logger.info(f"迁移 {unbound_rules} 个未绑定的发货规则到admin用户...")
|
||
cursor.execute("UPDATE delivery_rules SET user_id = ? WHERE user_id IS NULL", (admin_id,))
|
||
logger.info("✅ 发货规则数据迁移完成")
|
||
|
||
# 迁移notification_channels表数据
|
||
cursor.execute("SELECT COUNT(*) FROM notification_channels WHERE user_id IS NULL")
|
||
unbound_channels = cursor.fetchone()[0]
|
||
|
||
if unbound_channels > 0:
|
||
logger.info(f"迁移 {unbound_channels} 个未绑定的通知渠道到admin用户...")
|
||
cursor.execute("UPDATE notification_channels SET user_id = ? WHERE user_id IS NULL", (admin_id,))
|
||
logger.info("✅ 通知渠道数据迁移完成")
|
||
|
||
conn.commit()
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"数据迁移失败: {e}")
|
||
conn.rollback()
|
||
return False
|
||
finally:
|
||
conn.close()
|
||
|
||
def verify_isolation():
|
||
"""验证数据隔离效果"""
|
||
conn = sqlite3.connect('xianyu_data.db')
|
||
cursor = conn.cursor()
|
||
|
||
try:
|
||
logger.info("验证数据隔离效果...")
|
||
|
||
# 检查各表的用户分布
|
||
tables = ['cards', 'delivery_rules', 'notification_channels']
|
||
|
||
for table in tables:
|
||
cursor.execute(f'''
|
||
SELECT u.username, COUNT(*) as count
|
||
FROM {table} t
|
||
JOIN users u ON t.user_id = u.id
|
||
GROUP BY u.id, u.username
|
||
ORDER BY count DESC
|
||
''')
|
||
|
||
results = cursor.fetchall()
|
||
logger.info(f"📊 {table} 表用户分布:")
|
||
for username, count in results:
|
||
logger.info(f" • {username}: {count} 条记录")
|
||
|
||
# 检查是否有未绑定的数据
|
||
cursor.execute(f"SELECT COUNT(*) FROM {table} WHERE user_id IS NULL")
|
||
unbound_count = cursor.fetchone()[0]
|
||
if unbound_count > 0:
|
||
logger.warning(f"⚠️ {table} 表还有 {unbound_count} 条未绑定用户的记录")
|
||
else:
|
||
logger.info(f"✅ {table} 表所有记录都已正确绑定用户")
|
||
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"验证失败: {e}")
|
||
return False
|
||
finally:
|
||
conn.close()
|
||
|
||
def create_default_user_settings():
|
||
"""为现有用户创建默认设置"""
|
||
conn = sqlite3.connect('xianyu_data.db')
|
||
cursor = conn.cursor()
|
||
|
||
try:
|
||
logger.info("为现有用户创建默认设置...")
|
||
|
||
# 获取所有用户
|
||
cursor.execute("SELECT id, username FROM users")
|
||
users = cursor.fetchall()
|
||
|
||
default_settings = [
|
||
('theme_color', '#1890ff', '主题颜色'),
|
||
('language', 'zh-CN', '界面语言'),
|
||
('notification_enabled', 'true', '通知开关'),
|
||
('auto_refresh', 'true', '自动刷新'),
|
||
]
|
||
|
||
for user_id, username in users:
|
||
logger.info(f"为用户 {username} 创建默认设置...")
|
||
|
||
for key, value, description in default_settings:
|
||
# 检查设置是否已存在
|
||
cursor.execute(
|
||
"SELECT id FROM user_settings WHERE user_id = ? AND key = ?",
|
||
(user_id, key)
|
||
)
|
||
|
||
if not cursor.fetchone():
|
||
cursor.execute('''
|
||
INSERT INTO user_settings (user_id, key, value, description)
|
||
VALUES (?, ?, ?, ?)
|
||
''', (user_id, key, value, description))
|
||
|
||
conn.commit()
|
||
logger.info("✅ 默认用户设置创建完成")
|
||
return True
|
||
|
||
except Exception as e:
|
||
logger.error(f"创建默认用户设置失败: {e}")
|
||
conn.rollback()
|
||
return False
|
||
finally:
|
||
conn.close()
|
||
|
||
def main():
|
||
"""主函数"""
|
||
print("🚀 完整的多用户数据隔离修复")
|
||
print("=" * 60)
|
||
|
||
# 1. 备份数据库
|
||
print("\n📦 1. 备份数据库")
|
||
backup_file = backup_database()
|
||
if not backup_file:
|
||
print("❌ 数据库备份失败,停止修复")
|
||
return False
|
||
|
||
# 2. 添加user_id字段
|
||
print("\n🔧 2. 添加用户隔离字段")
|
||
if not add_user_id_columns():
|
||
print("❌ 添加user_id字段失败")
|
||
return False
|
||
|
||
# 3. 创建用户设置表
|
||
print("\n📋 3. 创建用户设置表")
|
||
if not create_user_settings_table():
|
||
print("❌ 创建用户设置表失败")
|
||
return False
|
||
|
||
# 4. 迁移现有数据
|
||
print("\n📦 4. 迁移现有数据")
|
||
if not migrate_existing_data():
|
||
print("❌ 数据迁移失败")
|
||
return False
|
||
|
||
# 5. 创建默认用户设置
|
||
print("\n⚙️ 5. 创建默认用户设置")
|
||
if not create_default_user_settings():
|
||
print("❌ 创建默认用户设置失败")
|
||
return False
|
||
|
||
# 6. 验证隔离效果
|
||
print("\n🔍 6. 验证数据隔离")
|
||
if not verify_isolation():
|
||
print("❌ 验证失败")
|
||
return False
|
||
|
||
print("\n" + "=" * 60)
|
||
print("🎉 多用户数据隔离修复完成!")
|
||
|
||
print("\n📋 修复内容:")
|
||
print("✅ 1. 为cards表添加用户隔离")
|
||
print("✅ 2. 为delivery_rules表添加用户隔离")
|
||
print("✅ 3. 为notification_channels表添加用户隔离")
|
||
print("✅ 4. 创建用户设置表")
|
||
print("✅ 5. 迁移现有数据到admin用户")
|
||
print("✅ 6. 创建默认用户设置")
|
||
|
||
print("\n⚠️ 下一步:")
|
||
print("1. 重启服务以应用数据库更改")
|
||
print("2. 运行API接口修复脚本")
|
||
print("3. 测试多用户数据隔离")
|
||
print("4. 更新前端代码")
|
||
|
||
print(f"\n💾 数据库备份文件: {backup_file}")
|
||
print("如有问题,可以使用备份文件恢复数据")
|
||
|
||
return True
|
||
|
||
if __name__ == "__main__":
|
||
main()
|