Explorar el Código

feat: 增加获取测试学生作答的统计数据

ChenSiTong hace 6 años
padre
commit
274e75c1b8

+ 9 - 2
src/main/java/nju/seec/helper/controller/QuizController.java

@@ -11,6 +11,7 @@ import nju.seec.helper.enums.QuizState;
 import nju.seec.helper.enums.UserType;
 import nju.seec.helper.service.QuizService;
 import nju.seec.helper.service.QuizStudentAnswerService;
+import nju.seec.helper.vo.QuizStudentAnswerStatisticVO;
 import nju.seec.helper.vo.quiz.QuizStudentAnswerVO;
 import nju.seec.helper.vo.quiz.QuizVO;
 import org.springframework.data.domain.Pageable;
@@ -125,7 +126,7 @@ public class QuizController {
     @Auth(roles = {UserType.TEACHER}, message = "获取学生作答")
     @GetMapping("/{quizId}/student-answers")
     public PageResponse<QuizStudentAnswerVO> getQuizStudentAnswers(LoginUser user, @PathVariable Long quizId, @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
-        return PageResponse.of(quizStudentAnswerService.teacherGetQuizStudentAnswers(user, quizId, pageable));
+        return PageResponse.of(quizStudentAnswerService.getQuizStudentAnswers(user, quizId, pageable));
     }
 
     /**
@@ -138,6 +139,12 @@ public class QuizController {
     @Auth(roles = {UserType.STUDENT}, message = "获取作答")
     @GetMapping("/{quizId}/student-answer")
     public QuizStudentAnswerVO getOneQuizStudentAnswer(LoginUser user, @PathVariable Long quizId) {
-        return quizStudentAnswerService.studentGetSelfQuizStudentAnswer(user, quizId);
+        return quizStudentAnswerService.getQuizStudentAnswer(user, quizId);
+    }
+
+    @Auth(roles = {UserType.TEACHER}, message = "获取测试统计消息")
+    @GetMapping("/{quizId}/statistic")
+    public QuizStudentAnswerStatisticVO getQuizStatistic(LoginUser user, @PathVariable Long quizId) {
+        return quizStudentAnswerService.getQuizStudentAnswerStatistic(user, quizId);
     }
 }

+ 9 - 8
src/main/java/nju/seec/helper/dao/QuizStudentAnswerDAO.java

@@ -8,6 +8,7 @@ import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
+import java.util.List;
 import java.util.Optional;
 
 /**
@@ -35,7 +36,7 @@ public interface QuizStudentAnswerDAO extends JpaRepository<QuizStudentAnswer, L
     Optional<QuizStudentAnswer> findByQuizAndStudent(Quiz quiz, User student);
 
     /**
-     * 根据测试检索
+     * 根据测试分页检索
      *
      * @param quiz
      * @param pageable
@@ -43,11 +44,11 @@ public interface QuizStudentAnswerDAO extends JpaRepository<QuizStudentAnswer, L
      */
     Page<QuizStudentAnswer> findByQuiz(Quiz quiz, Pageable pageable);
 
-//    Set<QuizStudentAnswer> findAllByQuiz(Quiz quiz);
-//
-//    Set<QuizStudentAnswer> findAllByStudent(User student);
-//
-//    Set<QuizStudentAnswer> findByStudentAndQuiz(User student, Quiz quiz);
-//
-//    Set<QuizStudentAnswer> findByStudentAndQuestionId(User student, String questionId);
+    /**
+     * 根据测试检索
+     *
+     * @param quiz
+     * @return
+     */
+    List<QuizStudentAnswer> findByQuiz(Quiz quiz);
 }

+ 1 - 1
src/main/java/nju/seec/helper/service/CourseService.java

@@ -120,5 +120,5 @@ public interface CourseService {
      * @param courseId
      * @return
      */
-    boolean existsCourseResourcesGetAuth(LoginUser user, Long courseId);
+    boolean existsCourseResourcesAuth(LoginUser user, Long courseId);
 }

