| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- <?php
- namespace App\Http\Controllers\Api;
- use App\Http\Controllers\Controller;
- use App\Services\ExamAnswerAnalysisService;
- use Illuminate\Http\Request;
- use Illuminate\Http\JsonResponse;
- use Illuminate\Support\Facades\Log;
- use Illuminate\Support\Facades\Validator;
- /**
- * 考试答题分析 API 控制器
- * 支持步骤级分析的 RESTful API
- */
- class ExamAnswerAnalysisController extends Controller
- {
- public function __construct(
- private readonly ExamAnswerAnalysisService $analysisService
- ) {}
- /**
- * 分析考试答题数据
- *
- * POST /api/exam-answer-analysis
- *
- * @param Request $request
- * @return JsonResponse
- */
- public function analyze(Request $request): JsonResponse
- {
- // 临时增加执行时间限制(避免超时)
- set_time_limit(120);
- ini_set('max_execution_time', 120);
- try {
- // 验证请求数据
- $validator = Validator::make($request->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);
- }
- }
- }
|