Преглед изворни кода

objective judgement service version0.1.0

UsagiHao пре 5 година
комит
c263fe19d2
26 измењених фајлова са 851 додато и 0 уклоњено
  1. 13 0
      Dockerfile
  2. 29 0
      judgement-client/pom.xml
  3. 9 0
      judgement-client/src/main/java/cn/seecoder/judgement/JudgementApi.java
  4. 98 0
      judgement-client/src/main/java/cn/seecoder/judgement/JudgementClient.java
  5. 34 0
      judgement-client/src/main/java/cn/seecoder/judgement/JudgementException.java
  6. 23 0
      judgement-common/pom.xml
  7. 44 0
      judgement-common/src/main/java/cn/seecoder/judgement/dto/StudentAnswerDTO.java
  8. 28 0
      judgement-common/src/main/java/cn/seecoder/judgement/enums/QuestionType.java
  9. 69 0
      judgement-common/src/main/java/cn/seecoder/judgement/vo/JudgedAnswersVO.java
  10. 31 0
      judgement-common/src/main/java/cn/seecoder/judgement/vo/QuestionVO.java
  11. 60 0
      judgement-common/src/main/java/cn/seecoder/judgement/vo/question/BaseQuestionVO.java
  12. 31 0
      judgement-common/src/main/java/cn/seecoder/judgement/vo/question/ChoiceQuestionVO.java
  13. 32 0
      judgement-common/src/main/java/cn/seecoder/judgement/vo/question/TrueOrFalseQuestionVO.java
  14. 65 0
      judgement-server/pom.xml
  15. 13 0
      judgement-server/src/main/java/cn/seecoder/judgement/JudgementApplication.java
  16. 26 0
      judgement-server/src/main/java/cn/seecoder/judgement/controller/JudgementController.java
  17. 18 0
      judgement-server/src/main/java/cn/seecoder/judgement/response/EmptyResponse.java
  18. 15 0
      judgement-server/src/main/java/cn/seecoder/judgement/response/ErrorResponse.java
  19. 14 0
      judgement-server/src/main/java/cn/seecoder/judgement/response/ResourceResponse.java
  20. 5 0
      judgement-server/src/main/java/cn/seecoder/judgement/response/Response.java
  21. 14 0
      judgement-server/src/main/java/cn/seecoder/judgement/service/JudgementService.java
  22. 19 0
      judgement-server/src/main/java/cn/seecoder/judgement/service/QuestionService.java
  23. 48 0
      judgement-server/src/main/java/cn/seecoder/judgement/service/impl/JudgementServiceImpl.java
  24. 39 0
      judgement-server/src/main/java/cn/seecoder/judgement/service/impl/QuestionServiceImpl.java
  25. 0 0
      judgement-server/src/main/resources/application.yml
  26. 74 0
      pom.xml

+ 13 - 0
Dockerfile

@@ -0,0 +1,13 @@
+FROM maven:3.6.1 AS compile_stage
+ENV PROJECT_NAME app
+ENV WORK_PATH /opt/$PROJECT_NAME
+COPY settings.xml /root/.m2/settings.xml
+COPY . $WORK_PATH
+RUN cd $WORK_PATH && mvn clean install -DskipTests -DfinalName=$PROJECT_NAME
+
+FROM java:8-jre-alpine
+ENV PROJECT_NAME app
+ENV WORK_PATH /opt/$PROJECT_NAME
+WORKDIR /app
+COPY --from=compile_stage $WORK_PATH/seecoder-gitlab-server/target/${PROJECT_NAME}.jar .
+CMD ["sh", "-c", "java -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=n -jar -server /app/${PROJECT_NAME}.jar"]

+ 29 - 0
judgement-client/pom.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>judgement</artifactId>
+        <groupId>cn.seecoder</groupId>
+        <version>0.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>judgement-client</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>cn.seecoder</groupId>
+            <artifactId>judgement-common</artifactId>
+            <version>0.1.0</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+
+</project>

+ 9 - 0
judgement-client/src/main/java/cn/seecoder/judgement/JudgementApi.java

@@ -0,0 +1,9 @@
+package cn.seecoder.judgement;
+
+import cn.seecoder.judgement.vo.JudgedAnswersVO;
+
+import java.util.List;
+
+public interface JudgementApi {
+    JudgedAnswersVO submitQuestionStudentAnswer(List<String> questionsIds, List<Object> answers) throws JudgementException;
+}

