| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- <?php
- namespace App\Services;
- use Illuminate\Support\Facades\Log;
- use App\Models\StudentKnowledgeMastery;
- use App\Models\MistakeRecord;
- use App\Models\Question;
- use App\Models\KnowledgePoint;
- class QuestionExpansionService
- {
- /**
- * 按优先级扩展题目数量
- * 优先级策略:
- * 1. 优先使用错题记录的题目
- * 2. 错题相同的知识点的其他题目
- * 3. 相同知识点的子知识点的题目
- * 4. 学生薄弱知识点相关题目
- * 5. 薄弱知识点的子知识点的题目
- */
- public function expandQuestions(array $baseParams, ?string $studentId, array $weaknessFilter, int $totalQuestions): array
- {
- Log::info('QuestionExpansionService: 开始题目扩展', [
- 'student_id' => $studentId,
- 'total_needed' => $totalQuestions,
- 'weakness_count' => count($weaknessFilter)
- ]);
- $strategy = [
- 'mistake_ids' => $baseParams['mistake_ids'] ?? [],
- 'mistake_question_ids' => $baseParams['mistake_question_ids'] ?? [],
- 'step2_count' => 0,
- 'step3_count' => 0,
- 'step4_count' => 0,
- 'step5_count' => 0,
- 'expansion_details' => []
- ];
- $includeMistakes = ($baseParams['mistake_options']['review_mistakes'] ?? true) && $studentId;
- if (!$includeMistakes) {
- Log::info('QuestionExpansionService: 未开启错题回顾,跳过扩展');
- return $strategy;
- }
- // Step 1: 获取学生历史错题(优先级:最高)
- // 直接使用学生历史错题,学习价值最高
- if (empty($strategy['mistake_question_ids'])) {
- $studentMistakes = $this->getStudentMistakes($studentId);
- $strategy['mistake_question_ids'] = array_values(array_unique(array_column($studentMistakes, 'question_id')));
- $strategy['expansion_details']['step1_mistakes'] = count($strategy['mistake_question_ids']);
- Log::info('QuestionExpansionService: Step1 - 获取历史错题', [
- 'count' => count($strategy['mistake_question_ids']),
- 'mistake_ids' => $strategy['mistake_question_ids']
- ]);
- }
- // 如果错题数量已满足需求,直接返回
- if (count($strategy['mistake_question_ids']) >= $totalQuestions) {
- Log::info('QuestionExpansionService: Step1 - 错题数量已满足需求', [
- 'total_needed' => $totalQuestions,
- 'mistake_count' => count($strategy['mistake_question_ids'])
- ]);
- return $strategy;
- }
- // Step 2: 错题相同的知识点的其他题目(优先级:高)
- // 强化特定知识点的掌握,题目类型多样化
- $remaining = $totalQuestions - count($strategy['mistake_question_ids']);
- $mistakeKps = $this->getMistakeKnowledgePoints($studentId, $strategy['mistake_question_ids']);
- if (!empty($mistakeKps) && $remaining > 0) {
- $step2Questions = $this->getQuestionsByKnowledgePoints($mistakeKps, $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' => $mistakeKps,
- 'added_count' => count($step2Questions)
- ];
- Log::info('QuestionExpansionService: Step2 - 获取同知识点其他题目', [
- 'knowledge_points' => $mistakeKps,
- '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: 相同知识点的子知识点的题目(优先级:中高)
- // 深入知识点的细分类别,实现精准练习
- if (!empty($mistakeKps) && $remaining > 0) {
- $childKps = $this->getChildKnowledgePoints($mistakeKps);
- 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'] = [
- 'child_knowledge_points' => $childKps,
- 'added_count' => count($step3Questions)
- ];
- Log::info('QuestionExpansionService: Step3 - 获取子知识点题目', [
- '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');
- $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: 薄弱知识点的子知识点的题目(优先级:低)
- // 全面覆盖薄弱环节的各个细分领域
- if (!empty($weaknessFilter) && $remaining > 0) {
- $weaknessKps = array_column($weaknessFilter, 'kp_code');
- $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'] = [
- 'child_weakness_knowledge_points' => $childWeaknessKps,
- 'added_count' => count($step5Questions)
- ];
- Log::info('QuestionExpansionService: Step5 - 获取薄弱点子知识点题目', [
- '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']),
- 'expansion_details' => $strategy['expansion_details']
- ]);
- return $strategy;
- }
- /**
- * 获取学生错题(去重,按question_id分组)
- */
- private function getStudentMistakes(string $studentId): array
- {
- try {
- // 使用 MistakeRecord 模型获取学生的错题(不限制数量,获取全部)
- // 按 question_id 分组去重,保留第一条记录
- $mistakeRecords = MistakeRecord::forStudent($studentId)
- ->where('review_status', '!=', MistakeRecord::REVIEW_STATUS_MASTERED) // 排除已掌握的错题
- ->select(['question_id', 'knowledge_point', 'error_type', 'difficulty'])
- ->get()
- ->unique('question_id') // 按 question_id 去重
- ->values(); // 重新索引
- Log::info('QuestionExpansionService: 获取学生错题(去重后)', [
- 'student_id' => $studentId,
- 'total_records' => MistakeRecord::forStudent($studentId)
- ->where('review_status', '!=', MistakeRecord::REVIEW_STATUS_MASTERED)->count(),
- 'unique_count' => $mistakeRecords->count()
- ]);
- return $mistakeRecords->map(function ($record) {
- return [
- 'question_id' => $record->question_id,
- 'kp_code' => $record->knowledge_point,
- 'error_type' => $record->error_type,
- 'difficulty' => $record->difficulty,
- ];
- })->toArray();
- } catch (\Exception $e) {
- Log::error('QuestionExpansionService: 获取学生错题失败', [
- 'student_id' => $studentId,
- 'error' => $e->getMessage()
- ]);
- return [];
- }
- }
- /**
- * 获取错题对应的知识点
- */
- private function getMistakeKnowledgePoints(string $studentId, array $questionIds): array
- {
- $mistakeRecords = MistakeRecord::forStudent($studentId)
- ->whereIn('question_id', $questionIds)
- ->whereNotNull('knowledge_point')
- ->select(['knowledge_point'])
- ->get();
- return array_values(array_unique(array_filter($mistakeRecords->pluck('knowledge_point')->toArray())));
- }
- /**
- * 根据知识点获取题目
- */
- private function getQuestionsByKnowledgePoints(array $kpCodes, array $excludeQuestionIds, int $limit): array
- {
- if (empty($kpCodes)) {
- return [];
- }
- try {
- $questions = Question::whereIn('kp_code', $kpCodes)
- ->whereNotIn('id', $excludeQuestionIds)
- ->select(['id'])
- ->limit($limit)
- ->get();
- return $questions->pluck('id')->toArray();
- } catch (\Exception $e) {
- Log::warning('QuestionExpansionService: 获取题目失败', [
- 'kp_codes' => $kpCodes,
- 'limit' => $limit,
- 'error' => $e->getMessage()
- ]);
- return [];
- }
- }
- /**
- * 获取子知识点
- */
- private function getChildKnowledgePoints(array $parentKpCodes): array
- {
- try {
- $childKps = KnowledgePoint::whereIn('parent_kp_code', $parentKpCodes)
- ->select(['kp_code'])
- ->get();
- return $childKps->pluck('kp_code')->toArray();
- } catch (\Exception $e) {
- Log::warning('QuestionExpansionService: 获取子知识点失败', [
- 'parent_kp_codes' => $parentKpCodes,
- 'error' => $e->getMessage()
- ]);
- return [];
- }
- }
- /**
- * 获取扩展统计信息
- */
- public function getExpansionStats(array $strategy): array
- {
- return [
- 'total_questions' => count($strategy['mistake_question_ids'] ?? []),
- 'step1_mistakes' => count($strategy['mistake_question_ids'] ?? []),
- 'step2_same_kp' => $strategy['step2_count'] ?? 0,
- 'step3_child_kp' => $strategy['step3_count'] ?? 0,
- 'step4_weakness' => $strategy['step4_count'] ?? 0,
- 'step5_child_weakness' => $strategy['step5_count'] ?? 0,
- 'expansion_rate' => $this->calculateExpansionRate($strategy),
- 'details' => $strategy['expansion_details'] ?? []
- ];
- }
- /**
- * 计算扩展率
- */
- private function calculateExpansionRate(array $strategy): float
- {
- $totalExpanded = ($strategy['step2_count'] ?? 0) +
- ($strategy['step3_count'] ?? 0) +
- ($strategy['step4_count'] ?? 0) +
- ($strategy['step5_count'] ?? 0);
- $totalQuestions = count($strategy['mistake_question_ids'] ?? []);
- if ($totalQuestions === 0) {
- return 0.0;
- }
- return round(($totalExpanded / $totalQuestions) * 100, 2);
- }
- }
|