""" 数据库初始化模块 在应用启动时创建默认演示账户和演示数据 """ 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("数据库初始化完成")