Răsfoiți Sursa

重构试卷和答案预览页面

deason 6 ani în urmă
părinte
comite
8415fe230d

+ 189 - 176
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/PaperController.java

@@ -1,14 +1,25 @@
 package cn.com.qmth.examcloud.core.questions.api;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
+import cn.com.qmth.examcloud.commons.base.exception.StatusException;
+import cn.com.qmth.examcloud.commons.base.util.JsonUtil;
+import cn.com.qmth.examcloud.commons.web.security.bean.User;
+import cn.com.qmth.examcloud.commons.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.core.questions.api.bean.ParsePaper;
+import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
+import cn.com.qmth.examcloud.core.questions.base.StringSimilarityUtils;
+import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperSearchInfo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+import cn.com.qmth.examcloud.core.questions.service.PaperService;
+import cn.com.qmth.examcloud.core.questions.service.QuesService;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailExp;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitExp;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperExp;
+import com.google.gson.Gson;
+import io.swagger.annotations.ApiOperation;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -18,48 +29,23 @@ import org.springframework.data.domain.PageImpl;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.ModelAttribute;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.servlet.ModelAndView;
 
-import com.google.gson.Gson;
-
-import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailExp;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitExp;
-import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperExp;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperSearchInfo;
-import cn.com.qmth.examcloud.core.questions.service.PaperService;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
-import cn.com.qmth.examcloud.core.questions.service.QuesService;
-import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
-import cn.com.qmth.examcloud.core.questions.base.StringSimilarityUtils;
-import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
-import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
-import cn.com.qmth.examcloud.commons.base.exception.StatusException;
-import cn.com.qmth.examcloud.commons.base.util.JsonUtil;
-import cn.com.qmth.examcloud.commons.web.security.bean.User;
-import cn.com.qmth.examcloud.commons.web.support.ControllerSupport;
-import io.swagger.annotations.ApiOperation;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Created by songyue on 16/12/28.
  * updated by weiwenhai on 2018.9.28
+ *
  * @code 160
  */
 @Controller
 @RequestMapping("${api_cqb}/")
-public class PaperController extends ControllerSupport{
+public class PaperController extends ControllerSupport {
 
     @Autowired
     PaperService paperService;
@@ -120,9 +106,9 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "查询所有导入试卷", notes = "查询所有导入试卷")
     @GetMapping(value = "/importPaper/{curPage}/{pageSize}")
     public ResponseEntity<Object> getImportPapers(@ModelAttribute PaperSearchInfo paperSearchInfo,
-                                          @PathVariable int curPage,
-                                          @PathVariable int pageSize) {
-    	User user = getAccessUser();
+                                                  @PathVariable int curPage,
+                                                  @PathVariable int pageSize) {
+        User user = getAccessUser();
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
         return new ResponseEntity<Object>(paperService.getImportPapers(paperSearchInfo, curPage, pageSize), HttpStatus.OK);
     }
@@ -131,7 +117,7 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "查询所有待审核和审核不通过的导入试卷", notes = "查询所有待审核和审核不通过的导入试卷")
     @GetMapping(value = "/importPaperNotSuccess/{curPage}/{pageSize}")
     public ResponseEntity<Object> getImportPapersNotSuccess(@ModelAttribute PaperSearchInfo paperSearchInfo, @PathVariable int curPage, @PathVariable int pageSize) {
-    	User user = getAccessUser();
+        User user = getAccessUser();
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
         return new ResponseEntity<Object>(paperService.getImportPapersNotSuccess(paperSearchInfo, curPage, pageSize),
                 HttpStatus.OK);
@@ -141,7 +127,7 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "根据条件查询导入试卷", notes = "根据条件查询导入试卷")
     @GetMapping(value = "/getImportPaper")
     public ResponseEntity<List<Paper>> getImportPapersBySearch(@ModelAttribute PaperSearchInfo paperSearchInfo) {
-    	User user = getAccessUser();
+        User user = getAccessUser();
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
         return new ResponseEntity<List<Paper>>(paperService.getImportPapersBySearch(paperSearchInfo), HttpStatus.OK);
     }
@@ -150,7 +136,7 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "根据条件查询已组试卷", notes = "根据条件查询已组试卷")
     @GetMapping(value = "/getGenPaper")
     public ResponseEntity<List<Paper>> getGenPapersBySearch(@ModelAttribute PaperSearchInfo paperSearchInfo) {
-    	User user = getAccessUser();
+        User user = getAccessUser();
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
         return new ResponseEntity<List<Paper>>(paperService.getGenPapersBySearch(paperSearchInfo), HttpStatus.OK);
     }
@@ -167,15 +153,13 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "查询所有已组试卷", notes = "查询所有已组试卷")
     @GetMapping(value = "/genPaper/{curPage}/{pageSize}")
     public ResponseEntity<Object> getGenPapers(@ModelAttribute PaperSearchInfo paperSearchInfo,
-            @PathVariable int curPage, @PathVariable int pageSize) {
-    	User user = getAccessUser();
+                                               @PathVariable int curPage, @PathVariable int pageSize) {
+        User user = getAccessUser();
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
         return new ResponseEntity<Object>(paperService.getGenPapers(paperSearchInfo, curPage, pageSize), HttpStatus.OK);
     }
-    
+
     /**
-     * 
-     * @param request
      * @param paperSearchInfo
      * @param ids
      * @param curPage
@@ -185,12 +169,12 @@ public class PaperController extends ControllerSupport{
     @ResponseBody
     @ApiOperation(value = "查询所有导入试卷(not in paperIds)", notes = "查询所有导入试卷(not in paperIds)")
     @GetMapping(value = "/genPaper/{ids}/{curPage}/{pageSize}")
-    public ResponseEntity<Object> getGenPapersNotInIds(@ModelAttribute PaperSearchInfo paperSearchInfo, 
-    		@PathVariable String[] ids, @PathVariable int curPage, @PathVariable int pageSize) {
-    	User user = getAccessUser();
+    public ResponseEntity<Object> getGenPapersNotInIds(@ModelAttribute PaperSearchInfo paperSearchInfo,
+                                                       @PathVariable String[] ids, @PathVariable int curPage, @PathVariable int pageSize) {
+        User user = getAccessUser();
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
         if (ids != null && ids.length > 0) {
-            return new ResponseEntity<Object>(paperService.getPapersNotInIds(paperSearchInfo, ids, curPage, pageSize,PaperType.GENERATE),
+            return new ResponseEntity<Object>(paperService.getPapersNotInIds(paperSearchInfo, ids, curPage, pageSize, PaperType.GENERATE),
                     HttpStatus.OK);
         } else {
             return new ResponseEntity<Object>(paperService.getGenPapers(paperSearchInfo, curPage, pageSize), HttpStatus.OK);
@@ -271,7 +255,7 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "获取卷库考试试卷", notes = "获取卷库考试试卷")
     @GetMapping(value = "/paper/list/{examId}/{courseCode}/{groupCode}")
     public List<Paper> listPaperById(@PathVariable String examId, @PathVariable String courseCode,
-            @PathVariable String groupCode) {
+                                     @PathVariable String groupCode) {
         return paperService.listExamPapers(Long.parseLong(examId), courseCode, groupCode);
     }
 
@@ -288,7 +272,7 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "新增考试试卷", notes = "新增考试试卷")
     @PostMapping(value = "/paper/join/{examId}/{courseCode}/{groupCode}/{paperId}")
     public ResponseEntity<Object> joinExamPaper(@PathVariable String examId, @PathVariable String courseCode,
-            @PathVariable String groupCode, @PathVariable String paperId) {
+                                                @PathVariable String groupCode, @PathVariable String paperId) {
         paperService.joinToExamPaper(Long.parseLong(examId), courseCode, groupCode, paperId);
         return new ResponseEntity<Object>(HttpStatus.OK);
     }
@@ -306,7 +290,7 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "删除考试试卷", notes = "删除考试试卷")
     @DeleteMapping(value = "/paper/release/{examId}/{courseCode}/{groupCode}/{paperId}")
     public ResponseEntity<Object> releaseExamPaper(@PathVariable String examId, @PathVariable String courseCode,
-            @PathVariable String groupCode, @PathVariable String paperId) {
+                                                   @PathVariable String groupCode, @PathVariable String paperId) {
 
         paperService.releaseExamPaper(Long.parseLong(examId), courseCode, groupCode, paperId);
         return new ResponseEntity<Object>(HttpStatus.OK);
@@ -338,7 +322,7 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "删除考试试卷类型", notes = "删除考试试卷类型")
     @DeleteMapping(value = "/paper/groupCode/{examId}/{courseCode}/{groupCode}")
     public ResponseEntity<Object> deleteGroup(@PathVariable String examId, @PathVariable String courseCode,
-            @PathVariable String groupCode) {
+                                              @PathVariable String groupCode) {
         paperService.deletGroupCode(Long.parseLong(examId), courseCode, groupCode);
         return new ResponseEntity<Object>(HttpStatus.OK);
     }
@@ -353,7 +337,7 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "删除导入试卷中的试题", notes = "删除导入试卷中的试题")
     @DeleteMapping(value = "/paper/deleteQuestion/{questionId}")
     public ResponseEntity<Object> deleteImportPaperQuestion(@PathVariable String questionId) {
-    	User user = getAccessUser();
+        User user = getAccessUser();
         List<String> paperNames = paperService.deleteImportQuestionById(questionId, user);
         return new ResponseEntity<Object>(paperNames, HttpStatus.OK);
     }
@@ -370,8 +354,8 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "向导入试卷中的新增试题", notes = "向导入试卷中的新增试题")
     @PostMapping(value = "/paper/addQuestion/{paperId}/{paperDetailId}")
     public ResponseEntity<Object> insertQuestionToPaper(@PathVariable String paperId,
-            @PathVariable String paperDetailId, @RequestBody Question question) {
-    	User user = getAccessUser();
+                                                        @PathVariable String paperDetailId, @RequestBody Question question) {
+        User user = getAccessUser();
         return new ResponseEntity<Object>(paperService.insertQuestionToPaper(paperId, paperDetailId, question, user),
                 HttpStatus.OK);
 
@@ -443,6 +427,7 @@ public class PaperController extends ControllerSupport{
 
     /**
      * 根据试题获取试卷名称
+     *
      * @param
      * @return
      */
