浏览代码

Merge remote-tracking branch 'origin/dev' into dev

wangliang 4 年之前
父节点
当前提交
02277201f7
共有 20 个文件被更改,包括 1251 次插入83 次删除
  1. 102 14
      themis-backend/src/main/java/com/qmth/themis/backend/api/TEExamPaperController.java
  2. 5 0
      themis-backend/src/main/java/com/qmth/themis/backend/constant/BackendConstant.java
  3. 49 0
      themis-business/src/main/java/com/qmth/themis/business/config/SysConfig.java
  4. 2 0
      themis-business/src/main/java/com/qmth/themis/business/dao/TEExamCourseMapper.java
  5. 5 1
      themis-business/src/main/java/com/qmth/themis/business/dao/TEExamPaperMapper.java
  6. 21 0
      themis-business/src/main/java/com/qmth/themis/business/domain/MqConfigDomain.java
  7. 15 0
      themis-business/src/main/java/com/qmth/themis/business/domain/SysDomain.java
  8. 2 0
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamCourseService.java
  9. 2 0
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamPaperService.java
  10. 16 4
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamCourseServiceImpl.java
  11. 22 1
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamPaperServiceImpl.java
  12. 398 0
      themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskExamPaperImportTemplete.java
  13. 32 0
      themis-business/src/main/java/com/qmth/themis/business/util/OssUtil.java
  14. 14 0
      themis-business/src/main/resources/mapper/TEExamCourseMapper.xml
  15. 9 2
      themis-business/src/main/resources/mapper/TEExamPaperMapper.xml
  16. 66 60
      themis-common/pom.xml
  17. 478 0
      themis-common/src/main/java/com/qmth/themis/common/util/FileUtil.java
  18. 3 0
      themis-mq/src/main/java/com/qmth/themis/mq/listener/RocketTaskConsumer.java
  19. 5 0
      themis-task/src/main/java/com/qmth/themis/task/constant/TaskConstant.java
  20. 5 1
      themis-task/src/main/resources/application.properties

+ 102 - 14
themis-backend/src/main/java/com/qmth/themis/backend/api/TEExamPaperController.java

@@ -1,29 +1,53 @@
 package com.qmth.themis.backend.api;
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.annotation.Resource;
+
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.qmth.themis.backend.config.DictionaryConfig;
+import com.qmth.themis.backend.constant.BackendConstant;
 import com.qmth.themis.backend.util.ServletUtil;
 import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.business.entity.TBAttachment;
+import com.qmth.themis.business.entity.TBTaskHistory;
 import com.qmth.themis.business.entity.TBUser;
+import com.qmth.themis.business.entity.TEExam;
 import com.qmth.themis.business.entity.TEExamPaper;
 import com.qmth.themis.business.enums.FieldUniqueEnum;
+import com.qmth.themis.business.enums.MqEnum;
+import com.qmth.themis.business.enums.TaskTypeEnum;
+import com.qmth.themis.business.enums.TaskTypeExecEnum;
+import com.qmth.themis.business.service.TBAttachmentService;
+import com.qmth.themis.business.service.TBTaskHistoryService;
 import com.qmth.themis.business.service.TEExamPaperService;
+import com.qmth.themis.business.service.TEExamService;
 import com.qmth.themis.business.util.JacksonUtil;
 import com.qmth.themis.common.contanst.Constants;
 import com.qmth.themis.common.enums.ExceptionResultEnum;
 import com.qmth.themis.common.exception.BusinessException;
 import com.qmth.themis.common.util.Result;
 import com.qmth.themis.common.util.ResultUtil;
-import io.swagger.annotations.*;
-import org.springframework.dao.DuplicateKeyException;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.multipart.MultipartFile;
+import com.qmth.themis.mq.dto.MqDto;
+import com.qmth.themis.mq.service.MqDtoService;
 
