Explorar o código

fix: 修改未缓存oss链接的bug

ChenSiTong %!s(int64=6) %!d(string=hai) anos
pai
achega
5836f9bdfe
Modificáronse 43 ficheiros con 870 adicións e 50 borrados
  1. 2 2
      src/main/java/nju/seec/helper/aspect/event/Event.java
  2. 3 3
      src/main/java/nju/seec/helper/aspect/event/EventAspect.java
  3. 2 2
      src/main/java/nju/seec/helper/aspect/record/ExerciseRecordAspect.java
  4. 2 2
      src/main/java/nju/seec/helper/controller/CommentController.java
  5. 2 2
      src/main/java/nju/seec/helper/controller/CourseController.java
  6. 1 1
      src/main/java/nju/seec/helper/controller/EventController.java
  7. 2 2
      src/main/java/nju/seec/helper/controller/KnowledgeController.java
  8. 3 3
      src/main/java/nju/seec/helper/controller/QuestionController.java
  9. 2 2
      src/main/java/nju/seec/helper/controller/QuizController.java
  10. 1 1
      src/main/java/nju/seec/helper/controller/RecordController.java
  11. 1 1
      src/main/java/nju/seec/helper/service/EventService.java
  12. 3 3
      src/main/java/nju/seec/helper/service/QuestionService.java
  13. 1 1
      src/main/java/nju/seec/helper/service/RecordService.java
  14. 1 5
      src/main/java/nju/seec/helper/service/impl/CommentServiceImpl.java
  15. 2 2
      src/main/java/nju/seec/helper/service/impl/EventServiceImpl.java
  16. 9 9
      src/main/java/nju/seec/helper/service/impl/QuestionServiceImpl.java
  17. 4 4
      src/main/java/nju/seec/helper/service/impl/RecordServiceImpl.java
  18. 46 0
      src/main/java/nju/seec/helper/sys/bok/api/KnowledgeApi.java
  19. 112 0
      src/main/java/nju/seec/helper/sys/bok/api/QuestionApi.java
  20. 61 0
      src/main/java/nju/seec/helper/sys/bok/api/QuestionQueryApi.java
  21. 54 0
      src/main/java/nju/seec/helper/sys/bok/api/RecordApi.java
  22. 69 0
      src/main/java/nju/seec/helper/sys/bok/api/UserInteractiveApi.java
  23. 28 0
      src/main/java/nju/seec/helper/sys/bok/constant/QuestionType.java
  24. 16 0
      src/main/java/nju/seec/helper/sys/bok/dto/CollectDTO.java
  25. 23 0
      src/main/java/nju/seec/helper/sys/bok/dto/RateDTO.java
  26. 20 0
      src/main/java/nju/seec/helper/sys/bok/dto/RecordDTO.java
  27. 20 0
      src/main/java/nju/seec/helper/sys/bok/vo/InteractiveVO.java
  28. 16 0
      src/main/java/nju/seec/helper/sys/bok/vo/KnowledgeNodeVO.java
  29. 14 0
      src/main/java/nju/seec/helper/sys/bok/vo/PageVO.java
  30. 14 0
      src/main/java/nju/seec/helper/sys/bok/vo/PageableQuestionListVO.java
  31. 31 0
      src/main/java/nju/seec/helper/sys/bok/vo/QuestionVO.java
  32. 21 0
      src/main/java/nju/seec/helper/sys/bok/vo/RecordQuestionVO.java
  33. 25 0
      src/main/java/nju/seec/helper/sys/bok/vo/RecordVO.java
  34. 87 0
      src/main/java/nju/seec/helper/sys/dataanalysis/api/EventApi.java
  35. 40 0
      src/main/java/nju/seec/helper/sys/dataanalysis/api/ExerciseApi.java
  36. 29 0
      src/main/java/nju/seec/helper/sys/dataanalysis/constant/EventAction.java
  37. 37 0
      src/main/java/nju/seec/helper/sys/dataanalysis/constant/EventType.java
  38. 21 0
      src/main/java/nju/seec/helper/sys/dataanalysis/model/Event.java
  39. 38 0
      src/main/java/nju/seec/helper/sys/dataanalysis/model/ExerciseRecord.java
  40. 3 1
      src/main/java/nju/seec/helper/util/OssUtils.java
  41. 2 2
      src/main/java/nju/seec/helper/vo/question/BaseQuestionVO.java
  42. 1 1
      src/main/java/nju/seec/helper/vo/question/ChoiceQuestionVO.java
  43. 1 1
      src/main/java/nju/seec/helper/vo/question/TrueOrFalseQuestionVO.java

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.aspect.event;
 
