# 修复 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`