OCRRecordView.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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. foreach ($record->questions as $question) {
  32. if ($question->manual_answer) {
  33. $this->manualAnswers[$question->id] = $question->manual_answer;
  34. }
  35. }
  36. // 检查是否已有AI分析结果
  37. $this->checkAnalysisResults($record);
  38. }
  39. }
  40. /**
  41. * Check if record already has AI analysis results
  42. */
  43. private function checkAnalysisResults(OCRRecord $record): void
  44. {
  45. $this->hasAnalysisResults = $record->questions()
  46. ->whereNotNull('ai_score')
  47. ->orWhereNotNull('ai_feedback')
  48. ->exists();
  49. }
  50. /**
  51. * Submit all questions for AI analysis.
  52. * Updates manual answers in batch, then sends data to LearningAnalytics.
  53. */
  54. public function submitForAnalysis(): void
  55. {
  56. $record = $this->record();
  57. if (! $record) {
  58. Notification::make()
  59. ->title('记录不存在')
  60. ->danger()
  61. ->send();
  62. return;
  63. }
  64. $updatedCount = 0;
  65. foreach ($record->questions as $question) {
  66. $manualAnswer = $this->manualAnswers[$question->id] ?? null;
  67. if ($manualAnswer && trim($manualAnswer) !== '') {
  68. $question->update([
  69. 'manual_answer' => trim($manualAnswer),
  70. 'answer_verified' => true,
  71. ]);
  72. $updatedCount++;
  73. }
  74. }
  75. // Call LearningAnalytics API
  76. $client = new \App\Services\LearningAnalyticsClient();
  77. $results = $client->analyze($record);
  78. $aiUpdated = 0;
  79. // LearningAnalyticsClient已经处理了数据更新,这里只需要更新记录状态
  80. if (is_array($results) && count($results) > 0) {
  81. $aiUpdated = count($results);
  82. // 更新记录状态为已分析
  83. $record->update([
  84. 'ai_analyzed_at' => now(),
  85. 'ai_analysis_count' => $aiUpdated,
  86. ]);
  87. // 重新检查分析结果状态
  88. $this->checkAnalysisResults($record);
  89. }
  90. Notification::make()
  91. ->title('分析完成')
  92. ->body("已更新 {$updatedCount} 道题的答案,AI 分析完成 {$aiUpdated} 道题目")
  93. ->success()
  94. ->send();
  95. }
  96. public function startRecognition(): void
  97. {
  98. $record = $this->record();
  99. if (! $record) {
  100. Notification::make()
  101. ->title('记录不存在')
  102. ->danger()
  103. ->send();
  104. return;
  105. }
  106. if ($record->status === 'processing') {
  107. Notification::make()
  108. ->title('正在处理中')
  109. ->warning()
  110. ->send();
  111. return;
  112. }
  113. ProcessOCRRecord::dispatch($record);
  114. $record->update(['status' => 'processing']);
  115. Notification::make()
  116. ->title('开始识别')
  117. ->body('OCR识别任务已启动,请稍后刷新查看结果')
  118. ->success()
  119. ->send();
  120. }
  121. public function getStatusBadgeConfig(string $status): array
  122. {
  123. return match ($status) {
  124. 'pending' => ['class' => 'badge-warning', 'text' => '待处理'],
  125. 'processing' => ['class' => 'badge-info', 'text' => '处理中'],
  126. 'completed' => ['class' => 'badge-success', 'text' => '已完成'],
  127. 'failed' => ['class' => 'badge-error', 'text' => '失败'],
  128. default => ['class' => 'badge-ghost', 'text' => $status],
  129. };
  130. }
  131. }