-import nju.seec.helper.api.dataanalysis.constant.EventAction;
-import nju.seec.helper.api.dataanalysis.constant.EventType;
+import nju.seec.helper.sys.dataanalysis.constant.EventAction;
+import nju.seec.helper.sys.dataanalysis.constant.EventType;
 
 import java.lang.annotation.*;
 

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

@@ -2,9 +2,9 @@ package nju.seec.helper.aspect.event;
 
 import com.google.common.collect.ImmutableMap;
 import lombok.SneakyThrows;
-import nju.seec.helper.api.dataanalysis.EventApi;
-import nju.seec.helper.api.dataanalysis.constant.EventAction;
-import nju.seec.helper.api.dataanalysis.constant.EventType;
+import nju.seec.helper.sys.dataanalysis.api.EventApi;
+import nju.seec.helper.sys.dataanalysis.constant.EventAction;
+import nju.seec.helper.sys.dataanalysis.constant.EventType;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dao.CourseDAO;
 import nju.seec.helper.dao.QuizDAO;

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.aspect.record;
 
-import nju.seec.helper.api.bok.RecordApi;
-import nju.seec.helper.api.dataanalysis.ExerciseApi;
+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;

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.controller;
 
-import nju.seec.helper.api.dataanalysis.constant.EventAction;
-import nju.seec.helper.api.dataanalysis.constant.EventType;
+import nju.seec.helper.sys.dataanalysis.constant.EventAction;
+import nju.seec.helper.sys.dataanalysis.constant.EventType;
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.aspect.event.Event;

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.controller;
 
-import nju.seec.helper.api.dataanalysis.constant.EventAction;
-import nju.seec.helper.api.dataanalysis.constant.EventType;
+import nju.seec.helper.sys.dataanalysis.constant.EventAction;
+import nju.seec.helper.sys.dataanalysis.constant.EventType;
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.aspect.event.Event;

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

@@ -1,6 +1,6 @@
 package nju.seec.helper.controller;
 
-import nju.seec.helper.api.dataanalysis.model.Event;
+import nju.seec.helper.sys.dataanalysis.model.Event;
 import nju.seec.helper.service.EventService;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.controller;
 
-import nju.seec.helper.api.bok.KnowledgeApi;
-import nju.seec.helper.api.bok.vo.KnowledgeNodeVO;
+import nju.seec.helper.sys.bok.api.KnowledgeApi;
+import nju.seec.helper.sys.bok.vo.KnowledgeNodeVO;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;

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

@@ -1,8 +1,8 @@
 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.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;

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.controller;
 
-import nju.seec.helper.api.dataanalysis.constant.EventAction;
-import nju.seec.helper.api.dataanalysis.constant.EventType;
+import nju.seec.helper.sys.dataanalysis.constant.EventAction;
+import nju.seec.helper.sys.dataanalysis.constant.EventType;
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.aspect.event.Event;

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

@@ -1,6 +1,6 @@
 package nju.seec.helper.controller;
 
-import nju.seec.helper.api.dataanalysis.model.ExerciseRecord;
+import nju.seec.helper.sys.dataanalysis.model.ExerciseRecord;
 import nju.seec.helper.service.RecordService;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;

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

@@ -1,6 +1,6 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.api.dataanalysis.model.Event;
+import nju.seec.helper.sys.dataanalysis.model.Event;
 
 import java.util.List;
 

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

@@ -1,8 +1,8 @@
 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.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;

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

@@ -1,6 +1,6 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.api.dataanalysis.model.ExerciseRecord;
+import nju.seec.helper.sys.dataanalysis.model.ExerciseRecord;
 
 import java.util.List;
 

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

@@ -12,7 +12,6 @@ import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.enums.MessageType;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.CommentService;
-import nju.seec.helper.service.MessageService;
 import nju.seec.helper.vo.CommentVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