+ 12 - 2
src/main/java/nju/seec/helper/service/QuizStudentAnswerService.java

@@ -2,6 +2,7 @@ package nju.seec.helper.service;
 
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.quiz.QuizStudentAnswerDTO;
+import nju.seec.helper.vo.QuizStudentAnswerStatisticVO;
 import nju.seec.helper.vo.quiz.QuizStudentAnswerVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -28,7 +29,7 @@ public interface QuizStudentAnswerService {
      * @param pageable
      * @return
      */
-    Page<QuizStudentAnswerVO> teacherGetQuizStudentAnswers(LoginUser user, Long quizId, Pageable pageable);
+    Page<QuizStudentAnswerVO> getQuizStudentAnswers(LoginUser user, Long quizId, Pageable pageable);
 
     /**
      * 学生查看自己的答案
@@ -37,5 +38,14 @@ public interface QuizStudentAnswerService {
      * @param quizId
      * @return
      */
-    QuizStudentAnswerVO studentGetSelfQuizStudentAnswer(LoginUser user, Long quizId);
+    QuizStudentAnswerVO getQuizStudentAnswer(LoginUser user, Long quizId);
+
+    /**
+     * 获取测试作答的统计信息
+     *
+     * @param user
+     * @param quizId
+     * @return
+     */
+    QuizStudentAnswerStatisticVO getQuizStudentAnswerStatistic(LoginUser user, Long quizId);
 }

+ 2 - 2
src/main/java/nju/seec/helper/service/impl/CourseFileServiceImpl.java

