فهرست منبع

merge from examcloud-R

deason 5 سال پیش
والد
کامیت
4fe1658848

+ 27 - 33
examcloud-exchange-base/pom.xml

@@ -1,38 +1,32 @@
 <?xml version="1.0"?>
 <project
-        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
-        xmlns="http://maven.apache.org/POM/4.0.0"
-        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>cn.com.qmth.examcloud.exchange</groupId>
-        <artifactId>examcloud-exchange</artifactId>
-        <version>2019-SNAPSHOT</version>
-    </parent>
-    <artifactId>examcloud-exchange-base</artifactId>
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+	xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>cn.com.qmth.examcloud.exchange</groupId>
+		<artifactId>examcloud-exchange</artifactId>
+		<version>2019-SNAPSHOT</version>
+	</parent>
+	<artifactId>examcloud-exchange-base</artifactId>
 
-    <dependencies>
-        <dependency>
-            <groupId>cn.com.qmth.examcloud</groupId>
-            <artifactId>examcloud-web</artifactId>
-            <version>${examcloud.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>cn.com.qmth.examcloud</groupId>
-            <artifactId>examcloud-support</artifactId>
-            <version>${examcloud.version}</version>
-        </dependency>
+	<dependencies>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud</groupId>
+			<artifactId>examcloud-web</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cn.com.qmth.examcloud</groupId>
+			<artifactId>examcloud-support</artifactId>
+			<version>${examcloud.version}</version>
+		</dependency>
 
-        <dependency>
-            <groupId>com.aliyun</groupId>
-            <artifactId>aliyun-java-sdk-core</artifactId>
-            <version>4.0.3</version>
-        </dependency>
-
-        <dependency>
-            <groupId>com.github.xiaoymin</groupId>
-            <artifactId>swagger-bootstrap-ui</artifactId>
-            <version>1.9.6</version>
-        </dependency>
-    </dependencies>
+		<dependency>
+			<groupId>com.aliyun</groupId>
+			<artifactId>aliyun-java-sdk-core</artifactId>
+			<version>4.0.3</version>
+		</dependency>
+	</dependencies>
 </project>

+ 329 - 216
examcloud-exchange-outer-api-provider/src/main/java/cn/com/qmth/examcloud/exchange/outer/api/provider/ExamQuestionOuterServiceProvider.java

@@ -9,32 +9,20 @@ import cn.com.qmth.examcloud.core.oe.admin.api.request.GetPagedToBeMarkExamRecor
 import cn.com.qmth.examcloud.core.oe.admin.api.response.GetPagedToBeMarkExamRecordResp;
 import cn.com.qmth.examcloud.exchange.outer.api.ExamQuestionOuterService;
 import cn.com.qmth.examcloud.exchange.outer.api.bean.*;
-import cn.com.qmth.examcloud.exchange.outer.api.request.OuterGetPaperStructReq;
 import cn.com.qmth.examcloud.exchange.outer.api.request.OuterGetQuestionAnswerReq;
 import cn.com.qmth.examcloud.exchange.outer.api.request.OuterGetSubjectivePaperStructReq;
 import cn.com.qmth.examcloud.exchange.outer.api.request.OuterGetSubjectiveQuestionReq;
-import cn.com.qmth.examcloud.exchange.outer.api.response.OuterGetPaperStructResp;
 import cn.com.qmth.examcloud.exchange.outer.api.response.OuterGetQuestionAnswerResp;
+import cn.com.qmth.examcloud.exchange.outer.api.response.OuterGetScoreQueueTopResp;
 import cn.com.qmth.examcloud.exchange.outer.api.response.OuterGetSubjectivePaperStructResp;
 import cn.com.qmth.examcloud.exchange.outer.api.response.OuterGetSubjectiveQuestionResp;
 import cn.com.qmth.examcloud.exchange.outer.service.OutletPaperStructService;
 import cn.com.qmth.examcloud.exchange.outer.service.bean.OuterCourseBean;
 import cn.com.qmth.examcloud.exchange.outer.service.bean.OuterQuestionBean;
-import cn.com.qmth.examcloud.question.commons.core.paper.DefaultPaper;
-import cn.com.qmth.examcloud.question.commons.core.paper.DefaultQuestionGroup;
-import cn.com.qmth.examcloud.question.commons.core.paper.DefaultQuestionStructureWrapper;
-import cn.com.qmth.examcloud.question.commons.core.paper.DefaultQuestionUnitWrapper;
 import cn.com.qmth.examcloud.question.commons.core.question.AnswerType;
 import cn.com.qmth.examcloud.question.commons.core.question.QuestionType;
 import cn.com.qmth.examcloud.support.cache.CacheHelper;
-import cn.com.qmth.examcloud.support.cache.bean.CourseCacheBean;
-import cn.com.qmth.examcloud.support.cache.bean.ExtractConfigPaperCacheBean;
 import cn.com.qmth.examcloud.support.cache.bean.QuestionAnswerCacheBean;
-import cn.com.qmth.examcloud.support.enums.BlockType;
-import cn.com.qmth.examcloud.support.handler.richText.RichTextHandler;
-import cn.com.qmth.examcloud.support.handler.richText.RichTextHandlerFactory;
-import cn.com.qmth.examcloud.support.handler.richText.bean.SectionBean;
-import cn.com.qmth.examcloud.support.handler.richText.bean.SectionCollectionBean;
 import cn.com.qmth.examcloud.support.helper.ExamCacheTransferHelper;
 import cn.com.qmth.examcloud.web.support.ControllerSupport;
 import cn.com.qmth.examcloud.web.support.StatusResponse;
@@ -44,6 +32,11 @@ import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 import org.apache.commons.collections.CollectionUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.safety.Whitelist;
+import org.jsoup.select.Elements;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -51,7 +44,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * @Description 考试作答服务
@@ -72,7 +69,6 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
     @Autowired
     private OutletPaperStructService outletPaperStructService;
 
-
     @Override
     @ApiOperation(value = "获取主观题试卷结构", httpMethod = "POST")
     @ApiResponses({@ApiResponse(code = 200, message = "成功", response = OuterGetSubjectivePaperStructResp.class),
@@ -131,8 +127,6 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
     @PostMapping("/getSubjectiveQuestion")
     @Override
     public OuterGetSubjectiveQuestionResp getSubjectiveQuestion(@RequestBody OuterGetSubjectiveQuestionReq req) {
-        Long st = System.currentTimeMillis();
-
         Long examId = req.getExamId();
         if (null == examId) {
             throw new StatusException("101001", "考试id不允许为空");
@@ -157,11 +151,6 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
             throw new StatusException("101005", "数据量最大不得超过500");
         }
 
-        long startTime = System.currentTimeMillis();
-        if (log.isDebugEnabled()) {
-            log.debug("0 [GET_SUBJECTIVE_QUESTION-" + examId + "-" + subjectCode + "-" + startId + "] start...");
-        }
-
         GetPagedToBeMarkExamRecordReq pagedReq = new GetPagedToBeMarkExamRecordReq();
         pagedReq.setExamId(examId);
         pagedReq.setSubjectCode(subjectCode);
@@ -169,27 +158,10 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
         pagedReq.setSize(size);
         GetPagedToBeMarkExamRecordResp pagedResp = examRecordCloudService.getPagedToBeMarkExamRecord(pagedReq);
 
-        if (log.isDebugEnabled()) {
-            log.debug("1.[GET_SUBJECTIVE_QUESTION-" + examId + "-" + subjectCode + "-" + startId + "]" +
-                    "通过rpc获取待阅卷列表耗时:" + (System.currentTimeMillis() - startTime) + " ms");
-        }
-
-        startTime = System.currentTimeMillis();
-
         OuterGetSubjectiveQuestionResp resp = new OuterGetSubjectiveQuestionResp();
         resp.setNextId(pagedResp.getNextId());
         resp.setDataList(getOuterExamRecordBean(pagedResp.getToBeMarkExamRecordBeanList()));
 
-        if (log.isDebugEnabled()) {
-            log.debug("2.[GET_SUBJECTIVE_QUESTION-" + examId + "-" + subjectCode + "-" + startId + "]" +
-                    "构造满足条件的考试记录集合耗时:" + (System.currentTimeMillis() - startTime) + " ms");
-        }
-
-        if (log.isDebugEnabled()) {
-            log.debug("999.[GET_SUBJECTIVE_QUESTION-" + examId + "-" + subjectCode + "-" + startId + "] end..." +
-                    "合计耗时:" + (System.currentTimeMillis() - st) + " ms");
-        }
-
         return resp;
     }
 
@@ -200,7 +172,7 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
     @Override
     public OuterGetQuestionAnswerResp getQuestionAnswer(@RequestBody OuterGetQuestionAnswerReq req) {
         if (StringUtils.isNullOrEmpty(req.getQuestionId())) {
-            throw new StatusException("102001", "题目id不允许为空");
+            throw new StatusException("102001","题目id不允许为空");
         }
 
         QuestionAnswerCacheBean questionAnswer = CacheHelper.getQuestionAnswer(req.getQuestionId());
@@ -211,94 +183,6 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
         return resp;
     }
 
-    @ApiOperation(value = "获取试卷结构(题库中的原始试卷结构)", httpMethod = "POST")
-    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = OuterGetQuestionAnswerResp.class),
-            @ApiResponse(code = 500, message = "系统异常(异常信息见响应体)", response = StatusResponse.class)})
-    @PostMapping("/getPaperStruct")
-    @Override
-    public OuterGetPaperStructResp getPaperStruct(@RequestBody OuterGetPaperStructReq req) {
-        if (null == req.getExamId()) {
-            throw new StatusException("103001", "考试记录id不允许为空");
-        }
-
-        if (null == req.getCourseId()) {
-            throw new StatusException("103002", "课程id不允许为空");
-        }
-
-        if (StringUtils.isNullOrEmpty(req.getPaperType())) {
-            throw new StatusException("103003", "试卷类型不允许为空");
-        }
-
-        if (StringUtils.isNullOrEmpty(req.getBasePaperId())) {
-            throw new StatusException("103004", "原始试卷id不允许为空");
-        }
-
-        CourseCacheBean course = CacheHelper.getCourse(req.getCourseId());
-        if (null == course) {
-            throw new StatusException("103005", "课程id不正确");
-        }
-
-        ExtractConfigPaperCacheBean extractConfigPaper = CacheHelper.getExtractConfigPaper(req.getExamId(),
-                course.getCode(), req.getPaperType(), req.getBasePaperId());
-
-        OuterGetPaperStructResp resp = new OuterGetPaperStructResp();
-        if (null != extractConfigPaper.getDefaultPaper()) {
-            resp.setDefaultPaper(transferPaperFrom(extractConfigPaper.getDefaultPaper()));
-        }
-
-        return resp;
-    }
-
-    private DefaultPaperBean transferPaperFrom(DefaultPaper defaultPaper) {
-        DefaultPaperBean defaultPaperBean = new DefaultPaperBean();
-        defaultPaperBean.setFullyObjective(defaultPaper.getFullyObjective());
-        defaultPaperBean.setName(defaultPaper.getName());
-
-        List<DefaultQuestionGroup> qgList = defaultPaper.getQuestionGroupList();
-        List<DefaultQuestionGroupBean> qgBeanList = new ArrayList<>();
-        for (DefaultQuestionGroup dqg : qgList) {
-            DefaultQuestionGroupBean dqgBean = new DefaultQuestionGroupBean();
-            dqgBean.setGroupName(dqg.getGroupName());
-            dqgBean.setGroupScore(dqg.getGroupScore());
-
-            List<DefaultQuestionStructureWrapper> qwList = dqg.getQuestionWrapperList();
-            List<DefaultQuestionStructureWrapperBean> qwBeanList = new ArrayList<>();
-            for (DefaultQuestionStructureWrapper qw : qwList) {
-                DefaultQuestionStructureWrapperBean qwBean = new DefaultQuestionStructureWrapperBean();
-                qwBean.setQuestionId(qw.getQuestionId());
-                qwBean.setVersion(qw.getVersion());
-                qwBean.setQuestionScore(qw.getQuestionScore());
-                qwBean.setLimitedPlayTimes(qw.getLimitedPlayTimes());
-                qwBean.setPlayedTimes(qw.getPlayedTimes());
-                qwBean.setTimeLimit(qw.getTimeLimit());
-
-                List<DefaultQuestionUnitWrapper> quwList = qw.getQuestionUnitWrapperList();
-                List<DefaultQuestionUnitWrapperBean> quwBeanList = new ArrayList<>();
-                for (DefaultQuestionUnitWrapper quw : quwList) {
-                    DefaultQuestionUnitWrapperBean quwBean = new DefaultQuestionUnitWrapperBean();
-                    quwBean.setOptionPermutation(quw.getOptionPermutation());
-                    quwBean.setQuestionScore(quw.getQuestionScore());
-                    quwBean.setQuestionType(quw.getQuestionType() == null ? null : quw.getQuestionType().name());
-                    quwBean.setAnswerType(quw.getAnswerType() == null ? null : quw.getAnswerType().name());
-
-                    quwBeanList.add(quwBean);
-                }
-
-                qwBean.setQuestionUnitWrapperList(quwBeanList);
-
-                qwBeanList.add(qwBean);
-            }
-
-            dqgBean.setQuestionWrapperList(qwBeanList);
-
-            qgBeanList.add(dqgBean);
-        }
-
-        defaultPaperBean.setQuestionGroupList(qgBeanList);
-
-        return defaultPaperBean;
-    }
-
 
     /**
      * 构造满足条件的考试记录集合
@@ -315,7 +199,7 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
         for (PagedToBeMarkExamRecordBean pb : toBeMarkExamRecordBeanList) {
             OuterExamRecordBean resultBean = new OuterExamRecordBean();
             resultBean.setExamId(pb.getExamId());
-            resultBean.setStudentCode(pb.getIdentityNumber());//因为外部接口定义的名字虽然叫studentCode,实际是身份证号
+            resultBean.setStudentCode(pb.getStudentCode());
             resultBean.setName(pb.getStudentName());
             resultBean.setCollege(pb.getGrade());
             resultBean.setClassName(null);
@@ -341,7 +225,6 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
     private List<OuterSubjectiveQuestionRecordBean> getSubjectives(Long examId,
                                                                    List<PagedToBeMarkSubjectiveAnswerBean> subjectiveAnswerList) {
         List<OuterSubjectiveQuestionRecordBean> resultList = new ArrayList<>();
-        RichTextHandler complexRichTextHandler = RichTextHandlerFactory.getHandler("complex");
 
         for (PagedToBeMarkSubjectiveAnswerBean pb : subjectiveAnswerList) {
             OuterSubjectiveQuestionRecordBean resultBean = new OuterSubjectiveQuestionRecordBean();
@@ -349,110 +232,132 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
             resultBean.setSubNumber(pb.getOrder());
             resultBean.setQuestionId(pb.getQuestionId());
 
-            resultBean.setAnswer(transferFrom(complexRichTextHandler.handle(pb.getAnswer())));
+            resultBean.setAnswer(getFormattedAnswer(pb.getAnswer()));
+            resultBean.setStudentAnswer(
+                    getFormattedStudentAnswer(pb.getStudentAnswer(), pb.getAnswerType(), pb.getQuestionType(), examId));
+            resultBean.setBody(getFormattedBody(pb.getBody()));
+            resultBean.setParentBody(getFormattedParentBody(pb.getParentBody()));
 
-            //格式化学生答案
-            String transformedAnswerType = getTransformedAnswerType(pb.getAnswerType(), pb.getQuestionType(), examId);
+            resultList.add(resultBean);
+        }
+
+        return resultList;
+    }
 
-            RichTextHandler richTextHandler = null;
-            String stuAnswer = pb.getStudentAnswer();
-            SectionCollectionBean formattedStudentAnswer = new SectionCollectionBean();
+    /**
+     * 格式化父题干
+     *
+     * @param parentBody
+     * @return
+     */
+    private OuterSectionCollectionBean getFormattedParentBody(String parentBody) {
+        if (StringUtils.isNullOrEmpty(parentBody)) {
+            return null;
+        }
 
-            //图片作答特殊处理:将图片作答中的文字部分和图片部分拆开
-            if (BlockType.image.name().equals(transformedAnswerType)) {
-                if (!StringUtils.isNullOrEmpty(stuAnswer)) {
-                    //图片作答的标识
-                    String firstMatch = RegExpUtil.find(stuAnswer, "<div \\S*photo-answers-block\\S*>");
+        return getBodyOrAnswer(parentBody);
+    }
 
-                    int index;
-                    if (StringUtils.isNullOrEmpty(firstMatch)) {
-                        index = -1;
-                    } else {
-                        index = stuAnswer.indexOf(firstMatch);
-                    }
+    /**
+     * 格式化小题题干
+     *
+     * @param body
+     * @return
+     */
+    private OuterSectionCollectionBean getFormattedBody(String body) {
+        if (StringUtils.isNullOrEmpty(body)) {
+            return null;
+        }
 
-                    //只有文字作答,处理文字
-                    if (index == -1) {
-                        richTextHandler = RichTextHandlerFactory.getHandler(BlockType.text.name());
-                        formattedStudentAnswer = richTextHandler.handle(stuAnswer);
-                    } else {
-                        //只有图片作答,处理图片
-                        if (index == 0) {
-                            richTextHandler = RichTextHandlerFactory.getHandler(BlockType.image.name());
-                            formattedStudentAnswer = richTextHandler.handle(stuAnswer);
-                        }
-                        //既有图片又有文字
-                        else {
-                            //文字部分
-                            String stuAnswer_text = stuAnswer.substring(0, index);
-                            SectionCollectionBean formattedStudentAnswer_text =
-                                    RichTextHandlerFactory.getHandler(BlockType.text.name()).handle(stuAnswer_text);
-
-                            //图片部分
-                            String stuAnswer_image = stuAnswer.substring(index);
-                            SectionCollectionBean formattedStudentAnswer_image =
-                                    RichTextHandlerFactory.getHandler(BlockType.image.name()).handle(stuAnswer_image);
-
-                            formattedStudentAnswer = new SectionCollectionBean();
-                            List<SectionBean> sections = new ArrayList<>();
-                            sections.addAll(formattedStudentAnswer_text.getSections());
-                            sections.addAll(formattedStudentAnswer_image.getSections());
-                            formattedStudentAnswer.setSections(sections);
+        return getBodyOrAnswer(body);
+    }
+
+    /**
+     * 格式化学生答案
+     *
+     * @param studentAnswer
+     * @return
+     */
+    private OuterSectionCollectionBean getFormattedStudentAnswer(String studentAnswer,
+                                                                 AnswerType answerType,
+                                                                 QuestionType questionType, Long examId) {
+        String transformedAnswerType = getTransformedAnswerType(answerType, questionType, examId);
+
+        List<OuterSectionBean> sectionBeanList = new ArrayList<>();
+        List<OuterBlockBean> blockBeanList = new ArrayList<>();
+
+
+        //图片文件特殊处理
+        if ("image".equals(transformedAnswerType)) {
+            //图片文本部分
+            String txtStr = getImgTxt(studentAnswer);
+            if (!StringUtils.isNullOrEmpty(txtStr)) {
+                OuterBlockBean blockBean = new OuterBlockBean();
+                blockBean.setType("text");
+                blockBean.setValue(txtStr);
+                blockBeanList.add(blockBean);
+            }
+
+            //图片部分
+            if (validateImgAnswer(studentAnswer)) {
+                String imgSrc = getImgSrc(studentAnswer);
+                if (imgSrc.indexOf("|") > -1) {
+                    String[] imgAnswers = imgSrc.split("\\|");
+
+                    for (int i = 0; i < imgAnswers.length; i++) {
+                        OuterBlockBean blockBean = new OuterBlockBean();
+                        blockBean.setType(transformedAnswerType);
+                        blockBean.setValue(getSimpleTransformedAnswer(transformedAnswerType, imgAnswers[i]));
+
+                        //又拍云图片的宽*高
+                        String imgWH = RegExpUtil.find(imgAnswers[i], "(\\d+)x(\\d+)");
+                        if (!StringUtils.isNullOrEmpty(imgWH)) {
+                            Map<String, Object> paramMap = new HashMap<>();
+                            paramMap.put("width", Integer.valueOf(imgWH.split("x")[0]));
+                            paramMap.put("height", Integer.valueOf(imgWH.split("x")[1]));
+                            blockBean.setParam(paramMap);
                         }
-                    }
 
+                        blockBeanList.add(blockBean);
+                    }
                 }
             }
-            //非图片作答,则直接根据作答类型进行处理
-            else {
-                richTextHandler = RichTextHandlerFactory.getHandler(transformedAnswerType);
-                formattedStudentAnswer = richTextHandler.handle(stuAnswer);
+        } else if ("audio".equals(transformedAnswerType)) {
+            if (validateAudioAnswer(studentAnswer)) {
+                OuterBlockBean blockBean = new OuterBlockBean();
+                blockBean.setType(transformedAnswerType);
+                blockBean.setValue(getSimpleTransformedAnswer(transformedAnswerType, studentAnswer));
+                blockBeanList.add(blockBean);
             }
 
-            OuterSectionCollectionBean stuAnswerResult = transferFrom(formattedStudentAnswer);
-
-            resultBean.setStudentAnswer(stuAnswerResult);
-
-            resultBean.setBody(transferFrom(complexRichTextHandler.handle(pb.getBody())));
-            resultBean.setParentBody(transferFrom(complexRichTextHandler.handle(pb.getParentBody())));
-
-            resultList.add(resultBean);
+        } else {
+            OuterBlockBean blockBean = new OuterBlockBean();
+            blockBean.setType(transformedAnswerType);
+            blockBean.setValue(getSimpleTransformedAnswer(transformedAnswerType, studentAnswer));
+            blockBeanList.add(blockBean);
         }
 
-        return resultList;
+        OuterSectionBean sectionBean = new OuterSectionBean();
+        sectionBean.setBlocks(blockBeanList);
+        sectionBeanList.add(sectionBean);
+
+        OuterSectionCollectionBean resultBean = new OuterSectionCollectionBean();
+        resultBean.setSections(sectionBeanList);
+        return resultBean;
     }
 
-    private static OuterSectionCollectionBean transferFrom(SectionCollectionBean sectionCollection) {
-        List<SectionBean> sections = sectionCollection.getSections();
-        if (null == sections || sections.isEmpty()) {
-            return new OuterSectionCollectionBean();
+    /**
+     * 格式化标准答案
+     *
+     * @param answer
+     * @return
+     */
+    private OuterSectionCollectionBean getFormattedAnswer(String answer) {
+        if (StringUtils.isNullOrEmpty(answer)) {
+            return null;
         }
 
-        OuterSectionCollectionBean result = new OuterSectionCollectionBean();
-
-        List<OuterSectionBean> outerSections = new ArrayList<>();
-        sections.stream().forEach(sec -> {
-            OuterSectionBean outerSection = new OuterSectionBean();
-            List<OuterBlockBean> outerBlocks = new ArrayList<>();
-
-            if (null != sec.getBlocks() && !sec.getBlocks().isEmpty()) {
-                sec.getBlocks().stream().forEach(bl -> {
-                    OuterBlockBean outerBlock = new OuterBlockBean();
-                    outerBlock.setType(bl.getType());
-                    outerBlock.setValue(bl.getValue());
-                    outerBlock.setParam(bl.getParam());
-                    outerBlock.setPlayTime(bl.getPlayTime());
-
-                    outerBlocks.add(outerBlock);
-                });
-            }
-
-            outerSection.setBlocks(outerBlocks);
-            outerSections.add(outerSection);
-        });
-
-        result.setSections(outerSections);
-        return result;
+        return getBodyOrAnswer(answer);
     }
 
     /**
@@ -478,4 +383,212 @@ public class ExamQuestionOuterServiceProvider extends ControllerSupport implemen
 
         return "text";
     }
+
+    /**
+     * 获取图片作答的文本部分
+     *
+     * @param studentAnswer
+     * @return
+     */
+    private String getImgTxt(String studentAnswer) {
+        if (!StringUtils.isNullOrEmpty(studentAnswer)) {
+            return Jsoup.clean(studentAnswer, Whitelist.none());
+        }
+
+        return null;
+    }
+
+    /**
+     * 获取图片作答图片路径
+     *
+     * @param studentAnswer
+     * @return
+     */
+    private String getImgSrc(String studentAnswer) {
+        if (StringUtils.isNullOrEmpty(studentAnswer)) {
+            return studentAnswer;
+        }
+
+        //图片题特殊处理(因为图片作答题中有html标签,只需要取url即可)
+        Document doc = Jsoup.parse(studentAnswer);
+        Elements imgElements = doc.select("img[src]");
+
+        String imgSrc = "";
+
+        for (Element el : imgElements) {
+            String src = el.attr("src");
+            if (!StringUtils.isNullOrEmpty(src)) {
+                imgSrc += src + "|";
+            }
+        }
+        if (!StringUtils.isNullOrEmpty(imgSrc)) {
+            return imgSrc.substring(0, imgSrc.length() - 1);
+        }
+
+        return imgSrc;
+    }
+
+    /**
+     * 单个答案再次格式化,去除地址中的多余后缀
+     *
+     * @param answerType
+     * @param studentAnswer
+     * @return
+     */
+    private String getSimpleTransformedAnswer(String answerType, String studentAnswer) {
+        if ("image".equals(answerType)) {
+            String regExp = "(ftp|https?)\\:\\/\\/([\\w\\_\\-]+)\\.([\\w\\-]+[\\.]?)*[\\w]+\\.[a-zA-Z]{2,10}(.*)\\.(png|jpg|gif|jpeg)";
+            return RegExpUtil.find(studentAnswer, regExp);
+        }
+        return studentAnswer;
+    }
+
+    /**
+     * 获取格式化后后的题干或标准答案
+     *
+     * @param str
+     * @return
+     */
+    private OuterSectionCollectionBean getBodyOrAnswer(String str) {
+        OuterSectionCollectionBean body = new OuterSectionCollectionBean();
+        List<OuterSectionBean> sections = new ArrayList<>();
+        // 得到小题题干或者答案行数
+        if (org.apache.commons.lang3.StringUtils.isBlank(str)) {
+            return body;
+        }
+        String[] questionRowStrings = str.split("</p>");
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            List<OuterBlockBean> blocks = disposeQuestionBodyOrOption(questionRowStrings[i]);
+            if (blocks != null && blocks.size() > 0) {
+                OuterSectionBean section = new OuterSectionBean();
+                // 将小题题干拆分为Block集合
+                section.setBlocks(blocks);
+                sections.add(section);
+            }
+        }
+        body.setSections(sections);
+        return body;
+    }
+
+    private List<OuterBlockBean> disposeQuestionBodyOrOption(String questionRow) {
+        List<OuterBlockBean> blocks = new ArrayList<>();
+        // 去掉每行里面的<p>,<span>,</span>标签
+        questionRow = questionRow.replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("<span>", "")
+                .replaceAll("</span>", "").replaceAll("</a>", "");
+        String[] questionRowStrings = questionRow.split("<|/>|>");
+        for (int i = 0; i < questionRowStrings.length; i++) {
+            OuterBlockBean block = new OuterBlockBean();
+            String rowStr = questionRowStrings[i];
+            // 判断是否有图片
+            if (rowStr.startsWith("img")) {
+                rowStr = "<" + rowStr + ">";
+                Map<String, Object> param = new HashMap<>();
+                // 需要继续做截取,取到Parma
+                block.setType("image");
+                // 获取图片的路径
+                List<String> strSrcList = getImg(rowStr, "src");
+                if (strSrcList.size() > 0) {
+                    String strSrc = strSrcList.get(0).replaceAll("src=\"", "").replaceAll("\"", "");
+                    block.setValue(strSrc);
+                }
+                // 获取图片的高度
+                List<String> strHeightList = getImg(rowStr, "height");
+                if (strHeightList.size() > 0) {
+                    String strHeight = strHeightList.get(0).replaceAll("height=\"", "").replaceAll("\"", "");
+                    param.put("height", strHeight);
+                }
+                // 获取图片的宽度
+                List<String> strWidthList = getImg(rowStr, "width");
+                if (strHeightList.size() > 0) {
+                    String strWidth = strWidthList.get(0).replaceAll("width=\"", "").replaceAll("\"", "");
+                    param.put("width", strWidth);
+                }
+                block.setParam(param);
+                blocks.add(block);
+            } else if (rowStr.startsWith("a") && rowStr.contains("id") && rowStr.contains("name")) { // 处理音频
+                rowStr = "<" + rowStr + ">";
+                block.setPlayTime(1);
+                block.setType("audio");
+                block.setValue(this.getAttrValue(rowStr, "url"));
+                blocks.add(block);
+            } else {
+                block.setType("text");
+                rowStr = rowStr.replace("&nbsp;", "");// 消除空格
+                rowStr = rowStr.replace("&quot;", "\"");// 将&quot;转换成\"
+                rowStr = rowStr.replace("&lt;", "<");// 将&lt;转换成<
+                rowStr = rowStr.replace("&gt;", ">");// 将&gt;转换成>
+                rowStr = rowStr.replace("&amp;", "&");// 将&amp;转换成&
+                if (org.apache.commons.lang3.StringUtils.isNotBlank(rowStr)) {
+                    block.setValue(rowStr);
+                    blocks.add(block);
+                }
+            }
+        }
+
+        return blocks;
+    }
+
+    /**
+     * 获取图片里面的路径,长度,宽度
+     */
+    private List<String> getImg(String s, String str) {
+        String regex;
+        List<String> list = new ArrayList<>();
+        regex = str + "=\"(.*?)\"";
+        Pattern pa = Pattern.compile(regex, Pattern.DOTALL);
+        Matcher ma = pa.matcher(s);
+        while (ma.find()) {
+            list.add(ma.group());
+        }
+        return list;
+    }
+
+    private String getAttrValue(String questionStr, String attrName) {
+        Pattern aPattern = Pattern.compile("a.*");
+        Matcher aMatcher = aPattern.matcher(questionStr);
+
+        if (aMatcher.find()) {
+            String idRegex = attrName + "=\".*?\"";
+            Pattern idPattern = Pattern.compile(idRegex);
+            Matcher idMatcher = idPattern.matcher(aMatcher.group());
+            if (idMatcher.find()) {
+                return idMatcher.group()
+                        .replaceAll(attrName + "=\"", "")
+                        .replaceAll("\"", "");
+            }
+        }
+
+        return "";
+    }
+
+    /**
+     * 校验图片答案格式是否正确
+     *
+     * @param studentAnswer
+     * @return
+     */
+    private boolean validateImgAnswer(String studentAnswer) {
+        if (StringUtils.isNullOrEmpty(studentAnswer)) {
+            return false;
+        }
+
+        String regExp = "[\\s\\S]*(ftp|https?)\\:\\/\\/([\\w\\_\\-]+)\\.([\\w\\-]+[\\.]?)*[\\w]+\\.[a-zA-Z]{2,10}(.*)\\.(png|jpg|gif|jpeg).*[\\s\\S]*";
+        return studentAnswer.matches(regExp);
+    }
+
+    /**
+     * 校验音频答案格式是否正确
+     *
+     * @param studentAnswer
+     * @return
+     */
+    private boolean validateAudioAnswer(String studentAnswer) {
+        if (StringUtils.isNullOrEmpty(studentAnswer)) {
+            return false;
+        }
+
+        String regExp = "^(ftp|https?)\\:\\/\\/([\\w\\_\\-]+)\\.([\\w\\-]+[\\.]?)*[\\w]+\\.[a-zA-Z]{2,10}(.*)\\.(mp3)";
+        return studentAnswer.matches(regExp);
+    }
+
 }

+ 2 - 0
examcloud-exchange-outer-api-provider/src/main/java/cn/com/qmth/examcloud/exchange/outer/api/provider/ScoreQueueServiceProvider.java

@@ -51,6 +51,8 @@ public class ScoreQueueServiceProvider extends ControllerSupport implements Scor
 	@PostMapping("/getScoreQueueTop")
 	@Override
 	public OuterGetScoreQueueTopResp getScoreQueueTop(@RequestBody OuterGetScoreQueueTopReq req) {
+
+
 		if (!getEnterpriseRootOrgId().equals(req.getRootOrgId())) {
 			throw new StatusException("1000001", "rootOrgId is wrong");
 		}

+ 0 - 14
examcloud-exchange-outer-api/src/main/java/cn/com/qmth/examcloud/exchange/outer/api/ExamQuestionOuterService.java

@@ -1,19 +1,12 @@
 package cn.com.qmth.examcloud.exchange.outer.api;
 
 import cn.com.qmth.examcloud.api.commons.EnterpriseService;
-import cn.com.qmth.examcloud.exchange.outer.api.request.OuterGetPaperStructReq;
 import cn.com.qmth.examcloud.exchange.outer.api.request.OuterGetQuestionAnswerReq;
 import cn.com.qmth.examcloud.exchange.outer.api.request.OuterGetSubjectivePaperStructReq;
 import cn.com.qmth.examcloud.exchange.outer.api.request.OuterGetSubjectiveQuestionReq;
-import cn.com.qmth.examcloud.exchange.outer.api.response.OuterGetPaperStructResp;
 import cn.com.qmth.examcloud.exchange.outer.api.response.OuterGetQuestionAnswerResp;
 import cn.com.qmth.examcloud.exchange.outer.api.response.OuterGetSubjectivePaperStructResp;
 import cn.com.qmth.examcloud.exchange.outer.api.response.OuterGetSubjectiveQuestionResp;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
 
 
 /**
@@ -47,11 +40,4 @@ public interface ExamQuestionOuterService extends EnterpriseService {
      * @return
      */
     OuterGetQuestionAnswerResp getQuestionAnswer(OuterGetQuestionAnswerReq req);
-
-    /**
-     * 获取试卷结构(题库中的原始试卷结构)
-     * @param req
-     * @return
-     */
-    OuterGetPaperStructResp getPaperStruct(OuterGetPaperStructReq req);
 }

+ 0 - 56
examcloud-exchange-outer-api/src/main/java/cn/com/qmth/examcloud/exchange/outer/api/bean/DefaultPaperBean.java

@@ -1,56 +0,0 @@
-package cn.com.qmth.examcloud.exchange.outer.api.bean;
-
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * 试卷结构
- *
- * @author WANGWEI
- * @date 2018年8月15日
- * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
- */
-public class DefaultPaperBean implements Serializable {
-
-	private static final long serialVersionUID = -5979287118960427883L;
-
-	/**
-	 * 试卷名称
-	 */
-	private String name;
-
-	/**
-	 * 是否全是客观题
-	 */
-	private Boolean fullyObjective;
-
-	/**
-	 * 分组集合
-	 */
-	private List<DefaultQuestionGroupBean> questionGroupList;
-
-	public String getName() {
-		return name;
-	}
-
-	public void setName(String name) {
-		this.name = name;
-	}
-
-	public Boolean getFullyObjective() {
-		return fullyObjective;
-	}
-
-	public void setFullyObjective(Boolean fullyObjective) {
-		this.fullyObjective = fullyObjective;
-	}
-
-	public List<DefaultQuestionGroupBean> getQuestionGroupList() {
-		return questionGroupList;
-	}
-
-	public void setQuestionGroupList(List<DefaultQuestionGroupBean> questionGroupList) {
-		this.questionGroupList = questionGroupList;
-	}
-
-}

+ 0 - 56
examcloud-exchange-outer-api/src/main/java/cn/com/qmth/examcloud/exchange/outer/api/bean/DefaultQuestionGroupBean.java

@@ -1,56 +0,0 @@
-package cn.com.qmth.examcloud.exchange.outer.api.bean;
-
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * 题分组集合
- *
- * @author WANGWEI
- * @date 2018年8月15日
- * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
- */
-public class DefaultQuestionGroupBean implements Serializable {
-
-	private static final long serialVersionUID = 2149814711274942645L;
-
-	/**
-	 * 题组名称
-	 */
-	private String groupName;
-
-	/**
-	 * 题包装器集合<br>
-	 */
-	private List<DefaultQuestionStructureWrapperBean> questionWrapperList;
-
-	/**
-	 * 题组总分
-	 */
-	private Double groupScore;
-
-	public String getGroupName() {
-		return groupName;
-	}
-
-	public void setGroupName(String groupName) {
-		this.groupName = groupName;
-	}
-
-	public List<DefaultQuestionStructureWrapperBean> getQuestionWrapperList() {
-		return questionWrapperList;
-	}
-
-	public void setQuestionWrapperList(List<DefaultQuestionStructureWrapperBean> questionWrapperList) {
-		this.questionWrapperList = questionWrapperList;
-	}
-
-	public Double getGroupScore() {
-		return groupScore;
-	}
-
-	public void setGroupScore(Double groupScore) {
-		this.groupScore = groupScore;
-	}
-
-}

+ 0 - 109
examcloud-exchange-outer-api/src/main/java/cn/com/qmth/examcloud/exchange/outer/api/bean/DefaultQuestionStructureWrapperBean.java

@@ -1,109 +0,0 @@
-package cn.com.qmth.examcloud.exchange.outer.api.bean;
-
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * 题结构包装器
- *
- * @author WANGWEI
- * @date 2018年8月15日
- * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
- */
-public class DefaultQuestionStructureWrapperBean implements Serializable {
-
-	private static final long serialVersionUID = 8423916951155451548L;
-
-	/**
-	 * 题ID
-	 */
-	private String questionId;
-
-	/**
-	 * 版本号
-	 */
-	private String version;
-
-	/**
-	 * 题分数
-	 */
-	private Double questionScore;
-
-	/**
-	 * 限制播放次数
-	 */
-	private Integer limitedPlayTimes;
-
-	/**
-	 * 已播放次数
-	 */
-	private Integer playedTimes;
-
-	/**
-	 * 作答限时
-	 */
-	private Long timeLimit;
-
-	/**
-	 * 题单元包装器
-	 */
-	private List<DefaultQuestionUnitWrapperBean> questionUnitWrapperList;
-
-	public String getQuestionId() {
-		return questionId;
-	}
-
-	public void setQuestionId(String questionId) {
-		this.questionId = questionId;
-	}
-
-	public String getVersion() {
-		return version;
-	}
-
-	public void setVersion(String version) {
-		this.version = version;
-	}
-
-	public Double getQuestionScore() {
-		return questionScore;
-	}
-
-	public void setQuestionScore(Double questionScore) {
-		this.questionScore = questionScore;
-	}
-
-	public Integer getLimitedPlayTimes() {
-		return limitedPlayTimes;
-	}
-
-	public void setLimitedPlayTimes(Integer limitedPlayTimes) {
-		this.limitedPlayTimes = limitedPlayTimes;
-	}
-
-	public Integer getPlayedTimes() {
-		return playedTimes;
-	}
-
-	public void setPlayedTimes(Integer playedTimes) {
-		this.playedTimes = playedTimes;
-	}
-
-	public Long getTimeLimit() {
-		return timeLimit;
-	}
-
-	public void setTimeLimit(Long timeLimit) {
-		this.timeLimit = timeLimit;
-	}
-
-	public List<DefaultQuestionUnitWrapperBean> getQuestionUnitWrapperList() {
-		return questionUnitWrapperList;
-	}
-
-	public void setQuestionUnitWrapperList(
-			List<DefaultQuestionUnitWrapperBean> questionUnitWrapperList) {
-		this.questionUnitWrapperList = questionUnitWrapperList;
-	}
-
-}

+ 0 - 67
examcloud-exchange-outer-api/src/main/java/cn/com/qmth/examcloud/exchange/outer/api/bean/DefaultQuestionUnitWrapperBean.java

@@ -1,67 +0,0 @@
-package cn.com.qmth.examcloud.exchange.outer.api.bean;
-
-import java.io.Serializable;
-
-/**
- * 题单元包装器
- *
- * @author WANGWEI
- * @date 2018年8月16日
- * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
- */
-public class DefaultQuestionUnitWrapperBean implements Serializable {
-
-	private static final long serialVersionUID = 7584275153456817959L;
-
-	/**
-	 * 选项排序值
-	 */
-	private Integer[] optionPermutation;
-
-	/**
-	 * 题分数
-	 */
-	private Double questionScore;
-
-	/**
-	 * 题型
-	 */
-	private String questionType;
-
-	/**
-	 * 作答类型
-	 */
-	private String answerType;
-
-	public Integer[] getOptionPermutation() {
-		return optionPermutation;
-	}
-
-	public void setOptionPermutation(Integer[] optionPermutation) {
-		this.optionPermutation = optionPermutation;
-	}
-
-	public Double getQuestionScore() {
-		return questionScore;
-	}
-
-	public void setQuestionScore(Double questionScore) {
-		this.questionScore = questionScore;
-	}
-
-	public String getQuestionType() {
-		return questionType;
-	}
-
-	public void setQuestionType(String questionType) {
-		this.questionType = questionType;
-	}
-
-	public String getAnswerType() {
-		return answerType;
-	}
-
-	public void setAnswerType(String answerType) {
-		this.answerType = answerType;
-	}
-}

+ 0 - 59
examcloud-exchange-outer-api/src/main/java/cn/com/qmth/examcloud/exchange/outer/api/request/OuterGetPaperStructReq.java

@@ -1,59 +0,0 @@
-package cn.com.qmth.examcloud.exchange.outer.api.request;
-
-import cn.com.qmth.examcloud.api.commons.exchange.EnterpriseRequest;
-import io.swagger.annotations.ApiModelProperty;
-
-/**
- * @Description 获取试卷结构
- * @Author lideyin
- * @Date 2020/5/25 15:56
- * @Version 1.0
- */
-public class OuterGetPaperStructReq extends EnterpriseRequest {
-
-    private static final long serialVersionUID = -8374755306658040184L;
-
-    @ApiModelProperty(value = "考试id", example = "123", required = true)
-    private Long examId;
-
-    @ApiModelProperty(value = "课程id", example = "123", required = true)
-    private Long courseId;
-
-    @ApiModelProperty(value = "卷型", example = "123", required = true)
-    private String paperType;
-
-    @ApiModelProperty(value = "原始试卷id", example = "123", required = true)
-    private String basePaperId;
-
-    public Long getExamId() {
-        return examId;
-    }
-
-    public void setExamId(Long examId) {
-        this.examId = examId;
-    }
-
-    public Long getCourseId() {
-        return courseId;
-    }
-
-    public void setCourseId(Long courseId) {
-        this.courseId = courseId;
-    }
-
-    public String getPaperType() {
-        return paperType;
-    }
-
-    public void setPaperType(String paperType) {
-        this.paperType = paperType;
-    }
-
-    public String getBasePaperId() {
-        return basePaperId;
-    }
-
-    public void setBasePaperId(String basePaperId) {
-        this.basePaperId = basePaperId;
-    }
-}

+ 0 - 28
examcloud-exchange-outer-api/src/main/java/cn/com/qmth/examcloud/exchange/outer/api/response/OuterGetPaperStructResp.java

@@ -1,28 +0,0 @@
-package cn.com.qmth.examcloud.exchange.outer.api.response;
-
-import cn.com.qmth.examcloud.api.commons.exchange.EnterpriseResponse;
-import cn.com.qmth.examcloud.exchange.outer.api.bean.DefaultPaperBean;
-
-/**
- * @Description 获取试卷结构
- * @Author lideyin
- * @Date 2020/5/25 15:40
- * @Version 1.0
- */
-public class OuterGetPaperStructResp extends EnterpriseResponse {
-
-    private static final long serialVersionUID = 8290190579593586203L;
-
-    /**
-     * 试卷结构
-     */
-    private DefaultPaperBean defaultPaper;
-
-    public DefaultPaperBean getDefaultPaper() {
-        return defaultPaper;
-    }
-
-    public void setDefaultPaper(DefaultPaperBean defaultPaper) {
-        this.defaultPaper = defaultPaper;
-    }
-}