where('textbook_id', $textbookId) ->where('node_type', 'chapter') ->orderBy('display_no') ->orderBy('sort_order') ->orderBy('id') ->get(); if ($chapters->isEmpty()) { Log::warning('DiagnosticChapterService: 未找到章节节点', [ 'textbook_id' => $textbookId, ]); return []; } $orderedCodes = []; $seen = []; foreach ($chapters as $chapter) { $sectionIds = TextbookCatalog::query() ->where('parent_id', $chapter->id) ->where('node_type', 'section') ->orderBy('display_no') ->orderBy('sort_order') ->orderBy('id') ->pluck('id') ->toArray(); if (empty($sectionIds)) { continue; } $kpCodes = TextbookChapterKnowledgeRelation::query() ->whereIn('catalog_chapter_id', $sectionIds) ->pluck('kp_code') ->filter() ->unique() ->values() ->toArray(); $kpCodes = $this->expandWithChildKnowledgePoints($kpCodes); foreach ($kpCodes as $kpCode) { if (isset($seen[$kpCode])) { continue; } $seen[$kpCode] = true; $orderedCodes[] = $kpCode; } } Log::info('DiagnosticChapterService: 获取教材知识点顺序列表', [ 'textbook_id' => $textbookId, 'kp_count' => count($orderedCodes), ]); return $orderedCodes; } public function getInitialChapterKnowledgePoints(int $textbookId): array { $chapter = TextbookCatalog::query() ->where('textbook_id', $textbookId) ->where('node_type', 'chapter') ->orderBy('display_no') ->orderBy('sort_order') ->orderBy('id') ->first(); if (!$chapter) { Log::warning('DiagnosticChapterService: 未找到章节节点', [ 'textbook_id' => $textbookId, ]); return []; } $sectionIds = TextbookCatalog::query() ->where('parent_id', $chapter->id) ->where('node_type', 'section') ->orderBy('display_no') ->orderBy('sort_order') ->orderBy('id') ->pluck('id') ->toArray(); if (empty($sectionIds)) { Log::warning('DiagnosticChapterService: 章节下未找到section节点', [ 'textbook_id' => $textbookId, 'chapter_id' => $chapter->id, ]); return []; } $kpCodes = TextbookChapterKnowledgeRelation::query() ->whereIn('catalog_chapter_id', $sectionIds) ->pluck('kp_code') ->filter() ->unique() ->values() ->toArray(); $kpCodes = $this->expandWithChildKnowledgePoints($kpCodes); Log::info('DiagnosticChapterService: 获取首章知识点', [ 'textbook_id' => $textbookId, 'chapter_id' => $chapter->id, 'section_count' => count($sectionIds), 'kp_count' => count($kpCodes), ]); return [ 'chapter_id' => $chapter->id, 'section_ids' => $sectionIds, 'kp_codes' => $kpCodes, ]; } public function getFirstUnmasteredChapterKnowledgePoints(int $textbookId, int $studentId, float $threshold = 0.9): array { $chapters = TextbookCatalog::query() ->where('textbook_id', $textbookId) ->where('node_type', 'chapter') ->orderBy('display_no') ->orderBy('sort_order') ->orderBy('id') ->get(); if ($chapters->isEmpty()) { Log::warning('DiagnosticChapterService: 未找到章节节点', [ 'textbook_id' => $textbookId, ]); return []; } foreach ($chapters as $chapter) { $sectionIds = TextbookCatalog::query() ->where('parent_id', $chapter->id) ->where('node_type', 'section') ->orderBy('display_no') ->orderBy('sort_order') ->orderBy('id') ->pluck('id') ->toArray(); if (empty($sectionIds)) { continue; } $kpCodes = TextbookChapterKnowledgeRelation::query() ->whereIn('catalog_chapter_id', $sectionIds) ->pluck('kp_code') ->filter() ->unique() ->values() ->toArray(); $kpCodes = $this->expandWithChildKnowledgePoints($kpCodes); if (empty($kpCodes)) { continue; } $allMastered = StudentKnowledgeMastery::allAtLeast($studentId, $kpCodes, $threshold); Log::info('DiagnosticChapterService: 章节掌握度评估', [ 'student_id' => $studentId, 'textbook_id' => $textbookId, 'chapter_id' => $chapter->id, 'section_count' => count($sectionIds), 'kp_count' => count($kpCodes), 'all_mastered' => $allMastered, 'threshold' => $threshold, ]); if (!$allMastered) { return [ 'chapter_id' => $chapter->id, 'section_ids' => $sectionIds, 'kp_codes' => $kpCodes, 'all_mastered' => $allMastered, ]; } } Log::info('DiagnosticChapterService: 所有章节均达到掌握度阈值', [ 'student_id' => $studentId, 'textbook_id' => $textbookId, 'threshold' => $threshold, ]); return []; } private function expandWithChildKnowledgePoints(array $kpCodes): array { if (empty($kpCodes)) { return []; } $baseCodes = collect($kpCodes)->filter()->values()->all(); $children = KnowledgePoint::query() ->whereIn('parent_kp_code', $baseCodes) ->orderBy('kp_code') ->get(['parent_kp_code', 'kp_code']); $childrenMap = []; foreach ($children as $child) { $childrenMap[$child->parent_kp_code][] = $child->kp_code; } $ordered = []; $seen = []; foreach ($baseCodes as $kpCode) { if (!isset($seen[$kpCode])) { $seen[$kpCode] = true; $ordered[] = $kpCode; } foreach ($childrenMap[$kpCode] ?? [] as $childCode) { if (isset($seen[$childCode])) { continue; } $seen[$childCode] = true; $ordered[] = $childCode; } } return $ordered; } }