Просмотр исходного кода

feat: 增加获取推荐题目接口

ChenSiTong 6 лет назад
Родитель
Сommit
e873bd4744

+ 79 - 79
src/main/java/nju/seec/helper/aspect/record/ExerciseRecordAspect.java

@@ -1,79 +1,79 @@
-package nju.seec.helper.aspect.record;
-
-import nju.seec.helper.sys.bok.api.RecordApi;
-import nju.seec.helper.sys.dataanalysis.api.ExerciseApi;
-import nju.seec.helper.aspect.auth.LoginUser;
-import nju.seec.helper.dto.quiz.QuizStudentAnswerDTO;
-import nju.seec.helper.vo.quiz.QuizStudentAnswerVO;
-import org.aspectj.lang.annotation.AfterReturning;
-import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Pointcut;
-import org.springframework.stereotype.Component;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-/**
- * @author cst
- */
-@Aspect
-@Component
-public class ExerciseRecordAspect {
-    private final ExerciseApi exerciseApi;
-    private final RecordApi recordApi;
-
-    public ExerciseRecordAspect(ExerciseApi exerciseApi, RecordApi recordApi) {
-        this.exerciseApi = exerciseApi;
-        this.recordApi = recordApi;
-    }
-
-    @Pointcut(value = "@annotation(exerciseRecord) && args(user,..,quizStudentAnswerDTO)", argNames = "exerciseRecord,user,quizStudentAnswerDTO")
-    public void pointCut(ExerciseRecord exerciseRecord, LoginUser user, QuizStudentAnswerDTO quizStudentAnswerDTO) {
-
-    }
-
-    @AfterReturning(value = "pointCut(exerciseRecord,user,quizStudentAnswerDTO)", returning = "quizStudentAnswerVO", argNames = "exerciseRecord,user,quizStudentAnswerDTO,quizStudentAnswerVO")
-    public void reportRecordToData(ExerciseRecord exerciseRecord, LoginUser user, QuizStudentAnswerDTO quizStudentAnswerDTO, QuizStudentAnswerVO quizStudentAnswerVO) {
-        String userId = String.valueOf(user.getPid());
-        Map<String, QuizStudentAnswerVO.QuestionStudentAnswerInfo> questionStudentAnswerInfoMap = quizStudentAnswerVO.getAnswers();
-        exerciseApi.reportExercise(
-                quizStudentAnswerDTO
-                        .getRecords()
-                        .entrySet()
-                        .stream()
-                        .map(answerRecordDTOEntry -> {
-                            String questionId = answerRecordDTOEntry.getKey();
-                            QuizStudentAnswerDTO.AnswerRecordDTO answerRecordDTO = answerRecordDTOEntry.getValue();
-                            QuizStudentAnswerVO.QuestionStudentAnswerInfo studentAnswerInfo = questionStudentAnswerInfoMap.get(questionId);
-                            return studentAnswerInfo != null
-                                    ? exerciseApi.createExerciseRecordDTO(userId, questionId, answerRecordDTO.getStartAt(), answerRecordDTO.getEndAt(), studentAnswerInfo.getCorrect())
-                                    : null;
-                        })
-                        .filter(Objects::nonNull)
-                        .collect(Collectors.toList())
-        );
-    }
-
-    @AfterReturning(value = "pointCut(exerciseRecord,user,quizStudentAnswerDTO)", returning = "quizStudentAnswerVO", argNames = "exerciseRecord,user,quizStudentAnswerDTO,quizStudentAnswerVO")
-    public void reportRecordToBok(ExerciseRecord exerciseRecord, LoginUser user, QuizStudentAnswerDTO quizStudentAnswerDTO, QuizStudentAnswerVO quizStudentAnswerVO) {
-        String userId = String.valueOf(user.getPid());
-        Map<String, QuizStudentAnswerVO.QuestionStudentAnswerInfo> questionStudentAnswerInfoMap = quizStudentAnswerVO.getAnswers();
-        recordApi.reportUserRecord(
-                quizStudentAnswerDTO
-                        .getRecords()
-                        .entrySet()
-                        .stream()
-                        .map(answerRecordDTOEntry -> {
-                            String questionId = answerRecordDTOEntry.getKey();
-                            QuizStudentAnswerDTO.AnswerRecordDTO answerRecordDTO = answerRecordDTOEntry.getValue();
-                            QuizStudentAnswerVO.QuestionStudentAnswerInfo studentAnswerInfo = questionStudentAnswerInfoMap.get(questionId);
-                            return studentAnswerInfo != null
-                                    ? recordApi.createRecordDTO(userId, questionId, studentAnswerInfo.getAnswer(), studentAnswerInfo.getCorrect(), answerRecordDTO.getStartAt(), answerRecordDTO.getEndAt())
-                                    : null;
-                        })
-                        .filter(Objects::nonNull)
-                        .collect(Collectors.toList())
-        );
-    }
-}
+//package nju.seec.helper.aspect.record;
+//
+//import nju.seec.helper.sys.bok.api.RecordApi;
+//import nju.seec.helper.sys.dataanalysis.api.ExerciseApi;
+//import nju.seec.helper.aspect.auth.LoginUser;
+//import nju.seec.helper.dto.quiz.StudentAnswerDTO;
+//import nju.seec.helper.vo.quiz.QuizStudentAnswerVO;
+//import org.aspectj.lang.annotation.AfterReturning;
+//import org.aspectj.lang.annotation.Aspect;
+//import org.aspectj.lang.annotation.Pointcut;
+//import org.springframework.stereotype.Component;
+//
+//import java.util.Map;
+//import java.util.Objects;
+//import java.util.stream.Collectors;
+//
+///**
+// * @author cst
+// */
+//@Aspect
+//@Component
+//public class ExerciseRecordAspect {
+//    private final ExerciseApi exerciseApi;
+//    private final RecordApi recordApi;
+//
+//    public ExerciseRecordAspect(ExerciseApi exerciseApi, RecordApi recordApi) {
+//        this.exerciseApi = exerciseApi;
+//        this.recordApi = recordApi;
+//    }
+//
+//    @Pointcut(value = "@annotation(exerciseRecord) && args(user,..,studentAnswerDTO)", argNames = "exerciseRecord,user,quizStudentAnswerDTO")
+//    public void pointCut(ExerciseRecord exerciseRecord, LoginUser user, StudentAnswerDTO studentAnswerDTO) {
+//
+//    }
+//
+//    @AfterReturning(value = "pointCut(exerciseRecord,user,studentAnswerDTO)", returning = "quizStudentAnswerVO", argNames = "exerciseRecord,user,quizStudentAnswerDTO,quizStudentAnswerVO")
+//    public void reportRecordToData(ExerciseRecord exerciseRecord, LoginUser user, StudentAnswerDTO studentAnswerDTO, QuizStudentAnswerVO quizStudentAnswerVO) {
+//        String userId = String.valueOf(user.getPid());
+//        Map<String, QuizStudentAnswerVO.QuestionStudentAnswerInfo> questionStudentAnswerInfoMap = quizStudentAnswerVO.getAnswers();
+//        exerciseApi.reportExercise(
+//                studentAnswerDTO
+//                        .getRecords()
+//                        .entrySet()
+//                        .stream()
+//                        .map(answerRecordDTOEntry -> {
+//                            String questionId = answerRecordDTOEntry.getKey();
+//                            StudentAnswerDTO.AnswerRecordDTO answerRecordDTO = answerRecordDTOEntry.getValue();
+//                            QuizStudentAnswerVO.QuestionStudentAnswerInfo studentAnswerInfo = questionStudentAnswerInfoMap.get(questionId);
+//                            return studentAnswerInfo != null
+//                                    ? exerciseApi.createExerciseRecordDTO(userId, questionId, answerRecordDTO.getStartAt(), answerRecordDTO.getEndAt(), studentAnswerInfo.getCorrect())
+//                                    : null;
+//                        })
+//                        .filter(Objects::nonNull)
+//                        .collect(Collectors.toList())
+//        );
+//    }
+//
+//    @AfterReturning(value = "pointCut(exerciseRecord,user,studentAnswerDTO)", returning = "quizStudentAnswerVO", argNames = "exerciseRecord,user,quizStudentAnswerDTO,quizStudentAnswerVO")
+//    public void reportRecordToBok(ExerciseRecord exerciseRecord, LoginUser user, StudentAnswerDTO studentAnswerDTO, QuizStudentAnswerVO quizStudentAnswerVO) {
+//        String userId = String.valueOf(user.getPid());
+//        Map<String, QuizStudentAnswerVO.QuestionStudentAnswerInfo> questionStudentAnswerInfoMap = quizStudentAnswerVO.getAnswers();
+//        recordApi.reportUserRecord(
+//                studentAnswerDTO
+//                        .getRecords()
+//                        .entrySet()
+//                        .stream()
+//                        .map(answerRecordDTOEntry -> {
+//                            String questionId = answerRecordDTOEntry.getKey();
+//                            StudentAnswerDTO.AnswerRecordDTO answerRecordDTO = answerRecordDTOEntry.getValue();
+//                            QuizStudentAnswerVO.QuestionStudentAnswerInfo studentAnswerInfo = questionStudentAnswerInfoMap.get(questionId);
+//                            return studentAnswerInfo != null
+//                                    ? recordApi.createRecordDTO(userId, questionId, studentAnswerInfo.getAnswer(), studentAnswerInfo.getCorrect(), answerRecordDTO.getStartAt(), answerRecordDTO.getEndAt())
+//                                    : null;
+//                        })
+//                        .filter(Objects::nonNull)
+//                        .collect(Collectors.toList())
+//        );
+//    }
+//}

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

