XuShengTao 6 лет назад
Родитель
Сommit
d10e398761
23 измененных файлов с 549 добавлено и 87 удалено
  1. 30 10
      src/main/java/nju/seec/helper/controller/QuestionController.java
  2. 22 28
      src/main/java/nju/seec/helper/controller/QuizController.java
  3. 3 0
      src/main/java/nju/seec/helper/dao/ChooseDAO.java
  4. 2 0
      src/main/java/nju/seec/helper/dao/QuizDAO.java
  5. 2 2
      src/main/java/nju/seec/helper/dao/QuizStudentAnswerDAO.java
  6. 7 0
      src/main/java/nju/seec/helper/dao/StudentScoreDAO.java
  7. 20 0
      src/main/java/nju/seec/helper/dto/QuestionStudentAnswerDTO.java
  8. 1 1
      src/main/java/nju/seec/helper/dto/QuizAnswerDTO.java
  9. 6 1
      src/main/java/nju/seec/helper/entity/Quiz.java
  10. 11 3
      src/main/java/nju/seec/helper/entity/QuizStudentAnswer.java
  11. 26 0
      src/main/java/nju/seec/helper/entity/StudentScore.java
  12. 8 0
      src/main/java/nju/seec/helper/service/QuestionService.java
  13. 15 0
      src/main/java/nju/seec/helper/service/QuizService.java
  14. 27 0
      src/main/java/nju/seec/helper/service/impl/QuestionServiceImpl.java
  15. 157 14
      src/main/java/nju/seec/helper/service/impl/QuizServiceImpl.java
  16. 6 2
      src/main/java/nju/seec/helper/service/impl/SlideServiceImpl.java
  17. 142 22
      src/main/java/nju/seec/helper/util/BOKUtil.java
  18. 11 3
      src/main/java/nju/seec/helper/util/RestRequestUtil.java
  19. 8 0
      src/main/java/nju/seec/helper/util/enums/ExceptionType.java
  20. 7 1
      src/main/java/nju/seec/helper/vo/quiz/ChoiceQuestionVO.java
  21. 4 0
      src/main/java/nju/seec/helper/vo/quiz/QuestionVO.java
  22. 29 0
      src/main/java/nju/seec/helper/vo/quiz/QuizResultVO.java
  23. 5 0
      src/main/java/nju/seec/helper/vo/quiz/TrueOrFalseQuestionVO.java

+ 30 - 10
src/main/java/nju/seec/helper/controller/QuestionController.java

@@ -7,9 +7,11 @@ 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.QuizState;
 import nju.seec.helper.util.enums.SlideState;
 import nju.seec.helper.util.enums.UserType;
+import nju.seec.helper.util.exception.HelperException;
 import nju.seec.helper.vo.CourseVO;
 import nju.seec.helper.vo.SlideVO;
 import nju.seec.helper.vo.quiz.ChoiceQuestionVO;
@@ -57,49 +59,67 @@ public class QuestionController {
     }
 
     /**
-     * 创建题目 Stub
+     * 获得题目列表
+     */
+    @Auth(roles = {UserType.TEACHER}, message = "获得题目详情")
+    @GetMapping("/{questionId}")
+        public QuestionVO getQuestion(LoginUser user,@PathVariable("questionId") String questionId) throws JsonProcessingException {
+        return questionService.getQuestion(questionId);
+    }
+
+
+    /**
+     * 创建题目
      */
     @Auth(roles = {UserType.TEACHER}, message = "创建题目")
     @PostMapping("")
     public QuestionVO PostQuestion(LoginUser user,  @RequestBody Map question) {
 
-        if(question.get("kind")==null){throw new ParseException('1',"no kind");}
+        if(question.get("kind")==null){throw HelperException.of(ExceptionType.PARAM_ERROR,"no kind");}
         String kind = (String)question.get("kind");
+        QuestionVO newQuestion=null;
         switch (kind){
             case "CHOICE":
-                return (objectMapper.convertValue(question, ChoiceQuestionVO.class));
+                newQuestion =  (objectMapper.convertValue(question, ChoiceQuestionVO.class));
+                return questionService.newQuestion(newQuestion);
             case "TRUE_FALSE":
-                return (objectMapper.convertValue(question, TrueOrFalseQuestionVO.class));
+                newQuestion =  (objectMapper.convertValue(question, TrueOrFalseQuestionVO.class));
+                return questionService.newQuestion(newQuestion);
         }
+
         throw new ParseException('1',"parse failed:"+question.toString());
     }
 
     /**
-     * 更新题目 Stub
+     * 更新题目
      */
