|
@@ -53,12 +53,12 @@ class ExamAnswerAnalysisService
|
|
|
// 【公司要求】1. 获取学案基准难度(取自学案的difficulty_category)
|
|
// 【公司要求】1. 获取学案基准难度(取自学案的difficulty_category)
|
|
|
$examBaseDifficulty = $this->getExamBaseDifficulty($examData['paper_id'] ?? '');
|
|
$examBaseDifficulty = $this->getExamBaseDifficulty($examData['paper_id'] ?? '');
|
|
|
|
|
|
|
|
- // 2. 保存答题记录到数据库
|
|
|
|
|
- $this->saveExamAnswerRecords($examData);
|
|
|
|
|
-
|
|
|
|
|
- // 3. 获取题目知识点映射
|
|
|
|
|
|
|
+ // 2. 获取题目知识点映射(批量查询,避免N+1)
|
|
|
$questionMappings = $this->getQuestionKnowledgeMappings($questions);
|
|
$questionMappings = $this->getQuestionKnowledgeMappings($questions);
|
|
|
|
|
|
|
|
|
|
+ // 3. 保存答题记录到数据库(复用已查询的知识点映射)
|
|
|
|
|
+ $this->saveExamAnswerRecords($examData, $questionMappings);
|
|
|
|
|
+
|
|
|
// 【公司要求】4. 计算每个知识点的加权掌握度(传入学案基准难度)
|
|
// 【公司要求】4. 计算每个知识点的加权掌握度(传入学案基准难度)
|
|
|
// 核心算法:难度映射 → 权重计算 → 数值更新(newMastery = oldMastery + change)
|
|
// 核心算法:难度映射 → 权重计算 → 数值更新(newMastery = oldMastery + change)
|
|
|
$knowledgeMasteryVector = $this->calculateKnowledgeMasteryVector($questions, $questionMappings, $examBaseDifficulty, $studentId);
|
|
$knowledgeMasteryVector = $this->calculateKnowledgeMasteryVector($questions, $questionMappings, $examBaseDifficulty, $studentId);
|
|
@@ -725,39 +725,44 @@ class ExamAnswerAnalysisService
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 更新学生掌握度(与历史数据合并)
|
|
|
|
|
|
|
+ * 更新学生掌握度(优化版:批量查询+批量upsert)
|
|
|
*/
|
|
*/
|
|
|
private function updateStudentMastery(string $studentId, array $knowledgeMasteryVector): array
|
|
private function updateStudentMastery(string $studentId, array $knowledgeMasteryVector): array
|
|
|
{
|
|
{
|
|
|
|
|
+ if (empty($knowledgeMasteryVector)) {
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
$updatedMastery = [];
|
|
$updatedMastery = [];
|
|
|
|
|
+ $kpIds = array_keys($knowledgeMasteryVector);
|
|
|
|
|
+ $now = now();
|
|
|
|
|
|
|
|
- foreach ($knowledgeMasteryVector as $kpId => $data) {
|
|
|
|
|
- // 获取历史掌握度
|
|
|
|
|
- $historyMastery = DB::connection('mysql')
|
|
|
|
|
- ->table('student_knowledge_mastery')
|
|
|
|
|
- ->where('student_id', $studentId)
|
|
|
|
|
- ->where('kp_code', $kpId)
|
|
|
|
|
- ->first();
|
|
|
|
|
|
|
+ // 【优化】批量查询所有历史掌握度(1次查询代替N次)
|
|
|
|
|
+ $historyRecords = DB::connection('mysql')
|
|
|
|
|
+ ->table('student_knowledge_mastery')
|
|
|
|
|
+ ->where('student_id', $studentId)
|
|
|
|
|
+ ->whereIn('kp_code', $kpIds)
|
|
|
|
|
+ ->get()
|
|
|
|
|
+ ->keyBy('kp_code');
|
|
|
|
|
|
|
|
|
|
+ // 准备批量upsert的数据
|
|
|
|
|
+ $upsertData = [];
|
|
|
|
|
+ foreach ($knowledgeMasteryVector as $kpId => $data) {
|
|
|
|
|
+ $historyMastery = $historyRecords->get($kpId);
|
|
|
$historyMasteryLevel = $historyMastery->mastery_level ?? 0.0;
|
|
$historyMasteryLevel = $historyMastery->mastery_level ?? 0.0;
|
|
|
|
|
|
|
|
- // 【公司要求】保存到数据库(只保存核心掌握度数据)
|
|
|
|
|
- DB::connection('mysql')
|
|
|
|
|
- ->table('student_knowledge_mastery')
|
|
|
|
|
- ->updateOrInsert(
|
|
|
|
|
- ['student_id' => $studentId, 'kp_code' => $kpId],
|
|
|
|
|
- [
|
|
|
|
|
- 'mastery_level' => $data['mastery'],
|
|
|
|
|
- 'confidence_level' => 0.0, // 不再保存置信度
|
|
|
|
|
- 'total_attempts' => ($historyMastery->total_attempts ?? 0) + 1,
|
|
|
|
|
- 'correct_attempts' => ($historyMastery->correct_attempts ?? 0) + intval($data['correct_attempts'] > 0),
|
|
|
|
|
- 'mastery_trend' => 'stable', // 不再判断趋势,统一设为stable
|
|
|
|
|
- 'last_mastery_update' => now(),
|
|
|
|
|
- 'updated_at' => now(),
|
|
|
|
|
- ]
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ $upsertData[] = [
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'kp_code' => $kpId,
|
|
|
|
|
+ 'mastery_level' => $data['mastery'],
|
|
|
|
|
+ 'confidence_level' => 0.0,
|
|
|
|
|
+ 'total_attempts' => ($historyMastery->total_attempts ?? 0) + 1,
|
|
|
|
|
+ 'correct_attempts' => ($historyMastery->correct_attempts ?? 0) + intval($data['correct_attempts'] > 0),
|
|
|
|
|
+ 'mastery_trend' => 'stable',
|
|
|
|
|
+ 'last_mastery_update' => $now,
|
|
|
|
|
+ 'updated_at' => $now,
|
|
|
|
|
+ ];
|
|
|
|
|
|
|
|
- // 【公司要求】返回值:只返回核心掌握度数据
|
|
|
|
|
$updatedMastery[$kpId] = [
|
|
$updatedMastery[$kpId] = [
|
|
|
'kp_id' => $kpId,
|
|
'kp_id' => $kpId,
|
|
|
'current_mastery' => $data['mastery'],
|
|
'current_mastery' => $data['mastery'],
|
|
@@ -768,8 +773,19 @@ class ExamAnswerAnalysisService
|
|
|
];
|
|
];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 【优化】批量upsert(1次查询代替N次 updateOrInsert)
|
|
|
|
|
+ if (!empty($upsertData)) {
|
|
|
|
|
+ DB::connection('mysql')
|
|
|
|
|
+ ->table('student_knowledge_mastery')
|
|
|
|
|
+ ->upsert(
|
|
|
|
|
+ $upsertData,
|
|
|
|
|
+ ['student_id', 'kp_code'], // 唯一键
|
|
|
|
|
+ ['mastery_level', 'confidence_level', 'total_attempts', 'correct_attempts', 'mastery_trend', 'last_mastery_update', 'updated_at']
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 【修复】计算并更新父节点掌握度,同时添加到返回数组中
|
|
// 【修复】计算并更新父节点掌握度,同时添加到返回数组中
|
|
|
- $parentMasteryData = $this->updateParentMasteryLevels($studentId, array_keys($knowledgeMasteryVector));
|
|
|
|
|
|
|
+ $parentMasteryData = $this->updateParentMasteryLevels($studentId, $kpIds);
|
|
|
|
|
|
|
|
// 合并父节点数据到返回数组
|
|
// 合并父节点数据到返回数组
|
|
|
$updatedMastery = array_merge($updatedMastery, $parentMasteryData);
|
|
$updatedMastery = array_merge($updatedMastery, $parentMasteryData);
|
|
@@ -1299,13 +1315,18 @@ class ExamAnswerAnalysisService
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private function saveExamAnswerRecords(array $examData): void
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 保存答题记录(优化版:批量INSERT)
|
|
|
|
|
+ * @param array $examData 考试数据
|
|
|
|
|
+ * @param array $questionMappings 已批量查询的知识点映射,避免N+1
|
|
|
|
|
+ */
|
|
|
|
|
+ private function saveExamAnswerRecords(array $examData, array $questionMappings = []): void
|
|
|
{
|
|
{
|
|
|
$studentId = $examData['student_id'];
|
|
$studentId = $examData['student_id'];
|
|
|
$examId = $examData['paper_id'];
|
|
$examId = $examData['paper_id'];
|
|
|
|
|
+ $now = now();
|
|
|
|
|
|
|
|
// 先清理该考试的所有答题记录(支持重复提交)
|
|
// 先清理该考试的所有答题记录(支持重复提交)
|
|
|
- // delete() 方法即使没有匹配数据也不会报错,返回0
|
|
|
|
|
try {
|
|
try {
|
|
|
DB::connection('mysql')->table('student_answer_questions')
|
|
DB::connection('mysql')->table('student_answer_questions')
|
|
|
->where('student_id', $studentId)
|
|
->where('student_id', $studentId)
|
|
@@ -1324,176 +1345,139 @@ class ExamAnswerAnalysisService
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $stepsSavedCount = 0;
|
|
|
|
|
- $questionsSavedCount = 0;
|
|
|
|
|
|
|
+ // 【优化】收集所有要插入的记录,最后批量INSERT
|
|
|
|
|
+ $stepsToInsert = [];
|
|
|
|
|
+ $questionsToInsert = [];
|
|
|
|
|
+ $mistakesToSave = []; // 收集错题记录
|
|
|
|
|
|
|
|
foreach ($examData['questions'] as $question) {
|
|
foreach ($examData['questions'] as $question) {
|
|
|
- // 兼容两种字段名:question_id 或 question_bank_id
|
|
|
|
|
$questionId = $question['question_id'] ?? $question['question_bank_id'] ?? null;
|
|
$questionId = $question['question_id'] ?? $question['question_bank_id'] ?? null;
|
|
|
if (empty($questionId)) {
|
|
if (empty($questionId)) {
|
|
|
- Log::warning('题目缺少ID,跳过保存', ['question' => $question]);
|
|
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 从 is_correct 数组确定步骤数量
|
|
|
|
|
|
|
+ // 【优化】使用传入的映射,不再查询数据库
|
|
|
|
|
+ $kpMappings = $questionMappings[$questionId]['kp_mapping'] ?? [];
|
|
|
|
|
+
|
|
|
$isCorrectArray = $question['is_correct'] ?? [];
|
|
$isCorrectArray = $question['is_correct'] ?? [];
|
|
|
if (!is_array($isCorrectArray)) {
|
|
if (!is_array($isCorrectArray)) {
|
|
|
- // 兼容非数组情况,转换为数组
|
|
|
|
|
$isCorrectArray = [$isCorrectArray ? 1 : 0];
|
|
$isCorrectArray = [$isCorrectArray ? 1 : 0];
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
$stepCount = count($isCorrectArray);
|
|
$stepCount = count($isCorrectArray);
|
|
|
|
|
|
|
|
- // 获取该题目关联的知识点(可能多个)
|
|
|
|
|
- $kpMappings = $this->getQuestionKnowledgePointsFromDb($questionId);
|
|
|
|
|
-
|
|
|
|
|
if ($stepCount > 1 || !empty($kpMappings)) {
|
|
if ($stepCount > 1 || !empty($kpMappings)) {
|
|
|
- // 多步骤题目:保存步骤级记录
|
|
|
|
|
- // 每个步骤对应 is_correct 数组中的一个元素,先使用平均分来代替
|
|
|
|
|
|
|
+ // 多步骤题目:收集步骤记录
|
|
|
$scorePerStep = ($question['score'] ?? 0) / max($stepCount, 1);
|
|
$scorePerStep = ($question['score'] ?? 0) / max($stepCount, 1);
|
|
|
-
|
|
|
|
|
- // 【优化】先收集错误知识点,避免重复调用 saveMistakeRecord
|
|
|
|
|
$hasMistake = false;
|
|
$hasMistake = false;
|
|
|
$allErrorKpCodes = [];
|
|
$allErrorKpCodes = [];
|
|
|
|
|
|
|
|
foreach ($isCorrectArray as $stepIndex => $isCorrect) {
|
|
foreach ($isCorrectArray as $stepIndex => $isCorrect) {
|
|
|
$isCorrectBool = (int) $isCorrect === 1;
|
|
$isCorrectBool = (int) $isCorrect === 1;
|
|
|
|
|
|
|
|
- // 如果有知识点映射,为每个知识点保存记录
|
|
|
|
|
if (!empty($kpMappings)) {
|
|
if (!empty($kpMappings)) {
|
|
|
foreach ($kpMappings as $kpMapping) {
|
|
foreach ($kpMappings as $kpMapping) {
|
|
|
- try {
|
|
|
|
|
- // 【修复】先检查记录是否已存在,避免重复键错误
|
|
|
|
|
- $exists = DB::connection('mysql')->table('student_answer_steps')
|
|
|
|
|
- ->where('student_id', $studentId)
|
|
|
|
|
- ->where('exam_id', $examId)
|
|
|
|
|
- ->where('question_id', $questionId)
|
|
|
|
|
- ->where('step_index', $stepIndex)
|
|
|
|
|
- ->where('kp_id', $kpMapping['kp_id'])
|
|
|
|
|
- ->exists();
|
|
|
|
|
-
|
|
|
|
|
- if (!$exists) {
|
|
|
|
|
- DB::connection('mysql')->table('student_answer_steps')->insert([
|
|
|
|
|
- 'student_id' => $studentId,
|
|
|
|
|
- 'exam_id' => $examId,
|
|
|
|
|
- 'question_id' => $questionId,
|
|
|
|
|
- 'step_index' => $stepIndex,
|
|
|
|
|
- 'kp_id' => $kpMapping['kp_id'],
|
|
|
|
|
- 'is_correct' => $isCorrectBool ? 1 : 0,
|
|
|
|
|
- 'step_score' => $isCorrectBool ? $scorePerStep : 0,
|
|
|
|
|
- 'created_at' => now(),
|
|
|
|
|
- 'updated_at' => now(),
|
|
|
|
|
- ]);
|
|
|
|
|
- $stepsSavedCount++;
|
|
|
|
|
- Log::debug('步骤记录保存成功', [
|
|
|
|
|
- 'student_id' => $studentId,
|
|
|
|
|
- 'question_id' => $questionId,
|
|
|
|
|
- 'step_index' => $stepIndex,
|
|
|
|
|
- 'kp_id' => $kpMapping['kp_id'],
|
|
|
|
|
- ]);
|
|
|
|
|
- } else {
|
|
|
|
|
- Log::debug('步骤记录已存在,跳过保存', [
|
|
|
|
|
- 'student_id' => $studentId,
|
|
|
|
|
- 'question_id' => $questionId,
|
|
|
|
|
- 'step_index' => $stepIndex,
|
|
|
|
|
- 'kp_id' => $kpMapping['kp_id'],
|
|
|
|
|
- ]);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 收集错误知识点(不重复调用 saveMistakeRecord)
|
|
|
|
|
- if (!$isCorrectBool) {
|
|
|
|
|
- $hasMistake = true;
|
|
|
|
|
- $allErrorKpCodes[] = $kpMapping['kp_id'];
|
|
|
|
|
- }
|
|
|
|
|
- } catch (\Exception $e) {
|
|
|
|
|
- Log::warning('保存步骤记录失败', [
|
|
|
|
|
- 'student_id' => $studentId,
|
|
|
|
|
- 'question_id' => $questionId,
|
|
|
|
|
- 'step_index' => $stepIndex,
|
|
|
|
|
- 'kp_id' => $kpMapping['kp_id'],
|
|
|
|
|
- 'error' => $e->getMessage(),
|
|
|
|
|
- ]);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // 没有知识点映射,仍保存步骤但 kp_id 为空
|
|
|
|
|
- try {
|
|
|
|
|
- DB::connection('mysql')->table('student_answer_steps')->insert([
|
|
|
|
|
|
|
+ $stepsToInsert[] = [
|
|
|
'student_id' => $studentId,
|
|
'student_id' => $studentId,
|
|
|
'exam_id' => $examId,
|
|
'exam_id' => $examId,
|
|
|
'question_id' => $questionId,
|
|
'question_id' => $questionId,
|
|
|
'step_index' => $stepIndex,
|
|
'step_index' => $stepIndex,
|
|
|
- 'kp_id' => null,
|
|
|
|
|
|
|
+ 'kp_id' => $kpMapping['kp_id'],
|
|
|
'is_correct' => $isCorrectBool ? 1 : 0,
|
|
'is_correct' => $isCorrectBool ? 1 : 0,
|
|
|
'step_score' => $isCorrectBool ? $scorePerStep : 0,
|
|
'step_score' => $isCorrectBool ? $scorePerStep : 0,
|
|
|
- 'created_at' => now(),
|
|
|
|
|
- 'updated_at' => now(),
|
|
|
|
|
- ]);
|
|
|
|
|
- $stepsSavedCount++;
|
|
|
|
|
|
|
+ 'created_at' => $now,
|
|
|
|
|
+ 'updated_at' => $now,
|
|
|
|
|
+ ];
|
|
|
|
|
|
|
|
- // 标记有错误(无知识点映射)
|
|
|
|
|
if (!$isCorrectBool) {
|
|
if (!$isCorrectBool) {
|
|
|
$hasMistake = true;
|
|
$hasMistake = true;
|
|
|
|
|
+ $allErrorKpCodes[] = $kpMapping['kp_id'];
|
|
|
}
|
|
}
|
|
|
- } catch (\Exception $e) {
|
|
|
|
|
- Log::warning('保存步骤记录失败(无知识点)', [
|
|
|
|
|
- 'student_id' => $studentId,
|
|
|
|
|
- 'question_id' => $questionId,
|
|
|
|
|
- 'step_index' => $stepIndex,
|
|
|
|
|
- 'error' => $e->getMessage(),
|
|
|
|
|
- ]);
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $stepsToInsert[] = [
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'exam_id' => $examId,
|
|
|
|
|
+ 'question_id' => $questionId,
|
|
|
|
|
+ 'step_index' => $stepIndex,
|
|
|
|
|
+ 'kp_id' => null,
|
|
|
|
|
+ 'is_correct' => $isCorrectBool ? 1 : 0,
|
|
|
|
|
+ 'step_score' => $isCorrectBool ? $scorePerStep : 0,
|
|
|
|
|
+ 'created_at' => $now,
|
|
|
|
|
+ 'updated_at' => $now,
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ if (!$isCorrectBool) {
|
|
|
|
|
+ $hasMistake = true;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 【错题本】一次性保存错题记录(合并所有错误知识点)
|
|
|
|
|
|
|
+ // 收集错题记录
|
|
|
if ($hasMistake) {
|
|
if ($hasMistake) {
|
|
|
$uniqueKpCodes = array_values(array_unique($allErrorKpCodes));
|
|
$uniqueKpCodes = array_values(array_unique($allErrorKpCodes));
|
|
|
- // 转换为 kpMapping 数组格式:[[kp_id: '...'], [kp_id: '...']]
|
|
|
|
|
- $kpMappingArray = array_map(fn($code) => ['kp_id' => $code], $uniqueKpCodes);
|
|
|
|
|
- $this->saveMistakeRecord($studentId, $questionId, $examId, $question, $kpMappingArray);
|
|
|
|
|
|
|
+ $mistakesToSave[] = [
|
|
|
|
|
+ 'questionId' => $questionId,
|
|
|
|
|
+ 'question' => $question,
|
|
|
|
|
+ 'kpMappings' => array_map(fn($code) => ['kp_id' => $code], $uniqueKpCodes),
|
|
|
|
|
+ ];
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- // 单步骤题目:保存题目级记录
|
|
|
|
|
- $isQuestionCorrect = (($question['score_obtained'] ?? 0) > 0);
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- DB::connection('mysql')->table('student_answer_questions')->insert([
|
|
|
|
|
- 'student_id' => $studentId,
|
|
|
|
|
- 'exam_id' => $examId,
|
|
|
|
|
- 'question_id' => $questionId,
|
|
|
|
|
- 'score_obtained' => $question['score_obtained'] ?? 0,
|
|
|
|
|
- 'max_score' => $question['score'] ?? 0,
|
|
|
|
|
- 'created_at' => now(),
|
|
|
|
|
- 'updated_at' => now(),
|
|
|
|
|
- ]);
|
|
|
|
|
- $questionsSavedCount++;
|
|
|
|
|
|
|
+ // 单步骤题目:收集题目级记录
|
|
|
|
|
+ $questionsToInsert[] = [
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'exam_id' => $examId,
|
|
|
|
|
+ 'question_id' => $questionId,
|
|
|
|
|
+ 'score_obtained' => $question['score_obtained'] ?? 0,
|
|
|
|
|
+ 'max_score' => $question['score'] ?? 0,
|
|
|
|
|
+ 'created_at' => $now,
|
|
|
|
|
+ 'updated_at' => $now,
|
|
|
|
|
+ ];
|
|
|
|
|
|
|
|
- // 【错题本】保存错题记录(题目级错误)
|
|
|
|
|
- if (!$isQuestionCorrect) {
|
|
|
|
|
- // 收集所有知识点,转换为数组格式
|
|
|
|
|
- $kpMappingArray = !empty($kpMappings)
|
|
|
|
|
|
|
+ // 收集错题记录
|
|
|
|
|
+ if (($question['score_obtained'] ?? 0) <= 0) {
|
|
|
|
|
+ $mistakesToSave[] = [
|
|
|
|
|
+ 'questionId' => $questionId,
|
|
|
|
|
+ 'question' => $question,
|
|
|
|
|
+ 'kpMappings' => !empty($kpMappings)
|
|
|
? array_map(fn($m) => ['kp_id' => $m['kp_id']], $kpMappings)
|
|
? array_map(fn($m) => ['kp_id' => $m['kp_id']], $kpMappings)
|
|
|
- : null;
|
|
|
|
|
- $this->saveMistakeRecord($studentId, $questionId, $examId, $question, $kpMappingArray);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (\Exception $e) {
|
|
|
|
|
- Log::warning('保存题目级记录失败', [
|
|
|
|
|
- 'student_id' => $studentId,
|
|
|
|
|
- 'exam_id' => $examId,
|
|
|
|
|
- 'question_id' => $questionId,
|
|
|
|
|
- 'error' => $e->getMessage(),
|
|
|
|
|
- ]);
|
|
|
|
|
|
|
+ : null,
|
|
|
|
|
+ ];
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 【优化】批量INSERT(每500条一批,避免超过MySQL限制)
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!empty($stepsToInsert)) {
|
|
|
|
|
+ foreach (array_chunk($stepsToInsert, 500) as $chunk) {
|
|
|
|
|
+ DB::connection('mysql')->table('student_answer_steps')->insert($chunk);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!empty($questionsToInsert)) {
|
|
|
|
|
+ foreach (array_chunk($questionsToInsert, 500) as $chunk) {
|
|
|
|
|
+ DB::connection('mysql')->table('student_answer_questions')->insert($chunk);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (\Exception $e) {
|
|
|
|
|
+ Log::error('批量保存答题记录失败', [
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'exam_id' => $examId,
|
|
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 保存错题记录(错题本逻辑相对复杂,保持单条处理)
|
|
|
|
|
+ foreach ($mistakesToSave as $mistake) {
|
|
|
|
|
+ $this->saveMistakeRecord($studentId, $mistake['questionId'], $examId, $mistake['question'], $mistake['kpMappings']);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
Log::info('答题记录保存完成', [
|
|
Log::info('答题记录保存完成', [
|
|
|
'student_id' => $studentId,
|
|
'student_id' => $studentId,
|
|
|
'exam_id' => $examId,
|
|
'exam_id' => $examId,
|
|
|
'total_questions' => count($examData['questions']),
|
|
'total_questions' => count($examData['questions']),
|
|
|
- 'steps_saved' => $stepsSavedCount,
|
|
|
|
|
- 'questions_saved' => $questionsSavedCount,
|
|
|
|
|
|
|
+ 'steps_saved' => count($stepsToInsert),
|
|
|
|
|
+ 'questions_saved' => count($questionsToInsert),
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
|
|
|