|
|
@@ -1277,11 +1277,7 @@ class LearningAnalyticsService
|
|
|
'填空题' => 30,
|
|
|
'解答题' => 30,
|
|
|
];
|
|
|
- $difficultyRatio = $params['difficulty_ratio'] ?? [
|
|
|
- '基础' => 50,
|
|
|
- '中等' => 35,
|
|
|
- '拔高' => 15,
|
|
|
- ];
|
|
|
+ // 注意: difficulty_ratio 参数已废弃,使用 difficulty_category 控制难度分布
|
|
|
$difficultyLevels = $params['difficulty_levels'] ?? [];
|
|
|
// 如果用户没有选择任何难度,difficultyLevels 为空数组,表示随机难度
|
|
|
|
|
|
@@ -1331,7 +1327,7 @@ class LearningAnalyticsService
|
|
|
]);
|
|
|
|
|
|
// 获取学生错题的详细信息
|
|
|
- $priorityQuestions = $this->getQuestionsFromBank([], [], $studentId, $questionTypeRatio, $difficultyRatio, 200, $mistakeQuestionIds);
|
|
|
+ $priorityQuestions = $this->getQuestionsFromBank([], [], $studentId, $questionTypeRatio, 200, $mistakeQuestionIds);
|
|
|
|
|
|
Log::info('LearningAnalyticsService: 错题获取完成', [
|
|
|
'priority_questions_count' => count($priorityQuestions),
|
|
|
@@ -1362,7 +1358,7 @@ class LearningAnalyticsService
|
|
|
'assemble_type' => $assembleType
|
|
|
]);
|
|
|
|
|
|
- $additionalQuestions = $this->getQuestionsFromBank($kpCodes, $skills, $studentId, $questionTypeRatio, $difficultyRatio, 200);
|
|
|
+ $additionalQuestions = $this->getQuestionsFromBank($kpCodes, $skills, $studentId, $questionTypeRatio, 200);
|
|
|
$allQuestions = array_merge($priorityQuestions, $additionalQuestions);
|
|
|
|
|
|
Log::info('getQuestionsFromBank 调用完成', [
|
|
|
@@ -1419,18 +1415,23 @@ class LearningAnalyticsService
|
|
|
}
|
|
|
|
|
|
// 3. 根据掌握度对题目进行筛选和排序
|
|
|
+ // 错题本类型:使用所有错题,不限制数量
|
|
|
+ $targetQuestionCount = $isMistakeBook ? count($allQuestions) : $totalQuestions;
|
|
|
+
|
|
|
Log::info('开始调用 selectQuestionsByMastery', [
|
|
|
'input_count' => count($allQuestions),
|
|
|
- 'target_count' => $totalQuestions
|
|
|
+ 'target_count' => $targetQuestionCount,
|
|
|
+ 'is_mistake_book' => $isMistakeBook,
|
|
|
+ 'assemble_type' => $assembleType,
|
|
|
+ 'total_questions_param' => $totalQuestions
|
|
|
]);
|
|
|
|
|
|
$startTime = microtime(true);
|
|
|
$selectedQuestions = $this->selectQuestionsByMastery(
|
|
|
$allQuestions,
|
|
|
$studentId,
|
|
|
- $totalQuestions,
|
|
|
+ $targetQuestionCount,
|
|
|
$questionTypeRatio,
|
|
|
- $difficultyRatio,
|
|
|
$difficultyLevels,
|
|
|
$weaknessFilter
|
|
|
);
|
|
|
@@ -1439,7 +1440,8 @@ class LearningAnalyticsService
|
|
|
Log::info('题目筛选结果', [
|
|
|
'input_count' => count($allQuestions),
|
|
|
'selected_count' => count($selectedQuestions),
|
|
|
- 'target_count' => $totalQuestions,
|
|
|
+ 'target_count' => $targetQuestionCount,
|
|
|
+ 'is_mistake_book' => $isMistakeBook,
|
|
|
'select_time_ms' => round($selectTime, 2)
|
|
|
]);
|
|
|
|
|
|
@@ -1517,7 +1519,7 @@ class LearningAnalyticsService
|
|
|
* 从本地题库获取题目(错题回顾优先)
|
|
|
* 支持优先获取指定题目ID的题目
|
|
|
*/
|
|
|
- private function getQuestionsFromBank(array $kpCodes, array $skills, ?string $studentId, array $questionTypeRatio = [], array $difficultyRatio = [], int $totalNeeded = 100, array $priorityQuestionIds = []): array
|
|
|
+ private function getQuestionsFromBank(array $kpCodes, array $skills, ?string $studentId, array $questionTypeRatio = [], int $totalNeeded = 100, array $priorityQuestionIds = []): array
|
|
|
{
|
|
|
$startTime = microtime(true);
|
|
|
|
|
|
@@ -1548,7 +1550,7 @@ class LearningAnalyticsService
|
|
|
'skills' => $skills,
|
|
|
'total_needed' => $totalNeeded,
|
|
|
'question_type_ratio' => $questionTypeRatio,
|
|
|
- 'difficulty_ratio' => $difficultyRatio
|
|
|
+ 'note' => '难度筛选由 QuestionLocalService 处理'
|
|
|
]);
|
|
|
|
|
|
$query = \App\Models\Question::query();
|
|
|
@@ -1574,22 +1576,8 @@ class LearningAnalyticsService
|
|
|
->where('solution', '!=', '')
|
|
|
->where('solution', '!=', '[]');
|
|
|
|
|
|
- // 按难度范围筛选
|
|
|
- if (!empty($difficultyRatio)) {
|
|
|
- $difficultyRanges = $this->buildDifficultyRanges($difficultyRatio);
|
|
|
- $query->where(function ($q) use ($difficultyRanges) {
|
|
|
- $first = true;
|
|
|
- foreach ($difficultyRanges as $range) {
|
|
|
- if ($first) {
|
|
|
- $q->whereBetween('difficulty', [$range['min'], $range['max']]);
|
|
|
- $first = false;
|
|
|
- } else {
|
|
|
- $q->orWhereBetween('difficulty', [$range['min'], $range['max']]);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- Log::info('应用难度筛选', ['difficulty_ranges' => $difficultyRanges]);
|
|
|
- }
|
|
|
+ // 注意: 难度筛选由 QuestionLocalService 的难度分布系统处理
|
|
|
+ // 不在这里进行难度筛选,让 QuestionLocalService 做精确的难度分布
|
|
|
|
|
|
// 限制数量并随机排序
|
|
|
$query->limit($totalNeeded * 2) // 多取一些用于后续筛选
|
|
|
@@ -1623,13 +1611,9 @@ class LearningAnalyticsService
|
|
|
];
|
|
|
})->toArray();
|
|
|
|
|
|
- // 按题型和难度配比筛选
|
|
|
- $selectedQuestions = $this->selectQuestionsByRatio(
|
|
|
- $formattedQuestions,
|
|
|
- $totalNeeded,
|
|
|
- $questionTypeRatio,
|
|
|
- $difficultyRatio
|
|
|
- );
|
|
|
+ // 注意: 题型和难度配比调整由 QuestionLocalService 处理
|
|
|
+ // 这里只做初步筛选,让 QuestionLocalService 做精确的配比调整
|
|
|
+ $selectedQuestions = array_slice($formattedQuestions, 0, $totalNeeded);
|
|
|
|
|
|
Log::info('getQuestionsFromBank 完成', [
|
|
|
'selected_count' => count($selectedQuestions),
|
|
|
@@ -1679,8 +1663,11 @@ class LearningAnalyticsService
|
|
|
})->toArray();
|
|
|
|
|
|
Log::info('getLocalQuestionsByIds 获取成功', [
|
|
|
- 'count' => count($result),
|
|
|
- 'question_ids' => $questionIds
|
|
|
+ 'requested_count' => count($questionIds),
|
|
|
+ 'found_count' => count($result),
|
|
|
+ 'missing_ids' => array_diff($questionIds, array_column($result, 'id')),
|
|
|
+ 'question_ids' => array_slice($questionIds, 0, 20),
|
|
|
+ 'found_question_ids' => array_slice(array_column($result, 'id'), 0, 20)
|
|
|
]);
|
|
|
|
|
|
return $result;
|
|
|
@@ -1721,40 +1708,20 @@ class LearningAnalyticsService
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 构建难度范围(根据配比)
|
|
|
- */
|
|
|
- private function buildDifficultyRanges(array $difficultyRatio): array
|
|
|
- {
|
|
|
- $ranges = [];
|
|
|
-
|
|
|
- if (isset($difficultyRatio['基础'])) {
|
|
|
- $ranges[] = ['min' => 0.0, 'max' => 0.4, 'label' => '基础'];
|
|
|
- }
|
|
|
- if (isset($difficultyRatio['中等'])) {
|
|
|
- $ranges[] = ['min' => 0.4, 'max' => 0.7, 'label' => '中等'];
|
|
|
- }
|
|
|
- if (isset($difficultyRatio['拔高'])) {
|
|
|
- $ranges[] = ['min' => 0.7, 'max' => 1.0, 'label' => '拔高'];
|
|
|
- }
|
|
|
-
|
|
|
- return $ranges;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 根据题型和难度配比选择题目
|
|
|
+ * 根据题型配比选择题目
|
|
|
+ * 注意: 难度配比调整由 QuestionLocalService 处理
|
|
|
*/
|
|
|
private function selectQuestionsByRatio(
|
|
|
array $questions,
|
|
|
int $totalNeeded,
|
|
|
- array $questionTypeRatio = [],
|
|
|
- array $difficultyRatio = []
|
|
|
+ array $questionTypeRatio = []
|
|
|
): array {
|
|
|
if (empty($questions)) {
|
|
|
return [];
|
|
|
}
|
|
|
|
|
|
// 如果没有配比要求,直接返回
|
|
|
- if (empty($questionTypeRatio) && empty($difficultyRatio)) {
|
|
|
+ if (empty($questionTypeRatio)) {
|
|
|
return array_slice($questions, 0, $totalNeeded);
|
|
|
}
|
|
|
|
|
|
@@ -1790,37 +1757,8 @@ class LearningAnalyticsService
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 如果还有空缺,按难度配比补充
|
|
|
- $remaining = $totalNeeded - count($selected);
|
|
|
- if ($remaining > 0 && !empty($difficultyRatio)) {
|
|
|
- $difficultyRanges = $this->buildDifficultyRanges($difficultyRatio);
|
|
|
-
|
|
|
- foreach ($difficultyRanges as $range) {
|
|
|
- if ($remaining <= 0) break;
|
|
|
-
|
|
|
- $diffQuestions = [];
|
|
|
- foreach ($questions as $idx => $q) {
|
|
|
- if (in_array($idx, $usedIndices)) continue;
|
|
|
-
|
|
|
- $difficulty = (float) ($q['difficulty'] ?? 0);
|
|
|
- if ($difficulty >= $range['min'] && $difficulty < $range['max']) {
|
|
|
- $diffQuestions[] = ['idx' => $idx, 'question' => $q];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 随机选择
|
|
|
- shuffle($diffQuestions);
|
|
|
- $count = min($remaining, count($diffQuestions));
|
|
|
-
|
|
|
- for ($i = 0; $i < $count; $i++) {
|
|
|
- $selected[] = $diffQuestions[$i]['question'];
|
|
|
- $usedIndices[] = $diffQuestions[$i]['idx'];
|
|
|
- $remaining--;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
// 如果还有空缺,补充剩余题目
|
|
|
+ // 注意: 难度配比调整由 QuestionLocalService 处理
|
|
|
if (count($selected) < $totalNeeded) {
|
|
|
foreach ($questions as $idx => $q) {
|
|
|
if (count($selected) >= $totalNeeded) break;
|
|
|
@@ -1842,7 +1780,6 @@ class LearningAnalyticsService
|
|
|
?string $studentId,
|
|
|
int $totalQuestions,
|
|
|
array $questionTypeRatio,
|
|
|
- array $difficultyRatio,
|
|
|
array $difficultyLevels,
|
|
|
array $weaknessFilter
|
|
|
): array {
|
|
|
@@ -1852,6 +1789,16 @@ class LearningAnalyticsService
|
|
|
'total_questions' => $totalQuestions
|
|
|
]);
|
|
|
|
|
|
+ // 错题本类型:使用所有题目,不进行权重分配和筛选
|
|
|
+ if ($totalQuestions >= count($questions)) {
|
|
|
+ Log::info('错题本类型:使用所有题目,跳过权重分配', [
|
|
|
+ 'question_count' => count($questions),
|
|
|
+ 'total_questions' => $totalQuestions,
|
|
|
+ 'input_question_count' => func_num_args() > 0 ? count($questions) : 'N/A'
|
|
|
+ ]);
|
|
|
+ return $questions;
|
|
|
+ }
|
|
|
+
|
|
|
// 如果未选择难度,则不过滤(随机生成所有难度)
|
|
|
if (empty($difficultyLevels)) {
|
|
|
Log::info('用户未选择难度,将随机生成所有难度的题目');
|
|
|
@@ -2001,8 +1948,8 @@ class LearningAnalyticsService
|
|
|
'target_count' => $totalQuestions
|
|
|
]);
|
|
|
|
|
|
- // 5. 按题型和难度进行微调
|
|
|
- return $this->adjustQuestionsByRatio($selectedQuestions, $questionTypeRatio, $difficultyRatio, $totalQuestions);
|
|
|
+ // 5. 按题型进行微调(难度分布由 QuestionLocalService 处理)
|
|
|
+ return $this->adjustQuestionsByRatio($selectedQuestions, $questionTypeRatio, $totalQuestions);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -2030,7 +1977,7 @@ class LearningAnalyticsService
|
|
|
/**
|
|
|
* 根据题型和难度配比调整题目
|
|
|
*/
|
|
|
- private function adjustQuestionsByRatio(array $questions, array $typeRatio, array $difficultyRatio, int $targetCount): array
|
|
|
+ private function adjustQuestionsByRatio(array $questions, array $typeRatio, int $targetCount): array
|
|
|
{
|
|
|
Log::info('开始题型配比调整', [
|
|
|
'input_questions' => count($questions),
|