Explorar el Código

feat: 增加课件测试功能

ChenSiTong hace 6 años
padre
commit
e846de7a26
Se han modificado 83 ficheros con 1806 adiciones y 1553 borrados
  1. 3 3
      pom.xml
  2. 1 1
      src/main/java/nju/seec/helper/aspect/auth/AuthAspect.java
  3. 2 2
      src/main/java/nju/seec/helper/controller/CommentController.java
  4. 3 3
      src/main/java/nju/seec/helper/controller/CourseController.java
  5. 2 2
      src/main/java/nju/seec/helper/controller/CourseFileController.java
  6. 2 2
      src/main/java/nju/seec/helper/controller/MessageController.java
  7. 10 3
      src/main/java/nju/seec/helper/controller/NoticeController.java
  8. 55 56
      src/main/java/nju/seec/helper/controller/QuestionController.java
  9. 131 150
      src/main/java/nju/seec/helper/controller/QuizController.java
  10. 2 2
      src/main/java/nju/seec/helper/controller/ReplyController.java
  11. 4 4
      src/main/java/nju/seec/helper/controller/SlideController.java
  12. 4 4
      src/main/java/nju/seec/helper/controller/UserController.java
  13. 42 41
      src/main/java/nju/seec/helper/dao/QuizDAO.java
  14. 46 11
      src/main/java/nju/seec/helper/dao/QuizStudentAnswerDAO.java
  15. 0 33
      src/main/java/nju/seec/helper/dto/QuizDTO.java
  16. 1 1
      src/main/java/nju/seec/helper/dto/comment/CommentDTO.java
  17. 1 1
      src/main/java/nju/seec/helper/dto/course/ChooseDTO.java
  18. 1 1
      src/main/java/nju/seec/helper/dto/course/CourseDTO.java
  19. 4 2
      src/main/java/nju/seec/helper/dto/course/CourseFileDTO.java
  20. 1 1
      src/main/java/nju/seec/helper/dto/message/MessageDTO.java
  21. 4 2
      src/main/java/nju/seec/helper/dto/notice/NoticeDTO.java
  22. 26 0
      src/main/java/nju/seec/helper/dto/question/BaseQuestionDTO.java
  23. 33 0
      src/main/java/nju/seec/helper/dto/question/ChoiceQuestionDTO.java
  24. 21 0
      src/main/java/nju/seec/helper/dto/question/SubjectiveQuestionDTO.java
  25. 24 0
      src/main/java/nju/seec/helper/dto/question/TrueOrFalseQuestionDTO.java
  26. 36 0
      src/main/java/nju/seec/helper/dto/quiz/QuizDTO.java
  27. 19 0
      src/main/java/nju/seec/helper/dto/quiz/QuizStudentAnswerDTO.java
  28. 1 1
      src/main/java/nju/seec/helper/dto/reply/ReplyDTO.java
  29. 1 1
      src/main/java/nju/seec/helper/dto/slide/SlideDTO.java
  30. 1 1
      src/main/java/nju/seec/helper/dto/slide/SlideFileDTO.java
  31. 1 1
      src/main/java/nju/seec/helper/dto/slide/SlideStateDTO.java
  32. 1 1
      src/main/java/nju/seec/helper/dto/user/LoginDTO.java
  33. 1 1
      src/main/java/nju/seec/helper/dto/user/LoginUser.java
  34. 1 1
      src/main/java/nju/seec/helper/dto/user/ResetPasswordDTO.java
  35. 1 1
      src/main/java/nju/seec/helper/dto/user/UserDTO.java
  36. 1 1
      src/main/java/nju/seec/helper/entity/Course.java
  37. 76 66
      src/main/java/nju/seec/helper/entity/Quiz.java
  38. 55 60
      src/main/java/nju/seec/helper/entity/QuizStudentAnswer.java
  39. 1 1
      src/main/java/nju/seec/helper/entity/Slide.java
  40. 0 25
      src/main/java/nju/seec/helper/entity/StudentScore.java
  41. 4 4
      src/main/java/nju/seec/helper/entity/converter/MapConverter.java
  42. 2 2
      src/main/java/nju/seec/helper/service/CommentService.java
  43. 2 2
      src/main/java/nju/seec/helper/service/CourseFileService.java
  44. 3 3
      src/main/java/nju/seec/helper/service/CourseService.java
  45. 2 2
      src/main/java/nju/seec/helper/service/MessageService.java
  46. 11 2
      src/main/java/nju/seec/helper/service/NoticeService.java
  47. 53 26
      src/main/java/nju/seec/helper/service/QuestionService.java
  48. 71 23
      src/main/java/nju/seec/helper/service/QuizService.java
  49. 40 0
      src/main/java/nju/seec/helper/service/QuizStudentAnswerService.java
  50. 2 2
      src/main/java/nju/seec/helper/service/ReplyService.java
  51. 4 4
      src/main/java/nju/seec/helper/service/SlideService.java
  52. 3 3
      src/main/java/nju/seec/helper/service/UserService.java
  53. 2 2
      src/main/java/nju/seec/helper/service/impl/CommentServiceImpl.java
  54. 2 2
      src/main/java/nju/seec/helper/service/impl/CourseFileServiceImpl.java
  55. 6 6
      src/main/java/nju/seec/helper/service/impl/CourseServiceImpl.java
  56. 2 2
      src/main/java/nju/seec/helper/service/impl/MessageServiceImpl.java
  57. 13 2
      src/main/java/nju/seec/helper/service/impl/NoticeServiceImpl.java
  58. 62 47
      src/main/java/nju/seec/helper/service/impl/QuestionServiceImpl.java
  59. 186 353
      src/main/java/nju/seec/helper/service/impl/QuizServiceImpl.java
  60. 104 0
      src/main/java/nju/seec/helper/service/impl/QuizStudentAnswerServiceImpl.java
  61. 2 2
      src/main/java/nju/seec/helper/service/impl/ReplyServiceImpl.java
  62. 26 26
      src/main/java/nju/seec/helper/service/impl/SlideServiceImpl.java
  63. 3 3
      src/main/java/nju/seec/helper/service/impl/UserServiceImpl.java
  64. 142 205
      src/main/java/nju/seec/helper/util/BokUtil.java
  65. 6 3
      src/main/java/nju/seec/helper/util/GuavaCacheUtil.java
  66. 2 6
      src/main/java/nju/seec/helper/util/RestRequestUtil.java
  67. 5 1
      src/main/java/nju/seec/helper/util/enums/MessageType.java
  68. 0 21
      src/main/java/nju/seec/helper/util/enums/QuestionKindState.java
  69. 8 0
      src/main/java/nju/seec/helper/util/enums/QuestionType.java
  70. 30 0
      src/main/java/nju/seec/helper/util/enums/QuizState.java
  71. 14 2
      src/main/java/nju/seec/helper/util/enums/QuizType.java
  72. 89 0
      src/main/java/nju/seec/helper/vo/question/BaseQuestionVO.java
  73. 31 0
      src/main/java/nju/seec/helper/vo/question/BokQuestion.java
  74. 43 0
      src/main/java/nju/seec/helper/vo/question/ChoiceQuestionVO.java
  75. 27 0
      src/main/java/nju/seec/helper/vo/question/SubjectiveQuestionVO.java
  76. 59 0
      src/main/java/nju/seec/helper/vo/question/TrueOrFalseQuestionVO.java
  77. 0 104
      src/main/java/nju/seec/helper/vo/quiz/BaseQuestionVO.java
  78. 0 26
      src/main/java/nju/seec/helper/vo/quiz/BokQuestion.java
  79. 0 54
      src/main/java/nju/seec/helper/vo/quiz/ChoiceQuestionVO.java
  80. 37 0
      src/main/java/nju/seec/helper/vo/quiz/QuizStudentAnswerVO.java
  81. 88 83
      src/main/java/nju/seec/helper/vo/quiz/QuizVO.java
  82. 0 44
      src/main/java/nju/seec/helper/vo/quiz/TrueOrFalseQuestionVO.java
  83. 1 0
      src/main/resources/update.sql

+ 3 - 3
pom.xml

@@ -8,9 +8,9 @@
         <version>2.2.4.RELEASE</version>
         <relativePath/> <!-- lookup parent from repository -->
     </parent>
-    <groupId>nju.seec</groupId>
-    <artifactId>helper</artifactId>
-    <version>0.0.1-SNAPSHOT</version>
+    <groupId>cn.seecoder</groupId>
+    <artifactId>helper-backend</artifactId>
+    <version>3.0.0-SNAPSHOT</version>
     <packaging>jar</packaging>
     <name>helper</name>
     <description>backend</description>

+ 1 - 1
src/main/java/nju/seec/helper/aspect/auth/AuthAspect.java

@@ -1,6 +1,6 @@
 package nju.seec.helper.aspect.auth;
 
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.util.Consts;
 import nju.seec.helper.util.enums.ExceptionType;
 import nju.seec.helper.util.exception.HelperException;

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

@@ -2,8 +2,8 @@ package nju.seec.helper.controller;
 
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.controller.response.PageResponse;
-import nju.seec.helper.dto.CommentDTO;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.comment.CommentDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.service.CommentService;
 import nju.seec.helper.util.JsonUtils;
 import nju.seec.helper.util.enums.UserType;

+ 3 - 3
src/main/java/nju/seec/helper/controller/CourseController.java

@@ -2,11 +2,11 @@ package nju.seec.helper.controller;
 
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.controller.response.PageResponse;
-import nju.seec.helper.dto.ChooseDTO;
-import nju.seec.helper.dto.CourseDTO;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.course.ChooseDTO;
+import nju.seec.helper.dto.course.CourseDTO;
 import nju.seec.helper.dto.groups.Create;
 import nju.seec.helper.dto.groups.Modify;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.service.CourseService;
 import nju.seec.helper.util.enums.UserType;
 import nju.seec.helper.vo.CourseVO;

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

@@ -2,8 +2,8 @@ package nju.seec.helper.controller;
 
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.controller.response.PageResponse;
-import nju.seec.helper.dto.CourseFileDTO;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.course.CourseFileDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.service.CourseFileService;
 import nju.seec.helper.util.enums.UserType;
 import nju.seec.helper.vo.CourseFileVO;

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

@@ -2,8 +2,8 @@ package nju.seec.helper.controller;
 
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.controller.response.PageResponse;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.MessageDTO;
+import nju.seec.helper.dto.message.MessageDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.entity.Message;
 import nju.seec.helper.service.MessageService;
 import nju.seec.helper.util.enums.UserType;

+ 10 - 3
src/main/java/nju/seec/helper/controller/NoticeController.java

@@ -2,10 +2,10 @@ package nju.seec.helper.controller;
 
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.controller.response.PageResponse;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.NoticeDTO;
 import nju.seec.helper.dto.groups.Create;
 import nju.seec.helper.dto.groups.Modify;
+import nju.seec.helper.dto.notice.NoticeDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.service.NoticeService;
 import nju.seec.helper.util.enums.UserType;
 import nju.seec.helper.vo.NoticeVO;
@@ -47,7 +47,7 @@ public class NoticeController {
         noticeService.deleteNotice(user, noticeId);
     }
 
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取公告")
+    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取课程公告")
     @GetMapping("/course/{courseId}")
     public PageResponse<NoticeVO> getNotices(LoginUser user,
                                              @PathVariable Long courseId,
@@ -55,4 +55,11 @@ public class NoticeController {
                                              @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
         return PageResponse.of(noticeService.getNoticesByCourse(user, courseId, key, pageable));
     }
+
+    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取公告")
+    @GetMapping("{noticeId}")
+    public NoticeVO getOneNotices(LoginUser user,
+                                  @PathVariable Long noticeId) {
+        return noticeService.getOneNotice(user, noticeId);
+    }
 }

+ 55 - 56
src/main/java/nju/seec/helper/controller/QuestionController.java

@@ -1,53 +1,52 @@
-//package nju.seec.helper.controller;
-//
-//import com.fasterxml.jackson.core.JsonProcessingException;
-//import nju.seec.helper.aspect.auth.Auth;
-//import nju.seec.helper.controller.response.PageResponse;
-//import nju.seec.helper.dao.CourseDAO;
-//import nju.seec.helper.dao.SlideDAO;
-//import nju.seec.helper.dto.LoginUser;
-//import nju.seec.helper.service.QuestionService;
-//import nju.seec.helper.util.enums.ExceptionType;
-//import nju.seec.helper.util.enums.UserType;
-//import nju.seec.helper.util.exception.HelperException;
-//import nju.seec.helper.vo.quiz.BaseQuestionVO;
-//import org.springframework.beans.factory.annotation.Autowired;
-//import org.springframework.data.domain.Page;
-//import org.springframework.data.domain.Pageable;
-//import org.springframework.data.web.PageableDefault;
-//import org.springframework.web.bind.annotation.*;
-//
-//import java.util.*;
-//
-///**
-// * 题库
-// *
-// * @doc http://47.100.18.120:3000/SEEC-BOK/API-DOC/src/master/helper/api/Quiz-%e6%b5%8b%e8%af%95.md
-// */
-//@RestController
-//@RequestMapping("/api/question")
-//public class QuestionController {
-//    @Autowired
-//    private CourseDAO courseDAO;
-//    @Autowired
-//    private SlideDAO slideDAO;
-//    @Autowired
-//    private QuestionService questionService;
-//
-//    /**
-//     * 获得题目列表
-//     */
-//    @Auth(roles = {UserType.TEACHER}, message = "获得题目列表")
-//    @GetMapping("")
-//    public Map getQuestionList(LoginUser user, String stem, @PageableDefault(Integer.MAX_VALUE) Pageable pageable) throws JsonProcessingException {
-//
-//        final Page<BaseQuestionVO> page = questionService.getQuestionList(user, stem, pageable);
-//        Map ret = new LinkedHashMap(2);
-//        ret.put("questions", page.getContent());
-//        ret.put("page", PageResponse.PageInfo.of(page.getNumber() + 1, page.getSize(), page.getTotalPages(), Long.valueOf(page.getTotalElements()).intValue()));
-//        return ret;
-//    }
-//
+package nju.seec.helper.controller;
+
+import nju.seec.helper.aspect.auth.Auth;
+import nju.seec.helper.controller.response.PageResponse;
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.service.QuestionService;
+import nju.seec.helper.util.enums.UserType;
+import nju.seec.helper.vo.question.BaseQuestionVO;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 题库
+ *
+ * @author xst
+ * <p>
+ * updated by cst
+ * @doc http://47.100.18.120:3000/SEEC-BOK/API-DOC/src/master/helper/api/Quiz-%e6%b5%8b%e8%af%95.md
+ */
+@RestController
+@RequestMapping("/api/question")
+public class QuestionController {
+    private final QuestionService questionService;
+
+    public QuestionController(QuestionService questionService) {
+        this.questionService = questionService;
+    }
+
+    /**
+     * 获得题目列表
+     */
+    @Auth(roles = {UserType.TEACHER}, message = "获得题目列表")
+    @GetMapping
+    public PageResponse<BaseQuestionVO> getQuestionList(LoginUser user, @RequestParam(required = false, defaultValue = "") String key, @PageableDefault(Integer.MAX_VALUE) Pageable pageable) {
+        return PageResponse.of(questionService.getQuestions(user, key, pageable));
+    }
+
+    /**
+     * 某一测试的题目
+     */
+    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获得测试题目")
+    @GetMapping("/quiz/{quizId}")
+    public List<BaseQuestionVO> getQuestionsOfQuiz(LoginUser user, @PathVariable Long quizId) {
+        return questionService.getQuestionsByQuiz(user, quizId);
+    }
+
 //    /**
 //     * 获得题目列表
 //     */
@@ -56,8 +55,8 @@
 //    public BaseQuestionVO getQuestion(LoginUser user, @PathVariable("questionId") String questionId) throws JsonProcessingException {
 //        return questionService.getQuestion(questionId);
 //    }
-//
-//
+
+
 //    /**
 //     * 创建题目
 //     */
@@ -70,7 +69,7 @@
 //
 //        return questionService.newQuestion(BaseQuestionVO.parse(question));
 //    }
-//
+
 //    /**
 //     * 更新题目
 //     */
@@ -83,8 +82,8 @@
 //
 //        return questionService.updateQuestion(BaseQuestionVO.parse(question));
 //    }
-//
-//
+
+
 //    /**
 //     * 删除题目
 //     */
@@ -97,4 +96,4 @@
 //        map.put("message", "success");
 //        return map;
 //    }
-//}
+}

+ 131 - 150
src/main/java/nju/seec/helper/controller/QuizController.java

