| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- <?php
- namespace App\Services;
- use App\Models\OCRRecord;
- use App\Models\Paper;
- use App\Models\Student;
- use App\Models\Teacher;
- use Illuminate\Support\Facades\Log;
- class ExamPaperService
- {
- protected QuestionBankService $questionBankService;
- public function __construct(QuestionBankService $questionBankService)
- {
- $this->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
- ]
- ];
- }
- }
- }
|