-import javax.annotation.Resource;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 /**
  * @Description: 考试试卷 前端控制器
@@ -39,6 +63,21 @@ public class TEExamPaperController {
 
     @Resource
     TEExamPaperService teExamPaperService;
+    
+    @Resource
+    TBTaskHistoryService taskHistoryService;
+    
+    @Resource
+    TEExamService teExamService;
+    
+    @Resource
+    TBAttachmentService tbAttachmentService;
+    
+    @Resource
+    MqDtoService mqDtoService;
+
+    @Resource
+    DictionaryConfig dictionaryConfig;
 
     @ApiOperation(value = "考试试卷查询接口")
     @RequestMapping(value = "/query", method = RequestMethod.GET)
@@ -53,7 +92,7 @@ public class TEExamPaperController {
         QueryWrapper<TEExamPaper> teExamPaperQueryWrapper = new QueryWrapper<>();
         teExamPaperQueryWrapper.lambda().eq(TEExamPaper::getExamId, examId).eq(TEExamPaper::getCourseCode, courseCode);
         List<TEExamPaper> teExamPaperList = teExamPaperService.list(teExamPaperQueryWrapper);
-        Map map = new HashMap<>();
+        Map<String,List<TEExamPaper>> map = new HashMap<String,List<TEExamPaper>>();
         map.put(SystemConstant.RECORDS, teExamPaperList);
         return ResultUtil.ok(map);
     }
@@ -96,10 +135,59 @@ public class TEExamPaperController {
     @RequestMapping(value = "/import", method = RequestMethod.POST)
     @Transactional
     @ApiResponses({@ApiResponse(code = 200, message = "{\"taskId\":0}", response = Result.class)})
-    public Result importData(@ApiParam(value = "上传文件", required = true) @RequestParam MultipartFile file, @ApiParam(value = "上传文件类型", required = true) @RequestParam Integer type) {
-        if (Objects.isNull(file) || Objects.equals(file, "")) {
+    public Result importData(@ApiParam(value = "上传文件", required = true) @RequestParam MultipartFile file
+    		, @ApiParam(value = "批次ID", required = true) @RequestParam Long examId
+    		, @ApiParam(value = "客观题乱序", required = false) @RequestParam Boolean objectiveShuffle
+    		, @ApiParam(value = "选项乱序", required = false) @RequestParam Boolean optionShuffle
+    		, @ApiParam(value = "音频播放次数", required = false) @RequestParam Integer audioPlayTime) {
+    	if (file==null) {
             throw new BusinessException(ExceptionResultEnum.ATTACHMENT_IS_NULL);
         }
-        return ResultUtil.ok(JacksonUtil.parseJson(SystemConstant.SUCCESS));
+        if (examId==null) {
+            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
+        }
+        Map<String, Object> mapParameter = BackendConstant.getAttachmentEnv(3);
+        TBAttachment tbAttachment = null;
+        TBTaskHistory tbTaskHistory = null;
+        Map<String,Object> transMap = new HashMap<String,Object>();
+        try {
+            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
+            tbAttachment = tbAttachmentService.saveAttachment(file, ServletUtil.getRequestMd5(), ServletUtil.getRequestPath(), mapParameter, tbUser.getOrgId(), tbUser.getId());
+            if (Objects.isNull(tbAttachment)) {
+                throw new BusinessException(ExceptionResultEnum.ATTACHMENT_ERROR);
+            } else {
+                //往任务表里插一条数据
+                tbTaskHistory = new TBTaskHistory(TaskTypeEnum.import_exam_paper.ordinal(), tbAttachment.getId(), TaskTypeExecEnum.init.ordinal(), SystemConstant.IMPORT_INIT, 0d, tbAttachment.getName(), tbAttachment.getRemark(), tbUser.getId());
+                taskHistoryService.save(tbTaskHistory);
+                transMap.put("tbTaskHistory", tbTaskHistory);
+            }
+            transMap.put("examId", examId);
+            transMap.put("createId", tbUser.getId());
+
+            //先查询考试相关信息
+            TEExam teExam = teExamService.getById(examId);
+            if (Objects.isNull(teExam)) {
+                throw new BusinessException(ExceptionResultEnum.EXAM_NO);
+            }
+            transMap.put("objectiveShuffle", objectiveShuffle);
+            transMap.put("optionShuffle", optionShuffle);
+            transMap.put("audioPlayTime", audioPlayTime);
+            //mq发送消息start
+            MqDto mqDto = new MqDto(dictionaryConfig.mqConfigDomain().getTaskTopic(), dictionaryConfig.mqConfigDomain().getTaskTopicExamPaperImportTag(), transMap, MqEnum.TASK_LOG, String.valueOf(tbTaskHistory.getId()), tbUser.getName());
+            mqDtoService.assembleSendOneWayMsg(mqDto);
+            //mq发送消息end
+        } catch (Exception e) {
+            if (Objects.nonNull(tbAttachment)) {
+                tbAttachmentService.deleteAttachment(mapParameter, tbAttachment);
+            }
+            if (e instanceof BusinessException) {
+                throw new BusinessException(e.getMessage());
+            } else {
+                throw new RuntimeException(e);
+            }
+        }
+        Map<String,Long> map = new HashMap<String,Long>();
+        map.put(SystemConstant.TASK_ID, tbTaskHistory.getId());
+        return ResultUtil.ok(map);
     }
 }

+ 5 - 0
themis-backend/src/main/java/com/qmth/themis/backend/constant/BackendConstant.java

@@ -42,4 +42,9 @@ public class BackendConstant {
         mapParameter.put(SystemConstant.UPLOAD_TYPE, uploadType);
         return mapParameter;
     }
+    
+    public static String getTempDir() {
+        DictionaryConfig dictionaryConfig = SpringContextHolder.getBean(DictionaryConfig.class);
+        return dictionaryConfig.sysDomain().getTempDataDir();
+    }
 }

+ 49 - 0
themis-business/src/main/java/com/qmth/themis/business/config/SysConfig.java

@@ -0,0 +1,49 @@
+package com.qmth.themis.business.config;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description: 
+ * @Author: xiatian
+ * @Date: 2020-07-21
+ */
+@Component
+public class SysConfig {
+
+    public String getTempDir() {
+    	String backendProject = "backend",
+    			taskProject = "task",
+    			backendPath = "com.qmth.themis.backend.constant.BackendConstant",
+    			taskPath = "com.qmth.themis.task.constant.TaskConstant",
+    			methodName = "getTempDir";
+        try {
+            String path = this.getClass().getResource(File.separator).getPath();
+            Class clazz = null;
+            try {
+                if (path.contains(backendProject)) {
+                    clazz = Class.forName(backendPath);
+                } else if (path.contains(taskProject)) {
+                    clazz = Class.forName(taskPath);
+                }
+            } catch (ClassNotFoundException e) {
+                e.printStackTrace();
+            }
+            Object o = clazz.newInstance();
+            Method method = clazz.getDeclaredMethod(methodName);
+            return (String) method.invoke(o);
+        } catch (NoSuchMethodException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+        } catch (InstantiationException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 2 - 0
themis-business/src/main/java/com/qmth/themis/business/dao/TEExamCourseMapper.java

@@ -29,4 +29,6 @@ public interface TEExamCourseMapper extends BaseMapper<TEExamCourse> {
      * @return
      */
     public IPage<TEExamCourse> examCourseQuery(IPage<Map> iPage, @Param("examId") Long examId, @Param("courseCode") String courseCode, @Param("courseName") String courseName, @Param("hasPaper") Integer hasPaper);
+
+    public TEExamCourse findByCode(@Param("courseCode") String courseCode);
 }

+ 5 - 1
themis-business/src/main/java/com/qmth/themis/business/dao/TEExamPaperMapper.java

@@ -1,8 +1,10 @@
 package com.qmth.themis.business.dao;
 
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.qmth.themis.business.entity.TEExamPaper;
-import org.apache.ibatis.annotations.Mapper;
 
 /**
  * @Description: 考试试卷 Mapper 接口
@@ -14,4 +16,6 @@ import org.apache.ibatis.annotations.Mapper;
 @Mapper
 public interface TEExamPaperMapper extends BaseMapper<TEExamPaper> {
 
+	public TEExamPaper findByExamIdAndCourseCodeAndPaperCode(@Param("examId") Long examId,
+			@Param("courseCode") String courseCode, @Param("paperCode") String paperCode);
 }

+ 21 - 0
themis-business/src/main/java/com/qmth/themis/business/domain/MqConfigDomain.java

@@ -48,6 +48,9 @@ public class MqConfigDomain {
     private String taskConsumerRoomCodeImportGroup;
     private String taskTopicRoomCodeExportTag;
     private String taskConsumerRoomCodeExportGroup;
+    
+    private String taskTopicExamPaperImportTag;
+    private String taskConsumerExamPaperImportGroup;
 
     public String getTaskTopicRoomCodeExportTag() {
         return taskTopicRoomCodeExportTag;
@@ -265,4 +268,22 @@ public class MqConfigDomain {
     public void setUserLogConsumerStudentGroup(String userLogConsumerStudentGroup) {
         this.userLogConsumerStudentGroup = userLogConsumerStudentGroup;
     }
+
+	public String getTaskTopicExamPaperImportTag() {
+		return taskTopicExamPaperImportTag;
+	}
+
+	public void setTaskTopicExamPaperImportTag(String taskTopicExamPaperImportTag) {
+		this.taskTopicExamPaperImportTag = taskTopicExamPaperImportTag;
+	}
+
+	public String getTaskConsumerExamPaperImportGroup() {
+		return taskConsumerExamPaperImportGroup;
+	}
+
+	public void setTaskConsumerExamPaperImportGroup(String taskConsumerExamPaperImportGroup) {
+		this.taskConsumerExamPaperImportGroup = taskConsumerExamPaperImportGroup;
+	}
+    
+    
 }

+ 15 - 0
themis-business/src/main/java/com/qmth/themis/business/domain/SysDomain.java

@@ -20,6 +20,11 @@ public class SysDomain {
     String fileHost;
 
     String serverHost;
+    
+    /**
+     * 本地临时文件目录
+     */
+    String tempDataDir;
 
     public String getFileHost() {
         return fileHost;
@@ -60,4 +65,14 @@ public class SysDomain {
     public void setServerUpload(String serverUpload) {
         this.serverUpload = serverUpload;
     }
+
+	public String getTempDataDir() {
+		return tempDataDir;
+	}
+
+	public void setTempDataDir(String tempDataDir) {
+		this.tempDataDir = tempDataDir;
+	}
+    
+    
 }

+ 2 - 0
themis-business/src/main/java/com/qmth/themis/business/service/TEExamCourseService.java

@@ -26,4 +26,6 @@ public interface TEExamCourseService extends IService<TEExamCourse> {
      * @return
      */
     public IPage<TEExamCourse> examCourseQuery(IPage<Map> iPage, Long examId, String courseCode, String courseName, Integer hasPaper);
+
+	TEExamCourse findByCode(String courseCode);
 }

+ 2 - 0
themis-business/src/main/java/com/qmth/themis/business/service/TEExamPaperService.java

@@ -12,4 +12,6 @@ import com.qmth.themis.business.entity.TEExamPaper;
  */
 public interface TEExamPaperService extends IService<TEExamPaper> {
 
+	TEExamPaper findByExamIdAndCourseCodeAndPaperCode(Long examId, String courseCode, String paperCode);
+
 }

+ 16 - 4
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamCourseServiceImpl.java

@@ -1,14 +1,18 @@
 package com.qmth.themis.business.service.impl;
 
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmth.themis.business.dao.TEExamCourseMapper;
 import com.qmth.themis.business.entity.TEExamCourse;
 import com.qmth.themis.business.service.TEExamCourseService;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.util.Map;
+import com.qmth.themis.common.exception.BusinessException;
 
 /**
  * @Description: 考试科目 服务实现类
@@ -37,4 +41,12 @@ public class TEExamCourseServiceImpl extends ServiceImpl<TEExamCourseMapper, TEE
     public IPage<TEExamCourse> examCourseQuery(IPage<Map> iPage, Long examId, String courseCode, String courseName, Integer hasPaper) {
         return teExamCourseMapper.examCourseQuery(iPage, examId, courseCode, courseName, hasPaper);
     }
+    
+    @Override
+    public TEExamCourse findByCode(String courseCode) {
+    	if(StringUtils.isBlank(courseCode)) {
+    		throw new BusinessException("courseCode is null");
+    	}
+    	return teExamCourseMapper.findByCode(courseCode);
+    }
 }

+ 22 - 1
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamPaperServiceImpl.java

@@ -1,10 +1,15 @@
 package com.qmth.themis.business.service.impl;
 
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmth.themis.business.dao.TEExamPaperMapper;
 import com.qmth.themis.business.entity.TEExamPaper;
 import com.qmth.themis.business.service.TEExamPaperService;
-import org.springframework.stereotype.Service;
+import com.qmth.themis.common.exception.BusinessException;
 
 /**
  * @Description: 考试试卷 服务实现类
@@ -16,4 +21,20 @@ import org.springframework.stereotype.Service;
 @Service
 public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExamPaper> implements TEExamPaperService {
 
+	@Resource
+	TEExamPaperMapper teExamPaperMapper;
+
+	@Override
+	public TEExamPaper findByExamIdAndCourseCodeAndPaperCode(Long examId, String courseCode, String paperCode) {
+		if (examId == null) {
+			throw new BusinessException("examId is null");
+		}
+		if (StringUtils.isBlank(courseCode)) {
+			throw new BusinessException("courseCode is null");
+		}
+		if (StringUtils.isBlank(paperCode)) {
+			throw new BusinessException("paperCode is null");
+		}
+		return teExamPaperMapper.findByExamIdAndCourseCodeAndPaperCode(examId, courseCode, paperCode);
+	}
 }

+ 398 - 0
themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskExamPaperImportTemplete.java

@@ -0,0 +1,398 @@
+package com.qmth.themis.business.templete.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.qmth.themis.business.config.EnvConfig;
+import com.qmth.themis.business.config.SysConfig;
+import com.qmth.themis.business.constant.SpringContextHolder;
+import com.qmth.themis.business.constant.SystemConstant;
+import com.qmth.themis.business.entity.TEExam;
+import com.qmth.themis.business.entity.TEExamCourse;
+import com.qmth.themis.business.entity.TEExamPaper;
+import com.qmth.themis.business.service.TEExamCourseService;
+import com.qmth.themis.business.service.TEExamPaperService;
+import com.qmth.themis.business.service.TEExamService;
+import com.qmth.themis.business.templete.TaskImportCommon;
+import com.qmth.themis.business.templete.TaskImportTemplete;
+import com.qmth.themis.business.util.JacksonUtil;
+import com.qmth.themis.business.util.OssUtil;
+import com.qmth.themis.common.enums.ExceptionResultEnum;
+import com.qmth.themis.common.exception.BusinessException;
+import com.qmth.themis.common.util.FileUtil;
+import com.qmth.themis.common.util.Result;
+import com.qmth.themis.common.util.ResultUtil;
+
+import cn.hutool.core.date.DateUtil;
+
+/**
+ * @Description: 试卷导入
+ * @Author: xiatian
+ * @Date: 2020-07-21
+ */
+@Service("taskExamPaperImportTemplete")
+public class TaskExamPaperImportTemplete implements TaskImportTemplete {
+	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
+	@Resource
+	TEExamService teExamService;
+	@Resource
+	SysConfig sysConfig;
+	@Resource
+	TEExamCourseService teExamCourseService;
+	@Resource
+	TEExamPaperService teExamPaperService;
+
+	@Override
+	@Transactional
+	public Result importTask(Map<String, Object> map) throws IOException {
+		long start = System.currentTimeMillis();
+		TaskImportCommon taskImportCommon = new TaskImportCommon(map);
+		taskImportCommon.init();
+		StringBuilder result = new StringBuilder();
+		String timeFormat = taskImportCommon.getTimeFormat();
+		addResult(result, DateUtil.format(new Date(), timeFormat) + "处理开始");
+		File file = null;
+		try {
+			file = taskImportCommon.getUploadFile();
+			dispose(file, map, result);
+		} catch (Exception e) {
+			addResult(result, "处理出错 " + e.getMessage());
+			throw e;
+		} finally {
+			if (file != null) {
+				file.delete();
+			}
+			addResult(result, DateUtil.format(new Date(), timeFormat) + "处理结束");
+			long end = System.currentTimeMillis();
+			addResult(result, "耗时(秒):" + ((end - start) / 1000));
+			taskImportCommon.writeImportResultTxt(result.toString());
+		}
+		return ResultUtil.ok(JacksonUtil.parseJson(SystemConstant.SUCCESS));
+	}
+
+	private void dispose(File file, Map<String, Object> map, StringBuilder result) throws IOException {
+		Long examId = (Long) map.get("examId");
+
+		TEExam teExam = teExamService.getById(examId);
+		if (Objects.isNull(teExam)) {
+			throw new BusinessException(ExceptionResultEnum.EXAM_NO);
+		}
+
+		String tempDir = sysConfig.getTempDir();
+		String dir = tempDir + "/" + uuid() + "/";
+		File dfile = new File(dir);
+		try {
+			dfile.mkdirs();
+			FileUtil.unZip(dfile, file);
+			if (!hasErr(dir, examId, dfile, result)) {
+				for (File courseDir : dfile.listFiles()) {
+					if (courseDir.isDirectory()) {
+						disposeCourseDir(dir, teExam, courseDir, map);
+					}
+				}
+			}
+		} finally {
+			FileUtil.deleteFolder(dir);
+		}
+	}
+
+	private void disposeCourseDir(String rootDir, TEExam teExam, File courseDir, Map<String, Object> map) {
+		String courseCode = courseDir.getName();
+		TEExamCourse course = teExamCourseService.findByCode(courseCode);
+		if (course == null) {
+			throw new BusinessException("科目编码不存在 " + courseCode);
+		}
+		File[] childs = courseDir.listFiles();
+		for (File paperDir : childs) {
+			if (paperDir.isDirectory()) {
+				disposePaperDir(rootDir, teExam, course, paperDir, map);
+			}
+		}
+		Boolean objectiveShuffle = (Boolean) map.get("objectiveShuffle");
+		Boolean optionShuffle = (Boolean) map.get("optionShuffle");
+		Integer audioPlayTime = (Integer) map.get("audioPlayTime");
+		if(objectiveShuffle!=null) {
+			course.setObjectiveShuffle(objectiveShuffle?1:0);
+		}
+		if(optionShuffle!=null) {
+			course.setOptionShuffle(optionShuffle?1:0);
+		}
+		if(audioPlayTime!=null) {
+			course.setAudioPlayTime(audioPlayTime);
+		}
+		if(objectiveShuffle!=null||optionShuffle!=null||audioPlayTime!=null) {
+			teExamCourseService.save(course);
+		}
+	}
+
+	private void disposePaperDir(String rootDir, TEExam teExam, TEExamCourse course, File paperDir,
+			Map<String, Object> map) {
+		String paperCode = paperDir.getName();
+		File[] childs = paperDir.listFiles();
+
+		File paperFile = null;
+		File answerFile = null;
+		File attachmentDir = null;
+		for (File cfile : childs) {
+			if ("paper.json".equals(cfile.getName()) && cfile.isFile()) {
+				paperFile = cfile;
+			}
+			if ("answer.json".equals(cfile.getName()) && cfile.isFile()) {
+				answerFile = cfile;
+			}
+			if ("attachment".equals(cfile.getName()) && cfile.isDirectory()) {
+				if (cfile.listFiles() != null && cfile.listFiles().length != 0) {
+					attachmentDir = cfile;
+				}
+			}
+		}
+		TEExamPaper paper = teExamPaperService.findByExamIdAndCourseCodeAndPaperCode(teExam.getId(),
+				course.getCourseCode(), paperCode);
+		if (paper == null) {
+			paper = new TEExamPaper();
+			Long createId = (Long) map.get("createId");
+			JSONObject paperJson = JSONObject.parseObject(FileUtil.readFileContent(paperFile));
+			paper.setName(paperJson.getString("name"));
+			paper.setTotalScore(paperJson.getDouble("totalScore"));
+			paper.setDecryptSecret(RandomStringUtils.randomAlphanumeric(16));
+			paper.setEncryptMode(0);
+			paper.setExamId(teExam.getId());
+			paper.setCode(paperCode);
+			paper.setHasAudio(paperJson.getInteger("hasAudio"));
+			paper.setCreateId(createId);
+			paper.setCourseCode(course.getCourseCode());
+			paper.setDecryptVector(RandomStringUtils.randomAlphanumeric(16));
+		}
+		disposePaper(rootDir, paper, paperFile, attachmentDir);
+		disposeStruct(rootDir, paper, paperFile);
+		disposeAnswer(rootDir, paper, answerFile);
+		
+		teExamPaperService.save(paper);
+	}
+
+	private void disposePaper(String rootDir, TEExamPaper paper, File paperFile, File attachmentDir) {
+		if (paperFile == null) {
+			return;
+		}
+		File zip = new File(rootDir + uuid() + ".zip");
+		List<File> files = new ArrayList<File>();
+		files.add(paperFile);
+		if (attachmentDir != null) {
+			files.add(attachmentDir);
+		}
+		FileUtil.doZip(zip, files);
+		File encryptZip = new File(rootDir + uuid() + ".zip");
+		FileUtil.encryptFile(zip, encryptZip, paper.getDecryptSecret(), paper.getDecryptVector());
+		String filePath = sdf.format(new Date()) + "/" + uuid() + ".zip";
+		paper.setPaperPath(filePath);
+		EnvConfig env = SpringContextHolder.getBean(EnvConfig.class);
+		OssUtil.ossUpload(env.getOssEnv(3), filePath, encryptZip);
+	}
+
+	private void disposeStruct(String rootDir, TEExamPaper paper, File paperFile) {
+		if (paperFile == null) {
+			return;
+		}
+		JSONObject paperJson = JSONObject.parseObject(FileUtil.readFileContent(paperFile));
+		JSONArray details = paperJson.getJSONArray("details");
+		for (int i = 0; i < details.size(); i++) {
+			JSONArray questions = details.getJSONObject(i).getJSONArray("questions");
+			for (int j = 0; j < questions.size(); j++) {
+				JSONObject question = questions.getJSONObject(j);
+				question.remove("body");
+				if (question.getInteger("structType").intValue() == 6) {
+					JSONArray subQuestions = question.getJSONArray("subQuestions");
+					for (int k = 0; k < subQuestions.size(); k++) {
+						JSONObject subquestion = subQuestions.getJSONObject(k);
+						subquestion.remove("body");
+					}
+				}
+			}
+		}
+
+		File file = new File(rootDir + uuid() + ".json");
+		FileUtil.saveAsFile(file.getAbsolutePath(), paperJson.toJSONString());
+		String filePath = sdf.format(new Date()) + "/" + uuid() + ".json";
+		paper.setStructPath(filePath);
+		EnvConfig env = SpringContextHolder.getBean(EnvConfig.class);
+		OssUtil.ossUpload(env.getOssEnv(3), filePath, file);
+	}
+
+	private void disposeAnswer(String rootDir, TEExamPaper paper, File answerFile) {
+		if (answerFile == null) {
+			return;
+		}
+		File encryptFile = new File(rootDir + uuid() + ".json");
+		FileUtil.encryptFile(answerFile, encryptFile, paper.getDecryptSecret(), paper.getDecryptVector());
+		String filePath = sdf.format(new Date()) + "/" + uuid() + ".json";
+		paper.setAnswerPath(filePath);
+		EnvConfig env = SpringContextHolder.getBean(EnvConfig.class);
+		OssUtil.ossUpload(env.getOssEnv(3), filePath, encryptFile);
+	}
+
+	private void addResult(StringBuilder result, String msg) {
+		result.append(msg).append("\r\n");
+	}
+
+	private boolean hasErr(String rootDir, Long examId, File dfile, StringBuilder result) {
+		boolean hasErr = false;
+		for (File courseDir : dfile.listFiles()) {// 校验每一个科目
+			if (courseDir.isDirectory()) {
+				String courseCode = courseDir.getName();
+				TEExamCourse course = teExamCourseService.findByCode(courseCode);
+				if (course == null) {
+					addResult(result, "科目编码不存在 " + courseCode);
+					hasErr = true;
+					continue;
+				}
+				File[] childs = courseDir.listFiles();
+				if (childs == null || childs.length == 0) {
+					addResult(result, "科目 " + courseCode + " 没有需要导入的文件");
+					hasErr = true;
+					continue;
+				}
+				for (File paperDir : childs) {// 校验每一套试卷
+					if (paperDir.isDirectory()) {
+						String paperCode = paperDir.getName();
+						File[] paperDirChilds = paperDir.listFiles();
+						if (paperDirChilds == null || paperDirChilds.length == 0) {
+							addResult(result, "科目 " + courseCode + " 试卷 " + paperCode + " 没有需要导入的文件");
+							hasErr = true;
+							continue;
+						}
+						try {
+							// 校验答案
+							checkAnswerFile(rootDir, paperDirChilds, examId, courseCode, paperCode);
+						} catch (Exception e) {
+							addResult(result, e.getMessage());
+							hasErr = true;
+							continue;
+						}
+					}
+				}
+			}
+		}
+		return hasErr;
+	}
+
+	private void checkAnswerFile(String rootDir, File[] paperDirChilds, Long examId, String courseCode,
+			String paperCode) {
+		File paperFile = null;
+		File answerFile = null;
+		for (File cfile : paperDirChilds) {// 校验试卷下的答案
+			if ("paper.json".equals(cfile.getName()) && cfile.isFile()) {
+				paperFile = cfile;
+			}
+			if ("answer.json".equals(cfile.getName()) && cfile.isFile()) {
+				answerFile = cfile;
+			}
+		}
+		if (answerFile != null) {
+			if (paperFile == null) {// 没有试卷信息,不能先导入答案
+				TEExamPaper paper = teExamPaperService.findByExamIdAndCourseCodeAndPaperCode(examId, courseCode,
+						paperCode);
+				if (paper == null) {
+					throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 没有试卷信息,不能先导入答案");
+				}
+				String structPath = paper.getStructPath();
+				EnvConfig env = SpringContextHolder.getBean(EnvConfig.class);
+				String url = OssUtil.getUrlForPrivateBucket(env.getOssEnv(3), structPath);
+				String name = structPath.substring(structPath.lastIndexOf("/") + 1);
+				File structFile = new File(rootDir + name);
+				try {
+					FileUtil.saveUrlAsFile(url, structFile);
+					compareAnswerAndPaper(courseCode, paperCode, answerFile, structFile);
+				} finally {
+					if (structFile.exists()) {
+						structFile.delete();
+					}
+				}
+			} else {
+				compareAnswerAndPaper(courseCode, paperCode, answerFile, paperFile);
+			}
+		}
+	}
+
+	private void compareAnswerAndPaper(String courseCode, String paperCode, File answerFile, File structFile) {
+		JSONObject answerJson = JSONObject.parseObject(FileUtil.readFileContent(answerFile));
+		JSONArray answerdetails = answerJson.getJSONArray("details");
+		JSONObject structJson = JSONObject.parseObject(FileUtil.readFileContent(structFile));
+		JSONArray structdetails = structJson.getJSONArray("details");
+		if (answerdetails.size() != structdetails.size()) {
+			throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷大题数量不一致");
+		}
+		for (int i = 0; i < answerdetails.size(); i++) {
+			JSONArray answerdetailquestions = answerdetails.getJSONObject(i).getJSONArray("questions");
+			JSONArray structdetailquestions = structdetails.getJSONObject(i).getJSONArray("questions");
+			if (answerdetailquestions.size() != structdetailquestions.size()) {
+				throw new BusinessException(
+						"科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1) + "大题的小题数量不一致");
+			}
+			for (int j = 0; j < answerdetailquestions.size(); j++) {
+				JSONObject answerquestion = answerdetailquestions.getJSONObject(j);
+				JSONObject structquestion = structdetailquestions.getJSONObject(j);
+				if (structquestion.getInteger("structType").intValue() == 1
+						|| structquestion.getInteger("structType").intValue() == 2) {
+					JSONArray answer = answerquestion.getJSONArray("answer");
+					if (answer == null || answer.size() == 0) {
+						throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1)
+								+ "大题第" + (j + 1) + "小题没有答案");
+					}
+				}
+				if (structquestion.getInteger("structType").intValue() == 3) {
+					Boolean answer = answerquestion.getBoolean("answer");
+					if (answer == null) {
+						throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1)
+								+ "大题第" + (j + 1) + "小题没有答案");
+					}
+				}
+				if (structquestion.getInteger("structType").intValue() == 6) {
+					JSONArray answersubQuestions = answerquestion.getJSONArray("subQuestions");
+					JSONArray structsubQuestions = structquestion.getJSONArray("subQuestions");
+					if (answersubQuestions.size() != structsubQuestions.size()) {
+						throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1)
+								+ "大题第" + (j + 1) + "小题子题数量不一致");
+					}
+					for (int k = 0; k < answersubQuestions.size(); k++) {
+						JSONObject answersubquestion = answerdetailquestions.getJSONObject(k);
+						JSONObject structsubquestion = structdetailquestions.getJSONObject(k);
+						if (structsubquestion.getInteger("structType").intValue() == 1
+								|| structsubquestion.getInteger("structType").intValue() == 2) {
+							JSONArray answer = answersubquestion.getJSONArray("answer");
+							if (answer == null || answer.size() == 0) {
+								throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第"
+										+ (i + 1) + "大题第" + (j + 1) + "小题第" + (k + 1) + "子题没有答案");
+							}
+						}
+						if (structsubquestion.getInteger("structType").intValue() == 3) {
+							Boolean answer = answersubquestion.getBoolean("answer");
+							if (answer == null) {
+								throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第"
+										+ (i + 1) + "大题第" + (j + 1) + "小题第" + (k + 1) + "子题没有答案");
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	private String uuid() {
+		return UUID.randomUUID().toString().replaceAll("-", "");
+	}
+}

+ 32 - 0
themis-business/src/main/java/com/qmth/themis/business/util/OssUtil.java

@@ -12,6 +12,8 @@ import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URL;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -180,4 +182,34 @@ public class OssUtil {
         // 关闭OSSClient。
         ossClient.shutdown();
     }
+    
+    /** 获取私有bucket文件访问url,一分钟有效
+     * @param map
+     * @param objectPath
+     * @param inputStream
+     * @return
+     */
+    public static String getUrlForPrivateBucket(Map<String, Object> map, String objectPath) {
+        String endpoint = String.valueOf(map.get(SystemConstant.END_POINT));
+        String accessKeyId = String.valueOf(map.get(SystemConstant.ACCESS_KEY_ID));
+        String accessKeySecret = String.valueOf(map.get(SystemConstant.ACCESS_KEY_SECRET));
+        String bucketName = String.valueOf(map.get(SystemConstant.BUCKET));
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        Date expiration = new Date(new Date().getTime() + 60 * 1000);
+        GeneratePresignedUrlRequest generatePresignedUrlRequest ;
+        generatePresignedUrlRequest =new GeneratePresignedUrlRequest(bucketName, objectPath);
+        generatePresignedUrlRequest.setExpiration(expiration);
+        URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest);
+        return url.toString();
+    }
+    
+    public static void ossUpload(Map<String, Object> map, String path, File file) {
+        String endpoint = String.valueOf(map.get(SystemConstant.END_POINT));
+        String accessKeyId = String.valueOf(map.get(SystemConstant.ACCESS_KEY_ID));
+        String accessKeySecret = String.valueOf(map.get(SystemConstant.ACCESS_KEY_SECRET));
+        String bucketName = String.valueOf(map.get(SystemConstant.BUCKET));
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, path, file);
+        ossClient.putObject(putObjectRequest);
+    }
 }

+ 14 - 0
themis-business/src/main/resources/mapper/TEExamCourseMapper.xml

@@ -27,4 +27,18 @@
             </if>
         </where>
     </select>
+    
+    <select id="findByCode" resultType="com.qmth.themis.business.entity.TEExamCourse">
+        select * from t_e_exam_course teec
+        <where>
+        	1=1
+            <if test="courseCode != null and courseCode != ''">
+                and teec.course_code =#{courseCode}
+            </if>
+            <if test="courseCode == null or courseCode == ''">
+                1=2
+            </if>
+        </where>
+    </select>
+    
 </mapper>

+ 9 - 2
themis-business/src/main/resources/mapper/TEExamPaperMapper.xml

@@ -1,5 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.qmth.themis.business.dao.TEExamPaperMapper">
-
+<mapper
+	namespace="com.qmth.themis.business.dao.TEExamPaperMapper">
+	<select id="findByExamIdAndCourseCodeAndPaperCode"
+		resultType="com.qmth.themis.business.entity.TEExamPaper">
+		select * from t_e_exam_paper t
+		where t.exam_id=#{examId} and
+		t.course_code=#{courseCode}
+		and t.code =#{paperCode}
+	</select>
 </mapper>

+ 66 - 60
themis-common/pom.xml

@@ -1,64 +1,70 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <groupId>com.qmth.themis.common</groupId>
-    <artifactId>themis-common</artifactId>
-    <version>1.0.0</version>
-    <packaging>jar</packaging>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.qmth.themis.common</groupId>
+	<artifactId>themis-common</artifactId>
+	<version>1.0.0</version>
+	<packaging>jar</packaging>
 
-    <parent>
-        <groupId>com.qmth.themis</groupId>
-        <artifactId>themis-service</artifactId>
-        <version>1.0.0</version>
-    </parent>
+	<parent>
+		<groupId>com.qmth.themis</groupId>
+		<artifactId>themis-service</artifactId>
+		<version>1.0.0</version>
+	</parent>
 
-    <dependencies>
-        <dependency>
-            <groupId>com.alibaba</groupId>
-            <artifactId>fastjson</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-annotations</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.google.code.gson</groupId>
-            <artifactId>gson</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>cn.hutool</groupId>
-            <artifactId>hutool-all</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.bouncycastle</groupId>
-            <artifactId>bcprov-jdk15on</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-web</artifactId>
-            <version>5.2.7.RELEASE</version>
-        </dependency>
-        <!--        <dependency>-->
-        <!--            <groupId>org.slf4j</groupId>-->
-        <!--            <artifactId>slf4j-api</artifactId>-->
-        <!--            <version>2.0.0-alpha1</version>-->
-        <!--        </dependency>-->
-    </dependencies>
+	<dependencies>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-annotations</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.google.code.gson</groupId>
+			<artifactId>gson</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>cn.hutool</groupId>
+			<artifactId>hutool-all</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.bouncycastle</groupId>
+			<artifactId>bcprov-jdk15on</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-web</artifactId>
+			<version>5.2.7.RELEASE</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>2.6</version>
+		</dependency>
+		<!-- <dependency> -->
+		<!-- <groupId>org.slf4j</groupId> -->
+		<!-- <artifactId>slf4j-api</artifactId> -->
+		<!-- <version>2.0.0-alpha1</version> -->
+		<!-- </dependency> -->
+	</dependencies>
 </project>

+ 478 - 0
themis-common/src/main/java/com/qmth/themis/common/util/FileUtil.java

@@ -0,0 +1,478 @@
+package com.qmth.themis.common.util;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.io.IOUtils;
+
+import com.qmth.themis.common.exception.BusinessException;
+
+public class FileUtil {
+	/**
+	 * 将网络文件保存到本地
+	 *
+	 * @param fileUrl       网络文件URL
+	 * @param localFilePath 例如D:/123.txt
+	 * @throws IOException
+	 */
+	public static void saveUrlAsFile(String fileUrl, String localFilePath) {
+		HttpURLConnection connection = null;
+		DataOutputStream dataOutputStream = null;
+		DataInputStream dataInputStream = null;
+		try {
+			URL url = new URL(fileUrl);
+
+			connection = (HttpURLConnection) url.openConnection();
+
+			dataInputStream = new DataInputStream(connection.getInputStream());
+			FileOutputStream fileOutputStream = new FileOutputStream(localFilePath);
+			dataOutputStream = new DataOutputStream(fileOutputStream);
+
+			byte[] buffer = new byte[4096];
+			int count;
+			while ((count = dataInputStream.read(buffer)) > 0) {
+				dataOutputStream.write(buffer, 0, count);
+			}
+			fileOutputStream.flush();
+			dataOutputStream.flush();
+		} catch (IOException e) {
+			throw new BusinessException("下载文件出错" + e.getMessage());
+		} finally {
+			if (connection != null) {
+				connection.disconnect();
+			}
+			if (dataOutputStream != null) {
+				try {
+					dataOutputStream.close();
+				} catch (IOException e) {
+				}
+			}
+			if (dataInputStream != null) {
+				try {
+					dataInputStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+	}
+
+	/**
+	 * 将网络文件保存到本地
+	 * 
+	 * @param fileUrl   网络文件URL
+	 * @param localFile 本地文件对象
+	 * @throws IOException
+	 */
+	public static void saveUrlAsFile(String fileUrl, File localFile) {
+
+		saveUrlAsFile(fileUrl, localFile.getAbsolutePath());
+	}
+
+	/**
+	 * 读取文件内容
+	 *
+	 * @param file
+	 * @return
+	 * @throws IOException
+	 */
+	public static String readFileContent(File file) {
+		StringBuilder content = new StringBuilder();
+		InputStreamReader streamReader = null;
+		BufferedReader bufferedReader = null;
+		try {
+			String encoding = "UTF-8";
+			if (file.exists() && file.isFile()) {
+				streamReader = new InputStreamReader(new FileInputStream(file), encoding);
+				bufferedReader = new BufferedReader(streamReader);
+				String line;
+				while ((line = bufferedReader.readLine()) != null) {
+					content.append(line);
+				}
+			}
+		} catch (IOException e) {
+			throw new BusinessException("读取文件出错" + e.getMessage());
+		} finally {
+			if (streamReader != null) {
+				try {
+					streamReader.close();
+				} catch (IOException e) {
+				}
+			}
+			if (bufferedReader != null) {
+				try {
+					bufferedReader.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+		return content.toString();
+	}
+
+	/**
+	 * 创建文件目录
+	 */
+	public static boolean makeDirs(String path) {
+		if (path == null || "".equals(path)) {
+			return false;
+		}
+		File folder = new File(path);
+		if (!folder.exists()) {
+			return folder.mkdirs();
+		}
+		return true;
+	}
+
+	/**
+	 * 保存字符串到文件中 @throws IOException @throws
+	 */
+	public static void saveAsFile(String path, String content) {
+		saveAsFile(path, content, null);
+	}
+
+	public static void saveAsFile(String path, String content, String encoding) {
+		if (path == null || content == null) {
+			return;
+		}
+
+		if (encoding == null) {
+			encoding = "UTF-8";
+		}
+		BufferedWriter bw = null;
+		try {
+			File file = new File(path);
+			if (!file.exists()) {
+				if (FileUtil.makeDirs(file.getParent())) {
+					boolean ok = file.createNewFile();
+					if (!ok) {
+						throw new BusinessException("文件创建失败!");
+					}
+				}
+			}
+
+			FileOutputStream fos = new FileOutputStream(file);
+			OutputStreamWriter write = new OutputStreamWriter(fos, encoding);
+			bw = new BufferedWriter(write);
+			bw.write(content);
+			bw.flush();
+		} catch (IOException e) {
+			throw new BusinessException("保存文件出错" + e.getMessage());
+		} finally {
+			if (bw != null) {
+				try {
+					bw.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+	}
+
+	/**
+	 * 解压文件
+	 *
+	 * @param targetDir 解压目录
+	 * @param zipFile   待解压的ZIP文件
+	 * @throws IOException
+	 */
+	public static List<File> unZip(File targetDir, File zipFile) throws IOException {
+		if (targetDir == null) {
+			throw new BusinessException("解压目录不能为空!");
+		}
+
+		if (zipFile == null) {
+			throw new BusinessException("待解压的文件不能为空!");
+		}
+
+		if (!zipFile.exists()) {
+			throw new BusinessException("待解压的文件不存在!" + zipFile.getAbsolutePath());
+		}
+
+		String zipName = zipFile.getName().toLowerCase();
+		if (zipFile.isDirectory() || zipName.indexOf(".zip") < 0) {
+			throw new BusinessException("待解压的文件格式错误!");
+		}
+
+		if (!targetDir.exists()) {
+			targetDir.mkdir();
+		}
+
+		List<File> result = new LinkedList<>();
+
+		try (ZipFile zip = new ZipFile(zipFile, Charset.forName("UTF-8"));) {
+
+			Enumeration entries = zip.entries();
+			while (entries.hasMoreElements()) {
+				ZipEntry entry = (ZipEntry) entries.nextElement();
+
+				// Linux中需要替换掉路径的反斜杠
+				String entryName = (File.separator + entry.getName()).replaceAll("\\\\", "/");
+
+				String filePath = targetDir.getAbsolutePath() + entryName;
+				File target = new File(filePath);
+				if (entry.isDirectory()) {
+					target.mkdirs();
+				} else {
+					File dir = target.getParentFile();
+					if (!dir.exists()) {
+						dir.mkdirs();
+					}
+
+					try (OutputStream os = new FileOutputStream(target); InputStream is = zip.getInputStream(entry);) {
+						IOUtils.copy(is, os);
+						os.flush();
+					}
+					result.add(target);
+				}
+			}
+
+		}
+
+		return result;
+	}
+
+	/**
+	 * 文件压缩
+	 *
+	 * @param target 目录或文件 @param zipFile 压缩后的ZIP文件 @throws IOException @throws
+	 */
+	public static boolean doZip(File target, File zipFile) throws IOException {
+		if (target == null || !target.exists()) {
+			throw new BusinessException("目录或文件不能为空!");
+		}
+
+		if (zipFile == null) {
+			throw new BusinessException("待压缩的文件不能为空!");
+		}
+
+		try (OutputStream outStream = new FileOutputStream(zipFile);
+				ZipOutputStream zipOutStream = new ZipOutputStream(outStream, Charset.forName("UTF-8"));) {
+			if (!zipFile.exists()) {
+				boolean ok = zipFile.createNewFile();
+				if (!ok) {
+					throw new BusinessException("压缩的文件创建失败!");
+				}
+			}
+
+			if (target.isDirectory()) {
+				File[] files = target.listFiles();
+				if (files.length == 0) {
+					throw new BusinessException("文件夹内未找到任何文件!");
+				}
+
+				for (File file : files) {
+					doZip(zipOutStream, file, null);
+				}
+			} else {
+				doZip(zipOutStream, target, null);
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * @param resultFile
+	 * @param sourseFiles
+	 * @return
+	 * @throws IOException
+	 */
+	public static boolean doZip(File resultFile, List<File> sourseFiles) {
+		if (sourseFiles == null || sourseFiles.size() == 0) {
+			throw new BusinessException("待压缩的文件不能为空!");
+		}
+
+		if (resultFile == null) {
+			throw new BusinessException("压缩后的文件不能为空!");
+		}
+		ZipOutputStream zipOutStream = null;
+		try {
+			OutputStream outStream = new FileOutputStream(resultFile);
+			zipOutStream = new ZipOutputStream(outStream, Charset.forName("UTF-8"));
+			if (!resultFile.exists()) {
+				boolean ok = resultFile.createNewFile();
+				if (!ok) {
+					throw new BusinessException("压缩的文件创建失败!");
+				}
+			}
+
+			for (File file : sourseFiles) {
+				doZip(zipOutStream, file, null);
+			}
+		} catch (IOException e) {
+			throw new BusinessException("压缩的文件创建失败!" + e.getMessage());
+		} finally {
+			if (zipOutStream != null) {
+				try {
+					zipOutStream.close();
+				} catch (IOException e) {
+				}
+			}
+		}
+
+		return true;
+	}
+
+	private static void doZip(ZipOutputStream zipOutStream, File target, String parentDir) throws IOException {
+		if (parentDir == null) {
+			parentDir = "";
+		}
+
+		if (!"".equals(parentDir) && !parentDir.endsWith(File.separator)) {
+			parentDir += File.separator;
+		}
+
+		if (target.isDirectory()) {
+			File[] files = target.listFiles();
+			if (files.length > 0) {
+				for (File file : files) {
+					doZip(zipOutStream, file, parentDir + target.getName());
+				}
+			} else {
+				zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
+				zipOutStream.closeEntry();
+			}
+		} else {
+			try (InputStream is = new FileInputStream(target);) {
+				zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
+				int len;
+				byte[] bytes = new byte[1024];
+				while ((len = is.read(bytes)) > 0) {
+					zipOutStream.write(bytes, 0, len);
+				}
+			}
+			zipOutStream.closeEntry();
+		}
+	}
+
+	public static void deleteFolder(String path) {
+
+		File file = new File(path);
+		if (file.exists()) {
+			if (file.isFile()) {
+				deleteFile(path);
+			} else {
+				deleteDirectory(path);
+			}
+		}
+	}
+
+	public static void deleteFile(String path) {
+		File file = new File(path);
+		if (file.isFile() && file.exists()) {
+			file.delete();
+		}
+	}
+
+	public static void deleteDirectory(String path) {
+		if (!path.endsWith(File.separator)) {
+			path = path + File.separator;
+		}
+		File dirFile = new File(path);
+		if (!dirFile.exists() || !dirFile.isDirectory()) {
+			return;
+		}
+		File[] files = dirFile.listFiles();
+		if (files != null) {
+			for (int i = 0; i < files.length; i++) {
+				if (files[i].isFile()) {
+					deleteFile(files[i].getAbsolutePath());
+				} else {
+					deleteDirectory(files[i].getAbsolutePath());
+				}
+			}
+		}
+
+		dirFile.delete();
+	}
+
+	private static Cipher initAESCipher(String sKey, String vector, int cipherMode) throws Exception {
+		byte[] raw;
+		raw = sKey.getBytes();
+		SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
+		Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+		IvParameterSpec iv = new IvParameterSpec(vector.getBytes());
+		cipher.init(cipherMode, skeySpec, iv);
+		return cipher;
+	}
+
+	/**
+	 * 解密文件
+	 * 
+	 * @param sourceFile  源文件
+	 * @param decryptFile 解密后的文件
+	 * @param sKey        密钥
+	 * @param vc          向量
+	 * @throws Exception
+	 */
+	public static void decryptFile(File sourceFile, File decryptFile, String sKey, String vc) {
+		decryptOrEncryptFile(Cipher.DECRYPT_MODE, sourceFile, decryptFile, sKey, vc);
+	}
+
+	/**
+	 * 加密文件
+	 * 
+	 * @param sourceFile  源文件
+	 * @param encryptFile 加密后的文件
+	 * @param sKey        密钥
+	 * @param vc          向量
+	 * @throws Exception
+	 */
+	public static void encryptFile(File sourceFile, File encryptFile, String sKey, String vc) {
+		decryptOrEncryptFile(Cipher.ENCRYPT_MODE, sourceFile, encryptFile, sKey, vc);
+	}
+
+	private static void decryptOrEncryptFile(int cipherMode, File sourceFile, File targetFile, String sKey, String vc) {
+		InputStream inputStream = null;
+		OutputStream outputStream = null;
+		try {
+			Cipher cipher = initAESCipher(sKey, vc, cipherMode);
+			inputStream = new FileInputStream(sourceFile);
+			outputStream = new FileOutputStream(targetFile);
+			CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
+			byte[] buffer = new byte[1024];
+			int r;
+			while ((r = inputStream.read(buffer)) >= 0) {
+				cipherOutputStream.write(buffer, 0, r);
+			}
+			cipherOutputStream.close();
+		} catch (Exception e) {
+			throw new BusinessException("加解密文件失败!" + e.getMessage());
+		} finally {
+			try {
+				if (inputStream != null) {
+					inputStream.close();
+				}
+			} catch (IOException e) {
+			}
+			try {
+				if (outputStream != null) {
+					outputStream.close();
+				}
+			} catch (IOException e) {
+			}
+		}
+	}
+}

+ 3 - 0
themis-mq/src/main/java/com/qmth/themis/mq/listener/RocketTaskConsumer.java

@@ -1,6 +1,7 @@
 package com.qmth.themis.mq.listener;
 
 import com.google.gson.Gson;
+import com.qmth.themis.business.constant.SpringContextHolder;
 import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.entity.TMRocketMessage;
 import com.qmth.themis.business.service.TMRocketMessageService;
@@ -83,6 +84,8 @@ public class RocketTaskConsumer {
                                 taskImportTemplete = new TaskExamStudentImportTemplete();
                             } else if (tag.contains("roomCodeImport")) {
                                 taskImportTemplete = new TaskRoomCodeImportTemplete();
+                            } else if(tag.contains("examPaperImport")) {
+                            	taskImportTemplete = SpringContextHolder.getBean("taskExamPaperImportTemplete");
                             }
                             try {
                                 taskImportTemplete.importTask(map);

+ 5 - 0
themis-task/src/main/java/com/qmth/themis/task/constant/TaskConstant.java

@@ -42,4 +42,9 @@ public class TaskConstant {
         mapParameter.put(SystemConstant.UPLOAD_TYPE, uploadType);
         return mapParameter;
     }
+    
+    public static String getTempDir() {
+        DictionaryConfig dictionaryConfig = SpringContextHolder.getBean(DictionaryConfig.class);
+        return dictionaryConfig.sysDomain().getTempDataDir();
+    }
 }

+ 5 - 1
themis-task/src/main/resources/application.properties

@@ -213,4 +213,8 @@ mq.config.taskConsumerRoomCodeImportGroup=${mq.config.taskConsumerGroup}-${mq.co
 
 #\u8003\u573A\u5BFC\u51FA
 mq.config.taskTopicRoomCodeExportTag=roomCodeExport
-mq.config.taskConsumerRoomCodeExportGroup=${mq.config.taskConsumerGroup}-${mq.config.taskTopicRoomCodeExportTag}
+mq.config.taskConsumerRoomCodeExportGroup=${mq.config.taskConsumerGroup}-${mq.config.taskTopicRoomCodeExportTag}
+
+#\u8BD5\u5377\u5BFC\u5165
+mq.config.taskTopicExamPaperImportTag=examPaperImport
+mq.config.taskConsumerExamPaperImportGroup=${mq.config.taskConsumerGroup}-${mq.config.taskTopicExamPaperImportTag}