Procházet zdrojové kódy

feat: 增加测验接口

老师创建测验,老师获取测验,学生获取测验,学生提交测验答案
ChenSiTong před 6 roky
rodič
revize
1a470095ec
37 změnil soubory, kde provedl 690 přidání a 249 odebrání
  1. 7 0
      pom.xml
  2. 0 22
      src/main/java/nju/seec/helper/data/converter/MapToJsonConverter.java
  3. 0 22
      src/main/java/nju/seec/helper/data/converter/SetToJsonConverter.java
  4. 22 0
      src/main/java/nju/seec/helper/data/dao/QuestionDAO.java
  5. 2 1
      src/main/java/nju/seec/helper/data/dao/QuizDAO.java
  6. 18 0
      src/main/java/nju/seec/helper/data/dao/SubmitAnswerDAO.java
  7. 47 0
      src/main/java/nju/seec/helper/data/entity/Question.java
  8. 14 25
      src/main/java/nju/seec/helper/data/entity/Quiz.java
  9. 0 33
      src/main/java/nju/seec/helper/data/entity/SlideQuiz.java
  10. 0 29
      src/main/java/nju/seec/helper/data/entity/StudentAnswer.java
  11. 38 0
      src/main/java/nju/seec/helper/data/entity/SubmitAnswer.java
  12. 0 1
      src/main/java/nju/seec/helper/logic/service/CourseService.java
  13. 18 0
      src/main/java/nju/seec/helper/logic/service/QuestionService.java
  14. 20 19
      src/main/java/nju/seec/helper/logic/service/QuizService.java
  15. 0 1
      src/main/java/nju/seec/helper/logic/service/SlideService.java
  16. 1 9
      src/main/java/nju/seec/helper/logic/service/impl/CourseServiceImpl.java
  17. 46 0
      src/main/java/nju/seec/helper/logic/service/impl/QuestionServiceImpl.java
  18. 111 0
      src/main/java/nju/seec/helper/logic/service/impl/QuizServiceImpl.java
  19. 7 24
      src/main/java/nju/seec/helper/logic/service/impl/SlideServiceImpl.java
  20. 0 15
      src/main/java/nju/seec/helper/logic/vo/AnswerVO.java
  21. 6 0
      src/main/java/nju/seec/helper/logic/vo/CourseVO.java
  22. 11 1
      src/main/java/nju/seec/helper/logic/vo/ItemVO.java
  23. 33 0
      src/main/java/nju/seec/helper/logic/vo/NoAnswerQuestionVO.java
  24. 38 0
      src/main/java/nju/seec/helper/logic/vo/QuestionVO.java
  25. 17 13
      src/main/java/nju/seec/helper/logic/vo/QuizVO.java
  26. 0 10
      src/main/java/nju/seec/helper/logic/vo/SlideQuizVO.java
  27. 17 1
      src/main/java/nju/seec/helper/logic/vo/SlideVO.java
  28. 88 0
      src/main/java/nju/seec/helper/logic/vo/StudentQuizVO.java
  29. 8 0
      src/main/java/nju/seec/helper/util/enums/QuestionType.java
  30. 1 1
      src/main/java/nju/seec/helper/util/enums/QuizType.java
  31. 0 8
      src/main/java/nju/seec/helper/util/enums/SlideQuizType.java
  32. 32 0
      src/main/java/nju/seec/helper/web/controller/QuestionController.java
  33. 52 0
      src/main/java/nju/seec/helper/web/controller/QuizController.java
  34. 2 0
      src/main/java/nju/seec/helper/web/dto/CommentDTO.java
  35. 20 0
      src/main/java/nju/seec/helper/web/dto/QuizDTO.java
  36. 0 14
      src/main/java/nju/seec/helper/web/dto/SlideQuizDTO.java
  37. 14 0
      src/main/java/nju/seec/helper/web/dto/SubmitAnswerDTO.java

+ 7 - 0
pom.xml

@@ -92,6 +92,13 @@
             <version>28.1-jre</version>
         </dependency>
 
+        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-text -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-text</artifactId>
+            <version>1.8</version>
+        </dependency>
+
         <!-- 开发阶段用于接口展示和调用 -->
         <dependency>
             <groupId>io.springfox</groupId>

+ 0 - 22
src/main/java/nju/seec/helper/data/converter/MapToJsonConverter.java

@@ -1,22 +0,0 @@
-package nju.seec.helper.data.converter;
-
-import nju.seec.helper.util.JsonUtils;
-
-import javax.persistence.AttributeConverter;
-import java.util.Map;
-
-/**
- * @author cst
- */
-@SuppressWarnings("rawtypes")
-public class MapToJsonConverter implements AttributeConverter<Map, String> {
-    @Override
-    public String convertToDatabaseColumn(Map map) {
-        return JsonUtils.toJson(map);
-    }
-
-    @Override
-    public Map convertToEntityAttribute(String s) {
-        return JsonUtils.fromJson(s, Map.class);
-    }
-}

+ 0 - 22
src/main/java/nju/seec/helper/data/converter/SetToJsonConverter.java

@@ -1,22 +0,0 @@
-package nju.seec.helper.data.converter;
-
-import nju.seec.helper.util.JsonUtils;
-
-import javax.persistence.AttributeConverter;
-import java.util.Set;
-
-/**
- * @author cst
- */
-@SuppressWarnings("rawtypes")
-public class SetToJsonConverter implements AttributeConverter<Set, String> {
-    @Override
-    public String convertToDatabaseColumn(Set set) {
-        return JsonUtils.toJson(set);
-    }
-
-    @Override
-    public Set convertToEntityAttribute(String s) {
-        return JsonUtils.fromJson(s, Set.class);
-    }
-}

