Browse Source

add basic request

Bay 5 years ago
parent
commit
9a85d35214
23 changed files with 1240 additions and 4 deletions
  1. 82 2
      pom.xml
  2. 33 0
      seecoder-bok-client/pom.xml
  3. 194 2
      seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokClient.java
  4. 15 0
      seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokConstants.java
  5. 22 0
      seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokException.java
  6. 14 0
      seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokKnowledgeApi.java
  7. 51 0
      seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokQuestionApi.java
  8. 13 0
      seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokQuestionQueryApi.java
  9. 38 0
      seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokRecordApi.java
  10. 59 0
      seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokUserInteractiveApi.java
  11. 82 0
      seecoder-bok-client/src/test/java/cn/seecoder/bok/client/SeecoderBokClientTest.java
  12. 17 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/QuestionType.java
  13. 33 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/dto/CollectDTO.java
  14. 59 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/dto/PageDTO.java
  15. 34 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/dto/RateDTO.java
  16. 53 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/InteractiveVO.java
  17. 74 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/KnowledgeNodeVO.java
  18. 53 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/PageVO.java
  19. 35 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/PageableQuestionListVO.java
  20. 25 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/PageableRecordQuestionVO.java
  21. 134 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/QuestionVO.java
  22. 35 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/RecordQuestionVO.java
  23. 85 0
      seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/RecordVO.java

+ 82 - 2
pom.xml

@@ -6,7 +6,7 @@
 
     <groupId>cn.seecoder</groupId>
     <artifactId>seecoder-bok</artifactId>
-    <version>${seecoder-bok.version}</version>
+    <version>0.1.0</version>
     <modules>
         <module>seecoder-bok-client</module>
         <module>seecoder-bok-common</module>
@@ -16,9 +16,89 @@
     <properties>
         <maven.compiler.source>8</maven.compiler.source>
         <maven.compiler.target>8</maven.compiler.target>
-        <seecoder-bok.version>0.1.0</seecoder-bok.version>
+
+        <jackson.version>2.12.0</jackson.version>
+        <slf4j.version>2.14.0</slf4j.version>
     </properties>
 
+    <dependencies>
+        <!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <version>2.0.1.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>30.1-jre</version>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>5.7.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>5.7.0</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>com.squareup.okhttp3</groupId>
+                <artifactId>okhttp</artifactId>
+                <version>4.9.0</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-commons -->
+            <dependency>
+                <groupId>org.springframework.data</groupId>
+                <artifactId>spring-data-commons</artifactId>
+                <version>2.3.6.RELEASE</version>
+            </dependency>
+            <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
+            <dependency>
+                <groupId>com.fasterxml.jackson.core</groupId>
+                <artifactId>jackson-databind</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.fasterxml.jackson.module</groupId>
+                <artifactId>jackson-module-parameter-names</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.fasterxml.jackson.datatype</groupId>
+                <artifactId>jackson-datatype-jdk8</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.fasterxml.jackson.datatype</groupId>
+                <artifactId>jackson-datatype-jsr310</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
     <distributionManagement>
         <repository>
             <id>nexus</id>

+ 33 - 0
seecoder-bok-client/pom.xml

@@ -16,4 +16,37 @@
         <maven.compiler.target>8</maven.compiler.target>
     </properties>
 
+    <dependencies>
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-commons</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.module</groupId>
+            <artifactId>jackson-module-parameter-names</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jdk8</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.seecoder</groupId>
+            <artifactId>seecoder-bok-common</artifactId>
+            <version>0.1.0</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
 </project>

+ 194 - 2
seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokClient.java

@@ -1,8 +1,200 @@
 package cn.seecoder.bok.client;
 
+import cn.seecoder.bok.common.dto.CollectDTO;
+import cn.seecoder.bok.common.dto.RateDTO;
+import cn.seecoder.bok.common.vo.*;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
+import okhttp3.*;
+import okio.BufferedSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
 /**
  * @author kunduin
  */
