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);
}
}