package learning import ( "context" "errors" "time" "github.com/flipped-aurora/gin-vue-admin/server/global" "github.com/flipped-aurora/gin-vue-admin/server/model/learning" learningReq "github.com/flipped-aurora/gin-vue-admin/server/model/learning/request" ) type UserExamService struct{} // CreateUserExam 创建用户考试记录 func (userExamService *UserExamService) CreateUserExam(ctx context.Context, userExam *learning.UserExam) (err error) { // 设置默认值 if userExam.Status == "" { userExam.Status = "in_progress" } if userExam.AttemptNumber == 0 { userExam.AttemptNumber = 1 } if userExam.StartTime.IsZero() { userExam.StartTime = time.Now() } err = global.GVA_DB.WithContext(ctx).Create(userExam).Error return err } // DeleteUserExam 删除用户考试记录 func (userExamService *UserExamService) DeleteUserExam(ctx context.Context, ID string) (err error) { err = global.GVA_DB.WithContext(ctx).Delete(&learning.UserExam{}, ID).Error return err } // DeleteUserExamByIds 批量删除用户考试记录 func (userExamService *UserExamService) DeleteUserExamByIds(ctx context.Context, IDs []string) (err error) { err = global.GVA_DB.WithContext(ctx).Delete(&[]learning.UserExam{}, "id in ?", IDs).Error return err } // UpdateUserExam 更新用户考试记录 func (userExamService *UserExamService) UpdateUserExam(ctx context.Context, userExam learning.UserExam) (err error) { err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}).Where("id = ?", userExam.ID).Updates(&userExam).Error return err } // GetUserExam 根据ID获取用户考试记录 func (userExamService *UserExamService) GetUserExam(ctx context.Context, ID string) (userExam learning.UserExam, err error) { err = global.GVA_DB.WithContext(ctx).Where("id = ?", ID).First(&userExam).Error if err != nil { return } // 手动查询关联的考试信息 userExamService.loadExamInfo(ctx, &userExam) return } // GetUserExamInfoList 分页获取用户考试记录列表 func (userExamService *UserExamService) GetUserExamInfoList(ctx context.Context, info learningReq.UserExamSearch) (list []learning.UserExam, total int64, err error) { limit := info.PageSize offset := info.PageSize * (info.Page - 1) // 创建db db := global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}) var userExams []learning.UserExam // 如果有条件搜索 下方会自动创建搜索语句 if info.StartCreatedAt != nil && info.EndCreatedAt != nil { db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt) } if info.UserId != 0 { db = db.Where("user_id = ?", info.UserId) } if info.ExamId != 0 { db = db.Where("exam_id = ?", info.ExamId) } if info.Status != "" { db = db.Where("status = ?", info.Status) } if info.IsPassed { db = db.Where("is_passed = ?", info.IsPassed) } err = db.Count(&total).Error if err != nil { return } if limit != 0 { db = db.Limit(limit).Offset(offset) } err = db.Order("created_at desc").Find(&userExams).Error if err != nil { return userExams, total, err } // 批量加载关联的考试信息 for i := range userExams { userExamService.loadExamInfo(ctx, &userExams[i]) } return userExams, total, err } // StartExam 开始考试 func (userExamService *UserExamService) StartExam(ctx context.Context, userId uint, examId uint) (userExam learning.UserExam, err error) { // 检查考试是否存在且可以参加 var exam learning.Exam err = global.GVA_DB.WithContext(ctx).Where("id = ? AND status = ?", examId, "published").First(&exam).Error if err != nil { return userExam, errors.New("考试不存在或未发布") } // 检查考试时间 now := time.Now() if now.Before(exam.StartTime) { return userExam, errors.New("考试尚未开始") } if now.After(exam.EndTime) { return userExam, errors.New("考试已结束") } // 检查用户是否已经参加过考试 var existingCount int64 global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}). Where("user_id = ? AND exam_id = ?", userId, examId). Count(&existingCount) // 检查是否允许重考和尝试次数 if existingCount > 0 && !exam.AllowRetake { return userExam, errors.New("该考试不允许重考") } if existingCount >= int64(exam.MaxAttempts) { return userExam, errors.New("已达到最大尝试次数") } // 创建考试记录 userExam = learning.UserExam{ UserId: userId, ExamId: examId, AttemptNumber: int(existingCount) + 1, StartTime: now, Status: "in_progress", } err = userExamService.CreateUserExam(ctx, &userExam) if err != nil { return userExam, err } // 加载考试信息 userExamService.loadExamInfo(ctx, &userExam) return userExam, nil } // SubmitExam 提交考试 func (userExamService *UserExamService) SubmitExam(ctx context.Context, userExamId uint, totalScore int, correctCount int, wrongCount int) (err error) { var userExam learning.UserExam err = global.GVA_DB.WithContext(ctx).Where("id = ?", userExamId).First(&userExam).Error if err != nil { return errors.New("考试记录不存在") } if userExam.Status != "in_progress" { return errors.New("考试已结束,无法重复提交") } // 获取考试信息 var exam learning.Exam err = global.GVA_DB.WithContext(ctx).Where("id = ?", userExam.ExamId).First(&exam).Error if err != nil { return err } // 计算考试时长 now := time.Now() duration := int(now.Sub(userExam.StartTime).Minutes()) // 判断是否通过 isPassed := totalScore >= exam.PassScore // 更新考试记录 updates := map[string]interface{}{ "end_time": now, "submitted_at": now, "duration": duration, "total_score": totalScore, "correct_count": correctCount, "wrong_count": wrongCount, "status": "completed", "is_passed": isPassed, } err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}). Where("id = ?", userExamId). Updates(updates).Error return err } // GetUserExamHistory 获取用户考试历史 func (userExamService *UserExamService) GetUserExamHistory(ctx context.Context, userId uint) (list []learning.UserExam, err error) { err = global.GVA_DB.WithContext(ctx). Where("user_id = ?", userId). Order("created_at desc"). Find(&list).Error // 批量加载关联的考试信息 for i := range list { userExamService.loadExamInfo(ctx, &list[i]) } return } // GetUserExamStatistics 获取用户考试统计 func (userExamService *UserExamService) GetUserExamStatistics(ctx context.Context, userId uint) (stats learningReq.UserExamStatistics, err error) { // 总考试次数 var totalExams int64 err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}). Where("user_id = ? AND status = ?", userId, "completed"). Count(&totalExams).Error if err != nil { return } stats.TotalExams = int(totalExams) if totalExams == 0 { return stats, nil } // 通过考试次数 var passedExams int64 err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}). Where("user_id = ? AND status = ? AND is_passed = ?", userId, "completed", true). Count(&passedExams).Error if err != nil { return } stats.PassedExams = int(passedExams) stats.FailedExams = stats.TotalExams - stats.PassedExams // 平均分数和最高分数 var result struct { AverageScore float64 HighestScore int TotalTime int } err = global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}). Select("AVG(total_score) as average_score, MAX(total_score) as highest_score, SUM(duration) as total_time"). Where("user_id = ? AND status = ?", userId, "completed"). Scan(&result).Error if err != nil { return } stats.AverageScore = result.AverageScore stats.HighestScore = result.HighestScore stats.TotalStudyTime = result.TotalTime // 通过率 if stats.TotalExams > 0 { stats.PassRate = float64(stats.PassedExams) / float64(stats.TotalExams) * 100 } return } // GetExamRanking 获取考试排行榜 func (userExamService *UserExamService) GetExamRanking(ctx context.Context, examId uint, limit int) (ranking learningReq.ExamRankingResponse, err error) { // 获取考试信息 var exam learning.Exam err = global.GVA_DB.WithContext(ctx).Where("id = ?", examId).First(&exam).Error if err != nil { return } ranking.ExamId = examId ranking.ExamTitle = exam.Title // 获取排行榜数据 var userExams []learning.UserExam db := global.GVA_DB.WithContext(ctx). Where("exam_id = ? AND status = ?", examId, "completed"). Order("total_score desc, duration asc") if limit > 0 { db = db.Limit(limit) } err = db.Find(&userExams).Error if err != nil { return } // 构建排行榜 for i, userExam := range userExams { item := learningReq.ExamRankingItem{ UserId: userExam.UserId, Score: userExam.TotalScore, Duration: userExam.Duration, Rank: i + 1, IsPassed: userExam.IsPassed, SubmittedAt: userExam.SubmittedAt, } ranking.Rankings = append(ranking.Rankings, item) } // 获取总数 global.GVA_DB.WithContext(ctx).Model(&learning.UserExam{}). Where("exam_id = ? AND status = ?", examId, "completed"). Count(&ranking.Total) return } // loadExamInfo 加载考试信息 func (userExamService *UserExamService) loadExamInfo(ctx context.Context, userExam *learning.UserExam) { if userExam.ExamId != 0 { var exam learning.Exam if err := global.GVA_DB.WithContext(ctx).Where("id = ?", userExam.ExamId).First(&exam).Error; err == nil { userExam.Exam = exam } } }