Forráskód Böngészése

feat: 增加取得与题目交互信息的接口

ChenSiTong 6 éve
szülő
commit
978bfa8d90
22 módosított fájl, 168 hozzáadás és 92 törlés
  1. 1 0
      pom.xml
  2. 2 0
      src/main/java/nju/seec/helper/HelperApplication.java
  3. 1 2
      src/main/java/nju/seec/helper/api/bok/QuestionApi.java
  4. 3 8
      src/main/java/nju/seec/helper/api/bok/QuestionQueryApi.java
  5. 4 2
      src/main/java/nju/seec/helper/api/bok/RecordApi.java
  6. 16 3
      src/main/java/nju/seec/helper/api/bok/UserInteractiveApi.java
  7. 20 0
      src/main/java/nju/seec/helper/api/bok/vo/InteractiveVO.java
  8. 3 1
      src/main/java/nju/seec/helper/api/dataanalysis/EventApi.java
  9. 2 0
      src/main/java/nju/seec/helper/api/dataanalysis/ExerciseApi.java
  10. 0 2
      src/main/java/nju/seec/helper/aspect/event/EventAspect.java
  11. 0 2
      src/main/java/nju/seec/helper/aspect/message/MessageAspect.java
  12. 48 24
      src/main/java/nju/seec/helper/aspect/record/ExerciseRecordAspect.java
  13. 1 1
      src/main/java/nju/seec/helper/controller/CommentController.java
  14. 9 15
      src/main/java/nju/seec/helper/controller/CommentWebsocket.java
  15. 18 6
      src/main/java/nju/seec/helper/controller/QuestionController.java
  16. 0 1
      src/main/java/nju/seec/helper/entity/User.java
  17. 11 1
      src/main/java/nju/seec/helper/service/QuestionService.java
  18. 3 1
      src/main/java/nju/seec/helper/service/impl/MessageServiceImpl.java
  19. 7 1
      src/main/java/nju/seec/helper/service/impl/QuestionServiceImpl.java
  20. 0 1
      src/main/java/nju/seec/helper/util/cache/CaffeineCacheUtils.java
  21. 17 14
      src/main/resources/application-deploy.yml
  22. 2 7
      src/main/resources/application-dev.yml

+ 1 - 0
pom.xml

@@ -63,6 +63,7 @@
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-websocket</artifactId>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>

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

@@ -5,11 +5,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
 import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.transaction.annotation.EnableTransactionManagement;
 
 /**
  * @author cst
  */
+@EnableScheduling
 @EnableCaching
 @EnableTransactionManagement
 @EnableAsync

+ 1 - 2
src/main/java/nju/seec/helper/api/bok/QuestionApi.java

@@ -45,9 +45,8 @@ public class QuestionApi {
         this.cacheUtils = cacheUtils;
     }
 