+ 22 - 0
src/main/java/nju/seec/helper/data/dao/QuestionDAO.java

@@ -0,0 +1,22 @@
+package nju.seec.helper.data.dao;
+
+import nju.seec.helper.data.entity.Question;
+import nju.seec.helper.util.exception.HelperException;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author cst
+ */
+@Repository
+public interface QuestionDAO extends JpaRepository<Question, Integer> {
+    /**
+     * 封装findById方法
+     *
+     * @param questionId
+     * @return
+     */
+    default Question findQuestionById(int questionId) {
+        return this.findById(questionId).orElseThrow(() -> HelperException.of(HelperException.ExceptionType.NOT_FOUND, "找不到题目"));
+    }
+}

+ 2 - 1
src/main/java/nju/seec/helper/data/dao/QuizDAO.java

@@ -1,6 +1,7 @@
 package nju.seec.helper.data.dao;
 
 import nju.seec.helper.data.entity.Quiz;
+import nju.seec.helper.util.enums.QuizType;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
@@ -9,5 +10,5 @@ import org.springframework.stereotype.Repository;
  */
 @Repository
 public interface QuizDAO extends JpaRepository<Quiz, Integer> {
-
+    Quiz findBySlideIdAndType(int slideId, QuizType type);
 }

+ 18 - 0
src/main/java/nju/seec/helper/data/dao/SubmitAnswerDAO.java

@@ -0,0 +1,18 @@
+package nju.seec.helper.data.dao;
+
+import nju.seec.helper.data.entity.SubmitAnswer;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * @author cst
+ */
+public interface SubmitAnswerDAO extends JpaRepository<SubmitAnswer, Integer> {
+    /**
+     * 根据测验ID和学生ID查询提交结果
+     *
+     * @param quizId
+     * @param studentId
+     * @return
+     */
+    SubmitAnswer findByQuizIdAndStudentId(Integer quizId, Integer studentId);
+}

+ 47 - 0
src/main/java/nju/seec/helper/data/entity/Question.java

@@ -0,0 +1,47 @@
+package nju.seec.helper.data.entity;
+
+import lombok.Data;
+import nju.seec.helper.util.enums.QuestionType;
+import org.hibernate.annotations.UpdateTimestamp;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author cst
+ */
+@Data
+@Entity
+@Table(name = "question")
+public class Question {
+    @Id
+    private Integer id;
+
+    @Enumerated(EnumType.STRING)
+    private QuestionType type;
+
+    @Column(nullable = false)
+    private String stem;
+
+    @ElementCollection
+    @CollectionTable(name = "quiz_option", joinColumns = @JoinColumn(name = "quiz_id"))
+    private Map<String, String> options;
+
+    @ElementCollection
+    @JoinTable(name = "quiz_tags", joinColumns = @JoinColumn(name = "quiz_id"))
+    private Set<String> tags;
+
+    @ElementCollection
+    @JoinTable(name = "quiz_knowledgeId", joinColumns = @JoinColumn(name = "quiz_id"))
+    private Set<String> knowledgeId;
+
+    @Column(name = "last_modified")
+    @UpdateTimestamp
+    private LocalDateTime lastModified;
+
+    private String answer;
+
+    private String analysis;
+}

+ 14 - 25
src/main/java/nju/seec/helper/data/entity/Quiz.java

@@ -1,14 +1,12 @@
 package nju.seec.helper.data.entity;
 
 import lombok.Data;
-import nju.seec.helper.data.converter.MapToJsonConverter;
-import nju.seec.helper.data.converter.SetToJsonConverter;
+import nju.seec.helper.util.enums.QuizType;
 import org.hibernate.annotations.UpdateTimestamp;
 
 import javax.persistence.*;
 import java.time.LocalDateTime;
-import java.util.Map;
-import java.util.Set;
+import java.util.List;
 
 /**
  * @author cst
@@ -18,31 +16,22 @@ import java.util.Set;
 @Table(name = "quiz")
 public class Quiz {
     @Id
-    private int id;
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
 
-    private String type;
+    @Column(name = "slide_id", nullable = false)
+    private Integer slideId;
 
     @Column(nullable = false)
-    private String stem;
+    private QuizType type;
 
-    @SuppressWarnings("all")
-    @Convert(converter = MapToJsonConverter.class)
-    @Column(columnDefinition = "json")
-    private Map<String, String> options;
+    @ManyToMany
+    @JoinTable(name = "quiz_question",
+            joinColumns = @JoinColumn(name = "quiz_id"),
+            inverseJoinColumns = @JoinColumn(name = "question_id"))
+    private List<Question> questions;
 
-    @Convert(converter = SetToJsonConverter.class)
-    @Column(columnDefinition = "json")
-    private Set<String> tags;
-
-    @Convert(converter = SetToJsonConverter.class)
-    @Column(name = "knowledge_id", columnDefinition = "json")
-    private Set<String> knowledgeId;
-
-    @Column(name = "last_modified")
+    @Column(name = "update_time")
     @UpdateTimestamp
-    private LocalDateTime lastModified;
-
-    private String answer;
-
-    private String analysis;
+    private LocalDateTime updateTime;
 }

+ 0 - 33
src/main/java/nju/seec/helper/data/entity/SlideQuiz.java

@@ -1,33 +0,0 @@
-package nju.seec.helper.data.entity;
-
-import lombok.Data;
-import nju.seec.helper.util.enums.SlideQuizType;
-import org.hibernate.annotations.CreationTimestamp;
-
-import javax.persistence.*;
-import java.time.LocalDateTime;
-
-/**
- * @author cst
- */
-@Data
-@Entity
-@Table(name = "slide_quiz")
-public class SlideQuiz {
-    @Id
-    @GeneratedValue(strategy = GenerationType.IDENTITY)
-    private Integer id;
-
-    @Column(name = "slide_id", nullable = false)
-    private Integer slideId;
-
-    @Column(name = "quiz_id", nullable = false)
-    private Integer quizId;
-
-    @Column(nullable = false)
-    private SlideQuizType type = SlideQuizType.IN_CLASS;
-
-    @Column(name = "create_time", updatable = false)
-    @CreationTimestamp
-    private LocalDateTime createTime;
-}