+ 98 - 0
judgement-client/src/main/java/cn/seecoder/judgement/JudgementClient.java

@@ -0,0 +1,98 @@
+package cn.seecoder.judgement;
+
+import cn.seecoder.judgement.dto.StudentAnswerDTO;
+import cn.seecoder.judgement.vo.JudgedAnswersVO;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import okhttp3.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import static cn.seecoder.judgement.JudgementException.BIZ_ERROR_CODE;
+
+public class JudgementClient implements JudgementApi {
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+    private OkHttpClient client;
+    private String host;
+    private String token;
+
+    public JudgementClient(String host,String token){
+        this.token=token;
+        this.host=host;
+        this.client=new OkHttpClient.Builder()
+                .connectTimeout(60, TimeUnit.SECONDS)
+                .writeTimeout(60,TimeUnit.SECONDS)
+                .retryOnConnectionFailure(true)
+                .build();
+    }
+
+    private <T> T processResponse(ObjectMapper objectMapper, Response response, Class<T> clazz) throws JudgementException{
+        try {
+            validateResponseStatus(response);
+            String body = response.body().string();
+            JsonNode node = objectMapper.readTree(body);
+            Integer code = Integer.parseInt(node.get("code").asText());
+            String msg = node.get("msg").asText();
+            if (code != 0) {
+                throw new JudgementException(BIZ_ERROR_CODE, msg);
+            }
+            if (clazz == null) {
+                return null;
+            }
+            return objectMapper.treeToValue(node.get("result"), clazz);
+        } catch (IOException e) {
+            logger.error("IOException", e);
+            throw JudgementException.IO_ERROR;
+        }
+    }
+
+    private void validateResponseStatus(Response response) throws JudgementException{
+        if (response.code() == 403) {
+            throw JudgementException.UNAHTORIZATION_ERROR;
+        } else if (response.code() == 500) {
+            throw JudgementException.INTERNAL_SERVER_ERROR;
+        } else if (response.code() != 200) {
+            throw JudgementException.UNKNOWN_ERROR;
+        }
+    }
+
+
+    @Override
+    public JudgedAnswersVO submitQuestionStudentAnswer(List<String> questionsIds, List<Object> answers) throws JudgementException{
+        try {
+            ObjectMapper objectMapper = new ObjectMapper();
+            Request.Builder builder = new Request.Builder()
+                    .addHeader("Authorization", token);
+            Map<String, StudentAnswerDTO.AnswerRecordDTO> records = new HashMap<>();
+            for (int i = 0; i < questionsIds.size(); i++){
+                StudentAnswerDTO.AnswerRecordDTO answerRecordDTO = new StudentAnswerDTO.AnswerRecordDTO();
+                answerRecordDTO.setAnswer(answers.get(i));
+                records.put(questionsIds.get(i), answerRecordDTO);
+            }
+            StudentAnswerDTO studentAnswerDTO=StudentAnswerDTO.builder()
+                    .records(records)
+                    .build();
+            builder = builder
+                    .url(host + "/api/judgement/submit")
+                    .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), objectMapper.writeValueAsString(studentAnswerDTO)));
+            Request request = builder.build();
+            final Call call = client.newCall(request);
+            Response response = call.execute();
+            JudgedAnswersVO judgedAnswersVO = processResponse(objectMapper, response, JudgedAnswersVO.class);
+            return judgedAnswersVO;
+        }catch (JsonProcessingException e) {
+            logger.error("JSON writeValueAsString error", e);
+            throw JudgementException.JSON_ERROR;
+        } catch (IOException e) {
+            logger.error("IOException", e);
+            throw JudgementException.IO_ERROR;
+        }
+    }
+}

+ 34 - 0
judgement-client/src/main/java/cn/seecoder/judgement/JudgementException.java

