Kaynağa Gözat

Merge branch 'iterator5'

ChenSiTong 6 yıl önce
ebeveyn
işleme
9ae8f70358
75 değiştirilmiş dosya ile 1570 ekleme ve 1084 silme
  1. 27 16
      pom.xml
  2. 52 0
      src/main/java/nju/seec/helper/api/bok/KnowledgeApi.java
  3. 52 49
      src/main/java/nju/seec/helper/api/bok/QuestionApi.java
  4. 20 0
      src/main/java/nju/seec/helper/api/dataanalysis/EventAction.java
  5. 25 0
      src/main/java/nju/seec/helper/api/dataanalysis/EventApi.java
  6. 17 0
      src/main/java/nju/seec/helper/api/dataanalysis/EventDTO.java
  7. 37 0
      src/main/java/nju/seec/helper/api/dataanalysis/EventType.java
  8. 2 2
      src/main/java/nju/seec/helper/aspect/auth/Auth.java
  9. 77 11
      src/main/java/nju/seec/helper/aspect/auth/AuthAspect.java
  10. 6 4
      src/main/java/nju/seec/helper/aspect/auth/LoginUser.java
  11. 0 22
      src/main/java/nju/seec/helper/config/PageableConfig.java
  12. 88 88
      src/main/java/nju/seec/helper/controller/AdminController.java
  13. 43 43
      src/main/java/nju/seec/helper/controller/CodeController.java
  14. 9 9
      src/main/java/nju/seec/helper/controller/CommentController.java
  15. 10 10
      src/main/java/nju/seec/helper/controller/CourseController.java
  16. 5 5
      src/main/java/nju/seec/helper/controller/CourseFileController.java
  17. 27 0
      src/main/java/nju/seec/helper/controller/EventController.java
  18. 27 0
      src/main/java/nju/seec/helper/controller/KnowledgeController.java
  19. 5 5
      src/main/java/nju/seec/helper/controller/MessageController.java
  20. 6 6
      src/main/java/nju/seec/helper/controller/NoticeController.java
  21. 15 9
      src/main/java/nju/seec/helper/controller/QuestionController.java
  22. 14 14
      src/main/java/nju/seec/helper/controller/QuizController.java
  23. 3 3
      src/main/java/nju/seec/helper/controller/ReplyController.java
  24. 10 10
      src/main/java/nju/seec/helper/controller/SlideController.java
  25. 75 88
      src/main/java/nju/seec/helper/controller/UserController.java
  26. 9 0
      src/main/java/nju/seec/helper/dao/ChooseDAO.java
  27. 38 22
      src/main/java/nju/seec/helper/dao/UserDAO.java
  28. 3 4
      src/main/java/nju/seec/helper/dto/question/BaseQuestionDTO.java
  29. 17 0
      src/main/java/nju/seec/helper/dto/quiz/QuizStudentAnswerDTO.java
  30. 2 2
      src/main/java/nju/seec/helper/dto/user/UserDTO.java
  31. 6 0
      src/main/java/nju/seec/helper/entity/Choose.java
  32. 3 0
      src/main/java/nju/seec/helper/entity/CourseFile.java
  33. 3 0
      src/main/java/nju/seec/helper/entity/Notice.java
  34. 12 10
      src/main/java/nju/seec/helper/entity/User.java
  35. 1 1
      src/main/java/nju/seec/helper/enums/ExceptionType.java
  36. 20 0
      src/main/java/nju/seec/helper/enums/UserRole.java
  37. 8 8
      src/main/java/nju/seec/helper/enums/UserState.java
  38. 0 9
      src/main/java/nju/seec/helper/enums/UserType.java
  39. 58 58
      src/main/java/nju/seec/helper/service/AdminService.java
  40. 20 20
      src/main/java/nju/seec/helper/service/CodeService.java
  41. 17 0
      src/main/java/nju/seec/helper/service/EventService.java
  42. 8 0
      src/main/java/nju/seec/helper/service/QuestionService.java
  43. 42 25
      src/main/java/nju/seec/helper/service/UserService.java
  44. 86 86
      src/main/java/nju/seec/helper/service/impl/AdminServiceImpl.java
  45. 48 48
      src/main/java/nju/seec/helper/service/impl/CodeServiceImpl.java
  46. 2 2
      src/main/java/nju/seec/helper/service/impl/CourseFileServiceImpl.java
  47. 2 2
      src/main/java/nju/seec/helper/service/impl/CourseServiceImpl.java
  48. 88 0
      src/main/java/nju/seec/helper/service/impl/EventServiceImpl.java
  49. 25 20
      src/main/java/nju/seec/helper/service/impl/QuestionServiceImpl.java
  50. 1 1
      src/main/java/nju/seec/helper/service/impl/QuizServiceImpl.java
  51. 17 7
      src/main/java/nju/seec/helper/service/impl/SlideServiceImpl.java
  52. 88 81
      src/main/java/nju/seec/helper/service/impl/UserServiceImpl.java
  53. 3 3
      src/main/java/nju/seec/helper/util/Consts.java
  54. 0 25
      src/main/java/nju/seec/helper/util/EncryptUtils.java
  55. 7 2
      src/main/java/nju/seec/helper/util/JsonUtils.java
  56. 32 32
      src/main/java/nju/seec/helper/util/MailUtils.java
  57. 65 65
      src/main/java/nju/seec/helper/util/SmsUtils.java
  58. 11 10
      src/main/java/nju/seec/helper/util/cache/GuavaCacheUtils.java
  59. 1 1
      src/main/java/nju/seec/helper/util/cache/RedisCacheUtils.java
  60. 32 34
      src/main/java/nju/seec/helper/util/file/FileUtils.java
  61. 2 3
      src/main/java/nju/seec/helper/util/oss/OssObjectUrlUtils.java
  62. 1 1
      src/main/java/nju/seec/helper/util/oss/OssUtils.java
  63. 0 21
      src/main/java/nju/seec/helper/util/serializer/LocalDateTimeSerializer.java
  64. 19 19
      src/main/java/nju/seec/helper/vo/AdminVO.java
  65. 3 3
      src/main/java/nju/seec/helper/vo/CommentVO.java
  66. 21 0
      src/main/java/nju/seec/helper/vo/EventVO.java
  67. 5 6
      src/main/java/nju/seec/helper/vo/UserVO.java
  68. 16 0
      src/main/java/nju/seec/helper/vo/knowledge/BokKnowledgeNodeVO.java
  69. 14 14
      src/main/java/nju/seec/helper/vo/question/BaseQuestionVO.java
  70. 4 4
      src/main/java/nju/seec/helper/vo/question/BokQuestionVO.java
  71. 5 5
      src/main/java/nju/seec/helper/vo/question/ChoiceQuestionVO.java
  72. 6 5
      src/main/java/nju/seec/helper/vo/question/TrueOrFalseQuestionVO.java
  73. 22 10
      src/main/resources/application-deploy.yml
  74. 22 10
      src/main/resources/application-dev.yml
  75. 3 11
      src/main/resources/update.sql

+ 27 - 16
pom.xml

@@ -10,7 +10,7 @@
     </parent>
     <groupId>cn.seecoder</groupId>
     <artifactId>helper-backend</artifactId>
-    <version>4.1.2</version>
+    <version>5.0.0</version>
     <packaging>jar</packaging>
     <name>helper</name>
     <description>backend</description>
@@ -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>
@@ -129,6 +129,17 @@
             <groupId>org.ehcache</groupId>
             <artifactId>ehcache</artifactId>
         </dependency>
+        <!--一个工具类库,不用自己造轮子了!!!-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.2.5</version>
+        </dependency>
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.10.2</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 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;
+    }
+}

+ 52 - 49
src/main/java/nju/seec/helper/api/BokApi.java → src/main/java/nju/seec/helper/api/bok/QuestionApi.java

@@ -1,20 +1,20 @@
-package nju.seec.helper.api;
+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 lombok.extern.slf4j.Slf4j;
 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.GuavaCacheUtil;
+import nju.seec.helper.util.Consts;
 import nju.seec.helper.util.RestRequestUtil;
-import nju.seec.helper.vo.question.BokQuestion;
+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;
@@ -32,11 +32,10 @@ import java.util.stream.Collectors;
  * <p>
  * updated by cst
  */
-@Slf4j
 @Component
