package nju.seec.helper.service.impl; import cn.hutool.core.util.IdUtil; import com.google.common.collect.Lists; import nju.seec.helper.aspect.auth.LoginUser; import nju.seec.helper.dao.*; import nju.seec.helper.dto.question.BaseQuestionDTO; import nju.seec.helper.dto.question.StudentAnswerDTO; import nju.seec.helper.entity.QuestionRecord; import nju.seec.helper.entity.Quiz; import nju.seec.helper.enums.ExceptionType; import nju.seec.helper.enums.QuizState; import nju.seec.helper.enums.UserRole; import nju.seec.helper.exception.HelperException; import nju.seec.helper.service.QuestionService; import nju.seec.helper.sys.bok.api.QuestionApi; import nju.seec.helper.sys.bok.api.QuestionQueryApi; import nju.seec.helper.sys.bok.api.RecordApi; import nju.seec.helper.sys.bok.api.UserInteractiveApi; import nju.seec.helper.sys.bok.dto.CollectDTO; import nju.seec.helper.sys.bok.dto.RateDTO; import nju.seec.helper.sys.bok.dto.RecordDTO; import nju.seec.helper.sys.bok.vo.InteractiveVO; import nju.seec.helper.sys.bok.vo.QuestionVO; import nju.seec.helper.sys.bok.vo.RecordQuestionVO; import nju.seec.helper.sys.dataanalysis.api.ExerciseApi; import nju.seec.helper.sys.dataanalysis.model.ExerciseRecord; import nju.seec.helper.sys.ml.api.RecommendationApi; import nju.seec.helper.vo.question.BaseQuestionVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * @author xst *