@@ -30,13 +29,10 @@ public class CommentServiceImpl implements CommentService {
     private final SlideDAO slideDAO;
     private final UserDAO userDAO;
 
-    private final MessageService messageService;
-
-    public CommentServiceImpl(CommentDAO commentDAO, SlideDAO slideDAO, UserDAO userDAO, MessageService messageService) {
+    public CommentServiceImpl(CommentDAO commentDAO, SlideDAO slideDAO, UserDAO userDAO) {
         this.commentDAO = commentDAO;
         this.slideDAO = slideDAO;
         this.userDAO = userDAO;
-        this.messageService = messageService;
     }
 
     @Message(MessageType.COMMENT_NEW)

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

@@ -1,8 +1,8 @@
 package nju.seec.helper.service.impl;
 
 import com.google.common.collect.Lists;
-import nju.seec.helper.api.dataanalysis.EventApi;
-import nju.seec.helper.api.dataanalysis.model.Event;
+import nju.seec.helper.sys.dataanalysis.api.EventApi;
+import nju.seec.helper.sys.dataanalysis.model.Event;
 import nju.seec.helper.dao.*;
 import nju.seec.helper.entity.*;
 import nju.seec.helper.enums.UserRole;

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

@@ -1,15 +1,15 @@
 package nju.seec.helper.service.impl;
 
 import cn.hutool.core.util.IdUtil;
-import nju.seec.helper.api.bok.QuestionApi;
-import nju.seec.helper.api.bok.QuestionQueryApi;
-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.api.bok.vo.RecordQuestionVO;
+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;

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

@@ -1,9 +1,9 @@
 package nju.seec.helper.service.impl;
 
-import nju.seec.helper.api.bok.RecordApi;
-import nju.seec.helper.api.bok.vo.RecordQuestionVO;
-import nju.seec.helper.api.dataanalysis.ExerciseApi;
-import nju.seec.helper.api.dataanalysis.model.ExerciseRecord;
+import nju.seec.helper.sys.bok.api.RecordApi;
+import nju.seec.helper.sys.bok.vo.RecordQuestionVO;
+import nju.seec.helper.sys.dataanalysis.api.ExerciseApi;
+import nju.seec.helper.sys.dataanalysis.model.ExerciseRecord;
 import nju.seec.helper.dao.UserDAO;
 import nju.seec.helper.service.RecordService;
 import org.springframework.data.domain.PageRequest;

+ 46 - 0
src/main/java/nju/seec/helper/sys/bok/api/KnowledgeApi.java

@@ -0,0 +1,46 @@
+package nju.seec.helper.sys.bok.api;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import nju.seec.helper.sys.bok.vo.KnowledgeNodeVO;
+import nju.seec.helper.util.RestRequestUtil;
+import nju.seec.helper.util.cache.CaffeineCacheUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+@Component
+@ConfigurationProperties("bok.knowledge")
+@Data
+public class KnowledgeApi {
+    @Value("${bok.knowledge-cache-name}")
+    private String bokKnowledgeCacheName;
+
+    private final RestRequestUtil restRequestUtil;
+    private final CaffeineCacheUtils cacheUtils;
+
+    private String url;
+
+    @SuppressWarnings("unchecked")
+    public List<KnowledgeNodeVO> getAllKnowledgeNodes() {
+        Object cache = cacheUtils.get(bokKnowledgeCacheName, "all");
+        if (cache instanceof List) {
+            return (List<KnowledgeNodeVO>) cache;
+        }
+        List<KnowledgeNodeVO> knowledgeNodeVOList = restRequestUtil.sendGetRequest(url, List.class);
+        cacheUtils.set(bokKnowledgeCacheName, "all", knowledgeNodeVOList);
+        return knowledgeNodeVOList;
+    }
+
+    @AllArgsConstructor(staticName = "of")
+    @Data
+    private static class KnowledgeGetDTO {
+        private String name;
+        private String relation;
+    }
+}

+ 112 - 0
src/main/java/nju/seec/helper/sys/bok/api/QuestionApi.java

@@ -0,0 +1,112 @@
+package nju.seec.helper.sys.bok.api;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.Data;
+import nju.seec.helper.dto.question.BaseQuestionDTO;
+import nju.seec.helper.dto.question.ChoiceQuestionDTO;
+import nju.seec.helper.dto.question.TrueOrFalseQuestionDTO;
+import nju.seec.helper.enums.ExceptionType;
+import nju.seec.helper.exception.HelperException;
+import nju.seec.helper.sys.bok.constant.QuestionType;
+import nju.seec.helper.sys.bok.vo.PageableQuestionListVO;
+import nju.seec.helper.sys.bok.vo.QuestionVO;
+import nju.seec.helper.util.RestRequestUtil;
+import nju.seec.helper.util.StringUtils;
+import nju.seec.helper.util.cache.CaffeineCacheUtils;
+import org.springframework.beans.factory.annotation.Value;
+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.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@Component
+@ConfigurationProperties("bok.question")
+@Data
+public class QuestionApi {
+    @Value("${bok.question-cache-name}")
+    private String bokQuestionCacheName;
+
+    private final RestRequestUtil restRequestUtil;
+    private final CaffeineCacheUtils cacheUtils;
+    private String url;
+    private String searchUrl;
+
+    public Page<QuestionVO> getQuestionList(String stem, String tag, String knowledgeId, Pageable pageable) {
+        Map<String, ?> urlParams = ImmutableMap.<String, Object>builder()
+                .put("stem", stem)
+                .put("tag", tag)
+                .put("knowledgeId", knowledgeId)
+                .put("page", 1 + pageable.getPageNumber())
+                .put("size", pageable.getPageSize())
+                .put("sort", StringUtils.getSortString(pageable.getSort()))
+                .build();
+        PageableQuestionListVO result = restRequestUtil.sendGetRequest(searchUrl, PageableQuestionListVO.class, urlParams);
+
+        List<QuestionVO> questionVOList = result.getQuestions();
+        cacheUtils.setAll(bokQuestionCacheName, questionVOList.parallelStream().collect(Collectors.toMap(QuestionVO::getId, bokQuestion -> bokQuestion)));
+        return new PageImpl<>(questionVOList, pageable, result.getPage().getTotalElements());
+    }
+
+    public QuestionVO getQuestionsById(String questionId) {
+        Object cachedBokQuestion = cacheUtils.get(bokQuestionCacheName, questionId);
+        if (cachedBokQuestion instanceof QuestionVO) {
+            return (QuestionVO) cachedBokQuestion;
+        }
+        QuestionVO questionVO = restRequestUtil.sendGetRequest(url + "/" + questionId, QuestionVO.class);
+        cacheUtils.set(bokQuestionCacheName, questionVO.getId(), questionVO);
+        return questionVO;
+    }
+
+    public QuestionVO createQuestion(String questionId, BaseQuestionDTO baseQuestionDTO) {
+        QuestionVO questionVO = createQuestionVO(baseQuestionDTO);
+        questionVO.setId(questionId);
+        questionVO = restRequestUtil.sendPostRequest(url, questionVO, QuestionVO.class);
+        return questionVO;
+    }
+
+    public QuestionVO updateQuestion(String questionId, BaseQuestionDTO baseQuestionDTO) {
+        QuestionVO questionVO = createQuestionVO(baseQuestionDTO);
+        questionVO.setId(questionId);
+        restRequestUtil.sendPutRequest(url + "/" + questionId, questionVO);
+        return getQuestionsById(questionId);
+    }
+
+    public void deleteQuestion(String questionId) {
+        restRequestUtil.sendDeleteRequest(url + "/" + questionId);
+    }
+
+    private QuestionVO createQuestionVO(BaseQuestionDTO baseQuestionDTO) {
+        QuestionVO questionVO = new QuestionVO();
+        questionVO.setType(QuestionType.valueOf(baseQuestionDTO.getType()));
+        questionVO.setStem(baseQuestionDTO.getStem());
+        questionVO.setKeyPoints(baseQuestionDTO.getKeyPoints());
+        questionVO.setTags(baseQuestionDTO.getTags());
+        questionVO.setKnowledgeId(baseQuestionDTO.getKnowledgeId());
+        questionVO.setAnalysis(baseQuestionDTO.getAnalysis());
+
+        switch (questionVO.getType()) {
+            case choice:
+                ChoiceQuestionDTO choiceQuestionDTO = (ChoiceQuestionDTO) baseQuestionDTO;
+                questionVO.setOptions(choiceQuestionDTO.getOptions());
+                questionVO.setAnswer(choiceQuestionDTO.getAnswer());
+                break;
+            case true_false:
+                TrueOrFalseQuestionDTO trueOrFalseQuestionDTO = (TrueOrFalseQuestionDTO) baseQuestionDTO;
+                questionVO.setAnswer(String.valueOf(trueOrFalseQuestionDTO.getAnswer()));
+                break;
+            default:
+                throw HelperException.of(ExceptionType.ERROR, "不支持的题目类型");
+        }
+        return questionVO;
+    }
+}

+ 61 - 0
src/main/java/nju/seec/helper/sys/bok/api/QuestionQueryApi.java

@@ -0,0 +1,61 @@
+package nju.seec.helper.sys.bok.api;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.gson.reflect.TypeToken;
+import lombok.Data;
+import nju.seec.helper.sys.bok.vo.QuestionVO;
+import nju.seec.helper.util.JsonUtils;
+import nju.seec.helper.util.RestRequestUtil;
+import nju.seec.helper.util.cache.CaffeineCacheUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author cst
+ */
+@Component
+@ConfigurationProperties("bok.question-query")
+@Data
+public class QuestionQueryApi {
+    @Value("${bok.question-cache-name}")
+    private String bokQuestionCacheName;
+
+    private final RestRequestUtil restRequestUtil;
+    private final CaffeineCacheUtils cacheUtils;
+
+    private String byIdUrl;
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public List<QuestionVO> getQuestionListById(final List<String> ids) {
+        Map<String, QuestionVO> bokQuestionsMap = Maps.newHashMapWithExpectedSize(ids.size());
+
+        final Map<String, QuestionVO> cacheBokQuestionsMap = (Map) cacheUtils.multiGet(bokQuestionCacheName, ids);
+        bokQuestionsMap.putAll(cacheBokQuestionsMap);
+
+        List<String> requestIds = Lists.newArrayList(ids);
+        requestIds.removeAll(cacheBokQuestionsMap.keySet());
+        //未缓存的去这里拿
+        if (!requestIds.isEmpty()) {
+            Map<String, String> uriVariables = ImmutableMap.of("id", requestIds.stream().reduce((a, b) -> a + "," + b).orElse(""));
+
+            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));
+
+            bokQuestionsMap.putAll(remoteBokQuestionsMap);
+            cacheUtils.setAll(bokQuestionCacheName, (Map) remoteBokQuestionsMap);
+        }
+
+        return ids.stream()
+                .map(bokQuestionsMap::get)
+                .collect(Collectors.toList());
+    }
+}