@@ -1,150 +1,131 @@
-//package nju.seec.helper.controller;
-//
-//import nju.seec.helper.aspect.auth.Auth;
-//import nju.seec.helper.controller.response.PageResponse;
-//import nju.seec.helper.dao.CourseDAO;
-//import nju.seec.helper.dao.SlideDAO;
-//import nju.seec.helper.dto.LoginUser;
-//import nju.seec.helper.dto.QuizAnswerDTO;
-//import nju.seec.helper.dto.QuizDTO;
-//import nju.seec.helper.dto.QuizUpdateDTO;
-//import nju.seec.helper.dto.groups.Create;
-//import nju.seec.helper.entity.Quiz;
-//import nju.seec.helper.service.QuizService;
-//import nju.seec.helper.util.BokUtil;
-//import nju.seec.helper.util.enums.QuizState;
-//import nju.seec.helper.util.enums.UserType;
-//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.data.web.PageableDefault;
-//import org.springframework.validation.annotation.Validated;
-//import org.springframework.web.bind.annotation.*;
-//
-//import javax.persistence.EntityNotFoundException;
-//import java.util.*;
-//import java.util.stream.Collectors;
-//
-///**
-// * 测试
-// *
-// * @doc http://47.100.18.120:3000/SEEC-BOK/API-DOC/src/master/helper/api/Quiz-%e6%b5%8b%e8%af%95.md
-// */
-//@RestController
-//@RequestMapping("/api/quiz")
-//public class QuizController {
-//    @Autowired
-//    private CourseDAO courseDAO;
-//    @Autowired
-//    private SlideDAO slideDAO;
-//    @Autowired
-//    private BokUtil bokUtil;
-//    @Autowired
-//    private QuizService quizService;
-//
-//    /**
-//     * 获得测试列表
-//     */
-//    @Auth(roles = {UserType.STUDENT, UserType.TEACHER}, message = "获得测试列表")
-//    @GetMapping("")
-//    public Map getBasicQuizList(LoginUser user, String slideId,
-//                                QuizState state, @PageableDefault(Integer.MAX_VALUE) Pageable pageable) {
-//        List<QuizVO> quizzes = new ArrayList<QuizVO>();
-//        Map ret = new LinkedHashMap(2);
-//        Page<Quiz> page;
-//        if (slideId != null) {
-//            page = quizService.getAllBasicQuizBySlide(Long.valueOf(slideId), pageable, state, user.getId());
-//        } else {
-//            page = quizService.getAllBasicQuizByUser(pageable, state, user.getId());
-//        }
-//        quizzes.addAll(page.getContent().stream().map(QuizVO::new).collect(Collectors.toList()));
-//        ret.put("page", PageResponse.PageInfo.of(page.getNumber() + 1, page.getSize(), page.getTotalPages(), Long.valueOf(page.getTotalElements()).intValue()));
-//        ret.put("quizzes", quizzes);
-//        return ret;
-//    }
-//
-//    /**
-//     * 获得某一测试的详细内容
-//     */
-//    @Auth(roles = {UserType.STUDENT, UserType.TEACHER}, message = "获得某一测试的详细内容")
-//    @GetMapping("/{quizId}")
-//    public QuizVO getOneQuiz(LoginUser user, @PathVariable("quizId") String quizId) {
-//        QuizVO quizVO = quizService.combineQuizWithAnswer(Long.valueOf(quizId), user.getId(), user.getType());
-//
-//        if (user.getType().equals(UserType.STUDENT) && !quizVO.getState().equals(QuizState.CLOSED)) {
-//            quizVO.excludeCorrectAnswer();
-//        }
-//        return quizVO;
-//    }
-//
-//    /**
-//     * 创建测试
-//     */
-//    @Auth(roles = {UserType.TEACHER}, message = "创建测试")
-//    @PostMapping
-//    public QuizVO createQuiz(LoginUser user, @Validated(Create.class) @RequestBody QuizDTO newQuiz) {
-//        final Quiz quiz = quizService.createQuiz(user, newQuiz);
-//        return quizService.combineQuiz(quiz.getId());
-//    }
-//
-//    /**
-//     * 修改测试 (仅限未开始时可以修改)
-//     */
-//    @Auth(roles = {UserType.TEACHER}, message = "修改测试")
-//    @PutMapping("/{quizId}")
-//    public QuizVO updateQuiz(LoginUser user, @RequestBody QuizUpdateDTO newQuiz, @PathVariable("quizId") String quizId) {
-//        Quiz ret = quizService.modifyQuiz(Long.valueOf(quizId), newQuiz.getName(),
-//                newQuiz.getQuestions(), newQuiz.getQuizTime(), newQuiz.getSlideId());
-//        return quizService.combineQuiz(ret.getId());
-//    }
-//
-//    /**
-//     * 删除测试 (仅限未开始时可以删除)
-//     */
-//    @Auth(roles = {UserType.TEACHER}, message = "删除测试")
-//    @DeleteMapping("/{quizId}")
-//    public Map deleteQuiz(LoginUser user, @PathVariable("quizId") String quizId) {
-//        Map map = new HashMap();
-//        String message = quizService.deleteQuiz(Long.valueOf(quizId));
-//        map.put("message", message);
-//        return map;
-//    }
-//
-//    /**
-//     * 获得测试结果统计
-//     */
-//    @Auth(roles = {UserType.TEACHER}, message = "获得测试结果统计")
-//    @GetMapping("/{quizId}/result")
-//    public Map getQuizResult(LoginUser user, @PathVariable("quizId") String quizId) {
-//        if (quizId == null) {
-//            throw new EntityNotFoundException("quiz can't be null");
-//        }
-//        Map map = new HashMap();
-//
-//        List<QuizResultVO> list = quizService.getQuizResult(Long.valueOf(quizId));
-//        map.put("message", "success");
-//        map.put("average", list.stream().mapToInt(QuizResultVO::getScore).average());
-//        map.put("data", list);
-//        return map;
-//    }
-//
-//    /**
-//     * 提交答案
-//     */
-//
-//    @Auth(roles = {UserType.STUDENT}, message = "提交答案")
-//    @PutMapping("/{quizId}/student-answer")
-//    public Map putStudentAnswer(LoginUser user, @PathVariable("quizId") String quizId, @RequestBody QuizAnswerDTO quizAnswerDTO) {
-//
-//        if (quizId == null) {
-//            throw new EntityNotFoundException("quiz can't be null");
-//        }
-//        Integer match = quizService.upStudentAnswer(Long.valueOf(quizId), user.getId(), quizAnswerDTO.getAnswers());
-//        Map map = new HashMap();
-//        map.put("message", "success");
-//        map.put("matchNum", match);
-//        return map;
-//    }
-//}
+package nju.seec.helper.controller;
+
+import nju.seec.helper.aspect.auth.Auth;
+import nju.seec.helper.controller.response.PageResponse;
+import nju.seec.helper.dto.groups.Create;
+import nju.seec.helper.dto.groups.Modify;
+import nju.seec.helper.dto.quiz.QuizDTO;
+import nju.seec.helper.dto.quiz.QuizStudentAnswerDTO;
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.service.QuizService;
+import nju.seec.helper.service.QuizStudentAnswerService;
+import nju.seec.helper.util.enums.UserType;
+import nju.seec.helper.vo.quiz.QuizStudentAnswerVO;
+import nju.seec.helper.vo.quiz.QuizVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 测试
+ *
+ * @author xst
+ * <p>
+ * updated by cst
+ * @doc http://47.100.18.120:3000/SEEC-BOK/API-DOC/src/master/helper/api/Quiz-%e6%b5%8b%e8%af%95.md
+ */
+@RestController
+@RequestMapping("/api/quiz")
+public class QuizController {
+    private final QuizService quizService;
+    private final QuizStudentAnswerService quizStudentAnswerService;
+
+    public QuizController(QuizService quizService, QuizStudentAnswerService quizStudentAnswerService) {
+        this.quizService = quizService;
+        this.quizStudentAnswerService = quizStudentAnswerService;
+    }
+
+    /**
+     * 老师获取测试列表
+     */
+    @Auth(roles = UserType.TEACHER, message = "获得测试列表")
+    @GetMapping("/teacher/slide/{slideId}")
+    public PageResponse<QuizVO> teacherGetQuizzes(LoginUser user,
+                                                  @PathVariable Long slideId,
+                                                  @RequestParam(required = false, defaultValue = "") String key,
+                                                  @PageableDefault(Integer.MAX_VALUE) Pageable pageable) {
+        return PageResponse.of(quizService.teacherGetQuizzes(user, slideId, key, pageable));
+    }
+
+    /**
+     * 学生获取测试列表
+     */
+    @Auth(roles = UserType.STUDENT, message = "获得测试列表")
+    @GetMapping("/student/slide/{slideId}")
+    public PageResponse<QuizVO> studentGetQuizzes(LoginUser user,
+                                                  @PathVariable Long slideId,
+                                                  @RequestParam(required = false, defaultValue = "") String key,
+                                                  @PageableDefault(Integer.MAX_VALUE) Pageable pageable) {
+        return PageResponse.of(quizService.studentGetQuizzes(user, slideId, key, pageable));
+    }
+
+    /**
+     * 获得某一测试的详细内容
+     */
+    @Auth(roles = {UserType.STUDENT, UserType.TEACHER}, message = "获得测试")
+    @GetMapping("/{quizId}")
+    public QuizVO getOneQuiz(LoginUser user, @PathVariable("quizId") Long quizId) {
+        return quizService.getOneQuiz(user, quizId);
+    }
+
+    /**
+     * 创建测试
+     */
+    @Auth(roles = {UserType.TEACHER}, message = "创建测试")
+    @PostMapping
+    public QuizVO createQuiz(LoginUser user, @Validated(Create.class) @RequestBody QuizDTO quizDTO) {
+        return quizService.createQuiz(user, quizDTO);
+    }
+
+    /**
+     * 修改测试 (仅限未开始时可以修改)
+     */
+    @Auth(roles = {UserType.TEACHER}, message = "修改测试")
+    @PutMapping
+    public QuizVO updateQuiz(LoginUser user, @Validated(Modify.class) @RequestBody QuizDTO quizDTO) {
+        return quizService.modifyQuiz(user, quizDTO);
+    }
+
+    /**
+     * 删除测试 (伪删除)
+     */
+    @Auth(roles = {UserType.TEACHER}, message = "删除测试")
+    @DeleteMapping("/{quizId}")
+    public void deleteQuiz(LoginUser user, @PathVariable("quizId") String quizId) {
+        quizService.deleteQuiz(user, Long.valueOf(quizId));
+    }
+
+    @Auth(roles = {UserType.STUDENT}, message = "提交作答")
+    @PostMapping("/student-answer")
+    public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, @RequestBody QuizStudentAnswerDTO quizStudentAnswerDTO) {
+        return quizStudentAnswerService.submitQuizStudentAnswer(user, quizStudentAnswerDTO);
+    }
+
+    /**
+     * 老师获取某一测试学生的作答列表
+     *
+     * @param user
+     * @param quizId
+     * @return
+     */
+    @Auth(roles = {UserType.TEACHER}, message = "获取学生作答")
+    @GetMapping("/{quizId}/teacher/student-answer/}")
+    public Page<QuizStudentAnswerVO> getQuizStudentAnswers(LoginUser user, @PathVariable Long quizId, @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
+        return quizStudentAnswerService.teacherGetQuizStudentAnswers(user, quizId, pageable);
+    }
+
+    /**
+     * 学生获取某一测试自己的作答
+     *
+     * @param user
+     * @param quizId
+     * @return
+     */
+    @Auth(roles = {UserType.STUDENT}, message = "获取作答")
+    @GetMapping("/{quizId}/student/student-answer/}")
+    public QuizStudentAnswerVO getOneQuizStudentAnswer(LoginUser user, @PathVariable Long quizId) {
+        return quizStudentAnswerService.studentGetSelfQuizStudentAnswer(user, quizId);
+    }
+}

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

@@ -1,8 +1,8 @@
 package nju.seec.helper.controller;
 
 import nju.seec.helper.aspect.auth.Auth;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.ReplyDTO;
+import nju.seec.helper.dto.reply.ReplyDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.service.ReplyService;
 import nju.seec.helper.util.enums.UserType;
 import nju.seec.helper.vo.ReplyVO;

+ 4 - 4
src/main/java/nju/seec/helper/controller/SlideController.java

@@ -2,12 +2,12 @@ package nju.seec.helper.controller;
 
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.controller.response.PageResponse;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.SlideDTO;
-import nju.seec.helper.dto.SlideFileDTO;
-import nju.seec.helper.dto.SlideStateDTO;
 import nju.seec.helper.dto.groups.Create;
 import nju.seec.helper.dto.groups.Modify;
+import nju.seec.helper.dto.slide.SlideDTO;
+import nju.seec.helper.dto.slide.SlideFileDTO;
+import nju.seec.helper.dto.slide.SlideStateDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.service.SlideService;
 import nju.seec.helper.util.enums.UserType;
 import nju.seec.helper.vo.SlideVO;

+ 4 - 4
src/main/java/nju/seec/helper/controller/UserController.java

@@ -1,12 +1,12 @@
 package nju.seec.helper.controller;
 
 import nju.seec.helper.aspect.auth.Auth;
-import nju.seec.helper.dto.LoginDTO;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.ResetPasswordDTO;
-import nju.seec.helper.dto.UserDTO;
 import nju.seec.helper.dto.groups.Create;
 import nju.seec.helper.dto.groups.Modify;
+import nju.seec.helper.dto.user.LoginDTO;
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.dto.user.ResetPasswordDTO;
+import nju.seec.helper.dto.user.UserDTO;
 import nju.seec.helper.service.UserService;
 import nju.seec.helper.util.Consts;
 import nju.seec.helper.util.enums.UserType;

+ 42 - 41
src/main/java/nju/seec/helper/dao/QuizDAO.java

@@ -1,46 +1,47 @@
-//package nju.seec.helper.dao;
-//
-//import nju.seec.helper.entity.Course;
-//import nju.seec.helper.entity.Quiz;
-//import nju.seec.helper.entity.Slide;
-//import nju.seec.helper.entity.User;
-//import nju.seec.helper.util.enums.ExceptionType;
-//import nju.seec.helper.util.enums.QuizState;
-//import nju.seec.helper.util.exception.HelperException;
-//import org.springframework.data.domain.Page;
-//import org.springframework.data.domain.Pageable;
-//import org.springframework.data.jpa.repository.JpaRepository;
-//import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-//import org.springframework.stereotype.Repository;
-//
-//import java.util.Collection;
-//import java.util.List;
-//
-//
-///**
-// * @author sheen
-// * <p>
-// * updated by cst
-// */
-//@Repository
-//public interface QuizDAO extends JpaRepository<Quiz, Long>, JpaSpecificationExecutor<Quiz> {
-//    /**
-//     * 封装findById
-//     *
-//     * @param id
-//     * @return
-//     */
-//    default Quiz findQuizById(Long id) {
-//        return this.findById(id)
-//                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到测试"));
-//    }
-//
+package nju.seec.helper.dao;
+
+import nju.seec.helper.entity.Quiz;
+import nju.seec.helper.entity.Slide;
+import nju.seec.helper.util.enums.ExceptionType;
+import nju.seec.helper.util.exception.HelperException;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+
+/**
+ * @author sheen
+ * <p>
+ * updated by cst
+ */
+@Repository
+public interface QuizDAO extends JpaRepository<Quiz, Long>, JpaSpecificationExecutor<Quiz> {
+    /**
+     * 封装findById
+     *
+     * @param id
+     * @return
+     */
+    default Quiz findQuizById(Long id) {
+        return this.findById(id)
+                .filter(quiz -> quiz.getDeleteAt() == 0L)
+                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到测试"));
+    }
+
 //    Page<Quiz> findAllBySlideAndState(Slide slide, QuizState state, Pageable pageable);
 //
 //    Page<Quiz> findAllBySlide(Slide slide, Pageable pageable);
-//
-//    List<Quiz> findAllBySlide(Slide slide);
-//
+
+    /**
+     * 根据课件检索
+     *
+     * @param slide
+     * @return
+     */
+    List<Quiz> findAllBySlide(Slide slide);
+
 //    Page<Quiz> findAllByCourseIn(Collection<Course> course, Pageable pageable);
 //
 //    Page<Quiz> findAllByCourseInAndState(Collection<Course> course, QuizState state, Pageable pageable);
@@ -48,4 +49,4 @@
 //    Page<Quiz> findByTeacher(User teacher, Pageable pageable);
 //
 //    Page<Quiz> findByTeacherAndState(User teacher, QuizState state, Pageable pageable);
-//}
+}

+ 46 - 11
src/main/java/nju/seec/helper/dao/QuizStudentAnswerDAO.java