* updated by cst */ @Service public class QuestionServiceImpl implements QuestionService { private final QuizDAO quizDAO; private final UserDAO userDAO; private final QuestionRecordDAO questionRecordDAO; private final ChooseDAO chooseDAO; private final CourseDAO courseDAO; private final QuestionApi questionApi; private final QuestionQueryApi questionQueryApi; private final UserInteractiveApi userInteractiveApi; private final RecordApi recordApi; private final RecommendationApi recommendationApi; private final ExerciseApi exerciseApi; @Autowired public QuestionServiceImpl(QuizDAO quizDAO , UserDAO userDAO , QuestionRecordDAO questionRecordDAO , ChooseDAO chooseDAO , CourseDAO courseDAO , QuestionApi questionApi , QuestionQueryApi questionQueryApi , UserInteractiveApi userInteractiveApi , RecordApi recordApi , RecommendationApi recommendationApi , ExerciseApi exerciseApi ) { this.quizDAO = quizDAO; this.userDAO = userDAO; this.questionRecordDAO = questionRecordDAO; this.chooseDAO = chooseDAO; this.courseDAO = courseDAO; this.questionApi = questionApi; this.questionQueryApi = questionQueryApi; this.userInteractiveApi = userInteractiveApi; this.recordApi = recordApi; this.recommendationApi = recommendationApi; this.exerciseApi = exerciseApi; } @Override public Page getQuestions(LoginUser user, String stem, String tag, String knowledgeId, Pageable pageable) { return questionApi.getQuestionList(stem, tag, knowledgeId, pageable).map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true)); } @Transactional(readOnly = true) @Override public List getQuestionsByQuizId(LoginUser user, Long quizId) { Quiz quiz = quizDAO.findQuizById(quizId); return questionQueryApi.getQuestionListById(quiz.getQuestions()) .stream() .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, quiz.getState() == QuizState.CLOSED || user.getRole() == UserRole.TEACHER)) .collect(Collectors.toList()); } @Override public List getQuestionsByIds(List questionIds) { return questionQueryApi.getQuestionListById(questionIds) .stream() .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true)) .collect(Collectors.toList()); } @Override public BaseQuestionVO getOneQuestion(LoginUser user, String questionId) { return BaseQuestionVO.convertBokQuestionToVO(questionApi.getQuestionsById(questionId), true); } @Transactional(rollbackFor = Exception.class) @Override public BaseQuestionVO createQuestion(LoginUser user, BaseQuestionDTO baseQuestionDTO) { String questionId; do { // 保证不产生重复QuestionId questionId = IdUtil.fastSimpleUUID(); } while (questionRecordDAO.existsByQuestionId(questionId)); QuestionVO questionVO = questionApi.createQuestion(questionId, baseQuestionDTO); questionRecordDAO.save(new QuestionRecord() .setTeacher(userDAO.findUserById(user.getId())) .setQuestionId(questionVO.getId())); return BaseQuestionVO.convertBokQuestionToVO(questionVO, true); } @Transactional(readOnly = true) @Override public BaseQuestionVO modifyQuestion(LoginUser user, String questionId, BaseQuestionDTO baseQuestionDTO) { QuestionRecord questionRecord = questionRecordDAO.findByTeacherAndQuestionId(userDAO.findUserById(user.getId()), questionId) .orElseThrow(() -> HelperException.of(ExceptionType.FORBIDDEN, "您无权修改该问题")); QuestionVO questionVO = questionApi.updateQuestion(questionId, baseQuestionDTO); questionRecordDAO.save(questionRecord); return BaseQuestionVO.convertBokQuestionToVO(questionVO, true); } @Transactional(rollbackFor = Exception.class) @Override public void deleteQuestion(LoginUser user, String questionId) { QuestionRecord questionRecord = questionRecordDAO.findByTeacherAndQuestionId(userDAO.findUserById(user.getId()), questionId) .orElseThrow(() -> HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该问题")); questionApi.deleteQuestion(questionId); questionRecordDAO.delete(questionRecord); } @Transactional(readOnly = true) @Override public Page getCreatedQuestions(LoginUser user, Pageable pageable) { Page questionIdPage = questionRecordDAO.findQuestionIdsByTeacher(userDAO.findUserById(user.getId()), pageable); return new PageImpl<>( questionQueryApi.getQuestionListById(questionIdPage.getContent()) .stream() .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true)) .collect(Collectors.toList()) , pageable , questionIdPage.getTotalElements() ); } @Transactional(readOnly = true) @Override public Set getIdsOfCreatedQuestions(LoginUser user) { return questionRecordDAO.findQuestionIdsByTeacher(userDAO.findUserById(user.getId())); } @Override public void collectQuestion(LoginUser user, String questionId, CollectDTO collectDTO) { userInteractiveApi.collectQuestion(String.valueOf(user.getPid()), questionId, collectDTO); } @Override public void rateQuestion(LoginUser user, String questionId, RateDTO rateDTO) { userInteractiveApi.rateQuestion(String.valueOf(user.getPid()), questionId, rateDTO); } @Transactional(readOnly = true) @Override public Page getCollectedAndRatedQuestions(LoginUser user, Boolean collect, Boolean rate, Pageable pageable) { return convert(user, userInteractiveApi.getCollectAndRateQuestions(String.valueOf(user.getPid()), collect, rate, pageable)); } @Transactional(readOnly = true) @Override public Page getCompletedQuestions(LoginUser user, Pageable pageable) { RecordQuestionVO recordQuestionVO = recordApi.getUserRecordQuestions(String.valueOf(user.getPid()), pageable); return convert(user, new PageImpl<>(recordQuestionVO.getQuestions(), pageable, recordQuestionVO.getPage().getTotalElements())); } @Override public List getInteractiveList(LoginUser user, String questionId) { return userInteractiveApi.getInteractive(String.valueOf(user.getPid()), questionId); } @Override public List getRecommendationQuestions(LoginUser user, List knowledgeId, Integer questionNum) { return questionQueryApi.getQuestionListById(recommendationApi.getRecommendResult(String.valueOf(user.getPid()), knowledgeId, questionNum)) .stream() .map(questionVO -> BaseQuestionVO.convertBokQuestionToVO(questionVO, true)) .collect(Collectors.toList()); } @Transactional(readOnly = true) @Override public List getRecommendationQuestionsByCourse(LoginUser user, Long courseId, Integer questionNum) { return this.getRecommendationQuestions(user, courseDAO.findCourseById(courseId).getKnowledgeId(), questionNum); } @Override public void record(LoginUser user, StudentAnswerDTO studentAnswerDTO) { if (studentAnswerDTO.getRecords() == null || studentAnswerDTO.getRecords().isEmpty()) { return; } Map recordMap = studentAnswerDTO.getRecords() .entrySet() .stream() .filter(entry -> entry.getValue().getStartAt().isBefore(entry.getValue().getEndAt())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); if (recordMap.isEmpty()) { return; } List baseQuestionVOList = questionQueryApi.getQuestionListById(Lists.newArrayList(recordMap.keySet())) .stream() .map(questionVO -> BaseQuestionVO.convertBokQuestionToVO(questionVO, true)) .collect(Collectors.toList()); // 创建两个列表 int size = recordMap.size(); List exerciseRecords = Lists.newArrayListWithExpectedSize(size); List records = Lists.newArrayListWithExpectedSize(size); // 填充列表 String userId = String.valueOf(user.getPid()); baseQuestionVOList.forEach(baseQuestionVO -> { String questionId = baseQuestionVO.getId(); StudentAnswerDTO.AnswerRecordDTO answerRecord = recordMap.get(questionId); Object answer = answerRecord.getAnswer(); Boolean pass = baseQuestionVO.pass(answer); exerciseRecords.add(exerciseApi.createExerciseRecordDTO(userId, questionId, answerRecord.getStartAt(), answerRecord.getEndAt(), pass)); records.add(recordApi.createRecordDTO(userId, questionId, answer, pass, answerRecord.getStartAt(), answerRecord.getEndAt())); }); exerciseApi.reportExercise(exerciseRecords); recordApi.reportUserRecord(records); } private static final Set HIDE_QUESTION_ANSWER_STATES = Collections.singleton(QuizState.ONGOING); private Page convert(LoginUser user, Page questionVOList) { List hideQuestionIds = quizDAO.findByCourseIdInAndStateIn(chooseDAO.findCourseIdsByStudentId(user.getId()), HIDE_QUESTION_ANSWER_STATES) .stream() .flatMap(quiz -> quiz.getQuestions().stream()) .collect(Collectors.toList()); return questionVOList.map(questionVO -> BaseQuestionVO.convertBokQuestionToVO(questionVO, !hideQuestionIds.contains(questionVO.getId()))); } }