@@ -0,0 +1,34 @@
+package cn.seecoder.judgement;
+
+public class JudgementException extends Exception{
+
+    public static final JudgementException UNKNOWN_ERROR = new JudgementException("-1", "Unknown Error");
+
+    public static final JudgementException JSON_ERROR = new JudgementException("101", "JSON Processing Error");
+
+    public static final JudgementException IO_ERROR = new JudgementException("102", "Execute IO Error");
+
+    public static final JudgementException UNAHTORIZATION_ERROR = new JudgementException("403", "Unauthorization Error");
+
+    public static final JudgementException NOT_FOUND_ERROR = new JudgementException("404", "Not Found Error");
+
+    public static final JudgementException INTERNAL_SERVER_ERROR = new JudgementException("500", "Internal Server Error");
+
+    public static final String BIZ_ERROR_CODE = "100";
+
+    private String code;
+
+    private String msg;
+
+    public JudgementException(String code, String msg) {
+        super(msg);
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public JudgementException(String code, String msg, Throwable cause) {
+        super(msg, cause);
+        this.code = code;
+        this.msg = msg;
+    }
+}

+ 23 - 0
judgement-common/pom.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>judgement</artifactId>
+        <groupId>cn.seecoder</groupId>
+        <version>0.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>judgement-common</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.2.5</version>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 44 - 0
judgement-common/src/main/java/cn/seecoder/judgement/dto/StudentAnswerDTO.java

@@ -0,0 +1,44 @@
+package cn.seecoder.judgement.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Past;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.Map;
+
+/**
+ * @author cst
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class StudentAnswerDTO implements Serializable {
+    private static final long serialVersionUID = -5846899734313166305L;
+//    @NotEmpty(message = "回答不能为空")
+//    private Map<String, Object> answers;
+
+    @NotNull(message = "缺少做题记录")
+    @Valid
+    private Map<String, AnswerRecordDTO> records;
+
+    @Data
+    public static class AnswerRecordDTO {
+        @NotNull(message = "缺少答案")
+        private Object answer;
+
+        @NotNull(message = "缺少做题开始时间")
+        @Past(message = "开始时间错误")
+        private LocalDateTime startAt;
+
+        @NotNull(message = "缺少做题结束时间")
+        @Past(message = "结束时间错误")
+        private LocalDateTime endAt;
+    }
+}

+ 28 - 0
judgement-common/src/main/java/cn/seecoder/judgement/enums/QuestionType.java

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

+ 69 - 0
judgement-common/src/main/java/cn/seecoder/judgement/vo/JudgedAnswersVO.java

@@ -0,0 +1,69 @@
+package cn.seecoder.judgement.vo;
+
+import cn.seecoder.judgement.dto.StudentAnswerDTO;
+import cn.seecoder.judgement.vo.question.BaseQuestionVO;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NonNull;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author cst
+ */
+@Data
+public class JudgedAnswersVO {
+    /**
+     * 回答情况
+     */
+    private Map<String, QuestionStudentAnswerInfo> answers;
+
+    /**
+     * 提交时间
+     */
+    private LocalDateTime submitAt;
+    /**
+     * 已提交次数
+     */
+    private Integer submitNumber;
+    /**
+     * 自动化判分的分数
+     */
+    private BigDecimal score;
+
+    public JudgedAnswersVO(Map<String, QuestionStudentAnswerInfo> answers){
+        this.answers = answers;
+    }
+
+    public JudgedAnswersVO(@NonNull Map<String, StudentAnswerDTO.AnswerRecordDTO> records, List<BaseQuestionVO> baseQuestionVOList) {
+      /*  this.quizId = quizStudentAnswer.getQuiz().getId();
+        this.quizName = quizStudentAnswer.getQuiz().getName();
+        this.studentId = quizStudentAnswer.getStudent().getId();
+        this.studentName = quizStudentAnswer.getStudent().getName();*/
+
+        this.answers = baseQuestionVOList.parallelStream()
+                .map(baseQuestionVO -> {
+                    String questionId = baseQuestionVO.getId();
+                    StudentAnswerDTO.AnswerRecordDTO questionRecord = records.get(questionId);
+                    Object questionStudentAnswer = questionRecord.getAnswer();
+                    return QuestionStudentAnswerInfo.of(questionId, questionStudentAnswer, baseQuestionVO.pass(questionStudentAnswer));
+                })
+                .collect(Collectors.toMap(QuestionStudentAnswerInfo::getQuestionId, questionStudentAnswerInfo -> questionStudentAnswerInfo));
+
+/*        this.submitAt = quizStudentAnswer.getSubmitAt();
+        this.submitNumber = quizStudentAnswer.getSubmitNumber();
+        this.score = quizStudentAnswer.getScore();*/
+    }
+
+    @Data
+    @AllArgsConstructor(staticName = "of")
+    public static class QuestionStudentAnswerInfo {
+        private String questionId;
+        private Object answer;
+        private Boolean correct;
+    }
+}

+ 31 - 0
judgement-common/src/main/java/cn/seecoder/judgement/vo/QuestionVO.java

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

+ 60 - 0
judgement-common/src/main/java/cn/seecoder/judgement/vo/question/BaseQuestionVO.java

@@ -0,0 +1,60 @@
+package cn.seecoder.judgement.vo.question;
+
+import cn.seecoder.bok.common.QuestionType;
+import cn.seecoder.bok.common.vo.QuestionVO;
+import lombok.Data;
+import lombok.NonNull;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author XuShengTao
+ * <p>
+ * updated by cst
+ */
+@Data
+public abstract class BaseQuestionVO {
+    protected String id;
+    protected QuestionType type;
+    protected Map<String, String> options;
+    protected String stem;
+    protected String analysis;
+    protected String keyPoints;
+    protected List<String> tags;
+    protected List<String> knowledgeId;
+    protected Date lastModified;
+
+    public static BaseQuestionVO convertBokQuestionToVO(@NonNull QuestionVO questionVO, boolean withAnswer) {
+        switch (questionVO.getType()) {
+            case choice:
+                return new ChoiceQuestionVO(questionVO, withAnswer);
+            case true_false:
+                return new TrueOrFalseQuestionVO(questionVO, withAnswer);
+//            case multiple_choice:
+//                return new MultipleChoiceQuestionVO(questionVO, withAnswer);
+            default:
+                return null;
+        }
+    }
+
+    protected BaseQuestionVO(QuestionVO questionVO) {
+        this.id = questionVO.getId();
+        this.type = questionVO.getType();
+        this.options = questionVO.getOptions();
+        this.stem = questionVO.getStem();
+        this.keyPoints = questionVO.getKeyPoints();
+        this.tags = questionVO.getTags();
+        this.knowledgeId = questionVO.getKnowledgeId();
+        this.lastModified = questionVO.getLastModifiedTime();
+    }
+
+    /**
+     * 检查答案是否正确
+     *
+     * @param answer 答案
+     * @return 是否正确
+     */
+    public abstract boolean pass(Object answer);
+}

+ 31 - 0
judgement-common/src/main/java/cn/seecoder/judgement/vo/question/ChoiceQuestionVO.java

@@ -0,0 +1,31 @@
+package cn.seecoder.judgement.vo.question;
+
+import cn.seecoder.bok.common.vo.QuestionVO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class ChoiceQuestionVO extends BaseQuestionVO {
+    private String answer;
+
+    public ChoiceQuestionVO(@NonNull QuestionVO questionVO, boolean withAnswer) {
+        super(questionVO);
+
+        if (withAnswer) {
+            this.answer = questionVO.getAnswer();
+            this.analysis = questionVO.getAnalysis();
+        }
+    }
+
+    @Override
+    public boolean pass(Object answer) {
+        return this.answer.equals(String.valueOf(answer));
+    }
+}

+ 32 - 0
judgement-common/src/main/java/cn/seecoder/judgement/vo/question/TrueOrFalseQuestionVO.java

@@ -0,0 +1,32 @@
+package cn.seecoder.judgement.vo.question;
+
+import cn.hutool.core.convert.Convert;
+import cn.seecoder.bok.common.vo.QuestionVO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NonNull;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class TrueOrFalseQuestionVO extends BaseQuestionVO {
+    private Boolean answer;
+
+    @Override
+    public boolean pass(Object answer) {
+        return this.answer.equals(Convert.toBool(answer));
+    }
+
+    public TrueOrFalseQuestionVO(@NonNull QuestionVO questionVO, boolean withAnswer) {
+        super(questionVO);
+
+        if (withAnswer) {
+            this.answer = Boolean.valueOf(questionVO.getAnswer());
+            this.analysis = questionVO.getAnalysis();
+        }
+    }
+}

+ 65 - 0
judgement-server/pom.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>judgement</artifactId>
+        <groupId>cn.seecoder</groupId>
+        <version>0.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>judgement-server</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+            <version>2.8.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.8.6</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>28.1-jre</version>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>cn.seecoder</groupId>
+            <artifactId>judgement-common</artifactId>
+            <version>0.1.0</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 13 - 0
judgement-server/src/main/java/cn/seecoder/judgement/JudgementApplication.java

@@ -0,0 +1,13 @@
+package cn.seecoder.judgement;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class JudgementApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(JudgementApplication.class, args);
+    }
+
+}