+ 54 - 0
src/main/java/nju/seec/helper/sys/bok/api/RecordApi.java

@@ -0,0 +1,54 @@
+package nju.seec.helper.sys.bok.api;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.Data;
+import nju.seec.helper.sys.bok.dto.RecordDTO;
+import nju.seec.helper.sys.bok.vo.RecordQuestionVO;
+import nju.seec.helper.util.RestRequestUtil;
+import nju.seec.helper.util.StringUtils;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.data.domain.Pageable;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@Component
+@ConfigurationProperties("bok.record")
+@Data
+public class RecordApi {
+    private String url;
+    private String questionUrl;
+    private final RestRequestUtil restRequestUtil;
+
+    @Async
+    public void reportUserRecord(List<RecordDTO> records) {
+        restRequestUtil.sendPostRequest(url, records, String.class);
+    }
+
+    public RecordDTO createRecordDTO(String studentId, String questionId, Object answer, Boolean pass, LocalDateTime startAt, LocalDateTime finishAt) {
+        return RecordDTO.builder()
+                .studentId(studentId)
+                .questionId(questionId)
+                .studentAnswer(answer)
+                .pass(pass)
+                .startAt(startAt)
+                .finishAt(finishAt)
+                .build();
+    }
+
+    public RecordQuestionVO getUserRecordQuestions(String userId, Pageable pageable) {
+        Map<String, ?> uriVariables = ImmutableMap.of(
+                "userId", userId,
+                "page", pageable.getPageNumber() + 1,
+                "size", pageable.getPageSize(),
+                "sort", StringUtils.getSortString(pageable.getSort())
+        );
+        return restRequestUtil.sendGetRequest(questionUrl, RecordQuestionVO.class, uriVariables);
+    }
+}

