Pārlūkot izejas kodu

add converter module.

deason 7 gadi atpakaļ
vecāks
revīzija
a617597517
25 mainītis faili ar 2439 papildinājumiem un 0 dzēšanām
  1. 46 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/Constants.java
  2. 43 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/PolicyType.java
  3. 125 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/QuesStructType.java
  4. 88 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/Result.java
  5. 54 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PaperAnswerDto.java
  6. 93 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PaperDetailDto.java
  7. 97 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PaperDto.java
  8. 81 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PolicyDto.java
  9. 45 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PolicyWeightDto.java
  10. 189 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/QuestionDto.java
  11. 36 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/QuestionOptionDto.java
  12. 112 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/SimpleQuestionDto.java
  13. 35 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/AnswerVo.java
  14. 45 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/BlockVo.java
  15. 35 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/BodyVo.java
  16. 44 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/OptionVo.java
  17. 68 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/PaperDetailVo.java
  18. 91 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/PaperVo.java
  19. 48 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/ParamVo.java
  20. 113 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/QuestionVo.java
  21. 35 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/SectionVo.java
  22. 226 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/Cryptogram.java
  23. 422 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/FileUtil.java
  24. 224 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/JsonMapper.java
  25. 44 0
      examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/NumUtil.java

+ 46 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/Constants.java

@@ -0,0 +1,46 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model;
+
+public interface Constants {
+
+    String ZIP = ".zip";
+
+    String JSON = ".json";
+
+    String TEXT = "text";
+
+    String WRONG = "错误";
+
+    String WRONG_NUM = "1";
+
+    String CORRECT = "正确";
+
+    String CORRECT_NUM = "2";
+
+    String IMAGE = "image";
+
+    String AUDIO = "audio";
+
+    String TAG_IMG = " <img style=\"%s\" src=\"%s\" /> ";
+
+    String FILE_PAPER = "paper.json";//试卷结构文件
+
+    String FILE_POLICY = "policy.json";//课程单独组卷配置文件
+
+    String FILE_ANSWER = "answer.json";//答案内容文件
+
+    short[] paperHeaders = {1, 1};//试卷头信息
+
+    short[] paperAesHeaders = {1, 2};//试卷加密头信息
+
+    short[] answerHeaders = {2, 1};//答案头信息
+
+    short[] answerAesHeaders = {2, 2};//答案加密头信息
+
+}

+ 43 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/PolicyType.java

