toString(); // 计算统计数据 $answers = $data['answers']; $correctCount = 0; $wrongCount = 0; $missingCount = 0; $totalScore = 0; $obtainedScore = 0; foreach ($answers as $answer) { $score = (float) ($answer['score'] ?? 0); $maxScore = (float) ($answer['max_score'] ?? $score); // 缺题不计入对错统计 if ($answer['is_missing'] ?? false) { $missingCount++; continue; } $totalScore += $maxScore; $obtainedScore += $score; if ($answer['is_correct']) { $correctCount++; } else { $wrongCount++; } } $accuracyRate = ($correctCount + $wrongCount) > 0 ? round($correctCount / ($correctCount + $wrongCount), 4) : 0; // 只返回统计信息,不进行数据库操作(避免阻塞) // 错题记录将在后台任务中异步处理 Log::info('作答记录已保存', [ 'record_id' => $recordId, 'paper_id' => $data['paper_id'], 'student_id' => $data['student_id'], 'total_questions' => count($answers), 'correct_count' => $correctCount, 'wrong_count' => $wrongCount, 'accuracy_rate' => $accuracyRate, ]); return [ 'record_id' => $recordId, 'paper_id' => $data['paper_id'], 'student_id' => $data['student_id'], 'total_score' => $totalScore, 'obtained_score' => $obtainedScore, 'accuracy_rate' => $accuracyRate, 'correct_count' => $correctCount, 'wrong_count' => $wrongCount, 'missing_count' => $missingCount, 'total_questions' => count($answers), ]; } /** * 保存错题记录 * 优化功能: * 1. 相同题目不重复出现在错题本中 * 2. 记录错误次数和作答次数 * 3. 增加错误类型分析 * 4. 记录知识点掌握度变化 */ private function saveMistakeRecord(array $data, array $answer): void { try { // 检查是否已存在相同的错题记录 $existing = MistakeRecord::where('student_id', $data['student_id']) ->where('question_id', $answer['question_id']) ->first(); if ($existing) { // 更新现有记录(不重复创建) $oldReviewCount = $existing->review_count; // 递增错误次数和作答次数 $existing->increment('review_count'); // 记录每次错误的时间戳 $errorHistory = json_decode($existing->remark ?? '[]', true) ?: []; $errorHistory[] = [ 'timestamp' => now()->toISOString(), 'paper_id' => $data['paper_id'], 'student_answer' => $answer['student_answer'] ?? '', 'correct_answer' => $answer['correct_answer'] ?? '', 'error_type' => $this->guessErrorType($answer), 'is_missing' => $answer['is_missing'] ?? false, ]; // 更新记录 $existing->update([ 'student_answer' => $answer['student_answer'] ?? '', 'correct_answer' => $answer['correct_answer'] ?? '', 'knowledge_point' => $answer['knowledge_point'] ?? $existing->knowledge_point, 'error_type' => $this->guessErrorType($answer), 'remark' => json_encode($errorHistory), 'updated_at' => now(), // 如果是缺题,更新为待复习状态 'review_status' => ($answer['is_missing'] ?? false) ? MistakeRecord::REVIEW_STATUS_PENDING : $existing->review_status, ]); Log::info('错题记录已更新', [ 'student_id' => $data['student_id'], 'question_id' => $answer['question_id'], 'old_review_count' => $oldReviewCount, 'new_review_count' => $existing->review_count, 'total_errors' => count($errorHistory), ]); } else { // 创建新记录 $errorHistory = [[ 'timestamp' => now()->toISOString(), 'paper_id' => $data['paper_id'], 'student_answer' => $answer['student_answer'] ?? '', 'correct_answer' => $answer['correct_answer'] ?? '', 'error_type' => $this->guessErrorType($answer), 'is_missing' => $answer['is_missing'] ?? false, ]]; $mistakeRecord = MistakeRecord::create([ 'student_id' => $data['student_id'], 'question_id' => $answer['question_id'], 'paper_id' => $data['paper_id'] ?? null, 'source' => MistakeRecord::SOURCE_EXAM, 'question_text' => json_encode([ 'question_number' => $answer['question_number'] ?? null, 'question_type' => $answer['question_type'] ?? null, 'is_missing' => $answer['is_missing'] ?? false, ]), 'student_answer' => $answer['student_answer'] ?? '', 'correct_answer' => $answer['correct_answer'] ?? '', 'knowledge_point' => $answer['knowledge_point'] ?? null, 'error_type' => $this->guessErrorType($answer), 'review_status' => ($answer['is_missing'] ?? false) ? MistakeRecord::REVIEW_STATUS_PENDING : MistakeRecord::REVIEW_STATUS_PENDING, 'review_count' => 1, // 第一次错误 'force_review' => false, 'is_favorite' => false, 'in_retry_list' => false, 'difficulty' => 0.5, 'mastery_level' => 0.0, 'remark' => json_encode($errorHistory), ]); Log::info('新错题记录已创建', [ 'student_id' => $data['student_id'], 'question_id' => $answer['question_id'], 'mistake_record_id' => $mistakeRecord->id, 'error_type' => $mistakeRecord->error_type, ]); } } catch (\Exception $e) { Log::error('保存错题记录失败', [ 'student_id' => $data['student_id'], 'question_id' => $answer['question_id'], 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); } } /** * 猜测错误类型 */ private function guessErrorType(array $answer): string { // 根据题型和答案特征猜测错误类型 $questionType = $answer['question_type'] ?? ''; if ($questionType === 'choice') { return MistakeRecord::ERROR_TYPE_CARELESS; } if ($questionType === 'fill' || $questionType === 'answer') { // 检查是否部分正确 if (isset($answer['step_scores']) && is_array($answer['step_scores'])) { $totalSteps = count($answer['step_scores']); $correctSteps = array_sum($answer['step_scores']); if ($correctSteps > 0 && $correctSteps < $totalSteps) { return MistakeRecord::ERROR_TYPE_PROCEDURE ?? MistakeRecord::ERROR_TYPE_CALCULATION; } } } return MistakeRecord::ERROR_TYPE_OTHER; } /** * 保存分析结果 * 简化版:只保存错题记录,不依赖其他表 */ public function saveAnalysisResults(array $answerRecord, array $analysisData, array $questionAnalyses): void { try { // 只更新错题记录的掌握度(如果题目有错误) foreach ($questionAnalyses as $questionAnalysis) { // 更新掌握度(如果题目有知识点且答错了) if (!empty($questionAnalysis['kp_code']) && !($questionAnalysis['is_correct'] ?? true)) { $this->updateMasteryForQuestion( $answerRecord['student_id'], $questionAnalysis['kp_code'], $questionAnalysis['is_correct'], $questionAnalysis['difficulty'] ?? 0.5 ); } } Log::info('分析结果已保存', [ 'student_id' => $answerRecord['student_id'], 'paper_id' => $answerRecord['paper_id'], 'question_count' => count($questionAnalyses), ]); } catch (\Exception $e) { Log::error('保存分析结果失败', [ 'student_id' => $answerRecord['student_id'], 'paper_id' => $answerRecord['paper_id'], 'error' => $e->getMessage(), ]); } } /** * 为单个题目更新掌握度 */ private function updateMasteryForQuestion(string $studentId, string $kpCode, bool $isCorrect, float $difficulty): void { try { // 使用AI分析服务更新掌握度(如果表存在) $result = $this->aiAnalysisService->updateMastery( $studentId, $kpCode, 0.5, // 默认掌握度 $isCorrect, $difficulty ); Log::info('掌握度已更新', [ 'student_id' => $studentId, 'kp_code' => $kpCode, 'is_correct' => $isCorrect, 'new_mastery' => $result['new_mastery'] ?? 'N/A', 'change' => $result['change'] ?? 'N/A', ]); } catch (\Exception $e) { Log::warning('更新掌握度失败(跳过)', [ 'student_id' => $studentId, 'kp_code' => $kpCode, 'error' => $e->getMessage(), ]); } } /** * 创建掌握度快照 */ public function createMasterySnapshot(string $studentId, ?string $paperId = null, ?string $answerRecordId = null): ?array { // 使用AI分析服务创建快照 return $this->aiAnalysisService->createMasterySnapshot($studentId, $paperId, $answerRecordId); } /** * 获取学生的学习历史 */ public function getStudentLearningHistory(string $studentId, int $limit = 10): array { try { // 获取错题记录历史(作为学习历史的主要数据) $mistakes = MistakeRecord::where('student_id', $studentId) ->orderBy('created_at', 'desc') ->limit($limit) ->get() ->toArray(); // 使用AI分析服务获取掌握度数据(如果表存在) $masteryData = ['data' => []]; try { $masteryData = $this->aiAnalysisService->getStudentMastery($studentId); } catch (\Exception $e) { Log::warning('获取掌握度数据失败', ['student_id' => $studentId, 'error' => $e->getMessage()]); } return [ 'mistakes' => $mistakes, 'mastery_data' => $masteryData['data'] ?? [], 'summary' => [ 'total_mistakes' => MistakeRecord::where('student_id', $studentId)->count(), 'total_mastery_items' => count($masteryData['data'] ?? []), ], ]; } catch (\Exception $e) { Log::error('获取学习历史失败', [ 'student_id' => $studentId, 'error' => $e->getMessage(), ]); return []; } } /** * 使用增强版步骤级分析算法 * * 这是基于《卷子分析思考.md》思路的增强版分析方法 * 支持步骤级分析、知识点映射和智能出卷推荐 * * @param array $examData 考试数据 * @return array 分析结果 */ public function analyzeWithEnhancedSteps(array $examData): array { Log::info('StudentAnswerAnalysisService: 开始增强版步骤级分析', [ 'exam_id' => $examData['exam_id'] ?? 'unknown', 'student_id' => $examData['student_id'] ?? 'unknown', 'has_steps' => !empty(array_filter($examData['questions'] ?? [], fn($q) => !empty($q['steps']))) ]); try { // 使用增强的分析算法 $result = $this->examAnswerAnalysisService->analyzeExamAnswers($examData); Log::info('StudentAnswerAnalysisService: 增强版步骤级分析完成', [ 'exam_id' => $examData['exam_id'], 'student_id' => $examData['student_id'], 'analyzed_knowledge_points' => count($result['knowledge_point_analysis'] ?? []) ]); return $result; } catch (\Exception $e) { Log::error('StudentAnswerAnalysisService: 增强版步骤级分析失败', [ 'exam_id' => $examData['exam_id'] ?? 'unknown', 'student_id' => $examData['student_id'] ?? 'unknown', 'error' => $e->getMessage() ]); throw new \Exception('增强版步骤级分析失败:' . $e->getMessage()); } } }