| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- package com.xuekairui.quiz.controller.user;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.xuekairui.common.Result;
- import com.xuekairui.quiz.entity.QuizChapter;
- import com.xuekairui.quiz.entity.QuizQuestion;
- import com.xuekairui.quiz.entity.QuizUserRecord;
- import com.xuekairui.quiz.mapper.QuizChapterMapper;
- import com.xuekairui.quiz.mapper.QuizQuestionMapper;
- import com.xuekairui.quiz.mapper.QuizUserRecordMapper;
- import com.xuekairui.quiz.vo.UserChapterNodeVO;
- import jakarta.servlet.http.HttpServletRequest;
- import lombok.RequiredArgsConstructor;
- import org.springframework.web.bind.annotation.*;
- import java.util.*;
- import java.util.stream.Collectors;
- @RestController
- @RequestMapping("/api/quiz/chapter")
- @RequiredArgsConstructor
- public class UserQuizChapterController {
- private final QuizChapterMapper chapterMapper;
- private final QuizUserRecordMapper userRecordMapper;
- private final QuizQuestionMapper questionMapper;
- @GetMapping("/list")
- public Result<List<UserChapterNodeVO>> list(@RequestParam Long subjectId, HttpServletRequest request) {
- Long userId = (Long) request.getAttribute("userId");
- List<QuizChapter> chapters = chapterMapper.selectList(
- new LambdaQueryWrapper<QuizChapter>()
- .eq(QuizChapter::getSubjectId, subjectId)
- .orderByAsc(QuizChapter::getSort));
- // 后台在"科目"下新增的章 parentId=科目ID;移动端省略科目层,统一归一化为 0 作为根节点
- for (QuizChapter ch : chapters) {
- if (subjectId.equals(ch.getParentId())) {
- ch.setParentId(0L);
- }
- }
- // 实时查询各考点(exam_point_id)直接关联的题目数
- Map<Long, Integer> directCountMap = getDirectQuestionCounts(subjectId);
- List<UserChapterNodeVO> tree = buildUserTree(chapters, 0L, userId, directCountMap);
- return Result.success(tree);
- }
- private List<UserChapterNodeVO> buildUserTree(List<QuizChapter> chapters, Long parentId, Long userId, Map<Long, Integer> directCountMap) {
- List<UserChapterNodeVO> result = new ArrayList<>();
- for (QuizChapter ch : chapters) {
- if (Objects.equals(ch.getParentId(), parentId)) {
- long doneCount = userRecordMapper.selectCount(
- new LambdaQueryWrapper<QuizUserRecord>()
- .eq(QuizUserRecord::getUserId, userId)
- .eq(QuizUserRecord::getExamPointId, ch.getId())
- .eq(QuizUserRecord::getStatus, 1));
- List<UserChapterNodeVO> children = null;
- // 只展开到"节"(level=3),不再往下展示"考点"(level=4)
- if (ch.getLevel() != null && ch.getLevel() < 3) {
- List<UserChapterNodeVO> subChildren = buildUserTree(chapters, ch.getId(), userId, directCountMap);
- if (!subChildren.isEmpty()) {
- children = subChildren;
- }
- }
- // 汇总本节点及所有子孙节点的题目数(实时查询,不依赖缓存字段)
- int totalQuestionCount = sumQuestionCount(chapters, ch.getId(), directCountMap)
- + directCountMap.getOrDefault(ch.getId(), 0);
- result.add(UserChapterNodeVO.builder()
- .id(ch.getId())
- .name(ch.getName())
- .level(ch.getLevel())
- .questionCount(totalQuestionCount)
- .doneCount(doneCount)
- .children(children)
- .build());
- }
- }
- return result;
- }
- /** 递归汇总所有子孙节点的题目数(实时查询) */
- private int sumQuestionCount(List<QuizChapter> chapters, Long parentId, Map<Long, Integer> directCountMap) {
- int sum = 0;
- for (QuizChapter ch : chapters) {
- if (Objects.equals(ch.getParentId(), parentId)) {
- sum += directCountMap.getOrDefault(ch.getId(), 0)
- + sumQuestionCount(chapters, ch.getId(), directCountMap);
- }
- }
- return sum;
- }
- /** 批量查询指定科目下各考点直接关联的练习题数(不含真题) */
- private Map<Long, Integer> getDirectQuestionCounts(Long subjectId) {
- List<QuizQuestion> questions = questionMapper.selectList(
- new LambdaQueryWrapper<QuizQuestion>()
- .eq(QuizQuestion::getSubjectId, subjectId)
- .eq(QuizQuestion::getIsReal, 0)
- .select(QuizQuestion::getExamPointId)
- );
- Map<Long, Integer> map = new HashMap<>();
- for (QuizQuestion q : questions) {
- if (q.getExamPointId() != null) {
- map.merge(q.getExamPointId(), 1, Integer::sum);
- }
- }
- return map;
- }
- }
|