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

+ 15 - 15
pom.xml

@@ -85,26 +85,26 @@
             <artifactId>pdfbox</artifactId>
             <version>2.0.18</version>
         </dependency>
-        <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi-ooxml</artifactId>
-            <version>4.1.2</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi-scratchpad</artifactId>
-            <version>4.1.2</version>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi-ooxml</artifactId>-->
+<!--            <version>4.1.2</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi-scratchpad</artifactId>-->
+<!--            <version>4.1.2</version>-->
+<!--        </dependency>-->
         <dependency>
             <groupId>com.aliyun.oss</groupId>
             <artifactId>aliyun-sdk-oss</artifactId>
             <version>3.8.1</version>
         </dependency>
-        <dependency>
-            <groupId>com.aliyun</groupId>
-            <artifactId>aliyun-java-sdk-core</artifactId>
-            <version>4.0.3</version>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>com.aliyun</groupId>-->
+<!--            <artifactId>aliyun-java-sdk-core</artifactId>-->
+<!--            <version>4.0.3</version>-->
+<!--        </dependency>-->
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>

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

@@ -0,0 +1,52 @@
+package nju.seec.helper.api.bok;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import nju.seec.helper.util.Consts;
+import nju.seec.helper.util.RestRequestUtil;
+import nju.seec.helper.util.cache.GuavaCacheUtils;
+import nju.seec.helper.vo.knowledge.BokKnowledgeNodeVO;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+@Component
+public class KnowledgeApi {
+    private final RestRequestUtil restRequestUtil;
+    private final GuavaCacheUtils cacheUtils;
+
+    @Value("${bok.knowledgeUrl}")
+    private String knowledgeUrl;
+
+    @Value("${bok.knowledgeGetAllUrl}")
+    private String knowledgeGetAllUrl;
+
+    private static final String BOK_KNOWLEDGE_CACHE_NAME = Consts.SYS_NAME + "_bok_knowledge";
+
+    public KnowledgeApi(RestRequestUtil restRequestUtil, GuavaCacheUtils cacheUtils) {
+        this.restRequestUtil = restRequestUtil;
+        this.cacheUtils = cacheUtils;
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<BokKnowledgeNodeVO> getKnowledgeNodes() {
+        Object cache = cacheUtils.get(BOK_KNOWLEDGE_CACHE_NAME, "all");
+        if (cache instanceof List) {
+            return (List<BokKnowledgeNodeVO>) cache;
+        }
+        List<BokKnowledgeNodeVO> bokKnowledgeNodeVOList = restRequestUtil.sendPostRequest(knowledgeGetAllUrl, KnowledgeGetDTO.of("", "part of"), List.class);
+        cacheUtils.set(BOK_KNOWLEDGE_CACHE_NAME, "all", bokKnowledgeNodeVOList);
+        return bokKnowledgeNodeVOList;
+    }
+
+    @AllArgsConstructor(staticName = "of")
+    @Data
+    private static class KnowledgeGetDTO {
+        private String name;
+        private String relation;
+    }
+}

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

@@ -0,0 +1,171 @@
+package nju.seec.helper.api.bok;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+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.enums.QuestionType;
+import nju.seec.helper.exception.HelperException;
+import nju.seec.helper.util.Consts;
+import nju.seec.helper.util.RestRequestUtil;
+import nju.seec.helper.util.cache.GuavaCacheUtils;
+import nju.seec.helper.vo.question.BokQuestionVO;
+import org.springframework.beans.factory.annotation.Value;
+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.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@Component
+public class QuestionApi {
+    private final RestRequestUtil restRequestUtil;
+    private final GuavaCacheUtils cacheUtils;
+    @Value("${bok.tqUrl}")
+    private String tqUrl;
+    @Value("${bok.tqStemSearchUrl}")
+    private String tqStemSearchUrl;
+    @Value("${bok.tqIdSearchUrl}")
+    private String tqIdSearchUrl;
+
+    public QuestionApi(RestRequestUtil restRequestUtil, GuavaCacheUtils cacheUtils) {
+        this.restRequestUtil = restRequestUtil;
+        this.cacheUtils = cacheUtils;
+    }
+
+    public Page<BokQuestionVO> bokFindByStemLike(String stem, Pageable pageable) {
+        Map<String, String> urlParams = ImmutableMap.of(
+                "content", stem,
+                "page", String.valueOf(1 + pageable.getPageNumber()),
+                "size", String.valueOf(pageable.getPageSize())
+        );
+        BokSearchResult result = restRequestUtil.sendGetRequest(tqStemSearchUrl, BokSearchResult.class, urlParams);
+
+        List<BokQuestionVO> bokQuestionVOList = result.getEmbedded().getQuestions();
+        cacheUtils.setAll(BOK_QUESTION_CACHE_NAME, bokQuestionVOList.parallelStream().collect(Collectors.toMap(BokQuestionVO::getId, bokQuestion -> bokQuestion)));
+        return new PageImpl<>(bokQuestionVOList, pageable, result.getPage().getTotalElements());
+    }
+
+    private static final String BOK_QUESTION_CACHE_NAME = Consts.SYS_NAME + "_bok_question";
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public List<BokQuestionVO> bokFindByIdIn(final List<String> ids) {
+        Map<String, BokQuestionVO> bokQuestionsMap = Maps.newHashMapWithExpectedSize(ids.size());
+
+        final Map<String, BokQuestionVO> cacheBokQuestionsMap = (Map) cacheUtils.multiGet(BOK_QUESTION_CACHE_NAME, ids);
+        bokQuestionsMap.putAll(cacheBokQuestionsMap);
+
+        List<String> requestIds = Lists.newArrayList(ids);
+        requestIds.removeAll(cacheBokQuestionsMap.keySet());
+        //未缓存的去这里拿
+        if (!requestIds.isEmpty()) {
+            Map<String, String> urlParams = ImmutableMap.of("id", requestIds.stream().reduce((a, b) -> a + "," + b).orElse(""));
+
+            BokSearchResult result = restRequestUtil.sendGetRequest(tqIdSearchUrl, BokSearchResult.class, urlParams);
+            List<BokQuestionVO> bokQuestionVOList = result.getEmbedded().getQuestions();
+
+            Map<String, BokQuestionVO> remoteBokQuestionsMap = bokQuestionVOList.parallelStream().collect(Collectors.toMap(BokQuestionVO::getId, bokQuestion -> bokQuestion));
+
+            bokQuestionsMap.putAll(remoteBokQuestionsMap);
+            cacheUtils.setAll(BOK_QUESTION_CACHE_NAME, (Map) remoteBokQuestionsMap);
+        }
+
+        return ids.stream()
+                .map(bokQuestionsMap::get)
+                .collect(Collectors.toList());
+    }
+
+    public BokQuestionVO bokFindById(String questionId) {
+        Object cachedBokQuestion = cacheUtils.get(BOK_QUESTION_CACHE_NAME, questionId);
+        if (cachedBokQuestion instanceof BokQuestionVO) {
+            return (BokQuestionVO) cachedBokQuestion;
+        }
+        BokQuestionVO bokQuestionVO = restRequestUtil.sendGetRequest(tqUrl + "/" + questionId, BokQuestionVO.class, Collections.emptyMap());
+        cacheUtils.set(BOK_QUESTION_CACHE_NAME, bokQuestionVO.getId(), bokQuestionVO);
+        return bokQuestionVO;
+    }
+
+    public BokQuestionVO createQuestion(String questionId, BaseQuestionDTO baseQuestionDTO) {
+        BokQuestionVO bokQuestionVO = getQuestion(baseQuestionDTO);
+        bokQuestionVO.setId(questionId);
+        bokQuestionVO = restRequestUtil.sendPostRequest(tqUrl, bokQuestionVO, BokQuestionVO.class);
+        return bokQuestionVO;
+    }
+
+    public BokQuestionVO modifyQuestion(String questionId, BaseQuestionDTO baseQuestionDTO) {
+        BokQuestionVO bokQuestionVO = getQuestion(baseQuestionDTO);
+        bokQuestionVO.setId(questionId);
+        restRequestUtil.sendPutRequest(tqUrl + "/" + questionId, bokQuestionVO);
+        return bokFindById(questionId);
+    }
+
+    public void deleteQuestion(String questionId) {
+        restRequestUtil.sendDeleteRequest(tqUrl + "/" + questionId);
+    }
+
+    public void starQuestion(String userId, String questionId) {
+
+    }
+
+    private BokQuestionVO getQuestion(BaseQuestionDTO baseQuestionDTO) {
+        BokQuestionVO bokQuestionVO = new BokQuestionVO();
+        bokQuestionVO.setType(baseQuestionDTO.getType());
+        bokQuestionVO.setStem(baseQuestionDTO.getStem());
+        bokQuestionVO.setKeyPoints(baseQuestionDTO.getKeyPoints());
+        bokQuestionVO.setTags(baseQuestionDTO.getTags());
+        bokQuestionVO.setKnowledgeId(baseQuestionDTO.getKnowledgeId());
+
+        final QuestionType questionType = QuestionType.getQuestionType(baseQuestionDTO.getType());
+        switch (Objects.requireNonNull(questionType)) {
+            case CHOICE:
+                ChoiceQuestionDTO choiceQuestionDTO = (ChoiceQuestionDTO) baseQuestionDTO;
+                bokQuestionVO.setOptions(choiceQuestionDTO.getOptions());
+                bokQuestionVO.setAnswer(choiceQuestionDTO.getAnswer());
+                bokQuestionVO.setAnalysis(choiceQuestionDTO.getAnalysis());
+                break;
+            case TRUE_FALSE:
+                TrueOrFalseQuestionDTO trueOrFalseQuestionDTO = (TrueOrFalseQuestionDTO) baseQuestionDTO;
+                bokQuestionVO.setAnswer(String.valueOf(trueOrFalseQuestionDTO.getAnswer()));
+                bokQuestionVO.setAnalysis(trueOrFalseQuestionDTO.getAnalysis());
+                break;
+            default:
+                throw HelperException.of(ExceptionType.ERROR, "不支持的题目类型");
+        }
+        return bokQuestionVO;
+    }
+
+    @Data
+    private static class BokSearchResult {
+        @JsonProperty("_embedded")
+        private Embedded embedded = new Embedded();
+        private Page page;
+
+        @Data
+        private static class Embedded {
+            private List<BokQuestionVO> questions = Collections.emptyList();
+        }
+
+        @Data
+        private static class Page {
+            private int size;
+            private int number;
+            private int totalPages;
+            private long totalElements;
+        }
+    }
+}

+ 20 - 0
src/main/java/nju/seec/helper/api/dataanalysis/EventAction.java

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

+ 12 - 2
src/main/java/nju/seec/helper/api/dataanalysis/EventApi.java

@@ -1,5 +1,7 @@
 package nju.seec.helper.api.dataanalysis;
 
+import nju.seec.helper.util.RestRequestUtil;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
@@ -8,8 +10,16 @@ import org.springframework.stereotype.Component;
  */
 @Component
 public class EventApi {
-    @Async
-    public void reportEvent() {
+    @Value("${data-analysis.eventReportUrl}")
+    private String eventReportUrl;
+    private final RestRequestUtil restRequestUtil;
+
+    public EventApi(RestRequestUtil restRequestUtil) {
+        this.restRequestUtil = restRequestUtil;
+    }
 
+    @Async
+    public void reportEvent(EventDTO eventDTO) {
+        restRequestUtil.sendPostRequest(eventReportUrl, eventDTO, Boolean.class);
     }
 }

+ 17 - 0
src/main/java/nju/seec/helper/api/dataanalysis/EventDTO.java

@@ -0,0 +1,17 @@
+package nju.seec.helper.api.dataanalysis;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author cst
+ */
+@Data
+public class EventDTO {
+    private String userId;
+    private EventType type;
+    private LocalDateTime time;
+    private EventAction action;
+    private String description;
+}

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

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

+ 9 - 4
src/main/java/nju/seec/helper/aspect/auth/AuthAspect.java

@@ -44,7 +44,7 @@ public class AuthAspect {
 
     @Before("execution(public * nju.seec.helper.controller.*.*(..)) && @annotation(auth)")
     public void authCheck(JoinPoint joinPoint, Auth auth) {
-        User user = getUser();
+        LoginUser user = getLoginUser();
 
         if (!Arrays
                 .stream(auth.roles())
@@ -62,11 +62,10 @@ public class AuthAspect {
         }
     }
 
-    private User getUser() {
+    private LoginUser getLoginUser() {
         HttpServletRequest httpServletRequest =
                 ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
                         .getRequest();
-
         String token = Optional.ofNullable(httpServletRequest.getHeader("Authorization"))
                 .orElseThrow(() -> HelperException.of(ExceptionType.UNAUTHORIZED, "缺少Token"));
 
@@ -88,6 +87,10 @@ public class AuthAspect {
 
         UserInfo userInfo = decodedJWT.getClaim("user_info").as(UserInfo.class);
 
+        if (userInfo.role == UserRole.ADMIN) {
+            return LoginUser.of(null, UserRole.ADMIN);
+        }
+
         User user = userService.getUserByPhoneOrPid(userInfo.phone, userInfo.id).orElse(new User());
 
         user.setName(userInfo.name)
@@ -96,7 +99,9 @@ public class AuthAspect {
                 .setPhone(userInfo.phone)
                 .setPid(userInfo.id);
 
-        return userService.createOrUpdateUser(user);
+        user = userService.createOrUpdateUser(user);
+
+        return LoginUser.of(user.getId(), user.getRole());
     }
 
     @Data

+ 4 - 2
src/main/java/nju/seec/helper/aspect/auth/LoginUser.java

@@ -1,14 +1,16 @@
 package nju.seec.helper.aspect.auth;
 
+import lombok.AllArgsConstructor;
 import lombok.Data;
-import lombok.experimental.Accessors;
+import lombok.NoArgsConstructor;
 import nju.seec.helper.enums.UserRole;
 
 /**
  * @author cst
  */
 @Data
-@Accessors(chain = true)
+@AllArgsConstructor(staticName = "of")
+@NoArgsConstructor
 public class LoginUser {
     private Long id;
     private UserRole role;

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

@@ -0,0 +1,27 @@
+package nju.seec.helper.controller;
+
+import nju.seec.helper.vo.EventVO;
+import nju.seec.helper.service.EventService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+@RestController
+@RequestMapping("/api/event")
+public class EventController {
+    private final EventService eventService;
+
+    public EventController(EventService eventService) {
+        this.eventService = eventService;
+    }
+
+    @GetMapping
+    public List<EventVO> getAllEvents() {
+        return eventService.getAllEvents();
+    }
+}

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

@@ -1,6 +1,6 @@
 package nju.seec.helper.controller;
 
-import nju.seec.helper.api.bok.BokKnowledgeApi;
+import nju.seec.helper.api.bok.KnowledgeApi;
 import nju.seec.helper.vo.knowledge.BokKnowledgeNodeVO;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -14,14 +14,14 @@ import java.util.List;
 @RestController
 @RequestMapping("/api/knowledge")
 public class KnowledgeController {
-    private final BokKnowledgeApi bokKnowledgeApi;
+    private final KnowledgeApi knowledgeApi;
 
-    public KnowledgeController(BokKnowledgeApi bokKnowledgeApi) {
-        this.bokKnowledgeApi = bokKnowledgeApi;
+    public KnowledgeController(KnowledgeApi knowledgeApi) {
+        this.knowledgeApi = knowledgeApi;
     }
 
     @GetMapping
     public List<BokKnowledgeNodeVO> getKnowledgeNodes() {
-        return bokKnowledgeApi.getKnowledgeNodes();
+        return knowledgeApi.getKnowledgeNodes();
     }
 }

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

@@ -0,0 +1,17 @@
+package nju.seec.helper.service;
+
+import nju.seec.helper.vo.EventVO;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+public interface EventService {
+    /**
+     * 取得所有事件
+     *
+     * @return
+     */
+    List<EventVO> getAllEvents();
+}

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

@@ -0,0 +1,62 @@
+package nju.seec.helper.service.impl;
+
+import com.google.common.collect.Lists;
+import nju.seec.helper.api.dataanalysis.EventType;
+import nju.seec.helper.dao.*;
+import nju.seec.helper.entity.Comment;
+import nju.seec.helper.entity.Course;
+import nju.seec.helper.entity.User;
+import nju.seec.helper.service.EventService;
+import nju.seec.helper.vo.EventVO;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author cst
+ */
+@Service
+public class EventServiceImpl implements EventService {
+    private final CourseDAO courseDAO;
+    private final ChooseDAO chooseDAO;
+    private final UserDAO userDAO;
+    private final CommentDAO commentDAO;
+    private final QuizDAO quizDAO;
+    private final QuizStudentAnswerDAO quizStudentAnswerDAO;
+
+    public EventServiceImpl(CourseDAO courseDAO, ChooseDAO chooseDAO, UserDAO userDAO, CommentDAO commentDAO, QuizDAO quizDAO, QuizStudentAnswerDAO quizStudentAnswerDAO) {
+        this.courseDAO = courseDAO;
+        this.chooseDAO = chooseDAO;
+        this.userDAO = userDAO;
+        this.commentDAO = commentDAO;
+        this.quizDAO = quizDAO;
+        this.quizStudentAnswerDAO = quizStudentAnswerDAO;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<EventVO> getAllEvents() {
+//        List<EventVO> eventVOList = Lists.newArrayList();
+//        List<Course> courses = courseDAO.findAll();
+//        courses.forEach(course -> {
+//            Set<Long> studentIds = chooseDAO.findStudentIdsByCourseId(course.getId());
+//            eventVOList.addAll(studentIds.parallelStream()
+//                    .map(studentId -> userDAO.findUserById(studentId))
+//                    .filter(user -> user.getPid() != null)
+//                    .map(user -> EventVO.of(user.getPid(), EventType.LESSON, ))
+//                    .collect(Collectors.toSet()))
+//            studentIds.forEach(studentId -> {
+//                User user = userDAO.findUserById(studentId);
+//                if (user.getPid() != null) {
+//                    eventVOList.addAll();
+//                }
+//            });
+//        });
+//        List<Comment> comments = commentDAO.findAll();
+        return Collections.emptyList();
+    }
+}

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

@@ -1,7 +1,7 @@
 package nju.seec.helper.service.impl;
 
 import cn.hutool.core.util.IdUtil;
-import nju.seec.helper.api.bok.BokQuestionApi;
+import nju.seec.helper.api.bok.QuestionApi;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dao.QuestionRecordDAO;
 import nju.seec.helper.dao.QuizDAO;
@@ -38,21 +38,21 @@ public class QuestionServiceImpl implements QuestionService {
     private final UserDAO userDAO;
     private final QuestionRecordDAO questionRecordDAO;
 
-    private final BokQuestionApi bokQuestionApi;
+    private final QuestionApi questionApi;
 
     @Autowired
     public QuestionServiceImpl(QuizDAO quizDAO
             , UserDAO userDAO
-            , QuestionRecordDAO questionRecordDAO, BokQuestionApi bokQuestionApi) {
+            , QuestionRecordDAO questionRecordDAO, QuestionApi questionApi) {
         this.quizDAO = quizDAO;
         this.userDAO = userDAO;
         this.questionRecordDAO = questionRecordDAO;
-        this.bokQuestionApi = bokQuestionApi;
+        this.questionApi = questionApi;
     }
 
     @Override
     public Page<BaseQuestionVO> getQuestions(LoginUser user, String stem, Pageable pageable) {
-        return bokQuestionApi.bokFindByStemLike(stem, pageable).map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true));
+        return questionApi.bokFindByStemLike(stem, pageable).map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true));
     }
 
     @Transactional(readOnly = true)
@@ -60,7 +60,7 @@ public class QuestionServiceImpl implements QuestionService {
     public List<BaseQuestionVO> getQuestionsByQuiz(LoginUser user, Long quizId) {
         Quiz quiz = quizDAO.findQuizById(quizId);
         List<String> questionIds = quiz.getQuestions();
-        return bokQuestionApi.bokFindByIdIn(questionIds)
+        return questionApi.bokFindByIdIn(questionIds)
                 .stream()
                 .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, quiz.getState() == QuizState.CLOSED || user.getRole() == UserRole.TEACHER))
                 .collect(Collectors.toList());
@@ -70,7 +70,7 @@ public class QuestionServiceImpl implements QuestionService {
     @Override
     public List<BaseQuestionVO> getQuestionsByQuiz(Quiz quiz) {
         List<String> questionIds = quiz.getQuestions();
-        return bokQuestionApi.bokFindByIdIn(questionIds)
+        return questionApi.bokFindByIdIn(questionIds)
                 .stream()
                 .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true))
                 .collect(Collectors.toList());
@@ -78,7 +78,7 @@ public class QuestionServiceImpl implements QuestionService {
 
     @Override
     public BaseQuestionVO getOneQuestion(LoginUser user, String questionId) {
-        return BaseQuestionVO.convertBokQuestionToVO(bokQuestionApi.bokFindById(questionId), true);
+        return BaseQuestionVO.convertBokQuestionToVO(questionApi.bokFindById(questionId), true);
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -90,7 +90,7 @@ public class QuestionServiceImpl implements QuestionService {
             questionId = IdUtil.fastSimpleUUID();
         } while (questionRecordDAO.existsByQuestionId(questionId));
 
-        BokQuestionVO bokQuestionVO = bokQuestionApi.createQuestion(questionId, baseQuestionDTO);
+        BokQuestionVO bokQuestionVO = questionApi.createQuestion(questionId, baseQuestionDTO);
         questionRecordDAO.save(new QuestionRecord()
                 .setTeacher(userDAO.findUserById(user.getId()))
                 .setQuestionId(bokQuestionVO.getId()));
@@ -102,7 +102,7 @@ public class QuestionServiceImpl implements QuestionService {
     public BaseQuestionVO modifyQuestion(LoginUser user, String questionId, BaseQuestionDTO baseQuestionDTO) {
         QuestionRecord questionRecord = questionRecordDAO.findByTeacherAndQuestionId(userDAO.findUserById(user.getId()), questionId)
                 .orElseThrow(() -> HelperException.of(ExceptionType.FORBIDDEN, "您无权修改该问题"));
-        BokQuestionVO bokQuestionVO = bokQuestionApi.modifyQuestion(questionId, baseQuestionDTO);
+        BokQuestionVO bokQuestionVO = questionApi.modifyQuestion(questionId, baseQuestionDTO);
         questionRecordDAO.save(questionRecord);
         return BaseQuestionVO.convertBokQuestionToVO(bokQuestionVO, true);
     }
@@ -112,7 +112,7 @@ public class QuestionServiceImpl implements QuestionService {
     public void deleteQuestion(LoginUser user, String questionId) {
         QuestionRecord questionRecord = questionRecordDAO.findByTeacherAndQuestionId(userDAO.findUserById(user.getId()), questionId)
                 .orElseThrow(() -> HelperException.of(ExceptionType.FORBIDDEN, "您无权删除该问题"));
-        bokQuestionApi.deleteQuestion(questionId);
+        questionApi.deleteQuestion(questionId);
         questionRecordDAO.delete(questionRecord);
     }
 
@@ -122,7 +122,7 @@ public class QuestionServiceImpl implements QuestionService {
         Page<String> questionIdPage = questionRecordDAO.findQuestionIdsByTeacher(userDAO.findUserById(user.getId()), pageable);
 
         return new PageImpl<>(
-                bokQuestionApi.bokFindByIdIn(questionIdPage.getContent())
+                questionApi.bokFindByIdIn(questionIdPage.getContent())
                         .stream()
                         .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true))
                         .collect(Collectors.toList())
@@ -139,6 +139,6 @@ public class QuestionServiceImpl implements QuestionService {
 
     @Override
     public void starQuestion(LoginUser user, String questionId) {
-        bokQuestionApi.starQuestion(String.valueOf(user.getId()), questionId);
+        questionApi.starQuestion(String.valueOf(user.getId()), questionId);
     }
 }

+ 1 - 0
src/main/java/nju/seec/helper/service/impl/UserServiceImpl.java

@@ -93,6 +93,7 @@ public class UserServiceImpl implements UserService {
         return new UserVO(userDAO.findUserById(userId));
     }
 
+    @Transactional(readOnly = true)
     @Override
     public Optional<User> getUserByPhoneOrPid(String phone, Long pid) {
         return Optionals.firstNonEmpty(() -> userDAO.findByPhone(phone), () -> userDAO.findByPid(pid));

+ 22 - 25
src/main/java/nju/seec/helper/util/file/FileUtils.java

@@ -8,7 +8,7 @@ import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.exception.HelperException;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.text.PDFTextStripper;
-import org.apache.poi.util.IOUtils;
+import org.apache.tomcat.util.http.fileupload.IOUtils;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -23,15 +23,12 @@ import java.util.Optional;
 @UtilityClass
 public class FileUtils {
     private static final Map<String, FileInfoParser> FILE_INFO_PARSER_MAP = ImmutableMap.of(
-            "pdf", inputStream -> {
-                System.out.println(inputStream);
-                return FileInfo.of(PDDocument.load(inputStream).getNumberOfPages(), "pdf");
-            }
+            "pdf", inputStream -> FileInfo.of(PDDocument.load(inputStream).getNumberOfPages(), "pdf")
     );
 
-    private static final Map<String, FileContentParser> FILE_CONTENT_PARSER_MAP = ImmutableMap.of(
-            "pdf", inputStream -> new PDFTextStripper().getText(PDDocument.load(inputStream))
-    );
+//    private static final Map<String, FileContentParser> FILE_CONTENT_PARSER_MAP = ImmutableMap.of(
+//            "pdf", inputStream -> new PDFTextStripper().getText(PDDocument.load(inputStream))
+//    );
 
     @SneakyThrows
     public FileInfo getFileInfo(InputStream inputStream) {
@@ -42,12 +39,12 @@ public class FileUtils {
                 .parse(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
     }
 
-    @SneakyThrows
-    public String getFileContent(InputStream inputStream) {
-        return Optional.ofNullable(FILE_CONTENT_PARSER_MAP.get(FileTypeUtil.getType(inputStream)))
-                .orElseThrow(() -> HelperException.of(ExceptionType.FORBIDDEN, "不支持的文件类型"))
-                .parse(inputStream);
-    }
+//    @SneakyThrows
+//    public String getFileContent(InputStream inputStream) {
+//        return Optional.ofNullable(FILE_CONTENT_PARSER_MAP.get(FileTypeUtil.getType(inputStream)))
+//                .orElseThrow(() -> HelperException.of(ExceptionType.FORBIDDEN, "不支持的文件类型"))
+//                .parse(inputStream);
+//    }
 
     @FunctionalInterface
     private interface FileInfoParser {
@@ -61,15 +58,15 @@ public class FileUtils {
         FileInfo parse(InputStream inputStream) throws IOException;
     }
 
-    @FunctionalInterface
-    private interface FileContentParser {
-        /**
-         * 获取文件内容
-         *
-         * @param inputStream
-         * @return
-         * @throws IOException
-         */
-        String parse(InputStream inputStream) throws IOException;
-    }
+//    @FunctionalInterface
+//    private interface FileContentParser {
+//        /**
+//         * 获取文件内容
+//         *
+//         * @param inputStream
+//         * @return
+//         * @throws IOException
+//         */
+//        String parse(InputStream inputStream) throws IOException;
+//    }
 }

+ 21 - 0
src/main/java/nju/seec/helper/vo/EventVO.java

@@ -0,0 +1,21 @@
+package nju.seec.helper.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import nju.seec.helper.api.dataanalysis.EventAction;
+import nju.seec.helper.api.dataanalysis.EventType;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author cst
+ */
+@Data
+@AllArgsConstructor(staticName = "of")
+public class EventVO {
+    private String userId;
+    private EventType type;
+    private LocalDateTime time;
+    private EventAction action;
+    private String description;
+}

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

@@ -49,11 +49,11 @@ aliyun:
     accessKeyId: LTAI4Fi1qmL4iuhH8t7G7r2H
     accessKeySecret: 7tJWyQqa0cMkQGaPMMvtH6c0VLiOO2
     bucketName: seec-helper-pdf
-  sms:
-    accessKeyId: LTAI4FiAXXyNfYJLweBXpKPx
-    accessSecret: IBoC7KGkZHOvxZQIfQgp98ZY48AVjW
-    signName: seeccoder
-    templateCode: SMS_181555598
+#  sms:
+#    accessKeyId: LTAI4FiAXXyNfYJLweBXpKPx
+#    accessSecret: IBoC7KGkZHOvxZQIfQgp98ZY48AVjW
+#    signName: seeccoder
+#    templateCode: SMS_181555598
 bok:
   url: http://bok.seecoder.cn
   tqUrl: ${bok.url}/api/question
@@ -62,6 +62,10 @@ bok:
   tqIdSearchUrl: ${bok.tqSearchUrl}/findByIdIn?id={id}
   knowledgeUrl: ${bok.url}/api/neo
   knowledgeGetAllUrl: ${bok.knowledgeUrl}/getAll
+data-analysis:
+  url: http://dataanalysis.seecoder.cn
+  eventUrl: ${data-analysis.url}/api/event
+  eventReportUrl: ${data-analysis.eventUrl}/report
 guavaCache:
   maximumSize: 1000
   expireAfterAccessInSeconds: 0
@@ -69,8 +73,8 @@ guavaCache:
 jwt:
   secret: SEEC-1919810-OIDC-114514
 # 自定义数据
-helper:
-  mail:
-    from: ${spring.mail.username}
-    subject: seecoder
-  user-timeout-seconds: 43200
+#helper:
+#  mail:
+#    from: ${spring.mail.username}
+#    subject: seecoder
+#  user-timeout-seconds: 43200

+ 14 - 10
src/main/resources/application-dev.yml

@@ -49,11 +49,11 @@ aliyun:
     accessKeyId: LTAI4Fi1qmL4iuhH8t7G7r2H
     accessKeySecret: 7tJWyQqa0cMkQGaPMMvtH6c0VLiOO2
     bucketName: seec-helper-dev
-  sms:
-    accessKeyId: LTAI4FiAXXyNfYJLweBXpKPx
-    accessSecret: IBoC7KGkZHOvxZQIfQgp98ZY48AVjW
-    signName: seeccoder
-    templateCode: SMS_181555598
+#  sms:
+#    accessKeyId: LTAI4FiAXXyNfYJLweBXpKPx
+#    accessSecret: IBoC7KGkZHOvxZQIfQgp98ZY48AVjW
+#    signName: seeccoder
+#    templateCode: SMS_181555598
 bok:
   url: http://bok.seecoder.cn
   tqUrl: ${bok.url}/api/question
@@ -62,6 +62,10 @@ bok:
   tqIdSearchUrl: ${bok.tqSearchUrl}/findByIdIn?id={id}
   knowledgeUrl: ${bok.url}/api/neo
   knowledgeGetAllUrl: ${bok.knowledgeUrl}/getAll
+data-analysis:
+  url: http://dataanalysis.seecoder.cn
+  eventUrl: ${data-analysis.url}/api/event
+  eventReportUrl: ${data-analysis.eventUrl}/report
 guavaCache:
   maximumSize: 1000
   expireAfterAccessInSeconds: 0
@@ -69,8 +73,8 @@ guavaCache:
 jwt:
   secret: SEEC-1919810-OIDC-114514
 # 自定义数据
-helper:
-  mail:
-    from: ${spring.mail.username}
-    subject: seecoder
-  user-timeout-seconds: 43200
+#helper:
+#  mail:
+#    from: ${spring.mail.username}
+#    subject: seecoder
+#  user-timeout-seconds: 43200