+ 0 - 29
src/main/java/nju/seec/helper/data/entity/StudentAnswer.java

@@ -1,29 +0,0 @@
-package nju.seec.helper.data.entity;
-
-import lombok.Data;
-
-import javax.persistence.*;
-
-/**
- * @author cst
- */
-@Data
-@Entity
-@Table(name = "student_answer")
-public class StudentAnswer {
-    @Id
-    @GeneratedValue(strategy = GenerationType.IDENTITY)
-    private Integer id;
-
-    @Column(name = "student_id", nullable = false)
-    private Integer studentId;
-
-    @Column(name = "slide_quiz_id", nullable = false)
-    private Integer slideQuizId;
-
-    @Column(name = "choose_option", nullable = false)
-    private String chooseOption;
-
-    @Column(name = "right_option", nullable = false)
-    private String rightOption;
-}

+ 38 - 0
src/main/java/nju/seec/helper/data/entity/SubmitAnswer.java

@@ -0,0 +1,38 @@
+package nju.seec.helper.data.entity;
+
+import lombok.Data;
+import org.hibernate.annotations.UpdateTimestamp;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@Data
+@Entity
+@Table(name = "submit_answer")
+public class SubmitAnswer {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Integer id;
+
+    @Column(name = "student_id")
+    private Integer studentId;
+
+    @Column(name = "quiz_id")
+    private Integer quizId;
+
+    @Column(name = "submit_num", nullable = false)
+    private Integer submitNum = 0;
+
+    @Column(name = "submit_time")
+    @UpdateTimestamp
+    private LocalDateTime submitTime;
+
+    @ElementCollection
+    @CollectionTable(name = "choose_option", joinColumns = @JoinColumn(name = "submit_answer_id"))
+    private Map<Integer, String> chooseOptions = new HashMap<>();
+}

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

@@ -1,6 +1,5 @@
 package nju.seec.helper.logic.service;
 
-import nju.seec.helper.data.entity.Course;
 import nju.seec.helper.logic.vo.CourseVO;
 import org.springframework.data.domain.Pageable;
 

+ 18 - 0
src/main/java/nju/seec/helper/logic/service/QuestionService.java

@@ -0,0 +1,18 @@
+package nju.seec.helper.logic.service;
+
+import nju.seec.helper.logic.vo.QuestionVO;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+public interface QuestionService {
+    /**
+     * 根据关键字获取问题
+     *
+     * @param key
+     * @return
+     */
+    List<QuestionVO> getQuestions(String key);
+}

+ 20 - 19
src/main/java/nju/seec/helper/logic/service/QuizService.java

@@ -1,45 +1,46 @@
 package nju.seec.helper.logic.service;
 
-import nju.seec.helper.data.entity.SlideQuiz;
 import nju.seec.helper.logic.vo.QuizVO;
-import nju.seec.helper.logic.vo.SlideQuizVO;
-import nju.seec.helper.util.enums.UserRole;
-
-import java.util.List;
+import nju.seec.helper.logic.vo.StudentQuizVO;
+import nju.seec.helper.util.enums.QuizType;
+import nju.seec.helper.web.dto.QuizDTO;
+import nju.seec.helper.web.dto.SubmitAnswerDTO;
 
 /**
  * @author cst
  */
 public interface QuizService {
     /**
-     * 根据关键字获取问题
+     * 创建幻灯片对应的测验
      *
-     * @param key
+     * @param quizDTO
      * @return
      */
-    QuizVO getQuizzes(String key);
+    QuizVO createQuiz(QuizDTO quizDTO);
 
     /**
-     * 创建幻灯片对应的问题
+     * 教师获取幻灯片中的测验
      *
-     * @param slideQuiz
+     * @param slideId
+     * @param quizType
      * @return
      */
-    SlideQuizVO createSlideQuiz(SlideQuiz slideQuiz);
+    QuizVO teacherGetQuiz(int slideId, QuizType quizType);
 
     /**
-     * 删除幻灯片对应的问题
+     * 学生获取幻灯片中的测验
      *
-     * @param slideQuizId
+     * @param slideId
+     * @param quizType
+     * @return
      */
-    void deleteSlideQuiz(int slideQuizId);
+    StudentQuizVO studentGetQuiz(int slideId, QuizType quizType);
 
     /**
-     * 获取幻灯片中的问题
+     * 学生回答测验
      *
-     * @param slideId
-     * @param userRole
-     * @return
+     * @param studentId
+     * @param submitAnswerDTO
      */
-    List<SlideQuizVO> getSlideQuizzes(int slideId, UserRole userRole);
+    void answerQuiz(Integer studentId, SubmitAnswerDTO submitAnswerDTO);
 }