+ 69 - 0
src/main/java/nju/seec/helper/sys/bok/api/UserInteractiveApi.java

@@ -0,0 +1,69 @@
+package nju.seec.helper.sys.bok.api;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.reflect.TypeToken;
+import lombok.Data;
+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.PageableQuestionListVO;
+import nju.seec.helper.sys.bok.vo.QuestionVO;
+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;
+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.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@Component
+@ConfigurationProperties("bok.user")
+@Data
+public class UserInteractiveApi {
+    private final RestRequestUtil restRequestUtil;
+    private final CaffeineCacheUtils cacheUtils;
+
+    private String rateUrl;
+    private String collectionUrl;
+    private String questionUrl;
+    private String interactiveUrl;
+
+    public void collectQuestion(String userId, String questionId, CollectDTO collectDTO) {
+        restRequestUtil.sendPostRequest(collectionUrl, collectDTO, Object.class, ImmutableMap.of("userId", userId, "questionId", questionId));
+    }
+
+    public void rateQuestion(String userId, String questionId, RateDTO rateDTO) {
+        restRequestUtil.sendPostRequest(rateUrl, rateDTO, Object.class, ImmutableMap.of("userId", userId, "questionId", questionId));
+    }
+
+    public Page<QuestionVO> getCollectAndRateQuestions(String userId, Boolean collect, Boolean rate, Pageable pageable) {
+        System.out.println(StringUtils.getSortString(pageable.getSort()));
+        Map<String, ?> uriVariables = ImmutableMap.<String, Object>builder()
+                .put("userId", userId)
+                .put("collection", collect == null ? "" : collect)
+                .put("rate", rate == null ? "" : rate)
+                .put("page", pageable.getPageNumber() + 1)
+                .put("size", pageable.getPageSize())
+                .put("sort", StringUtils.getSortString(pageable.getSort()))
+                .build();
+        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>>() {
+        });
+    }
+}

