组卷逻辑修复总结_本分支.md 5.8 KB

组卷逻辑修复总结(本分支)

一、修改概览

文件 修改类型 说明
app/Services/ExamTypeStrategy.php 组卷去重、参数传递 排除已做题、grade 推断
app/Services/LearningAnalyticsService.php 智能补充、已学知识点 补充范围、totalNeeded、新方法

二、具体改动清单

2.1 ExamTypeStrategy.php

① 组卷去重:getStudentAnsweredQuestionIds

原逻辑:从 student_answer_questions 表按 student_idquestion_id
新逻辑:从 paper_questions 表,按该学生名下试卷的 question_bank_id 去重

// 唯一来源:paper_questions → question_bank_id(对应 questions.id)
// 确保新卷子不出现学生做过的重复题目
PaperQuestion::query()
    ->whereHas('paper', fn ($q) => $q->where('student_id', $studentId))
    ->whereNotNull('question_bank_id')
    ->where('question_bank_id', '>', 0)
    ->distinct()
    ->pluck('question_bank_id')

原因:与组卷实际数据源(paper_questions)保持一致,去重更准确。


② grade 推断:教材组卷 buildTextbookAssembleParams

新增:当传入 textbook_idgrade 为空时,从 textbooks 表按 textbook_id 查询 grade

if ($textbookId && $grade === null) {
    $grade = DB::table('textbooks')->where('id', $textbookId)->value('grade');
}

保证params 中同时传递 textbook_idgrade,供智能补充使用。


③ grade 推断:智能组卷 buildIntelligentAssembleParams

新增:同上,从教材推断 grade,保证智能补充可触发。


2.2 LearningAnalyticsService.php

① totalNeeded 传参修复(智能补充触发)

原逻辑:传入 $poolLimit(固定为 0),totalNeeded 恒为 0,智能补充条件 totalNeeded > 0 && count < totalNeeded 恒不成立
新逻辑:传入 $needCount = $totalQuestions - count($priorityQuestions),题目不足时可正确触发智能补充


② 智能补充范围:getSupplementaryQuestionsForGrade

补充范围策略

场景 补充来源 说明
textbookId getGradeKnowledgePoints(grade, textbookId) 同教材知识点
textbookId + textbookCatalogNodeIds 上述 + getEarlierChapterNodeIds 仅同教材前章节
textbookId + 有 studentId getStudentLearnedKpCodes(studentId) 仅学生已学知识点
textbookId + 无 studentId 不补充 无法确定学过的内容

③ 新增方法

  • getStudentLearnedKpCodes(?string $studentId)
    paper_questions + Paper + Question 获取该学生做过的题目的 kp_code,作为「已学知识点」。

  • getEarlierChapterNodeIds(int $textbookId, array $chapterNodeIds)
    sort_order <= max(选中章节) 筛选同教材节点,用于「前章节补充」。


④ 新增参数

getSupplementaryQuestionsForGrade 新增参数:

  • excludeQuestionIds:排除学生已做题目,避免重复
  • textbookCatalogNodeIds:教材组卷时限制为前章节
  • studentId:无教材时获取已学知识点

三、潜在风险与应对

风险点 分析 结论
新学生无做题记录 getStudentLearnedKpCodes 返回 [],gradeKpCodes 为空,不补充 ✅ 正确:宁可少题,不补未学内容
无教材且无 studentId 如教师预览卷子,不补充 ✅ 正确:无法确定学过的内容时不补充
textbooks 表无 grade value('grade') 返回 null,grade 为 null,智能补充不触发 ⚠️ 依赖数据:需保证教材配置完整
paper_questions.question_bank_id 为空 getStudentAnsweredQuestionIds 已过滤 whereNotNull> 0 ✅ 已处理
poolLimit 改为 needCount poolLimit=0 导致智能补充从不触发;现传入实际需要数量 ✅ 修复正确
getStudentAnsweredQuestionIds 的 $kpCodes 参数 仍保留参数,内部已不再使用 ✅ 兼容调用方,无影响

四、数据流校验

组卷请求
  → ExamTypeStrategy::buildParams
    → getStudentAnsweredQuestionIds(studentId) → exclude_question_ids
    → 教材组卷:grade 推断、textbook_id、textbook_catalog_node_ids
    → 智能组卷:grade 推断
  → LearningAnalyticsService::generateIntelligentExam
    → getQuestionsFromBank(kpCodes, ..., excludeQuestionIds, textbookCatalogNodeIds, grade, textbookId)
      → 主查询:whereNotIn('id', excludeQuestionIds)
      → 不足时:getSupplementaryQuestionsForGrade(grade, existingKps, deficit, ..., textbookId, excludeQuestionIds, textbookCatalogNodeIds, studentId)
        → 有教材:getGradeKnowledgePoints + getEarlierChapterNodeIds(若有章节)
        → 无教材:getStudentLearnedKpCodes(studentId)
      → whereNotIn('id', excludeQuestionIds) 再次排除

五、验证结论

  1. 去重:paper_132736368400673、paper_132736388400529、paper_132736538400759、paper_132736648500556 均无重复出题。
  2. 智能补充:paper_132736648500556(修复后)仅从学生已学 31 个知识点补充,无 10/12 年级或未学章节内容。
  3. 教材组卷textbook_idtextbookCatalogNodeIds 传递正确,前章节补充逻辑生效。
  4. totalNeeded:题目不足时可正常触发智能补充。

六、建议后续关注

  1. 教材配置:确认 textbooks.grade 有值,避免 grade 推断失败。
  2. 新学生首卷:无做题记录时可能题量不足,可在产品侧提示或放宽策略(若业务允许)。
  3. 题型分布:智能补充后填空可能偏少(取决于已学 KP 下填空题目数量),可视情况调整题型池或策略。