yemeishu 5 дней назад
Родитель
Сommit
35579d5944

+ 6 - 6
app/Http/Controllers/Api/ExamAnswerAnalysisController.php

@@ -90,7 +90,7 @@ class ExamAnswerAnalysisController extends Controller
     public function getAnalysisResult(string $studentId, string $examId): JsonResponse
     {
         try {
-            $result = \DB::connection('pgsql')
+            $result = \DB::connection('mysql')
                 ->table('exam_analysis_results')
                 ->where('student_id', $studentId)
                 ->where('exam_id', $examId)
@@ -138,7 +138,7 @@ class ExamAnswerAnalysisController extends Controller
             $limit = $request->input('limit', 10);
             $offset = $request->input('offset', 0);
 
-            $results = \DB::connection('pgsql')
+            $results = \DB::connection('mysql')
                 ->table('exam_analysis_results')
                 ->where('student_id', $studentId)
                 ->orderBy('created_at', 'desc')
@@ -159,7 +159,7 @@ class ExamAnswerAnalysisController extends Controller
                 'pagination' => [
                     'limit' => $limit,
                     'offset' => $offset,
-                    'total' => \DB::connection('pgsql')
+                    'total' => \DB::connection('mysql')
                         ->table('exam_analysis_results')
                         ->where('student_id', $studentId)
                         ->count()
@@ -193,7 +193,7 @@ class ExamAnswerAnalysisController extends Controller
         try {
             $kpCode = $request->input('kp_code');
 
-            $query = \DB::connection('pgsql')
+            $query = \DB::connection('mysql')
                 ->table('exam_analysis_results')
                 ->where('student_id', $studentId)
                 ->orderBy('created_at', 'desc');
@@ -251,7 +251,7 @@ class ExamAnswerAnalysisController extends Controller
     {
         try {
             // 获取最近一次分析结果
-            $latestAnalysis = \DB::connection('pgsql')
+            $latestAnalysis = \DB::connection('mysql')
                 ->table('exam_analysis_results')
                 ->where('student_id', $studentId)
                 ->orderBy('created_at', 'desc')
@@ -309,7 +309,7 @@ class ExamAnswerAnalysisController extends Controller
         try {
             $format = $request->input('format', 'json'); // json, pdf
 
-            $result = \DB::connection('pgsql')
+            $result = \DB::connection('mysql')
                 ->table('exam_analysis_results')
                 ->where('student_id', $studentId)
                 ->where('exam_id', $examId)

+ 7 - 6
app/Http/Controllers/Api/IntelligentExamController.php

@@ -49,17 +49,17 @@ class IntelligentExamController extends Controller
 
         $validator = validator($normalized, [
             'student_id' => 'required|string',
-            'teacher_id' => 'required|string',
+            'teacher_id' => 'nullable|string',
             'paper_name' => 'nullable|string|max:255',
             'grade' => 'nullable|string|max:50',
-            'total_questions' => 'required|integer|min:6|max:100',
+            'total_questions' => 'nullable|integer|min:6|max:100',
             'difficulty_category' => 'nullable|string',
             'kp_codes' => 'nullable|array',
             'kp_codes.*' => 'string',
-            'skills' => 'array',
+            'skills' => 'nullable|array',
             'skills.*' => 'string',
-            'question_type_ratio' => 'array',
-            'difficulty_ratio' => 'array',
+            'question_type_ratio' => 'nullable|array',
+            'difficulty_ratio' => 'nullable|array',
             'total_score' => 'nullable|numeric|min:1|max:1000',
             'mistake_ids' => 'nullable|array',
             'mistake_ids.*' => 'string',
@@ -76,6 +76,7 @@ class IntelligentExamController extends Controller
         }
 
         $data = $validator->validated();
+        $data['total_questions'] = $data['total_questions'] ?? 20;
 
         // 确保 kp_codes 是数组
         $data['kp_codes'] = $data['kp_codes'] ?? [];
@@ -156,7 +157,7 @@ class IntelligentExamController extends Controller
             $paperId = $this->questionBankService->saveExamToDatabase([
                 'paper_name' => $paperName,
                 'student_id' => $data['student_id'],
-                'teacher_id' => $data['teacher_id'],
+                'teacher_id' => $data['teacher_id'] ?? null,
                 'difficulty_category' => $difficultyCategory,
                 'total_score' => $data['total_score'] ?? $totalScore,
                 'questions' => $questions,

+ 452 - 0
app/Http/Controllers/Api/PaperSubmitAnalysisController.php

@@ -0,0 +1,452 @@
+<?php
+
+namespace App\Http\Controllers\Api;
+
+use App\Http\Controllers\Controller;
+use App\Models\Paper;
+use App\Models\PaperQuestion;
+use App\Services\ExamAnswerAnalysisService;
+use App\Services\MistakeBookService;
+use App\Services\QuestionBankService;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Validator;
+
+/**
+ * 试卷提交分析控制器
+ *
+ * 接收前端提交的试卷答题数据,进行学情分析并写入错题本
+ */
+class PaperSubmitAnalysisController extends Controller
+{
+    public function __construct(
+        private readonly ExamAnswerAnalysisService $analysisService,
+        private readonly MistakeBookService $mistakeBookService,
+        private readonly QuestionBankService $questionBankService
+    ) {}
+
+    /**
+     * 提交试卷答题数据进行分析
+     *
+     * POST /api/paper-submit-analysis
+     *
+     * 请求格式:
+     * {
+     *     "paperId": "paper_661325736792",
+     *     "questions": [
+     *         {
+     *             "question_bank_id": 876,
+     *             "student_answer": "C",
+     *             "is_correct": [0],           // 0=错, 1=对;简答题分步骤 [1,0,1]
+     *             "teacher_comment": null
+     *         }
+     *     ]
+     * }
+     */
+    public function analyze(Request $request): JsonResponse
+    {
+        try {
+            // 1. 验证请求数据
+            $validator = Validator::make($request->all(), [
+                'paperId' => 'required|string|max:255',
+                'questions' => 'required|array|min:1',
+                'questions.*.question_bank_id' => 'required|integer',
+                'questions.*.student_answer' => 'nullable|string',
+                'questions.*.is_correct' => 'required|array|min:1',
+                'questions.*.is_correct.*' => 'integer|in:0,1',
+                'questions.*.teacher_comment' => 'nullable|string',
+            ]);
+
+            if ($validator->fails()) {
+                return response()->json([
+                    'success' => false,
+                    'error' => '参数验证失败',
+                    'details' => $validator->errors()
+                ], 422);
+            }
+
+            $paperId = $request->input('paperId');
+            $questionsData = $request->input('questions');
+
+            Log::info('开始处理试卷提交分析', [
+                'paper_id' => $paperId,
+                'questions_count' => count($questionsData)
+            ]);
+
+            // 2. 通过 paperId 查询试卷获取 student_id
+            $paper = Paper::where('paper_id', $paperId)->first();
+            if (!$paper) {
+                return response()->json([
+                    'success' => false,
+                    'error' => '试卷不存在',
+                    'paper_id' => $paperId
+                ], 404);
+            }
+
+            $studentId = $paper->student_id;
+            if (!$studentId) {
+                return response()->json([
+                    'success' => false,
+                    'error' => '试卷未关联学生',
+                    'paper_id' => $paperId
+                ], 400);
+            }
+
+            // 3. 获取题目详情并转换数据格式
+            $transformedQuestions = $this->transformQuestionsData($questionsData, $paperId);
+
+            // 4. 调用学情分析服务(可能失败,不影响错题本写入)
+            $analysisResult = [];
+            $analysisError = null;
+            try {
+                $examData = [
+                    'exam_id' => $paperId,
+                    'student_id' => $studentId,
+                    'questions' => $transformedQuestions['questions'],
+                ];
+
+                $analysisResult = $this->analysisService->analyzeExamAnswers($examData);
+            } catch (\Exception $e) {
+                $analysisError = $e->getMessage();
+                Log::warning('学情分析失败,继续处理错题本', [
+                    'paper_id' => $paperId,
+                    'error' => $analysisError
+                ]);
+            }
+
+            // 5. 将错题写入错题本
+            $mistakesAdded = $this->addMistakesToBook(
+                $studentId,
+                $paperId,
+                $questionsData,
+                $transformedQuestions['question_details']
+            );
+
+            // 6. 更新试卷状态为已完成
+            $this->updatePaperStatus($paperId, $questionsData);
+
+            Log::info('试卷提交分析完成', [
+                'paper_id' => $paperId,
+                'student_id' => $studentId,
+                'questions_analyzed' => count($transformedQuestions['questions']),
+                'mistakes_added' => $mistakesAdded
+            ]);
+
+            return response()->json([
+                'success' => true,
+                'data' => [
+                    'paper_id' => $paperId,
+                    'student_id' => $studentId,
+                    'analysis_summary' => $analysisResult['overall_summary'] ?? null,
+                    'knowledge_point_analysis' => $analysisResult['knowledge_point_analysis'] ?? [],
+                    'smart_quiz_recommendation' => $analysisResult['smart_quiz_recommendation'] ?? null,
+                    'mastery_vector' => $analysisResult['mastery_vector'] ?? [],
+                    'mistakes_added' => $mistakesAdded,
+                    'total_questions' => count($questionsData),
+                    'correct_count' => $this->countCorrectQuestions($questionsData),
+                    'incorrect_count' => $mistakesAdded,
+                    'analysis_error' => $analysisError, // 如果学情分析失败,返回错误信息
+                ],
+                'message' => "分析完成,新增 {$mistakesAdded} 条错题记录" . ($analysisError ? "(学情分析暂不可用)" : "")
+            ]);
+
+        } catch (\Exception $e) {
+            Log::error('试卷提交分析失败', [
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString(),
+                'request_data' => $request->all()
+            ]);
+
+            return response()->json([
+                'success' => false,
+                'error' => '分析失败:' . $e->getMessage()
+            ], 500);
+        }
+    }
+
+    /**
+     * 转换前端数据格式为分析服务所需格式
+     */
+    private function transformQuestionsData(array $questionsData, string $paperId): array
+    {
+        $transformedQuestions = [];
+        $questionDetails = [];
+        $questionBankIds = array_column($questionsData, 'question_bank_id');
+
+        // 批量获取题目详情
+        $questionBankData = $this->fetchQuestionBankData($questionBankIds);
+
+        // 获取试卷中题目的分数信息
+        $paperQuestions = PaperQuestion::where('paper_id', $paperId)
+            ->whereIn('question_bank_id', $questionBankIds)
+            ->get()
+            ->keyBy('question_bank_id');
+
+        foreach ($questionsData as $index => $questionData) {
+            $questionBankId = $questionData['question_bank_id'];
+            $isCorrectArray = $questionData['is_correct'];
+            $studentAnswer = $questionData['student_answer'] ?? null;
+            $teacherComment = $questionData['teacher_comment'] ?? null;
+
+            // 获取题目详情
+            $qbData = $questionBankData[$questionBankId] ?? null;
+            $paperQuestion = $paperQuestions->get($questionBankId);
+
+            // 确定题目分数
+            $maxScore = $paperQuestion->score ?? ($qbData['score'] ?? 5.0);
+            $kpCode = $qbData['kp_code'] ?? ($paperQuestion->knowledge_point ?? 'K-GENERAL');
+            $kpName = $qbData['kp_name'] ?? $kpCode;
+            $questionType = $qbData['question_type'] ?? ($paperQuestion->question_type ?? 'unknown');
+
+            // 保存详情供后续使用
+            $questionDetails[$questionBankId] = [
+                'question_bank_id' => $questionBankId,
+                'stem' => $qbData['stem'] ?? ($paperQuestion->question_text ?? ''),
+                'answer' => $qbData['answer'] ?? ($paperQuestion->correct_answer ?? ''),
+                'solution' => $qbData['solution'] ?? ($paperQuestion->solution ?? ''),
+                'kp_code' => $kpCode,
+                'kp_name' => $kpName,
+                'question_type' => $questionType,
+                'max_score' => $maxScore,
+                'skills' => $qbData['skills'] ?? [],
+            ];
+
+            // 转换 is_correct 数组为 steps 格式
+            $steps = [];
+            $totalSteps = count($isCorrectArray);
+            $scorePerStep = $maxScore / $totalSteps;
+            $scoreObtained = 0;
+
+            foreach ($isCorrectArray as $stepIndex => $isCorrect) {
+                $stepCorrect = (bool) $isCorrect;
+                if ($stepCorrect) {
+                    $scoreObtained += $scorePerStep;
+                }
+
+                $steps[] = [
+                    'step_index' => $stepIndex + 1,
+                    'is_correct' => $stepCorrect,
+                    'kp_id' => $kpCode,
+                    'score' => $scorePerStep,
+                    'weight' => 1.0,
+                ];
+            }
+
+            $transformedQuestions[] = [
+                'question_id' => (string) $questionBankId,
+                'score' => $maxScore,
+                'score_obtained' => round($scoreObtained, 2),
+                'steps' => $totalSteps > 1 ? $steps : [], // 单步骤不传steps
+            ];
+        }
+
+        return [
+            'questions' => $transformedQuestions,
+            'question_details' => $questionDetails,
+        ];
+    }
+
+    /**
+     * 批量获取题库题目详情
+     */
+    private function fetchQuestionBankData(array $questionBankIds): array
+    {
+        try {
+            $response = $this->questionBankService->getQuestionsByIds($questionBankIds);
+            $questions = $response['data'] ?? $response;
+
+            // 转换为以 id 为 key 的数组
+            $result = [];
+            foreach ($questions as $question) {
+                $id = $question['id'] ?? $question['question_id'] ?? null;
+                if ($id) {
+                    $result[$id] = $question;
+                }
+            }
+            return $result;
+        } catch (\Exception $e) {
+            Log::warning('获取题库详情失败,使用空数据', [
+                'error' => $e->getMessage(),
+                'question_bank_ids' => $questionBankIds
+            ]);
+            return [];
+        }
+    }
+
+    /**
+     * 将错题写入错题本
+     */
+    private function addMistakesToBook(
+        string $studentId,
+        string $paperId,
+        array $questionsData,
+        array $questionDetails
+    ): int {
+        $mistakesAdded = 0;
+
+        foreach ($questionsData as $questionData) {
+            $isCorrectArray = $questionData['is_correct'];
+
+            // 判断是否有错误(数组中存在0)
+            $hasError = in_array(0, $isCorrectArray, true);
+            if (!$hasError) {
+                continue;
+            }
+
+            $questionBankId = $questionData['question_bank_id'];
+            $detail = $questionDetails[$questionBankId] ?? [];
+
+            try {
+                $payload = [
+                    'student_id' => $studentId,
+                    'question_id' => $questionBankId,
+                    'paper_id' => $paperId,
+                    'my_answer' => $questionData['student_answer'] ?? '',
+                    'correct_answer' => $detail['answer'] ?? '',
+                    'question_text' => $detail['stem'] ?? '',
+                    'knowledge_point' => $detail['kp_name'] ?? '',
+                    'explanation' => $detail['solution'] ?? '',
+                    'kp_ids' => [$detail['kp_code'] ?? 'K-GENERAL'],
+                    'source' => "paper:{$paperId}",
+                    'happened_at' => now()->toISOString(),
+                ];
+
+                $result = $this->mistakeBookService->createMistake($payload);
+
+                // 如果不是重复记录,则计数
+                if (!($result['duplicate'] ?? false)) {
+                    $mistakesAdded++;
+                }
+
+                Log::debug('错题写入成功', [
+                    'student_id' => $studentId,
+                    'question_bank_id' => $questionBankId,
+                    'duplicate' => $result['duplicate'] ?? false
+                ]);
+
+            } catch (\Exception $e) {
+                Log::error('写入错题本失败', [
+                    'student_id' => $studentId,
+                    'question_bank_id' => $questionBankId,
+                    'error' => $e->getMessage()
+                ]);
+            }
+        }
+
+        return $mistakesAdded;
+    }
+
+    /**
+     * 更新试卷状态和题目作答信息
+     */
+    private function updatePaperStatus(string $paperId, array $questionsData): void
+    {
+        try {
+            // 更新试卷状态为已完成
+            Paper::where('paper_id', $paperId)->update([
+                'status' => 'completed',
+                'completed_at' => now(),
+            ]);
+
+            // 更新每道题目的作答信息
+            foreach ($questionsData as $questionData) {
+                $questionBankId = $questionData['question_bank_id'];
+                $isCorrectArray = $questionData['is_correct'];
+
+                // 计算得分比例
+                $correctCount = array_sum($isCorrectArray);
+                $totalSteps = count($isCorrectArray);
+                $scoreRatio = $totalSteps > 0 ? $correctCount / $totalSteps : 0;
+
+                // 判断是否全对
+                $isFullyCorrect = !in_array(0, $isCorrectArray, true);
+
+                PaperQuestion::where('paper_id', $paperId)
+                    ->where('question_bank_id', $questionBankId)
+                    ->update([
+                        'student_answer' => $questionData['student_answer'] ?? null,
+                        'is_correct' => $isFullyCorrect,
+                        'score_ratio' => $scoreRatio,
+                        'score_obtained' => DB::raw("score * {$scoreRatio}"),
+                        'teacher_comment' => $questionData['teacher_comment'] ?? null,
+                        'graded_at' => now(),
+                    ]);
+            }
+
+            Log::info('试卷状态更新完成', ['paper_id' => $paperId]);
+
+        } catch (\Exception $e) {
+            Log::error('更新试卷状态失败', [
+                'paper_id' => $paperId,
+                'error' => $e->getMessage()
+            ]);
+        }
+    }
+
+    /**
+     * 统计正确题目数量
+     */
+    private function countCorrectQuestions(array $questionsData): int
+    {
+        $correctCount = 0;
+        foreach ($questionsData as $questionData) {
+            $isCorrectArray = $questionData['is_correct'];
+            // 全对才算正确
+            if (!in_array(0, $isCorrectArray, true)) {
+                $correctCount++;
+            }
+        }
+        return $correctCount;
+    }
+
+    /**
+     * 获取试卷分析结果
+     *
+     * GET /api/paper-submit-analysis/{paperId}
+     */
+    public function getResult(string $paperId): JsonResponse
+    {
+        try {
+            $paper = Paper::where('paper_id', $paperId)->first();
+            if (!$paper) {
+                return response()->json([
+                    'success' => false,
+                    'error' => '试卷不存在'
+                ], 404);
+            }
+
+            // 从数据库获取分析结果
+            $result = DB::connection('pgsql')
+                ->table('exam_analysis_results')
+                ->where('exam_id', $paperId)
+                ->where('student_id', $paper->student_id)
+                ->orderBy('created_at', 'desc')
+                ->first();
+
+            if (!$result) {
+                return response()->json([
+                    'success' => false,
+                    'error' => '未找到分析结果,请先提交试卷进行分析'
+                ], 404);
+            }
+
+            return response()->json([
+                'success' => true,
+                'data' => json_decode($result->analysis_data, true)
+            ]);
+
+        } catch (\Exception $e) {
+            Log::error('获取试卷分析结果失败', [
+                'paper_id' => $paperId,
+                'error' => $e->getMessage()
+            ]);
+
+            return response()->json([
+                'success' => false,
+                'error' => '获取分析结果失败:' . $e->getMessage()
+            ], 500);
+        }
+    }
+}

+ 1 - 0
app/Models/MistakeRecord.php

@@ -17,6 +17,7 @@ class MistakeRecord extends Model
     protected $fillable = [
         'student_id',
         'question_id',
+        'paper_id',
         'source',
         'question_text',
         'student_answer',

+ 31 - 8
app/Services/ExamAnswerAnalysisService.php

@@ -116,10 +116,33 @@ class ExamAnswerAnalysisService
 
         // 从题库获取题目知识点映射
         try {
-            $response = $this->questionBankService->getQuestionsKnowledgeMapping($questionIds);
+            $response = $this->questionBankService->getQuestionsByIds($questionIds);
+            $questionsData = $response['data'] ?? $response;
+
+            foreach ($questionsData as $questionData) {
+                $questionId = $questionData['id'] ?? $questionData['question_id'];
+                if (!$questionId) continue;
+
+                // 提取知识点信息
+                $kpMapping = [];
+                if (!empty($questionData['kp_code'])) {
+                    $kpMapping[] = [
+                        'kp_id' => $questionData['kp_code'],
+                        'kp_name' => $questionData['kp_name'] ?? $questionData['kp_code'],
+                        'weight' => 1.0
+                    ];
+                } else {
+                    $kpMapping[] = [
+                        'kp_id' => 'K-GENERAL',
+                        'kp_name' => '综合',
+                        'weight' => 1.0
+                    ];
+                }
 
-            foreach ($response as $mapping) {
-                $mappings[$mapping['question_id']] = $mapping;
+                $mappings[$questionId] = [
+                    'question_id' => $questionId,
+                    'kp_mapping' => $kpMapping
+                ];
             }
         } catch (\Exception $e) {
             Log::warning('获取题目知识点映射失败,使用默认映射', [
@@ -242,7 +265,7 @@ class ExamAnswerAnalysisService
 
         foreach ($knowledgeMasteryVector as $kpId => $data) {
             // 获取历史掌握度
-            $historyMastery = DB::connection('pgsql')
+            $historyMastery = DB::connection('mysql')
                 ->table('student_knowledge_mastery')
                 ->where('student_id', $studentId)
                 ->where('kp_code', $kpId)
@@ -261,7 +284,7 @@ class ExamAnswerAnalysisService
             $newConfidence = $data['confidence'];
 
             // 保存到数据库
-            DB::connection('pgsql')
+            DB::connection('mysql')
                 ->table('student_knowledge_mastery')
                 ->updateOrInsert(
                     ['student_id' => $studentId, 'kp_code' => $kpId],
@@ -477,7 +500,7 @@ class ExamAnswerAnalysisService
             // 保存步骤级记录
             if (!empty($steps)) {
                 foreach ($steps as $step) {
-                    DB::connection('pgsql')->table('student_answer_steps')->insert([
+                    DB::connection('mysql')->table('student_answer_steps')->insert([
                         'student_id' => $studentId,
                         'exam_id' => $examId,
                         'question_id' => $questionId,
@@ -491,7 +514,7 @@ class ExamAnswerAnalysisService
                 }
             } else {
                 // 保存题目级记录
-                DB::connection('pgsql')->table('student_answer_questions')->insert([
+                DB::connection('mysql')->table('student_answer_questions')->insert([
                     'student_id' => $studentId,
                     'exam_id' => $examId,
                     'question_id' => $questionId,
@@ -509,7 +532,7 @@ class ExamAnswerAnalysisService
      */
     private function saveAnalysisResult(string $studentId, string $examId, array $result): void
     {
-        DB::connection('pgsql')->table('exam_analysis_results')->insert([
+        DB::connection('mysql')->table('exam_analysis_results')->insert([
             'student_id' => $studentId,
             'exam_id' => $examId,
             'analysis_data' => json_encode($result),

+ 37 - 12
app/Services/MistakeBookService.php

@@ -36,8 +36,13 @@ class MistakeBookService
     {
         $studentId = $payload['student_id'] ?? null;
         $questionId = $payload['question_id'] ?? null;
+        $paperId = $payload['paper_id'] ?? null;
         $myAnswer = $payload['my_answer'] ?? null;
         $correctAnswer = $payload['correct_answer'] ?? null;
+        $questionText = $payload['question_text'] ?? null;
+        $knowledgePoint = $payload['knowledge_point'] ?? null;
+        $explanation = $payload['explanation'] ?? null;
+        $kpIds = $payload['kp_ids'] ?? null;
         $source = $payload['source'] ?? MistakeRecord::SOURCE_PRACTICE;
         $happenedAt = $payload['happened_at'] ?? now();
 
@@ -46,13 +51,23 @@ class MistakeBookService
         }
 
         // 使用事务确保数据一致性
-        return \DB::transaction(function () use ($studentId, $questionId, $myAnswer, $correctAnswer, $source, $happenedAt) {
+        return \DB::transaction(function () use (
+            $studentId, $questionId, $paperId, $myAnswer, $correctAnswer,
+            $questionText, $knowledgePoint, $explanation, $kpIds, $source, $happenedAt
+        ) {
             // 检查是否已存在相同错题(避免重复)
-            $existingMistake = MistakeRecord::where('student_id', $studentId)
-                ->where('question_id', $questionId)
+            $query = MistakeRecord::where('student_id', $studentId)
                 ->where('source', $source)
-                ->where('created_at', '>=', now()->subDay())
-                ->first();
+                ->where('created_at', '>=', now()->subDay());
+
+            // 如果有 question_id,用它去重;否则用 question_text
+            if ($questionId) {
+                $query->where('question_id', $questionId);
+            } elseif ($questionText) {
+                $query->where('question_text', $questionText);
+            }
+
+            $existingMistake = $query->first();
 
             if ($existingMistake) {
                 return [
@@ -66,8 +81,13 @@ class MistakeBookService
             $mistake = MistakeRecord::create([
                 'student_id' => $studentId,
                 'question_id' => $questionId,
+                'paper_id' => $paperId,
                 'student_answer' => $myAnswer,
                 'correct_answer' => $correctAnswer,
+                'question_text' => $questionText,
+                'knowledge_point' => $knowledgePoint,
+                'explanation' => $explanation,
+                'kp_ids' => $kpIds,
                 'source' => $source,
                 'created_at' => $happenedAt,
                 'review_status' => MistakeRecord::REVIEW_STATUS_PENDING,
@@ -685,14 +705,19 @@ class MistakeBookService
      */
     private function clearCache(string|int $studentId): void
     {
-        $patterns = [
-            "mistake_book:list:*{$studentId}*",
-            "mistake_book:summary:{$studentId}",
-            "mistake_book:patterns:{$studentId}",
-        ];
+        try {
+            $patterns = [
+                "mistake_book:list:{$studentId}",
+                "mistake_book:summary:{$studentId}",
+                "mistake_book:patterns:{$studentId}",
+            ];
 
-        foreach ($patterns as $pattern) {
-            Cache::tags(['mistake_book', "student_{$studentId}"])->flush();
+            foreach ($patterns as $key) {
+                Cache::forget($key);
+            }
+        } catch (\Exception $e) {
+            // 缓存清除失败不影响主流程
+            Log::debug('缓存清除失败(可忽略)', ['error' => $e->getMessage()]);
         }
     }
 }

+ 27 - 0
routes/api.php

@@ -957,6 +957,7 @@ Route::get('/student-answers/history/{studentId}', [StudentAnswerAnalysisControl
 */
 
 use App\Http\Controllers\Api\ExamAnswerAnalysisController;
+use App\Http\Controllers\Api\PaperSubmitAnalysisController;
 
 // 分析考试答题数据
 Route::post('/exam-answer-analysis', [ExamAnswerAnalysisController::class, 'analyze'])
@@ -1049,3 +1050,29 @@ Route::get('/tasks/status/{taskId}', function (string $taskId) {
         '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');