@@ -1,13 +1,48 @@
-//package nju.seec.helper.dao;
-//
-//import nju.seec.helper.entity.Quiz;
-//import nju.seec.helper.entity.QuizStudentAnswer;
-//import nju.seec.helper.entity.User;
-//import org.springframework.data.jpa.repository.JpaRepository;
-//
-//import java.util.Set;
-//
-//public interface QuizStudentAnswerDAO extends JpaRepository<QuizStudentAnswer, Long> {
+package nju.seec.helper.dao;
+
+import nju.seec.helper.entity.Quiz;
+import nju.seec.helper.entity.QuizStudentAnswer;
+import nju.seec.helper.entity.User;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@Repository
+public interface QuizStudentAnswerDAO extends JpaRepository<QuizStudentAnswer, Long> {
+    /**
+     * 检查测试是否已有提交
+     *
+     * @param quiz
+     * @return
+     */
+    boolean existsByQuiz(Quiz quiz);
+
+    /**
+     * 根据测试和学生进行检索
+     *
+     * @param quiz
+     * @param student
+     * @return
+     */
+    Optional<QuizStudentAnswer> findByQuizAndStudent(Quiz quiz, User student);
+
+    /**
+     * 根据测试检索
+     *
+     * @param quiz
+     * @param pageable
+     * @return
+     */
+    Page<QuizStudentAnswer> findByQuiz(Quiz quiz, Pageable pageable);
+
 //    Set<QuizStudentAnswer> findAllByQuiz(Quiz quiz);
 //
 //    Set<QuizStudentAnswer> findAllByStudent(User student);
@@ -15,4 +50,4 @@
 //    Set<QuizStudentAnswer> findByStudentAndQuiz(User student, Quiz quiz);
 //
 //    Set<QuizStudentAnswer> findByStudentAndQuestionId(User student, String questionId);
-//}
+}

+ 0 - 33
src/main/java/nju/seec/helper/dto/QuizDTO.java

@@ -1,33 +0,0 @@
-//package nju.seec.helper.dto;
-//
-//import lombok.Data;
-//import nju.seec.helper.dto.groups.Create;
-//import nju.seec.helper.dto.groups.Modify;
-//import nju.seec.helper.util.enums.QuizType;
-//import org.hibernate.validator.constraints.Length;
-//
-//import javax.validation.constraints.NotBlank;
-//import javax.validation.constraints.NotEmpty;
-//import javax.validation.constraints.NotNull;
-//import java.io.Serializable;
-//import java.util.List;
-//
-///**
-// * @author sheen
-// */
-//@Data
-//public class QuizDTO implements Serializable {
-//    private static final long serialVersionUID = -5856536594930871925L;
-//    @NotNull(message = "缺少测试ID", groups = Modify.class)
-//    private Long id;
-//    @NotBlank(message = "测试名不能为空")
-//    @Length(max = 50, message = "测试名长度不能超过50")
-//    private String name;
-//    @NotNull(message = "缺少测试类型")
-//    private QuizType type;
-//    @NotNull(message = "缺少课件ID", groups = Create.class)
-//    private Long slideId;
-//    @NotEmpty(message = "问题列表不能为空")
-//    private List<String> questions;
-//
-//}

+ 1 - 1
src/main/java/nju/seec/helper/dto/CommentDTO.java → src/main/java/nju/seec/helper/dto/comment/CommentDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.comment;
 
 import lombok.Data;
 import org.hibernate.validator.constraints.Length;

+ 1 - 1
src/main/java/nju/seec/helper/dto/ChooseDTO.java → src/main/java/nju/seec/helper/dto/course/ChooseDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.course;
 
 import lombok.Data;
 

+ 1 - 1
src/main/java/nju/seec/helper/dto/CourseDTO.java → src/main/java/nju/seec/helper/dto/course/CourseDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.course;
 
 import lombok.Data;
 import nju.seec.helper.dto.groups.Modify;

+ 4 - 2
src/main/java/nju/seec/helper/dto/CourseFileDTO.java → src/main/java/nju/seec/helper/dto/course/CourseFileDTO.java

@@ -1,15 +1,17 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.course;
 
 import lombok.Data;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.validation.constraints.NotNull;
+import java.io.Serializable;
 
 /**
  * @author cst
  */
 @Data
-public class CourseFileDTO {
+public class CourseFileDTO implements Serializable {
+    private static final long serialVersionUID = -3754505745869667461L;
     @NotNull(message = "缺少课程ID")
     private Long courseId;
     @NotNull(message = "缺少文件")

+ 1 - 1
src/main/java/nju/seec/helper/dto/MessageDTO.java → src/main/java/nju/seec/helper/dto/message/MessageDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.message;
 
 import lombok.Data;
 

+ 4 - 2
src/main/java/nju/seec/helper/dto/NoticeDTO.java → src/main/java/nju/seec/helper/dto/notice/NoticeDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.notice;
 
 import lombok.Data;
 import nju.seec.helper.dto.groups.Create;
@@ -7,12 +7,14 @@ import org.hibernate.validator.constraints.Length;
 
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
+import java.io.Serializable;
 
 /**
  * @author cst
  */
 @Data
-public class NoticeDTO {
+public class NoticeDTO implements Serializable {
+    private static final long serialVersionUID = 5254633675811687992L;
     @NotNull(message = "缺少公告ID", groups = Modify.class)
     private Long id;
     @NotNull(message = "缺少课程ID", groups = Create.class)

+ 26 - 0
src/main/java/nju/seec/helper/dto/question/BaseQuestionDTO.java

@@ -0,0 +1,26 @@
+package nju.seec.helper.dto.question;
+
+import lombok.Data;
+import nju.seec.helper.util.enums.QuestionType;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author cst
+ */
+@Data
+public abstract class BaseQuestionDTO {
+    @NotBlank(message = "题干不能为空")
+    @Length(max = 1000, message = "题干长度不能超过1000")
+    private String stem;
+    @Length(max = 1000, message = "解析长度不能超过1000")
+    private String analysis;
+
+    /**
+     * 获取题目类型
+     *
+     * @return
+     */
+    public abstract QuestionType getQuestionType();
+}

+ 33 - 0
src/main/java/nju/seec/helper/dto/question/ChoiceQuestionDTO.java

@@ -0,0 +1,33 @@
+package nju.seec.helper.dto.question;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import nju.seec.helper.util.enums.QuestionType;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class ChoiceQuestionDTO extends BaseQuestionDTO implements Serializable {
+    private static final long serialVersionUID = -2991414748803093291L;
+    @NotEmpty(message = "选项不能为空")
+    @Size(max = 10, message = "选项数量不能超过10个")
+    private Map<String, String> options;
+
+    @NotBlank(message = "答案不能为空")
+    @Length(max = 10, message = "答案长度不能超过10")
+    private String answer;
+
+    @Override
+    public QuestionType getQuestionType() {
+        return QuestionType.CHOICE;
+    }
+}

+ 21 - 0
src/main/java/nju/seec/helper/dto/question/SubjectiveQuestionDTO.java

@@ -0,0 +1,21 @@
+package nju.seec.helper.dto.question;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import nju.seec.helper.util.enums.QuestionType;
+
+import java.io.Serializable;
+
+/**
+ * @author cst
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SubjectiveQuestionDTO extends BaseQuestionDTO implements Serializable {
+    private static final long serialVersionUID = -4391297345486351815L;
+
+    @Override
+    public QuestionType getQuestionType() {
+        return QuestionType.SUBJECTIVE;
+    }
+}

+ 24 - 0
src/main/java/nju/seec/helper/dto/question/TrueOrFalseQuestionDTO.java

@@ -0,0 +1,24 @@
+package nju.seec.helper.dto.question;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import nju.seec.helper.util.enums.QuestionType;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * @author cst
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class TrueOrFalseQuestionDTO extends BaseQuestionDTO implements Serializable {
+    private static final long serialVersionUID = -2256887591745338790L;
+    @NotNull(message = "缺少答案")
+    private Boolean answer;
+
+    @Override
+    public QuestionType getQuestionType() {
+        return QuestionType.TRUE_FALSE;
+    }
+}

+ 36 - 0
src/main/java/nju/seec/helper/dto/quiz/QuizDTO.java

@@ -0,0 +1,36 @@
+package nju.seec.helper.dto.quiz;
+
+import lombok.Data;
+import nju.seec.helper.dto.groups.Create;
+import nju.seec.helper.dto.groups.Modify;
+import nju.seec.helper.util.enums.QuizType;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author sheen
+ * <p>
+ * updated by cst
+ */
+@Data
+public class QuizDTO implements Serializable {
+    private static final long serialVersionUID = -5856536594930871925L;
+    @NotNull(message = "缺少测试ID", groups = Modify.class)
+    private Long id;
+    @NotBlank(message = "测试名不能为空")
+    @Length(max = 50, message = "测试名长度不能超过50")
+    private String name;
+    @NotNull(message = "缺少最大提交次数")
+    private Integer maxSubmitTime;
+    @NotNull(message = "缺少测试类型")
+    private QuizType type;
+    @NotNull(message = "缺少课件ID", groups = Create.class)
+    private Long slideId;
+    @NotEmpty(message = "问题列表不能为空")
+    private List<String> questions;
+}

+ 19 - 0
src/main/java/nju/seec/helper/dto/quiz/QuizStudentAnswerDTO.java

@@ -0,0 +1,19 @@
+package nju.seec.helper.dto.quiz;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@Data
+public class QuizStudentAnswerDTO {
+    @NotNull(message = "缺少测试ID")
+    private Long quizId;
+
+    @NotEmpty(message = "回答不能为空")
+    private Map<String, Object> answers;
+}

+ 1 - 1
src/main/java/nju/seec/helper/dto/ReplyDTO.java → src/main/java/nju/seec/helper/dto/reply/ReplyDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.reply;
 
 import lombok.Data;
 import org.hibernate.validator.constraints.Length;

+ 1 - 1
src/main/java/nju/seec/helper/dto/SlideDTO.java → src/main/java/nju/seec/helper/dto/slide/SlideDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.slide;
 
 import lombok.Data;
 import nju.seec.helper.dto.groups.Create;

+ 1 - 1
src/main/java/nju/seec/helper/dto/SlideFileDTO.java → src/main/java/nju/seec/helper/dto/slide/SlideFileDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.slide;
 
 import lombok.Data;
 import org.springframework.web.multipart.MultipartFile;

+ 1 - 1
src/main/java/nju/seec/helper/dto/SlideStateDTO.java → src/main/java/nju/seec/helper/dto/slide/SlideStateDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.slide;
 
 import lombok.Data;
 import nju.seec.helper.util.enums.SlideState;

+ 1 - 1
src/main/java/nju/seec/helper/dto/LoginDTO.java → src/main/java/nju/seec/helper/dto/user/LoginDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.user;
 
 import lombok.Data;
 import nju.seec.helper.dto.validator.schoolemail.SchoolEmail;

+ 1 - 1
src/main/java/nju/seec/helper/dto/LoginUser.java → src/main/java/nju/seec/helper/dto/user/LoginUser.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.user;
 
 import lombok.Data;
 import lombok.experimental.Accessors;

+ 1 - 1
src/main/java/nju/seec/helper/dto/ResetPasswordDTO.java → src/main/java/nju/seec/helper/dto/user/ResetPasswordDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.user;
 
 import lombok.Data;
 import nju.seec.helper.dto.validator.schoolemail.SchoolEmail;

+ 1 - 1
src/main/java/nju/seec/helper/dto/UserDTO.java → src/main/java/nju/seec/helper/dto/user/UserDTO.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.dto;
+package nju.seec.helper.dto.user;
 
 import lombok.Data;
 import nju.seec.helper.dto.groups.Create;

+ 1 - 1
src/main/java/nju/seec/helper/entity/Course.java

@@ -16,7 +16,7 @@ import java.time.LocalDateTime;
 @Accessors(chain = true)
 @Entity
 @Table(name = "course",
-        uniqueConstraints = @UniqueConstraint(name = "课程名唯一约束", columnNames = {"teacher_id", "name", "delete_at"}))
+        uniqueConstraints = @UniqueConstraint(name = "course_name_constraint", columnNames = {"teacher_id", "delete_at", "name"}))
 @DynamicUpdate
 @DynamicInsert
 public class Course {

+ 76 - 66
src/main/java/nju/seec/helper/entity/Quiz.java

@@ -1,66 +1,76 @@
-//package nju.seec.helper.entity;
-//
-//import lombok.Data;
-//import lombok.experimental.Accessors;
-//import nju.seec.helper.entity.converter.ListConverter;
-//import nju.seec.helper.util.enums.QuizState;
-//import nju.seec.helper.util.enums.QuizType;
-//import org.hibernate.annotations.CreationTimestamp;
-//import org.hibernate.annotations.DynamicInsert;
-//import org.hibernate.annotations.DynamicUpdate;
-//import org.hibernate.annotations.UpdateTimestamp;
-//
-//import javax.persistence.*;
-//import java.time.LocalDateTime;
-//import java.util.List;
-//
-///**
-// * @author sheen
-// * <p>
-// * updated by cst
-// */
-//@Data
-//@Accessors(chain = true)
-//@Entity
-//@Table(name = "quiz", uniqueConstraints = @UniqueConstraint(name = "测试名约束", columnNames = {"slide_id", "name"}))
-//@DynamicInsert
-//@DynamicUpdate
-//public class Quiz {
-//    @Id
-//    @GeneratedValue(strategy = GenerationType.IDENTITY)
-//    @Column(name = "id")
-//    private Long id;
-//
-//    @Basic
-//    @Column(name = "name", nullable = false)
-//    private String name;
-//
-//    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
-//    @JoinColumn(name = "slide_id", referencedColumnName = "id")
-//    private Slide slide;
-//
-//    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
-//    @JoinColumn(name = "teacher_id", referencedColumnName = "id")
-//    private User teacher;
-//
-//    @Convert(converter = ListConverter.class)
-//    private List<String> questions;
-//
-//    @Enumerated(value = EnumType.STRING)
-//    @Column(name = "type", nullable = false)
-//    private QuizType type;
-//
-//    @Enumerated(value = EnumType.STRING)
-//    @Column(name = "state", nullable = false)
-//    private QuizState state;
-//
-//    @Basic
-//    @Column(name = "create_at", nullable = false, updatable = false)
-//    @CreationTimestamp
-//    private LocalDateTime createAt;
-//
-//    @Basic
-//    @Column(name = "update_at", nullable = false)
-//    @UpdateTimestamp
-//    private LocalDateTime updateAt;
-//}
+package nju.seec.helper.entity;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+import nju.seec.helper.entity.converter.ListConverter;
+import nju.seec.helper.util.enums.QuizState;
+import nju.seec.helper.util.enums.QuizType;
+import org.hibernate.annotations.CreationTimestamp;
+import org.hibernate.annotations.DynamicInsert;
+import org.hibernate.annotations.DynamicUpdate;
+import org.hibernate.annotations.UpdateTimestamp;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * @author sheen
+ * <p>
+ * updated by cst
+ */
+@Data
+@Accessors(chain = true)
+@Entity
+@Table(name = "quiz", uniqueConstraints = @UniqueConstraint(name = "quiz_name_constraint", columnNames = {"slide_id", "delete_at", "name",}))
+@DynamicInsert
+@DynamicUpdate
+public class Quiz {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private Long id;
+
+    @Basic
+    @Column(name = "name", nullable = false)
+    private String name;
+
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "course_id", referencedColumnName = "id")
+    private Course course;
+
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "slide_id", referencedColumnName = "id")
+    private Slide slide;
+
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "teacher_id", referencedColumnName = "id")
+    private User teacher;
+
+    @Convert(converter = ListConverter.class)
+    private List<String> questions;
+
+    @Enumerated(value = EnumType.STRING)
+    @Column(name = "type", nullable = false)
+    private QuizType type;
+
+    @Enumerated(value = EnumType.STRING)
+    @Column(name = "state", nullable = false)
+    private QuizState state;
+
+    @Column(name = "max_submit_number", nullable = false)
+    private Integer maxSubmitNumber;
+
+    @Basic
+    @Column(name = "create_at", nullable = false, updatable = false)
+    @CreationTimestamp
+    private LocalDateTime createAt;
+
+    @Basic
+    @Column(name = "update_at", nullable = false)
+    @UpdateTimestamp
+    private LocalDateTime updateAt;
+
+    @Column(name = "delete_at", nullable = false, columnDefinition = "bigint default 0 not null")
+    private Long deleteAt = 0L;
+}

+ 55 - 60
src/main/java/nju/seec/helper/entity/QuizStudentAnswer.java

@@ -1,60 +1,55 @@
-//package nju.seec.helper.entity;
-//
-//
-//import lombok.Data;
-//import nju.seec.helper.entity.converter.MapConverter;
-//import org.hibernate.annotations.CreationTimestamp;
-//import org.hibernate.annotations.DynamicInsert;
-//import org.hibernate.annotations.DynamicUpdate;
-//import org.hibernate.annotations.UpdateTimestamp;
-//
-//import javax.persistence.*;
-//import java.math.BigDecimal;
-//import java.time.LocalDateTime;
-//import java.util.Map;
-//
-///**
-// * @author sxt
-// * <p>
-// * updated by cst
-// */
-//@Data
-//@Entity
-//@Table(name = "quiz_student_answer",
-//        indexes = {@Index(columnList = "quiz"),
-//                @Index(columnList = "student ,quiz")})
-//@DynamicUpdate
-//@DynamicInsert
-//public class QuizStudentAnswer {
-//    @Id
-//    @GeneratedValue(strategy = GenerationType.IDENTITY)
-//    @Column(name = "id")
-//    private Long id;
-//
-//    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
-//    @JoinColumn(name = "quiz_id", referencedColumnName = "id")
-//    private Quiz quiz;
-//
-//    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
-//    @JoinColumn(name = "student_id", referencedColumnName = "id")
-//    private User student;
-//
-//    @SuppressWarnings("all")
-//    @Convert(converter = MapConverter.class)
-//    @Column(nullable = false)
-//    private Map<String, String> answers;
-//
-//    @Column(name = "create_at", nullable = false, updatable = false)
-//    @CreationTimestamp
-//    private LocalDateTime createAt;
-//
-//    @Column(name = "modified_at", nullable = false)
-//    @UpdateTimestamp
-//    private LocalDateTime modifiedAt;
-//
-//    @Column(name = "submit_number", nullable = false, columnDefinition = "int default 1 not null")
-//    private Integer submitNumber;
-//
-//    @Column(nullable = false)
-//    private BigDecimal score;
-//}
+package nju.seec.helper.entity;
+
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+import nju.seec.helper.entity.converter.MapConverter;
+import org.hibernate.annotations.DynamicInsert;
+import org.hibernate.annotations.DynamicUpdate;
+import org.hibernate.annotations.UpdateTimestamp;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Map;
+
+/**
+ * @author sxt
+ * <p>
+ * updated by cst
+ */
+@Data
+@Accessors(chain = true)
+@Entity
+@Table(name = "quiz_student_answer", uniqueConstraints = @UniqueConstraint(columnNames = {"quiz_id", "student_id"}))
+@DynamicUpdate
+@DynamicInsert
+public class QuizStudentAnswer {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private Long id;
+
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "quiz_id", referencedColumnName = "id")
+    private Quiz quiz;
+
+    @ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
+    @JoinColumn(name = "student_id", referencedColumnName = "id")
+    private User student;
+
+    @SuppressWarnings("all")
+    @Convert(converter = MapConverter.class)
+    @Column(nullable = false)
+    private Map<String, Object> answers;
+
+    @Column(name = "modified_at", nullable = false)
+    @UpdateTimestamp
+    private LocalDateTime modifiedAt;
+
+    @Column(name = "submit_number", nullable = false, columnDefinition = "int default 1 not null")
+    private Integer submitNumber = 0;
+
+    @Column(nullable = false)
+    private BigDecimal score;
+}