+ 0 - 1
src/main/java/nju/seec/helper/logic/service/SlideService.java

@@ -1,6 +1,5 @@
 package nju.seec.helper.logic.service;
 
-import nju.seec.helper.data.entity.Slide;
 import nju.seec.helper.logic.vo.SlideVO;
 import nju.seec.helper.util.enums.SlideState;
 import nju.seec.helper.web.dto.SlideDTO;

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

@@ -5,8 +5,6 @@ import nju.seec.helper.data.entity.Course;
 import nju.seec.helper.logic.service.CourseService;
 import nju.seec.helper.logic.vo.CourseVO;
 import nju.seec.helper.util.FakerUtils;
-import nju.seec.helper.util.exception.HelperException;
-import org.springframework.beans.BeanUtils;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 
@@ -44,7 +42,7 @@ public class CourseServiceImpl implements CourseService {
         }
 
         return courses.stream()
-                .map(this::copyCourseToCourseVO)
+                .map(CourseVO::new)
                 .collect(Collectors.toList());
     }
 
@@ -52,10 +50,4 @@ public class CourseServiceImpl implements CourseService {
     public List<CourseVO> getStudentCourses(int studentId, Pageable pageable) {
         return getTeacherCourses(studentId, pageable);
     }
-
-    private CourseVO copyCourseToCourseVO(Course course) {
-        CourseVO courseVO = new CourseVO();
-        BeanUtils.copyProperties(course, courseVO);
-        return courseVO;
-    }
 }

+ 46 - 0
src/main/java/nju/seec/helper/logic/service/impl/QuestionServiceImpl.java

@@ -0,0 +1,46 @@
+package nju.seec.helper.logic.service.impl;
+
+import com.google.common.collect.ImmutableMap;
+import nju.seec.helper.data.dao.QuestionDAO;
+import nju.seec.helper.data.entity.Question;
+import nju.seec.helper.logic.service.QuestionService;
+import nju.seec.helper.logic.vo.QuestionVO;
+import nju.seec.helper.util.FakerUtils;
+import nju.seec.helper.util.enums.QuestionType;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author cst
+ */
+@Service
+public class QuestionServiceImpl implements QuestionService {
+    private final QuestionDAO questionDAO;
+
+    public QuestionServiceImpl(QuestionDAO questionDAO) {
+        this.questionDAO = questionDAO;
+    }
+
+    @Override
+    public List<QuestionVO> getQuestions(String key) {
+        Question fakerQuestion = new Question();
+        fakerQuestion.setId(FakerUtils.getFaker().number().randomDigit());
+        fakerQuestion.setStem("1+1=?");
+        fakerQuestion.setType(QuestionType.choice);
+        fakerQuestion.setOptions(ImmutableMap.of("A", "0", "B", "1", "C", "2", "D", "3"));
+        fakerQuestion.setTags(Collections.singleton("加法"));
+        fakerQuestion.setKnowledgeId(Collections.singleton("plus"));
+        fakerQuestion.setAnswer("C");
+        fakerQuestion.setAnalysis("1+1=2");
+        fakerQuestion.setLastModified(LocalDateTime.now());
+        List<Question> questions = Collections.singletonList(fakerQuestion);
+        questionDAO.saveAll(questions);
+        return questions.stream()
+                .map(QuestionVO::new)
+                .collect(Collectors.toList());
+    }
+}

+ 111 - 0
src/main/java/nju/seec/helper/logic/service/impl/QuizServiceImpl.java

