import os import sys import asyncio import argparse from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) from scripts.backup_db import backup_database, list_backups, cleanup_old_backups, BACKUP_DIR from scripts.restore_db import restore_database, interactive_restore from scripts.cleanup_db import cleanup_database, remove_orphaned_records async def main(): parser = argparse.ArgumentParser(description='数据库备份和恢复工具') subparsers = parser.add_subparsers(dest='command', help='子命令') backup_parser = subparsers.add_parser('backup', help='备份数据库') backup_parser.add_argument('--name', type=str, help='备份名称,不含扩展名') backup_parser.add_argument('--no-compress', action='store_true', help='不压缩备份文件') backup_parser.add_argument('--keep', type=int, default=10, help='保留的备份数量') restore_parser = subparsers.add_parser('restore', help='恢复数据库') restore_parser.add_argument('--file', type=str, help='要恢复的备份文件') list_parser = subparsers.add_parser('list', help='列出所有备份') cleanup_parser = subparsers.add_parser('cleanup', help='清理旧备份') cleanup_parser.add_argument('--keep', type=int, default=10, help='保留的备份数量') clean_db_parser = subparsers.add_parser('clean-db', help='清理数据库中未使用的表和字段') clean_db_parser.add_argument('--execute', action='store_true', help='执行实际清理(不使用此选项则只进行模拟运行)') clean_db_parser.add_argument('--orphaned', action='store_true', help='仅清理孤立记录') clean_db_parser.add_argument('--schema', action='store_true', help='仅清理未使用的表和字段') clean_db_parser.add_argument('--backup-first', action='store_true', help='清理前先进行备份') clean_db_parser.add_argument('--drop-tables', type=str, help='指定要删除的表,用逗号分隔') args = parser.parse_args() if args.command == 'backup': await backup_database(args.name, not args.no_compress if hasattr(args, 'no_compress') else True) await cleanup_old_backups(args.keep if hasattr(args, 'keep') else 10) elif args.command == 'restore': if args.file: await restore_database(args.file) else: await interactive_restore() elif args.command == 'list': backups = await list_backups() if backups: print("\n可用的备份文件:") for backup in backups: file_path = os.path.join(BACKUP_DIR, backup) size = os.path.getsize(file_path) / (1024 * 1024) # 转换为MB print(f"{backup} ({size:.2f} MB)") else: print("没有找到可用的备份") elif args.command == 'cleanup': keep = args.keep if hasattr(args, 'keep') else 10 await cleanup_old_backups(keep) elif args.command == 'clean-db': dry_run = not args.execute if args.backup_first: print("清理前进行数据库备份...") await backup_database(f"pre_cleanup_{int(asyncio.get_event_loop().time())}", True) if args.drop_tables: from scripts.cleanup_db import get_mysql_connection, drop_specific_tables conn = await get_mysql_connection() if conn: try: tables_to_drop = [t.strip() for t in args.drop_tables.split(',')] await drop_specific_tables(conn, tables_to_drop, dry_run) finally: conn.close() else: if not args.orphaned: print("\n分析数据库架构...") await cleanup_database(dry_run) if not args.schema: print("\n检查孤立记录...") await remove_orphaned_records(dry_run) if dry_run: print("\n这是一次模拟运行,没有执行任何实际更改。") print("要执行实际的清理操作,请使用 --execute 选项。") else: parser.print_help() if __name__ == "__main__": asyncio.run(main())