|
@@ -413,6 +413,20 @@ class DiagnosticChapterService
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ $widePayload = $this->buildTextbookWideChapterPayload($chaptersInTextbook);
|
|
|
|
|
+ if ($widePayload !== null) {
|
|
|
|
|
+ $widePayload['is_restart'] = true;
|
|
|
|
|
+ Log::info('DiagnosticChapterService: 全教材已摸底且单章无有效题,合并全教材知识点重启', [
|
|
|
|
|
+ 'textbook_id' => $anchorTextbookId,
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'diagnostic_chapter_id' => $widePayload['chapter_id'],
|
|
|
|
|
+ 'kp_count' => count($widePayload['kp_codes']),
|
|
|
|
|
+ 'section_count' => count($widePayload['section_ids']),
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ return $widePayload;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -529,18 +543,32 @@ class DiagnosticChapterService
|
|
|
// 所有章节都已摸底,返回第一章(重新开始)
|
|
// 所有章节都已摸底,返回第一章(重新开始)
|
|
|
$firstChapter = $chapters->first();
|
|
$firstChapter = $chapters->first();
|
|
|
$firstPayload = $firstChapter ? $this->buildChapterPayload((int) $firstChapter->id, $firstChapter, $payloadCache) : null;
|
|
$firstPayload = $firstChapter ? $this->buildChapterPayload((int) $firstChapter->id, $firstChapter, $payloadCache) : null;
|
|
|
- if ($firstPayload === null) {
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ if ($firstPayload !== null) {
|
|
|
|
|
+ Log::info('DiagnosticChapterService: 所有章节都已摸底,返回第一章', [
|
|
|
|
|
+ 'textbook_id' => $textbookId,
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'chapter_id' => $firstChapter->id,
|
|
|
|
|
+ ]);
|
|
|
|
|
+ $firstPayload['is_restart'] = true;
|
|
|
|
|
+
|
|
|
|
|
+ return $firstPayload;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- Log::info('DiagnosticChapterService: 所有章节都已摸底,返回第一章', [
|
|
|
|
|
- 'textbook_id' => $textbookId,
|
|
|
|
|
- 'student_id' => $studentId,
|
|
|
|
|
- 'chapter_id' => $firstChapter->id,
|
|
|
|
|
- ]);
|
|
|
|
|
|
|
+ $widePayload = $this->buildTextbookWideChapterPayload($chapters);
|
|
|
|
|
+ if ($widePayload !== null) {
|
|
|
|
|
+ $widePayload['is_restart'] = true;
|
|
|
|
|
+ Log::info('DiagnosticChapterService: 所有章节已摸底且第一章无有效题,合并全教材知识点重启', [
|
|
|
|
|
+ 'textbook_id' => $textbookId,
|
|
|
|
|
+ 'student_id' => $studentId,
|
|
|
|
|
+ 'diagnostic_chapter_id' => $widePayload['chapter_id'],
|
|
|
|
|
+ 'kp_count' => count($widePayload['kp_codes']),
|
|
|
|
|
+ 'section_count' => count($widePayload['section_ids']),
|
|
|
|
|
+ ]);
|
|
|
|
|
|
|
|
- $firstPayload['is_restart'] = true;
|
|
|
|
|
- return $firstPayload; // 标记是重新开始
|
|
|
|
|
|
|
+ return $widePayload;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -597,6 +625,66 @@ class DiagnosticChapterService
|
|
|
return $chapterIds;
|
|
return $chapterIds;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按教材章节顺序合并全部 section 与知识点,再过滤有题知识点。
|
|
|
|
|
+ * 用于「全章已摸底」且单章(含第一章)无可用题时的兜底。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param iterable<int, \App\Models\TextbookCatalog> $chapters
|
|
|
|
|
+ */
|
|
|
|
|
+ private function buildTextbookWideChapterPayload(iterable $chapters): ?array
|
|
|
|
|
+ {
|
|
|
|
|
+ $firstChapter = null;
|
|
|
|
|
+ $allSectionIds = [];
|
|
|
|
|
+ $seenSection = [];
|
|
|
|
|
+ $orderedKp = [];
|
|
|
|
|
+ $seenKp = [];
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($chapters as $chapter) {
|
|
|
|
|
+ if ($firstChapter === null) {
|
|
|
|
|
+ $firstChapter = $chapter;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $chapterId = (int) $chapter->id;
|
|
|
|
|
+ $chapterData = $this->getChapterKnowledgePointsSimple($chapterId);
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($chapterData['section_ids'] as $sid) {
|
|
|
|
|
+ $sid = (int) $sid;
|
|
|
|
|
+ if ($sid <= 0 || isset($seenSection[$sid])) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ $seenSection[$sid] = true;
|
|
|
|
|
+ $allSectionIds[] = $sid;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($chapterData['kp_codes'] as $kpCode) {
|
|
|
|
|
+ if ($kpCode === null || $kpCode === '') {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isset($seenKp[$kpCode])) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ $seenKp[$kpCode] = true;
|
|
|
|
|
+ $orderedKp[] = $kpCode;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($firstChapter === null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $kpCodesWithQuestions = $this->filterKpCodesWithQuestions($orderedKp);
|
|
|
|
|
+ if (empty($kpCodesWithQuestions)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'chapter_id' => (int) $firstChapter->id,
|
|
|
|
|
+ 'chapter_name' => $firstChapter->name ?? $firstChapter->title ?? '',
|
|
|
|
|
+ 'section_ids' => $allSectionIds,
|
|
|
|
|
+ 'kp_codes' => $kpCodesWithQuestions,
|
|
|
|
|
+ ];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 根据 chapter_id 生成摸底章节载荷,若章节无可用题目知识点则返回 null。
|
|
* 根据 chapter_id 生成摸底章节载荷,若章节无可用题目知识点则返回 null。
|
|
|
*/
|
|
*/
|