package models import ( "database/sql/driver" "encoding/json" "time" "gorm.io/gorm" ) // WrongQuestion 错题记录 type WrongQuestion struct { ID uint `gorm:"primarykey" json:"id"` UserID uint `gorm:"index;not null" json:"user_id"` QuestionID uint `gorm:"index;not null" json:"question_id"` FirstWrongTime time.Time `json:"first_wrong_time"` LastWrongTime time.Time `json:"last_wrong_time"` TotalWrongCount int `gorm:"default:1" json:"total_wrong_count"` MasteryLevel int `gorm:"default:0" json:"mastery_level"` // 0-100 ConsecutiveCorrect int `gorm:"default:0" json:"consecutive_correct"` IsMastered bool `gorm:"default:false" json:"is_mastered"` Tags QuestionTags `gorm:"type:text" json:"tags"` // JSON 存储标签 CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 关联 PracticeQuestion *PracticeQuestion `gorm:"foreignKey:QuestionID;references:ID" json:"question,omitempty"` History []WrongQuestionHistory `gorm:"foreignKey:WrongQuestionID" json:"history,omitempty"` } // WrongQuestionHistory 错误历史记录 type WrongQuestionHistory struct { ID uint `gorm:"primarykey" json:"id"` WrongQuestionID uint `gorm:"index;not null" json:"wrong_question_id"` UserAnswer string `gorm:"type:text;not null" json:"user_answer"` // JSON 存储 CorrectAnswer string `gorm:"type:text;not null" json:"correct_answer"` // JSON 存储 AnsweredAt time.Time `gorm:"index" json:"answered_at"` TimeSpent int `json:"time_spent"` // 答题用时(秒) IsCorrect bool `json:"is_correct"` } // WrongQuestionTag 错题标签 type WrongQuestionTag struct { ID uint `gorm:"primarykey" json:"id"` UserID uint `gorm:"index;not null" json:"user_id"` Name string `gorm:"size:50;not null" json:"name"` Color string `gorm:"size:20;default:'#1890ff'" json:"color"` Description string `gorm:"size:200" json:"description"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` } // QuestionTags 标签列表(用于 JSON 存储) type QuestionTags []string // Scan 从数据库读取 func (t *QuestionTags) Scan(value interface{}) error { if value == nil { *t = []string{} return nil } bytes, ok := value.([]byte) if !ok { str, ok := value.(string) if !ok { *t = []string{} return nil } bytes = []byte(str) } if len(bytes) == 0 || string(bytes) == "null" { *t = []string{} return nil } return json.Unmarshal(bytes, t) } // Value 写入数据库 func (t QuestionTags) Value() (driver.Value, error) { if len(t) == 0 { return "[]", nil } bytes, err := json.Marshal(t) return string(bytes), err } // TableName 指定表名 func (WrongQuestion) TableName() string { return "wrong_questions" } // TableName 指定表名 func (WrongQuestionHistory) TableName() string { return "wrong_question_history" } // TableName 指定表名 func (WrongQuestionTag) TableName() string { return "wrong_question_tags" } // WrongQuestionDTO 错题数据传输对象 type WrongQuestionDTO struct { ID uint `json:"id"` QuestionID uint `json:"question_id"` Question *PracticeQuestionDTO `json:"question"` FirstWrongTime time.Time `json:"first_wrong_time"` LastWrongTime time.Time `json:"last_wrong_time"` TotalWrongCount int `json:"total_wrong_count"` MasteryLevel int `json:"mastery_level"` ConsecutiveCorrect int `json:"consecutive_correct"` IsMastered bool `json:"is_mastered"` Tags []string `json:"tags"` RecentHistory []WrongQuestionHistoryDTO `json:"recent_history,omitempty"` // 最近3次历史 } // WrongQuestionHistoryDTO 错误历史 DTO type WrongQuestionHistoryDTO struct { ID uint `json:"id"` UserAnswer interface{} `json:"user_answer"` CorrectAnswer interface{} `json:"correct_answer"` AnsweredAt time.Time `json:"answered_at"` TimeSpent int `json:"time_spent"` IsCorrect bool `json:"is_correct"` } // WrongQuestionStats 错题统计 type WrongQuestionStats struct { TotalWrong int `json:"total_wrong"` // 总错题数 Mastered int `json:"mastered"` // 已掌握数 NotMastered int `json:"not_mastered"` // 未掌握数 NeedReview int `json:"need_review"` // 需要复习数 TypeStats map[string]int `json:"type_stats"` // 按题型统计 CategoryStats map[string]int `json:"category_stats"` // 按分类统计 MasteryLevelDist map[string]int `json:"mastery_level_dist"` // 掌握度分布 TagStats map[string]int `json:"tag_stats"` // 按标签统计 TrendData []TrendPoint `json:"trend_data"` // 错题趋势 } // TrendPoint 趋势数据点 type TrendPoint struct { Date string `json:"date"` Count int `json:"count"` } // RecordWrongAnswer 记录错误答案 func (wq *WrongQuestion) RecordWrongAnswer() { now := time.Now() if wq.FirstWrongTime.IsZero() { wq.FirstWrongTime = now } wq.LastWrongTime = now wq.TotalWrongCount++ wq.ConsecutiveCorrect = 0 // 重置连续答对次数 wq.IsMastered = false // 重新标记为未掌握 wq.MasteryLevel = 0 // 重置掌握度 } // RecordCorrectAnswer 记录正确答案 func (wq *WrongQuestion) RecordCorrectAnswer() { wq.ConsecutiveCorrect++ // 根据连续答对次数更新掌握度(每答对一次增加16.67%) // 连续答对6次即达到100% wq.MasteryLevel = (wq.ConsecutiveCorrect * 100) / 6 if wq.MasteryLevel > 100 { wq.MasteryLevel = 100 } // 连续答对6次标记为已掌握 if wq.ConsecutiveCorrect >= 6 { wq.IsMastered = true wq.MasteryLevel = 100 } } // AddTag 添加标签 func (wq *WrongQuestion) AddTag(tag string) { for _, t := range wq.Tags { if t == tag { return // 标签已存在 } } wq.Tags = append(wq.Tags, tag) } // RemoveTag 移除标签 func (wq *WrongQuestion) RemoveTag(tag string) { newTags := []string{} for _, t := range wq.Tags { if t != tag { newTags = append(newTags, t) } } wq.Tags = newTags }