+ 28 - 0
src/main/java/nju/seec/helper/sys/bok/constant/QuestionType.java

@@ -0,0 +1,28 @@
+package nju.seec.helper.sys.bok.constant;
+
+
+/**
+ * @author bok
+ */
+public enum QuestionType {
+    /**
+     * 选择题
+     */
+    choice,
+    /**
+     * 判断题
+     */
+    true_false,
+    /**
+     * 多选题
+     */
+    multiple_choice,
+    /**
+     * 填空题
+     */
+    blank,
+    /**
+     * 简答题
+     */
+    short_answer
+}

+ 16 - 0
src/main/java/nju/seec/helper/sys/bok/dto/CollectDTO.java

@@ -0,0 +1,16 @@
+package nju.seec.helper.sys.bok.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author bok
+ * <p>
+ * updated by cst
+ */
+@Data
+public class CollectDTO {
+    @NotNull(message = "缺少是否收藏")
+    Boolean collect;
+}

+ 23 - 0
src/main/java/nju/seec/helper/sys/bok/dto/RateDTO.java

@@ -0,0 +1,23 @@
+package nju.seec.helper.sys.bok.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author bok
+ * <p>
+ * updated by cst
+ */
+@Data
+public class RateDTO {
+    /**
+     * 取值 1-5
+     */
+    @Max(value = 5, message = "最大评分为5")
+    @Min(value = 1, message = "最小评分为1")
+    @NotNull(message = "缺少评分")
+    Integer rate;
+}

+ 20 - 0
src/main/java/nju/seec/helper/sys/bok/dto/RecordDTO.java

@@ -0,0 +1,20 @@
+package nju.seec.helper.sys.bok.dto;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author Kunduin
+ */
+@Data
+@Builder
+public class RecordDTO {
+    String questionId;
+    String studentId;
+    Object studentAnswer;
+    Boolean pass;
+    LocalDateTime startAt;
+    LocalDateTime finishAt;
+}

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

@@ -0,0 +1,20 @@
+package nju.seec.helper.sys.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;
+}

+ 16 - 0
src/main/java/nju/seec/helper/sys/bok/vo/KnowledgeNodeVO.java

@@ -0,0 +1,16 @@
+package nju.seec.helper.sys.bok.vo;
+
+import lombok.Data;
+
+/**
+ * @author cst
+ */
+@Data
+public class KnowledgeNodeVO {
+    private String id;
+    private String abbreviation;
+    private String name;
+    private String englishName;
+    private String parentId;
+    private String parentName;
+}

+ 14 - 0
src/main/java/nju/seec/helper/sys/bok/vo/PageVO.java

@@ -0,0 +1,14 @@
+package nju.seec.helper.sys.bok.vo;
+
+import lombok.Data;
+
+/**
+ * @author Kunduin
+ */
+@Data
+public class PageVO {
+    Integer size;
+    Long totalElements;
+    Integer totalPages;
+    Integer number;
+}

+ 14 - 0
src/main/java/nju/seec/helper/sys/bok/vo/PageableQuestionListVO.java

@@ -0,0 +1,14 @@
+package nju.seec.helper.sys.bok.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author Kunduin
+ */
+@Data
+public class PageableQuestionListVO {
+    List<QuestionVO> questions;
+    PageVO page;
+}

+ 31 - 0
src/main/java/nju/seec/helper/sys/bok/vo/QuestionVO.java

@@ -0,0 +1,31 @@
+package nju.seec.helper.sys.bok.vo;
+
+import lombok.Data;
+import nju.seec.helper.sys.bok.constant.QuestionType;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Kunduin
+ */
+@Data
+public class QuestionVO {
+    String id;
+    QuestionType type;
+    String stem;
+    Map<String, String> options;
+
+    /**
+     * 填空题 __1__ __2__
+     */
+    Map<String, String> blanks;
+
+    String answer;
+    String keyPoints;
+    String analysis;
+    List<String> tags;
+    List<String> knowledgeId;
+    Date lastModifiedTime;
+}

