# 数据库设计 ## 概述 PR-Helper 使用 MySQL 作为主数据库,存储用户信息、仓库元数据、分析结果和审查笔记。 ## 数据库配置 ### 连接参数 ```env MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 MYSQL_USER=root MYSQL_PASSWORD= MYSQL_DATABASE=pr_helper ``` ### DSN 格式 ``` {user}:{password}@tcp({host}:{port})/{database}?charset=utf8mb4&parseTime=True&loc=Local ``` ## 表结构 ### 1. settings 表 全局键值存储,保存系统配置。 ```sql CREATE TABLE IF NOT EXISTS settings ( `key` VARCHAR(255) PRIMARY KEY, value TEXT NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` | 字段 | 类型 | 说明 | |------|------|------| | key | VARCHAR(255) | 配置键名(主键) | | value | TEXT | 配置值 | #### 默认设置 ```go var DefaultSettings = map[string]string{ "llm.endpoint": "https://api.deepseek.com", "llm.api_key": "", "llm.model": "deepseek-v4-pro", "review.top_n": "20", "review.concurrency": "5", "cache.max_age_days": "7", "cache.max_size_mb": "5000", } ``` ### 2. users 表 用户账户信息。 ```sql CREATE TABLE IF NOT EXISTS users ( id BIGINT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, password_hash TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 用户 ID(主键,自增) | | email | VARCHAR(255) | 邮箱(唯一) | | password_hash | TEXT | 密码哈希 | | created_at | DATETIME | 创建时间 | | updated_at | DATETIME | 更新时间 | ### 3. user_settings 表 用户个人设置,覆盖全局默认值。 ```sql CREATE TABLE IF NOT EXISTS user_settings ( user_id BIGINT NOT NULL, `key` VARCHAR(255) NOT NULL, value TEXT NOT NULL, PRIMARY KEY (user_id, `key`), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` | 字段 | 类型 | 说明 | |------|------|------| | user_id | BIGINT | 用户 ID(外键) | | key | VARCHAR(255) | 配置键名 | | value | TEXT | 配置值 | #### 常用配置键 | 键名 | 说明 | 默认值 | |------|------|--------| | llm.endpoint | LLM API 端点 | https://api.deepseek.com | | llm.api_key | LLM API 密钥 | (空) | | llm.model | LLM 模型名称 | deepseek-v4-pro | | review.top_n | 审查文件数上限 | 20 | | review.concurrency | 审查并发数 | 5 | ### 4. repositories 表 克隆的仓库元数据。 ```sql CREATE TABLE IF NOT EXISTS repositories ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT, url TEXT NOT NULL, local_path TEXT NOT NULL, size_bytes BIGINT DEFAULT 0, cloned_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_used DATETIME DEFAULT CURRENT_TIMESTAMP, auth_type VARCHAR(20) NOT NULL DEFAULT 'none', credential TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 仓库 ID(主键,自增) | | user_id | BIGINT | 所有者用户 ID(外键) | | url | TEXT | 仓库 URL | | local_path | TEXT | 本地存储路径 | | size_bytes | BIGINT | 仓库大小(字节) | | cloned_at | DATETIME | 克隆时间 | | last_used | DATETIME | 最后使用时间 | | auth_type | VARCHAR(20) | 认证类型(none/https/ssh) | | credential | TEXT | 认证凭证(不明文返回) | ### 5. analyses 表 分析结果存储(PR 描述和代码审查)。 ```sql CREATE TABLE IF NOT EXISTS analyses ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT, repo_id BIGINT, type VARCHAR(50) NOT NULL, base_ref TEXT NOT NULL, head_ref TEXT NOT NULL, result LONGTEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (repo_id) REFERENCES repositories(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 分析 ID(主键,自增) | | user_id | BIGINT | 用户 ID(外键) | | repo_id | BIGINT | 仓库 ID(外键) | | type | VARCHAR(50) | 类型(pr_description/code_review) | | base_ref | TEXT | 基准分支 | | head_ref | TEXT | 目标分支 | | result | LONGTEXT | 结果 JSON | | created_at | DATETIME | 创建时间 | #### result 字段格式 **PR 描述类型**: ```json { "content": "# 标题\n\n## 概述\n..." } ``` **代码审查类型**: ```json { "file_reviews": [ { "file_name": "main.go", "change_lines": 42, "suggestions": [ { "severity": "warning", "description": "...", "suggestion": "...", "code_example": "..." } ], "raw_review": "..." } ], "summary": { "score": 7, "overall": "...", "findings": "...", "recommendations": "..." }, "top_n": 20 } ``` ### 6. review_notes 表 用户添加的审查笔记。 ```sql CREATE TABLE IF NOT EXISTS review_notes ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT, analysis_id BIGINT, scope VARCHAR(50) NOT NULL, scope_key TEXT NOT NULL, content TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (analysis_id) REFERENCES analyses(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 笔记 ID(主键,自增) | | user_id | BIGINT | 用户 ID(外键) | | analysis_id | BIGINT | 分析 ID(外键) | | scope | VARCHAR(50) | 作用域类型 | | scope_key | TEXT | 作用域键 | | content | TEXT | 笔记内容 | | created_at | DATETIME | 创建时间 | | updated_at | DATETIME | 更新时间 | #### scope 类型 | scope | scope_key | 说明 | |-------|-----------|------| | overall | (空) | 整体审查笔记 | | file | 文件名 | 文件级别笔记 | | suggestion | 建议 ID | 建议级别笔记 | ## 索引设计 ### 主键索引 - 所有表使用 BIGINT 自增主键 - settings 表使用 key 作为主键 ### 唯一索引 - users.email: 唯一约束 ### 复合主键 - user_settings: (user_id, key) ### 外键索引 - user_settings.user_id → users.id - repositories.user_id → users.id - analyses.user_id → users.id - analyses.repo_id → repositories.id - review_notes.user_id → users.id - review_notes.analysis_id → analyses.id ## 迁移策略 ### 自动迁移 ```go func (db *DB) migrate() error { stmts := []string{ "CREATE TABLE IF NOT EXISTS ...", // ... } for _, s := range stmts { if _, err := db.conn.Exec(s); err != nil { return err } } return nil } ``` ### 增量迁移 ```go alterStmts := []string{ "ALTER TABLE repositories ADD COLUMN auth_type VARCHAR(20) NOT NULL DEFAULT 'none'", "ALTER TABLE repositories ADD COLUMN credential TEXT", } for _, s := range alterStmts { db.conn.Exec(s) // 忽略错误(列已存在) } ``` - 使用 `IF NOT EXISTS` 避免重复创建 - 忽略 "duplicate column" 错误实现幂等性 ### 默认数据初始化 ```go func (db *DB) seedDefaults() error { for key, val := range models.DefaultSettings { _, err := db.conn.Exec( "INSERT IGNORE INTO settings (`key`, value) VALUES (?, ?)", key, val, ) // ... } return nil } ``` - 使用 `INSERT IGNORE` 避免重复插入 ## 数据完整性 ### 外键约束 - 级联删除(ON DELETE CASCADE) - 删除用户时自动清理相关数据 ### 数据验证 - 应用层验证(handler 层) - 数据库约束(NOT NULL, UNIQUE) ## 性能优化 ### 查询优化 - 使用参数化查询避免 SQL 注入 - 避免 SELECT *,只查询需要的字段 - 合理使用索引 ### 连接管理 - 使用 database/sql 连接池 - 设置合理的连接超时 - 及时关闭连接 ### 大字段处理 - result 使用 LONGTEXT - 考虑分表或外部存储(未来优化) ## 备份策略 ### 逻辑备份 ```bash mysqldump -u root -p pr_helper > backup.sql ``` ### 恢复 ```bash mysql -u root -p pr_helper < backup.sql ``` ### 定期备份 - 建议每日备份 - 保留最近 7 天备份 - 异地备份(生产环境) ## 监控 ### 关键指标 - 连接数 - 查询耗时 - 慢查询 - 表大小 ### 命令 ```sql -- 查看连接数 SHOW STATUS LIKE 'Threads_connected'; -- 查看慢查询 SHOW VARIABLES LIKE 'slow_query_log'; -- 查看表大小 SELECT table_name, round(((data_length + index_length) / 1024 / 1024), 2) AS "Size (MB)" FROM information_schema.tables WHERE table_schema = 'pr_helper'; ```