UserQuizChapterController.java 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package com.xuekairui.quiz.controller.user;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.xuekairui.common.Result;
  4. import com.xuekairui.quiz.entity.QuizChapter;
  5. import com.xuekairui.quiz.entity.QuizQuestion;
  6. import com.xuekairui.quiz.entity.QuizUserRecord;
  7. import com.xuekairui.quiz.mapper.QuizChapterMapper;
  8. import com.xuekairui.quiz.mapper.QuizQuestionMapper;
  9. import com.xuekairui.quiz.mapper.QuizUserRecordMapper;
  10. import com.xuekairui.quiz.vo.UserChapterNodeVO;
  11. import jakarta.servlet.http.HttpServletRequest;
  12. import lombok.RequiredArgsConstructor;
  13. import org.springframework.web.bind.annotation.*;
  14. import java.util.*;
  15. import java.util.stream.Collectors;
  16. @RestController
  17. @RequestMapping("/api/quiz/chapter")
  18. @RequiredArgsConstructor
  19. public class UserQuizChapterController {
  20. private final QuizChapterMapper chapterMapper;
  21. private final QuizUserRecordMapper userRecordMapper;
  22. private final QuizQuestionMapper questionMapper;
  23. @GetMapping("/list")
  24. public Result<List<UserChapterNodeVO>> list(@RequestParam Long subjectId, HttpServletRequest request) {
  25. Long userId = (Long) request.getAttribute("userId");
  26. List<QuizChapter> chapters = chapterMapper.selectList(
  27. new LambdaQueryWrapper<QuizChapter>()
  28. .eq(QuizChapter::getSubjectId, subjectId)
  29. .orderByAsc(QuizChapter::getSort));
  30. // 后台在"科目"下新增的章 parentId=科目ID;移动端省略科目层,统一归一化为 0 作为根节点
  31. for (QuizChapter ch : chapters) {
  32. if (subjectId.equals(ch.getParentId())) {
  33. ch.setParentId(0L);
  34. }
  35. }
  36. // 实时查询各考点(exam_point_id)直接关联的题目数
  37. Map<Long, Integer> directCountMap = getDirectQuestionCounts(subjectId);
  38. List<UserChapterNodeVO> tree = buildUserTree(chapters, 0L, userId, directCountMap);
  39. return Result.success(tree);
  40. }
  41. private List<UserChapterNodeVO> buildUserTree(List<QuizChapter> chapters, Long parentId, Long userId, Map<Long, Integer> directCountMap) {
  42. List<UserChapterNodeVO> result = new ArrayList<>();
  43. for (QuizChapter ch : chapters) {
  44. if (Objects.equals(ch.getParentId(), parentId)) {
  45. long doneCount = userRecordMapper.selectCount(
  46. new LambdaQueryWrapper<QuizUserRecord>()
  47. .eq(QuizUserRecord::getUserId, userId)
  48. .eq(QuizUserRecord::getExamPointId, ch.getId())
  49. .eq(QuizUserRecord::getStatus, 1));
  50. List<UserChapterNodeVO> children = null;
  51. // 只展开到"节"(level=3),不再往下展示"考点"(level=4)
  52. if (ch.getLevel() != null && ch.getLevel() < 3) {
  53. List<UserChapterNodeVO> subChildren = buildUserTree(chapters, ch.getId(), userId, directCountMap);
  54. if (!subChildren.isEmpty()) {
  55. children = subChildren;
  56. }
  57. }
  58. // 汇总本节点及所有子孙节点的题目数(实时查询,不依赖缓存字段)
  59. int totalQuestionCount = sumQuestionCount(chapters, ch.getId(), directCountMap)
  60. + directCountMap.getOrDefault(ch.getId(), 0);
  61. result.add(UserChapterNodeVO.builder()
  62. .id(ch.getId())
  63. .name(ch.getName())
  64. .level(ch.getLevel())
  65. .questionCount(totalQuestionCount)
  66. .doneCount(doneCount)
  67. .children(children)
  68. .build());
  69. }
  70. }
  71. return result;
  72. }
  73. /** 递归汇总所有子孙节点的题目数(实时查询) */
  74. private int sumQuestionCount(List<QuizChapter> chapters, Long parentId, Map<Long, Integer> directCountMap) {
  75. int sum = 0;
  76. for (QuizChapter ch : chapters) {
  77. if (Objects.equals(ch.getParentId(), parentId)) {
  78. sum += directCountMap.getOrDefault(ch.getId(), 0)
  79. + sumQuestionCount(chapters, ch.getId(), directCountMap);
  80. }
  81. }
  82. return sum;
  83. }
  84. /** 批量查询指定科目下各考点直接关联的练习题数(不含真题) */
  85. private Map<Long, Integer> getDirectQuestionCounts(Long subjectId) {
  86. List<QuizQuestion> questions = questionMapper.selectList(
  87. new LambdaQueryWrapper<QuizQuestion>()
  88. .eq(QuizQuestion::getSubjectId, subjectId)
  89. .eq(QuizQuestion::getIsReal, 0)
  90. .select(QuizQuestion::getExamPointId)
  91. );
  92. Map<Long, Integer> map = new HashMap<>();
  93. for (QuizQuestion q : questions) {
  94. if (q.getExamPointId() != null) {
  95. map.merge(q.getExamPointId(), 1, Integer::sum);
  96. }
  97. }
  98. return map;
  99. }
  100. }