+ 26 - 0
judgement-server/src/main/java/cn/seecoder/judgement/controller/JudgementController.java

@@ -0,0 +1,26 @@
+package cn.seecoder.judgement.controller;
+
+import cn.seecoder.judgement.dto.StudentAnswerDTO;
+import cn.seecoder.judgement.service.JudgementService;
+import cn.seecoder.judgement.response.ResourceResponse;
+import cn.seecoder.judgement.response.Response;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * author:hr
+ */
+@RestController
+@RequestMapping("api/judgement")
+public class JudgementController {
+    private final JudgementService judgementService;
+
+    public JudgementController(JudgementService judgementService){
+        this.judgementService = judgementService;
+    }
+
+    @RequestMapping("/submit")
+    public Response submitQuestionsStudentAnswer(@Validated @RequestBody StudentAnswerDTO studentAnswerDTO) {
+        return new ResourceResponse(judgementService.submitQuestionsStudentAnswer(studentAnswerDTO));
+    }
+}

+ 18 - 0
judgement-server/src/main/java/cn/seecoder/judgement/response/EmptyResponse.java

@@ -0,0 +1,18 @@
+package cn.seecoder.judgement.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class EmptyResponse implements Response{
+    @JsonProperty("err")
+    private int error;
+
+    public EmptyResponse() {
+        this.error = 0;
+    }
+
+    protected EmptyResponse(int error) {
+        this.error = error;
+    }
+}

