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()->unique()->values()->all(); $children = KnowledgePoint::query() ->whereIn('parent_kp_code', $baseCodes) ->pluck('kp_code') ->filter() ->unique() ->values() ->toArray(); return array_values(array_unique(array_merge($baseCodes, $children))); } }