mirror of
https://github.com/Cccc-owo/CheckInApp.git
synced 2026-06-17 14:06:28 +00:00
refactor: v2
backend & frontend
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
创建管理员用户的脚本
|
||||
|
||||
使用方法:
|
||||
python backend/scripts/create_admin.py
|
||||
|
||||
或使用虚拟环境:
|
||||
./venv/Scripts/python.exe backend/scripts/create_admin.py
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到路径
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
sys.path.insert(0, str(BASE_DIR))
|
||||
|
||||
from backend.models import init_db, User
|
||||
from backend.models.database import SessionLocal
|
||||
from backend.services.auth_service import AuthService
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_admin_user(alias: str):
|
||||
"""
|
||||
将现有用户升级为管理员(或创建管理员占位符)
|
||||
|
||||
Args:
|
||||
alias: 用户别名
|
||||
"""
|
||||
# 初始化数据库
|
||||
init_db()
|
||||
|
||||
# 创建数据库会话
|
||||
db = SessionLocal()
|
||||
|
||||
try:
|
||||
# 检查别名是否已存在
|
||||
existing_user = db.query(User).filter(User.alias == alias).first()
|
||||
|
||||
if existing_user:
|
||||
print(f"[OK] 找到用户:{alias}")
|
||||
print(f" 用户 ID: {existing_user.id}")
|
||||
print(f" QQ 标识 (jwt_sub): {existing_user.jwt_sub}")
|
||||
print(f" 当前角色: {existing_user.role}")
|
||||
print(f" 审批状态: {existing_user.is_approved}")
|
||||
|
||||
# 如果已经是管理员
|
||||
if existing_user.role == "admin":
|
||||
print("\n该用户已经是管理员")
|
||||
return
|
||||
|
||||
# 升级为管理员
|
||||
response = input("\n是否将该用户升级为管理员?(y/n): ")
|
||||
if response.lower() == 'y':
|
||||
existing_user.role = "admin"
|
||||
existing_user.is_approved = True # 确保已审批
|
||||
db.commit()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("[成功] 用户已升级为管理员!")
|
||||
print("=" * 60)
|
||||
print(f" 用户 ID: {existing_user.id}")
|
||||
print(f" 别名: {existing_user.alias}")
|
||||
print(f" QQ 标识: {existing_user.jwt_sub}")
|
||||
print(f" 角色: admin")
|
||||
print("=" * 60)
|
||||
else:
|
||||
print("操作已取消")
|
||||
else:
|
||||
print(f"\n[错误] 未找到别名为 '{alias}' 的用户")
|
||||
print("\n请先使用该别名进行 QQ 扫码注册,然后再运行此脚本升级为管理员")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[错误] 操作失败: {e}")
|
||||
db.rollback()
|
||||
raise
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=" * 60)
|
||||
print("接龙自动打卡系统 - 设置管理员")
|
||||
print("=" * 60)
|
||||
print()
|
||||
print("[说明]")
|
||||
print(" 此脚本将已注册的用户升级为管理员")
|
||||
print(" 请先使用别名进行 QQ 扫码注册,然后运行此脚本")
|
||||
print()
|
||||
|
||||
# 获取用户别名
|
||||
alias = input("请输入要设置为管理员的用户别名 [admin]: ").strip() or "admin"
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print(f"准备将用户 '{alias}' 设置为管理员")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
create_admin_user(alias)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
数据库迁移脚本:为 task_templates 表添加 parent_id 字段
|
||||
|
||||
运行方法:
|
||||
python backend/scripts/migrate_add_parent_id_to_templates.py
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# 设置 UTF-8 编码输出(Windows 兼容)
|
||||
if sys.platform == "win32":
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
||||
|
||||
# 添加项目根目录到 Python 路径
|
||||
project_root = Path(__file__).parent.parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from sqlalchemy import text
|
||||
from backend.models.database import engine, SessionLocal
|
||||
|
||||
|
||||
def migrate():
|
||||
"""为 task_templates 表添加 parent_id 字段"""
|
||||
print("=" * 60)
|
||||
print("开始数据库迁移:添加 parent_id 字段到 task_templates 表")
|
||||
print("=" * 60)
|
||||
|
||||
db = SessionLocal()
|
||||
|
||||
try:
|
||||
# 检查字段是否已存在
|
||||
result = db.execute(text(
|
||||
"SELECT COUNT(*) FROM pragma_table_info('task_templates') WHERE name='parent_id'"
|
||||
))
|
||||
field_exists = result.fetchone()[0] > 0
|
||||
|
||||
if field_exists:
|
||||
print("⚠️ parent_id 字段已存在,跳过迁移")
|
||||
return
|
||||
|
||||
# 添加 parent_id 字段
|
||||
print("📝 正在添加 parent_id 字段...")
|
||||
db.execute(text(
|
||||
"ALTER TABLE task_templates ADD COLUMN parent_id INTEGER"
|
||||
))
|
||||
db.commit()
|
||||
print("✅ parent_id 字段添加成功")
|
||||
|
||||
# 创建外键约束(SQLite 不支持直接添加外键,需要重建表)
|
||||
print("\n📝 注意:SQLite 不支持直接添加外键约束")
|
||||
print(" 如需外键约束,请重建表或在下次完整迁移时处理")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✅ 数据库迁移完成!")
|
||||
print("=" * 60)
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ 迁移失败: {str(e)}")
|
||||
db.rollback()
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
migrate()
|
||||
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
添加 payload_config 字段到 check_in_tasks 表的迁移脚本
|
||||
|
||||
运行方式:
|
||||
python backend/scripts/migrate_add_payload_config.py
|
||||
或
|
||||
.venv/Scripts/python.exe backend/scripts/migrate_add_payload_config.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到 Python 路径
|
||||
project_root = Path(__file__).resolve().parent.parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from sqlalchemy import text
|
||||
from backend.models.database import engine
|
||||
|
||||
|
||||
def migrate():
|
||||
"""执行迁移"""
|
||||
print("开始迁移:添加 payload_config 字段...")
|
||||
|
||||
with engine.connect() as conn:
|
||||
# 检查字段是否已存在
|
||||
result = conn.execute(text("PRAGMA table_info(check_in_tasks)"))
|
||||
columns = [row[1] for row in result]
|
||||
|
||||
if 'payload_config' in columns:
|
||||
print("[OK] payload_config 字段已存在,跳过迁移")
|
||||
return
|
||||
|
||||
# 添加 payload_config 字段(JSON 文本,存储完整的 payload 配置)
|
||||
print("添加 payload_config 字段...")
|
||||
conn.execute(text("""
|
||||
ALTER TABLE check_in_tasks
|
||||
ADD COLUMN payload_config TEXT DEFAULT '{}' NOT NULL
|
||||
"""))
|
||||
conn.commit()
|
||||
|
||||
print("[OK] payload_config 字段添加成功")
|
||||
print("\n注意:现有任务的 payload_config 默认为空 JSON {},")
|
||||
print(" Worker 将使用默认的固定字段值。")
|
||||
print(" 新创建的任务将从模板继承完整的 payload 配置。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
migrate()
|
||||
print("\n[SUCCESS] 迁移完成!")
|
||||
except Exception as e:
|
||||
print(f"\n[ERROR] 迁移失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,132 @@
|
||||
"""
|
||||
删除 check_in_tasks 表中不再需要的旧列的迁移脚本
|
||||
|
||||
删除的列:
|
||||
- signature (VARCHAR) - 已在 payload_config 中
|
||||
- texts (VARCHAR) - 已在 payload_config 中
|
||||
- values (TEXT) - 已在 payload_config 中
|
||||
- thread_id (VARCHAR) - 已在 payload_config 的 ThreadId 中
|
||||
- email (VARCHAR) - 从 user 表的 email 字段获取
|
||||
|
||||
新架构只保留:
|
||||
- id, user_id, payload_config, name, is_active, created_at, updated_at
|
||||
|
||||
运行方式:
|
||||
python backend/scripts/migrate_remove_old_columns.py
|
||||
或
|
||||
venv/Scripts/python.exe backend/scripts/migrate_remove_old_columns.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到 Python 路径
|
||||
project_root = Path(__file__).resolve().parent.parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from sqlalchemy import text, inspect
|
||||
from backend.models.database import engine
|
||||
|
||||
|
||||
def migrate():
|
||||
"""执行迁移:删除旧列"""
|
||||
print("开始迁移:删除 check_in_tasks 表中的旧列...")
|
||||
print("将删除的列: signature, texts, values, thread_id, email")
|
||||
print("=" * 60)
|
||||
|
||||
with engine.connect() as conn:
|
||||
# 检查表结构
|
||||
inspector = inspect(engine)
|
||||
columns = [col['name'] for col in inspector.get_columns('check_in_tasks')]
|
||||
|
||||
print(f"\n当前表列: {', '.join(columns)}")
|
||||
|
||||
old_columns = ['signature', 'texts', 'values', 'thread_id', 'email']
|
||||
columns_to_remove = [col for col in old_columns if col in columns]
|
||||
|
||||
if not columns_to_remove:
|
||||
print("\n[OK] 旧列已被删除,跳过迁移")
|
||||
return
|
||||
|
||||
print(f"\n需要删除的列: {', '.join(columns_to_remove)}")
|
||||
|
||||
# SQLite 不支持直接 DROP COLUMN,需要重建表
|
||||
# 步骤:
|
||||
# 1. 创建新表(只包含需要的列)
|
||||
# 2. 复制数据
|
||||
# 3. 删除旧表
|
||||
# 4. 重命名新表
|
||||
|
||||
print("\n正在重建表结构...")
|
||||
|
||||
# 1. 创建新表
|
||||
conn.execute(text("""
|
||||
CREATE TABLE check_in_tasks_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
payload_config TEXT NOT NULL DEFAULT '{}',
|
||||
name VARCHAR(100) DEFAULT '',
|
||||
is_active BOOLEAN DEFAULT 1,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)
|
||||
"""))
|
||||
print(" [OK] 创建新表结构")
|
||||
|
||||
# 2. 复制数据(只复制保留的列)
|
||||
conn.execute(text("""
|
||||
INSERT INTO check_in_tasks_new
|
||||
(id, user_id, payload_config, name, is_active, created_at, updated_at)
|
||||
SELECT
|
||||
id, user_id, payload_config, name, is_active, created_at, updated_at
|
||||
FROM check_in_tasks
|
||||
"""))
|
||||
print(" [OK] 复制数据到新表")
|
||||
|
||||
# 3. 删除旧表
|
||||
conn.execute(text("DROP TABLE check_in_tasks"))
|
||||
print(" [OK] 删除旧表")
|
||||
|
||||
# 4. 重命名新表
|
||||
conn.execute(text("ALTER TABLE check_in_tasks_new RENAME TO check_in_tasks"))
|
||||
print(" [OK] 重命名新表")
|
||||
|
||||
# 5. 重建索引
|
||||
conn.execute(text("""
|
||||
CREATE INDEX ix_check_in_tasks_user_id ON check_in_tasks(user_id)
|
||||
"""))
|
||||
conn.execute(text("""
|
||||
CREATE INDEX ix_check_in_tasks_id ON check_in_tasks(id)
|
||||
"""))
|
||||
conn.execute(text("""
|
||||
CREATE INDEX ix_task_user_active ON check_in_tasks(user_id, is_active)
|
||||
"""))
|
||||
print(" [OK] 重建索引")
|
||||
|
||||
conn.commit()
|
||||
|
||||
print("\n[SUCCESS] 表结构迁移成功!")
|
||||
print("\n新的表结构:")
|
||||
inspector = inspect(engine)
|
||||
new_columns = [col['name'] for col in inspector.get_columns('check_in_tasks')]
|
||||
print(f" 列: {', '.join(new_columns)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
migrate()
|
||||
print("\n" + "=" * 60)
|
||||
print("[完成] 迁移成功完成!")
|
||||
print("\n数据库已更新为新架构:")
|
||||
print(" - 删除了 signature, texts, values, thread_id, email 列")
|
||||
print(" - 保留了 payload_config 列(存储完整的 JSON payload)")
|
||||
print(" - ThreadId 现在存储在 payload_config 中")
|
||||
print(" - Email 现在从 user 表获取")
|
||||
print("=" * 60)
|
||||
except Exception as e:
|
||||
print(f"\n[ERROR] 迁移失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user