+ 1 - 1
src/main/java/nju/seec/helper/entity/Slide.java

@@ -18,7 +18,7 @@ import java.time.LocalDateTime;
 @Accessors(chain = true)
 @Entity
 @Table(name = "slide",
-        uniqueConstraints = @UniqueConstraint(name = "课件名唯一约束", columnNames = {"course_id", "name", "delete_at"}))
+        uniqueConstraints = @UniqueConstraint(name = "slide_name_constraint", columnNames = {"course_id", "delete_at", "name",}))
 @DynamicUpdate
 @DynamicInsert
 public class Slide {

+ 0 - 25
src/main/java/nju/seec/helper/entity/StudentScore.java

@@ -1,25 +0,0 @@
-//package nju.seec.helper.entity;
-//
-//import lombok.Data;
-//
-//import javax.persistence.*;
-//import java.io.Serializable;
-//
-//@Data
-//@Entity
-//@Table(name = "student_score")
-//public class StudentScore implements Serializable {
-//    @Id
-//    @GeneratedValue(strategy = GenerationType.IDENTITY)
-//    @Column(name = "id")
-//    private Long id;
-//
-//    @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
-//    @JoinColumn(name = "student", referencedColumnName = "id")
-//    private User student;
-//
-//    @Column(name = "quiz_id")
-//    private Long quizId;
-//    @Column(name = "score")
-//    private Integer score;
-//}

+ 4 - 4
src/main/java/nju/seec/helper/entity/converter/MapConverter.java

@@ -9,15 +9,15 @@ import java.util.Map;
 /**
  * @author cst
  */
-public class MapConverter implements AttributeConverter<Map<String, String>, String> {
+public class MapConverter implements AttributeConverter<Map<String, Object>, String> {
     @Override
-    public String convertToDatabaseColumn(Map<String, String> attribute) {
+    public String convertToDatabaseColumn(Map<String, Object> attribute) {
         return JsonUtils.toJson(attribute);
     }
 
     @Override
-    public Map<String, String> convertToEntityAttribute(String dbData) {
-        return JsonUtils.fromJson(dbData, new TypeToken<Map<String, String>>() {
+    public Map<String, Object> convertToEntityAttribute(String dbData) {
+        return JsonUtils.fromJson(dbData, new TypeToken<Map<String, Object>>() {
         });
     }
 }

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.dto.CommentDTO;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.comment.CommentDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.vo.CommentVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.dto.CourseFileDTO;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.course.CourseFileDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.vo.CourseFileVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;

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

@@ -1,8 +1,8 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.dto.ChooseDTO;
-import nju.seec.helper.dto.CourseDTO;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.course.ChooseDTO;
+import nju.seec.helper.dto.course.CourseDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.vo.CourseVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.MessageDTO;
+import nju.seec.helper.dto.message.MessageDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.entity.Message;
 import nju.seec.helper.util.enums.MessageType;
 import org.springframework.data.domain.Page;

+ 11 - 2
src/main/java/nju/seec/helper/service/NoticeService.java

@@ -1,7 +1,7 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.NoticeDTO;
+import nju.seec.helper.dto.notice.NoticeDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.vo.NoticeVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -46,4 +46,13 @@ public interface NoticeService {
      * @param noticeId
      */
     void deleteNotice(LoginUser user, Long noticeId);
+
+    /**
+     * 取得某一公告
+     *
+     * @param user
+     * @param noticeId
+     * @return
+     */
+    NoticeVO getOneNotice(LoginUser user, Long noticeId);
 }

+ 53 - 26
src/main/java/nju/seec/helper/service/QuestionService.java

@@ -1,34 +1,61 @@
-//package nju.seec.helper.service;
-//
-//import com.fasterxml.jackson.core.JsonProcessingException;
-//import nju.seec.helper.dto.LoginUser;
-//import nju.seec.helper.vo.quiz.BaseQuestionVO;
-//import org.springframework.data.domain.Page;
-//import org.springframework.data.domain.Pageable;
-//
-//public interface QuestionService {
-//    /**
-//     * @param user
-//     * @param stem
-//     * @param pageable
-//     * @return
-//     * @throws JsonProcessingException
-//     */
-//    Page<BaseQuestionVO> getQuestionList(LoginUser user, String stem, Pageable pageable) throws JsonProcessingException;
-//
-//    /**
-//     * @param questionId
-//     * @return
-//     */
-//    BaseQuestionVO getQuestion(String questionId);
-//
+package nju.seec.helper.service;
+
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.entity.Quiz;
+import nju.seec.helper.vo.question.BaseQuestionVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.List;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+public interface QuestionService {
+    /**
+     * 取得题目列表
+     *
+     * @param user
+     * @param stem
+     * @param pageable
+     * @return
+     */
+    Page<BaseQuestionVO> getQuestions(LoginUser user, String stem, Pageable pageable);
+
+    /**
+     * 取得题目详情
+     *
+     * @param questionId
+     * @return
+     */
+    BaseQuestionVO getQuestion(String questionId);
+
+    /**
+     * 根据测试获取题目
+     *
+     * @param user
+     * @param quizId
+     * @return
+     */
+    List<BaseQuestionVO> getQuestionsByQuiz(LoginUser user, Long quizId);
+
+    /**
+     * 根据测试获取题目(无需权限检验)
+     *
+     * @param quiz
+     * @return
+     */
+    List<BaseQuestionVO> getQuestionsByQuiz(Quiz quiz);
+
 //    /**
 //     * @param newQuestion
 //     * @return
 //     */
-//    BaseQuestionVO newQuestion(BaseQuestionVO newQuestion);
+//    BaseQuestionVO createQuestion(BaseQuestionVO newQuestion);
 //
 //    BaseQuestionVO updateQuestion(BaseQuestionVO newQuestion);
 //
 //    void deleteQuestion(String questionId);
-//}
+}

+ 71 - 23
src/main/java/nju/seec/helper/service/QuizService.java

@@ -1,9 +1,15 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.dto.LoginUser;
-//import nju.seec.helper.dto.QuizDTO;
+//import nju.seec.helper.dto.quiz.QuizDTO;
 //import nju.seec.helper.vo.quiz.QuizVO;
 
+import nju.seec.helper.dto.quiz.QuizDTO;
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.entity.Slide;
+import nju.seec.helper.vo.quiz.QuizVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
 /**
  * @author xst
  * <p>
@@ -11,23 +17,70 @@ import nju.seec.helper.dto.LoginUser;
  */
 public interface QuizService {
 
-//    /**
-//     * 创建测试
-//     *
-//     * @param user
-//     * @param quizDTO
-//     * @return
-//     */
-//    QuizVO createQuiz(LoginUser user, QuizDTO quizDTO);
+    /**
+     * 创建测试
+     *
+     * @param user
+     * @param quizDTO
+     * @return
+     */
+    QuizVO createQuiz(LoginUser user, QuizDTO quizDTO);
 
-//    /**
-//     * 修改测试
-//     *
-//     * @param user
-//     * @param quizDTO
-//     * @return
-//     */
-//    QuizVO modifyQuiz(LoginUser user, QuizDTO quizDTO);
+    /**
+     * 修改测试
+     *
+     * @param user
+     * @param quizDTO
+     * @return
+     */
+    QuizVO modifyQuiz(LoginUser user, QuizDTO quizDTO);
+
+    /**
+     * 删除测试
+     *
+     * @param user
+     * @param quizId
+     * @return
+     */
+    void deleteQuiz(LoginUser user, Long quizId);
+
+    /**
+     * 教师获取测试
+     *
+     * @param user
+     * @param slideId
+     * @param key
+     * @param pageable
+     * @return
+     */
+    Page<QuizVO> teacherGetQuizzes(LoginUser user, Long slideId, String key, Pageable pageable);
+
+    /**
+     * 学生获取测试
+     *
+     * @param user
+     * @param slideId
+     * @param key
+     * @param pageable
+     * @return
+     */
+    Page<QuizVO> studentGetQuizzes(LoginUser user, Long slideId, String key, Pageable pageable);
+
+    /**
+     * 获取某个测试
+     *
+     * @param user
+     * @param quizId
+     * @return
+     */
+    QuizVO getOneQuiz(LoginUser user, Long quizId);
+
+    /**
+     * 修改测试状态
+     *
+     * @param slide
+     */
+    void modifyQuizState(Slide slide);
 
 //    /**
 //     * @param quizId
@@ -74,11 +127,6 @@ public interface QuizService {
 //     */
 //    QuizVO combineQuizWithAnswer(Long quizId, Long uid, UserType type);
 //
-//    /**
-//     * @param quizId
-//     * @return
-//     */
-//    String deleteQuiz(Long quizId);
 //
 //    /**
 //     * @param quizId

+ 40 - 0
src/main/java/nju/seec/helper/service/QuizStudentAnswerService.java

@@ -0,0 +1,40 @@
+package nju.seec.helper.service;
+
+import nju.seec.helper.dto.quiz.QuizStudentAnswerDTO;
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.vo.quiz.QuizStudentAnswerVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * @author cst
+ */
+public interface QuizStudentAnswerService {
+    /**
+     * 提交测试作答
+     *
+     * @param user
+     * @param quizStudentAnswerDTO
+     * @return
+     */
+    QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, QuizStudentAnswerDTO quizStudentAnswerDTO);
+
+    /**
+     * 老师查看学生答案列表
+     *
+     * @param user
+     * @param quizId
+     * @param pageable
+     * @return
+     */
+    Page<QuizStudentAnswerVO> teacherGetQuizStudentAnswers(LoginUser user, Long quizId, Pageable pageable);
+
+    /**
+     * 学生查看自己的答案
+     *
+     * @param user
+     * @param quizId
+     * @return
+     */
+    QuizStudentAnswerVO studentGetSelfQuizStudentAnswer(LoginUser user, Long quizId);
+}

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.ReplyDTO;
+import nju.seec.helper.dto.reply.ReplyDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.vo.ReplyVO;
 
 /**

+ 4 - 4
src/main/java/nju/seec/helper/service/SlideService.java

@@ -1,9 +1,9 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.SlideDTO;
-import nju.seec.helper.dto.SlideFileDTO;
-import nju.seec.helper.dto.SlideStateDTO;
+import nju.seec.helper.dto.slide.SlideDTO;
+import nju.seec.helper.dto.slide.SlideFileDTO;
+import nju.seec.helper.dto.slide.SlideStateDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.vo.SlideVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;

+ 3 - 3
src/main/java/nju/seec/helper/service/UserService.java

@@ -1,8 +1,8 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.ResetPasswordDTO;
-import nju.seec.helper.dto.UserDTO;
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.dto.user.ResetPasswordDTO;
+import nju.seec.helper.dto.user.UserDTO;
 import nju.seec.helper.vo.UserVO;
 
 /**

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

@@ -3,8 +3,8 @@ package nju.seec.helper.service.impl;
 import nju.seec.helper.dao.CommentDAO;
 import nju.seec.helper.dao.SlideDAO;
 import nju.seec.helper.dao.UserDAO;
-import nju.seec.helper.dto.CommentDTO;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.comment.CommentDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.entity.Comment;
 import nju.seec.helper.entity.Slide;
 import nju.seec.helper.service.CommentService;

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

@@ -4,8 +4,8 @@ import lombok.SneakyThrows;
 import nju.seec.helper.dao.ChooseDAO;
 import nju.seec.helper.dao.CourseDAO;
 import nju.seec.helper.dao.CourseFileDAO;
-import nju.seec.helper.dto.CourseFileDTO;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.course.CourseFileDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.entity.CourseFile;
 import nju.seec.helper.service.CourseFileService;

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

@@ -3,9 +3,9 @@ package nju.seec.helper.service.impl;
 import nju.seec.helper.dao.ChooseDAO;
 import nju.seec.helper.dao.CourseDAO;
 import nju.seec.helper.dao.UserDAO;
-import nju.seec.helper.dto.ChooseDTO;
-import nju.seec.helper.dto.CourseDTO;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.course.ChooseDTO;
+import nju.seec.helper.dto.course.CourseDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.entity.Choose;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.service.CourseService;
@@ -143,8 +143,8 @@ public class CourseServiceImpl implements CourseService {
         long exists = courseDAO.count((Specification<Course>) (root, query, cb) ->
                 cb.and(cb.notEqual(root.get("id"), courseId),
                         cb.equal(root.get("teacher").get("id"), teacherId),
-                        cb.equal(root.get("name"), name),
-                        cb.equal(root.get("deleteAt"), 0L)
+                        cb.equal(root.get("deleteAt"), 0L),
+                        cb.equal(root.get("name"), name)
                 ));
         if (exists > 0) {
             throw HelperException.of(ExceptionType.CONFLICT, "该课程名已使用");
@@ -154,8 +154,8 @@ public class CourseServiceImpl implements CourseService {
     private Page<Course> findByTeacherIdAndKey(Long teacherId, String key, Pageable pageable) {
         return courseDAO.findAll(
                 (Specification<Course>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("deleteAt"), 0L),
                         cb.equal(root.get("teacher").get("id"), teacherId),
+                        cb.equal(root.get("deleteAt"), 0L),
                         cb.like(root.get("name"), StringUtils.keyPattern(key))),
                 pageable);
     }

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

@@ -1,8 +1,8 @@
 package nju.seec.helper.service.impl;
 
 import nju.seec.helper.dao.MessageDAO;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.MessageDTO;
+import nju.seec.helper.dto.message.MessageDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.entity.Message;
 import nju.seec.helper.service.MessageService;
 import nju.seec.helper.util.enums.MessageType;

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

@@ -3,8 +3,8 @@ package nju.seec.helper.service.impl;
 import nju.seec.helper.dao.ChooseDAO;
 import nju.seec.helper.dao.CourseDAO;
 import nju.seec.helper.dao.NoticeDAO;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.NoticeDTO;
+import nju.seec.helper.dto.notice.NoticeDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.entity.Notice;
 import nju.seec.helper.service.MessageService;
@@ -17,6 +17,7 @@ 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.Set;
 
@@ -38,6 +39,7 @@ public class NoticeServiceImpl implements NoticeService {
         this.messageService = messageService;
     }
 
+    @Transactional(rollbackFor = Exception.class)
     @Override
     public NoticeVO create(LoginUser user, NoticeDTO noticeDTO) {
         Course course = courseDAO.findCourseById(noticeDTO.getCourseId());
@@ -53,6 +55,7 @@ public class NoticeServiceImpl implements NoticeService {
         return new NoticeVO(noticeDAO.save(notice));
     }
 
+    @Transactional(rollbackFor = Exception.class)
     @Override
     public NoticeVO modify(LoginUser user, NoticeDTO noticeDTO) {
         Notice notice = noticeDAO.findNoticeById(noticeDTO.getId());
@@ -62,11 +65,13 @@ public class NoticeServiceImpl implements NoticeService {
         return new NoticeVO(noticeDAO.save(notice));
     }
 
+    @Transactional(readOnly = true)
     @Override
     public Page<NoticeVO> getNoticesByCourse(LoginUser user, Long courseId, String key, Pageable pageable) {
         return findByCourseIdAndKey(courseId, key, pageable).map(NoticeVO::new);
     }
 
+    @Transactional(rollbackFor = Exception.class)
     @Override
     public void deleteNotice(LoginUser user, Long noticeId) {
         Notice notice = noticeDAO.findNoticeById(noticeId);
@@ -74,6 +79,12 @@ public class NoticeServiceImpl implements NoticeService {
         noticeDAO.deleteById(noticeId);
     }
 
+    @Transactional(readOnly = true)
+    @Override
+    public NoticeVO getOneNotice(LoginUser user, Long noticeId) {
+        return new NoticeVO(noticeDAO.findNoticeById(noticeId));
+    }
+
     private Page<Notice> findByCourseIdAndKey(Long courseId, String key, Pageable pageable) {
         String keyPattern = StringUtils.keyPattern(key);
 

+ 62 - 47
src/main/java/nju/seec/helper/service/impl/QuestionServiceImpl.java

@@ -1,48 +1,63 @@
-//package nju.seec.helper.service.impl;
-//
-//import com.fasterxml.jackson.core.JsonProcessingException;
-//import nju.seec.helper.dto.LoginUser;
-//import nju.seec.helper.service.QuestionService;
-//import nju.seec.helper.util.BokUtil;
-//import nju.seec.helper.util.enums.ExceptionType;
-//import nju.seec.helper.util.exception.HelperException;
-//import nju.seec.helper.vo.quiz.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 java.util.ArrayList;
-//import java.util.HashMap;
-//import java.util.List;
-//import java.util.Map;
-//
-//@Service
-//public class QuestionServiceImpl implements QuestionService {
-//    private final BokUtil bokUtil;
-//
-//    @Autowired
-//    public QuestionServiceImpl(BokUtil bokUtil) {
-//        this.bokUtil = bokUtil;
-//    }
-//
-//    @Override
-//    public Page<BaseQuestionVO> getQuestionList(LoginUser user, String stem, Pageable pageable) {
-//        //应该对象化
-//        List<BaseQuestionVO> questions = new ArrayList<>();
-//        Map page = new HashMap();
-//
-//        bokUtil.bokFindByStemLike(stem, questions, page, pageable);
-//        return new PageImpl<>(questions, pageable, Long.valueOf((Integer) page.get("totalElements")));
-//    }
-//
-//    @Override
-//    public BaseQuestionVO getQuestion(String questionId) {
-//        return bokUtil.bokFindById(questionId);
-//    }
-//
-//    @Override
+package nju.seec.helper.service.impl;
+
+import nju.seec.helper.dao.QuizDAO;
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.entity.Quiz;
+import nju.seec.helper.service.QuestionService;
+import nju.seec.helper.util.BokUtil;
+import nju.seec.helper.util.enums.QuizState;
+import nju.seec.helper.util.enums.UserType;
+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.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@Service
+public class QuestionServiceImpl implements QuestionService {
+    private final QuizDAO quizDAO;
+    private final BokUtil bokUtil;
+
+    @Autowired
+    public QuestionServiceImpl(QuizDAO quizDAO, BokUtil bokUtil) {
+        this.quizDAO = quizDAO;
+        this.bokUtil = bokUtil;
+    }
+
+    @Override
+    public Page<BaseQuestionVO> getQuestions(LoginUser user, String stem, Pageable pageable) {
+        return bokUtil.bokFindByStemLike(stem, pageable, true);
+    }
+
+    @Override
+    public BaseQuestionVO getQuestion(String questionId) {
+        return bokUtil.bokFindById(questionId, true);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<BaseQuestionVO> getQuestionsByQuiz(LoginUser user, Long quizId) {
+        Quiz quiz = quizDAO.findQuizById(quizId);
+        List<String> questionIds = quiz.getQuestions();
+        return bokUtil.bokFindByIdIn(questionIds, quiz.getState() == QuizState.CLOSED || user.getType() == UserType.TEACHER);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<BaseQuestionVO> getQuestionsByQuiz(Quiz quiz) {
+        List<String> questionIds = quiz.getQuestions();
+        return bokUtil.bokFindByIdIn(questionIds, true);
+    }
+
+    //    @Override
 //    public BaseQuestionVO newQuestion(BaseQuestionVO newQuestion) {
 //        try {
 //            return bokUtil.postNewQuestion(newQuestion);
@@ -64,5 +79,5 @@
 //    public void deleteQuestion(String questionId) {
 //        bokUtil.deleteQuestion(questionId);
 //    }
-//
-//}
+
+}

+ 186 - 353
src/main/java/nju/seec/helper/service/impl/QuizServiceImpl.java

@@ -1,12 +1,29 @@
 package nju.seec.helper.service.impl;
 
-import nju.seec.helper.dto.LoginUser;
-//import nju.seec.helper.dto.QuizDTO;
+import com.google.common.collect.ImmutableSet;
+import nju.seec.helper.dao.*;
+import nju.seec.helper.dto.quiz.QuizDTO;
+import nju.seec.helper.dto.user.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.vo.quiz.QuizVO;
+import nju.seec.helper.service.util.AuthUtils;
+import nju.seec.helper.service.util.StringUtils;
+import nju.seec.helper.util.enums.*;
+import nju.seec.helper.util.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
@@ -15,356 +32,172 @@ import org.springframework.transaction.annotation.Transactional;
  */
 @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;
-//    }
+    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, QuizDTO quizDTO) {
+        Quiz quiz = quizDAO.findQuizById(quizDTO.getId());
+
+        AuthUtils.checkDataAuth(user.getId(), quiz.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<Quiz>) (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.getId(), "您无权删除该测试");
+        quiz.setDeleteAt(System.currentTimeMillis());
+        quizDAO.save(quiz);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public Page<QuizVO> teacherGetQuizzes(LoginUser user, Long slideId, String key, Pageable pageable) {
+        return findBySlideIdAndKey(slideId, key, pageable).map(QuizVO::new);
+    }
+
+    public static final Set<QuizState> STUDENT_NOT_GET_SET = ImmutableSet.of(QuizState.NOT_STARTED);
+
+    @Transactional(readOnly = true)
+    @Override
+    public Page<QuizVO> studentGetQuizzes(LoginUser user, Long slideId, String key, Pageable pageable) {
+        return findBySlideIdAndStateNotInAndKey(slideId, STUDENT_NOT_GET_SET, 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<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());
+            }
+        });
+    }
+
+    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<Quiz> findBySlideIdAndKey(Long slideId, String key, Pageable pageable) {
+        return quizDAO.findAll(
+                (Specification<Quiz>) (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))),
+                pageable);
+    }
 
-//    @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()
-//                .setSlide(slide)
-//                .setTeacher(slide.getTeacher())
-//                .setType(quizDTO.getType())
-//                .setName(quizDTO.getName())
-//                .setQuestions(quizDTO.getQuestions());
-//
-//        return new QuizVO(quizDAO.save(quiz));
-//    }
 
-//    @Transactional(rollbackFor = Exception.class)
-//    @Override
-//    public QuizVO modifyQuiz(LoginUser user, QuizDTO quizDTO) {
-//        Quiz quiz = quizDAO.findQuizById(quizDTO.getId());
-//
-//        AuthUtils.checkDataAuth(user.getId(), quiz.getId(), "您无权创建该测试");
-//        checkSameName(0L, quizDTO.getSlideId(), quizDTO.getName());
-//
-//        Slide slide = quiz.getSlide();
-//        checkQuizTypeOkToSlideState(slide.getState(), quizDTO.getType());
-//
-//    }
-//
-//    @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
-//    public Page<Quiz> getAllBasicQuizByUser(Pageable pageable, QuizState state, Long uid) {
-//        User user = userDAO.findUserById(uid);
-//        //todo: 这里写的太狗屎了,可以用重写sql或者用实例查询
-//        if (user.getType().equals(UserType.STUDENT)) {
-//            final Set<Long> courseIdsByStudentId = chooseDAO.findCourseIdsByStudentId(uid);
-//            final Set<Course> 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<QuizResultVO> getQuizResult(Long quizId) {
-//        Assert.notNull(quizId, "错误的测试!");
-//        final Quiz quiz = quizDAO.findQuizById(quizId);
-//        if (quiz.getState() != QuizState.CLOSED) {
-//            throw new RuntimeException("测试未关闭,无法得到测试结果");
-//        }
-//        List<QuizResultVO> retList = new ArrayList<>();
-//        final List<StudentScore> studentScoreList = quiz.getStudentScore();
-//        Map<Long, List<QuizStudentAnswer>> 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<QuizStudentAnswer> list = idQuizStudentAnswerMap.get(student.getId());
-//            retList.add(new QuizResultVO(studentScore, student, list));
-//        }
-//        return retList;
-//    }
-//
-//    @Override
-//    public void modifySlideState(Slide slide) {
-//        final List<Quiz> quizzes = quizDAO.findAllBySlide(slide);
-//        quizzes.forEach(quiz -> checkQuizState(quiz, slide));
-//    }
-//
-//    @Override
-//    @Transactional(rollbackFor = Exception.class)
-//    public Integer upStudentAnswer(Long quizId, Long uid, List<QuestionStudentAnswerDTO> 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<QuizStudentAnswer> quizAnswers = quizStudentAnswerDAO.findByStudentAndQuiz(student, quiz);
-//        Set<String> questionIdSet = new HashSet<>(quiz.getQuestions());
-//        Map<String, QuizStudentAnswer> qsaMap = new HashMap<>(questionIdSet.size());
-//        for (QuizStudentAnswer qsa : quizAnswers) {
-//            qsaMap.put(qsa.getQuestionId(), qsa);
-//        }
-//        //确定被更新或者新建的学生答案提交
-//        List<QuizStudentAnswer> 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<Quiz> 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<QuizStudentAnswer> quizAnswers = quizStudentAnswerDAO.findAllByQuiz(quiz);
-//                final Set<Long> studentIds = chooseDAO.findStudentIdsByCourseId(slide.getCourse().getId());
-//                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<Long, Map<String, QuizStudentAnswer>> student2QuestionId2Answer;
-//
-//        Student2QuestionId2Answer(Set<Long> studentIds, Integer quizQuestionSize) {
-//            student2QuestionId2Answer = new LinkedHashMap<>(studentIds.size());
-//            for (Long studentId : studentIds) {
-//                Map<String, QuizStudentAnswer> questionId2Answer = new LinkedHashMap<>(quizQuestionSize);
-//                student2QuestionId2Answer.put(studentId, questionId2Answer);
-//            }
-//        }
-//
-//        void addAnswer(QuizStudentAnswer answers) {
-//            Map<String, QuizStudentAnswer> stringQuizStudentAnswerMap = student2QuestionId2Answer.get(answers.getStudent().getId());
-//            if (stringQuizStudentAnswerMap == null) {
-//                return;
-//            }
-//            stringQuizStudentAnswerMap.put(answers.getQuestionId(), answers);
-//        }
-//
-//        void fullAnswer(Quiz quiz) {
-//            List<String> 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<BaseQuestionVO> 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<QuizStudentAnswer> getAll() {
-//            List<QuizStudentAnswer> 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<BaseQuestionVO> 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<BaseQuestionVO> questionVoS = bokUtil.bokFindByIdIn(quizById.getQuestions());
-//            return new QuizVO(quizById, questionVoS);
-//        }
-//        if (quizById.getState().equals(QuizState.NOT_STARTED)) {
-//            //未开始看不到题目
-//            return new QuizVO(quizById);
-//        }
-//
-//        List<BaseQuestionVO> questionVos = bokUtil.bokFindByIdIn(quizById.getQuestions());
-//        final Set<QuizStudentAnswer> quizAnswers = quizStudentAnswerDAO.findByStudentAndQuiz(student, quizById);
-//        Map<String, QuizStudentAnswer> 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);
-//    }
-//
-//    private void checkSameName(Long quizId, Long slideId, String name) {
-//        long exists = quizDAO.count((Specification<Quiz>) (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 Page<Quiz> findBySlideIdAndStateNotInAndKey(Long slideId, Set<QuizState> exclusiveStates, String key, Pageable pageable) {
+        return quizDAO.findAll(
+                (Specification<Quiz>) (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))),
+                pageable);
+    }
 }

