Quellcode durchsuchen

feat: 增加问题相关的数据结构,增加课程接口,增加文档注解,增加Json工具类和假数据工具类

ChenSiTong vor 6 Jahren
Ursprung
Commit
f002ea37da
42 geänderte Dateien mit 632 neuen und 136 gelöschten Zeilen
  1. 6 2
      .gitignore
  2. 23 0
      package.json
  3. 30 2
      pom.xml
  4. 11 0
      src/main/java/nju/seec/helper/ServletInitializer.java
  5. 8 22
      src/main/java/nju/seec/helper/aop/log/LogAspect.java
  6. 39 0
      src/main/java/nju/seec/helper/config/Swagger2Config.java
  7. 19 0
      src/main/java/nju/seec/helper/data/converter/MapToJsonConverter.java
  8. 19 0
      src/main/java/nju/seec/helper/data/converter/SetToJsonConverter.java
  9. 11 1
      src/main/java/nju/seec/helper/data/dao/SlideDAO.java
  10. 9 4
      src/main/java/nju/seec/helper/data/entity/Comment.java
  11. 3 2
      src/main/java/nju/seec/helper/data/entity/Item.java
  12. 30 4
      src/main/java/nju/seec/helper/data/entity/Quiz.java
  13. 4 8
      src/main/java/nju/seec/helper/data/entity/Slide.java
  14. 33 0
      src/main/java/nju/seec/helper/data/entity/SlideQuiz.java
  15. 29 0
      src/main/java/nju/seec/helper/data/entity/StudentAnswer.java
  16. 4 16
      src/main/java/nju/seec/helper/logic/convert/DTOConvertFactory.java
  17. 15 24
      src/main/java/nju/seec/helper/logic/convert/EntityConvertFactory.java
  18. 29 0
      src/main/java/nju/seec/helper/logic/service/CourseService.java
  19. 45 0
      src/main/java/nju/seec/helper/logic/service/QuizService.java
  20. 21 5
      src/main/java/nju/seec/helper/logic/service/SlideService.java
  21. 35 0
      src/main/java/nju/seec/helper/logic/service/impl/CourseServiceImpl.java
  22. 19 5
      src/main/java/nju/seec/helper/logic/service/impl/SlideServiceImpl.java
  23. 12 0
      src/main/java/nju/seec/helper/logic/vo/AnswerVO.java
  24. 1 2
      src/main/java/nju/seec/helper/logic/vo/ItemVO.java
  25. 16 6
      src/main/java/nju/seec/helper/logic/vo/QuizVO.java
  26. 4 0
      src/main/java/nju/seec/helper/logic/vo/SlideQuizVO.java
  27. 0 4
      src/main/java/nju/seec/helper/logic/vo/SlideVO.java
  28. 18 0
      src/main/java/nju/seec/helper/util/FakerUtils.java
  29. 20 0
      src/main/java/nju/seec/helper/util/JsonUtils.java
  30. 4 0
      src/main/java/nju/seec/helper/util/enums/ItemTransition.java
  31. 1 1
      src/main/java/nju/seec/helper/util/enums/QuizType.java
  32. 8 0
      src/main/java/nju/seec/helper/util/enums/SlideQuizType.java
  33. 0 1
      src/main/java/nju/seec/helper/util/enums/SlideState.java
  34. 8 0
      src/main/java/nju/seec/helper/util/enums/UserRole.java
  35. 1 1
      src/main/java/nju/seec/helper/util/exception/HelperException.java
  36. 43 0
      src/main/java/nju/seec/helper/web/controller/CourseController.java
  37. 30 13
      src/main/java/nju/seec/helper/web/controller/SlideController.java
  38. 1 1
      src/main/java/nju/seec/helper/web/dto/CommentDTO.java
  39. 3 6
      src/main/java/nju/seec/helper/web/dto/ItemDTO.java
  40. 3 6
      src/main/java/nju/seec/helper/web/dto/SlideDTO.java
  41. 14 0
      src/main/java/nju/seec/helper/web/dto/SlideQuizDTO.java
  42. 3 0
      src/main/resources/application-dev.yml

+ 6 - 2
.gitignore

@@ -29,5 +29,9 @@ HELP.md
 .vscode/
 
 
-### MACOS
-.DS_Store
+### MacOS
+.DS_Store
+
+### NPM
+node_modules/
+package-lock.json

+ 23 - 0
package.json

@@ -0,0 +1,23 @@
+{
+  "name": "backend",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "repository": {
+    "type": "git",
+    "url": "http://47.100.18.120:3000/SEEC-HELPER/backend.git"
+  },
+  "author": "cst",
+  "license": "ISC",
+  "devDependencies": {
+    "cz-conventional-changelog": "^3.0.2"
+  },
+  "config": {
+    "commitizen": {
+      "path": "./node_modules/cz-conventional-changelog"
+    }
+  }
+}

+ 30 - 2
pom.xml

@@ -13,9 +13,9 @@
     <version>0.0.1-SNAPSHOT</version>
     <name>helper</name>
     <description>Demo project for Spring Boot</description>
+    <packaging>war</packaging>
 
     <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <java.version>1.8</java.version>
     </properties>
 
@@ -28,9 +28,14 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-actuator</artifactId>
         </dependency>
