OCRRecordView.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <?php
  2. namespace App\Filament\Pages;
  3. use App\Models\OCRRecord;
  4. use App\Models\OCRQuestionResult;
  5. use App\Jobs\ProcessOCRRecord;
  6. use Filament\Notifications\Notification;
  7. use Filament\Pages\Page;
  8. use Livewire\Attributes\Computed;
  9. class OCRRecordView extends Page
  10. {
  11. protected static ?string $title = 'OCR记录详情';
  12. protected static ?string $slug = 'ocr-record-view/{recordId}';
  13. protected string $view = 'filament.pages.ocr-record-view-new';
  14. public static function shouldRegisterNavigation(): bool
  15. {
  16. return false;
  17. }
  18. public string $recordId = '';
  19. public array $manualAnswers = [];
  20. public bool $hasAnalysisResults = false;
  21. #[Computed]
  22. public function record(): ?OCRRecord
  23. {
  24. return OCRRecord::with(['student', 'questions'])->find($this->recordId);
  25. }
  26. public function mount(string $recordId): void
  27. {
  28. $this->recordId = $recordId;
  29. $record = $this->record();
  30. if ($record) {
  31. // Fix stuck processing status: if status is processing but we have questions, it's actually completed
  32. if ($record->status === 'processing' && $record->questions()->count() > 0) {
  33. $record->update([
  34. 'status' => 'completed',
  35. 'processed_at' => $record->processed_at ?? now(),
  36. 'total_questions' => $record->questions()->count(),
  37. 'processed_questions' => $record->questions()->count(),
  38. ]);
  39. // Refresh record to get updated status
  40. $record = $this->record();
  41. }
  42. foreach ($record->questions as $question) {
  43. if ($question->manual_answer) {
  44. $this->manualAnswers[$question->id] = $question->manual_answer;
  45. }
  46. }
  47. // 检查是否已有AI分析结果
  48. $this->checkAnalysisResults($record);
  49. }
  50. }
  51. /**
  52. * Check if record already has AI analysis results
  53. */
  54. private function checkAnalysisResults(OCRRecord $record): void
  55. {
  56. // Only consider analyzed if ai_analyzed_at is set AND we have scores
  57. $this->hasAnalysisResults = $record->ai_analyzed_at && $record->questions()
  58. ->whereNotNull('ai_score')
  59. ->exists();
  60. }
  61. /**
  62. * Submit all questions for AI analysis.
  63. * Updates manual answers in batch, then sends data to LearningAnalytics.
  64. */
  65. public function submitForAnalysis(): void
  66. {
  67. $record = $this->record();
  68. if (! $record) {
  69. Notification::make()
  70. ->title('记录不存在')
  71. ->danger()
  72. ->send();
  73. return;
  74. }
  75. $updatedCount = 0;
  76. foreach ($record->questions as $question) {
  77. $manualAnswer = $this->manualAnswers[$question->id] ?? null;
  78. if ($manualAnswer && trim($manualAnswer) !== '') {
  79. $question->update([
  80. 'manual_answer' => trim($manualAnswer),
  81. 'answer_verified' => true,
  82. ]);
  83. $updatedCount++;
  84. }
  85. }
  86. // Call LearningAnalytics API
  87. $client = new \App\Services\LearningAnalyticsClient();
  88. $results = $client->analyze($record);
  89. if ($results === null) {
  90. Notification::make()
  91. ->title('分析失败')
  92. ->body('无法连接到分析服务或服务返回错误,请检查日志。')
  93. ->danger()
  94. ->send();
  95. return;
  96. }
  97. $aiUpdated = 0;
  98. // LearningAnalyticsClient已经处理了数据更新,这里只需要更新记录状态
  99. if (is_array($results) && count($results) > 0) {
  100. $aiUpdated = count($results);
  101. // 更新记录状态为已分析
  102. $record->update([
  103. 'ai_analyzed_at' => now(),
  104. 'ai_analysis_count' => $aiUpdated,
  105. ]);
  106. // 重新检查分析结果状态
  107. $this->checkAnalysisResults($record);
  108. }
  109. Notification::make()
  110. ->title('分析完成')
  111. ->body("已更新 {$updatedCount} 道题的答案,AI 分析完成 {$aiUpdated} 道题目")
  112. ->success()
  113. ->send();
  114. }
  115. public function startRecognition(): void
  116. {
  117. $record = $this->record();
  118. if (! $record) {
  119. Notification::make()
  120. ->title('记录不存在')
  121. ->danger()
  122. ->send();
  123. return;
  124. }
  125. if ($record->status === 'processing') {
  126. Notification::make()
  127. ->title('正在处理中')
  128. ->warning()
  129. ->send();
  130. return;
  131. }
  132. ProcessOCRRecord::dispatch($record);
  133. $record->update(['status' => 'processing']);
  134. Notification::make()
  135. ->title('开始识别')
  136. ->body('OCR识别任务已启动,请稍后刷新查看结果')
  137. ->success()
  138. ->send();
  139. }
  140. public function getStatusBadgeConfig(string $status): array
  141. {
  142. return match ($status) {
  143. 'pending' => ['class' => 'badge-warning', 'text' => '待处理'],
  144. 'processing' => ['class' => 'badge-info', 'text' => '处理中'],
  145. 'completed' => ['class' => 'badge-success', 'text' => '已完成'],
  146. 'failed' => ['class' => 'badge-error', 'text' => '失败'],
  147. default => ['class' => 'badge-ghost', 'text' => $status],
  148. };
  149. }
  150. }