json()->all(); if (empty($payload)) { $payload = $request->all(); } // student_id类型转换:支持数字和字符串输入 if (isset($payload['student_id'])) { $payload['student_id'] = (string) $payload['student_id']; } // 验证参数 $validator = validator($payload, [ 'paper_id' => 'required|string', 'student_id' => 'required|string|min:1', 'answers' => 'required|array', 'answers.*.question_id' => 'required|string', 'answers.*.question_number' => 'nullable|string', 'answers.*.is_correct' => 'required|boolean', 'answers.*.student_answer' => 'nullable|string', 'answers.*.correct_answer' => 'nullable|string', 'answers.*.score' => 'nullable|numeric', 'answers.*.max_score' => 'nullable|numeric', 'answers.*.step_scores' => 'nullable|array', // 简答题步骤得分 'answers.*.knowledge_point' => 'nullable|string', 'answers.*.question_type' => 'nullable|string', 'answer_time' => 'nullable|timestamp', 'submit_time' => 'nullable|timestamp', 'source_system' => 'nullable|string', 'callback_url' => 'nullable|url', 'missing_questions' => 'nullable|array', // 缺题列表(可选) ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => '参数错误', 'errors' => $validator->errors(), ], 422); } $data = $validator->validated(); try { // 使用TaskManager创建异步任务 $taskId = $this->taskManager->createTask( TaskManager::TASK_TYPE_ANALYSIS, array_merge($data, ['type' => 'answer_analysis']) ); Log::info('StudentAnswerAnalysisController: 收到作答结果', [ 'task_id' => $taskId, 'paper_id' => $data['paper_id'], 'student_id' => $data['student_id'], 'answer_count' => count($data['answers']), ]); // 触发后台分析处理 $this->processAnswerAnalysis($taskId, $data); return response()->json([ 'success' => true, 'message' => '作答结果已提交,正在分析中...', 'data' => [ 'task_id' => $taskId, 'paper_id' => $data['paper_id'], 'student_id' => $data['student_id'], 'status' => 'processing', 'created_at' => now()->toISOString(), ], ]); } catch (\Exception $e) { Log::error('提交作答结果失败', [ 'paper_id' => $data['paper_id'] ?? 'unknown', 'student_id' => $data['student_id'] ?? 'unknown', 'error' => $e->getMessage(), ]); return response()->json([ 'success' => false, 'message' => '提交失败:' . $e->getMessage(), ], 500); } } /** * 查询分析任务状态 */ public function getAnalysisStatus(string $taskId): JsonResponse { try { $task = $this->taskManager->getTaskStatus($taskId); if (!$task) { return response()->json([ 'success' => false, 'message' => '任务不存在', ], 404); } return response()->json([ 'success' => true, 'data' => $task, ]); } catch (\Exception $e) { Log::error('查询分析状态失败', [ 'task_id' => $taskId, 'error' => $e->getMessage(), ]); return response()->json([ 'success' => false, 'message' => '查询失败:' . $e->getMessage(), ], 500); } } /** * 处理作答分析(后台任务) */ private function processAnswerAnalysis(string $taskId, array $data): void { try { $this->taskManager->updateTaskProgress($taskId, 10, '正在处理缺题(默认正确)...'); // 处理缺题:对于没有提交的题目,默认标记为正确 $allAnswers = $this->processMissingQuestions($data); $this->taskManager->updateTaskProgress($taskId, 30, '正在保存作答记录...'); // 保存作答记录到数据库(包含缺题) $answerRecord = $this->answerAnalysisService->saveAnswerRecord([ ...$data, 'answers' => $allAnswers, ]); $this->taskManager->updateTaskProgress($taskId, 50, '正在分析每道题(包括缺题处理)...'); // 简化分析:不调用AI,直接使用基础分析 $questionAnalyses = []; foreach ($allAnswers as $answer) { $questionAnalyses[] = [ 'question_id' => $answer['question_id'], 'question_number' => $answer['question_number'] ?? null, 'kp_code' => $answer['knowledge_point'] ?? null, 'student_answer' => $answer['student_answer'] ?? '', 'correct_answer' => $answer['correct_answer'] ?? '', 'is_correct' => $answer['is_correct'], 'score_obtained' => (float) ($answer['score'] ?? 0), 'max_score' => (float) ($answer['max_score'] ?? 10), 'difficulty' => 0.5, 'is_missing' => $answer['is_missing'] ?? false, 'model_used' => 'simple-rules', ]; } $this->taskManager->updateTaskProgress($taskId, 60, '正在保存分析结果...'); // 准备分析结果数据 $analysisData = [ 'question_results' => $questionAnalyses, 'total_questions' => count($questionAnalyses), 'correct_count' => count(array_filter($questionAnalyses, function($q) { return $q['correct'] ?? false; })), 'wrong_count' => count(array_filter($questionAnalyses, function($q) { return !($q['correct'] ?? true); })), 'model_used' => $questionAnalyses[0]['model_used'] ?? 'unknown', ]; // 保存分析结果 $this->answerAnalysisService->saveAnalysisResults($answerRecord, $analysisData, $questionAnalyses); $this->taskManager->updateTaskProgress($taskId, 80, '正在生成掌握度快照...'); // 生成掌握度快照(记录每次分析的掌握度变化) $masterySnapshot = $this->answerAnalysisService->createMasterySnapshot( $data['student_id'], $data['paper_id'], $answerRecord['record_id'] ); $this->taskManager->updateTaskProgress($taskId, 90, '正在生成学情分析报告...'); // 生成学情分析报告 $reportUrl = $this->generateLearningReport($taskId, $data, $answerRecord, $questionAnalyses, $masterySnapshot); // 标记任务完成 $this->taskManager->markTaskCompleted($taskId, [ 'answer_record_id' => $answerRecord['record_id'], 'analysis_id' => 'analysis_' . uniqid(), 'mastery_snapshot_id' => $masterySnapshot['snapshot_id'] ?? null, 'correct_count' => $answerRecord['correct_count'], 'wrong_count' => $answerRecord['wrong_count'], 'overall_mastery' => $masterySnapshot['overall_mastery'] ?? null, 'report_url' => $reportUrl, // 学情分析报告URL ]); Log::info('作答分析完成', [ 'task_id' => $taskId, 'paper_id' => $data['paper_id'], 'student_id' => $data['student_id'], 'answer_record_id' => $answerRecord['record_id'], ]); // 发送回调通知 $this->taskManager->sendCallback($taskId); } catch (\Exception $e) { Log::error('作答分析失败', [ 'task_id' => $taskId, 'paper_id' => $data['paper_id'], 'student_id' => $data['student_id'], 'error' => $e->getMessage(), ]); $this->taskManager->markTaskFailed($taskId, $e->getMessage()); } } /** * 获取题目文本内容 */ private function getQuestionText(string $questionId): string { try { // 这里可以调用 QuestionBankService 获取题目内容 // 目前返回空字符串,让AI分析基于学生答案进行分析 return ''; } catch (\Exception $e) { Log::warning('获取题目文本失败', [ 'question_id' => $questionId, 'error' => $e->getMessage(), ]); return ''; } } /** * 处理缺题逻辑:对于没有提交的题目,默认标记为正确 * 通过paper_id查询paper_question表获取总题数 */ private function processMissingQuestions(array $data): array { $answers = $data['answers'] ?? []; $submittedQuestionIds = array_column($answers, 'question_id'); // 获取缺题列表 $missingQuestions = $data['missing_questions'] ?? []; // 如果没有提供missing_questions,通过paper_id查询paper_question表 if (empty($missingQuestions)) { try { // 通过paper_id查询paper_question表获取题目总数 $totalQuestions = DB::table('paper_questions') ->where('paper_id', $data['paper_id']) ->count(); Log::info('从paper_question表获取题目总数', [ 'paper_id' => $data['paper_id'], 'total_questions' => $totalQuestions, 'submitted_count' => count($submittedQuestionIds), ]); // 自动生成缺题列表(根据paper_question表的题目编号) $allQuestionIds = DB::table('paper_questions') ->where('paper_id', $data['paper_id']) ->pluck('question_id') ->toArray(); foreach ($allQuestionIds as $questionId) { if (!in_array($questionId, $submittedQuestionIds)) { $missingQuestions[] = $questionId; } } } catch (\Exception $e) { Log::warning('查询paper_question表失败,跳过缺题处理', [ 'paper_id' => $data['paper_id'], 'error' => $e->getMessage(), ]); } } // 为每个缺题创建默认正确的记录 foreach ($missingQuestions as $missingQuestionId) { $answers[] = [ 'question_id' => $missingQuestionId, 'question_number' => $missingQuestionId, 'is_correct' => true, // 缺题默认正确 'student_answer' => '[缺题]', 'correct_answer' => '[未作答]', 'score' => 0, // 缺题不计分 'max_score' => 0, 'knowledge_point' => null, 'question_type' => 'missing', 'is_missing' => true, // 标记为缺题 'answer_time' => $data['answer_time'] ?? now(), ]; } Log::info('缺题处理完成', [ 'paper_id' => $data['paper_id'], 'submitted_count' => count($data['answers'] ?? []), 'missing_count' => count($missingQuestions), 'total_count' => count($answers), ]); return $answers; } /** * 生成学情分析报告并异步生成PDF */ private function generateLearningReport( string $taskId, array $data, array $answerRecord, array $questionAnalyses, ?array $masterySnapshot ): ?string { try { // 构建报告数据 $reportData = [ 'task_id' => $taskId, 'paper_id' => $data['paper_id'], 'student_id' => $data['student_id'], 'submit_time' => now()->toISOString(), 'answer_record' => $answerRecord, 'question_analyses' => $questionAnalyses, 'mastery_snapshot' => $masterySnapshot, 'report_type' => 'learning_analysis', ]; // 创建异步任务生成PDF $pdfTaskId = $this->taskManager->createTask( TaskManager::TASK_TYPE_PDF, array_merge($reportData, ['type' => 'learning_report']) ); Log::info('学情分析报告任务已创建', [ 'pdf_task_id' => $pdfTaskId, 'paper_id' => $data['paper_id'], 'student_id' => $data['student_id'], ]); // 返回报告URL(异步生成) return route('api.reports.learning', [ 'task_id' => $pdfTaskId, 'student_id' => $data['student_id'], ]); } catch (\Exception $e) { Log::error('生成学情分析报告失败', [ 'task_id' => $taskId, 'error' => $e->getMessage(), ]); return null; } } /** * 获取学生学习历史 */ public function getStudentLearningHistory(string $studentId): JsonResponse { try { $history = $this->answerAnalysisService->getStudentLearningHistory($studentId); return response()->json([ 'success' => true, 'data' => $history, ]); } catch (\Exception $e) { Log::error('获取学习历史失败', [ 'student_id' => $studentId, 'error' => $e->getMessage(), ]); return response()->json([ 'success' => false, 'message' => '获取失败:' . $e->getMessage(), ], 500); } } }