@@ -1,16 +1,17 @@
 package nju.seec.helper.controller;
 
-import nju.seec.helper.sys.bok.dto.CollectDTO;
-import nju.seec.helper.sys.bok.dto.RateDTO;
-import nju.seec.helper.sys.bok.vo.InteractiveVO;
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.aspect.auth.LoginUser;
 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.question.BaseQuestionDTO;
+import nju.seec.helper.dto.quiz.StudentAnswerDTO;
 import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.QuestionService;
+import nju.seec.helper.sys.bok.dto.CollectDTO;
+import nju.seec.helper.sys.bok.dto.RateDTO;
+import nju.seec.helper.sys.bok.vo.InteractiveVO;
 import nju.seec.helper.vo.question.BaseQuestionVO;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.web.PageableDefault;
@@ -55,7 +56,7 @@ public class QuestionController {
     @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获得测试题目")
     @GetMapping("/quiz/{quizId}")
     public List<BaseQuestionVO> getQuestionsByQuiz(LoginUser user, @PathVariable Long quizId) {
-        return questionService.getQuestionsByQuiz(user, quizId);
+        return questionService.getQuestionsByQuizId(user, quizId);
     }
 
     /**
@@ -162,4 +163,16 @@ public class QuestionController {
     public List<InteractiveVO> getInteractiveList(LoginUser user, String questionId) {
         return questionService.getInteractiveList(user, questionId);
     }
+
+    @Auth(roles = UserRole.STUDENT, message = "取得推荐题目")
+    @GetMapping("/recommendation")
+    public List<BaseQuestionVO> getRecommendationQuestions(LoginUser user, @RequestParam(required = false) List<String> knowledgeId, Integer questionNum) {
+        return questionService.getRecommendationQuestions(user, knowledgeId, questionNum);
+    }
+
+    @Auth(roles = UserRole.STUDENT, message = "提交做题情况")
+    @PostMapping("/record")
+    public void submitAnswerRecord(LoginUser user, @Validated @RequestBody StudentAnswerDTO studentAnswerDTO) {
+        questionService.record(user, studentAnswerDTO);
+    }
 }

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

@@ -10,7 +10,7 @@ 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.quiz.StudentAnswerDTO;
 import nju.seec.helper.enums.QuizState;
 import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.QuizService;
@@ -122,8 +122,8 @@ public class QuizController {
     @Event(type = EventType.QUIZ, action = EventAction.COMPLETE)
     @Auth(roles = {UserRole.STUDENT}, message = "提交作答")
     @PostMapping("/{quizId}/student-answer")
-    public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, @PathVariable Long quizId, @Validated @RequestBody QuizStudentAnswerDTO quizStudentAnswerDTO) {
-        return quizStudentAnswerService.submitQuizStudentAnswer(user, quizId, quizStudentAnswerDTO);
+    public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, @PathVariable Long quizId, @Validated @RequestBody StudentAnswerDTO studentAnswerDTO) {
+        return quizStudentAnswerService.submitQuizStudentAnswer(user, quizId, studentAnswerDTO);
     }
 
     /**

+ 38 - 0
src/main/java/nju/seec/helper/dto/quiz/StudentAnswerDTO.java

@@ -0,0 +1,38 @@
+package nju.seec.helper.dto.quiz;
+
+import lombok.Data;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Past;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@Data
+public class StudentAnswerDTO implements Serializable {
+    private static final long serialVersionUID = -5846899734313166305L;
+//    @NotEmpty(message = "回答不能为空")
+//    private Map<String, Object> answers;
+
+    @NotNull(message = "缺少做题记录")
+    @Valid
+    private Map<String, AnswerRecordDTO> records;
+
+    @Data
+    public static class AnswerRecordDTO {
+        @NotNull(message = "缺少答案")
+        private Object answer;
+
+        @NotNull(message = "缺少做题开始时间")
+        @Past(message = "开始时间错误")
+        private LocalDateTime startAt;
+
+        @NotNull(message = "缺少做题结束时间")
+        @Past(message = "结束时间错误")
+        private LocalDateTime endAt;
+    }
+}

+ 25 - 7
src/main/java/nju/seec/helper/service/QuestionService.java

@@ -1,11 +1,11 @@
 package nju.seec.helper.service;
 
+import nju.seec.helper.aspect.auth.LoginUser;
+import nju.seec.helper.dto.question.BaseQuestionDTO;
+import nju.seec.helper.dto.quiz.StudentAnswerDTO;
 import nju.seec.helper.sys.bok.dto.CollectDTO;
 import nju.seec.helper.sys.bok.dto.RateDTO;
 import nju.seec.helper.sys.bok.vo.InteractiveVO;
-import nju.seec.helper.aspect.auth.LoginUser;
-import nju.seec.helper.dto.question.BaseQuestionDTO;
-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;
@@ -37,15 +37,15 @@ public interface QuestionService {
      * @param quizId
      * @return
      */
-    List<BaseQuestionVO> getQuestionsByQuiz(LoginUser user, Long quizId);
+    List<BaseQuestionVO> getQuestionsByQuizId(LoginUser user, Long quizId);
 
     /**
      * 根据测试获取题目(无需权限检验)
      *
-     * @param quiz
-     * @return
+     *
+     * @param questionIds@return
      */
-    List<BaseQuestionVO> getQuestionsByQuiz(Quiz quiz);
+    List<BaseQuestionVO> getQuestionsByIds(List<String> questionIds);
 
     /**
      * 获取某一题目
@@ -146,4 +146,22 @@ public interface QuestionService {
      * @return
      */
     List<InteractiveVO> getInteractiveList(LoginUser user, String questionId);
+
+    /**
+     * 取得推荐题目
+     *
+     * @param user
+     * @param knowledgeId
+     * @param questionNum
+     * @return
+     */
+    List<BaseQuestionVO> getRecommendationQuestions(LoginUser user, List<String> knowledgeId, Integer questionNum);
+
+    /**
+     * 记录做题情况
+     *
+     * @param user
+     * @param studentAnswerDTO
+     */
+    void record(LoginUser user, StudentAnswerDTO studentAnswerDTO);
 }

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.service;
 
 import nju.seec.helper.aspect.auth.LoginUser;
