|
@@ -2957,6 +2957,39 @@ class LearningAnalyticsService
|
|
|
return [];
|
|
return [];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 有教材且带章节:用 textbook_chapter_knowledge_relation 由「前章节」解析知识点,再按 kp_code 选题(不用 questions.textbook_catalog_nodes_id)
|
|
|
|
|
+ $effectiveKpCodes = $gradeKpCodes;
|
|
|
|
|
+ if ($textbookId && ! empty($textbookCatalogNodeIds)) {
|
|
|
|
|
+ $allowedNodeIds = $this->getEarlierChapterNodeIds((int) $textbookId, $textbookCatalogNodeIds);
|
|
|
|
|
+ if (empty($allowedNodeIds)) {
|
|
|
|
|
+ Log::warning('getSupplementaryQuestionsForGrade: 未找到前章节节点,跳过补充');
|
|
|
|
|
+
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+ $chapterKpCodes = $this->getKpCodesForCatalogChapterIds($allowedNodeIds);
|
|
|
|
|
+ if (empty($chapterKpCodes)) {
|
|
|
|
|
+ Log::warning('getSupplementaryQuestionsForGrade: 前章节在 textbook_chapter_knowledge_relation 中无知识点', [
|
|
|
|
|
+ 'allowed_node_ids' => $allowedNodeIds,
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+ $effectiveKpCodes = array_values(array_intersect($gradeKpCodes, $chapterKpCodes));
|
|
|
|
|
+ if (empty($effectiveKpCodes)) {
|
|
|
|
|
+ Log::warning('getSupplementaryQuestionsForGrade: 教材年级知识点与章节关联知识点无交集', [
|
|
|
|
|
+ 'grade_kp_count' => count($gradeKpCodes),
|
|
|
|
|
+ 'chapter_kp_count' => count($chapterKpCodes),
|
|
|
|
|
+ ]);
|
|
|
|
|
+
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+ Log::info('getSupplementaryQuestionsForGrade: 按章节关联知识点缩小补充范围', [
|
|
|
|
|
+ 'allowed_chapter_nodes' => count($allowedNodeIds),
|
|
|
|
|
+ 'chapter_kp_count' => count($chapterKpCodes),
|
|
|
|
|
+ 'effective_kp_count' => count($effectiveKpCodes),
|
|
|
|
|
+ ]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 查询同年级其他知识点的题目
|
|
// 查询同年级其他知识点的题目
|
|
|
$query = \App\Models\Question::query();
|
|
$query = \App\Models\Question::query();
|
|
|
|
|
|
|
@@ -2982,22 +3015,7 @@ class LearningAnalyticsService
|
|
|
$query->whereNotIn('kp_code', $existingKpCodes);
|
|
$query->whereNotIn('kp_code', $existingKpCodes);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $query->whereIn('kp_code', $gradeKpCodes);
|
|
|
|
|
-
|
|
|
|
|
- // 【新增】仅从同教材前章节补充:部分章节尚未学过,不补充未学章节的题目
|
|
|
|
|
- if ($textbookId && !empty($textbookCatalogNodeIds)) {
|
|
|
|
|
- $allowedNodeIds = $this->getEarlierChapterNodeIds((int) $textbookId, $textbookCatalogNodeIds);
|
|
|
|
|
- if (!empty($allowedNodeIds)) {
|
|
|
|
|
- $query->whereIn('textbook_catalog_nodes_id', $allowedNodeIds);
|
|
|
|
|
- Log::info('getSupplementaryQuestionsForGrade: 限制为前章节', [
|
|
|
|
|
- 'allowed_node_count' => count($allowedNodeIds),
|
|
|
|
|
- 'max_sort_order' => '同选中章节及之前'
|
|
|
|
|
- ]);
|
|
|
|
|
- } else {
|
|
|
|
|
- Log::warning('getSupplementaryQuestionsForGrade: 未找到前章节节点,跳过补充');
|
|
|
|
|
- return [];
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ $query->whereIn('kp_code', $effectiveKpCodes);
|
|
|
|
|
|
|
|
// 筛选有解题思路的题目
|
|
// 筛选有解题思路的题目
|
|
|
$query->whereNotNull('solution')
|
|
$query->whereNotNull('solution')
|
|
@@ -3180,6 +3198,32 @@ class LearningAnalyticsService
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 由 catalog 章节节点 ID 列表,从 textbook_chapter_knowledge_relation 取关联的知识点编码(用于选题,而非题目表上的章节字段)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param array<int> $catalogChapterIds textbook_catalog_nodes.id
|
|
|
|
|
+ * @return list<string>
|
|
|
|
|
+ */
|
|
|
|
|
+ private function getKpCodesForCatalogChapterIds(array $catalogChapterIds): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $ids = array_values(array_unique(array_filter(array_map(
|
|
|
|
|
+ static fn ($id) => (int) $id,
|
|
|
|
|
+ $catalogChapterIds
|
|
|
|
|
+ ), static fn (int $id) => $id > 0)));
|
|
|
|
|
+
|
|
|
|
|
+ if ($ids === []) {
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return array_values(array_filter(array_unique(DB::table('textbook_chapter_knowledge_relation')
|
|
|
|
|
+ ->whereIn('catalog_chapter_id', $ids)
|
|
|
|
|
+ ->where(function ($q) {
|
|
|
|
|
+ $q->where('is_deleted', 0)->orWhereNull('is_deleted');
|
|
|
|
|
+ })
|
|
|
|
|
+ ->pluck('kp_code')
|
|
|
|
|
+ ->toArray())));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private function getGradeKnowledgePoints(int $grade, ?int $textbookId = null): array
|
|
private function getGradeKnowledgePoints(int $grade, ?int $textbookId = null): array
|
|
|
{
|
|
{
|
|
|
try {
|
|
try {
|