|
|
@@ -1271,12 +1271,37 @@ class LearningAnalyticsService
|
|
|
file_put_contents($logFile, $logMsg, FILE_APPEND);
|
|
|
|
|
|
// 2. 调用题库API获取符合条件的所有题目
|
|
|
- $allQuestions = $this->getQuestionsFromBank($kpCodes, $skills, $studentId, $questionTypeRatio, $difficultyRatio, 200);
|
|
|
+ try {
|
|
|
+ Log::info('开始调用 getQuestionsFromBank', [
|
|
|
+ 'kp_codes_count' => count($kpCodes),
|
|
|
+ 'skills_count' => count($skills)
|
|
|
+ ]);
|
|
|
|
|
|
- $logMsg = "getQuestionsFromBank 返回\n";
|
|
|
- $logMsg .= "questions_count: " . count($allQuestions) . "\n";
|
|
|
- $logMsg .= "耗时: " . round((microtime(true) - $startTime) * 1000, 2) . "ms\n\n";
|
|
|
- file_put_contents($logFile, $logMsg, FILE_APPEND);
|
|
|
+ $allQuestions = $this->getQuestionsFromBank($kpCodes, $skills, $studentId, $questionTypeRatio, $difficultyRatio, 200);
|
|
|
+
|
|
|
+ Log::info('getQuestionsFromBank 调用完成', [
|
|
|
+ 'questions_count' => count($allQuestions),
|
|
|
+ 'is_array' => is_array($allQuestions),
|
|
|
+ 'first_question_id' => !empty($allQuestions) ? ($allQuestions[0]['id'] ?? 'N/A') : 'N/A'
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $logMsg = "getQuestionsFromBank 返回\n";
|
|
|
+ $logMsg .= "questions_count: " . count($allQuestions) . "\n";
|
|
|
+ $logMsg .= "耗时: " . round((microtime(true) - $startTime) * 1000, 2) . "ms\n\n";
|
|
|
+ file_put_contents($logFile, $logMsg, FILE_APPEND);
|
|
|
+
|
|
|
+ Log::info('getQuestionsFromBank 返回', [
|
|
|
+ 'questions_count' => count($allQuestions),
|
|
|
+ 'time_ms' => round((microtime(true) - $startTime) * 1000, 2)
|
|
|
+ ]);
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ Log::error('getQuestionsFromBank 调用失败', [
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ 'trace' => $e->getTraceAsString()
|
|
|
+ ]);
|
|
|
+
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
|
|
|
if (empty($allQuestions)) {
|
|
|
// 如果指定了知识点但题库为空,给出明确提示
|
|
|
@@ -1304,6 +1329,12 @@ class LearningAnalyticsService
|
|
|
}
|
|
|
|
|
|
// 3. 根据掌握度对题目进行筛选和排序
|
|
|
+ Log::info('开始调用 selectQuestionsByMastery', [
|
|
|
+ 'input_count' => count($allQuestions),
|
|
|
+ 'target_count' => $totalQuestions
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $startTime = microtime(true);
|
|
|
$selectedQuestions = $this->selectQuestionsByMastery(
|
|
|
$allQuestions,
|
|
|
$studentId,
|
|
|
@@ -1313,11 +1344,13 @@ class LearningAnalyticsService
|
|
|
$difficultyLevels,
|
|
|
$weaknessFilter
|
|
|
);
|
|
|
+ $selectTime = (microtime(true) - $startTime) * 1000;
|
|
|
|
|
|
Log::info('题目筛选结果', [
|
|
|
'input_count' => count($allQuestions),
|
|
|
'selected_count' => count($selectedQuestions),
|
|
|
- 'target_count' => $totalQuestions
|
|
|
+ 'target_count' => $totalQuestions,
|
|
|
+ 'select_time_ms' => round($selectTime, 2)
|
|
|
]);
|
|
|
|
|
|
if (empty($selectedQuestions)) {
|
|
|
@@ -1397,24 +1430,57 @@ class LearningAnalyticsService
|
|
|
$questions = $this->questionBankService->selectQuestionsForExam($totalNeeded, $filters);
|
|
|
|
|
|
if (!empty($questions)) {
|
|
|
- // 过滤掉没有解题思路的题目
|
|
|
- $questionsWithSolution = array_filter($questions, function($q) {
|
|
|
- $solution = $q['solution'] ?? '';
|
|
|
- // 处理 solution 可能是数组的情况
|
|
|
- if (is_array($solution)) {
|
|
|
- $solution = json_encode($solution, JSON_UNESCAPED_UNICODE);
|
|
|
+ Log::info('从题库获取到题目,开始过滤解题思路', [
|
|
|
+ 'total_from_bank' => count($questions),
|
|
|
+ 'filters' => $filters
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $filterStartTime = microtime(true);
|
|
|
+ $questionsWithSolution = [];
|
|
|
+ $noSolutionCount = 0;
|
|
|
+
|
|
|
+ foreach ($questions as $index => $q) {
|
|
|
+ try {
|
|
|
+ $solution = $q['solution'] ?? '';
|
|
|
+ // 处理 solution 可能是数组的情况
|
|
|
+ if (is_array($solution)) {
|
|
|
+ $solution = json_encode($solution, JSON_UNESCAPED_UNICODE);
|
|
|
+ }
|
|
|
+ if (!empty(trim($solution))) {
|
|
|
+ $questionsWithSolution[] = $q;
|
|
|
+ } else {
|
|
|
+ $noSolutionCount++;
|
|
|
+ }
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ Log::error('过滤解题思路时出错', [
|
|
|
+ 'question_index' => $index,
|
|
|
+ 'error' => $e->getMessage()
|
|
|
+ ]);
|
|
|
}
|
|
|
- return !empty(trim($solution));
|
|
|
- });
|
|
|
+ }
|
|
|
+
|
|
|
+ $filterTime = (microtime(true) - $filterStartTime) * 1000;
|
|
|
+ $hasSolutionCount = count($questionsWithSolution);
|
|
|
|
|
|
Log::info('从题库智能获取题目', [
|
|
|
'total_from_bank' => count($questions),
|
|
|
- 'has_solution' => count($questionsWithSolution),
|
|
|
- 'filtered_out' => count($questions) - count($questionsWithSolution),
|
|
|
+ 'has_solution' => $hasSolutionCount,
|
|
|
+ 'no_solution' => $noSolutionCount,
|
|
|
+ 'filter_time_ms' => round($filterTime, 2),
|
|
|
'filters' => $filters
|
|
|
]);
|
|
|
|
|
|
- return array_values($questionsWithSolution);
|
|
|
+ if ($hasSolutionCount > 0) {
|
|
|
+ Log::info('返回有解题思路的题目', [
|
|
|
+ 'count' => $hasSolutionCount
|
|
|
+ ]);
|
|
|
+ return array_values($questionsWithSolution);
|
|
|
+ } else {
|
|
|
+ Log::warning('所有题目都没有解题思路,返回空数组', [
|
|
|
+ 'total_questions' => count($questions)
|
|
|
+ ]);
|
|
|
+ return [];
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
Log::warning('智能选题返回空结果', [
|
|
|
@@ -1443,6 +1509,12 @@ class LearningAnalyticsService
|
|
|
array $difficultyLevels,
|
|
|
array $weaknessFilter
|
|
|
): array {
|
|
|
+ Log::info('selectQuestionsByMastery 开始', [
|
|
|
+ 'question_count' => count($questions),
|
|
|
+ 'student_id' => $studentId,
|
|
|
+ 'total_questions' => $totalQuestions
|
|
|
+ ]);
|
|
|
+
|
|
|
// 如果未选择难度,则不过滤(随机生成所有难度)
|
|
|
if (empty($difficultyLevels)) {
|
|
|
Log::info('用户未选择难度,将随机生成所有难度的题目');
|
|
|
@@ -1457,7 +1529,16 @@ class LearningAnalyticsService
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
+ Log::info('难度筛选完成', [
|
|
|
+ 'after_filter_count' => count($questions)
|
|
|
+ ]);
|
|
|
+
|
|
|
// 1. 按知识点分组
|
|
|
+ Log::info('开始按知识点分组', [
|
|
|
+ 'question_count' => count($questions)
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $groupStartTime = microtime(true);
|
|
|
$questionsByKp = [];
|
|
|
foreach ($questions as $question) {
|
|
|
$kpCode = $question['kp_code'] ?? '';
|
|
|
@@ -1466,13 +1547,53 @@ class LearningAnalyticsService
|
|
|
}
|
|
|
$questionsByKp[$kpCode][] = $question;
|
|
|
}
|
|
|
+ $groupTime = (microtime(true) - $groupStartTime) * 1000;
|
|
|
+
|
|
|
+ Log::info('按知识点分组完成', [
|
|
|
+ 'kp_count' => count($questionsByKp),
|
|
|
+ 'group_time_ms' => round($groupTime, 2)
|
|
|
+ ]);
|
|
|
|
|
|
// 2. 为每个知识点计算权重
|
|
|
$kpWeights = [];
|
|
|
- foreach (array_keys($questionsByKp) as $kpCode) {
|
|
|
+ $kpCodes = array_keys($questionsByKp);
|
|
|
+ Log::info('开始计算知识点权重', [
|
|
|
+ 'kp_count' => count($kpCodes),
|
|
|
+ 'student_id' => $studentId
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $startTime = microtime(true);
|
|
|
+ $allMastery = [];
|
|
|
+
|
|
|
+ if ($studentId) {
|
|
|
+ // 批量获取所有知识点的掌握度(一次查询)
|
|
|
+ $masteryStart = microtime(true);
|
|
|
+ try {
|
|
|
+ $masteryRecords = DB::table('student_mastery')
|
|
|
+ ->where('student_id', $studentId)
|
|
|
+ ->whereIn('kp', $kpCodes)
|
|
|
+ ->pluck('mastery', 'kp')
|
|
|
+ ->all();
|
|
|
+
|
|
|
+ Log::debug('批量获取掌握度', [
|
|
|
+ 'student_id' => $studentId,
|
|
|
+ 'kp_count' => count($kpCodes),
|
|
|
+ 'found_count' => count($masteryRecords),
|
|
|
+ 'time_ms' => round((microtime(true) - $masteryStart) * 1000, 2)
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $allMastery = $masteryRecords;
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ Log::warning('批量获取掌握度失败,将使用默认值', [
|
|
|
+ 'student_id' => $studentId,
|
|
|
+ 'error' => $e->getMessage()
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($kpCodes as $kpCode) {
|
|
|
if ($studentId) {
|
|
|
- // 获取学生对该知识点的掌握度
|
|
|
- $mastery = $this->getStudentKpMastery($studentId, $kpCode);
|
|
|
+ $mastery = $allMastery[$kpCode] ?? 0.5; // 默认0.5(中等掌握度)
|
|
|
|
|
|
// 薄弱点权重更高
|
|
|
if (in_array($kpCode, $weaknessFilter)) {
|
|
|
@@ -1481,11 +1602,24 @@ class LearningAnalyticsService
|
|
|
// 掌握度越低,权重越高
|
|
|
$kpWeights[$kpCode] = 1.0 + (1.0 - $mastery) * 1.5;
|
|
|
}
|
|
|
+
|
|
|
+ Log::debug('计算知识点权重', [
|
|
|
+ 'kp_code' => $kpCode,
|
|
|
+ 'mastery' => $mastery,
|
|
|
+ 'weight' => $kpWeights[$kpCode]
|
|
|
+ ]);
|
|
|
} else {
|
|
|
$kpWeights[$kpCode] = 1.0; // 未指定学生时平均分配
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ $totalWeightTime = (microtime(true) - $startTime) * 1000;
|
|
|
+ Log::info('知识点权重计算完成', [
|
|
|
+ 'total_kp_count' => count($kpCodes),
|
|
|
+ 'total_weight_time_ms' => round($totalWeightTime, 2),
|
|
|
+ 'avg_time_per_kp_ms' => count($kpCodes) > 0 ? round($totalWeightTime / count($kpCodes), 2) : 0
|
|
|
+ ]);
|
|
|
+
|
|
|
// 3. 按权重分配题目数量
|
|
|
$totalWeight = array_sum($kpWeights);
|
|
|
$selectedQuestions = [];
|