-public class BokApi {
+public class QuestionApi {
     private final RestRequestUtil restRequestUtil;
-    private final GuavaCacheUtil cacheUtils;
+    private final GuavaCacheUtils cacheUtils;
     @Value("${bok.tqUrl}")
     private String tqUrl;
     @Value("${bok.tqStemSearchUrl}")
@@ -44,12 +43,12 @@ public class BokApi {
     @Value("${bok.tqIdSearchUrl}")
     private String tqIdSearchUrl;
 
-    public BokApi(RestRequestUtil restRequestUtil, GuavaCacheUtil cacheUtils) {
+    public QuestionApi(RestRequestUtil restRequestUtil, GuavaCacheUtils cacheUtils) {
         this.restRequestUtil = restRequestUtil;
         this.cacheUtils = cacheUtils;
     }
 
-    public Page<BokQuestion> bokFindByStemLike(String stem, Pageable pageable) {
+    public Page<BokQuestionVO> bokFindByStemLike(String stem, Pageable pageable) {
         Map<String, String> urlParams = ImmutableMap.of(
                 "content", stem,
                 "page", String.valueOf(1 + pageable.getPageNumber()),
@@ -57,18 +56,18 @@ public class BokApi {
         );
         BokSearchResult result = restRequestUtil.sendGetRequest(tqStemSearchUrl, BokSearchResult.class, urlParams);
 
-        List<BokQuestion> bokQuestions = result.getEmbedded().getQuestions();
-        cacheUtils.setAll(BOK_CACHE_NAME, bokQuestions.parallelStream().collect(Collectors.toMap(BokQuestion::getId, bokQuestion -> bokQuestion)));
-        return new PageImpl<>(bokQuestions, pageable, result.getPage().getTotalElements());
+        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 String BOK_CACHE_NAME = "BOK";
+    private static final String BOK_QUESTION_CACHE_NAME = Consts.SYS_NAME + "_bok_question";
 
     @SuppressWarnings({"unchecked", "rawtypes"})
-    public List<BokQuestion> bokFindByIdIn(final List<String> ids) {
-        Map<String, BokQuestion> bokQuestionsMap = Maps.newHashMapWithExpectedSize(ids.size());
+    public List<BokQuestionVO> bokFindByIdIn(final List<String> ids) {
+        Map<String, BokQuestionVO> bokQuestionsMap = Maps.newHashMapWithExpectedSize(ids.size());
 
-        final Map<String, BokQuestion> cacheBokQuestionsMap = (Map) cacheUtils.multiGet(BOK_CACHE_NAME, ids);
+        final Map<String, BokQuestionVO> cacheBokQuestionsMap = (Map) cacheUtils.multiGet(BOK_QUESTION_CACHE_NAME, ids);
         bokQuestionsMap.putAll(cacheBokQuestionsMap);
 
         List<String> requestIds = Lists.newArrayList(ids);
@@ -78,12 +77,12 @@ public class BokApi {
             Map<String, String> urlParams = ImmutableMap.of("id", requestIds.stream().reduce((a, b) -> a + "," + b).orElse(""));
 
             BokSearchResult result = restRequestUtil.sendGetRequest(tqIdSearchUrl, BokSearchResult.class, urlParams);
-            List<BokQuestion> bokQuestions = result.getEmbedded().getQuestions();
+            List<BokQuestionVO> bokQuestionVOList = result.getEmbedded().getQuestions();
 
-            Map<String, BokQuestion> remoteBokQuestionsMap = bokQuestions.parallelStream().collect(Collectors.toMap(BokQuestion::getId, bokQuestion -> bokQuestion));
+            Map<String, BokQuestionVO> remoteBokQuestionsMap = bokQuestionVOList.parallelStream().collect(Collectors.toMap(BokQuestionVO::getId, bokQuestion -> bokQuestion));
 
             bokQuestionsMap.putAll(remoteBokQuestionsMap);
-            cacheUtils.setAll(BOK_CACHE_NAME, (Map) remoteBokQuestionsMap);
+            cacheUtils.setAll(BOK_QUESTION_CACHE_NAME, (Map) remoteBokQuestionsMap);
         }
 
         return ids.stream()
@@ -91,27 +90,27 @@ public class BokApi {
                 .collect(Collectors.toList());
     }
 
-    public BokQuestion bokFindById(String questionId) {
-        Object cachedBokQuestion = cacheUtils.get(BOK_CACHE_NAME, questionId);
-        if (cachedBokQuestion instanceof BokQuestion) {
-            return (BokQuestion) cachedBokQuestion;
+    public BokQuestionVO bokFindById(String questionId) {
+        Object cachedBokQuestion = cacheUtils.get(BOK_QUESTION_CACHE_NAME, questionId);
+        if (cachedBokQuestion instanceof BokQuestionVO) {
+            return (BokQuestionVO) cachedBokQuestion;
         }
-        BokQuestion bokQuestion = restRequestUtil.sendGetRequest(tqUrl + "/" + questionId, BokQuestion.class, Collections.emptyMap());
-        cacheUtils.set(BOK_CACHE_NAME, bokQuestion.getId(), bokQuestion);
-        return bokQuestion;
+        BokQuestionVO bokQuestionVO = restRequestUtil.sendGetRequest(tqUrl + "/" + questionId, BokQuestionVO.class, Collections.emptyMap());
+        cacheUtils.set(BOK_QUESTION_CACHE_NAME, bokQuestionVO.getId(), bokQuestionVO);
+        return bokQuestionVO;
     }
 
-    public BokQuestion createQuestion(String questionId, BaseQuestionDTO baseQuestionDTO) {
-        BokQuestion bokQuestion = getQuestion(baseQuestionDTO);
-        bokQuestion.setId(questionId);
-        bokQuestion = restRequestUtil.sendPostRequest(tqUrl, bokQuestion, BokQuestion.class);
-        return bokQuestion;
+    public BokQuestionVO createQuestion(String questionId, BaseQuestionDTO baseQuestionDTO) {
+        BokQuestionVO bokQuestionVO = getQuestion(baseQuestionDTO);
+        bokQuestionVO.setId(questionId);
+        bokQuestionVO = restRequestUtil.sendPostRequest(tqUrl, bokQuestionVO, BokQuestionVO.class);
+        return bokQuestionVO;
     }
 
-    public BokQuestion modifyQuestion(String questionId, BaseQuestionDTO baseQuestionDTO) {
-        BokQuestion bokQuestion = getQuestion(baseQuestionDTO);
-        bokQuestion.setId(questionId);
-        restRequestUtil.sendPutRequest(tqUrl + "/" + questionId, bokQuestion);
+    public BokQuestionVO modifyQuestion(String questionId, BaseQuestionDTO baseQuestionDTO) {
+        BokQuestionVO bokQuestionVO = getQuestion(baseQuestionDTO);
+        bokQuestionVO.setId(questionId);
+        restRequestUtil.sendPutRequest(tqUrl + "/" + questionId, bokQuestionVO);
         return bokFindById(questionId);
     }
 
@@ -119,31 +118,35 @@ public class BokApi {
         restRequestUtil.sendDeleteRequest(tqUrl + "/" + questionId);
     }
 
-    private BokQuestion getQuestion(BaseQuestionDTO baseQuestionDTO) {
-        BokQuestion bokQuestion = new BokQuestion();
-        bokQuestion.setType(baseQuestionDTO.getType());
-        bokQuestion.setStem(baseQuestionDTO.getStem());
-        bokQuestion.setKeyPoints(baseQuestionDTO.getKeyPoints());
-        bokQuestion.setTags(baseQuestionDTO.getTags());
-        bokQuestion.setKnowledgeId(baseQuestionDTO.getKnowledgeId());
+    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;
-                bokQuestion.setOptions(choiceQuestionDTO.getOptions());
-                bokQuestion.setAnswer(choiceQuestionDTO.getAnswer());
-                bokQuestion.setAnalysis(choiceQuestionDTO.getAnalysis());
+                bokQuestionVO.setOptions(choiceQuestionDTO.getOptions());
+                bokQuestionVO.setAnswer(choiceQuestionDTO.getAnswer());
+                bokQuestionVO.setAnalysis(choiceQuestionDTO.getAnalysis());
                 break;
             case TRUE_FALSE:
                 TrueOrFalseQuestionDTO trueOrFalseQuestionDTO = (TrueOrFalseQuestionDTO) baseQuestionDTO;
-                bokQuestion.setAnswer(String.valueOf(trueOrFalseQuestionDTO.getAnswer()));
-                bokQuestion.setAnalysis(trueOrFalseQuestionDTO.getAnalysis());
+                bokQuestionVO.setAnswer(String.valueOf(trueOrFalseQuestionDTO.getAnswer()));
+                bokQuestionVO.setAnalysis(trueOrFalseQuestionDTO.getAnalysis());
                 break;
             default:
                 throw HelperException.of(ExceptionType.ERROR, "不支持的题目类型");
         }
-        return bokQuestion;
+        return bokQuestionVO;
     }
 
     @Data
@@ -154,7 +157,7 @@ public class BokApi {
 
         @Data
         private static class Embedded {
-            private List<BokQuestion> questions = Collections.emptyList();
+            private List<BokQuestionVO> questions = Collections.emptyList();
         }
 
         @Data

+ 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;
+    }
+}

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

@@ -0,0 +1,25 @@
+//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;
+//
+///**
+// * @author cst
+// */
+//@Component
+//public class EventApi {
+//    @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;
+    }
+}

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

@@ -1,6 +1,6 @@
 package nju.seec.helper.aspect.auth;
 
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 
 import java.lang.annotation.*;
 
@@ -11,7 +11,7 @@ import java.lang.annotation.*;
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface Auth {
-    UserType[] roles() default {};
+    UserRole[] roles() default {};
 
     String message() default "";
 }

+ 77 - 11
src/main/java/nju/seec/helper/aspect/auth/AuthAspect.java

@@ -1,19 +1,30 @@
 package nju.seec.helper.aspect.auth;
 
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import lombok.Data;
+import nju.seec.helper.entity.User;
 import nju.seec.helper.enums.ExceptionType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.exception.HelperException;
-import nju.seec.helper.util.Consts;
+import nju.seec.helper.service.UserService;
 import org.aspectj.lang.JoinPoint;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Before;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 
-import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 /**
@@ -22,20 +33,23 @@ import java.util.stream.Collectors;
 @Aspect
 @Component
 public class AuthAspect {
+    @Value("${jwt.secret}")
+    private String jwtSecret;
+
+    private final UserService userService;
+
+    public AuthAspect(UserService userService) {
+        this.userService = userService;
+    }
+
     @Before("execution(public * nju.seec.helper.controller.*.*(..)) && @annotation(auth)")
     public void authCheck(JoinPoint joinPoint, Auth auth) {
-        HttpSession session =
-                ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
-                        .getRequest()
-                        .getSession();
+        LoginUser user = getLoginUser();
 
-        LoginUser user = (LoginUser) session.getAttribute(Consts.SESSION_USER_NAME);
-        if (user == null) {
-            throw HelperException.of(ExceptionType.NOT_LOGIN, "您未登录");
-        } else if (!Arrays
+        if (!Arrays
                 .stream(auth.roles())
                 .collect(Collectors.toSet())
-                .contains(user.getType())) {
+                .contains(user.getRole())) {
             throw HelperException.of(ExceptionType.FORBIDDEN, String.format("您暂时无法%s,请重新登录", auth.message()));
         }
 
@@ -47,4 +61,56 @@ public class AuthAspect {
             }
         }
     }
+
+    private LoginUser getLoginUser() {
+        HttpServletRequest httpServletRequest =
+                ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
+                        .getRequest();
+        String token = Optional.ofNullable(httpServletRequest.getHeader("Authorization"))
+                .orElseThrow(() -> HelperException.of(ExceptionType.UNAUTHORIZED, "缺少Token"));
+
+        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecret)).build();
+
+        DecodedJWT decodedJWT;
+
+        try {
+            decodedJWT = jwtVerifier.verify(token);
+        } catch (JWTVerificationException e) {
+//            e.printStackTrace();
+            throw HelperException.of(ExceptionType.UNAUTHORIZED, "无效Token");
+        }
+
+        boolean isExpired = JWT.decode(token).getExpiresAt().before(new Date());
+
+        if (isExpired) {
+            throw HelperException.of(ExceptionType.UNAUTHORIZED, "Token已过期");
+        }
+
+        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)
+                .setEmail(userInfo.email)
+                .setRole(userInfo.role)
+                .setPhone(userInfo.phone)
+                .setPid(userInfo.id);
+
+        user = userService.createOrUpdateUser(user);
+
+        return LoginUser.of(user.getId(), user.getRole());
+    }
+
+    @Data
+    private static class UserInfo {
+        private Long id;
+        private String phone;
+        private String email;
+        private String name;
+        private UserRole role;
+    }
 }

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

@@ -1,15 +1,17 @@
 package nju.seec.helper.aspect.auth;
 
+import lombok.AllArgsConstructor;
 import lombok.Data;
-import lombok.experimental.Accessors;
-import nju.seec.helper.enums.UserType;
+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 UserType type;
+    private UserRole role;
 }

+ 0 - 22
src/main/java/nju/seec/helper/config/PageableConfig.java

@@ -1,22 +0,0 @@
-package nju.seec.helper.config;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.web.config.PageableHandlerMethodArgumentResolverCustomizer;
-
-/**
- * @ClassName PageableConfig
- * @PackageName nju.seec.helper.config
- * @Author SheentXu
- * @Date 2019/12/24
- * @Version 1.0
- * @Description
- **/
-@Configuration
-public class PageableConfig {
-
-    @Bean
-    PageableHandlerMethodArgumentResolverCustomizer pageableResolverCustomizer() {
-        return pageableResolver -> pageableResolver.setOneIndexedParameters(true);
-    }
-}

+ 88 - 88
src/main/java/nju/seec/helper/controller/AdminController.java

@@ -1,88 +1,88 @@
-package nju.seec.helper.controller;
-
-import nju.seec.helper.aspect.auth.Auth;
-import nju.seec.helper.aspect.auth.LoginUser;
-import nju.seec.helper.controller.response.PageResponse;
-import nju.seec.helper.dto.common.LoginDTO;
-import nju.seec.helper.enums.UserState;
-import nju.seec.helper.enums.UserType;
-import nju.seec.helper.service.AdminService;
-import nju.seec.helper.util.Consts;
-import nju.seec.helper.vo.AdminVO;
-import nju.seec.helper.vo.UserVO;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.web.PageableDefault;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import javax.servlet.http.HttpSession;
-
-/**
- * 管理员
- *
- * @author cst
- */
-@RestController
-@RequestMapping("/api/admin")
-public class AdminController {
-    private final AdminService adminService;
-
-    public AdminController(AdminService adminService) {
-        this.adminService = adminService;
-    }
-
-    /**
-     * 管理员登录
-     */
-    @PostMapping("/login")
-    public AdminVO login(@Validated @RequestBody LoginDTO loginDTO, HttpSession session) {
-        AdminVO adminVO = adminService.getAdminByUsernameAndPassword(loginDTO.getUsername(), loginDTO.getPassword());
-
-        LoginUser user = new LoginUser()
-                .setId(adminVO.getId())
-                .setType(UserType.ADMIN);
-
-        session.setAttribute(Consts.SESSION_USER_NAME, user);
-        // 30分钟有效期
-        session.setMaxInactiveInterval(30 * 60);
-        return adminVO;
-    }
-
-    /**
-     * 获取注册教师列表
-     */
-    @Auth(roles = UserType.ADMIN, message = "获取注册教师列表")
-    @GetMapping("/user")
-    public PageResponse<UserVO> getUsers(LoginUser admin, UserType type, UserState state,
-                                         @RequestParam(required = false, defaultValue = "") String key,
-                                         @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
-        return PageResponse.of(adminService.getUsers(type, state, key, pageable));
-    }
-
-    /**
-     * 审核通过用户
-     */
-    @Auth(roles = UserType.ADMIN, message = "审核通过用户")
-    @PutMapping("/user/{userId}/pass")
-    public void passUser(LoginUser admin, @PathVariable Long userId) {
-        adminService.passUser(admin, userId);
-    }
-
-    /**
-     * 封禁用户
-     */
-    @Auth(roles = UserType.ADMIN, message = "封禁用户")
-    @PutMapping("/user/{userId}/lock")
-    public void lockUser(LoginUser admin, @PathVariable Long userId) {
-        adminService.lockUser(admin, userId);
-    }
-
-    /**
-     * 解封用户
-     */
-    @Auth(roles = UserType.ADMIN, message = "解封用户")
-    @PutMapping("/user/{userId}/unlock")
-    public void unlockUser(LoginUser admin, @PathVariable Long userId) {
-        adminService.unlockUser(admin, userId);
-    }
-}
+//package nju.seec.helper.controller;
+//
+//import nju.seec.helper.aspect.auth.Auth;
+//import nju.seec.helper.aspect.auth.LoginUser;
+//import nju.seec.helper.controller.response.PageResponse;
+//import nju.seec.helper.dto.common.LoginDTO;
+//import nju.seec.helper.enums.UserState;
+//import nju.seec.helper.enums.UserRole;
+//import nju.seec.helper.service.AdminService;
+//import nju.seec.helper.util.Consts;
+//import nju.seec.helper.vo.AdminVO;
+//import nju.seec.helper.vo.UserVO;
+//import org.springframework.data.domain.Pageable;
+//import org.springframework.data.web.PageableDefault;
+//import org.springframework.validation.annotation.Validated;
+//import org.springframework.web.bind.annotation.*;
+//
+//import javax.servlet.http.HttpSession;
+//
+///**
+// * 管理员
+// *
+// * @author cst
+// */
+//@RestController
+//@RequestMapping("/api/admin")
+//public class AdminController {
+//    private final AdminService adminService;
+//
+//    public AdminController(AdminService adminService) {
+//        this.adminService = adminService;
+//    }
+//
+//    /**
+//     * 管理员登录
+//     */
+//    @PostMapping("/login")
+//    public AdminVO login(@Validated @RequestBody LoginDTO loginDTO, HttpSession session) {
+//        AdminVO adminVO = adminService.getAdminByUsernameAndPassword(loginDTO.getUsername(), loginDTO.getPassword());
+//
+//        LoginUser user = new LoginUser()
+//                .setId(adminVO.getId())
+//                .setRole(UserRole.ADMIN);
+//
+//        session.setAttribute(Consts.SESSION_USER_NAME, user);
+//        // 30分钟有效期
+//        session.setMaxInactiveInterval(30 * 60);
+//        return adminVO;
+//    }
+//
+//    /**
+//     * 获取注册教师列表
+//     */
+//    @Auth(roles = UserRole.ADMIN, message = "获取注册教师列表")
+//    @GetMapping("/user")
+//    public PageResponse<UserVO> getUsers(LoginUser admin, UserRole role, UserState state,
+//                                         @RequestParam(required = false, defaultValue = "") String key,
+//                                         @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
+//        return PageResponse.of(adminService.getUsers(role, state, key, pageable));
+//    }
+//
+//    /**
+//     * 审核通过用户
+//     */
+//    @Auth(roles = UserRole.ADMIN, message = "审核通过用户")
+//    @PutMapping("/user/{userId}/pass")
+//    public void passUser(LoginUser admin, @PathVariable Long userId) {
+//        adminService.passUser(admin, userId);
+//    }
+//
+//    /**
+//     * 封禁用户
+//     */
+//    @Auth(roles = UserRole.ADMIN, message = "封禁用户")
+//    @PutMapping("/user/{userId}/lock")
+//    public void lockUser(LoginUser admin, @PathVariable Long userId) {
+//        adminService.lockUser(admin, userId);
+//    }
+//
+//    /**
+//     * 解封用户
+//     */
+//    @Auth(roles = UserRole.ADMIN, message = "解封用户")
+//    @PutMapping("/user/{userId}/unlock")
+//    public void unlockUser(LoginUser admin, @PathVariable Long userId) {
+//        adminService.unlockUser(admin, userId);
+//    }
+//}

+ 43 - 43
src/main/java/nju/seec/helper/controller/CodeController.java

@@ -1,43 +1,43 @@
-package nju.seec.helper.controller;
-
-import nju.seec.helper.dto.validator.phone.Phone;
-import nju.seec.helper.dto.validator.schoolemail.SchoolEmail;
-import nju.seec.helper.service.CodeService;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.validation.constraints.NotBlank;
-
-/**
- * 发送验证码
- *
- * @author cst
- */
-@Validated
-@RestController
-@RequestMapping("/api/code")
-public class CodeController {
-    private final CodeService codeService;
-
-    public CodeController(CodeService codeService) {
-        this.codeService = codeService;
-    }
-
-    /**
-     * 发送邮箱验证码
-     */
-    @PostMapping("/email")
-    public void sendEmailCode(@NotBlank(message = "邮箱不能为空") @SchoolEmail String email) {
-        codeService.sendEmailCode(email);
-    }
-
-    /**
-     * 发送手机验证码
-     */
-    @PostMapping("/phone")
-    public void sendPhoneCode(@NotBlank(message = "手机号不能为空") @Phone String phone) {
-        codeService.sendPhoneCode(phone);
-    }
-}
+//package nju.seec.helper.controller;
+//
+//import nju.seec.helper.dto.validator.phone.Phone;
+//import nju.seec.helper.dto.validator.schoolemail.SchoolEmail;
+//import nju.seec.helper.service.CodeService;
+//import org.springframework.validation.annotation.Validated;
+//import org.springframework.web.bind.annotation.PostMapping;
+//import org.springframework.web.bind.annotation.RequestMapping;
+//import org.springframework.web.bind.annotation.RestController;
+//
+//import javax.validation.constraints.NotBlank;
+//
+///**
+// * 发送验证码
+// *
+// * @author cst
+// */
+//@Validated
+//@RestController
+//@RequestMapping("/api/code")
+//public class CodeController {
+//    private final CodeService codeService;
+//
+//    public CodeController(CodeService codeService) {
+//        this.codeService = codeService;
+//    }
+//
+//    /**
+//     * 发送邮箱验证码
+//     */
+//    @PostMapping("/email")
+//    public void sendEmailCode(@NotBlank(message = "邮箱不能为空") @SchoolEmail String email) {
+//        codeService.sendEmailCode(email);
+//    }
+//
+//    /**
+//     * 发送手机验证码
+//     */
+//    @PostMapping("/phone")
+//    public void sendPhoneCode(@NotBlank(message = "手机号不能为空") @Phone String phone) {
+//        codeService.sendPhoneCode(phone);
+//    }
+//}

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

@@ -4,7 +4,7 @@ import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.controller.response.PageResponse;
 import nju.seec.helper.dto.comment.CommentDTO;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.CommentService;
 import nju.seec.helper.util.JsonUtils;
 import nju.seec.helper.vo.CommentVO;
@@ -33,7 +33,7 @@ public class CommentController {
     /**
      * 发表评论
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "发表评论")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "发表评论")
     @PostMapping
     public CommentVO createComment(LoginUser user,
                                    @Validated @RequestBody CommentDTO commentDTO) {
@@ -45,7 +45,7 @@ public class CommentController {
     /**
      * 删除评论
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "删除评论")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "删除评论")
     @DeleteMapping("/{commentId}")
     public void deleteComment(LoginUser user,
                               @PathVariable Long commentId) {
@@ -55,7 +55,7 @@ public class CommentController {
     /**
      * 教师置顶评论
      */
-    @Auth(roles = UserType.TEACHER, message = "置顶评论")
+    @Auth(roles = UserRole.TEACHER, message = "置顶评论")
     @PostMapping("/{commentId}/top")
     public void topComment(LoginUser user,
                            @PathVariable Long commentId) {
@@ -65,7 +65,7 @@ public class CommentController {
     /**
      * 教师取消置顶评论
      */
-    @Auth(roles = UserType.TEACHER, message = "取消置顶评论")
+    @Auth(roles = UserRole.TEACHER, message = "取消置顶评论")
     @PostMapping("/{commentId}/unTop")
     public void unTopComment(LoginUser user,
                              @PathVariable Long commentId) {
@@ -75,7 +75,7 @@ public class CommentController {
     /**
      * 取得某一页的评论信息
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取评论")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取评论")
     @GetMapping("/slide/{slideId}")
     public PageResponse<CommentVO> getComments(@PathVariable Long slideId,
                                                @RequestParam(required = false, defaultValue = "1") Integer pageNumber,
@@ -86,7 +86,7 @@ public class CommentController {
     /**
      * 取得某一评论
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取评论")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取评论")
     @GetMapping("/{commentId}")
     public CommentVO getOneComment(LoginUser user, @PathVariable Long commentId) {
         return commentService.getOneComment(user, commentId);
@@ -95,7 +95,7 @@ public class CommentController {
     /**
      * 取得自己某一页的评论
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取自己的评论")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取自己的评论")
     @GetMapping("/slide/{slideId}/self")
     public PageResponse<CommentVO> getSelfComments(LoginUser user,
                                                    @PathVariable Long slideId,
@@ -105,7 +105,7 @@ public class CommentController {
         return PageResponse.of(commentService.getSelfCommentsBySlideAndPageNumber(user, slideId, pageNumber, show, pageable));
     }
 
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "修改评论展示状态")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "修改评论展示状态")
     @PutMapping("/{commentId}/show")
     public void modifyCommentShow(LoginUser user,
                                   @PathVariable Long commentId,

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

@@ -7,7 +7,7 @@ import nju.seec.helper.dto.course.ChooseDTO;
 import nju.seec.helper.dto.course.CourseDTO;
 import nju.seec.helper.dto.groups.Create;
 import nju.seec.helper.dto.groups.Modify;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.CourseService;
 import nju.seec.helper.vo.CourseVO;
 import nju.seec.helper.vo.UserVO;
@@ -33,7 +33,7 @@ public class CourseController {
     /**
      * 教师创建课程
      */
-    @Auth(roles = UserType.TEACHER, message = "创建课程")
+    @Auth(roles = UserRole.TEACHER, message = "创建课程")
     @PostMapping
     public CourseVO createCourse(LoginUser user,
                                  @Validated(Create.class) @RequestBody CourseDTO courseDTO) {
@@ -43,7 +43,7 @@ public class CourseController {
     /**
      * 教师删除课程(伪删除)
      */
-    @Auth(roles = UserType.TEACHER, message = "删除课程")
+    @Auth(roles = UserRole.TEACHER, message = "删除课程")
     @DeleteMapping("/{courseId}")
     public void deleteCourse(LoginUser user,
                              @PathVariable Long courseId) {
@@ -53,7 +53,7 @@ public class CourseController {
     /**
      * 教师修改课程
      */
-    @Auth(roles = UserType.TEACHER, message = "修改课程")
+    @Auth(roles = UserRole.TEACHER, message = "修改课程")
     @PutMapping("/{courseId}")
     public CourseVO modifyCourse(LoginUser user,
                                  @PathVariable Long courseId,
@@ -64,7 +64,7 @@ public class CourseController {
     /**
      * 学生选课
      */
-    @Auth(roles = UserType.STUDENT, message = "选课")
+    @Auth(roles = UserRole.STUDENT, message = "选课")
     @PostMapping("/{courseId}/choose")
     public void chooseCourse(LoginUser user,
                              @PathVariable Long courseId,
@@ -75,7 +75,7 @@ public class CourseController {
     /**
      * 学生退课
      */
-    @Auth(roles = UserType.STUDENT, message = "退课")
+    @Auth(roles = UserRole.STUDENT, message = "退课")
     @PostMapping("/{courseId}/quit")
     public void quitCourse(LoginUser user,
                            @PathVariable Long courseId) {
@@ -94,7 +94,7 @@ public class CourseController {
     /**
      * 获取创建课程列表
      */
-    @Auth(roles = UserType.TEACHER, message = "获取创建课程列表")
+    @Auth(roles = UserRole.TEACHER, message = "获取创建课程列表")
     @GetMapping("/created")
     public PageResponse<CourseVO> getCreatedCourses(LoginUser user,
                                                     @RequestParam(required = false, defaultValue = "") String key,
@@ -105,7 +105,7 @@ public class CourseController {
     /**
      * 获取选课列表
      */
-    @Auth(roles = UserType.STUDENT, message = "获取选课列表")
+    @Auth(roles = UserRole.STUDENT, message = "获取选课列表")
     @GetMapping("/chosen")
     public PageResponse<CourseVO> getChosenCourses(LoginUser user,
                                                    @RequestParam(required = false, defaultValue = "") String key,
@@ -116,7 +116,7 @@ public class CourseController {
     /**
      * 获取课程选课码
      */
-    @Auth(roles = UserType.TEACHER, message = "获取选课码")
+    @Auth(roles = UserRole.TEACHER, message = "获取选课码")
     @GetMapping("/{courseId}/code")
     public String getCourseCode(LoginUser user,
                                 @PathVariable Long courseId) {
@@ -134,7 +134,7 @@ public class CourseController {
     /**
      * 获取选课名单
      */
-    @Auth(roles = UserType.TEACHER, message = "获取选课名单")
+    @Auth(roles = UserRole.TEACHER, message = "获取选课名单")
     @GetMapping("/{courseId}/students")
     public PageResponse<UserVO> getChooseStudents(LoginUser user,
                                                   @PathVariable Long courseId,

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

@@ -4,7 +4,7 @@ import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.controller.response.PageResponse;
 import nju.seec.helper.dto.course.CourseFileDTO;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.CourseFileService;
 import nju.seec.helper.vo.CourseFileVO;
 import org.springframework.data.domain.Pageable;
@@ -29,7 +29,7 @@ public class CourseFileController {
     /**
      * 上传课程附件
      */
-    @Auth(roles = UserType.TEACHER, message = "上传课程附件")
+    @Auth(roles = UserRole.TEACHER, message = "上传课程附件")
     @PostMapping
     public void uploadCourseFile(LoginUser user,
                                  @Validated CourseFileDTO courseFileDTO) {
@@ -39,7 +39,7 @@ public class CourseFileController {
     /**
      * 删除课程附件
      */
-    @Auth(roles = UserType.TEACHER, message = "删除课程附件")
+    @Auth(roles = UserRole.TEACHER, message = "删除课程附件")
     @DeleteMapping("/{courseFileId}")
     public void deleteCourseFile(LoginUser user,
                                  @PathVariable Long courseFileId) {
@@ -49,7 +49,7 @@ public class CourseFileController {
     /**
      * 获取课程附件
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取课程附件")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取课程附件")
     @GetMapping("/course/{courseId}")
     public PageResponse<CourseFileVO> getCourseFilesByCourse(LoginUser user,
                                                              @PathVariable Long courseId,
@@ -61,7 +61,7 @@ public class CourseFileController {
     /**
      * 获取课程附件链接
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取课程附件链接")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取课程附件链接")
     @GetMapping("/{courseFileId}/url")
     public String getOneCourseFile(LoginUser user,
                                    @PathVariable Long courseFileId) {

+ 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();
+    }
+}

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

@@ -0,0 +1,27 @@
+package nju.seec.helper.controller;
+
+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;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+@RestController
+@RequestMapping("/api/knowledge")
+public class KnowledgeController {
+    private final KnowledgeApi knowledgeApi;
+
+    public KnowledgeController(KnowledgeApi knowledgeApi) {
+        this.knowledgeApi = knowledgeApi;
+    }
+
+    @GetMapping
+    public List<BokKnowledgeNodeVO> getKnowledgeNodes() {
+        return knowledgeApi.getKnowledgeNodes();
+    }
+}

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

@@ -5,7 +5,7 @@ import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.controller.response.PageResponse;
 import nju.seec.helper.dto.message.MessageDTO;
 import nju.seec.helper.entity.Message;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.MessageService;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.web.PageableDefault;
@@ -29,7 +29,7 @@ public class MessageController {
     /**
      * 设置已读
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "设置已读")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "设置已读")
     @PutMapping("/read")
     public void readMessages(LoginUser user,
                              @Validated @RequestBody MessageDTO messageDTO) {
@@ -39,7 +39,7 @@ public class MessageController {
     /**
      * 删除消息
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "删除消息")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "删除消息")
     @PutMapping("/delete")
     public void deleteMessages(LoginUser user,
                                @Validated @RequestBody MessageDTO messageDTO) {
@@ -49,7 +49,7 @@ public class MessageController {
     /**
      * 获取未读消息
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取未读消息")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取未读消息")
     @GetMapping("/unread")
     public PageResponse<Message> getUnreadMessages(LoginUser user,
                                                    @RequestParam(required = false, defaultValue = "") String key,
@@ -60,7 +60,7 @@ public class MessageController {
     /**
      * 获取已读消息
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取已读消息")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取已读消息")
     @GetMapping("/read")
     public PageResponse<Message> getReadMessages(LoginUser user,
                                                  @RequestParam(required = false, defaultValue = "") String key,

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

@@ -6,7 +6,7 @@ import nju.seec.helper.controller.response.PageResponse;
 import nju.seec.helper.dto.groups.Create;
 import nju.seec.helper.dto.groups.Modify;
 import nju.seec.helper.dto.notice.NoticeDTO;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.NoticeService;
 import nju.seec.helper.vo.NoticeVO;
 import org.springframework.data.domain.Pageable;
@@ -35,7 +35,7 @@ public class NoticeController {
      * @param noticeDTO
      * @return
      */
-    @Auth(roles = UserType.TEACHER, message = "发布公告")
+    @Auth(roles = UserRole.TEACHER, message = "发布公告")
     @PostMapping
     public NoticeVO createNotice(LoginUser user,
                                  @Validated(Create.class) @RequestBody NoticeDTO noticeDTO) {
@@ -50,7 +50,7 @@ public class NoticeController {
      * @param noticeDTO
      * @return
      */
-    @Auth(roles = UserType.TEACHER, message = "修改公告")
+    @Auth(roles = UserRole.TEACHER, message = "修改公告")
     @PutMapping("/{noticeId}")
     public NoticeVO modifyNotice(LoginUser user,
                                  @PathVariable Long noticeId,
@@ -61,7 +61,7 @@ public class NoticeController {
     /**
      * 删除公告
      */
-    @Auth(roles = UserType.TEACHER, message = "删除公告")
+    @Auth(roles = UserRole.TEACHER, message = "删除公告")
     @DeleteMapping("/{noticeId}")
     public void deleteNotice(LoginUser user,
                              @PathVariable Long noticeId) {
@@ -71,7 +71,7 @@ public class NoticeController {
     /**
      * 获取课程公告
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取课程公告")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取课程公告")
     @GetMapping("/course/{courseId}")
     public PageResponse<NoticeVO> getNotices(LoginUser user,
                                              @PathVariable Long courseId,
@@ -83,7 +83,7 @@ public class NoticeController {
     /**
      * 获取某一公告
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取公告")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取公告")
     @GetMapping("/{noticeId}")
     public NoticeVO getOneNotice(LoginUser user,
                                  @PathVariable Long noticeId) {

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

@@ -6,7 +6,7 @@ import nju.seec.helper.controller.response.PageResponse;
 import nju.seec.helper.dto.groups.Create;
 import nju.seec.helper.dto.groups.Modify;
 import nju.seec.helper.dto.question.BaseQuestionDTO;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.QuestionService;
 import nju.seec.helper.vo.question.BaseQuestionVO;
 import org.springframework.data.domain.Pageable;
@@ -37,7 +37,7 @@ public class QuestionController {
     /**
      * 获得题目列表
      */
-    @Auth(roles = {UserType.TEACHER}, message = "获得题目列表")
+    @Auth(roles = {UserRole.TEACHER}, message = "获得题目列表")
     @GetMapping
     public PageResponse<BaseQuestionVO> getQuestionList(LoginUser user, @RequestParam(required = false, defaultValue = "") String key, @PageableDefault(Integer.MAX_VALUE) Pageable pageable) {
         return PageResponse.of(questionService.getQuestions(user, key, pageable));
@@ -46,7 +46,7 @@ public class QuestionController {
     /**
      * 获取某一测试题目
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获得测试题目")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获得测试题目")
     @GetMapping("/quiz/{quizId}")
     public List<BaseQuestionVO> getQuestionsByQuiz(LoginUser user, @PathVariable Long quizId) {
         return questionService.getQuestionsByQuiz(user, quizId);
@@ -55,7 +55,7 @@ public class QuestionController {
     /**
      * 获取某一题目信息
      */
-    @Auth(roles = UserType.TEACHER, message = "获取题目信息")
+    @Auth(roles = UserRole.TEACHER, message = "获取题目信息")
     @GetMapping("/{questionId}")
     public BaseQuestionVO getOneQuestion(LoginUser user, @PathVariable String questionId) {
         return questionService.getOneQuestion(user, questionId);
@@ -64,7 +64,7 @@ public class QuestionController {
     /**
      * 创建题目
      */
-    @Auth(roles = UserType.TEACHER, message = "创建题目")
+    @Auth(roles = UserRole.TEACHER, message = "创建题目")
     @PostMapping
     public BaseQuestionVO createQuestion(LoginUser user, @Validated(Create.class) @RequestBody BaseQuestionDTO baseQuestionDTO) {
         return questionService.createQuestion(user, baseQuestionDTO);
@@ -73,7 +73,7 @@ public class QuestionController {
     /**
      * 修改题目
      */
-    @Auth(roles = UserType.TEACHER, message = "修改题目")
+    @Auth(roles = UserRole.TEACHER, message = "修改题目")
     @PutMapping("/{questionId}")
     public BaseQuestionVO modifyQuestion(LoginUser user, @PathVariable String questionId, @Validated(Modify.class) @RequestBody BaseQuestionDTO baseQuestionDTO) {
         return questionService.modifyQuestion(user, questionId, baseQuestionDTO);
@@ -82,7 +82,7 @@ public class QuestionController {
     /**
      * 删除题目
      */
-    @Auth(roles = UserType.TEACHER, message = "删除题目")
+    @Auth(roles = UserRole.TEACHER, message = "删除题目")
     @DeleteMapping("/{questionId}")
     public void deleteQuestion(LoginUser user, @PathVariable String questionId) {
         questionService.deleteQuestion(user, questionId);
@@ -91,7 +91,7 @@ public class QuestionController {
     /**
      * 获取创建题目
      */
-    @Auth(roles = UserType.TEACHER, message = "获取创建的题目")
+    @Auth(roles = UserRole.TEACHER, message = "获取创建的题目")
     @GetMapping("/created")
     public PageResponse<BaseQuestionVO> getCreatedQuestions(LoginUser user,
                                                             @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
@@ -101,9 +101,15 @@ public class QuestionController {
     /**
      * 获取创建题目ID
      */
-    @Auth(roles = UserType.TEACHER)
+    @Auth(roles = UserRole.TEACHER)
     @GetMapping("/created/ids")
     public Set<String> getCreatedQuestionIds(LoginUser user) {
         return questionService.getIdsOfCreatedQuestions(user);
     }
+
+    @Auth(roles = UserRole.STUDENT, message = "收藏题目")
+    @PostMapping("/{questionId}/star")
+    public void starQuestion(LoginUser user, @PathVariable String questionId) {
+        questionService.starQuestion(user, questionId);
+    }
 }

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

@@ -8,7 +8,7 @@ import nju.seec.helper.dto.groups.Modify;
 import nju.seec.helper.dto.quiz.QuizDTO;
 import nju.seec.helper.dto.quiz.QuizStudentAnswerDTO;
 import nju.seec.helper.enums.QuizState;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.QuizService;
 import nju.seec.helper.service.QuizStudentAnswerService;
 import nju.seec.helper.vo.quiz.QuizStudentAnswerStatisticVO;
@@ -41,13 +41,13 @@ public class QuizController {
     /**
      * 基于课件获取测试列表
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获得测试列表")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获得测试列表")
     @GetMapping("/slide/{slideId}")
     public PageResponse<QuizVO> getQuizzesBySlide(LoginUser user,
                                                   @PathVariable Long slideId,
                                                   @RequestParam(required = false, defaultValue = "") String key,
                                                   @PageableDefault(Integer.MAX_VALUE) Pageable pageable) {
-        switch (user.getType()) {
+        switch (user.getRole()) {
             case TEACHER:
                 return PageResponse.of(quizService.teacherGetQuizzesBySlide(user, slideId, key, pageable));
             case STUDENT:
@@ -60,13 +60,13 @@ public class QuizController {
     /**
      * 基于状态获取测试列表
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取测试列表")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取测试列表")
     @GetMapping("/state")
     public PageResponse<QuizVO> getQuizzesByState(LoginUser user,
                                                   QuizState state,
                                                   @RequestParam(required = false, defaultValue = "") String key,
                                                   @PageableDefault(Integer.MAX_VALUE) Pageable pageable) {
-        switch (user.getType()) {
+        switch (user.getRole()) {
             case TEACHER:
                 return PageResponse.of(quizService.teacherGetQuizzesByState(user, state, key, pageable));
             case STUDENT:
@@ -79,7 +79,7 @@ public class QuizController {
     /**
      * 获得某一测试的详细内容
      */
-    @Auth(roles = {UserType.STUDENT, UserType.TEACHER}, message = "获得测试")
+    @Auth(roles = {UserRole.STUDENT, UserRole.TEACHER}, message = "获得测试")
     @GetMapping("/{quizId}")
     public QuizVO getOneQuiz(LoginUser user, @PathVariable("quizId") Long quizId) {
         return quizService.getOneQuiz(user, quizId);
@@ -88,7 +88,7 @@ public class QuizController {
     /**
      * 创建测试
      */
-    @Auth(roles = {UserType.TEACHER}, message = "创建测试")
+    @Auth(roles = {UserRole.TEACHER}, message = "创建测试")
     @PostMapping
     public QuizVO createQuiz(LoginUser user, @Validated(Create.class) @RequestBody QuizDTO quizDTO) {
         return quizService.createQuiz(user, quizDTO);
@@ -97,7 +97,7 @@ public class QuizController {
     /**
      * 修改测试 (仅限未开始时可以修改)
      */
-    @Auth(roles = {UserType.TEACHER}, message = "修改测试")
+    @Auth(roles = {UserRole.TEACHER}, message = "修改测试")
     @PutMapping("/{quizId}")
     public QuizVO updateQuiz(LoginUser user, @PathVariable Long quizId, @Validated(Modify.class) @RequestBody QuizDTO quizDTO) {
         return quizService.modifyQuiz(user, quizId, quizDTO);
@@ -106,7 +106,7 @@ public class QuizController {
     /**
      * 删除测试 (伪删除)
      */
-    @Auth(roles = {UserType.TEACHER}, message = "删除测试")
+    @Auth(roles = {UserRole.TEACHER}, message = "删除测试")
     @DeleteMapping("/{quizId}")
     public void deleteQuiz(LoginUser user, @PathVariable("quizId") String quizId) {
         quizService.deleteQuiz(user, Long.valueOf(quizId));
@@ -115,16 +115,16 @@ public class QuizController {
     /**
      * 提交作答
      */
-    @Auth(roles = {UserType.STUDENT}, message = "提交作答")
+    @Auth(roles = {UserRole.STUDENT}, message = "提交作答")
     @PostMapping("/{quizId}/student-answer")
-    public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, @PathVariable Long quizId, @RequestBody QuizStudentAnswerDTO quizStudentAnswerDTO) {
+    public QuizStudentAnswerVO submitQuizStudentAnswer(LoginUser user, @PathVariable Long quizId, @Validated @RequestBody QuizStudentAnswerDTO quizStudentAnswerDTO) {
         return quizStudentAnswerService.submitQuizStudentAnswer(user, quizId, quizStudentAnswerDTO);
     }
 
     /**
      * 获取测试作答列表
      */
-    @Auth(roles = {UserType.TEACHER}, message = "获取学生作答")
+    @Auth(roles = {UserRole.TEACHER}, message = "获取学生作答")
     @GetMapping("/{quizId}/student-answers")
     public PageResponse<QuizStudentAnswerVO> getQuizStudentAnswers(LoginUser user,
                                                                    @PathVariable Long quizId,
@@ -136,7 +136,7 @@ public class QuizController {
     /**
      * 学生获取某一测试自己的作答
      */
-    @Auth(roles = {UserType.STUDENT}, message = "获取作答")
+    @Auth(roles = {UserRole.STUDENT}, message = "获取作答")
     @GetMapping("/{quizId}/student-answer")
     public QuizStudentAnswerVO getOneQuizStudentAnswer(LoginUser user, @PathVariable Long quizId) {
         return quizStudentAnswerService.getQuizStudentAnswer(user, quizId);
@@ -145,7 +145,7 @@ public class QuizController {
     /**
      * 获取测试作答提交的统计信息
      */
-    @Auth(roles = {UserType.TEACHER}, message = "获取测试统计消息")
+    @Auth(roles = {UserRole.TEACHER}, message = "获取测试统计消息")
     @GetMapping("/{quizId}/statistic")
     public QuizStudentAnswerStatisticVO getQuizStatistic(LoginUser user, @PathVariable Long quizId) {
         return quizStudentAnswerService.getQuizStudentAnswerStatistic(user, quizId);

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

@@ -3,7 +3,7 @@ package nju.seec.helper.controller;
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dto.reply.ReplyDTO;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.ReplyService;
 import nju.seec.helper.vo.ReplyVO;
 import org.springframework.validation.annotation.Validated;
@@ -26,7 +26,7 @@ public class ReplyController {
     /**
      * 教师回复
      */
-    @Auth(roles = UserType.TEACHER, message = "回复")
+    @Auth(roles = UserRole.TEACHER, message = "回复")
     @PostMapping
     public ReplyVO createReply(LoginUser user,
                                @Validated @RequestBody ReplyDTO replyDTO) {
@@ -36,7 +36,7 @@ public class ReplyController {
     /**
      * 教师删除回复
      */
-    @Auth(roles = UserType.TEACHER, message = "删除回复")
+    @Auth(roles = UserRole.TEACHER, message = "删除回复")
     @DeleteMapping("/{replyId}")
     public void deleteReply(LoginUser user,
                             @PathVariable Long replyId) {

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

@@ -8,7 +8,7 @@ import nju.seec.helper.dto.groups.Modify;
 import nju.seec.helper.dto.slide.SlideDTO;
 import nju.seec.helper.dto.slide.SlideFileDTO;
 import nju.seec.helper.dto.slide.SlideStateDTO;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.SlideService;
 import nju.seec.helper.vo.SlideVO;
 import org.springframework.data.domain.Pageable;
@@ -34,7 +34,7 @@ public class SlideController {
     /**
      * 教师创建课件
      */
-    @Auth(roles = UserType.TEACHER, message = "创建课件")
+    @Auth(roles = UserRole.TEACHER, message = "创建课件")
     @PostMapping
     public ResponseEntity<SlideVO> createSlide(LoginUser user,
                                                @Validated(Create.class) SlideDTO slideDTO) {
@@ -45,7 +45,7 @@ public class SlideController {
     /**
      * 教师修改课件
      */
-    @Auth(roles = UserType.TEACHER, message = "修改课件")
+    @Auth(roles = UserRole.TEACHER, message = "修改课件")
     @PutMapping("/{slideId}")
     public SlideVO modifySlide(LoginUser user,
                                @PathVariable Long slideId,
@@ -56,7 +56,7 @@ public class SlideController {
     /**
      * 教师修改课件状态
      */
-    @Auth(roles = UserType.TEACHER, message = "修改课件状态")
+    @Auth(roles = UserRole.TEACHER, message = "修改课件状态")
     @PutMapping("/{slideId}/state")
     public void modifySlideState(LoginUser user,
                                  @PathVariable Long slideId,
@@ -67,7 +67,7 @@ public class SlideController {
     /**
      * 教师修改课件文件
      */
-    @Auth(roles = UserType.TEACHER, message = "修改课件文件")
+    @Auth(roles = UserRole.TEACHER, message = "修改课件文件")
     @PutMapping("/{slideId}/file")
     public void modifySlideFile(LoginUser user,
                                 @PathVariable Long slideId,
@@ -78,7 +78,7 @@ public class SlideController {
     /**
      * 教师删除课件
      */
-    @Auth(roles = UserType.TEACHER, message = "删除课件")
+    @Auth(roles = UserRole.TEACHER, message = "删除课件")
     @DeleteMapping("/{slideId}")
     public void deleteSlide(LoginUser user,
                             @PathVariable Long slideId) {
@@ -88,7 +88,7 @@ public class SlideController {
     /**
      * 教师获取课件
      */
-    @Auth(roles = UserType.TEACHER, message = "获取课件列表")
+    @Auth(roles = UserRole.TEACHER, message = "获取课件列表")
     @GetMapping
     public PageResponse<SlideVO> getSlides(LoginUser user,
                                            @RequestParam(required = false, defaultValue = "") String key,
@@ -99,7 +99,7 @@ public class SlideController {
     /**
      * 基于课程获取课件
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取课件列表")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取课件列表")
     @GetMapping("/course/{courseId}")
     public PageResponse<SlideVO> getSlidesByCourse(LoginUser user,
                                                    @PathVariable Long courseId,
@@ -111,7 +111,7 @@ public class SlideController {
     /**
      * 获取课件链接
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取课件链接")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取课件链接")
     @GetMapping("/{slideId}/url")
     public String getSlideUrl(LoginUser user,
                               @PathVariable Long slideId) {
@@ -121,7 +121,7 @@ public class SlideController {
     /**
      * 获取某课件
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取课件")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取课件")
     @GetMapping("/{slideId}")
     public SlideVO getOneSlide(LoginUser user, @PathVariable Long slideId) {
         return slideService.getOneSlide(user, slideId);

+ 75 - 88
src/main/java/nju/seec/helper/controller/UserController.java

@@ -1,26 +1,13 @@
 package nju.seec.helper.controller;
 
-import com.google.common.collect.ImmutableMap;
 import nju.seec.helper.aspect.auth.Auth;
 import nju.seec.helper.aspect.auth.LoginUser;
-import nju.seec.helper.dto.common.LoginDTO;
-import nju.seec.helper.dto.groups.Create;
-import nju.seec.helper.dto.groups.Modify;
-import nju.seec.helper.dto.user.ResetPasswordDTO;
-import nju.seec.helper.dto.user.UserDTO;
-import nju.seec.helper.enums.ExceptionType;
-import nju.seec.helper.enums.UserState;
-import nju.seec.helper.enums.UserType;
-import nju.seec.helper.exception.HelperException;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.service.UserService;
-import nju.seec.helper.util.Consts;
 import nju.seec.helper.vo.UserVO;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import javax.servlet.http.HttpSession;
-import java.util.Map;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 /**
  * 用户
@@ -30,86 +17,86 @@ import java.util.Map;
 @RestController
 @RequestMapping("/api/user")
 public class UserController {
-    @Value("${helper.user-timeout-seconds}")
-    private int userTimeoutSeconds;
-
+    //    @Value("${helper.user-timeout-seconds}")
+//    private int userTimeoutSeconds;
+//
     private final UserService userService;
 
     public UserController(UserService userService) {
         this.userService = userService;
     }
-
-    /**
-     * 注册
-     */
-    @PostMapping("/register")
-    public UserVO register(@Validated(Create.class) @RequestBody UserDTO userDTO) {
-        return userService.createUser(userDTO);
-    }
-
-    private static final Map<UserState, String> STATE_MESSAGE_MAP = ImmutableMap.of(
-            UserState.CHECKING, "帐号审核中",
-            UserState.LOCKED, "帐号封禁中"
-    );
-
-    /**
-     * 邮箱登录
-     */
-    @PostMapping("/login/email")
-    public UserVO loginByEmail(@Validated @RequestBody LoginDTO loginDTO,
-                               HttpSession session) {
-        UserVO userVO = userService.getUserByEmailAndPassword(loginDTO.getUsername(), loginDTO.getPassword());
-
-        if (userVO.getState() != UserState.NORMAL) {
-            throw HelperException.of(ExceptionType.FORBIDDEN, STATE_MESSAGE_MAP.get(userVO.getState()));
-        }
-
-        LoginUser user = new LoginUser()
-                .setId(userVO.getId())
-                .setType(userVO.getType());
-
-        session.setAttribute(Consts.SESSION_USER_NAME, user);
-        session.setMaxInactiveInterval(userTimeoutSeconds);
-        return userVO;
-    }
-
-    /**
-     * 登出
-     */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "登出")
-    @PostMapping("/logout")
-    public void logout(HttpSession session) {
-        session.removeAttribute(Consts.SESSION_USER_NAME);
-    }
-
-    /**
-     * 修改个人信息
-     */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "修改个人信息")
-    @PutMapping
-    public UserVO modifyUser(LoginUser user,
-                             @Validated(Modify.class) @RequestBody UserDTO userDTO,
-                             HttpSession session) {
-        UserVO userVO = userService.modifyUser(user, userDTO);
-        user = ((LoginUser) session.getAttribute(Consts.SESSION_USER_NAME))
-                .setId(userVO.getId())
-                .setType(userVO.getType());
-        session.setAttribute(Consts.SESSION_USER_NAME, user);
-        return userVO;
-    }
-
-    /**
-     * 重设密码
-     */
-    @PutMapping("/resetPassword/email")
-    public void resetPasswordByEmail(@Validated @RequestBody ResetPasswordDTO resetPasswordDTO) {
-        userService.resetPasswordByEmail(resetPasswordDTO);
-    }
+//
+//    /**
+//     * 注册
+//     */
+//    @PostMapping("/register")
+//    public UserVO register(@Validated(Create.class) @RequestBody UserDTO userDTO) {
+//        return userService.createUser(userDTO);
+//    }
+//
+//    private static final Map<UserState, String> STATE_MESSAGE_MAP = ImmutableMap.of(
+//            UserState.CHECKING, "帐号审核中",
+//            UserState.LOCKED, "帐号封禁中"
+//    );
+//
+//    /**
+//     * 邮箱登录
+//     */
+//    @PostMapping("/login/email")
+//    public UserVO loginByEmail(@Validated @RequestBody LoginDTO loginDTO,
+//                               HttpSession session) {
+//        UserVO userVO = userService.getUserByEmailAndPassword(loginDTO.getUsername(), loginDTO.getPassword());
+//
+//        if (userVO.getState() != UserState.NORMAL) {
+//            throw HelperException.of(ExceptionType.FORBIDDEN, STATE_MESSAGE_MAP.get(userVO.getState()));
+//        }
+//
+//        LoginUser user = new LoginUser()
+//                .setId(userVO.getId())
+//                .setType(userVO.getType());
+//
+//        session.setAttribute(Consts.SESSION_USER_NAME, user);
+//        session.setMaxInactiveInterval(userTimeoutSeconds);
+//        return userVO;
+//    }
+//
+//    /**
+//     * 登出
+//     */
+//    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "登出")
+//    @PostMapping("/logout")
+//    public void logout(HttpSession session) {
+//        session.removeAttribute(Consts.SESSION_USER_NAME);
+//    }
+//
+//    /**
+//     * 修改个人信息
+//     */
+//    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "修改个人信息")
+//    @PutMapping
+//    public UserVO modifyUser(LoginUser user,
+//                             @Validated(Modify.class) @RequestBody UserDTO userDTO,
+//                             HttpSession session) {
+//        UserVO userVO = userService.modifyUser(user, userDTO);
+//        user = ((LoginUser) session.getAttribute(Consts.SESSION_USER_NAME))
+//                .setId(userVO.getId())
+//                .setType(userVO.getType());
+//        session.setAttribute(Consts.SESSION_USER_NAME, user);
+//        return userVO;
+//    }
+//
+//    /**
+//     * 重设密码
+//     */
+//    @PutMapping("/resetPassword/email")
+//    public void resetPasswordByEmail(@Validated @RequestBody ResetPasswordDTO resetPasswordDTO) {
+//        userService.resetPasswordByEmail(resetPasswordDTO);
+//    }
 
     /**
      * 获取个人信息
      */
-    @Auth(roles = {UserType.TEACHER, UserType.STUDENT}, message = "获取个人信息")
+    @Auth(roles = {UserRole.TEACHER, UserRole.STUDENT}, message = "获取个人信息")
     @GetMapping
     public UserVO getUserInfo(LoginUser user) {
         return userService.getUserById(user.getId());

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

@@ -5,6 +5,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -46,4 +47,12 @@ public interface ChooseDAO extends JpaRepository<Choose, Long> {
      */
     @Query("select distinct c.studentId from Choose c where c.courseId=?1")
     Set<Long> findStudentIdsByCourseId(Long courseId);
+
+    /**
+     * 根据课程检索
+     *
+     * @param courseId
+     * @return
+     */
+    List<Choose> findByCourseId(Long courseId);
 }

+ 38 - 22
src/main/java/nju/seec/helper/dao/UserDAO.java

@@ -28,39 +28,55 @@ public interface UserDAO extends JpaRepository<User, Long>, JpaSpecificationExec
                 .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "找不到用户信息"));
     }
 
-    /**
-     * 检查邮箱是否已存在
-     *
-     * @param email
-     * @return
-     */
-    boolean existsByEmail(String email);
-
+//    /**
+////     * 检查邮箱是否已存在
+////     *
+////     * @param email
+////     * @return
+////     */
+////    boolean existsByEmail(String email);
+////
+////
+////    /**
+////     * 检查手机号是否已存在
+////     *
+////     * @param phone
+////     * @return
+////     */
+////    boolean existsByPhone(String phone);
+////
+////    /**
+////     * 邮箱密码匹配
+////     *
+////     * @param email
+////     * @param password
+////     * @return
+////     */
+////    Optional<User> findByEmailAndPassword(String email, String password);
+////
+////    /**
+////     * 根据邮箱查询
+////     *
+////     * @param email
+////     * @return
+////     */
+////    Optional<User> findByEmail(String email);
 
     /**
-     * 检查手机号是否已存在
+     * 根据手机号查询
      *
      * @param phone
      * @return
      */
-    boolean existsByPhone(String phone);
-
-    /**
-     * 邮箱密码匹配
-     *
-     * @param email
-     * @param password
-     * @return
-     */
-    Optional<User> findByEmailAndPassword(String email, String password);
+    Optional<User> findByPhone(String phone);
 
     /**
-     * 根据邮箱查询
+     * 根据PID查询
      *
-     * @param email
+     * @param pid
      * @return
      */
-    Optional<User> findByEmail(String email);
+    Optional<User> findByPid(Long pid);
 
     /**
      * ID列表查询

+ 3 - 4
src/main/java/nju/seec/helper/dto/question/BaseQuestionDTO.java

@@ -8,7 +8,7 @@ import org.hibernate.validator.constraints.Length;
 
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
-import java.util.Set;
+import java.util.List;
 
 /**
  * @author cst
@@ -37,7 +37,6 @@ public abstract class BaseQuestionDTO {
     protected String type;
 
     protected String keyPoints;
-    protected Set<String> tags;
-    protected Set<String> knowledgeId;
-
+    protected List<String> tags;
+    protected List<String> knowledgeId;
 }

+ 17 - 0
src/main/java/nju/seec/helper/dto/quiz/QuizStudentAnswerDTO.java

@@ -2,8 +2,11 @@ package nju.seec.helper.dto.quiz;
 
 import lombok.Data;
 
+import javax.validation.Valid;
 import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
 import java.io.Serializable;
+import java.time.LocalDateTime;
 import java.util.Map;
 
 /**
@@ -14,4 +17,18 @@ public class QuizStudentAnswerDTO implements Serializable {
     private static final long serialVersionUID = -5846899734313166305L;
     @NotEmpty(message = "回答不能为空")
     private Map<String, Object> answers;
+
+    @Valid
+    private Map<String, AnswerRecordDTO> records;
+
+    @Data
+    public static class AnswerRecordDTO {
+        @NotNull(message = "做题记录缺少做题日期")
+        private LocalDateTime answerAt;
+        /**
+         * 秒数
+         */
+        @NotNull(message = "做题记录缺少时长")
+        private Long answerSeconds;
+    }
 }

+ 2 - 2
src/main/java/nju/seec/helper/dto/user/UserDTO.java

@@ -4,7 +4,7 @@ import lombok.Data;
 import nju.seec.helper.dto.groups.Create;
 import nju.seec.helper.dto.validator.phone.Phone;
 import nju.seec.helper.dto.validator.schoolemail.SchoolEmail;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import org.hibernate.validator.constraints.Length;
 
 import javax.validation.constraints.NotBlank;
@@ -35,5 +35,5 @@ public class UserDTO implements Serializable {
     private String password;
 
     @NotNull(message = "缺少用户类型", groups = Create.class)
-    private UserType type;
+    private UserRole role;
 }

+ 6 - 0
src/main/java/nju/seec/helper/entity/Choose.java

@@ -2,8 +2,10 @@ package nju.seec.helper.entity;
 
 import lombok.Data;
 import lombok.experimental.Accessors;
+import org.hibernate.annotations.CreationTimestamp;
 
 import javax.persistence.*;
+import java.time.LocalDateTime;
 
 /**
  * @author cst
@@ -22,4 +24,8 @@ public class Choose {
 
     @Column(name = "student_id", nullable = false)
     private Long studentId;
+
+    @Column(name = "choose_at")
+    @CreationTimestamp
+    private LocalDateTime chooseAt;
 }

+ 3 - 0
src/main/java/nju/seec/helper/entity/CourseFile.java

@@ -2,6 +2,7 @@ package nju.seec.helper.entity;
 
 import lombok.Data;
 import lombok.experimental.Accessors;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
 import org.hibernate.annotations.CreationTimestamp;
 
 import javax.persistence.*;
@@ -14,6 +15,8 @@ import java.time.LocalDateTime;
 @Accessors(chain = true)
 @Entity
 @Table(name = "course_file", uniqueConstraints = {@UniqueConstraint(name = "course_file_name_unique", columnNames = {"course_id", "file_name"})})
+@Cacheable
+@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
 public class CourseFile {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)

+ 3 - 0
src/main/java/nju/seec/helper/entity/Notice.java

@@ -2,6 +2,7 @@ package nju.seec.helper.entity;
 
 import lombok.Data;
 import lombok.experimental.Accessors;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
 import org.hibernate.annotations.CreationTimestamp;
 import org.hibernate.annotations.UpdateTimestamp;
 
@@ -15,6 +16,8 @@ import java.time.LocalDateTime;
 @Accessors(chain = true)
 @Entity
 @Table(name = "notice")
+@Cacheable
+@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
 public class Notice {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)

+ 12 - 10
src/main/java/nju/seec/helper/entity/User.java

@@ -2,12 +2,12 @@ package nju.seec.helper.entity;
 
 import lombok.Data;
 import lombok.experimental.Accessors;
-import nju.seec.helper.enums.UserState;
-import nju.seec.helper.enums.UserType;
-import org.hibernate.annotations.*;
+import nju.seec.helper.enums.UserRole;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.hibernate.annotations.CreationTimestamp;
+import org.hibernate.annotations.DynamicInsert;
+import org.hibernate.annotations.DynamicUpdate;
 
-import javax.persistence.Entity;
-import javax.persistence.Table;
 import javax.persistence.*;
 import java.time.LocalDateTime;
 
@@ -36,7 +36,7 @@ public class User {
 
     @Enumerated(EnumType.STRING)
     @Column(nullable = false, updatable = false)
-    private UserType type;
+    private UserRole role;
 
     @Column(nullable = false)
     private String phone;
@@ -48,8 +48,10 @@ public class User {
     @CreationTimestamp
     private LocalDateTime createAt;
 
-    @ColumnDefault("'NORMAL'")
-    @Enumerated(EnumType.STRING)
-    @Column(nullable = false)
-    private UserState state;
+//    @ColumnDefault("'NORMAL'")
+//    @Enumerated(EnumType.STRING)
+//    @Column(nullable = false)
+//    private UserState state;
+
+    private Long pid;
 }

+ 1 - 1
src/main/java/nju/seec/helper/enums/ExceptionType.java

@@ -10,7 +10,7 @@ import org.springframework.http.HttpStatus;
 @Getter
 public enum ExceptionType {
     // http code
-    PARAM_ERROR(HttpStatus.BAD_REQUEST), NOT_LOGIN(HttpStatus.UNAUTHORIZED), FORBIDDEN(HttpStatus.FORBIDDEN), NOT_FOUND(HttpStatus.NOT_FOUND), CONFLICT(HttpStatus.CONFLICT), ERROR(HttpStatus.INTERNAL_SERVER_ERROR);
+    PARAM_ERROR(HttpStatus.BAD_REQUEST), UNAUTHORIZED(HttpStatus.UNAUTHORIZED), FORBIDDEN(HttpStatus.FORBIDDEN), NOT_FOUND(HttpStatus.NOT_FOUND), CONFLICT(HttpStatus.CONFLICT), ERROR(HttpStatus.INTERNAL_SERVER_ERROR);
 
     private HttpStatus status;
 

+ 20 - 0
src/main/java/nju/seec/helper/enums/UserRole.java

@@ -0,0 +1,20 @@
+package nju.seec.helper.enums;
+
+/**
+ * @author cst
+ */
+
+public enum UserRole {
+    /**
+     * 教师
+     */
+    TEACHER,
+    /**
+     * 学生
+     */
+    STUDENT,
+    /**
+     * 管理员
+     */
+    ADMIN
+}

+ 8 - 8
src/main/java/nju/seec/helper/enums/UserState.java

@@ -1,8 +1,8 @@
-package nju.seec.helper.enums;
-
-/**
- * @author cst
- */
-public enum UserState {
-    CHECKING, NORMAL, LOCKED
-}
+//package nju.seec.helper.enums;
+//
+///**
+// * @author cst
+// */
+//public enum UserState {
+//    CHECKING, NORMAL, LOCKED
+//}

+ 0 - 9
src/main/java/nju/seec/helper/enums/UserType.java

@@ -1,9 +0,0 @@
-package nju.seec.helper.enums;
-
-/**
- * @author cst
- */
-
-public enum UserType {
-    TEACHER, STUDENT, ADMIN
-}

+ 58 - 58
src/main/java/nju/seec/helper/service/AdminService.java

@@ -1,58 +1,58 @@
-package nju.seec.helper.service;
-
-import nju.seec.helper.aspect.auth.LoginUser;
-import nju.seec.helper.enums.UserState;
-import nju.seec.helper.enums.UserType;
-import nju.seec.helper.vo.AdminVO;
-import nju.seec.helper.vo.UserVO;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
-
-/**
- * @author cst
- */
-public interface AdminService {
-    /**
-     * 用户名密码获取
-     *
-     * @param username
-     * @param password
-     * @return
-     */
-    AdminVO getAdminByUsernameAndPassword(String username, String password);
-
-    /**
-     * 获取用户列表
-     *
-     * @param type
-     * @param state
-     * @param key
-     * @param pageable
-     * @return
-     */
-    Page<UserVO> getUsers(UserType type, UserState state, String key, Pageable pageable);
-
-    /**
-     * 审核通过用户
-     *
-     * @param admin
-     * @param userId
-     */
-    void passUser(LoginUser admin, Long userId);
-
-    /**
-     * 封禁用户
-     *
-     * @param admin
-     * @param userId
-     */
-    void lockUser(LoginUser admin, Long userId);
-
-    /**
-     * 解封用户
-     *
-     * @param admin
-     * @param userId
-     */
-    void unlockUser(LoginUser admin, Long userId);
-}
+//package nju.seec.helper.service;
+//
+//import nju.seec.helper.aspect.auth.LoginUser;
+//import nju.seec.helper.enums.UserRole;
+//import nju.seec.helper.enums.UserState;
+//import nju.seec.helper.vo.AdminVO;
+//import nju.seec.helper.vo.UserVO;
+//import org.springframework.data.domain.Page;
+//import org.springframework.data.domain.Pageable;
+//
+///**
+// * @author cst
+// */
+//public interface AdminService {
+//    /**
+//     * 用户名密码获取
+//     *
+//     * @param username
+//     * @param password
+//     * @return
+//     */
+//    AdminVO getAdminByUsernameAndPassword(String username, String password);
+//
+//    /**
+//     * 获取用户列表
+//     *
+//     * @param role
+//     * @param state
+//     * @param key
+//     * @param pageable
+//     * @return
+//     */
+//    Page<UserVO> getUsers(UserRole role, UserState state, String key, Pageable pageable);
+//
+//    /**
+//     * 审核通过用户
+//     *
+//     * @param admin
+//     * @param userId
+//     */
+//    void passUser(LoginUser admin, Long userId);
+//
+//    /**
+//     * 封禁用户
+//     *
+//     * @param admin
+//     * @param userId
+//     */
+//    void lockUser(LoginUser admin, Long userId);
+//
+//    /**
+//     * 解封用户
+//     *
+//     * @param admin
+//     * @param userId
+//     */
+//    void unlockUser(LoginUser admin, Long userId);
+//}

+ 20 - 20
src/main/java/nju/seec/helper/service/CodeService.java

@@ -1,20 +1,20 @@
-package nju.seec.helper.service;
-
-/**
- * @author cst
- */
-public interface CodeService {
-    /**
-     * 发送邮箱验证码
-     *
-     * @param email
-     */
-    void sendEmailCode(String email);
-
-    /**
-     * 发送手机验证码
-     *
-     * @param phone
-     */
-    void sendPhoneCode(String phone);
-}
+//package nju.seec.helper.service;
+//
+///**
+// * @author cst
+// */
+//public interface CodeService {
+//    /**
+//     * 发送邮箱验证码
+//     *
+//     * @param email
+//     */
+//    void sendEmailCode(String email);
+//
+//    /**
+//     * 发送手机验证码
+//     *
+//     * @param phone
+//     */
+//    void sendPhoneCode(String phone);
+//}

+ 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();
+}

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

@@ -95,4 +95,12 @@ public interface QuestionService {
      * @return
      */
     Set<String> getIdsOfCreatedQuestions(LoginUser user);
+
+    /**
+     * 收藏题目
+     *
+     * @param user
+     * @param questionId
+     */
+    void starQuestion(LoginUser user, String questionId);
 }

+ 42 - 25
src/main/java/nju/seec/helper/service/UserService.java

@@ -1,46 +1,63 @@
 package nju.seec.helper.service;
 
-import nju.seec.helper.aspect.auth.LoginUser;
-import nju.seec.helper.dto.user.ResetPasswordDTO;
-import nju.seec.helper.dto.user.UserDTO;
+import nju.seec.helper.entity.User;
 import nju.seec.helper.vo.UserVO;
 
+import java.util.Optional;
+
 /**
  * @author cst
  */
 public interface UserService {
-    /**
-     * 创建用户
-     *
-     * @param userDTO
-     * @return
-     */
-    UserVO createUser(UserDTO userDTO);
+//    /**
+//     * 创建用户
+//     *
+//     * @param userDTO
+//     * @return
+//     */
+//    UserVO createUser(UserDTO userDTO);
+//
+//    /**
+//     * 修改用户信息
+//     *
+//     * @param loginUser
+//     * @param userDTO
+//     * @return
+//     */
+//    UserVO modifyUser(LoginUser loginUser, UserDTO userDTO);
+//
+//    /**
+//     * 重设密码
+//     *
+//     * @param resetPasswordDTO
+//     */
+//    void resetPasswordByEmail(ResetPasswordDTO resetPasswordDTO);
+//
+//    /**
+//     * 根据邮箱和密码匹配用户
+//     *
+//     * @param email
+//     * @param password
+//     * @return
+//     */
+//    UserVO getUserByEmailAndPassword(String email, String password);
 
     /**
-     * 修改用户信息
+     * 与门户对接
      *
-     * @param loginUser
-     * @param userDTO
+     * @param user
      * @return
      */
-    UserVO modifyUser(LoginUser loginUser, UserDTO userDTO);
-
-    /**
-     * 重设密码
-     *
-     * @param resetPasswordDTO
-     */
-    void resetPasswordByEmail(ResetPasswordDTO resetPasswordDTO);
+    User createOrUpdateUser(User user);
 
     /**
-     * 根据邮箱和密码匹配用户
+     * 根据电话或PID查询
      *
-     * @param email
-     * @param password
+     * @param phone
+     * @param pid
      * @return
      */
-    UserVO getUserByEmailAndPassword(String email, String password);
+    Optional<User> getUserByPhoneOrPid(String phone, Long pid);
 
     /**
      * 根据ID获取用户信息

+ 86 - 86
src/main/java/nju/seec/helper/service/impl/AdminServiceImpl.java

@@ -1,86 +1,86 @@
-package nju.seec.helper.service.impl;
-
-import nju.seec.helper.aspect.auth.LoginUser;
-import nju.seec.helper.dao.AdminDAO;
-import nju.seec.helper.dao.UserDAO;
-import nju.seec.helper.entity.User;
-import nju.seec.helper.enums.ExceptionType;
-import nju.seec.helper.enums.UserState;
-import nju.seec.helper.enums.UserType;
-import nju.seec.helper.exception.HelperException;
-import nju.seec.helper.service.AdminService;
-import nju.seec.helper.util.EncryptUtils;
-import nju.seec.helper.util.StringUtils;
-import nju.seec.helper.vo.AdminVO;
-import nju.seec.helper.vo.UserVO;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
-import org.springframework.data.jpa.domain.Specification;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * @author cst
- */
-@Service
-public class AdminServiceImpl implements AdminService {
-    private final AdminDAO adminDAO;
-    private final UserDAO userDAO;
-
-    public AdminServiceImpl(AdminDAO adminDAO, UserDAO userDAO) {
-        this.adminDAO = adminDAO;
-        this.userDAO = userDAO;
-    }
-
-    @Transactional(readOnly = true)
-    @Override
-    public AdminVO getAdminByUsernameAndPassword(String username, String password) {
-        return adminDAO.findByUsernameAndPassword(username, EncryptUtils.encode(password))
-                .map(AdminVO::new)
-                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "用户名密码错误"));
-    }
-
-    @Transactional(readOnly = true)
-    @Override
-    public Page<UserVO> getUsers(UserType type, UserState state, String key, Pageable pageable) {
-        return findUsersByTypeAndStateAndKey(type, state, key, pageable).map(UserVO::new);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public void passUser(LoginUser admin, Long userId) {
-        User user = userDAO.findUserById(userId);
-        if (user.getState() != UserState.CHECKING) {
-            throw HelperException.of(ExceptionType.FORBIDDEN, "该帐号暂时无法审核通过");
-        }
-        userDAO.save(user.setState(UserState.NORMAL));
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public void lockUser(LoginUser admin, Long userId) {
-        userDAO.save(userDAO.findUserById(userId).setState(UserState.LOCKED));
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public void unlockUser(LoginUser admin, Long userId) {
-        User user = userDAO.findUserById(userId);
-        if (user.getState() != UserState.LOCKED) {
-            throw HelperException.of(ExceptionType.FORBIDDEN, "该帐号暂时无法解封");
-        }
-        userDAO.save(user.setState(UserState.NORMAL));
-    }
-
-    private Page<User> findUsersByTypeAndStateAndKey(UserType userType, UserState userState, String key, Pageable pageable) {
-        final String keyPattern = StringUtils.keyPattern(key);
-
-        return userDAO.findAll(
-                (Specification<User>) (root, query, cb) -> cb.and(
-                        cb.equal(root.get("type").as(UserType.class), userType)
-                        , cb.equal(root.get("state").as(UserState.class), userState)
-                        , cb.or(cb.like(root.get("email"), keyPattern), cb.like(root.get("phone"), keyPattern), cb.like(root.get("name"), keyPattern))
-                )
-                , pageable);
-    }
-}
+//package nju.seec.helper.service.impl;
+//
+//import cn.hutool.crypto.SecureUtil;
+//import nju.seec.helper.aspect.auth.LoginUser;
+//import nju.seec.helper.dao.AdminDAO;
+//import nju.seec.helper.dao.UserDAO;
+//import nju.seec.helper.entity.User;
+//import nju.seec.helper.enums.ExceptionType;
+//import nju.seec.helper.enums.UserState;
+//import nju.seec.helper.enums.UserRole;
+//import nju.seec.helper.exception.HelperException;
+//import nju.seec.helper.service.AdminService;
+//import nju.seec.helper.util.StringUtils;
+//import nju.seec.helper.vo.AdminVO;
+//import nju.seec.helper.vo.UserVO;
+//import org.springframework.data.domain.Page;
+//import org.springframework.data.domain.Pageable;
+//import org.springframework.data.jpa.domain.Specification;
+//import org.springframework.stereotype.Service;
+//import org.springframework.transaction.annotation.Transactional;
+//
+///**
+// * @author cst
+// */
+//@Service
+//public class AdminServiceImpl implements AdminService {
+//    private final AdminDAO adminDAO;
+//    private final UserDAO userDAO;
+//
+//    public AdminServiceImpl(AdminDAO adminDAO, UserDAO userDAO) {
+//        this.adminDAO = adminDAO;
+//        this.userDAO = userDAO;
+//    }
+//
+//    @Transactional(readOnly = true)
+//    @Override
+//    public AdminVO getAdminByUsernameAndPassword(String username, String password) {
+//        return adminDAO.findByUsernameAndPassword(username, SecureUtil.sha256(password))
+//                .map(AdminVO::new)
+//                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "用户名密码错误"));
+//    }
+//
+//    @Transactional(readOnly = true)
+//    @Override
+//    public Page<UserVO> getUsers(UserRole role, UserState state, String key, Pageable pageable) {
+//        return findUsersByTypeAndStateAndKey(type, state, key, pageable).map(UserVO::new);
+//    }
+//
+//    @Transactional(rollbackFor = Exception.class)
+//    @Override
+//    public void passUser(LoginUser admin, Long userId) {
+//        User user = userDAO.findUserById(userId);
+//        if (user.getState() != UserState.CHECKING) {
+//            throw HelperException.of(ExceptionType.FORBIDDEN, "该帐号暂时无法审核通过");
+//        }
+//        userDAO.save(user.setState(UserState.NORMAL));
+//    }
+//
+//    @Transactional(rollbackFor = Exception.class)
+//    @Override
+//    public void lockUser(LoginUser admin, Long userId) {
+//        userDAO.save(userDAO.findUserById(userId).setState(UserState.LOCKED));
+//    }
+//
+//    @Transactional(rollbackFor = Exception.class)
+//    @Override
+//    public void unlockUser(LoginUser admin, Long userId) {
+//        User user = userDAO.findUserById(userId);
+//        if (user.getState() != UserState.LOCKED) {
+//            throw HelperException.of(ExceptionType.FORBIDDEN, "该帐号暂时无法解封");
+//        }
+//        userDAO.save(user.setState(UserState.NORMAL));
+//    }
+//
+//    private Page<User> findUsersByTypeAndStateAndKey(UserRole userRole, UserState userState, String key, Pageable pageable) {
+//        final String keyPattern = StringUtils.keyPattern(key);
+//
+//        return userDAO.findAll(
+//                (Specification<User>) (root, query, cb) -> cb.and(
+//                        cb.equal(root.get("type").as(UserRole.class), userRole)
+//                        , cb.equal(root.get("state").as(UserState.class), userState)
+//                        , cb.or(cb.like(root.get("email"), keyPattern), cb.like(root.get("phone"), keyPattern), cb.like(root.get("name"), keyPattern))
+//                )
+//                , pageable);
+//    }
+//}

+ 48 - 48
src/main/java/nju/seec/helper/service/impl/CodeServiceImpl.java

@@ -1,48 +1,48 @@
-package nju.seec.helper.service.impl;
-
-import nju.seec.helper.service.CodeService;
-import nju.seec.helper.util.Consts;
-import nju.seec.helper.util.MailUtils;
-import nju.seec.helper.util.RedisCacheUtils;
-import nju.seec.helper.util.SmsUtils;
-import org.springframework.stereotype.Service;
-
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
-
-/**
- * @author cst
- */
-@Service
-public class CodeServiceImpl implements CodeService {
-    private static final int MAIL_CODE_LIVING_MINUTES = 5;
-    private static final int PHONE_CODE_LIVING_MINUTES = 5;
-
-    private final MailUtils mailUtils;
-    private final SmsUtils smsUtils;
-    private final RedisCacheUtils cacheUtils;
-
-    public CodeServiceImpl(MailUtils mailUtils, SmsUtils smsUtils, RedisCacheUtils cacheUtils) {
-        this.mailUtils = mailUtils;
-        this.smsUtils = smsUtils;
-        this.cacheUtils = cacheUtils;
-    }
-
-    @Override
-    public void sendEmailCode(String email) {
-        String code = getSixBitsCode();
-        mailUtils.sendSimpleMailMessage(String.format("您的验证码:%s,%d分钟内有效,请勿泄漏于他人!", code, MAIL_CODE_LIVING_MINUTES), email);
-        cacheUtils.set(Consts.EMAIL_CACHE_NAME, email, code, MAIL_CODE_LIVING_MINUTES, TimeUnit.MINUTES);
-    }
-
-    private String getSixBitsCode() {
-        return String.valueOf(new Random().nextInt(900000) + 100000);
-    }
-
-    @Override
-    public void sendPhoneCode(String phone) {
-        String code = getSixBitsCode();
-        smsUtils.sendSmsCode(phone, code);
-        cacheUtils.set(Consts.PHONE_CACHE_NAME, phone, code, PHONE_CODE_LIVING_MINUTES, TimeUnit.MINUTES);
-    }
-}
+//package nju.seec.helper.service.impl;
+//
+//import nju.seec.helper.service.CodeService;
+//import nju.seec.helper.util.Consts;
+//import nju.seec.helper.util.MailUtils;
+//import nju.seec.helper.util.cache.RedisCacheUtils;
+//import nju.seec.helper.util.SmsUtils;
+//import org.springframework.stereotype.Service;
+//
+//import java.util.Random;
+//import java.util.concurrent.TimeUnit;
+//
+///**
+// * @author cst
+// */
+//@Service
+//public class CodeServiceImpl implements CodeService {
+//    private static final int MAIL_CODE_LIVING_MINUTES = 5;
+//    private static final int PHONE_CODE_LIVING_MINUTES = 5;
+//
+//    private final MailUtils mailUtils;
+//    private final SmsUtils smsUtils;
+//    private final RedisCacheUtils cacheUtils;
+//
+//    public CodeServiceImpl(MailUtils mailUtils, SmsUtils smsUtils, RedisCacheUtils cacheUtils) {
+//        this.mailUtils = mailUtils;
+//        this.smsUtils = smsUtils;
+//        this.cacheUtils = cacheUtils;
+//    }
+//
+//    @Override
+//    public void sendEmailCode(String email) {
+//        String code = getSixBitsCode();
+//        mailUtils.sendSimpleMailMessage(String.format("您的验证码:%s,%d分钟内有效,请勿泄漏于他人!", code, MAIL_CODE_LIVING_MINUTES), email);
+//        cacheUtils.set(Consts.EMAIL_CACHE_NAME, email, code, MAIL_CODE_LIVING_MINUTES, TimeUnit.MINUTES);
+//    }
+//
+//    private String getSixBitsCode() {
+//        return String.valueOf(new Random().nextInt(900000) + 100000);
+//    }
+//
+//    @Override
+//    public void sendPhoneCode(String phone) {
+//        String code = getSixBitsCode();
+//        smsUtils.sendSmsCode(phone, code);
+//        cacheUtils.set(Consts.PHONE_CACHE_NAME, phone, code, PHONE_CODE_LIVING_MINUTES, TimeUnit.MINUTES);
+//    }
+//}

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

@@ -11,8 +11,8 @@ import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.CourseFileService;
 import nju.seec.helper.service.CourseService;
-import nju.seec.helper.util.OssObjectUrlUtils;
-import nju.seec.helper.util.OssUtils;
+import nju.seec.helper.util.oss.OssObjectUrlUtils;
+import nju.seec.helper.util.oss.OssUtils;
 import nju.seec.helper.vo.CourseFileVO;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;

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

@@ -9,7 +9,7 @@ import nju.seec.helper.dto.course.CourseDTO;
 import nju.seec.helper.entity.Choose;
 import nju.seec.helper.entity.Course;
 import nju.seec.helper.enums.ExceptionType;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.CourseService;
 import nju.seec.helper.vo.CourseVO;
@@ -163,7 +163,7 @@ public class CourseServiceImpl implements CourseService {
 
     @Override
     public boolean existsAddAuth(LoginUser user, Course course) {
-        return user.getType() == UserType.TEACHER;
+        return user.getRole() == UserRole.TEACHER;
     }
 
     @Override

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

@@ -0,0 +1,88 @@
+package nju.seec.helper.service.impl;
+
+import cn.hutool.core.text.StrBuilder;
+import com.google.common.collect.Lists;
+import nju.seec.helper.api.dataanalysis.EventAction;
+import nju.seec.helper.api.dataanalysis.EventType;
+import nju.seec.helper.dao.*;
+import nju.seec.helper.entity.*;
+import nju.seec.helper.enums.UserRole;
+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.List;
+
+/**
+ * @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 QuizStudentAnswerDAO quizStudentAnswerDAO;
+
+    public EventServiceImpl(CourseDAO courseDAO, ChooseDAO chooseDAO, UserDAO userDAO, CommentDAO commentDAO, QuizStudentAnswerDAO quizStudentAnswerDAO) {
+        this.courseDAO = courseDAO;
+        this.chooseDAO = chooseDAO;
+        this.userDAO = userDAO;
+        this.commentDAO = commentDAO;
+        this.quizStudentAnswerDAO = quizStudentAnswerDAO;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<EventVO> getAllEvents() {
+        List<EventVO> eventVOList = Lists.newArrayList();
+        List<Course> courses = courseDAO.findAll();
+        courses.forEach(course -> {
+            List<Choose> chooses = chooseDAO.findByCourseId(course.getId());
+            chooses.forEach(
+                    choose -> {
+                        User student = userDAO.findUserById(choose.getStudentId());
+                        if (student.getPid() != null) {
+                            eventVOList.add(new EventVO()
+                                    .setUserId(String.valueOf(student.getPid()))
+                                    .setTime(choose.getChooseAt())
+                                    .setType(EventType.LESSON)
+                                    .setAction(EventAction.PARTICIPATE)
+                                    .setDescription(course.getName())
+                            );
+                        }
+                    }
+            );
+        });
+        List<Comment> comments = commentDAO.findAll();
+        comments.forEach(comment -> {
+            User user = comment.getUser();
+            if (user.getPid() != null && user.getRole() == UserRole.STUDENT) {
+                eventVOList.add(new EventVO()
+                        .setUserId(String.valueOf(user.getPid()))
+                        .setTime(comment.getCreateAt())
+                        .setType(EventType.POST)
+                        .setAction(EventAction.INITIATE)
+                        .setDescription(comment.getTitle())
+                );
+            }
+        });
+        List<QuizStudentAnswer> quizStudentAnswers = quizStudentAnswerDAO.findAll();
+        quizStudentAnswers.forEach(quizStudentAnswer -> {
+            User student = quizStudentAnswer.getStudent();
+            if (student.getPid() != null) {
+                Quiz quiz = quizStudentAnswer.getQuiz();
+                Course course = quiz.getCourse();
+                eventVOList.add(new EventVO()
+                        .setUserId(String.valueOf(student.getPid()))
+                        .setTime(quizStudentAnswer.getSubmitAt())
+                        .setType(EventType.QUIZ)
+                        .setAction(EventAction.COMPLETE)
+                        .setDescription(StrBuilder.create().append(course.getName()).append(";").append(quiz.getName()).toStringAndReset())
+                );
+            }
+        });
+        return eventVOList;
+    }
+}

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

@@ -1,6 +1,7 @@
 package nju.seec.helper.service.impl;
 
-import nju.seec.helper.api.BokApi;
+import cn.hutool.core.util.IdUtil;
+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;
@@ -10,11 +11,11 @@ import nju.seec.helper.entity.QuestionRecord;
 import nju.seec.helper.entity.Quiz;
 import nju.seec.helper.enums.ExceptionType;
 import nju.seec.helper.enums.QuizState;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.QuestionService;
 import nju.seec.helper.vo.question.BaseQuestionVO;
-import nju.seec.helper.vo.question.BokQuestion;
+import nju.seec.helper.vo.question.BokQuestionVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageImpl;
@@ -24,7 +25,6 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 import java.util.Set;
-import java.util.UUID;
 import java.util.stream.Collectors;
 
 /**
@@ -38,21 +38,21 @@ public class QuestionServiceImpl implements QuestionService {
     private final UserDAO userDAO;
     private final QuestionRecordDAO questionRecordDAO;
 
-    private final BokApi bokApi;
+    private final QuestionApi questionApi;
 
     @Autowired
     public QuestionServiceImpl(QuizDAO quizDAO
             , UserDAO userDAO
-            , QuestionRecordDAO questionRecordDAO, BokApi bokApi) {
+            , QuestionRecordDAO questionRecordDAO, QuestionApi questionApi) {
         this.quizDAO = quizDAO;
         this.userDAO = userDAO;
         this.questionRecordDAO = questionRecordDAO;
-        this.bokApi = bokApi;
+        this.questionApi = questionApi;
     }
 
     @Override
     public Page<BaseQuestionVO> getQuestions(LoginUser user, String stem, Pageable pageable) {
-        return bokApi.bokFindByStemLike(stem, pageable).map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true));
+        return questionApi.bokFindByStemLike(stem, pageable).map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true));
     }
 
     @Transactional(readOnly = true)
@@ -60,9 +60,9 @@ public class QuestionServiceImpl implements QuestionService {
     public List<BaseQuestionVO> getQuestionsByQuiz(LoginUser user, Long quizId) {
         Quiz quiz = quizDAO.findQuizById(quizId);
         List<String> questionIds = quiz.getQuestions();
-        return bokApi.bokFindByIdIn(questionIds)
+        return questionApi.bokFindByIdIn(questionIds)
                 .stream()
-                .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, quiz.getState() == QuizState.CLOSED || user.getType() == UserType.TEACHER))
+                .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 bokApi.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(bokApi.bokFindById(questionId), true);
+        return BaseQuestionVO.convertBokQuestionToVO(questionApi.bokFindById(questionId), true);
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -87,14 +87,14 @@ public class QuestionServiceImpl implements QuestionService {
         String questionId;
         do {
             // 保证不产生重复QuestionId
-            questionId = UUID.randomUUID().toString().replaceAll("-", "");
+            questionId = IdUtil.fastSimpleUUID();
         } while (questionRecordDAO.existsByQuestionId(questionId));
 
-        BokQuestion bokQuestion = bokApi.createQuestion(questionId, baseQuestionDTO);
+        BokQuestionVO bokQuestionVO = questionApi.createQuestion(questionId, baseQuestionDTO);
         questionRecordDAO.save(new QuestionRecord()
                 .setTeacher(userDAO.findUserById(user.getId()))
-                .setQuestionId(bokQuestion.getId()));
-        return BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true);
+                .setQuestionId(bokQuestionVO.getId()));
+        return BaseQuestionVO.convertBokQuestionToVO(bokQuestionVO, true);
     }
 
     @Transactional(readOnly = true)
@@ -102,9 +102,9 @@ 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, "您无权修改该问题"));
-        BokQuestion bokQuestion = bokApi.modifyQuestion(questionId, baseQuestionDTO);
+        BokQuestionVO bokQuestionVO = questionApi.modifyQuestion(questionId, baseQuestionDTO);
         questionRecordDAO.save(questionRecord);
-        return BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true);
+        return BaseQuestionVO.convertBokQuestionToVO(bokQuestionVO, true);
     }
 
     @Transactional(rollbackFor = Exception.class)
@@ -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, "您无权删除该问题"));
-        bokApi.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<>(
-                bokApi.bokFindByIdIn(questionIdPage.getContent())
+                questionApi.bokFindByIdIn(questionIdPage.getContent())
                         .stream()
                         .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true))
                         .collect(Collectors.toList())
@@ -136,4 +136,9 @@ public class QuestionServiceImpl implements QuestionService {
     public Set<String> getIdsOfCreatedQuestions(LoginUser user) {
         return questionRecordDAO.findQuestionIdsByTeacher(userDAO.findUserById(user.getId()));
     }
+
+    @Override
+    public void starQuestion(LoginUser user, String questionId) {
+        questionApi.starQuestion(String.valueOf(user.getId()), questionId);
+    }
 }

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

@@ -221,6 +221,6 @@ public class QuizServiceImpl implements QuizService {
     @Override
     public boolean existsGetAuth(LoginUser user, Quiz quiz) {
         return quiz.getState() != QuizState.NOT_STARTED
-                || user.getType() == UserType.TEACHER;
+                || user.getRole() == UserRole.TEACHER;
     }
 }

+ 17 - 7
src/main/java/nju/seec/helper/service/impl/SlideServiceImpl.java

@@ -1,7 +1,7 @@
 package nju.seec.helper.service.impl;
 
+import cn.hutool.core.text.StrBuilder;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.io.Files;
 import lombok.SneakyThrows;
 import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dao.ChooseDAO;
@@ -21,16 +21,17 @@ import nju.seec.helper.service.CourseService;
 import nju.seec.helper.service.MessageService;
 import nju.seec.helper.service.QuizService;
 import nju.seec.helper.service.SlideService;
-import nju.seec.helper.util.OssObjectUrlUtils;
-import nju.seec.helper.util.OssUtils;
 import nju.seec.helper.util.file.FileInfo;
 import nju.seec.helper.util.file.FileUtils;
+import nju.seec.helper.util.oss.OssObjectUrlUtils;
+import nju.seec.helper.util.oss.OssUtils;
 import nju.seec.helper.vo.SlideVO;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.File;
@@ -115,7 +116,7 @@ public class SlideServiceImpl implements SlideService {
         try {
             slide.setName(slideDTO.getName());
             String oldObjectName = slide.getObjectName();
-            String newObjectName = getObjectName(slide.getId(), slide.getName(), Files.getFileExtension(oldObjectName));
+            String newObjectName = getObjectName(slide.getId(), slide.getName(), StringUtils.getFilenameExtension(oldObjectName));
             slide.setObjectName(newObjectName);
             slide = slideDAO.save(slide);
 
@@ -192,11 +193,20 @@ public class SlideServiceImpl implements SlideService {
 
     @SneakyThrows
     private FileInfo getFileInfo(MultipartFile file) {
-        return FileUtils.getFileInfo(file);
+        return FileUtils.getFileInfo(file.getInputStream());
     }
 
     private String getObjectName(Long slideId, String slideName, String extension) {
-        return SLIDE_STORE_DIR + File.separator + slideId + File.separator + slideName + "." + extension;
+        return StrBuilder
+                .create()
+                .append(SLIDE_STORE_DIR)
+                .append(File.separator)
+                .append(slideId)
+                .append(File.separator)
+                .append(slideName)
+                .append(".")
+                .append(extension)
+                .toStringAndReset();
     }
 
     @Transactional(readOnly = true)
@@ -216,7 +226,7 @@ public class SlideServiceImpl implements SlideService {
             throw HelperException.of(ExceptionType.FORBIDDEN, "您无权访问该课程的课件");
         }
 
-        switch (user.getType()) {
+        switch (user.getRole()) {
             case TEACHER:
                 return slideDAO.findByCourseAndNameContains(course, key, pageable).map(SlideVO::new);
             case STUDENT:

+ 88 - 81
src/main/java/nju/seec/helper/service/impl/UserServiceImpl.java

@@ -1,112 +1,119 @@
 package nju.seec.helper.service.impl;
 
-import nju.seec.helper.aspect.auth.LoginUser;
 import nju.seec.helper.dao.UserDAO;
-import nju.seec.helper.dto.user.ResetPasswordDTO;
-import nju.seec.helper.dto.user.UserDTO;
 import nju.seec.helper.entity.User;
-import nju.seec.helper.enums.ExceptionType;
-import nju.seec.helper.enums.UserState;
-import nju.seec.helper.enums.UserType;
-import nju.seec.helper.exception.HelperException;
 import nju.seec.helper.service.UserService;
-import nju.seec.helper.util.Consts;
-import nju.seec.helper.util.EncryptUtils;
-import nju.seec.helper.util.RedisCacheUtils;
 import nju.seec.helper.vo.UserVO;
+import org.springframework.data.util.Optionals;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.Optional;
+
 /**
  * @author cst
  */
 @Service
 public class UserServiceImpl implements UserService {
-    private final RedisCacheUtils cacheUtils;
+    //    private final RedisCacheUtils cacheUtils;
     private final UserDAO userDAO;
 
-    public UserServiceImpl(UserDAO userDAO, RedisCacheUtils cacheUtils) {
+    public UserServiceImpl(UserDAO userDAO
+//            , RedisCacheUtils cacheUtils
+    ) {
         this.userDAO = userDAO;
-        this.cacheUtils = cacheUtils;
+//        this.cacheUtils = cacheUtils;
     }
 
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public UserVO createUser(UserDTO userDTO) {
-        checkSameEmail(userDTO.getEmail());
-        checkSamePhone(userDTO.getPhone());
-
-        String email = userDTO.getEmail();
-        if (!userDTO.getEmailCode().equals(cacheUtils.get(Consts.EMAIL_CACHE_NAME, email))) {
-            throw HelperException.of(ExceptionType.NOT_FOUND, "邮箱或验证码错误");
-        }
-
-        String phone = userDTO.getPhone();
-        if (!userDTO.getPhoneCode().equals(cacheUtils.get(Consts.PHONE_CACHE_NAME, phone))) {
-            throw HelperException.of(ExceptionType.NOT_FOUND, "手机号或验证码错误");
-        }
-
-        User user = userDAO.save(new User()
-                .setName(userDTO.getName())
-                .setEmail(userDTO.getEmail())
-                .setPhone(userDTO.getPhone())
-                .setPassword(EncryptUtils.encode(userDTO.getPassword()))
-                .setType(userDTO.getType())
-                .setState(userDTO.getType() == UserType.TEACHER ? UserState.CHECKING : UserState.NORMAL));
-
-        cacheUtils.remove(Consts.EMAIL_CACHE_NAME, email);
-        cacheUtils.remove(Consts.PHONE_CACHE_NAME, phone);
-        return new UserVO(user);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public UserVO modifyUser(LoginUser loginUser, UserDTO userDTO) {
-        User user = userDAO.findUserById(loginUser.getId());
-        user.setName(userDTO.getName());
-        user = userDAO.save(user);
-        return new UserVO(user);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public void resetPasswordByEmail(ResetPasswordDTO resetPasswordDTO) {
-        User user = userDAO.findByEmail(resetPasswordDTO.getUsername())
-                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "该邮箱未在系统中注册"));
-
-        if (!resetPasswordDTO.getCode().equals(cacheUtils.get(Consts.EMAIL_CACHE_NAME, resetPasswordDTO.getUsername()))) {
-            throw HelperException.of(ExceptionType.NOT_FOUND, "邮箱或验证码错误");
-        }
-
-        user.setPassword(EncryptUtils.encode(resetPasswordDTO.getPassword()));
-        userDAO.save(user);
-        cacheUtils.remove(Consts.EMAIL_CACHE_NAME, resetPasswordDTO.getUsername());
-    }
+//    @Transactional(rollbackFor = Exception.class)
+//    @Override
+//    public UserVO createUser(UserDTO userDTO) {
+//        checkSameEmail(userDTO.getEmail());
+//        checkSamePhone(userDTO.getPhone());
+//
+//        String email = userDTO.getEmail();
+//        if (!userDTO.getEmailCode().equals(cacheUtils.get(Consts.EMAIL_CACHE_NAME, email))) {
+//            throw HelperException.of(ExceptionType.NOT_FOUND, "邮箱或验证码错误");
+//        }
+//
+//        String phone = userDTO.getPhone();
+//        if (!userDTO.getPhoneCode().equals(cacheUtils.get(Consts.PHONE_CACHE_NAME, phone))) {
+//            throw HelperException.of(ExceptionType.NOT_FOUND, "手机号或验证码错误");
+//        }
+//
+//        User user = userDAO.save(new User()
+//                .setName(userDTO.getName())
+//                .setEmail(userDTO.getEmail())
+//                .setPhone(userDTO.getPhone())
+//                .setPassword(SecureUtil.sha256(userDTO.getPassword()))
+//                .setType(userDTO.getType())
+//                .setState(userDTO.getType() == UserRole.TEACHER ? UserState.CHECKING : UserState.NORMAL));
+//
+//        cacheUtils.remove(Consts.EMAIL_CACHE_NAME, email);
+//        cacheUtils.remove(Consts.PHONE_CACHE_NAME, phone);
+//        return new UserVO(user);
+//    }
+//
+//    @Transactional(rollbackFor = Exception.class)
+//    @Override
+//    public UserVO modifyUser(LoginUser loginUser, UserDTO userDTO) {
+//        User user = userDAO.findUserById(loginUser.getId());
+//        user.setName(userDTO.getName());
+//        user = userDAO.save(user);
+//        return new UserVO(user);
+//    }
+//
+//    @Transactional(rollbackFor = Exception.class)
+//    @Override
+//    public void resetPasswordByEmail(ResetPasswordDTO resetPasswordDTO) {
+//        User user = userDAO.findByEmail(resetPasswordDTO.getUsername())
+//                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "该邮箱未在系统中注册"));
+//
+//        if (!resetPasswordDTO.getCode().equals(cacheUtils.get(Consts.EMAIL_CACHE_NAME, resetPasswordDTO.getUsername()))) {
+//            throw HelperException.of(ExceptionType.NOT_FOUND, "邮箱或验证码错误");
+//        }
+//
+//        user.setPassword(SecureUtil.sha256(resetPasswordDTO.getPassword()));
+//        userDAO.save(user);
+//        cacheUtils.remove(Consts.EMAIL_CACHE_NAME, resetPasswordDTO.getUsername());
+//    }
+//
+//    @Transactional(readOnly = true)
+//    @Override
+//    public UserVO getUserByEmailAndPassword(String email, String password) {
+//        return userDAO
+//                .findByEmailAndPassword(email, SecureUtil.sha256(password))
+//                .map(UserVO::new)
+//                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "邮箱或密码错误"));
+//    }
 
     @Transactional(readOnly = true)
     @Override
-    public UserVO getUserByEmailAndPassword(String email, String password) {
-        return userDAO
-                .findByEmailAndPassword(email, EncryptUtils.encode(password))
-                .map(UserVO::new)
-                .orElseThrow(() -> HelperException.of(ExceptionType.NOT_FOUND, "邮箱或密码错误"));
+    public UserVO getUserById(Long userId) {
+        return new UserVO(userDAO.findUserById(userId));
     }
 
     @Transactional(readOnly = true)
     @Override
-    public UserVO getUserById(Long userId) {
-        return new UserVO(userDAO.findUserById(userId));
+    public Optional<User> getUserByPhoneOrPid(String phone, Long pid) {
+        return Optionals.firstNonEmpty(() -> userDAO.findByPhone(phone), () -> userDAO.findByPid(pid));
     }
 
-    private void checkSameEmail(String email) {
-        if (userDAO.existsByEmail(email)) {
-            throw HelperException.of(ExceptionType.CONFLICT, "该邮箱已注册");
-        }
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public User createOrUpdateUser(User user) {
+        return userDAO.save(user);
     }
 
-    private void checkSamePhone(String phone) {
-        if (userDAO.existsByPhone(phone)) {
-            throw HelperException.of(ExceptionType.CONFLICT, "该手机号已注册");
-        }
-    }
+    //    private void checkSameEmail(String email) {
+//        if (userDAO.existsByEmail(email)) {
+//            throw HelperException.of(ExceptionType.CONFLICT, "该邮箱已注册");
+//        }
+//    }
+//
+//    private void checkSamePhone(String phone) {
+//        if (userDAO.existsByPhone(phone)) {
+//            throw HelperException.of(ExceptionType.CONFLICT, "该手机号已注册");
+//        }
+//    }
 }

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

@@ -6,7 +6,7 @@ package nju.seec.helper.util;
 public class Consts {
     public static final String SYS_NAME = "helper";
 
-    public static final String SESSION_USER_NAME = SYS_NAME + "_user";
-    public static final String EMAIL_CACHE_NAME = SYS_NAME + "_email";
-    public static final String PHONE_CACHE_NAME = SYS_NAME + "_phone";
+//    public static final String SESSION_USER_NAME = SYS_NAME + "_user";
+//    public static final String EMAIL_CACHE_NAME = SYS_NAME + "_email";
+//    public static final String PHONE_CACHE_NAME = SYS_NAME + "_phone";
 }

+ 0 - 25
src/main/java/nju/seec/helper/util/EncryptUtils.java

@@ -1,25 +0,0 @@
-package nju.seec.helper.util;
-
-import lombok.SneakyThrows;
-import lombok.experimental.UtilityClass;
-import org.apache.commons.codec.binary.Hex;
-
-import java.security.MessageDigest;
-
-/**
- * @author cst
- */
-@UtilityClass
-public class EncryptUtils {
-    private static final String ALGORITHM = "SHA-256";
-
-    /**
-     * 加密
-     */
-    @SneakyThrows
-    public String encode(String string) {
-        MessageDigest messageDigest = MessageDigest.getInstance(ALGORITHM);
-        byte[] hash = messageDigest.digest(string.getBytes());
-        return Hex.encodeHexString(hash);
-    }
-}

+ 7 - 2
src/main/java/nju/seec/helper/util/JsonUtils.java

@@ -2,18 +2,23 @@ package nju.seec.helper.util;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializer;
 import com.google.gson.reflect.TypeToken;
 import lombok.experimental.UtilityClass;
-import nju.seec.helper.util.serializer.LocalDateTimeSerializer;
 
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 
 /**
  * @author cst
  */
 @UtilityClass
 public class JsonUtils {
-    private static final Gson GSON = new GsonBuilder().setPrettyPrinting().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer()).create();
+    private static final Gson GSON = new GsonBuilder()
+            .setPrettyPrinting()
+            .registerTypeAdapter(LocalDateTime.class, (JsonSerializer<LocalDateTime>) (localDateTime, type, jsonSerializationContext) -> new JsonPrimitive(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)))
+            .create();
 
     public String toJson(Object o) {
         return GSON.toJson(o);

+ 32 - 32
src/main/java/nju/seec/helper/util/MailUtils.java

@@ -1,32 +1,32 @@
-package nju.seec.helper.util;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.mail.SimpleMailMessage;
-import org.springframework.mail.javamail.JavaMailSender;
-import org.springframework.stereotype.Component;
-
-/**
- * @author cst
- */
-@Data
-@Component
-@ConfigurationProperties("helper.mail")
-public class MailUtils {
-    private String from;
-    private String subject;
-    private final JavaMailSender mailSender;
-
-    public MailUtils(JavaMailSender mailSender) {
-        this.mailSender = mailSender;
-    }
-
-    public void sendSimpleMailMessage(String text, String... to) {
-        SimpleMailMessage message = new SimpleMailMessage();
-        message.setFrom(from);
-        message.setTo(to);
-        message.setSubject(subject);
-        message.setText(text);
-        mailSender.send(message);
-    }
-}
+//package nju.seec.helper.util;
+//
+//import lombok.Data;
+//import org.springframework.boot.context.properties.ConfigurationProperties;
+//import org.springframework.mail.SimpleMailMessage;
+//import org.springframework.mail.javamail.JavaMailSender;
+//import org.springframework.stereotype.Component;
+//
+///**
+// * @author cst
+// */
+//@Data
+//@Component
+//@ConfigurationProperties("helper.mail")
+//public class MailUtils {
+//    private String from;
+//    private String subject;
+//    private final JavaMailSender mailSender;
+//
+//    public MailUtils(JavaMailSender mailSender) {
+//        this.mailSender = mailSender;
+//    }
+//
+//    public void sendSimpleMailMessage(String text, String... to) {
+//        SimpleMailMessage message = new SimpleMailMessage();
+//        message.setFrom(from);
+//        message.setTo(to);
+//        message.setSubject(subject);
+//        message.setText(text);
+//        mailSender.send(message);
+//    }
+//}

+ 65 - 65
src/main/java/nju/seec/helper/util/SmsUtils.java

@@ -1,65 +1,65 @@
-package nju.seec.helper.util;
-
-import com.aliyuncs.CommonRequest;
-import com.aliyuncs.CommonResponse;
-import com.aliyuncs.DefaultAcsClient;
-import com.aliyuncs.IAcsClient;
-import com.aliyuncs.exceptions.ClientException;
-import com.aliyuncs.http.MethodType;
-import com.aliyuncs.profile.DefaultProfile;
-import lombok.Data;
-import lombok.extern.slf4j.Slf4j;
-import nju.seec.helper.enums.ExceptionType;
-import nju.seec.helper.exception.HelperException;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-
-/**
- * @author cst
- */
-@Component
-@Slf4j
-@Data
-@ConfigurationProperties("aliyun.sms")
-public class SmsUtils {
-    private static final String OK = "OK";
-
-    private String accessKeyId;
-    private String accessSecret;
-    private String signName;
-    private String templateCode;
-
-    public void sendSmsCode(String to, String code) {
-        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessSecret);
-        IAcsClient client = new DefaultAcsClient(profile);
-
-        CommonRequest request = new CommonRequest();
-        request.setMethod(MethodType.POST);
-        request.setDomain("dysmsapi.aliyuncs.com");
-        request.setVersion("2017-05-25");
-        request.setAction("SendSms");
-        request.putQueryParameter("RegionId", "cn-hangzhou");
-        request.putQueryParameter("PhoneNumbers", to);
-        request.putQueryParameter("SignName", signName);
-        request.putQueryParameter("TemplateCode", templateCode);
-        request.putQueryParameter("TemplateParam", "{\"code\":" + code + "}");
-        try {
-            CommonResponse response = client.getCommonResponse(request);
-            SendSmsResult sendSmsResult = JsonUtils.fromJson(response.getData(), SendSmsResult.class);
-            if (!OK.equals(sendSmsResult.Code)) {
-                log.error(sendSmsResult.Message);
-                throw HelperException.of(ExceptionType.ERROR, "短信验证码发送失败");
-            }
-        } catch (ClientException e) {
-            log.error(e.getErrCode());
-        }
-    }
-
-    @Data
-    public static class SendSmsResult {
-        private String Message;
-        private String RequestId;
-        private String BizId;
-        private String Code;
-    }
-}
+//package nju.seec.helper.util;
+//
+//import com.aliyuncs.CommonRequest;
+//import com.aliyuncs.CommonResponse;
+//import com.aliyuncs.DefaultAcsClient;
+//import com.aliyuncs.IAcsClient;
+//import com.aliyuncs.exceptions.ClientException;
+//import com.aliyuncs.http.MethodType;
+//import com.aliyuncs.profile.DefaultProfile;
+//import lombok.Data;
+//import lombok.extern.slf4j.Slf4j;
+//import nju.seec.helper.enums.ExceptionType;
+//import nju.seec.helper.exception.HelperException;
+//import org.springframework.boot.context.properties.ConfigurationProperties;
+//import org.springframework.stereotype.Component;
+//
+///**
+// * @author cst
+// */
+//@Component
+//@Slf4j
+//@Data
+//@ConfigurationProperties("aliyun.sms")
+//public class SmsUtils {
+//    private static final String OK = "OK";
+//
+//    private String accessKeyId;
+//    private String accessSecret;
+//    private String signName;
+//    private String templateCode;
+//
+//    public void sendSmsCode(String to, String code) {
+//        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessSecret);
+//        IAcsClient client = new DefaultAcsClient(profile);
+//
+//        CommonRequest request = new CommonRequest();
+//        request.setMethod(MethodType.POST);
+//        request.setDomain("dysmsapi.aliyuncs.com");
+//        request.setVersion("2017-05-25");
+//        request.setAction("SendSms");
+//        request.putQueryParameter("RegionId", "cn-hangzhou");
+//        request.putQueryParameter("PhoneNumbers", to);
+//        request.putQueryParameter("SignName", signName);
+//        request.putQueryParameter("TemplateCode", templateCode);
+//        request.putQueryParameter("TemplateParam", "{\"code\":" + code + "}");
+//        try {
+//            CommonResponse response = client.getCommonResponse(request);
+//            SendSmsResult sendSmsResult = JsonUtils.fromJson(response.getData(), SendSmsResult.class);
+//            if (!OK.equals(sendSmsResult.Code)) {
+//                log.error(sendSmsResult.Message);
+//                throw HelperException.of(ExceptionType.ERROR, "短信验证码发送失败");
+//            }
+//        } catch (ClientException e) {
+//            log.error(e.getErrCode());
+//        }
+//    }
+//
+//    @Data
+//    private static class SendSmsResult {
+//        private String Message;
+//        private String RequestId;
+//        private String BizId;
+//        private String Code;
+//    }
+//}

+ 11 - 10
src/main/java/nju/seec/helper/util/GuavaCacheUtil.java → src/main/java/nju/seec/helper/util/cache/GuavaCacheUtils.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.util;
+package nju.seec.helper.util.cache;
 
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
@@ -17,17 +17,18 @@ import java.util.stream.Collectors;
  * updated by cst
  */
 @Component
-public class GuavaCacheUtil {
+public class GuavaCacheUtils {
     private Cache<Object, Object> cache;
 
-    public GuavaCacheUtil(@Value("${guavaCache.maximumSize}") Long maximumSize,
-                          @Value("${guavaCache.expireAfterAccessInSeconds}") Long expireAfterAccessInSeconds,
-                          @Value("${guavaCache.expireAfterWriteInSeconds}") Long expireAfterWriteInSeconds) {
-        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
-        builder.maximumSize(maximumSize);
-        builder.expireAfterAccess(expireAfterAccessInSeconds, TimeUnit.SECONDS);
-        builder.expireAfterWrite(expireAfterWriteInSeconds, TimeUnit.SECONDS);
-        cache = builder.build();
+    public GuavaCacheUtils(@Value("${guavaCache.maximumSize}") Long maximumSize,
+                           @Value("${guavaCache.expireAfterAccessInSeconds}") Long expireAfterAccessInSeconds,
+                           @Value("${guavaCache.expireAfterWriteInSeconds}") Long expireAfterWriteInSeconds) {
+        cache = CacheBuilder
+                .newBuilder()
+                .maximumSize(maximumSize)
+                .expireAfterAccess(expireAfterAccessInSeconds, TimeUnit.SECONDS)
+                .expireAfterWrite(expireAfterWriteInSeconds, TimeUnit.SECONDS)
+                .build();
     }
 
     public void set(String cacheName, String key, Object value) {

+ 1 - 1
src/main/java/nju/seec/helper/util/RedisCacheUtils.java → src/main/java/nju/seec/helper/util/cache/RedisCacheUtils.java

@@ -1,4 +1,4 @@
-package nju.seec.helper.util;
+package nju.seec.helper.util.cache;
 
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;

+ 32 - 34
src/main/java/nju/seec/helper/util/file/FileUtils.java

@@ -1,5 +1,6 @@
 package nju.seec.helper.util.file;
 
+import cn.hutool.core.io.FileTypeUtil;
 import com.google.common.collect.ImmutableMap;
 import lombok.SneakyThrows;
 import lombok.experimental.UtilityClass;
@@ -7,11 +8,10 @@ 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.hslf.usermodel.HSLFSlideShow;
-import org.apache.poi.sl.extractor.SlideShowExtractor;
-import org.apache.poi.xslf.usermodel.XMLSlideShow;
-import org.springframework.web.multipart.MultipartFile;
+import org.apache.tomcat.util.http.fileupload.IOUtils;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Map;
@@ -23,52 +23,50 @@ import java.util.Optional;
 @UtilityClass
 public class FileUtils {
     private static final Map<String, FileInfoParser> FILE_INFO_PARSER_MAP = ImmutableMap.of(
-            "application/pdf", file -> FileInfo.of(PDDocument.load(file.getInputStream()).getNumberOfPages(), "pdf")
-            , "application/vnd.ms-powerpoint", file -> FileInfo.of(new HSLFSlideShow(file.getInputStream()).getSlides().size(), "ppt")
-            , "application/vnd.openxmlformats-officedocument.presentationml.presentation", file -> FileInfo.of(new XMLSlideShow(file.getInputStream()).getSlides().size(), "pptx")
+            "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))
-            , "ppt", inputStream -> new SlideShowExtractor<>(new HSLFSlideShow(inputStream)).getText()
-            , "pptx", inputStream -> new SlideShowExtractor<>(new XMLSlideShow(inputStream)).getText()
-    );
+//    private static final Map<String, FileContentParser> FILE_CONTENT_PARSER_MAP = ImmutableMap.of(
+//            "pdf", inputStream -> new PDFTextStripper().getText(PDDocument.load(inputStream))
+//    );
 
     @SneakyThrows
-    public FileInfo getFileInfo(MultipartFile file) {
-        return Optional.ofNullable(FILE_INFO_PARSER_MAP.get(file.getContentType()))
+    public FileInfo getFileInfo(InputStream inputStream) {
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        IOUtils.copy(inputStream, byteArrayOutputStream);
+        return Optional.ofNullable(FILE_INFO_PARSER_MAP.get(FileTypeUtil.getType(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()))))
                 .orElseThrow(() -> HelperException.of(ExceptionType.FORBIDDEN, "不支持的文件类型"))
-                .parse(file);
+                .parse(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
     }
 
-    @SneakyThrows
-    public String getFileContent(String extension, InputStream inputStream) {
-        return Optional.ofNullable(FILE_CONTENT_PARSER_MAP.get(extension))
-                .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 {
         /**
          * 解析文件信息
          *
-         * @param file
-         * @return
-         * @throws IOException
-         */
-        FileInfo parse(MultipartFile file) throws IOException;
-    }
-
-    @FunctionalInterface
-    private interface FileContentParser {
-        /**
-         * 获取文件内容
-         *
          * @param inputStream
          * @return
          * @throws IOException
          */
-        String parse(InputStream inputStream) throws IOException;
+        FileInfo parse(InputStream inputStream) throws IOException;
     }
+
+//    @FunctionalInterface
+//    private interface FileContentParser {
+//        /**
+//         * 获取文件内容
+//         *
+//         * @param inputStream
+//         * @return
+//         * @throws IOException
+//         */
+//        String parse(InputStream inputStream) throws IOException;
+//    }
 }

+ 2 - 3
src/main/java/nju/seec/helper/util/OssObjectUrlUtils.java → src/main/java/nju/seec/helper/util/oss/OssObjectUrlUtils.java

@@ -1,8 +1,7 @@
-package nju.seec.helper.util;
+package nju.seec.helper.util.oss;
 
 import nju.seec.helper.util.Consts;
-import nju.seec.helper.util.OssUtils;
-import nju.seec.helper.util.RedisCacheUtils;
+import nju.seec.helper.util.cache.RedisCacheUtils;
 import org.springframework.stereotype.Component;
 
 import java.util.concurrent.TimeUnit;

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

@@ -1,4 +1,4 @@
-package nju.seec.helper.util;
+package nju.seec.helper.util.oss;
 
 import com.aliyun.oss.ClientException;
 import com.aliyun.oss.OSS;

+ 0 - 21
src/main/java/nju/seec/helper/util/serializer/LocalDateTimeSerializer.java

@@ -1,21 +0,0 @@
-package nju.seec.helper.util.serializer;
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-import java.lang.reflect.Type;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-
-/**
- * @author cst
- */
-public class LocalDateTimeSerializer implements JsonSerializer<LocalDateTime> {
-
-    @Override
-    public JsonElement serialize(LocalDateTime localDateTime, Type type, JsonSerializationContext jsonSerializationContext) {
-        return new JsonPrimitive(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
-    }
-}

+ 19 - 19
src/main/java/nju/seec/helper/vo/AdminVO.java

@@ -1,19 +1,19 @@
-package nju.seec.helper.vo;
-
-import lombok.Data;
-import lombok.NonNull;
-import nju.seec.helper.entity.Admin;
-
-/**
- * @author cst
- */
-@Data
-public class AdminVO {
-    private Long id;
-    private String username;
-
-    public AdminVO(@NonNull Admin admin) {
-        this.id = admin.getId();
-        this.username = admin.getUsername();
-    }
-}
+//package nju.seec.helper.vo;
+//
+//import lombok.Data;
+//import lombok.NonNull;
+//import nju.seec.helper.entity.Admin;
+//
+///**
+// * @author cst
+// */
+//@Data
+//public class AdminVO {
+//    private Long id;
+//    private String username;
+//
+//    public AdminVO(@NonNull Admin admin) {
+//        this.id = admin.getId();
+//        this.username = admin.getUsername();
+//    }
+//}

+ 3 - 3
src/main/java/nju/seec/helper/vo/CommentVO.java

@@ -3,7 +3,7 @@ package nju.seec.helper.vo;
 import lombok.Data;
 import lombok.NonNull;
 import nju.seec.helper.entity.Comment;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 
 import java.time.LocalDateTime;
 
@@ -29,7 +29,7 @@ public class CommentVO {
     private String content;
     private Long userId;
     private String userName;
-    private UserType userType;
+    private UserRole userRole;
     private LocalDateTime createAt;
 
     private ReplyVO reply;
@@ -45,7 +45,7 @@ public class CommentVO {
         this.content = comment.getContent();
         this.userId = comment.getUser().getId();
         this.userName = comment.getUser().getName();
-        this.userType = comment.getUser().getType();
+        this.userRole = comment.getUser().getRole();
         this.createAt = comment.getCreateAt();
         this.reply = comment.getReply() == null ? null : new ReplyVO(comment.getReply());
         this.show = comment.getShow();

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

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

+ 5 - 6
src/main/java/nju/seec/helper/vo/UserVO.java

@@ -3,8 +3,7 @@ package nju.seec.helper.vo;
 import lombok.Data;
 import lombok.NonNull;
 import nju.seec.helper.entity.User;
-import nju.seec.helper.enums.UserState;
-import nju.seec.helper.enums.UserType;
+import nju.seec.helper.enums.UserRole;
 
 import java.time.LocalDateTime;
 
@@ -17,19 +16,19 @@ public class UserVO {
     private String name;
     private String email;
     private String phone;
-    private UserType type;
+    private UserRole role;
     private LocalDateTime createAt;
 
-    private UserState state;
+//    private UserState state;
 
     public UserVO(@NonNull User user) {
         this.id = user.getId();
         this.name = user.getName();
         this.email = user.getEmail();
         this.phone = user.getPhone();
-        this.type = user.getType();
+        this.role = user.getRole();
         this.createAt = user.getCreateAt();
 
-        this.state = user.getState();
+//        this.state = user.getState();
     }
 }

+ 16 - 0
src/main/java/nju/seec/helper/vo/knowledge/BokKnowledgeNodeVO.java

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

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

@@ -5,7 +5,7 @@ import lombok.NonNull;
 import nju.seec.helper.enums.QuestionType;
 
 import java.util.Date;
-import java.util.Set;
+import java.util.List;
 
 /**
  * @author XuShengTao
@@ -19,28 +19,28 @@ public abstract class BaseQuestionVO {
     protected String stem;
     protected String analysis;
     protected String keyPoints;
-    protected Set<String> tags;
-    protected Set<String> knowledgeId;
+    protected List<String> tags;
+    protected List<String> knowledgeId;
     private Date lastModified;
 
-    public static BaseQuestionVO convertBokQuestionToVO(@NonNull BokQuestion bokQuestion, boolean withAnswer) {
-        switch (bokQuestion.getType()) {
+    public static BaseQuestionVO convertBokQuestionToVO(@NonNull BokQuestionVO bokQuestionVO, boolean withAnswer) {
+        switch (bokQuestionVO.getType()) {
             case "choice":
-                return new ChoiceQuestionVO(bokQuestion, withAnswer);
+                return new ChoiceQuestionVO(bokQuestionVO, withAnswer);
             case "true_false":
-                return new TrueOrFalseQuestionVO(bokQuestion, withAnswer);
+                return new TrueOrFalseQuestionVO(bokQuestionVO, withAnswer);
             default:
                 return null;
         }
     }
 
-    protected BaseQuestionVO(BokQuestion bokQuestion) {
-        this.id = bokQuestion.getId();
-        this.stem = bokQuestion.getStem();
-        this.keyPoints = bokQuestion.getKeyPoints();
-        this.tags = bokQuestion.getTags();
-        this.knowledgeId = bokQuestion.getKnowledgeId();
-        this.lastModified = bokQuestion.getLastModified();
+    protected BaseQuestionVO(BokQuestionVO bokQuestionVO) {
+        this.id = bokQuestionVO.getId();
+        this.stem = bokQuestionVO.getStem();
+        this.keyPoints = bokQuestionVO.getKeyPoints();
+        this.tags = bokQuestionVO.getTags();
+        this.knowledgeId = bokQuestionVO.getKnowledgeId();
+        this.lastModified = bokQuestionVO.getLastModified();
     }
 
     /**

+ 4 - 4
src/main/java/nju/seec/helper/vo/question/BokQuestion.java → src/main/java/nju/seec/helper/vo/question/BokQuestionVO.java

@@ -5,8 +5,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
 
 import java.util.Date;
+import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * @author sheen
@@ -14,7 +14,7 @@ import java.util.Set;
  * updated by cst
  */
 @Data
-public class BokQuestion {
+public class BokQuestionVO {
     private String id;
     private String type;
     private String stem;
@@ -23,8 +23,8 @@ public class BokQuestion {
     @JsonProperty(value = "key_points")
     private String keyPoints;
     private String analysis;
-    private Set<String> tags;
-    private Set<String> knowledgeId;
+    private List<String> tags;
+    private List<String> knowledgeId;
     @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
     private Date lastModified;
 }

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

@@ -18,14 +18,14 @@ public class ChoiceQuestionVO extends BaseQuestionVO {
     private Map<String, String> options;
     private String answer;
 
-    public ChoiceQuestionVO(@NonNull BokQuestion bokQuestion, boolean withAnswer) {
-        super(bokQuestion);
+    public ChoiceQuestionVO(@NonNull BokQuestionVO bokQuestionVO, boolean withAnswer) {
+        super(bokQuestionVO);
         this.type = QuestionType.CHOICE;
-        this.options = bokQuestion.getOptions();
+        this.options = bokQuestionVO.getOptions();
 
         if (withAnswer) {
-            this.answer = bokQuestion.getAnswer();
-            this.analysis = bokQuestion.getAnalysis();
+            this.answer = bokQuestionVO.getAnswer();
+            this.analysis = bokQuestionVO.getAnalysis();
         }
     }
 

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

@@ -1,5 +1,6 @@
 package nju.seec.helper.vo.question;
 
+import cn.hutool.core.convert.Convert;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NonNull;
@@ -18,16 +19,16 @@ public class TrueOrFalseQuestionVO extends BaseQuestionVO {
 
     @Override
     public boolean pass(Object answer) {
-        return this.answer.equals(Boolean.valueOf(String.valueOf(answer)));
+        return this.answer.equals(Convert.toBool(answer));
     }
 
-    public TrueOrFalseQuestionVO(@NonNull BokQuestion bokQuestion, boolean withAnswer) {
-        super(bokQuestion);
+    public TrueOrFalseQuestionVO(@NonNull BokQuestionVO bokQuestionVO, boolean withAnswer) {
+        super(bokQuestionVO);
         this.type = QuestionType.TRUE_FALSE;
 
         if (withAnswer) {
-            this.answer = Boolean.valueOf(bokQuestion.getAnswer());
-            this.analysis = bokQuestion.getAnalysis();
+            this.answer = Boolean.valueOf(bokQuestionVO.getAnswer());
+            this.analysis = bokQuestionVO.getAnalysis();
         }
     }
 }

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

@@ -21,6 +21,10 @@ spring:
     redis:
       repositories:
         enabled: false
+    web:
+      pageable:
+        max-page-size: 1000
+        one-indexed-parameters: true
   mail:
     host: smtp.exmail.qq.com
     username: noreply@seecoder.cn
@@ -45,24 +49,32 @@ 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
   tqSearchUrl: ${bok.tqUrl}/search
   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:
+#  url: http://dataanalysis.seecoder.cn
+#  eventUrl: ${data-analysis.url}/api/event
+#  eventReportUrl: ${data-analysis.eventUrl}/report
 guavaCache:
   maximumSize: 1000
   expireAfterAccessInSeconds: 0
   expireAfterWriteInSeconds: 600
+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

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

@@ -21,6 +21,10 @@ spring:
     redis:
       repositories:
         enabled: false
+    web:
+      pageable:
+        max-page-size: 1000
+        one-indexed-parameters: true
   mail:
     host: smtp.exmail.qq.com
     username: noreply@seecoder.cn
@@ -45,24 +49,32 @@ 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
   tqSearchUrl: ${bok.tqUrl}/search
   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:
+#  url: http://dataanalysis.seecoder.cn
+#  eventUrl: ${data-analysis.url}/api/event
+#  eventReportUrl: ${data-analysis.eventUrl}/report
 guavaCache:
   maximumSize: 1000
   expireAfterAccessInSeconds: 0
   expireAfterWriteInSeconds: 600
+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

+ 3 - 11
src/main/resources/update.sql

@@ -1,13 +1,5 @@
-update user
-set password = sha2(from_base64(password), 256);
+drop table admin;
 
-update course
-set code = from_base64(code);
+alter table user change type role varchar(255) not null;
 
-update admin
-set password = sha2(from_base64(password), 256);
-
-alter table comment
-    add index comment_slide_page_user (slide_id, page_number, user_id);
-
-drop index slide_page_top on comment;
+alter table user drop column state;