This commit is contained in:
sjk
2025-11-17 14:09:17 +08:00
commit 31e46c5bf6
479 changed files with 109324 additions and 0 deletions

View File

@@ -0,0 +1,216 @@
package models
import (
"time"
"gorm.io/gorm"
)
// ListeningMaterial 听力材料模型
type ListeningMaterial struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:材料ID"`
Title string `json:"title" gorm:"type:varchar(200);not null;comment:标题"`
Description *string `json:"description" gorm:"type:text;comment:描述"`
AudioURL string `json:"audio_url" gorm:"type:varchar(500);not null;comment:音频URL"`
Transcript *string `json:"transcript" gorm:"type:longtext;comment:音频文本"`
Duration int `json:"duration" gorm:"type:int;comment:时长(秒)"`
Level string `json:"level" gorm:"type:enum('beginner','intermediate','advanced');not null;comment:难度级别"`
Category string `json:"category" gorm:"type:varchar(50);comment:分类"`
Tags *string `json:"tags" gorm:"type:json;comment:标签(JSON数组)"`
IsActive bool `json:"is_active" gorm:"type:boolean;default:true;comment:是否启用"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index;comment:删除时间"`
// 关联关系
ListeningRecords []ListeningRecord `json:"listening_records,omitempty" gorm:"foreignKey:MaterialID"`
}
// ListeningRecord 听力练习记录模型
type ListeningRecord struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:记录ID"`
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index;comment:用户ID"`
MaterialID string `json:"material_id" gorm:"type:varchar(36);not null;index;comment:材料ID"`
Score *float64 `json:"score" gorm:"type:decimal(5,2);comment:得分"`
Accuracy *float64 `json:"accuracy" gorm:"type:decimal(5,2);comment:准确率"`
CompletionRate *float64 `json:"completion_rate" gorm:"type:decimal(5,2);comment:完成率"`
TimeSpent int `json:"time_spent" gorm:"type:int;comment:用时(秒)"`
Answers *string `json:"answers" gorm:"type:json;comment:答案(JSON对象)"`
Feedback *string `json:"feedback" gorm:"type:text;comment:AI反馈"`
StartedAt time.Time `json:"started_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:开始时间"`
CompletedAt *time.Time `json:"completed_at" gorm:"type:timestamp;comment:完成时间"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
User User `json:"-" gorm:"foreignKey:UserID"`
Material ListeningMaterial `json:"-" gorm:"foreignKey:MaterialID"`
}
// ReadingMaterial 阅读材料模型
type ReadingMaterial struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:材料ID"`
Title string `json:"title" gorm:"type:varchar(200);not null;comment:标题"`
Content string `json:"content" gorm:"type:longtext;not null;comment:内容"`
Summary *string `json:"summary" gorm:"type:text;comment:摘要"`
WordCount int `json:"word_count" gorm:"type:int;comment:字数"`
Level string `json:"level" gorm:"type:enum('beginner','intermediate','advanced');not null;comment:难度级别"`
Category string `json:"category" gorm:"type:varchar(50);comment:分类"`
Tags *string `json:"tags" gorm:"type:json;comment:标签(JSON数组)"`
Source *string `json:"source" gorm:"type:varchar(200);comment:来源"`
Author *string `json:"author" gorm:"type:varchar(100);comment:作者"`
IsActive bool `json:"is_active" gorm:"type:boolean;default:true;comment:是否启用"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index;comment:删除时间"`
// 关联关系
ReadingRecords []ReadingRecord `json:"reading_records,omitempty" gorm:"foreignKey:MaterialID"`
}
// ReadingRecord 阅读练习记录模型
type ReadingRecord struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:记录ID"`
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index;comment:用户ID"`
MaterialID string `json:"material_id" gorm:"type:varchar(36);not null;index;comment:材料ID"`
ReadingTime int `json:"reading_time" gorm:"type:int;comment:阅读时间(秒)"`
ComprehensionScore *float64 `json:"comprehension_score" gorm:"type:decimal(5,2);comment:理解得分"`
ReadingSpeed *float64 `json:"reading_speed" gorm:"type:decimal(8,2);comment:阅读速度(词/分钟)"`
Progress float64 `json:"progress" gorm:"type:decimal(5,2);default:0;comment:阅读进度"`
Bookmarks *string `json:"bookmarks" gorm:"type:json;comment:书签(JSON数组)"`
Notes *string `json:"notes" gorm:"type:text;comment:笔记"`
QuizAnswers *string `json:"quiz_answers" gorm:"type:json;comment:测验答案(JSON对象)"`
StartedAt time.Time `json:"started_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:开始时间"`
CompletedAt *time.Time `json:"completed_at" gorm:"type:timestamp;comment:完成时间"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
User User `json:"-" gorm:"foreignKey:UserID"`
Material ReadingMaterial `json:"-" gorm:"foreignKey:MaterialID"`
}
// WritingPrompt 写作题目模型
type WritingPrompt struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:题目ID"`
Title string `json:"title" gorm:"type:varchar(200);not null;comment:标题"`
Prompt string `json:"prompt" gorm:"type:text;not null;comment:题目内容"`
Instructions *string `json:"instructions" gorm:"type:text;comment:写作要求"`
MinWords *int `json:"min_words" gorm:"type:int;comment:最少字数"`
MaxWords *int `json:"max_words" gorm:"type:int;comment:最多字数"`
TimeLimit *int `json:"time_limit" gorm:"type:int;comment:时间限制(分钟)"`
Level string `json:"level" gorm:"type:enum('beginner','intermediate','advanced');not null;comment:难度级别"`
Category string `json:"category" gorm:"type:varchar(50);comment:分类"`
Tags *string `json:"tags" gorm:"type:json;comment:标签(JSON数组)"`
SampleAnswer *string `json:"sample_answer" gorm:"type:longtext;comment:参考答案"`
Rubric *string `json:"rubric" gorm:"type:json;comment:评分标准(JSON对象)"`
IsActive bool `json:"is_active" gorm:"type:boolean;default:true;comment:是否启用"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index;comment:删除时间"`
// 关联关系
WritingSubmissions []WritingSubmission `json:"writing_submissions,omitempty" gorm:"foreignKey:PromptID"`
}
// WritingSubmission 写作提交模型
type WritingSubmission struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:提交ID"`
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index;comment:用户ID"`
PromptID string `json:"prompt_id" gorm:"type:varchar(36);not null;index;comment:题目ID"`
Content string `json:"content" gorm:"type:longtext;not null;comment:写作内容"`
WordCount int `json:"word_count" gorm:"type:int;comment:字数"`
TimeSpent int `json:"time_spent" gorm:"type:int;comment:用时(秒)"`
Score *float64 `json:"score" gorm:"type:decimal(5,2);comment:总分"`
GrammarScore *float64 `json:"grammar_score" gorm:"type:decimal(5,2);comment:语法得分"`
VocabScore *float64 `json:"vocab_score" gorm:"type:decimal(5,2);comment:词汇得分"`
CoherenceScore *float64 `json:"coherence_score" gorm:"type:decimal(5,2);comment:连贯性得分"`
Feedback *string `json:"feedback" gorm:"type:longtext;comment:AI反馈"`
Suggestions *string `json:"suggestions" gorm:"type:json;comment:改进建议(JSON数组)"`
StartedAt time.Time `json:"started_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:开始时间"`
SubmittedAt *time.Time `json:"submitted_at" gorm:"type:timestamp;comment:提交时间"`
GradedAt *time.Time `json:"graded_at" gorm:"type:timestamp;comment:批改时间"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
User User `json:"-" gorm:"foreignKey:UserID"`
Prompt WritingPrompt `json:"-" gorm:"foreignKey:PromptID"`
}
// SpeakingScenario 口语场景模型
type SpeakingScenario struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:场景ID"`
Title string `json:"title" gorm:"type:varchar(200);not null;comment:标题"`
Description string `json:"description" gorm:"type:text;not null;comment:场景描述"`
Context *string `json:"context" gorm:"type:text;comment:背景信息"`
Level string `json:"level" gorm:"type:enum('beginner','intermediate','advanced');not null;comment:难度级别"`
Category string `json:"category" gorm:"type:varchar(50);comment:分类"`
Tags *string `json:"tags" gorm:"type:json;comment:标签(JSON数组)"`
Dialogue *string `json:"dialogue" gorm:"type:json;comment:对话模板(JSON数组)"`
KeyPhrases *string `json:"key_phrases" gorm:"type:json;comment:关键短语(JSON数组)"`
IsActive bool `json:"is_active" gorm:"type:boolean;default:true;comment:是否启用"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index;comment:删除时间"`
// 关联关系
SpeakingRecords []SpeakingRecord `json:"speaking_records,omitempty" gorm:"foreignKey:ScenarioID"`
}
// SpeakingRecord 口语练习记录模型
type SpeakingRecord struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:记录ID"`
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index;comment:用户ID"`
ScenarioID string `json:"scenario_id" gorm:"type:varchar(36);not null;index;comment:场景ID"`
AudioURL *string `json:"audio_url" gorm:"type:varchar(500);comment:录音URL"`
Transcript *string `json:"transcript" gorm:"type:longtext;comment:语音识别文本"`
Duration int `json:"duration" gorm:"type:int;comment:录音时长(秒)"`
PronunciationScore *float64 `json:"pronunciation_score" gorm:"type:decimal(5,2);comment:发音得分"`
FluencyScore *float64 `json:"fluency_score" gorm:"type:decimal(5,2);comment:流利度得分"`
AccuracyScore *float64 `json:"accuracy_score" gorm:"type:decimal(5,2);comment:准确度得分"`
OverallScore *float64 `json:"overall_score" gorm:"type:decimal(5,2);comment:总分"`
Feedback *string `json:"feedback" gorm:"type:longtext;comment:AI反馈"`
Suggestions *string `json:"suggestions" gorm:"type:json;comment:改进建议(JSON数组)"`
StartedAt time.Time `json:"started_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:开始时间"`
CompletedAt *time.Time `json:"completed_at" gorm:"type:timestamp;comment:完成时间"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
User User `json:"-" gorm:"foreignKey:UserID"`
Scenario SpeakingScenario `json:"-" gorm:"foreignKey:ScenarioID"`
}
// TableName 指定表名
func (ListeningMaterial) TableName() string {
return "ai_listening_materials"
}
func (ListeningRecord) TableName() string {
return "ai_listening_records"
}
func (ReadingMaterial) TableName() string {
return "ai_reading_materials"
}
func (ReadingRecord) TableName() string {
return "ai_reading_records"
}
func (WritingPrompt) TableName() string {
return "ai_writing_prompts"
}
func (WritingSubmission) TableName() string {
return "ai_writing_submissions"
}
func (SpeakingScenario) TableName() string {
return "ai_speaking_scenarios"
}
func (SpeakingRecord) TableName() string {
return "ai_speaking_records"
}

View File

@@ -0,0 +1,44 @@
package models
import (
"time"
"gorm.io/gorm"
)
// Notification 通知模型
type Notification struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:通知ID"`
UserID int64 `json:"user_id" gorm:"type:bigint;not null;index;comment:用户ID"`
Type string `json:"type" gorm:"type:varchar(50);not null;index;comment:通知类型"`
Title string `json:"title" gorm:"type:varchar(255);not null;comment:通知标题"`
Content string `json:"content" gorm:"type:text;not null;comment:通知内容"`
Link *string `json:"link" gorm:"type:varchar(500);comment:跳转链接"`
IsRead bool `json:"is_read" gorm:"type:boolean;default:false;index;comment:是否已读"`
Priority int `json:"priority" gorm:"type:tinyint;default:0;comment:优先级:0-普通,1-重要,2-紧急"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;index;comment:创建时间"`
ReadAt *time.Time `json:"read_at" gorm:"type:timestamp;comment:阅读时间"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index;comment:删除时间"`
// 关联关系
User User `json:"-"`
}
// TableName 指定表名
func (Notification) TableName() string {
return "ai_notifications"
}
// NotificationType 通知类型常量
const (
NotificationTypeSystem = "system" // 系统通知
NotificationTypeLearning = "learning" // 学习提醒
NotificationTypeAchievement = "achievement" // 成就通知
)
// NotificationPriority 通知优先级常量
const (
NotificationPriorityNormal = 0 // 普通
NotificationPriorityImportant = 1 // 重要
NotificationPriorityUrgent = 2 // 紧急
)

