package nju.seec.helper.service.impl; import nju.seec.helper.dao.*; import nju.seec.helper.dto.QuestionStudentAnswerDTO; import nju.seec.helper.entity.*; import nju.seec.helper.service.QuizService; import nju.seec.helper.util.BokUtil; import nju.seec.helper.util.enums.ExceptionType; import nju.seec.helper.util.enums.QuizState; import nju.seec.helper.util.enums.SlideState; import nju.seec.helper.util.enums.UserType; import nju.seec.helper.util.exception.HelperException; import nju.seec.helper.vo.quiz.BaseQuestionVO; import nju.seec.helper.vo.quiz.QuizResultVO; 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.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import java.util.*; import java.util.stream.Collectors; @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 BokUtil bokUtil; @Autowired public QuizServiceImpl(SlideDAO slideDAO, QuizDAO quizDAO, QuizStudentAnswerDAO quizStudentAnswerDAO, ChooseDAO chooseDAO, CourseDAO courseDAO, UserDAO userDAO, BokUtil bokUtil) { this.slideDAO = slideDAO; this.quizDAO = quizDAO; this.quizStudentAnswerDAO = quizStudentAnswerDAO; this.chooseDAO = chooseDAO; this.courseDAO = courseDAO; this.userDAO = userDAO; this.bokUtil = bokUtil; } @Override @Transactional(rollbackFor = Exception.class) public Quiz createQuiz(String name, Long uid, SlideState quizTime, Long slideId, List questions){ User teacher =new User(); teacher.setId(uid); Slide slide = slideDAO.findSlideById(slideId); Course course = new Course(); course.setId(slide.getCourseId()); Quiz quiz = new Quiz(); quiz.setCourse(course); quiz.setName(name); quiz.setQuestions(questions); quiz.setQuizTime(quizTime); quiz.setSlide(slide); quiz.setTeacher(teacher); checkQuizState(quiz,slide); quiz=quizDAO.saveAndFlush(quiz); return quiz; } @Override public String deleteQuiz(Long quizId) { Quiz quiz =quizDAO.findQuizById(quizId); if(quiz.getState()!=QuizState.NOT_STARTED){throw HelperException.of(ExceptionType.FORBIDDEN,"不能删除已经开始的测试!");} quizDAO.delete(quiz); return "delete success"; } @Override @Transactional(rollbackFor = Exception.class) public Quiz modifyQuiz(Long quizId, String name, List questions, SlideState quizTime, String slideId) { Quiz quiz =quizDAO.findQuizById(quizId); if(quiz.getState()!=QuizState.NOT_STARTED){throw HelperException.of(ExceptionType.FORBIDDEN,"不能修改已经开始的测试!");} if(name!=null) { quiz.setName(name); } if(questions!=null) { quiz.setQuestions(questions); } if(quizTime!=null) { quiz.setQuizTime(quizTime); checkQuizState(quiz,quiz.getSlide()); } return quizDAO.saveAndFlush(quiz); } @Override public Page getAllBasicQuizByUser(Pageable pageable, QuizState state, Long uid) { User user=userDAO.findUserById(uid); //todo: 这里写的太狗屎了,可以用重写sql或者用实例查询 if(user.getType().equals(UserType.STUDENT)){ final Set courseIdsByStudentId = chooseDAO.findCourseIdsByStudentId(uid); final Set collect = courseIdsByStudentId.stream().map(i -> { Course c = new Course(); c.setId(i); return c; }).collect(Collectors.toSet()); if(state!=null){ return quizDAO.findAllByCourseInAndState(collect,state,pageable); }else{ return quizDAO.findAllByCourseIn(collect,pageable); } }else{ if(state!=null){ return quizDAO.findByTeacherAndState(user,state,pageable); }else{ return quizDAO.findByTeacher(user,pageable); } } } @Override public List getQuizResult(Long quizId) { Assert.notNull(quizId,"错误的测试!"); final Quiz quiz = quizDAO.findQuizById(quizId); if(quiz.getState()!=QuizState.CLOSED){throw new RuntimeException("测试未关闭,无法得到测试结果");} List retList=new ArrayList<>(); final List studentScoreList = quiz.getStudentScore(); Map> idQuizStudentAnswerMap=new HashMap<>(); for(QuizStudentAnswer qsa:quizStudentAnswerDAO.findAllByQuiz(quiz)){ idQuizStudentAnswerMap.computeIfAbsent(qsa.getStudent().getId(), k -> new ArrayList<>()); idQuizStudentAnswerMap.get(qsa.getStudent().getId()).add(qsa); } for(StudentScore studentScore:studentScoreList){ final User student = studentScore.getStudent(); final List list = idQuizStudentAnswerMap.get(student.getId()); retList.add(new QuizResultVO(studentScore,student,list)); } return retList; } @Override public void modifySlideState(Slide slide) { final List quizzes = quizDAO.findAllBySlide(slide); quizzes.forEach(quiz->checkQuizState(quiz,slide)); } @Override @Transactional(rollbackFor = Exception.class) public Integer upStudentAnswer(Long quizId, Long uid, List answers) { Assert.notNull(quizId,"错误的测试!"); final Quiz quiz = quizDAO.findQuizById(quizId); if(quiz.getState()!=QuizState.ONGOING){throw HelperException.of(ExceptionType.FORBIDDEN,"不能提交尚未开始的作业!");} User student =new User();student.setId(uid); Set quizAnswers = quizStudentAnswerDAO.findByStudentAndQuiz(student,quiz); Set questionIdSet = new HashSet<>(quiz.getQuestions()); Map qsaMap=new HashMap<>(questionIdSet.size()); for(QuizStudentAnswer qsa: quizAnswers){ qsaMap.put(qsa.getQuestionId(),qsa); } //确定被更新或者新建的学生答案提交 List quizStudentAnswers=new ArrayList<>(); for(QuestionStudentAnswerDTO studentAnswer:answers){ String id = studentAnswer.getQuestionId(); String answer = studentAnswer.getStudentAnswer().toString(); if(!questionIdSet.contains(id)){break;} if(qsaMap.get(id)!=null){ if(qsaMap.get(id).getStudentAnswer().equals(answer)){ continue; } qsaMap.get(id).setStudentAnswer(answer); System.out.println(answer); quizStudentAnswers.add(qsaMap.get(id)); }else { QuizStudentAnswer qsa=QuizStudentAnswer.builder() .questionId(id).quiz(quiz).student(student).studentAnswer(answer) .build(); quizStudentAnswers.add(qsa); } } Integer savedNum=quizStudentAnswers.size(); quizStudentAnswerDAO.saveAll(quizStudentAnswers); return savedNum; } @Override public Page getAllBasicQuizBySlide(Long slideId, Pageable pageable, QuizState state, Long uid) { Assert.notNull(slideId,"错误的幻灯片!"); Slide slide=new Slide(); slide.setId(slideId); if(state!=null) { return quizDAO.findAllBySlideAndState(slide, state, pageable); } else { return quizDAO.findAllBySlide(slide, pageable); } } @Override @Transactional(rollbackFor = Exception.class) public QuizState checkQuizState(Quiz quiz, Slide slide) { int quizTime = quiz.getQuizTime().getNum(); int slideTime = slide.getState().getNum(); if (quiz.getId() == null){ //未持久化的quiz quiz.setState(slideTime < quizTime ? QuizState.NOT_STARTED : QuizState.ONGOING); } else { //持久化后,不仅仅需要推进状态,还需要判分,保存等步骤 if (slideTime < quizTime) { if(quiz.getState().equals(QuizState.NOT_STARTED)){return QuizState.NOT_STARTED;} //未开始 quiz.setState(QuizState.NOT_STARTED); } else if (slideTime == quizTime) { if(quiz.getState().equals(QuizState.ONGOING)){return QuizState.ONGOING;} quiz.setState(QuizState.ONGOING); } else { if(quiz.getState().equals(QuizState.CLOSED)){return QuizState.CLOSED;} quiz.setState(QuizState.CLOSED); Set quizAnswers = quizStudentAnswerDAO.findAllByQuiz(quiz); final Set studentIds = chooseDAO.findStudentIdsByCourseId(slide.getCourseId()); Student2QuestionId2Answer triadMap = new Student2QuestionId2Answer(studentIds, quiz.getQuestions().size()); //将已经作答的学生答案加入 for (QuizStudentAnswer answers : quizAnswers) { triadMap.addAnswer(answers); } //生成未作答的学生答案 triadMap.fullAnswer(quiz); //检查答案,赋分 triadMap.checkAnswerAndFullPass(bokUtil.bokFindByIdIn(quiz.getQuestions()), quiz); //保存现场 quizStudentAnswerDAO.saveAll(triadMap.getAll()); } quizDAO.save(quiz); } return quiz.getState(); } @Override public QuizState checkQuizState(Long quizId, Long slideId){ Quiz quiz=quizDAO.findQuizById(quizId); Slide slide=slideDAO.findSlideById(slideId); return checkQuizState(quiz,slide); } private class Student2QuestionId2Answer { Map> student2QuestionId2Answer; Student2QuestionId2Answer(Set studentIds, Integer quizQuestionSize) { student2QuestionId2Answer =new LinkedHashMap<>(studentIds.size()); for(Long studentId:studentIds){ Map questionId2Answer= new LinkedHashMap<>(quizQuestionSize); student2QuestionId2Answer.put(studentId,questionId2Answer); } } void addAnswer(QuizStudentAnswer answers){ Map stringQuizStudentAnswerMap = student2QuestionId2Answer.get(answers.getStudent().getId()); if(stringQuizStudentAnswerMap==null){ return ; } stringQuizStudentAnswerMap.put(answers.getQuestionId(),answers); } void fullAnswer(Quiz quiz){ List questions=quiz.getQuestions(); student2QuestionId2Answer.forEach((studentId, map)->{ if(questions.size()!=map.size()){ for(String questionId:questions){ if(map.get(questionId)==null){ User student =new User();student.setId(studentId); QuizStudentAnswer qsa=QuizStudentAnswer.builder() .questionId(questionId).quiz(quiz).student(student) .build(); map.put(questionId,qsa); } } } }); } void checkAnswerAndFullPass(List questionVos, Quiz quiz){ if(quiz.getStudentScore()==null){quiz.setStudentScore(new ArrayList<>(student2QuestionId2Answer.size()));} student2QuestionId2Answer.forEach((studentId, map)->{ int passCount=0; for(BaseQuestionVO vo:questionVos){ final String studentAnswer = map.get(vo.getQuestionId()).getStudentAnswer(); boolean pass=vo.checkAnswer(studentAnswer); if(pass){passCount++;} map.get(vo.getQuestionId()).setPass(pass); } Double score = (100.0*passCount)/questionVos.size(); User student =new User();student.setId(studentId); StudentScore studentScore=new StudentScore(); studentScore.setQuizId(quiz.getId()); studentScore.setScore(Long.valueOf(Math.round(score)).intValue()); studentScore.setStudent(student); quiz.getStudentScore().add(studentScore); }); } List getAll(){ List list=new ArrayList<>(); for(Long sid: student2QuestionId2Answer.keySet()){ list.addAll(student2QuestionId2Answer.get(sid).values()); } return list; } } @Override public QuizVO combineQuiz(Long quizId){ Assert.notNull(quizId,"错误的测试!"); final Quiz quizById = quizDAO.findQuizById(quizId); final List questionVoS = bokUtil.bokFindByIdIn(quizById.getQuestions()); return new QuizVO(quizById,questionVoS); } @Override public QuizVO combineQuizWithAnswer(Long quizId, Long uid,UserType userType){ Assert.notNull(quizId,"错误的测试!"); final Quiz quizById = quizDAO.findQuizById(quizId); User student =new User(); student.setId(uid); if(userType==null||userType.equals(UserType.TEACHER)){ //向老师展示 List questionVoS = bokUtil.bokFindByIdIn(quizById.getQuestions()); return new QuizVO(quizById,questionVoS); } if(quizById.getState().equals(QuizState.NOT_STARTED)){ //未开始看不到题目 return new QuizVO(quizById); } List questionVos = bokUtil.bokFindByIdIn(quizById.getQuestions()); final Set quizAnswers = quizStudentAnswerDAO.findByStudentAndQuiz(student,quizById); Map qsaMap=new HashMap<>(quizAnswers.size()); for(QuizStudentAnswer qsa: quizAnswers){ qsaMap.put(qsa.getQuestionId(),qsa); } for(BaseQuestionVO vo:questionVos){ if(qsaMap.get(vo.getQuestionId())!=null){ vo.exAddAnswer(qsaMap.get(vo.getQuestionId())); } } return new QuizVO(quizById,questionVos); } }