瀏覽代碼

题库复制加试卷后缀

xiatian 6 月之前
父節點
當前提交
1928a1b19b

+ 184 - 164
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/CopyDataController.java

@@ -1,164 +1,184 @@
-package cn.com.qmth.examcloud.core.questions.api.controller;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletResponse;
-import javax.validation.constraints.NotNull;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.poi.xssf.usermodel.XSSFRow;
-import org.apache.poi.xssf.usermodel.XSSFSheet;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RequestPart;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
-
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.core.basic.api.CourseCloudService;
-import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
-import cn.com.qmth.examcloud.core.basic.api.request.GetCourseByOrgReq;
-import cn.com.qmth.examcloud.core.basic.api.response.GetCourseByOrgResp;
-import cn.com.qmth.examcloud.core.questions.service.CopyDataService;
-import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataCourseInfo;
-import cn.com.qmth.examcloud.web.redis.RedisClient;
-import cn.com.qmth.examcloud.web.support.ControllerSupport;
-import cn.com.qmth.examcloud.web.support.ServletUtil;
-
-@RestController
-@RequestMapping("${api_cqb}/copy")
-public class CopyDataController extends ControllerSupport {
-	private static int cacheLockTimeout = 60 * 10;
-	private static String copyQuestionDataLock = "$_COPY_QUESTION_DATA_LOCK";
-	@Autowired
-	private CopyDataService copyDataService;
-	@Autowired
-	private RedisClient redisClient;
-	@Autowired
-	private CourseCloudService courseCloudService;
-
-	@PostMapping(value = "data")
-	public void copyData(HttpServletResponse response,
-			@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile, @RequestParam Long fromRootOrgId,
-			@RequestParam Long toRootOrgId) {
-		User user = getAccessUser();
-		if (!user.getRootOrgId().equals(toRootOrgId)) {
-			ServletUtil.returnJson("请求失败,没有权限", response);
-			return;
-		}
-		Boolean lock = redisClient.setIfAbsent(copyQuestionDataLock, copyQuestionDataLock, cacheLockTimeout);
-		if (!lock) {
-			ServletUtil.returnJson("请求失败,正在处理数据", response);
-			return;
-		}
-		try {
-			LinkedHashSet<String> cs = readSubject(dataFile);
-			if (CollectionUtils.isEmpty(cs)) {
-				throw new StatusException("请填写要处理的课程");
-			}
-			Map<String, Long> coursesMap = new HashMap<>();
-			GetCourseByOrgReq courseReq = new GetCourseByOrgReq();
-			courseReq.setRootOrgId(toRootOrgId);
-			GetCourseByOrgResp res = courseCloudService.getCourseByOrg(courseReq);
-			if (CollectionUtils.isEmpty(res.getCourseList())) {
-				throw new StatusException("请先导入课程");
-			}
-
-			for (CourseBean c : res.getCourseList()) {
-				coursesMap.put(c.getCode(), c.getId());
-			}
-			List<CopyDataCourseInfo> courses = new ArrayList<>();
-			for (String c : cs) {
-				if (coursesMap.get(c) == null) {
-					throw new StatusException("未找到课程信息:" + c);
-				} else {
-					courses.add(new CopyDataCourseInfo(coursesMap.get(c), c));
-				}
-			}
-			copyDataService.copyData(user, fromRootOrgId, toRootOrgId, courses);
-			ServletUtil.returnJson("请求成功", response);
-		} catch (StatusException e) {
-			ServletUtil.returnJson("请求失败" + e.getDesc(), response);
-			redisClient.delete(copyQuestionDataLock);
-		} catch (Exception e) {
-			ServletUtil.returnJson("请求失败" + e.getMessage(), response);
-			redisClient.delete(copyQuestionDataLock);
-			throw e;
-		}
-
-	}
-
-	private LinkedHashSet<String> readSubject(MultipartFile dataFile) {
-		LinkedHashSet<String> list = new LinkedHashSet<>();
-		XSSFWorkbook wb = null;
-		InputStream in = null;
-		try {
-			in = dataFile.getInputStream();
-			wb = new XSSFWorkbook(in);
-			XSSFSheet sheet = wb.getSheetAt(0);
-			int rows = sheet.getLastRowNum();
-			for (int i = 1; i <= rows; i++) {
-				XSSFRow row = sheet.getRow(i);
-				String code;
-				try {
-					code = row.getCell(0).getStringCellValue().trim();
-				} catch (Exception e) {
-					code = (row.getCell(0).getNumericCellValue() + "").trim();
-				}
-				if(StringUtils.isNotBlank(code)) {
-					list.add(code);
-				}
-			}
-		} catch (IOException e) {
-			throw new RuntimeException(e);
-		} finally {
-			if (in != null) {
-				try {
-					in.close();
-				} catch (IOException e) {
-				}
-			}
-			if (wb != null) {
-				try {
-					wb.close();
-				} catch (IOException e) {
-				}
-			}
-		}
-		return list;
-	}
-	// 试题去重后复制
-//	@GetMapping("noreduplicate")
-//	public void copyNoReduplicate(HttpServletResponse response,@RequestParam String batch) {
-//		Boolean lock = redisClient.setIfAbsent(withoutReduplicateLock, withoutReduplicateLock, cacheLockTimeout);
-//		if (!lock) {
-//			ServletUtil.returnJson("请求失败,正在处理数据", response);
-//			return;
-//		}
-//		copyDataService.copyNoReduplicate(getAccessUser(), batch);
-//		ServletUtil.returnJson("请求成功", response);
-//	}
-
-	@GetMapping("status")
-	public void copyNoReduplicateStatus(HttpServletResponse response) {
-		String lock = redisClient.get(copyQuestionDataLock, String.class);
-		if (lock != null) {
-			ServletUtil.returnJson("正在处理数据", response);
-		} else {
-			ServletUtil.returnJson("处理数据结束", response);
-		}
-	}
-
-}
+package cn.com.qmth.examcloud.core.questions.api.controller;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotNull;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.CourseCloudService;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.basic.api.request.GetCourseByOrgReq;
+import cn.com.qmth.examcloud.core.basic.api.response.GetCourseByOrgResp;
+import cn.com.qmth.examcloud.core.questions.service.CopyDataService;
+import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataCourseInfo;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import cn.com.qmth.examcloud.web.support.ServletUtil;
+
+@RestController
+@RequestMapping("${api_cqb}/copy")
+public class CopyDataController extends ControllerSupport {
+
+    private static int cacheLockTimeout = 60 * 10;
+
+    private static String copyQuestionDataLock = "$_COPY_QUESTION_DATA_LOCK";
+
+    @Autowired
+    private CopyDataService copyDataService;
+
+    @Autowired
+    private RedisClient redisClient;
+
+    @Autowired
+    private CourseCloudService courseCloudService;
+
+    /**
+     * @param response
+     * @param dataFile
+     *            科目代码列表
+     * @param fromRootOrgId
+     *            原学校
+     * @param toRootOrgId
+     *            目标学校
+     * @param paperSuff
+     *            复制后的目标学校试卷后缀
+     * @param copyAll
+     *            是否全部试卷复制(已经复制过的id有记录)
+     */
+    @PostMapping(value = "data")
+    public void copyData(HttpServletResponse response,
+            @RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile, @RequestParam Long fromRootOrgId,
+            @RequestParam Long toRootOrgId, @RequestParam String paperSuff, @RequestParam Boolean copyAll) {
+        User user = getAccessUser();
+        if (!user.getRootOrgId().equals(toRootOrgId)) {
+            ServletUtil.returnJson("请求失败,没有权限", response);
+            return;
+        }
+        Boolean lock = redisClient.setIfAbsent(copyQuestionDataLock, copyQuestionDataLock, cacheLockTimeout);
+        if (!lock) {
+            ServletUtil.returnJson("请求失败,正在处理数据", response);
+            return;
+        }
+        try {
+            LinkedHashSet<String> cs = readSubject(dataFile);
+            if (CollectionUtils.isEmpty(cs)) {
+                throw new StatusException("请填写要处理的课程");
+            }
+            Map<String, Long> coursesMap = new HashMap<>();
+            GetCourseByOrgReq courseReq = new GetCourseByOrgReq();
+            courseReq.setRootOrgId(toRootOrgId);
+            GetCourseByOrgResp res = courseCloudService.getCourseByOrg(courseReq);
+            if (CollectionUtils.isEmpty(res.getCourseList())) {
+                throw new StatusException("请先导入课程");
+            }
+
+            for (CourseBean c : res.getCourseList()) {
+                coursesMap.put(c.getCode(), c.getId());
+            }
+            List<CopyDataCourseInfo> courses = new ArrayList<>();
+            for (String c : cs) {
+                if (coursesMap.get(c) == null) {
+                    throw new StatusException("未找到课程信息:" + c);
+                } else {
+                    courses.add(new CopyDataCourseInfo(coursesMap.get(c), c));
+                }
+            }
+            copyDataService.copyData(user, fromRootOrgId, toRootOrgId, courses, paperSuff, copyAll);
+            ServletUtil.returnJson("请求成功", response);
+        } catch (StatusException e) {
+            ServletUtil.returnJson("请求失败" + e.getDesc(), response);
+            redisClient.delete(copyQuestionDataLock);
+        } catch (Exception e) {
+            ServletUtil.returnJson("请求失败" + e.getMessage(), response);
+            redisClient.delete(copyQuestionDataLock);
+            throw e;
+        }
+
+    }
+
+    private LinkedHashSet<String> readSubject(MultipartFile dataFile) {
+        LinkedHashSet<String> list = new LinkedHashSet<>();
+        XSSFWorkbook wb = null;
+        InputStream in = null;
+        try {
+            in = dataFile.getInputStream();
+            wb = new XSSFWorkbook(in);
+            XSSFSheet sheet = wb.getSheetAt(0);
+            int rows = sheet.getLastRowNum();
+            for (int i = 1; i <= rows; i++) {
+                XSSFRow row = sheet.getRow(i);
+                String code;
+                try {
+                    code = row.getCell(0).getStringCellValue().trim();
+                } catch (Exception e) {
+                    code = (row.getCell(0).getNumericCellValue() + "").trim();
+                }
+                if (StringUtils.isNotBlank(code)) {
+                    list.add(code);
+                }
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                }
+            }
+            if (wb != null) {
+                try {
+                    wb.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return list;
+    }
+    // 试题去重后复制
+    // @GetMapping("noreduplicate")
+    // public void copyNoReduplicate(HttpServletResponse response,@RequestParam
+    // String batch) {
+    // Boolean lock = redisClient.setIfAbsent(withoutReduplicateLock,
+    // withoutReduplicateLock, cacheLockTimeout);
+    // if (!lock) {
+    // ServletUtil.returnJson("请求失败,正在处理数据", response);
+    // return;
+    // }
+    // copyDataService.copyNoReduplicate(getAccessUser(), batch);
+    // ServletUtil.returnJson("请求成功", response);
+    // }
+
+    @GetMapping("status")
+    public void copyNoReduplicateStatus(HttpServletResponse response) {
+        String lock = redisClient.get(copyQuestionDataLock, String.class);
+        if (lock != null) {
+            ServletUtil.returnJson("正在处理数据", response);
+        } else {
+            ServletUtil.returnJson("处理数据结束", response);
+        }
+    }
+
+}

+ 229 - 218
examcloud-core-questions-api-provider/src/main/java/cn/com/qmth/examcloud/core/questions/api/controller/ImportPaperController.java

@@ -1,218 +1,229 @@
-package cn.com.qmth.examcloud.core.questions.api.controller;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.validation.constraints.NotNull;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-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.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RequestPart;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.multipart.commons.CommonsMultipartFile;
-
-import cn.com.qmth.examcloud.api.commons.enums.AdminOperateType;
-import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
-import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.core.questions.base.Model;
-import cn.com.qmth.examcloud.core.questions.base.exception.PaperException;
-import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Course;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
-import cn.com.qmth.examcloud.core.questions.service.ClonePaperService;
-import cn.com.qmth.examcloud.core.questions.service.ImportDdCollegePaperService;
-import cn.com.qmth.examcloud.core.questions.service.ImportPaperService;
-import cn.com.qmth.examcloud.core.questions.service.impl.CourseService;
-import cn.com.qmth.examcloud.core.questions.service.temp.CqdxService;
-import cn.com.qmth.examcloud.core.questions.service.yunkai.YunkaiImportPaperParam;
-import cn.com.qmth.examcloud.core.questions.service.yunkai.YunkaiService;
-import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
-import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
-import cn.com.qmth.examcloud.web.security.DataRule;
-import cn.com.qmth.examcloud.web.support.ControllerSupport;
-import io.swagger.annotations.ApiOperation;
-
-/**
- * @author weiwenhai
- * @date 2017年4月14日 下午6:05:37
- * @company QMTH
- * @description 导入试卷控制器
- * @code 090
- */
-@RestController
-@RequestMapping("${api_cqb}/")
-public class ImportPaperController extends ControllerSupport {
-	private static final Logger LOG = LoggerFactory.getLogger(ImportPaperController.class);
-
-	@Autowired
-	private ClonePaperService clonePaperService;
-	@Autowired
-	private ImportPaperService importPaperService;
-	@Autowired
-	private ImportDdCollegePaperService importDdCollegePaperService;
-	@Autowired
-	private CqdxService cqdxService;
-	@Autowired
-	private YunkaiService yunkaiService;
-	@Autowired
-	private CourseService courseService;
-	@Autowired
-	private PaperRepo paperRepo;
-
-	/**
-	 * 导入试卷
-	 *
-	 * @param file
-	 * @return
-	 */
-	@DataRule(type = DataRuleType.COURSE)
-	@ApiOperation(value = "导入试卷", notes = "导入试卷")
-	@PostMapping(value = "/importPaper")
-	public ResponseEntity<Object> importPaper(@ModelAttribute Paper paper,
-			@RequestParam("file") CommonsMultipartFile file) {
-		User user = getAccessUser();
-		UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
-		File tempFile = null;
-
-		try {
-			LOG.warn("试卷导入操作!courseCode:{} paperName:{}", paper.getCourseNo() , paper.getName());
-
-			tempFile = importPaperService.getUploadFile(file);
-			Paper newPaper = importPaperService.importPaper(paper, user, tempFile, ud);
-			Course course = paper.getCourse();
-			StringBuilder sb = new StringBuilder();
-			sb.append("课程:" + course.getName() + "(" + course.getCode() + ")");
-			sb.append(" 试卷名称:" + paper.getName());
-
-			ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
-					AdminOperateType.TYPE29.getDesc(), sb.toString()));
-			return new ResponseEntity<>(newPaper, HttpStatus.OK);
-		} catch (StatusException e) {
-			throw e;
-		} catch (Exception e) {
-			LOG.error("试卷导入异常!"+e.getMessage(), e);
-			throw new StatusException("500", e.getMessage(), e);
-		}
-	}
-
-	/**
-	 * 新增试卷
-	 *
-	 * @param courseNo
-	 * @param paperName
-	 * @return
-	 */
-	@ApiOperation(value = "保存导入类型空白试卷", notes = "保存导入类型空白试卷")
-	@PostMapping(value = "/importPaper/saveBlankPaper/{courseNo}/{paperName}")
-	public ResponseEntity<Object> saveBlankPaper(@PathVariable String courseNo, @PathVariable String paperName) {
-		User user = getAccessUser();
-		Map<String, Object> returnMap = importPaperService.saveBlankPaper(courseNo, paperName, user);
-		if (returnMap.get("msg").equals("success")) {
-			return new ResponseEntity<>(returnMap, HttpStatus.OK);
-		} else {
-			return new ResponseEntity<>(returnMap, HttpStatus.INTERNAL_SERVER_ERROR);
-		}
-	}
-
-	/**
-	 * 克隆试卷
-	 *
-	 * @return
-	 */
-	@ApiOperation(value = "克隆试卷", notes = "克隆试卷")
-	@PostMapping(value = "/clonePaper/{paperId}/{paperName}/{courseNo}")
-	public ResponseEntity<Object> clonePaper(@PathVariable String paperId, @PathVariable String paperName,
-			@PathVariable String courseNo) {
-		User user = getAccessUser();
-		Map<String, Object> map = new HashMap<>();
-
-		try {
-			LOG.warn("克隆试卷操作!courseCode:{} paperName:{} paperId:{}", courseNo, paperName, paperId);
-
-			clonePaperService.clonePaper(paperId, paperName, courseNo, user);
-			Paper paper = Model.of(paperRepo.findById(paperId));
-
-			Course course = courseService.getCourse(user.getRootOrgId(), courseNo);
-			StringBuilder sb = new StringBuilder();
-			sb.append("来源课程:" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")");
-			sb.append(" 来源试卷名称:" + paper.getName());
-			sb.append(" 复制到课程:" + course.getName() + "(" + course.getCode() + ")");
-			sb.append(" 复制到试卷名称:" + paperName);
-			ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
-					AdminOperateType.TYPE30.getDesc(), sb.toString()));
-			map.put("msg", "success");
-			return new ResponseEntity<>(map, HttpStatus.OK);
-		} catch (PaperException e) {
-			map.put("msg", e.getMessage());
-
-			LOG.error("试卷克隆异常!" + e.getMessage());
-			return new ResponseEntity<>(map, HttpStatus.INTERNAL_SERVER_ERROR);
-		}
-	}
-
-	// @ApiOperation(value = "导入地大试卷", notes = "导入地大试卷")
-	// @PostMapping(value = "/importDdCollegePaper")
-	// public ResponseEntity<Object> importDdCollegePaper(
-	// 		@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile, @RequestParam Long rootOrgId,
-	// 		@RequestParam String courseCode, @RequestParam(required = false) String suff) {
-	// 	User user = getAccessUser();
-	// 	importDdCollegePaperService.importDdCollegePaper(dataFile, user, rootOrgId, courseCode, suff);
-	// 	return new ResponseEntity<>(HttpStatus.OK);
-	// }
-
-	// @ApiOperation(value = "导入重庆试卷", notes = "导入重庆试卷")
-	// @PostMapping(value = "/importCqCollegePaper")
-	// public ResponseEntity<Object> importCqCollegePaper(
-	// 		@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile, @RequestParam Long rootOrgId,
-	// 		@RequestParam String paperNameSuffix, @RequestParam String impType) {
-	// 	User user = getAccessUser();
-	// 	cqdxService.bulidPaper(dataFile, user, rootOrgId, paperNameSuffix, impType);
-	// 	return new ResponseEntity<>(HttpStatus.OK);
-	// }
-
-	//	@ApiOperation(value = "云开导入前清理课程属性、试题属性")
-	//	@PostMapping(value = "/importYunkaiProp/clear")
-	//	public void importYunkaiPropClear(Long rootOrgId, @RequestParam String batch) {
-	//		yunkaiService.propClear(rootOrgId, batch);
-	//	}
-
-	@ApiOperation(value = "云开导入课程属性、试题属性")
-	@PostMapping(value = "/importYunkaiProp")
-	public void importYunkaiProp(@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile,
-			@RequestParam Long rootOrgId, @RequestParam String courseCodes, @RequestParam String batch) {
-		User user = getAccessUser();
-		LOG.warn("云开-课程和试题属性导入操作!courseCodes:{} batch:{}", courseCodes, batch);
-
-		List<String> cs = Arrays.asList(courseCodes.split(","));
-		yunkaiService.saveProp(user, dataFile, rootOrgId, cs, batch);
-
-	}
-
-	@ApiOperation(value = "云开导入试卷")
-	@PostMapping(value = "/importYunkaiPaper")
-	public void importYunkaiPaper(@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile,
-			@RequestParam Long rootOrgId, @RequestParam String courseCodes,@RequestParam String batch) {
-		User user = getAccessUser();
-		LOG.warn("云开-试卷导入操作!courseCodes:{} batch:{}", courseCodes, batch);
-
-		List<String> cs = Arrays.asList(courseCodes.split(","));
-		YunkaiImportPaperParam p = new YunkaiImportPaperParam(user, dataFile, rootOrgId, cs);
-		p.setBatch(batch);
-		yunkaiService.importYunkaiPaper(p);
-	}
-
-}
+package cn.com.qmth.examcloud.core.questions.api.controller;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.validation.constraints.NotNull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
+
+import cn.com.qmth.examcloud.api.commons.enums.AdminOperateType;
+import cn.com.qmth.examcloud.api.commons.enums.DataRuleType;
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.api.commons.security.bean.UserDataRule;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.questions.base.Model;
+import cn.com.qmth.examcloud.core.questions.base.exception.PaperException;
+import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Course;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+import cn.com.qmth.examcloud.core.questions.service.ClonePaperService;
+import cn.com.qmth.examcloud.core.questions.service.ImportPaperService;
+import cn.com.qmth.examcloud.core.questions.service.impl.CourseService;
+import cn.com.qmth.examcloud.core.questions.service.yunkai.YunkaiImportPaperParam;
+import cn.com.qmth.examcloud.core.questions.service.yunkai.YunkaiService;
+import cn.com.qmth.examcloud.reports.commons.bean.AdminOperateReport;
+import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
+import cn.com.qmth.examcloud.web.security.DataRule;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.ApiOperation;
+
+/**
+ * @author weiwenhai
+ * @date 2017年4月14日 下午6:05:37
+ * @company QMTH
+ * @description 导入试卷控制器
+ * @code 090
+ */
+@RestController
+@RequestMapping("${api_cqb}/")
+public class ImportPaperController extends ControllerSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ImportPaperController.class);
+
+    @Autowired
+    private ClonePaperService clonePaperService;
+
+    @Autowired
+    private ImportPaperService importPaperService;
+
+    @Autowired
+    private YunkaiService yunkaiService;
+
+    @Autowired
+    private CourseService courseService;
+
+    @Autowired
+    private PaperRepo paperRepo;
+
+    /**
+     * 导入试卷
+     *
+     * @param file
+     * @return
+     */
+    @DataRule(type = DataRuleType.COURSE)
+    @ApiOperation(value = "导入试卷", notes = "导入试卷")
+    @PostMapping(value = "/importPaper")
+    public ResponseEntity<Object> importPaper(@ModelAttribute Paper paper,
+            @RequestParam("file") CommonsMultipartFile file) {
+        User user = getAccessUser();
+        UserDataRule ud = getUserDataRule(DataRuleType.COURSE);
+        File tempFile = null;
+
+        try {
+            LOG.warn("试卷导入操作!courseCode:{} paperName:{}", paper.getCourseNo(), paper.getName());
+
+            tempFile = importPaperService.getUploadFile(file);
+            Paper newPaper = importPaperService.importPaper(paper, user, tempFile, ud);
+            Course course = paper.getCourse();
+            StringBuilder sb = new StringBuilder();
+            sb.append("课程:" + course.getName() + "(" + course.getCode() + ")");
+            sb.append(" 试卷名称:" + paper.getName());
+
+            ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                    AdminOperateType.TYPE29.getDesc(), sb.toString()));
+            return new ResponseEntity<>(newPaper, HttpStatus.OK);
+        } catch (StatusException e) {
+            throw e;
+        } catch (Exception e) {
+            LOG.error("试卷导入异常!" + e.getMessage(), e);
+            throw new StatusException("500", e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 新增试卷
+     *
+     * @param courseNo
+     * @param paperName
+     * @return
+     */
+    @ApiOperation(value = "保存导入类型空白试卷", notes = "保存导入类型空白试卷")
+    @PostMapping(value = "/importPaper/saveBlankPaper/{courseNo}/{paperName}")
+    public ResponseEntity<Object> saveBlankPaper(@PathVariable String courseNo, @PathVariable String paperName) {
+        User user = getAccessUser();
+        Map<String, Object> returnMap = importPaperService.saveBlankPaper(courseNo, paperName, user);
+        if (returnMap.get("msg").equals("success")) {
+            return new ResponseEntity<>(returnMap, HttpStatus.OK);
+        } else {
+            return new ResponseEntity<>(returnMap, HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    /**
+     * 克隆试卷
+     *
+     * @return
+     */
+    @ApiOperation(value = "克隆试卷", notes = "克隆试卷")
+    @PostMapping(value = "/clonePaper/{paperId}/{paperName}/{courseNo}")
+    public ResponseEntity<Object> clonePaper(@PathVariable String paperId, @PathVariable String paperName,
+            @PathVariable String courseNo) {
+        User user = getAccessUser();
+        Map<String, Object> map = new HashMap<>();
+
+        try {
+            LOG.warn("克隆试卷操作!courseCode:{} paperName:{} paperId:{}", courseNo, paperName, paperId);
+
+            clonePaperService.clonePaper(paperId, paperName, courseNo, user);
+            Paper paper = Model.of(paperRepo.findById(paperId));
+
+            Course course = courseService.getCourse(user.getRootOrgId(), courseNo);
+            StringBuilder sb = new StringBuilder();
+            sb.append("来源课程:" + paper.getCourse().getName() + "(" + paper.getCourse().getCode() + ")");
+            sb.append(" 来源试卷名称:" + paper.getName());
+            sb.append(" 复制到课程:" + course.getName() + "(" + course.getCode() + ")");
+            sb.append(" 复制到试卷名称:" + paperName);
+            ReportsUtil.report(new AdminOperateReport(user.getRootOrgId(), user.getUserId(),
+                    AdminOperateType.TYPE30.getDesc(), sb.toString()));
+            map.put("msg", "success");
+            return new ResponseEntity<>(map, HttpStatus.OK);
+        } catch (PaperException e) {
+            map.put("msg", e.getMessage());
+
+            LOG.error("试卷克隆异常!" + e.getMessage());
+            return new ResponseEntity<>(map, HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    // @ApiOperation(value = "导入地大试卷", notes = "导入地大试卷")
+    // @PostMapping(value = "/importDdCollegePaper")
+    // public ResponseEntity<Object> importDdCollegePaper(
+    // @RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile,
+    // @RequestParam Long rootOrgId,
+    // @RequestParam String courseCode, @RequestParam(required = false) String
+    // suff) {
+    // User user = getAccessUser();
+    // importDdCollegePaperService.importDdCollegePaper(dataFile, user,
+    // rootOrgId, courseCode, suff);
+    // return new ResponseEntity<>(HttpStatus.OK);
+    // }
+
+    // @ApiOperation(value = "导入重庆试卷", notes = "导入重庆试卷")
+    // @PostMapping(value = "/importCqCollegePaper")
+    // public ResponseEntity<Object> importCqCollegePaper(
+    // @RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile,
+    // @RequestParam Long rootOrgId,
+    // @RequestParam String paperNameSuffix, @RequestParam String impType) {
+    // User user = getAccessUser();
+    // cqdxService.bulidPaper(dataFile, user, rootOrgId, paperNameSuffix,
+    // impType);
+    // return new ResponseEntity<>(HttpStatus.OK);
+    // }
+
+    // @ApiOperation(value = "云开导入前清理课程属性、试题属性")
+    // @PostMapping(value = "/importYunkaiProp/clear")
+    // public void importYunkaiPropClear(Long rootOrgId, @RequestParam String
+    // batch) {
+    // yunkaiService.propClear(rootOrgId, batch);
+    // }
+
+    @ApiOperation(value = "云开导入课程属性、试题属性")
+    @PostMapping(value = "/importYunkaiProp")
+    public void importYunkaiProp(@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile,
+            @RequestParam Long rootOrgId, @RequestParam String courseCodes, @RequestParam String batch) {
+        User user = getAccessUser();
+        if (!user.getRootOrgId().equals(rootOrgId)) {
+            throw new StatusException("学校id和账号不一致");
+        }
+        LOG.warn("云开-课程和试题属性导入操作!courseCodes:{} batch:{}", courseCodes, batch);
+
+        List<String> cs = Arrays.asList(courseCodes.split(","));
+        yunkaiService.saveProp(user, dataFile, rootOrgId, cs, batch);
+
+    }
+
+    @ApiOperation(value = "云开导入试卷")
+    @PostMapping(value = "/importYunkaiPaper")
+    public void importYunkaiPaper(@RequestPart @NotNull(message = "上传文件不能为空!") MultipartFile dataFile,
+            @RequestParam Long rootOrgId, @RequestParam String courseCodes, @RequestParam String batch) {
+        User user = getAccessUser();
+        if (!user.getRootOrgId().equals(rootOrgId)) {
+            throw new StatusException("学校id和账号不一致");
+        }
+        LOG.warn("云开-试卷导入操作!courseCodes:{} batch:{}", courseCodes, batch);
+
+        List<String> cs = Arrays.asList(courseCodes.split(","));
+        YunkaiImportPaperParam p = new YunkaiImportPaperParam(user, dataFile, rootOrgId, cs);
+        p.setBatch(batch);
+        yunkaiService.importYunkaiPaper(p);
+    }
+
+}

+ 15 - 15
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/CopyDataService.java

@@ -1,15 +1,15 @@
-package cn.com.qmth.examcloud.core.questions.service;
-
-import java.util.List;
-
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataCourseInfo;
-
-public interface CopyDataService {
-
-	void copyData(User user, Long fromRootOrgId, Long toRootOrgId,List<CopyDataCourseInfo> courses);
-
-//	void copyNoReduplicate(User user,String batch);
-
-}
-
+package cn.com.qmth.examcloud.core.questions.service;
+
+import java.util.List;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataCourseInfo;
+
+public interface CopyDataService {
+
+    void copyData(User user, Long fromRootOrgId, Long toRootOrgId, List<CopyDataCourseInfo> courses, String paperSuff,
+            Boolean copyAll);
+
+    // void copyNoReduplicate(User user,String batch);
+
+}

+ 116 - 76
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/bean/CopyDataDto.java

@@ -1,76 +1,116 @@
-package cn.com.qmth.examcloud.core.questions.service.bean;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
-
-public class CopyDataDto {
-	private CopyDataCourseInfo course;
-	private Long fromRootOrgId;
-	private Long toRootOrgId;
-	private User user;
-
-	private Map<String, Property> souceIdMap = new HashMap<>();
-	// 复制原卷类型
-	private PaperType paperType = PaperType.IMPORT;
-
-	public Long getToRootOrgId() {
-		return toRootOrgId;
-	}
-
-	public void setToRootOrgId(Long toRootOrgId) {
-		this.toRootOrgId = toRootOrgId;
-	}
-
-	public User getUser() {
-		return user;
-	}
-
-	public void setUser(User user) {
-		this.user = user;
-	}
-
-	public Long getFromRootOrgId() {
-		return fromRootOrgId;
-	}
-
-	public void setFromRootOrgId(Long fromRootOrgId) {
-		this.fromRootOrgId = fromRootOrgId;
-	}
-
-	public CopyDataCourseInfo getCourse() {
-		return course;
-	}
-
-	public void setCourse(CopyDataCourseInfo course) {
-		this.course = course;
-	}
-
-	public CopyDataDto(CopyDataCourseInfo course, Long fromRootOrgId, Long toRootOrgId, User user) {
-		super();
-		this.course = course;
-		this.fromRootOrgId = fromRootOrgId;
-		this.toRootOrgId = toRootOrgId;
-		this.user = user;
-	}
-
-	public Map<String, Property> getSouceIdMap() {
-		return souceIdMap;
-	}
-
-	public void setSouceIdMap(Map<String, Property> souceIdMap) {
-		this.souceIdMap = souceIdMap;
-	}
-
-	public PaperType getPaperType() {
-		return paperType;
-	}
-
-	public void setPaperType(PaperType paperType) {
-		this.paperType = paperType;
-	}
-
-}
+package cn.com.qmth.examcloud.core.questions.service.bean;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
+
+public class CopyDataDto {
+
+    private CopyDataCourseInfo course;
+
+    private Long fromRootOrgId;
+
+    private Long toRootOrgId;
+
+    private User user;
+
+    private String paperSuff;
+
+    private Boolean copyAll;
+
+    // 原属性对应的新属性id
+    private Map<String, Property> souceIdMap = new HashMap<>();
+
+    // 原question对应的新question id
+    private Map<String, String> souceIdToQuestionMap = new HashMap<>();
+
+    // 复制原卷类型
+    private PaperType paperType = PaperType.IMPORT;
+
+    public Long getToRootOrgId() {
+        return toRootOrgId;
+    }
+
+    public void setToRootOrgId(Long toRootOrgId) {
+        this.toRootOrgId = toRootOrgId;
+    }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public Long getFromRootOrgId() {
+        return fromRootOrgId;
+    }
+
+    public void setFromRootOrgId(Long fromRootOrgId) {
+        this.fromRootOrgId = fromRootOrgId;
+    }
+
+    public CopyDataCourseInfo getCourse() {
+        return course;
+    }
+
+    public void setCourse(CopyDataCourseInfo course) {
+        this.course = course;
+    }
+
+    public CopyDataDto(CopyDataCourseInfo course, Long fromRootOrgId, Long toRootOrgId, User user, String paperSuff,
+            Boolean copyAll) {
+        super();
+        this.course = course;
+        this.fromRootOrgId = fromRootOrgId;
+        this.toRootOrgId = toRootOrgId;
+        this.user = user;
+        this.paperSuff = paperSuff;
+        this.copyAll = copyAll;
+    }
+
+    public Map<String, Property> getSouceIdMap() {
+        return souceIdMap;
+    }
+
+    public void setSouceIdMap(Map<String, Property> souceIdMap) {
+        this.souceIdMap = souceIdMap;
+    }
+
+    public PaperType getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(PaperType paperType) {
+        this.paperType = paperType;
+    }
+
+    public String getPaperSuff() {
+        return paperSuff;
+    }
+
+    public void setPaperSuff(String paperSuff) {
+        this.paperSuff = paperSuff;
+    }
+
+    public Boolean getCopyAll() {
+        return copyAll;
+    }
+
+    public void setCopyAll(Boolean copyAll) {
+        this.copyAll = copyAll;
+    }
+
+    public Map<String, String> getSouceIdToQuestionMap() {
+        return souceIdToQuestionMap;
+    }
+
+    public void setSouceIdToQuestionMap(Map<String, String> souceIdToQuestionMap) {
+        this.souceIdToQuestionMap = souceIdToQuestionMap;
+    }
+
+}

+ 630 - 608
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/consumer/CopyDataConsumer.java

@@ -1,608 +1,630 @@
-package cn.com.qmth.examcloud.core.questions.service.consumer;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.UUID;
-
-import cn.com.qmth.examcloud.core.questions.base.FileConstants;
-import cn.com.qmth.examcloud.support.fss.FssFactory;
-import cn.com.qmth.examcloud.support.fss.FssHelper;
-import cn.com.qmth.examcloud.support.fss.model.FssFileInfo;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Scope;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.commons.exception.StatusException;
-import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
-import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
-import cn.com.qmth.examcloud.core.questions.base.IoUtils;
-import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
-import cn.com.qmth.examcloud.core.questions.base.multithread.Consumer;
-import cn.com.qmth.examcloud.core.questions.dao.CoursePropertyRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperDetailRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
-import cn.com.qmth.examcloud.core.questions.dao.PropertyRepo;
-import cn.com.qmth.examcloud.core.questions.dao.QuesRepo;
-import cn.com.qmth.examcloud.core.questions.dao.entity.CourseProperty;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
-import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuesProperty;
-import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
-import cn.com.qmth.examcloud.core.questions.service.CoursePropertyService;
-import cn.com.qmth.examcloud.core.questions.service.PaperService;
-import cn.com.qmth.examcloud.core.questions.service.PropertyService;
-import cn.com.qmth.examcloud.core.questions.service.QuesService;
-import cn.com.qmth.examcloud.core.questions.service.QuestionAudioService;
-import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataDto;
-import cn.com.qmth.examcloud.web.redis.RedisClient;
-
-@Scope("prototype")
-@Service
-public class CopyDataConsumer extends Consumer<CopyDataDto> {
-	private static final Logger log = LoggerFactory.getLogger(CopyDataConsumer.class);
-	private static int cacheLockTimeout = 60 * 10;
-	private static String copyQuestionDataLock = "$_COPY_QUESTION_DATA_LOCK";
-	@Autowired
-	private PaperRepo paperRepo;
-	@Autowired
-	private CoursePropertyService coursePropertyService;
-	@Autowired
-	private CoursePropertyRepo coursePropertyRepo;
-	@Autowired
-	private PropertyService propertyService;
-	@Autowired
-	private PropertyRepo propertyRepo;
-
-	@Autowired
-	private PaperDetailRepo paperDetailRepo;
-
-	@Autowired
-	private PaperDetailUnitRepo paperDetailUnitRepo;
-
-	@Autowired
-	private QuestionAudioService questionAudioService;
-
-	@Autowired
-	private PaperService paperService;
-	@Autowired
-	private QuesRepo quesRepo;
-	@Autowired
-	private QuesService quesService;
-	@Autowired
-	private RedisClient redisClient;
-
-	@Transactional
-	@Override
-	public int consume(CopyDataDto dto) {
-		disposeCourse(dto);
-		dto.setSouceIdMap(null);
-		dto.setPaperType(PaperType.GENERATE);
-		disposeCourse(dto);
-		return 1;
-	}
-
-	private void disposeCourse(CopyDataDto dto) {
-		redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
-
-		if (PaperType.IMPORT.equals(dto.getPaperType())) {
-			copyCourseProperty(dto);
-		}
-		List<Paper> papers = paperService.getPapersByCourse(dto.getFromRootOrgId(), dto.getCourse().getCourseCode(),
-				dto.getPaperType());
-
-		redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
-
-		if (CollectionUtils.isEmpty(papers)) {
-			return;
-		}
-		if (PaperType.IMPORT.equals(dto.getPaperType())) {
-			fillSouceIdMap(dto);
-		}
-		for (Paper paper : papers) {
-			if (!paperService.existsPaperBySourceId(dto.getToRootOrgId(), dto.getCourse().getCourseCode(),
-					dto.getPaperType(), paper.getId())) {
-				clonePaper(paper, dto);
-			}
-			redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
-		}
-	}
-
-	private void fillSouceIdMap(CopyDataDto dto) {
-		List<CourseProperty> cps = coursePropertyService.findAllByOrgIdCourseCode(dto.getToRootOrgId(),
-				dto.getCourse().getCourseCode());
-		if (CollectionUtils.isEmpty(cps)) {
-			return;
-		}
-		for (CourseProperty cp : cps) {
-			List<Property> ps = propertyService.findByOrgIdAndCoursePropertyId(dto.getToRootOrgId(), cp.getId());
-			if (CollectionUtils.isEmpty(ps)) {
-				continue;
-			}
-			for (Property p : ps) {
-				dto.getSouceIdMap().put(p.getSourceId(), p);
-			}
-		}
-	}
-
-	private void copyCourseProperty(CopyDataDto dto) {
-		List<CourseProperty> cps = coursePropertyService.findAllByOrgIdCourseCode(dto.getFromRootOrgId(),
-				dto.getCourse().getCourseCode());
-		if (CollectionUtils.isEmpty(cps)) {
-			return;
-		}
-		for (CourseProperty cp : cps) {
-			CourseProperty targetCourseProper = null;
-			List<CourseProperty> targetFirstPropers = coursePropertyService
-					.findByOrgIdAndCourseIdAndSourceId(dto.getToRootOrgId(), dto.getCourse().getCourseId(), cp.getId());
-			if (CollectionUtils.isEmpty(targetFirstPropers)) {
-				targetCourseProper = BeanCopierUtil.copyProperties(cp, CourseProperty.class);
-				targetCourseProper.setId(null);
-				targetCourseProper.setOrgId(dto.getToRootOrgId());
-				targetCourseProper.setCourseId(dto.getCourse().getCourseId());
-				targetCourseProper.setUpdateTime(new Date());
-				targetCourseProper.setSourceId(cp.getId());
-				coursePropertyRepo.save(targetCourseProper);
-			} else {
-				targetCourseProper = targetFirstPropers.get(0);
-			}
-			// 处理一级属性
-			List<Property> fps = propertyService.findParentProperties(cp.getId(), dto.getFromRootOrgId());
-			if (CollectionUtils.isEmpty(fps)) {
-				continue;
-			}
-			List<Property> targetFps = propertyService.findParentProperties(targetCourseProper.getId(),
-					dto.getToRootOrgId());
-			int firstNum = 0;
-			if (CollectionUtils.isNotEmpty(targetFps)) {
-				firstNum = targetFps.size();
-			}
-			Map<String, Property> targetsouceIds = getSourceIds(targetFps);
-			for (Property fp : fps) {
-				Property targetFp = null;
-				if (targetsouceIds.get(fp.getId()) == null) {
-					firstNum++;
-					targetFp = BeanCopierUtil.copyProperties(fp, Property.class);
-					targetFp.setId(null);
-					targetFp.setOrgId(dto.getToRootOrgId());
-					targetFp.setCoursePropertyId(targetCourseProper.getId());
-					targetFp.setParentId(Property.ROOT_PARENT_ID);
-					targetFp.setNumber(firstNum);
-					targetFp.setSourceId(fp.getId());
-					propertyRepo.save(targetFp);
-				} else {
-					targetFp = targetsouceIds.get(fp.getId());
-				}
-				// 处理二级属性
-				List<Property> sps = propertyService.findSonProperties(fp.getId());
-				if (CollectionUtils.isEmpty(sps)) {
-					continue;
-				}
-				List<Property> targetSps = propertyService.findSonProperties(targetFp.getId());
-				int secondNum = 0;
-				if (CollectionUtils.isNotEmpty(targetSps)) {
-					secondNum = targetSps.size();
-				}
-				Map<String, Property> targetSecondSouceIds = getSourceIds(targetSps);
-				for (Property sp : sps) {
-					Property targetSp = null;
-					if (targetSecondSouceIds.get(sp.getId()) == null) {
-						secondNum++;
-						targetSp = BeanCopierUtil.copyProperties(sp, Property.class);
-						targetSp.setId(null);
-						targetSp.setOrgId(dto.getToRootOrgId());
-						targetSp.setCoursePropertyId(targetCourseProper.getId());
-						targetSp.setParentId(targetFp.getId());
-						targetSp.setNumber(secondNum);
-						targetSp.setSourceId(sp.getId());
-						propertyRepo.save(targetSp);
-					} else {
-						targetFp = targetsouceIds.get(fp.getId());
-					}
-				}
-			}
-		}
-	}
-
-	private Map<String, Property> getSourceIds(List<Property> targetFps) {
-		Map<String, Property> set = new HashMap<>();
-		if (CollectionUtils.isEmpty(targetFps)) {
-			return set;
-		}
-		for (Property p : targetFps) {
-			set.put(p.getSourceId(), p);
-		}
-		return set;
-	}
-
-	private void clonePaper(Paper oldPaper, CopyDataDto dto) {
-		User user = dto.getUser();
-
-		if (user == null) {
-			throw new StatusException("当前用户信息不能为空!");
-		}
-
-		if (user.getRootOrgId() == null) {
-			throw new StatusException("当前用户的顶级机构ID不能为空!");
-		}
-
-		// 复制原试卷的所有试题信息
-		Map<PaperDetail, List<PaperDetailUnit>> detailMaps = this.copyPaperDetails(oldPaper, dto);
-		if(detailMaps==null) {
-			return;
-		}
-		if (PaperType.IMPORT.equals(dto.getPaperType())) {// 复制原卷才处理音频
-			// 处理试题的音频(下载上传到云存储)
-			try {
-				this.dealQuestionAudios(detailMaps, user.getDisplayName());
-			} catch (Exception e) {
-				throw new StatusException("500", "复制音频失败!", e);
-			}
-		}
-
-		// 复制原试卷的信息
-		Paper newPaper = this.copyPaper(oldPaper, dto);
-		// 保存新试卷
-		paperRepo.save(newPaper);
-		try {
-			// 保存新试卷的所有试题信息
-			this.savePaperDetails(detailMaps, newPaper, user);
-		} catch (Exception e) {
-			throw new StatusException("500", "克隆试卷失败!", e);
-		}
-	}
-
-	private void savePaperDetails(Map<PaperDetail, List<PaperDetailUnit>> detailMaps, Paper newPaper, User user) {
-		if (StringUtils.isEmpty(newPaper.getId())) {
-			throw new StatusException("试卷保存失败!");
-		}
-		for (Map.Entry<PaperDetail, List<PaperDetailUnit>> entry : detailMaps.entrySet()) {
-			PaperDetail detail = entry.getKey();
-			detail.setPaper(newPaper);
-		}
-		// 批量保存大题
-		paperDetailRepo.saveAll(detailMaps.keySet());
-		List<PaperDetailUnit> newDetailUnits = new ArrayList<>();
-		List<Question> newQuestions = new ArrayList<>();
-		for (Map.Entry<PaperDetail, List<PaperDetailUnit>> entry : detailMaps.entrySet()) {
-			PaperDetail detail = entry.getKey();
-			List<PaperDetailUnit> detailUnits = entry.getValue();
-			for (PaperDetailUnit detailUnit : detailUnits) {
-				detailUnit.setPaper(newPaper);
-				detailUnit.setPaperDetail(detail);
-				newDetailUnits.add(detailUnit);
-				newQuestions.add(detailUnit.getQuestion());
-			}
-		}
-		if (PaperType.IMPORT.equals(newPaper.getPaperType())) {// 复制原卷才处理
-			// 批量新增试题
-			quesRepo.saveAll(newQuestions);
-
-			for (Question question : newQuestions) {
-				List<QuestionAudio> audioList = question.getAudioList();
-				if (audioList != null && audioList.size() > 0) {
-					for (QuestionAudio audio : audioList) {
-						audio.setQuestionId(question.getId());
-						questionAudioService.saveQuestionAudio(audio, user);
-					}
-					this.updateQuestionBody(question, audioList);
-					// 如果试题存在音频,则更新试题的音频信息
-					quesRepo.save(question);
-				}
-			}
-		}
-
-		// 批量保存试题单元
-		paperDetailUnitRepo.saveAll(newDetailUnits);
-	}
-
-	/**
-	 * 更新试题题干或选项中的音频存储ID
-	 */
-	private void updateQuestionBody(Question question, List<QuestionAudio> audioList) {
-		Map<String, String> audioMaps = new HashMap<>();
-		for (QuestionAudio audio : audioList) {
-			audioMaps.put(audio.getFileName(), audio.getId());
-		}
-
-		// 在题干html片段中替换音频存储ID
-		String body = question.getQuesBody();
-		List<String> ids = CommonUtils.getTagANames(body);
-		List<String> names = CommonUtils.getTagANames2(body);
-		Map<String, String> oldBodyMap = new HashMap<>();
-		for (int i = 0; i < ids.size(); i++) {
-			oldBodyMap.put(names.get(i), ids.get(i));
-		}
-		for (String key : oldBodyMap.keySet()) {
-			body = body.replace(oldBodyMap.get(key), audioMaps.get(key));
-		}
-		question.setQuesBody(body);
-
-		if (question.getQuesOptions() == null || question.getQuesOptions().size() == 0) {
-			return;
-		}
-
-		// 替换选项中的音频存储ID
-		for (QuesOption option : question.getQuesOptions()) {
-			String newOptionBody = option.getOptionBody();
-			List<String> optionIds = CommonUtils.getTagANames(newOptionBody);
-			List<String> optionNames = CommonUtils.getTagANames2(newOptionBody);
-
-			Map<String, String> oldOptionMap = new HashMap<>();
-			for (int i = 0; i < optionIds.size(); i++) {
-				oldOptionMap.put(optionNames.get(i), optionIds.get(i));
-			}
-			for (String key : oldOptionMap.keySet()) {
-				newOptionBody = newOptionBody.replace(oldOptionMap.get(key), audioMaps.get(key));
-			}
-			option.setOptionBody(newOptionBody);
-		}
-	}
-
-	/**
-	 * 处理试题的音频(下载上传到云存储)
-	 */
-	private void dealQuestionAudios(Map<PaperDetail, List<PaperDetailUnit>> detailMaps, String userName) {
-		String copyAudioPath = FileConstants.TEMP_FILE_EXPORT + File.separator + randomUUID();
-		File copyAudioDir = new File(copyAudioPath);
-		if (!copyAudioDir.exists()) {
-			copyAudioDir.mkdirs();
-		}
-		for (Map.Entry<PaperDetail, List<PaperDetailUnit>> entry : detailMaps.entrySet()) {
-			List<PaperDetailUnit> detailUnits = entry.getValue();
-			for (PaperDetailUnit detailUnit : detailUnits) {
-				Question question = detailUnit.getQuestion();
-				List<QuestionAudio> audioList = question.getAudioList();
-				if (audioList == null || audioList.size() == 0) {
-					continue;
-				}
-				for (QuestionAudio audio : audioList) {
-					// 定义文件下载名称,并下载音频文件
-					String newAudioFileName = randomUUID() + "_" + audio.getFileName();
-					File audioFile = new File(copyAudioPath + File.separator + newAudioFileName);
-
-					// 通用存储
-					String filePath = FssHelper.fixFilePath(audio.getFileUrl());
-					FssFactory.getInstance().readFile(filePath, audioFile);
-
-					// 重新上传新的音频文件
-					String newPath = String.format(FileConstants.RADIO_UPLOAD_PATH, newAudioFileName);
-					try {
-						// 通用存储
-						FssFileInfo result = FssFactory.getInstance().writeFile(newPath, audioFile, null);
-						audio.setFileUrl(result.getFileUrl());//设置新路径
-					} catch (Exception e) {
-						throw new StatusException("上传音频文件失败!");
-					} finally {
-						IoUtils.removeFile(audioFile);
-					}
-				}
-			}
-		}
-
-		IoUtils.removeFile(copyAudioDir);
-	}
-
-	/**
-	 * 复制试卷的所有试题信息
-	 */
-	private Map<PaperDetail, List<PaperDetailUnit>> copyPaperDetails(Paper oldPaper, CopyDataDto dto) {
-		// 批量查询原试卷的所有试题
-		List<PaperDetailUnit> allDetailUnits = paperDetailUnitRepo.findByPaperOrderByNumber(oldPaper);
-		if (allDetailUnits == null || allDetailUnits.size() == 0) {
-			log.warn("忽略试卷复制,原试卷的试题不存在,courseCode:"+dto.getCourse().getCourseCode()+"|paperName:"+oldPaper.getName());
-			return null;
-		}
-		// 按大题有序封装对应的试题列表
-		Map<PaperDetail, List<PaperDetailUnit>> oldDetailMaps = new TreeMap<>();
-		for (PaperDetailUnit detailUnit : allDetailUnits) {
-			PaperDetail detail = detailUnit.getPaperDetail();
-			if (!oldDetailMaps.containsKey(detail)) {
-				oldDetailMaps.put(detail, new ArrayList<>());
-			}
-			List<PaperDetailUnit> detailUnits = oldDetailMaps.get(detail);
-			detailUnits.add(detailUnit);
-		}
-		// 开始复制
-		Map<PaperDetail, List<PaperDetailUnit>> detailMaps = new TreeMap<>();
-		for (Map.Entry<PaperDetail, List<PaperDetailUnit>> entry : oldDetailMaps.entrySet()) {
-			// 复制大题信息
-			PaperDetail oldPaperDetail = entry.getKey();
-			PaperDetail paperDetail = this.copyPaperDetail(oldPaperDetail, dto);
-			// 复制大题下的所有试题单元
-			List<PaperDetailUnit> oldDetailUnits = entry.getValue();
-			List<PaperDetailUnit> detailUnits = new ArrayList<>();
-			for (PaperDetailUnit oldDetailUnit : oldDetailUnits) {
-				PaperDetailUnit detailUnit = this.copyPaperDetailUnit(oldDetailUnit, dto);
-				detailUnits.add(detailUnit);
-			}
-			detailMaps.put(paperDetail, detailUnits);
-		}
-		return detailMaps;
-	}
-
-	/**
-	 * 复制试题的音频
-	 */
-	private QuestionAudio copyQuestionAudio(QuestionAudio oldAudio, CopyDataDto dto) {
-		QuestionAudio audio = new QuestionAudio();
-		audio.setId(null);
-		audio.setQuestionId(null);
-		audio.setFileUrl(oldAudio.getFileUrl());
-		audio.setFileName(oldAudio.getFileName());
-		audio.setFileSuffixes(oldAudio.getFileSuffixes());
-		audio.setCreateTime(new Date());
-		audio.setCreateUser(dto.getUser().getDisplayName());
-		audio.setOrgId(dto.getToRootOrgId());
-		audio.setCreationBy(dto.getUser().getUserId());
-		audio.setUpdateBy(dto.getUser().getUserId());
-		return audio;
-	}
-
-	/**
-	 * 复制试题信息
-	 */
-	private Question copyQuestion(Question oldQuestion, CopyDataDto dto) {
-		Question question = BeanCopierUtil.copyProperties(oldQuestion, Question.class);
-		question.setId(null);
-		question.getCourse().setId(dto.getCourse().getCourseId().toString());
-		question.getCourse().setOrgId(dto.getToRootOrgId().toString());
-		question.setOrgId(dto.getToRootOrgId().toString());
-		disposeProperty(question, dto);
-		question.setCreateTime(CommonUtils.getCurDateTime());
-		question.setUpdateTime(CommonUtils.getCurDateTime());
-		question.setPropertyGroup(null);
-		question.setCreationBy(dto.getUser().getUserId());
-		question.setUpdateBy(dto.getUser().getUserId());
-		question.setSourceId(oldQuestion.getId());
-		if (oldQuestion.getHasAudio() == null || !oldQuestion.getHasAudio()) {
-			question.setHasAudio(false);
-			return question;
-		}
-		// 复制试题的音频
-		List<QuestionAudio> audioList = questionAudioService.findQuestionAudiosByQuestionId(oldQuestion.getId());
-		if (audioList != null && audioList.size() > 0) {
-			for (QuestionAudio oldAudio : audioList) {
-				QuestionAudio audio = this.copyQuestionAudio(oldAudio, dto);
-				question.addAudio(audio);
-			}
-		}
-		return question;
-	}
-
-	private void disposeProperty(Question question, CopyDataDto dto) {
-		if (CollectionUtils.isNotEmpty(question.getQuesProperties())) {
-			for (QuesProperty qp : question.getQuesProperties()) {
-				if (qp != null) {
-					if (qp.getFirstProperty() != null && qp.getFirstProperty().getId() != null) {
-						Property p = dto.getSouceIdMap().get(qp.getFirstProperty().getId());
-						if (p == null) {
-							throw new StatusException("未找到试题属性 CourseCode:" + dto.getCourse().getCourseCode()
-									+ " | PropertyId:" + qp.getFirstProperty().getId());
-						}
-						qp.setFirstProperty(p);
-					}
-					if (qp.getSecondProperty() != null && qp.getSecondProperty().getId() != null) {
-						Property p = dto.getSouceIdMap().get(qp.getSecondProperty().getId());
-						if (p == null) {
-							throw new StatusException("未找到试题属性 CourseCode:" + dto.getCourse().getCourseCode()
-									+ " | PropertyId:" + qp.getSecondProperty().getId());
-						}
-						qp.setSecondProperty(p);
-					}
-				}
-			}
-		}
-		if (CollectionUtils.isNotEmpty(question.getSubQuestions())) {
-			for (Question sub : question.getSubQuestions()) {
-				disposeProperty(sub, dto);
-			}
-		}
-	}
-
-	/**
-	 * 复制试题单元信息
-	 */
-	private PaperDetailUnit copyPaperDetailUnit(PaperDetailUnit oldDetailUnit, CopyDataDto dto) {
-		Question oldQuestion = oldDetailUnit.getQuestion();
-		if (oldQuestion == null) {
-			throw new StatusException("原试题详细信息不存在!");
-		}
-		PaperDetailUnit detailUnit = new PaperDetailUnit();
-		detailUnit.setNumber(oldDetailUnit.getNumber());
-		detailUnit.setScore(oldDetailUnit.getScore());
-		detailUnit.setSubScoreList(oldDetailUnit.getSubScoreList());
-		detailUnit.setQuestionType(oldDetailUnit.getQuestionType());
-		detailUnit.setOptionOrder(oldDetailUnit.getOptionOrder());
-		detailUnit.setPaperType(oldDetailUnit.getPaperType());
-		detailUnit.setCreator(dto.getUser().getDisplayName());
-		detailUnit.setCreateTime(CommonUtils.getCurDateTime());
-		detailUnit.setOrgId(dto.getToRootOrgId());
-		detailUnit.setCreationBy(dto.getUser().getUserId());
-		detailUnit.setUpdateBy(dto.getUser().getUserId());
-		detailUnit.setPaper(null);
-		detailUnit.setPaperDetail(null);
-		if (PaperType.IMPORT.equals(dto.getPaperType())) {// 原卷才处理
-			// 复制试题信息
-			Question question = this.copyQuestion(oldQuestion, dto);
-			detailUnit.setQuestion(question);
-		} else {
-			List<Question> qs = quesService.findByOrgIdAndCourseCodeAndSourceId(dto.getToRootOrgId(),
-					dto.getCourse().getCourseCode(), oldQuestion.getId());
-			if (CollectionUtils.isEmpty(qs)) {
-				throw new StatusException("原试题信息不存在:courseCode" + dto.getCourse().getCourseCode() + "|" + "sourceId:"
-						+ oldQuestion.getId());
-			}
-			if (qs.size() > 1) {
-				throw new StatusException("原试题信息存在多个:courseCode" + dto.getCourse().getCourseCode() + "|" + "sourceId:"
-						+ oldQuestion.getId());
-			}
-			detailUnit.setQuestion(qs.get(0));
-		}
-		return detailUnit;
-	}
-
-	/**
-	 * 复制大题信息
-	 */
-	private PaperDetail copyPaperDetail(PaperDetail oldPaperDetail, CopyDataDto dto) {
-		PaperDetail paperDetail = new PaperDetail();
-		paperDetail.setNumber(oldPaperDetail.getNumber());
-		paperDetail.setName(oldPaperDetail.getName());
-		paperDetail.setTitle(oldPaperDetail.getTitle());
-		paperDetail.setScore(oldPaperDetail.getScore());
-		paperDetail.setUnitCount(oldPaperDetail.getUnitCount());
-		paperDetail.setCreator(dto.getUser().getDisplayName());
-		paperDetail.setCreateTime(CommonUtils.getCurDateTime());
-		paperDetail.setPaper(null);
-		paperDetail.setOrgId(dto.getToRootOrgId());
-		paperDetail.setCreationBy(dto.getUser().getUserId());
-		paperDetail.setUpdateBy(dto.getUser().getUserId());
-		return paperDetail;
-	}
-
-	/**
-	 * 复制试卷信息
-	 */
-	private Paper copyPaper(Paper oldPaper, CopyDataDto dto) {
-		Paper newPaper = BeanCopierUtil.copyProperties(oldPaper, Paper.class);
-		newPaper.getCourse().setId(dto.getCourse().getCourseId().toString());
-		newPaper.getCourse().setOrgId(dto.getToRootOrgId().toString());
-		newPaper.setOrgId(String.valueOf(dto.getToRootOrgId()));
-		newPaper.setCreateTime(CommonUtils.getCurDateTime());
-		newPaper.setLastModifyName(dto.getUser().getDisplayName());
-		newPaper.setCreator(dto.getUser().getDisplayName());
-		newPaper.setCreationBy(dto.getUser().getUserId());
-		newPaper.setUpdateBy(dto.getUser().getUserId());
-		newPaper.setInUse(0);
-		newPaper.setId(null);
-		newPaper.setSourceId(oldPaper.getId());
-		return newPaper;
-	}
-
-	private String randomUUID() {
-		return UUID.randomUUID().toString().replaceAll("-", "");
-	}
-//	private Long getCourseId(Map<String,Long> courses,String code) {
-//		Long id=courses.get(code);
-//		if(id==null) {
-//			throw new StatusException("没有课程信息code="+code);
-//		}
-//		return id;
-//	}
-}
+package cn.com.qmth.examcloud.core.questions.service.consumer;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+
+import cn.com.qmth.examcloud.core.questions.base.FileConstants;
+import cn.com.qmth.examcloud.support.fss.FssFactory;
+import cn.com.qmth.examcloud.support.fss.FssHelper;
+import cn.com.qmth.examcloud.support.fss.model.FssFileInfo;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.questions.base.BeanCopierUtil;
+import cn.com.qmth.examcloud.core.questions.base.CommonUtils;
+import cn.com.qmth.examcloud.core.questions.base.IoUtils;
+import cn.com.qmth.examcloud.core.questions.base.enums.PaperType;
+import cn.com.qmth.examcloud.core.questions.base.multithread.Consumer;
+import cn.com.qmth.examcloud.core.questions.dao.CoursePropertyRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PaperDetailRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PaperDetailUnitRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PaperRepo;
+import cn.com.qmth.examcloud.core.questions.dao.PropertyRepo;
+import cn.com.qmth.examcloud.core.questions.dao.QuesRepo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.CourseProperty;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Paper;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetail;
+import cn.com.qmth.examcloud.core.questions.dao.entity.PaperDetailUnit;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Property;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesOption;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesProperty;
+import cn.com.qmth.examcloud.core.questions.dao.entity.Question;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuestionAudio;
+import cn.com.qmth.examcloud.core.questions.service.CoursePropertyService;
+import cn.com.qmth.examcloud.core.questions.service.PaperService;
+import cn.com.qmth.examcloud.core.questions.service.PropertyService;
+import cn.com.qmth.examcloud.core.questions.service.QuesService;
+import cn.com.qmth.examcloud.core.questions.service.QuestionAudioService;
+import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataDto;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+
+@Scope("prototype")
+@Service
+public class CopyDataConsumer extends Consumer<CopyDataDto> {
+
+    private static final Logger log = LoggerFactory.getLogger(CopyDataConsumer.class);
+
+    private static int cacheLockTimeout = 60 * 10;
+
+    private static String copyQuestionDataLock = "$_COPY_QUESTION_DATA_LOCK";
+
+    @Autowired
+    private PaperRepo paperRepo;
+
+    @Autowired
+    private CoursePropertyService coursePropertyService;
+
+    @Autowired
+    private CoursePropertyRepo coursePropertyRepo;
+
+    @Autowired
+    private PropertyService propertyService;
+
+    @Autowired
+    private PropertyRepo propertyRepo;
+
+    @Autowired
+    private PaperDetailRepo paperDetailRepo;
+
+    @Autowired
+    private PaperDetailUnitRepo paperDetailUnitRepo;
+
+    @Autowired
+    private QuestionAudioService questionAudioService;
+
+    @Autowired
+    private PaperService paperService;
+
+    @Autowired
+    private QuesRepo quesRepo;
+
+    @Autowired
+    private QuesService quesService;
+
+    @Autowired
+    private RedisClient redisClient;
+
+    @Transactional
+    @Override
+    public int consume(CopyDataDto dto) {
+        // 先处理原卷
+        disposeCourse(dto);
+        // 成卷不需要处理属性,可先清空
+        dto.setSouceIdMap(null);
+        // 处理成卷
+        dto.setPaperType(PaperType.GENERATE);
+        disposeCourse(dto);
+        return 1;
+    }
+
+    private void disposeCourse(CopyDataDto dto) {
+        redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
+
+        if (PaperType.IMPORT.equals(dto.getPaperType())) {
+            copyCourseProperty(dto);
+        }
+        List<Paper> papers = paperService.getPapersByCourse(dto.getFromRootOrgId(), dto.getCourse().getCourseCode(),
+                dto.getPaperType());
+
+        redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
+
+        if (CollectionUtils.isEmpty(papers)) {
+            return;
+        }
+        if (PaperType.IMPORT.equals(dto.getPaperType())) {
+            fillSouceIdMap(dto);
+        }
+        for (Paper paper : papers) {
+            if (dto.getCopyAll()) {
+                clonePaper(paper, dto);
+            } else {
+                if (!paperService.existsPaperBySourceId(dto.getToRootOrgId(), dto.getCourse().getCourseCode(),
+                        dto.getPaperType(), paper.getId())) {
+                    clonePaper(paper, dto);
+                }
+            }
+            redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
+        }
+    }
+
+    private void fillSouceIdMap(CopyDataDto dto) {
+        List<CourseProperty> cps = coursePropertyService.findAllByOrgIdCourseCode(dto.getToRootOrgId(),
+                dto.getCourse().getCourseCode());
+        if (CollectionUtils.isEmpty(cps)) {
+            return;
+        }
+        for (CourseProperty cp : cps) {
+            List<Property> ps = propertyService.findByOrgIdAndCoursePropertyId(dto.getToRootOrgId(), cp.getId());
+            if (CollectionUtils.isEmpty(ps)) {
+                continue;
+            }
+            for (Property p : ps) {
+                dto.getSouceIdMap().put(p.getSourceId(), p);
+            }
+        }
+    }
+
+    private void copyCourseProperty(CopyDataDto dto) {
+        List<CourseProperty> cps = coursePropertyService.findAllByOrgIdCourseCode(dto.getFromRootOrgId(),
+                dto.getCourse().getCourseCode());
+        if (CollectionUtils.isEmpty(cps)) {
+            return;
+        }
+        for (CourseProperty cp : cps) {
+            CourseProperty targetCourseProper = null;
+            List<CourseProperty> targetFirstPropers = coursePropertyService
+                    .findByOrgIdAndCourseIdAndSourceId(dto.getToRootOrgId(), dto.getCourse().getCourseId(), cp.getId());
+            if (CollectionUtils.isEmpty(targetFirstPropers)) {
+                targetCourseProper = BeanCopierUtil.copyProperties(cp, CourseProperty.class);
+                targetCourseProper.setId(null);
+                targetCourseProper.setOrgId(dto.getToRootOrgId());
+                targetCourseProper.setCourseId(dto.getCourse().getCourseId());
+                targetCourseProper.setUpdateTime(new Date());
+                targetCourseProper.setSourceId(cp.getId());
+                coursePropertyRepo.save(targetCourseProper);
+            } else {
+                targetCourseProper = targetFirstPropers.get(0);
+            }
+            // 处理一级属性
+            List<Property> fps = propertyService.findParentProperties(cp.getId(), dto.getFromRootOrgId());
+            if (CollectionUtils.isEmpty(fps)) {
+                continue;
+            }
+            List<Property> targetFps = propertyService.findParentProperties(targetCourseProper.getId(),
+                    dto.getToRootOrgId());
+            int firstNum = 0;
+            if (CollectionUtils.isNotEmpty(targetFps)) {
+                firstNum = targetFps.size();
+            }
+            Map<String, Property> targetsouceIds = getSourceIds(targetFps);
+            for (Property fp : fps) {
+                Property targetFp = null;
+                if (targetsouceIds.get(fp.getId()) == null) {
+                    firstNum++;
+                    targetFp = BeanCopierUtil.copyProperties(fp, Property.class);
+                    targetFp.setId(null);
+                    targetFp.setOrgId(dto.getToRootOrgId());
+                    targetFp.setCoursePropertyId(targetCourseProper.getId());
+                    targetFp.setParentId(Property.ROOT_PARENT_ID);
+                    targetFp.setNumber(firstNum);
+                    targetFp.setSourceId(fp.getId());
+                    propertyRepo.save(targetFp);
+                } else {
+                    targetFp = targetsouceIds.get(fp.getId());
+                }
+                // 处理二级属性
+                List<Property> sps = propertyService.findSonProperties(fp.getId());
+                if (CollectionUtils.isEmpty(sps)) {
+                    continue;
+                }
+                List<Property> targetSps = propertyService.findSonProperties(targetFp.getId());
+                int secondNum = 0;
+                if (CollectionUtils.isNotEmpty(targetSps)) {
+                    secondNum = targetSps.size();
+                }
+                Map<String, Property> targetSecondSouceIds = getSourceIds(targetSps);
+                for (Property sp : sps) {
+                    Property targetSp = null;
+                    if (targetSecondSouceIds.get(sp.getId()) == null) {
+                        secondNum++;
+                        targetSp = BeanCopierUtil.copyProperties(sp, Property.class);
+                        targetSp.setId(null);
+                        targetSp.setOrgId(dto.getToRootOrgId());
+                        targetSp.setCoursePropertyId(targetCourseProper.getId());
+                        targetSp.setParentId(targetFp.getId());
+                        targetSp.setNumber(secondNum);
+                        targetSp.setSourceId(sp.getId());
+                        propertyRepo.save(targetSp);
+                    } else {
+                        targetFp = targetsouceIds.get(fp.getId());
+                    }
+                }
+            }
+        }
+    }
+
+    private Map<String, Property> getSourceIds(List<Property> targetFps) {
+        Map<String, Property> set = new HashMap<>();
+        if (CollectionUtils.isEmpty(targetFps)) {
+            return set;
+        }
+        for (Property p : targetFps) {
+            set.put(p.getSourceId(), p);
+        }
+        return set;
+    }
+
+    private void clonePaper(Paper oldPaper, CopyDataDto dto) {
+        User user = dto.getUser();
+
+        if (user == null) {
+            throw new StatusException("当前用户信息不能为空!");
+        }
+
+        if (user.getRootOrgId() == null) {
+            throw new StatusException("当前用户的顶级机构ID不能为空!");
+        }
+
+        // 复制原试卷的所有试题信息
+        Map<PaperDetail, List<PaperDetailUnit>> detailMaps = this.copyPaperDetails(oldPaper, dto);
+        if (detailMaps == null) {
+            return;
+        }
+        if (PaperType.IMPORT.equals(dto.getPaperType())) {// 复制原卷才处理音频
+            // 处理试题的音频(下载上传到云存储)
+            try {
+                this.dealQuestionAudios(detailMaps, user.getDisplayName());
+            } catch (Exception e) {
+                throw new StatusException("500", "复制音频失败!", e);
+            }
+        }
+
+        // 复制原试卷的信息
+        Paper newPaper = this.copyPaper(oldPaper, dto);
+        // 保存新试卷
+        paperRepo.save(newPaper);
+        try {
+            // 保存新试卷的所有试题信息
+            this.savePaperDetails(detailMaps, newPaper, user, dto);
+        } catch (Exception e) {
+            throw new StatusException("500", "克隆试卷失败!", e);
+        }
+    }
+
+    private void savePaperDetails(Map<PaperDetail, List<PaperDetailUnit>> detailMaps, Paper newPaper, User user,
+            CopyDataDto dto) {
+        if (StringUtils.isEmpty(newPaper.getId())) {
+            throw new StatusException("试卷保存失败!");
+        }
+        for (Map.Entry<PaperDetail, List<PaperDetailUnit>> entry : detailMaps.entrySet()) {
+            PaperDetail detail = entry.getKey();
+            detail.setPaper(newPaper);
+        }
+        // 批量保存大题
+        paperDetailRepo.saveAll(detailMaps.keySet());
+        List<PaperDetailUnit> newDetailUnits = new ArrayList<>();
+        List<Question> newQuestions = new ArrayList<>();
+        for (Map.Entry<PaperDetail, List<PaperDetailUnit>> entry : detailMaps.entrySet()) {
+            PaperDetail detail = entry.getKey();
+            List<PaperDetailUnit> detailUnits = entry.getValue();
+            for (PaperDetailUnit detailUnit : detailUnits) {
+                detailUnit.setPaper(newPaper);
+                detailUnit.setPaperDetail(detail);
+                newDetailUnits.add(detailUnit);
+                newQuestions.add(detailUnit.getQuestion());
+            }
+        }
+        if (PaperType.IMPORT.equals(newPaper.getPaperType())) {// 复制原卷才处理
+            // 批量新增试题
+            quesRepo.saveAll(newQuestions);
+
+            for (Question question : newQuestions) {
+                dto.getSouceIdToQuestionMap().put(question.getSourceId(), question.getId());
+                List<QuestionAudio> audioList = question.getAudioList();
+                if (audioList != null && audioList.size() > 0) {
+                    for (QuestionAudio audio : audioList) {
+                        audio.setQuestionId(question.getId());
+                        questionAudioService.saveQuestionAudio(audio, user);
+                    }
+                    this.updateQuestionBody(question, audioList);
+                    // 如果试题存在音频,则更新试题的音频信息
+                    quesRepo.save(question);
+                }
+            }
+        }
+
+        // 批量保存试题单元
+        paperDetailUnitRepo.saveAll(newDetailUnits);
+    }
+
+    /**
+     * 更新试题题干或选项中的音频存储ID
+     */
+    private void updateQuestionBody(Question question, List<QuestionAudio> audioList) {
+        Map<String, String> audioMaps = new HashMap<>();
+        for (QuestionAudio audio : audioList) {
+            audioMaps.put(audio.getFileName(), audio.getId());
+        }
+
+        // 在题干html片段中替换音频存储ID
+        String body = question.getQuesBody();
+        List<String> ids = CommonUtils.getTagANames(body);
+        List<String> names = CommonUtils.getTagANames2(body);
+        Map<String, String> oldBodyMap = new HashMap<>();
+        for (int i = 0; i < ids.size(); i++) {
+            oldBodyMap.put(names.get(i), ids.get(i));
+        }
+        for (String key : oldBodyMap.keySet()) {
+            body = body.replace(oldBodyMap.get(key), audioMaps.get(key));
+        }
+        question.setQuesBody(body);
+
+        if (question.getQuesOptions() == null || question.getQuesOptions().size() == 0) {
+            return;
+        }
+
+        // 替换选项中的音频存储ID
+        for (QuesOption option : question.getQuesOptions()) {
+            String newOptionBody = option.getOptionBody();
+            List<String> optionIds = CommonUtils.getTagANames(newOptionBody);
+            List<String> optionNames = CommonUtils.getTagANames2(newOptionBody);
+
+            Map<String, String> oldOptionMap = new HashMap<>();
+            for (int i = 0; i < optionIds.size(); i++) {
+                oldOptionMap.put(optionNames.get(i), optionIds.get(i));
+            }
+            for (String key : oldOptionMap.keySet()) {
+                newOptionBody = newOptionBody.replace(oldOptionMap.get(key), audioMaps.get(key));
+            }
+            option.setOptionBody(newOptionBody);
+        }
+    }
+
+    /**
+     * 处理试题的音频(下载上传到云存储)
+     */
+    private void dealQuestionAudios(Map<PaperDetail, List<PaperDetailUnit>> detailMaps, String userName) {
+        String copyAudioPath = FileConstants.TEMP_FILE_EXPORT + File.separator + randomUUID();
+        File copyAudioDir = new File(copyAudioPath);
+        if (!copyAudioDir.exists()) {
+            copyAudioDir.mkdirs();
+        }
+        for (Map.Entry<PaperDetail, List<PaperDetailUnit>> entry : detailMaps.entrySet()) {
+            List<PaperDetailUnit> detailUnits = entry.getValue();
+            for (PaperDetailUnit detailUnit : detailUnits) {
+                Question question = detailUnit.getQuestion();
+                List<QuestionAudio> audioList = question.getAudioList();
+                if (audioList == null || audioList.size() == 0) {
+                    continue;
+                }
+                for (QuestionAudio audio : audioList) {
+                    // 定义文件下载名称,并下载音频文件
+                    String newAudioFileName = randomUUID() + "_" + audio.getFileName();
+                    File audioFile = new File(copyAudioPath + File.separator + newAudioFileName);
+
+                    // 通用存储
+                    String filePath = FssHelper.fixFilePath(audio.getFileUrl());
+                    FssFactory.getInstance().readFile(filePath, audioFile);
+
+                    // 重新上传新的音频文件
+                    String newPath = String.format(FileConstants.RADIO_UPLOAD_PATH, newAudioFileName);
+                    try {
+                        // 通用存储
+                        FssFileInfo result = FssFactory.getInstance().writeFile(newPath, audioFile, null);
+                        audio.setFileUrl(result.getFileUrl());// 设置新路径
+                    } catch (Exception e) {
+                        throw new StatusException("上传音频文件失败!");
+                    } finally {
+                        IoUtils.removeFile(audioFile);
+                    }
+                }
+            }
+        }
+
+        IoUtils.removeFile(copyAudioDir);
+    }
+
+    /**
+     * 复制试卷的所有试题信息
+     */
+    private Map<PaperDetail, List<PaperDetailUnit>> copyPaperDetails(Paper oldPaper, CopyDataDto dto) {
+        // 批量查询原试卷的所有试题
+        List<PaperDetailUnit> allDetailUnits = paperDetailUnitRepo.findByPaperOrderByNumber(oldPaper);
+        if (allDetailUnits == null || allDetailUnits.size() == 0) {
+            log.warn("忽略试卷复制,原试卷的试题不存在,courseCode:" + dto.getCourse().getCourseCode() + "|paperName:"
+                    + oldPaper.getName());
+            return null;
+        }
+        // 按大题有序封装对应的试题列表
+        Map<PaperDetail, List<PaperDetailUnit>> oldDetailMaps = new TreeMap<>();
+        for (PaperDetailUnit detailUnit : allDetailUnits) {
+            PaperDetail detail = detailUnit.getPaperDetail();
+            if (!oldDetailMaps.containsKey(detail)) {
+                oldDetailMaps.put(detail, new ArrayList<>());
+            }
+            List<PaperDetailUnit> detailUnits = oldDetailMaps.get(detail);
+            detailUnits.add(detailUnit);
+        }
+        // 开始复制
+        Map<PaperDetail, List<PaperDetailUnit>> detailMaps = new TreeMap<>();
+        for (Map.Entry<PaperDetail, List<PaperDetailUnit>> entry : oldDetailMaps.entrySet()) {
+            // 复制大题信息
+            PaperDetail oldPaperDetail = entry.getKey();
+            PaperDetail paperDetail = this.copyPaperDetail(oldPaperDetail, dto);
+            // 复制大题下的所有试题单元
+            List<PaperDetailUnit> oldDetailUnits = entry.getValue();
+            List<PaperDetailUnit> detailUnits = new ArrayList<>();
+            for (PaperDetailUnit oldDetailUnit : oldDetailUnits) {
+                PaperDetailUnit detailUnit = this.copyPaperDetailUnit(oldDetailUnit, dto);
+                detailUnits.add(detailUnit);
+            }
+            detailMaps.put(paperDetail, detailUnits);
+        }
+        return detailMaps;
+    }
+
+    /**
+     * 复制试题的音频
+     */
+    private QuestionAudio copyQuestionAudio(QuestionAudio oldAudio, CopyDataDto dto) {
+        QuestionAudio audio = new QuestionAudio();
+        audio.setId(null);
+        audio.setQuestionId(null);
+        audio.setFileUrl(oldAudio.getFileUrl());
+        audio.setFileName(oldAudio.getFileName());
+        audio.setFileSuffixes(oldAudio.getFileSuffixes());
+        audio.setCreateTime(new Date());
+        audio.setCreateUser(dto.getUser().getDisplayName());
+        audio.setOrgId(dto.getToRootOrgId());
+        audio.setCreationBy(dto.getUser().getUserId());
+        audio.setUpdateBy(dto.getUser().getUserId());
+        return audio;
+    }
+
+    /**
+     * 复制试题信息
+     */
+    private Question copyQuestion(Question oldQuestion, CopyDataDto dto) {
+        Question question = BeanCopierUtil.copyProperties(oldQuestion, Question.class);
+        question.setId(null);
+        question.getCourse().setId(dto.getCourse().getCourseId().toString());
+        question.getCourse().setOrgId(dto.getToRootOrgId().toString());
+        question.setOrgId(dto.getToRootOrgId().toString());
+        disposeProperty(question, dto);
+        question.setCreateTime(CommonUtils.getCurDateTime());
+        question.setUpdateTime(CommonUtils.getCurDateTime());
+        question.setPropertyGroup(null);
+        question.setCreationBy(dto.getUser().getUserId());
+        question.setUpdateBy(dto.getUser().getUserId());
+        question.setSourceId(oldQuestion.getId());
+        if (oldQuestion.getHasAudio() == null || !oldQuestion.getHasAudio()) {
+            question.setHasAudio(false);
+            return question;
+        }
+        // 复制试题的音频
+        List<QuestionAudio> audioList = questionAudioService.findQuestionAudiosByQuestionId(oldQuestion.getId());
+        if (audioList != null && audioList.size() > 0) {
+            for (QuestionAudio oldAudio : audioList) {
+                QuestionAudio audio = this.copyQuestionAudio(oldAudio, dto);
+                question.addAudio(audio);
+            }
+        }
+        return question;
+    }
+
+    private void disposeProperty(Question question, CopyDataDto dto) {
+        if (CollectionUtils.isNotEmpty(question.getQuesProperties())) {
+            for (QuesProperty qp : question.getQuesProperties()) {
+                if (qp != null) {
+                    if (qp.getFirstProperty() != null && qp.getFirstProperty().getId() != null) {
+                        Property p = dto.getSouceIdMap().get(qp.getFirstProperty().getId());
+                        if (p == null) {
+                            throw new StatusException("未找到试题属性 CourseCode:" + dto.getCourse().getCourseCode()
+                                    + " | PropertyId:" + qp.getFirstProperty().getId());
+                        }
+                        qp.setFirstProperty(p);
+                    }
+                    if (qp.getSecondProperty() != null && qp.getSecondProperty().getId() != null) {
+                        Property p = dto.getSouceIdMap().get(qp.getSecondProperty().getId());
+                        if (p == null) {
+                            throw new StatusException("未找到试题属性 CourseCode:" + dto.getCourse().getCourseCode()
+                                    + " | PropertyId:" + qp.getSecondProperty().getId());
+                        }
+                        qp.setSecondProperty(p);
+                    }
+                }
+            }
+        }
+        if (CollectionUtils.isNotEmpty(question.getSubQuestions())) {
+            for (Question sub : question.getSubQuestions()) {
+                disposeProperty(sub, dto);
+            }
+        }
+    }
+
+    /**
+     * 复制试题单元信息
+     */
+    private PaperDetailUnit copyPaperDetailUnit(PaperDetailUnit oldDetailUnit, CopyDataDto dto) {
+        Question oldQuestion = oldDetailUnit.getQuestion();
+        if (oldQuestion == null) {
+            throw new StatusException("原试题详细信息不存在!");
+        }
+        PaperDetailUnit detailUnit = new PaperDetailUnit();
+        detailUnit.setNumber(oldDetailUnit.getNumber());
+        detailUnit.setScore(oldDetailUnit.getScore());
+        detailUnit.setSubScoreList(oldDetailUnit.getSubScoreList());
+        detailUnit.setQuestionType(oldDetailUnit.getQuestionType());
+        detailUnit.setOptionOrder(oldDetailUnit.getOptionOrder());
+        detailUnit.setPaperType(oldDetailUnit.getPaperType());
+        detailUnit.setCreator(dto.getUser().getDisplayName());
+        detailUnit.setCreateTime(CommonUtils.getCurDateTime());
+        detailUnit.setOrgId(dto.getToRootOrgId());
+        detailUnit.setCreationBy(dto.getUser().getUserId());
+        detailUnit.setUpdateBy(dto.getUser().getUserId());
+        detailUnit.setPaper(null);
+        detailUnit.setPaperDetail(null);
+        if (PaperType.IMPORT.equals(dto.getPaperType())) {// 原卷才处理
+            // 复制试题信息
+            Question question = this.copyQuestion(oldQuestion, dto);
+            detailUnit.setQuestion(question);
+        } else {
+            String newQuestionId = dto.getSouceIdToQuestionMap().get(oldQuestion.getId());
+            if (StringUtils.isBlank(newQuestionId)) {
+                throw new StatusException("原试题信息不存在:courseCode" + dto.getCourse().getCourseCode() + "|" + "sourceId:"
+                        + oldQuestion.getId());
+            }
+            Question newqs = quesService.findById(newQuestionId);
+            if (newqs == null) {
+                throw new StatusException("原试题信息未保存:courseCode" + dto.getCourse().getCourseCode() + "|" + "sourceId:"
+                        + oldQuestion.getId() + " | newQuestionId:" + newQuestionId);
+            }
+            detailUnit.setQuestion(newqs);
+        }
+        return detailUnit;
+    }
+
+    /**
+     * 复制大题信息
+     */
+    private PaperDetail copyPaperDetail(PaperDetail oldPaperDetail, CopyDataDto dto) {
+        PaperDetail paperDetail = new PaperDetail();
+        paperDetail.setNumber(oldPaperDetail.getNumber());
+        paperDetail.setName(oldPaperDetail.getName());
+        paperDetail.setTitle(oldPaperDetail.getTitle());
+        paperDetail.setScore(oldPaperDetail.getScore());
+        paperDetail.setUnitCount(oldPaperDetail.getUnitCount());
+        paperDetail.setCreator(dto.getUser().getDisplayName());
+        paperDetail.setCreateTime(CommonUtils.getCurDateTime());
+        paperDetail.setPaper(null);
+        paperDetail.setOrgId(dto.getToRootOrgId());
+        paperDetail.setCreationBy(dto.getUser().getUserId());
+        paperDetail.setUpdateBy(dto.getUser().getUserId());
+        return paperDetail;
+    }
+
+    /**
+     * 复制试卷信息
+     */
+    private Paper copyPaper(Paper oldPaper, CopyDataDto dto) {
+        Paper newPaper = BeanCopierUtil.copyProperties(oldPaper, Paper.class);
+        newPaper.setName(newPaper.getName() + "-" + dto.getPaperSuff());
+        newPaper.getCourse().setId(dto.getCourse().getCourseId().toString());
+        newPaper.getCourse().setOrgId(dto.getToRootOrgId().toString());
+        newPaper.setOrgId(String.valueOf(dto.getToRootOrgId()));
+        newPaper.setCreateTime(CommonUtils.getCurDateTime());
+        newPaper.setLastModifyName(dto.getUser().getDisplayName());
+        newPaper.setCreator(dto.getUser().getDisplayName());
+        newPaper.setCreationBy(dto.getUser().getUserId());
+        newPaper.setUpdateBy(dto.getUser().getUserId());
+        newPaper.setInUse(0);
+        newPaper.setId(null);
+        newPaper.setSourceId(oldPaper.getId());
+        return newPaper;
+    }
+
+    private String randomUUID() {
+        return UUID.randomUUID().toString().replaceAll("-", "");
+    }
+    // private Long getCourseId(Map<String,Long> courses,String code) {
+    // Long id=courses.get(code);
+    // if(id==null) {
+    // throw new StatusException("没有课程信息code="+code);
+    // }
+    // return id;
+    // }
+}

+ 217 - 207
examcloud-core-questions-service/src/main/java/cn/com/qmth/examcloud/core/questions/service/impl/CopyDataServiceImpl.java

@@ -1,207 +1,217 @@
-package cn.com.qmth.examcloud.core.questions.service.impl;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.commons.collections4.CollectionUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Service;
-
-import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.core.questions.dao.QuesTypeNameRepo;
-import cn.com.qmth.examcloud.core.questions.dao.entity.QuesTypeName;
-import cn.com.qmth.examcloud.core.questions.service.CopyDataService;
-import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataCourseInfo;
-import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataDto;
-import cn.com.qmth.examcloud.core.questions.service.producer.CopyDataProducer;
-import cn.com.qmth.examcloud.web.redis.RedisClient;
-
-@Service("copyDataService")
-public class CopyDataServiceImpl implements CopyDataService {
-	private static final Logger log = LoggerFactory.getLogger(CopyDataService.class);
-	private static int cacheLockTimeout = 60 * 10;
-	private static String copyQuestionDataLock = "$_COPY_QUESTION_DATA_LOCK";
-	@Autowired
-	private CopyDataProducer copyDataProducer;
-
-//	@Autowired
-//	private CopyDataNoReduplicateProducer copyDataNoReduplicateProducer;
-
-	@Autowired
-	private QuesTypeNameRepo quesTypeNameRepo;
-	@Autowired
-	private RedisClient redisClient;
-
-//	@Async
-//	@Override
-//	@Transactional
-//	public void copyNoReduplicate(User user, String batch) {
-//		Long rootOrgId = user.getRootOrgId();
-//		boolean sucss = true;
-//		long s1 = System.currentTimeMillis();
-//		try {
-//			log.warn("数据复制开始 | rootOrgId=" + rootOrgId);
-//			disposeCopyNoReduplicate(user, batch);
-//		} catch (Exception e) {
-//			sucss = false;
-//			throw e;
-//		} finally {
-//			long s2 = System.currentTimeMillis();
-//			if (sucss) {
-//				log.warn("数据复制结束 | 成功 |  " + ((s2 - s1) / 1000) + "秒 |  rootOrgId=" + rootOrgId);
-//			} else {
-//				log.warn("数据复制结束 | 失败 |  " + ((s2 - s1) / 1000) + "秒 |  rootOrgId=" + rootOrgId);
-//			}
-//			redisClient.delete(copyQuestionDataLock);
-//		}
-//	}
-//
-//	private void disposeCopyNoReduplicate(User user, String batch) {
-//		Long rootOrgId=user.getRootOrgId();
-//		redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
-//		List<CourseBean> courses = new ArrayList<>();
-//		GetCourseByOrgReq courseReq = new GetCourseByOrgReq();
-//		courseReq.setRootOrgId(rootOrgId);
-//		GetCourseByOrgResp res = courseCloudService.getCourseByOrg(courseReq);
-//		if (CollectionUtils.isEmpty(res.getCourseList())) {
-//			log.warn("没有课程数据");
-//		}
-//		for (CourseBean c : res.getCourseList()) {
-//			if (c.getEnable()) {
-//				courses.add(c);
-//			}
-//		}
-//		if (courses.size() == 0) {
-//			log.warn("没有课程数据");
-//		}
-//		redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
-//		List<CopyDataNoReduplicateDto> dtos = new ArrayList<>();
-//		for (CourseBean c : courses) {
-//			dtos.add(new CopyDataNoReduplicateDto(c, user,batch));
-//		}
-//		Map<String, Object> param = new HashMap<>();
-//		param.put("dtos", dtos);
-//		copyDataNoReduplicateProducer.startDispose(4, param, dtos.size());
-//		String countInfo = "成功科目数:" + copyDataNoReduplicateProducer.getProcess() + " 科目总数:" + copyDataNoReduplicateProducer.getTotal();
-//		log.warn(countInfo);
-//	}
-
-	@Async
-	@Override
-	public void copyData(User user, Long fromRootOrgId, Long toRootOrgId, List<CopyDataCourseInfo> courses) {
-		boolean sucss = true;
-		long s1 = System.currentTimeMillis();
-		try {
-			log.warn("CopyData | 数据复制开始 | fromRootOrgId=" + fromRootOrgId + " | toRootOrgId=" + toRootOrgId);
-			redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
-			if (CollectionUtils.isNotEmpty(courses)) {
-				List<CopyDataDto> dtos = new ArrayList<>();
-				for (CopyDataCourseInfo course : courses) {
-					dtos.add(new CopyDataDto(course, fromRootOrgId, toRootOrgId, user));
-					redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
-				}
-				Map<String, Object> param = new HashMap<>();
-				param.put("dtos", dtos);
-				copyDataProducer.startDispose(8, param, dtos.size());
-				redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
-				copyQuesTypeName(fromRootOrgId, toRootOrgId);
-				String countInfo = "CopyData | 成功数:" + copyDataProducer.getProcess() + " 总数:" + copyDataProducer.getTotal();
-				log.warn(countInfo);
-			} else {
-				log.warn("没有试卷数据需要处理");
-			}
-		}catch (Exception e) {
-			sucss = false;
-			throw e;
-		} finally {
-			redisClient.delete(copyQuestionDataLock);
-			long s2 = System.currentTimeMillis();
-			if (sucss) {
-				log.warn("CopyData | 数据复制结束 | 成功 |  " + ((s2 - s1) / 1000) + "秒 |  fromRootOrgId=" + fromRootOrgId
-						+ " | toRootOrgId=" + toRootOrgId);
-			} else {
-				log.warn("CopyData | 数据复制结束 | 失败 |  " + ((s2 - s1) / 1000) + "秒 |  fromRootOrgId=" + fromRootOrgId
-						+ " | toRootOrgId=" + toRootOrgId);
-			}
-		}
-	}
-
-	private void copyQuesTypeName(Long fromRootOrgId, Long toRootOrgId) {
-		List<QuesTypeName> from = quesTypeNameRepo.findByOrgId(fromRootOrgId.toString());
-		if (CollectionUtils.isEmpty(from)) {
-			return;
-		}
-		List<QuesTypeName> to = quesTypeNameRepo.findByOrgId(toRootOrgId.toString());
-
-		if (CollectionUtils.isEmpty(to)) {
-			for (QuesTypeName cp : from) {
-				cp.setOrgId(toRootOrgId.toString());
-				cp.setId(null);
-			}
-			quesTypeNameRepo.saveAll(from);
-		} else {
-			Map<String, QuesTypeName> fromMap = new HashMap<>();
-			Map<String, QuesTypeName> toMap = new HashMap<>();
-			List<QuesTypeName> add = new ArrayList<>();
-			List<QuesTypeName> update = new ArrayList<>();
-			for (QuesTypeName cp : from) {
-				String key = cp.getCourseNo() + "-" + cp.getQuestionType().name();
-				fromMap.put(key, cp);
-			}
-			for (QuesTypeName cp : to) {
-				String key = cp.getCourseNo() + "-" + cp.getQuestionType().name();
-				toMap.put(key, cp);
-			}
-			for (QuesTypeName cp : from) {
-				String key = cp.getCourseNo() + "-" + cp.getQuestionType().name();
-				QuesTypeName temUp = toMap.get(key);
-				if (temUp != null) {
-					temUp.setQuesNames(merge(temUp.getQuesNames(), cp.getQuesNames()));
-					update.add(temUp);
-				} else {
-					cp.setOrgId(toRootOrgId.toString());
-					cp.setId(null);
-					add.add(cp);
-				}
-			}
-			if (CollectionUtils.isNotEmpty(add)) {
-				quesTypeNameRepo.saveAll(add);
-			}
-			if (CollectionUtils.isNotEmpty(update)) {
-				quesTypeNameRepo.saveAll(update);
-			}
-		}
-	}
-
-	private List<String> merge(List<String> list1, List<String> list2) {
-		if (list1 == null && list2 == null) {
-			return new ArrayList<>();
-		}
-		if (list1 == null) {
-			return list2;
-		}
-		if (list2 == null) {
-			return list1;
-		}
-		Set<String> set = new LinkedHashSet<>();
-		set.addAll(list1);
-		set.addAll(list2);
-		return new ArrayList<>(set);
-	}
-
-//	private void clearData(Long toRootOrgId) {
-//		quesTypeNameRepo.deleteByOrgId(toRootOrgId.toString());
-//		paperRepo.deleteByOrgId(toRootOrgId.toString());
-//		paperDetailRepo.deleteByOrgId(toRootOrgId);
-//		paperDetailUnitRepo.deleteByOrgId(toRootOrgId);
-//		quesRepo.deleteByOrgId(toRootOrgId.toString());
-//		questionAudioRepo.deleteByOrgId(toRootOrgId);
-//	}
-}
+package cn.com.qmth.examcloud.core.questions.service.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.questions.dao.QuesTypeNameRepo;
+import cn.com.qmth.examcloud.core.questions.dao.entity.QuesTypeName;
+import cn.com.qmth.examcloud.core.questions.service.CopyDataService;
+import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataCourseInfo;
+import cn.com.qmth.examcloud.core.questions.service.bean.CopyDataDto;
+import cn.com.qmth.examcloud.core.questions.service.producer.CopyDataProducer;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+
+@Service("copyDataService")
+public class CopyDataServiceImpl implements CopyDataService {
+
+    private static final Logger log = LoggerFactory.getLogger(CopyDataService.class);
+
+    private static int cacheLockTimeout = 60 * 10;
+
+    private static String copyQuestionDataLock = "$_COPY_QUESTION_DATA_LOCK";
+
+    @Autowired
+    private CopyDataProducer copyDataProducer;
+
+    // @Autowired
+    // private CopyDataNoReduplicateProducer copyDataNoReduplicateProducer;
+
+    @Autowired
+    private QuesTypeNameRepo quesTypeNameRepo;
+
+    @Autowired
+    private RedisClient redisClient;
+
+    // @Async
+    // @Override
+    // @Transactional
+    // public void copyNoReduplicate(User user, String batch) {
+    // Long rootOrgId = user.getRootOrgId();
+    // boolean sucss = true;
+    // long s1 = System.currentTimeMillis();
+    // try {
+    // log.warn("数据复制开始 | rootOrgId=" + rootOrgId);
+    // disposeCopyNoReduplicate(user, batch);
+    // } catch (Exception e) {
+    // sucss = false;
+    // throw e;
+    // } finally {
+    // long s2 = System.currentTimeMillis();
+    // if (sucss) {
+    // log.warn("数据复制结束 | 成功 | " + ((s2 - s1) / 1000) + "秒 | rootOrgId=" +
+    // rootOrgId);
+    // } else {
+    // log.warn("数据复制结束 | 失败 | " + ((s2 - s1) / 1000) + "秒 | rootOrgId=" +
+    // rootOrgId);
+    // }
+    // redisClient.delete(copyQuestionDataLock);
+    // }
+    // }
+    //
+    // private void disposeCopyNoReduplicate(User user, String batch) {
+    // Long rootOrgId=user.getRootOrgId();
+    // redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
+    // List<CourseBean> courses = new ArrayList<>();
+    // GetCourseByOrgReq courseReq = new GetCourseByOrgReq();
+    // courseReq.setRootOrgId(rootOrgId);
+    // GetCourseByOrgResp res = courseCloudService.getCourseByOrg(courseReq);
+    // if (CollectionUtils.isEmpty(res.getCourseList())) {
+    // log.warn("没有课程数据");
+    // }
+    // for (CourseBean c : res.getCourseList()) {
+    // if (c.getEnable()) {
+    // courses.add(c);
+    // }
+    // }
+    // if (courses.size() == 0) {
+    // log.warn("没有课程数据");
+    // }
+    // redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
+    // List<CopyDataNoReduplicateDto> dtos = new ArrayList<>();
+    // for (CourseBean c : courses) {
+    // dtos.add(new CopyDataNoReduplicateDto(c, user,batch));
+    // }
+    // Map<String, Object> param = new HashMap<>();
+    // param.put("dtos", dtos);
+    // copyDataNoReduplicateProducer.startDispose(4, param, dtos.size());
+    // String countInfo = "成功科目数:" + copyDataNoReduplicateProducer.getProcess()
+    // + " 科目总数:" + copyDataNoReduplicateProducer.getTotal();
+    // log.warn(countInfo);
+    // }
+
+    @Async
+    @Override
+    public void copyData(User user, Long fromRootOrgId, Long toRootOrgId, List<CopyDataCourseInfo> courses,
+            String paperSuff, Boolean copyAll) {
+        boolean sucss = true;
+        long s1 = System.currentTimeMillis();
+        try {
+            log.warn("CopyData | 数据复制开始 | fromRootOrgId=" + fromRootOrgId + " | toRootOrgId=" + toRootOrgId);
+            redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
+            if (CollectionUtils.isNotEmpty(courses)) {
+                List<CopyDataDto> dtos = new ArrayList<>();
+                for (CopyDataCourseInfo course : courses) {
+                    dtos.add(new CopyDataDto(course, fromRootOrgId, toRootOrgId, user, paperSuff, copyAll));
+                    redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
+                }
+                Map<String, Object> param = new HashMap<>();
+                param.put("dtos", dtos);
+                copyDataProducer.startDispose(8, param, dtos.size());
+                redisClient.expire(copyQuestionDataLock, cacheLockTimeout);
+                copyQuesTypeName(fromRootOrgId, toRootOrgId);
+                String countInfo = "CopyData | 成功数:" + copyDataProducer.getProcess() + " 总数:"
+                        + copyDataProducer.getTotal();
+                log.warn(countInfo);
+            } else {
+                log.warn("没有试卷数据需要处理");
+            }
+        } catch (Exception e) {
+            sucss = false;
+            throw e;
+        } finally {
+            redisClient.delete(copyQuestionDataLock);
+            long s2 = System.currentTimeMillis();
+            if (sucss) {
+                log.warn("CopyData | 数据复制结束 | 成功 |  " + ((s2 - s1) / 1000) + "秒 |  fromRootOrgId=" + fromRootOrgId
+                        + " | toRootOrgId=" + toRootOrgId);
+            } else {
+                log.warn("CopyData | 数据复制结束 | 失败 |  " + ((s2 - s1) / 1000) + "秒 |  fromRootOrgId=" + fromRootOrgId
+                        + " | toRootOrgId=" + toRootOrgId);
+            }
+        }
+    }
+
+    private void copyQuesTypeName(Long fromRootOrgId, Long toRootOrgId) {
+        List<QuesTypeName> from = quesTypeNameRepo.findByOrgId(fromRootOrgId.toString());
+        if (CollectionUtils.isEmpty(from)) {
+            return;
+        }
+        List<QuesTypeName> to = quesTypeNameRepo.findByOrgId(toRootOrgId.toString());
+
+        if (CollectionUtils.isEmpty(to)) {
+            for (QuesTypeName cp : from) {
+                cp.setOrgId(toRootOrgId.toString());
+                cp.setId(null);
+            }
+            quesTypeNameRepo.saveAll(from);
+        } else {
+            Map<String, QuesTypeName> fromMap = new HashMap<>();
+            Map<String, QuesTypeName> toMap = new HashMap<>();
+            List<QuesTypeName> add = new ArrayList<>();
+            List<QuesTypeName> update = new ArrayList<>();
+            for (QuesTypeName cp : from) {
+                String key = cp.getCourseNo() + "-" + cp.getQuestionType().name();
+                fromMap.put(key, cp);
+            }
+            for (QuesTypeName cp : to) {
+                String key = cp.getCourseNo() + "-" + cp.getQuestionType().name();
+                toMap.put(key, cp);
+            }
+            for (QuesTypeName cp : from) {
+                String key = cp.getCourseNo() + "-" + cp.getQuestionType().name();
+                QuesTypeName temUp = toMap.get(key);
+                if (temUp != null) {
+                    temUp.setQuesNames(merge(temUp.getQuesNames(), cp.getQuesNames()));
+                    update.add(temUp);
+                } else {
+                    cp.setOrgId(toRootOrgId.toString());
+                    cp.setId(null);
+                    add.add(cp);
+                }
+            }
+            if (CollectionUtils.isNotEmpty(add)) {
+                quesTypeNameRepo.saveAll(add);
+            }
+            if (CollectionUtils.isNotEmpty(update)) {
+                quesTypeNameRepo.saveAll(update);
+            }
+        }
+    }
+
+    private List<String> merge(List<String> list1, List<String> list2) {
+        if (list1 == null && list2 == null) {
+            return new ArrayList<>();
+        }
+        if (list1 == null) {
+            return list2;
+        }
+        if (list2 == null) {
+            return list1;
+        }
+        Set<String> set = new LinkedHashSet<>();
+        set.addAll(list1);
+        set.addAll(list2);
+        return new ArrayList<>(set);
+    }
+
+    // private void clearData(Long toRootOrgId) {
+    // quesTypeNameRepo.deleteByOrgId(toRootOrgId.toString());
+    // paperRepo.deleteByOrgId(toRootOrgId.toString());
+    // paperDetailRepo.deleteByOrgId(toRootOrgId);
+    // paperDetailUnitRepo.deleteByOrgId(toRootOrgId);
+    // quesRepo.deleteByOrgId(toRootOrgId.toString());
+    // questionAudioRepo.deleteByOrgId(toRootOrgId);
+    // }
+}