View File

@@ -0,0 +1,59 @@
package models
import (
"time"
"gorm.io/gorm"
)
// StudyPlan 学习计划模型
type StudyPlan struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:计划ID"`
UserID int64 `json:"user_id" gorm:"type:bigint;not null;index;comment:用户ID"`
BookID *string `json:"book_id" gorm:"type:varchar(36);index;comment:词汇书ID(可选)"`
PlanName string `json:"plan_name" gorm:"type:varchar(200);not null;comment:计划名称"`
Description *string `json:"description" gorm:"type:text;comment:计划描述"`
DailyGoal int `json:"daily_goal" gorm:"type:int;not null;default:20;comment:每日目标单词数"`
TotalWords int `json:"total_words" gorm:"type:int;default:0;comment:计划总单词数"`
LearnedWords int `json:"learned_words" gorm:"type:int;default:0;comment:已学单词数"`
StartDate time.Time `json:"start_date" gorm:"type:date;not null;comment:开始日期"`
EndDate *time.Time `json:"end_date" gorm:"type:date;comment:结束日期"`
Status string `json:"status" gorm:"type:enum('active','paused','completed','cancelled');default:'active';comment:计划状态"`
RemindTime *string `json:"remind_time" gorm:"type:varchar(10);comment:提醒时间(HH:mm格式)"`
RemindDays *string `json:"remind_days" gorm:"type:varchar(20);comment:提醒日期(1,2,3..7表示周一到周日)"`
IsRemindEnabled bool `json:"is_remind_enabled" gorm:"type:boolean;default:false;comment:是否启用提醒"`
LastStudyDate *time.Time `json:"last_study_date" gorm:"type:date;comment:最后学习日期"`
StreakDays int `json:"streak_days" gorm:"type:int;default:0;comment:连续学习天数"`
CompletedAt *time.Time `json:"completed_at" gorm:"type:timestamp;comment:完成时间"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index;comment:删除时间"`
// 关联关系
User User `json:"-" gorm:"foreignKey:UserID"`
Book *VocabularyBook `json:"book,omitempty" gorm:"-"`
}
func (StudyPlan) TableName() string {
return "ai_study_plans"
}
// StudyPlanRecord 学习计划完成记录
type StudyPlanRecord struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:记录ID"`
PlanID int64 `json:"plan_id" gorm:"type:bigint;not null;index;comment:计划ID"`
UserID int64 `json:"user_id" gorm:"type:bigint;not null;index;comment:用户ID"`
StudyDate time.Time `json:"study_date" gorm:"type:date;not null;index;comment:学习日期"`
WordsStudied int `json:"words_studied" gorm:"type:int;default:0;comment:学习单词数"`
GoalCompleted bool `json:"goal_completed" gorm:"type:boolean;default:false;comment:是否完成目标"`
StudyDuration int `json:"study_duration" gorm:"type:int;default:0;comment:学习时长(分钟)"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
// 关联关系
Plan StudyPlan `json:"-" gorm:"foreignKey:PlanID"`
User User `json:"-" gorm:"foreignKey:UserID"`
}
func (StudyPlanRecord) TableName() string {
return "ai_study_plan_records"
}

View File

@@ -0,0 +1,190 @@
package models
import (
"time"
)
// TestType 测试类型
type TestType string
const (
TestTypeQuick TestType = "quick" // 快速测试
TestTypeComprehensive TestType = "comprehensive" // 综合测试
TestTypeDaily TestType = "daily" // 每日测试
TestTypeCustom TestType = "custom" // 自定义测试
)
// TestDifficulty 测试难度
type TestDifficulty string
const (
TestDifficultyBeginner TestDifficulty = "beginner" // 初级
TestDifficultyIntermediate TestDifficulty = "intermediate" // 中级
TestDifficultyAdvanced TestDifficulty = "advanced" // 高级
)
// TestStatus 测试状态
type TestStatus string
const (
TestStatusPending TestStatus = "pending" // 待开始
TestStatusInProgress TestStatus = "in_progress" // 进行中
TestStatusPaused TestStatus = "paused" // 已暂停
TestStatusCompleted TestStatus = "completed" // 已完成
TestStatusAbandoned TestStatus = "abandoned" // 已放弃
)
// SkillType 技能类型
type SkillType string
const (
SkillTypeVocabulary SkillType = "vocabulary" // 词汇
SkillTypeGrammar SkillType = "grammar" // 语法
SkillTypeReading SkillType = "reading" // 阅读
SkillTypeListening SkillType = "listening" // 听力
SkillTypeSpeaking SkillType = "speaking" // 口语
SkillTypeWriting SkillType = "writing" // 写作
)
// QuestionType 题目类型
type QuestionType string
const (
QuestionTypeSingleChoice QuestionType = "single_choice" // 单选题
QuestionTypeMultipleChoice QuestionType = "multiple_choice" // 多选题
QuestionTypeTrueFalse QuestionType = "true_false" // 判断题
QuestionTypeFillBlank QuestionType = "fill_blank" // 填空题
QuestionTypeShortAnswer QuestionType = "short_answer" // 简答题
)
// TestTemplate 测试模板
type TestTemplate struct {
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
Title string `json:"title" gorm:"type:varchar(255);not null"`
Description string `json:"description" gorm:"type:text"`
Type TestType `json:"type" gorm:"type:varchar(50);not null"`
Difficulty TestDifficulty `json:"difficulty" gorm:"type:varchar(50)"`
Duration int `json:"duration" gorm:"comment:测试时长(秒)"`
TotalQuestions int `json:"total_questions" gorm:"comment:总题目数"`
PassingScore int `json:"passing_score" gorm:"comment:及格分数"`
MaxScore int `json:"max_score" gorm:"comment:最高分数"`
QuestionConfig string `json:"question_config" gorm:"type:json;comment:题目配置"`
SkillDistribution string `json:"skill_distribution" gorm:"type:json;comment:技能分布"`
IsActive bool `json:"is_active" gorm:"default:true"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// TableName 指定表名
func (TestTemplate) TableName() string {
return "test_templates"
}
// TestQuestion 测试题目
type TestQuestion struct {
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
TemplateID string `json:"template_id" gorm:"type:varchar(36);index"`
QuestionType QuestionType `json:"question_type" gorm:"type:varchar(50);not null"`
SkillType SkillType `json:"skill_type" gorm:"type:varchar(50);not null"`
Difficulty TestDifficulty `json:"difficulty" gorm:"type:varchar(50)"`
Content string `json:"content" gorm:"type:text;not null;comment:题目内容"`
Options string `json:"options" gorm:"type:json;comment:选项(JSON数组)"`
CorrectAnswer string `json:"correct_answer" gorm:"type:text;comment:正确答案"`
Explanation string `json:"explanation" gorm:"type:text;comment:答案解析"`
Points int `json:"points" gorm:"default:1;comment:分值"`
OrderIndex int `json:"order_index" gorm:"comment:题目顺序"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// TableName 指定表名
func (TestQuestion) TableName() string {
return "test_questions"
}
// TestSession 测试会话
type TestSession struct {
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
TemplateID string `json:"template_id" gorm:"type:varchar(36);not null;index"`
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index"`
Status TestStatus `json:"status" gorm:"type:varchar(50);not null;default:'pending'"`
StartTime *time.Time `json:"start_time"`
EndTime *time.Time `json:"end_time"`
PausedAt *time.Time `json:"paused_at"`
TimeRemaining int `json:"time_remaining" gorm:"comment:剩余时间(秒)"`
CurrentQuestionIndex int `json:"current_question_index" gorm:"default:0"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// 关联
Template *TestTemplate `json:"template,omitempty" gorm:"foreignKey:TemplateID"`
Questions []TestQuestion `json:"questions,omitempty" gorm:"many2many:test_session_questions"`
Answers []TestAnswer `json:"answers,omitempty" gorm:"foreignKey:SessionID"`
}
// TableName 指定表名
func (TestSession) TableName() string {
return "test_sessions"
}
// TestAnswer 测试答案
type TestAnswer struct {
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
SessionID string `json:"session_id" gorm:"type:varchar(36);not null;index"`
QuestionID string `json:"question_id" gorm:"type:varchar(36);not null;index"`
Answer string `json:"answer" gorm:"type:text;comment:用户答案"`
IsCorrect *bool `json:"is_correct" gorm:"comment:是否正确"`
Score int `json:"score" gorm:"default:0;comment:得分"`
TimeSpent int `json:"time_spent" gorm:"comment:答题用时(秒)"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// 关联
Question *TestQuestion `json:"question,omitempty" gorm:"foreignKey:QuestionID"`
}
// TableName 指定表名
func (TestAnswer) TableName() string {
return "test_answers"
}
// TestResult 测试结果
type TestResult struct {
ID string `json:"id" gorm:"primaryKey;type:varchar(36)"`
SessionID string `json:"session_id" gorm:"type:varchar(36);not null;unique;index"`
UserID string `json:"user_id" gorm:"type:varchar(36);not null;index"`
TemplateID string `json:"template_id" gorm:"type:varchar(36);not null;index"`
TotalScore int `json:"total_score" gorm:"comment:总得分"`
MaxScore int `json:"max_score" gorm:"comment:最高分"`
Percentage float64 `json:"percentage" gorm:"type:decimal(5,2);comment:得分百分比"`
CorrectCount int `json:"correct_count" gorm:"comment:正确题数"`
WrongCount int `json:"wrong_count" gorm:"comment:错误题数"`
SkippedCount int `json:"skipped_count" gorm:"comment:跳过题数"`
TimeSpent int `json:"time_spent" gorm:"comment:总用时(秒)"`
SkillScores string `json:"skill_scores" gorm:"type:json;comment:各技能得分"`
Passed bool `json:"passed" gorm:"comment:是否通过"`
CompletedAt time.Time `json:"completed_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// 关联
Session *TestSession `json:"session,omitempty" gorm:"foreignKey:SessionID"`
Template *TestTemplate `json:"template,omitempty" gorm:"foreignKey:TemplateID"`
}
// TableName 指定表名
func (TestResult) TableName() string {
return "test_results"
}
// TestSessionQuestion 测试会话题目关联表
type TestSessionQuestion struct {
SessionID string `gorm:"primaryKey;type:varchar(36)"`
QuestionID string `gorm:"primaryKey;type:varchar(36)"`
OrderIndex int `gorm:"comment:题目顺序"`
}
// TableName 指定表名
func (TestSessionQuestion) TableName() string {
return "test_session_questions"
}

View File

@@ -0,0 +1,84 @@
package models
import (
"time"
"gorm.io/gorm"
)
// User 用户模型
type User struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:用户ID"`
Username string `json:"username" gorm:"type:varchar(50);uniqueIndex;not null;comment:用户名"`
Email string `json:"email" gorm:"type:varchar(100);uniqueIndex;not null;comment:邮箱"`
Phone *string `json:"phone" gorm:"type:varchar(20);uniqueIndex;comment:手机号"`
PasswordHash string `json:"-" gorm:"type:varchar(255);not null;comment:密码哈希"`
Nickname *string `json:"nickname" gorm:"type:varchar(100);comment:昵称"`
Avatar *string `json:"avatar" gorm:"type:varchar(500);comment:头像URL"`
Gender *string `json:"gender" gorm:"type:enum('male','female','other');comment:性别"`
BirthDate *time.Time `json:"birth_date" gorm:"type:date;comment:出生日期"`
Bio *string `json:"bio" gorm:"type:text;comment:个人简介"`
Location *string `json:"location" gorm:"type:varchar(100);comment:所在地"`
Timezone string `json:"timezone" gorm:"type:varchar(50);default:'Asia/Shanghai';comment:时区"`
Language string `json:"language" gorm:"type:varchar(10);default:'zh-CN';comment:界面语言"`
EmailVerified bool `json:"email_verified" gorm:"type:boolean;default:false;comment:邮箱是否验证"`
PhoneVerified bool `json:"phone_verified" gorm:"type:boolean;default:false;comment:手机是否验证"`
Status string `json:"status" gorm:"type:enum('active','inactive','suspended','deleted');default:'active';comment:账户状态"`
LastLoginAt *time.Time `json:"last_login_at" gorm:"type:timestamp;comment:最后登录时间"`
LastLoginIP *string `json:"last_login_ip" gorm:"type:varchar(45);comment:最后登录IP"`
LoginCount int `json:"login_count" gorm:"type:int;default:0;comment:登录次数"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index;comment:删除时间"`
// 关联关系
SocialLinks []UserSocialLink `json:"social_links,omitempty"`
Preferences *UserPreference `json:"preferences,omitempty"`
VocabularyProgress []UserVocabularyProgress `json:"vocabulary_progress,omitempty"`
}
// UserSocialLink 用户社交链接模型
type UserSocialLink struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:ID"`
UserID int64 `json:"user_id" gorm:"type:bigint;not null;index;comment:用户ID"`
Platform string `json:"platform" gorm:"type:varchar(50);not null;comment:平台名称"`
URL string `json:"url" gorm:"type:varchar(500);not null;comment:链接地址"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
User User `json:"-"`
}
// UserPreference 用户偏好设置模型
type UserPreference struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:ID"`
UserID int64 `json:"user_id" gorm:"type:bigint;uniqueIndex;not null;comment:用户ID"`
DailyGoal int `json:"daily_goal" gorm:"type:int;default:50;comment:每日学习目标(分钟)"`
WeeklyGoal int `json:"weekly_goal" gorm:"type:int;default:350;comment:每周学习目标(分钟)"`
ReminderEnabled bool `json:"reminder_enabled" gorm:"type:boolean;default:true;comment:是否启用提醒"`
ReminderTime *string `json:"reminder_time" gorm:"type:time;comment:提醒时间"`
DifficultyLevel string `json:"difficulty_level" gorm:"type:enum('beginner','intermediate','advanced');default:'beginner';comment:难度级别"`
LearningMode string `json:"learning_mode" gorm:"type:enum('casual','intensive','exam_prep');default:'casual';comment:学习模式"`
PreferredTopics *string `json:"preferred_topics" gorm:"type:json;comment:偏好话题(JSON数组)"`
NotificationSettings *string `json:"notification_settings" gorm:"type:json;comment:通知设置(JSON对象)"`
PrivacySettings *string `json:"privacy_settings" gorm:"type:json;comment:隐私设置(JSON对象)"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
User User `json:"-"`
}
// TableName 指定表名
func (User) TableName() string {
return "ai_users"
}
func (UserSocialLink) TableName() string {
return "ai_user_social_links"
}
func (UserPreference) TableName() string {
return "ai_user_preferences"
}

View File

@@ -0,0 +1,314 @@
package models
import (
"testing"
"time"
)
// TestUser tests the User model
func TestUser(t *testing.T) {
t.Run("Create User", func(t *testing.T) {
user := &User{
Username: "testuser",
Email: "test@example.com",
PasswordHash: "hashedpassword",
Status: "active",
Timezone: "Asia/Shanghai",
Language: "zh-CN",
}
if user.Username != "testuser" {
t.Errorf("Expected username 'testuser', got '%s'", user.Username)
}
if user.Email != "test@example.com" {
t.Errorf("Expected email 'test@example.com', got '%s'", user.Email)
}
if user.Status != "active" {
t.Errorf("Expected status 'active', got '%s'", user.Status)
}
})
t.Run("User Validation", func(t *testing.T) {
tests := []struct {
name string
user User
expected bool
}{
{
name: "Valid User",
user: User{
Username: "validuser",
Email: "valid@example.com",
PasswordHash: "validpassword",
Status: "active",
},
expected: true,
},
{
name: "Empty Username",
user: User{
Username: "",
Email: "valid@example.com",
PasswordHash: "validpassword",
},
expected: false,
},
{
name: "Empty Email",
user: User{
Username: "validuser",
Email: "",
PasswordHash: "validpassword",
},
expected: false,
},
{
name: "Empty Password",
user: User{
Username: "validuser",
Email: "valid@example.com",
PasswordHash: "",
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isValid := validateUser(tt.user)
if isValid != tt.expected {
t.Errorf("Expected %v, got %v", tt.expected, isValid)
}
})
}
})
t.Run("User Timestamps", func(t *testing.T) {
user := &User{
Username: "testuser",
Email: "test@example.com",
PasswordHash: "hashedpassword",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if user.CreatedAt.IsZero() {
t.Error("CreatedAt should not be zero")
}
if user.UpdatedAt.IsZero() {
t.Error("UpdatedAt should not be zero")
}
})
}
// validateUser is a helper function for testing user validation
func validateUser(user User) bool {
if user.Username == "" {
return false
}
if user.Email == "" {
return false
}
if user.PasswordHash == "" {
return false
}
return true
}
// TestUserPreference tests the UserPreference model
func TestUserPreference(t *testing.T) {
t.Run("Create UserPreference", func(t *testing.T) {
preference := &UserPreference{
UserID: 1,
DailyGoal: 50,
WeeklyGoal: 350,
ReminderEnabled: true,
DifficultyLevel: "beginner",
LearningMode: "casual",
}
if preference.UserID != 1 {
t.Errorf("Expected UserID 1, got %d", preference.UserID)
}
if preference.DailyGoal != 50 {
t.Errorf("Expected DailyGoal 50, got %d", preference.DailyGoal)
}
if preference.DifficultyLevel != "beginner" {
t.Errorf("Expected DifficultyLevel 'beginner', got '%s'", preference.DifficultyLevel)
}
})
t.Run("Preference Validation", func(t *testing.T) {
tests := []struct {
name string
preference UserPreference
expected bool
}{
{
name: "Valid Preference",
preference: UserPreference{
UserID: 1,
DailyGoal: 50,
WeeklyGoal: 350,
DifficultyLevel: "beginner",
LearningMode: "casual",
},
expected: true,
},
{
name: "Invalid UserID",
preference: UserPreference{
UserID: 0,
DailyGoal: 50,
DifficultyLevel: "beginner",
},
expected: false,
},
{
name: "Negative Daily Goal",
preference: UserPreference{
UserID: 1,
DailyGoal: -10,
DifficultyLevel: "beginner",
},
expected: false,
},
{
name: "Invalid Difficulty Level",
preference: UserPreference{
UserID: 1,
DailyGoal: 50,
DifficultyLevel: "invalid",
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isValid := validateUserPreference(tt.preference)
if isValid != tt.expected {
t.Errorf("Expected %v, got %v", tt.expected, isValid)
}
})
}
})
}
// validateUserPreference is a helper function for testing user preference validation
func validateUserPreference(preference UserPreference) bool {
if preference.UserID <= 0 {
return false
}
if preference.DailyGoal < 0 || preference.WeeklyGoal < 0 {
return false
}
validDifficultyLevels := []string{"beginner", "intermediate", "advanced"}
validDifficulty := false
for _, level := range validDifficultyLevels {
if preference.DifficultyLevel == level {
validDifficulty = true
break
}
}
if !validDifficulty {
return false
}
return true
}
// TestUserSocialLink tests the UserSocialLink model
func TestUserSocialLink(t *testing.T) {
t.Run("Create UserSocialLink", func(t *testing.T) {
socialLink := &UserSocialLink{
UserID: 1,
Platform: "github",
URL: "https://github.com/testuser",
}
if socialLink.UserID != 1 {
t.Errorf("Expected UserID 1, got %d", socialLink.UserID)
}
if socialLink.Platform != "github" {
t.Errorf("Expected Platform 'github', got '%s'", socialLink.Platform)
}
if socialLink.URL != "https://github.com/testuser" {
t.Errorf("Expected URL 'https://github.com/testuser', got '%s'", socialLink.URL)
}
})
t.Run("Social Link Validation", func(t *testing.T) {
tests := []struct {
name string
socialLink UserSocialLink
expected bool
}{
{
name: "Valid Social Link",
socialLink: UserSocialLink{
UserID: 1,
Platform: "github",
URL: "https://github.com/testuser",
},
expected: true,
},
{
name: "Invalid UserID",
socialLink: UserSocialLink{
UserID: 0,
Platform: "github",
URL: "https://github.com/testuser",
},
expected: false,
},
{
name: "Empty Platform",
socialLink: UserSocialLink{
UserID: 1,
Platform: "",
URL: "https://github.com/testuser",
},
expected: false,
},
{
name: "Empty URL",
socialLink: UserSocialLink{
UserID: 1,
Platform: "github",
URL: "",
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isValid := validateUserSocialLink(tt.socialLink)
if isValid != tt.expected {
t.Errorf("Expected %v, got %v", tt.expected, isValid)
}
})
}
})
}
// validateUserSocialLink is a helper function for testing user social link validation
func validateUserSocialLink(socialLink UserSocialLink) bool {
if socialLink.UserID <= 0 {
return false
}
if socialLink.Platform == "" {
return false
}
if socialLink.URL == "" {
return false
}
return true
}

View File

@@ -0,0 +1,282 @@
package models
import (
"time"
"gorm.io/gorm"
)
// VocabularyCategory 词汇分类模型
type VocabularyCategory struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:分类ID"`
Name string `json:"name" gorm:"type:varchar(100);not null;comment:分类名称"`
Description *string `json:"description" gorm:"type:text;comment:分类描述"`
Level string `json:"level" gorm:"type:enum('beginner','intermediate','advanced');not null;comment:难度级别"`
Icon *string `json:"icon" gorm:"type:varchar(255);comment:图标URL"`
Color *string `json:"color" gorm:"type:varchar(7);comment:主题色"`
SortOrder int `json:"sort_order" gorm:"type:int;default:0;comment:排序"`
IsActive bool `json:"is_active" gorm:"type:boolean;default:true;comment:是否启用"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
Vocabularies []Vocabulary `json:"vocabularies,omitempty" gorm:"many2many:ai_vocabulary_category_relations;foreignKey:ID;joinForeignKey:CategoryID;References:ID;joinReferences:VocabularyID;"`
}
// Vocabulary 词汇模型
type Vocabulary struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement;comment:词汇ID"`
Word string `json:"word" gorm:"column:word;type:varchar(100);uniqueIndex;not null;comment:单词"`
Phonetic *string `json:"phonetic" gorm:"column:phonetic;type:varchar(200);comment:音标"`
AudioURL *string `json:"audio_url" gorm:"column:audio_url;type:varchar(500);comment:音频URL"`
Level string `json:"level" gorm:"column:level;type:enum('beginner','intermediate','advanced');not null;comment:难度级别"`
Frequency int `json:"frequency" gorm:"column:frequency;type:int;default:0;comment:使用频率"`
IsActive bool `json:"is_active" gorm:"column:is_active;type:boolean;default:true;comment:是否启用"`
CreatedAt time.Time `json:"created_at" gorm:"column:created_at;type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"column:deleted_at;index;comment:删除时间"`
// 关联关系
Definitions []VocabularyDefinition `json:"definitions,omitempty" gorm:"foreignKey:VocabularyID"`
Examples []VocabularyExample `json:"examples,omitempty" gorm:"foreignKey:VocabularyID"`
Images []VocabularyImage `json:"images,omitempty" gorm:"foreignKey:VocabularyID"`
Categories []VocabularyCategory `json:"categories,omitempty" gorm:"many2many:ai_vocabulary_category_relations;foreignKey:ID;joinForeignKey:VocabularyID;References:ID;joinReferences:CategoryID;"`
UserProgress []UserVocabularyProgress `json:"user_progress,omitempty" gorm:"foreignKey:VocabularyID"`
}
// VocabularyDefinition 词汇定义模型
type VocabularyDefinition struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement;comment:定义ID"`
VocabularyID int64 `json:"vocabulary_id" gorm:"column:vocabulary_id;not null;index;comment:词汇ID"`
PartOfSpeech string `json:"part_of_speech" gorm:"column:part_of_speech;type:varchar(20);not null;comment:词性"`
Definition string `json:"definition" gorm:"column:definition_en;type:text;not null;comment:英文定义"`
Translation string `json:"translation" gorm:"column:definition_cn;type:text;not null;comment:中文翻译"`
SortOrder int `json:"sort_order" gorm:"column:sort_order;type:int;default:0;comment:排序"`
CreatedAt time.Time `json:"created_at" gorm:"column:created_at;type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
// 关联关系
Vocabulary Vocabulary `json:"-" gorm:"foreignKey:VocabularyID"`
}
// VocabularyExample 词汇例句模型
type VocabularyExample struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement;comment:例句ID"`
VocabularyID int64 `json:"vocabulary_id" gorm:"column:vocabulary_id;not null;index;comment:词汇ID"`
Example string `json:"example" gorm:"column:sentence_en;type:text;not null;comment:英文例句"`
Translation string `json:"translation" gorm:"column:sentence_cn;type:text;not null;comment:中文翻译"`
AudioURL *string `json:"audio_url" gorm:"column:audio_url;type:varchar(500);comment:音频URL"`
SortOrder int `json:"sort_order" gorm:"column:sort_order;type:int;default:0;comment:排序"`
CreatedAt time.Time `json:"created_at" gorm:"column:created_at;type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
// 关联关系
Vocabulary Vocabulary `json:"-" gorm:"foreignKey:VocabularyID"`
}
// VocabularyImage 词汇图片模型
type VocabularyImage struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:图片ID"`
VocabularyID string `json:"vocabulary_id" gorm:"type:varchar(36);not null;index;comment:词汇ID"`
ImageURL string `json:"image_url" gorm:"type:varchar(500);not null;comment:图片URL"`
AltText *string `json:"alt_text" gorm:"type:varchar(255);comment:替代文本"`
Caption *string `json:"caption" gorm:"type:text;comment:图片说明"`
SortOrder int `json:"sort_order" gorm:"type:int;default:0;comment:排序"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
Vocabulary Vocabulary `json:"-" gorm:"foreignKey:VocabularyID"`
}
// VocabularyCategoryRelation 词汇分类关系模型
type VocabularyCategoryRelation struct {
VocabularyID string `json:"vocabulary_id" gorm:"type:varchar(36);primaryKey;comment:词汇ID"`
CategoryID string `json:"category_id" gorm:"type:varchar(36);primaryKey;comment:分类ID"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
}
// UserVocabularyProgress 用户词汇学习进度模型
type UserVocabularyProgress struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:进度ID"`
UserID int64 `json:"user_id" gorm:"type:bigint;not null;index;comment:用户ID"`
VocabularyID string `json:"vocabulary_id" gorm:"type:varchar(36);not null;index;comment:词汇ID"`
MasteryLevel int `json:"mastery_level" gorm:"type:int;default:0;comment:掌握程度(0-100)"`
StudyCount int `json:"study_count" gorm:"type:int;default:0;comment:学习次数"`
CorrectCount int `json:"correct_count" gorm:"type:int;default:0;comment:正确次数"`
IncorrectCount int `json:"incorrect_count" gorm:"type:int;default:0;comment:错误次数"`
LastStudiedAt *time.Time `json:"last_studied_at" gorm:"type:timestamp;comment:最后学习时间"`
NextReviewAt *time.Time `json:"next_review_at" gorm:"type:timestamp;comment:下次复习时间"`
IsMarkedDifficult bool `json:"is_marked_difficult" gorm:"type:boolean;default:false;comment:是否标记为困难"`
IsFavorite bool `json:"is_favorite" gorm:"type:boolean;default:false;comment:是否收藏"`
Notes *string `json:"notes" gorm:"type:text;comment:学习笔记"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
User User `json:"-" gorm:"foreignKey:UserID"`
Vocabulary Vocabulary `json:"-" gorm:"foreignKey:VocabularyID"`
}
// UserWordProgress 用户单词学习进度模型
type UserWordProgress struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:进度ID"`
UserID int64 `json:"user_id" gorm:"type:bigint;not null;index:idx_user_vocab;comment:用户ID"`
VocabularyID int64 `json:"vocabulary_id" gorm:"type:bigint;not null;index:idx_user_vocab;comment:单词ID"`
Status string `json:"status" gorm:"type:varchar(20);default:'not_started';comment:学习状态"`
StudyCount int `json:"study_count" gorm:"type:int;default:0;comment:学习次数"`
CorrectCount int `json:"correct_count" gorm:"type:int;default:0;comment:正确次数"`
WrongCount int `json:"wrong_count" gorm:"type:int;default:0;comment:错误次数"`
Proficiency int `json:"proficiency" gorm:"type:int;default:0;comment:熟练度(0-100)"`
IsFavorite bool `json:"is_favorite" gorm:"type:boolean;default:false;comment:是否收藏"`
NextReviewAt *time.Time `json:"next_review_at" gorm:"type:timestamp;comment:下次复习时间"`
ReviewInterval int `json:"review_interval" gorm:"type:int;default:1;comment:复习间隔(天)"`
FirstStudiedAt time.Time `json:"first_studied_at" gorm:"type:timestamp;comment:首次学习时间"`
LastStudiedAt time.Time `json:"last_studied_at" gorm:"type:timestamp;comment:最后学习时间"`
MasteredAt *time.Time `json:"mastered_at" gorm:"type:timestamp;comment:掌握时间"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
User User `json:"-" gorm:"foreignKey:UserID"`
Vocabulary Vocabulary `json:"-" gorm:"foreignKey:VocabularyID"`
}
// VocabularyTest 词汇测试模型
type VocabularyTest struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:测试ID"`
UserID int64 `json:"user_id" gorm:"type:bigint;not null;index;comment:用户ID"`
TestType string `json:"test_type" gorm:"type:enum('placement','progress','review');not null;comment:测试类型"`
Level string `json:"level" gorm:"type:enum('beginner','intermediate','advanced');comment:测试级别"`
TotalWords int `json:"total_words" gorm:"type:int;not null;comment:总词汇数"`
CorrectWords int `json:"correct_words" gorm:"type:int;default:0;comment:正确词汇数"`
Score float64 `json:"score" gorm:"type:decimal(5,2);comment:得分"`
Duration int `json:"duration" gorm:"type:int;comment:测试时长(秒)"`
StartedAt time.Time `json:"started_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:开始时间"`
CompletedAt *time.Time `json:"completed_at" gorm:"type:timestamp;comment:完成时间"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系
User User `json:"-" gorm:"foreignKey:UserID"`
}
// TableName 指定表名
func (VocabularyCategory) TableName() string {
return "ai_vocabulary_categories"
}
func (Vocabulary) TableName() string {
return "ai_vocabulary"
}
func (VocabularyDefinition) TableName() string {
return "ai_vocabulary_definitions"
}
func (VocabularyExample) TableName() string {
return "ai_vocabulary_examples"
}
func (VocabularyImage) TableName() string {
return "ai_vocabulary_images"
}
func (VocabularyCategoryRelation) TableName() string {
return "ai_vocabulary_category_relations"
}
func (UserVocabularyProgress) TableName() string {
return "ai_user_vocabulary_progress"
}
func (VocabularyTest) TableName() string {
return "ai_vocabulary_tests"
}
// VocabularyBook 词汇书模型
type VocabularyBook struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey;comment:词汇书ID"`
Name string `json:"name" gorm:"type:varchar(200);not null;comment:词汇书名称"`
Description *string `json:"description" gorm:"type:text;comment:词汇书描述"`
Category string `json:"category" gorm:"type:varchar(100);not null;comment:分类"`
Level string `json:"level" gorm:"type:enum('beginner','elementary','intermediate','advanced','expert');not null;comment:难度级别"`
TotalWords int `json:"total_words" gorm:"type:int;default:0;comment:总单词数"`
CoverImage *string `json:"cover_image" gorm:"type:varchar(500);comment:封面图片URL"`
Icon *string `json:"icon" gorm:"type:varchar(255);comment:图标"`
Color *string `json:"color" gorm:"type:varchar(7);comment:主题色"`
IsSystem bool `json:"is_system" gorm:"type:boolean;default:true;comment:是否系统词汇书"`
IsActive bool `json:"is_active" gorm:"type:boolean;default:true;comment:是否启用"`
SortOrder int `json:"sort_order" gorm:"type:int;default:0;comment:排序"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
// 关联关系(不使用外键约束)
Words []VocabularyBookWord `json:"words,omitempty" gorm:"-"`
}
// VocabularyBookWord 词汇书单词关联模型
type VocabularyBookWord struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:关联ID"`
BookID string `json:"book_id" gorm:"type:varchar(36);not null;index;comment:词汇书ID"`
VocabularyID string `json:"vocabulary_id" gorm:"type:varchar(36);not null;index;comment:词汇ID"`
SortOrder int `json:"sort_order" gorm:"type:int;default:0;comment:排序"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
// 关联关系(不使用外键约束)
Book VocabularyBook `json:"-" gorm:"-"`
Vocabulary *Vocabulary `json:"word,omitempty" gorm:"-"`
}
func (VocabularyBook) TableName() string {
return "ai_vocabulary_books"
}
func (VocabularyBookWord) TableName() string {
return "ai_vocabulary_book_words"
}
func (UserWordProgress) TableName() string {
return "ai_user_word_progress"
}
// LearningSession 学习会话模型
type LearningSession struct {
ID int64 `json:"id" gorm:"type:bigint;primaryKey;autoIncrement;comment:会话ID"`
UserID int64 `json:"user_id" gorm:"type:bigint;not null;index;comment:用户ID"`
BookID string `json:"book_id" gorm:"type:varchar(36);not null;index;comment:词汇书ID"`
DailyGoal int `json:"daily_goal" gorm:"type:int;default:20;comment:每日学习目标"`
NewWordsCount int `json:"new_words_count" gorm:"type:int;default:0;comment:新学单词数"`
ReviewCount int `json:"review_count" gorm:"type:int;default:0;comment:复习单词数"`
MasteredCount int `json:"mastered_count" gorm:"type:int;default:0;comment:掌握单词数"`
StartedAt time.Time `json:"started_at" gorm:"type:timestamp;comment:开始时间"`
CompletedAt *time.Time `json:"completed_at" gorm:"type:timestamp;comment:完成时间"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP;comment:创建时间"`
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
}
func (LearningSession) TableName() string {
return "ai_learning_sessions"
}
// UserVocabularyBookProgress 用户词汇书学习进度
type UserVocabularyBookProgress struct {
ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`
UserID int64 `gorm:"not null;index:idx_user_book" json:"user_id"`
BookID string `gorm:"type:varchar(36);not null;index:idx_user_book" json:"book_id"`
LearnedWords int `gorm:"default:0" json:"learned_words"`
MasteredWords int `gorm:"default:0" json:"mastered_words"`
ProgressPercentage float64 `gorm:"type:decimal(5,2);default:0.00" json:"progress_percentage"`
StreakDays int `gorm:"default:0" json:"streak_days"`
TotalStudyDays int `gorm:"default:0" json:"total_study_days"`
AverageDailyWords float64 `gorm:"type:decimal(5,2);default:0.00" json:"average_daily_words"`
EstimatedCompletionDate *time.Time `json:"estimated_completion_date"`
IsCompleted bool `gorm:"default:false" json:"is_completed"`
CompletedAt *time.Time `json:"completed_at"`
StartedAt time.Time `gorm:"not null" json:"started_at"`
LastStudiedAt time.Time `json:"last_studied_at"`
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
}
func (UserVocabularyBookProgress) TableName() string {
return "user_vocabulary_book_progress"
}

View File

@@ -0,0 +1,482 @@
package models
import (
"testing"
"time"
)
// TestVocabulary tests the Vocabulary model
func TestVocabulary(t *testing.T) {
t.Run("Create Vocabulary", func(t *testing.T) {
vocab := &Vocabulary{
ID: "vocab-123",
Word: "hello",
Level: "beginner",
Frequency: 100,
IsActive: true,
}
if vocab.Word != "hello" {
t.Errorf("Expected word 'hello', got '%s'", vocab.Word)
}
if vocab.Level != "beginner" {
t.Errorf("Expected level 'beginner', got '%s'", vocab.Level)
}
if vocab.Frequency != 100 {
t.Errorf("Expected frequency 100, got %d", vocab.Frequency)
}
})
t.Run("Vocabulary Validation", func(t *testing.T) {
tests := []struct {
name string
vocab Vocabulary
expected bool
}{
{
name: "Valid Vocabulary",
vocab: Vocabulary{
ID: "vocab-123",
Word: "test",
Level: "beginner",
Frequency: 50,
},
expected: true,
},
{
name: "Empty Word",
vocab: Vocabulary{
ID: "vocab-123",
Word: "",
Level: "beginner",
},
expected: false,
},
{
name: "Invalid Level",
vocab: Vocabulary{
ID: "vocab-123",
Word: "test",
Level: "invalid",
},
expected: false,
},
{
name: "Negative Frequency",
vocab: Vocabulary{
ID: "vocab-123",
Word: "test",
Level: "beginner",
Frequency: -1,
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isValid := validateVocabulary(tt.vocab)
if isValid != tt.expected {
t.Errorf("Expected %v, got %v", tt.expected, isValid)
}
})
}
})
}
// validateVocabulary is a helper function for testing vocabulary validation
func validateVocabulary(vocab Vocabulary) bool {
if vocab.Word == "" {
return false
}
validLevels := []string{"beginner", "intermediate", "advanced"}
validLevel := false
for _, level := range validLevels {
if vocab.Level == level {
validLevel = true
break
}
}
if !validLevel {
return false
}
if vocab.Frequency < 0 {
return false
}
return true
}
// TestVocabularyCategory tests the VocabularyCategory model
func TestVocabularyCategory(t *testing.T) {
t.Run("Create VocabularyCategory", func(t *testing.T) {
category := &VocabularyCategory{
ID: "cat-123",
Name: "Animals",
Level: "beginner",
SortOrder: 1,
IsActive: true,
}
if category.Name != "Animals" {
t.Errorf("Expected name 'Animals', got '%s'", category.Name)
}
if category.Level != "beginner" {
t.Errorf("Expected level 'beginner', got '%s'", category.Level)
}
if category.SortOrder != 1 {
t.Errorf("Expected sort order 1, got %d", category.SortOrder)
}
})
t.Run("Category Validation", func(t *testing.T) {
tests := []struct {
name string
category VocabularyCategory
expected bool
}{
{
name: "Valid Category",
category: VocabularyCategory{
ID: "cat-123",
Name: "Animals",
Level: "beginner",
SortOrder: 1,
},
expected: true,
},
{
name: "Empty Name",
category: VocabularyCategory{
ID: "cat-123",
Name: "",
Level: "beginner",
},
expected: false,
},
{
name: "Invalid Level",
category: VocabularyCategory{
ID: "cat-123",
Name: "Animals",
Level: "invalid",
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isValid := validateVocabularyCategory(tt.category)
if isValid != tt.expected {
t.Errorf("Expected %v, got %v", tt.expected, isValid)
}
})
}
})
}
// validateVocabularyCategory is a helper function for testing vocabulary category validation
func validateVocabularyCategory(category VocabularyCategory) bool {
if category.Name == "" {
return false
}
validLevels := []string{"beginner", "intermediate", "advanced"}
validLevel := false
for _, level := range validLevels {
if category.Level == level {
validLevel = true
break
}
}
return validLevel
}
// TestVocabularyDefinition tests the VocabularyDefinition model
func TestVocabularyDefinition(t *testing.T) {
t.Run("Create VocabularyDefinition", func(t *testing.T) {
definition := &VocabularyDefinition{
ID: "def-123",
VocabularyID: "vocab-123",
PartOfSpeech: "noun",
Definition: "A greeting or expression of goodwill",
SortOrder: 1,
}
if definition.PartOfSpeech != "noun" {
t.Errorf("Expected part of speech 'noun', got '%s'", definition.PartOfSpeech)
}
if definition.Definition != "A greeting or expression of goodwill" {
t.Errorf("Expected definition 'A greeting or expression of goodwill', got '%s'", definition.Definition)
}
})
t.Run("Definition Validation", func(t *testing.T) {
tests := []struct {
name string
definition VocabularyDefinition
expected bool
}{
{
name: "Valid Definition",
definition: VocabularyDefinition{
ID: "def-123",
VocabularyID: "vocab-123",
PartOfSpeech: "noun",
Definition: "A greeting",
},
expected: true,
},
{
name: "Empty VocabularyID",
definition: VocabularyDefinition{
ID: "def-123",
VocabularyID: "",
PartOfSpeech: "noun",
Definition: "A greeting",
},
expected: false,
},
{
name: "Empty Definition",
definition: VocabularyDefinition{
ID: "def-123",
VocabularyID: "vocab-123",
PartOfSpeech: "noun",
Definition: "",
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isValid := validateVocabularyDefinition(tt.definition)
if isValid != tt.expected {
t.Errorf("Expected %v, got %v", tt.expected, isValid)
}
})
}
})
}
// validateVocabularyDefinition is a helper function for testing vocabulary definition validation
func validateVocabularyDefinition(definition VocabularyDefinition) bool {
if definition.VocabularyID == "" {
return false
}
if definition.Definition == "" {
return false
}
return true
}
// TestUserVocabularyProgress tests the UserVocabularyProgress model
func TestUserVocabularyProgress(t *testing.T) {
t.Run("Create UserVocabularyProgress", func(t *testing.T) {
now := time.Now()
progress := &UserVocabularyProgress{
UserID: 1,
VocabularyID: "vocab-123",
MasteryLevel: 75,
StudyCount: 10,
CorrectCount: 8,
IncorrectCount: 2,
LastStudiedAt: &now,
IsMarkedDifficult: false,
IsFavorite: true,
}
if progress.UserID != 1 {
t.Errorf("Expected UserID 1, got %d", progress.UserID)
}
if progress.MasteryLevel != 75 {
t.Errorf("Expected MasteryLevel 75, got %d", progress.MasteryLevel)
}
if progress.StudyCount != 10 {
t.Errorf("Expected StudyCount 10, got %d", progress.StudyCount)
}
})
t.Run("Progress Validation", func(t *testing.T) {
tests := []struct {
name string
progress UserVocabularyProgress
expected bool
}{
{
name: "Valid Progress",
progress: UserVocabularyProgress{
UserID: 1,
VocabularyID: "vocab-123",
MasteryLevel: 75,
StudyCount: 10,
},
expected: true,
},
{
name: "Invalid UserID",
progress: UserVocabularyProgress{
UserID: 0,
VocabularyID: "vocab-123",
MasteryLevel: 75,
},
expected: false,
},
{
name: "Invalid MasteryLevel",
progress: UserVocabularyProgress{
UserID: 1,
VocabularyID: "vocab-123",
MasteryLevel: 150, // Over 100
},
expected: false,
},
{
name: "Empty VocabularyID",
progress: UserVocabularyProgress{
UserID: 1,
VocabularyID: "",
MasteryLevel: 75,
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isValid := validateUserVocabularyProgress(tt.progress)
if isValid != tt.expected {
t.Errorf("Expected %v, got %v", tt.expected, isValid)
}
})
}
})
}
// validateUserVocabularyProgress is a helper function for testing user vocabulary progress validation
func validateUserVocabularyProgress(progress UserVocabularyProgress) bool {
if progress.UserID <= 0 {
return false
}
if progress.VocabularyID == "" {
return false
}
if progress.MasteryLevel < 0 || progress.MasteryLevel > 100 {
return false
}
return true
}
// TestVocabularyTest tests the VocabularyTest model
func TestVocabularyTest(t *testing.T) {
t.Run("Create VocabularyTest", func(t *testing.T) {
test := &VocabularyTest{
UserID: 1,
TestType: "placement",
Level: "beginner",
TotalWords: 20,
CorrectWords: 15,
Score: 75.0,
Duration: 300, // 5 minutes
StartedAt: time.Now(),
}
if test.UserID != 1 {
t.Errorf("Expected UserID 1, got %d", test.UserID)
}
if test.TestType != "placement" {
t.Errorf("Expected TestType 'placement', got '%s'", test.TestType)
}
if test.Score != 75.0 {
t.Errorf("Expected Score 75.0, got %f", test.Score)
}
})
t.Run("Test Validation", func(t *testing.T) {
tests := []struct {
name string
test VocabularyTest
expected bool
}{
{
name: "Valid Test",
test: VocabularyTest{
UserID: 1,
TestType: "placement",
Level: "beginner",
TotalWords: 20,
CorrectWords: 15,
},
expected: true,
},
{
name: "Invalid UserID",
test: VocabularyTest{
UserID: 0,
TestType: "placement",
TotalWords: 20,
},
expected: false,
},
{
name: "Invalid TestType",
test: VocabularyTest{
UserID: 1,
TestType: "invalid",
TotalWords: 20,
},
expected: false,
},
{
name: "Correct Words Greater Than Total",
test: VocabularyTest{
UserID: 1,
TestType: "placement",
TotalWords: 20,
CorrectWords: 25,
},
expected: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isValid := validateVocabularyTest(tt.test)
if isValid != tt.expected {
t.Errorf("Expected %v, got %v", tt.expected, isValid)
}
})
}
})
}
// validateVocabularyTest is a helper function for testing vocabulary test validation
func validateVocabularyTest(test VocabularyTest) bool {
if test.UserID <= 0 {
return false
}
validTestTypes := []string{"placement", "progress", "review"}
validTestType := false
for _, testType := range validTestTypes {
if test.TestType == testType {
validTestType = true
break
}
}
if !validTestType {
return false
}
if test.CorrectWords > test.TotalWords {
return false
}
return true
}