+ 15 - 0
judgement-server/src/main/java/cn/seecoder/judgement/response/ErrorResponse.java

@@ -0,0 +1,15 @@
+package cn.seecoder.judgement.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class ErrorResponse extends EmptyResponse{
+    @JsonProperty("msg")
+    private String message;
+
+    public ErrorResponse(int err, String message) {
+        super(err);
+        this.message = message;
+    }
+}

+ 14 - 0
judgement-server/src/main/java/cn/seecoder/judgement/response/ResourceResponse.java

@@ -0,0 +1,14 @@
+package cn.seecoder.judgement.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class ResourceResponse extends EmptyResponse{
+    @JsonProperty("res")
+    private Object resource;
+
+    public ResourceResponse(Object resource) {
+        this.resource = resource;
+    }
+}

+ 5 - 0
judgement-server/src/main/java/cn/seecoder/judgement/response/Response.java

@@ -0,0 +1,5 @@
+package cn.seecoder.judgement.response;
+
+public interface Response {
+    public int getError();
+}

+ 14 - 0
judgement-server/src/main/java/cn/seecoder/judgement/service/JudgementService.java

@@ -0,0 +1,14 @@
+package cn.seecoder.judgement.service;
+
+import cn.seecoder.judgement.dto.StudentAnswerDTO;
+import cn.seecoder.judgement.vo.JudgedAnswersVO;
+
+public interface JudgementService {
+    /**
+     * 提交测试作答
+     *
+     * @param studentAnswerDTO
+     * @return
+     */
+    JudgedAnswersVO submitQuestionsStudentAnswer(StudentAnswerDTO studentAnswerDTO);
+}

+ 19 - 0
judgement-server/src/main/java/cn/seecoder/judgement/service/QuestionService.java

@@ -0,0 +1,19 @@
+package cn.seecoder.judgement.service;
+
+import cn.seecoder.judgement.vo.question.BaseQuestionVO;
+
+import java.util.List;
+
+/**
+ * @author xst
+ * <p>
+ * updated by cst
+ */
+public interface QuestionService {
+    /**
+     * 根据测试获取题目(无需权限检验)
+     *
+     * @param questionIds@return
+     */
+    List<BaseQuestionVO> getQuestionsByIds(List<String> questionIds);
+}

+ 48 - 0
judgement-server/src/main/java/cn/seecoder/judgement/service/impl/JudgementServiceImpl.java