-import nju.seec.helper.dto.quiz.QuizStudentAnswerDTO;
+import nju.seec.helper.dto.quiz.StudentAnswerDTO;
 import nju.seec.helper.vo.quiz.QuizStudentAnswerStatisticVO;
 import nju.seec.helper.vo.quiz.QuizStudentAnswerVO;
 import org.springframework.data.domain.Page;
@@ -16,10 +16,10 @@ public interface QuizStudentAnswerService {
      *
      * @param user
      * @param quizId
-     * @param quizStudentAnswerDTO
+     * @param studentAnswerDTO
      * @return
      */
-    QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, Long quizId, QuizStudentAnswerDTO quizStudentAnswerDTO);
+    QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, Long quizId, StudentAnswerDTO studentAnswerDTO);
 
     /**
      * 老师查看学生答案列表

+ 60 - 15
src/main/java/nju/seec/helper/service/impl/QuestionServiceImpl.java

@@ -1,21 +1,13 @@
 package nju.seec.helper.service.impl;
 
 import cn.hutool.core.util.IdUtil;
-import nju.seec.helper.sys.bok.api.QuestionApi;
-import nju.seec.helper.sys.bok.api.QuestionQueryApi;
-import nju.seec.helper.sys.bok.api.RecordApi;
-import nju.seec.helper.sys.bok.api.UserInteractiveApi;
-import nju.seec.helper.sys.bok.dto.CollectDTO;
-import nju.seec.helper.sys.bok.dto.RateDTO;
-import nju.seec.helper.sys.bok.vo.InteractiveVO;
-import nju.seec.helper.sys.bok.vo.QuestionVO;
-import nju.seec.helper.sys.bok.vo.RecordQuestionVO;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dao.ChooseDAO;
 import nju.seec.helper.dao.QuestionRecordDAO;
 import nju.seec.helper.dao.QuizDAO;
 import nju.seec.helper.dao.UserDAO;
 import nju.seec.helper.dto.question.BaseQuestionDTO;
+import nju.seec.helper.dto.quiz.StudentAnswerDTO;
 import nju.seec.helper.entity.QuestionRecord;
 import nju.seec.helper.entity.Quiz;
 import nju.seec.helper.enums.ExceptionType;
@@ -23,6 +15,17 @@ import nju.seec.helper.enums.QuizState;
 import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.QuestionService;
+import nju.seec.helper.sys.bok.api.QuestionApi;
+import nju.seec.helper.sys.bok.api.QuestionQueryApi;
+import nju.seec.helper.sys.bok.api.RecordApi;
+import nju.seec.helper.sys.bok.api.UserInteractiveApi;
+import nju.seec.helper.sys.bok.dto.CollectDTO;
+import nju.seec.helper.sys.bok.dto.RateDTO;
+import nju.seec.helper.sys.bok.vo.InteractiveVO;
+import nju.seec.helper.sys.bok.vo.QuestionVO;
+import nju.seec.helper.sys.bok.vo.RecordQuestionVO;
+import nju.seec.helper.sys.dataanalysis.api.ExerciseApi;
+import nju.seec.helper.sys.ml.api.RecommendationApi;
 import nju.seec.helper.vo.question.BaseQuestionVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
@@ -52,6 +55,8 @@ public class QuestionServiceImpl implements QuestionService {
     private final QuestionQueryApi questionQueryApi;
     private final UserInteractiveApi userInteractiveApi;
     private final RecordApi recordApi;
+    private final RecommendationApi recommendationApi;
+    private final ExerciseApi exerciseApi;
 
     @Autowired
     public QuestionServiceImpl(QuizDAO quizDAO
@@ -62,6 +67,8 @@ public class QuestionServiceImpl implements QuestionService {
             , QuestionQueryApi questionQueryApi
             , UserInteractiveApi userInteractiveApi
             , RecordApi recordApi
+            , RecommendationApi recommendationApi
+            , ExerciseApi exerciseApi
     ) {
         this.quizDAO = quizDAO;
         this.userDAO = userDAO;
@@ -71,6 +78,8 @@ public class QuestionServiceImpl implements QuestionService {
         this.questionQueryApi = questionQueryApi;
         this.userInteractiveApi = userInteractiveApi;
         this.recordApi = recordApi;
+        this.recommendationApi = recommendationApi;
+        this.exerciseApi = exerciseApi;
     }
 
     @Override
@@ -80,19 +89,16 @@ public class QuestionServiceImpl implements QuestionService {
 
     @Transactional(readOnly = true)
     @Override
-    public List<BaseQuestionVO> getQuestionsByQuiz(LoginUser user, Long quizId) {
+    public List<BaseQuestionVO> getQuestionsByQuizId(LoginUser user, Long quizId) {
         Quiz quiz = quizDAO.findQuizById(quizId);
-        List<String> questionIds = quiz.getQuestions();
-        return questionQueryApi.getQuestionListById(questionIds)
+        return questionQueryApi.getQuestionListById(quiz.getQuestions())
                 .stream()
                 .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, quiz.getState() == QuizState.CLOSED || user.getRole() == UserRole.TEACHER))
                 .collect(Collectors.toList());
     }
 
-    @Transactional(readOnly = true)
     @Override
-    public List<BaseQuestionVO> getQuestionsByQuiz(Quiz quiz) {
-        List<String> questionIds = quiz.getQuestions();
+    public List<BaseQuestionVO> getQuestionsByIds(List<String> questionIds) {
         return questionQueryApi.getQuestionListById(questionIds)
                 .stream()
                 .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true))
@@ -188,6 +194,45 @@ public class QuestionServiceImpl implements QuestionService {
         return userInteractiveApi.getInteractive(String.valueOf(user.getPid()), questionId);
     }
 
+    @Override
+    public List<BaseQuestionVO> getRecommendationQuestions(LoginUser user, List<String> knowledgeId, Integer questionNum) {
+        return questionQueryApi.getQuestionListById(recommendationApi.getRecommendResult(String.valueOf(user.getPid()), knowledgeId, questionNum))
+                .stream()
+                .map(questionVO -> BaseQuestionVO.convertBokQuestionToVO(questionVO, false))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public void record(LoginUser user, StudentAnswerDTO studentAnswerDTO) {
+        System.out.println(studentAnswerDTO);
+//        // 获取已有信息
+//        String userId = String.valueOf(user.getPid());
+//
+//        Map<String, StudentAnswerDTO.AnswerRecordDTO> recordMap = studentAnswerDTO.getRecords();
+//        List<BaseQuestionVO> baseQuestionVOList = questionQueryApi.getQuestionListById(Lists.newArrayList(recordMap.keySet()))
+//                .stream()
+//                .map(questionVO -> BaseQuestionVO.convertBokQuestionToVO(questionVO, true))
+//                .collect(Collectors.toList());
+//
+//        // 创建两个列表
+//        int size = recordMap.size();
+//        List<ExerciseRecord> exerciseRecords = Lists.newArrayListWithExpectedSize(size);
+//        List<RecordDTO> records = Lists.newArrayListWithExpectedSize(size);
+//
+//        // 填充列表
+//        baseQuestionVOList.forEach(baseQuestionVO -> {
+//            String questionId = baseQuestionVO.getId();
+//            StudentAnswerDTO.AnswerRecordDTO answerRecord = recordMap.get(questionId);
+//            Object answer = answerRecord.getAnswer();
+//            Boolean pass = baseQuestionVO.pass(answer);
+//            exerciseRecords.add(exerciseApi.createExerciseRecordDTO(userId, questionId, answerRecord.getStartAt(), answerRecord.getEndAt(), pass));
+//            records.add(recordApi.createRecordDTO(userId, questionId, answer, pass, answerRecord.getStartAt(), answerRecord.getEndAt()));
+//        });
+//
+//        exerciseApi.reportExercise(exerciseRecords);
+//        recordApi.reportUserRecord(records);
+    }
+
     private static final Set<QuizState> HIDE_QUESTION_ANSWER_STATES = Collections.singleton(QuizState.ONGOING);
 
     private Page<BaseQuestionVO> convert(LoginUser user, Page<QuestionVO> questionVOList) {

+ 11 - 9
src/main/java/nju/seec/helper/service/impl/QuizStudentAnswerServiceImpl.java

@@ -5,7 +5,7 @@ import nju.seec.helper.aspect.auth.LoginUser;
 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.quiz.StudentAnswerDTO;
 import nju.seec.helper.entity.Quiz;
 import nju.seec.helper.entity.QuizStudentAnswer;
 import nju.seec.helper.entity.User;
@@ -26,6 +26,7 @@ import java.math.RoundingMode;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 /**
  * @author cst
@@ -47,7 +48,7 @@ public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
 
     @Transactional(rollbackFor = Exception.class)
     @Override
-    public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, Long quizId, QuizStudentAnswerDTO quizStudentAnswerDTO) {
+    public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, Long quizId, StudentAnswerDTO studentAnswerDTO) {
         Quiz quiz = quizDAO.findQuizById(quizId);
 
         switch (quiz.getState()) {
@@ -68,14 +69,14 @@ public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
         }
 
         // 计算得分
-        List<BaseQuestionVO> questionVOList = questionService.getQuestionsByQuiz(quiz);
+        List<BaseQuestionVO> questionVOList = questionService.getQuestionsByIds(quiz.getQuestions());
 
         AtomicInteger correct = new AtomicInteger();
 
-        Map<String, Object> answers = quizStudentAnswerDTO.getAnswers();
+        Map<String, StudentAnswerDTO.AnswerRecordDTO> records = studentAnswerDTO.getRecords();
         questionVOList.forEach(baseQuestionVO -> {
             Object answer;
-            if ((answer = answers.get(baseQuestionVO.getId())) != null && baseQuestionVO.pass(answer)) {
+            if ((answer = records.get(baseQuestionVO.getId()).getAnswer()) != null && baseQuestionVO.pass(answer)) {
                 correct.getAndIncrement();
             }
         });
@@ -85,18 +86,19 @@ public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
 
         quizStudentAnswer.setQuiz(quiz)
                 .setStudent(student)
-                .setAnswers(quizStudentAnswerDTO.getAnswers())
                 .setSubmitNumber(quizStudentAnswer.getSubmitNumber() + 1)
-                .setAnswers(answers)
+                .setAnswers(records.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, recordEntry -> recordEntry.getValue().getAnswer())))
                 .setScore(score);
 
+        questionService.record(user, studentAnswerDTO);
+
         return new QuizStudentAnswerVO(quizStudentAnswerDAO.save(quizStudentAnswer), questionVOList);
     }
 
     @Transactional(readOnly = true)
     @Override
     public Page<QuizStudentAnswerVO> getQuizStudentAnswers(LoginUser user, Long quizId, String key, Pageable pageable) {
-        return quizStudentAnswerDAO.findByQuizIdAndKey(quizId, key, pageable).map(quizStudentAnswer -> new QuizStudentAnswerVO(quizStudentAnswer, questionService.getQuestionsByQuiz(quizDAO.findQuizById(quizId))));
+        return quizStudentAnswerDAO.findByQuizIdAndKey(quizId, key, pageable).map(quizStudentAnswer -> new QuizStudentAnswerVO(quizStudentAnswer, questionService.getQuestionsByIds(quizDAO.findQuizById(quizId).getQuestions())));
     }
 
     @Transactional(readOnly = true)
@@ -104,7 +106,7 @@ public class QuizStudentAnswerServiceImpl implements QuizStudentAnswerService {
     public QuizStudentAnswerVO getQuizStudentAnswer(LoginUser user, Long quizId) {
         return quizStudentAnswerDAO
                 .findByQuizAndStudent(quizDAO.findQuizById(quizId), userDAO.findUserById(user.getId()))
-                .map(quizStudentAnswer -> new QuizStudentAnswerVO(quizStudentAnswer, questionService.getQuestionsByQuiz(quizDAO.findQuizById(quizId))))
+                .map(quizStudentAnswer -> new QuizStudentAnswerVO(quizStudentAnswer, questionService.getQuestionsByIds(quizDAO.findQuizById(quizId).getQuestions())))
                 .orElse(null);
     }
 

+ 32 - 0
src/main/java/nju/seec/helper/sys/ml/api/RecommendationApi.java

@@ -0,0 +1,32 @@
+package nju.seec.helper.sys.ml.api;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.Data;
+import nju.seec.helper.sys.ml.vo.RecommendResultVO;
+import nju.seec.helper.util.RestRequestUtil;
+import nju.seec.helper.util.StringUtils;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@Component
+@Data
+@ConfigurationProperties("ml.recommendation")
+public class RecommendationApi {
+    private String getUrl;
+    private final RestRequestUtil restRequestUtil;
+
+    public List<String> getRecommendResult(String userId, List<String> knowledgeId, Integer questionNum) {
+        Map<String, ?> uriVariables = ImmutableMap.<String, Object>builder()
+                .put("userId", userId)
+                .put("knowledgeId", knowledgeId == null ? "" : StringUtils.getParamValuesString(knowledgeId, "knowledgeId"))
+                .put("questionNum", questionNum)
+                .build();
+        return restRequestUtil.sendGetRequest(getUrl, List.class, uriVariables);
+    }
+}

+ 15 - 0
src/main/java/nju/seec/helper/sys/ml/vo/RecommendResultVO.java

@@ -0,0 +1,15 @@
+package nju.seec.helper.sys.ml.vo;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author Kunduin
+ */
+@Data
+@Builder
+public class RecommendResultVO {
+    List<String> questionIdList;
+}