@@ -0,0 +1,43 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:09.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model;
+
+/**
+ * 调卷规则类型
+ */
+public enum PolicyType {
+
+    Random("随机抽卷", 1), BlueMap("蓝图组卷", 2);
+
+    private String name;
+
+    private int id;
+
+    PolicyType(String name, int id) {
+        this.name = name;
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public static PolicyType findById(int id) {
+        for (PolicyType type : PolicyType.values()) {
+            if (type.id == id) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+}

+ 125 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/QuesStructType.java

@@ -0,0 +1,125 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:09.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model;
+
+public enum QuesStructType {
+    SINGLE_ANSWER_QUESTION(1, "单选题", true, false),
+    MULTIPLE_ANSWER_QUESTION(2, "多选题", true, false),
+    BOOL_ANSWER_QUESTION(3, "判断题", true, false),
+    FILL_BLANK_QUESTION(4, "填空题", false, false),
+    TEXT_ANSWER_QUESTION(5, "问答题", false, false),
+    NESTED_ANSWER_QUESTION(6, "套题", false, true);
+
+    private int id;
+    private String name;
+    private boolean objective;//是否是客观题
+    private boolean combine;//是否是组合题
+
+    QuesStructType(int id, String name, boolean objective, boolean combine) {
+        this.id = id;
+        this.name = name;
+        this.objective = objective;
+        this.combine = combine;
+    }
+
+    /**
+     * 通过ID获取试题类型
+     */
+    public static QuesStructType getTypeById(Integer id) {
+        if (id == null) {
+            return null;
+        }
+        for (QuesStructType type : QuesStructType.values()) {
+            if (type.getId() == id) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 通过名称获取试题类型
+     */
+    public static QuesStructType getTypeByName(String name) {
+        if (name == null) {
+            return null;
+        }
+        for (QuesStructType type : QuesStructType.values()) {
+            if (type.getName().equals(name)) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 根据ID判断是否为客观题
+     */
+    public static boolean isObjective(Integer id) {
+        if (id == null) {
+            return false;
+        }
+        for (QuesStructType type : QuesStructType.values()) {
+            if (type.getId() == id) {
+                return type.objective;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 根据名称判断是否为客观题
+     */
+    public static boolean isObjective(String name) {
+        if (name == null) {
+            return false;
+        }
+        for (QuesStructType type : QuesStructType.values()) {
+            if (type.getName().equals(name)) {
+                return type.objective;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 根据ID判断是否为套题
+     */
+    public static boolean isCombine(Integer id) {
+        if (id == null) {
+            return false;
+        }
+        for (QuesStructType type : QuesStructType.values()) {
+            if (type.getId() == id) {
+                return type.combine;
+            }
+        }
+        return false;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isObjective() {
+        return objective;
+    }
+
+    public boolean isCombine() {
+        return combine;
+    }
+
+    public String toString() {
+        return getName();
+    }
+
+}

+ 88 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/Result.java

@@ -0,0 +1,88 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.io.Serializable;
+
+public class Result<T> implements Serializable {
+    private static final long serialVersionUID = 1L;
+    private boolean success;//是否成功
+    private String message;//描述信息
+    private T data;//结果数据
+
+    public Result() {
+
+    }
+
+    public Result(boolean success, String message, T data) {
+        this.success = success;
+        this.message = message;
+        this.data = data;
+    }
+
+    /**
+     * 成功结果
+     */
+    public Result success(T data) {
+        this.success = true;
+        this.data = data;
+        return this;
+    }
+
+    public Result success() {
+        this.success = true;
+        return this;
+    }
+
+    /**
+     * 错误结果
+     */
+    public Result error(String message) {
+        this.success = false;
+        this.message = message;
+        return this;
+    }
+
+    public Result error() {
+        this.success = false;
+        return this;
+    }
+
+    public boolean isSuccess() {
+        return success;
+    }
+
+    public void setSuccess(boolean success) {
+        this.success = success;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
+    }
+
+}

+ 54 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PaperAnswerDto.java

@@ -0,0 +1,54 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.dps;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 试卷答案
+ */
+public class PaperAnswerDto implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String id;//注:这里的Id对应试卷代码
+    private List<SimpleQuestionDto> questions;//大题列表
+
+    public PaperAnswerDto(String id) {
+        this.id = id;
+    }
+
+    public void addQuestion(SimpleQuestionDto question) {
+        if (questions == null) {
+            questions = new ArrayList<>();
+        }
+        questions.add(question);
+    }
+
+
+    public PaperAnswerDto() {
+
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public List<SimpleQuestionDto> getQuestions() {
+        return questions;
+    }
+
+    public void setQuestions(List<SimpleQuestionDto> questions) {
+        this.questions = questions;
+    }
+
+}

+ 93 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PaperDetailDto.java

@@ -0,0 +1,93 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.dps;
+
+import cn.com.qmth.examcloud.core.questions.base.converter.model.platform.PaperDetailVo;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 试卷的大题项
+ */
+public class PaperDetailDto implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private Integer number;//序号
+    private String name;//题型名称
+    private Double totalScore;//总分
+    private Integer questionCount;//题数量
+    private List<QuestionDto> questions;//试题列表
+
+    public PaperDetailDto(PaperDetailVo detail) {
+        this.number = detail.getNumber();
+        this.name = detail.getName();
+        this.totalScore = detail.getTotalScore();
+        this.questionCount = detail.getQuestionCount();
+    }
+
+    public PaperDetailDto(String name) {
+        this.name = name;
+    }
+
+    public PaperDetailDto() {
+
+    }
+
+    public void addQuestion(QuestionDto question) {
+        if (questions == null) {
+            questions = new ArrayList<>();
+        }
+        questions.add(question);
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Integer getQuestionCount() {
+        //return questionCount;
+        return getQuestions().size();
+    }
+
+    public void setQuestionCount(Integer questionCount) {
+        this.questionCount = questionCount;
+    }
+
+    public List<QuestionDto> getQuestions() {
+        if (questions == null) {
+            return new ArrayList<>();
+        }
+        return questions;
+    }
+
+    public void setQuestions(List<QuestionDto> questions) {
+        this.questions = questions;
+    }
+
+}

+ 97 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PaperDto.java

@@ -0,0 +1,97 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.dps;
+
+import cn.com.qmth.examcloud.core.questions.base.converter.model.platform.PaperVo;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 试卷
+ */
+public class PaperDto implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String id;//注:这里的Id对应试卷代码
+    private String name;//试卷名称
+    private String courseCode;//课程代码
+    private String paperType;//试卷类型
+    private Double totalScore;//试卷总分
+    private Integer detailCount;//大题数量
+    private List<PaperDetailDto> details;//大题列表
+
+    public PaperDto(PaperVo paper) {
+        this.id = paper.getId();
+        this.name = paper.getName();
+        this.courseCode = paper.getCourseCode();
+        //this.paperType = "A";
+        this.totalScore = paper.getTotalScore();
+        this.detailCount = paper.getDetailCount();
+    }
+
+    public PaperDto() {
+
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Integer getDetailCount() {
+        return detailCount;
+    }
+
+    public void setDetailCount(Integer detailCount) {
+        this.detailCount = detailCount;
+    }
+
+    public List<PaperDetailDto> getDetails() {
+        return details;
+    }
+
+    public void setDetails(List<PaperDetailDto> details) {
+        this.details = details;
+    }
+
+}

+ 81 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PolicyDto.java

@@ -0,0 +1,81 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.dps;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 抽卷规则配置
+ */
+public class PolicyDto implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String courseCode;//课程代码
+    private String paperType;//试卷类型
+    private Integer type;//规则类型
+    private Boolean objectiveShuffle;//是否主观题乱序
+    private Boolean optionShuffle;//是否选项乱序
+    private List<PolicyWeightDto> weights;//试卷权重列表
+
+    public void addWeight(PolicyWeightDto weight) {
+        if (weights == null) {
+            weights = new ArrayList<>();
+        }
+        weights.add(weight);
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    public Boolean getObjectiveShuffle() {
+        return objectiveShuffle;
+    }
+
+    public void setObjectiveShuffle(Boolean objectiveShuffle) {
+        this.objectiveShuffle = objectiveShuffle;
+    }
+
+    public Boolean getOptionShuffle() {
+        return optionShuffle;
+    }
+
+    public void setOptionShuffle(Boolean optionShuffle) {
+        this.optionShuffle = optionShuffle;
+    }
+
+    public List<PolicyWeightDto> getWeights() {
+        return weights;
+    }
+
+    public void setWeights(List<PolicyWeightDto> weights) {
+        this.weights = weights;
+    }
+
+}

+ 45 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/PolicyWeightDto.java

@@ -0,0 +1,45 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:09.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.dps;
+
+import java.io.Serializable;
+
+/**
+ * 试卷权重规则
+ */
+public class PolicyWeightDto implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String paperId;//注:这里的Id对应试卷代码
+    private Double weight;//权重值
+
+    public PolicyWeightDto(String paperId, Double weight) {
+        this.paperId = paperId;
+        this.weight = weight;
+    }
+
+    public PolicyWeightDto() {
+
+    }
+
+    public String getPaperId() {
+        return paperId;
+    }
+
+    public void setPaperId(String paperId) {
+        this.paperId = paperId;
+    }
+
+    public Double getWeight() {
+        return weight;
+    }
+
+    public void setWeight(Double weight) {
+        this.weight = weight;
+    }
+
+}

+ 189 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/QuestionDto.java

@@ -0,0 +1,189 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.dps;
+
+import cn.com.qmth.examcloud.core.questions.base.converter.model.Constants;
+import cn.com.qmth.examcloud.core.questions.base.converter.model.platform.*;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 试题
+ */
+public class QuestionDto implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String id;
+    private Integer number;//题序号
+    private Double score;//满分
+    private Integer structType;//题类型
+    private Boolean objective;//是否主观题
+    private String body;//题干
+    private String answer;//正确答案
+    private List<QuestionOptionDto> options;//题选项列表
+    private List<QuestionDto> subQuestions;//套题下的子题列表
+
+    public QuestionDto(QuestionVo question) {
+        this.id = question.getId();
+        this.number = question.getNumber();
+        this.score = question.getScore() != null ? question.getScore() : 1d;
+        this.structType = question.getStructType();
+        this.objective = question.getObjective();
+
+        //转换题干内容
+        StringBuilder bodyStr = new StringBuilder();
+        BodyVo body = question.getBody();
+        if (body != null && body.hasSection()) {
+            for (SectionVo section : body.getSections()) {
+                if (section.hasBlock()) {
+                    for (BlockVo block : section.getBlocks()) {
+                        if (Constants.TEXT.equals(block.getType())) {
+                            bodyStr.append(block.getValue());
+                        } else if (Constants.IMAGE.equals(block.getType())) {
+                            ParamVo param = block.getParam();
+                            if (param != null) {
+                                bodyStr.append(String.format(Constants.TAG_IMG, param.getStyle(), block.getValue()));
+                            } else {
+                                bodyStr.append(String.format(Constants.TAG_IMG, "", block.getValue()));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        this.body = bodyStr.toString();
+
+        //转换选项内容
+        if (question.hasOption()) {
+            for (OptionVo option : question.getOptions()) {
+                StringBuilder optionBodyStr = new StringBuilder();
+                BodyVo optionBody = option.getBody();
+                if (optionBody != null && optionBody.hasSection()) {
+                    for (SectionVo section : optionBody.getSections()) {
+                        if (section.hasBlock()) {
+                            for (BlockVo block : section.getBlocks()) {
+                                if (Constants.TEXT.equals(block.getType())) {
+                                    optionBodyStr.append(block.getValue());
+                                } else if (Constants.IMAGE.equals(block.getType())) {
+                                    ParamVo param = block.getParam();
+                                    if (param != null) {
+                                        optionBodyStr.append(String.format(Constants.TAG_IMG, param.getStyle(), block.getValue()));
+                                    } else {
+                                        optionBodyStr.append(String.format(Constants.TAG_IMG, "", block.getValue()));
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                QuestionOptionDto newOption = new QuestionOptionDto();
+                newOption.setNumber(option.getNumber());
+                newOption.setBody(optionBodyStr.toString());
+                this.addOption(newOption);
+            }
+        }
+    }
+
+    public void addOption(QuestionOptionDto option) {
+        if (options == null) {
+            options = new ArrayList<>();
+        }
+        options.add(option);
+    }
+
+    public void addSubQuestion(QuestionDto question) {
+        if (subQuestions == null) {
+            subQuestions = new ArrayList<>();
+        }
+        subQuestions.add(question);
+    }
+
+    public QuestionDto() {
+
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public Double getScore() {
+        return score;
+    }
+
+    public void setScore(Double score) {
+        this.score = score;
+    }
+
+    public Integer getStructType() {
+        return structType;
+    }
+
+    public void setStructType(Integer structType) {
+        this.structType = structType;
+    }
+
+    public Boolean getObjective() {
+        return objective;
+    }
+
+    public void setObjective(Boolean objective) {
+        this.objective = objective;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public void setBody(String body) {
+        this.body = body;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    public List<QuestionOptionDto> getOptions() {
+        if (options == null) {
+            return new ArrayList<>();
+        }
+        return options;
+    }
+
+    public void setOptions(List<QuestionOptionDto> options) {
+        this.options = options;
+    }
+
+    public List<QuestionDto> getSubQuestions() {
+        if (subQuestions == null) {
+            return new ArrayList<>();
+        }
+        return subQuestions;
+    }
+
+    public void setSubQuestions(List<QuestionDto> subQuestions) {
+        this.subQuestions = subQuestions;
+    }
+
+}

+ 36 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/QuestionOptionDto.java

@@ -0,0 +1,36 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.dps;
+
+import java.io.Serializable;
+
+/**
+ * 试题的答案项
+ */
+public class QuestionOptionDto implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private Integer number;//答案序号
+    private String body;//答案内容
+
+    public Integer getNumber() {
+        return number != null ? number : 1;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public String getBody() {
+        return body;
+    }
+
+    public void setBody(String body) {
+        this.body = body;
+    }
+
+}

+ 112 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/dps/SimpleQuestionDto.java

@@ -0,0 +1,112 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.dps;
+
+import cn.com.qmth.examcloud.core.questions.base.converter.model.Constants;
+import cn.com.qmth.examcloud.core.questions.base.converter.model.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.base.converter.model.platform.AnswerVo;
+import cn.com.qmth.examcloud.core.questions.base.converter.model.platform.BlockVo;
+import cn.com.qmth.examcloud.core.questions.base.converter.model.platform.QuestionVo;
+import cn.com.qmth.examcloud.core.questions.base.converter.model.platform.SectionVo;
+import cn.com.qmth.examcloud.core.questions.base.converter.utils.NumUtil;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 试题
+ */
+public class SimpleQuestionDto implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String id;
+    private Integer number;//题号
+    private String answer;//正确答案
+    private List<SimpleQuestionDto> subQuestions;//套题下的子题列表
+
+    public SimpleQuestionDto(QuestionVo question) {
+        this.id = question.getId();
+        QuesStructType type = QuesStructType.getTypeById(question.getStructType());
+        if (type == null) {
+            throw new IllegalArgumentException("Question struct type is wrong");
+        }
+        //转换答案内容
+        StringBuilder answerStr = new StringBuilder();
+        AnswerVo answer = question.getAnswer();
+        if (answer != null && answer.hasSection()) {
+            for (SectionVo section : answer.getSections()) {
+                if (section.hasBlock()) {
+                    for (BlockVo block : section.getBlocks()) {
+                        if (Constants.TEXT.equals(block.getType())) {
+                            String value = block.getValue();
+                            if (type.isObjective()) {
+                                if (QuesStructType.SINGLE_ANSWER_QUESTION.getId() == type.getId()) {
+                                    value = NumUtil.toNumber(value);
+                                } else if (QuesStructType.MULTIPLE_ANSWER_QUESTION.getId() == type.getId()) {
+                                    value = NumUtil.toNumber(value);
+                                } else if (QuesStructType.BOOL_ANSWER_QUESTION.getId() == type.getId()) {
+                                    if (Constants.CORRECT.equals(value)) {
+                                        value = Constants.CORRECT_NUM;
+                                    } else {
+                                        value = Constants.WRONG_NUM;
+                                    }
+                                }
+                            }
+                            answerStr.append(value);
+                        }
+                    }
+                }
+            }
+        }
+        this.answer = answerStr.toString();
+    }
+
+    public void addSubQuestion(SimpleQuestionDto question) {
+        if (subQuestions == null) {
+            subQuestions = new ArrayList<>();
+        }
+        subQuestions.add(question);
+    }
+
+    public SimpleQuestionDto() {
+
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public String getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(String answer) {
+        this.answer = answer;
+    }
+
+    public List<SimpleQuestionDto> getSubQuestions() {
+        return subQuestions;
+    }
+
+    public void setSubQuestions(List<SimpleQuestionDto> subQuestions) {
+        this.subQuestions = subQuestions;
+    }
+
+}

+ 35 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/AnswerVo.java

@@ -0,0 +1,35 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.platform;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 答案信息
+ */
+public class AnswerVo implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private List<SectionVo> sections;//分类列表
+
+    public boolean hasSection() {
+        if (sections != null && sections.size() > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    public List<SectionVo> getSections() {
+        return sections;
+    }
+
+    public void setSections(List<SectionVo> sections) {
+        this.sections = sections;
+    }
+
+}

+ 45 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/BlockVo.java

@@ -0,0 +1,45 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.platform;
+
+import java.io.Serializable;
+
+/**
+ * 模块信息
+ */
+public class BlockVo implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String type;//模块类型
+    private String value;//模块内容
+    private ParamVo param;//附属参数
+
+    public String getType() {
+        return type != null ? type : "";
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getValue() {
+        return value != null ? value : "";
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public ParamVo getParam() {
+        return param;
+    }
+
+    public void setParam(ParamVo param) {
+        this.param = param;
+    }
+
+}

+ 35 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/BodyVo.java

@@ -0,0 +1,35 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.platform;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 题干信息
+ */
+public class BodyVo implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private List<SectionVo> sections;//分类列表
+
+    public boolean hasSection() {
+        if (sections != null && sections.size() > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    public List<SectionVo> getSections() {
+        return sections;
+    }
+
+    public void setSections(List<SectionVo> sections) {
+        this.sections = sections;
+    }
+
+}

+ 44 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/OptionVo.java

@@ -0,0 +1,44 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.platform;
+
+import java.io.Serializable;
+
+/**
+ * 选项信息
+ */
+public class OptionVo implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private Integer number;//题序号
+    private Boolean correct;//是否正确答案
+    private BodyVo body;//内容
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public Boolean getCorrect() {
+        return correct;
+    }
+
+    public void setCorrect(Boolean correct) {
+        this.correct = correct;
+    }
+
+    public BodyVo getBody() {
+        return body;
+    }
+
+    public void setBody(BodyVo body) {
+        this.body = body;
+    }
+}

+ 68 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/PaperDetailVo.java

@@ -0,0 +1,68 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.platform;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 试卷的大题项
+ */
+public class PaperDetailVo implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private Integer number;//序号
+    private String name;//题型名称
+    private Double totalScore;//总分
+    private Integer questionCount;//题数量
+    private List<QuestionVo> questions;//试题列表
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Integer getQuestionCount() {
+        return questionCount;
+    }
+
+    public void setQuestionCount(Integer questionCount) {
+        this.questionCount = questionCount;
+    }
+
+    public List<QuestionVo> getQuestions() {
+        if (questions == null) {
+            return new ArrayList<>();
+        }
+        return questions;
+    }
+
+    public void setQuestions(List<QuestionVo> questions) {
+        this.questions = questions;
+    }
+
+}

+ 91 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/PaperVo.java

@@ -0,0 +1,91 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.platform;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 试卷
+ */
+public class PaperVo implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String id;//注:这里的Id对应试卷代码
+    private String name;//试卷名称
+    private String courseCode;//课程代码
+    private String courseName;//课程名称
+    private Double totalScore;//试卷总分
+    private Integer hasVideo;//是否有音频
+    private Integer detailCount;//大题数量
+    private List<PaperDetailVo> details;//大题列表
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Integer getHasVideo() {
+        return hasVideo;
+    }
+
+    public void setHasVideo(Integer hasVideo) {
+        this.hasVideo = hasVideo;
+    }
+
+    public Integer getDetailCount() {
+        return detailCount;
+    }
+
+    public void setDetailCount(Integer detailCount) {
+        this.detailCount = detailCount;
+    }
+
+    public List<PaperDetailVo> getDetails() {
+        return details;
+    }
+
+    public void setDetails(List<PaperDetailVo> details) {
+        this.details = details;
+    }
+
+}

+ 48 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/ParamVo.java

@@ -0,0 +1,48 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.platform;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+
+/**
+ * 模块信息
+ */
+public class ParamVo implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String width;
+    private String height;
+
+    public String getStyle() {
+        return "max-width:500px;max-height:500px;" + getWidth() + getHeight();
+    }
+
+    public String getWidth() {
+        if (StringUtils.isBlank(width)) {
+            return "";
+        }
+        return String.format("width:%spx;", width);
+    }
+
+    public void setWidth(String width) {
+        this.width = width;
+    }
+
+    public String getHeight() {
+        if (StringUtils.isBlank(height)) {
+            return "";
+        }
+        return String.format("height:%spx;", height);
+    }
+
+    public void setHeight(String height) {
+        this.height = height;
+    }
+
+}

+ 113 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/QuestionVo.java

@@ -0,0 +1,113 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.platform;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 试题
+ */
+public class QuestionVo implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private String id;
+    private Integer number;//题序号
+    private Double score;//满分
+    private Integer structType;//题类型
+    private Boolean objective;//是否主观题
+    private BodyVo body;//题干
+    private AnswerVo answer;//答案
+    private List<OptionVo> options;//选项
+    private List<QuestionVo> subQuestions;//套题下的子题列表
+
+    public boolean hasOption() {
+        if (options != null && options.size() > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    public boolean hasSubQuestion() {
+        if (subQuestions != null && subQuestions.size() > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public Double getScore() {
+        return score;
+    }
+
+    public void setScore(Double score) {
+        this.score = score;
+    }
+
+    public Integer getStructType() {
+        return structType;
+    }
+
+    public void setStructType(Integer structType) {
+        this.structType = structType;
+    }
+
+    public Boolean getObjective() {
+        return objective;
+    }
+
+    public void setObjective(Boolean objective) {
+        this.objective = objective;
+    }
+
+    public BodyVo getBody() {
+        return body;
+    }
+
+    public void setBody(BodyVo body) {
+        this.body = body;
+    }
+
+    public AnswerVo getAnswer() {
+        return answer;
+    }
+
+    public void setAnswer(AnswerVo answer) {
+        this.answer = answer;
+    }
+
+    public List<OptionVo> getOptions() {
+        return options;
+    }
+
+    public void setOptions(List<OptionVo> options) {
+        this.options = options;
+    }
+
+    public List<QuestionVo> getSubQuestions() {
+        return subQuestions;
+    }
+
+    public void setSubQuestions(List<QuestionVo> subQuestions) {
+        this.subQuestions = subQuestions;
+    }
+}

+ 35 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/model/platform/SectionVo.java

@@ -0,0 +1,35 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.model.platform;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 分类信息
+ */
+public class SectionVo implements Serializable {
+    private static final long serialVersionUID = 2180336388838138300L;
+    private List<BlockVo> blocks;//模块列表
+
+    public boolean hasBlock() {
+        if (blocks != null && blocks.size() > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    public List<BlockVo> getBlocks() {
+        return blocks;
+    }
+
+    public void setBlocks(List<BlockVo> blocks) {
+        this.blocks = blocks;
+    }
+
+}

+ 226 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/Cryptogram.java

@@ -0,0 +1,226 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.utils;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+
+/**
+ * 支持DES/AES对称加密的工具类
+ */
+public class Cryptogram {
+    private static Logger log = LoggerFactory.getLogger(Cryptogram.class);
+    private static final String AES = "AES";
+    private static final String AES_CBC = "AES/CBC/PKCS5Padding";
+    private static final String IV_SPEC = "0102030405060708";
+    private static final int DEFAULT_AES_KEY_SIZE = 128;
+    private static final int DEFAULT_IV_SIZE = 16;
+    private static SecureRandom random = new SecureRandom();
+
+    /**
+     * 使用AES加密字符串
+     *
+     * @param str 待加密字符串
+     * @param key 符合AES要求的密钥
+     * @return
+     */
+    public static String aesEncrypt(String str, String key) {
+        if (str == null || key == null) {
+            return null;
+        }
+        try {
+            byte[] strBytes = str.getBytes("UTF-8");
+            byte[] keyBytes = key.getBytes("ASCII");
+//            byte[] ivBytes = IV_SPEC.getBytes();//默认向量值
+//            byte[] bytes = aes(strBytes, keyBytes, ivBytes, Cipher.ENCRYPT_MODE);
+            byte[] bytes = aes(strBytes, keyBytes, Cipher.ENCRYPT_MODE);
+            return byteToHex(bytes);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 使用AES解密字符串
+     *
+     * @param str 待解密字符串
+     * @param key 符合AES要求的密钥
+     * @return
+     */
+    public static String aesDecrypt(String str, String key) {
+        if (str == null || key == null) {
+            return null;
+        }
+        try {
+            byte[] strBytes = hexToByte(str);
+            byte[] keyBytes = key.getBytes("ASCII");
+//            byte[] ivBytes = IV_SPEC.getBytes();//默认向量值
+//            byte[] decryptResult = aes(strBytes, keyBytes, ivBytes, Cipher.DECRYPT_MODE);
+            byte[] decryptResult = aes(strBytes, keyBytes, Cipher.DECRYPT_MODE);
+            return new String(decryptResult);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果
+     *
+     * @param input    原始字节数组
+     * @param keyBytes 符合AES要求的密钥
+     * @param mode     Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
+     */
+    public static byte[] aes(byte[] input, byte[] keyBytes, int mode) {
+        try {
+            SecretKey secretKey = new SecretKeySpec(keyBytes, AES);
+            Cipher cipher = Cipher.getInstance(AES);
+            cipher.init(mode, secretKey);
+            return cipher.doFinal(input);
+        } catch (BadPaddingException e) {
+            log.error("加解密密匙无效:" + e.getMessage());
+            return null;
+        } catch (Exception e) {
+            log.error("加解密错误:" + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果
+     *
+     * @param input    原始字节数组
+     * @param keyBytes 符合AES要求的密钥
+     * @param ivBytes  初始向量
+     * @param mode     Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
+     */
+    public static byte[] aes(byte[] input, byte[] keyBytes, byte[] ivBytes, int mode) {
+        try {
+            SecretKey secretKey = new SecretKeySpec(keyBytes, AES);
+            Cipher cipher = Cipher.getInstance(AES_CBC);
+            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
+            cipher.init(mode, secretKey, ivSpec);
+            return cipher.doFinal(input);
+        } catch (BadPaddingException e) {
+            log.error("加解密密匙无效:" + e.getMessage());
+            return null;
+        } catch (Exception e) {
+            log.error("加解密错误:" + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 生成AES密钥,返回字节数组, 默认长度为128位(16字节)
+     */
+    public static byte[] generateAesKey() {
+        try {
+            KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
+            keyGenerator.init(DEFAULT_AES_KEY_SIZE);
+            SecretKey secretKey = keyGenerator.generateKey();
+            return secretKey.getEncoded();
+        } catch (GeneralSecurityException e) {
+            log.error(e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 生成随机向量,默认大小为cipher.getBlockSize(), 16字节
+     */
+    public static byte[] generateIV() {
+        byte[] bytes = new byte[DEFAULT_IV_SIZE];
+        random.nextBytes(bytes);
+        return bytes;
+    }
+
+    public static String byteToHex(byte[] bytes) {
+        StringBuilder hex = new StringBuilder();
+        for (int n = 0; n < bytes.length; n++) {
+            String tmp = (Integer.toHexString(bytes[n] & 0XFF));
+            if (tmp.length() == 1) {
+                hex.append("0").append(tmp);
+            } else {
+                hex.append(tmp);
+            }
+        }
+        return hex.toString().toUpperCase();
+    }
+
+    public static byte[] hexToByte(String strHex) {
+        if (strHex == null) {
+            return null;
+        }
+        int size = strHex.length();
+        if (size % 2 == 1) {
+            return null;
+        }
+        byte[] bytes = new byte[size / 2];
+        for (int i = 0; i != size / 2; i++) {
+            String s = strHex.substring(i * 2, i * 2 + 2);
+            bytes[i] = (byte) Integer.parseInt(s, 16);
+        }
+        return bytes;
+    }
+
+    /**
+     * 将字符串MD5后,再从MD5后的字符串里截取前后8位,合成16位KEY(转大写)
+     */
+    public static String convertKey(String password) {
+        if (password == null) {
+            return null;
+        }
+        try {
+            String md5 = md5Encrypt(password);
+            //System.out.println(md5 + " - " + md5.length());
+            //MD5后字符串长度默认32位
+            String start = md5.substring(0, 8);
+            String end = md5.substring(md5.length() - 8, md5.length());
+            return (start + end).toUpperCase();
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 将密码MD5两次后返回
+     */
+    public static String md5Key(String password) {
+        if (password == null) {
+            return null;
+        }
+        try {
+            String md5 = md5Encrypt(password);
+            return md5Encrypt(md5);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            return null;
+        }
+    }
+
+    public static String md5Encrypt(String str) {
+        if (StringUtils.isEmpty(str)) {
+            return str;
+        }
+        return DigestUtils.md5Hex(str);
+    }
+
+}

+ 422 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/FileUtil.java

@@ -0,0 +1,422 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.utils;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public class FileUtil {
+    private static Logger log = LoggerFactory.getLogger(FileUtil.class);
+
+    /**
+     * 分隔文件
+     *
+     * @param sourcePath 原文件
+     * @param targetPath 目标文件
+     * @param n          跳过的字节数
+     * @return
+     */
+    public static File cutFile(String sourcePath, String targetPath, int n) {
+        FileInputStream fis = null;
+        InputStream is = null;
+        OutputStream os = null;
+        try {
+            File file = new File(sourcePath);
+            fis = new FileInputStream(file);
+            //从n个字节开始读,注意中文是两个字节
+            fis.skip(n);
+            //指定文件位置读取的文件流
+            is = new BufferedInputStream(fis);
+            //存入新文件
+            File newFile = new File(targetPath);
+            os = new FileOutputStream(newFile);
+            byte buffer[] = new byte[4 * 1024];
+            int len;
+            while ((len = is.read(buffer)) != -1) {
+                os.write(buffer, 0, len);
+            }
+            os.flush();
+            return newFile;
+        } catch (FileNotFoundException e) {
+            log.error(e.getMessage(), e);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(fis);
+            IOUtils.closeQuietly(is);
+            IOUtils.closeQuietly(os);
+        }
+        return null;
+    }
+
+    /**
+     * 读取文件前面部分N个字节
+     *
+     * @param path       文件路径
+     * @param headerSize 头信息字节数(必须2的倍数)
+     * @param signSize   签名信息字节数
+     * @return
+     */
+    public static String[] readFileHeader(String path, int headerSize, int signSize) {
+        int n = headerSize / 2;
+        String[] codes = new String[n + 1];
+        FileInputStream fis = null;
+        DataInputStream ois = null;
+        try {
+            File file = new File(path);
+            fis = new FileInputStream(file);
+            ois = new DataInputStream(fis);
+            //分n次读取文件(n * 2)个字节
+            for (int i = 0; i < n; i++) {
+                codes[i] = String.valueOf(ois.readShort());
+            }
+            if (signSize > 0) {
+                StringBuilder ss = new StringBuilder();
+                for (int i = 0; i < signSize; i++) {
+                    ss.append((char) ois.readByte());
+                }
+                codes[2] = ss.toString();
+            }
+        } catch (FileNotFoundException e) {
+            log.error(e.getMessage(), e);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(fis);
+            IOUtils.closeQuietly(ois);
+        }
+        return codes;
+    }
+
+    /**
+     * 读取文件内容
+     *
+     * @param file
+     * @return
+     */
+    public static String readFileContent(File file) {
+        StringBuilder content = new StringBuilder();
+        InputStreamReader streamReader = null;
+        BufferedReader bufferedReader = null;
+        try {
+            String encoding = "UTF-8";
+            if (file.exists() && file.isFile()) {
+                streamReader = new InputStreamReader(new FileInputStream(file), encoding);
+                bufferedReader = new BufferedReader(streamReader);
+                String line;
+                while ((line = bufferedReader.readLine()) != null) {
+                    content.append(line);
+                }
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(streamReader);
+            IOUtils.closeQuietly(bufferedReader);
+        }
+        return content.toString();
+    }
+
+    /**
+     * 在文件流前面追加头信息和签名信息,并生成新的“.tk”文件
+     */
+    public static boolean appendHeader(File file, short[] headers, String sign) {
+        FileInputStream fis = null;
+        InputStream is = null;
+        FileOutputStream fos = null;
+        DataOutputStream dos = null;
+        if (file == null || !file.exists()) {
+            return false;
+        }
+        if (!file.isFile()) {
+            return false;
+        }
+        try {
+            //创建临时文件
+            String baseFilePath = file.getAbsolutePath();
+            String targetFilePath = getFilePathName(baseFilePath) + ".tk";
+            File newFile = new File(targetFilePath);
+            fos = new FileOutputStream(newFile);
+            dos = new DataOutputStream(fos);
+            //写入头信息
+            for (short s : headers) {
+                dos.writeShort(s);
+            }
+            if (sign != null && !"".equals(sign)) {
+                //写入签名信息
+                dos.write(sign.getBytes("ISO-8859-1"));
+            }
+            //在临时文件中追加原始文件内容
+            fis = new FileInputStream(file);
+            is = new BufferedInputStream(fis);
+            byte buffer[] = new byte[4 * 1024];
+            int len;
+            while ((len = is.read(buffer)) != -1) {
+                dos.write(buffer, 0, len);
+            }
+            dos.flush();
+            return true;
+        } catch (FileNotFoundException e) {
+            log.error(e.getMessage(), e);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(is);
+            IOUtils.closeQuietly(fis);
+            IOUtils.closeQuietly(dos);
+            IOUtils.closeQuietly(fos);
+        }
+        return false;
+    }
+
+    /**
+     * 生成日期目录路径
+     */
+    public static String generateDateDir() {
+        return "/" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + "/";
+    }
+
+    public static String generateFileName() {
+        return UUID.randomUUID().toString().replaceAll("-", "");
+    }
+
+    public static String generateDateName() {
+        return new SimpleDateFormat("yyMMddHHmmss").format(new Date());
+    }
+
+    /**
+     * 获取文件后缀名(包含".")
+     */
+    public static String getFileSuffix(String fileName) {
+        if (fileName == null) {
+            return "";
+        }
+        int index = fileName.lastIndexOf(".");
+        if (index > -1) {
+            return fileName.substring(index).toLowerCase();
+        }
+        return "";
+    }
+
+    /**
+     * 获取无后缀的文件名
+     *
+     * @param fileName 示例:../xxx/abc.xx
+     * @return 示例:../xxx/abc
+     */
+    public static String getFilePathName(String fileName) {
+        if (fileName != null && fileName.length() > 0) {
+            int index = fileName.lastIndexOf(".");
+            if (index != -1) {
+                return fileName.substring(0, index);
+            }
+        }
+        return "";
+    }
+
+    /**
+     * 创建文件目录
+     */
+    public static boolean makeDirs(String path) {
+        if (path == null || "".equals(path)) {
+            return false;
+        }
+        File folder = new File(path);
+        if (!folder.exists()) {
+            return folder.mkdirs();
+        }
+        return true;
+    }
+
+    /**
+     * 保存字符串到文件中
+     */
+    public static void saveAsFile(String path, String content) {
+        saveAsFile(path, content, null);
+    }
+
+    public static void saveAsFile(String path, String content, String encoding) {
+        if (path == null || content == null) {
+            return;
+        }
+        if (encoding == null) {
+            encoding = "UTF-8";
+        }
+        BufferedWriter bw = null;
+        try {
+            File file = new File(path);
+            if (!file.exists()) {
+                if (FileUtil.makeDirs(file.getParent())) {
+                    file.createNewFile();
+                }
+            }
+            OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(file), encoding);
+            bw = new BufferedWriter(write);
+            bw.write(content);
+            log.info("save as file success. " + path);
+        } catch (IOException e) {
+            log.error("save as file error. " + path);
+        } finally {
+            try {
+                if (bw != null) {
+                    bw.flush();
+                    bw.close();
+                }
+            } catch (IOException e) {
+                log.error(e.getMessage(), e);
+            }
+        }
+    }
+
+    /**
+     * 解压文件
+     *
+     * @param targetDir 解压目录
+     * @param zipFile   待解压的ZIP文件
+     */
+    public static List<File> unZip(File targetDir, File zipFile) throws IOException {
+        if (targetDir == null) {
+            log.error("解压目录不能为空!");
+            return null;
+        }
+        if (zipFile == null) {
+            log.error("待解压的文件不能为空!");
+            return null;
+        }
+        if (!zipFile.exists()) {
+            log.error("待解压的文件不存在!" + zipFile.getAbsolutePath());
+            return null;
+        }
+        String zipName = zipFile.getName().toLowerCase();
+        if (zipFile.isDirectory() || zipName.indexOf(".zip") < 0) {
+            log.error("待解压的文件格式错误!");
+            return null;
+        }
+        if (!targetDir.exists()) {
+            targetDir.mkdir();
+        }
+        log.info("UnZip targetDir:" + targetDir.getAbsolutePath());
+        List<File> result = new LinkedList<>();
+        ZipFile zip = new ZipFile(zipFile, Charset.forName("UTF-8"));
+        Enumeration entries = zip.entries();
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = (ZipEntry) entries.nextElement();
+            //Linux中需要替换掉路径的反斜杠
+            String entryName = (File.separator + entry.getName()).replaceAll("\\\\", "/");
+            log.info("UnZip:" + entryName);
+            String filePath = targetDir.getAbsolutePath() + entryName;
+            File target = new File(filePath);
+            if (entry.isDirectory()) {
+                target.mkdirs();
+            } else {
+                File dir = target.getParentFile();
+                if (!dir.exists()) {
+                    dir.mkdirs();
+                }
+                OutputStream os = new FileOutputStream(target);
+                InputStream is = zip.getInputStream(entry);
+                IOUtils.copy(is, os);
+                os.flush();
+                IOUtils.closeQuietly(os);
+                IOUtils.closeQuietly(is);
+                result.add(target);
+            }
+        }
+        zip.close();
+        return result;
+    }
+
+    /**
+     * 文件压缩
+     *
+     * @param target  目录或文件
+     * @param zipFile 压缩后的ZIP文件
+     */
+    public static boolean doZip(File target, File zipFile) {
+        if (target == null || !target.exists()) {
+            log.error("目录或文件不能为空!");
+            return false;
+        }
+        if (zipFile == null) {
+            log.error("待压缩的文件不能为空!");
+            return false;
+        }
+        ZipOutputStream zipOutStream = null;
+        OutputStream outStream = null;
+        try {
+            if (!zipFile.exists()) {
+                zipFile.createNewFile();
+            }
+            outStream = new FileOutputStream(zipFile);
+            zipOutStream = new ZipOutputStream(outStream, Charset.forName("UTF-8"));
+            if (target.isDirectory()) {
+                File[] files = target.listFiles();
+                if (files.length == 0) {
+                    log.error("文件夹内未找到任何文件!");
+                    return false;
+                }
+                for (File file : files) {
+                    doZip(zipOutStream, file, null);
+                }
+            } else {
+                doZip(zipOutStream, target, null);
+            }
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            if (zipOutStream != null) {
+                IOUtils.closeQuietly(zipOutStream);
+            }
+            if (outStream != null) {
+                IOUtils.closeQuietly(outStream);
+            }
+        }
+        return true;
+    }
+
+    private static void doZip(ZipOutputStream zipOutStream, File target, String parentDir) throws IOException {
+        //log.info("Zip:" + parentDir);
+        if (parentDir == null) {
+            parentDir = "";
+        }
+        if (!"".equals(parentDir) && !parentDir.endsWith(File.separator)) {
+            parentDir += File.separator;
+        }
+        if (target.isDirectory()) {
+            File[] files = target.listFiles();
+            if (files.length > 0) {
+                for (File file : files) {
+                    doZip(zipOutStream, file, parentDir + target.getName());
+                }
+            } else {
+                zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
+                zipOutStream.closeEntry();
+            }
+        } else {
+            InputStream is = new FileInputStream(target);
+            zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
+            int len = 0;
+            byte[] bytes = new byte[1024];
+            while ((len = is.read(bytes)) > 0) {
+                zipOutStream.write(bytes, 0, len);
+            }
+            IOUtils.closeQuietly(is);
+            zipOutStream.closeEntry();
+        }
+    }
+
+}

+ 224 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/JsonMapper.java

@@ -0,0 +1,224 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:09.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.utils;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.util.JSONPObject;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 简单封装Jackson,实现JSON 与 Java Object互相转换的Mapper
+ * 封装不同的输出风格, 使用不同的builder函数创建实例
+ */
+@SuppressWarnings("unchecked")
+public class JsonMapper {
+    private static Logger log = LoggerFactory.getLogger(JsonMapper.class);
+    private ObjectMapper mapper;
+
+    public JsonMapper() {
+        this(null);
+    }
+
+    public JsonMapper(Include include) {
+        mapper = new ObjectMapper();
+        //设置输出时包含属性的风格
+        if (include != null) {
+            mapper.setSerializationInclusion(include);
+        }
+        //设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
+        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+    }
+
+    /**
+     * 创建只输出非Null且非Empty(如List.isEmpty)的属性到Json字符串的Mapper,建议在外部接口中使用
+     */
+    public static JsonMapper nonEmptyMapper() {
+        return new JsonMapper(Include.NON_EMPTY);
+    }
+
+    public static JsonMapper nonNullMapper() {
+        return new JsonMapper(Include.NON_NULL);
+    }
+
+    /**
+     * 创建只输出初始值被改变的属性到Json字符串的Mapper, 最节约的存储方式,建议在内部接口中使用
+     */
+    public static JsonMapper nonDefaultMapper() {
+        return new JsonMapper(Include.NON_DEFAULT);
+    }
+
+    /**
+     * Object可以是POJO,也可以是Collection或数组
+     * 如果对象为Null, 返回"null"
+     * 如果集合为空集合, 返回"[]"
+     */
+    public String toJson(Object object) {
+        try {
+            return mapper.writeValueAsString(object);
+        } catch (IOException e) {
+            log.error("write to json string error:" + object);
+            return null;
+        }
+    }
+
+    /**
+     * 反序列化POJO或简单Collection如List<String>
+     * 如果JSON字符串为Null或"null"字符串, 返回Null
+     * 如果JSON字符串为"[]", 返回空集合
+     * 如需反序列化复杂Collection如List<MyBean>, 请使用fromJson(String, JavaType)
+     */
+    public <T> T fromJson(String jsonString, Class<T> clazz) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            return mapper.readValue(jsonString, clazz);
+        } catch (IOException e) {
+            log.error("parse json string error", e);
+            return null;
+        }
+    }
+
+    /**
+     * 反序列化复杂Collection如List<Bean>, 先使用createCollectionType()或constructMapType()构造类型, 然后调用本函数
+     */
+    public <T> T fromJson(String jsonString, JavaType javaType) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            return (T) mapper.readValue(jsonString, javaType);
+        } catch (IOException e) {
+            log.error("parse json string error", e);
+            return null;
+        }
+    }
+
+    /**
+     * 反序列化复杂的对象,如Page<Bean>
+     */
+    public <T> T fromJson(String jsonString, TypeReference javaType) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            return (T) mapper.readValue(jsonString, javaType);
+        } catch (IOException e) {
+            log.error("parse json string error", e);
+            return null;
+        }
+    }
+
+    /**
+     * json to list
+     */
+    public <T> List<T> toList(String jsonString, Class<T> bean) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            JavaType javaType = constructCollectionType(List.class, bean);
+            return mapper.readValue(jsonString, javaType);
+        } catch (IOException e) {
+            log.error("parse json string error", e);
+            return null;
+        }
+    }
+
+    /**
+     * json to simple HashMap
+     */
+    public <T> Map<String, T> toHashMap(String jsonString, Class<T> bean) {
+        if (StringUtils.isEmpty(jsonString)) {
+            return null;
+        }
+        try {
+            JavaType javaType = constructMapType(HashMap.class, String.class, bean);
+            return mapper.readValue(jsonString, javaType);
+        } catch (IOException e) {
+            log.error("parse json string error:", e);
+            return null;
+        }
+    }
+
+    /**
+     * 构造Collection类型
+     */
+    public JavaType constructCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass) {
+        return mapper.getTypeFactory().constructCollectionType(collectionClass, elementClass);
+    }
+
+    /**
+     * 构造Map类型
+     */
+    public JavaType constructMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
+        return mapper.getTypeFactory().constructMapType(mapClass, keyClass, valueClass);
+    }
+
+    /**
+     * 当JSON里只含有Bean的部分屬性時,更新一個已存在Bean,只覆盖該部分的屬性
+     */
+    public void update(String jsonString, Object object) {
+        try {
+            mapper.readerForUpdating(object).readValue(jsonString);
+        } catch (JsonProcessingException e) {
+            log.error("update json string:" + jsonString + " to object:" + object + " error.");
+        } catch (IOException e) {
+            log.error("update json string:" + jsonString + " to object:" + object + " error.");
+        }
+    }
+
+    /**
+     * 輸出JSONP格式数据
+     */
+    public String toJsonP(String functionName, Object object) {
+        return toJson(new JSONPObject(functionName, object));
+    }
+
+    /**
+     * 設定是否使用Enum的toString函數來读写Enum
+     * 為False時使用Enum的name()函數來读写Enum, 默認為False
+     * 注意本函數一定要在Mapper創建後, 所有的读写動作之前調用
+     */
+    public void enableEnumUseToString() {
+        mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
+        mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
+    }
+
+    /**
+     * 取出Mapper做进一步的设置或使用其他序列化API
+     */
+    public ObjectMapper getMapper() {
+        return mapper;
+    }
+
+    /***
+     * 把Json字符串转换成Node对象
+     */
+    public JsonNode getNode(String jsonStr) {
+        try {
+            //mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
+            return mapper.readTree(jsonStr);
+        } catch (IOException e) {
+            log.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+}

+ 44 - 0
examcloud-core-questions-base/src/main/java/cn/com/qmth/examcloud/core/questions/base/converter/utils/NumUtil.java

@@ -0,0 +1,44 @@
+/*
+ * *************************************************
+ * Copyright (c) 2018 QMTH. All Rights Reserved.
+ * Created by Deason on 2018-07-12 15:31:10.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.base.converter.utils;
+
+public class NumUtil {
+    public static final String[] CHAR = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
+
+    /**
+     * 将(1-26)区间的数字转换成字母
+     */
+    public static String toEnNumber(int number) {
+        String result = "";
+        if (number < 1 || number > 26) {
+            //限制支持的数字范围
+            return result;
+        }
+        return CHAR[number - 1];
+    }
+
+    /**
+     * 将字符串内的字母转换成(1-26)区间的数字
+     */
+    public static String toNumber(String character) {
+        if (character == null) {
+            return "";
+        }
+        StringBuilder str = new StringBuilder();
+        char[] chars = character.toUpperCase().toCharArray();
+        for (char c : chars) {
+            if (String.valueOf(c).matches("[a-zA-Z]")) {
+                str.append(c - 64);
+            } else {
+                str.append(c);
+            }
+        }
+        return str.toString();
+    }
+
+}