| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- <?php
- namespace App\Filament\Pages;
- use App\Models\OCRRecord;
- use App\Models\OCRQuestionResult;
- use App\Models\Paper;
- use App\Models\PaperQuestion;
- use App\Jobs\ProcessOCRRecord;
- use Filament\Notifications\Notification;
- use Filament\Pages\Page;
- use Livewire\Attributes\Computed;
- use Livewire\Attributes\On;
- class OCRRecordViewEnhanced extends Page
- {
- protected static ?string $title = 'OCR记录详情';
- protected static ?string $slug = 'ocr-record-view/{recordId}';
- protected string $view = 'filament.pages.ocr-record-view-enhanced';
- public static function shouldRegisterNavigation(): bool
- {
- return false;
- }
- public string $recordId = '';
- public array $manualAnswers = [];
- public bool $hasAnalysisResults = false;
- public array $questionGrades = [];
- public bool $isGenerating = false;
- public ?string $generationTaskId = null;
- public array $questionGenerationStatus = [];
- // 新增:判断是否为系统生成的试卷
- public ?Paper $associatedPaper = null;
- public bool $isSystemGeneratedPaper = false;
- #[Computed]
- public function record(): ?OCRRecord
- {
- return OCRRecord::with(['student', 'questions'])->find($this->recordId);
- }
- public function mount(string $recordId): void
- {
- $this->recordId = $recordId;
- $record = $this->record();
- if ($record) {
- // 检查是否有关联的系统生成试卷
- if ($record->analysis_id) {
- $this->associatedPaper = Paper::where('paper_id', $record->analysis_id)->first();
- if ($this->associatedPaper && $this->associatedPaper->paper_type === 'auto_generated') {
- $this->isSystemGeneratedPaper = true;
- $this->initializeForSystemPaper();
- }
- }
- // 修复卡在处理状态的问题
- if ($record->status === 'processing' && $record->questions()->count() > 0) {
- $record->update([
- 'status' => 'completed',
- 'processed_at' => $record->processed_at ?? now(),
- 'total_questions' => $record->questions()->count(),
- 'processed_questions' => $record->questions()->count(),
- ]);
- $record = $this->record();
- }
- // 对于非系统生成的试卷,加载常规初始化
- if (!$this->isSystemGeneratedPaper) {
- $this->initializeForUploadedPaper($record);
- }
- $this->checkAnalysisResults($record);
- }
- }
- /**
- * 初始化系统生成试卷的处理逻辑
- */
- private function initializeForSystemPaper(): void
- {
- if (!$this->associatedPaper) return;
- // 获取系统生成试卷的题目
- $paperQuestions = PaperQuestion::where('paper_id', $this->associatedPaper->paper_id)
- ->orderBy('question_number')
- ->get();
- // 为OCR识别结果匹配系统试卷的题目
- $ocrQuestions = OCRQuestionResult::where('ocr_record_id', $this->recordId)
- ->orderBy('question_number')
- ->get();
- foreach ($ocrQuestions as $ocrQuestion) {
- // 尝试匹配相同题号的系统题目
- $matchedPaperQuestion = $paperQuestions->firstWhere('question_number', $ocrQuestion->question_number);
- if ($matchedPaperQuestion) {
- // 更新OCR记录,关联到系统题库
- $ocrQuestion->update([
- 'question_bank_id' => $matchedPaperQuestion->question_id,
- 'kp_code' => $matchedPaperQuestion->knowledge_point,
- 'ai_score' => null, // 等待评分
- ]);
- // 预设正确答案(来自系统试卷)
- $this->manualAnswers[$ocrQuestion->id] = $matchedPaperQuestion->correct_answer;
- }
- // 加载已有的评分
- if ($ocrQuestion->ai_score !== null || $ocrQuestion->score_value !== null) {
- $this->questionGrades[$ocrQuestion->id] = [
- 'score' => $ocrQuestion->ai_score ?? $ocrQuestion->score_value,
- 'is_correct' => $ocrQuestion->is_correct,
- 'student_answer' => $ocrQuestion->student_answer,
- ];
- }
- }
- }
- /**
- * 初始化上传试卷的处理逻辑(原逻辑)
- */
- private function initializeForUploadedPaper(OCRRecord $record): void
- {
- foreach ($record->questions as $question) {
- if ($question->manual_answer) {
- $this->manualAnswers[$question->id] = $question->manual_answer;
- }
- if ($question->ai_score !== null || $question->score_value !== null) {
- $this->questionGrades[$question->id] = [
- 'score' => $question->ai_score ?? $question->score_value,
- 'is_correct' => $question->is_correct,
- ];
- }
- $this->questionGenerationStatus[$question->id] = $question->generation_status ?? 'pending';
- if ($question->generation_status === 'generating' && $question->generation_task_id) {
- $this->isGenerating = true;
- $this->generationTaskId = $question->generation_task_id;
- }
- }
- }
- /**
- * 检查分析结果
- */
- private function checkAnalysisResults(OCRRecord $record): void
- {
- $this->hasAnalysisResults = $record->ai_analyzed_at && $record->questions()
- ->whereNotNull('ai_score')
- ->exists();
- }
- /**
- * 判断是否可以提交分析
- */
- public function canSubmitAnalysis(): bool
- {
- $record = $this->record();
- if (!$record) return false;
- // 对于系统生成的试卷,题目已自动匹配,无需检查question_bank_id
- if ($this->isSystemGeneratedPaper) {
- return true;
- }
- // 对于上传的试卷,需要检查是否所有题目都已关联题库
- return !$record->questions()->whereNull('question_bank_id')->exists();
- }
- /**
- * 提交分析
- */
- public function submitForAnalysis(): void
- {
- $record = $this->record();
- if (!$record) {
- Notification::make()->title('记录不存在')->danger()->send();
- return;
- }
- // 对于系统生成的试卷,直接进行评分分析
- if ($this->isSystemGeneratedPaper) {
- $this->analyzeSystemPaperAnswers();
- return;
- }
- // 原有的上传试卷分析逻辑
- if (!$this->canSubmitAnalysis()) {
- Notification::make()
- ->title('请先生成题库题目')
- ->body('分析前需要确保所有题目都已在题库中创建并关联')
- ->warning()
- ->send();
- return;
- }
- // ... 原有的提交分析逻辑 ...
- Notification::make()->title('分析提交成功')->success()->send();
- }
- /**
- * 分析系统生成试卷的答案
- */
- private function analyzeSystemPaperAnswers(): void
- {
- try {
- $ocrQuestions = OCRQuestionResult::where('ocr_record_id', $this->recordId)->get();
- $analysisResults = [];
- // 1. 尝试获取原始OCR数据并进行增强匹配
- $rawOcrData = \Illuminate\Support\Facades\DB::table('ocr_raw_data')
- ->where('ocr_record_id', $this->recordId)
- ->value('raw_response');
- $enhancedAnswers = [];
- if ($rawOcrData) {
- $rawOcrData = json_decode($rawOcrData, true);
- $paperQuestions = PaperQuestion::where('paper_id', $this->associatedPaper->paper_id)
- ->orderBy('question_number')
- ->get();
-
- $parser = new \App\Services\OCRDataParser();
- $enhancedAnswers = $parser->matchWithSystemPaper($rawOcrData, $paperQuestions);
-
- \Log::info('增强匹配结果', ['record_id' => $this->recordId, 'matches' => $enhancedAnswers]);
- }
- foreach ($ocrQuestions as $ocrQuestion) {
- $paperQuestion = PaperQuestion::where('paper_id', $this->associatedPaper->paper_id)
- ->where('question_number', $ocrQuestion->question_number)
- ->first();
- if ($paperQuestion) {
- // 优先使用增强匹配的答案
- $studentAnswer = $ocrQuestion->student_answer;
- if (isset($enhancedAnswers[$ocrQuestion->question_number])) {
- $enhancedAnswer = $enhancedAnswers[$ocrQuestion->question_number]['student_answer'];
- if (!empty($enhancedAnswer)) {
- $studentAnswer = $enhancedAnswer;
- // 更新OCR记录中的答案,以便用户看到的是真实的提取内容
- $ocrQuestion->update(['student_answer' => $studentAnswer]);
- }
- }
- // 比较学生答案和正确答案
- $isCorrect = $this->compareAnswers(
- $studentAnswer,
- $paperQuestion->correct_answer
- );
- $analysisResults[] = [
- 'ocr_question_id' => $ocrQuestion->id,
- 'paper_question_id' => $paperQuestion->id,
- 'is_correct' => $isCorrect,
- 'student_answer' => $studentAnswer,
- 'correct_answer' => $paperQuestion->correct_answer,
- 'score' => $isCorrect ? $paperQuestion->score : 0,
- ];
- // 更新OCR记录
- $ocrQuestion->update([
- 'ai_score' => $isCorrect ? $paperQuestion->score : 0,
- 'is_correct' => $isCorrect,
- ]);
- }
- }
- // 更新OCR记录状态
- $record = $this->record();
- $record->update([
- 'ai_analyzed_at' => now(),
- ]);
- // 可选:调用Learning Analytics API进行深度分析
- $this->sendToLearningAnalytics($analysisResults);
- Notification::make()
- ->title('试卷分析完成')
- ->body('系统生成试卷的答题分析已完成')
- ->success()
- ->send();
- } catch (\Exception $e) {
- \Log::error('系统试卷分析失败: ' . $e->getMessage());
- Notification::make()
- ->title('分析失败')
- ->body('分析过程中出现错误:' . $e->getMessage())
- ->danger()
- ->send();
- }
- }
- /**
- * 比较答案(支持多种答案格式)
- */
- private function compareAnswers(string $studentAnswer, string $correctAnswer): bool
- {
- // 标准化答案格式
- $studentAnswer = trim(strtolower($studentAnswer));
- $correctAnswer = trim(strtolower($correctAnswer));
- // 处理选择题(A, B, C, D 或 ①, ②, ③, ④)
- if (preg_match('/^[a-d①②③④]$/', $studentAnswer) && preg_match('/^[a-d①②③④]$/', $correctAnswer)) {
- return $this->normalizeChoiceAnswer($studentAnswer) === $this->normalizeChoiceAnswer($correctAnswer);
- }
- // 处理数字答案
- if (is_numeric($studentAnswer) && is_numeric($correctAnswer)) {
- return abs(floatval($studentAnswer) - floatval($correctAnswer)) < 0.001;
- }
- // 文本答案直接比较
- return $studentAnswer === $correctAnswer;
- }
- /**
- * 标准化选择题答案格式
- */
- private function normalizeChoiceAnswer(string $answer): string
- {
- $map = [
- '①' => 'a', '②' => 'b', '③' => 'c', '④' => 'd',
- '1' => 'a', '2' => 'b', '3' => 'c', '4' => 'd'
- ];
- return $map[$answer] ?? $answer;
- }
- /**
- * 发送分析结果到Learning Analytics
- */
- private function sendToLearningAnalytics(array $analysisResults): void
- {
- // 实现发送逻辑...
- }
- /**
- * 获取试卷类型描述
- */
- public function getPaperTypeDescription(): string
- {
- if ($this->isSystemGeneratedPaper) {
- return '系统生成的智能试卷 - 题目已自动匹配,可直接进行答题分析';
- }
- return '上传的试卷 - 需要先生成题库题目后再进行分析';
- }
- /**
- * 获取匹配的题目数量
- */
- public function getMatchedQuestionsCount(): int
- {
- if ($this->isSystemGeneratedPaper) {
- return OCRQuestionResult::where('ocr_record_id', $this->recordId)
- ->whereNotNull('question_bank_id')
- ->count();
- }
- return 0;
- }
- }
|