AnCao/docs/fix_practice_progress_index.md

186 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 修复 practice_progress 表索引问题
## 问题描述
在测试中发现除了单选题multiple-choice能正常插入进度数据外其他类型的题目无法插入数据到 `practice_progress` 表。
## 问题原因
### 错误的索引定义
**之前的模型定义**(错误):
```go
type PracticeProgress struct {
ID int64 `gorm:"primarykey" json:"id"`
CurrentQuestionID int64 `gorm:"not null" json:"current_question_id"`
UserID int64 `gorm:"not null;uniqueIndex:idx_user_question" json:"user_id"` // ❌ 只在 user_id 上建索引
Type string `gorm:"type:varchar(255);not null" json:"type"`
UserAnswerRecords datatypes.JSON `gorm:"type:jsonp" json:"answers"`
}
```
**问题**
- 唯一索引 `idx_user_question` 只在 `user_id` 字段上
- 同一用户只能有一条进度记录
- 当用户答第二种题型时,因为 `user_id` 重复,插入失败
- 日志中应该会看到类似错误:`duplicate key value violates unique constraint "idx_user_question"`
### 正确的索引定义
**修复后的模型定义**(正确):
```go
type PracticeProgress struct {
ID int64 `gorm:"primarykey" json:"id"`
CurrentQuestionID int64 `gorm:"not null" json:"current_question_id"`
UserID int64 `gorm:"not null;uniqueIndex:idx_user_type" json:"user_id"` // ✅ 联合索引
Type string `gorm:"type:varchar(255);not null;uniqueIndex:idx_user_type" json:"type"` // ✅ 联合索引
UserAnswerRecords datatypes.JSON `gorm:"type:jsonb" json:"answers"`
}
```
**改进**
- 唯一索引 `idx_user_type``(user_id, type)` 的联合索引
- 同一用户可以有多条进度记录(每种题型一条)
- 例如用户1 可以有 `(1, "multiple-choice")``(1, "true-false")` 两条记录
## 解决方案
### 方案1使用 GORM 自动迁移(推荐)
1. **停止当前服务**
2. **删除旧表并重建**(谨慎:会丢失所有进度数据)
连接到 PostgreSQL 数据库:
```bash
psql -U your_username -d your_database
```
执行:
```sql
DROP TABLE IF EXISTS practice_progress;
```
3. **重启服务GORM 会自动创建正确的表结构**
```bash
.\bin\server.exe
```
### 方案2手动修复索引保留数据
1. **连接到 PostgreSQL 数据库**
```bash
psql -U your_username -d your_database
```
2. **手动执行 SQL**
```sql
-- 删除旧索引
DROP INDEX IF EXISTS idx_user_question;
-- 创建新索引
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_type ON practice_progress(user_id, type);
```
3. **验证索引**
```sql
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'practice_progress';
```
应该看到:
```
indexname | indexdef
--------------|--------------------------------------------------
idx_user_type | CREATE UNIQUE INDEX idx_user_type ON practice_progress USING btree (user_id, type)
```
4. **检查现有数据是否有冲突**
```sql
SELECT user_id, type, COUNT(*)
FROM practice_progress
GROUP BY user_id, type
HAVING COUNT(*) > 1;
```
如果有重复数据,需要手动清理:
```sql
-- 保留每组的最新记录,删除旧记录
DELETE FROM practice_progress a
WHERE id NOT IN (
SELECT MAX(id)
FROM practice_progress b
WHERE a.user_id = b.user_id AND a.type = b.type
);
```
## 验证修复
### 1. 检查表结构
```sql
\d practice_progress
```
应该看到:
```
Indexes:
"practice_progress_pkey" PRIMARY KEY, btree (id)
"idx_user_type" UNIQUE, btree (user_id, type)
```
### 2. 测试插入不同题型
**测试步骤**
1. 登录系统
2. 选择"单选题",答几道题
3. 切换到"多选题",答几道题
4. 切换到"判断题",答几道题
**检查数据库**
```sql
SELECT id, user_id, type, current_question_id
FROM practice_progress
WHERE user_id = 1; -- 替换为你的用户ID
```
应该看到多条记录:
```
id | user_id | type | current_question_id
---|---------|---------------------|--------------------
1 | 1 | multiple-choice | 157
2 | 1 | multiple-selection | 45
3 | 1 | true-false | 10
```
### 3. 检查后端日志
如果之前有错误,应该不再看到类似日志:
```
保存练习进度失败: duplicate key value violates unique constraint "idx_user_question"
```
## 其他注意事项
1. **JSONB vs JSONP**
- 修改了 `UserAnswerRecords` 的类型从 `jsonp` 改为 `jsonb`
- `jsonb` 是正确的 PostgreSQL JSON 类型
- 性能更好,支持索引
2. **数据备份**
- 在修改表结构前,建议备份数据:
```bash
pg_dump -U your_username -d your_database -t practice_progress > backup_practice_progress.sql
```
3. **回滚方案**
- 如果需要回滚,可以恢复备份:
```bash
psql -U your_username -d your_database < backup_practice_progress.sql
```
## 相关文件
- 模型定义:`internal/models/practice_progress.go`
- 写入逻辑:`internal/handlers/practice_handler.go:356-387`