2026-04-20 22:47:51 +08:00
|
|
|
|
# 数据库设计文档
|
|
|
|
|
|
|
|
|
|
|
|
## 一、数据库概览
|
|
|
|
|
|
|
|
|
|
|
|
| 项目 | 说明 |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| 数据库名称 | `wordbook` |
|
|
|
|
|
|
| 字符集 | `utf8mb4` |
|
|
|
|
|
|
| 排序规则 | `utf8mb4_unicode_ci` |
|
|
|
|
|
|
| 存储引擎 | `InnoDB` |
|
|
|
|
|
|
|
|
|
|
|
|
## 二、ER 图
|
|
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
erDiagram
|
|
|
|
|
|
USER ||--|{ WORD : has
|
|
|
|
|
|
USER {
|
|
|
|
|
|
uuid id PK "用户ID"
|
|
|
|
|
|
string username UK "用户名"
|
|
|
|
|
|
string password "密码(hash)"
|
|
|
|
|
|
datetime created_at "创建时间"
|
|
|
|
|
|
datetime updated_at "更新时间"
|
2026-04-22 10:10:19 +08:00
|
|
|
|
tinyint is_deleted "软删除标记"
|
2026-04-20 22:47:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
WORD {
|
|
|
|
|
|
uuid id PK "单词记录ID"
|
|
|
|
|
|
uuid user_id FK "所属用户ID"
|
|
|
|
|
|
string word "单词"
|
|
|
|
|
|
text definition "释义"
|
|
|
|
|
|
json examples "例句列表"
|
|
|
|
|
|
string ai_provider "AI模型来源"
|
|
|
|
|
|
datetime created_at "创建时间"
|
|
|
|
|
|
datetime updated_at "更新时间"
|
2026-04-22 10:10:19 +08:00
|
|
|
|
tinyint is_deleted "软删除标记"
|
2026-04-20 22:47:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 三、数据表详情
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 用户表 (users)
|
|
|
|
|
|
|
|
|
|
|
|
| 用户表名 | `users` |
|
|
|
|
|
|
|----------|---------|
|
|
|
|
|
|
|
|
|
|
|
|
| 字段名 | 数据类型 | 约束 | 索引 | 说明 |
|
|
|
|
|
|
|--------|----------|------|------|------|
|
|
|
|
|
|
| `id` | CHAR(36) | PRIMARY KEY | - | 用户唯一标识符(UUID 格式) |
|
|
|
|
|
|
| `username` | VARCHAR(50) | NOT NULL, UNIQUE | UNIQUE KEY | 用户名,全局唯一,用于登录 |
|
|
|
|
|
|
| `password` | VARCHAR(255) | NOT NULL | - | 用户密码,存储 bcrypt 哈希值,严禁明文存储 |
|
|
|
|
|
|
| `created_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | - | 账户创建时间 |
|
|
|
|
|
|
| `updated_at` | DATETIME | ON UPDATE CURRENT_TIMESTAMP | - | 账户最后更新时间 |
|
2026-04-22 10:10:19 +08:00
|
|
|
|
| `is_deleted` | TINYINT | DEFAULT 0 | INDEX | 软删除标记,0 表示未删除,1 表示已删除 |
|
2026-04-20 22:47:51 +08:00
|
|
|
|
|
|
|
|
|
|
**索引设计:**
|
|
|
|
|
|
- 主键索引:`id`
|
|
|
|
|
|
- 唯一索引:`username`(防止重复注册)
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 单词记录表 (words)
|
|
|
|
|
|
|
|
|
|
|
|
| 单词表名 | `words` |
|
|
|
|
|
|
|-----------|---------|
|
|
|
|
|
|
|
2026-04-22 10:10:19 +08:00
|
|
|
|
| 字段名 | 数据类型 | 约束 | 索引 | 说明 |
|
|
|
|
|
|
|--------|----------|------|------|------|
|
|
|
|
|
|
| `id` | CHAR(36) | PRIMARY KEY | - | 单词记录唯一标识符(UUID 格式) |
|
|
|
|
|
|
| `user_id` | CHAR(36) | NOT NULL, FOREIGN KEY | - | 所属用户 ID,关联 users.id |
|
|
|
|
|
|
| `word` | VARCHAR(100) | NOT NULL | INDEX idx_word_search(20) | 单词文本,支持前缀搜索优化 |
|
|
|
|
|
|
| `definition` | TEXT | NOT NULL | - | AI 生成的单词释义 |
|
|
|
|
|
|
| `examples` | JSON | NOT NULL | - | 例句列表,存储 JSON 数组格式 |
|
|
|
|
|
|
| `ai_provider` | ENUM | NOT NULL | - | AI 模型来源标识(`deepseek`=DeepSeek,`qwen`=通义千问) |
|
|
|
|
|
|
| `created_at` | DATETIME | DEFAULT CURRENT_TIMESTAMP | - | 单词记录创建时间 |
|
|
|
|
|
|
| `updated_at` | DATETIME | ON UPDATE CURRENT_TIMESTAMP | - | 单词记录最后更新时间 |
|
|
|
|
|
|
| `is_deleted` | TINYINT | DEFAULT 0 | INDEX idx_user_word | 软删除标记 |
|
2026-04-20 22:47:51 +08:00
|
|
|
|
|
|
|
|
|
|
**索引设计:**
|
|
|
|
|
|
- 主键索引:`id`
|
|
|
|
|
|
- 外键索引:`user_id`(自动创建)
|
|
|
|
|
|
- 唯一索引:`uk_user_word` (user_id, word(50)) - 防止同一用户保存重复单词
|
2026-04-22 10:10:19 +08:00
|
|
|
|
- 复合索引:`idx_user_word` (user_id, is_deleted) - 优化用户单词列表分页查询
|
2026-04-20 22:47:51 +08:00
|
|
|
|
- 前缀索引:`idx_word_search` (word(20)) - 支持单词前缀搜索优化
|
|
|
|
|
|
|
|
|
|
|
|
## 四、表关联关系
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 用户与单词的关系
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
用户 (users) 1 ---- N 单词记录 (words)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- **关系类型**:一对多
|
|
|
|
|
|
- **外键约束**:`words.user_id` → `users.id`
|
|
|
|
|
|
- **级联规则**:`ON DELETE CASCADE`
|
|
|
|
|
|
- 当用户被删除时,该用户的所有单词记录也会被自动删除
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 关联查询示例
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 查询某用户的所有单词(排除已删除)
|
|
|
|
|
|
SELECT * FROM words
|
2026-04-22 10:10:19 +08:00
|
|
|
|
WHERE user_id = ? AND is_deleted = 0
|
2026-04-20 22:47:51 +08:00
|
|
|
|
ORDER BY created_at DESC
|
|
|
|
|
|
LIMIT ? OFFSET ?;
|
|
|
|
|
|
|
|
|
|
|
|
-- 联表查询用户信息及单词数量
|
|
|
|
|
|
SELECT u.id, u.username, COUNT(w.id) as word_count
|
|
|
|
|
|
FROM users u
|
2026-04-22 10:10:19 +08:00
|
|
|
|
LEFT JOIN words w ON u.id = w.user_id AND w.is_deleted = 0
|
|
|
|
|
|
WHERE u.is_deleted = 0
|
2026-04-20 22:47:51 +08:00
|
|
|
|
GROUP BY u.id;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 唯一约束说明
|
|
|
|
|
|
|
|
|
|
|
|
`uk_user_word` 约束确保同一用户不能保存相同的单词两次:
|
|
|
|
|
|
- **触发场景**:在"手动保存单词"时
|
|
|
|
|
|
- **业务逻辑**:前端应先调用"智能查询单词"接口检查是否已保存,避免触发重复错误
|
|
|
|
|
|
- **不触发场景**:"智能查询单词"接口只查询不保存,不会触发此约束
|
|
|
|
|
|
|
|
|
|
|
|
## 五、核心业务 SQL 查询示例
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 智能查询单词
|
|
|
|
|
|
|
|
|
|
|
|
**业务逻辑**:先检查数据库,未命中则调用 AI(不保存)
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 查询:检查用户是否已保存该单词
|
|
|
|
|
|
SELECT id, word, definition, examples, ai_provider
|
|
|
|
|
|
FROM words
|
2026-04-22 10:10:19 +08:00
|
|
|
|
WHERE user_id = ? AND word = ? AND is_deleted = 0;
|
2026-04-20 22:47:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 手动保存单词
|
|
|
|
|
|
|
|
|
|
|
|
**业务逻辑**:将 AI 返回的结果写入数据库
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 插入:保存单词记录(受 uk_user_word 唯一约束保护)
|
|
|
|
|
|
INSERT INTO words (id, user_id, word, definition, examples, ai_provider)
|
|
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.3 获取单词列表(分页)
|
|
|
|
|
|
|
|
|
|
|
|
**业务逻辑**:获取用户所有已保存的单词,支持分页
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 查询:分页获取单词列表(按创建时间倒序)
|
|
|
|
|
|
SELECT id, word, definition, examples, ai_provider, created_at
|
|
|
|
|
|
FROM words
|
2026-04-22 10:10:19 +08:00
|
|
|
|
WHERE user_id = ? AND is_deleted = 0
|
2026-04-20 22:47:51 +08:00
|
|
|
|
ORDER BY created_at DESC
|
|
|
|
|
|
LIMIT ? OFFSET ?;
|
|
|
|
|
|
|
|
|
|
|
|
-- 计算总数(用于分页器)
|
|
|
|
|
|
SELECT COUNT(*) as total
|
|
|
|
|
|
FROM words
|
2026-04-22 10:10:19 +08:00
|
|
|
|
WHERE user_id = ? AND is_deleted = 0;
|
2026-04-20 22:47:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.4 删除单词(软删除)
|
|
|
|
|
|
|
|
|
|
|
|
**业务逻辑**:根据单词 ID 软删除记录
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 更新:软删除单词记录
|
|
|
|
|
|
UPDATE words
|
2026-04-22 10:10:19 +08:00
|
|
|
|
SET is_deleted = 1, updated_at = CURRENT_TIMESTAMP
|
|
|
|
|
|
WHERE id = ? AND user_id = ? AND is_deleted = 0;
|
2026-04-20 22:47:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 六、索引优化说明
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 查询场景分析
|
|
|
|
|
|
|
|
|
|
|
|
| 查询场景 | 涉及字段 | 索引策略 |
|
|
|
|
|
|
|----------|----------|----------|
|
|
|
|
|
|
| 用户登录 | username | UNIQUE KEY |
|
|
|
|
|
|
| 智能查询单词 | user_id, word, deleted_at | uk_user_word(唯一约束) |
|
|
|
|
|
|
| 用户单词列表(分页) | user_id, deleted_at | 复合索引 idx_user_word |
|
|
|
|
|
|
| 单词搜索 | word | 前缀索引 idx_word_search |
|
|
|
|
|
|
| 按用户 ID 查找单词 | user_id | 外键自动索引 |
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 示例 JSON 数据结构 (examples 字段)
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
[
|
|
|
|
|
|
"The word 'serendipity' means finding something good without looking for it.",
|
|
|
|
|
|
"It was pure serendipity that I met my best friend at the coffee shop.",
|
|
|
|
|
|
"Many scientific discoveries are the result of serendipity."
|
|
|
|
|
|
]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 七、数据初始化
|
|
|
|
|
|
|
|
|
|
|
|
数据库初始化脚本位于 `docs/init.sql`,该脚本会在 Docker Compose 启动 MySQL 容器时自动执行。
|
|
|
|
|
|
|
|
|
|
|
|
**严禁使用 GORM 的 `AutoMigrate` 功能进行建表,必须通过此 SQL 脚本初始化数据库。**
|