loadMissing('questions'); $questions = $paper->questions ?? collect(); $codes = $this->generatePaperCodes($paper->paper_id); return [ 'paper_info' => [ 'paper_id' => $paper->paper_id, 'paper_name' => $paper->paper_name ?? '', 'student_id' => $paper->student_id ?? '', 'teacher_id' => $paper->teacher_id ?? '', 'total_questions' => $questions->count(), 'total_score' => $paper->total_score ?? 0, 'difficulty_category' => $paper->difficulty_category ?? '基础', 'created_at' => $paper->created_at?->toISOString(), 'updated_at' => $paper->updated_at?->toISOString(), 'exam_code' => $codes['exam_code'], 'grading_code' => $codes['grading_code'], 'paper_id_num' => $codes['paper_id_num'], ], 'questions' => $questions->map(function (PaperQuestion $q) { $options = []; if ($q->question_type === 'choice') { $questionText = $q->question_text ?? ''; preg_match_all('/([A-D])\s*[\.\、\:]\s*([^A-D]+?)(?=[A-D]\s*[\.\、\:]|$)/u', $questionText, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $options[] = [ 'label' => $match[1], 'content' => trim($match[2]), ]; } } return [ 'question_number' => $q->question_number, 'question_id' => $q->question_id, 'question_bank_id' => $q->question_bank_id, 'question_type' => $q->question_type, 'knowledge_point' => $q->knowledge_point, 'difficulty' => $q->difficulty, 'score' => $q->score, 'estimated_time' => $q->estimated_time, 'stem' => $q->question_text ?? '', 'options' => $options, 'correct_answer' => $q->correct_answer ?? '', 'solution' => $q->solution ?? '', 'student_answer' => $q->student_answer, 'is_correct' => $q->is_correct, 'score_obtained' => $q->score_obtained, 'score_ratio' => $q->score_ratio, 'teacher_comment' => $q->teacher_comment, 'graded_at' => $q->graded_at?->toISOString(), 'graded_by' => $q->graded_by, 'metadata' => [ 'has_solution' => !empty($q->solution), 'is_choice' => $q->question_type === 'choice', 'is_fill' => $q->question_type === 'fill', 'is_answer' => $q->question_type === 'answer', 'difficulty_label' => $this->getDifficultyLabel($q->difficulty), 'question_type_label' => $this->getQuestionTypeLabel($q->question_type), ], ]; })->toArray(), 'statistics' => [ 'type_distribution' => $this->getTypeDistribution($questions), 'difficulty_distribution' => $this->getDifficultyDistribution($questions), 'knowledge_point_distribution' => $this->getKnowledgePointDistribution($questions), 'average_difficulty' => $questions->avg('difficulty'), 'total_estimated_time' => $questions->sum('estimated_time'), ], 'knowledge_points' => $questions->pluck('knowledge_point')->unique()->filter()->values()->toArray(), 'skills' => $this->extractSkillsFromQuestions($questions), ]; } public function buildPaperApiPayload(Paper $paper): array { $examContent = $this->buildExamContent($paper); $codes = $this->generatePaperCodes($paper->paper_id); return [ 'success' => true, 'paper_id' => $paper->paper_id, 'status' => 'ready', 'exam_code' => $codes['exam_code'], 'grading_code' => $codes['grading_code'], 'paper_id_num' => $codes['paper_id_num'], 'exam_content' => $examContent, 'urls' => [ 'grading_url' => route('filament.admin.auth.intelligent-exam.grading', ['paper_id' => $paper->paper_id]), 'student_exam_url' => route('filament.admin.auth.intelligent-exam.pdf', ['paper_id' => $paper->paper_id, 'answer' => 'false']), ], 'pdfs' => [ 'exam_paper_pdf' => $paper->exam_pdf_url, 'grading_pdf' => $paper->grading_pdf_url, ], 'stats' => $examContent['statistics'] ?? null, 'created_at' => $paper->created_at?->toISOString(), 'updated_at' => $paper->updated_at?->toISOString(), ]; } public function generatePaperCodes(string $paperId): array { if (preg_match('/paper_(\d{12})/', $paperId, $matches)) { $paperIdNum = $matches[1]; } else { $paperIdNum = preg_replace('/[^0-9]/', '', $paperId); $paperIdNum = str_pad(substr($paperIdNum, 0, 12), 12, '0', STR_PAD_LEFT); } return [ 'paper_id_num' => $paperIdNum, 'exam_code' => '1' . $paperIdNum, 'grading_code' => '2' . $paperIdNum, ]; } private function getQuestionTypeLabel(?string $type): string { return match ($type) { 'choice' => '选择题', 'fill' => '填空题', 'answer' => '解答题', default => '未知题型', }; } private function getDifficultyLabel(?float $difficulty): string { if ($difficulty === null) { return '未知'; } if ($difficulty <= 0.4) { return '基础'; } if ($difficulty <= 0.7) { return '中等'; } return '拔高'; } private function getTypeDistribution($questions): array { $distribution = []; foreach ($questions as $question) { $type = $question->question_type; $distribution[$type] = ($distribution[$type] ?? 0) + 1; } return $distribution; } private function getDifficultyDistribution($questions): array { $distribution = []; foreach ($questions as $question) { $label = $this->getDifficultyLabel($question->difficulty); $distribution[$label] = ($distribution[$label] ?? 0) + 1; } return $distribution; } private function getKnowledgePointDistribution($questions): array { $distribution = []; foreach ($questions as $question) { $kp = $question->knowledge_point; if ($kp) { $distribution[$kp] = ($distribution[$kp] ?? 0) + 1; } } return $distribution; } private function extractSkillsFromQuestions($questions): array { $skills = []; foreach ($questions as $question) { $solution = $question->solution ?? ''; if ($solution) { $skillKeywords = ['代入法', '配方法', '因式分解', '换元法', '判别式', '求根公式', '韦达定理']; foreach ($skillKeywords as $keyword) { if (strpos($solution, $keyword) !== false) { $skills[] = $keyword; } } } $stem = $question->question_text ?? ''; if ($stem) { preg_match_all('/\{([^}]+)\}/', $stem, $matches); foreach ($matches[1] as $match) { $skillList = array_map('trim', explode(',', $match)); $skills = array_merge($skills, $skillList); } } } return array_unique(array_filter($skills)); } }