+ 11 - 2
src/main/java/nju/seec/helper/util/StringUtils.java

@@ -4,6 +4,8 @@ import cn.hutool.core.util.StrUtil;
 import lombok.experimental.UtilityClass;
 import org.springframework.data.domain.Sort;
 
+import java.util.Collection;
+
 /**
  * @author cst
  */
@@ -15,8 +17,15 @@ public class StringUtils {
 
     public String getSortString(Sort sort) {
         return sort.stream()
-                .map(order -> StrUtil.concat(true, "sort=", order.getProperty(), ",", order.getDirection().toString()))
-                .reduce((o1, o2) -> StrUtil.join("&", o1, o2))
+                .map(order -> StrUtil.concat(true, order.getProperty(), ",", order.getDirection().toString()))
+                .reduce((o1, o2) -> StrUtil.join("&sort=", o1, o2))
+                .orElse("");
+    }
+
+    public String getParamValuesString(Collection<?> values, String param) {
+        return values.stream()
+                .map(Object::toString)
+                .reduce((o1, o2) -> StrUtil.join(param, o1, o2))
                 .orElse("");
     }
 }

+ 8 - 3
src/main/resources/application-dev.yml

@@ -62,7 +62,7 @@ bok:
   knowledge-cache-name: ${helper.sys-name}_bok_knowledge
   question:
     url: ${bok.api-url}/question
