StudentAnswerAnalysisController.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use App\Http\Controllers\Controller;
  4. use App\Services\TaskManager;
  5. use App\Services\LocalAIAnalysisService;
  6. use App\Services\StudentAnswerAnalysisService;
  7. use Illuminate\Http\JsonResponse;
  8. use Illuminate\Http\Request;
  9. use Illuminate\Support\Facades\Log;
  10. class StudentAnswerAnalysisController extends Controller
  11. {
  12. public function __construct(
  13. private readonly TaskManager $taskManager,
  14. private readonly LocalAIAnalysisService $aiAnalysisService,
  15. private readonly StudentAnswerAnalysisService $answerAnalysisService
  16. ) {}
  17. /**
  18. * 接收学生作答结果并进行分析
  19. *
  20. * @param Request $request
  21. * @return JsonResponse
  22. */
  23. public function submitAnswers(Request $request): JsonResponse
  24. {
  25. $data = $request->validate([
  26. 'paper_id' => 'required|string',
  27. 'student_id' => 'required|string',
  28. 'answers' => 'required|array',
  29. 'answers.*.question_id' => 'required|string',
  30. 'answers.*.question_number' => 'nullable|string',
  31. 'answers.*.is_correct' => 'required|boolean',
  32. 'answers.*.student_answer' => 'nullable|string',
  33. 'answers.*.correct_answer' => 'nullable|string',
  34. 'answers.*.score' => 'nullable|numeric',
  35. 'answers.*.max_score' => 'nullable|numeric',
  36. 'answers.*.step_scores' => 'nullable|array', // 简答题步骤得分
  37. 'answers.*.knowledge_point' => 'nullable|string',
  38. 'answers.*.question_type' => 'nullable|string',
  39. 'answer_time' => 'nullable|timestamp',
  40. 'submit_time' => 'nullable|timestamp',
  41. 'source_system' => 'nullable|string',
  42. 'callback_url' => 'nullable|url',
  43. ]);
  44. try {
  45. // 使用TaskManager创建异步任务
  46. $taskId = $this->taskManager->createTask(
  47. TaskManager::TASK_TYPE_ANALYSIS,
  48. array_merge($data, ['type' => 'answer_analysis'])
  49. );
  50. Log::info('StudentAnswerAnalysisController: 收到作答结果', [
  51. 'task_id' => $taskId,
  52. 'paper_id' => $data['paper_id'],
  53. 'student_id' => $data['student_id'],
  54. 'answer_count' => count($data['answers']),
  55. ]);
  56. // 触发后台分析处理
  57. $this->processAnswerAnalysis($taskId, $data);
  58. return response()->json([
  59. 'success' => true,
  60. 'message' => '作答结果已提交,正在分析中...',
  61. 'data' => [
  62. 'task_id' => $taskId,
  63. 'paper_id' => $data['paper_id'],
  64. 'student_id' => $data['student_id'],
  65. 'status' => 'processing',
  66. 'created_at' => now()->toISOString(),
  67. ],
  68. ]);
  69. } catch (\Exception $e) {
  70. Log::error('提交作答结果失败', [
  71. 'paper_id' => $data['paper_id'] ?? 'unknown',
  72. 'student_id' => $data['student_id'] ?? 'unknown',
  73. 'error' => $e->getMessage(),
  74. ]);
  75. return response()->json([
  76. 'success' => false,
  77. 'message' => '提交失败:' . $e->getMessage(),
  78. ], 500);
  79. }
  80. }
  81. /**
  82. * 查询分析任务状态
  83. */
  84. public function getAnalysisStatus(string $taskId): JsonResponse
  85. {
  86. try {
  87. $task = $this->taskManager->getTaskStatus($taskId);
  88. if (!$task) {
  89. return response()->json([
  90. 'success' => false,
  91. 'message' => '任务不存在',
  92. ], 404);
  93. }
  94. return response()->json([
  95. 'success' => true,
  96. 'data' => $task,
  97. ]);
  98. } catch (\Exception $e) {
  99. Log::error('查询分析状态失败', [
  100. 'task_id' => $taskId,
  101. 'error' => $e->getMessage(),
  102. ]);
  103. return response()->json([
  104. 'success' => false,
  105. 'message' => '查询失败:' . $e->getMessage(),
  106. ], 500);
  107. }
  108. }
  109. /**
  110. * 处理作答分析(后台任务)
  111. */
  112. private function processAnswerAnalysis(string $taskId, array $data): void
  113. {
  114. try {
  115. $this->taskManager->updateTaskProgress($taskId, 10, '正在保存作答记录...');
  116. // 保存作答记录到数据库
  117. $answerRecord = $this->answerAnalysisService->saveAnswerRecord($data);
  118. $this->taskManager->updateTaskProgress($taskId, 30, '正在分析每道题...');
  119. // 使用本地AI分析服务分析每道题
  120. $questionAnalyses = [];
  121. foreach ($data['answers'] as $answer) {
  122. // 获取题目内容(如果有)
  123. $questionText = $this->getQuestionText($answer['question_id']);
  124. // 构建分析数据
  125. $analysisData = [
  126. 'question_id' => $answer['question_id'],
  127. 'question_number' => $answer['question_number'] ?? null,
  128. 'question_text' => $questionText,
  129. 'student_answer' => $answer['student_answer'] ?? '',
  130. 'correct_answer' => $answer['correct_answer'] ?? '',
  131. 'score' => (float) ($answer['score'] ?? 0),
  132. 'max_score' => (float) ($answer['max_score'] ?? 10),
  133. 'kp_code' => $answer['knowledge_point'] ?? null,
  134. 'difficulty' => 0.5, // 默认难度
  135. ];
  136. // 调用AI分析
  137. $analysisResult = $this->aiAnalysisService->analyzeAnswer($analysisData);
  138. if ($analysisResult['success']) {
  139. $questionAnalyses[] = array_merge($analysisData, $analysisResult['data']);
  140. } else {
  141. Log::warning('AI分析失败,使用规则分析', [
  142. 'question_id' => $answer['question_id'],
  143. ]);
  144. $questionAnalyses[] = array_merge($analysisData, $analysisResult['data']);
  145. }
  146. }
  147. $this->taskManager->updateTaskProgress($taskId, 60, '正在保存分析结果...');
  148. // 准备分析结果数据
  149. $analysisData = [
  150. 'question_results' => $questionAnalyses,
  151. 'total_questions' => count($questionAnalyses),
  152. 'correct_count' => count(array_filter($questionAnalyses, fn($q) => $q['correct'] ?? false)),
  153. 'wrong_count' => count(array_filter($questionAnalyses, fn($q) => !($q['correct'] ?? true))),
  154. 'model_used' => $questionAnalyses[0]['model_used'] ?? 'unknown',
  155. ];
  156. // 保存分析结果
  157. $this->answerAnalysisService->saveAnalysisResults($answerRecord, $analysisData, $questionAnalyses);
  158. $this->taskManager->updateTaskProgress($taskId, 80, '正在生成掌握度快照...');
  159. // 生成掌握度快照
  160. $masterySnapshot = $this->answerAnalysisService->createMasterySnapshot(
  161. $data['student_id'],
  162. $data['paper_id'],
  163. $answerRecord['record_id']
  164. );
  165. // 标记任务完成
  166. $this->taskManager->markTaskCompleted($taskId, [
  167. 'answer_record_id' => $answerRecord['record_id'],
  168. 'analysis_id' => 'analysis_' . uniqid(),
  169. 'mastery_snapshot_id' => $masterySnapshot['snapshot_id'] ?? null,
  170. 'correct_count' => $answerRecord['correct_count'],
  171. 'wrong_count' => $answerRecord['wrong_count'],
  172. 'overall_mastery' => $masterySnapshot['overall_mastery'] ?? null,
  173. ]);
  174. Log::info('作答分析完成', [
  175. 'task_id' => $taskId,
  176. 'paper_id' => $data['paper_id'],
  177. 'student_id' => $data['student_id'],
  178. 'answer_record_id' => $answerRecord['record_id'],
  179. ]);
  180. // 发送回调通知
  181. $this->taskManager->sendCallback($taskId);
  182. } catch (\Exception $e) {
  183. Log::error('作答分析失败', [
  184. 'task_id' => $taskId,
  185. 'paper_id' => $data['paper_id'],
  186. 'student_id' => $data['student_id'],
  187. 'error' => $e->getMessage(),
  188. ]);
  189. $this->taskManager->markTaskFailed($taskId, $e->getMessage());
  190. }
  191. }
  192. /**
  193. * 获取题目文本内容
  194. */
  195. private function getQuestionText(string $questionId): string
  196. {
  197. try {
  198. // 这里可以调用 QuestionBankService 获取题目内容
  199. // 目前返回空字符串,让AI分析基于学生答案进行分析
  200. return '';
  201. } catch (\Exception $e) {
  202. Log::warning('获取题目文本失败', [
  203. 'question_id' => $questionId,
  204. 'error' => $e->getMessage(),
  205. ]);
  206. return '';
  207. }
  208. }
  209. /**
  210. * 获取学生学习历史
  211. */
  212. public function getStudentLearningHistory(string $studentId): JsonResponse
  213. {
  214. try {
  215. $history = $this->answerAnalysisService->getStudentLearningHistory($studentId);
  216. return response()->json([
  217. 'success' => true,
  218. 'data' => $history,
  219. ]);
  220. } catch (\Exception $e) {
  221. Log::error('获取学习历史失败', [
  222. 'student_id' => $studentId,
  223. 'error' => $e->getMessage(),
  224. ]);
  225. return response()->json([
  226. 'success' => false,
  227. 'message' => '获取失败:' . $e->getMessage(),
  228. ], 500);
  229. }
  230. }
  231. }