get('/pre-questions', [PreQuestionApiController::class, 'index']) ->name('api.pre-questions.index'); Route::get('/questions/search', QuestionSearchController::class)->name('api.questions.search'); Route::get('/questions/random', QuestionRandomController::class)->name('api.questions.random'); Route::post('/papers/assemble', PaperAssembleController::class)->name('api.papers.assemble'); Route::get('/papers/{paperId}/json', [PaperJsonController::class, 'show'])->name('api.papers.json'); Route::get('/questions/{id}/solution', QuestionSolutionController::class)->name('api.questions.solution'); Route::get('/knowledge/recommend', KnowledgeRecommendController::class)->name('api.knowledge.recommend'); Route::post('/abilities/evaluate', AbilityEvaluateController::class)->name('api.abilities.evaluate'); // 接收题目生成回调 Route::post('/questions/callback', function () { try { $data = request()->all(); Log::info('Received question generation callback', $data); // 验证回调数据 if (!isset($data['task_id']) || !isset($data['status'])) { return response()->json(['error' => 'Invalid callback data'], 400); } // 处理回调数据并存储通知到session if ($data['status'] === 'completed') { $result = $data['result'] ?? []; $total = $result['total'] ?? $data['total'] ?? ($result['saved'] ?? 0); $kpCode = $result['kp_code'] ?? $data['kp_code'] ?? ''; // 将成功通知存储到session,供下次页面刷新时显示 session()->flash('notification', [ 'type' => 'success', 'title' => '✅ 题目生成完成', 'body' => "任务 ID: {$data['task_id']}\n生成题目: {$total} 道" . ($kpCode ? "\n知识点: {$kpCode}" : ''), 'color' => 'success' ]); Log::info("题目生成成功通知已存储", [ 'task_id' => $data['task_id'], 'total' => $total, 'kp_code' => $kpCode ]); } elseif ($data['status'] === 'failed') { $error = $data['error'] ?? '未知错误'; // 将失败通知存储到session session()->flash('notification', [ 'type' => 'error', 'title' => '❌ 题目生成失败', 'body' => "任务 ID: {$data['task_id']}\n错误: {$error}", 'color' => 'danger' ]); Log::error("题目生成失败通知已存储", [ 'task_id' => $data['task_id'], 'error' => $error ]); } return response()->json([ 'success' => true, 'message' => 'Callback received and notification stored', 'status' => $data['status'] ]); } catch (\Exception $e) { Log::error('Callback processing failed: ' . $e->getMessage()); return response()->json(['error' => $e->getMessage()], 500); } })->name('api.questions.callback'); // 接收OCR题目生成回调 Route::post('/ocr-question-callback', function () { try { $data = request()->all(); Log::info('Received OCR question generation callback', $data); // 验证必要的回调数据 if (!isset($data['task_id']) || !isset($data['status']) || !isset($data['ocr_record_id'])) { Log::error('OCR callback missing required fields', $data); return response()->json([ 'success' => false, 'error' => 'Missing required fields: task_id, status, ocr_record_id' ], 400); } $taskId = $data['task_id']; $ocrRecordId = $data['ocr_record_id']; $status = $data['status']; // 将回调结果存储到缓存中,供前端查询(保留30秒) $cacheKey = "ocr_callback_{$ocrRecordId}_{$taskId}"; cache([$cacheKey => $data], now()->addSeconds(30)); Log::info("OCR callback cached with key: {$cacheKey}", [ 'ocr_record_id' => $ocrRecordId, 'task_id' => $taskId, 'status' => $status, 'total_generated' => $data['result']['total_generated'] ?? 0, 'total_saved' => $data['result']['total_saved'] ?? 0 ]); // 处理题目关联逻辑 if ($status === 'completed') { $updatedCount = 0; // 从result中提取question_mappings(QuestionBank API将它放在result字段中) $mappings = $data['result']['question_mappings'] ?? $data['question_mappings'] ?? []; Log::info("Processing OCR question associations", [ 'ocr_record_id' => $ocrRecordId, 'task_id' => $taskId, 'mappings_count' => count($mappings) ]); // 更新ocr_question_results表中的关联关系 foreach ($mappings as $mapping) { try { $ocrQuestionNumber = $mapping['ocr_question_number'] ?? null; $questionBankId = $mapping['question_bank_id'] ?? null; $questionCode = $mapping['question_code'] ?? null; if ($ocrQuestionNumber && $questionBankId) { // 查找对应的OCR题目结果并更新 $updated = DB::table('ocr_question_results') ->where('ocr_record_id', $ocrRecordId) ->where('question_number', $ocrQuestionNumber) ->update([ 'question_bank_id' => $questionBankId, 'generation_status' => 'completed', 'generation_task_id' => $taskId, 'generation_error' => null, ]); if ($updated) { $updatedCount++; Log::info("Updated OCR question association", [ 'ocr_record_id' => $ocrRecordId, 'question_number' => $ocrQuestionNumber, 'question_bank_id' => $questionBankId, 'question_code' => $questionCode ]); } else { Log::warning("No OCR question result found for association", [ 'ocr_record_id' => $ocrRecordId, 'question_number' => $ocrQuestionNumber ]); } } } catch (\Exception $e) { Log::error("Failed to update OCR question association", [ 'mapping' => $mapping, 'error' => $e->getMessage() ]); } } Log::info("OCR question association completed", [ 'ocr_record_id' => $ocrRecordId, 'task_id' => $taskId, 'total_mappings' => count($mappings), 'updated_count' => $updatedCount ]); // 更新OCR记录的整体状态为已完成 try { DB::table('ocr_records') ->where('id', $ocrRecordId) ->update([ 'status' => 'completed', 'processed_at' => now(), 'updated_at' => now() ]); Log::info("Updated OCR record status to completed", [ 'ocr_record_id' => $ocrRecordId, 'task_id' => $taskId ]); } catch (\Exception $e) { Log::error("Failed to update OCR record status", [ 'ocr_record_id' => $ocrRecordId, 'error' => $e->getMessage() ]); } } elseif ($status === 'failed') { // 更新所有相关的OCR题目结果为失败状态 try { $updated = DB::table('ocr_question_results') ->where('ocr_record_id', $ocrRecordId) ->where('generation_status', 'pending') // 只更新待处理的 ->update([ 'generation_status' => 'failed', 'generation_task_id' => $taskId, 'generation_error' => $data['error'] ?? 'Unknown error', ]); Log::info("Updated OCR questions to failed status", [ 'ocr_record_id' => $ocrRecordId, 'task_id' => $taskId, 'updated_count' => $updated, 'error' => $data['error'] ?? 'Unknown error' ]); // 更新OCR记录的状态为失败 DB::table('ocr_records') ->where('id', $ocrRecordId) ->update([ 'status' => 'failed', 'error_message' => $data['error'] ?? 'Question generation failed', 'updated_at' => now() ]); Log::info("Updated OCR record status to failed", [ 'ocr_record_id' => $ocrRecordId, 'task_id' => $taskId, 'error' => $data['error'] ?? 'Unknown error' ]); } catch (\Exception $e) { Log::error("Failed to update OCR questions to failed status", [ 'ocr_record_id' => $ocrRecordId, 'error' => $e->getMessage() ]); } } return response()->json([ 'success' => true, 'message' => 'OCR callback received and processed', 'data' => [ 'task_id' => $taskId, 'ocr_record_id' => $ocrRecordId, 'status' => $status, 'cache_key' => $cacheKey, 'associations_processed' => $status === 'completed' ? count($data['question_mappings'] ?? []) : 0 ] ]); } catch (\Exception $e) { Log::error('OCR callback processing failed: ' . $e->getMessage()); Log::error('Exception details: ' . $e->getTraceAsString()); return response()->json([ 'success' => false, 'error' => 'Callback processing failed: ' . $e->getMessage() ], 500); } })->name('api.ocr.callback'); // 获取题目生成回调结果 Route::get('/questions/callback/{taskId}', function (string $taskId) { // ✅ 优先从缓存读取(跨域友好) $callbackData = cache($taskId); if ($callbackData) { // 清除已读取的回调数据 cache()->forget($taskId); session()->forget('question_gen_callback_' . $taskId); return response()->json($callbackData); } // 备选:从session读取 $sessionData = session('question_gen_callback_' . $taskId); if ($sessionData) { // 清除已读取的回调数据 session()->forget('question_gen_callback_' . $taskId); return response()->json($sessionData); } // 未收到回调 return response()->json(['status' => 'pending'], 202); })->name('api.questions.callback.get'); // 获取OCR题目生成回调结果 Route::get('/ocr-question-callback/{ocrRecordId}/{taskId}', function (int $ocrRecordId, string $taskId) { $cacheKey = "ocr_callback_{$ocrRecordId}_{$taskId}"; $callbackData = cache($cacheKey); if ($callbackData) { // 清除已读取的回调数据 cache()->forget($cacheKey); return response()->json([ 'success' => true, 'data' => $callbackData ]); } return response()->json([ 'success' => false, 'status' => 'pending', 'message' => 'OCR callback not received yet' ], 202); })->name('api.ocr.callback.get'); // 题目相关 API Route::get('/questions', function (QuestionServiceApi $service) { try { $page = (int) request()->get('page', 1); $perPage = (int) request()->get('per_page', 25); $filters = [ 'kp_code' => request()->get('kp_code'), 'difficulty' => request()->get('difficulty'), 'search' => request()->get('search'), ]; $response = $service->listQuestions($page, $perPage, $filters); return response()->json($response); } catch (\Exception $e) { \Log::error('Failed to fetch questions: ' . $e->getMessage()); return response()->json([ 'data' => [], 'meta' => [ 'page' => 1, 'per_page' => 25, 'total' => 0, 'total_pages' => 0, ], 'error' => $e->getMessage(), ], 500); } }); // 获取题目统计信息 Route::get('/questions/statistics', function (QuestionServiceApi $service) { try { $stats = $service->getStatistics(); return response()->json($stats); } catch (\Exception $e) { \Log::error('Failed to get question statistics: ' . $e->getMessage()); return response()->json(['error' => $e->getMessage()], 500); } }); // 语义搜索题目 Route::post('/questions/search', function (QuestionServiceApi $service) { try { $data = request()->only(['query', 'limit']); $results = $service->searchQuestions($data['query'], $data['limit'] ?? 20); return response()->json($results); } catch (\Exception $e) { \Log::error('Question search failed: ' . $e->getMessage()); return response()->json(['error' => $e->getMessage()], 500); } }); // 获取单个题目详情 Route::get('/questions/{id}', function (int $id, QuestionServiceApi $service) { try { $question = $service->getQuestionById($id); if (!$question) { return response()->json(['error' => 'Question not found'], 404); } return response()->json($question); } catch (\Exception $e) { \Log::error("Failed to get question {$id}: " . $e->getMessage()); return response()->json(['error' => $e->getMessage()], 500); } }); // AI 生成题目 Route::post('/questions/generate', function (QuestionServiceApi $service) { try { $data = request()->only(['kp_code', 'keyword', 'count', 'strategy']); $result = $service->generateQuestions($data); return response()->json($result); } catch (\Exception $e) { \Log::error('Question generation failed: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 500); } }); // 删除题目 Route::delete('/questions/{id}', function (int $id, QuestionServiceApi $service) { try { $deleted = $service->deleteQuestion($id); return response()->json([ 'success' => $deleted, 'message' => $deleted ? 'Question deleted' : 'Failed to delete', ]); } catch (\Exception $e) { \Log::error("Failed to delete question {$id}: " . $e->getMessage()); return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 500); } }); use App\Http\Controllers\Api\KnowledgePointTreeController; // 获取知识点树形结构(从 MySQL 数据库) Route::get('/knowledge-points', [KnowledgePointTreeController::class, 'index']) ->name('api.knowledge-points.index'); // 智能出卷对外接口:生成试卷并返回PDF/判卷地址 Route::post('/intelligent-exams', [IntelligentExamController::class, 'store']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.intelligent-exams.store'); // 智能出卷任务状态查询 Route::get('/intelligent-exams/status/{taskId}', [IntelligentExamController::class, 'status']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.intelligent-exams.status'); // 学情报告对外接口:生成并返回学情报告 PDF Route::post('/exam-analysis/report', [ExamAnalysisApiController::class, 'store']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.exam-analysis.report'); // 学情报告任务状态查询 Route::get('/exam-analysis/status/{taskId}', [ExamAnalysisApiController::class, 'status']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.exam-analysis.status'); /* |-------------------------------------------------------------------------- | 错题本 API 路由 |-------------------------------------------------------------------------- */ // 获取错题列表 Route::get('/mistake-book', [MistakeBookController::class, 'listMistakes']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.list'); // 新增错题 Route::post('/mistake-book', [MistakeBookController::class, 'addMistake']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.create'); // 获取单条错题详情 Route::get('/mistake-book/{mistakeId}', [MistakeBookController::class, 'getMistakeDetail']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->whereNumber('mistakeId') ->name('api.mistake-book.detail'); // 获取错题统计概要 Route::get('/mistake-book/summary', [MistakeBookController::class, 'getSummary']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.summary'); // 获取错误模式分析 Route::get('/mistake-book/analytics/mistake-pattern', [MistakeBookController::class, 'getMistakePatterns']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.patterns'); // 收藏/取消收藏错题 Route::post('/mistake-book/{mistakeId}/favorite', [MistakeBookController::class, 'toggleFavorite']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.favorite'); // 标记错题已复习 Route::post('/mistake-book/{mistakeId}/review', [MistakeBookController::class, 'markReviewed']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.review'); // 加入重练清单 Route::post('/mistake-book/{mistakeId}/retry-list', [MistakeBookController::class, 'addToRetryList']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.retry-list'); // 推荐练习题 Route::post('/mistake-book/recommend-practice', [MistakeBookController::class, 'recommendPractice']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.recommend-practice'); // 获取错题本快照数据(仪表板用) Route::get('/mistake-book/snapshot', [MistakeBookController::class, 'getSnapshot']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.snapshot'); /* |-------------------------------------------------------------------------- | 错题复习状态管理 API 路由 |-------------------------------------------------------------------------- */ Route::post('/mistake-book/{mistakeId}/review-status', [MistakeBookController::class, 'updateReviewStatus']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.review-status.update'); Route::get('/mistake-book/{mistakeId}/review-status', [MistakeBookController::class, 'getReviewStatus']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.review-status.get'); Route::post('/mistake-book/{mistakeId}/increment-review', [MistakeBookController::class, 'incrementReview']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.increment-review'); Route::post('/mistake-book/{mistakeId}/reset-review', [MistakeBookController::class, 'resetReview']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.reset-review'); /* |-------------------------------------------------------------------------- | 错题批量操作 API 路由 |-------------------------------------------------------------------------- */ Route::post('/mistake-book/batch-operation', [MistakeBookController::class, 'batchOperation']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.batch-operation'); Route::post('/mistake-book/batch/mark-reviewed', [MistakeBookController::class, 'batchMarkReviewed']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.batch.mark-reviewed'); Route::post('/mistake-book/batch/mark-mastered', [MistakeBookController::class, 'batchMarkMastered']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.batch.mark-mastered'); Route::post('/mistake-book/batch/add-to-retry-list', [MistakeBookController::class, 'batchAddToRetryList']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.batch.add-to-retry-list'); Route::post('/mistake-book/batch/remove-from-retry-list', [MistakeBookController::class, 'batchRemoveFromRetryList']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.batch.remove-from-retry-list'); Route::post('/mistake-book/batch/set-error-type', [MistakeBookController::class, 'batchSetErrorType']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.batch.set-error-type'); Route::post('/mistake-book/batch/set-importance', [MistakeBookController::class, 'batchSetImportance']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.batch.set-importance'); Route::post('/mistake-book/batch/toggle-favorite', [MistakeBookController::class, 'batchToggleFavorite']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.mistake-book.batch.toggle-favorite'); /* |-------------------------------------------------------------------------- | 知识点掌握情况 API 路由 |-------------------------------------------------------------------------- */ use App\Http\Controllers\Api\KnowledgeMasteryController; // 获取学生知识点掌握情况统计 Route::get('/knowledge-mastery/stats/{studentId}', [KnowledgeMasteryController::class, 'stats']) ->where('studentId', '[0-9]+') // 限制为数字 ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.knowledge-mastery.stats'); // 获取学生知识点掌握摘要 Route::get('/knowledge-mastery/summary/{studentId}', [KnowledgeMasteryController::class, 'summary']) ->where('studentId', '[0-9]+') // 限制为数字 ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.knowledge-mastery.summary'); // 获取学生知识点图谱数据 Route::get('/knowledge-mastery/graph/{studentId}', [KnowledgeMasteryController::class, 'graph']) ->where('studentId', '[0-9]+') // 限制为数字 ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.knowledge-mastery.graph'); // 获取学生知识点图谱快照列表 Route::get('/knowledge-mastery/graph/snapshots/{studentId}', [KnowledgeMasteryController::class, 'graphSnapshots']) ->where('studentId', '[0-9]+') // 限制为数字 ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.knowledge-mastery.graph.snapshots'); // 获取学生知识点快照列表(简化路径) Route::get('/knowledge-mastery/snapshots/{studentId}', [KnowledgeMasteryController::class, 'snapshots']) ->where('studentId', '[0-9]+') // 限制为数字 ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.knowledge-mastery.snapshots'); // 创建知识点掌握度快照 Route::post('/knowledge-mastery/snapshot/{studentId}', [KnowledgeMasteryController::class, 'createSnapshot']) ->where('studentId', '[0-9]+') // 限制为数字 ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.knowledge-mastery.snapshot.create'); /* |-------------------------------------------------------------------------- | 教材管理 API 路由 |-------------------------------------------------------------------------- */ // 获取教材列表(按年级排序) Route::get('/textbooks', [TextbookApiController::class, 'index']) ->name('api.textbooks.index'); // 根据年级获取教材 Route::get('/textbooks/grade/{grade}', [TextbookApiController::class, 'getByGrade']) ->name('api.textbooks.by-grade'); // 获取教材系列列表(必须在 {id} 路由之前定义) Route::get('/textbooks/series', [TextbookApiController::class, 'getSeries']) ->name('api.textbooks.series'); // 获取年级枚举 Route::get('/textbooks/grades', [TextbookApiController::class, 'getGradeEnums']) ->name('api.textbooks.grades'); // 获取单个教材详情 Route::get('/textbooks/{id}', [TextbookApiController::class, 'show']) ->name('api.textbooks.show'); // 获取教材目录 Route::get('/textbooks/{id}/catalog', [TextbookApiController::class, 'getCatalog']) ->name('api.textbooks.catalog'); /* |-------------------------------------------------------------------------- | MathRecSys 集成 API 路由 |-------------------------------------------------------------------------- */ use App\Http\Controllers\Api\StudentController; // 健康检查 Route::get('/mathrecsys/health', [StudentController::class, 'checkServiceHealth'])->name('api.mathrecsys.health'); // 学生相关 API Route::prefix('mathrecsys/students')->name('api.mathrecsys.students.')->group(function () { // 获取学生完整信息 Route::get('{studentId}', [StudentController::class, 'show']) ->where('studentId', '[0-9]+') // 限制为数字 ->name('show'); // 获取个性化推荐 Route::get('{studentId}/recommendations', [StudentController::class, 'getRecommendations']) ->where('studentId', '[0-9]+') // 限制为数字 ->name('recommendations'); // 获取学习轨迹 Route::get('{studentId}/trajectory', [StudentController::class, 'getTrajectory']) ->where('studentId', '[0-9]+') // 限制为数字 ->name('trajectory'); // 获取学习建议 Route::get('{studentId}/suggestions', [StudentController::class, 'getSuggestions']) ->where('studentId', '[0-9]+') // 限制为数字 ->name('suggestions'); // 智能分析题目 Route::post('{studentId}/analyze', [StudentController::class, 'analyzeQuestion']) ->where('studentId', '[0-9]+') // 限制为数字 ->name('analyze'); // 更新掌握度 Route::put('{studentId}/mastery', [StudentController::class, 'updateMastery']) ->where('studentId', '[0-9]+') // 限制为数字 ->name('update-mastery'); }); // 班级分析 API Route::prefix('mathrecsys/classes')->name('api.mathrecsys.classes.')->group(function () { Route::get('{classId}/analysis', [StudentController::class, 'classAnalysis']) ->where('classId', '[0-9]+') // 限制为数字 ->name('analysis'); }); // 测试 API Route::get('/mathrecsys/test', function () { return response()->json([ 'success' => true, 'message' => 'MathRecSys API integration is working', 'timestamp' => now()->toISOString() ]); })->name('api.mathrecsys.test'); // 测试OCR题目生成API调用 Route::post('/test-ocr-generation', function () { try { $service = new \App\Services\QuestionBankService(); // 模拟前端传递的OCR题目数据 $questions = [ [ 'id' => 1, 'content' => '计算:2+3-4' ], [ 'id' => 2, 'content' => '解方程:x+5=10' ] ]; Log::info('开始测试OCR题目生成', [ 'questions_count' => count($questions), 'ocr_record_id' => 12 ]); // 使用异步API,系统自动生成回调URL $response = $service->generateQuestionsFromOcrAsync( $questions, '高一', '数学', 12, // OCR记录ID null, // 让系统自动生成回调URL 'api.ocr.callback' // 回调路由名称 ); Log::info('OCR题目生成响应', [ 'response' => $response, 'status' => $response['status'] ?? 'unknown', 'task_id' => $response['task_id'] ?? 'N/A' ]); return response()->json([ 'success' => true, 'message' => 'OCR题目生成测试完成', 'data' => $response ]); } catch (\Exception $e) { Log::error('测试OCR题目生成失败', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return response()->json([ 'success' => false, 'error' => $e->getMessage() ], 500); } })->name('api.test.ocr.generation'); /* |-------------------------------------------------------------------------- | 学生作答分析 API 路由 |-------------------------------------------------------------------------- */ // 提交学生作答结果 Route::post('/student-answers/analyze', [StudentAnswerAnalysisController::class, 'submitAnswers']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.student-answers.analyze'); // 查询分析任务状态 Route::get('/student-answers/analysis/status/{taskId}', [StudentAnswerAnalysisController::class, 'getAnalysisStatus']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.student-answers.analysis.status'); // 获取学生学习历史 Route::get('/student-answers/history/{studentId}', [StudentAnswerAnalysisController::class, 'getStudentLearningHistory']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.student-answers.history'); /* |-------------------------------------------------------------------------- | 考试答题分析 API 路由(步骤级分析) |-------------------------------------------------------------------------- */ use App\Http\Controllers\Api\ExamAnswerAnalysisController; use App\Http\Controllers\Api\PaperSubmitAnalysisController; use App\Http\Controllers\Api\HealthCheckController; // 分析考试答题数据 Route::post('/exam-answer-analysis', [ExamAnswerAnalysisController::class, 'analyze']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.exam-answer-analysis.analyze'); // 获取分析结果 Route::get('/exam-answer-analysis/{student_id}/{exam_id}', [ExamAnswerAnalysisController::class, 'getAnalysisResult']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->where('student_id', '.*') ->where('exam_id', '.*') ->name('api.exam-answer-analysis.result'); // 获取学生历史分析记录 Route::get('/exam-answer-analysis/history/{student_id}', [ExamAnswerAnalysisController::class, 'getHistory']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->where('student_id', '.*') ->name('api.exam-answer-analysis.history'); // 获取知识点掌握度趋势 Route::get('/exam-answer-analysis/mastery-trend/{student_id}', [ExamAnswerAnalysisController::class, 'getMasteryTrend']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->where('student_id', '.*') ->name('api.exam-answer-analysis.mastery-trend'); // 获取智能出卷推荐 Route::get('/exam-answer-analysis/smart-quiz/{student_id}', [ExamAnswerAnalysisController::class, 'getSmartQuizRecommendation']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->where('student_id', '.*') ->name('api.exam-answer-analysis.smart-quiz'); // 导出分析报告 Route::get('/exam-answer-analysis/export/{student_id}/{exam_id}', [ExamAnswerAnalysisController::class, 'export']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->where('student_id', '.*') ->where('exam_id', '.*') ->name('api.exam-answer-analysis.export'); // 批量分析多个学生的考试数据 Route::post('/exam-answer-analysis/batch', [ExamAnswerAnalysisController::class, 'batchAnalyze']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.exam-answer-analysis.batch'); Route::get('/tasks/status/{taskId}', function (string $taskId) { $task = app(\App\Services\TaskManager::class)->getTaskStatus($taskId); if (!$task) { return response()->json([ 'success' => false, 'message' => '任务不存在', ], 404); } return response()->json([ 'success' => true, 'data' => $task, ]); })->name('api.tasks.status'); /* |-------------------------------------------------------------------------- | 试卷提交分析 API 路由(前端提交答题数据) |-------------------------------------------------------------------------- */ // 提交试卷答题数据进行分析 Route::post('/paper-submit-analysis', [PaperSubmitAnalysisController::class, 'analyze']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.paper-submit-analysis.analyze'); // 获取试卷分析结果 Route::get('/paper-submit-analysis/{paperId}', [PaperSubmitAnalysisController::class, 'getResult']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.paper-submit-analysis.result'); /* |-------------------------------------------------------------------------- | 健康检查 API 路由 |-------------------------------------------------------------------------- */ // 检查系统健康状态 Route::get('/health', [HealthCheckController::class, 'index']) ->withoutMiddleware([ Authenticate::class, 'auth', 'auth:sanctum', 'auth:api', ]) ->name('api.health.index');