AnCao/docs/fix_practice_progress_index.md
yanlongqi 59364700bc feat: 练习进度系统重构和AI评分持久化
重大改进:
- 练习进度模型优化:从"每题一条记录"改为"每用户每类型一条记录",提升性能和数据管理
- 完全基于后端数据库恢复答题进度,移除 localStorage 依赖,提高可靠性
- AI评分结果持久化:在答题记录中保存AI评分、评语和建议,支持历史查看

后端改进:
- 新增 /api/practice/progress 接口获取练习进度(支持按类型筛选)
- 新增 /api/practice/progress 接口清除练习进度(支持按类型清除)
- PracticeProgress 模型重构:添加 current_question_id 和 user_answer_records 字段
- UserAnswerRecord 模型增强:添加 ai_score、ai_feedback、ai_suggestion 字段
- 提交答案时自动保存AI评分到数据库

前端优化:
- 答题进度完全从后端加载,移除 localStorage 备份逻辑
- 修复判断题答案格式转换问题(boolean -> string)
- 优化随机模式:首次答题时随机选择起始题目
- 改进答题历史显示:显示答题序号和历史答案标识
- 已答题目切换时保持答案和结果显示状态
- 清除进度时支持按类型清除(而非清空所有)

技术优化:
- 统一索引策略:从 idx_user_question 改为 idx_user_type
- JSON 字段类型从 jsonp 改为 jsonb(PostgreSQL 性能优化)
- 增加详细的日志记录,便于调试和追踪

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 05:51:48 +08:00

5.3 KiB
Raw Blame History

修复 practice_progress 表索引问题

问题描述

在测试中发现除了单选题multiple-choice能正常插入进度数据外其他类型的题目无法插入数据到 practice_progress 表。

问题原因

错误的索引定义

之前的模型定义(错误):

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"

正确的索引定义

修复后的模型定义(正确):

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 数据库:

    psql -U your_username -d your_database
    

    执行:

    DROP TABLE IF EXISTS practice_progress;
    
  3. 重启服务GORM 会自动创建正确的表结构

    .\bin\server.exe
    

方案2手动修复索引保留数据

  1. 连接到 PostgreSQL 数据库

    psql -U your_username -d your_database
    
  2. 执行 SQL 脚本

    \i scripts/fix_practice_progress_index.sql
    

    或手动执行:

    -- 删除旧索引
    DROP INDEX IF EXISTS idx_user_question;
    
    -- 创建新索引
    CREATE UNIQUE INDEX IF NOT EXISTS idx_user_type ON practice_progress(user_id, type);
    
  3. 验证索引

    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. 检查现有数据是否有冲突

    SELECT user_id, type, COUNT(*)
    FROM practice_progress
    GROUP BY user_id, type
    HAVING COUNT(*) > 1;
    

    如果有重复数据,需要手动清理:

    -- 保留每组的最新记录,删除旧记录
    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. 检查表结构

\d practice_progress

应该看到:

Indexes:
    "practice_progress_pkey" PRIMARY KEY, btree (id)
    "idx_user_type" UNIQUE, btree (user_id, type)

2. 测试插入不同题型

测试步骤

  1. 登录系统
  2. 选择"单选题",答几道题
  3. 切换到"多选题",答几道题
  4. 切换到"判断题",答几道题

检查数据库

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. 数据备份

    • 在修改表结构前,建议备份数据:
    pg_dump -U your_username -d your_database -t practice_progress > backup_practice_progress.sql
    
  3. 回滚方案

    • 如果需要回滚,可以恢复备份:
    psql -U your_username -d your_database < backup_practice_progress.sql
    

相关文件

  • 模型定义:internal/models/practice_progress.go
  • 写入逻辑:internal/handlers/practice_handler.go:356-387
  • SQL 修复脚本:scripts/fix_practice_progress_index.sql