ExamAnalysisApiController.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use App\Http\Controllers\Controller;
  4. use App\Services\ExamAnalysisService;
  5. use App\Services\TaskManager;
  6. use Illuminate\Http\JsonResponse;
  7. use Illuminate\Http\Request;
  8. use Illuminate\Support\Facades\DB;
  9. use Illuminate\Support\Facades\Log;
  10. class ExamAnalysisApiController extends Controller
  11. {
  12. public function __construct(
  13. private readonly ExamAnalysisService $examAnalysisService,
  14. private readonly TaskManager $taskManager
  15. ) {}
  16. /**
  17. * 生成学情报告(异步模式)
  18. * 立即返回任务ID,PDF生成在后台进行
  19. */
  20. public function store(Request $request): JsonResponse
  21. {
  22. $data = $request->validate([
  23. 'paper_id' => 'required|string',
  24. 'student_id' => 'nullable|string',
  25. 'record_id' => 'nullable|string',
  26. 'callback_url' => 'nullable|url',
  27. ]);
  28. $paperId = $data['paper_id'];
  29. $studentId = $data['student_id'] ?? null;
  30. $recordId = $data['record_id'] ?? null;
  31. try {
  32. // 如果没有提供student_id,尝试从paper_id获取
  33. if (!$studentId) {
  34. $studentId = $this->getStudentIdFromPaper($paperId);
  35. }
  36. if (!$studentId) {
  37. return response()->json([
  38. 'success' => false,
  39. 'message' => '缺少 student_id',
  40. ], 422);
  41. }
  42. // 使用ExamAnalysisService生成报告
  43. $taskId = $this->examAnalysisService->generateReport($paperId, $studentId, $recordId);
  44. // 构建返回数据
  45. $payload = [
  46. 'success' => true,
  47. 'message' => '学情报告任务已创建,正在后台生成PDF...',
  48. 'data' => [
  49. 'task_id' => $taskId,
  50. 'paper_id' => $paperId,
  51. 'student_id' => $studentId,
  52. 'record_id' => $recordId,
  53. 'status' => 'processing',
  54. 'analysis_url' => route('filament.admin.pages.exam-analysis', [
  55. 'paperId' => $paperId,
  56. 'studentId' => $studentId,
  57. 'recordId' => $recordId,
  58. ]),
  59. 'pdf_url' => null, // 稍后生成
  60. 'pdf_poll_interval_seconds' => 2,
  61. 'created_at' => now()->toISOString(),
  62. ],
  63. ];
  64. return response()->json($payload, 200, [], JSON_UNESCAPED_SLASHES);
  65. } catch (\Exception $e) {
  66. Log::error('学情报告API失败', [
  67. 'paper_id' => $paperId,
  68. 'student_id' => $studentId,
  69. 'record_id' => $recordId,
  70. 'error' => $e->getMessage(),
  71. ]);
  72. return response()->json([
  73. 'success' => false,
  74. 'message' => '服务异常:' . $e->getMessage(),
  75. ], 500);
  76. }
  77. }
  78. /**
  79. * 轮询任务状态
  80. */
  81. public function status(string $taskId): JsonResponse
  82. {
  83. try {
  84. $task = $this->taskManager->getTaskStatus($taskId);
  85. if (!$task) {
  86. return response()->json([
  87. 'success' => false,
  88. 'message' => '任务不存在',
  89. ], 404);
  90. }
  91. return response()->json([
  92. 'success' => true,
  93. 'data' => $task,
  94. ]);
  95. } catch (\Exception $e) {
  96. Log::error('查询学情报告任务状态失败', [
  97. 'task_id' => $taskId,
  98. 'error' => $e->getMessage(),
  99. ]);
  100. return response()->json([
  101. 'success' => false,
  102. 'message' => '查询失败:' . $e->getMessage(),
  103. ], 500);
  104. }
  105. }
  106. /**
  107. * 获取PDF报告URL
  108. * 查询指定试卷是否已有生成好的学情分析报告
  109. */
  110. public function getPdfUrl(string $paperId): JsonResponse
  111. {
  112. try {
  113. // 从 exam_analysis_results 表查询PDF URL
  114. $report = DB::table('exam_analysis_results')
  115. ->where('paper_id', $paperId)
  116. ->first();
  117. if ($report && !empty($report->analysis_pdf_url)) {
  118. return response()->json([
  119. 'success' => true,
  120. 'data' => [
  121. 'paper_id' => $paperId,
  122. 'status' => 'completed',
  123. 'pdf_url' => $report->analysis_pdf_url,
  124. 'message' => '报告已生成',
  125. 'generated_at' => $report->created_at ?? null,
  126. ]
  127. ], 200, [], JSON_UNESCAPED_SLASHES);
  128. }
  129. return response()->json([
  130. 'success' => true,
  131. 'data' => [
  132. 'paper_id' => $paperId,
  133. 'status' => 'not_found',
  134. 'pdf_url' => null,
  135. 'retry_after_seconds' => 2,
  136. 'message' => '报告尚未生成,请先提交试卷进行分析',
  137. ]
  138. ], 200, [], JSON_UNESCAPED_SLASHES);
  139. } catch (\Exception $e) {
  140. Log::error('查询学情报告PDF URL失败', [
  141. 'paper_id' => $paperId,
  142. 'error' => $e->getMessage(),
  143. ]);
  144. return response()->json([
  145. 'success' => false,
  146. 'message' => '查询失败:' . $e->getMessage(),
  147. ], 500);
  148. }
  149. }
  150. /**
  151. * 从试卷ID获取学生ID
  152. */
  153. private function getStudentIdFromPaper(string $paperId): ?string
  154. {
  155. try {
  156. $paper = \App\Models\Paper::find($paperId);
  157. return $paper?->student_id;
  158. } catch (\Exception $e) {
  159. Log::warning('获取试卷学生ID失败', [
  160. 'paper_id' => $paperId,
  161. 'error' => $e->getMessage(),
  162. ]);
  163. return null;
  164. }
  165. }
  166. }