+        <!--        <dependency>-->
+        <!--            <groupId>org.springframework.boot</groupId>-->
+        <!--            <artifactId>spring-boot-starter-data-jdbc</artifactId>-->
+        <!--        </dependency>-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-data-jdbc</artifactId>
+            <artifactId>spring-boot-starter-tomcat</artifactId>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -45,6 +50,7 @@
             <artifactId>spring-boot-starter-cache</artifactId>
         </dependency>
 
+        <!-- 开发阶段使用内存数据库 -->
         <dependency>
             <groupId>com.h2database</groupId>
             <artifactId>h2</artifactId>
@@ -89,9 +95,31 @@
             <artifactId>guava</artifactId>
             <version>28.1-jre</version>
         </dependency>
+
+        <!-- 开发阶段用于接口展示和调用 -->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <version>2.8.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>2.8.0</version>
+        </dependency>
+
+        <!-- 开发阶段用于假数据生成 -->
+        <!-- https://mvnrepository.com/artifact/com.github.javafaker/javafaker -->
+        <dependency>
+            <groupId>com.github.javafaker</groupId>
+            <artifactId>javafaker</artifactId>
+            <version>1.0.1</version>
+        </dependency>
     </dependencies>
 
     <build>
+        <finalName>seec_helper</finalName>
         <plugins>
             <plugin>
                 <groupId>org.springframework.boot</groupId>

+ 11 - 0
src/main/java/nju/seec/helper/ServletInitializer.java

@@ -0,0 +1,11 @@
+package nju.seec.helper;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+public class ServletInitializer extends SpringBootServletInitializer {
+    @Override
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
+        return builder.sources(HelperApplication.class);
+    }
+}

+ 8 - 22
src/main/java/nju/seec/helper/aop/log/LogAspect.java

@@ -1,13 +1,11 @@
 package nju.seec.helper.aop.log;
 
-import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.AfterThrowing;
-import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
-import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.annotation.Before;
+import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
@@ -17,18 +15,13 @@ import javax.servlet.http.HttpServletRequest;
 /**
  * @author cst
  */
+@Aspect
+@Order(1)
 @Component
 @Slf4j
