questionBankService = $questionBankService; } /** * 获取所有老师列表 */ public function getTeachers(?string $currentTeacherId = null): array { try { $query = Teacher::query() ->leftJoin('users as u', 'teachers.teacher_id', '=', 'u.user_id') ->select( 'teachers.teacher_id', 'teachers.name', 'teachers.subject', 'u.username', 'u.email' ); // 如果指定了当前老师ID,只返回该老师 if ($currentTeacherId) { $query->where('teachers.teacher_id', $currentTeacherId); } $teachers = $query->orderBy('teachers.name')->get(); // 检查是否有学生没有对应的老师记录 $teacherIds = $teachers->pluck('teacher_id')->toArray(); $missingTeacherIds = Student::query() ->distinct() ->whereNotIn('teacher_id', $teacherIds) ->pluck('teacher_id') ->toArray(); $teachersArray = $teachers->all(); if (!empty($missingTeacherIds)) { foreach ($missingTeacherIds as $missingId) { $teachersArray[] = (object) [ 'teacher_id' => $missingId, 'name' => '未知老师 (' . $missingId . ')', 'subject' => '未知', 'username' => null, 'email' => null ]; } usort($teachersArray, function($a, $b) { return strcmp($a->name, $b->name); }); } return $teachersArray; } catch (\Exception $e) { Log::error('ExamPaperService: 加载老师列表失败', [ 'error' => $e->getMessage() ]); return []; } } /** * 获取指定老师的学生列表 */ public function getStudents(?string $teacherId): array { if (empty($teacherId)) { return []; } try { return Student::query() ->leftJoin('users as u', 'students.student_id', '=', 'u.user_id') ->where('students.teacher_id', $teacherId) ->select( 'students.student_id', 'students.name', 'students.grade', 'students.class_name', 'u.username', 'u.email' ) ->orderBy('students.grade') ->orderBy('students.class_name') ->orderBy('students.name') ->get() ->all(); } catch (\Exception $e) { Log::error('ExamPaperService: 加载学生列表失败', [ 'teacher_id' => $teacherId, 'error' => $e->getMessage() ]); return []; } } /** * 获取最近的记录(OCR和试卷) */ public function getRecentRecords(?string $studentId = null, int $limit = 10): array { // 1. 获取OCR记录(图片上传) $ocrQuery = OCRRecord::with('student'); if (!empty($studentId)) { $ocrQuery->where('student_id', $studentId); } $ocrRecords = $ocrQuery->latest()->take(5)->get() ->map(function($record) { $studentName = $record->student?->name ?: ('学生ID: ' . $record->student_id); return [ 'type' => 'ocr_upload', 'id' => $record->id, 'record_id' => $record->id, 'paper_id' => null, 'student_id' => $record->student_id, 'student_name' => $studentName, 'paper_type' => $record->paper_type_label, 'paper_name' => $record->image_filename ?: '未命名图片', 'status' => $record->status, 'total_questions' => $record->total_questions, 'processed_questions' => $record->processed_questions ?? 0, 'created_at' => $record->created_at->format('Y-m-d H:i'), 'is_completed' => $record->status === 'completed', ]; })->toArray(); // 2. 获取所有Paper记录(包括草稿和已评分) $paperQuery = Paper::with('student'); if (!empty($studentId)) { $paperQuery->where('student_id', $studentId); } $allPapers = $paperQuery->latest()->take(5)->get() ->map(function($paper) { $type = $paper->status === 'completed' ? 'graded_paper' : 'generated'; $paperType = $paper->status === 'completed' ? '已评分试卷' : '系统生成试卷'; $iconColor = $paper->status === 'completed' ? 'text-green-500' : 'text-blue-500'; $studentName = $paper->student?->name ?: ('学生ID: ' . $paper->student_id); return [ 'type' => $type, 'id' => $paper->paper_id, 'record_id' => null, 'paper_id' => $paper->paper_id, 'student_id' => $paper->student_id, 'student_name' => $studentName, 'paper_type' => $paperType, 'paper_name' => $paper->paper_name ?? '未命名试卷', 'status' => $paper->difficulty_category, 'total_questions' => $paper->question_count ?? 0, 'created_at' => $paper->created_at->format('Y-m-d H:i'), 'is_completed' => $paper->status === 'completed', 'icon_color' => $iconColor, ]; })->toArray(); // 3. 合并并按时间排序 $allRecords = array_merge($ocrRecords, $allPapers); usort($allRecords, function($a, $b) { return strcmp($b['created_at'], $a['created_at']); }); return array_slice($allRecords, 0, $limit); } /** * 获取学生的试卷列表 */ public function getStudentPapers(?string $studentId): array { if (empty($studentId)) { return []; } try { $student = Student::find($studentId); if (!$student) { Log::warning('ExamPaperService: 未找到指定学生', ['student_id' => $studentId]); return []; } return $student->papers() ->withCount('questions') ->orderBy('created_at', 'desc') ->take(20) ->get() ->map(function($paper) { return [ 'paper_id' => $paper->paper_id, 'paper_name' => $paper->paper_name ?? '未命名试卷', 'total_questions' => $paper->questions_count ?? 0, 'total_score' => $paper->total_score ?? 0, 'created_at' => $paper->created_at->format('Y-m-d H:i'), ]; }) ->toArray(); } catch (\Exception $e) { Log::error('ExamPaperService: 获取学生试卷列表失败', [ 'student_id' => $studentId, 'error' => $e->getMessage() ]); return []; } } /** * 获取试卷题目详情 */ public function getPaperQuestions(?string $paperId): array { if (empty($paperId)) { return []; } try { // 首先检查试卷是否存在 $paper = Paper::where('paper_id', $paperId)->first(); if (!$paper) { Log::warning('ExamPaperService: 未找到指定试卷', ['paper_id' => $paperId]); return []; } // 使用关联关系查询题目 - 按题型和题号排序 $paperWithQuestions = Paper::with(['questions' => function($query) { // 自定义排序:选择题、填空题、解答题的顺序 $query->orderByRaw(" CASE question_type WHEN 'choice' THEN 1 WHEN 'fill' THEN 2 WHEN 'answer' THEN 3 ELSE 4 END ")->orderBy('question_number'); }])->where('paper_id', $paperId)->first(); $questions = $paperWithQuestions ? $paperWithQuestions->questions : collect([]); // 处理数据不一致的情况:如果题目为空但试卷显示有题目 if ($questions->isEmpty() && ($paper->question_count ?? 0) > 0) { Log::warning('ExamPaperService: 试卷显示有题目但实际题目数据缺失', [ 'paper_id' => $paperId, 'expected_questions' => $paper->question_count, 'actual_questions' => 0 ]); return [ [ 'id' => 'missing_data', 'question_number' => 1, 'question_bank_id' => null, 'question_type' => 'info', 'content' => "⚠️ 数据异常:试卷显示应有 {$paper->question_count} 道题目,但未找到题目数据。这通常是试卷创建过程中断导致的。请联系管理员或重新创建试卷。", 'answer' => '', 'score' => 0, 'is_missing_data' => true ] ]; } if ($questions->isEmpty()) { Log::info('ExamPaperService: 试卷确实没有题目', ['paper_id' => $paperId]); return [ [ 'id' => 'no_questions', 'question_number' => 1, 'question_bank_id' => null, 'question_type' => 'info', 'content' => '该试卷暂无题目数据', 'answer' => '', 'score' => 0, 'is_empty' => true ] ]; } // 获取题目详情 $questionIds = $questions->pluck('question_bank_id')->filter()->unique()->toArray(); if (empty($questionIds)) { Log::info('ExamPaperService: 题目没有关联题库ID', ['paper_id' => $paperId]); return $questions->map(function($q) { return [ 'id' => $q->id, 'question_number' => $q->question_number, 'question_bank_id' => $q->question_bank_id, 'question_type' => $q->question_type, 'content' => '题目内容未关联到题库', 'answer' => '', 'score' => $q->score ?? 5, ]; })->toArray(); } $questionsResponse = $this->questionBankService->getQuestionsByIds($questionIds); $questionDetails = collect($questionsResponse['data'] ?? [])->keyBy('id'); return $questions->map(function($q) use ($questionDetails) { $detail = $questionDetails->get($q->question_bank_id); // 获取题目内容,优先从 Question Bank 获取 stem,然后fallback $questionContent = null; if ($detail) { $questionContent = $detail['stem'] ?? $detail['content'] ?? $detail['question_text'] ?? null; } // 如果 Question Bank 中没有内容,尝试从本地数据库的 question_text 获取 if (!$questionContent && $q->question_text) { $questionContent = $q->question_text; } // 如果还是没有内容,显示友好提示 if (!$questionContent) { $questionContent = '⚠️ 题目内容暂未加载完整'; } return [ 'id' => $q->id, 'question_number' => $q->question_number, 'question_bank_id' => $q->question_bank_id, 'question_type' => $q->question_type, 'content' => $questionContent, 'answer' => $detail['answer'] ?? $detail['correct_answer'] ?? $q->answer ?? '', 'score' => $q->score ?? 5, 'kp_code' => $q->knowledge_point, // 从本地数据库获取知识点代码 ]; })->toArray(); } catch (\Exception $e) { Log::error('ExamPaperService: 获取试卷题目失败', [ 'paper_id' => $paperId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return [ [ 'id' => 'error', 'question_number' => 1, 'question_bank_id' => null, 'question_type' => 'error', 'content' => '获取题目数据时发生错误:' . $e->getMessage(), 'answer' => '', 'score' => 0, 'is_error' => true ] ]; } } }