-    search-url: ${bok.question.url}?stem={stem}&tag={tag}&knowledgeId={knowledgeId}&page={page}&size={size}&{sort}
+    search-url: ${bok.question.url}?stem={stem}&tag={tag}&knowledgeId={knowledgeId}&page={page}&size={size}&sort={sort}
   question-query:
     url: ${bok.api-url}/question-query
     by-id-url: ${bok.question-query.url}/id?id={id}
@@ -72,11 +72,11 @@ bok:
     url: ${bok.api-url}/user
     rate-url: ${bok.user.url}/{userId}/question/{questionId}/rate
     collection-url: ${bok.user.url}/{userId}/question/{questionId}/collection
-    question-url: ${bok.user.url}/{userId}/question?collection={collection}&rate={rate}&page={page}&size={size}&{sort}
+    question-url: ${bok.user.url}/{userId}/question?collection={collection}&rate={rate}&page={page}&size={size}&sort={sort}
     interactive-url: ${bok.user.url}/{userId}/questionInList?questionId={questionId}
   record:
     url: ${bok.api-url}/record
-    question-url: ${bok.record.url}/{userId}/question?page={page}&size={size}&{sort}
+    question-url: ${bok.record.url}/{userId}/question?page={page}&size={size}&sort={sort}
 data-analysis:
   api-url: http://dataanalysis.seecoder.cn/api
   event:
@@ -85,6 +85,11 @@ data-analysis:
   exercise:
     url: ${data-analysis.api-url}/exercise
     report-url: ${data-analysis.exercise.url}/report
+ml:
+  api-url: http://ml.seec.seecoder.cn/api
+  recommendation:
+    url: ${ml.api-url}/recommendation
+    get-url: ${ml.recommendation.url}?userId={userId}&{knowledgeId}&questionNum={questionNum}
 jwt:
   secret: SEEC-1919810-OIDC-114514
 # 自定义数据