-@Aspect
 public class LogAspect {
-    @Pointcut("execution(public * nju.seec.helper.web.controller..*.*(..))")
-    public void controllerPointcut() {
-        // Do nothing because it's just a controller pointcut
-    }
-
-    @SneakyThrows
-    @Around("controllerPointcut()")
-    public Object aroundController(ProceedingJoinPoint joinPoint) {
+    @Before("execution(public * nju.seec.helper.web.controller..*.*(..))")
+    public void logControllerRequest(JoinPoint joinPoint) {
         ServletRequestAttributes requestAttributes = (ServletRequestAttributes)
                 RequestContextHolder.getRequestAttributes();
 
@@ -38,17 +31,10 @@ public class LogAspect {
                     joinPoint.getTarget().getClass().getName() + " 类 " +
                     joinPoint.getSignature().getName() + " 方法");
         }
-
-        return joinPoint.proceed();
-    }
-
-    @Pointcut("execution(public * nju.seec.helper.logic.service.impl..*.*(..))")
-    public void servicePointcut() {
-        // Do nothing because it's just a service pointcut
     }
 
-    @AfterThrowing(pointcut = "servicePointcut()", throwing = "e")
-    public void afterServiceThrowing(JoinPoint joinPoint, Throwable e) {
+    @AfterThrowing(pointcut = "execution(public * nju.seec.helper.logic.service.impl..*.*(..))", throwing = "e")
+    public void logServiceThrowing(JoinPoint joinPoint, Throwable e) {
         log.error(joinPoint.getTarget().getClass().getName() + " 类 " +
                 joinPoint.getSignature().getName() + " 方法出现异常:" +
                 e.getLocalizedMessage()

+ 39 - 0
src/main/java/nju/seec/helper/config/Swagger2Config.java

@@ -0,0 +1,39 @@
+package nju.seec.helper.config;
+
+import io.swagger.annotations.Api;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * @author cst
+ */
+@Configuration
+@EnableSwagger2
+public class Swagger2Config {
+    @Bean
+    public Docket createDocket() {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
+                .paths(PathSelectors.any())
+                .build();
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("SEEC-Helper后端API文档")
+                .contact(new Contact("ChenSiTong", "", "chen_st@foxmail.com"))
+                .description("")
+                .version("1.0")
+                .build();
+    }
+}

+ 19 - 0
src/main/java/nju/seec/helper/data/converter/MapToJsonConverter.java

@@ -0,0 +1,19 @@
+package nju.seec.helper.data.converter;
+
+import nju.seec.helper.util.JsonUtils;
+
+import javax.persistence.AttributeConverter;
+import java.util.Map;
+
+@SuppressWarnings("rawtypes")
+public class MapToJsonConverter implements AttributeConverter<Map, String> {
+    @Override
+    public String convertToDatabaseColumn(Map map) {
+        return JsonUtils.toJson(map);
+    }
+
+    @Override
+    public Map convertToEntityAttribute(String s) {
+        return JsonUtils.fromJson(s, Map.class);
+    }
+}

+ 19 - 0
src/main/java/nju/seec/helper/data/converter/SetToJsonConverter.java

@@ -0,0 +1,19 @@
+package nju.seec.helper.data.converter;
+
+import nju.seec.helper.util.JsonUtils;
+
+import javax.persistence.AttributeConverter;
+import java.util.Set;
+
+@SuppressWarnings("rawtypes")
+public class SetToJsonConverter implements AttributeConverter<Set, String> {
+    @Override
+    public String convertToDatabaseColumn(Set set) {
+        return JsonUtils.toJson(set);
+    }
+
+    @Override
+    public Set convertToEntityAttribute(String s) {
+        return JsonUtils.fromJson(s, Set.class);
+    }
+}

+ 11 - 1
src/main/java/nju/seec/helper/data/dao/SlideDAO.java

@@ -12,7 +12,17 @@ import org.springframework.stereotype.Repository;
 @Repository
 public interface SlideDAO extends JpaRepository<Slide, Integer> {
     /**
-     * 以幻灯片名作为关键字分页查询
+     * 根据课程ID和幻灯片名是否包含关键字分页查询
+     *
+     * @param courseId
+     * @param key
+     * @param pageable
+     * @return
+     */
+    Page<Slide> findByCourseIdAndNameContains(String courseId, String key, Pageable pageable);
+
+    /**
+     * 根据幻灯片名是否包含关键字分页查询
      *
      * @param key
      * @param pageable

+ 9 - 4
src/main/java/nju/seec/helper/data/entity/Comment.java

@@ -16,14 +16,19 @@ public class Comment {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private int id;
+
     @Column(name = "item_id", nullable = false)
     private int itemId;
+
     @Column(name = "before_comment_id")
     private Integer beforeCommentId;
-    @Column(name = "student_name", nullable = false)
-    private String studentName;
-    @Column(name = "student_id", nullable = false)
-    private String studentId;
+
+    @Column(name = "commentator_name", nullable = false)
+    private String commentatorName;
+
+    @Column(name = "commentator_id", nullable = false)
+    private String commentatorId;
+
     @Column(name = "create_time", updatable = false)
     @CreationTimestamp
     private LocalDateTime createTime;

+ 3 - 2
src/main/java/nju/seec/helper/data/entity/Item.java

@@ -20,10 +20,11 @@ public class Item {
     @Column(name = "frontend_id")
     private String frontendId;
 
-    @Column(nullable = false)
-    private int number;
+    private String placeholder;
+
     @Enumerated(EnumType.STRING)
     private ItemTransition transition;
+
     @Enumerated(EnumType.STRING)
     private ItemType type = ItemType.MD;
 

+ 30 - 4
src/main/java/nju/seec/helper/data/entity/Quiz.java

@@ -1,8 +1,14 @@
 package nju.seec.helper.data.entity;
 
 import lombok.Data;
+import nju.seec.helper.data.converter.MapToJsonConverter;
+import nju.seec.helper.data.converter.SetToJsonConverter;
+import org.hibernate.annotations.UpdateTimestamp;
 
 import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * @author cst
@@ -12,11 +18,31 @@ import javax.persistence.*;
 @Table(name = "quiz")
 public class Quiz {
     @Id
-    @GeneratedValue(strategy = GenerationType.IDENTITY)
     private int id;
 
-    @Column(name = "system_id", nullable = false)
-    private String systemId;
+    private String type;
 
-    private String content;
+    @Column(nullable = false)
+    private String stem;
+
+    @SuppressWarnings("all")
+    @Convert(converter = MapToJsonConverter.class)
+    @Column(columnDefinition = "json")
+    private Map<String, String> options;
+
+    @Convert(converter = SetToJsonConverter.class)
+    @Column(columnDefinition = "json")
+    private Set<String> tags;
+
+    @Convert(converter = SetToJsonConverter.class)
+    @Column(name = "knowledge_id", columnDefinition = "json")
+    private Set<String> knowledgeId;
+
+    @Column(name = "last_modified")
+    @UpdateTimestamp
+    private LocalDateTime lastModified;
+
+    private String answer;
+
+    private String analysis;
 }

+ 4 - 8
src/main/java/nju/seec/helper/data/entity/Slide.java

@@ -7,8 +7,8 @@ import org.hibernate.annotations.UpdateTimestamp;
 
 import javax.persistence.*;
 import java.time.LocalDateTime;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * @author cst
@@ -24,11 +24,6 @@ public class Slide {
     @Column(name = "course_id", nullable = false)
     private String courseId;
 
-    @Column(name = "frontend_id")
-    private String frontendId;
-
-    private String placeholder;
-
     @Enumerated(EnumType.STRING)
     @Column(nullable = false)
     private SlideState state;
@@ -37,7 +32,8 @@ public class Slide {
     private String name;
 
     @OneToMany(mappedBy = "slide")
-    private Set<Item> items = new HashSet<>();
+    @OrderColumn(name = "index")
+    private List<Item> items = new ArrayList<>();
 
     @Column(name = "create_time", updatable = false)
     @CreationTimestamp

+ 33 - 0
src/main/java/nju/seec/helper/data/entity/SlideQuiz.java

@@ -0,0 +1,33 @@
+package nju.seec.helper.data.entity;
+
+import lombok.Data;
+import nju.seec.helper.util.enums.SlideQuizType;
+import org.hibernate.annotations.CreationTimestamp;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+
+/**
+ * @author cst
+ */
+@Data
+@Entity
+@Table(name = "slide_quiz")
+public class SlideQuiz {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private int id;
+
+    @Column(name = "slide_id", nullable = false)
+    private int slideId;
+
+    @Column(name = "quiz_id", nullable = false)
+    private int quizId;
+
+    @Column(nullable = false)
+    private SlideQuizType type = SlideQuizType.IN_CLASS;
+
+    @Column(name = "create_time", updatable = false)
+    @CreationTimestamp
+    private LocalDateTime createTime;
+}

+ 29 - 0
src/main/java/nju/seec/helper/data/entity/StudentAnswer.java

@@ -0,0 +1,29 @@
+package nju.seec.helper.data.entity;
+
+import lombok.Data;
+
+import javax.persistence.*;
+
+/**
+ * @author cst
+ */
+@Data
+@Entity
+@Table(name = "student_answer")
+public class StudentAnswer {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private int id;
+
+    @Column(name = "student_id", nullable = false)
+    private int studentId;
+
+    @Column(name = "slide_quiz_id", nullable = false)
+    private int slideQuizId;
+
+    @Column(name = "choose_option", nullable = false)
+    private String chooseOption;
+
+    @Column(name = "right_option", nullable = false)
+    private String rightOption;
+}

+ 4 - 16
src/main/java/nju/seec/helper/logic/convert/DTOConvertFactory.java

@@ -5,9 +5,9 @@ import lombok.experimental.UtilityClass;
 import nju.seec.helper.data.entity.Item;
 import nju.seec.helper.data.entity.Slide;
 import nju.seec.helper.web.dto.SlideDTO;
+import org.springframework.beans.BeanUtils;
 
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 /**
@@ -19,25 +19,13 @@ public class DTOConvertFactory {
     private static final Map<Class, DTOConverter> MAP = new ImmutableMap.Builder<Class, DTOConverter>()
             .put(SlideDTO.class, (DTOConverter<SlideDTO, Slide>) slideDTO -> {
                 Slide slide = new Slide();
-                slide.setId(slideDTO.getId());
-                slide.setCourseId(slideDTO.getCourseId());
-                slide.setFrontendId(slideDTO.getFrontendId());
-                slide.setState(slideDTO.getState());
-                slide.setName(slideDTO.getName());
-                slide.setPlaceholder(slideDTO.getPlaceholder());
-                AtomicInteger itemIndex = new AtomicInteger();
+                BeanUtils.copyProperties(slideDTO, slide, "items");
                 slide.setItems(slideDTO.getItems().stream()
                         .map(itemDTO -> {
                             Item item = new Item();
-                            item.setContent(itemDTO.getContent());
-                            item.setFrontendId(itemDTO.getFrontendId());
-                            item.setId(itemDTO.getId());
-                            item.setTransition(itemDTO.getTransition());
-                            item.setType(itemDTO.getType());
-
-                            item.setNumber(itemIndex.getAndIncrement());
+                            BeanUtils.copyProperties(itemDTO, item);
                             return item;
-                        }).collect(Collectors.toSet()));
+                        }).collect(Collectors.toList()));
                 return slide;
             })
             .build();

+ 15 - 24
src/main/java/nju/seec/helper/logic/convert/EntityConvertFactory.java

@@ -2,13 +2,12 @@ package nju.seec.helper.logic.convert;
 
 import com.google.common.collect.ImmutableMap;
 import lombok.experimental.UtilityClass;
-import nju.seec.helper.data.entity.Item;
 import nju.seec.helper.data.entity.Slide;
 import nju.seec.helper.logic.vo.ItemVO;
 import nju.seec.helper.logic.vo.SlideVO;
+import org.springframework.beans.BeanUtils;
 import org.springframework.data.util.Pair;
 
-import java.util.Comparator;
 import java.util.Map;
 import java.util.stream.Collectors;
 
@@ -19,31 +18,23 @@ import java.util.stream.Collectors;
 public class EntityConvertFactory {
     @SuppressWarnings("rawtypes")
     private final Map<Pair, EntityConverter> MAP = new ImmutableMap.Builder<Pair, EntityConverter>()
-            .put(Pair.of(Slide.class, SlideVO.class), (EntityConverter<Slide, SlideVO>) slide -> SlideVO.builder()
-                    .id(slide.getId())
-                    .courseId(slide.getCourseId())
-                    .frontendId(slide.getFrontendId())
-                    .placeholder(slide.getPlaceholder())
-                    .name(slide.getName())
-                    .state(slide.getState())
-                    .createTime(slide.getCreateTime())
-                    .updateTime(slide.getUpdateTime())
-                    .items(slide.getItems().stream()
-                            .sorted(Comparator.comparingInt(Item::getNumber))
-                            .map(item -> ItemVO.builder()
-                                    .id(item.getId())
-                                    .frontendId(item.getFrontendId())
-                                    .content(item.getContent())
-                                    .transition(item.getTransition())
-                                    .type(item.getType())
-                                    .build())
-                            .collect(Collectors.toList()))
-                    .build())
+            .put(Pair.of(Slide.class, SlideVO.class), (EntityConverter<Slide, SlideVO>) slide -> {
+                SlideVO slideVO = new SlideVO();
+                BeanUtils.copyProperties(slide, slideVO, "items");
+                slideVO.setItems(slide.getItems().stream()
+                        .map(item -> {
+                            ItemVO itemVO = new ItemVO();
+                            BeanUtils.copyProperties(item, itemVO);
+                            return itemVO;
+                        })
+                        .collect(Collectors.toList()));
+                return slideVO;
+            })
             .build();
 
     @SuppressWarnings("unchecked")
-    public <Entity, VO> VO convert(Entity entity, Class<VO> voClass) {
-        return (VO) MAP.get(Pair.of(entity.getClass(), voClass))
+    public <Entity, VO> VO convert(Entity entity, Class<VO> classOfVO) {
+        return (VO) MAP.get(Pair.of(entity.getClass(), classOfVO))
                 .convert(entity);
     }
 }

+ 29 - 0
src/main/java/nju/seec/helper/logic/service/CourseService.java

@@ -0,0 +1,29 @@
+package nju.seec.helper.logic.service;
+
+import nju.seec.helper.logic.vo.CourseVO;
+import org.springframework.data.domain.Pageable;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+public interface CourseService {
+    /**
+     * 老师获取创建的课程
+     *
+     * @param teacherId
+     * @param pageable
+     * @return
+     */
+    List<CourseVO> getTeacherCourses(int teacherId, Pageable pageable);
+
+    /**
+     * 学生获取所选的课程
+     *
+     * @param studentId
+     * @param pageable
+     * @return
+     */
+    List<CourseVO> getStudentCourses(int studentId, Pageable pageable);
+}

+ 45 - 0
src/main/java/nju/seec/helper/logic/service/QuizService.java

@@ -0,0 +1,45 @@
+package nju.seec.helper.logic.service;
+
+import nju.seec.helper.data.entity.SlideQuiz;
+import nju.seec.helper.logic.vo.QuizVO;
+import nju.seec.helper.logic.vo.SlideQuizVO;
+import nju.seec.helper.util.enums.UserRole;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+public interface QuizService {
+    /**
+     * 根据关键字获取问题
+     *
+     * @param key
+     * @return
+     */
+    QuizVO getQuizzes(String key);
+
+    /**
+     * 创建幻灯片对应的问题
+     *
+     * @param slideQuiz
+     * @return
+     */
+    SlideQuizVO createSlideQuiz(SlideQuiz slideQuiz);
+
+    /**
+     * 删除幻灯片对应的问题
+     *
+     * @param slideQuizId
+     */
+    void deleteSlideQuiz(int slideQuizId);
+
+    /**
+     * 获取幻灯片中的问题
+     *
+     * @param slideId
+     * @param userRole
+     * @return
+     */
+    List<SlideQuizVO> getSlideQuizzes(int slideId, UserRole userRole);
+}

+ 21 - 5
src/main/java/nju/seec/helper/logic/service/SlideService.java

@@ -2,6 +2,7 @@ package nju.seec.helper.logic.service;
 
 import nju.seec.helper.logic.vo.SlideVO;
 import nju.seec.helper.web.dto.SlideDTO;
+import org.springframework.data.domain.Pageable;
 
 import java.util.List;
 
@@ -10,18 +11,26 @@ import java.util.List;
  */
 public interface SlideService {
     /**
-     * 根据课程id,分页取得幻灯片概要列表
+     * 根据课程ID和关键字取得幻灯片列表
      *
      * @param courseId
-     * @param page
-     * @param size
      * @param key
+     * @param pageable
      * @return
      */
-    List<SlideVO> getSlides(String courseId, int page, int size, String key);
+    List<SlideVO> getSlides(String courseId, String key, Pageable pageable);
 
     /**
-     * 根据id取得幻灯片
+     * 根据关键字取得幻灯片列表
+     *
+     * @param key
+     * @param pageable
+     * @return
+     */
+    List<SlideVO> getSlides(String key, Pageable pageable);
+
+    /**
+     * 根据ID取得幻灯片
      *
      * @param slideId
      * @return
@@ -43,4 +52,11 @@ public interface SlideService {
      * @return
      */
     SlideVO update(SlideDTO slideDTO);
+
+    /**
+     * 删除幻灯片
+     *
+     * @param slideId
+     */
+    void delete(int slideId);
 }

+ 35 - 0
src/main/java/nju/seec/helper/logic/service/impl/CourseServiceImpl.java

@@ -0,0 +1,35 @@
+package nju.seec.helper.logic.service.impl;
+
+import nju.seec.helper.logic.service.CourseService;
+import nju.seec.helper.logic.vo.CourseVO;
+import nju.seec.helper.util.FakerUtils;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author cst
+ * <p> 该实现类完全基于伪造数据
+ */
+@Service
+public class CourseServiceImpl implements CourseService {
+    @Override
+    public List<CourseVO> getTeacherCourses(int teacherId, Pageable pageable) {
+        List<CourseVO> courseVOs = new ArrayList<>();
+        int size = Math.min(pageable.getPageSize(), 10);
+        for (int i = 0; i < size; i++) {
+            CourseVO courseVO = new CourseVO();
+            courseVO.setId(FakerUtils.getFaker().idNumber().invalidSvSeSsn());
+            courseVO.setName(FakerUtils.getFaker().job().field());
+            courseVOs.add(courseVO);
+        }
+        return courseVOs;
+    }
+
+    @Override
+    public List<CourseVO> getStudentCourses(int studentId, Pageable pageable) {
+        return getTeacherCourses(studentId, pageable);
+    }
+}

+ 19 - 5
src/main/java/nju/seec/helper/logic/service/impl/SlideServiceImpl.java

@@ -10,10 +10,11 @@ import nju.seec.helper.util.enums.SlideState;
 import nju.seec.helper.util.exception.HelperException;
 import nju.seec.helper.web.dto.SlideDTO;
 import org.springframework.cache.annotation.CacheConfig;
+import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.CachePut;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
@@ -32,8 +33,17 @@ public class SlideServiceImpl implements SlideService {
     }
 
     @Override
-    public List<SlideVO> getSlides(String courseId, int page, int size, String key) {
-        Page<Slide> slidePage = slideDAO.findByNameContains(key, PageRequest.of(page - 1, size));
+    public List<SlideVO> getSlides(String courseId, String key, Pageable pageable) {
+        Page<Slide> slidePage = slideDAO.findByCourseIdAndNameContains(courseId, key, pageable);
+
+        return slidePage.getContent().stream()
+                .map(slide -> EntityConvertFactory.convert(slide, SlideVO.class))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public List<SlideVO> getSlides(String key, Pageable pageable) {
+        Page<Slide> slidePage = slideDAO.findByNameContains(key, pageable);
 
         return slidePage.getContent().stream()
                 .map(slide -> EntityConvertFactory.convert(slide, SlideVO.class))
@@ -51,8 +61,6 @@ public class SlideServiceImpl implements SlideService {
     public SlideVO create(SlideDTO slideDTO) {
         Slide slide = new Slide();
         slide.setCourseId(slideDTO.getCourseId());
-        slide.setPlaceholder(slideDTO.getPlaceholder());
-        slide.setFrontendId(slideDTO.getFrontendId());
         slide.setName(slideDTO.getName());
         slide.setState(SlideState.DRAFT);
         slide = slideDAO.save(slide);
@@ -67,6 +75,12 @@ public class SlideServiceImpl implements SlideService {
         return EntityConvertFactory.convert(findSlideById(slideDTO.getId()), SlideVO.class);
     }
 
+    @CacheEvict(key = "#slideId")
+    @Override
+    public void delete(int slideId) {
+        slideDAO.deleteById(slideId);
+    }
+
     private Slide findSlideById(int slideId) {
         return slideDAO.findById(slideId).orElseThrow(() -> HelperException.of(
                 HelperException.ExceptionType.NOT_FOUND, "找不到幻灯片")

+ 12 - 0
src/main/java/nju/seec/helper/logic/vo/AnswerVO.java

@@ -0,0 +1,12 @@
+package nju.seec.helper.logic.vo;
+
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+public class AnswerVO {
+    private int quizId;
+    private String answer;
+    private String analysis;
+}

+ 1 - 2
src/main/java/nju/seec/helper/logic/vo/ItemVO.java

@@ -1,6 +1,5 @@
 package nju.seec.helper.logic.vo;
 
-import lombok.Builder;
 import lombok.Data;
 import nju.seec.helper.util.enums.ItemTransition;
 import nju.seec.helper.util.enums.ItemType;
@@ -9,9 +8,9 @@ import nju.seec.helper.util.enums.ItemType;
  * @author cst
  */
 @Data
-@Builder
 public class ItemVO {
     private String frontendId;
+    private String placeholder;
     private Integer id;
     private ItemTransition transition;
     private ItemType type;

+ 16 - 6
src/main/java/nju/seec/helper/logic/vo/QuizVO.java

@@ -1,17 +1,27 @@
 package nju.seec.helper.logic.vo;
 
-import lombok.Builder;
 import lombok.Data;
-import nju.seec.helper.util.enums.QuizType;
+
+import java.time.LocalDateTime;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * @author cst
  */
 @Data
-@Builder
 public class QuizVO {
-    private QuizType quizType;
-    private String systemId;
     private Integer id;
-    private String content;
+    private String type;
+    private String stem;
+    private Map<String, String> options;
+    private Set<String> tags;
+    private Set<String> knowledgeId;
+    private LocalDateTime lastModified;
+
+    /**
+     * 以下属性根据访问系统角色判断是否赋值
+     */
+    private String answer;
+    private String analysis;
 }

+ 4 - 0
src/main/java/nju/seec/helper/logic/vo/SlideQuizVO.java

@@ -0,0 +1,4 @@
+package nju.seec.helper.logic.vo;
+
+public class SlideQuizVO {
+}

+ 0 - 4
src/main/java/nju/seec/helper/logic/vo/SlideVO.java

@@ -1,6 +1,5 @@
 package nju.seec.helper.logic.vo;
 
-import lombok.Builder;
 import lombok.Data;
 import nju.seec.helper.util.enums.SlideState;
 
@@ -11,11 +10,8 @@ import java.util.List;
  * @author cst
  */
 @Data
-@Builder
 public class SlideVO {
     private String courseId;
-    private String frontendId;
-    private String placeholder;
     private Integer id;
     private SlideState state;
     private String name;

+ 18 - 0
src/main/java/nju/seec/helper/util/FakerUtils.java

@@ -0,0 +1,18 @@
+package nju.seec.helper.util;
+
+import com.github.javafaker.Faker;
+import lombok.experimental.UtilityClass;
+
+import java.util.Locale;
+
+/**
+ * @author cst
+ */
+@UtilityClass
+public class FakerUtils {
+    private static final Faker FAKER = new Faker(new Locale("zh-CN"));
+
+    public Faker getFaker() {
+        return FAKER;
+    }
+}

+ 20 - 0
src/main/java/nju/seec/helper/util/JsonUtils.java

@@ -0,0 +1,20 @@
+package nju.seec.helper.util;
+
+import com.google.gson.Gson;
+import lombok.experimental.UtilityClass;
+
+/**
+ * @author cst
+ */
+@UtilityClass
+public class JsonUtils {
+    private static final Gson GSON = new Gson();
+
+    public String toJson(Object object) {
+        return GSON.toJson(object);
+    }
+
+    public <T> T fromJson(String json, Class<T> classOfT) {
+        return GSON.fromJson(json, classOfT);
+    }
+}

+ 4 - 0
src/main/java/nju/seec/helper/util/enums/ItemTransition.java

@@ -1,4 +1,8 @@
 package nju.seec.helper.util.enums;
 
+/**
+ * @author cst
+ */
 public enum ItemTransition {
+    NORMAL
 }

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

@@ -4,5 +4,5 @@ package nju.seec.helper.util.enums;
  * @author cst
  */
 public enum QuizType {
-
+    choice
 }

+ 8 - 0
src/main/java/nju/seec/helper/util/enums/SlideQuizType.java

@@ -0,0 +1,8 @@
+package nju.seec.helper.util.enums;
+
+/**
+ * @author cst
+ */
+public enum SlideQuizType {
+    PREVIEW, IN_CLASS, REVIEW, ALL
+}

+ 0 - 1
src/main/java/nju/seec/helper/util/enums/SlideState.java

@@ -3,7 +3,6 @@ package nju.seec.helper.util.enums;
 /**
  * @author cst
  */
-
 public enum SlideState {
     DRAFT
 }

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

@@ -0,0 +1,8 @@
+package nju.seec.helper.util.enums;
+
+/**
+ * @author cst
+ */
+public enum UserRole {
+    TEACHER, STUDENT
+}

+ 1 - 1
src/main/java/nju/seec/helper/util/exception/HelperException.java

@@ -15,7 +15,7 @@ public class HelperException extends RuntimeException {
     @Getter
     public enum ExceptionType {
         // http code
-        FORBIDDEN(403), NOT_FOUND(404), CONFLICT(409), SYSTEM_ERROR(500);
+        NOT_LOGIN(401), FORBIDDEN(403), NOT_FOUND(404), CONFLICT(409), SYSTEM_ERROR(500);
 
         private int code;
 

+ 43 - 0
src/main/java/nju/seec/helper/web/controller/CourseController.java

@@ -0,0 +1,43 @@
+package nju.seec.helper.web.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import nju.seec.helper.logic.service.CourseService;
+import nju.seec.helper.logic.vo.CourseVO;
+import nju.seec.helper.web.response.PageResourceResponse;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.web.PageableDefault;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @author cst
+ */
+@Api(tags = "课程")
+@CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST})
+@RestController
+@RequestMapping("/api/course")
+public class CourseController {
+    private final CourseService courseService;
+
+    public CourseController(CourseService courseService) {
+        this.courseService = courseService;
+    }
+
+    @ApiOperation(value = "教师获取创建课程", notes = "支持定制化分页排序,例:" + "/course/seec?page=3&size=10&sort=id,asc&sort=name,desc" + ",即以id正向排序再以name倒序排序,请求第3页,返回10条数据")
+    @GetMapping("/teacher")
+    public PageResourceResponse<CourseVO> getTeacherCourses(@ApiParam(hidden = true) @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
+        List<CourseVO> courseVOs = courseService.getTeacherCourses(1, pageable);
+        return PageResourceResponse.of(courseVOs, PageResourceResponse.PageInfo.of(pageable.getPageNumber(), courseVOs.size()));
+    }
+
+    @ApiOperation(value = "学生获取所选课程", notes = "支持定制化分页排序,例:" + "/course/seec?page=3&size=10&sort=id,asc&sort=name,desc" + ",即以id正向排序再以name倒序排序,请求第3页,返回10条数据")
+    @GetMapping("/student")
+    public PageResourceResponse<CourseVO> getStudentCourse(@ApiParam(hidden = true) @PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
+        List<CourseVO> courseVOs = courseService.getStudentCourses(1, pageable);
+        return PageResourceResponse.of(courseVOs, PageResourceResponse.PageInfo.of(pageable.getPageNumber(), courseVOs.size()));
+
+    }
+}

+ 30 - 13
src/main/java/nju/seec/helper/web/controller/SlideController.java

@@ -1,11 +1,17 @@
 package nju.seec.helper.web.controller;
 
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import nju.seec.helper.logic.service.SlideService;
 import nju.seec.helper.logic.vo.SlideVO;
 import nju.seec.helper.web.dto.SlideDTO;
 import nju.seec.helper.web.response.EmptyResponse;
 import nju.seec.helper.web.response.PageResourceResponse;
 import nju.seec.helper.web.response.ResourceResponse;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.web.PageableDefault;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
@@ -14,6 +20,8 @@ import java.util.List;
 /**
  * @author cst
  */
+@Api(tags = "幻灯片")
+@CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST})
 @RestController
 @RequestMapping("/api/slide")
 public class SlideController {
@@ -23,34 +31,43 @@ public class SlideController {
         this.slideService = slideService;
     }
 
+    @ApiOperation(value = "教师基于课程ID和关键字获取创建的幻灯片列表", notes = "支持定制化分页排序,例:" + "/course/seec?page=3&size=10&sort=id,asc&sort=name,desc" + ",即以id正向排序再以name倒序排序,请求第3页,返回10条数据")
     @GetMapping("/course/{courseId}")
     public PageResourceResponse<SlideVO> getSlides(@PathVariable String courseId,
-                                                   @RequestParam(defaultValue = "1") int page,
-                                                   @RequestParam(defaultValue = Integer.MAX_VALUE + "") int size,
-                                                   @RequestParam(defaultValue = "") String key) {
-        List<SlideVO> slideVOs = slideService.getSlides(courseId, page, size, key);
-        return PageResourceResponse.of(slideVOs, PageResourceResponse.PageInfo.of(page, slideVOs.size()));
+                                                   @RequestParam(defaultValue = "") String key,
+                                                   @ApiParam(hidden = true) @PageableDefault(size = Integer.MAX_VALUE, sort = "updateTime", direction = Sort.Direction.DESC) Pageable pageable) {
+        List<SlideVO> slideVOs = slideService.getSlides(courseId, key, pageable);
+        return PageResourceResponse.of(slideVOs, PageResourceResponse.PageInfo.of(pageable.getPageNumber(), slideVOs.size()));
+    }
+
+
+    @ApiOperation(value = "教师基于关键字获取创建的幻灯片列表", notes = "支持定制化分页排序,例:" + "/course/seec?page=3&size=10&sort=id,asc&sort=name,desc" + ",即以id正向排序再以name倒序排序,请求第3页,返回10条数据")
+    @GetMapping("")
+    public PageResourceResponse<SlideVO> getSlides(@RequestParam(defaultValue = "") String key,
+                                                   @ApiParam(hidden = true) @PageableDefault(size = Integer.MAX_VALUE, sort = "updateTime", direction = Sort.Direction.DESC) Pageable pageable) {
+        List<SlideVO> slideVOs = slideService.getSlides(key, pageable);
+        return PageResourceResponse.of(slideVOs, PageResourceResponse.PageInfo.of(pageable.getPageNumber(), slideVOs.size()));
     }
 
-    /**
-     * 迭代一只考虑返回给老师的结果
-     *
-     * @param slideId
-     * @return
-     */
     @GetMapping("/{slideId:\\d+}")
     public ResourceResponse<SlideVO> getSlide(@PathVariable int slideId) {
         return ResourceResponse.of(slideService.getOneSlide(slideId));
     }
 
+
     @PostMapping("/create")
     public ResourceResponse<SlideVO> create(@RequestBody @Validated SlideDTO slideDTO) {
         return ResourceResponse.of(slideService.create(slideDTO));
     }
 
     @PostMapping("/update")
-    public EmptyResponse update(@RequestBody @Validated SlideDTO slideDTO) {
-        slideService.update(slideDTO);
+    public ResourceResponse<SlideVO> update(@RequestBody @Validated SlideDTO slideDTO) {
+        return ResourceResponse.of(slideService.update(slideDTO));
+    }
+
+    @PostMapping("/delete/{slideId:\\d+}")
+    public EmptyResponse delete(@PathVariable int slideId) {
+        slideService.delete(slideId);
         return EmptyResponse.getInstance();
     }
 }

+ 1 - 1
src/main/java/nju/seec/helper/web/dto/CommentDTO.java

@@ -11,7 +11,7 @@ import javax.validation.constraints.NotNull;
 @Data
 public class CommentDTO {
     @NotNull
-    private Integer pageId;
+    private Integer itemId;
     private Integer beforeCommentId;
     @NotEmpty
     private String content;

+ 3 - 6
src/main/java/nju/seec/helper/web/dto/ItemDTO.java

@@ -4,18 +4,15 @@ import lombok.Data;
 import nju.seec.helper.util.enums.ItemTransition;
 import nju.seec.helper.util.enums.ItemType;
 
-import javax.validation.constraints.NotNull;
-
 /**
  * @author cst
  */
 @Data
 public class ItemDTO {
     private String frontendId;
+    private String placeholder;
     private Integer id;
-    @NotNull
-    private ItemTransition transition;
-    @NotNull
-    private ItemType type;
+    private ItemTransition transition = ItemTransition.NORMAL;
+    private ItemType type = ItemType.MD;
     private String content;
 }

+ 3 - 6
src/main/java/nju/seec/helper/web/dto/SlideDTO.java

@@ -4,7 +4,7 @@ import lombok.Data;
 import nju.seec.helper.util.enums.SlideState;
 
 import javax.validation.constraints.NotEmpty;
-import javax.validation.constraints.NotNull;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -14,12 +14,9 @@ import java.util.List;
 public class SlideDTO {
     @NotEmpty
     private String courseId;
-    private String frontendId;
-    private String placeholder;
     private Integer id;
-    @NotNull
-    private SlideState state;
+    private SlideState state = SlideState.DRAFT;
     @NotEmpty
     private String name;
-    private List<ItemDTO> items;
+    private List<ItemDTO> items = Collections.emptyList();
 }

+ 14 - 0
src/main/java/nju/seec/helper/web/dto/SlideQuizDTO.java

@@ -0,0 +1,14 @@
+package nju.seec.helper.web.dto;
+
+import lombok.Data;
+import nju.seec.helper.util.enums.SlideQuizType;
+
+/**
+ * @author cst
+ */
+@Data
+public class SlideQuizDTO {
+    private Integer slideId;
+    private Integer quizId;
+    private SlideQuizType type;
+}

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

@@ -12,6 +12,9 @@ spring:
     console:
       enabled: true
       path: /h2
+  http:
+    encoding:
+      force: true
 server:
   port: 18080