Browse Source

fix(exam): 指定章节摸底后续按同教材推进

指定 chapter_id_list 时优先命中未摸底章节,若列表已摸底则在该章节所属教材内按顺序继续查找未摸底章节。

Made-with: Cursor
yemeishu 1 month ago
parent
commit
e045636449
1 changed files with 64 additions and 11 deletions
  1. 64 11
      app/Services/DiagnosticChapterService.php

+ 64 - 11
app/Services/DiagnosticChapterService.php

@@ -308,8 +308,9 @@ class DiagnosticChapterService
 
     /**
      * 获取章节摸底目标章节
-     * - 传入 targetChapterIds: 仅在指定章节范围内按顺序找“有题知识点”章节(允许重复摸底)
-     * - 未传 targetChapterIds: 保持原逻辑,找第一个未摸底章节
+     * - 传入 targetChapterIds: 先在指定章节内按顺序找第一个未摸底章节;
+     *   若指定章节都已摸底,则在该章节所属教材内继续按顺序找未摸底章节。
+     * - 未传 targetChapterIds: 保持原逻辑,找教材内第一个未摸底章节。
      */
     public function getFirstUndiagnosedChapter(int $textbookId, int $studentId, ?array $targetChapterIds = null): ?array
     {
@@ -317,8 +318,7 @@ class DiagnosticChapterService
             ? array_values(array_unique(array_filter(array_map('intval', $targetChapterIds), fn ($id) => $id > 0)))
             : [];
 
-        // 只要传了 chapter_id_list(非空),就以传入参数为准;
-        // 仅当未传或为空时,才走教材默认章节逻辑。
+        // 指定章节摸底流程:先在传入列表找未摸底章节,若都摸底则扩展到同教材继续找。
         if (!empty($targetChapterIds)) {
             // 兜底:允许传入 section/subsection,自动向上映射到 chapter
             $targetChapterIds = $this->normalizeToChapterIds($targetChapterIds);
@@ -335,14 +335,21 @@ class DiagnosticChapterService
             $chapterMap = TextbookCatalog::query()
                 ->whereIn('id', $targetChapterIds)
                 ->where('node_type', 'chapter')
-                ->get()
+                ->get(['id', 'textbook_id', 'title', 'parent_id', 'node_type', 'sort_order', 'display_no'])
                 ->keyBy('id');
 
+            $orderedChapters = [];
             foreach ($targetChapterIds as $chapterId) {
                 $chapter = $chapterMap->get($chapterId);
                 if (!$chapter) {
                     continue;
                 }
+                $orderedChapters[] = $chapter;
+
+                // 指定列表优先找“未摸底”章节
+                if ($this->hasChapterDiagnostic($studentId, (int) $chapter->id)) {
+                    continue;
+                }
 
                 $chapterData = $this->getChapterKnowledgePointsSimple($chapter->id);
                 $kpCodesWithQuestions = $this->filterKpCodesWithQuestions($chapterData['kp_codes']);
@@ -351,25 +358,71 @@ class DiagnosticChapterService
                     continue;
                 }
 
-                Log::info('DiagnosticChapterService: 指定章节摸底命中章节(允许重复)', [
-                    'textbook_id' => $textbookId,
+                Log::info('DiagnosticChapterService: 指定章节摸底命中未摸底章节', [
+                    'textbook_id' => $chapter->textbook_id,
                     'student_id' => $studentId,
                     'target_chapter_ids' => $targetChapterIds,
                     'chapter_id' => $chapter->id,
-                    'chapter_name' => $chapter->name ?? '',
+                    'chapter_name' => $chapter->name ?? $chapter->title ?? '',
                     'kp_count' => count($kpCodesWithQuestions),
                 ]);
 
                 return [
                     'chapter_id' => $chapter->id,
-                    'chapter_name' => $chapter->name ?? '',
+                    'chapter_name' => $chapter->name ?? $chapter->title ?? '',
                     'section_ids' => $chapterData['section_ids'],
                     'kp_codes' => $kpCodesWithQuestions,
                 ];
             }
 
-            Log::warning('DiagnosticChapterService: 指定章节均无可用题目知识点', [
-                'textbook_id' => $textbookId,
+            if (empty($orderedChapters)) {
+                Log::warning('DiagnosticChapterService: 指定章节未找到有效chapter节点', [
+                    'student_id' => $studentId,
+                    'target_chapter_ids' => $targetChapterIds,
+                ]);
+                return null;
+            }
+
+            // 指定章节都已摸底:在同教材内继续按顺序找未摸底章节
+            $anchorTextbookId = (int) ($orderedChapters[0]->textbook_id ?? $textbookId);
+            $chaptersInTextbook = TextbookCatalog::query()
+                ->where('textbook_id', $anchorTextbookId)
+                ->where('node_type', 'chapter')
+                ->orderBy('sort_order')
+                ->orderBy('display_no')
+                ->orderBy('id')
+                ->get();
+
+            foreach ($chaptersInTextbook as $chapter) {
+                if ($this->hasChapterDiagnostic($studentId, (int) $chapter->id)) {
+                    continue;
+                }
+
+                $chapterData = $this->getChapterKnowledgePointsSimple($chapter->id);
+                $kpCodesWithQuestions = $this->filterKpCodesWithQuestions($chapterData['kp_codes']);
+                if (empty($kpCodesWithQuestions)) {
+                    continue;
+                }
+
+                Log::info('DiagnosticChapterService: 指定章节已摸底完,切换同教材后续未摸底章节', [
+                    'anchor_textbook_id' => $anchorTextbookId,
+                    'student_id' => $studentId,
+                    'target_chapter_ids' => $targetChapterIds,
+                    'chapter_id' => $chapter->id,
+                    'chapter_name' => $chapter->name ?? $chapter->title ?? '',
+                    'kp_count' => count($kpCodesWithQuestions),
+                ]);
+
+                return [
+                    'chapter_id' => $chapter->id,
+                    'chapter_name' => $chapter->name ?? $chapter->title ?? '',
+                    'section_ids' => $chapterData['section_ids'],
+                    'kp_codes' => $kpCodesWithQuestions,
+                ];
+            }
+
+            Log::warning('DiagnosticChapterService: 指定章节及同教材章节均无可用未摸底章节', [
+                'textbook_id' => $anchorTextbookId,
                 'student_id' => $studentId,
                 'target_chapter_ids' => $targetChapterIds,
             ]);