-    @SuppressWarnings({"unchecked", "rawtypes"})
     public Page<QuestionVO> getQuestionList(String stem, String tag, String knowledgeId, Pageable pageable) {
-        Map<String, ?> urlParams = (Map) ImmutableMap.builder()
+        Map<String, ?> urlParams = ImmutableMap.<String, Object>builder()
                 .put("stem", stem)
                 .put("tag", tag)
                 .put("knowledgeId", knowledgeId)

+ 3 - 8
src/main/java/nju/seec/helper/api/bok/QuestionQueryApi.java

@@ -47,17 +47,12 @@ public class QuestionQueryApi {
         requestIds.removeAll(cacheBokQuestionsMap.keySet());
         //未缓存的去这里拿
         if (!requestIds.isEmpty()) {
-            Map<String, String> urlParams = ImmutableMap.of("id", requestIds.stream().reduce((a, b) -> a + "," + b).orElse(""));
+            Map<String, String> uriVariables = ImmutableMap.of("id", requestIds.stream().reduce((a, b) -> a + "," + b).orElse(""));
 
-            String json = restRequestUtil.sendGetRequest(byIdUrl, String.class, urlParams);
-
-            List<QuestionVO> questionVOList = JsonUtils.fromJson(json, new TypeToken<List<QuestionVO>>() {
+            List<QuestionVO> questionVOList = JsonUtils.fromJson(restRequestUtil.sendGetRequest(byIdUrl, String.class, uriVariables), new TypeToken<List<QuestionVO>>() {
             });
 
-            Map<String, QuestionVO> remoteBokQuestionsMap =
-                    questionVOList
-                            .parallelStream()
-                            .collect(Collectors.toMap(QuestionVO::getId, bokQuestion -> bokQuestion));
+            Map<String, QuestionVO> remoteBokQuestionsMap = questionVOList.parallelStream().collect(Collectors.toMap(QuestionVO::getId, bokQuestion -> bokQuestion));
 
             bokQuestionsMap.putAll(remoteBokQuestionsMap);
             cacheUtils.setAll(BOK_QUESTION_CACHE_NAME, (Map) remoteBokQuestionsMap);

+ 4 - 2
src/main/java/nju/seec/helper/api/bok/RecordApi.java

@@ -11,6 +11,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.Pageable;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
 import java.time.LocalDateTime;
@@ -28,6 +29,7 @@ public class RecordApi {
     private String questionUrl;
     private final RestRequestUtil restRequestUtil;
 
+    @Async
     public void reportUserRecord(List<RecordDTO> records) {
         System.out.println(records);
 //        restRequestUtil.sendPostRequest(url, records, String.class);
@@ -45,13 +47,13 @@ public class RecordApi {
     }
 
     public Page<QuestionVO> getCompletedQuestions(String userId, Pageable pageable) {
-        Map<String, ?> uriParams = ImmutableMap.of(
+        Map<String, ?> uriVariables = ImmutableMap.of(
                 "userId", userId,
                 "page", pageable.getPageNumber() + 1,
                 "size", pageable.getPageSize(),
                 "sort", StringUtils.getSortString(pageable.getSort())
         );
-        PageableQuestionListVO questionListVO = restRequestUtil.sendGetRequest(questionUrl, PageableQuestionListVO.class, uriParams);
+        PageableQuestionListVO questionListVO = restRequestUtil.sendGetRequest(questionUrl, PageableQuestionListVO.class, uriVariables);
         return new PageImpl<>(questionListVO.getQuestions(), pageable, questionListVO.getPage().getTotalElements());
     }
 }

+ 16 - 3
src/main/java/nju/seec/helper/api/bok/UserInteractiveApi.java

@@ -1,12 +1,15 @@
 package nju.seec.helper.api.bok;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.gson.reflect.TypeToken;
 import lombok.Data;
 import nju.seec.helper.api.bok.dto.CollectDTO;
 import nju.seec.helper.api.bok.dto.RateDTO;
+import nju.seec.helper.api.bok.vo.InteractiveVO;
 import nju.seec.helper.api.bok.vo.PageableQuestionListVO;
 import nju.seec.helper.api.bok.vo.QuestionVO;
 import nju.seec.helper.util.Consts;
+import nju.seec.helper.util.JsonUtils;
 import nju.seec.helper.util.RestRequestUtil;
 import nju.seec.helper.util.StringUtils;
 import nju.seec.helper.util.cache.CaffeineCacheUtils;
@@ -16,6 +19,7 @@ import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -33,6 +37,7 @@ public class UserInteractiveApi {
     private String rateUrl;
     private String collectionUrl;
     private String questionUrl;
+    private String interactiveUrl;
 
     public UserInteractiveApi(RestRequestUtil restRequestUtil, CaffeineCacheUtils cacheUtils) {
         this.restRequestUtil = restRequestUtil;
@@ -47,9 +52,8 @@ public class UserInteractiveApi {
         restRequestUtil.sendPostRequest(rateUrl, rateDTO, Object.class, ImmutableMap.of("userId", userId, "questionId", questionId));
     }
 
-    @SuppressWarnings({"unchecked", "rawtypes"})
     public Page<QuestionVO> getCollectAndRateQuestions(String userId, Boolean collect, Boolean rate, Pageable pageable) {
-        Map<String, ?> uriParams = (Map) ImmutableMap.builder()
+        Map<String, ?> uriVariables = ImmutableMap.<String, Object>builder()
                 .put("userId", userId)
                 .put("collection", collect)
                 .put("rate", rate)
@@ -57,7 +61,16 @@ public class UserInteractiveApi {
                 .put("size", pageable.getPageSize())
                 .put("sort", StringUtils.getSortString(pageable.getSort()))
                 .build();
-        PageableQuestionListVO pageableQuestionListVO = restRequestUtil.sendGetRequest(questionUrl, PageableQuestionListVO.class, uriParams);
+        PageableQuestionListVO pageableQuestionListVO = restRequestUtil.sendGetRequest(questionUrl, PageableQuestionListVO.class, uriVariables);
         return new PageImpl<>(pageableQuestionListVO.getQuestions(), pageable, pageableQuestionListVO.getPage().getTotalElements());
     }
+
+    public List<InteractiveVO> getInteractive(String userId, String questionId) {
+        Map<String, ?> uriVariables = ImmutableMap.<String, Object>builder()
+                .put("userId", userId)
+                .put("questionId", questionId)
+                .build();
+        return JsonUtils.fromJson(restRequestUtil.sendGetRequest(interactiveUrl, String.class, uriVariables), new TypeToken<List<InteractiveVO>>() {
+        });
+    }
 }

+ 20 - 0
src/main/java/nju/seec/helper/api/bok/vo/InteractiveVO.java

@@ -0,0 +1,20 @@
+package nju.seec.helper.api.bok.vo;
+
+import lombok.Data;
+
+/**
+ * @author cst
+ */
+@Data
+public class InteractiveVO {
+    private String userId;
+    private String questionId;
+    /**
+     * 为null表示未对该题做过收藏处理
+     */
+    private Boolean collection;
+    /**
+     * 为null表示未对该题做过评价处理
+     */
+    private Integer rate;
+}

+ 3 - 1
src/main/java/nju/seec/helper/api/dataanalysis/EventApi.java

@@ -9,6 +9,7 @@ import nju.seec.helper.entity.Course;
 import nju.seec.helper.entity.Quiz;
 import nju.seec.helper.util.RestRequestUtil;
 import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
 import java.time.LocalDateTime;
@@ -26,7 +27,8 @@ public class EventApi {
     public EventApi(RestRequestUtil restRequestUtil) {
         this.restRequestUtil = restRequestUtil;
     }
-    
+
+    @Async
     public void reportEvent(EventDTO eventDTO) {
         System.out.println(eventDTO);
 //            restRequestUtil.sendPostRequest(eventReportUrl, event, Boolean.class);

+ 2 - 0
src/main/java/nju/seec/helper/api/dataanalysis/ExerciseApi.java

@@ -4,6 +4,7 @@ import lombok.Data;
 import nju.seec.helper.api.dataanalysis.dto.ExerciseRecordDTO;
 import nju.seec.helper.util.RestRequestUtil;
 import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
 import java.time.Duration;
@@ -24,6 +25,7 @@ public class ExerciseApi {
         this.restRequestUtil = restRequestUtil;
     }
 
+    @Async
     public void reportExercise(List<ExerciseRecordDTO> exerciseRecords) {
         System.out.println(exerciseRecords);
 //        restRequestUtil.sendPostRequest(exerciseReportUrl, exerciseRecords, Boolean.class);

+ 0 - 2
src/main/java/nju/seec/helper/aspect/event/EventAspect.java

@@ -12,7 +12,6 @@ import nju.seec.helper.dto.comment.CommentDTO;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.annotation.AfterReturning;
 import org.aspectj.lang.annotation.Aspect;
-import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -83,7 +82,6 @@ public class EventAspect {
     private final EventHandler doNothing = (joinPoint, user) -> {
     };
 
-    @Async
     @SneakyThrows
     @AfterReturning("@annotation(event) && args(user,..)")
     public void reportEvent(JoinPoint joinPoint, Event event, LoginUser user) {

+ 0 - 2
src/main/java/nju/seec/helper/aspect/message/MessageAspect.java

@@ -17,7 +17,6 @@ import nju.seec.helper.vo.quiz.QuizVO;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.annotation.AfterReturning;
 import org.aspectj.lang.annotation.Aspect;
-import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
 import java.util.Collections;
@@ -107,7 +106,6 @@ public class MessageAspect {
                 .build();
     }
 
-    @Async
     @AfterReturning(value = "@annotation(message)", returning = "object")
     public void createMessage(JoinPoint joinPoint, Message message, Object object) {
         messageCreatorMap.getOrDefault(message.value(), doNothing).createMessage(joinPoint, object);

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

@@ -1,20 +1,18 @@
 package nju.seec.helper.aspect.record;
 
-import com.google.common.collect.Lists;
 import nju.seec.helper.api.bok.RecordApi;
-import nju.seec.helper.api.bok.dto.RecordDTO;
 import nju.seec.helper.api.dataanalysis.ExerciseApi;
-import nju.seec.helper.api.dataanalysis.dto.ExerciseRecordDTO;
 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.springframework.scheduling.annotation.Async;
+import org.aspectj.lang.annotation.Pointcut;
 import org.springframework.stereotype.Component;
 
-import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * @author cst
@@ -30,26 +28,52 @@ public class ExerciseRecordAspect {
         this.recordApi = recordApi;
     }
 
-    @Async
-    @AfterReturning(value = "@annotation(exerciseRecord) && args(user,..,quizStudentAnswerDTO)", returning = "quizStudentAnswerVO", argNames = "exerciseRecord,user,quizStudentAnswerDTO,quizStudentAnswerVO")
-    public void reportExerciseRecordEvent(ExerciseRecord exerciseRecord, LoginUser user, QuizStudentAnswerDTO quizStudentAnswerDTO, QuizStudentAnswerVO quizStudentAnswerVO) {
+    @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();
-        int size = quizStudentAnswerDTO.getRecords().size();
-        List<ExerciseRecordDTO> exerciseRecords = Lists.newArrayListWithExpectedSize(size);
-        List<RecordDTO> records = Lists.newArrayListWithExpectedSize(size);
-        quizStudentAnswerDTO
-                .getRecords()
-                .forEach((key, value) -> {
-                    QuizStudentAnswerVO.QuestionStudentAnswerInfo studentAnswerInfo = questionStudentAnswerInfoMap.get(key);
-                    if (studentAnswerInfo != null) {
-                        Boolean pass = studentAnswerInfo.getCorrect();
-                        Object answer = studentAnswerInfo.getAnswer();
-                        exerciseRecords.add(exerciseApi.createExerciseRecordDTO(userId, key, value.getStartAt(), value.getEndAt(), pass));
-                        records.add(recordApi.createRecordDTO(userId, key, answer, pass, value.getStartAt(), value.getEndAt()));
-                    }
-                });
-        exerciseApi.reportExercise(exerciseRecords);
-        recordApi.reportUserRecord(records);
+        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())
+        );
     }
 }

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

@@ -42,7 +42,7 @@ public class CommentController {
     public CommentVO createComment(LoginUser user,
                                    @Validated @RequestBody CommentDTO commentDTO) {
         CommentVO commentVO = commentService.createComment(user, commentDTO);
-        CommentWebsocket.sendAllMessage(commentDTO.getSlideId(), JsonUtils.toJson(commentVO));
+        CommentWebsocket.sendAllMessageToOneSlide(commentDTO.getSlideId(), JsonUtils.toJson(commentVO));
         return commentVO;
     }
 

+ 9 - 15
src/main/java/nju/seec/helper/controller/CommentWebsocket.java

@@ -12,6 +12,7 @@ import javax.websocket.server.PathParam;
 import javax.websocket.server.ServerEndpoint;
 import java.util.Collections;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -24,7 +25,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
 @EqualsAndHashCode
 @Component
 public class CommentWebsocket {
-    private static Map<Long, Set<CommentWebsocket>> COMMENT_WEBSOCKET_MAP = new ConcurrentHashMap<>();
+    private static final Map<Long, Set<CommentWebsocket>> COMMENT_WEBSOCKET_MAP = new ConcurrentHashMap<>();
     private Long slideId;
     private Session session;
 
@@ -34,10 +35,10 @@ public class CommentWebsocket {
         session.setMaxIdleTimeout(3 * 60 * 60 * 1000);
         this.session = session;
         this.slideId = slideId;
-        if (!COMMENT_WEBSOCKET_MAP.containsKey(slideId)) {
-            COMMENT_WEBSOCKET_MAP.put(slideId, new CopyOnWriteArraySet<>());
-        }
-        COMMENT_WEBSOCKET_MAP.get(slideId).add(this);
+        Set<CommentWebsocket> websocketSet = new CopyOnWriteArraySet<>();
+        Optional.ofNullable(COMMENT_WEBSOCKET_MAP.putIfAbsent(slideId, websocketSet))
+                .orElse(websocketSet)
+                .add(this);
     }
 
     @OnClose
@@ -47,19 +48,12 @@ public class CommentWebsocket {
 
     @OnError
     public void onError(Throwable throwable) {
-        log.error(throwable.getLocalizedMessage(), throwable);
+        log.error(throwable.getLocalizedMessage());
     }
 
-    public static void sendAllMessage(Long slideId, String message) {
+    public static void sendAllMessageToOneSlide(Long slideId, String message) {
         COMMENT_WEBSOCKET_MAP
                 .getOrDefault(slideId, Collections.emptySet())
-                .parallelStream()
-                .forEach(commentWebsocket -> {
-                    try {
-                        commentWebsocket.session.getAsyncRemote().sendText(message);
-                    } catch (Exception e) {
-                        log.error(e.getLocalizedMessage());
-                    }
-                });
+                .forEach(commentWebsocket -> commentWebsocket.session.getAsyncRemote().sendText(message));
     }
 }

+ 18 - 6
src/main/java/nju/seec/helper/controller/QuestionController.java

@@ -2,6 +2,7 @@ package nju.seec.helper.controller;
 
 import nju.seec.helper.api.bok.dto.CollectDTO;
 import nju.seec.helper.api.bok.dto.RateDTO;
+import nju.seec.helper.api.bok.vo.InteractiveVO;
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.controller.response.PageResponse;
@@ -134,12 +135,17 @@ public class QuestionController {
      * 取得收藏评价的题目
      */
     @Auth(roles = UserRole.STUDENT, message = "取得收藏评价的题目")
-    @GetMapping("/collect-rate")
-    public PageResponse<BaseQuestionVO> getCollectAndRateQuestions(LoginUser user,
-                                                                   @RequestParam(required = false, defaultValue = "true") Boolean collect,
-                                                                   @RequestParam(required = false, defaultValue = "true") Boolean rate,
-                                                                   @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
-        return PageResponse.of(questionService.getCollectAndRateQuestions(user, collect, rate, pageable));
+    @GetMapping("/collected")
+    public PageResponse<BaseQuestionVO> getCollectedQuestions(LoginUser user,
+                                                              @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
+        return PageResponse.of(questionService.getCollectedAndRatedQuestions(user, true, false, pageable));
+    }
+
+    @Auth(roles = UserRole.STUDENT, message = "取得收藏评价的题目")
+    @GetMapping("/rated")
+    public PageResponse<BaseQuestionVO> getRatedQuestions(LoginUser user,
+                                                          @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
+        return PageResponse.of(questionService.getCollectedAndRatedQuestions(user, false, true, pageable));
     }
 
     /**
@@ -150,4 +156,10 @@ public class QuestionController {
     public PageResponse<BaseQuestionVO> getCompletedQuestions(LoginUser user, @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
         return PageResponse.of(questionService.getCompletedQuestions(user, pageable));
     }
+
+    @Auth(roles = UserRole.STUDENT, message = "取得与题目的交互信息")
+    @GetMapping("/interactive")
+    public List<InteractiveVO> getInteractiveList(LoginUser user, String questionId) {
+        return questionService.getInteractiveList(user, questionId);
+    }
 }

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

@@ -43,7 +43,6 @@ public class User {
     @Column(nullable = false)
     private String phone;
 
-    @Column(nullable = false)
     private String password;
 
     @Column(name = "create_at", updatable = false, nullable = false)

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

@@ -2,6 +2,7 @@ package nju.seec.helper.service;
 
 import nju.seec.helper.api.bok.dto.CollectDTO;
 import nju.seec.helper.api.bok.dto.RateDTO;
+import nju.seec.helper.api.bok.vo.InteractiveVO;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.question.BaseQuestionDTO;
 import nju.seec.helper.entity.Quiz;
@@ -126,7 +127,7 @@ public interface QuestionService {
      * @param pageable
      * @return
      */
-    Page<BaseQuestionVO> getCollectAndRateQuestions(LoginUser user, Boolean collect, Boolean rate, Pageable pageable);
+    Page<BaseQuestionVO> getCollectedAndRatedQuestions(LoginUser user, Boolean collect, Boolean rate, Pageable pageable);
 
     /**
      * 取得完成的题目
@@ -136,4 +137,13 @@ public interface QuestionService {
      * @return
      */
     Page<BaseQuestionVO> getCompletedQuestions(LoginUser user, Pageable pageable);
+
+    /**
+     * 取得题目交互信息
+     *
+     * @param user
+     * @param questionId
+     * @return
+     */
+    List<InteractiveVO> getInteractiveList(LoginUser user, String questionId);
 }

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

@@ -8,6 +8,7 @@ import nju.seec.helper.enums.MessageType;
 import nju.seec.helper.service.MessageService;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -24,7 +25,8 @@ public class MessageServiceImpl implements MessageService {
     public MessageServiceImpl(MessageDAO messageDAO) {
         this.messageDAO = messageDAO;
     }
-    
+
+    @Async
     @Override
     public void createMessage(Set<Long> toUserIds, MessageType type, String content, Long refId) {
         messageDAO.saveAll(

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

@@ -7,6 +7,7 @@ import nju.seec.helper.api.bok.RecordApi;
 import nju.seec.helper.api.bok.UserInteractiveApi;
 import nju.seec.helper.api.bok.dto.CollectDTO;
 import nju.seec.helper.api.bok.dto.RateDTO;
+import nju.seec.helper.api.bok.vo.InteractiveVO;
 import nju.seec.helper.api.bok.vo.QuestionVO;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dao.ChooseDAO;
@@ -170,7 +171,7 @@ public class QuestionServiceImpl implements QuestionService {
 
     @Transactional(readOnly = true)
     @Override
-    public Page<BaseQuestionVO> getCollectAndRateQuestions(LoginUser user, Boolean collect, Boolean rate, Pageable pageable) {
+    public Page<BaseQuestionVO> getCollectedAndRatedQuestions(LoginUser user, Boolean collect, Boolean rate, Pageable pageable) {
         return convert(user, userInteractiveApi.getCollectAndRateQuestions(String.valueOf(user.getPid()), collect, rate, pageable));
     }
 
@@ -180,6 +181,11 @@ public class QuestionServiceImpl implements QuestionService {
         return convert(user, recordApi.getCompletedQuestions(String.valueOf(user.getPid()), pageable));
     }
 
+    @Override
+    public List<InteractiveVO> getInteractiveList(LoginUser user, String questionId) {
+        return userInteractiveApi.getInteractive(String.valueOf(user.getPid()), questionId);
+    }
+
     private static final Set<QuizState> HIDE_QUESTION_ANSWER_STATES = Collections.singleton(QuizState.ONGOING);
 
     private Page<BaseQuestionVO> convert(LoginUser user, Page<QuestionVO> questionVOList) {

+ 0 - 1
src/main/java/nju/seec/helper/util/cache/CaffeineCacheUtils.java

@@ -40,7 +40,6 @@ public class CaffeineCacheUtils {
     }
 
     public Object get(String cacheName, String key) {
-        cache.get(key, k -> k);
         return cache.getIfPresent(combineKey(cacheName, key));
     }
 

+ 17 - 14
src/main/resources/application-deploy.yml

@@ -2,7 +2,7 @@ server:
   port: 9095
 spring:
   datasource:
-    url: jdbc:mysql://localhost:13306/helper?setUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
+    url: jdbc:mysql://localhost:33306/helper?setUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
     driver-class-name: com.mysql.cj.jdbc.Driver
     username: root
     password: NJU67helper
@@ -57,28 +57,31 @@ aliyun:
 #    signName: seeccoder
 #    templateCode: SMS_181555598
 bok:
-  url: http://120.78.159.171:6060
+  api-url: http://120.78.159.171:6060/api
   question:
-    url: ${bok.url}/api/question
+    url: ${bok.api-url}/question
     search-url: ${bok.question.url}?stem={stem}&tag={tag}&knowledgeId={knowledgeId}&page={page}&size={size}&{sort}
   question-query:
-    url: ${bok.url}/api/question-query
+    url: ${bok.api-url}/question-query
     by-id-url: ${bok.question-query.url}/id?id={id}
   knowledge:
-    url: ${bok.url}/api/knowledge
+    url: ${bok.api-url}/knowledge
   user:
-    url: ${bok.url}/api/user
+    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}
-#data-analysis:
-#  url: http://dataanalysis.seecoder.cn
-#  eventUrl: ${data-analysis.url}/api/event
-#  eventReportUrl: ${data-analysis.eventUrl}/report
-guavaCache:
-  maximumSize: 1000
-  expireAfterAccessInSeconds: 0
-  expireAfterWriteInSeconds: 900
+  record:
+    url: ${bok.api-url}/record
+    question-url: ${bok.record.url}/{userId}/question?page={page}&size={size}&{sort}
+data-analysis:
+  api-url: http://dataanalysis.seecoder.cn/api
+  event:
+    url: ${data-analysis.api-url}/event
+    report-url: ${data-analysis.event.url}/report
+  exercise:
+    url: ${data-analysis.api-url}/exercise
+    report-url: ${data-analysis.exercise.url}/report
 jwt:
   secret: SEEC-1919810-OIDC-114514
 # 自定义数据

+ 2 - 7
src/main/resources/application-dev.yml

@@ -68,16 +68,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}
+    question-url: ${bok.user.url}/{userId}/question?collection={collection}&rate={rate}&page={page}&size={size}&{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}
-#  tqUrl: ${bok.url}/api/question
-#  tqSearchUrl: ${bok.tqUrl}
-#  tqStemSearchUrl: ${bok.tqSearchUrl}/findByStemLike?content={content}&size={size}&page={page}
-#  tqIdSearchUrl: ${bok.tqSearchUrl}/findByIdIn?id={id}
-#  knowledgeUrl: ${bok.url}/api/neo
-#  knowledgeGetAllUrl: ${bok.knowledgeUrl}/getAll
 data-analysis:
   api-url: http://dataanalysis.seecoder.cn/api
   event: