onQueue('pdf'); $this->afterCommit(); } public int $tries = 3; public int $timeout = 300; public function handle( ExamPdfExportService $examPdfExportService, TaskManager $taskManager ): void { try { $record = KnowledgeExplanation::query() ->where('knowledge_id', $this->knowledgeId) ->first(); if (! $record) { $taskManager->markTaskFailed($this->taskId, '知识点讲解记录不存在'); return; } $taskManager->updateTaskProgress($this->taskId, 70, '开始渲染知识点讲解PDF...'); $pdfUrl = $examPdfExportService->generateKnowledgeExplanationStandalonePdf($record, $this->knowledgePoints); if (! $pdfUrl) { $record->update(['status' => 'failed']); $taskManager->markTaskFailed($this->taskId, '知识点讲解PDF生成失败'); return; } $record->update([ 'status' => 'completed', 'pdf_url' => $pdfUrl, 'generated_at' => now(), ]); $this->syncPaperRecord($record, $pdfUrl); $taskManager->markTaskCompleted($this->taskId, [ 'paper_id' => $this->knowledgeId, 'knowledge_id' => $this->knowledgeId, 'pdfs' => [ 'all_pdf' => $pdfUrl, ], 'exam_content' => $this->buildKnowledgeExamContent($record, $pdfUrl), ]); $taskManager->sendCallback($this->taskId); } catch (\Throwable $e) { Log::error('GenerateKnowledgeExplanationPdfJob 失败', [ 'task_id' => $this->taskId, 'knowledge_id' => $this->knowledgeId, 'error' => $e->getMessage(), ]); KnowledgeExplanation::query() ->where('knowledge_id', $this->knowledgeId) ->update(['status' => 'failed']); $taskManager->markTaskFailed($this->taskId, $e->getMessage()); } } public function failed(Throwable $exception): void { KnowledgeExplanation::query() ->where('knowledge_id', $this->knowledgeId) ->update(['status' => 'failed']); app(TaskManager::class)->markTaskFailed($this->taskId, $exception->getMessage()); } private function syncPaperRecord(KnowledgeExplanation $record, string $pdfUrl): void { $paperId = (string) ($record->knowledge_id ?? ''); if ($paperId === '') { return; } $displayCode = (string) preg_replace('/^(paper_|knowledge_)/', '', $paperId); if ($displayCode === '') { $displayCode = $paperId; } Paper::query()->updateOrCreate( ['paper_id' => $paperId], [ 'student_id' => (string) ($record->student_id ?? ''), 'teacher_id' => (string) ($record->teacher_id ?? ''), 'params' => [ 'source' => 'knowledge_explanation', 'knowledge_id' => $paperId, 'kp_codes' => is_array($record->kp_codes) ? $record->kp_codes : [], ], 'paper_name' => '知识点讲解_' . $displayCode, 'paper_type' => 22, 'total_questions' => 0, 'total_score' => 100, 'status' => 'completed', 'difficulty_category' => null, 'exam_pdf_url' => $pdfUrl, 'grading_pdf_url' => null, 'all_pdf_url' => $pdfUrl, 'completed_at' => now(), ] ); } private function buildKnowledgeExamContent(KnowledgeExplanation $record, string $pdfUrl): array { $paperId = (string) ($record->knowledge_id ?? ''); $displayCode = (string) preg_replace('/^(paper_|knowledge_)/', '', $paperId); if ($displayCode === '') { $displayCode = $paperId; } $kpCodes = is_array($record->kp_codes) ? array_values($record->kp_codes) : []; $questions = $this->buildKnowledgeQuestions(100); $knowledgeDistribution = []; foreach ($kpCodes as $kpCode) { $knowledgeDistribution[(string) $kpCode] = 0; } foreach ($questions as $q) { $kp = (string) ($q['knowledge_point'] ?? ''); if ($kp === '') { continue; } $knowledgeDistribution[$kp] = (int) ($knowledgeDistribution[$kp] ?? 0) + 1; } return [ 'paper_info' => [ 'paper_id' => $paperId, 'paper_name' => '知识点讲解_' . $displayCode, 'student_id' => (string) ($record->student_id ?? ''), 'teacher_id' => (string) ($record->teacher_id ?? ''), 'total_questions' => count($questions), 'total_score' => 100, 'difficulty_category' => null, 'created_at' => optional($record->created_at)->toISOString(), 'updated_at' => optional($record->updated_at)->toISOString(), 'exam_code' => $displayCode, 'grading_code' => null, 'paper_id_num' => $displayCode, ], 'questions' => $questions, 'knowledge_points' => $kpCodes, 'statistics' => [ 'type_distribution' => [ 'choice' => 0, 'fill' => 0, 'answer' => count($questions), ], 'difficulty_distribution' => [], 'knowledge_point_distribution' => $knowledgeDistribution, 'average_difficulty' => null, 'total_estimated_time' => 0, ], 'pdfs' => [ 'all_pdf' => $pdfUrl, ], 'source' => 'knowledge_explanation', ]; } private function buildKnowledgeQuestions(int $totalScore = 100): array { $questions = []; $seq = 1; foreach ($this->knowledgePoints as $point) { $kpCode = (string) ($point['kp_code'] ?? ''); $kpName = (string) ($point['kp_name'] ?? $kpCode); $cases = is_array($point['cases'] ?? null) ? $point['cases'] : []; foreach ($cases as $case) { $questionId = (int) ($case['question_id'] ?? 0); $questions[] = [ 'question_number' => $seq++, 'question_id' => $questionId > 0 ? (string) $questionId : '', 'question_bank_id' => $questionId > 0 ? $questionId : null, 'question_type' => (string) ($case['question_type'] ?? 'answer'), 'knowledge_point' => $kpCode, 'knowledge_point_name' => $kpName, 'difficulty' => isset($case['difficulty']) && is_numeric($case['difficulty']) ? (float) $case['difficulty'] : null, 'score' => 0, 'estimated_time' => null, 'stem' => (string) ($case['stem'] ?? ''), 'options' => is_array($case['options'] ?? null) ? $case['options'] : [], 'correct_answer' => (string) ($case['answer'] ?? ''), 'solution' => (string) ($case['solution'] ?? ''), 'metadata' => [ 'source_type' => (string) ($case['source_type'] ?? ''), 'source_label' => (string) ($case['source_label'] ?? ''), 'is_wrong_case' => (bool) ($case['is_wrong_case'] ?? false), 'child_kp_code' => $case['child_kp_code'] ?? null, 'child_kp_name' => $case['child_kp_name'] ?? null, ], ]; } } $count = count($questions); if ($count > 0) { $baseScore = intdiv($totalScore, $count); $remainder = $totalScore - ($baseScore * $count); foreach ($questions as &$q) { $q['score'] = $baseScore; } unset($q); if ($remainder > 0) { // 余数分散到末尾若干题,保证总分精确为 totalScore for ($i = $count - $remainder; $i < $count; $i++) { if ($i >= 0 && isset($questions[$i])) { $questions[$i]['score'] += 1; } } } } return $questions; } }