@@ -0,0 +1,48 @@
+package cn.seecoder.judgement.service.impl;
+
+import cn.seecoder.judgement.dto.StudentAnswerDTO;
+import cn.seecoder.judgement.service.JudgementService;
+import cn.seecoder.judgement.service.QuestionService;
+import cn.seecoder.judgement.vo.JudgedAnswersVO;
+import cn.seecoder.judgement.vo.question.BaseQuestionVO;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * author:hr
+ */
+
+@Service
+public class JudgementServiceImpl implements JudgementService {
+    private final QuestionService questionService;
+
+    public JudgementServiceImpl(QuestionService questionService){
+        this.questionService = questionService;
+    }
+    @Override
+    public JudgedAnswersVO submitQuestionsStudentAnswer(StudentAnswerDTO studentAnswerDTO) {
+        Map<String, StudentAnswerDTO.AnswerRecordDTO> records = studentAnswerDTO.getRecords();
+        List<String> questionIds = new ArrayList<>(records.keySet());
+        List<BaseQuestionVO> questionVOList = questionService.getQuestionsByIds(questionIds);
+
+        AtomicInteger correct = new AtomicInteger();
+
+        Map<String, JudgedAnswersVO.QuestionStudentAnswerInfo> judgedAnswers = new HashMap<>();
+        questionVOList.forEach(baseQuestionVO -> {
+            String questionId = baseQuestionVO.getId();
+            Object answer = records.get(questionId).getAnswer();
+            boolean isTrue = baseQuestionVO.pass(answer);
+            if (isTrue) {
+                correct.getAndIncrement();
+            }
+            judgedAnswers.put(questionId, JudgedAnswersVO.QuestionStudentAnswerInfo.of(questionId, answer, isTrue));
+        });
+
+        return new JudgedAnswersVO(judgedAnswers);
+    }
+}

+ 39 - 0
judgement-server/src/main/java/cn/seecoder/judgement/service/impl/QuestionServiceImpl.java

@@ -0,0 +1,39 @@
+package cn.seecoder.judgement.service.impl;
+
+import cn.seecoder.bok.client.SeecoderBokClient;
+import cn.seecoder.judgement.service.QuestionService;
+import cn.seecoder.judgement.vo.question.BaseQuestionVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+/*
+public class QuestionServiceImpl implements QuestionService {
+    private final QuestionQueryApi questionQueryApi;
+
+    @Autowired
+    public QuestionServiceImpl(QuestionQueryApi questionQueryApi){
+        this.questionQueryApi = questionQueryApi;
+    }
+
+    @Override
+    public List<BaseQuestionVO> getQuestionsByIds(List<String> questionIds) {
+        return questionQueryApi.getQuestionListById(questionIds)
+                .stream()
+                .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true))
+                .collect(Collectors.toList());    }
+}
+*/
+public class QuestionServiceImpl implements QuestionService {
+    @Override
+    public List<BaseQuestionVO> getQuestionsByIds(List<String> questionIds) {
+        SeecoderBokClient seecoderBokClient = new SeecoderBokClient("http://bok.seecoder.cn", 0);
+        List<BaseQuestionVO> list1 = seecoderBokClient.getQuestionListByIds(questionIds).stream()
+                .map(bokQuestion -> BaseQuestionVO.convertBokQuestionToVO(bokQuestion, true))
+                .collect(Collectors.toList());
+        return list1;
+    }
+}

+ 0 - 0
judgement-server/src/main/resources/application.yml


+ 74 - 0
pom.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>pom</packaging>
+    <modules>
+        <module>judgement-client</module>
+        <module>judgement-server</module>
+        <module>judgement-common</module>
+    </modules>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.4.1</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>cn.seecoder</groupId>
+    <artifactId>judgement</artifactId>
+    <version>0.1.0</version>
+    <name>judgement</name>
+    <description>Demo project for Spring Boot</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <judgement-version>0.1.0</judgement-version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.seecoder</groupId>
+            <artifactId>seecoder-bok-client</artifactId>
+            <version>0.0.2</version>
+        </dependency>
+    </dependencies>
+    <distributionManagement>
+        <repository>
+            <id>nexus</id>
+            <url>http://nexus.seecoder.cn/repository/maven-releases/</url>
+        </repository>
+    </distributionManagement>
+</project>