chore: 删除老旧的 migrate 迁移工具脚本

移除了整个 scripts/migrate 目录下的所有迁移相关脚本文件,包括README、多平台启动脚本、各功能迁移脚本等,不再维护这套手动迁移方案。
This commit is contained in:
2026-05-28 20:20:44 +08:00
parent c1075bc9ab
commit 49b4046fd4
6 changed files with 0 additions and 1125 deletions
-63
View File
@@ -1,63 +0,0 @@
# Monitor Models 迁移工具
## 📁 文件说明
| 文件 | 用途 |
|------|------|
| `migrate_all_in_one.bat` / `.sh` | ✨ **唯一需要运行的脚本** |
| `auto_migrate.py` | 自动迁移脚本 |
| `migrate_with_tortoise.py` | Tortoise 表创建脚本 |
| `add_log_query_indexes.py` | 日志查询索引优化脚本 |
| `monitor_orm_config.py` | ORM 配置 |
| `README.md` | 本说明文件 |
---
## 🚀 使用方法
**只需要运行一个文件:**
**Windows:**
```
scripts\migrate\migrate_all_in_one.bat
```
**Linux:**
```
./scripts/migrate/migrate_all_in_one.sh
```
---
## 📋 菜单选项
| 选项 | 功能 | 推荐使用场景 |
|------|------|--------------|
| **[1]** | Auto Migration(自动迁移) | ✅ **日常新增/更新模型(推荐)** |
| **[2]** | Create tables with Tortoise | 仅创建新表 |
| **[3]** | Reset migrations | 重置所有迁移 |
| **[4]** | Add log query indexes | ✅ **优化日志查询性能** |
| **[5]** | Backup only | 仅备份数据库 |
| **[Q]** | 退出 | - |
---
## 🎯 日常流程
### 新增/更新模型
1.`apps/common/monitor/models.py` 中添加/修改模型
2. 运行迁移脚本
3. 选择 **[1]**
4. 完成!✅
### 优化日志查询性能
1. 运行迁移脚本
2. 选择 **[4]**
3. 自动创建索引(client_ip, method, timestamp, level 等)
4. 完成!✅
---
## 💡 推荐
**日常开发请优先使用 [1] Auto Migration** - 自动备份、更简单、更可靠!
-248
View File
@@ -1,248 +0,0 @@
"""
数据库索引迁移脚本 - 日志查询功能强化
任务ID: T1.1
为支持日志查询功能强化(高级过滤、分页排序),添加以下索引:
1. api_requests: client_ip, method, (timestamp, status_code)
2. outbound_api_requests: method, (timestamp, status_code)
3. system_logs: (timestamp, level)
用法:
# 执行迁移(创建索引)
python scripts/migrate/add_log_query_indexes.py --action migrate
# 回滚迁移(删除索引)
python scripts/migrate/add_log_query_indexes.py --action rollback
# 检查状态
python scripts/migrate/add_log_query_indexes.py --action status
"""
import asyncio
import argparse
import sys
from pathlib import Path
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))
from tortoise import Tortoise
from tortoise.backends.sqlite.client import SqliteClient
from globalobjects import logger
INDEX_DEFINITIONS = {
"api_requests": {
"idx_api_requests_client_ip": ["client_ip"],
"idx_api_requests_method": ["method"],
"idx_api_requests_timestamp_status": ["timestamp", "status_code"],
},
"outbound_api_requests": {
"idx_outbound_method": ["method"],
"idx_outbound_timestamp_status": ["timestamp", "status_code"],
},
"system_logs": {
"idx_logs_timestamp_level": ["timestamp", "level"],
},
}
async def get_db_client() -> SqliteClient:
"""获取数据库客户端"""
from core.settings import SQLITE_FILE
if not Tortoise._inited:
await Tortoise.init(
db_url=f"sqlite://{SQLITE_FILE}",
modules={"models": ["apps.common.monitor.models"]},
)
return Tortoise.get_connection("default")
async def check_table_exists(client: SqliteClient, table: str) -> bool:
"""检查表是否存在"""
query = f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table}'"
result = await client.execute_query(query)
return len(result[1]) > 0
async def check_index_exists(client: SqliteClient, table: str, index_name: str) -> bool:
"""检查索引是否存在(SQLite"""
if not await check_table_exists(client, table):
return False
query = f"SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='{table}' AND name='{index_name}'"
result = await client.execute_query(query)
return len(result[1]) > 0
async def create_index(client: SqliteClient, table: str, index_name: str, columns: list) -> bool:
"""创建索引"""
try:
if not await check_table_exists(client, table):
logger.info(f"表不存在,跳过: {table}")
return False
if await check_index_exists(client, table, index_name):
logger.info(f"索引已存在,跳过: {table}.{index_name}")
return False
cols = ", ".join(columns)
sql = f"CREATE INDEX {index_name} ON {table} ({cols})"
await client.execute_query(sql)
logger.success(f"创建索引成功", f"{table}.{index_name}", f"({cols})")
return True
except Exception as e:
logger.error(f"创建索引失败: {table}.{index_name}", exc_info=True)
return False
async def drop_index(client: SqliteClient, table: str, index_name: str) -> bool:
"""删除索引"""
try:
if not await check_index_exists(client, table, index_name):
logger.info(f"索引不存在,跳过: {table}.{index_name}")
return False
sql = f"DROP INDEX {index_name}"
await client.execute_query(sql)
logger.success(f"删除索引成功", f"{table}.{index_name}")
return True
except Exception as e:
logger.error(f"删除索引失败: {table}.{index_name}", exc_info=True)
return False
async def migrate():
"""执行迁移:创建所有索引"""
logger.start("执行索引迁移", "创建")
client = await get_db_client()
created_count = 0
skipped_count = 0
failed_count = 0
for table, indexes in INDEX_DEFINITIONS.items():
logger.info(f"处理表: {table}")
# 检查表是否存在
if not await check_table_exists(client, table):
logger.info(f"表不存在,跳过所有索引: {table}")
skipped_count += len(indexes)
continue
for index_name, columns in indexes.items():
result = await create_index(client, table, index_name, columns)
if result:
created_count += 1
elif await check_index_exists(client, table, index_name):
skipped_count += 1
else:
failed_count += 1
logger.stop("索引迁移完成", f"创建:{created_count} 跳过:{skipped_count} 失败:{failed_count}")
if failed_count > 0:
logger.error("部分索引创建失败,请检查日志")
return False
return True
async def rollback():
"""回滚迁移:删除所有索引"""
logger.start("执行索引迁移", "回滚")
client = await get_db_client()
dropped_count = 0
skipped_count = 0
failed_count = 0
for table, indexes in INDEX_DEFINITIONS.items():
logger.info(f"处理表: {table}")
for index_name in indexes.keys():
result = await drop_index(client, table, index_name)
if result:
dropped_count += 1
elif not await check_index_exists(client, table, index_name):
skipped_count += 1
else:
failed_count += 1
logger.stop("索引回滚完成", f"删除:{dropped_count} 跳过:{skipped_count} 失败:{failed_count}")
if failed_count > 0:
logger.error("部分索引删除失败,请检查日志")
return False
return True
async def status():
"""检查索引状态"""
logger.info("检查索引状态...")
client = await get_db_client()
print("\n" + "=" * 60)
print("索引状态报告")
print("=" * 60)
total_count = 0
existing_count = 0
table_not_exists_count = 0
for table, indexes in INDEX_DEFINITIONS.items():
table_exists = await check_table_exists(client, table)
print(f"\n表: {table}", "- 存在" if table_exists else "- 不存在")
print("-" * 40)
if not table_exists:
table_not_exists_count += len(indexes)
for index_name, columns in indexes.items():
print(f" {index_name:40} ⊘ 表不存在")
continue
for index_name, columns in indexes.items():
exists = await check_index_exists(client, table, index_name)
status_str = "✓ 已创建" if exists else "✗ 未创建"
print(f" {index_name:40} {status_str}")
total_count += 1
if exists:
existing_count += 1
print("\n" + "=" * 60)
print(f"总计: {existing_count}/{total_count} 个索引已创建, {table_not_exists_count} 个索引待表创建")
print("=" * 60 + "\n")
async def main():
parser = argparse.ArgumentParser(description="日志查询功能强化 - 索引迁移脚本")
parser.add_argument(
"--action",
choices=["migrate", "rollback", "status"],
default="status",
help="操作类型: migrate(创建), rollback(回滚), status(检查)"
)
args = parser.parse_args()
try:
if args.action == "migrate":
success = await migrate()
sys.exit(0 if success else 1)
elif args.action == "rollback":
success = await rollback()
sys.exit(0 if success else 1)
elif args.action == "status":
await status()
sys.exit(0)
except Exception as e:
logger.exception(f"执行失败: {e}")
sys.exit(1)
finally:
if Tortoise._inited:
await Tortoise.close_connections()
if __name__ == "__main__":
asyncio.run(main())
-257
View File
@@ -1,257 +0,0 @@
#!/usr/bin/env python3
"""
智能自动迁移脚本
功能:
1. 自动备份数据库(安全第一)
2. 自动检测数据库表结构
3. 自动创建缺失的表
4. 自动添加缺失的字段
5. 保留现有数据
6. 无需用户干预,一键完成迁移
使用方法:
python scripts/migrate/auto_migrate.py
"""
import os
import sys
import asyncio
import shutil
from datetime import datetime
from pathlib import Path
# 设置标准输出为 UTF-8 编码
sys.stdout.reconfigure(encoding='utf-8')
# 添加项目根目录到路径
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from tortoise import Tortoise
def backup_database(db_path):
"""备份数据库文件"""
backup_dir = Path("backups")
backup_dir.mkdir(exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = backup_dir / f"{db_path.stem}_{timestamp}.sqlite3"
try:
shutil.copy2(db_path, backup_path)
print(f"✅ 数据库备份成功: {backup_path}")
return backup_path
except Exception as e:
print(f"❌ 数据库备份失败: {e}")
return None
async def get_existing_tables(db):
"""获取数据库中已存在的表"""
# execute_query 返回 (count, rows) 元组
count, rows = await db.execute_query("SELECT name FROM sqlite_master WHERE type='table';")
return {row[0] for row in rows} if rows else set()
async def get_existing_columns(db, table_name):
"""获取指定表中已存在的字段"""
# execute_query 返回 (count, rows) 元组
count, rows = await db.execute_query(f"PRAGMA table_info({table_name});")
return {row[1] for row in rows} if rows else set()
async def get_model_fields(model):
"""获取模型定义的字段"""
return {field_name: field for field_name, field in model._meta.fields_map.items()}
def get_sqlite_type(field):
"""获取字段对应的 SQLite 类型"""
field_type = type(field).__name__
type_mapping = {
'IntField': 'INTEGER',
'BigIntField': 'BIGINT',
'SmallIntField': 'INTEGER',
'CharField': 'TEXT',
'TextField': 'TEXT',
'LongTextField': 'TEXT',
'FloatField': 'REAL',
'DecimalField': 'REAL',
'DatetimeField': 'TIMESTAMP',
'DateField': 'DATE',
'TimeField': 'TIME',
'BooleanField': 'INTEGER',
'JSONField': 'TEXT',
'UUIDField': 'TEXT',
'BinaryField': 'BLOB',
}
return type_mapping.get(field_type)
def format_sql_value(value):
"""格式化 SQL 值"""
if value is None:
return 'NULL'
if isinstance(value, str):
return f"'{value}'"
if isinstance(value, bool):
return '1' if value else '0'
return str(value)
async def add_missing_columns(db, table_name, model):
"""为指定表添加缺失的字段"""
existing_columns = await get_existing_columns(db, table_name)
model_fields = await get_model_fields(model)
added_columns = []
for field_name, field in model_fields.items():
if field_name not in existing_columns:
# 跳过主键字段(应该已经存在)
if isinstance(field, type) and hasattr(field, 'pk') and field.pk:
continue
# 生成 ALTER TABLE 语句
sqlite_type = get_sqlite_type(field)
if not sqlite_type:
print(f" ⚠️ 无法识别字段类型: {field_name} ({type(field).__name__})")
continue
sql = f"ALTER TABLE {table_name} ADD COLUMN {field_name} {sqlite_type}"
# 添加默认值(如果有)
if hasattr(field, 'default') and field.default is not None and field.default != type(field).NO_DEFAULT:
if callable(field.default):
try:
default_val = field.default()
if default_val is not None:
sql += f" DEFAULT {format_sql_value(default_val)}"
except Exception:
pass
else:
sql += f" DEFAULT {format_sql_value(field.default)}"
# 如果字段允许为空,添加 NULL
if hasattr(field, 'null') and field.null:
sql += " NULL"
else:
sql += " NOT NULL"
print(f" 添加字段: {field_name}")
await db.execute_query(sql)
added_columns.append(field_name)
return added_columns
async def create_table_if_not_exists(db, model):
"""如果表不存在,创建表"""
table_name = model._meta.db_table
existing_tables = await get_existing_tables(db)
if table_name not in existing_tables:
print(f" 创建表: {table_name}")
# 使用 Tortoise ORM 的 generate_schemas 方法,safe=True 表示只创建不存在的表
await Tortoise.generate_schemas(safe=True)
return True
return False
def get_user_choice():
"""获取用户选择"""
print("========================================")
print(" 🚀 智能自动迁移脚本")
print("========================================")
print()
print("请选择迁移方式:")
print(" 1. 不带备份(快速)")
print(" 2. 带备份(安全,推荐)")
print()
while True:
choice = input("请输入选项 (1 或 2): ").strip()
if choice in ["1", "2"]:
return choice == "2" # 返回是否需要备份
print("❌ 无效输入,请输入 1 或 2")
async def main():
# 获取用户选择
need_backup = get_user_choice()
# 获取数据库路径
from core.settings import SQLITE_FILE
db_path = Path("storage") / f"{SQLITE_FILE}.sqlite3"
backup_path = None
# Step 1: 备份数据库(如果用户选择)
if need_backup:
print("\n[Step 1] 备份数据库...")
backup_path = backup_database(db_path)
if not backup_path:
print(" ⚠️ 备份失败,继续执行迁移...")
else:
print("\n[Step 1] 跳过备份(用户选择)")
# Step 2: 初始化 Tortoise
print("\n[Step 2] 连接数据库...")
from scripts.migrate.migrate_with_tortoise import monitor_orm_config
try:
await Tortoise.init(config=monitor_orm_config)
print(" ✅ 数据库连接成功")
except Exception as e:
print(f" ❌ 数据库连接失败: {e}")
sys.exit(1)
try:
db = Tortoise.get_connection(SQLITE_FILE)
# Step 3: 获取所有注册的模型
from apps.common.monitor.models import APIRequest, OutboundAPIRequest, SystemLog, FailedOperation, BinlogPosition, ProcessedEvent
models = [APIRequest, OutboundAPIRequest, SystemLog, FailedOperation, BinlogPosition, ProcessedEvent]
print("\n[Step 3] 处理表结构...")
total_tables_created = 0
total_columns_added = 0
for model in models:
table_name = model._meta.db_table
print(f"\n 处理表: {table_name}")
# 创建表(如果不存在)
created = await create_table_if_not_exists(db, model)
if created:
total_tables_created += 1
print(f" ✅ 表已创建")
else:
# 添加缺失字段
added_columns = await add_missing_columns(db, table_name, model)
if added_columns:
total_columns_added += len(added_columns)
print(f" ✅ 添加了 {len(added_columns)} 个字段: {', '.join(added_columns)}")
else:
print(f" ✅ 字段已完整,无需更新")
# Step 4: 完成
print("\n========================================")
print(" ✅ 迁移完成!")
print("========================================")
print(f" 结果统计:")
print(f" - 创建表: {total_tables_created}")
print(f" - 添加字段: {total_columns_added}")
if backup_path:
print(f" - 备份文件: {backup_path}")
print("\n 🎉 数据库结构已同步到最新模型定义")
finally:
await Tortoise.close_connections()
if __name__ == "__main__":
asyncio.run(main())
-266
View File
@@ -1,266 +0,0 @@
@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
set "PYTHON_VENV_DIR=venv"
echo ========================================
echo Monitor Models - Setup Tool
echo ========================================
echo [INFO] Current dir: %cd%
echo [INFO] Script path: %~dp0
echo.
echo [INFO] Checking required files...
if not exist "%~dp0\..\..\.env" (
echo [ERROR] .env file not found!
pause
exit /b 1
)
echo [OK] .env file exists
for /f "tokens=2 delims==" %%a in ('findstr "^PYTHON_VENV_DIR=" "%~dp0\..\..\.env"') do set "PYTHON_VENV_DIR=%%a"
set "PYTHON_VENV_DIR=%PYTHON_VENV_DIR: =%"
echo [INFO] Virtual env dir: %PYTHON_VENV_DIR%
if not exist "%~dp0\..\..\%PYTHON_VENV_DIR%\Scripts\python.exe" (
echo [ERROR] Python not found in venv!
pause
exit /b 1
)
echo [OK] Python exists
echo.
:menu
cls
echo ========================================
echo Monitor Models - Migration Tool
echo ========================================
echo.
echo Default option [1] runs auto migration
echo.
echo Please select an operation:
echo.
echo [1] Auto Migration (Recommended)
echo - Backup database automatically
echo - Create missing tables
echo - Add missing fields
echo - Preserve existing data
echo.
echo [2] Create tables with Tortoise
echo - Only create new tables
echo.
echo [3] Reset migrations
echo - Delete all migrations and re-init
echo.
echo [4] Add log query indexes
echo - Optimize log query performance
echo.
echo [5] Backup only
echo - Just backup database
echo.
echo [Q] Exit
echo.
echo ========================================
set /p choice="Enter option (default: 1): "
if /i "%choice%"=="" goto :auto_migrate
if /i "%choice%"=="1" goto :auto_migrate
if /i "%choice%"=="2" goto :tortoise
if /i "%choice%"=="3" goto :reset
if /i "%choice%"=="4" goto :add_indexes
if /i "%choice%"=="5" goto :backup_only
if /i "%choice%"=="Q" goto :end
if /i "%choice%"=="q" goto :end
echo [ERROR] Invalid option, please try again
pause
goto :menu
:backup_only
echo.
echo ========================================
echo [5] Backup Only
echo ========================================
call :setup_env
if errorlevel 1 goto :end
echo.
echo [Step 1] Checking database file...
if not exist "storage\%SQLITE_FILE%.sqlite3" (
echo [ERROR] Database not found: storage\%SQLITE_FILE%.sqlite3
pause
goto :end
)
echo [OK] Database exists
echo.
echo [Step 2] Creating backup...
set "BACKUP_DIR=backups"
if not exist "%BACKUP_DIR%" mkdir "%BACKUP_DIR%"
set "TIMESTAMP=%date:~0,4%%date:~5,2%%date:~8,2%_%time:~0,2%%time:~3,2%%time:~6,2%"
set "TIMESTAMP=%TIMESTAMP: =0%"
set "BACKUP_FILE=%BACKUP_DIR%\%SQLITE_FILE%_%TIMESTAMP%.sqlite3"
copy "storage\%SQLITE_FILE%.sqlite3" "%BACKUP_FILE%"
if errorlevel 1 (
echo [ERROR] Backup failed
pause
goto :end
)
echo [OK] Backup successful: %BACKUP_FILE%
echo.
echo ========================================
echo Backup completed!
echo ========================================
goto :end
:auto_migrate
echo.
echo ========================================
echo [1] Auto Migration
echo ========================================
call :setup_env
if errorlevel 1 goto :end
echo.
echo [INFO] Running auto migration...
echo.
%PYTHON_VENV_DIR%\Scripts\python.exe scripts\migrate\auto_migrate.py
if errorlevel 1 goto :error
goto :success
:tortoise
echo.
echo ========================================
echo [2] Create tables with Tortoise
echo ========================================
call :setup_env
if errorlevel 1 goto :end
echo.
echo [INFO] This option only creates new tables
echo.
%PYTHON_VENV_DIR%\Scripts\python.exe scripts\migrate\migrate_with_tortoise.py
if errorlevel 1 goto :error
goto :success
:reset
echo.
echo ========================================
echo [3] Reset migrations
echo ========================================
echo.
echo [WARNING] This will delete all migrations!
choice /C YN /M "Are you sure"
if errorlevel 2 (
echo [INFO] Cancelled
goto :end
)
call :setup_env
if errorlevel 1 goto :end
echo.
echo [1/2] Deleting migrations...
if exist "migrations\monitor_models" (
rmdir /s /q "migrations\monitor_models"
echo [OK] Deleted
)
if exist "migrations" (
for /d %%d in (migrations\*) do rmdir /s /q "%%d"
echo [OK] All deleted
)
echo.
echo [2/2] Re-creating tables with Tortoise...
%PYTHON_VENV_DIR%\Scripts\python.exe scripts\migrate\migrate_with_tortoise.py
if errorlevel 1 goto :error
echo.
echo ========================================
echo Migrations reset!
echo ========================================
goto :end
:add_indexes
echo.
echo ========================================
echo [4] Add Log Query Indexes
echo ========================================
call :setup_env
if errorlevel 1 goto :end
echo.
echo [INFO] Creating indexes for log query optimization...
echo.
%PYTHON_VENV_DIR%\Scripts\python.exe scripts\migrate\add_log_query_indexes.py --action migrate
if errorlevel 1 goto :error
goto :success
:setup_env
cd /d "%~dp0\..\.."
set "PROJECT_DIR="
set "SQLITE_FILE=local_data"
for /f "tokens=2 delims==" %%a in ('findstr "^PROJECT_DIR=" "%~dp0\..\..\.env"') do set "PROJECT_DIR=%%a"
for /f "tokens=2 delims==" %%a in ('findstr "^SQLITE_FILE=" "%~dp0\..\..\.env"') do set "SQLITE_FILE=%%a"
if "%PROJECT_DIR%"=="" (
echo [ERROR] PROJECT_DIR not found in .env
pause
exit /b 1
)
setx PROJECT_DIR "%PROJECT_DIR%" >nul 2>&1
set PROJECT_DIR=%PROJECT_DIR%
set "SQLITE_FILE=%SQLITE_FILE:.sqlite3=%"
echo Project Directory: %PROJECT_DIR%
echo SQLite File: %SQLITE_FILE%
if not exist "storage" (
echo [INFO] Creating storage...
mkdir storage
if errorlevel 1 (
echo [ERROR] Failed to create storage
pause
exit /b 1
)
echo [OK] Created
)
exit /b 0
:success
echo.
echo ========================================
echo Operation completed successfully!
echo ========================================
goto :end
:error
echo.
echo ========================================
echo Operation failed!
echo ========================================
pause
exit /b 1
:end
echo.
pause
-226
View File
@@ -1,226 +0,0 @@
#!/bin/bash
# =====================================================
# Monitor Models - Migration Tool (Ubuntu/Linux)
# Usage: ./migrate_all_in_one.sh [option]
# 1 - Auto Migration (default)
# 2 - Create tables with Tortoise
# 3 - Reset migrations
# 5 - Backup only
# =====================================================
set -e
# 项目根目录
PROJECT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$PROJECT_DIR"
# 查找Python解释器
find_python() {
if [ -f "$PROJECT_DIR/venv/bin/python" ]; then
echo "$PROJECT_DIR/venv/bin/python"
return
fi
if command -v python3 &> /dev/null; then
echo "python3"
return
fi
echo "python3"
}
PYTHON_CMD=$(find_python)
# 从.env读取配置
load_env() {
if [ -f "$PROJECT_DIR/.env" ]; then
export $(grep -v '^#' "$PROJECT_DIR/.env" | grep -v '^$' | xargs)
fi
}
load_env
SQLITE_FILE="${SQLITE_FILE:-local_data}"
SQLITE_FILE="${SQLITE_FILE%.sqlite3}"
# 显示菜单
show_menu() {
clear
echo "========================================"
echo " Monitor Models - Migration Tool"
echo "========================================"
echo ""
echo " Default option [1] runs auto migration"
echo ""
echo " Please select an operation:"
echo ""
echo " [1] Auto Migration (Recommended)"
echo " - Backup database automatically"
echo " - Create missing tables"
echo " - Add missing fields"
echo " - Preserve existing data"
echo ""
echo " [2] Create tables with Tortoise"
echo " - Only create new tables"
echo ""
echo " [3] Reset migrations"
echo " - Delete all migrations and re-init"
echo ""
echo " [4] Add log query indexes"
echo " - Optimize log query performance"
echo ""
echo " [5] Backup only"
echo " - Just backup database"
echo ""
echo " [Q] Exit"
echo ""
echo "========================================"
}
# 备份数据库
backup_db() {
echo ""
echo "========================================"
echo " Backup Only"
echo "========================================"
local db_file="storage/${SQLITE_FILE}.sqlite3"
if [ ! -f "$db_file" ]; then
echo "[ERROR] Database not found: $db_file"
return 1
fi
echo "[OK] Database exists"
local backup_dir="backups"
mkdir -p "$backup_dir"
local timestamp=$(date +%Y%m%d_%H%M%S)
local backup_file="${backup_dir}/${SQLITE_FILE}_${timestamp}.sqlite3"
cp "$db_file" "$backup_file"
echo "[OK] Backup successful: $backup_file"
echo ""
echo "========================================"
echo " Backup completed!"
echo "========================================"
}
# 自动迁移
auto_migrate() {
echo ""
echo "========================================"
echo " Auto Migration"
echo "========================================"
echo ""
echo "[INFO] Running auto migration..."
echo ""
$PYTHON_CMD scripts/migrate/auto_migrate.py
}
# 使用 Tortoise 创建表
tortoise_create() {
echo ""
echo "========================================"
echo " Create tables with Tortoise"
echo "========================================"
echo ""
echo "[INFO] This option only creates new tables"
echo ""
$PYTHON_CMD scripts/migrate/migrate_with_tortoise.py
}
# 重置迁移
reset_migrations() {
echo ""
echo "========================================"
echo " Reset migrations"
echo "========================================"
echo ""
echo "[WARNING] This will delete all migrations!"
read -p "Are you sure? (y/N): " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
echo "[INFO] Cancelled"
return 0
fi
echo ""
echo "[1/2] Deleting migrations..."
if [ -d "migrations/monitor_models" ]; then
rm -rf "migrations/monitor_models"
echo "[OK] Deleted migrations/monitor_models"
fi
if [ -d "migrations" ]; then
rm -rf migrations/*
echo "[OK] Cleared migrations directory"
fi
echo ""
echo "[2/2] Re-creating tables with Tortoise..."
$PYTHON_CMD scripts/migrate/migrate_with_tortoise.py
echo ""
echo "========================================"
echo " Migrations reset!"
echo "========================================"
}
# 添加日志查询索引
add_log_indexes() {
echo ""
echo "========================================"
echo " Add Log Query Indexes"
echo "========================================"
echo ""
echo "[INFO] Creating indexes for log query optimization..."
echo ""
$PYTHON_CMD scripts/migrate/add_log_query_indexes.py --action migrate
}
# 主函数
main() {
local choice="${1:-}"
if [ -z "$choice" ]; then
show_menu
read -p "Enter option (default: 1): " choice
choice="${choice:-1}"
fi
case "$choice" in
1)
auto_migrate
;;
2)
tortoise_create
;;
3)
reset_migrations
;;
4)
add_log_indexes
;;
5)
backup_db
;;
[Qq])
echo "Exit"
exit 0
;;
*)
echo "[ERROR] Invalid option: $choice"
show_menu
exit 1
;;
esac
}
main "$@"
-65
View File
@@ -1,65 +0,0 @@
"""
使用 Tortoise ORM 直接生成表的脚本(更可靠)
替代 aerich 迁移方案,直接调用 generate_schemas()
同时提供 Aerich 迁移所需的配置
"""
import asyncio
import sys
import os
# 添加项目路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from tortoise import Tortoise
from core.database import TORTOISE_ORM_CONFIG
from globalobjects import logger as log_config
logger = log_config.get_logger(__name__)
# Aerich 迁移所需的配置变量
monitor_orm_config = TORTOISE_ORM_CONFIG
async def main():
"""主函数 - 直接生成所有表"""
logger.info("=" * 50)
logger.info(" Tortoise ORM - 直接生成数据库表")
logger.info("=" * 50)
try:
# 初始化 Tortoise ORM
logger.info("⏳ 初始化 Tortoise ORM...")
await Tortoise.init(config=TORTOISE_ORM_CONFIG)
# 直接生成所有 schema
logger.info("⏳ 生成数据库表 (generate_schemas)...")
await Tortoise.generate_schemas(safe=True) # safe=True: 不删除已存在的表
logger.info("✅ 所有表已生成/更新成功!")
# 列出所有注册的模型
logger.info("")
logger.info("📋 已注册的模型:")
# 从配置中获取模型信息
for app_name, app_config in TORTOISE_ORM_CONFIG['apps'].items():
logger.info(f" - App '{app_name}':")
models = app_config.get('models', [])
for model_path in models:
if model_path == 'aerich.models':
continue
logger.info(f" - {model_path}")
# 关闭连接
await Tortoise.close_connections()
logger.info("")
logger.info("✅ 数据库连接已关闭")
logger.info("=" * 50)
except Exception as e:
logger.error(f"❌ 生成表失败: {e}")
import traceback
logger.error(f"堆栈信息: {traceback.format_exc()}")
raise
if __name__ == "__main__":
# 运行主函数
asyncio.run(main())