pdfService = $pdfService; } /** * Generate PDF for specified questions * * POST /api/questions/pdf * * Request body: * { * "question_ids": [19903, 17766, ...], * "student_id": "123456", * "student_name": "张三", // optional * "student_grade": "初二", // optional * "teacher_name": "李老师", // optional * "paper_name": "专项练习", // optional * "include_grading": false // optional, whether to generate grading PDF * } */ public function generate(Request $request) { $validator = Validator::make($request->all(), [ 'question_ids' => 'required|array|min:1|max:100', 'question_ids.*' => 'required|integer', 'student_id' => 'required|string', 'student_name' => 'nullable|string|max:50', 'student_grade' => 'nullable|string|max:20', 'teacher_name' => 'nullable|string|max:50', 'paper_name' => 'nullable|string|max:100', 'include_grading' => 'nullable|boolean', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => '参数验证失败', 'errors' => $validator->errors(), ], 422); } $questionIds = $request->input('question_ids'); $studentId = $request->input('student_id'); $studentName = $request->input('student_name', ''); $studentGrade = $request->input('student_grade', ''); $teacherName = $request->input('teacher_name', ''); $paperName = $request->input('paper_name', '专项练习'); $includeGrading = $request->input('include_grading', false); Log::info('生成指定题目PDF', [ 'question_ids' => $questionIds, 'student_id' => $studentId, 'count' => count($questionIds), ]); try { // 1. Fetch questions from database $questions = DB::connection('remote_mysql') ->table('questions') ->whereIn('id', $questionIds) ->get(); if ($questions->isEmpty()) { return response()->json([ 'success' => false, 'message' => '未找到指定的题目', ], 404); } // 2. Group questions by type $groupedQuestions = $this->groupQuestionsByType($questions, $questionIds); // 3. Build virtual paper structure $paper = $this->buildVirtualPaper($paperName, $studentId, $groupedQuestions); // 4. Generate PDF $result = $this->pdfService->generateByQuestions( $paper, $groupedQuestions, [ 'name' => $studentName, 'grade' => $studentGrade, ], [ 'name' => $teacherName, ], $includeGrading ); Log::info('指定题目PDF生成成功', [ 'student_id' => $studentId, 'question_count' => count($questionIds), 'pdf_url' => $result['pdf_url'] ?? null, ]); return response()->json([ 'success' => true, 'message' => 'PDF生成成功', 'data' => $result, ]); } catch (\Exception $e) { Log::error('生成指定题目PDF失败', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); return response()->json([ 'success' => false, 'message' => '生成PDF失败: ' . $e->getMessage(), ], 500); } } /** * Group questions by type and maintain order */ private function groupQuestionsByType($questions, array $originalOrder): array { // Create a map for quick lookup and preserve original order $questionMap = []; foreach ($questions as $q) { $questionMap[$q->id] = $q; } $grouped = [ 'choice' => [], 'fill' => [], 'answer' => [], ]; $questionNumber = 1; // Process in original order foreach ($originalOrder as $id) { if (!isset($questionMap[$id])) { continue; } $q = $questionMap[$id]; $type = $this->normalizeQuestionType($q->question_type); // Build question object for template $questionObj = (object) [ 'id' => $q->id, 'question_number' => $questionNumber++, 'content' => $q->stem, 'options' => is_string($q->options) ? json_decode($q->options, true) : ($q->options ?? []), 'answer' => $q->answer, 'solution' => $q->solution, 'score' => $this->getDefaultScore($type), 'difficulty' => $q->difficulty, 'kp_code' => $q->kp_code, ]; $grouped[$type][] = $questionObj; } return $grouped; } /** * Normalize question type to standard format */ private function normalizeQuestionType(?string $type): string { if (!$type) { return 'answer'; } $type = strtolower(trim($type)); $typeMap = [ 'choice' => 'choice', '选择题' => 'choice', 'single_choice' => 'choice', 'multiple_choice' => 'choice', 'fill' => 'fill', '填空题' => 'fill', 'blank' => 'fill', 'answer' => 'answer', '解答题' => 'answer', 'subjective' => 'answer', 'calculation' => 'answer', 'proof' => 'answer', ]; return $typeMap[$type] ?? 'answer'; } /** * Get default score by question type */ private function getDefaultScore(string $type): int { return match ($type) { 'choice' => 5, 'fill' => 5, 'answer' => 10, default => 5, }; } /** * Build virtual paper structure */ private function buildVirtualPaper(string $paperName, string $studentId, array $groupedQuestions): object { $totalScore = 0; $totalQuestions = 0; foreach ($groupedQuestions as $type => $questions) { foreach ($questions as $q) { $totalScore += $q->score; $totalQuestions++; } } // Generate unique paper ID $paperId = 'custom_' . $studentId . '_' . time() . '_' . uniqid(); return (object) [ 'paper_id' => $paperId, 'paper_name' => $paperName, 'total_score' => $totalScore, 'total_questions' => $totalQuestions, 'created_at' => now()->toDateTimeString(), ]; } }