all(), [ 'paper_id' => 'required|string|max:255', 'student_id' => 'required|string|max:255', 'questions' => 'required|array|min:1', 'questions.*.question_id' => 'required|string|max:255', 'questions.*.score' => 'required|numeric|min:0', 'questions.*.score_obtained' => 'required|numeric|min:0', 'questions.*.steps' => 'sometimes|array', 'questions.*.steps.*.step_index' => 'required_with:questions.*.steps|integer|min:1', 'questions.*.steps.*.is_correct' => 'required_with:questions.*.steps|boolean', 'questions.*.steps.*.kp_id' => 'required_with:questions.*.steps|string|max:255', 'questions.*.steps.*.score' => 'required_with:questions.*.steps|numeric|min:0', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'error' => 'Validation failed', 'details' => $validator->errors() ], 422); } $examData = $request->only(['paper_id', 'student_id', 'questions']); // 调用分析服务 $result = $this->analysisService->analyzeExamAnswers($examData); return response()->json([ 'success' => true, 'data' => $result, 'message' => '分析完成' ]); } catch (\Exception $e) { Log::error('考试答题分析失败', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'request_data' => $request->all() ]); return response()->json([ 'success' => false, 'error' => '分析失败:' . $e->getMessage() ], 500); } } /** * 获取分析结果 * * GET /api/exam-answer-analysis/{student_id}/{paper_id} * * @param string $studentId * @param string $paperId * @return JsonResponse */ public function getAnalysisResult(string $studentId, string $paperId): JsonResponse { try { $result = \DB::connection('mysql') ->table('exam_analysis_results') ->where('student_id', $studentId) ->where('paper_id', $paperId) ->orderBy('created_at', 'desc') ->first(); if (!$result) { return response()->json([ 'success' => false, 'error' => '未找到分析结果' ], 404); } return response()->json([ 'success' => true, 'data' => json_decode($result->analysis_data, true) ]); } catch (\Exception $e) { Log::error('获取分析结果失败', [ 'student_id' => $studentId, 'paper_id' => $paperId, 'error' => $e->getMessage() ]); return response()->json([ 'success' => false, 'error' => '获取分析结果失败:' . $e->getMessage() ], 500); } } /** * 获取学生历史分析记录 * * GET /api/exam-answer-analysis/history/{student_id} * * @param string $studentId * @param Request $request * @return JsonResponse */ public function getHistory(string $studentId, Request $request): JsonResponse { try { $limit = $request->input('limit', 10); $offset = $request->input('offset', 0); $results = \DB::connection('mysql') ->table('exam_analysis_results') ->where('student_id', $studentId) ->orderBy('created_at', 'desc') ->offset($offset) ->limit($limit) ->get() ->map(function ($item) { return [ 'paper_id' => $item->paper_id, 'created_at' => $item->created_at, 'analysis_summary' => json_decode($item->analysis_data, true)['overall_summary'] ?? null ]; }); return response()->json([ 'success' => true, 'data' => $results, 'pagination' => [ 'limit' => $limit, 'offset' => $offset, 'total' => \DB::connection('mysql') ->table('exam_analysis_results') ->where('student_id', $studentId) ->count() ] ]); } catch (\Exception $e) { Log::error('获取历史分析记录失败', [ 'student_id' => $studentId, 'error' => $e->getMessage() ]); return response()->json([ 'success' => false, 'error' => '获取历史记录失败:' . $e->getMessage() ], 500); } } /** * 获取知识点掌握度趋势 * * GET /api/exam-answer-analysis/mastery-trend/{student_id} * * @param string $studentId * @param Request $request * @return JsonResponse */ public function getMasteryTrend(string $studentId, Request $request): JsonResponse { try { $kpCode = $request->input('kp_code'); $query = \DB::connection('mysql') ->table('exam_analysis_results') ->where('student_id', $studentId) ->orderBy('created_at', 'desc'); if ($kpCode) { $query->whereRaw("analysis_data->>'kp_code' = ?", [$kpCode]); } $results = $query->limit(20)->get(); $trendData = []; foreach ($results as $result) { $analysisData = json_decode($result->analysis_data, true); if (isset($analysisData['mastery_vector'])) { foreach ($analysisData['mastery_vector'] as $kpId => $data) { if (!$kpCode || $kpId === $kpCode) { $trendData[$kpId][] = [ 'date' => $result->created_at, 'mastery' => $data['current_mastery'], 'change' => $data['change'] ?? 0 ]; } } } } return response()->json([ 'success' => true, 'data' => $trendData ]); } catch (\Exception $e) { Log::error('获取掌握度趋势失败', [ 'student_id' => $studentId, 'error' => $e->getMessage() ]); return response()->json([ 'success' => false, 'error' => '获取趋势数据失败:' . $e->getMessage() ], 500); } } /** * 获取智能出卷推荐 * * GET /api/exam-answer-analysis/smart-quiz/{student_id} * * @param string $studentId * @param Request $request * @return JsonResponse */ public function getSmartQuizRecommendation(string $studentId, Request $request): JsonResponse { try { // 获取最近一次分析结果 $latestAnalysis = \DB::connection('mysql') ->table('exam_analysis_results') ->where('student_id', $studentId) ->orderBy('created_at', 'desc') ->first(); if (!$latestAnalysis) { return response()->json([ 'success' => false, 'error' => '未找到分析记录,无法生成推荐' ], 404); } $analysisData = json_decode($latestAnalysis->analysis_data, true); $recommendation = $analysisData['smart_quiz_recommendation'] ?? null; if (!$recommendation) { return response()->json([ 'success' => false, 'error' => '未找到推荐数据' ], 404); } return response()->json([ 'success' => true, 'data' => $recommendation, 'based_on_exam' => $latestAnalysis->paper_id, 'generated_at' => $latestAnalysis->created_at ]); } catch (\Exception $e) { Log::error('获取智能出卷推荐失败', [ 'student_id' => $studentId, 'error' => $e->getMessage() ]); return response()->json([ 'success' => false, 'error' => '获取推荐失败:' . $e->getMessage() ], 500); } } /** * 导出分析报告 * * GET /api/exam-answer-analysis/export/{student_id}/{paper_id} * * @param string $studentId * @param string $paperId * @param Request $request * @return JsonResponse */ public function export(string $studentId, string $paperId, Request $request): JsonResponse { try { $format = $request->input('format', 'json'); // json, pdf $result = \DB::connection('mysql') ->table('exam_analysis_results') ->where('student_id', $studentId) ->where('paper_id', $paperId) ->orderBy('created_at', 'desc') ->first(); if (!$result) { return response()->json([ 'success' => false, 'error' => '未找到分析结果' ], 404); } $analysisData = json_decode($result->analysis_data, true); if ($format === 'pdf') { // TODO: 实现 PDF 导出 return response()->json([ 'success' => false, 'error' => 'PDF 导出功能尚未实现' ], 501); } return response()->json([ 'success' => true, 'data' => $analysisData, 'metadata' => [ 'student_id' => $studentId, 'paper_id' => $paperId, 'generated_at' => $result->created_at, 'format' => $format ] ]); } catch (\Exception $e) { Log::error('导出分析报告失败', [ 'student_id' => $studentId, 'paper_id' => $paperId, 'error' => $e->getMessage() ]); return response()->json([ 'success' => false, 'error' => '导出失败:' . $e->getMessage() ], 500); } } /** * 批量分析多个学生的考试数据 * * POST /api/exam-answer-analysis/batch * * @param Request $request * @return JsonResponse */ public function batchAnalyze(Request $request): JsonResponse { try { $validator = Validator::make($request->all(), [ 'exam_data_list' => 'required|array|min:1', 'exam_data_list.*.paper_id' => 'required|string', 'exam_data_list.*.student_id' => 'required|string', 'exam_data_list.*.questions' => 'required|array', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'error' => 'Validation failed', 'details' => $validator->errors() ], 422); } $examDataList = $request->input('exam_data_list'); $results = []; foreach ($examDataList as $examData) { try { $result = $this->analysisService->analyzeExamAnswers($examData); $results[] = [ 'student_id' => $examData['student_id'], 'paper_id' => $examData['paper_id'], 'success' => true, 'data' => $result ]; } catch (\Exception $e) { $results[] = [ 'student_id' => $examData['student_id'], 'paper_id' => $examData['paper_id'], 'success' => false, 'error' => $e->getMessage() ]; } } $successCount = count(array_filter($results, fn($r) => $r['success'])); return response()->json([ 'success' => true, 'data' => $results, 'summary' => [ 'total' => count($results), 'success' => $successCount, 'failed' => count($results) - $successCount ], 'message' => "批量分析完成,成功 {$successCount} 条,失败 " . (count($results) - $successCount) . " 条" ]); } catch (\Exception $e) { Log::error('批量分析失败', [ 'error' => $e->getMessage(), 'request_data' => $request->all() ]); return response()->json([ 'success' => false, 'error' => '批量分析失败:' . $e->getMessage() ], 500); } } }