@@ -83,7 +83,7 @@ public class CourseFileServiceImpl implements CourseFileService {
     @Transactional(readOnly = true)
     @Override
     public Page<CourseFileVO> getCourseFilesByCourse(LoginUser user, Long courseId, String key, Pageable pageable) {
-        if (!courseService.existsCourseResourcesGetAuth(user, courseId)) {
+        if (!courseService.existsCourseResourcesAuth(user, courseId)) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该课程的附件");
         }
         return courseFileDAO.findByCourseIdAndFileNameContains(courseId, key, pageable).map(CourseFileVO::new);
@@ -94,7 +94,7 @@ public class CourseFileServiceImpl implements CourseFileService {
     public String getCourseFileUrl(LoginUser user, Long courseFileId) {
         CourseFile courseFile = courseFileDAO.findCourseFileById(courseFileId);
 
-        if (!courseService.existsCourseResourcesGetAuth(user, courseFile.getCourseId())) {
+        if (!courseService.existsCourseResourcesAuth(user, courseFile.getCourseId())) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权获取该附件的链接");
         }
 

+ 1 - 1
src/main/java/nju/seec/helper/service/impl/CourseServiceImpl.java

@@ -143,7 +143,7 @@ public class CourseServiceImpl implements CourseService {
     }
 
     @Override
-    public boolean existsCourseResourcesGetAuth(LoginUser user, Long courseId) {
+    public boolean existsCourseResourcesAuth(LoginUser user, Long courseId) {
         switch (user.getType()) {
             case TEACHER:
                 return courseDAO.findCourseById(courseId).getTeacher().getId().equals(user.getId());

+ 1 - 0
src/main/java/nju/seec/helper/service/impl/MessageServiceImpl.java

@@ -45,6 +45,7 @@ public class MessageServiceImpl implements MessageService {
         messageDAO.deleteByToUserIdAndIdIn(user.getId(), messageDTO.getIds());
     }
 
+    @Async
     @Override
     public void deleteMessages(Long refId, MessageType messageType) {
         messageDAO.deleteByRefIdAndType(refId, messageType);

+ 29 - 17
src/main/java/nju/seec/helper/service/impl/QuizServiceImpl.java

@@ -2,7 +2,10 @@ package nju.seec.helper.service.impl;
 
 import com.google.common.collect.ImmutableSet;
 import nju.seec.helper.aspect.auth.LoginUser;
-import nju.seec.helper.dao.*;
+import nju.seec.helper.dao.ChooseDAO;
+import nju.seec.helper.dao.QuizDAO;
+import nju.seec.helper.dao.QuizStudentAnswerDAO;
+import nju.seec.helper.dao.SlideDAO;
 import nju.seec.helper.dto.quiz.QuizDTO;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.entity.Quiz;
@@ -36,19 +39,15 @@ public class QuizServiceImpl implements QuizService {
     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) {
+    public QuizServiceImpl(SlideDAO slideDAO, QuizDAO quizDAO, QuizStudentAnswerDAO quizStudentAnswerDAO, ChooseDAO chooseDAO, MessageService messageService) {
         this.slideDAO = slideDAO;
         this.quizDAO = quizDAO;
         this.quizStudentAnswerDAO = quizStudentAnswerDAO;
         this.chooseDAO = chooseDAO;
-        this.courseDAO = courseDAO;
-        this.userDAO = userDAO;
         this.messageService = messageService;
     }
 
@@ -61,15 +60,18 @@ public class QuizServiceImpl implements QuizService {
         checkSameName(0L, quizDTO.getSlideId(), quizDTO.getName());
         checkQuizTypeOkToSlideState(slide.getState(), quizDTO.getType());
 
-        return new QuizVO(quizDAO.save(new Quiz()
+        QuizState quizState = QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quizDTO.getType());
+        Quiz quiz = quizDAO.save(new Quiz()
                 .setCourse(slide.getCourse())
                 .setSlide(slide)
                 .setTeacher(slide.getTeacher())
                 .setType(quizDTO.getType())
-                .setState(QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quizDTO.getType()))
+                .setState(quizState)
                 .setName(quizDTO.getName())
                 .setMaxSubmitNumber(quizDTO.getMaxSubmitTime())
-                .setQuestions(quizDTO.getQuestions())));
+                .setQuestions(quizDTO.getQuestions()));
+        sendMessages(quiz);
+        return new QuizVO(quiz);
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -85,11 +87,15 @@ public class QuizServiceImpl implements QuizService {
 
         checkSameName(quiz.getId(), quizDTO.getSlideId(), quizDTO.getName());
 
-        return new QuizVO(quizDAO.save(quiz.setType(quizDTO.getType())
-                .setState(QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quizDTO.getType()))
+        QuizState quizState = QuizState.getQuizStateBySlideStateAndQuizType(slide.getState(), quizDTO.getType());
+        quiz = quizDAO.save(quiz
+                .setType(quizDTO.getType())
+                .setState(quizState)
                 .setName(quizDTO.getName())
                 .setMaxSubmitNumber(quizDTO.getMaxSubmitTime())
-                .setQuestions(quizDTO.getQuestions())));
+                .setQuestions(quizDTO.getQuestions()));
+        sendMessages(quiz);
+        return new QuizVO(quiz);
     }
 
     private void checkSameName(Long quizId, Long slideId, String name) {
@@ -122,6 +128,7 @@ public class QuizServiceImpl implements QuizService {
         AuthUtils.checkDataAuth(user.getId(), quiz.getTeacher().getId(), "您无权删除该测试");
         quiz.setDeleteAt(System.currentTimeMillis());
         quizDAO.save(quiz);
+        messageService.deleteMessages(quizId, MessageType.QUIZ_NEW);
     }
 
     @Transactional(readOnly = true)
@@ -160,17 +167,22 @@ public class QuizServiceImpl implements QuizService {
     @Override
     public void modifyQuizState(Slide slide) {
         List<Quiz> quizzes = quizDAO.findAllBySlide(slide);
-        Course course = slide.getCourse();
-        Set<Long> 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());
-            }
+            sendMessages(quiz);
         });
     }
 
+    private void sendMessages(Quiz quiz) {
+        Slide slide = quiz.getSlide();
+        Course course = slide.getCourse();
+        Set<Long> chooseStudentIds = chooseDAO.findStudentIdsByCourseId(course.getId());
+        if (quiz.getState() == QuizState.ONGOING) {
+            messageService.createMessage(chooseStudentIds, MessageType.QUIZ_NEW, String.format("[%s] - [%s] 的测试 [%s] 已开放", course.getName(), slide.getName(), quiz.getName()), quiz.getId());
+        }
+    }
+
     private void checkDataAccessAuth(LoginUser user, Quiz quiz) {
         HelperException forbiddenEx = HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该测试");
 

+ 30 - 3
src/main/java/nju/seec/helper/service/impl/QuizStudentAnswerServiceImpl.java

@@ -1,5 +1,6 @@
 package nju.seec.helper.service.impl;
 
+import com.google.common.collect.Maps;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dao.QuizDAO;
 import nju.seec.helper.dao.QuizStudentAnswerDAO;
@@ -10,8 +11,10 @@ import nju.seec.helper.entity.QuizStudentAnswer;
 import nju.seec.helper.entity.User;
 import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.exception.HelperException;
+import nju.seec.helper.service.CourseService;
 import nju.seec.helper.service.QuestionService;
 import nju.seec.helper.service.QuizStudentAnswerService;
+import nju.seec.helper.vo.QuizStudentAnswerStatisticVO;
 import nju.seec.helper.vo.question.BaseQuestionVO;
 import nju.seec.helper.vo.quiz.QuizStudentAnswerVO;
 import org.springframework.data.domain.Page;
@@ -35,12 +38,14 @@ public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
     private final QuizStudentAnswerDAO quizStudentAnswerDAO;
 
     private final QuestionService questionService;
+    private final CourseService courseService;
 
-    public QuizStudentAnswerServiceImpl(QuizDAO quizDAO, UserDAO userDAO, QuizStudentAnswerDAO quizStudentAnswerDAO, QuestionService questionService) {
+    public QuizStudentAnswerServiceImpl(QuizDAO quizDAO, UserDAO userDAO, QuizStudentAnswerDAO quizStudentAnswerDAO, QuestionService questionService, CourseService courseService) {
         this.quizDAO = quizDAO;
         this.userDAO = userDAO;
         this.quizStudentAnswerDAO = quizStudentAnswerDAO;
         this.questionService = questionService;
+        this.courseService = courseService;
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -48,6 +53,10 @@ public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
     public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, Long quizId, QuizStudentAnswerDTO quizStudentAnswerDTO) {
         Quiz quiz = quizDAO.findQuizById(quizId);
 
+        if (!courseService.existsCourseResourcesAuth(user, quiz.getCourse().getId())) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "您无权提交该测试的作答");
+        }
+
         switch (quiz.getState()) {
             case NOT_STARTED:
                 throw HelperException.of(ExceptionType.FORBIDDEN, "测试暂未开始");
@@ -93,16 +102,34 @@ public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
 
     @Transactional(readOnly = true)
     @Override
-    public Page<QuizStudentAnswerVO> teacherGetQuizStudentAnswers(LoginUser user, Long quizId, Pageable pageable) {
+    public Page<QuizStudentAnswerVO> getQuizStudentAnswers(LoginUser user, Long quizId, Pageable pageable) {
         return quizStudentAnswerDAO.findByQuiz(quizDAO.findQuizById(quizId), pageable).map(quizStudentAnswer -> new QuizStudentAnswerVO(quizStudentAnswer, questionService.getQuestionsByQuiz(quizDAO.findQuizById(quizId))));
     }
 
     @Transactional(readOnly = true)
     @Override
-    public QuizStudentAnswerVO studentGetSelfQuizStudentAnswer(LoginUser user, Long quizId) {
+    public QuizStudentAnswerVO getQuizStudentAnswer(LoginUser user, Long quizId) {
         return quizStudentAnswerDAO
                 .findByQuizAndStudent(quizDAO.findQuizById(quizId), userDAO.findUserById(user.getId()))
                 .map(quizStudentAnswer -> new QuizStudentAnswerVO(quizStudentAnswer, questionService.getQuestionsByQuiz(quizDAO.findQuizById(quizId))))
                 .orElse(null);
     }
+
+    @Transactional(readOnly = true)
+    @Override
+    public QuizStudentAnswerStatisticVO getQuizStudentAnswerStatistic(LoginUser user, Long quizId) {
+        List<QuizStudentAnswer> quizStudentAnswers = quizStudentAnswerDAO.findByQuiz(quizDAO.findQuizById(quizId));
+
+        Map<BigDecimal, Integer> scores = Maps.newHashMapWithExpectedSize(quizStudentAnswers.size());
+        Map<Integer, Integer> submitNums = Maps.newHashMapWithExpectedSize(quizStudentAnswers.size());
+
+        quizStudentAnswers.forEach(
+                quizStudentAnswer -> {
+                    scores.merge(quizStudentAnswer.getScore(), 1, Integer::sum);
+                    submitNums.merge(quizStudentAnswer.getSubmitNumber(), 1, Integer::sum);
+                }
+        );
+
+        return QuizStudentAnswerStatisticVO.of(quizId, scores, submitNums);
+    }
 }

+ 5 - 4
src/main/java/nju/seec/helper/service/impl/SlideServiceImpl.java

@@ -146,7 +146,7 @@ public class SlideServiceImpl implements SlideService {
         // 增加通知
         if (slide.getState() == SlideState.IN_CLASS) {
             Set<Long> studentIds = chooseDAO.findStudentIdsByCourseId(slide.getCourse().getId());
-            messageService.createMessage(studentIds, MessageType.SLIDE_IN_CLASS, String.format(" [%s] - [%s] 已开课", slide.getCourse().getName(), slide.getName()), slide.getId());
+            messageService.createMessage(studentIds, MessageType.SLIDE_IN_CLASS, String.format("[%s] - [%s] 已开课", slide.getCourse().getName(), slide.getName()), slide.getId());
         }
 
         // 修改Slide状态同时推进quiz状态
@@ -183,6 +183,7 @@ public class SlideServiceImpl implements SlideService {
 
         slide.setDeleteAt(System.currentTimeMillis());
         slideDAO.save(slide);
+        messageService.deleteMessages(slideId, MessageType.SLIDE_IN_CLASS);
     }
 
     @SneakyThrows
@@ -203,7 +204,7 @@ public class SlideServiceImpl implements SlideService {
     @Transactional(readOnly = true)
     @Override
     public Page<SlideVO> getSlidesByCourse(LoginUser user, Long courseId, String key, Pageable pageable) {
-        if (!courseService.existsCourseResourcesGetAuth(user, courseId)) {
+        if (!courseService.existsCourseResourcesAuth(user, courseId)) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该课程的课件");
         }
         switch (user.getType()) {
@@ -230,7 +231,7 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public String getSlideUrl(LoginUser user, Long slideId) {
         Slide slide = slideDAO.findSlideById(slideId);
-        if (!courseService.existsCourseResourcesGetAuth(user, slide.getCourse().getId())) {
+        if (!courseService.existsCourseResourcesAuth(user, slide.getCourse().getId())) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权获取该课件的链接");
         }
 
@@ -241,7 +242,7 @@ public class SlideServiceImpl implements SlideService {
     @Override
     public SlideVO getOneSlide(LoginUser user, Long slideId) {
         Slide slide = slideDAO.findSlideById(slideId);
-        if (!courseService.existsCourseResourcesGetAuth(user, slide.getCourse().getId())) {
+        if (!courseService.existsCourseResourcesAuth(user, slide.getCourse().getId())) {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该课件");
         }
         return new SlideVO(slide);

+ 18 - 0
src/main/java/nju/seec/helper/vo/QuizStudentAnswerStatisticVO.java

@@ -0,0 +1,18 @@
+package nju.seec.helper.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@AllArgsConstructor(staticName = "of")
+@Data
+public class QuizStudentAnswerStatisticVO {
+    private Long quizId;
+    private Map<BigDecimal, Integer> scores;
+    private Map<Integer, Integer> submitNums;
+}