ChatGPTAnalysisService.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php
  2. namespace App\Services;
  3. use Illuminate\Support\Facades\Http;
  4. use Illuminate\Support\Facades\Log;
  5. use Illuminate\Support\Facades\DB;
  6. class ChatGPTAnalysisService
  7. {
  8. protected string $learningAnalyticsUrl;
  9. protected int $timeout;
  10. public function __construct()
  11. {
  12. $this->learningAnalyticsUrl = config('services.learning_analytics.base_url', env('LEARNING_ANALYTICS_URL', 'http://localhost:5016'));
  13. $this->timeout = 180; // ChatGPT分析超时
  14. }
  15. /**
  16. * 使用ChatGPT分析试卷图片
  17. *
  18. * @param string $paperId 试卷ID
  19. * @param string $imageUrl 试卷图片URL
  20. * @return array 分析结果
  21. */
  22. public function analyzeExamPaper(string $paperId, string $imageUrl): array
  23. {
  24. try {
  25. Log::info('开始ChatGPT分析', [
  26. 'paper_id' => $paperId,
  27. 'image_url' => $imageUrl
  28. ]);
  29. // 获取题目数据
  30. $questionsData = $this->getPaperQuestions($paperId);
  31. if (empty($questionsData)) {
  32. throw new \Exception('未找到试卷题目');
  33. }
  34. // 调用LearningAnalytics的ChatGPT分析API
  35. $requestData = [
  36. 'paper_id' => $paperId,
  37. 'questions' => $questionsData,
  38. 'image_url' => $imageUrl,
  39. 'student_id' => $this->getStudentId($paperId),
  40. ];
  41. Log::info('调用LearningAnalytics ChatGPT分析API', [
  42. 'url' => $this->learningAnalyticsUrl . '/api/v1/chatgpt/analyze',
  43. 'question_count' => count($questionsData)
  44. ]);
  45. $response = Http::timeout($this->timeout)
  46. ->post($this->learningAnalyticsUrl . '/api/v1/chatgpt/analyze', $requestData);
  47. if (!$response->successful()) {
  48. throw new \Exception('LearningAnalytics API调用失败: ' . $response->body());
  49. }
  50. $responseData = $response->json();
  51. Log::info('ChatGPT分析完成', [
  52. 'paper_id' => $paperId,
  53. 'questions_count' => count($responseData['questions'] ?? []),
  54. 'success' => true
  55. ]);
  56. return [
  57. 'success' => true,
  58. 'data' => $responseData,
  59. ];
  60. } catch (\Exception $e) {
  61. Log::error('ChatGPT分析失败', [
  62. 'paper_id' => $paperId,
  63. 'error' => $e->getMessage(),
  64. 'trace' => $e->getTraceAsString()
  65. ]);
  66. return [
  67. 'success' => false,
  68. 'error' => $e->getMessage(),
  69. 'data' => null
  70. ];
  71. }
  72. }
  73. /**
  74. * 获取试卷题目
  75. */
  76. private function getPaperQuestions(string $paperId): array
  77. {
  78. try {
  79. // 处理OCR记录
  80. if (str_starts_with($paperId, 'ocr_')) {
  81. $recordId = substr($paperId, 4);
  82. $ocrQuestions = DB::table('ocr_question_results')
  83. ->where('ocr_record_id', $recordId)
  84. ->orderBy('question_number')
  85. ->get()
  86. ->map(function ($q) {
  87. // 优先使用人工校准的答案
  88. $studentAnswer = !empty(trim($q->manual_answer ?? ''))
  89. ? trim($q->manual_answer)
  90. : trim($q->student_answer ?? '');
  91. return [
  92. 'question_number' => $q->question_number,
  93. 'question_id' => $q->question_number,
  94. 'content' => $q->question_text,
  95. 'correct_answer' => '', // ChatGPT会从图片识别
  96. 'knowledge_point' => $q->kp_code,
  97. 'question_type' => 'unknown',
  98. 'score' => $q->score_total ?? 5
  99. ];
  100. })
  101. ->toArray();
  102. return $ocrQuestions;
  103. }
  104. // 处理系统生成卷子
  105. $questions = DB::table('paper_questions')
  106. ->where('paper_id', $paperId)
  107. ->orderBy('question_number')
  108. ->get()
  109. ->map(function ($q) {
  110. return [
  111. 'question_number' => $q->question_number,
  112. 'question_id' => $q->question_bank_id,
  113. 'content' => $q->question_text,
  114. 'correct_answer' => '', // ChatGPT会从图片识别
  115. 'knowledge_point' => $q->knowledge_point,
  116. 'question_type' => $q->question_type,
  117. 'score' => $q->score
  118. ];
  119. })
  120. ->toArray();
  121. return $questions;
  122. } catch (\Exception $e) {
  123. Log::error('获取试卷题目失败', [
  124. 'paper_id' => $paperId,
  125. 'error' => $e->getMessage()
  126. ]);
  127. return [];
  128. }
  129. }
  130. /**
  131. * 获取学生ID
  132. */
  133. private function getStudentId(string $paperId): ?string
  134. {
  135. try {
  136. // 处理OCR记录
  137. if (str_starts_with($paperId, 'ocr_')) {
  138. $recordId = substr($paperId, 4);
  139. $record = DB::table('ocr_records')->where('id', $recordId)->first();
  140. return $record->student_id ?? null;
  141. }
  142. // 处理系统生成卷子
  143. $paper = DB::table('papers')->where('paper_id', $paperId)->first();
  144. return $paper->student_id ?? null;
  145. } catch (\Exception $e) {
  146. Log::error('获取学生ID失败', [
  147. 'paper_id' => $paperId,
  148. 'error' => $e->getMessage()
  149. ]);
  150. return null;
  151. }
  152. }
  153. /**
  154. * 保存ChatGPT分析结果到数据库
  155. */
  156. public function saveAnalysisResult(string $paperId, array $analysisData): bool
  157. {
  158. try {
  159. return DB::transaction(function () use ($paperId, $analysisData) {
  160. // 更新papers表的analysis_id
  161. DB::table('papers')
  162. ->where('paper_id', $paperId)
  163. ->update([
  164. 'analysis_id' => $analysisData['analysis_id'] ?? ('chatgpt_' . uniqid()),
  165. 'updated_at' => now()
  166. ]);
  167. // 保存详细分析结果到answer_analysis表
  168. if (isset($analysisData['questions'])) {
  169. foreach ($analysisData['questions'] as $questionData) {
  170. $questionNumber = $questionData['q'] ?? null;
  171. if (!$questionNumber) continue;
  172. // 查找对应的paper_question记录
  173. $paperQuestion = null;
  174. if (str_starts_with($paperId, 'ocr_')) {
  175. // OCR记录使用question_number作为ID
  176. $paperQuestion = (object)[
  177. 'question_bank_id' => 'ocr_q' . $questionNumber,
  178. 'score' => 5
  179. ];
  180. } else {
  181. $paperQuestion = DB::table('paper_questions')
  182. ->where('paper_id', $paperId)
  183. ->where('question_number', $questionNumber)
  184. ->first();
  185. }
  186. if ($paperQuestion) {
  187. DB::table('answer_analysis')->updateOrInsert(
  188. [
  189. 'paper_id' => $paperId,
  190. 'question_id' => $paperQuestion->question_bank_id,
  191. 'question_number' => $questionNumber,
  192. ],
  193. [
  194. 'student_answer' => $questionData['student_answer'] ?? null,
  195. 'correct_answer' => $questionData['correct_answer'] ?? null,
  196. 'is_correct' => $questionData['is_correct'] ?? false,
  197. 'score_obtained' => $questionData['is_correct'] ? ($paperQuestion->score ?? 0) : 0,
  198. 'max_score' => $paperQuestion->score ?? 0,
  199. 'analysis_result' => json_encode($questionData, JSON_UNESCAPED_UNICODE),
  200. 'updated_at' => now()
  201. ]
  202. );
  203. }
  204. }
  205. }
  206. Log::info('ChatGPT分析结果保存成功', [
  207. 'paper_id' => $paperId,
  208. 'questions_count' => count($analysisData['questions'] ?? [])
  209. ]);
  210. return true;
  211. });
  212. } catch (\Exception $e) {
  213. Log::error('保存ChatGPT分析结果失败', [
  214. 'paper_id' => $paperId,
  215. 'error' => $e->getMessage()
  216. ]);
  217. return false;
  218. }
  219. }
  220. }