+ 104 - 0
src/main/java/nju/seec/helper/service/impl/QuizStudentAnswerServiceImpl.java

@@ -0,0 +1,104 @@
+package nju.seec.helper.service.impl;
+
+import nju.seec.helper.dao.QuizDAO;
+import nju.seec.helper.dao.QuizStudentAnswerDAO;
+import nju.seec.helper.dao.UserDAO;
+import nju.seec.helper.dto.quiz.QuizStudentAnswerDTO;
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.entity.Quiz;
+import nju.seec.helper.entity.QuizStudentAnswer;
+import nju.seec.helper.entity.User;
+import nju.seec.helper.service.QuestionService;
+import nju.seec.helper.service.QuizStudentAnswerService;
+import nju.seec.helper.util.enums.ExceptionType;
+import nju.seec.helper.util.enums.QuizState;
+import nju.seec.helper.util.exception.HelperException;
+import nju.seec.helper.vo.question.BaseQuestionVO;
+import nju.seec.helper.vo.quiz.QuizStudentAnswerVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * @author cst
+ */
+@Service
+public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
+    private final QuizDAO quizDAO;
+    private final UserDAO userDAO;
+    private final QuizStudentAnswerDAO quizStudentAnswerDAO;
+
+    private final QuestionService questionService;
+
+    public QuizStudentAnswerServiceImpl(QuizDAO quizDAO, UserDAO userDAO, QuizStudentAnswerDAO quizStudentAnswerDAO, QuestionService questionService) {
+        this.quizDAO = quizDAO;
+        this.userDAO = userDAO;
+        this.quizStudentAnswerDAO = quizStudentAnswerDAO;
+        this.questionService = questionService;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, QuizStudentAnswerDTO quizStudentAnswerDTO) {
+        Quiz quiz = quizDAO.findQuizById(quizStudentAnswerDTO.getQuizId());
+
+        if (quiz.getState() == QuizState.CLOSED) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, "测试已关闭,无法提交");
+        }
+
+        User student = userDAO.findUserById(user.getId());
+
+        QuizStudentAnswer quizStudentAnswer = quizStudentAnswerDAO.findByQuizAndStudent(quiz, student).orElse(new QuizStudentAnswer());
+
+        if (quizStudentAnswer.getSubmitNumber() >= quiz.getMaxSubmitNumber()) {
+            throw HelperException.of(ExceptionType.FORBIDDEN, String.format("已达最大提交次数%d次,无法再提交", quiz.getMaxSubmitNumber()));
+        }
+
+        // 计算得分
+        List<BaseQuestionVO> questionVOList = questionService.getQuestionsByQuiz(quiz);
+
+        AtomicReference<Integer> correct = new AtomicReference<>(0);
+
+        Map<String, Object> answers = quizStudentAnswerDTO.getAnswers();
+        questionVOList.forEach(baseQuestionVO -> {
+            Object answer;
+            if ((answer = answers.get(baseQuestionVO.getId())) != null && baseQuestionVO.pass(answer)) {
+                correct.getAndSet(correct.get() + 1);
+            }
+        });
+
+        BigDecimal score = BigDecimal.valueOf(correct.get() * 100).divide(BigDecimal.valueOf(questionVOList.size()), 2, RoundingMode.HALF_UP);
+        // 计算完毕
+
+        quizStudentAnswer.setQuiz(quiz)
+                .setStudent(student)
+                .setAnswers(quizStudentAnswerDTO.getAnswers())
+                .setSubmitNumber(quizStudentAnswer.getSubmitNumber() + 1)
+                .setAnswers(answers)
+                .setScore(score);
+
+        return new QuizStudentAnswerVO(quizStudentAnswerDAO.save(quizStudentAnswer));
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public Page<QuizStudentAnswerVO> teacherGetQuizStudentAnswers(LoginUser user, Long quizId, Pageable pageable) {
+        return quizStudentAnswerDAO.findByQuiz(quizDAO.findQuizById(quizId), pageable).map(QuizStudentAnswerVO::new);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public QuizStudentAnswerVO studentGetSelfQuizStudentAnswer(LoginUser user, Long quizId) {
+        return quizStudentAnswerDAO
+                .findByQuizAndStudent(quizDAO.findQuizById(quizId), userDAO.findUserById(user.getId()))
+                .map(QuizStudentAnswerVO::new)
+                .orElse(null);
+    }
+}

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

@@ -5,8 +5,8 @@ import nju.seec.helper.dao.CommentDAO;
 import nju.seec.helper.dao.ReplyDAO;
 import nju.seec.helper.dao.SlideDAO;
 import nju.seec.helper.dao.UserDAO;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.ReplyDTO;
