LearningAnalyticsClient.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. <?php
  2. namespace App\Services;
  3. use App\Models\OCRRecord;
  4. use App\Models\OCRQuestionResult;
  5. use Illuminate\Support\Facades\Http;
  6. use Illuminate\Support\Facades\Log;
  7. class LearningAnalyticsClient
  8. {
  9. /**
  10. * Send OCR record data to LearningAnalytics for AI analysis.
  11. *
  12. * @param OCRRecord $record
  13. * @return array|null Returns array of results keyed by question id, or null on failure.
  14. */
  15. public function analyze(OCRRecord $record): ?array
  16. {
  17. $url = env('LEARNING_ANALYTICS_URL') . '/api/analysis/process-answers';
  18. $payload = [
  19. 'exam_id' => $record->exam_id ?? 'exam_' . $record->id,
  20. 'student_id' => $record->student_id ?? 'student_' . $record->id,
  21. 'ocr_record_id' => $record->id,
  22. 'teacher_name' => 'System', // 可以根据需要调整
  23. 'analysis_type' => 'mastery',
  24. 'questions' => $record->questions->map(function (OCRQuestionResult $q) {
  25. return [
  26. 'question_id' => $q->id,
  27. 'question_number' => (string)($q->question_number ?? $q->id),
  28. 'kp_code' => $q->kp_code,
  29. 'student_answer' => $q->manual_answer ?? $q->student_answer,
  30. 'correct_answer' => $q->correct_answer, // 如果有正确答案
  31. 'teacher_validated' => !is_null($q->manual_answer), // 手动答案表示已校验
  32. 'ocr_confidence' => $q->answer_confidence ?? 1.0,
  33. 'score_value' => $q->score_value,
  34. 'mark_detected' => $q->mark_detected,
  35. ];
  36. })->toArray(),
  37. ];
  38. try {
  39. $response = Http::timeout(30)->post($url, $payload);
  40. if ($response->successful()) {
  41. // Expected format: { "success": true, "data": { "question_results": [...] } }
  42. $data = $response->json();
  43. $questionResults = $data['data']['question_results'] ?? null;
  44. // Save analysis results back to database
  45. if ($questionResults) {
  46. $this->saveAnalysisResults($record, $questionResults);
  47. }
  48. return $questionResults;
  49. }
  50. Log::error('LearningAnalytics API error', ['status' => $response->status(), 'body' => $response->body()]);
  51. } catch (\Exception $e) {
  52. Log::error('LearningAnalytics request failed', ['exception' => $e]);
  53. }
  54. return null;
  55. }
  56. /**
  57. * Save AI analysis results back to OCR question results.
  58. *
  59. * @param OCRRecord $record
  60. * @param array $questionResults
  61. * @return void
  62. */
  63. private function saveAnalysisResults(OCRRecord $record, array $questionResults): void
  64. {
  65. foreach ($questionResults as $result) {
  66. $questionId = $result['question_id'] ?? null;
  67. if (!$questionId) continue;
  68. $questionResult = $record->questions()->where('id', $questionId)->first();
  69. if (!$questionResult) continue;
  70. // Extract relevant data from analysis result
  71. $aiScore = $result['score'] ?? null;
  72. $aiFeedback = $result['suggestions'] ?? $result['reason'] ?? null;
  73. $aiConfidence = $result['confidence'] ?? null;
  74. $aiAnalysisMethod = $result['analysis_method'] ?? null;
  75. // Update the question result with AI analysis data
  76. $questionResult->update([
  77. 'ai_score' => $aiScore,
  78. 'ai_feedback' => $aiFeedback,
  79. 'ai_confidence' => $aiConfidence,
  80. 'ai_analysis_method' => $aiAnalysisMethod,
  81. 'ai_analyzed_at' => now(),
  82. ]);
  83. }
  84. }
  85. }