taskId = $taskId; $this->paperId = $paperId; } public function handle( ExamPdfExportService $pdfExportService, QuestionBankService $questionBankService, PaperPayloadService $paperPayloadService, TaskManager $taskManager ): void { try { Log::info('开始处理PDF生成队列任务', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'attempt' => $this->attempts(), ]); // 【新增】快速检查:如果任务已完成,直接跳过 $task = $taskManager->getTaskStatus($this->taskId); if ($task && $task['status'] === 'completed') { Log::info('【跳过执行】任务已完成,无需重复生成PDF', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'status' => $task['status'] ]); return; } // 【修复】首先检查试卷是否存在 $paperModel = Paper::with('questions')->find($this->paperId); if (!$paperModel) { Log::error('PDF生成队列任务失败:试卷不存在', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'attempt' => $this->attempts(), ]); // 如果试卷不存在,判断是否需要重试 if ($this->attempts() < $this->maxAttempts) { Log::info('试卷不存在,将在2秒后重试', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'attempt' => $this->attempts(), 'next_attempt' => $this->attempts() + 1, ]); // 延迟2秒后重试(缩短间隔,减少对回调的影响) $this->release(2); return; } else { Log::error('试卷不存在且已达到最大重试次数,标记任务失败', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'attempts' => $this->attempts(), ]); $taskManager->markTaskFailed($this->taskId, "试卷不存在: {$this->paperId}"); return; } } // 检查试卷是否有题目 if ($paperModel->questions->isEmpty()) { Log::error('PDF生成队列任务失败:试卷没有题目数据', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'question_count' => 0, ]); if ($this->attempts() < $this->maxAttempts) { Log::info('试卷没有题目,将在1秒后重试', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'attempt' => $this->attempts(), ]); // 延迟1秒后重试(更短间隔) $this->release(1); return; } else { $taskManager->markTaskFailed($this->taskId, "试卷没有题目数据: {$this->paperId}"); return; } } $taskManager->updateTaskProgress($this->taskId, 10, '开始生成试卷PDF...'); // 生成试卷PDF $pdfUrl = $pdfExportService->generateExamPdf($this->paperId) ?? $questionBankService->exportExamToPdf($this->paperId) ?? route('filament.admin.auth.intelligent-exam.pdf', ['paper_id' => $this->paperId, 'answer' => 'false']); $taskManager->updateTaskProgress($this->taskId, 50, '试卷PDF生成完成,开始生成判卷PDF...'); // 生成判卷PDF $gradingPdfUrl = $pdfExportService->generateGradingPdf($this->paperId) ?? route('filament.admin.auth.intelligent-exam.pdf', ['paper_id' => $this->paperId, 'answer' => 'true']); $taskManager->updateTaskProgress($this->taskId, 70, '判卷PDF生成完成,开始合并PDF...'); // 【优化】生成合并PDF(试卷 + 判卷) - 使用快速合并模式 $mergedPdfUrl = $pdfExportService->generateMergedPdf($this->paperId, function($percentage, $message) use ($taskManager) { // 进度更新:70% 开始,最高到 95% $progress = 70 + ($percentage / 100) * 25; $taskManager->updateTaskProgress($this->taskId, round($progress, 0), $message); }); // 【新增】验证合并后的PDF URL if (!$mergedPdfUrl) { Log::error('PDF生成队列任务失败:合并PDF失败', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'attempt' => $this->attempts(), ]); if ($this->attempts() < $this->maxAttempts) { Log::info('合并PDF失败,将在3秒后重试', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'attempt' => $this->attempts(), 'next_attempt' => $this->attempts() + 1, ]); // 延迟3秒后重试 $this->release(3); return; } else { Log::error('合并PDF失败且已达到最大重试次数,标记任务失败', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'attempts' => $this->attempts(), ]); $taskManager->markTaskFailed($this->taskId, "合并PDF失败: {$this->paperId}"); return; } } Log::info('PDF合并成功验证', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'merged_pdf_url' => $mergedPdfUrl, 'url_length' => strlen($mergedPdfUrl) ]); // 构建完整的试卷内容 $examContent = $paperPayloadService->buildExamContent($paperModel); // 标记任务完成(包含合并后的PDF URL) $taskManager->markTaskCompleted($this->taskId, [ 'exam_content' => $examContent, 'pdfs' => [ 'exam_paper_pdf' => $pdfUrl, 'grading_pdf' => $gradingPdfUrl, 'all_pdf' => $mergedPdfUrl, // 【新增】合并后的完整PDF ], ]); Log::info('PDF生成队列任务完成', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'pdf_url' => $pdfUrl, 'grading_pdf_url' => $gradingPdfUrl, 'merged_pdf_url' => $mergedPdfUrl, 'question_count' => $paperModel->questions->count(), ]); // 发送回调通知(在合并PDF完成后) $taskManager->sendCallback($this->taskId); } catch (\Exception $e) { Log::error('PDF生成队列任务失败', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); // 如果是第一次失败且试卷可能还在创建中,等待后重试 if ($this->attempts() < $this->maxAttempts && strpos($e->getMessage(), '不存在') !== false) { Log::info('检测到试卷不存在错误,将在2秒后重试', [ 'task_id' => $this->taskId, 'paper_id' => $this->paperId, 'attempt' => $this->attempts(), ]); $this->release(2); return; } $taskManager->markTaskFailed($this->taskId, $e->getMessage()); } } }