package nju.seec.helper.service.impl; import com.google.common.collect.ImmutableSet; import nju.seec.helper.dao.*; import nju.seec.helper.dto.quiz.QuizDTO; import nju.seec.helper.aspect.auth.LoginUser; import nju.seec.helper.entity.Course; import nju.seec.helper.entity.Quiz; import nju.seec.helper.entity.Slide; import nju.seec.helper.service.MessageService; import nju.seec.helper.service.QuizService; import nju.seec.helper.service.util.AuthUtils; import nju.seec.helper.service.util.StringUtils; import nju.seec.helper.enums.*; import nju.seec.helper.exception.HelperException; import nju.seec.helper.vo.quiz.QuizVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Set; /** * @author xst *

* updated by cst */ @Service public class QuizServiceImpl implements QuizService { private final SlideDAO slideDAO; private final QuizDAO quizDAO; private final QuizStudentAnswerDAO quizStudentAnswerDAO; private final ChooseDAO chooseDAO; private final CourseDAO courseDAO; private final UserDAO userDAO; private final MessageService messageService; @Autowired public QuizServiceImpl(SlideDAO slideDAO, QuizDAO quizDAO, QuizStudentAnswerDAO quizStudentAnswerDAO, ChooseDAO chooseDAO, CourseDAO courseDAO, UserDAO userDAO, MessageService messageService) { this.slideDAO = slideDAO; this.quizDAO = quizDAO; this.quizStudentAnswerDAO = quizStudentAnswerDAO; this.chooseDAO = chooseDAO; this.courseDAO = courseDAO; this.userDAO = userDAO; this.messageService = messageService; } @Transactional(rollbackFor = Exception.class) @Override public QuizVO createQuiz(LoginUser user, QuizDTO quizDTO) { Slide slide = slideDAO.findSlideById(quizDTO.getSlideId()); AuthUtils.checkDataAuth(user.getId(), slide.getTeacher().getId(), "您无权创建该课件的测试"); checkSameName(0L, quizDTO.getSlideId(), quizDTO.getName()); checkQuizTypeOkToSlideState(slide.getState(), quizDTO.getType()); Quiz quiz = new Quiz() .setCourse(slide.getCourse()) .setSlide(slide) .setTeacher(slide.getTeacher()) .setType(quizDTO.getType()) .setState(QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quizDTO.getType())) .setName(quizDTO.getName()) .setMaxSubmitNumber(quizDTO.getMaxSubmitTime()) .setQuestions(quizDTO.getQuestions()); return new QuizVO(quizDAO.save(quiz)); } @Transactional(rollbackFor = Exception.class) @Override public QuizVO modifyQuiz(LoginUser user, Long quizId, QuizDTO quizDTO) { Quiz quiz = quizDAO.findQuizById(quizId); AuthUtils.checkDataAuth(user.getId(), quiz.getTeacher().getId(), "您无权修改该测试"); checkSubmit(quiz); Slide slide = quiz.getSlide(); checkQuizTypeOkToSlideState(slide.getState(), quizDTO.getType()); checkSameName(quiz.getId(), quizDTO.getSlideId(), quizDTO.getName()); quiz.setType(quizDTO.getType()) .setState(QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quizDTO.getType())) .setName(quizDTO.getName()) .setMaxSubmitNumber(quizDTO.getMaxSubmitTime()) .setQuestions(quizDTO.getQuestions()); return new QuizVO(quizDAO.save(quiz)); } private void checkSameName(Long quizId, Long slideId, String name) { long exists = quizDAO.count((Specification) (root, query, cb) -> cb.and(cb.notEqual(root.get("id"), quizId), cb.equal(root.get("slide").get("id"), slideId), cb.equal(root.get("name"), name) )); if (exists > 0) { throw HelperException.of(ExceptionType.CONFLICT, "该测试名已使用"); } } private void checkQuizTypeOkToSlideState(SlideState slideState, QuizType quizType) { if (!QuizType.getQuizTypesBySlideState(slideState).contains(quizType)) { throw HelperException.of(ExceptionType.FORBIDDEN, "课件暂时不支持创建该类型的测试,请检查课件状态"); } } private void checkSubmit(Quiz quiz) { if (quizStudentAnswerDAO.existsByQuiz(quiz)) { throw HelperException.of(ExceptionType.FORBIDDEN, "该测试已有提交,无法修改"); } } @Transactional(rollbackFor = Exception.class) @Override public void deleteQuiz(LoginUser user, Long quizId) { Quiz quiz = quizDAO.findQuizById(quizId); AuthUtils.checkDataAuth(user.getId(), quiz.getTeacher().getId(), "您无权删除该测试"); quiz.setDeleteAt(System.currentTimeMillis()); quizDAO.save(quiz); } @Transactional(readOnly = true) @Override public Page teacherGetQuizzesBySlide(LoginUser user, Long slideId, String key, Pageable pageable) { return findBySlideIdAndKey(slideId, key, pageable).map(QuizVO::new); } public static final Set STATES_NOT_GET_BY_STUDENT_SET = ImmutableSet.of(QuizState.NOT_STARTED); @Transactional(readOnly = true) @Override public Page studentGetQuizzesBySlide(LoginUser user, Long slideId, String key, Pageable pageable) { return findBySlideIdAndStateNotInAndKey(slideId, STATES_NOT_GET_BY_STUDENT_SET, key, pageable).map(QuizVO::new); } @Transactional(readOnly = true) @Override public Page studentGetQuizzesByState(LoginUser user, QuizState state, String key, Pageable pageable) { if (STATES_NOT_GET_BY_STUDENT_SET.contains(state)) { throw HelperException.of(ExceptionType.FORBIDDEN, "您暂时无法获取该状态的测试"); } Set courseIds = chooseDAO.findCourseIdsByStudentId(user.getId()); return findByCourseIdInAndStateAndKey(courseIds, state, key, pageable).map(QuizVO::new); } @Transactional(readOnly = true) @Override public QuizVO getOneQuiz(LoginUser user, Long quizId) { Quiz quiz = quizDAO.findQuizById(quizId); checkDataAccessAuth(user, quiz); return new QuizVO(quiz); } @Transactional(rollbackFor = Exception.class) @Override public void modifyQuizState(Slide slide) { List quizzes = quizDAO.findAllBySlide(slide); Course course = slide.getCourse(); Set chooseStudentIds = chooseDAO.findStudentIdsByCourseId(course.getId()); quizzes.forEach(quiz -> { QuizState quizState = QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quiz.getType()); quiz.setState(quizState); if (quizState == QuizState.ONGOING) { messageService.createMessage(chooseStudentIds, MessageType.QUIZ_NEW, String.format("[%s] - [%s] 的测试 [%s] 已开放", course.getName(), slide.getName(), quiz.getName()), slide.getId()); } }); } private void checkDataAccessAuth(LoginUser user, Quiz quiz) { HelperException forbiddenEx = HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该测试"); switch (user.getType()) { case TEACHER: break; case STUDENT: if (quiz.getState() == QuizState.NOT_STARTED) { throw forbiddenEx; } break; default: throw forbiddenEx; } } private Page findByCourseIdInAndStateAndKey(Set courseIds, QuizState state, String key, Pageable pageable) { return quizDAO.findAll( (Specification) (root, query, cb) -> cb.and( root.get("course").get("id").in(courseIds) , cb.equal(root.get("deleteAt"), 0L) , cb.equal(root.get("state"), state) , cb.like(root.get("name"), StringUtils.keyPattern(key)) , cb.equal(root.get("slide").get("deleteAt"), 0L) , cb.equal(root.get("course").get("deleteAt"), 0L) ), pageable); } private Page findBySlideIdAndKey(Long slideId, String key, Pageable pageable) { return quizDAO.findAll( (Specification) (root, query, cb) -> cb.and( cb.equal(root.get("slide").get("id"), slideId) , cb.equal(root.get("deleteAt"), 0L) , cb.like(root.get("name"), StringUtils.keyPattern(key)) , cb.equal(root.get("slide").get("deleteAt"), 0L) , cb.equal(root.get("course").get("deleteAt"), 0L) ), pageable); } private Page findBySlideIdAndStateNotInAndKey(Long slideId, Set exclusiveStates, String key, Pageable pageable) { return quizDAO.findAll( (Specification) (root, query, cb) -> cb.and( cb.equal(root.get("slide").get("id"), slideId) , cb.equal(root.get("deleteAt"), 0L) , cb.like(root.get("name"), StringUtils.keyPattern(key)) , cb.not(root.get("state").in(exclusiveStates)) , cb.equal(root.get("slide").get("deleteAt"), 0L) , cb.equal(root.get("course").get("deleteAt"), 0L) ), pageable); } }