@@ -0,0 +1,111 @@
+package nju.seec.helper.logic.service.impl;
+
+import nju.seec.helper.data.dao.QuestionDAO;
+import nju.seec.helper.data.dao.QuizDAO;
+import nju.seec.helper.data.dao.SlideDAO;
+import nju.seec.helper.data.dao.SubmitAnswerDAO;
+import nju.seec.helper.data.entity.Question;
+import nju.seec.helper.data.entity.Quiz;
+import nju.seec.helper.data.entity.Slide;
+import nju.seec.helper.data.entity.SubmitAnswer;
+import nju.seec.helper.logic.service.QuizService;
+import nju.seec.helper.logic.vo.QuizVO;
+import nju.seec.helper.logic.vo.StudentQuizVO;
+import nju.seec.helper.util.enums.QuizType;
+import nju.seec.helper.util.exception.HelperException;
+import nju.seec.helper.web.dto.QuizDTO;
+import nju.seec.helper.web.dto.SubmitAnswerDTO;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author cst
+ */
+@Service
+public class QuizServiceImpl implements QuizService {
+    private final QuestionDAO questionDAO;
+    private final SlideDAO slideDAO;
+    private final QuizDAO quizDAO;
+    private final SubmitAnswerDAO submitAnswerDAO;
+
+    public QuizServiceImpl(QuestionDAO questionDAO, SlideDAO slideDAO, QuizDAO quizDAO, SubmitAnswerDAO submitAnswerDAO) {
+        this.questionDAO = questionDAO;
+        this.slideDAO = slideDAO;
+        this.quizDAO = quizDAO;
+        this.submitAnswerDAO = submitAnswerDAO;
+    }
+
+    @Override
+    public QuizVO createQuiz(QuizDTO quizDTO) {
+        List<Question> questions = quizDTO.getQuestionIds()
+                .stream()
+                .map(questionDAO::findQuestionById)
+                .collect(Collectors.toList());
+        Quiz quiz = this.copyQuizDTOToQuiz(quizDTO);
+        quiz.setQuestions(questions);
+        quiz = quizDAO.save(quiz);
+        return new QuizVO(quiz);
+    }
+
+    @Override
+    public QuizVO teacherGetQuiz(int slideId, QuizType quizType) {
+        Quiz quiz = quizDAO.findBySlideIdAndType(slideId, quizType);
+        return quiz == null ? null : new QuizVO(quiz);
+    }
+
+    @Override
+    public StudentQuizVO studentGetQuiz(int slideId, QuizType quizType) {
+        Slide slide = slideDAO.findSlideById(slideId);
+        HelperException forbiddenException = HelperException.of(HelperException.ExceptionType.FORBIDDEN, "你暂时无权限查看该测验");
+        switch (slide.getState()) {
+            case DRAFT:
+                throw forbiddenException;
+            case BEFORE_CLASS:
+                if (quizType != QuizType.PREVIEW) {
+                    throw forbiddenException;
+                }
+                break;
+            case IN_CLASS:
+                if (quizType != QuizType.PREVIEW && quizType != QuizType.STUDY) {
+                    throw forbiddenException;
+                }
+                break;
+            case AFTER_CLASS:
+                if (quizType != QuizType.PREVIEW && quizType != QuizType.STUDY && quizType != QuizType.REVIEW) {
+                    throw forbiddenException;
+                }
+            default:
+                break;
+        }
+
+        Quiz quiz = quizDAO.findBySlideIdAndType(slideId, quizType);
+        if (quiz == null) {
+            return null;
+        }
+        SubmitAnswer submitAnswer = submitAnswerDAO.findByQuizIdAndStudentId(quiz.getId(), 1);
+        return new StudentQuizVO(quiz, submitAnswer);
+    }
+
+    @Override
+    public void answerQuiz(Integer studentId, SubmitAnswerDTO submitAnswerDTO) {
+        SubmitAnswer submitAnswer = submitAnswerDAO.findByQuizIdAndStudentId(submitAnswerDTO.getQuizId(), studentId);
+        if (submitAnswer == null) {
+            submitAnswer = new SubmitAnswer();
+            submitAnswer.setChooseOptions(submitAnswerDTO.getChooseOptions());
+            submitAnswer.setSubmitNum(1);
+        } else {
+            submitAnswer.setChooseOptions(submitAnswerDTO.getChooseOptions());
+            submitAnswer.setSubmitNum(submitAnswer.getSubmitNum() + 1);
+        }
+        submitAnswerDAO.save(submitAnswer);
+    }
+
+    private Quiz copyQuizDTOToQuiz(QuizDTO quizDTO) {
+        Quiz quiz = new Quiz();
+        BeanUtils.copyProperties(quizDTO, quiz, "questionIds");
+        return quiz;
+    }
+}

+ 7 - 24
src/main/java/nju/seec/helper/logic/service/impl/SlideServiceImpl.java

@@ -6,7 +6,6 @@ import nju.seec.helper.data.entity.Course;
 import nju.seec.helper.data.entity.Item;
 import nju.seec.helper.data.entity.Slide;
 import nju.seec.helper.logic.service.SlideService;
-import nju.seec.helper.logic.vo.ItemVO;
 import nju.seec.helper.logic.vo.SlideVO;
 import nju.seec.helper.util.enums.SlideState;
 import nju.seec.helper.util.exception.HelperException;
@@ -38,7 +37,7 @@ public class SlideServiceImpl implements SlideService {
         Page<Slide> slidePage = slideDAO.findByCourseIdAndNameContains(courseId, key, pageable);
 
         return slidePage.getContent().stream()
-                .map(this::copySlideToSlideVO)
+                .map(SlideVO::new)
                 .collect(Collectors.toList());
     }
 
@@ -47,14 +46,14 @@ public class SlideServiceImpl implements SlideService {
         Page<Slide> slidePage = slideDAO.findByNameContains(key, pageable);
 
         return slidePage.getContent().stream()
-                .map(this::copySlideToSlideVO)
+                .map(SlideVO::new)
                 .collect(Collectors.toList());
     }
 
     @Override
     public SlideVO getOneSlide(int slideId) {
         Slide slide = slideDAO.findSlideById(slideId);
-        return this.copySlideToSlideVO(slide);
+        return new SlideVO(slide);
     }
 
     @Override
@@ -69,7 +68,7 @@ public class SlideServiceImpl implements SlideService {
         slide.setCourseName(course.getName());
         slide.setItems(Collections.emptyList());
         slide = slideDAO.save(slide);
-        return this.copySlideToSlideVO(slide);
+        return new SlideVO(slide);
     }
 
     @Override
@@ -77,7 +76,7 @@ public class SlideServiceImpl implements SlideService {
         Slide slide = slideDAO.findSlideById(slideDTO.getId());
         this.copySlideDTOToSlide(slideDTO, slide);
         slide = slideDAO.save(slide);
-        return this.copySlideToSlideVO(slide);
+        return new SlideVO(slide);
     }
 
     @Override