-    @Auth(roles = {UserType.TEACHER}, message = "更新题目")
+//    @Auth(roles = {UserType.TEACHER}, message = "更新题目")
     @PutMapping("/{questionId}")
     public QuestionVO updateQuestion(LoginUser user, @RequestBody Map question, @PathVariable("questionId") String questionId) {
 
         if(question.get("kind")==null){throw new ParseException('1',"no kind");}
         String kind = (String)question.get("kind");
+        QuestionVO newQuestion=null;
         switch (kind){
             case "CHOICE":
-                return (objectMapper.convertValue(question, ChoiceQuestionVO.class));
+                newQuestion= (objectMapper.convertValue(question, ChoiceQuestionVO.class));
+                return questionService.updateQuestion(newQuestion);
             case "TRUE_FALSE":
-                return (objectMapper.convertValue(question, TrueOrFalseQuestionVO.class));
+                newQuestion = (objectMapper.convertValue(question, TrueOrFalseQuestionVO.class));
+                return questionService.updateQuestion(newQuestion);
         }
         throw new ParseException('1',"parse failed:"+question.toString());
     }
 
 
     /**
-     * 更新题目 Stub
+     * 删除题目
      */
-    @Auth(roles = {UserType.TEACHER}, message = "更新题目")
+    @Auth(roles = {UserType.TEACHER}, message = "删除题目")
     @DeleteMapping("/{questionId}")
     public Map deleteQuestion(LoginUser user, @PathVariable("questionId") String questionId) {
 
+        questionService.deleteQuestion(questionId);
         Map map=new HashMap();
         map.put("message","success");
         return map;

+ 22 - 28
src/main/java/nju/seec/helper/controller/QuizController.java

@@ -18,16 +18,15 @@ import nju.seec.helper.util.enums.SlideState;
 import nju.seec.helper.util.enums.UserType;
 import nju.seec.helper.vo.CourseVO;
 import nju.seec.helper.vo.SlideVO;
-import nju.seec.helper.vo.quiz.ChoiceQuestionVO;
-import nju.seec.helper.vo.quiz.QuestionVO;
-import nju.seec.helper.vo.quiz.QuizVO;
-import nju.seec.helper.vo.quiz.TrueOrFalseQuestionVO;
+import nju.seec.helper.vo.UserVO;
+import nju.seec.helper.vo.quiz.*;
 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 javax.persistence.EntityNotFoundException;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -53,9 +52,9 @@ public class QuizController {
      */
     @Auth(roles = {UserType.STUDENT,UserType.TEACHER}, message = "获得测试列表")
     @GetMapping("")
-    public Map getBasicQuizList(LoginUser user, String slideId, boolean subscribe,
+    public Map getBasicQuizList(LoginUser user, String slideId,
                       QuizState state	, @PageableDefault(Integer.MAX_VALUE) Pageable pageable) {
-        List<QuizVO> quizs=new ArrayList<QuizVO>();
+        List<QuizVO> quizzes=new ArrayList<QuizVO>();
         Map ret=new LinkedHashMap(2);
         Page<Quiz> page;
         if(slideId!=null){
@@ -63,9 +62,9 @@ public class QuizController {
            }else{
             page = quizService.getAllBasicQuizByUser( pageable, state, user.getId());
         }
-        quizs.addAll(page.getContent().stream().map(QuizVO::new).collect(Collectors.toList()));
+        quizzes.addAll(page.getContent().stream().map(QuizVO::new).collect(Collectors.toList()));
         ret.put("page",PageResponse.PageInfo.of(page.getNumber(), page.getSize(), page.getTotalPages(), Long.valueOf(page.getTotalElements()).intValue()));
-        ret.put("quizs",quizs);
+        ret.put("quizzes",quizzes);
         return ret;
     }
 
@@ -115,18 +114,24 @@ public class QuizController {
     }
 
     /**
-     * 获得测试结果统计 Stub
+     * 获得测试结果统计
      */
-    @Auth(roles = {UserType.TEACHER}, message = "获得测试结果统计")
+//    @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("avarage","88");
+        map.put("avarage",list.stream().mapToInt(QuizResultVO::getScore).average());
+        map.put("data",list);
         return map;
     }
     /**
-     * 提交答案 Stub
+     * 提交答案
      */
     static ObjectMapper objectMapper = new ObjectMapper();
 
@@ -134,24 +139,13 @@ public class QuizController {
     @PutMapping("/{quizId}/student-answer")
     public Map putStudentAnswer(LoginUser user, @PathVariable("quizId") String quizId, @RequestBody QuizAnswerDTO quizAnswerDTO) {
 
-        Map map=new HashMap();
-        map.put("message","fail to parse body");
-        List<QuestionVO> questions=new ArrayList<>();
-        for(Object q:quizAnswerDTO.getAnswers()){
-            final Map question = (Map) q;
-            if(question.get("kind")==null){return map;}
-            String kind = (String)question.get("kind");
-            switch (kind){
-                case "CHOICE":
-                    questions.add(objectMapper.convertValue(q, ChoiceQuestionVO.class));
-                    break;
-                case "TRUE_FALSE":
-                    questions.add(objectMapper.convertValue(q, TrueOrFalseQuestionVO.class));
-                    break;
-            }
+        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("echo",questions);
+        map.put("matchNum",match);
         return map;
     }
     // --------------------------------stub---------------------------------------

+ 3 - 0
src/main/java/nju/seec/helper/dao/ChooseDAO.java

@@ -39,4 +39,7 @@ public interface ChooseDAO extends JpaRepository<Choose, Integer> {
      */
     @Query("select distinct choose.courseId from Choose choose where choose.studentId=?1")
     Set<Integer> findCourseIdsByStudentId(Integer studentId);
+
+    @Query("select distinct choose.studentId from Choose choose where choose.courseId=?1")
+    Set<Integer> findStudentIdsByCourseId(Integer courseId);
 }

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

@@ -12,6 +12,7 @@ import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.JpaRepository;
 
 import java.util.Collection;
+import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 
@@ -22,6 +23,7 @@ public interface QuizDAO extends JpaRepository<Quiz,Long> {
     }
     Page<Quiz> findAllBySlideAndState(Slide slide, QuizState state, Pageable pageable);
     Page<Quiz> findAllBySlide(Slide slide, Pageable pageable);
+    List<Quiz> findAllBySlide(Slide slide);
     Page<Quiz> findAllByCourseIn(Collection<Course> course, Pageable pageable);
     Page<Quiz> findAllByCourseInAndState(Collection<Course> course, QuizState state,Pageable pageable);
     Page<Quiz> findByTeacher(User teacher, Pageable pageable);

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

@@ -11,6 +11,6 @@ import java.util.Set;
 public interface QuizStudentAnswerDAO extends JpaRepository<QuizStudentAnswer,Long> {
     Set<QuizStudentAnswer> findAllByQuiz(Quiz quiz);
     Set<QuizStudentAnswer> findAllByStudent(User student);
-    Set<QuizStudentAnswer> findByQuizAndStudent(Quiz quiz, User student);
-    Set<QuizStudentAnswer> findByQuestionIdAndStudent(String questionId, User student);
+    Set<QuizStudentAnswer> findByStudentAndQuiz(User student, Quiz quiz);
+    Set<QuizStudentAnswer> findByStudentAndQuestionId(User student, String questionId);
 }

+ 7 - 0
src/main/java/nju/seec/helper/dao/StudentScoreDAO.java

@@ -0,0 +1,7 @@
+package nju.seec.helper.dao;
+
+import nju.seec.helper.entity.StudentScore;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface StudentScoreDAO extends JpaRepository<StudentScore,Long> {
+}

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

@@ -0,0 +1,20 @@
+package nju.seec.helper.dto;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import nju.seec.helper.util.enums.QuestionKindState;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.Map;
+
+@Data
+public class QuestionStudentAnswerDTO {
+    @JsonProperty(value = "id")
+    @NotBlank
+    private String questionId;
+    @JsonProperty(value = "studentAnswer")
+    @NotNull
+    private Object studentAnswer;
+}

+ 1 - 1
src/main/java/nju/seec/helper/dto/QuizAnswerDTO.java

@@ -9,5 +9,5 @@ import java.util.Map;
 
 @Data
 public class QuizAnswerDTO {
-    List answers;
+    List<QuestionStudentAnswerDTO> answers;
 }

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

@@ -10,6 +10,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 import javax.persistence.*;
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 @Data
@@ -42,11 +43,15 @@ public class Quiz {
     @ElementCollection
     private List<String> questions;
 
+    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
+    @JoinColumn(name="studentScore")
+    private List<StudentScore> studentScore;
+
     @Enumerated(value = EnumType.STRING)
     @Column(name = "quizTime")
     private SlideState quizTime = SlideState.BEFORE_CLASS;
     @Enumerated(value = EnumType.STRING)
-    @Column(name = "state")
+    @Column(name = "state",nullable = false)
     private QuizState state;
 
     @Basic

+ 11 - 3
src/main/java/nju/seec/helper/entity/QuizStudentAnswer.java

@@ -1,7 +1,10 @@
 package nju.seec.helper.entity;
 
 
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 import org.springframework.data.annotation.CreatedDate;
 import org.springframework.data.jpa.domain.support.AuditingEntityListener;
 
@@ -10,8 +13,13 @@ import java.time.LocalDateTime;
 
 @Data
 @Entity
-@Table(name = "quiz_student_answer",indexes = {@Index(columnList = "quiz"),@Index(columnList = "student")})
+@Builder
+@Table(name = "quiz_student_answer",
+        indexes = {@Index(columnList = "quiz"),
+                    @Index(columnList = "student ,quiz")})
 @EntityListeners(AuditingEntityListener.class)
+@AllArgsConstructor
+@NoArgsConstructor
 public class QuizStudentAnswer {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -27,7 +35,7 @@ public class QuizStudentAnswer {
     private User student;
 
     @Basic
-    @Column(name = "questionId")
+    @Column(name = "questionId",nullable = false)
     private String questionId;
 
     @Basic
@@ -45,6 +53,6 @@ public class QuizStudentAnswer {
     private String studentAnswer;
 
     @Basic
-    @Column(name = "name", nullable = false)
+    @Column(name = "pass", nullable = true)
     private Boolean pass;
 }

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

@@ -0,0 +1,26 @@
+package nju.seec.helper.entity;
+
+import lombok.Data;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Map;
+
+@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;
+}

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

@@ -9,4 +9,12 @@ import java.awt.print.Pageable;
 
 public interface QuestionService {
     Page<QuestionVO> getQuestionList(LoginUser user, String stem, org.springframework.data.domain.Pageable pageable) throws JsonProcessingException;
+
+    QuestionVO getQuestion(String questionId);
+
+    QuestionVO newQuestion(QuestionVO newQuestion);
+
+    QuestionVO updateQuestion(QuestionVO newQuestion);
+
+    void deleteQuestion(String questionId);
 }

+ 15 - 0
src/main/java/nju/seec/helper/service/QuizService.java

@@ -1,9 +1,12 @@
 package nju.seec.helper.service;
 
+import nju.seec.helper.dto.QuestionStudentAnswerDTO;
 import nju.seec.helper.entity.Quiz;
+import nju.seec.helper.entity.Slide;
 import nju.seec.helper.util.enums.QuizState;
 import nju.seec.helper.util.enums.SlideState;
 import nju.seec.helper.util.enums.UserType;
+import nju.seec.helper.vo.quiz.QuizResultVO;
 import nju.seec.helper.vo.quiz.QuizVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -11,6 +14,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 public interface QuizService {
 
@@ -18,8 +22,15 @@ public interface QuizService {
     Quiz CreateQuiz(String name, Integer uid, SlideState quizTime,
                     Integer slideId, List<String> questions);
 
+    Integer upStudentAnswer(Long quizId, Integer uid, List<QuestionStudentAnswerDTO> answers);
+
     Page<Quiz> getAllBasicQuizBySlide(Integer slideId, Pageable pageable, QuizState state, Integer uid);
 
+    @Transactional
+    QuizState checkQuizState(Quiz quiz, Slide slide);
+
+    QuizState checkQuizState(Long quizId, Integer slideId);
+
     QuizVO combineQuiz(Long quizId);
 
     QuizVO combineQuizWithAnswer(Long quizId, Integer uid, UserType type);
@@ -29,4 +40,8 @@ public interface QuizService {
     Quiz modifyQuiz(Long quizId, String name, List<String> questions, SlideState quizTime, String slideId);
 
     Page<Quiz> getAllBasicQuizByUser(Pageable pageable, QuizState state, Integer uid);
+
+    List<QuizResultVO> getQuizResult(Long aLong);
+
+    void modifySlideState(Slide slide);
 }

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

@@ -6,7 +6,9 @@ import nju.seec.helper.service.QuestionService;
 import nju.seec.helper.util.BOKUtil;
 import nju.seec.helper.util.JsonUtils;
 import nju.seec.helper.util.RestRequestUtil;
+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.ChoiceQuestionVO;
 import nju.seec.helper.vo.quiz.QuestionVO;
 import nju.seec.helper.vo.quiz.TrueOrFalseQuestionVO;
@@ -37,6 +39,31 @@ public class QuestionServiceImpl implements QuestionService {
         return ret;
     }
 
+    @Override
+    public QuestionVO getQuestion(String questionId) {
+        return bokUtil.BokFindById(questionId);
+    }
+    @Override
+    public QuestionVO newQuestion(QuestionVO newQuestion) {
+        try {
+            return bokUtil.PostNewQuestion(newQuestion);
+        } catch (JsonProcessingException e) {
+            throw HelperException.of(ExceptionType.ERROR,"请求BOK题库数据错误:"+e.getMessage());
+        }
+    }
 
+    @Override
+    public QuestionVO updateQuestion(QuestionVO newQuestion) {
+        try {
+            return bokUtil.PutQuestion(newQuestion);
+        } catch (JsonProcessingException e) {
+            throw HelperException.of(ExceptionType.ERROR,"请求BOK题库数据错误:"+e.getMessage());
+        }
+    }
+
+    @Override
+    public void deleteQuestion(String questionId) {
+        bokUtil.deleteQuestion(questionId);
+    }
 
 }

+ 157 - 14
src/main/java/nju/seec/helper/service/impl/QuizServiceImpl.java

@@ -1,7 +1,7 @@
 package nju.seec.helper.service.impl;
 
 import nju.seec.helper.dao.*;
-import nju.seec.helper.dto.LoginUser;
+import nju.seec.helper.dto.QuestionStudentAnswerDTO;
 import nju.seec.helper.entity.*;
 import nju.seec.helper.service.QuizService;
 import nju.seec.helper.util.BOKUtil;
@@ -9,6 +9,7 @@ import nju.seec.helper.util.enums.QuizState;
 import nju.seec.helper.util.enums.SlideState;
 import nju.seec.helper.util.enums.UserType;
 import nju.seec.helper.vo.quiz.QuestionVO;
+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;
@@ -111,6 +112,66 @@ public class QuizServiceImpl implements QuizService {
 
     }
 
+    @Override
+    public List<QuizResultVO> getQuizResult(Long quizId) {
+        Assert.notNull(quizId,"QUIZ ID can't be null!");
+        final Quiz quiz = quizDAO.findQuizById(quizId);
+        if(quiz.getState()!=QuizState.CLOSED){throw new RuntimeException("quiz not close!");}
+        List<QuizResultVO> retList=new ArrayList<>();
+        final List<StudentScore> studentScoreList = quiz.getStudentScore();
+        Map<Integer,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
+    public Integer upStudentAnswer(Long quizId, Integer uid, List<QuestionStudentAnswerDTO> answers) {
+        Assert.notNull(quizId,"QUIZ ID can't be null!");
+        final Quiz quiz = quizDAO.findQuizById(quizId);
+        if(quiz.getState()!=QuizState.ONGOING){throw new RuntimeException("can't put not ONGOING quiz answer!");}
+        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> toSaveQSA=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){
+                qsaMap.get(id).setStudentAnswer(answer);
+                toSaveQSA.add(qsaMap.get(id));
+                break;
+            }
+            QuizStudentAnswer qsa=QuizStudentAnswer.builder()
+                    .questionId(id).quiz(quiz).student(student).studentAnswer(answer)
+                    .build();
+            toSaveQSA.add(qsa);
+        }
+        Integer savedNum=toSaveQSA.size();
+        quizStudentAnswerDAO.saveAll(toSaveQSA);
+        return savedNum;
+    }
+
     @Override
     public Page<Quiz> getAllBasicQuizBySlide(Integer slideId, Pageable pageable, QuizState state, Integer uid) {
         Assert.notNull(slideId,"slide can't be null");
@@ -121,33 +182,115 @@ public class QuizServiceImpl implements QuizService {
         else
             return quizDAO.findAllBySlide(slide,pageable);
     }
-
-    public void checkQuizState(Quiz quiz,Slide slide){
-        int QuizTime=quiz.getQuizTime().getNum();
-        int slideTime=slide.getState().getNum();
-        if(quiz.getId()==null)
+    @Override
+    @Transactional
+    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);
+            quiz.setState(slideTime < QuizTime ? QuizState.NOT_STARTED : QuizState.ONGOING);
         else {
-            //持久化后,不仅仅需要推进状态,还需要判分等步骤
-            if(slideTime<QuizTime){
+            //持久化后,不仅仅需要推进状态,还需要判分,保存等步骤
+            if (slideTime < QuizTime) {
+                if(quiz.getState().equals(QuizState.NOT_STARTED)){return QuizState.NOT_STARTED;}
                 //未开始
                 quiz.setState(QuizState.NOT_STARTED);
-            }else if(slideTime==QuizTime){
+            } else if (slideTime == QuizTime) {
+                if(quiz.getState().equals(QuizState.ONGOING)){return QuizState.ONGOING;}
                 quiz.setState(QuizState.ONGOING);
-            }else{
+            } else {
+                if(quiz.getState().equals(QuizState.CLOSED)){return QuizState.CLOSED;}
                 quiz.setState(QuizState.CLOSED);
+                Set<QuizStudentAnswer> QuizAnswers = quizStudentAnswerDAO.findAllByQuiz(quiz);
+                final Set<Integer> studentIds = chooseDAO.findStudentIdsByCourseId(slide.getCourseId());
+                Student2_QuestionId2Answer triadMap = new Student2_QuestionId2Answer(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, Integer slideId){
+        Quiz quiz=quizDAO.findQuizById(quizId);
+        Slide slide=slideDAO.findSlideById(slideId);
+        return checkQuizState(quiz,slide);
+    }
+    private class Student2_QuestionId2Answer{
+        Map<Integer,Map<String,QuizStudentAnswer>> Student2_QuestionId2Answer;
+        public Student2_QuestionId2Answer(Set<Integer> studentIds,Integer quizQuestionSize) {
+            Student2_QuestionId2Answer =new LinkedHashMap<>(studentIds.size());
+            for(Integer studentId:studentIds){
+                Map<String,QuizStudentAnswer> QuestionId2Answer= new LinkedHashMap<>(quizQuestionSize);
+                Student2_QuestionId2Answer.put(studentId,QuestionId2Answer);
+            }
+        }
+        public void addAnswer(QuizStudentAnswer answers){
+            Map<String, QuizStudentAnswer> stringQuizStudentAnswerMap = Student2_QuestionId2Answer.get(answers.getStudent().getId());
+            if(stringQuizStudentAnswerMap==null){
+                return ;
+            }
+            stringQuizStudentAnswerMap.put(answers.getQuestionId(),answers);
+        }
+        public void fullAnswer(Quiz quiz){
+            List<String> questions=quiz.getQuestions();
+            Student2_QuestionId2Answer.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);
+                        }
+                    }
+                }
+            });
+        }
+        public void checkAnswerAndFullPass(List<QuestionVO> questionVOS,Quiz quiz){
+            if(quiz.getStudentScore()==null){quiz.setStudentScore(new ArrayList<>(Student2_QuestionId2Answer.size()));}
+            Student2_QuestionId2Answer.forEach((studentId,map)->{
+                int passCount=0;
+                for(QuestionVO 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);
+            });
+        }
+        public List<QuizStudentAnswer> getAll(){
+            List<QuizStudentAnswer> list=new ArrayList<>();
+            for(Integer sid:Student2_QuestionId2Answer.keySet()){
+                list.addAll(Student2_QuestionId2Answer.get(sid).values());
+            }
+            return list;
         }
     }
-
     @Override
     public QuizVO combineQuiz(Long quizId){
         Assert.notNull(quizId,"QUIZ ID can't be null!");
         final Quiz quizById = quizDAO.findQuizById(quizId);
         final List<QuestionVO> questionVOS = bokUtil.BokFindByIdIn(quizById.getQuestions());
-        System.out.println(questionVOS);
         QuizVO ret= new QuizVO(quizById,questionVOS);
         return ret;
     }
@@ -169,7 +312,7 @@ public class QuizServiceImpl implements QuizService {
         }
 
         List<QuestionVO> questionVOS = bokUtil.BokFindByIdIn(quizById.getQuestions());
-        final Set<QuizStudentAnswer> QuizAnswers = quizStudentAnswerDAO.findByQuizAndStudent(quizById, student);
+        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);

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

@@ -13,6 +13,7 @@ import nju.seec.helper.dto.SlideStateDTO;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.entity.Slide;
 import nju.seec.helper.service.AuthUtil;
+import nju.seec.helper.service.QuizService;
 import nju.seec.helper.service.SlideService;
 import nju.seec.helper.util.CacheUtils;
 import nju.seec.helper.util.Consts;
@@ -22,6 +23,7 @@ import nju.seec.helper.util.enums.SlideState;
 import nju.seec.helper.util.exception.HelperException;
 import nju.seec.helper.vo.SlideVO;
 import org.apache.pdfbox.pdmodel.PDDocument;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
@@ -43,14 +45,15 @@ public class SlideServiceImpl implements SlideService {
     private final CourseDAO courseDAO;
     private final SlideDAO slideDAO;
     private final ChooseDAO chooseDAO;
-
+    private final QuizService quizService;
     private final FileUtils fileUtils;
     private final CacheUtils cacheUtils;
 
-    public SlideServiceImpl(CourseDAO courseDAO, SlideDAO slideDAO, ChooseDAO chooseDAO, FileUtils fileUtils, CacheUtils cacheUtils) {
+    public SlideServiceImpl(CourseDAO courseDAO, SlideDAO slideDAO, ChooseDAO chooseDAO, QuizService quizService, FileUtils fileUtils, CacheUtils cacheUtils) {
         this.courseDAO = courseDAO;
         this.slideDAO = slideDAO;
         this.chooseDAO = chooseDAO;
+        this.quizService = quizService;
         this.fileUtils = fileUtils;
         this.cacheUtils = cacheUtils;
     }
@@ -112,6 +115,7 @@ public class SlideServiceImpl implements SlideService {
 
         slide.setState(slideStateDTO.getState());
         slideDAO.save(slide);
+        quizService.modifySlideState(slide);
     }
 
     @Override

+ 142 - 22
src/main/java/nju/seec/helper/util/BOKUtil.java

@@ -1,18 +1,23 @@
 package nju.seec.helper.util;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.core.JsonProcessingException;
+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.ChoiceQuestionVO;
 import nju.seec.helper.vo.quiz.QuestionVO;
 import nju.seec.helper.vo.quiz.TrueOrFalseQuestionVO;
 import org.springframework.beans.factory.annotation.Autowired;
 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.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Component
@@ -21,6 +26,7 @@ public class BOKUtil {
     private RestRequestUtil restRequestUtil;
 
     private String searchUrl="http://bok.seecoder.cn/api/tq/search/";
+    private String tqUrl="http://bok.seecoder.cn/api/tq/";
     // 应该做成bok的库
     public void BokFindByStemLike(String stem, List<QuestionVO> Questions, Map page, Pageable pageable) {
         Map<String,String> urlParams=new HashMap<>();
@@ -29,9 +35,8 @@ public class BOKUtil {
         urlParams.put("size",String.valueOf(pageable.getPageSize()));
         urlParams.put("page",String.valueOf(1+pageable.getPageNumber()));
         try {
-            Map ret = (Map)restRequestUtil.sendPostGet(
+            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);
@@ -56,7 +61,7 @@ public class BOKUtil {
         urlParams.put("size",String.valueOf(Ids.size()));
         urlParams.put("page","1");
         try {
-            Map ret = (Map)restRequestUtil.sendPostGet(
+            Map ret = (Map)restRequestUtil.sendGetRequest(
                     searchUrl+"findByIdIn?id={id}&size={size}&page={page}", urlParams);
             List<Map> tqs= ((List)((Map)ret.get("_embedded")).get("choiceQuestions"));
             List<QuestionVO> collect = parseBody(tqs);
@@ -71,22 +76,137 @@ public class BOKUtil {
     }
 
     private List parseBody(List<Map> tqs) {
-        final List collect =
-                tqs.stream().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.CHOICE)
-                            .answer((Boolean) (q.get("answer")))
-                            .analysis((String) q.get("analysis")).QuestionId(String.valueOf((Integer) q.get("tq_id"))).build();
+        final List collect = tqs.stream().map(BOKUtil::parseOneQuestion).collect(Collectors.toList());
+        return collect;
+    }
+    private static QuestionVO 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.CHOICE)
+                        .answer(Boolean.valueOf((String) q.get("answer")))
+                        .analysis((String) q.get("analysis")).QuestionId(String.valueOf((Integer) q.get("tq_id"))).build();
+
+        }
+        throw HelperException.of(ExceptionType.ERROR,"unknown type:" + (String) q.get("type"));
+    }
+    public QuestionVO BokFindById(String questionId)  {
+        Map<String,String> urlParams=new HashMap<>();
+        try {
+            Map ret = (Map)restRequestUtil.sendGetRequest(tqUrl+questionId, urlParams);
+            return parseOneQuestion(ret);
+        } catch (JsonProcessingException e) {
+            throw HelperException.of(ExceptionType.ERROR,"请求BOK题库数据错误:"+e.getMessage());
+        }catch (HttpClientErrorException e){
+            throw HelperException.of(ExceptionType.valueOf(e.getStatusCode()),"请求BOK题库数据错误:"+e.getMessage());
+        }
+    }
+    @Transactional(isolation=Isolation.SERIALIZABLE)
+    public QuestionVO PostNewQuestion(QuestionVO vo) throws JsonProcessingException {
+        if(vo.getQuestionId()!=null)
+            throw HelperException.of(ExceptionType.PARAM_ERROR,"should POST a new question");
 
+        String questionId = null;
+        //我后悔le,我当时应该用uuid的,但是线上已经部署了,只能下次再改叻(如果有下次
+        for(int i=0;i<100;i++){
+            Date date=new Date();
+            Calendar calendar = Calendar.getInstance();
+            int year=calendar.get(Calendar.YEAR);String month=String.valueOf(1+calendar.get(Calendar.MONTH));String day=String.valueOf(calendar.get(Calendar.DATE));
+            if(month.length()<2){month="0"+month;}
+            if(day.length()<2){month="0"+month;}
+            String random=String.valueOf((int)(Math.random()*10000));
+            while (random.length()<4){
+                random="0"+random;
             }
-            return new Exception("unknown type:" + (String) q.get("type"));
-        }).collect(Collectors.toList());
-        return collect;
+            questionId=""+(year-2010)+(month)+day+(random);
+            try {
+                Map ret = (Map)restRequestUtil.sendGetRequest(tqUrl+questionId, new HashMap<>());
+            } catch (HttpClientErrorException e){
+                if(e.getStatusCode().equals(HttpStatus.NOT_FOUND)){
+                    break;
+                }
+            }
+            questionId=null;
+        }
+        if(questionId==null){throw HelperException.of(ExceptionType.ERROR,"暂时无法保存题目,请重试!");}
+
+        vo.setQuestionId(questionId);
+
+        BOK_TQ bok_tq=new BOK_TQ(vo);
+        restRequestUtil.sendPutRequest(tqUrl+questionId, bok_tq);
+
+        return vo;
+
+    }
+    @Transactional(isolation=Isolation.SERIALIZABLE)
+    public QuestionVO PutQuestion(QuestionVO vo) throws JsonProcessingException {
+        if(vo.getQuestionId()==null)
+            throw HelperException.of(ExceptionType.PARAM_ERROR,"should POST a present question");
+
+        String questionId=vo.getQuestionId();
+        try {
+            restRequestUtil.sendGetRequest(tqUrl+questionId, new HashMap<>());
+        } catch (HttpClientErrorException e){
+            if(e.getStatusCode().equals(HttpStatus.NOT_FOUND)){
+                throw HelperException.of(ExceptionType.PARAM_ERROR,"should POST a present question");
+            }
+        }
+
+        BOK_TQ bok_tq=new BOK_TQ(vo);
+        restRequestUtil.sendPutRequest(tqUrl+questionId, bok_tq);
+        return vo;
+    }
+    public void deleteQuestion(String questionId) {
+        try {
+            restRequestUtil.sendDeleteRequest(tqUrl+questionId);
+        } catch (JsonProcessingException e) {
+            throw HelperException.of(ExceptionType.ERROR,"请求BOK题库数据错误:"+e.getMessage());
+        }
+    }
+
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @Data
+    class BOK_TQ{
+        String id;
+        String type;
+        String answer;
+        String key_points;
+        String analysis;
+        String lastModified;
+        String stem;
+        Map options;
+        List tags;
+        List knowledgeId;
+
+        private BOK_TQ(QuestionVO 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;
+            }
+
+            this.key_points = null;
+            this.lastModified = null;
+            this.tags = null;
+            this.knowledgeId = null;
+        }
     }
 }

+ 11 - 3
src/main/java/nju/seec/helper/util/RestRequestUtil.java

@@ -10,16 +10,24 @@ import java.util.Map;
 @Component
 public class RestRequestUtil {
 
-    public List sendPostRequest(String url, Map<String, String> params) throws JsonProcessingException {
+    public List sendPostRequest(String url, Object params) throws JsonProcessingException {
         RestTemplate client = new RestTemplate();
         HttpHeaders headers = new HttpHeaders();
         HttpMethod method = HttpMethod.POST;
         headers.setContentType(MediaType.APPLICATION_JSON);
-        HttpEntity<Map> requestEntity = new HttpEntity<>(params, headers);
+        HttpEntity<Object> requestEntity = new HttpEntity<>(params, headers);
         ResponseEntity ret = client.postForEntity(url, requestEntity, List.class);
         return (List) ret.getBody();
     }
-    public Object sendPostGet(String url, Map<String, String> urlParams) throws JsonProcessingException {
+    public void sendPutRequest(String url, Object params) throws JsonProcessingException {
+        RestTemplate client = new RestTemplate();
+        client.put(url, params);
+    }
+    public void sendDeleteRequest(String url) throws JsonProcessingException {
+        RestTemplate client = new RestTemplate();
+        client.delete(url);
+    }
+    public Object sendGetRequest(String url, Map<String, String> urlParams) throws JsonProcessingException {
         RestTemplate client = new RestTemplate();
         HttpHeaders headers = new HttpHeaders();
         HttpMethod method = HttpMethod.GET;

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

@@ -1,5 +1,6 @@
 package nju.seec.helper.util.enums;
 
+import lombok.AllArgsConstructor;
 import lombok.Getter;
 import org.springframework.http.HttpStatus;
 
@@ -17,4 +18,11 @@ public enum ExceptionType {
     ExceptionType(HttpStatus status) {
         this.status = status;
     }
+    public static ExceptionType valueOf(HttpStatus status){
+        for(ExceptionType exceptionType:ExceptionType.values()){
+            if(exceptionType.getStatus().equals(status))
+                return exceptionType;
+        }
+        return ExceptionType.ERROR;
+    }
 }

+ 7 - 1
src/main/java/nju/seec/helper/vo/quiz/ChoiceQuestionVO.java

@@ -28,9 +28,15 @@ public class ChoiceQuestionVO extends QuestionVO{
 
     @Override
     public void exAddAnswer(QuizStudentAnswer answer) {
-        this.answer=answer.getStudentAnswer();
+        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());
+    }
 }
 //kind: "CHOICE";
 //  options: {

+ 4 - 0
src/main/java/nju/seec/helper/vo/quiz/QuestionVO.java

@@ -10,6 +10,9 @@ import nju.seec.helper.entity.QuizStudentAnswer;
 import nju.seec.helper.util.enums.QuestionKindState;
 import nju.seec.helper.vo.CourseVO;
 import org.springframework.beans.BeanUtils;
+
+import java.util.Map;
+
 /**
  * @author XuShengTao
  */
@@ -25,6 +28,7 @@ public abstract class QuestionVO {
     @JsonProperty(value = "stem")
     private String stem;
     public abstract void exAddAnswer(QuizStudentAnswer answer);
+    public abstract boolean checkAnswer(Object answer);
 }
 //QuestionSerializer
 //问题的详细定义

+ 29 - 0
src/main/java/nju/seec/helper/vo/quiz/QuizResultVO.java

@@ -0,0 +1,29 @@
+package nju.seec.helper.vo.quiz;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+import lombok.Data;
+import nju.seec.helper.entity.QuizStudentAnswer;
+import nju.seec.helper.entity.StudentScore;
+import nju.seec.helper.entity.User;
+import nju.seec.helper.vo.UserVO;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Data
+public class QuizResultVO {
+    @JsonUnwrapped
+    UserVO student;
+    Integer score;
+    Map<String,Boolean> detail;
+
+    public QuizResultVO(StudentScore studentScore, User student, List<QuizStudentAnswer> list) {
+        this.student=new UserVO(student);
+        this.score=studentScore.getScore();
+        this.detail=list.stream().collect(
+                Collectors.toMap(QuizStudentAnswer::getQuestionId,QuizStudentAnswer::getPass));
+    }
+}

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

@@ -29,4 +29,9 @@ public class TrueOrFalseQuestionVO extends QuestionVO{
         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());
+    }
 }