|
@@ -0,0 +1,372 @@
|
|
|
|
+package com.qmth.distributed.print.business.service.impl;
|
|
|
|
+
|
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|
|
|
+import com.qmth.boot.tools.signature.SignatureType;
|
|
|
|
+import com.qmth.distributed.print.business.bean.dto.SyncExamCardDto;
|
|
|
|
+import com.qmth.distributed.print.business.bean.dto.SyncExamStudentDto;
|
|
|
|
+import com.qmth.distributed.print.business.config.DictionaryConfig;
|
|
|
|
+import com.qmth.distributed.print.business.entity.*;
|
|
|
|
+import com.qmth.distributed.print.business.enums.PrintPlanStatusEnum;
|
|
|
|
+import com.qmth.distributed.print.business.enums.TaskResultEnum;
|
|
|
|
+import com.qmth.distributed.print.business.enums.TaskStatusEnum;
|
|
|
|
+import com.qmth.distributed.print.business.service.*;
|
|
|
|
+import com.qmth.distributed.print.business.util.HttpKit;
|
|
|
|
+import com.qmth.distributed.print.common.SignatureEntityTest;
|
|
|
|
+import com.qmth.distributed.print.common.contant.SystemConstant;
|
|
|
|
+import com.qmth.distributed.print.common.enums.ExceptionResultEnum;
|
|
|
|
+import org.apache.commons.codec.digest.DigestUtils;
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
|
+
|
|
|
|
+import java.io.*;
|
|
|
|
+import java.util.*;
|
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
|
+import java.util.concurrent.Executors;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * @Date: 2021/5/20.
|
|
|
|
+ */
|
|
|
|
+@Service
|
|
|
|
+public class DataSyncServiceImpl implements DataSyncService {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private ExamPrintPlanService examPrintPlanService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private TBSyncTaskService tbSyncTaskService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private DictionaryConfig dictionaryConfig;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private CacheService cacheService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private ExamStudentService examStudentService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private ExamDetailService examDetailService;
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private ExamCardService examCardService;
|
|
|
|
+
|
|
|
|
+ private ExecutorService executors = Executors.newFixedThreadPool(5);
|
|
|
|
+
|
|
|
|
+ private static final String SAVE_EXAM_TYPE = "MULTI_MEDIA";
|
|
|
|
+ private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
|
|
|
+ private static final String POST_METHOD = "POST";
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 定时任务批量同步
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
|
|
+ public void syncToCloudReview() {
|
|
|
|
+ // 查询可同步计划(同步状态为空:未同步,false:同步失败)
|
|
|
|
+ QueryWrapper<ExamPrintPlan> queryWrapper = new QueryWrapper<>();
|
|
|
|
+ queryWrapper.lambda().eq(ExamPrintPlan::getStatus, PrintPlanStatusEnum.END)
|
|
|
|
+ .and(q -> q.isNull(ExamPrintPlan::getSyncStatus).or().eq(ExamPrintPlan::getSyncStatus, false));
|
|
|
|
+ List<ExamPrintPlan> examPrintPlans = examPrintPlanService.list(queryWrapper);
|
|
|
|
+ if (!CollectionUtils.isEmpty(examPrintPlans)) {
|
|
|
|
+ for (ExamPrintPlan examPrintPlan : examPrintPlans) {
|
|
|
|
+ executors.execute(syncData(examPrintPlan));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 单个同步
|
|
|
|
+ *
|
|
|
|
+ * @param printPlanId
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
|
|
+ public void syncDataCloud(Long printPlanId) {
|
|
|
|
+ ExamPrintPlan examPrintPlan = examPrintPlanService.getById(printPlanId);
|
|
|
|
+ if (examPrintPlan == null) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("印刷计划数据异常");
|
|
|
|
+ }
|
|
|
|
+ if (!PrintPlanStatusEnum.PRINT_FINISH.equals(examPrintPlan.getStatus())) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("印刷计划未结束,不能同步数据");
|
|
|
|
+ }
|
|
|
|
+ if (Objects.nonNull(examPrintPlan.getSyncStatus()) && examPrintPlan.getSyncStatus()) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("印刷计划数据同步成功");
|
|
|
|
+ }
|
|
|
|
+ executors.execute(syncData(examPrintPlan));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private TimerTask syncData(ExamPrintPlan examPrintPlan) {
|
|
|
|
+ return new TimerTask() {
|
|
|
|
+ @Override
|
|
|
|
+ public void run() {
|
|
|
|
+ TBSyncTask syncTask = tbSyncTaskService.saveTask(examPrintPlan);
|
|
|
|
+ doSyncCore(examPrintPlan, syncTask);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 同步核心方法
|
|
|
|
+ *
|
|
|
|
+ * @param syncTask
|
|
|
|
+ */
|
|
|
|
+ @Transactional
|
|
|
|
+ public void doSyncCore(ExamPrintPlan examPrintPlan, TBSyncTask syncTask) {
|
|
|
|
+ try {
|
|
|
|
+ if (!PrintPlanStatusEnum.PRINT_FINISH.equals(examPrintPlan.getStatus())) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("印刷计划未结束,不能同步数据");
|
|
|
|
+ }
|
|
|
|
+ // 校验同步url
|
|
|
|
+ validatUrl();
|
|
|
|
+ UpdateWrapper<TBSyncTask> tbSyncTaskUpdateWrapper = new UpdateWrapper<>();
|
|
|
|
+ tbSyncTaskUpdateWrapper.lambda().set(TBSyncTask::getStatus, TaskStatusEnum.RUNNING).eq(TBSyncTask::getId, syncTask.getId());
|
|
|
|
+ tbSyncTaskService.update(tbSyncTaskUpdateWrapper);
|
|
|
|
+
|
|
|
|
+ // 同步计划 -> 对应云阅卷考试
|
|
|
|
+ ExamPrintPlan printPlan = examSave(examPrintPlan);
|
|
|
|
+ // 考试同步成功,才能同步考生和题卡
|
|
|
|
+ if (Objects.nonNull(printPlan.getThirdRelateId())) {
|
|
|
|
+ studentSave(printPlan);
|
|
|
|
+ cardUpload(printPlan);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 更新计划状态
|
|
|
|
+ UpdateWrapper<ExamPrintPlan> updateWrapper = new UpdateWrapper<>();
|
|
|
|
+ updateWrapper.lambda().set(ExamPrintPlan::getSyncStatus, true).eq(ExamPrintPlan::getId, examPrintPlan.getId());
|
|
|
|
+ examPrintPlanService.update(updateWrapper);
|
|
|
|
+
|
|
|
|
+ // 更新日志表
|
|
|
|
+ syncTask.setResult(TaskResultEnum.SUCCESS);
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ syncTask.setResult(TaskResultEnum.ERROR);
|
|
|
|
+ syncTask.setErrorMessage(e.getMessage());
|
|
|
|
+ } finally {
|
|
|
|
+ syncTask.setStatus(TaskStatusEnum.FINISH);
|
|
|
|
+ tbSyncTaskService.saveOrUpdate(syncTask);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 校验url是否非空
|
|
|
|
+ */
|
|
|
|
+ private void validatUrl() {
|
|
|
|
+ String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
|
|
|
|
+ String examSaveUrl = dictionaryConfig.syncDataDomain().getExamSaveUrl();
|
|
|
|
+ String studentSaveUrl = dictionaryConfig.syncDataDomain().getStudentSaveUrl();
|
|
|
|
+ String cardUploadUrl = dictionaryConfig.syncDataDomain().getCardUploadUrl();
|
|
|
|
+
|
|
|
|
+ if (StringUtils.isAnyBlank(hostUrl, examSaveUrl, studentSaveUrl, cardUploadUrl)) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("云阅卷同步接口URL未正确配置");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 创建考试
|
|
|
|
+ *
|
|
|
|
+ * @param examPrintPlan
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ public ExamPrintPlan examSave(ExamPrintPlan examPrintPlan) {
|
|
|
|
+ String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
|
|
|
|
+ String examSaveUrl = dictionaryConfig.syncDataDomain().getExamSaveUrl();
|
|
|
|
+ String postUrl = hostUrl.concat(examSaveUrl);
|
|
|
|
+ try {
|
|
|
|
+ //参数
|
|
|
|
+ Map<String, String> map = new HashMap<>();
|
|
|
|
+ map.put("code", String.valueOf(examPrintPlan.getId()));
|
|
|
|
+ map.put("name", examPrintPlan.getName());
|
|
|
|
+ map.put("examTime", DateUtil.format(new Date(examPrintPlan.getExamEndTime()), DATE_FORMAT));
|
|
|
|
+ map.put("type", SAVE_EXAM_TYPE);
|
|
|
|
+
|
|
|
|
+ String result = HttpKit.sendPost(postUrl, getHeaders(examPrintPlan.getSchoolId(), examSaveUrl), map, null, null, null);
|
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(result);
|
|
|
|
+ if (jsonObject.containsKey("id")) {
|
|
|
|
+ Long id = Long.valueOf(jsonObject.get("id").toString());
|
|
|
|
+ UpdateWrapper<ExamPrintPlan> updateWrapper = new UpdateWrapper<>();
|
|
|
|
+ updateWrapper.lambda().set(ExamPrintPlan::getThirdRelateId, id).eq(ExamPrintPlan::getId, examPrintPlan.getId());
|
|
|
|
+ examPrintPlanService.update(updateWrapper);
|
|
|
|
+ return examPrintPlanService.getById(examPrintPlan.getId());
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception(e.getMessage());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 新增考生
|
|
|
|
+ *
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ public void studentSave(ExamPrintPlan examPrintPlan) {
|
|
|
|
+ String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
|
|
|
|
+ String studentSaveUrl = dictionaryConfig.syncDataDomain().getStudentSaveUrl();
|
|
|
|
+ String postUrl = hostUrl.concat(studentSaveUrl);
|
|
|
|
+
|
|
|
|
+ List<SyncExamStudentDto> examStudents = examStudentService.listStudentByPrintPlanIdAndSyncStatus(examPrintPlan.getId());
|
|
|
|
+ for (SyncExamStudentDto examStudent : examStudents) {
|
|
|
|
+ try {
|
|
|
|
+ //参数
|
|
|
|
+ Map<String, String> map = new HashMap<>();
|
|
|
|
+ map.put("examId", String.valueOf(examPrintPlan.getThirdRelateId()));
|
|
|
|
+ map.put("examNumber", examStudent.getTicketNumber());
|
|
|
|
+ map.put("studentCode", examStudent.getStudentCode());
|
|
|
|
+ map.put("name", examStudent.getStudentName());
|
|
|
|
+ map.put("college", "无");
|
|
|
|
+ map.put("className", getClassName(examStudent.getExtendFields()));
|
|
|
|
+ map.put("teacher", "无");
|
|
|
|
+ map.put("subjectCode", examStudent.getPaperNumber()); // 取试卷编号
|
|
|
|
+ map.put("subjectName", examStudent.getCourseName());
|
|
|
|
+
|
|
|
|
+ String result = HttpKit.sendPost(postUrl, getHeaders(examPrintPlan.getSchoolId(), studentSaveUrl), map, null, null, null);
|
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(result);
|
|
|
|
+ if (jsonObject.containsKey("updateTime")) {
|
|
|
|
+ UpdateWrapper<ExamStudent> updateWrapper = new UpdateWrapper<>();
|
|
|
|
+ updateWrapper.lambda().set(ExamStudent::getSyncStatus, true).eq(ExamStudent::getId, examStudent.getId());
|
|
|
|
+ examStudentService.update(updateWrapper);
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception(e.getMessage());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 推送题卡
|
|
|
|
+ *
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ public void cardUpload(ExamPrintPlan examPrintPlan) {
|
|
|
|
+ String hostUrl = dictionaryConfig.syncDataDomain().getHostUrl();
|
|
|
|
+ String cardUploadUrl = dictionaryConfig.syncDataDomain().getCardUploadUrl();
|
|
|
|
+ String postUrl = hostUrl.concat(cardUploadUrl);
|
|
|
|
+ List<ExamDetailCourse> examDetailCourses = examDetailService.listSyncPaperNumberByPrintPlanId(examPrintPlan.getId());
|
|
|
|
+ if (CollectionUtils.isEmpty(examDetailCourses)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ for (ExamDetailCourse examDetailCours : examDetailCourses) {
|
|
|
|
+ List<SyncExamCardDto> syncExamCardDtos = examCardService.listSyncCardByCourseCodeAndPaperNumber(examDetailCours.getSchoolId(), examDetailCours.getCourseCode(), examDetailCours.getPaperNumber());
|
|
|
|
+ if (CollectionUtils.isEmpty(syncExamCardDtos) || syncExamCardDtos.size() != 1) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception(String.format("数据异常,通过学校:%s,课程代码:%s,试卷编号:%s查出多个题卡数据", examDetailCours.getSchoolId(), examDetailCours.getCourseCode(), examDetailCours.getPaperNumber()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ SyncExamCardDto syncExamCardDto = syncExamCardDtos.get(0);
|
|
|
|
+ if (StringUtils.isNotEmpty(syncExamCardDto.getContent())) {
|
|
|
|
+ //生成json文件
|
|
|
|
+ File file = null;
|
|
|
|
+ try {
|
|
|
|
+ String filePath = SystemConstant.TEMP_FILES_DIR + File.separator + "card-upload" + File.separator + System.currentTimeMillis();
|
|
|
|
+ file = createJsonFile(filePath, syncExamCardDto.getContent());
|
|
|
|
+ Map<String, String> files = new HashMap<>();
|
|
|
|
+ if (file.exists()) {
|
|
|
|
+ files.put(syncExamCardDto.getTitle(), file.getPath());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //表单数据
|
|
|
|
+ Map<String, String> formText = new HashMap<>();
|
|
|
|
+ formText.put("examId", String.valueOf(examPrintPlan.getThirdRelateId()));
|
|
|
|
+ formText.put("subjectCode", syncExamCardDto.getPaperNumber());
|
|
|
|
+ formText.put("format", "json");
|
|
|
|
+ formText.put("md5", DigestUtils.md5Hex(new FileInputStream(file)));
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ String result = HttpKit.sendPost(postUrl, getHeaders(examDetailCours.getSchoolId(), cardUploadUrl), formText, files, null, null);
|
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(result);
|
|
|
|
+ if (jsonObject.containsKey("success")) {
|
|
|
|
+ String success = jsonObject.get("success").toString();
|
|
|
|
+ if (Boolean.valueOf(success)) {
|
|
|
|
+ UpdateWrapper<ExamCard> updateWrapper = new UpdateWrapper<>();
|
|
|
|
+ updateWrapper.lambda().set(ExamCard::getSyncStatus, true).eq(ExamCard::getId, syncExamCardDto.getId());
|
|
|
|
+ examCardService.update(updateWrapper);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception(e.getMessage());
|
|
|
|
+ } finally {
|
|
|
|
+ if (file != null && file.exists()) {
|
|
|
|
+ file.delete();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * http请求头
|
|
|
|
+ *
|
|
|
|
+ * @param url
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ private Map<String, String> getHeaders(Long schoolId, String url) {
|
|
|
|
+ long time = System.currentTimeMillis();
|
|
|
|
+ Map<String, String> header = new HashMap<>();
|
|
|
|
+ header.put(SystemConstant.HEADER_AUTHORIZATION, createSign(schoolId, time, url));
|
|
|
|
+ header.put(SystemConstant.HEADER_TIME, String.valueOf(time));
|
|
|
|
+ return header;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 签名
|
|
|
|
+ *
|
|
|
|
+ * @param schoolId
|
|
|
|
+ * @param time
|
|
|
|
+ * @param url
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ private String createSign(Long schoolId, long time, String url) {
|
|
|
|
+ BasicSchool basicSchool = cacheService.schoolCache(schoolId);
|
|
|
|
+ if (basicSchool == null) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("学校不存在");
|
|
|
|
+ } else {
|
|
|
|
+ if (!basicSchool.getEnable()) {
|
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("学校已禁用");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ String signature = SignatureEntityTest.build(SignatureType.SECRET, POST_METHOD, url, time, basicSchool.getAccessKey(), basicSchool.getAccessSecret());
|
|
|
|
+ return signature;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String getClassName(String extendCloumn) {
|
|
|
|
+ List<Map> mapList = JSONObject.parseArray(extendCloumn, Map.class);
|
|
|
|
+ for (Map map : mapList) {
|
|
|
|
+ if (Objects.equals("className", map.get("code").toString())) {
|
|
|
|
+ return map.get("value").toString();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return "无";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 生成文件
|
|
|
|
+ * @param file 文件路径+文件名称
|
|
|
|
+ * @param conent 要生成的文件内容
|
|
|
|
+ */
|
|
|
|
+ public static File createJsonFile(String url, String conent) {
|
|
|
|
+
|
|
|
|
+ File file = new File(url);
|
|
|
|
+ if (!file.exists()) {
|
|
|
|
+ file.mkdirs();
|
|
|
|
+ }
|
|
|
|
+ BufferedWriter out = null;
|
|
|
|
+ file = new File(file, UUID.randomUUID().toString() + ".json");
|
|
|
|
+ try {
|
|
|
|
+ file.createNewFile();
|
|
|
|
+ out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
|
|
|
|
+ out.write(conent);
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
+ } finally {
|
|
|
|
+ try {
|
|
|
|
+ out.close();
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return file;
|
|
|
|
+ }
|
|
|
|
+}
|