@@ -85,7 +84,7 @@ public class SlideServiceImpl implements SlideService {
         Slide slide = slideDAO.findSlideById(slideId);
         slide.setState(state);
         slide = slideDAO.save(slide);
-        return this.copySlideToSlideVO(slide);
+        return new SlideVO(slide);
     }
 
     @Override
@@ -97,26 +96,10 @@ public class SlideServiceImpl implements SlideService {
     public List<SlideVO> getStudentSlides(String courseId, String key, Pageable pageable) {
         Page<Slide> slidePage = slideDAO.findByCourseIdAndStateNotAndNameContains(courseId, SlideState.DRAFT, key, pageable);
         return slidePage.getContent().stream()
-                .map(this::copySlideToSlideVO)
+                .map(SlideVO::new)
                 .collect(Collectors.toList());
     }
 
-    private SlideVO copySlideToSlideVO(Slide slide) {
-        SlideVO slideVO = new SlideVO();
-        BeanUtils.copyProperties(slide, slideVO, "items");
-        slideVO.setItems(slide.getItems().stream()
-                .map(this::copyItemToItemVO)
-                .collect(Collectors.toList())
-        );
-        return slideVO;
-    }
-
-    private ItemVO copyItemToItemVO(Item item) {
-        ItemVO itemVO = new ItemVO();
-        BeanUtils.copyProperties(item, itemVO);
-        return itemVO;
-    }
-
     private void copySlideDTOToSlide(SlideDTO slideDTO, Slide slide) {
         BeanUtils.copyProperties(slideDTO, slide, "courseId", "courseName", "createTime", "updateTime", "items");
         slide.setItems(slideDTO.getItems().stream()

+ 0 - 15
src/main/java/nju/seec/helper/logic/vo/AnswerVO.java

@@ -1,15 +0,0 @@
-package nju.seec.helper.logic.vo;
-
-import lombok.Builder;
-import lombok.Data;
-
-/**
- * @author cst
- */
-@Data
-@Builder
-public class AnswerVO {
-    private Integer quizId;
-    private String answer;
-    private String analysis;
-}

+ 6 - 0
src/main/java/nju/seec/helper/logic/vo/CourseVO.java

@@ -1,6 +1,7 @@
 package nju.seec.helper.logic.vo;
 
 import lombok.Data;
+import nju.seec.helper.data.entity.Course;
 
 /**
  * @author cst
@@ -9,4 +10,9 @@ import lombok.Data;
 public class CourseVO {
     private String id;
     private String name;
+
+    public CourseVO(Course course) {
+        this.id = course.getId();
+        this.name = course.getName();
+    }
 }

+ 11 - 1
src/main/java/nju/seec/helper/logic/vo/ItemVO.java

@@ -1,6 +1,7 @@
 package nju.seec.helper.logic.vo;
 
 import lombok.Data;
+import nju.seec.helper.data.entity.Item;
 import nju.seec.helper.util.enums.ItemTransition;
 import nju.seec.helper.util.enums.ItemType;
 
@@ -9,10 +10,19 @@ import nju.seec.helper.util.enums.ItemType;
  */
 @Data
 public class ItemVO {
+    private Integer id;
     private String frontendId;
     private String placeholder;
-    private Integer id;
     private ItemTransition transition;
     private ItemType type;
     private String content;
+
+    public ItemVO(Item item) {
+        this.id = item.getId();
+        this.frontendId = item.getFrontendId();
+        this.placeholder = item.getPlaceholder();
+        this.transition = item.getTransition();
+        this.type = item.getType();
+        this.content = item.getContent();
+    }
 }

+ 33 - 0
src/main/java/nju/seec/helper/logic/vo/NoAnswerQuestionVO.java

@@ -0,0 +1,33 @@
+package nju.seec.helper.logic.vo;
+
+import lombok.Data;
+import nju.seec.helper.data.entity.Question;
+import nju.seec.helper.util.enums.QuestionType;
+
+import java.time.LocalDateTime;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author cst
+ */
+@Data
+public class NoAnswerQuestionVO {
+    private Integer id;
+    private QuestionType type;
+    private String stem;
+    private Map<String, String> options;
+    private Set<String> tags;
+    private Set<String> knowledgeId;
+    private LocalDateTime lastModified;
+
+    public NoAnswerQuestionVO(Question question) {
+        this.id = question.getId();
+        this.type = question.getType();
+        this.stem = question.getStem();
+        this.options = question.getOptions();
+        this.tags = question.getTags();
+        this.knowledgeId = question.getKnowledgeId();
+        this.lastModified = question.getLastModified();
+    }
+}

+ 38 - 0
src/main/java/nju/seec/helper/logic/vo/QuestionVO.java

@@ -0,0 +1,38 @@
+package nju.seec.helper.logic.vo;
+
+import lombok.Data;
+import nju.seec.helper.data.entity.Question;
+import nju.seec.helper.util.enums.QuestionType;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author cst
+ */
+@Data
+public class QuestionVO {
+    private Integer id;
+    private QuestionType type;
+    private String stem;
+    private Map<String, String> options;
+    private Set<String> tags;
+    private Set<String> knowledgeId;
+    private LocalDateTime lastModified;
+    private String answer;
+    private String analysis;
+
+    public QuestionVO(Question question) {
+        this.id = question.getId();
+        this.type = question.getType();
+        this.stem = question.getStem();
+        this.options = question.getOptions();
+        this.tags = question.getTags();
+        this.knowledgeId = question.getKnowledgeId();
+        this.lastModified = question.getLastModified();
+        this.answer = question.getAnswer();
+        this.analysis = question.getAnalysis();
+    }
+}

+ 17 - 13
src/main/java/nju/seec/helper/logic/vo/QuizVO.java

@@ -1,10 +1,12 @@
 package nju.seec.helper.logic.vo;
 
 import lombok.Data;
+import nju.seec.helper.data.entity.Quiz;
+import nju.seec.helper.util.enums.QuizType;
 
 import java.time.LocalDateTime;
-import java.util.Map;
-import java.util.Set;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * @author cst
@@ -12,16 +14,18 @@ import java.util.Set;
 @Data
 public class QuizVO {
     private Integer id;
-    private String type;
-    private String stem;
-    private Map<String, String> options;
-    private Set<String> tags;
-    private Set<String> knowledgeId;
-    private LocalDateTime lastModified;
+    private Integer slideId;
+    private List<QuestionVO> questions;
+    private QuizType type;
+    private LocalDateTime updateTime;
 
-    /**
-     * 以下属性根据访问系统角色判断是否赋值
-     */
-    private String answer;
-    private String analysis;
+    public QuizVO(Quiz quiz) {
+        this.id = quiz.getId();
+        this.slideId = quiz.getSlideId();
+        this.type = quiz.getType();
+        this.updateTime = quiz.getUpdateTime();
+        this.questions = quiz.getQuestions().stream()
+                .map(QuestionVO::new)
+                .collect(Collectors.toList());
+    }
 }

+ 0 - 10
src/main/java/nju/seec/helper/logic/vo/SlideQuizVO.java

@@ -1,10 +0,0 @@
-package nju.seec.helper.logic.vo;
-
-import lombok.Data;
-
-/**
- * @author cst
- */
-@Data
-public class SlideQuizVO {
-}

+ 17 - 1
src/main/java/nju/seec/helper/logic/vo/SlideVO.java

@@ -1,22 +1,38 @@
 package nju.seec.helper.logic.vo;
 
 import lombok.Data;
+import nju.seec.helper.data.entity.Slide;
 import nju.seec.helper.util.enums.SlideState;
 
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * @author cst
  */
 @Data
 public class SlideVO {
+    private Integer id;
     private String courseId;
     private String courseName;
-    private Integer id;
     private SlideState state;
     private String name;
     private List<ItemVO> items;
     private LocalDateTime createTime;
     private LocalDateTime updateTime;
+
+    public SlideVO(Slide slide) {
+        this.id = slide.getId();
+        this.courseId = slide.getCourseId();
+        this.courseName = slide.getCourseName();
+        this.state = slide.getState();
+        this.name = slide.getName();
+        this.createTime = slide.getCreateTime();
+        this.updateTime = slide.getUpdateTime();
+        this.items = slide.getItems().stream()
+                .map(ItemVO::new)
+                .collect(Collectors.toList());
+
+    }
 }

+ 88 - 0
src/main/java/nju/seec/helper/logic/vo/StudentQuizVO.java

@@ -0,0 +1,88 @@
+package nju.seec.helper.logic.vo;
+
+import lombok.Data;
+import nju.seec.helper.data.entity.Question;
+import nju.seec.helper.data.entity.Quiz;
+import nju.seec.helper.data.entity.SubmitAnswer;
+import nju.seec.helper.util.enums.QuizType;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author cst
+ */
+@Data
+public class StudentQuizVO {
+    private Integer id;
+    private Integer slideId;
+    private List<NoAnswerQuestionVO> questions;
+    private QuizType type;
+
+    private SubmitAnswerVO submitAnswer;
+
+    public StudentQuizVO(Quiz quiz, SubmitAnswer submitAnswer) {
+        this.id = quiz.getId();
+        this.slideId = quiz.getSlideId();
+        this.type = quiz.getType();
+        if (submitAnswer == null) {
+            this.questions = quiz.getQuestions().stream()
+                    .map(NoAnswerQuestionVO::new)
+                    .collect(Collectors.toList());
+            this.submitAnswer = null;
+        } else {
+            SubmitAnswerVO submitAnswerVO = new SubmitAnswerVO();
+            submitAnswerVO.id = submitAnswer.getId();
+            submitAnswerVO.quizId = submitAnswer.getQuizId();
+            submitAnswerVO.studentId = submitAnswer.getStudentId();
+            submitAnswerVO.submitTime = submitAnswer.getSubmitTime();
+            submitAnswerVO.submitNum = submitAnswer.getSubmitNum();
+
+            List<Question> questions = quiz.getQuestions();
+            Map<Integer, String> chooseOptions = submitAnswer.getChooseOptions();
+            this.questions = new ArrayList<>();
+            submitAnswerVO.chooseOptions = new ArrayList<>();
+            submitAnswerVO.rightNum = 0;
+            for (Question question : questions) {
+                this.questions.add(new NoAnswerQuestionVO(question));
+                String chooseOption = chooseOptions.get(question.getId());
+                submitAnswerVO.chooseOptions.add(chooseOption);
+                if (question.getAnswer().equals(chooseOption)) {
+                    submitAnswerVO.rightNum++;
+                }
+            }
+        }
+    }
+
+    @Data
+    public static class SubmitAnswerVO {
+        private Integer id;
+        /**
+         * 提交学生编号
+         */
+        private Integer studentId;
+        /**
+         *
+         */
+        private Integer quizId;
+        /**
+         * 学生上次提交选项列表,与问题列表一一对应
+         */
+        private List<String> chooseOptions;
+        /**
+         * 上次提交时间
+         */
+        private LocalDateTime submitTime;
+        /**
+         * 已提交次数
+         */
+        private Integer submitNum;
+        /**
+         * 正确个数
+         */
+        private Integer rightNum;
+    }
+}

+ 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
+}

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

@@ -4,5 +4,5 @@ package nju.seec.helper.util.enums;
  * @author cst
  */
 public enum QuizType {
-    choice
+    PREVIEW, STUDY, REVIEW
 }

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

@@ -1,8 +0,0 @@
-package nju.seec.helper.util.enums;
-
-/**
- * @author cst
- */
-public enum SlideQuizType {
-    PREVIEW, IN_CLASS, REVIEW, ALL
-}

+ 32 - 0
src/main/java/nju/seec/helper/web/controller/QuestionController.java

@@ -0,0 +1,32 @@
+package nju.seec.helper.web.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import nju.seec.helper.logic.service.QuestionService;
+import nju.seec.helper.logic.vo.QuestionVO;
+import nju.seec.helper.web.response.ResourceResponse;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+@Api(tags = "问题")
+@RestController
+@RequestMapping("/api/question")
+public class QuestionController {
+    private final QuestionService questionService;
+
+    public QuestionController(QuestionService questionService) {
+        this.questionService = questionService;
+    }
+
+    @ApiOperation("教师取得问题")
+    @GetMapping
+    public ResourceResponse<List<QuestionVO>> getQuestions(String key) {
+        return ResourceResponse.of(questionService.getQuestions(key));
+    }
+}

+ 52 - 0
src/main/java/nju/seec/helper/web/controller/QuizController.java

@@ -0,0 +1,52 @@
+package nju.seec.helper.web.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import nju.seec.helper.logic.service.QuizService;
+import nju.seec.helper.logic.vo.QuizVO;
+import nju.seec.helper.logic.vo.StudentQuizVO;
+import nju.seec.helper.util.enums.QuizType;
+import nju.seec.helper.web.dto.QuizDTO;
+import nju.seec.helper.web.dto.SubmitAnswerDTO;
+import nju.seec.helper.web.response.EmptyResponse;
+import nju.seec.helper.web.response.ResourceResponse;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author cst
+ */
+@Api(tags = "测验")
+@RestController
+@RequestMapping("/api/quiz")
+public class QuizController {
+    private final QuizService quizService;
+
+    public QuizController(QuizService quizService) {
+        this.quizService = quizService;
+    }
+
+    @ApiOperation("教师创建测验")
+    @PostMapping
+    public ResourceResponse<QuizVO> createQuiz(@RequestBody QuizDTO quizDTO) {
+        return ResourceResponse.of(quizService.createQuiz(quizDTO));
+    }
+
+    @ApiOperation("教师获取测验")
+    @GetMapping("/teacher/{slideId:\\d+}")
+    public ResourceResponse<QuizVO> teacherGetSlideQuizzes(@PathVariable("slideId") int slideId, String type) {
+        return ResourceResponse.of(quizService.teacherGetQuiz(slideId, QuizType.valueOf(type)));
+    }
+
+    @ApiOperation("学生获取测验")
+    @GetMapping("/student/{slideId:\\d+}")
+    public ResourceResponse<StudentQuizVO> studentGetSlideQuizzes(@PathVariable("slideId") int slideId, String type) {
+        return ResourceResponse.of(quizService.studentGetQuiz(slideId, QuizType.valueOf(type)));
+    }
+
+    @ApiOperation("学生作答测验")
+    @PostMapping("/answer")
+    public EmptyResponse answerQuiz(@RequestBody SubmitAnswerDTO submitAnswerDTO) {
+        quizService.answerQuiz(1, submitAnswerDTO);
+        return EmptyResponse.getInstance();
+    }
+}

+ 2 - 0
src/main/java/nju/seec/helper/web/dto/CommentDTO.java

@@ -10,6 +10,8 @@ import javax.validation.constraints.NotNull;
  */
 @Data
 public class CommentDTO {
+    @NotNull
+    private Integer slideId;
     @NotNull
     private Integer itemId;
     private Integer beforeCommentId;

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

@@ -0,0 +1,20 @@
+package nju.seec.helper.web.dto;
+
+import lombok.Data;
+import nju.seec.helper.util.enums.QuizType;
+
+import javax.validation.constraints.NotNull;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author cst
+ */
+@Data
+public class QuizDTO {
+    @NotNull
+    private Integer slideId;
+    private List<Integer> questionIds = Collections.emptyList();
+    @NotNull
+    private QuizType type;
+}

+ 0 - 14
src/main/java/nju/seec/helper/web/dto/SlideQuizDTO.java

@@ -1,14 +0,0 @@
-package nju.seec.helper.web.dto;
-
-import lombok.Data;
-import nju.seec.helper.util.enums.SlideQuizType;
-
-/**
- * @author cst
- */
-@Data
-public class SlideQuizDTO {
-    private Integer slideId;
-    private Integer quizId;
-    private SlideQuizType type;
-}

+ 14 - 0
src/main/java/nju/seec/helper/web/dto/SubmitAnswerDTO.java

@@ -0,0 +1,14 @@
+package nju.seec.helper.web.dto;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@Data
+public class SubmitAnswerDTO {
+    private Integer quizId;
+    private Map<Integer, String> chooseOptions;
+}