+import nju.seec.helper.dto.reply.ReplyDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.entity.Comment;
 import nju.seec.helper.entity.Reply;
 import nju.seec.helper.entity.Slide;

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

@@ -7,10 +7,10 @@ import nju.seec.helper.dao.ChooseDAO;
 import nju.seec.helper.dao.CourseDAO;
 import nju.seec.helper.dao.SlideDAO;
 import nju.seec.helper.dao.UserDAO;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.SlideDTO;
-import nju.seec.helper.dto.SlideFileDTO;
-import nju.seec.helper.dto.SlideStateDTO;
+import nju.seec.helper.dto.slide.SlideDTO;
+import nju.seec.helper.dto.slide.SlideFileDTO;
+import nju.seec.helper.dto.slide.SlideStateDTO;
+import nju.seec.helper.dto.user.LoginUser;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.entity.Slide;
 import nju.seec.helper.service.MessageService;
@@ -121,6 +121,18 @@ public class SlideServiceImpl implements SlideService {
         return new SlideVO(slide);
     }
 
+    private void checkSameName(Long slideId, Long courseId, String name) {
+        long exists = slideDAO.count((Specification<Slide>) (root, query, cb) -> cb.and(
+                cb.notEqual(root.get("id"), slideId),
+                cb.equal(root.get("course").get("id"), courseId),
+                cb.equal(root.get("deleteAt"), 0L),
+                cb.equal(root.get("name"), name)));
+
+        if (exists > 0) {
+            throw HelperException.of(ExceptionType.CONFLICT, "该课件名已使用");
+        }
+    }
+
     @Transactional(rollbackFor = Exception.class)
     @Override
     public void modifySlideState(LoginUser user, SlideStateDTO slideStateDTO) {
@@ -129,14 +141,15 @@ public class SlideServiceImpl implements SlideService {
 
         slide.setState(slideStateDTO.getState());
         slide = slideDAO.save(slide);
-//        // 修改Slide状态同时推进quiz状态
-//        quizService.modifySlideState(slide);
 
         // 增加通知
         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());
         }
+
+        // 修改Slide状态同时推进quiz状态
+        quizService.modifyQuizState(slide);
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -206,6 +219,8 @@ public class SlideServiceImpl implements SlideService {
         return findByCourseIdAndTeacherIdAndKey(courseId, teacher.getId(), key, pageable).map(SlideVO::new);
     }
 
+    private static final Set<SlideState> STUDENT_NOT_GET_SET = ImmutableSet.of(SlideState.DRAFT);
+
     @Transactional(readOnly = true)
     @Override
     public Page<SlideVO> studentGetSlidesByCourse(LoginUser student, Long courseId, String key, Pageable pageable) {
@@ -213,7 +228,7 @@ public class SlideServiceImpl implements SlideService {
             throw HelperException.of(ExceptionType.FORBIDDEN, "你无权查看该课程下的课件");
         }
 
-        return findByCourseIdAndKeyAndStateNotIn(courseId, ImmutableSet.of(SlideState.DRAFT), key, pageable).map(SlideVO::new);
+        return findByCourseIdAndKeyAndStateNotInAndKey(courseId, STUDENT_NOT_GET_SET, key, pageable).map(SlideVO::new);
     }
 
     @Transactional(readOnly = true)
@@ -239,21 +254,6 @@ public class SlideServiceImpl implements SlideService {
         return new SlideVO(slide);
     }
 
