CodeReview/backend/app/db/init_db.py

277 lines
12 KiB
Python

"""
数据库初始化模块
在应用启动时创建默认演示账户和演示数据
"""
import json
import logging
from datetime import datetime, timedelta, timezone
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from app.core.security import get_password_hash
from app.models.user import User
from app.models.project import Project, ProjectMember
from app.models.audit import AuditTask, AuditIssue
from app.models.analysis import InstantAnalysis
logger = logging.getLogger(__name__)
# 默认演示账户配置
DEFAULT_DEMO_EMAIL = "demo@example.com"
DEFAULT_DEMO_PASSWORD = "demo123"
DEFAULT_DEMO_NAME = "演示用户"
async def create_demo_user(db: AsyncSession) -> User | None:
"""
创建演示用户账户
- demo@example.com / demo123
"""
result = await db.execute(select(User).where(User.email == DEFAULT_DEMO_EMAIL))
demo_user = result.scalars().first()
if not demo_user:
demo_user = User(
email=DEFAULT_DEMO_EMAIL,
hashed_password=get_password_hash(DEFAULT_DEMO_PASSWORD),
full_name=DEFAULT_DEMO_NAME,
is_active=True,
is_superuser=True, # 演示用户拥有管理员权限以便体验所有功能
role="admin",
)
db.add(demo_user)
await db.flush()
logger.info(f"✓ 创建演示账户: {DEFAULT_DEMO_EMAIL}")
return demo_user
else:
logger.info(f"演示账户已存在: {DEFAULT_DEMO_EMAIL}")
return demo_user
async def create_demo_data(db: AsyncSession, user: User) -> None:
"""
为演示用户创建演示数据,用于仪表盘展示
"""
# 检查是否已有演示数据
result = await db.execute(select(Project).where(Project.owner_id == user.id))
existing_projects = result.scalars().all()
if existing_projects:
logger.info("演示数据已存在,跳过创建")
return
logger.info("开始创建演示数据...")
now = datetime.now(timezone.utc)
# ==================== 创建演示项目 ====================
projects_data = [
{
"name": "电商平台后端",
"description": "基于 Spring Boot 的电商平台后端服务,包含用户管理、商品管理、订单处理等模块",
"source_type": "repository",
"repository_url": "https://github.com/example/ecommerce-backend",
"repository_type": "github",
"default_branch": "main",
"programming_languages": json.dumps(["Java", "SQL"]),
},
{
"name": "移动端 App",
"description": "React Native 跨平台移动应用,支持 iOS 和 Android",
"source_type": "repository",
"repository_url": "https://github.com/example/mobile-app",
"repository_type": "github",
"default_branch": "develop",
"programming_languages": json.dumps(["TypeScript", "JavaScript"]),
},
{
"name": "数据分析平台",
"description": "Python 数据分析和可视化平台,集成机器学习模型",
"source_type": "zip",
"repository_url": None,
"repository_type": "other",
"default_branch": "main",
"programming_languages": json.dumps(["Python"]),
},
{
"name": "微服务网关",
"description": "基于 Go 的高性能 API 网关,支持限流、熔断、负载均衡",
"source_type": "repository",
"repository_url": "https://gitlab.com/example/api-gateway",
"repository_type": "gitlab",
"default_branch": "master",
"programming_languages": json.dumps(["Go"]),
},
{
"name": "智能客服系统",
"description": "基于 NLP 的智能客服系统,支持多轮对话、意图识别和知识库问答",
"source_type": "repository",
"repository_url": "https://github.com/example/smart-customer-service",
"repository_type": "github",
"default_branch": "main",
"programming_languages": json.dumps(["Python", "JavaScript"]),
},
{
"name": "区块链钱包",
"description": "多链加密货币钱包,支持 ETH、BTC 等主流币种的存储和转账",
"source_type": "zip",
"repository_url": None,
"repository_type": "other",
"default_branch": "main",
"programming_languages": json.dumps(["Rust", "TypeScript"]),
},
]
projects = []
for i, pdata in enumerate(projects_data):
project = Project(
owner_id=user.id,
is_active=True,
created_at=now - timedelta(days=30 - i * 5),
**pdata
)
db.add(project)
projects.append(project)
await db.flush()
logger.info(f"✓ 创建了 {len(projects)} 个演示项目")
# ==================== 创建审计任务和问题 ====================
tasks_data = [
# 项目1: 电商平台后端
{"project_idx": 0, "status": "completed", "days_ago": 25, "files": 156, "lines": 12500, "issues": 23, "score": 72.5},
{"project_idx": 0, "status": "completed", "days_ago": 15, "files": 162, "lines": 13200, "issues": 18, "score": 78.3},
{"project_idx": 0, "status": "completed", "days_ago": 5, "files": 168, "lines": 14100, "issues": 12, "score": 85.2},
# 项目2: 移动端 App
{"project_idx": 1, "status": "completed", "days_ago": 20, "files": 89, "lines": 8900, "issues": 15, "score": 68.7},
{"project_idx": 1, "status": "completed", "days_ago": 8, "files": 95, "lines": 9500, "issues": 8, "score": 82.1},
{"project_idx": 1, "status": "running", "days_ago": 0, "files": 98, "lines": 9800, "issues": 0, "score": 0},
# 项目3: 数据分析平台
{"project_idx": 2, "status": "completed", "days_ago": 12, "files": 45, "lines": 5600, "issues": 9, "score": 76.4},
{"project_idx": 2, "status": "completed", "days_ago": 2, "files": 52, "lines": 6200, "issues": 5, "score": 88.9},
# 项目4: 微服务网关
{"project_idx": 3, "status": "completed", "days_ago": 18, "files": 78, "lines": 9200, "issues": 11, "score": 74.8},
{"project_idx": 3, "status": "failed", "days_ago": 3, "files": 0, "lines": 0, "issues": 0, "score": 0},
# 项目5: 智能客服系统
{"project_idx": 4, "status": "completed", "days_ago": 22, "files": 134, "lines": 15800, "issues": 19, "score": 71.2},
{"project_idx": 4, "status": "completed", "days_ago": 10, "files": 142, "lines": 16500, "issues": 14, "score": 79.6},
{"project_idx": 4, "status": "completed", "days_ago": 1, "files": 148, "lines": 17200, "issues": 7, "score": 86.8},
# 项目6: 区块链钱包
{"project_idx": 5, "status": "completed", "days_ago": 16, "files": 67, "lines": 8400, "issues": 16, "score": 65.3},
{"project_idx": 5, "status": "completed", "days_ago": 6, "files": 72, "lines": 9100, "issues": 9, "score": 77.5},
]
tasks = []
for tdata in tasks_data:
task_time = now - timedelta(days=tdata["days_ago"])
task = AuditTask(
project_id=projects[tdata["project_idx"]].id,
created_by=user.id,
task_type="full_scan",
status=tdata["status"],
branch_name="main",
total_files=tdata["files"],
scanned_files=tdata["files"] if tdata["status"] == "completed" else 0,
total_lines=tdata["lines"],
issues_count=tdata["issues"],
quality_score=tdata["score"],
started_at=task_time,
completed_at=task_time + timedelta(minutes=5) if tdata["status"] == "completed" else None,
created_at=task_time,
)
db.add(task)
tasks.append(task)
await db.flush()
logger.info(f"✓ 创建了 {len(tasks)} 个审计任务")
# ==================== 创建审计问题 ====================
issue_templates = [
{"type": "security", "severity": "critical", "title": "SQL 注入漏洞", "file": "UserService.java", "line": 45},
{"type": "security", "severity": "high", "title": "硬编码密钥", "file": "config/secrets.py", "line": 12},
{"type": "security", "severity": "high", "title": "XSS 跨站脚本攻击风险", "file": "components/Comment.tsx", "line": 78},
{"type": "security", "severity": "medium", "title": "不安全的随机数生成", "file": "utils/token.go", "line": 23},
{"type": "bug", "severity": "high", "title": "空指针异常风险", "file": "OrderController.java", "line": 156},
{"type": "bug", "severity": "medium", "title": "数组越界访问", "file": "DataProcessor.py", "line": 89},
{"type": "bug", "severity": "low", "title": "未处理的 Promise 拒绝", "file": "api/client.ts", "line": 34},
{"type": "performance", "severity": "medium", "title": "N+1 查询问题", "file": "ProductRepository.java", "line": 67},
{"type": "performance", "severity": "low", "title": "不必要的重复渲染", "file": "pages/Dashboard.tsx", "line": 112},
{"type": "style", "severity": "low", "title": "函数过长,建议拆分", "file": "services/payment.go", "line": 45},
{"type": "maintainability", "severity": "medium", "title": "重复代码块", "file": "handlers/auth.go", "line": 78},
{"type": "maintainability", "severity": "low", "title": "缺少错误处理", "file": "utils/http.py", "line": 56},
]
issue_count = 0
for task in tasks:
if task.status != "completed" or task.issues_count == 0:
continue
# 为每个完成的任务创建问题
num_issues = min(task.issues_count, len(issue_templates))
for i in range(num_issues):
template = issue_templates[i % len(issue_templates)]
issue = AuditIssue(
task_id=task.id,
file_path=f"src/{template['file']}",
line_number=template["line"] + i * 10,
issue_type=template["type"],
severity=template["severity"],
title=template["title"],
message=template["title"],
description=f"在文件 {template['file']}{template['line'] + i * 10} 行发现 {template['title']},这可能导致安全风险或程序异常。",
suggestion="建议进行代码审查并修复此问题。详细修复方案请参考相关安全规范。",
status="open" if i % 3 != 0 else "resolved",
resolved_by=user.id if i % 3 == 0 else None,
resolved_at=now - timedelta(days=i) if i % 3 == 0 else None,
created_at=task.created_at,
)
db.add(issue)
issue_count += 1
await db.flush()
logger.info(f"✓ 创建了 {issue_count} 个审计问题")
# ==================== 创建即时分析记录 ====================
analyses_data = [
{"lang": "Python", "issues": 3, "score": 75.5, "days_ago": 10},
{"lang": "JavaScript", "issues": 5, "score": 68.2, "days_ago": 8},
{"lang": "Java", "issues": 2, "score": 82.1, "days_ago": 6},
{"lang": "Go", "issues": 1, "score": 91.3, "days_ago": 4},
{"lang": "TypeScript", "issues": 4, "score": 72.8, "days_ago": 2},
{"lang": "Python", "issues": 0, "score": 95.0, "days_ago": 1},
]
for adata in analyses_data:
analysis = InstantAnalysis(
user_id=user.id,
language=adata["lang"],
code_content="# 演示代码\nprint('Hello, World!')",
analysis_result=json.dumps({"issues": [], "summary": "演示分析结果"}),
issues_count=adata["issues"],
quality_score=adata["score"],
analysis_time=2.5,
created_at=now - timedelta(days=adata["days_ago"]),
)
db.add(analysis)
await db.flush()
logger.info(f"✓ 创建了 {len(analyses_data)} 条即时分析记录")
await db.commit()
logger.info("✓ 演示数据创建完成")
async def init_db(db: AsyncSession) -> None:
"""
初始化数据库
"""
logger.info("开始初始化数据库...")
# 创建演示用户
demo_user = await create_demo_user(db)
# 创建演示数据
if demo_user:
await create_demo_data(db, demo_user)
await db.commit()
logger.info("数据库初始化完成")