@@ -463,24 +448,25 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "查询用于选题的试题列表", notes = "查询用于选题的试题列表")
     @GetMapping(value = "/paper/listQuestion/{paperId}/{curPage}/{pageSize}")
     public ResponseEntity<Object> listQuestionforSelect(@PathVariable String paperId,
-                                                	@PathVariable int curPage,
-                                                	@PathVariable int pageSize,
-                                                	@RequestParam(name = "quesType") String quesType,
-                                                	@RequestParam(name = "quesBody") String quesBody) {
-    	User user = getAccessUser();
-        if(user == null){
-            return new ResponseEntity<Object>(new PageImpl<Question>(new ArrayList<Question>()),HttpStatus.OK);
+                                                        @PathVariable int curPage,
+                                                        @PathVariable int pageSize,
+                                                        @RequestParam(name = "quesType") String quesType,
+                                                        @RequestParam(name = "quesBody") String quesBody) {
+        User user = getAccessUser();
+        if (user == null) {
+            return new ResponseEntity<Object>(new PageImpl<Question>(new ArrayList<Question>()), HttpStatus.OK);
         }
         QuesStructType quesStructType = null;
         if (StringUtils.isNotEmpty(quesType)) {
-        	quesStructType = QuesStructType.valueOf(quesType);
+            quesStructType = QuesStructType.valueOf(quesType);
         }
-        Page<Question> questionPageList = paperService.listQuestionforSelect(paperId,curPage,pageSize,quesStructType,user,quesBody);
-        return new ResponseEntity<Object>(questionPageList,HttpStatus.OK);
+        Page<Question> questionPageList = paperService.listQuestionforSelect(paperId, curPage, pageSize, quesStructType, user, quesBody);
+        return new ResponseEntity<Object>(questionPageList, HttpStatus.OK);
     }
 
     /**
      * 向试卷中某道大题插入多道试题(已存在的试题)
+     *
      * @param paperId
      * @param paperDetailId
      * @param questions
@@ -490,15 +476,14 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "向导入试卷中的新增试题", notes = "向导入试卷中的新增试题")
     @PostMapping(value = "/paper/selectQuestions/{paperId}/{paperDetailId}")
     public ResponseEntity<Object> selectQuestionsToPaper(@PathVariable String paperId,
-            @PathVariable String paperDetailId, @RequestBody List<Question> questions) {
-    	User user = getAccessUser();
+                                                         @PathVariable String paperDetailId, @RequestBody List<Question> questions) {
+        User user = getAccessUser();
         return new ResponseEntity<Object>(paperService.selectQuestionsToPaper(paperId, paperDetailId, questions, user),
                 HttpStatus.OK);
 
     }
 
     /**
-     * @param request
      * @param paperSearchInfo
      * @param ids
      * @param curPage
@@ -509,11 +494,11 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "查询所有导入试卷(not in paperIds)", notes = "查询所有导入试卷(not in paperIds)")
     @GetMapping(value = "/importPaper/{ids}/{curPage}/{pageSize}")
     public ResponseEntity<Object> getImportPapersNotInIds(@ModelAttribute PaperSearchInfo paperSearchInfo, @PathVariable String[] ids, @PathVariable int curPage,
-            @PathVariable int pageSize) {
-    	User user = getAccessUser();
+                                                          @PathVariable int pageSize) {
+        User user = getAccessUser();
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
         if (ids != null && ids.length > 0) {
-            return new ResponseEntity<Object>(paperService.getPapersNotInIds(paperSearchInfo, ids, curPage, pageSize,PaperType.IMPORT),
+            return new ResponseEntity<Object>(paperService.getPapersNotInIds(paperSearchInfo, ids, curPage, pageSize, PaperType.IMPORT),
                     HttpStatus.OK);
         } else {
             return new ResponseEntity<Object>(paperService.getImportPapers(paperSearchInfo, curPage, pageSize), HttpStatus.OK);
@@ -521,8 +506,6 @@ public class PaperController extends ControllerSupport{
     }
 
     /**
-     * 
-     * @param request
      * @param paperSearchInfo
      * @return
      */
@@ -530,7 +513,7 @@ public class PaperController extends ControllerSupport{
     @ApiOperation(value = "查询该课程的所有导入试卷", notes = "查询该课程的所有导入试卷")
     @GetMapping(value = "/importPaper/course")
     public ResponseEntity<Object> getImportPapersByCourseNo(@ModelAttribute PaperSearchInfo paperSearchInfo) {
-    	User user = getAccessUser();
+        User user = getAccessUser();
         paperSearchInfo.setOrgId(user.getRootOrgId().toString());
         paperService.formatPaperSearchInfo(paperSearchInfo);
         Paper importPaper = BeanCopierUtil.copyProperties(paperSearchInfo, Paper.class);
@@ -538,129 +521,159 @@ public class PaperController extends ControllerSupport{
         return new ResponseEntity<Object>(paperRepo.findAll(Example.of(importPaper)), HttpStatus.OK);
 
     }
+
     /**
      * 使用原卷
-     * @param request
+     *
      * @param paperIds
      * @return
      */
     @ResponseBody
     @ApiOperation(value = "复制导入试卷为组卷", notes = "复制导入试卷为组卷")
     @PutMapping(value = "/useBasePaper/{paperIds}")
-    public ResponseEntity<Object> useBasePaper(@PathVariable String paperIds){
-    	User user = getAccessUser();
-    	try{
-    		String[] paperIdArray = paperIds.split(",");
-        	for(int i = 0;i<paperIdArray.length;i++){
-        		Paper oldpaper = paperRepo.findOne(paperIdArray[i]);
-        		boolean result = paperService.checkPaperName(oldpaper.getName(),PaperType.GENERATE,user.getRootOrgId()+"");
-        		if(!result){
-        			throw new StatusException("Q-160565","考试试卷:"+oldpaper.getName()+"已经存在");
-        		}
-        	}
-    		paperService.useBasePaper(paperIds, user.getDisplayName());
-    		return new ResponseEntity<Object>(HttpStatus.OK);
-    	}catch(Exception e){
-    		throw new StatusException("Q-160537",e.getMessage());
-    	}
-    }
-    
+    public ResponseEntity<Object> useBasePaper(@PathVariable String paperIds) {
+        User user = getAccessUser();
+        try {
+            String[] paperIdArray = paperIds.split(",");
+            for (int i = 0; i < paperIdArray.length; i++) {
+                Paper oldpaper = paperRepo.findOne(paperIdArray[i]);
+                boolean result = paperService.checkPaperName(oldpaper.getName(), PaperType.GENERATE, user.getRootOrgId() + "");
+                if (!result) {
+                    throw new StatusException("Q-160565", "考试试卷:" + oldpaper.getName() + "已经存在");
+                }
+            }
+            paperService.useBasePaper(paperIds, user.getDisplayName());
+            return new ResponseEntity<Object>(HttpStatus.OK);
+        } catch (Exception e) {
+            throw new StatusException("Q-160537", e.getMessage());
+        }
+    }
+
     /**
      * 上传音频文件检查
+     *
      * @param paperId
      * @param filesName
      * @return
      */
     @ResponseBody
-    @ApiOperation(value="上传音频文件检查",notes="上传音频文件检查")
-    @PostMapping(value="/checkRadioFile/{paperId}")
-    public ResponseEntity<Object> checkRadioFile(@PathVariable String paperId,@RequestBody List<String> filesName){
-    	Map<String, String> errorMessage = paperService.checkRadioFile(paperId, filesName);
-    	return new ResponseEntity<Object>(errorMessage,HttpStatus.OK);
+    @ApiOperation(value = "上传音频文件检查", notes = "上传音频文件检查")
+    @PostMapping(value = "/checkRadioFile/{paperId}")
+    public ResponseEntity<Object> checkRadioFile(@PathVariable String paperId, @RequestBody List<String> filesName) {
+        Map<String, String> errorMessage = paperService.checkRadioFile(paperId, filesName);
+        return new ResponseEntity<Object>(errorMessage, HttpStatus.OK);
     }
-    
+
     @ResponseBody
-    @ApiOperation(value="上传音频文件", notes="上传音频文件")
-    @PostMapping(value="/uploadRadio/{paperId}")
-    public ResponseEntity<Object> uploadRadio(List<MultipartFile> files,@PathVariable String paperId){
-    	User user = getAccessUser();
-    	try {
-			paperService.uploadRadio(files, paperId, user);
-			return new ResponseEntity<Object>(HttpStatus.OK);
-		} catch (Exception e) {
-			e.printStackTrace();
-			return new ResponseEntity<Object>(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
-		}
-    }
-    
+    @ApiOperation(value = "上传音频文件", notes = "上传音频文件")
+    @PostMapping(value = "/uploadRadio/{paperId}")
+    public ResponseEntity<Object> uploadRadio(List<MultipartFile> files, @PathVariable String paperId) {
+        User user = getAccessUser();
+        try {
+            paperService.uploadRadio(files, paperId, user);
+            return new ResponseEntity<Object>(HttpStatus.OK);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return new ResponseEntity<Object>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    @ApiOperation(value = "预览试卷页面")
+    @GetMapping(value = "/paper/pdf/{paperId}")
+    public String viewPaper(Model model, @PathVariable String paperId) {
+        PaperExp paper;
+        try {
+            paper = paperService.getPaperDto(paperId);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+            throw new StatusException("500", "试卷信息不存在!");
+        }
+        model.addAttribute("htmlList", JsonUtil.toJson(ParsePaper.convertPaperHtml(paper)));
+        model.addAttribute("courseName", paper.getCourseName());
+        model.addAttribute("courseNo", paper.getCourseNo());
+        return "paper";
+    }
+
+    @ApiOperation(value = "预览试卷答案页面")
+    @GetMapping(value = "/paper/answer/pdf/{paperId}")
+    public String viewPaperAnswer(Model model, @PathVariable String paperId) {
+        PaperExp paper;
+        try {
+            paper = paperService.getPaperDto(paperId);
+        } catch (Exception e) {
+            throw new StatusException("500", "试卷信息不存在!");
+        }
+        model.addAttribute("htmlList", JsonUtil.toJson(ParsePaper.convertAnswerHtml(paper)));
+        model.addAttribute("courseNo", paper.getCourseNo());
+        model.addAttribute("courseName", paper.getCourseName());
+        return "paperAnswer";
+    }
+
     /**
      * 根据id获取试卷的html片段集合
-     * @return
      */
-    @ApiOperation(value="根据id获取试卷的html片段集合", notes="根据id获取试卷的html片段集合")
-    @GetMapping(value="/paper/pdf/{paperId}")
-    public ModelAndView getPaperPDFById(@PathVariable String paperId){
-    	ModelAndView view = new ModelAndView("paper");
-    	Map<String, Object> mag;
-		try {
-			mag = paperService.getPaperPDF(paperId,"paper");
-			view.addObject("htmlList", JsonUtil.toJson(mag.get("htmlList")));
-			view.addObject("courseName", mag.get("courseName"));
-			view.addObject("courseNo", mag.get("courseNo"));
-			return view;
-		} catch (Exception e) {
-			e.printStackTrace();
-			return view;
-		}
-    }
-    
+    /*@ApiOperation(value = "根据id获取试卷的html片段集合", notes = "根据id获取试卷的html片段集合")
+    @GetMapping(value = "/paper/pdf/{paperId}")
+    public ModelAndView getPaperPDFById(@PathVariable String paperId) {
+        ModelAndView view = new ModelAndView("paper_bak");
+        Map<String, Object> mag;
+        try {
+            mag = paperService.getPaperPDF(paperId, "paper");
+            view.addObject("htmlList", JsonUtil.toJson(mag.get("htmlList")));
+            view.addObject("courseName", mag.get("courseName"));
+            view.addObject("courseNo", mag.get("courseNo"));
+            return view;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return view;
+        }
+    }*/
+
     /**
      * 根据id获取试卷答案的html片段集合
-     * @return
      */
-    @ApiOperation(value="根据id获取试卷的html片段集合", notes="根据id获取试卷的html片段集合")
-    @GetMapping(value="/paper/answer/pdf/{paperId}")
-    public ModelAndView getPaperAnswerPDFById(@PathVariable String paperId){
-    	ModelAndView view = new ModelAndView("answer");
-    	Map<String, Object> mag;
-		try {
-			mag = paperService.getPaperPDF(paperId,"answer");
-			view.addObject("htmlList", JsonUtil.toJson(mag.get("htmlList")));
-			view.addObject("courseName", mag.get("courseName"));
-			view.addObject("courseNo", mag.get("courseNo"));
-			return view;
-		} catch (Exception e) {
-			e.printStackTrace();
-			return view;
-		}
-    }
-    
-    @ApiOperation(value="传送到印刷平台", notes="传送到印刷平台")
-    @GetMapping(value="/sendPrint/{paperId}/{examId}/{orgId}")
+    /*@ApiOperation(value = "根据id获取试卷的html片段集合", notes = "根据id获取试卷的html片段集合")
+    @GetMapping(value = "/paper/answer/pdf/{paperId}")
+    public ModelAndView getPaperAnswerPDFById(@PathVariable String paperId) {
+        ModelAndView view = new ModelAndView("paperAnswer_bak");
+        Map<String, Object> mag;
+        try {
+            mag = paperService.getPaperPDF(paperId, "answer");
+            view.addObject("htmlList", JsonUtil.toJson(mag.get("htmlList")));
+            view.addObject("courseName", mag.get("courseName"));
+            view.addObject("courseNo", mag.get("courseNo"));
+            return view;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return view;
+        }
+    }*/
+    @ApiOperation(value = "传送到印刷平台", notes = "传送到印刷平台")
+    @GetMapping(value = "/sendPrint/{paperId}/{examId}/{orgId}")
     public ResponseEntity<Object> sendPrint(@PathVariable String paperId,
-    										@PathVariable String orgId,
-    										@PathVariable String examId){
-    	paperService.sendPrint(paperId,orgId,examId);
-    	return new ResponseEntity<Object>(HttpStatus.OK);
+                                            @PathVariable String orgId,
+                                            @PathVariable String examId) {
+        paperService.sendPrint(paperId, orgId, examId);
+        return new ResponseEntity<Object>(HttpStatus.OK);
     }
-    
+
     @ResponseBody
-    @ApiOperation(value="根绝试卷id查询不同类型,题的数量")
-    @GetMapping(value="/paper/questionNumbers/{paperId}/{publicityType}/{difficultyType}")
+    @ApiOperation(value = "根绝试卷id查询不同类型,题的数量")
+    @GetMapping(value = "/paper/questionNumbers/{paperId}/{publicityType}/{difficultyType}")
     public ResponseEntity<Object> questionNumbers(@PathVariable String paperId,
-    											  @PathVariable Integer publicityType,
-    											  @PathVariable Integer difficultyType){
-    	int total = paperService.getQuestionTypeNumbers(paperId, publicityType, difficultyType);
-    	return new ResponseEntity<Object>(total,HttpStatus.OK);
+                                                  @PathVariable Integer publicityType,
+                                                  @PathVariable Integer difficultyType) {
+        int total = paperService.getQuestionTypeNumbers(paperId, publicityType, difficultyType);
+        return new ResponseEntity<Object>(total, HttpStatus.OK);
     }
-    
+
     @ResponseBody
-    @ApiOperation(value="根绝试卷id查询不同类型,题的数量")
-    @GetMapping(value="/paper/questionScores/{paperId}/{publicityType}/{difficultyType}")
+    @ApiOperation(value = "根绝试卷id查询不同类型,题的数量")
+    @GetMapping(value = "/paper/questionScores/{paperId}/{publicityType}/{difficultyType}")
     public ResponseEntity<Object> questionScores(@PathVariable String paperId,
-			  									 @PathVariable Integer publicityType,
-			  									 @PathVariable Integer difficultyType){
-    	double total = paperService.getQuestionTypeScore(paperId, publicityType, difficultyType);
-    	return new ResponseEntity<Object>(total,HttpStatus.OK);
+                                                 @PathVariable Integer publicityType,
+                                                 @PathVariable Integer difficultyType) {
+        double total = paperService.getQuestionTypeScore(paperId, publicityType, difficultyType);
+        return new ResponseEntity<Object>(total, HttpStatus.OK);
     }
 }

+ 200 - 0
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/bean/ParsePaper.java

@@ -0,0 +1,200 @@
+/*
+ * *************************************************
+ * Copyright (c) 2019 QMTH. All Rights Reserved.
+ * Created by Deason on 2019-01-18 16:55:58.
+ * *************************************************
+ */
+
+package cn.com.qmth.examcloud.core.questions.api.bean;
+
+import cn.com.qmth.examcloud.commons.base.exception.StatusException;
+import cn.com.qmth.examcloud.core.questions.base.question.enums.QuesStructType;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailExp;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperDetailUnitExp;
+import cn.com.qmth.examcloud.core.questions.service.bean.dto.PaperExp;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ParsePaper {
+    private static final Logger log = LoggerFactory.getLogger(ParsePaper.class);
+    private final static String SECTION_STYLE = "class=\"pDetail\"";//试卷大题样式
+    private final static String QUESTION_STYLE = "class=\"pQues\"";//试题标题样式
+    private final static String OPTION_STYLE = "class=\"pOpt\"";//试题答案项样式
+    private final static String FORMAT_SECTION = "<p %s><b>%s、%s (本大题共%s小题,共%s分)</b></p>";//大题标题格式
+    private final static String FORMAT_QUESTION = "<p %s>%s、%s (%s分)</p>";//试题标题格式
+    private final static String FORMAT_SUB_QUESTION = "<p %s>(%s)、%s (%s分)</p>";//子试题标题格式
+    private final static String FORMAT_OPTION = "<p %s>%s、%s</p>";//选项内容格式
+    private final static String[] REGEX_FILL_1 = {"[#][#]", "____"};//正则关键字
+    private final static String[] REGEX_FILL_2 = {"[_][#]", "_"};//正则关键字
+
+    public static List<String> convertPaperHtml(PaperExp data) {
+        if (data == null) {
+            log.warn("Paper is null.");
+            throw new StatusException("500", "试卷信息不存在!");
+        }
+
+        List<PaperDetailExp> details = data.getPaperDetails();
+        if (details == null || details.size() == 0) {
+            log.warn("Paper Detail is empty.");
+            return Lists.newArrayList();
+        }
+
+        //封装试卷的HTML内容
+        List<String> htmlList = new ArrayList<>();
+        for (int x = 0; x < details.size(); x++) {
+            PaperDetailExp detail = details.get(x);
+            if (StringUtils.isNotBlank(detail.getTitle())) {
+                htmlList.add(String.format(FORMAT_SECTION, SECTION_STYLE, detail.getCnNum(), detail.getName() + detail.getTitle(), detail.getUnitCount(), formatValue(detail.getScore())));
+            } else {
+                htmlList.add(String.format(FORMAT_SECTION, SECTION_STYLE, detail.getCnNum(), detail.getName(), detail.getUnitCount(), formatValue(detail.getScore())));
+            }
+
+            //封装大题的试题列表
+            List<PaperDetailUnitExp> units = detail.getPaperDetailUnits();
+            for (int y = 0; y < units.size(); y++) {
+                PaperDetailUnitExp unit = units.get(y);
+                Question question = unit.getQuestion();
+                if (question == null) {
+                    continue;
+                }
+
+                String qBody = question.getQuesBody();
+                if (StringUtils.isEmpty(qBody)) {
+                    log.warn("Question body is empty.");
+                    qBody = " ";
+                }
+
+                //题干 替换特殊关键字
+                qBody = replaceFill(question, qBody);
+                htmlList.add(String.format(FORMAT_QUESTION, QUESTION_STYLE, unit.getNumber(), qBody, formatValue(question.getScore())));
+
+                //客观题 选项列表
+                List<QuesOption> options = question.getQuesOptions();
+                if (options != null && options.size() > 0) {
+                    for (QuesOption option : options) {
+                        htmlList.add(String.format(FORMAT_OPTION, OPTION_STYLE, formatNum(option.getNumber()), replaceTagP(option.getOptionBody())));
+                    }
+                }
+
+                if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+                    //套题的子题
+                    List<Question> subQuestions = question.getSubQuestions();
+                    if (subQuestions != null && subQuestions.size() > 0) {
+                        for (int m = 0; m < subQuestions.size(); m++) {
+                            Question subQues = subQuestions.get(m);
+
+                            String subBody = subQues.getQuesBody();
+                            if (StringUtils.isEmpty(subBody)) {
+                                log.warn("SubQuestion body is empty.");
+                                subBody = " ";
+                            }
+
+                            //题干 替换特殊关键字
+                            subBody = replaceFill(subQues, subBody);
+                            String subQuesNumber = subQues.getQuesParams().get("number");
+                            htmlList.add(String.format(FORMAT_SUB_QUESTION, QUESTION_STYLE, subQuesNumber, subBody, formatValue(subQues.getScore())));
+
+                            //客观题 选项列表
+                            List<QuesOption> subOptions = subQues.getQuesOptions();
+                            if (subOptions != null && subOptions.size() > 0) {
+                                for (int n = 0; n < subOptions.size(); n++) {
+                                    QuesOption subOpt = subOptions.get(n);
+                                    htmlList.add(String.format(FORMAT_OPTION, OPTION_STYLE, formatNum(subOpt.getNumber()), replaceTagP(subOpt.getOptionBody())));
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return htmlList;
+    }
+
+    public static List<String> convertAnswerHtml(PaperExp data) {
+        if (data == null) {
+            throw new StatusException("500", "试卷信息不存在!");
+        }
+
+        List<PaperDetailExp> details = data.getPaperDetails();
+        if (details == null || details.size() == 0) {
+            return Lists.newArrayList();
+        }
+
+        //封装试卷答案的HTML内容
+        List<String> htmlList = new ArrayList<>();
+        for (PaperDetailExp detail : details) {
+            htmlList.add(String.format(FORMAT_SECTION, SECTION_STYLE, detail.getCnNum(), detail.getName(), detail.getUnitCount(), formatValue(detail.getScore())));
+
+            List<PaperDetailUnitExp> units = detail.getPaperDetailUnits();
+            for (PaperDetailUnitExp unit : units) {
+                Question question = unit.getQuestion();
+                if (question == null) {
+                    continue;
+                }
+
+                if (question.getQuestionType() == QuesStructType.NESTED_ANSWER_QUESTION) {
+                    htmlList.add(String.format(FORMAT_QUESTION, QUESTION_STYLE, unit.getNumber(), "题", formatValue(question.getScore())));
+                    List<Question> subQuestions = question.getSubQuestions();
+                    if (subQuestions != null && subQuestions.size() > 0) {
+                        for (Question subQues : subQuestions) {
+                            String subQuesNumber = subQues.getQuesParams().get("number");
+                            htmlList.add(String.format(FORMAT_SUB_QUESTION, QUESTION_STYLE, subQuesNumber, replaceFill(subQues, subQues.getQuesAnswer()), formatValue(subQues.getScore())));
+                        }
+                    }
+                } else {
+                    htmlList.add(String.format(FORMAT_QUESTION, QUESTION_STYLE, unit.getNumber(), replaceFill(question, question.getQuesAnswer()), formatValue(question.getScore())));
+                }
+            }
+        }
+        return htmlList;
+    }
+
+    public static String formatValue(Double score) {
+        if (score == null) {
+            return "";
+        }
+        DecimalFormat decimalFormat = new DecimalFormat("#.#");
+        return decimalFormat.format(score);
+    }
+
+    public static String formatNum(String numStr) {
+        int number = Integer.parseInt(numStr);
+        char c = (char) (64 + number);
+        return String.valueOf(c);
+    }
+
+    private static String replaceFill(Question question, String body) {
+        if (StringUtils.isEmpty(body)) {
+            return "";
+        }
+
+        body = replaceTagP(body);
+        body = body.replaceAll(REGEX_FILL_1[0], REGEX_FILL_1[1]);
+
+        //填空题情况
+        if (question.getQuestionType() == QuesStructType.FILL_BLANK_QUESTION) {
+            body = body.replaceAll(REGEX_FILL_2[0], REGEX_FILL_2[1]);
+        }
+        return body;
+    }
+
+    /**
+     * 去掉"p"标签
+     */
+    public static String replaceTagP(String str) {
+        if (str == null) {
+            return null;
+        }
+        return str.replace("<p>", "")
+                .replace("</p>", "");
+    }
+
+}

+ 244 - 227
examcloud-core-questions-starter/src/main/resources/templates/paper.ftl

@@ -1,263 +1,280 @@
 <!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="UTF-8" />
-    <title>template</title>
-    <style>
-      .center {
-        text-align: center;
-      }
-      .left_small {
-        margin-left: 10px;
-      }
-      .left_sm {
-        margin-left: 30px;
-      }
-      .top_small {
-        margin-top: 10px;
-      }
-      .firstPage {
-        /*设置高度和宽度*/
-        margin-left: 50px;
-        width: 2250px;
-        position: relative;
-      }
-      .firstColumn {
-        /*设置高度和宽度*/
-        width: 1630px;
-        height: 120px;
-        /*绝对定位到中间*/
-        overflow: auto;
-        margin: auto;
-        position: absolute;
-        top: 0;
-        left: 0;
-        bottom: 0;
-        right: 0;
-        /* 逆时针旋转90度 */
-        transform: rotateZ(270deg);
-        /*往左平移*/
-        margin-right: 60.5%;
-        margin-top: 34%;
-      }
-      #allContent {
-        width: 2000px;
-        float: left;
-        margin-left: 145px;
-      }
-      .content-page {
-        overflow: hidden;
-        height: 1660px;
-        float: left;
-        width: 990px;
-        position: relative;
-        border-bottom: 1px dashed black;
-      }
-      .secondColumn {
-        float: left;
-        width: 990px;
-        padding-top: 10px;
-        margin-top: 5px;
-        margin-left: 6px;
-        margin-right: 50px;
-      }
-      /* 侧边栏 */
-      .sidebar-font {
-        margin-top: 10px;
-        margin-left: 70px;
-      }
-      /* 密封线*/
-      .sidebar-thread {
-        margin-top: 10px;
-        text-align: center;
-      }
-      .paper_title {
-        height: 184px;
-      }
-      #optionContent {
-        visibility: hidden;
-        width: 990px;
-      }
-      .item-option-4 {
-        width: 100%;
-      }
-      .item-option-2 {
-        width: 50%;
-      }
-      .item-option-1 {
-        width: 25%;
-      }
-      .item-option {
-        display: inline-block;
-        line-height: 20px;
-      }
+<html lang="zh-cn">
+<head>
+    <title>试卷</title>
+    <meta charset="UTF-8"/>
+    <meta name="renderer" content="webkit"/>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
+    <style type="text/css">
+        .center {
+            text-align: center;
+        }
+
+        .left_small {
+            margin-left: 10px;
+        }
+
+        .left_sm {
+            margin-left: 30px;
+        }
+
+        .top_small {
+            margin-top: 10px;
+        }
+
+        .firstPage {
+            /*设置高度和宽度*/
+            margin-left: 50px;
+            width: 2250px;
+            position: relative;
+        }
+
+        .firstColumn {
+            position: absolute;
+            width: 1450px;
+            height: 120px;
+            position: absolute;
+            top: 760px;
+            left: -640px;
+            /* 逆时针旋转90度 */
+            transform: rotateZ(270deg);
+            -webkit-transform: rotateZ(270deg);
+            /*往左平移*/
+        }
+
+        #allContent {
+            width: 2000px;
+            float: left;
+            margin-left: 130px;
+        }
+
+        .content-page {
+            overflow: hidden;
+            height: 1560px;
+            float: left;
+            margin: 22px 15px;
+
+            width: 960px;
+            position: relative;
+            border-top: 0px dashed black;
+            border-bottom: 0px dashed black;
+            box-sizing: border-box;
+        }
+
+        .secondColumn {
+            float: left;
+            width: 100%;
+            padding-top: 10px;
+        }
+
+        /* 侧边栏 */
+        .sidebar-font {
+            text-align: center;
+        }
+
+        /* 密封线*/
+        .sidebar-thread {
+            margin-top: 10px;
+            border-bottom: 1px dashed #333;
+            height: 20px;
+            line-height: 25px;
+            text-align: center;
+        }
+
+        .sidebar-thread span {
+            margin: 0 150px;
+        }
+
+        .paper_title {
+            height: 184px;
+        }
+
+        #optionContent {
+            visibility: hidden;
+            width: 990px;
+        }
+
+        .item-option-4 {
+            width: 100%;
+        }
+
+        .item-option-2 {
+            width: 50%;
+        }
+
+        .item-option-1 {
+            width: 25%;
+        }
+
+        .item-option {
+            display: inline-block;
+            line-height: 20px;
+            padding-right: 15px;
+            box-sizing: border-box;
+            -webkit-box-zizig: border-box;
+        }
+
+        .pOpt {
+            margin: 5px auto;
+        }
+
+        @media print {
+            .content-page:nth-child(even) {
+                page-break-before: always;
+            }
+
+            .content-page:first-child {
+                page-break-before: auto;
+            }
+
+            @page {
+                size: A3 landscape;
+            }
+        }
     </style>
-  </head>
-  <body>
-    <div class="firstPage" id="firstPage">
-      <!-- 密封线 -->
-      <div class="firstColumn">
+</head>
+<body>
+<div class="firstPage" id="firstPage">
+    <!-- 密封线 -->
+    <div class="firstColumn">
         <div class="sidebar-font">
-          <span>姓名________________________________</span>
-          <span>学号________________________________</span>
-          <span>专业________________________________</span>
-          <span>学习中心________________________________</span>
-          <span>考试名称________________________________</span>
+            <span>姓名___________________________</span>
+            <span>学号___________________________</span>
+            <span>专业___________________________</span>
+            <span>学习中心___________________________</span>
+            <span>考试名称___________________________</span>
         </div>
         <div class="sidebar-thread">
-          <span
-            >...........................................................................................密...........................................................................................封..............................................................................线..............................................................................</span
-          >
+            <span>密</span>
+            <span>封</span>
+            <span>线</span>
         </div>
-      </div>
+    </div>
 
-      <!-- 选项临时存放点 -->
-      <div id="optionContent"></div>
-      <div id="allContent">
+    <!--选项临时存放点-->
+    <div id="optionContent"></div>
+    <div id="allContent">
         <div class="content-page" id="content-page">
-          <div class="secondColumn" id="secondColumn0">
-            <div class="paper_title">
-              <div>
-                <h1>
-                  <span style="margin-left:20%;">网络教育学院考试</span>
-                </h1>
-                <h3><span style="margin-left:35%;">考试试卷</span></h3>
-              </div>
-              <div>
-                <span>课程名称:</span
-                ><span style="color: #FF0000" id="courseName"></span>
-                <span style="margin-left: 20px;">课程代码:</span
-                ><span style="color: #FF0000" id="courseNo"></span><br />
-              </div>
-              <div class="top_small">
-                <span
-                  >注意事项:必须按试题顺序在“答题卡”上按要求填涂、作答,答在试卷上的答案无效。</span
-                >
-              </div>
+            <div class="secondColumn" id="secondColumn0">
+                <div class="paper_title">
+                    <div style="text-align: center">
+                        <h1><span style="margin-right:20%;">网络教育学院考试</span></h1>
+                        <h3><span style="margin-right:20%;">考试试卷</span></h3>
+                    </div>
+                    <div>
+                        <b><span>课程名称:</span><span style="color: #FF0000">${courseName!}</span>
+                            <span style="margin-left: 20px;">课程代码:</span><span
+                                    style="color: #FF0000">${courseNo!}</span>
+                        </b>
+                    </div>
+                    <div class="top_small">
+                        <span><b>注意事项:必须按试题顺序在“答题卡”上按要求填涂、作答,答在试卷上的答案无效。</b></span>
+                    </div>
+                </div>
+                <div class="top_small">
+                </div>
             </div>
-            <div class="top_small"></div>
-          </div>
         </div>
-      </div>
     </div>
-  </body>
-  <script>
-    
-      var a3width = 990; //a3宽
-      var a3height = 1660; //a3高
-
-      var secondColumnIndex = 0;
-      var pageSize = 1;
-      var optionArray = [];
-      var secondColumn = document.getElementById(
-        "secondColumn" + secondColumnIndex
-      );
-      var optionContent = document.getElementById("optionContent");
+</div>
+</body>
+<script>
+    /* A3宽 */
+    var a3width = 990 - 60;
+    /* A3高 */
+    var a3height = 1560;
+    var secondColumnIndex = 0;
+    var optionArray = [];
+    var secondColumn = document.getElementById("secondColumn0");
+    var optionContent = document.getElementById("optionContent");
 
-      
-
-      function initialize(dataList,courseName,courseNo) {
-      	document.getElementById("courseName").innerHTML = courseName;
-      	document.getElementById("courseNo").innerHTML = courseNo;
-      	typesetting(dataList);
-      }
-
-      function typesetting(dataList) {
+    function typesetting(dataList) {
+        /* 遍历数据 */
         for (var i = 0; i < dataList.length; i++) {
-          var div = createDivDom(dataList[i], i);
-          if (div.className.indexOf("item-option") > -1) {
-            optionArray.push(div);
-            optionContent.appendChild(div);
-          } else if (
-            (div.className.indexOf("item-quesBody") > -1 ||
-              div.className.indexOf("item-paperDetail") > -1) &&
-            optionArray.length > 0
-          ) {
-            handleQuesOption();
-            appendPageBox(div);
-          } else {
-            appendPageBox(div);
-          }
+            var div = createDom(dataList[i], i);
+            if (div.className.indexOf("item-option") > -1) {
+                optionArray.push(div);
+                optionContent.appendChild(div);
+            } else if ((div.className.indexOf("item-pDetail") > -1 || div.className.indexOf("item-pQues") > -1)
+                && optionArray.length > 0) {
+                handleQuesOption();
+                appendPageBox(div);
+            } else {
+                appendPageBox(div);
+            }
         }
-        //如果最后一次是选择题
+        /* 如果最后一次是选择题 */
         if (optionArray.length > 0) {
-          handleQuesOption();
+            handleQuesOption();
         }
-      }
+        console.log('finished');
+    }
 
-      //处理选项
-      function handleQuesOption() {
+    function handleQuesOption() {
         var cls = getLineType(optionArray);
-        optionContent.innerHTML = "";
-        optionArray.forEach(function(item) {
-          item.setAttribute("class", "item-option " + cls);
-          appendPageBox(item);
+        optionContent.innerHTML = '';
+        optionArray.forEach(function (item) {
+            item.setAttribute('class', 'item-option ' + cls);
+            appendPageBox(item);
         });
         optionArray = [];
-      }
+    }
 
-      function createDivDom(data, index) {
-        var divlog = document.createElement("div");
-        var spanlog = document.createElement("span");
+    function createDom(data, index) {
+        var divLog = document.createElement('div');
         var cls = "";
-        if (data.indexOf("paperDetailTitle") > -1) {
-          cls = "item-paperDetail";
+        if (data.indexOf("pQues") > -1) {
+            cls = "item-pQues";
         }
-        if (data.indexOf("questionBody") > -1) {
-          cls = "item-quesBody";
+        if (data.indexOf("pDetail") > -1) {
+            cls = "item-pDetail";
         }
-        if (data.indexOf("selectOption") > -1) {
-          cls = "item-option";
+        if (data.indexOf("pOpt") > -1) {
+            cls = "item-option";
         }
-        divlog.setAttribute("class", cls);
-        divlog.setAttribute("data-index", index);
-        spanlog.innerHTML = data;
-        divlog.appendChild(spanlog);
-        return divlog;
-      }
+        divLog.setAttribute('class', cls);
+        divLog.setAttribute('data-index', index);
+        divLog.innerHTML = data;
+        return divLog;
+    }
 
-      function appendPageBox(dom) {
+    function appendPageBox(dom) {
         secondColumn.appendChild(dom);
         var pch = secondColumn.clientHeight;
         if (pch > a3height) {
-          secondColumn.removeChild(dom);
-          secondColumnIndex++;
-          var allContent = document.getElementById("allContent");
-          var contentPageDom = document.createElement("div");
-          contentPageDom.setAttribute("class", "content-page");
-
-          var pageBoxDom = document.createElement("div");
-          pageBoxDom.setAttribute("class", "secondColumn");
-          pageBoxDom.id = "secondColumn" + secondColumnIndex;
-          pageBoxDom.appendChild(dom);
-          contentPageDom.appendChild(pageBoxDom);
-          allContent.appendChild(contentPageDom);
-          secondColumn = document.getElementById(
-            "secondColumn" + secondColumnIndex
-          );
+            secondColumn.removeChild(dom);
+            secondColumnIndex++;
+            var allContent = document.getElementById("allContent");
+            var contentPageDom = document.createElement('div');
+            contentPageDom.setAttribute('class', 'content-page');
+            var pageBoxDom = document.createElement('div');
+            pageBoxDom.setAttribute('class', 'secondColumn');
+            pageBoxDom.id = "secondColumn" + (secondColumnIndex);
+            pageBoxDom.appendChild(dom);
+            contentPageDom.appendChild(pageBoxDom);
+            allContent.appendChild(contentPageDom);
+            secondColumn = document.getElementById("secondColumn" + secondColumnIndex);
         }
-      }
+    }
 
-      function getLineType(optionWs) {
-        var type = "item-option-1";
+    function getLineType(optionWs) {
+        var type = 'item-option-1';
         for (var i = 0; i < optionWs.length; i++) {
-          var rate = optionWs[i].clientWidth / a3width;
-          if (rate > 0.5) {
-            type = "item-option-4";
-            return type;
-          }
-          if (rate > 0.25) type = "item-option-2";
+            var rate = optionWs[i].clientWidth / a3width;
+            if (rate > 0.5) {
+                type = 'item-option-4';
+                return type
+            }
+            if (rate > 0.25) type = 'item-option-2'
         }
-        return type;
-      }
-      
-      
+        return type
+    }
+
     window.onload = function () {
-    
-        var dateList = eval(${htmlList!});
+        var paperDetails = eval(${htmlList!});
 
-        initialize(dateList,"${courseName!}","${courseNo!}");
+        typesetting(paperDetails);
     };
-    
-  </script>
-</html>
+</script>
+</html>

+ 275 - 0
examcloud-core-questions-starter/src/main/resources/templates/paperAnswer.ftl

@@ -0,0 +1,275 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+<head>
+    <title>试卷</title>
+    <meta charset="UTF-8"/>
+    <meta name="renderer" content="webkit"/>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
+    <style type="text/css">
+        .center {
+            text-align: center;
+        }
+
+        .left_small {
+            margin-left: 10px;
+        }
+
+        .left_sm {
+            margin-left: 30px;
+        }
+
+        .top_small {
+            margin-top: 10px;
+        }
+
+        .firstPage {
+            /*设置高度和宽度*/
+            margin-left: 50px;
+            width: 2250px;
+            position: relative;
+        }
+
+        .firstColumn {
+            position: absolute;
+            width: 1450px;
+            height: 120px;
+            position: absolute;
+            top: 760px;
+            left: -640px;
+            /* 逆时针旋转90度 */
+            transform: rotateZ(270deg);
+            -webkit-transform: rotateZ(270deg);
+            /*往左平移*/
+        }
+
+        #allContent {
+            width: 2000px;
+            float: left;
+            margin-left: 130px;
+        }
+
+        .content-page {
+            overflow: hidden;
+            height: 1560px;
+            float: left;
+            margin: 22px 15px;
+
+            width: 960px;
+            position: relative;
+            border-top: 0px dashed black;
+            border-bottom: 0px dashed black;
+            box-sizing: border-box;
+        }
+
+        .secondColumn {
+            float: left;
+            width: 100%;
+            padding-top: 10px;
+        }
+
+        /* 侧边栏 */
+        .sidebar-font {
+            text-align: center;
+        }
+
+        /* 密封线*/
+        .sidebar-thread {
+            margin-top: 10px;
+            border-bottom: 1px dashed #333;
+            height: 20px;
+            line-height: 25px;
+            text-align: center;
+        }
+
+        .sidebar-thread span {
+            margin: 0 150px;
+        }
+
+        .paper_title {
+            height: 150px;
+        }
+
+        #optionContent {
+            visibility: hidden;
+            width: 990px;
+        }
+
+        .item-option-4 {
+            width: 100%;
+        }
+
+        .item-option-2 {
+            width: 50%;
+        }
+
+        .item-option-1 {
+            width: 25%;
+        }
+
+        .item-option {
+            display: inline-block;
+            line-height: 20px;
+            padding-right: 15px;
+            box-sizing: border-box;
+            -webkit-box-zizig: border-box;
+        }
+
+        .pOpt {
+            margin: 5px auto;
+        }
+
+        @media print {
+            .content-page:nth-child(even) {
+                page-break-before: always;
+            }
+
+            .content-page:first-child {
+                page-break-before: auto;
+            }
+
+            @page {
+                size: A3 landscape;
+            }
+        }
+    </style>
+</head>
+<body>
+<div class="firstPage" id="firstPage">
+    <!-- 密封线 -->
+    <div class="firstColumn">
+        <div class="sidebar-font">
+            <span>姓名___________________________</span>
+            <span>学号___________________________</span>
+            <span>专业___________________________</span>
+            <span>学习中心___________________________</span>
+            <span>考试名称___________________________</span>
+        </div>
+        <div class="sidebar-thread">
+            <span>密</span>
+            <span>封</span>
+            <span>线</span>
+        </div>
+    </div>
+
+    <!--选项临时存放点-->
+    <div id="optionContent"></div>
+    <div id="allContent">
+        <div class="content-page" id="content-page">
+            <div class="secondColumn" id="secondColumn0">
+                <div class="paper_title">
+                    <div style="text-align: center">
+                        <h1><span style="margin-right:20%;">网络教育学院考试</span></h1>
+                        <h3><span style="margin-right:20%;">考试试卷答案</span></h3>
+                    </div>
+                    <div>
+                        <b><span>课程名称:</span><span style="color: #FF0000">${courseName!}</span>
+                            <span style="margin-left: 20px;">课程代码:</span><span
+                                    style="color: #FF0000">${courseNo!}</span>
+                        </b>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+</body>
+<script>
+    /* A3宽 */
+    var a3width = 990 - 60;
+    /* A3高 */
+    var a3height = 1560;
+    var secondColumnIndex = 0;
+    var optionArray = [];
+    var secondColumn = document.getElementById("secondColumn0");
+    var optionContent = document.getElementById("optionContent");
+
+    function typesetting(dataList) {
+        /* 遍历数据 */
+        for (var i = 0; i < dataList.length; i++) {
+            var div = createDom(dataList[i], i);
+            if (div.className.indexOf("item-option") > -1) {
+                optionArray.push(div);
+                optionContent.appendChild(div);
+            } else if ((div.className.indexOf("item-pDetail") > -1 || div.className.indexOf("item-pQues") > -1)
+                && optionArray.length > 0) {
+                handleQuesOption();
+                appendPageBox(div);
+            } else {
+                appendPageBox(div);
+            }
+        }
+        /* 如果最后一次是选择题 */
+        if (optionArray.length > 0) {
+            handleQuesOption();
+        }
+        console.log('finished');
+    }
+
+    function handleQuesOption() {
+        var cls = getLineType(optionArray);
+        optionContent.innerHTML = '';
+        optionArray.forEach(function (item) {
+            item.setAttribute('class', 'item-option ' + cls);
+            appendPageBox(item);
+        });
+        optionArray = [];
+    }
+
+    function createDom(data, index) {
+        var divLog = document.createElement('div');
+        var cls = "";
+        if (data.indexOf("pQues") > -1) {
+            cls = "item-pQues";
+        }
+        if (data.indexOf("pDetail") > -1) {
+            cls = "item-pDetail";
+        }
+        if (data.indexOf("pOpt") > -1) {
+            cls = "item-option";
+        }
+        divLog.setAttribute('class', cls);
+        divLog.setAttribute('data-index', index);
+        divLog.innerHTML = data;
+        return divLog;
+    }
+
+    function appendPageBox(dom) {
+        secondColumn.appendChild(dom);
+        var pch = secondColumn.clientHeight;
+        if (pch > a3height) {
+            secondColumn.removeChild(dom);
+            secondColumnIndex++;
+            var allContent = document.getElementById("allContent");
+            var contentPageDom = document.createElement('div');
+            contentPageDom.setAttribute('class', 'content-page');
+            var pageBoxDom = document.createElement('div');
+            pageBoxDom.setAttribute('class', 'secondColumn');
+            pageBoxDom.id = "secondColumn" + (secondColumnIndex);
+            pageBoxDom.appendChild(dom);
+            contentPageDom.appendChild(pageBoxDom);
+            allContent.appendChild(contentPageDom);
+            secondColumn = document.getElementById("secondColumn" + secondColumnIndex);
+        }
+    }
+
+    function getLineType(optionWs) {
+        var type = 'item-option-1';
+        for (var i = 0; i < optionWs.length; i++) {
+            var rate = optionWs[i].clientWidth / a3width;
+            if (rate > 0.5) {
+                type = 'item-option-4';
+                return type
+            }
+            if (rate > 0.25) type = 'item-option-2'
+        }
+        return type
+    }
+
+    window.onload = function () {
+        var paperDetails = eval(${htmlList!});
+
+        typesetting(paperDetails);
+    };
+</script>
+</html>

+ 0 - 0
examcloud-core-questions-starter/src/main/resources/templates/answer.ftl → examcloud-core-questions-starter/src/main/resources/templates/paperAnswer_bak.ftl


+ 263 - 0
examcloud-core-questions-starter/src/main/resources/templates/paper_bak.ftl

@@ -0,0 +1,263 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>template</title>
+    <style>
+      .center {
+        text-align: center;
+      }
+      .left_small {
+        margin-left: 10px;
+      }
+      .left_sm {
+        margin-left: 30px;
+      }
+      .top_small {
+        margin-top: 10px;
+      }
+      .firstPage {
+        /*设置高度和宽度*/
+        margin-left: 50px;
+        width: 2250px;
+        position: relative;
+      }
+      .firstColumn {
+        /*设置高度和宽度*/
+        width: 1630px;
+        height: 120px;
+        /*绝对定位到中间*/
+        overflow: auto;
+        margin: auto;
+        position: absolute;
+        top: 0;
+        left: 0;
+        bottom: 0;
+        right: 0;
+        /* 逆时针旋转90度 */
+        transform: rotateZ(270deg);
+        /*往左平移*/
+        margin-right: 60.5%;
+        margin-top: 34%;
+      }
+      #allContent {
+        width: 2000px;
+        float: left;
+        margin-left: 145px;
+      }
+      .content-page {
+        overflow: hidden;
+        height: 1660px;
+        float: left;
+        width: 990px;
+        position: relative;
+        border-bottom: 1px dashed black;
+      }
+      .secondColumn {
+        float: left;
+        width: 990px;
+        padding-top: 10px;
+        margin-top: 5px;
+        margin-left: 6px;
+        margin-right: 50px;
+      }
+      /* 侧边栏 */
+      .sidebar-font {
+        margin-top: 10px;
+        margin-left: 70px;
+      }
+      /* 密封线*/
+      .sidebar-thread {
+        margin-top: 10px;
+        text-align: center;
+      }
+      .paper_title {
+        height: 184px;
+      }
+      #optionContent {
+        visibility: hidden;
+        width: 990px;
+      }
+      .item-option-4 {
+        width: 100%;
+      }
+      .item-option-2 {
+        width: 50%;
+      }
+      .item-option-1 {
+        width: 25%;
+      }
+      .item-option {
+        display: inline-block;
+        line-height: 20px;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="firstPage" id="firstPage">
+      <!-- 密封线 -->
+      <div class="firstColumn">
+        <div class="sidebar-font">
+          <span>姓名________________________________</span>
+          <span>学号________________________________</span>
+          <span>专业________________________________</span>
+          <span>学习中心________________________________</span>
+          <span>考试名称________________________________</span>
+        </div>
+        <div class="sidebar-thread">
+          <span
+            >...........................................................................................密...........................................................................................封..............................................................................线..............................................................................</span
+          >
+        </div>
+      </div>
+
+      <!-- 选项临时存放点 -->
+      <div id="optionContent"></div>
+      <div id="allContent">
+        <div class="content-page" id="content-page">
+          <div class="secondColumn" id="secondColumn0">
+            <div class="paper_title">
+              <div>
+                <h1>
+                  <span style="margin-left:20%;">网络教育学院考试</span>
+                </h1>
+                <h3><span style="margin-left:35%;">考试试卷</span></h3>
+              </div>
+              <div>
+                <span>课程名称:</span
+                ><span style="color: #FF0000" id="courseName"></span>
+                <span style="margin-left: 20px;">课程代码:</span
+                ><span style="color: #FF0000" id="courseNo"></span><br />
+              </div>
+              <div class="top_small">
+                <span
+                  >注意事项:必须按试题顺序在“答题卡”上按要求填涂、作答,答在试卷上的答案无效。</span
+                >
+              </div>
+            </div>
+            <div class="top_small"></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </body>
+  <script>
+    
+      var a3width = 990; //a3宽
+      var a3height = 1660; //a3高
+
+      var secondColumnIndex = 0;
+      var pageSize = 1;
+      var optionArray = [];
+      var secondColumn = document.getElementById(
+        "secondColumn" + secondColumnIndex
+      );
+      var optionContent = document.getElementById("optionContent");
+
+      
+
+      function initialize(dataList,courseName,courseNo) {
+      	document.getElementById("courseName").innerHTML = courseName;
+      	document.getElementById("courseNo").innerHTML = courseNo;
+      	typesetting(dataList);
+      }
+
+      function typesetting(dataList) {
+        for (var i = 0; i < dataList.length; i++) {
+          var div = createDivDom(dataList[i], i);
+          if (div.className.indexOf("item-option") > -1) {
+            optionArray.push(div);
+            optionContent.appendChild(div);
+          } else if (
+            (div.className.indexOf("item-quesBody") > -1 ||
+              div.className.indexOf("item-paperDetail") > -1) &&
+            optionArray.length > 0
+          ) {
+            handleQuesOption();
+            appendPageBox(div);
+          } else {
+            appendPageBox(div);
+          }
+        }
+        //如果最后一次是选择题
+        if (optionArray.length > 0) {
+          handleQuesOption();
+        }
+      }
+
+      //处理选项
+      function handleQuesOption() {
+        var cls = getLineType(optionArray);
+        optionContent.innerHTML = "";
+        optionArray.forEach(function(item) {
+          item.setAttribute("class", "item-option " + cls);
+          appendPageBox(item);
+        });
+        optionArray = [];
+      }
+
+      function createDivDom(data, index) {
+        var divlog = document.createElement("div");
+        var spanlog = document.createElement("span");
+        var cls = "";
+        if (data.indexOf("paperDetailTitle") > -1) {
+          cls = "item-paperDetail";
+        }
+        if (data.indexOf("questionBody") > -1) {
+          cls = "item-quesBody";
+        }
+        if (data.indexOf("selectOption") > -1) {
+          cls = "item-option";
+        }
+        divlog.setAttribute("class", cls);
+        divlog.setAttribute("data-index", index);
+        spanlog.innerHTML = data;
+        divlog.appendChild(spanlog);
+        return divlog;
+      }
+
+      function appendPageBox(dom) {
+        secondColumn.appendChild(dom);
+        var pch = secondColumn.clientHeight;
+        if (pch > a3height) {
+          secondColumn.removeChild(dom);
+          secondColumnIndex++;
+          var allContent = document.getElementById("allContent");
+          var contentPageDom = document.createElement("div");
+          contentPageDom.setAttribute("class", "content-page");
+
+          var pageBoxDom = document.createElement("div");
+          pageBoxDom.setAttribute("class", "secondColumn");
+          pageBoxDom.id = "secondColumn" + secondColumnIndex;
+          pageBoxDom.appendChild(dom);
+          contentPageDom.appendChild(pageBoxDom);
+          allContent.appendChild(contentPageDom);
+          secondColumn = document.getElementById(
+            "secondColumn" + secondColumnIndex
+          );
+        }
+      }
+
+      function getLineType(optionWs) {
+        var type = "item-option-1";
+        for (var i = 0; i < optionWs.length; i++) {
+          var rate = optionWs[i].clientWidth / a3width;
+          if (rate > 0.5) {
+            type = "item-option-4";
+            return type;
+          }
+          if (rate > 0.25) type = "item-option-2";
+        }
+        return type;
+      }
+      
+      
+    window.onload = function () {
+    
+        var dateList = eval(${htmlList!});
+
+        initialize(dateList,"${courseName!}","${courseNo!}");
+    };
+    
+  </script>
+</html>