-public class SeecoderBokClient {
-    
+public class SeecoderBokClient implements SeecoderBokQuestionApi, SeecoderBokQuestionQueryApi,
+        SeecoderBokRecordApi, SeecoderBokUserInteractiveApi, SeecoderBokKnowledgeApi {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    private final OkHttpClient client;
+    private final ObjectMapper mapper;
+
+    private final int CONFIG_PAGE_START;
+    private final String CONFIG_HOST;
+
+    public SeecoderBokClient(String host) {
+        this(host, 0);
+    }
+
+    public SeecoderBokClient(String host, int pageStart) {
+        this.CONFIG_HOST = host;
+        this.CONFIG_PAGE_START = pageStart;
+        client = new OkHttpClient();
+        mapper = JsonMapper.builder()
+                .addModule(new ParameterNamesModule())
+                .addModule(new Jdk8Module())
+                .addModule(new JavaTimeModule())
+                .build();
+    }
+
+    @Override
+    public List<KnowledgeNodeVO> getKnowledgeNode(String name) {
+        HttpUrl httpUrl = createUrlBuilder("/api/knowledge")
+                .addQueryParameter("name", name)
+                .build();
+        Response response = get(httpUrl);
+        return getResponseBody(response,
+                new TypeReference<List<KnowledgeNodeVO>>() {
+                }).orElse(new ArrayList<>());
+    }
+
+    @Override
+    public Page<QuestionVO> getQuestionList(String stem, String tag, String knowledgeId, Pageable pageable) {
+        return null;
+    }
+
+    @Override
+    public QuestionVO createQuestion(@NotNull QuestionVO question) {
+        return null;
+    }
+
+    @Override
+    public Optional<QuestionVO> getQuestionById(@NotBlank String questionId) {
+        return Optional.empty();
+    }
+
+    @Override
+    public QuestionVO updateQuestion(@NotNull QuestionVO question) {
+        return null;
+    }
+
+    @Override
+    public void deleteQuestionById(@NotBlank String questionId) {
+
+    }
+
+    @Override
+    public List<QuestionVO> getQuestionListByIds(@NotNull List<String> ids) {
+        return null;
+    }
+
+    @Override
+    public List<RecordVO> createUserRecord(@NotNull List<RecordVO> records) {
+        return null;
+    }
+
+    @Override
+    public Page<RecordVO> getUserRecord(List<String> userIdList, List<String> questionIdList, List<String> recordIdList, Pageable pageable) {
+        return null;
+    }
+
+    @Override
+    public Page<RecordQuestionVO> getUserRecordQuestions(@NotNull String userId, Pageable pageable) {
+        return null;
+    }
+
+    @Override
+    public void collectQuestion(@NotBlank String userId, @NotBlank String questionId, @NotNull CollectDTO collectDTO) {
+
+    }
+
+    @Override
+    public void rateQuestion(@NotBlank String userId, @NotBlank String questionId, @NotNull RateDTO rateDTO) {
+
+    }
+
+    @Override
+    public Page<QuestionVO> getUserInteractiveQuestions(@NotBlank String userId, Boolean collect, Boolean rate, Pageable pageable) {
+        return null;
+    }
+
+    @Override
+    public InteractiveVO getUserInteractiveQuestion(@NotBlank String userId, @NotBlank String questionId) {
+        return null;
+    }
+
+    @Override
+    public List<InteractiveVO> getUserInteractiveQuestionIn(@NotBlank String userId, List<String> questionIdList) {
+        return null;
+    }
+
+    private Response get(HttpUrl httpUrl) {
+        return requestWithMethod("GET", httpUrl, null);
+    }
+
+    private Response post(HttpUrl httpUrl, RequestBody requestBody) {
+        return requestWithMethod("POST", httpUrl, requestBody);
+    }
+
+    private Response put(HttpUrl httpUrl, RequestBody requestBody) {
+        return requestWithMethod("PUT", httpUrl, requestBody);
+    }
+
+
+    private Response requestWithMethod(String method, HttpUrl httpUrl, RequestBody requestBody) {
+        Request request = new Request.Builder().url(httpUrl).method(method, requestBody).build();
+        try {
+            return client.newCall(request).execute();
+        } catch (IOException e) {
+            logger.error("IOException.", e);
+            throw SeecoderBokException.IO_EXCEPTION;
+        }
+    }
+
+    private HttpUrl.Builder createUrlBuilder(final String path) {
+        return Objects.requireNonNull(HttpUrl.parse(CONFIG_HOST + path)).newBuilder();
+    }
+
+    private <T> Optional<T> getResponseBody(Response response, Class<T> tClass) {
+        return getResponseBody(response, bodyString -> {
+            try {
+                return mapper.readValue(bodyString, tClass);
+            } catch (JsonProcessingException e) {
+                logger.error("JsonProcessingException.", e);
+                throw SeecoderBokException.JSON_EXCEPTION;
+            }
+        });
+    }
+
+    private <T> Optional<T> getResponseBody(Response response, TypeReference<T> typeReference) {
+        return getResponseBody(response, bodyString -> {
+            try {
+                return mapper.readValue(bodyString, typeReference);
+            } catch (JsonProcessingException e) {
+                logger.error("JsonProcessingException.", e);
+                throw SeecoderBokException.JSON_EXCEPTION;
+            }
+        });
+    }
+
+    private <T> Optional<T> getResponseBody(Response response, Function<String, T> mapType) {
+        return Optional.ofNullable(response.body()).map(body -> {
+            try {
+                String bodyString = body.string();
+                return mapType.apply(bodyString);
+            } catch (IOException e) {
+                logger.error("IOException.", e);
+                throw SeecoderBokException.IO_EXCEPTION;
+            }
+        });
+    }
 }

+ 15 - 0
seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokConstants.java

@@ -0,0 +1,15 @@
+package cn.seecoder.bok.client;
+
+
+import okhttp3.MediaType;
+
+/**
+ * @author Kunduin
+ */
+public class SeecoderBokConstants {
+
+    public final static int PAGE_START = 1;
+
+    public static final MediaType JSON
+            = MediaType.parse("application/json; charset=utf-8");
+}

+ 22 - 0
seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokException.java

@@ -0,0 +1,22 @@
+package cn.seecoder.bok.client;
+
+/**
+ * @author Kunduin
+ */
+public class SeecoderBokException extends RuntimeException {
+
+    private final int code;
+
+    public SeecoderBokException(String message, int code) {
+        super(message);
+        this.code = code;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public static SeecoderBokException JSON_EXCEPTION = new SeecoderBokException("JSON exception", 101);
+
+    public static SeecoderBokException IO_EXCEPTION = new SeecoderBokException("IO exception", 102);
+}

+ 14 - 0
seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokKnowledgeApi.java

@@ -1,7 +1,21 @@
 package cn.seecoder.bok.client;
 
+import cn.seecoder.bok.common.vo.KnowledgeNodeVO;
+
+import java.util.List;
+
 /**
  * @author kunduin
  */
 public interface SeecoderBokKnowledgeApi {
+    /**
+     * 获得所有知识点列表。知识点本身存储为列表,但其实是有树状结构
+     * 知识点的 abbreviation 字段,包含知识点与父级知识点的全部信息
+     * 若知识点 A 的简写(abbreviation)为 a,知识点 B 为知识点 A 的子知识点
+     * 则 B 的简写(abbreviation)为 a.b
+     *
+     * @param name 知识点name包含关键词
+     * @return 知识点列表
+     */
+    List<KnowledgeNodeVO> getKnowledgeNode(String name);
 }

+ 51 - 0
seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokQuestionApi.java

@@ -1,7 +1,58 @@
 package cn.seecoder.bok.client;
 
+import cn.seecoder.bok.common.vo.QuestionVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.Optional;
+
 /**
  * @author kunduin
  */
 public interface SeecoderBokQuestionApi {
+
+    /**
+     * 获得问题列表
+     *
+     * @param stem        模糊字符串匹配
+     * @param tag         标签
+     * @param knowledgeId 知识点
+     * @param pageable    分页
+     * @return 分页后的题目列表
+     */
+    Page<QuestionVO> getQuestionList(String stem, String tag,
+                                     String knowledgeId, Pageable pageable);
+
+    /**
+     * 创建题目
+     *
+     * @param question 题目信息
+     * @return 创建后赋予id的题目信息
+     */
+    QuestionVO createQuestion(@NotNull QuestionVO question);
+
+    /**
+     * 根据题目id获取题目
+     *
+     * @param questionId 题目id
+     * @return 题目信息
+     */
+    Optional<QuestionVO> getQuestionById(@NotBlank String questionId);
+
+    /**
+     * 更新题目信息
+     *
+     * @param question 题目信息
+     * @return 题目信息
+     */
+    QuestionVO updateQuestion(@NotNull QuestionVO question);
+
+    /**
+     * 删除题目
+     *
+     * @param questionId 题目id
+     */
+    void deleteQuestionById(@NotBlank String questionId);
 }

+ 13 - 0
seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokQuestionQueryApi.java

@@ -1,7 +1,20 @@
 package cn.seecoder.bok.client;
 
+import cn.seecoder.bok.common.vo.QuestionVO;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
 /**
  * @author kunduin
  */
 public interface SeecoderBokQuestionQueryApi {
+
+    /**
+     * 通过id列表获取题目列表
+     *
+     * @param ids id列表
+     * @return 题目列表
+     */
+    List<QuestionVO> getQuestionListByIds(@NotNull List<String> ids);
 }

+ 38 - 0
seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokRecordApi.java

@@ -1,7 +1,45 @@
 package cn.seecoder.bok.client;
 
+import cn.seecoder.bok.common.vo.RecordQuestionVO;
+import cn.seecoder.bok.common.vo.RecordVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
 /**
  * @author kunduin
  */
 public interface SeecoderBokRecordApi {
+
+    /**
+     * 新增用户的做题记录
+     *
+     * @param records 做题记录
+     * @return 做题记录
+     */
+    List<RecordVO> createUserRecord(@NotNull List<RecordVO> records);
+
+    /**
+     * 获得用户的记录
+     *
+     * @param userIdList     用户id列表
+     * @param questionIdList 题目id列表
+     * @param recordIdList   记录id列表
+     * @param pageable       分页
+     * @return 分页的记录
+     */
+    Page<RecordVO> getUserRecord(List<String> userIdList, List<String> questionIdList,
+                                 List<String> recordIdList, Pageable pageable);
+
+    /**
+     * 获得用户所有的做题记录
+     *
+     * @param userId   用户id
+     * @param pageable 分页
+     * @return 按照做题记录(Record)分页
+     */
+    Page<RecordQuestionVO> getUserRecordQuestions(@NotNull String userId, Pageable pageable);
+
 }

+ 59 - 0
seecoder-bok-client/src/main/java/cn/seecoder/bok/client/SeecoderBokUserInteractiveApi.java

@@ -1,7 +1,66 @@
 package cn.seecoder.bok.client;
 
+import cn.seecoder.bok.common.dto.CollectDTO;
+import cn.seecoder.bok.common.dto.RateDTO;
+import cn.seecoder.bok.common.vo.InteractiveVO;
+import cn.seecoder.bok.common.vo.QuestionVO;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
 /**
  * @author kunduin
  */
 public interface SeecoderBokUserInteractiveApi {
+    /**
+     * 收藏题目
+     *
+     * @param userId     用户
+     * @param questionId 题目
+     * @param collectDTO 收藏信息
+     */
+    void collectQuestion(@NotBlank String userId, @NotBlank String questionId, @NotNull CollectDTO collectDTO);
+
+    /**
+     * 为题目打分
+     *
+     * @param userId     用户
+     * @param questionId 题目
+     * @param rateDTO    打分信息
+     */
+    void rateQuestion(@NotBlank String userId, @NotBlank String questionId, @NotNull RateDTO rateDTO);
+
+    /**
+     * 获得用户交互的题目列表
+     *
+     * @param userId   用户id
+     * @param collect  是否收集
+     * @param rate     是否评分
+     * @param pageable 分页
+     * @return 题目列表
+     */
+    Page<QuestionVO> getUserInteractiveQuestions(
+            @NotBlank String userId, Boolean collect, Boolean rate, Pageable pageable);
+
+    /**
+     * 获得某用户交互的题目
+     *
+     * @param userId     用户
+     * @param questionId 题目
+     * @return 交互信息
+     */
+    InteractiveVO getUserInteractiveQuestion(@NotBlank String userId, @NotBlank String questionId);
+
+    /**
+     * 获得某用户交互的题目列表
+     *
+     * @param userId         用户
+     * @param questionIdList 题目列表
+     * @return 交互列表
+     */
+    List<InteractiveVO> getUserInteractiveQuestionIn(@NotBlank String userId, List<String> questionIdList);
+
 }

+ 82 - 0
seecoder-bok-client/src/test/java/cn/seecoder/bok/client/SeecoderBokClientTest.java

@@ -0,0 +1,82 @@
+package cn.seecoder.bok.client;
+
+import cn.seecoder.bok.common.vo.KnowledgeNodeVO;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SeecoderBokClientTest {
+
+    @Test
+    void getKnowledgeNode() {
+        SeecoderBokClient seecoderBokClient = new SeecoderBokClient("http://bok.seecoder.cn");
+        List<KnowledgeNodeVO> knowledgeNode = seecoderBokClient.getKnowledgeNode("软件需求");
+        System.out.println(knowledgeNode);
+        KnowledgeNodeVO one = knowledgeNode.stream()
+                .filter(node -> node.getAbbreviation().equals("SR.RV.CM"))
+                .findFirst()
+                .orElseThrow(() -> new AssertionFailedError("SR.RV.CM not found."));
+        assertEquals(one.getName(), "变更管理");
+    }
+
+    @Test
+    void getQuestionList() {
+    }
+
+    @Test
+    void createQuestion() {
+    }
+
+    @Test
+    void getQuestionById() {
+    }
+
+    @Test
+    void updateQuestion() {
+    }
+
+    @Test
+    void deleteQuestionById() {
+    }
+
+    @Test
+    void getQuestionListByIds() {
+    }
+
+    @Test
+    void createUserRecord() {
+    }
+
+    @Test
+    void getUserRecord() {
+    }
+
+    @Test
+    void getUserRecordQuestions() {
+    }
+
+    @Test
+    void collectQuestion() {
+    }
+
+    @Test
+    void rateQuestion() {
+    }
+
+    @Test
+    void getUserInteractiveQuestions() {
+    }
+
+    @Test
+    void getUserInteractiveQuestion() {
+    }
+
+    @Test
+    void getUserInteractiveQuestionIn() {
+    }
+}

+ 17 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/QuestionType.java

@@ -0,0 +1,17 @@
+package cn.seecoder.bok.common;
+
+/**
+ * @author Kunduin
+ */
+public enum QuestionType {
+    /** 选择题 */
+    choice,
+    /** 判断题 */
+    true_false,
+    /** 多选题 */
+    multiple_choice,
+    /** 填空题 */
+    blank,
+    /** 简答题 */
+    short_answer
+}

+ 33 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/dto/CollectDTO.java

@@ -0,0 +1,33 @@
+package cn.seecoder.bok.common.dto;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Kunduin
+ */
+public class CollectDTO {
+    @NotNull(message = "缺少是否收藏")
+    private Boolean collect;
+
+    public CollectDTO() {
+    }
+
+    public CollectDTO(Boolean collect) {
+        this.collect = collect;
+    }
+
+    public Boolean getCollect() {
+        return collect;
+    }
+
+    public void setCollect(Boolean collect) {
+        this.collect = collect;
+    }
+
+    @Override
+    public String toString() {
+        return "CollectDTO{" +
+                "collect=" + collect +
+                '}';
+    }
+}

+ 59 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/dto/PageDTO.java

@@ -0,0 +1,59 @@
+package cn.seecoder.bok.common.dto;
+
+import java.util.List;
+
+/**
+ * @author Kunduin
+ */
+public class PageDTO {
+    private Integer size;
+    private Integer page;
+    private List<String> sort;
+
+    public PageDTO() {
+    }
+
+    public PageDTO(Integer size, Integer page) {
+        this.size = size;
+        this.page = page;
+    }
+
+    public PageDTO(Integer size, Integer page, List<String> sort) {
+        this.size = size;
+        this.page = page;
+        this.sort = sort;
+    }
+
+    public Integer getSize() {
+        return size;
+    }
+
+    public void setSize(Integer size) {
+        this.size = size;
+    }
+
+    public Integer getPage() {
+        return page;
+    }
+
+    public void setPage(Integer page) {
+        this.page = page;
+    }
+
+    public List<String> getSort() {
+        return sort;
+    }
+
+    public void setSort(List<String> sort) {
+        this.sort = sort;
+    }
+
+    @Override
+    public String toString() {
+        return "PageDTO{" +
+                "size=" + size +
+                ", page=" + page +
+                ", sort=" + sort +
+                '}';
+    }
+}

+ 34 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/dto/RateDTO.java

@@ -0,0 +1,34 @@
+package cn.seecoder.bok.common.dto;
+
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Kunduin
+ */
+public class RateDTO {
+    /**
+     * 取值 1-5
+     */
+    @Max(value = 5, message = "最大评分为5")
+    @Min(value = 1, message = "最小评分为1")
+    @NotNull(message = "缺少评分")
+    private Integer rate;
+
+    public Integer getRate() {
+        return rate;
+    }
+
+    public void setRate(Integer rate) {
+        this.rate = rate;
+    }
+
+    @Override
+    public String toString() {
+        return "RateDTO{" +
+                "rate=" + rate +
+                '}';
+    }
+}

+ 53 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/InteractiveVO.java

@@ -0,0 +1,53 @@
+package cn.seecoder.bok.common.vo;
+
+/**
+ * @author Kunduin
+ */
+public class InteractiveVO {
+    private String userId;
+    private String questionId;
+    private Boolean collection;
+    private Integer rate;
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getQuestionId() {
+        return questionId;
+    }
+
+    public void setQuestionId(String questionId) {
+        this.questionId = questionId;
+    }
+
+    public Boolean getCollection() {
+        return collection;
+    }
+
+    public void setCollection(Boolean collection) {
+        this.collection = collection;
+    }
+
+    public Integer getRate() {
+        return rate;
+    }
+
+    public void setRate(Integer rate) {
+        this.rate = rate;
+    }
+
+    @Override
+    public String toString() {
+        return "InteractiveVO{" +
+                "userId='" + userId + '\'' +
+                ", questionId='" + questionId + '\'' +
+                ", collection=" + collection +
+                ", rate=" + rate +
+                '}';
+    }
+}

+ 74 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/KnowledgeNodeVO.java

@@ -0,0 +1,74 @@
+package cn.seecoder.bok.common.vo;
+
+public class KnowledgeNodeVO {
+    private String id;
+    /**
+     * 缩写为实际使用的唯一标识
+     */
+    private String abbreviation;
+
+    private String name;
+    private String englishName;
+    private String parentId;
+    private String parentName;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getAbbreviation() {
+        return abbreviation;
+    }
+
+    public void setAbbreviation(String abbreviation) {
+        this.abbreviation = abbreviation;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getEnglishName() {
+        return englishName;
+    }
+
+    public void setEnglishName(String englishName) {
+        this.englishName = englishName;
+    }
+
+    public String getParentId() {
+        return parentId;
+    }
+
+    public void setParentId(String parentId) {
+        this.parentId = parentId;
+    }
+
+    public String getParentName() {
+        return parentName;
+    }
+
+    public void setParentName(String parentName) {
+        this.parentName = parentName;
+    }
+
+    @Override
+    public String toString() {
+        return "KnowledgeNodeVO{" +
+                "id='" + id + '\'' +
+                ", abbreviation='" + abbreviation + '\'' +
+                ", name='" + name + '\'' +
+                ", englishName='" + englishName + '\'' +
+                ", parentId='" + parentId + '\'' +
+                ", parentName='" + parentName + '\'' +
+                '}';
+    }
+}

+ 53 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/PageVO.java

@@ -0,0 +1,53 @@
+package cn.seecoder.bok.common.vo;
+
+/**
+ * @author Kunduin
+ */
+public class PageVO {
+    private Integer size;
+    private Long totalElements;
+    private Integer totalPages;
+    private Integer number;
+
+    public Integer getSize() {
+        return size;
+    }
+
+    public void setSize(Integer size) {
+        this.size = size;
+    }
+
+    public Long getTotalElements() {
+        return totalElements;
+    }
+
+    public void setTotalElements(Long totalElements) {
+        this.totalElements = totalElements;
+    }
+
+    public Integer getTotalPages() {
+        return totalPages;
+    }
+
+    public void setTotalPages(Integer totalPages) {
+        this.totalPages = totalPages;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    @Override
+    public String toString() {
+        return "PageVO{" +
+                "size=" + size +
+                ", totalElements=" + totalElements +
+                ", totalPages=" + totalPages +
+                ", number=" + number +
+                '}';
+    }
+}

+ 35 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/PageableQuestionListVO.java

@@ -0,0 +1,35 @@
+package cn.seecoder.bok.common.vo;
+
+import java.util.List;
+
+/**
+ * @author Kunduin
+ */
+public class PageableQuestionListVO {
+    private List<QuestionVO> questions;
+    private PageVO page;
+
+    public List<QuestionVO> getQuestions() {
+        return questions;
+    }
+
+    public void setQuestions(List<QuestionVO> questions) {
+        this.questions = questions;
+    }
+
+    public PageVO getPage() {
+        return page;
+    }
+
+    public void setPage(PageVO page) {
+        this.page = page;
+    }
+
+    @Override
+    public String toString() {
+        return "QuestionListVO{" +
+                "questions=" + questions +
+                ", page=" + page +
+                '}';
+    }
+}

+ 25 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/PageableRecordQuestionVO.java

@@ -0,0 +1,25 @@
+package cn.seecoder.bok.common.vo;
+
+import java.util.List;
+
+/**
+ * @author Kunduin
+ */
+public class PageableRecordQuestionVO extends RecordQuestionVO {
+    private PageVO page;
+
+    public PageVO getPage() {
+        return page;
+    }
+
+    public void setPage(PageVO page) {
+        this.page = page;
+    }
+
+    @Override
+    public String toString() {
+        return "PageableRecordQuestionVO{" +
+                "page=" + page +
+                "} " + super.toString();
+    }
+}

+ 134 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/QuestionVO.java

@@ -0,0 +1,134 @@
+package cn.seecoder.bok.common.vo;
+
+import cn.seecoder.bok.common.QuestionType;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Kunduin
+ */
+public class QuestionVO {
+    private String id;
+    private QuestionType type;
+    private String stem;
+
+    private Map<String, String> options;
+
+    /** 填空题 __1__ __2__ */
+    private Map<String, String> blanks;
+
+    private String answer;
+    private String keyPoints;
+    private String analysis;
+
+    private List<String> tags;
+    private List<String> knowledgeId;
+    private Date lastModifiedTime;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public QuestionType getType() {
+        return type;
+    }
+
+    public void setType(QuestionType type) {
+        this.type = type;
+    }
+
+    public String getStem() {
+        return stem;
+    }
+
+    public void setStem(String stem) {
+        this.stem = stem;
+    }
+
+    public Map<String, String> getOptions() {
+        return options;
+    }
+
+    public void setOptions(Map<String, String> options) {
+        this.options = options;
+    }
+
+    public Map<String, String> getBlanks() {
+        return blanks;
+    }
+
+    public void setBlanks(Map<String, String> blanks) {
+        this.blanks = blanks;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    public String getKeyPoints() {
+        return keyPoints;
+    }
+
+    public void setKeyPoints(String keyPoints) {
+        this.keyPoints = keyPoints;
+    }
+
+    public String getAnalysis() {
+        return analysis;
+    }
+
+    public void setAnalysis(String analysis) {
+        this.analysis = analysis;
+    }
+
+    public List<String> getTags() {
+        return tags;
+    }
+
+    public void setTags(List<String> tags) {
+        this.tags = tags;
+    }
+
+    public List<String> getKnowledgeId() {
+        return knowledgeId;
+    }
+
+    public void setKnowledgeId(List<String> knowledgeId) {
+        this.knowledgeId = knowledgeId;
+    }
+
+    public Date getLastModifiedTime() {
+        return lastModifiedTime;
+    }
+
+    public void setLastModifiedTime(Date lastModifiedTime) {
+        this.lastModifiedTime = lastModifiedTime;
+    }
+
+    @Override
+    public String toString() {
+        return "QuestionVO{" +
+                "id='" + id + '\'' +
+                ", type=" + type +
+                ", stem='" + stem + '\'' +
+                ", options=" + options +
+                ", blanks=" + blanks +
+                ", answer='" + answer + '\'' +
+                ", keyPoints='" + keyPoints + '\'' +
+                ", analysis='" + analysis + '\'' +
+                ", tags=" + tags +
+                ", knowledgeId=" + knowledgeId +
+                ", lastModifiedTime=" + lastModifiedTime +
+                '}';
+    }
+}

+ 35 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/RecordQuestionVO.java

@@ -0,0 +1,35 @@
+package cn.seecoder.bok.common.vo;
+
+import java.util.List;
+
+/**
+ * @author Kunduin
+ */
+public class RecordQuestionVO {
+    private List<QuestionVO> questions;
+    private List<RecordVO> records;
+
+    public List<QuestionVO> getQuestions() {
+        return questions;
+    }
+
+    public void setQuestions(List<QuestionVO> questions) {
+        this.questions = questions;
+    }
+
+    public List<RecordVO> getRecords() {
+        return records;
+    }
+
+    public void setRecords(List<RecordVO> records) {
+        this.records = records;
+    }
+
+    @Override
+    public String toString() {
+        return "RecordQuestionVO{" +
+                "questions=" + questions +
+                ", records=" + records +
+                '}';
+    }
+}

+ 85 - 0
seecoder-bok-common/src/main/java/cn/seecoder/bok/common/vo/RecordVO.java

@@ -0,0 +1,85 @@
+package cn.seecoder.bok.common.vo;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author Kunduin
+ */
+public class RecordVO {
+    private Long id;
+    private String userId;
+    private String questionId;
+    private String studentAnswer;
+    private Boolean pass;
+    private LocalDateTime startAt;
+    private LocalDateTime finishAt;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getQuestionId() {
+        return questionId;
+    }
+
+    public void setQuestionId(String questionId) {
+        this.questionId = questionId;
+    }
+
+    public String getStudentAnswer() {
+        return studentAnswer;
+    }
+
+    public void setStudentAnswer(String studentAnswer) {
+        this.studentAnswer = studentAnswer;
+    }
+
+    public Boolean getPass() {
+        return pass;
+    }
+
+    public void setPass(Boolean pass) {
+        this.pass = pass;
+    }
+
+    public LocalDateTime getStartAt() {
+        return startAt;
+    }
+
+    public void setStartAt(LocalDateTime startAt) {
+        this.startAt = startAt;
+    }
+
+    public LocalDateTime getFinishAt() {
+        return finishAt;
+    }
+
+    public void setFinishAt(LocalDateTime finishAt) {
+        this.finishAt = finishAt;
+    }
+
+    @Override
+    public String toString() {
+        return "RecordVO{" +
+                "id=" + id +
+                ", userId='" + userId + '\'' +
+                ", questionId='" + questionId + '\'' +
+                ", studentAnswer='" + studentAnswer + '\'' +
+                ", pass=" + pass +
+                ", startAt=" + startAt +
+                ", finishAt=" + finishAt +
+                '}';
+    }
+}