275e5cc886
- 01-architecture.md: 架构概览 - 02-backend-services.md: 后端服务层 - 03-frontend-interaction.md: 前端交互设计 - 04-database-design.md: 数据库设计 - 05-api-reference.md: API 接口文档 - 06-sse-streaming.md: SSE 流式传输 - 07-llm-integration.md: LLM 集成 - 08-deployment.md: 部署运维 - 09-development-guide.md: 开发指南 - 10-troubleshooting.md: 故障排查
395 lines
8.8 KiB
Markdown
395 lines
8.8 KiB
Markdown
# 数据库设计
|
||
|
||
## 概述
|
||
|
||
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';
|
||
```
|