> $questions */ public function __construct( public string $paperId, public string $studentId, public array $questions ) { $this->questions = $this->compactQuestions($questions); // Online difficulty calibration is not on the user-visible critical // path. Keep it away from PDF workers so it cannot race report output. $this->onQueue((string) config('queue.workloads.question_difficulty_calibration', 'default')); $this->afterCommit(); } public function handle(QuestionDifficultyCalibrationService $calibrationService): void { Log::warning('QuestionDifficultyCalibrationJob: 开始异步在线难度校准', [ 'paper_id' => $this->paperId, 'student_id' => $this->studentId, 'question_count' => count($this->questions), 'attempt' => $this->attempts(), 'memory_mb' => round(memory_get_usage(true) / 1024 / 1024, 2), ]); $updatedQuestions = $calibrationService->updateOnlineFromPaper($this->paperId, $this->questions); Log::warning('QuestionDifficultyCalibrationJob: 异步在线难度校准完成', [ 'paper_id' => $this->paperId, 'student_id' => $this->studentId, 'updated_questions' => $updatedQuestions, 'memory_peak_mb' => round(memory_get_peak_usage(true) / 1024 / 1024, 2), ]); } public function failed(Throwable $exception): void { Log::error('QuestionDifficultyCalibrationJob: 异步在线难度校准最终失败', [ 'paper_id' => $this->paperId, 'student_id' => $this->studentId, 'question_count' => count($this->questions), 'error' => $exception->getMessage(), 'exception' => get_class($exception), 'memory_peak_mb' => round(memory_get_peak_usage(true) / 1024 / 1024, 2), ]); } /** * @param list> $questions * @return list> */ private function compactQuestions(array $questions): array { $compact = []; foreach ($questions as $question) { if (! is_array($question)) { continue; } $questionId = $question['question_id'] ?? $question['question_bank_id'] ?? null; if ($questionId === null || $questionId === '') { continue; } $compact[] = [ 'question_id' => $questionId, 'is_correct' => $question['is_correct'] ?? [], ]; } return $compact; } }