-    /**
-     * 检查同名课件
-     */
-    private void checkSameName(Long slideId, Long courseId, String name) {
-        long exists = slideDAO.count((Specification<Slide>) (root, query, cb) -> cb.and(
-                cb.notEqual(root.get("id"), slideId),
-                cb.equal(root.get("course").get("id"), courseId),
-                cb.equal(root.get("deleteAt"), 0L),
-                cb.equal(root.get("name"), name)));
-
-        if (exists > 0) {
-            throw HelperException.of(ExceptionType.CONFLICT, "该课件名已使用");
-        }
-    }
-
     private Page<Slide> findByTeacherIdAndKey(Long teacherId, String key, Pageable pageable) {
         return slideDAO.findAll(
                 (Specification<Slide>) (root, query, cb) -> cb.and(
@@ -266,20 +266,20 @@ public class SlideServiceImpl implements SlideService {
     private Page<Slide> findByCourseIdAndTeacherIdAndKey(Long courseId, Long teacherId, String key, Pageable pageable) {
         return slideDAO.findAll(
                 (Specification<Slide>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("course").get("id"), courseId),
                         cb.equal(root.get("teacher").get("id"), teacherId),
+                        cb.equal(root.get("course").get("id"), courseId),
                         cb.equal(root.get("deleteAt"), 0L),
                         cb.like(root.get("name"), StringUtils.keyPattern(key))),
                 pageable);
     }
 
-    private Page<Slide> findByCourseIdAndKeyAndStateNotIn(Long courseId, Set<SlideState> exclusiveStates, String key, Pageable pageable) {
+    private Page<Slide> findByCourseIdAndKeyAndStateNotInAndKey(Long courseId, Set<SlideState> exclusiveStates, String key, Pageable pageable) {
         return slideDAO.findAll(
                 (Specification<Slide>) (root, query, cb) -> cb.and(
                         cb.equal(root.get("course").get("id"), courseId),
                         cb.equal(root.get("deleteAt"), 0L),
-                        cb.not(root.get("state").in(exclusiveStates)),
-                        cb.like(root.get("name"), StringUtils.keyPattern(key))
+                        cb.like(root.get("name"), StringUtils.keyPattern(key)),
+                        cb.not(root.get("state").in(exclusiveStates))
                 ),
                 pageable);
     }

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

@@ -2,9 +2,9 @@ package nju.seec.helper.service.impl;
 
 import com.google.common.collect.ImmutableMap;
 import nju.seec.helper.dao.UserDAO;
-import nju.seec.helper.dto.LoginUser;
-import nju.seec.helper.dto.ResetPasswordDTO;
-import nju.seec.helper.dto.UserDTO;
+import nju.seec.helper.dto.user.LoginUser;
+import nju.seec.helper.dto.user.ResetPasswordDTO;
+import nju.seec.helper.dto.user.UserDTO;
 import nju.seec.helper.entity.User;
 import nju.seec.helper.service.UserService;
 import nju.seec.helper.service.util.AuthUtils;

+ 142 - 205
src/main/java/nju/seec/helper/util/BokUtil.java

@@ -1,160 +1,125 @@
-//package nju.seec.helper.util;
-//
-//import com.fasterxml.jackson.annotation.JsonInclude;
-//import com.fasterxml.jackson.annotation.JsonProperty;
-//import com.fasterxml.jackson.core.JsonProcessingException;
-//import com.google.common.collect.ImmutableMap;
-//import lombok.Data;
-//import nju.seec.helper.util.enums.ExceptionType;
-//import nju.seec.helper.util.enums.QuestionKindState;
-//import nju.seec.helper.util.exception.HelperException;
-//import nju.seec.helper.vo.quiz.BaseQuestionVO;
-//import nju.seec.helper.vo.quiz.ChoiceQuestionVO;
-//import nju.seec.helper.vo.quiz.TrueOrFalseQuestionVO;
-//import org.springframework.beans.factory.annotation.Value;
-//import org.springframework.data.domain.Pageable;
-//import org.springframework.http.HttpStatus;
-//import org.springframework.stereotype.Component;
-//import org.springframework.transaction.annotation.Isolation;
-//import org.springframework.transaction.annotation.Transactional;
-//import org.springframework.web.client.HttpClientErrorException;
-//
-//import java.util.*;
-//import java.util.stream.Collectors;
-//
-///**
-// * @author xst
-// * <p>
-// * updated by cst
-// */
-//@Component
-//public class BokUtil {
-//    private final RestRequestUtil restRequestUtil;
-//    private final GuavaCacheUtil cacheUtils;
-//    @Value("${bok.url}")
-//    private String bokUrl = "http://bok.seecoder.cn";
-//    private String searchUrl = bokUrl + "/api/tq/search/";
-//    private String tqUrl = bokUrl + "/api/tq/";
-//
-//    public BokUtil(RestRequestUtil restRequestUtil, GuavaCacheUtil cacheUtils) {
-//        this.restRequestUtil = restRequestUtil;
-//        this.cacheUtils = cacheUtils;
-//    }
-//
-//    public void bokFindByStemLike(String stem, List<BaseQuestionVO> questions, Map page, Pageable pageable) {
-//        Map<String, String> urlParams = new HashMap<>();
-//        urlParams.put("content", stem);
-//        urlParams.put("sort", pageable.getSort().toString().replaceAll(" ", "").replaceAll(":", ","));
-//        urlParams.put("size", String.valueOf(pageable.getPageSize()));
-//        urlParams.put("page", String.valueOf(1 + pageable.getPageNumber()));
-//        try {
-//            Map ret = (Map) restRequestUtil.sendGetRequest(
-//                    searchUrl + "findByStemLike?content={content}&sort={sort}&size={size}&page={page}", urlParams);
-//            List<Map> tqs = ((List) ((Map) ret.get("_embedded")).get("choiceQuestions"));
-//
-//            List collect = parseBody(tqs);
-//            try {
-//                page.putAll((Map) ret.get("page"));
-//                questions.addAll(collect);
-//            } catch (Exception e) {
-//                e.printStackTrace();
-//            }
-//
-//        } catch (Exception e) {
-//            e.printStackTrace();
-//        }
-//    }
-//
-//    private static String CACHE_NAME = "BOK";
-//
-//    public List<BaseQuestionVO> bokFindByIdIn(List<String> ids) {
-//        //todo: 从缓存中取Ids
-//        Set<String> requestIds = new HashSet<>(ids);
-//        final ImmutableMap<Object, Object> bok = cacheUtils.multiGet(CACHE_NAME, requestIds);
-//
-//        Map<String, BaseQuestionVO> questionsMap = new HashMap<>(ids.size());
-//        bok.values().forEach(vo -> {
-//            if (vo instanceof BaseQuestionVO) {
-//                BaseQuestionVO questionVO = (BaseQuestionVO) vo;
-//                questionsMap.put(questionVO.getQuestionId(), questionVO);
-//                requestIds.remove(questionVO.getQuestionId());
-//            }
-//        });
-//        //未缓存的去这里拿
-//        if (requestIds.size() > 0) {
-//            Map<String, String> urlParams = new HashMap<>();
-//            String idsStr = requestIds.stream().reduce((a, b) -> {
-//                return a + "," + b;
-//            }).orElse("");
-//            urlParams.put("id", idsStr);
-//            urlParams.put("size", String.valueOf(requestIds.size()));
-//            urlParams.put("page", "1");
-//            List<Map> tqs = null;
-//            try {
-//                Map ret = (Map) restRequestUtil.sendGetRequest(
-//                        searchUrl + "findByIdIn?id={id}&size={size}&page={page}", urlParams);
-//                tqs = ((List) ((Map) ret.get("_embedded")).get("choiceQuestions"));
-//            } catch (Exception e) {
-//                e.printStackTrace();
-//                throw new RuntimeException("bok request error!for findByIdIn:" + Arrays.toString(requestIds.toArray()));
-//            }
-//            List<BaseQuestionVO> collect = parseBody(tqs);
-//            Map<String, Object> newGet = new HashMap<>(collect.size());
-//            for (BaseQuestionVO vo : collect) {
-//                System.out.println(vo);
-//                questionsMap.put(vo.getQuestionId(), vo);
-//                newGet.put(vo.getQuestionId(), vo);
-//            }
-//            cacheUtils.setAll(CACHE_NAME, newGet);
-//        }
-//
-//        // todo: 按照Ids重排序
-//        List<BaseQuestionVO> ret = new ArrayList<>();
-//        for (String id : ids) {
-//            ret.add(questionsMap.get(id));
-//        }
-//        return (ret);
-//    }
-//
-//    private List parseBody(List<Map> tqs) {
-//        final List collect = tqs.stream().map(BokUtil::parseOneQuestion).collect(Collectors.toList());
-//        return collect;
-//    }
-//
-//    private static BaseQuestionVO parseOneQuestion(Map q) {
-//        switch ((String) q.get("type")) {
-//            case "choice":
-//                return ChoiceQuestionVO.builder().stem((String) q.get("stem")).kind(QuestionKindState.CHOICE)
-//                        .options((Map<String, String>) q.get("options")).answer(((String) q.get("answer")).replaceAll("【答案】", ""))
-//                        .analysis((String) q.get("analysis")).questionId(String.valueOf((Integer) q.get("tq_id"))).build();
-//
-//            case "true_false":
-//                return TrueOrFalseQuestionVO.builder().stem((String) q.get("stem")).kind(QuestionKindState.TRUE_FALSE)
-//                        .answer(Boolean.valueOf((String) q.get("answer")))
-//                        .analysis((String) q.get("analysis")).questionId(String.valueOf((Integer) q.get("tq_id"))).build();
-//            default:
-//        }
-//        throw HelperException.of(ExceptionType.ERROR, "unknown type:" + (String) q.get("type"));
-//    }
-//
-//    public BaseQuestionVO bokFindById(String questionId) {
-//        Map<String, String> urlParams = new HashMap<>();
-//        Object cachedVo = cacheUtils.get(CACHE_NAME, questionId);
-//        if (cachedVo instanceof BaseQuestionVO) {
-//            return (BaseQuestionVO) cachedVo;
-//        }
-//        try {
-//            Map ret = (Map) restRequestUtil.sendGetRequest(tqUrl + questionId, urlParams);
-//            final BaseQuestionVO vo = parseOneQuestion(ret);
-//            cacheUtils.set(CACHE_NAME, vo.getQuestionId(), vo);
-//            return vo;
-//        } catch (JsonProcessingException e) {
-//            throw HelperException.of(ExceptionType.ERROR, "请求BOK题库数据错误:" + e.getMessage());
-//        } catch (HttpClientErrorException e) {
-//            throw HelperException.of(ExceptionType.valueOf(e.getStatusCode()), "请求BOK题库数据错误:" + e.getMessage());
-//        }
-//    }
-//
+package nju.seec.helper.util;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import nju.seec.helper.util.enums.ExceptionType;
+import nju.seec.helper.util.exception.HelperException;
+import nju.seec.helper.vo.question.BaseQuestionVO;
+import nju.seec.helper.vo.question.BokQuestion;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@Slf4j
+@Component
+public class BokUtil {
+    private final RestRequestUtil restRequestUtil;
+    private final GuavaCacheUtil cacheUtils;
+    @Value("${bok.url}")
+    private String bokUrl = "http://bok.seecoder.cn";
+    private String searchUrl = bokUrl + "/api/tq/search/";
+    private String tqUrl = bokUrl + "/api/tq/";
+
+    public BokUtil(RestRequestUtil restRequestUtil, GuavaCacheUtil cacheUtils) {
+        this.restRequestUtil = restRequestUtil;
+        this.cacheUtils = cacheUtils;
+    }
+
+    public Page<BaseQuestionVO> bokFindByStemLike(String stem, Pageable pageable, boolean withAnswer) {
+        try {
+            Map<String, String> urlParams = ImmutableMap.of(
+                    "content", stem,
+                    "page", String.valueOf(1 + pageable.getPageNumber()),
+                    "size", String.valueOf(pageable.getPageSize()),
+                    "sort", pageable.getSort().toString().replaceAll(" ", "").replaceAll(":", ",")
+            );
+            BokSearchResult result = restRequestUtil.sendGetRequest(
+                    searchUrl + "findByStemLike?content={content}&sort={sort}&size={size}&page={page}", BokSearchResult.class, urlParams);
+
+            List<BokQuestion> questions = result.get_embedded().getChoiceQuestions();
+            List<BaseQuestionVO> questionVOList =
+                    questions.stream()
+                            .map(question -> {
+                                cacheUtils.set(CACHE_NAME, question.getTqId(), question);
+                                return BaseQuestionVO.convertBokQuestionToVO(question, withAnswer);
+                            })
+                            .collect(Collectors.toList());
+            return new PageImpl<>(questionVOList, pageable, result.getPage().getTotalElements());
+        } catch (Exception e) {
+            log.error(e.getLocalizedMessage(), e);
+            throw HelperException.of(ExceptionType.ERROR, "获取题目失败");
+        }
+    }
+
+    private static String CACHE_NAME = "BOK";
+
+    public List<BaseQuestionVO> bokFindByIdIn(final List<String> ids, boolean withAnswer) {
+        try {
+            final ImmutableMap<Object, Object> bokQuestionsMap = cacheUtils.multiGet(CACHE_NAME, ids);
+
+            List<String> requestIds = new ArrayList<>(ids);
+            Map<String, BaseQuestionVO> questionVOMap = Maps.newHashMapWithExpectedSize(ids.size());
+
+            bokQuestionsMap
+                    .values()
+                    .forEach(value -> {
+                        BokQuestion bokQuestion = (BokQuestion) value;
+                        BaseQuestionVO questionVO = BaseQuestionVO.convertBokQuestionToVO(bokQuestion, withAnswer);
+                        questionVOMap.put(questionVO.getId(), questionVO);
+                        requestIds.remove(bokQuestion.getTqId());
+                    });
+            //未缓存的去这里拿
+            if (!requestIds.isEmpty()) {
+                Map<String, String> urlParams = ImmutableMap.of("id", requestIds.stream().reduce((a, b) -> a + "," + b).orElse(""));
+
+                BokSearchResult result = restRequestUtil.sendGetRequest(searchUrl + "findByIdIn?id={id}", BokSearchResult.class, urlParams);
+                List<BokQuestion> questions = result.get_embedded().getChoiceQuestions();
+                questions.forEach(question -> {
+                    cacheUtils.set(CACHE_NAME, question.getTqId(), question);
+                    BaseQuestionVO questionVO = BaseQuestionVO.convertBokQuestionToVO(question, withAnswer);
+                    questionVOMap.put(questionVO.getId(), questionVO);
+                });
+            }
+
+            return ids.stream()
+                    .map(questionVOMap::get)
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error(e.getLocalizedMessage(), e);
+            throw HelperException.of(ExceptionType.ERROR, "获取题目失败");
+        }
+    }
+
+    public BaseQuestionVO bokFindById(String questionId, boolean withAnswer) {
+        Object cachedBokQuestion = cacheUtils.get(CACHE_NAME, questionId);
+        if (cachedBokQuestion instanceof BokQuestion) {
+            return BaseQuestionVO.convertBokQuestionToVO((BokQuestion) cachedBokQuestion, withAnswer);
+        }
+        try {
+            BokQuestion bokQuestion = restRequestUtil.sendGetRequest(tqUrl + questionId, BokQuestion.class, Collections.emptyMap());
+            cacheUtils.set(CACHE_NAME, bokQuestion.getTqId(), bokQuestion);
+            return BaseQuestionVO.convertBokQuestionToVO(bokQuestion, withAnswer);
+        } catch (Exception e) {
+            log.error(e.getLocalizedMessage(), e);
+            throw HelperException.of(ExceptionType.ERROR, "获取题目失败");
+        }
+    }
+
 //    @Transactional(isolation = Isolation.SERIALIZABLE, rollbackFor = Exception.class)
 //    public BaseQuestionVO postNewQuestion(BaseQuestionVO vo) throws JsonProcessingException {
 //        if (vo.getQuestionId() != null) {
@@ -233,51 +198,23 @@
 //        }
 //        cacheUtils.remove(CACHE_NAME, questionId);
 //    }
-//
-//    /**
-//     * 来自restBok接口的对象
-//     */
-//    @JsonInclude(JsonInclude.Include.NON_NULL)
-//    @Data
-//    static class BokTq {
-//        String id;
-//        String type;
-//        String answer;
-//        @JsonProperty(value = "key_points")
-//        String keyPoints;
-//        String analysis;
-//        String lastModified;
-//        String stem;
-//        Map options;
-//        List tags;
-//        List knowledgeId;
-//
-//        private BokTq(BaseQuestionVO vo) {
-//            this.id = vo.getQuestionId();
-//            this.stem = vo.getStem();
-//            switch (vo.getKind()) {
-//
-//                case CHOICE:
-//                    ChoiceQuestionVO cqvo = (ChoiceQuestionVO) vo;
-//                    this.options = cqvo.getOptions();
-//                    this.type = "choice";
-//                    this.analysis = cqvo.getAnalysis();
-//                    this.answer = cqvo.getAnswer();
-//                    break;
-//                case TRUE_FALSE:
-//                    TrueOrFalseQuestionVO tfvo = (TrueOrFalseQuestionVO) vo;
-//                    this.options = null;
-//                    this.type = "true_false";
-//                    this.analysis = tfvo.getAnalysis();
-//                    this.answer = tfvo.getAnswer().toString();
-//                    break;
-//                default:
-//            }
-//
-//            this.keyPoints = null;
-//            this.lastModified = null;
-//            this.tags = null;
-//            this.knowledgeId = null;
-//        }
-//    }
-//}
+
+    @Data
+    private static class BokSearchResult {
+        private Embedded _embedded;
+        private Page page;
+
+        @Data
+        private static class Embedded {
+            private List<BokQuestion> choiceQuestions;
+        }
+
+        @Data
+        private static class Page {
+            private int size;
+            private int number;
+            private int totalPages;
+            private long totalElements;
+        }
+    }
+}

+ 6 - 3
src/main/java/nju/seec/helper/util/GuavaCacheUtil.java

@@ -11,6 +11,11 @@ import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
 @Component
 public class GuavaCacheUtil {
     private Cache<Object, Object> cache;
@@ -37,9 +42,7 @@ public class GuavaCacheUtil {
     }
 
     public void setAll(String cacheName, Map<String, Object> putAll) {
-        final Map<String, Object> all = putAll.entrySet().stream().collect(Collectors.toMap(entry -> {
-            return combineKey(cacheName, entry.getKey());
-        }, Map.Entry::getValue));
+        final Map<String, Object> all = putAll.entrySet().stream().collect(Collectors.toMap(entry -> combineKey(cacheName, entry.getKey()), Map.Entry::getValue));
         cache.putAll(all);
     }
 

+ 2 - 6
src/main/java/nju/seec/helper/util/RestRequestUtil.java

@@ -31,12 +31,8 @@ public class RestRequestUtil {
         client.delete(url);
     }
 
-    public Object sendGetRequest(String url, Map<String, String> urlParams) throws JsonProcessingException {
+    public <T> T sendGetRequest(String url, Class<T> responseType, Map<String, String> urlParams) {
         RestTemplate client = new RestTemplate();
-        HttpHeaders headers = new HttpHeaders();
-        HttpMethod method = HttpMethod.GET;
-        headers.setContentType(MediaType.APPLICATION_JSON);
-        ResponseEntity ret = client.getForEntity(url, Object.class, urlParams);
-        return ret.getBody();
+        return client.getForObject(url, responseType, urlParams);
     }
 }

+ 5 - 1
src/main/java/nju/seec/helper/util/enums/MessageType.java

@@ -19,5 +19,9 @@ public enum MessageType {
     /**
      * 新公告
      */
-    NOTICE_NEW
+    NOTICE_NEW,
+    /**
+     * 新测试
+     */
+    QUIZ_NEW
 }

+ 0 - 21
src/main/java/nju/seec/helper/util/enums/QuestionKindState.java

@@ -1,21 +0,0 @@
-package nju.seec.helper.util.enums;
-
-/**
- * @author cst
- */
-public enum QuestionKindState {
-    CHOICE("选择题"), TRUE_FALSE("判断题");
-    private String name;
-
-    QuestionKindState(String name) {
-        this.name = name;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-}

+ 8 - 0
src/main/java/nju/seec/helper/util/enums/QuestionType.java

@@ -0,0 +1,8 @@
+package nju.seec.helper.util.enums;
+
+/**
+ * @author cst
+ */
+public enum QuestionType {
+    CHOICE, TRUE_FALSE, SUBJECTIVE
+}

+ 30 - 0
src/main/java/nju/seec/helper/util/enums/QuizState.java

@@ -1,5 +1,13 @@
 package nju.seec.helper.util.enums;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import lombok.NonNull;
+import lombok.val;
+
+import java.util.Map;
+import java.util.Set;
+
 /**
  * @author xst
  * <p>
@@ -20,4 +28,26 @@ public enum QuizState {
     public void setName(String name) {
         this.name = name;
     }
+
+    private static final Map<SlideState, Map<QuizState, Set<QuizType>>> MAP = ImmutableMap.of(
+            SlideState.DRAFT, ImmutableMap.of(NOT_STARTED, ImmutableSet.of(QuizType.BEFORE_CLASS, QuizType.IN_CLASS, QuizType.AFTER_CLASS)),
+            SlideState.BEFORE_CLASS, ImmutableMap.of(NOT_STARTED, ImmutableSet.of(QuizType.IN_CLASS, QuizType.AFTER_CLASS),
+                    ONGOING, ImmutableSet.of(QuizType.BEFORE_CLASS)),
+            SlideState.IN_CLASS, ImmutableMap.of(NOT_STARTED, ImmutableSet.of(QuizType.AFTER_CLASS),
+                    ONGOING, ImmutableSet.of(QuizType.IN_CLASS),
+                    CLOSED, ImmutableSet.of(QuizType.BEFORE_CLASS)),
+            SlideState.AFTER_CLASS, ImmutableMap.of(ONGOING, ImmutableSet.of(QuizType.AFTER_CLASS),
+                    CLOSED, ImmutableSet.of(QuizType.BEFORE_CLASS, QuizType.IN_CLASS)),
+            SlideState.FINISH, ImmutableMap.of(CLOSED, ImmutableSet.of(QuizType.BEFORE_CLASS, QuizType.IN_CLASS, QuizType.AFTER_CLASS))
+    );
+
+    public static QuizState getQuizStateBySlideStateAndQuizType(@NonNull SlideState slideState, @NonNull QuizType quizType) {
+        Map<QuizState, Set<QuizType>> map = MAP.get(slideState);
+        for (val entry : map.entrySet()) {
+            if (entry.getValue().contains(quizType)) {
+                return entry.getKey();
+            }
+        }
+        return null;
+    }
 }

+ 14 - 2
src/main/java/nju/seec/helper/util/enums/QuizType.java

@@ -2,6 +2,7 @@ package nju.seec.helper.util.enums;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import lombok.NonNull;
 
 import java.util.Collections;
 import java.util.Map;
@@ -11,7 +12,18 @@ import java.util.Set;
  * @author cst
  */
 public enum QuizType {
-    BEFORE_CLASS, IN_CLASS, AFTER_CLASS;
+    /**
+     * 课前测试
+     */
+    BEFORE_CLASS,
+    /**
+     * 课中测试
+     */
+    IN_CLASS,
+    /**
+     * 课后测试
+     */
+    AFTER_CLASS;
 
     private final static Map<SlideState, Set<QuizType>> SLIDE_QUIZ_MAP = ImmutableMap.of(
             SlideState.DRAFT, ImmutableSet.of(BEFORE_CLASS, IN_CLASS, AFTER_CLASS),
@@ -21,7 +33,7 @@ public enum QuizType {
             SlideState.FINISH, Collections.emptySet()
     );
 
-    public static Set<QuizType> getQuizTypesBySlideState(SlideState slideState) {
+    public static Set<QuizType> getQuizTypesBySlideState(@NonNull SlideState slideState) {
         return SLIDE_QUIZ_MAP.get(slideState);
     }
 }

+ 89 - 0
src/main/java/nju/seec/helper/vo/question/BaseQuestionVO.java

@@ -0,0 +1,89 @@
+package nju.seec.helper.vo.question;
+
+import lombok.Data;
+import lombok.NonNull;
+import nju.seec.helper.util.enums.ExceptionType;
+import nju.seec.helper.util.enums.QuestionType;
+import nju.seec.helper.util.exception.HelperException;
+
+/**
+ * @author XuShengTao
+ * <p>
+ * updated by cst
+ */
+@Data
+public abstract class BaseQuestionVO {
+    protected String id;
+    protected QuestionType type;
+    protected String stem;
+    protected String analysis;
+
+    public static BaseQuestionVO convertBokQuestionToVO(@NonNull BokQuestion bokQuestion, boolean withAnswer) {
+        switch (bokQuestion.getType()) {
+            case "choice":
+                return new ChoiceQuestionVO(bokQuestion, withAnswer);
+            case "true_false":
+                return new TrueOrFalseQuestionVO(bokQuestion, withAnswer);
+//            case "subjective":
+//                return new SubjectiveQuestionVO(bokQuestion, withAnswer);
+            default:
+                throw HelperException.of(ExceptionType.ERROR, "无法解析该题目");
+        }
+    }
+
+    /**
+     * 检查答案是否正确
+     *
+     * @param answer
+     * @return
+     */
+    public abstract boolean pass(Object answer);
+}
+//QuestionSerializer
+//问题的详细定义
+//
+//Attribute
+//type QuestionSerializer =
+//  | ChoiceQuestionSerializer
+//  | TrueOrFalseQuestionSerializer;
+//
+//type QuestionWithoutAnswerSerializer = Omit<
+//  QuestionSerializer,
+//  "answer" | "analysis"
+//>;
+//
+//type QuestionWithStudentAnswerSerializer =
+//  | ChoiceQuestionWithStudentAnswer
+//  | TrueOrFalseQuestionWithStudentAnswer;
+//
+//interface Question {
+//  id: number; // 题目id
+//  kind: "CHOICE" | "TRUE_FALSE";
+//  stem: string; // 题干
+//}
+//
+//interface ChoiceQuestionSerializer extends Question {
+//  kind: "CHOICE";
+//  options: {
+//    [key: string]: string;
+//  };
+//  answer: string; // 答案 [与 options 的 key 一致]
+//  analysis?: string; // 解析
+//}
+//
+//interface ChoiceQuestionWithStudentAnswer extends ChoiceQuestionSerializer {
+//  studentAnswer: string;
+//  pass: boolean; // 是否正确
+//}
+//
+//interface TrueOrFalseQuestionSerializer extends Question {
+//  kind: "TRUE_FALSE";
+//  answer: boolean; // 答案
+//  analysis?: string; // 解析
+//}
+//
+//interface TrueOrFalseQuestionWithStudentAnswer
+//  extends TrueOrFalseQuestionSerializer {
+//  studentAnswer: boolean;
+//  pass: boolean; // 是否正确
+//}

+ 31 - 0
src/main/java/nju/seec/helper/vo/question/BokQuestion.java

@@ -0,0 +1,31 @@
+package nju.seec.helper.vo.question;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author sheen
+ * <p>
+ * updated by cst
+ */
+@Data
+public class BokQuestion {
+    @JsonProperty(value = "tq_id")
+    private String tqId;
+    private String type;
+    private String stem;
+    private Map<String, String> options;
+    private String answer;
+    @JsonProperty(value = "key_points")
+    private String keyPoints;
+    private String analysis;
+    private Set<String> tags;
+    private Set<String> knowledgeId;
+    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
+    private Date lastModified;
+}

+ 43 - 0
src/main/java/nju/seec/helper/vo/question/ChoiceQuestionVO.java

@@ -0,0 +1,43 @@
+package nju.seec.helper.vo.question;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+import nju.seec.helper.util.enums.QuestionType;
+
+import java.util.Map;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class ChoiceQuestionVO extends BaseQuestionVO {
+    private Map<String, String> options;
+    private String answer;
+
+    public ChoiceQuestionVO(@NonNull BokQuestion bokQuestion, boolean withAnswer) {
+        this.id = bokQuestion.getTqId();
+        this.type = QuestionType.CHOICE;
+        this.stem = bokQuestion.getStem();
+        this.options = bokQuestion.getOptions();
+
+        if (withAnswer) {
+            this.answer = bokQuestion.getAnswer();
+            this.analysis = bokQuestion.getAnalysis();
+        }
+    }
+
+    @Override
+    public boolean pass(Object answer) {
+        return this.answer.equals(String.valueOf(answer));
+    }
+}
+//kind: "CHOICE";
+//  options: {
+//    [key: string]: string;
+//  };
+//  answer: string; // 答案 [与 options 的 key 一致]
+//  analysis?: string; // 解析

+ 27 - 0
src/main/java/nju/seec/helper/vo/question/SubjectiveQuestionVO.java

@@ -0,0 +1,27 @@
+//package nju.seec.helper.vo.question;
+//
+//import lombok.Data;
+//import lombok.EqualsAndHashCode;
+//import lombok.NonNull;
+//import nju.seec.helper.util.enums.QuestionType;
+//
+//import java.io.Serializable;
+//
+///**
+// * @author cst
+// */
+//@EqualsAndHashCode(callSuper = true)
+//@Data
+//public class SubjectiveQuestionVO extends BaseQuestionVO implements Serializable {
+//    private static final long serialVersionUID = -7173121632346669906L;
+//
+//    public SubjectiveQuestionVO(@NonNull BokQuestion bokQuestion, boolean withAnswer) {
+//        this.id = bokQuestion.getTqId();
+//        this.type = QuestionType.SUBJECTIVE;
+//        this.stem = bokQuestion.getStem();
+//
+//        if (withAnswer) {
+//            this.analysis = bokQuestion.getAnalysis();
+//        }
+//    }
+//}

+ 59 - 0
src/main/java/nju/seec/helper/vo/question/TrueOrFalseQuestionVO.java

@@ -0,0 +1,59 @@
+package nju.seec.helper.vo.question;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+import nju.seec.helper.util.enums.QuestionType;
+//import nju.seec.helper.entity.QuizStudentAnswer;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class TrueOrFalseQuestionVO extends BaseQuestionVO {
+    private Boolean answer;
+
+    @Override
+    public boolean pass(Object answer) {
+        return this.answer.equals(Boolean.valueOf(String.valueOf(answer)));
+    }
+
+    public TrueOrFalseQuestionVO(@NonNull BokQuestion bokQuestion, boolean withAnswer) {
+        this.id = bokQuestion.getTqId();
+        this.type = QuestionType.TRUE_FALSE;
+        this.stem = bokQuestion.getStem();
+
+        if (withAnswer) {
+            this.answer = Boolean.valueOf(bokQuestion.getAnswer());
+            this.analysis = bokQuestion.getAnalysis();
+        }
+    }
+//    @JsonProperty(value = "studentAnswer")
+//    private Boolean studentAnswer;
+//    @JsonProperty(value = "pass")
+//    private Boolean pass;
+
+//    @Override
+//    public void exAddAnswer(QuizStudentAnswer answer) {
+//        this.pass = answer.getPass();
+//        this.studentAnswer = Boolean.valueOf(answer.getStudentAnswer());
+//    }
+//
+//    @Override
+//    public boolean checkAnswer(Object answer) {
+//        if (answer == null) {
+//            return false;
+//        }
+//        return this.answer.toString().toLowerCase().equals(answer.toString().toLowerCase());
+//    }
+//
+//    @Override
+//    public void excludeCorrectAnswer() {
+//        this.answer = null;
+//        this.pass = null;
+//        this.analysis = null;
+//    }
+}

+ 0 - 104
src/main/java/nju/seec/helper/vo/quiz/BaseQuestionVO.java

@@ -1,104 +0,0 @@
-//package nju.seec.helper.vo.quiz;
-//
-//import com.fasterxml.jackson.annotation.JsonProperty;
-//import com.fasterxml.jackson.databind.ObjectMapper;
-//import lombok.AllArgsConstructor;
-//import lombok.Data;
-//import lombok.NoArgsConstructor;
-//import lombok.experimental.SuperBuilder;
-//import nju.seec.helper.entity.QuizStudentAnswer;
-//import nju.seec.helper.util.enums.ExceptionType;
-//import nju.seec.helper.util.enums.QuestionKindState;
-//import nju.seec.helper.util.exception.HelperException;
-//
-//import javax.validation.constraints.NotNull;
-//import java.util.Map;
-//
-///**
-// * @author XuShengTao
-// */
-//@Data
-//@NoArgsConstructor
-//@AllArgsConstructor
-//@SuperBuilder
-//public abstract class BaseQuestionVO {
-//    @NotNull
-//    @JsonProperty(value = "id")
-//    private String questionId;
-//    @NotNull
-//    @JsonProperty(value = "kind")
-//    private QuestionKindState kind;
-//    @JsonProperty(value = "stem")
-//    private String stem;
-//
-//    public abstract void exAddAnswer(QuizStudentAnswer answer);
-//
-//    public abstract boolean checkAnswer(Object answer);
-//
-//    public abstract void excludeCorrectAnswer();
-//
-//    private static ObjectMapper objectMapper = new ObjectMapper();
-//
-//    public static BaseQuestionVO parse(Map question) {
-//        String kind = (String) question.get("kind");
-//        BaseQuestionVO newQuestion = null;
-//        switch (kind) {
-//            case "CHOICE":
-//                newQuestion = (objectMapper.convertValue(question, ChoiceQuestionVO.class));
-//                return newQuestion;
-//            case "TRUE_FALSE":
-//                newQuestion = (objectMapper.convertValue(question, TrueOrFalseQuestionVO.class));
-//                return newQuestion;
-//            default:
-//        }
-//        throw HelperException.of(ExceptionType.PARAM_ERROR, "parse failed:" + question.toString());
-//    }
-//}
-////QuestionSerializer
-////问题的详细定义
-////
-////Attribute
-////type QuestionSerializer =
-////  | ChoiceQuestionSerializer
-////  | TrueOrFalseQuestionSerializer;
-////
-////type QuestionWithoutAnswerSerializer = Omit<
-////  QuestionSerializer,
-////  "answer" | "analysis"
-////>;
-////
-////type QuestionWithStudentAnswerSerializer =
-////  | ChoiceQuestionWithStudentAnswer
-////  | TrueOrFalseQuestionWithStudentAnswer;
-////
-////interface Question {
-////  id: number; // 题目id
-////  kind: "CHOICE" | "TRUE_FALSE";
-////  stem: string; // 题干
-////}
-////
-////interface ChoiceQuestionSerializer extends Question {
-////  kind: "CHOICE";
-////  options: {
-////    [key: string]: string;
-////  };
-////  answer: string; // 答案 [与 options 的 key 一致]
-////  analysis?: string; // 解析
-////}
-////
-////interface ChoiceQuestionWithStudentAnswer extends ChoiceQuestionSerializer {
-////  studentAnswer: string;
-////  pass: boolean; // 是否正确
-////}
-////
-////interface TrueOrFalseQuestionSerializer extends Question {
-////  kind: "TRUE_FALSE";
-////  answer: boolean; // 答案
-////  analysis?: string; // 解析
-////}
-////
-////interface TrueOrFalseQuestionWithStudentAnswer
-////  extends TrueOrFalseQuestionSerializer {
-////  studentAnswer: boolean;
-////  pass: boolean; // 是否正确
-////}

+ 0 - 26
src/main/java/nju/seec/helper/vo/quiz/BokQuestion.java

@@ -1,26 +0,0 @@
-//package nju.seec.helper.vo.quiz;
-//
-//import com.fasterxml.jackson.annotation.JsonProperty;
-//import lombok.Data;
-//
-//import java.util.Collection;
-//import java.util.Map;
-//
-///**
-// * @author sheen
-// */
-//@Data
-//public class BokQuestion {
-//    @JsonProperty(value = "tq_id")
-//    String tqId;
-//    String type;
-//    String stem;
-//    Map<String, String> options;
-//    String answer;
-//    @JsonProperty(value = "key_points")
-//    String keyPoints;
-//    String analysis;
-//    Collection<String> tags;
-//    Collection<String> knowledgeId;
-//    String lastModified;
-//}

+ 0 - 54
src/main/java/nju/seec/helper/vo/quiz/ChoiceQuestionVO.java

@@ -1,54 +0,0 @@
-//package nju.seec.helper.vo.quiz;
-//
-//import com.fasterxml.jackson.annotation.JsonProperty;
-//import lombok.AllArgsConstructor;
-//import lombok.Data;
-//import lombok.NoArgsConstructor;
-//import lombok.experimental.SuperBuilder;
-//import nju.seec.helper.entity.QuizStudentAnswer;
-//
-//import java.util.Map;
-//
-//@Data
-//@SuperBuilder
-//@AllArgsConstructor
-//@NoArgsConstructor
-//public class ChoiceQuestionVO extends BaseQuestionVO {
-//    @JsonProperty(value = "options")
-//    private Map<String, String> options;
-//    @JsonProperty(value = "answer")
-//    private String answer;
-//    @JsonProperty(value = "analysis")
-//    private String analysis;
-//    @JsonProperty(value = "studentAnswer")
-//    private String studentAnswer;
-//    @JsonProperty(value = "pass")
-//    private Boolean pass;
-//
-//    @Override
-//    public void exAddAnswer(QuizStudentAnswer answer) {
-//        this.studentAnswer = answer.getStudentAnswer();
-//        this.pass = answer.getPass();
-//    }
-//
-//    @Override
-//    public boolean checkAnswer(Object answer) {
-//        if (answer == null) {
-//            return false;
-//        }
-//        return this.answer.toLowerCase().equals(answer.toString().toLowerCase());
-//    }
-//
-//    @Override
-//    public void excludeCorrectAnswer() {
-//        this.answer = null;
-//        this.pass = null;
-//        this.analysis = null;
-//    }
-//}
-////kind: "CHOICE";
-////  options: {
-////    [key: string]: string;
-////  };
-////  answer: string; // 答案 [与 options 的 key 一致]
-////  analysis?: string; // 解析

+ 37 - 0
src/main/java/nju/seec/helper/vo/quiz/QuizStudentAnswerVO.java

@@ -0,0 +1,37 @@
+package nju.seec.helper.vo.quiz;
+
+import lombok.Data;
+import lombok.NonNull;
+import nju.seec.helper.entity.QuizStudentAnswer;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@Data
+public class QuizStudentAnswerVO {
+    private Long quizId;
+    private String quizName;
+    private Long studentId;
+    private String studentName;
+
+    private Map<String, Object> answers;
+
+    private LocalDateTime modifiedAt;
+    private Integer submitNumber;
+    private BigDecimal score;
+
+    public QuizStudentAnswerVO(@NonNull QuizStudentAnswer quizStudentAnswer) {
+        this.quizId = quizStudentAnswer.getQuiz().getId();
+        this.quizName = quizStudentAnswer.getQuiz().getName();
+        this.studentId = quizStudentAnswer.getStudent().getId();
+        this.studentName = quizStudentAnswer.getStudent().getName();
+        this.answers = quizStudentAnswer.getAnswers();
+        this.modifiedAt = quizStudentAnswer.getModifiedAt();
+        this.submitNumber = quizStudentAnswer.getSubmitNumber();
+        this.score = quizStudentAnswer.getScore();
+    }
+}

+ 88 - 83
src/main/java/nju/seec/helper/vo/quiz/QuizVO.java

@@ -1,87 +1,92 @@
-//package nju.seec.helper.vo.quiz;
-//
-//
-//import lombok.*;
-//import nju.seec.helper.entity.Quiz;
-//import nju.seec.helper.util.enums.QuizType;
-//
-//import java.time.LocalDateTime;
-//
-///**
-// * @author xst
-// * <p>
-// * updated by cst
-// */
-//@Data
-//@Builder
-//@NoArgsConstructor
-//@AllArgsConstructor
-//public class QuizVO {
-//    private Long id;
-//    private String name;
-//    private QuizType type;
-//    private Long slideId;
-//    private String slideName;
-//    private Long teacherId;
-//    private String teacherName;
-//    private LocalDateTime createAt;
-//    private LocalDateTime updateAt;
-//
-////    public QuizVO(Quiz quiz, List<BaseQuestionVO> questions) {
-////        this.quizId = quiz.getId().toString();
-////        this.name = quiz.getName();
-////        this.state = quiz.getState();
-////        this.courseVO = new CourseVO(quiz.getCourse());
-////        this.quizTime = quiz.getQuizTime();
-////        this.slide = new SlideVO(quiz.getSlide());
-////        this.questionNumber = questions.size();
-////        this.questions = questions;
-////    }
-//
-//    public QuizVO(@NonNull Quiz quiz) {
-//        this.id = quiz.getId();
+package nju.seec.helper.vo.quiz;
+
+
+import lombok.*;
+import nju.seec.helper.entity.Quiz;
+import nju.seec.helper.util.enums.QuizState;
+import nju.seec.helper.util.enums.QuizType;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class QuizVO {
+    private Long id;
+    private String name;
+    private Integer maxSubmitTime;
+    private QuizType type;
+    private QuizState state;
+    private Long slideId;
+    private String slideName;
+    private Long teacherId;
+    private String teacherName;
+    private LocalDateTime createAt;
+    private LocalDateTime updateAt;
+
+//    public QuizVO(Quiz quiz, List<BaseQuestionVO> questions) {
+//        this.quizId = quiz.getId().toString();
 //        this.name = quiz.getName();
-//        this.type = quiz.getType();
-//        this.slideId = quiz.getSlide().getId();
-//        this.slideName = quiz.getSlide().getName();
-//        this.teacherId = quiz.getTeacher().getId();
-//        this.teacherName = quiz.getTeacher().getName();
-//        this.createAt = quiz.getCreateAt();
-//        this.updateAt = quiz.getUpdateAt();
+//        this.state = quiz.getState();
+//        this.courseVO = new CourseVO(quiz.getCourse());
+//        this.quizTime = quiz.getQuizTime();
+//        this.slide = new SlideVO(quiz.getSlide());
+//        this.questionNumber = questions.size();
+//        this.questions = questions;
+//    }
+
+    public QuizVO(@NonNull Quiz quiz) {
+        this.id = quiz.getId();
+        this.name = quiz.getName();
+        this.maxSubmitTime = quiz.getMaxSubmitNumber();
+        this.type = quiz.getType();
+        this.state = quiz.getState();
+        this.slideId = quiz.getSlide().getId();
+        this.slideName = quiz.getSlide().getName();
+        this.teacherId = quiz.getTeacher().getId();
+        this.teacherName = quiz.getTeacher().getName();
+        this.createAt = quiz.getCreateAt();
+        this.updateAt = quiz.getUpdateAt();
+    }
+
+//    public void excludeCorrectAnswer() {
+//        questions.forEach(BaseQuestionVO::excludeCorrectAnswer);
 //    }
+}
+// http://47.100.18.120:3000/SEEC-BOK/API-DOC/src/master/helper/serializer/QuizSerializer.md
+// /** 已实现的枚举类 */
+//    type SlideState =
+//  | "DRAFT"
+//          | "BEFORE_CLASS" // 课前
+//          | "IN_CLASS" // 课中
+//          | "AFTER_CLASS" // 课后
+//          | "FINISH"; // 最终截止
 //
-////    public void excludeCorrectAnswer() {
-////        questions.forEach(BaseQuestionVO::excludeCorrectAnswer);
-////    }
+//          type QuizState =
+//          | "NOT_STARTED" // 未开始
+//          | "ONGOING" // 正在进行
+//          | "CLOSED"; // 已结束
+//
+//interface QuizSerializer<T extends QuizState> {
+//    id: number;
+//    name: string; // 测试名
+//    state: T; // 考试状态
+//    course: CourseSerializer; // 课程详情
+//    quizTime: SlideState;
+//    slide: SlideSerializer;
+//    questions: Array<
+//            T extends "CLOSED"
+//            ? QuestionWithStudentAnswerSerializer
+//      : QuestionWithoutAnswerSerializer
+//  >;
 //}
-//// http://47.100.18.120:3000/SEEC-BOK/API-DOC/src/master/helper/serializer/QuizSerializer.md
-//// /** 已实现的枚举类 */
-////    type SlideState =
-////  | "DRAFT"
-////          | "BEFORE_CLASS" // 课前
-////          | "IN_CLASS" // 课中
-////          | "AFTER_CLASS" // 课后
-////          | "FINISH"; // 最终截止
-////
-////          type QuizState =
-////          | "NOT_STARTED" // 未开始
-////          | "ONGOING" // 正在进行
-////          | "CLOSED"; // 已结束
-////
-////interface QuizSerializer<T extends QuizState> {
-////    id: number;
-////    name: string; // 测试名
-////    state: T; // 考试状态
-////    course: CourseSerializer; // 课程详情
-////    quizTime: SlideState;
-////    slide: SlideSerializer;
-////    questions: Array<
-////            T extends "CLOSED"
-////            ? QuestionWithStudentAnswerSerializer
-////      : QuestionWithoutAnswerSerializer
-////  >;
-////}
-////
-////interface QuizBasicSerialzer extends Omit<QuizSerialzier, "questions"> {
-////        questionNumber: number; // 题目数量
-////        }
+//
+//interface QuizBasicSerialzer extends Omit<QuizSerialzier, "questions"> {
+//        questionNumber: number; // 题目数量
+//        }

+ 0 - 44
src/main/java/nju/seec/helper/vo/quiz/TrueOrFalseQuestionVO.java

@@ -1,44 +0,0 @@
-//package nju.seec.helper.vo.quiz;
-//
-//import com.fasterxml.jackson.annotation.JsonProperty;
-//import lombok.AllArgsConstructor;
-//import lombok.Data;
-//import lombok.NoArgsConstructor;
-//import lombok.experimental.SuperBuilder;
-//import nju.seec.helper.entity.QuizStudentAnswer;
-//
-//@Data
-//@SuperBuilder
-//@AllArgsConstructor
-//@NoArgsConstructor
-//public class TrueOrFalseQuestionVO extends BaseQuestionVO {
-//    @JsonProperty(value = "answer")
-//    private Boolean answer;
-//    @JsonProperty(value = "analysis")
-//    private String analysis;
-//    @JsonProperty(value = "studentAnswer")
-//    private Boolean studentAnswer;
-//    @JsonProperty(value = "pass")
-//    private Boolean pass;
-//
-//    @Override
-//    public void exAddAnswer(QuizStudentAnswer answer) {
-//        this.pass = answer.getPass();
-//        this.studentAnswer = Boolean.valueOf(answer.getStudentAnswer());
-//    }
-//
-//    @Override
-//    public boolean checkAnswer(Object answer) {
-//        if (answer == null) {
-//            return false;
-//        }
-//        return this.answer.toString().toLowerCase().equals(answer.toString().toLowerCase());
-//    }
-//
-//    @Override
-//    public void excludeCorrectAnswer() {
-//        this.answer = null;
-//        this.pass = null;
-//        this.analysis = null;
-//    }
-//}

+ 1 - 0
src/main/resources/update.sql

@@ -14,6 +14,7 @@ alter table choose
     modify course_id bigint not null;
 alter table choose
     modify student_id bigint not null;
+drop index UKafeybda7scb19wl3qlyjxey4p on choose;
 
 alter table slide
     modify id bigint auto_increment;