+ 21 - 0
src/main/java/nju/seec/helper/sys/bok/vo/RecordQuestionVO.java

@@ -0,0 +1,21 @@
+package nju.seec.helper.sys.bok.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * @author bok
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class RecordQuestionVO {
+    PageVO page;
+    List<QuestionVO> questions;
+    List<RecordVO> records;
+}

+ 25 - 0
src/main/java/nju/seec/helper/sys/bok/vo/RecordVO.java

@@ -0,0 +1,25 @@
+package nju.seec.helper.sys.bok.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author Kunduin
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class RecordVO {
+    String id;
+    String userId;
+    String questionId;
+    Object studentAnswer;
+    Boolean pass;
+    LocalDateTime startAt;
+    LocalDateTime finishAt;
+}

+ 87 - 0
src/main/java/nju/seec/helper/sys/dataanalysis/api/EventApi.java

@@ -0,0 +1,87 @@
+package nju.seec.helper.sys.dataanalysis.api;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Data;
+import nju.seec.helper.entity.Course;
+import nju.seec.helper.entity.Quiz;
+import nju.seec.helper.sys.dataanalysis.constant.EventAction;
+import nju.seec.helper.sys.dataanalysis.constant.EventType;
+import nju.seec.helper.sys.dataanalysis.model.Event;
+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;
+
+/**
+ * @author cst
+ */
+@Component
+@ConfigurationProperties("data-analysis.event")
+@Data
+public class EventApi {
+    private String reportUrl;
+    private final RestRequestUtil restRequestUtil;
+
+    @Async
+    public void reportEvent(Event event) {
+//        System.out.println(event);
+//        restRequestUtil.sendPostRequest(eventReportUrl, event, Boolean.class);
+    }
+
+    /**
+     * 取得参与课程事件
+     *
+     * @param userId
+     * @param time
+     * @param course
+     * @return
+     */
+    public Event createParticipateClassEventDTO(String userId, LocalDateTime time, Course course) {
+        return Event.builder()
+                .userId(userId).time(time)
+                .type(EventType.LESSON)
+                .action(EventAction.PARTICIPATE)
+                .description(course.getName())
+                .build();
+    }
+
+    /**
+     * 取得创建帖子事件
+     *
+     * @param userId
+     * @param time
+     * @param title
+     * @return
+     */
+    public Event createInitPostEventDTO(String userId, LocalDateTime time, String title) {
+        return Event
+                .builder()
+                .userId(userId)
+                .time(time)
+                .type(EventType.POST)
+                .action(EventAction.INITIATE)
+                .description(title)
+                .build();
+    }
+
+    /**
+     * 取得完成测验事件
+     *
+     * @param userId
+     * @param time
+     * @param quiz
+     * @return
+     */
+    public Event createCompleteQuizEventDTO(String userId, LocalDateTime time, Quiz quiz) {
+        return Event
+                .builder()
+                .userId(userId)
+                .time(time)
+                .type(EventType.QUIZ)
+                .action(EventAction.COMPLETE)
+                .description(StrUtil.join(":", quiz.getCourse().getName(), quiz.getName()))
+                .build();
+    }
+}

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

@@ -0,0 +1,40 @@
+package nju.seec.helper.sys.dataanalysis.api;
+
+import lombok.Data;
+import nju.seec.helper.sys.dataanalysis.model.ExerciseRecord;
+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;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * @author cst
+ */
+@Component
+@ConfigurationProperties("data-analysis.exercise")
+@Data
+public class ExerciseApi {
+    private String reportUrl;
+    private final RestRequestUtil restRequestUtil;
+
+    @Async
+    public void reportExercise(List<ExerciseRecord> exerciseRecords) {
+        System.out.println(exerciseRecords);
+//        restRequestUtil.sendPostRequest(exerciseReportUrl, exerciseRecords, Boolean.class);
+    }
+
+    public ExerciseRecord createExerciseRecordDTO(String userId, String questionId, LocalDateTime startAt, LocalDateTime endAt, Boolean pass) {
+        return ExerciseRecord.builder()
+                .userId(userId)
+                .questionId(questionId)
+                .startAt(startAt)
+                .endAt(endAt)
+                .durationSeconds(Duration.between(startAt, endAt).getSeconds())
+                .pass(pass)
+                .build();
+    }
+}

