search) { $query->where('paper_name', 'like', '%' . $this->search . '%'); } // 应用状态过滤 if ($this->statusFilter) { $query->where('status', $this->statusFilter); } // 应用难度过滤 if ($this->difficultyFilter) { $query->where('difficulty_category', $this->difficultyFilter); } // 分页 $total = $query->count(); $papers = $query->withCount('questions') ->orderBy('created_at', 'desc') ->skip(($this->currentPage - 1) * $this->perPage) ->take($this->perPage) ->get() ->map(function ($paper) { return [ 'id' => $paper->paper_id, 'paper_name' => $paper->paper_name, 'question_count' => $paper->questions_count, // 使用实际的题目数量 'total_score' => $paper->total_score, 'difficulty_category' => $paper->difficulty_category, 'status' => $paper->status, 'created_at' => $paper->created_at, ]; }) ->toArray(); return [ 'data' => $papers, 'meta' => [ 'page' => $this->currentPage, 'per_page' => $this->perPage, 'total' => $total, 'total_pages' => ceil($total / $this->perPage), ] ]; } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error('获取试卷列表失败', ['error' => $e->getMessage()]); return [ 'data' => [], 'meta' => ['page' => 1, 'per_page' => 20, 'total' => 0, 'total_pages' => 0] ]; } } #[Computed(cache: false)] public function meta(): array { $examsData = $this->exams(); return $examsData['meta'] ?? ['page' => 1, 'per_page' => 20, 'total' => 0, 'total_pages' => 0]; } // 侧边栏详情功能已移除,使用独立页面 // public function viewExamDetail(string $examId) // { // return redirect()->route('filament.admin.auth.exam-detail', ['paperId' => $examId]); // } public function exportPdf(string $examId) { try { $questionBankService = app(QuestionBankService::class); $pdfUrl = $questionBankService->exportExamToPdf($examId); if ($pdfUrl) { // TODO: 实际下载PDF Notification::make() ->title('PDF导出成功') ->body('试卷已导出为PDF格式') ->success() ->send(); } else { Notification::make() ->title('PDF导出暂时不可用') ->body('外部题库服务暂时不可用,请稍后重试或联系管理员') ->warning() ->send(); } } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error('PDF导出异常', [ 'exam_id' => $examId, 'error' => $e->getMessage() ]); Notification::make() ->title('PDF导出失败') ->body('导出过程中发生错误') ->danger() ->send(); } } public function duplicateExam(array $examData) { // 复制试卷配置,用于快速生成类似试卷 $learningService = app(\App\Services\LearningAnalyticsService::class); // 提取试卷配置 $examConfig = [ 'paper_name' => $examData['paper_name'] . ' (副本)', 'total_questions' => $examData['question_count'], 'difficulty_category' => $examData['difficulty_category'] ?? '基础', 'question_type_ratio' => [ '选择题' => 40, '填空题' => 30, '解答题' => 30, ], 'difficulty_ratio' => [ '基础' => 50, '中等' => 35, '拔高' => 15, ], ]; // TODO: 跳转到智能出卷页面并预填充配置 // 这里可以通过session传递配置,或者使用URL参数 Notification::make() ->title('试卷配置已复制') ->body('请前往智能出卷页面查看并使用该配置') ->success() ->send(); } public function deleteExam(string $examId) { try { \Illuminate\Support\Facades\DB::beginTransaction(); $paper = \App\Models\Paper::find($examId); if (!$paper) { throw new \Exception('试卷不存在'); } // 删除关联的题目 $deletedQuestions = $paper->questions()->delete(); // 删除试卷 $paper->delete(); \Illuminate\Support\Facades\DB::commit(); Notification::make() ->title('删除成功') ->body("试卷及其 {$deletedQuestions} 道关联题目已删除") ->success() ->send(); $this->reset('selectedExamId', 'selectedExamDetail'); } catch (\Exception $e) { \Illuminate\Support\Facades\DB::rollBack(); \Illuminate\Support\Facades\Log::error('删除试卷失败', [ 'exam_id' => $examId, 'error' => $e->getMessage() ]); Notification::make() ->title('删除失败') ->body($e->getMessage()) ->danger() ->send(); } } public function startEditExam(string $examId) { $paper = \App\Models\Paper::find($examId); if ($paper) { $this->editingExamId = $examId; $this->editForm = [ 'paper_name' => $paper->paper_name, 'difficulty_category' => $paper->difficulty_category, 'status' => $paper->status, ]; } } public function saveExamEdit() { try { $paper = \App\Models\Paper::find($this->editingExamId); if (!$paper) { throw new \Exception('试卷不存在'); } // 验证表单数据 $validated = \Illuminate\Support\Facades\Validator::make($this->editForm, [ 'paper_name' => 'required|string|max:255', 'difficulty_category' => 'required|in:基础,进阶,竞赛', 'status' => 'required|in:draft,completed,graded', ])->validate(); $paper->update($validated); Notification::make() ->title('修改成功') ->body('试卷信息已更新') ->success() ->send(); $this->reset('editingExamId', 'editForm'); } catch (\Illuminate\Validation\ValidationException $e) { Notification::make() ->title('验证失败') ->body('请检查输入的数据是否正确') ->danger() ->send(); } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error('修改试卷失败', [ 'exam_id' => $this->editingExamId, 'error' => $e->getMessage() ]); Notification::make() ->title('修改失败') ->body($e->getMessage()) ->danger() ->send(); } } public function cancelEdit() { $this->reset('editingExamId', 'editForm'); } public function getStatusColor(string $status): string { return match($status) { 'draft' => 'ghost', 'completed' => 'success', 'graded' => 'primary', default => 'ghost', }; } public function getStatusLabel(string $status): string { return match($status) { 'draft' => '草稿', 'completed' => '已完成', 'graded' => '已评分', default => '未知', }; } public function getDifficultyColor(string $difficulty): string { return match($difficulty) { '基础' => 'success', '进阶' => 'warning', '竞赛' => 'error', default => 'ghost', }; } /** * 删除试卷中的题目 */ public function deleteQuestion(string $paperId, int $questionId) { try { \Illuminate\Support\Facades\DB::beginTransaction(); $paperQuestion = \App\Models\PaperQuestion::where('paper_id', $paperId) ->where('id', $questionId) ->first(); if (!$paperQuestion) { throw new \Exception('题目不存在'); } $paperQuestion->delete(); // 更新试卷的题目数量 $paper = \App\Models\Paper::find($paperId); if ($paper) { $paper->question_count = $paper->questions()->count(); $paper->save(); } \Illuminate\Support\Facades\DB::commit(); Notification::make() ->title('删除成功') ->body('题目已从试卷中删除') ->success() ->send(); // 刷新试卷详情 if ($this->selectedExamId === $paperId) { $this->loadExamDetail(); } } catch (\Exception $e) { \Illuminate\Support\Facades\DB::rollBack(); \Illuminate\Support\Facades\Log::error('删除题目失败', [ 'paper_id' => $paperId, 'question_id' => $questionId, 'error' => $e->getMessage() ]); Notification::make() ->title('删除失败') ->body($e->getMessage()) ->danger() ->send(); } } /** * 获取可选题目列表 */ #[Computed(cache: false)] public function availableQuestions(): array { if (!$this->selectedKnowledgePoint && !$this->selectedQuestionType) { return []; } try { $query = \App\Models\Question::query(); // 按知识点过滤 if ($this->selectedKnowledgePoint) { $query->where('kp_code', 'like', '%' . $this->selectedKnowledgePoint . '%'); } // 按题型过滤 if ($this->selectedQuestionType) { // 这里需要根据实际的数据结构来过滤 // PaperQuestion 表中有 question_type,但 Question 表中没有 // 可能需要通过 join 或其他方式处理 } $questions = $query->limit(50)->get()->map(function ($question) { return [ 'id' => $question->id, 'question_code' => $question->question_code, 'kp_code' => $question->kp_code, 'stem' => \Illuminate\Support\Str::limit($question->stem, 100), 'difficulty' => $question->difficulty, 'difficulty_label' => $question->difficulty_label, ]; })->toArray(); return $questions; } catch (\Exception $e) { \Illuminate\Support\Facades\Log::error('获取可选题目列表失败', ['error' => $e->getMessage()]); return []; } } /** * 添加题目到试卷 */ public function addQuestion(string $paperId, int $questionBankId) { try { \Illuminate\Support\Facades\DB::beginTransaction(); $paper = \App\Models\Paper::find($paperId); if (!$paper) { throw new \Exception('试卷不存在'); } $question = \App\Models\Question::find($questionBankId); if (!$question) { throw new \Exception('题目不存在'); } // 检查题目是否已在试卷中 $exists = \App\Models\PaperQuestion::where('paper_id', $paperId) ->where('question_bank_id', $questionBankId) ->exists(); if ($exists) { throw new \Exception('题目已在试卷中'); } // 获取当前试卷的最大题号 $maxQuestionNumber = \App\Models\PaperQuestion::where('paper_id', $paperId) ->max('question_number') ?? 0; // 创建新的试卷题目记录 \App\Models\PaperQuestion::create([ 'paper_id' => $paperId, 'question_id' => 'PQ_' . uniqid(), 'question_bank_id' => $questionBankId, 'knowledge_point' => $question->kp_code, 'question_type' => $this->getQuestionTypeFromQuestion($question), 'question_text' => $question->stem, 'difficulty' => $question->difficulty, 'score' => $this->calculateScore($question->difficulty), 'estimated_time' => $this->calculateEstimatedTime($question->difficulty), 'question_number' => $maxQuestionNumber + 1, ]); // 更新试卷的题目数量和总分 $paper->question_count = $paper->questions()->count(); $paper->total_score = $paper->questions()->sum('score'); $paper->save(); \Illuminate\Support\Facades\DB::commit(); Notification::make() ->title('添加成功') ->body('题目已添加到试卷中') ->success() ->send(); // 刷新试卷详情 if ($this->selectedExamId === $paperId) { $this->loadExamDetail(); } // 清空搜索条件 $this->reset('selectedKnowledgePoint', 'selectedQuestionType', 'availableQuestions'); } catch (\Exception $e) { \Illuminate\Support\Facades\DB::rollBack(); \Illuminate\Support\Facades\Log::error('添加题目失败', [ 'paper_id' => $paperId, 'question_bank_id' => $questionBankId, 'error' => $e->getMessage() ]); Notification::make() ->title('添加失败') ->body($e->getMessage()) ->danger() ->send(); } } /** * 根据题目确定题型 */ private function getQuestionTypeFromQuestion(\App\Models\Question $question): string { // 这里需要根据题目的特征来判断题型 // 临时返回默认值,可以根据实际需求调整 return 'choice'; } /** * 根据难度计算分数 */ private function calculateScore(float $difficulty): float { return match (true) { $difficulty <= 0.4 => 5.0, $difficulty <= 0.7 => 10.0, default => 15.0, }; } /** * 根据难度计算预计用时(秒) */ private function calculateEstimatedTime(float $difficulty): int { return match (true) { $difficulty <= 0.4 => 120, $difficulty <= 0.7 => 180, default => 300, }; } }