package handlers import ( "ankao/internal/database" "ankao/internal/models" "ankao/internal/services" "encoding/json" "fmt" "log" "gorm.io/datatypes" ) // ReGradeExam 公开的重新阅卷函数,可被外部调用 func ReGradeExam(recordID uint, examID uint, userID uint) { gradeExam(recordID, examID, userID) } // gradeExam 异步阅卷函数 func gradeExam(recordID uint, examID uint, userID uint) { db := database.GetDB() // 查询考试记录 var record models.ExamRecord if err := db.Where("id = ?", recordID).First(&record).Error; err != nil { log.Printf("查询考试记录失败: %v", err) return } // 查询试卷 var exam models.Exam if err := db.Where("id = ?", examID).First(&exam).Error; err != nil { log.Printf("查询试卷失败: %v", err) return } // 从 ExamUserAnswer 表读取所有答案 var userAnswers []models.ExamUserAnswer if err := db.Where("exam_record_id = ?", recordID).Find(&userAnswers).Error; err != nil { log.Printf("查询用户答案失败: %v", err) return } // 转换为 map 格式方便查找 answersMap := make(map[int64]interface{}) for _, ua := range userAnswers { var answer interface{} if err := json.Unmarshal(ua.Answer, &answer); err != nil { log.Printf("解析答案失败: %v", err) continue } answersMap[ua.QuestionID] = answer } // 解析题目ID列表 var questionIDs []uint if err := json.Unmarshal(exam.QuestionIDs, &questionIDs); err != nil { log.Printf("解析题目ID失败: %v", err) return } // 查询题目详情 var questions []models.PracticeQuestion if err := db.Where("id IN ?", questionIDs).Find(&questions).Error; err != nil { log.Printf("查询题目失败: %v", err) return } // 使用固定的题型分值映射 scoreMap := map[string]float64{ "fill-in-blank": 2.0, "true-false": 2.0, "multiple-choice": 1.0, "multiple-selection": 2.5, "short-answer": 8.0, // 简答题 8 分 "ordinary-essay": 9.0, // 论述题 9 分 "management-essay": 9.0, // 论述题 9 分 } // 评分 totalScore := 0.0 aiService, err := services.NewAIGradingService() if err != nil { log.Printf("AI服务初始化失败: %v,将跳过AI评分", err) // 不返回错误,继续评分流程,只是跳过AI评分 } for _, question := range questions { userAnswerRaw, answered := answersMap[question.ID] if !answered { // 更新数据库中的 ExamUserAnswer 记录为未作答 var userAnswer models.ExamUserAnswer result := db.Where("exam_record_id = ? AND question_id = ?", recordID, question.ID).First(&userAnswer) if result.Error == nil { updates := map[string]interface{}{ "is_correct": false, "score": 0.0, } db.Model(&userAnswer).Updates(updates) } continue } // 根据题型判断答案 var isCorrect bool var score float64 var aiGrading *models.AIGrading switch question.Type { case "fill-in-blank": // 填空题:比较数组 userAnswerArr, ok := userAnswerRaw.([]interface{}) if !ok { isCorrect = false score = 0 // 更新数据库 var userAnswer models.ExamUserAnswer if result := db.Where("exam_record_id = ? AND question_id = ?", recordID, question.ID).First(&userAnswer); result.Error == nil { db.Model(&userAnswer).Updates(map[string]interface{}{ "is_correct": false, "score": 0.0, }) } continue } var correctAnswers []string if err := json.Unmarshal([]byte(question.AnswerData), &correctAnswers); err != nil { log.Printf("解析填空题答案失败: %v", err) continue } isCorrect = len(userAnswerArr) == len(correctAnswers) if isCorrect { for i, ua := range userAnswerArr { if i >= len(correctAnswers) || fmt.Sprintf("%v", ua) != correctAnswers[i] { isCorrect = false break } } } if isCorrect { score = scoreMap["fill-in-blank"] } case "true-false": // 判断题 - AnswerData 直接存储 "true" 或 "false" 字符串 correctAnswer := question.AnswerData isCorrect = fmt.Sprintf("%v", userAnswerRaw) == correctAnswer if isCorrect { score = scoreMap["true-false"] } case "multiple-choice": correctAnswer := question.AnswerData isCorrect = fmt.Sprintf("\"%v\"", userAnswerRaw) == correctAnswer if isCorrect { score = scoreMap["multiple-choice"] } case "multiple-selection": // 多选题:比较数组(顺序无关) userAnswerArr, ok := userAnswerRaw.([]interface{}) if !ok { isCorrect = false score = 0 // 更新数据库 var userAnswer models.ExamUserAnswer if result := db.Where("exam_record_id = ? AND question_id = ?", recordID, question.ID).First(&userAnswer); result.Error == nil { db.Model(&userAnswer).Updates(map[string]interface{}{ "is_correct": false, "score": 0.0, }) } continue } var correctAnswers []string if err := json.Unmarshal([]byte(question.AnswerData), &correctAnswers); err != nil { log.Printf("解析多选题答案失败: %v", err) continue } userAnswerSet := make(map[string]bool) for _, ua := range userAnswerArr { userAnswerSet[fmt.Sprintf("%v", ua)] = true } isCorrect = len(userAnswerSet) == len(correctAnswers) if isCorrect { for _, ca := range correctAnswers { if !userAnswerSet[ca] { isCorrect = false break } } } if isCorrect { score = scoreMap["multiple-selection"] } case "short-answer", "ordinary-essay", "management-essay": // 简答题和论述题:使用AI评分 // AnswerData 直接存储答案文本 correctAnswer := question.AnswerData userAnswerStr := fmt.Sprintf("%v", userAnswerRaw) // 检查AI服务是否可用 if aiService == nil { log.Printf("AI服务不可用,无法评分问题 %d", question.ID) isCorrect = false score = 0 } else { aiResult, aiErr := aiService.GradeShortAnswer(question.Question, correctAnswer, userAnswerStr) if aiErr != nil { log.Printf("AI评分失败: %v", aiErr) isCorrect = false score = 0 } else { isCorrect = aiResult.IsCorrect // 按AI评分比例计算 var questionScore float64 if question.Type == "short-answer" { questionScore = scoreMap["short-answer"] } else if question.Type == "ordinary-essay" { questionScore = scoreMap["ordinary-essay"] } else if question.Type == "management-essay" { questionScore = scoreMap["management-essay"] } score = questionScore * (aiResult.Score / 100.0) aiGrading = &models.AIGrading{ Score: aiResult.Score, Feedback: aiResult.Feedback, Suggestion: aiResult.Suggestion, } } } } totalScore += score // 更新数据库中的 ExamUserAnswer 记录 var userAnswer models.ExamUserAnswer result := db.Where("exam_record_id = ? AND question_id = ?", recordID, question.ID).First(&userAnswer) if result.Error == nil { // 序列化 AI 评分数据 var aiGradingJSON datatypes.JSON if aiGrading != nil { aiGradingData, _ := json.Marshal(aiGrading) aiGradingJSON = datatypes.JSON(aiGradingData) } // 更新评分结果 updates := map[string]interface{}{ "is_correct": isCorrect, "score": score, "ai_grading_data": aiGradingJSON, } db.Model(&userAnswer).Updates(updates) } } // 保存分数和状态到考试记录 record.Score = totalScore record.Status = "graded" record.IsPassed = totalScore >= float64(exam.PassScore) if err := db.Save(&record).Error; err != nil { log.Printf("保存考试记录失败: %v", err) return } log.Printf("阅卷完成: 考试记录ID=%d, 总分=%.2f, 是否通过=%v", recordID, totalScore, record.IsPassed) }