+ 29 - 0
src/main/java/nju/seec/helper/sys/dataanalysis/constant/EventAction.java

@@ -0,0 +1,29 @@
+package nju.seec.helper.sys.dataanalysis.constant;
+
+import lombok.Getter;
+
+/**
+ * @author seec data analysis
+ */
+@Getter
+public enum EventAction {
+    /**
+     * 参与
+     */
+    PARTICIPATE("参与"),
+    /**
+     * 完成
+     */
+    COMPLETE("完成"),
+    /**
+     * 创建
+     */
+    INITIATE("创建"),
+    ;
+
+    private final String description;
+
+    EventAction(String description) {
+        this.description = description;
+    }
+}

+ 37 - 0
src/main/java/nju/seec/helper/sys/dataanalysis/constant/EventType.java

@@ -0,0 +1,37 @@
+package nju.seec.helper.sys.dataanalysis.constant;
+
+import lombok.Getter;
+
+/**
+ * @author seec data analysis
+ */
+@Getter
+public enum EventType {
+    /**
+     * 课程事件
+     */
+    LESSON("课程"),
+    /**
+     * 帖子事件
+     */
+    POST("帖子"),
+    /**
+     * 测验事件
+     */
+    QUIZ("课堂测验"),
+    /**
+     * 考试事件
+     */
+    EXAM("考试"),
+    /**
+     * 作业事件
+     */
+    HOMEWORK("作业"),
+    ;
+
+    private final String description;
+
+    EventType(String description) {
+        this.description = description;
+    }
+}

+ 21 - 0
src/main/java/nju/seec/helper/sys/dataanalysis/model/Event.java

@@ -0,0 +1,21 @@
+package nju.seec.helper.sys.dataanalysis.model;
+
+import lombok.Builder;
+import lombok.Data;
+import nju.seec.helper.sys.dataanalysis.constant.EventAction;
+import nju.seec.helper.sys.dataanalysis.constant.EventType;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author seec data analysis
+ */
+@Data
+@Builder
+public class Event {
+    private String userId;
+    private EventType type;
+    private LocalDateTime time;
+    private EventAction action;
+    private String description;
+}

+ 38 - 0
src/main/java/nju/seec/helper/sys/dataanalysis/model/ExerciseRecord.java

@@ -0,0 +1,38 @@
+package nju.seec.helper.sys.dataanalysis.model;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author seec data analysis
+ */
+@Data
+@Builder
+public class ExerciseRecord {
+    /**
+     * 问题的ID
+     */
+    private String questionId;
+    /**
+     * 用户的ID
+     */
+    private String userId;
+    /**
+     * 做题开始时间
+     */
+    private LocalDateTime startAt;
+    /**
+     * 做题结束时间
+     */
+    private LocalDateTime endAt;
+    /**
+     * 做题的时间(秒)
+     */
+    private Long durationSeconds;
+    /**
+     * 该题是否通过
+     */
+    private Boolean pass;
+}

+ 3 - 1
src/main/java/nju/seec/helper/util/OssUtils.java

@@ -60,7 +60,9 @@ public class OssUtils {
         Date expiration = new Date(System.currentTimeMillis() + objectUrlLivingSeconds * 1000);
         try {
             URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
-            return url.toString();
+            String objectUrl = url.toString();
+            cacheUtils.set(objectUrlCacheName, objectName, objectUrl);
+            return objectUrl;
         } catch (OSSException | ClientException e) {
             log.error(e.getMessage());
             throw HelperException.of(ExceptionType.ERROR, "获取文件链接失败");

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

@@ -2,8 +2,8 @@ package nju.seec.helper.vo.question;
 
 import lombok.Data;
 import lombok.NonNull;
-import nju.seec.helper.api.bok.constant.QuestionType;
-import nju.seec.helper.api.bok.vo.QuestionVO;
+import nju.seec.helper.sys.bok.constant.QuestionType;
+import nju.seec.helper.sys.bok.vo.QuestionVO;
 
 import java.util.Date;
 import java.util.List;

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

@@ -3,7 +3,7 @@ package nju.seec.helper.vo.question;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NonNull;
-import nju.seec.helper.api.bok.vo.QuestionVO;
+import nju.seec.helper.sys.bok.vo.QuestionVO;
 
 /**
  * @author xst

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

@@ -4,7 +4,7 @@ import cn.hutool.core.convert.Convert;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NonNull;
-import nju.seec.helper.api.bok.vo.QuestionVO;
+import nju.seec.helper.sys.bok.vo.QuestionVO;
 
 /**
  * @author xst