|
|
@@ -308,4 +308,199 @@ class QuestionExpansionService
|
|
|
|
|
|
return round(($totalExpanded / $totalQuestions) * 100, 2);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按知识点优先级扩展题目数量(按知识点组卷专用)
|
|
|
+ * 优先级策略:
|
|
|
+ * 1. 直接关联知识点题目(来自输入数组)
|
|
|
+ * 2. 相同知识点其他题目
|
|
|
+ * 3. 子知识点题目(下探1层)
|
|
|
+ * 4. 薄弱知识点题目
|
|
|
+ * 5. 子知识点题目(下探2层)
|
|
|
+ */
|
|
|
+ public function expandQuestionsByKnowledgePoints(array $baseParams, ?string $studentId, array $targetKnowledgePoints, array $weaknessFilter, int $totalQuestions): array
|
|
|
+ {
|
|
|
+ Log::info('QuestionExpansionService: 开始按知识点扩展题目', [
|
|
|
+ 'student_id' => $studentId,
|
|
|
+ 'target_knowledge_points' => $targetKnowledgePoints,
|
|
|
+ 'total_needed' => $totalQuestions,
|
|
|
+ 'weakness_count' => count($weaknessFilter)
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $strategy = [
|
|
|
+ 'mistake_ids' => [],
|
|
|
+ 'mistake_question_ids' => [],
|
|
|
+ 'step2_count' => 0,
|
|
|
+ 'step3_count' => 0,
|
|
|
+ 'step4_count' => 0,
|
|
|
+ 'step5_count' => 0,
|
|
|
+ 'expansion_details' => []
|
|
|
+ ];
|
|
|
+
|
|
|
+ // Step 1: 获取直接关联知识点题目(优先级:最高)
|
|
|
+ // 直接使用用户指定的知识点数组中的题目
|
|
|
+ if (!empty($targetKnowledgePoints)) {
|
|
|
+ $step1Questions = $this->getQuestionsByKnowledgePoints($targetKnowledgePoints, $strategy['mistake_question_ids'], $totalQuestions);
|
|
|
+ $strategy['mistake_question_ids'] = array_merge($strategy['mistake_question_ids'], $step1Questions);
|
|
|
+ $strategy['expansion_details']['step1_direct_kp'] = [
|
|
|
+ 'target_knowledge_points' => $targetKnowledgePoints,
|
|
|
+ 'added_count' => count($step1Questions)
|
|
|
+ ];
|
|
|
+ Log::info('QuestionExpansionService: Step1 - 获取直接关联知识点题目', [
|
|
|
+ 'target_knowledge_points' => $targetKnowledgePoints,
|
|
|
+ 'added_count' => count($step1Questions),
|
|
|
+ 'total_count' => count($strategy['mistake_question_ids'])
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果直接关联题目数量已满足需求,直接返回
|
|
|
+ if (count($strategy['mistake_question_ids']) >= $totalQuestions) {
|
|
|
+ Log::info('QuestionExpansionService: Step1 - 直接关联知识点题目数量已满足需求', [
|
|
|
+ 'total_needed' => $totalQuestions,
|
|
|
+ 'direct_kp_count' => count($strategy['mistake_question_ids'])
|
|
|
+ ]);
|
|
|
+ return $strategy;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 2: 相同知识点其他题目(优先级:高)
|
|
|
+ // 强化特定知识点的掌握,题目类型多样化
|
|
|
+ $remaining = $totalQuestions - count($strategy['mistake_question_ids']);
|
|
|
+ if (!empty($targetKnowledgePoints) && $remaining > 0) {
|
|
|
+ $step2Questions = $this->getQuestionsByKnowledgePoints($targetKnowledgePoints, $strategy['mistake_question_ids'], $remaining);
|
|
|
+ $strategy['mistake_question_ids'] = array_merge($strategy['mistake_question_ids'], $step2Questions);
|
|
|
+ $strategy['step2_count'] = count($step2Questions);
|
|
|
+ $strategy['expansion_details']['step2_same_kp'] = [
|
|
|
+ 'knowledge_points' => $targetKnowledgePoints,
|
|
|
+ 'added_count' => count($step2Questions)
|
|
|
+ ];
|
|
|
+ Log::info('QuestionExpansionService: Step2 - 获取相同知识点其他题目', [
|
|
|
+ 'knowledge_points' => $targetKnowledgePoints,
|
|
|
+ 'added_count' => count($step2Questions),
|
|
|
+ 'total_count' => count($strategy['mistake_question_ids'])
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果数量仍不足,继续扩展
|
|
|
+ $remaining = $totalQuestions - count($strategy['mistake_question_ids']);
|
|
|
+ if ($remaining <= 0) {
|
|
|
+ return $strategy;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 3: 子知识点题目(下探1层)(优先级:中高)
|
|
|
+ // 深入知识点的细分类别,实现精准练习
|
|
|
+ if (!empty($targetKnowledgePoints) && $remaining > 0) {
|
|
|
+ $childKps = $this->getChildKnowledgePoints($targetKnowledgePoints);
|
|
|
+ if (!empty($childKps)) {
|
|
|
+ $step3Questions = $this->getQuestionsByKnowledgePoints($childKps, $strategy['mistake_question_ids'], $remaining);
|
|
|
+ $strategy['mistake_question_ids'] = array_merge($strategy['mistake_question_ids'], $step3Questions);
|
|
|
+ $strategy['step3_count'] = count($step3Questions);
|
|
|
+ $strategy['expansion_details']['step3_child_kp_level1'] = [
|
|
|
+ 'child_knowledge_points' => $childKps,
|
|
|
+ 'added_count' => count($step3Questions)
|
|
|
+ ];
|
|
|
+ Log::info('QuestionExpansionService: Step3 - 获取子知识点题目(下探1层)', [
|
|
|
+ 'child_knowledge_points' => $childKps,
|
|
|
+ 'added_count' => count($step3Questions),
|
|
|
+ 'total_count' => count($strategy['mistake_question_ids'])
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果数量仍不足,继续扩展
|
|
|
+ $remaining = $totalQuestions - count($strategy['mistake_question_ids']);
|
|
|
+ if ($remaining <= 0) {
|
|
|
+ return $strategy;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 4: 薄弱知识点题目(优先级:中)
|
|
|
+ // 针对性强化薄弱环节,提升整体掌握度
|
|
|
+ if (!empty($weaknessFilter) && $remaining > 0) {
|
|
|
+ $weaknessKps = array_column($weaknessFilter, 'kp_code');
|
|
|
+ // 排除已使用的目标知识点
|
|
|
+ $weaknessKps = array_diff($weaknessKps, $targetKnowledgePoints);
|
|
|
+ if (!empty($weaknessKps)) {
|
|
|
+ $step4Questions = $this->getQuestionsByKnowledgePoints($weaknessKps, $strategy['mistake_question_ids'], $remaining);
|
|
|
+ $strategy['mistake_question_ids'] = array_merge($strategy['mistake_question_ids'], $step4Questions);
|
|
|
+ $strategy['step4_count'] = count($step4Questions);
|
|
|
+ $strategy['expansion_details']['step4_weakness'] = [
|
|
|
+ 'weakness_knowledge_points' => $weaknessKps,
|
|
|
+ 'added_count' => count($step4Questions)
|
|
|
+ ];
|
|
|
+ Log::info('QuestionExpansionService: Step4 - 获取薄弱知识点题目', [
|
|
|
+ 'weakness_knowledge_points' => $weaknessKps,
|
|
|
+ 'added_count' => count($step4Questions),
|
|
|
+ 'total_count' => count($strategy['mistake_question_ids'])
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果数量仍不足,继续扩展
|
|
|
+ $remaining = $totalQuestions - count($strategy['mistake_question_ids']);
|
|
|
+ if ($remaining <= 0) {
|
|
|
+ return $strategy;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Step 5: 子知识点题目(下探2层)(优先级:低)
|
|
|
+ // 全面覆盖薄弱环节的各个细分领域
|
|
|
+ if (!empty($weaknessFilter) && $remaining > 0) {
|
|
|
+ $weaknessKps = array_column($weaknessFilter, 'kp_code');
|
|
|
+ // 排除已使用的目标知识点
|
|
|
+ $weaknessKps = array_diff($weaknessKps, $targetKnowledgePoints);
|
|
|
+ if (!empty($weaknessKps)) {
|
|
|
+ $childWeaknessKps = $this->getChildKnowledgePoints($weaknessKps);
|
|
|
+ if (!empty($childWeaknessKps)) {
|
|
|
+ $step5Questions = $this->getQuestionsByKnowledgePoints($childWeaknessKps, $strategy['mistake_question_ids'], $remaining);
|
|
|
+ $strategy['mistake_question_ids'] = array_merge($strategy['mistake_question_ids'], $step5Questions);
|
|
|
+ $strategy['step5_count'] = count($step5Questions);
|
|
|
+ $strategy['expansion_details']['step5_child_weakness_level2'] = [
|
|
|
+ 'child_weakness_knowledge_points' => $childWeaknessKps,
|
|
|
+ 'added_count' => count($step5Questions)
|
|
|
+ ];
|
|
|
+ Log::info('QuestionExpansionService: Step5 - 获取薄弱点子知识点题目(下探2层)', [
|
|
|
+ 'child_weakness_knowledge_points' => $childWeaknessKps,
|
|
|
+ 'added_count' => count($step5Questions),
|
|
|
+ 'total_count' => count($strategy['mistake_question_ids'])
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Log::info('QuestionExpansionService: 按知识点题目扩展完成', [
|
|
|
+ 'total_final_count' => count($strategy['mistake_question_ids']),
|
|
|
+ 'target_knowledge_points' => $targetKnowledgePoints,
|
|
|
+ 'expansion_details' => $strategy['expansion_details']
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return $strategy;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取子知识点(下探多层)
|
|
|
+ * 支持追溯下探两层
|
|
|
+ */
|
|
|
+ private function getChildKnowledgePointsMultiLevel(array $parentKpCodes, int $maxDepth = 2): array
|
|
|
+ {
|
|
|
+ $allChildKps = [];
|
|
|
+ $currentLevelKps = $parentKpCodes;
|
|
|
+ $depth = 0;
|
|
|
+
|
|
|
+ while ($depth < $maxDepth && !empty($currentLevelKps)) {
|
|
|
+ Log::info('QuestionExpansionService: 获取子知识点', [
|
|
|
+ 'depth' => $depth + 1,
|
|
|
+ 'parent_kp_codes' => $currentLevelKps
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $childKps = $this->getChildKnowledgePoints($currentLevelKps);
|
|
|
+ if (!empty($childKps)) {
|
|
|
+ $allChildKps = array_merge($allChildKps, $childKps);
|
|
|
+ $currentLevelKps = $childKps; // 下一层的父级是这一层的子级
|
|
|
+ } else {
|
|
|
+ break; // 没有更多子知识点时停止
|
|
|
+ }
|
|
|
+ $depth++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 去重
|
|
|
+ return array_values(array_unique($allChildKps));
|
|
|
+ }
|
|
|
}
|