소스 검색

客观题缓存加载

xiatian 4 년 전
부모
커밋
3810a2107f
26개의 변경된 파일895개의 추가작업 그리고 399개의 파일을 삭제
  1. 23 91
      themis-admin/src/main/java/com/qmth/themis/admin/api/TEExamActivityController.java
  2. 4 0
      themis-admin/src/main/java/com/qmth/themis/admin/api/TEExamController.java
  3. 45 0
      themis-business/src/main/java/com/qmth/themis/business/cache/ObjectiveAnswerCacheUtil.java
  4. 28 0
      themis-business/src/main/java/com/qmth/themis/business/cache/RedisKeyHelper.java
  5. 4 0
      themis-business/src/main/java/com/qmth/themis/business/dao/TEExamActivityMapper.java
  6. 2 0
      themis-business/src/main/java/com/qmth/themis/business/dao/TEExamPaperMapper.java
  7. 1 1
      themis-business/src/main/java/com/qmth/themis/business/enums/MqExecTypeEnum.java
  8. 1 1
      themis-business/src/main/java/com/qmth/themis/business/enums/MqTagEnum.java
  9. 8 5
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamActivityService.java
  10. 19 9
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamPaperService.java
  11. 1 1
      themis-business/src/main/java/com/qmth/themis/business/service/TOeExamRecordService.java
  12. 125 12
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamActivityServiceImpl.java
  13. 117 30
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamPaperServiceImpl.java
  14. 232 62
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamServiceImpl.java
  15. 169 162
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeExamRecordServiceImpl.java
  16. 7 2
      themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskExamPaperImportTemplete.java
  17. 8 0
      themis-business/src/main/java/com/qmth/themis/business/util/RedisUtil.java
  18. 12 0
      themis-business/src/main/resources/mapper/TEExamActivityMapper.xml
  19. 6 0
      themis-business/src/main/resources/mapper/TEExamPaperMapper.xml
  20. 4 0
      themis-common/src/main/java/com/qmth/themis/common/enums/ExceptionResultEnum.java
  21. 1 1
      themis-mq/src/main/java/com/qmth/themis/mq/service/MqLogicService.java
  22. 12 12
      themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java
  23. 7 7
      themis-mq/src/main/java/com/qmth/themis/mq/templete/impl/CalculateObjectiveScoreConcurrentlyImpl.java
  24. 6 1
      themis-task/src/main/java/com/qmth/themis/task/enums/QuartzTaskEnum.java
  25. 42 0
      themis-task/src/main/java/com/qmth/themis/task/quartz/ObjectiveAnswerCacheLoadJob.java
  26. 11 2
      themis-task/src/main/java/com/qmth/themis/task/start/StartRunning.java

+ 23 - 91
themis-admin/src/main/java/com/qmth/themis/admin/api/TEExamActivityController.java

@@ -1,31 +1,30 @@
 package com.qmth.themis.admin.api;
 
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.qmth.themis.business.constant.SystemConstant;
-import com.qmth.themis.business.dto.MqDto;
-import com.qmth.themis.business.entity.TBUser;
-import com.qmth.themis.business.entity.TEExam;
 import com.qmth.themis.business.entity.TEExamActivity;
-import com.qmth.themis.business.enums.FieldUniqueEnum;
-import com.qmth.themis.business.enums.InvigilateMonitorStatusEnum;
-import com.qmth.themis.business.enums.MqTagEnum;
-import com.qmth.themis.business.service.MqDtoService;
 import com.qmth.themis.business.service.TEExamActivityService;
-import com.qmth.themis.business.service.TEExamService;
-import com.qmth.themis.business.util.*;
-import com.qmth.themis.common.enums.ExceptionResultEnum;
-import com.qmth.themis.common.exception.BusinessException;
+import com.qmth.themis.business.service.TEExamPaperService;
 import com.qmth.themis.common.util.Result;
 import com.qmth.themis.common.util.ResultUtil;
-import io.swagger.annotations.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.dao.DuplicateKeyException;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.bind.annotation.*;
 
-import javax.annotation.Resource;
-import java.util.*;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
 
 /**
  * @Description: 考试场次 前端控制器
@@ -39,25 +38,11 @@ import java.util.*;
 @RequestMapping("/${prefix.url.admin}/activity")
 public class TEExamActivityController {
 
-    private final static Logger log = LoggerFactory.getLogger(TEExamActivityController.class);
-
     @Resource
-    TEExamActivityService teExamActivityService;
-
-    @Resource
-    MqDtoService mqDtoService;
-
-    @Resource
-    TEExamService teExamService;
-
-    @Resource
-    RedisUtil redisUtil;
-
-    @Resource
-    UidUtil uidUtil;
-
+    private TEExamActivityService teExamActivityService;
+    
     @Resource
-    MqUtil mqUtil;
+    private  TEExamPaperService examPaperService;
 
     @ApiOperation(value = "考试场次修改/新增接口")
     @RequestMapping(value = "/save", method = RequestMethod.POST)
@@ -65,61 +50,8 @@ public class TEExamActivityController {
     @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
     public Result save(
             @ApiParam(value = "考试场次信息", required = true) @RequestBody List<TEExamActivity> teExamActivityList) {
-        if (Objects.isNull(teExamActivityList) || teExamActivityList.size() == 0) {
-            throw new BusinessException(ExceptionResultEnum.EXAM_INFO_IS_NULL);
-        }
-        Long examId = null;
-        try {
-            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
-            examId = teExamActivityList.get(0).getExamId();
-            TEExam teExam = teExamService.getById(teExamActivityList.get(0).getExamId());
-            if (Objects.nonNull(teExam.getMonitorStatus()) && Objects
-                    .equals(teExam.getMonitorStatus(), InvigilateMonitorStatusEnum.FINISHED)) {
-                throw new BusinessException("监考结束的考试场次不可以修改");
-            }
-            teExamActivityList.forEach(s -> {
-                if (Objects.nonNull(s.getId())) {
-                    s.setUpdateId(tbUser.getId());
-                } else {
-                    s.setId(uidUtil.getId());
-                    s.setCreateId(tbUser.getId());
-                    s.setCode(String.valueOf(redisUtil.getRedisActivityCodeSequence(s.getExamId())));
-                }
-                teExamActivityService.saveOrUpdate(s);
-            });
-            for (TEExamActivity ac : teExamActivityList) {
-                teExamActivityService.updateExamActivityCacheBean(ac.getId());
-            }
-            if (Objects.nonNull(teExam.getForceFinish()) && teExam.getForceFinish().intValue() == 1) {
-                //新增quartz任务,发送mq消息start
-                Map<String, Object> prop = new HashMap<>();
-                prop.put("oper", "insert");
-                prop.put("exam", teExam);
-                MqDto mqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.EXAM_ACTIVITY.name(),
-                        JacksonUtil.parseJson(teExamActivityList), MqTagEnum.EXAM_ACTIVITY,
-                        String.valueOf(teExam.getId()), prop, tbUser.getName());
-                mqDtoService.assembleSendOneWayMsg(mqDto);
-                //新增quartz任务,发送mq消息end
-            }
-        } catch (Exception e) {
-            log.error("请求出错", e);
-            if (Objects.nonNull(examId) && Objects.nonNull(teExamActivityList) && (Objects.nonNull(teExamActivityList)
-                    && teExamActivityList.size() > 0 && Objects.nonNull(teExamActivityList.get(0)))) {
-                redisUtil.setRedisActivityCodeSequence(examId,
-                        Integer.parseInt(teExamActivityList.get(0).getCode()) - 1);
-            }
-            if (e instanceof DuplicateKeyException) {
-                String errorColumn = e.getCause().toString();
-                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length())
-                        .replaceAll("'", "");
-                throw new BusinessException("机构id[" + teExamActivityList.get(0).getExamId() + "]下的" + FieldUniqueEnum
-                        .convertToCode(columnStr) + "数据不允许重复插入");
-            } else if (e instanceof BusinessException) {
-                throw new BusinessException(e.getMessage());
-            } else {
-                throw new RuntimeException(e);
-            }
-        }
+    	teExamActivityService.saveExamActivity(teExamActivityList);
+    	examPaperService.disposeObjectiveAnswer(teExamActivityList.get(0).getExamId());
         return ResultUtil.ok(Collections.singletonMap(SystemConstant.SUCCESS, true));
     }
 

+ 4 - 0
themis-admin/src/main/java/com/qmth/themis/admin/api/TEExamController.java

@@ -77,6 +77,9 @@ public class TEExamController {
 
     @Resource
     TOeExamRecordService tOeExamRecordService;
+    
+    @Resource
+    private  TEExamPaperService examPaperService;
 
     @ApiOperation(value = "考试批次修改/新增接口")
     @RequestMapping(value = "/save", method = RequestMethod.POST)
@@ -237,6 +240,7 @@ public class TEExamController {
             }
         }
         teExamService.updateExamCacheBean(teExam.getId());
+        examPaperService.disposeObjectiveAnswer(teExam.getId());
         return ResultUtil.ok(Collections.singletonMap(SystemConstant.SUCCESS, true));
     }
 

+ 45 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/ObjectiveAnswerCacheUtil.java

@@ -0,0 +1,45 @@
+package com.qmth.themis.business.cache;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import com.qmth.themis.business.cache.bean.ObjectiveAnswerCacheBean;
+import com.qmth.themis.business.constant.SpringContextHolder;
+import com.qmth.themis.business.util.RedisUtil;
+
+/**客观题缓存
+ * @Description: 
+ * @Author: xiatian
+ * @Date: 2020-12-29
+ */
+public class ObjectiveAnswerCacheUtil {
+
+    private static RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);
+    
+    public static void saveObjectiveAnswerCache(Long examId,Long paperId,Map<String, ObjectiveAnswerCacheBean> val) {
+    	redisUtil.set(RedisKeyHelper.objectiveAnswerCacheKey(examId), RedisKeyHelper.objectiveAnswerCacheHashKey(paperId),val);
+    }
+    
+    public static void expireObjectiveAnswerCache(Long examId,Long hours) {
+    	redisUtil.expire(RedisKeyHelper.objectiveAnswerCacheKey(examId), hours, TimeUnit.HOURS);
+    }
+    
+    public static void deleteObjectiveAnswerCache(Long examId,Long paperId) {
+    	redisUtil.delete(RedisKeyHelper.objectiveAnswerCacheKey(examId), RedisKeyHelper.objectiveAnswerCacheHashKey(paperId));
+    }
+    
+    @SuppressWarnings("unchecked")
+	public static Map<String, ObjectiveAnswerCacheBean> getObjectiveAnswerCache(Long examId,Long paperId) {
+    	return (Map<String, ObjectiveAnswerCacheBean>)redisUtil.get(RedisKeyHelper.objectiveAnswerCacheKey(examId), RedisKeyHelper.objectiveAnswerCacheHashKey(paperId));
+    }
+    
+    public static Long getHashCacheSize(Long examId) {
+    	Long ret=redisUtil.getHashSize(RedisKeyHelper.objectiveAnswerCacheKey(examId));
+    	if(ret==null) {
+    		return 0L;
+    	}else {
+    		return ret;
+    	}
+    }
+
+}

+ 28 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/RedisKeyHelper.java

@@ -60,6 +60,17 @@ public class RedisKeyHelper {
 	 * 未完成考试记录id
 	 */
 	private static String unFinishedRecordIdKeyPrefix = "un_finished_record_id::student_id_";
+	
+	/**
+	 * 客观题答案
+	 */
+	private static String objectiveAnswerKeyPrefix = "objective_answer::exam_id_";
+	
+	/**
+	 * 客观题答案
+	 */
+	private static String objectiveAnswerHashKeyPrefix = "paper_id_";
+	
 	/**
 	 * 场次
 	 * 
@@ -189,4 +200,21 @@ public class RedisKeyHelper {
 		return unFinishedRecordIdKeyPrefix + studentId;
 	}
 	
+	/**客观题答案大key
+	 * @param examId
+	 * @return
+	 */
+	public static String objectiveAnswerCacheKey(Long examId) {
+		return objectiveAnswerKeyPrefix+examId;
+	}
+	
+	/**客观题答案小key
+	 * @param paperId
+	 * @return
+	 */
+	public static String objectiveAnswerCacheHashKey(Long paperId) {
+		return objectiveAnswerHashKeyPrefix+paperId;
+	}
+	
+	
 }

+ 4 - 0
themis-business/src/main/java/com/qmth/themis/business/dao/TEExamActivityMapper.java

@@ -95,4 +95,8 @@ public interface TEExamActivityMapper extends BaseMapper<TEExamActivity> {
     public TEExamActivityDto getWaitingExamByExamActivityId(@Param("examActivityId") Long examActivityId);
 
     public List<TEExamActivity> findByExamIdAndOrgId(@Param("examId") Long examId, @Param("orgId") Long orgId);
+    
+    public List<Map<String,Long>> findMinStartTimeAndMaxFinshTimeByExamId(@Param("examId") Long examId);
+    
+    public List<Long> findExamIdToday(@Param("startTime") Long startTime,@Param("endTime") Long endTime);
 }

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

@@ -23,6 +23,8 @@ public interface TEExamPaperMapper extends BaseMapper<TEExamPaper> {
 
 	public List<TEExamPaper> findListByExamIdAndCourseCode(@Param("examId") Long examId,
 			@Param("courseCode") String courseCode);
+	
+	public List<TEExamPaper> findByExamId(@Param("examId") Long examId);
 
 	public void updateWeight(@Param("id") Long id, @Param("weight") Double weight);
 }

+ 1 - 1
themis-business/src/main/java/com/qmth/themis/business/enums/MqExecTypeEnum.java

@@ -35,7 +35,7 @@ public enum MqExecTypeEnum {
 
     EXEC_MQ_CALCULATE_SCORE_LOGIC("重新算分逻辑", "execMqCalculateScoreLogic"),
 
-    EXEC_MQ_CALCULATE_OBJECTIVE_SCORE_LOGIC("计算客观分逻辑", "execMqCalculateObjectiveScoreLogic"),
+//    EXEC_MQ_CALCULATE_OBJECTIVE_SCORE_LOGIC("计算客观分逻辑", "execMqCalculateObjectiveScoreLogic"),
 
     EXEC_MQ_WEBSOCKET_UN_NORMAL_LOGIC("websocket非正常退出,延时消息逻辑", "execMqWebsocketUnNormalLogic"),
 

+ 1 - 1
themis-business/src/main/java/com/qmth/themis/business/enums/MqTagEnum.java

@@ -30,7 +30,7 @@ public enum MqTagEnum {
     OE_UN_NORMAL("websocket超时退出标签", "websocket超时退出", "delay", 18),
     EXAM_ACTIVITY("考场一次性延时任务标签", "考场一次性延时任务", "normal", 19),
     QUARTZ("quartz标签", "quartz任务", "normal", 20),
-    CALCULATE_OBJECTIVE_SCORE("计算客观分标签", "计算客观分", "normal", 21),
+//    CALCULATE_OBJECTIVE_SCORE("计算客观分标签", "计算客观分", "normal", 21),
     FACE_VERIFY_SAVE("人脸验证保存标签", "人脸验证", "normal", 22),
     LIVENESS_VERIFY_SAVE("活体验证保存标签", "活体验证", "normal", 23),
     EXAM_RECORD_PERSISTED("考试记录数据持久化标签", "考试", "normal", 24),

+ 8 - 5
themis-business/src/main/java/com/qmth/themis/business/service/TEExamActivityService.java

@@ -1,18 +1,17 @@
 package com.qmth.themis.business.service;
 
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.qmth.themis.business.bean.exam.ExamPrepareBean;
 import com.qmth.themis.business.cache.bean.ExamActivityCacheBean;
 import com.qmth.themis.business.dto.response.TEExamActivityDto;
 import com.qmth.themis.business.dto.response.TEExamActivityQueryDto;
 import com.qmth.themis.business.dto.response.TEExamActivityWaitDto;
 import com.qmth.themis.business.entity.TEExamActivity;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 /**
  * @Description: 考试场次 服务类
  * @Param:
@@ -119,4 +118,8 @@ public interface TEExamActivityService extends IService<TEExamActivity> {
     void deleteExamActivityCacheBean(Long examActivityId);
 
     public List<TEExamActivity> findByExamIdAndOrgId(Long examId, Long orgId);
+
+	void saveExamActivity(List<TEExamActivity> teExamActivityList);
+
+	List<Long> findExamIdToday();
 }

+ 19 - 9
themis-business/src/main/java/com/qmth/themis/business/service/TEExamPaperService.java

@@ -40,15 +40,6 @@ public interface TEExamPaperService extends IService<TEExamPaper> {
 
     Map<String, ObjectiveAnswerCacheBean> getObjectiveAnswerCacheBean(Long paperId);
 
-    void savePaperWeight(Map<Long, Double> map);
-
-	/**
-	 * 删除客观题标答缓存
-	 *
-	 * @param paperId
-	 */
-	void deleteObjectiveAnswerCacheBean(Long paperId);
-
 	/**
 	 * 删除试卷结构缓存
 	 *
@@ -58,4 +49,23 @@ public interface TEExamPaperService extends IService<TEExamPaper> {
 
 	Map<String, Integer> getPaperStructCacheBean(Long paperId);
 
+	void savePaperWeight(Map<Long, Double> map);
+
+	/**处理试卷缓存
+	 * @param examId
+	 * @param paperIds
+	 */
+	void disposePaperCacheOnPaperImport(Long examId,List<Long> paperIds);
+
+	/**加载客观题答案缓存
+	 * @param paperId
+	 * @param timeOutHours
+	 */
+	void reloadObjectiveAnswerCacheBean(Long paperId,Long timeOutHours);
+
+	/**处理考试批次下的客观题答案缓存
+	 * @param examId
+	 */
+	void disposeObjectiveAnswer(Long examId);
+
 }

+ 1 - 1
themis-business/src/main/java/com/qmth/themis/business/service/TOeExamRecordService.java

@@ -36,7 +36,7 @@ public interface TOeExamRecordService extends IService<TOeExamRecord> {
      *
      * @param param
      */
-    void calculateObjectiveScore(Map<String, Object> param);
+//    void calculateObjectiveScore(Map<String, Object> param);
 
     /**
      * 考试记录数据持久化

+ 125 - 12
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamActivityServiceImpl.java

@@ -1,31 +1,57 @@
 package com.qmth.themis.business.service.impl;
 
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.annotation.Resource;
+
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.qmth.themis.business.bean.exam.ExamPrepareBean;
 import com.qmth.themis.business.cache.bean.ExamActivityCacheBean;
 import com.qmth.themis.business.cache.bean.ExamCacheBean;
 import com.qmth.themis.business.cache.bean.ExamCourseCacheBean;
 import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
 import com.qmth.themis.business.dao.TEExamActivityMapper;
+import com.qmth.themis.business.dto.MqDto;
 import com.qmth.themis.business.dto.response.TEExamActivityDto;
 import com.qmth.themis.business.dto.response.TEExamActivityQueryDto;
 import com.qmth.themis.business.dto.response.TEExamActivityWaitDto;
+import com.qmth.themis.business.entity.TBUser;
+import com.qmth.themis.business.entity.TEExam;
 import com.qmth.themis.business.entity.TEExamActivity;
 import com.qmth.themis.business.enums.EntryAuthenticationPolicyEnum;
+import com.qmth.themis.business.enums.FieldUniqueEnum;
 import com.qmth.themis.business.enums.HardwareTestEnum;
+import com.qmth.themis.business.enums.InvigilateMonitorStatusEnum;
 import com.qmth.themis.business.enums.MonitorVideoSourceEnum;
+import com.qmth.themis.business.enums.MqTagEnum;
+import com.qmth.themis.business.service.MqDtoService;
 import com.qmth.themis.business.service.TEExamActivityService;
 import com.qmth.themis.business.service.TEExamCourseService;
 import com.qmth.themis.business.service.TEExamService;
 import com.qmth.themis.business.service.TEExamStudentService;
-import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.cache.annotation.CachePut;
-import org.springframework.cache.annotation.Cacheable;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.util.*;
+import com.qmth.themis.business.util.JacksonUtil;
+import com.qmth.themis.business.util.MqUtil;
+import com.qmth.themis.business.util.RedisUtil;
+import com.qmth.themis.business.util.ServletUtil;
+import com.qmth.themis.business.util.UidUtil;
+import com.qmth.themis.common.enums.ExceptionResultEnum;
+import com.qmth.themis.common.exception.BusinessException;
 
 /**
  * @Description: 考试场次 服务实现类
@@ -38,17 +64,28 @@ import java.util.*;
 public class TEExamActivityServiceImpl extends ServiceImpl<TEExamActivityMapper, TEExamActivity> implements TEExamActivityService {
 
     @Resource
-    TEExamActivityMapper teExamActivityMapper;
+    private TEExamActivityMapper teExamActivityMapper;
+
+    @Resource
+    private TEExamService teExamService;
+
+    @Resource
+    private TEExamStudentService teExamStudentService;
+
+    @Resource
+    private TEExamCourseService teExamCourseService;
 
     @Resource
-    TEExamService teExamService;
+    private MqDtoService mqDtoService;
 
     @Resource
-    TEExamStudentService teExamStudentService;
+    private RedisUtil redisUtil;
 
     @Resource
-    TEExamCourseService teExamCourseService;
+    private UidUtil uidUtil;
 
+    @Resource
+    private MqUtil mqUtil;
     /**
      * 表是否存在
      *
@@ -259,4 +296,80 @@ public class TEExamActivityServiceImpl extends ServiceImpl<TEExamActivityMapper,
     public List<TEExamActivity> findByExamIdAndOrgId(Long examId, Long orgId) {
         return teExamActivityMapper.findByExamIdAndOrgId(examId, orgId);
     }
+    
+    @Transactional
+    @Override
+    public void saveExamActivity(List<TEExamActivity> teExamActivityList) {
+    	if (Objects.isNull(teExamActivityList) || teExamActivityList.size() == 0) {
+            throw new BusinessException(ExceptionResultEnum.EXAM_INFO_IS_NULL);
+        }
+        Long examId = null;
+        try {
+            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
+            examId = teExamActivityList.get(0).getExamId();
+            TEExam teExam = teExamService.getById(teExamActivityList.get(0).getExamId());
+            if (Objects.nonNull(teExam.getMonitorStatus()) && Objects
+                    .equals(teExam.getMonitorStatus(), InvigilateMonitorStatusEnum.FINISHED)) {
+                throw new BusinessException("监考结束的考试场次不可以修改");
+            }
+            teExamActivityList.forEach(s -> {
+                if (Objects.nonNull(s.getId())) {
+                    s.setUpdateId(tbUser.getId());
+                } else {
+                    s.setId(uidUtil.getId());
+                    s.setCreateId(tbUser.getId());
+                    s.setCode(String.valueOf(redisUtil.getRedisActivityCodeSequence(s.getExamId())));
+                }
+                this.saveOrUpdate(s);
+            });
+            for (TEExamActivity ac : teExamActivityList) {
+                this.updateExamActivityCacheBean(ac.getId());
+            }
+            if (Objects.nonNull(teExam.getForceFinish()) && teExam.getForceFinish().intValue() == 1) {
+                //新增quartz任务,发送mq消息start
+                Map<String, Object> prop = new HashMap<>();
+                prop.put("oper", "insert");
+                prop.put("exam", teExam);
+                MqDto mqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.EXAM_ACTIVITY.name(),
+                        JacksonUtil.parseJson(teExamActivityList), MqTagEnum.EXAM_ACTIVITY,
+                        String.valueOf(teExam.getId()), prop, tbUser.getName());
+                mqDtoService.assembleSendOneWayMsg(mqDto);
+                //新增quartz任务,发送mq消息end
+            }
+        } catch (Exception e) {
+            log.error("请求出错", e);
+            if (Objects.nonNull(examId) && Objects.nonNull(teExamActivityList) && (Objects.nonNull(teExamActivityList)
+                    && teExamActivityList.size() > 0 && Objects.nonNull(teExamActivityList.get(0)))) {
+                redisUtil.setRedisActivityCodeSequence(examId,
+                        Integer.parseInt(teExamActivityList.get(0).getCode()) - 1);
+            }
+            if (e instanceof DuplicateKeyException) {
+                String errorColumn = e.getCause().toString();
+                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length())
+                        .replaceAll("'", "");
+                throw new BusinessException("机构id[" + teExamActivityList.get(0).getExamId() + "]下的" + FieldUniqueEnum
+                        .convertToCode(columnStr) + "数据不允许重复插入");
+            } else if (e instanceof BusinessException) {
+                throw new BusinessException(e.getMessage());
+            } else {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+    
+    @Override
+    public List<Long> findExamIdToday(){
+    	try {
+			Date now=new Date();
+			SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd");
+			String today=sdf1.format(now);
+			SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+			Long start=sdf2.parse(today+" 00:00:00").getTime();
+			Long end=sdf2.parse(today+" 23:59:59").getTime();
+			return teExamActivityMapper.findExamIdToday(start, end);
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+    	return null;
+    }
 }

+ 117 - 30
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamPaperServiceImpl.java

@@ -1,29 +1,40 @@
 package com.qmth.themis.business.service.impl;
 
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.aliyun.oss.common.utils.BinaryUtil;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.themis.business.cache.ObjectiveAnswerCacheUtil;
 import com.qmth.themis.business.cache.RedisKeyHelper;
 import com.qmth.themis.business.cache.bean.ExamPaperCacheBean;
 import com.qmth.themis.business.cache.bean.ObjectiveAnswerCacheBean;
 import com.qmth.themis.business.constant.SpringContextHolder;
+import com.qmth.themis.business.dao.TEExamActivityMapper;
 import com.qmth.themis.business.dao.TEExamPaperMapper;
 import com.qmth.themis.business.entity.TEExamPaper;
 import com.qmth.themis.business.service.TEExamPaperService;
+import com.qmth.themis.business.service.TEExamService;
 import com.qmth.themis.business.util.OssUtil;
 import com.qmth.themis.common.exception.BusinessException;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.cache.annotation.CachePut;
-import org.springframework.cache.annotation.Cacheable;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import javax.annotation.Resource;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
 
 /**
  * @Description: 考试试卷 服务实现类
@@ -34,12 +45,20 @@ import java.util.*;
  */
 @Service
 public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExamPaper> implements TEExamPaperService {
-
+	
+	private final static long objectiveAnswerCacheTimeOutHours=48L;
+	
+	@Resource
+	private TEExamService examService;
+	
     @Resource
     OssUtil ossUtil;
 
     @Resource
     TEExamPaperMapper teExamPaperMapper;
+    
+    @Resource
+    TEExamActivityMapper  examActivityMapper;
 
     @Override
     public TEExamPaper findByExamIdAndCourseCodeAndPaperCode(Long examId, String courseCode, String paperCode) {
@@ -130,38 +149,56 @@ public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExa
         ret.setPaperViewPath(ep.getPaperViewPath());
         return ret;
     }
-
-    /**
-     * 删除客观题标答缓存
-     *
-     * @param paperId
-     */
+    
     @Override
-    @CacheEvict(value = "objective_answer", key = "#paperId")
-    public void deleteObjectiveAnswerCacheBean(Long paperId) {
-
+    public void reloadObjectiveAnswerCacheBean(Long paperId,Long timeOutHours) {
+        ExamPaperCacheBean ep = getExamPaperCacheBean(paperId);
+        if (ep == null) {
+            return ;
+        }
+        Long examId=ep.getExamId();
+        if (StringUtils.isBlank(ep.getAnswerPath())) {//没有答案文件
+            return ;
+        }
+        try {
+            String structJson = new String(ossUtil.download(false, ep.getStructPath()), StandardCharsets.UTF_8);
+            String answerjson = new String(ossUtil.download(false, ep.getAnswerPath()), StandardCharsets.UTF_8);
+            Map<String, ObjectiveAnswerCacheBean> map=buildObjectiveAnswerCache(structJson, answerjson);
+            ObjectiveAnswerCacheUtil.saveObjectiveAnswerCache(examId, paperId, map);
+            ObjectiveAnswerCacheUtil.expireObjectiveAnswerCache(examId, timeOutHours);
+        } catch (Exception e) {
+            log.error("get struct/answer json error", e);
+            throw new RuntimeException(e);
+        }
     }
 
-    @Cacheable(value = "objective_answer", key = "#paperId", unless = "#result == null")
     @Override
     public Map<String, ObjectiveAnswerCacheBean> getObjectiveAnswerCacheBean(Long paperId) {
         ExamPaperCacheBean ep = getExamPaperCacheBean(paperId);
         if (ep == null) {
             return null;
         }
+        Long examId=ep.getExamId();
         if (StringUtils.isBlank(ep.getAnswerPath())) {//没有答案文件
             return null;
         }
+        Map<String, ObjectiveAnswerCacheBean> map=ObjectiveAnswerCacheUtil.getObjectiveAnswerCache(examId, paperId);
+        if(map!=null) {
+        	return map;
+        }
         try {
             String structJson = new String(ossUtil.download(false, ep.getStructPath()), StandardCharsets.UTF_8);
             String answerjson = new String(ossUtil.download(false, ep.getAnswerPath()), StandardCharsets.UTF_8);
-            return buildObjectiveAnswerCache(structJson, answerjson);
+            map=buildObjectiveAnswerCache(structJson, answerjson);
+            ObjectiveAnswerCacheUtil.saveObjectiveAnswerCache(examId, paperId, map);
+            ObjectiveAnswerCacheUtil.expireObjectiveAnswerCache(examId, objectiveAnswerCacheTimeOutHours);
+            return map;
         } catch (Exception e) {
             log.error("get struct/answer json error", e);
             throw new RuntimeException(e);
         }
     }
-
+    
     private Map<String, ObjectiveAnswerCacheBean> buildObjectiveAnswerCache(String structStr, String answerStr) {
         Map<String, ObjectiveAnswerCacheBean> map = new HashMap<String, ObjectiveAnswerCacheBean>();
         JSONObject answerJson = JSONObject.parseObject(answerStr);
@@ -224,10 +261,6 @@ public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExa
         return map;
     }
 
-    private String uuid() {
-        return UUID.randomUUID().toString().replaceAll("-", "");
-    }
-
     @Override
     public List<TEExamPaper> findByExamIdAndCourseCode(Long examId, String courseCode) {
         return teExamPaperMapper.findListByExamIdAndCourseCode(examId, courseCode);
@@ -236,11 +269,65 @@ public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExa
     @Transactional
     @Override
     public void savePaperWeight(Map<Long, Double> map) {
-        TEExamPaperService examPaperService = SpringContextHolder.getBean(TEExamPaperService.class);
         for (Long id : map.keySet()) {
             teExamPaperMapper.updateWeight(id, map.get(id));
+        }
+    }
+    
+    /**获取缓存过期时间,不满足条件的返回-1.条件:场次最小startTime在当天的,或者当前时间在场次最小startTime和最大finishTime之间的。
+     * @param examId
+     * @return
+     */
+    private Long getObjectiveAnswerCacheTimeOut(Long examId) {
+    	Long objectiveAnswerCacheTimeOut=-1L;
+    	List<Map<String, Long>> list=examActivityMapper.findMinStartTimeAndMaxFinshTimeByExamId(examId);
+    	if(list!=null&&list.size()>0) {
+    		SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
+    		Date now=new Date();
+    		Long startTime=list.get(0).get("startTime");
+    		Long finishTime=list.get(0).get("finishTime");
+    		Date start=new Date(startTime);
+    		if(sdf.format(now).equals(sdf.format(start))||(now.getTime()>=startTime&&now.getTime()<=finishTime)) {
+    			objectiveAnswerCacheTimeOut=(finishTime-now.getTime())/(1000*60*60)+24;
+        	}
+    	}
+    	return objectiveAnswerCacheTimeOut;
+    }
+    
+    /**处理考试批次下的客观题答案缓存
+     * @param examId
+     */
+    @Override
+    public void disposeObjectiveAnswer(Long examId) {
+    	Long objectiveAnswerCacheTimeOut=getObjectiveAnswerCacheTimeOut(examId);
+    	if(objectiveAnswerCacheTimeOut>0) {
+    		List<TEExamPaper> list=teExamPaperMapper.findByExamId(examId);
+    		if(list!=null&&list.size()>0) {
+    			for(TEExamPaper paper:list) {
+    				if(ObjectiveAnswerCacheUtil.getObjectiveAnswerCache(examId, paper.getId())==null) {
+    					reloadObjectiveAnswerCacheBean(paper.getId(), objectiveAnswerCacheTimeOut);
+    				}
+    			}
+    		}
+    		Long count=ObjectiveAnswerCacheUtil.getHashCacheSize(examId);
+    		if(count!=null&&count>0) {
+    			ObjectiveAnswerCacheUtil.expireObjectiveAnswerCache(examId, objectiveAnswerCacheTimeOut);
+    		}
+    	}
+    }
+    
+    /**
+     *导入试卷时处理缓存
+     */
+    @Override
+    public void disposePaperCacheOnPaperImport(Long examId,List<Long> paperIds) {
+        TEExamPaperService examPaperService = SpringContextHolder.getBean(TEExamPaperService.class);
+        for (Long id : paperIds) {
             examPaperService.deleteExamPaperCacheBean(id);
-            examPaperService.deleteObjectiveAnswerCacheBean(id);
+            Long objectiveAnswerCacheTimeOut=getObjectiveAnswerCacheTimeOut(examId);
+            if(objectiveAnswerCacheTimeOut>0) {
+            	examPaperService.reloadObjectiveAnswerCacheBean(id,objectiveAnswerCacheTimeOut);
+            }
             examPaperService.deletePaperStructCacheBean(id);
         }
     }

+ 232 - 62
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamServiceImpl.java

@@ -1,14 +1,68 @@
 package com.qmth.themis.business.service.impl;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.aliyun.oss.common.utils.BinaryUtil;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmth.themis.business.bean.admin.OpenExamBean;
-import com.qmth.themis.business.bean.exam.*;
-import com.qmth.themis.business.cache.*;
-import com.qmth.themis.business.cache.bean.*;
+import com.qmth.themis.business.bean.exam.AnswerSubmitBean;
+import com.qmth.themis.business.bean.exam.AudioLeftPlayCountSubmitBean;
+import com.qmth.themis.business.bean.exam.ExamFileUploadBean;
+import com.qmth.themis.business.bean.exam.ExamFinishBean;
+import com.qmth.themis.business.bean.exam.ExamPaperDownloadBean;
+import com.qmth.themis.business.bean.exam.ExamPrepareBean;
+import com.qmth.themis.business.bean.exam.ExamResultBean;
+import com.qmth.themis.business.bean.exam.ExamResumeBean;
+import com.qmth.themis.business.bean.exam.ExamStartBean;
+import com.qmth.themis.business.bean.exam.StudentPaperStructBean;
+import com.qmth.themis.business.cache.ExamActivityRecordCacheUtil;
+import com.qmth.themis.business.cache.ExamBreakCacheUtil;
+import com.qmth.themis.business.cache.ExamRecordCacheUtil;
+import com.qmth.themis.business.cache.ExamingDataCacheUtil;
+import com.qmth.themis.business.cache.RedisKeyHelper;
+import com.qmth.themis.business.cache.bean.ExamActivityCacheBean;
+import com.qmth.themis.business.cache.bean.ExamCacheBean;
+import com.qmth.themis.business.cache.bean.ExamCourseCacheBean;
+import com.qmth.themis.business.cache.bean.ExamPaperCacheBean;
+import com.qmth.themis.business.cache.bean.ExamStudentAnswerCacheBean;
+import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
+import com.qmth.themis.business.cache.bean.ExamStudentPaperStructCacheBean;
+import com.qmth.themis.business.cache.bean.ObjectiveAnswerCacheBean;
 import com.qmth.themis.business.constant.SpringContextHolder;
 import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dao.TEExamMapper;
@@ -22,31 +76,41 @@ import com.qmth.themis.business.entity.TBSession;
 import com.qmth.themis.business.entity.TBTaskHistory;
 import com.qmth.themis.business.entity.TEExam;
 import com.qmth.themis.business.entity.TOeExamRecord;
-import com.qmth.themis.business.enums.*;
-import com.qmth.themis.business.service.*;
-import com.qmth.themis.business.util.*;
+import com.qmth.themis.business.enums.ExamModeEnum;
+import com.qmth.themis.business.enums.ExamRecordStatusEnum;
+import com.qmth.themis.business.enums.FinishExamResultEnum;
+import com.qmth.themis.business.enums.FinishTypeEnum;
+import com.qmth.themis.business.enums.InvigilateMonitorStatusEnum;
+import com.qmth.themis.business.enums.InvigilateVerifyEnum;
+import com.qmth.themis.business.enums.MonitorStatusSourceEnum;
+import com.qmth.themis.business.enums.MonitorVideoSourceEnum;
+import com.qmth.themis.business.enums.MqTagEnum;
+import com.qmth.themis.business.enums.ObjectiveScorePolicyEnum;
+import com.qmth.themis.business.enums.ReviewResultEnum;
+import com.qmth.themis.business.enums.ScoreStatusEnum;
+import com.qmth.themis.business.enums.SystemOperationEnum;
+import com.qmth.themis.business.enums.TaskStatusEnum;
+import com.qmth.themis.business.enums.WebsocketStatusEnum;
+import com.qmth.themis.business.service.MqDtoService;
+import com.qmth.themis.business.service.TBTaskHistoryService;
+import com.qmth.themis.business.service.TEExamActivityService;
+import com.qmth.themis.business.service.TEExamCourseService;
+import com.qmth.themis.business.service.TEExamPaperService;
+import com.qmth.themis.business.service.TEExamService;
+import com.qmth.themis.business.service.TEExamStudentService;
+import com.qmth.themis.business.service.TGErrorService;
+import com.qmth.themis.business.service.TOeExamBreakHistoryService;
+import com.qmth.themis.business.service.TOeExamRecordService;
+import com.qmth.themis.business.util.JacksonUtil;
+import com.qmth.themis.business.util.MqUtil;
+import com.qmth.themis.business.util.OssUtil;
+import com.qmth.themis.business.util.RedisUtil;
+import com.qmth.themis.business.util.ServletUtil;
+import com.qmth.themis.business.util.TencentYunUtil;
 import com.qmth.themis.common.enums.ExceptionResultEnum;
 import com.qmth.themis.common.exception.BusinessException;
 import com.qmth.themis.common.util.HexUtils;
 import com.qmth.themis.common.util.IpUtil;
-import org.springframework.beans.BeanUtils;
-import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.cache.annotation.CachePut;
-import org.springframework.cache.annotation.Cacheable;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.web.multipart.MultipartFile;
-
-import javax.annotation.Resource;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigDecimal;
-import java.text.SimpleDateFormat;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 
 /**
  * @Description: 考试批次 服务实现类
@@ -119,7 +183,8 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
      * @param type
      * @return
      */
-    @Override
+    @SuppressWarnings("rawtypes")
+	@Override
     public IPage<TEExamQueryDto> examQuery(IPage<Map> iPage, Long userId, Long id, String code, String name,
                                            String mode, Integer enable, Long orgId, String type) {
         return teExamMapper.examQuery(iPage, userId, id, code, name, mode, enable, orgId, type);
@@ -616,28 +681,124 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
                 ExamRecordCacheUtil.getHasAnswerFile(recordId) :
                 0;
         if (hasAnswerFile.intValue() == 1) {
-            // 每次提交,清空得分
-            answerCache.setScore(null);
+        	calculateObjectiveScore(answerCache, recordId);
         }
         // 更新考生作答
         redisUtil.set(RedisKeyHelper.examAnswerKey(recordId),
                 RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex), answerCache);
-        if (hasAnswerFile.intValue() == 1) {
-            // 如果是客观题,重置考试记录客观题得分
-            if (answerCache.getObjective()) {
-                ExamRecordCacheUtil.setObjectiveScore(recordId, null);
-                toeExamRecordService.sendExamRecordDataSaveMq(recordId, System.currentTimeMillis());
-                // 发消息计算客观分
-                calculateObjectiveScoreMsg(recordId, mainNumber, subNumber, subIndex);
-            }
-        }
 
         AnswerSubmitBean ret = new AnswerSubmitBean();
         ret.setVersion(version);
 
         return ret;
     }
+    
+    private void calculateObjectiveScore(ExamStudentAnswerCacheBean answer,Long recordId) {
+    	if(answer.getObjective()==null||!answer.getObjective()) {
+    		return;
+    	}
+    	answer.setScore(0.0);
+        Integer mainNumber = answer.getMainNumber();
+        Integer subNumber = answer.getSubNumber();
+        Integer subIndex = answer.getSubIndex();
+        Long paperId = ExamRecordCacheUtil.getPaperId(recordId);
+        String key = RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex);
 
+        //整卷客观题标答缓存集合
+        Map<String, ObjectiveAnswerCacheBean> map = examPaperService.getObjectiveAnswerCacheBean(paperId);
+        if (map == null || map.size() == 0) {
+        	throw new BusinessException(ExceptionResultEnum.NOT_FOUND_PAPER_OBJECTIVE_ANSWER);
+        }
+        //客观题标答缓存
+        ObjectiveAnswerCacheBean cb = map.get(key);
+        if (cb == null) {
+        	throw new BusinessException(ExceptionResultEnum.NOT_FOUND_QUESTION_OBJECTIVE_ANSWER);
+        }
+
+        if (cb.getStructType().intValue() == 1) {
+            if (checkSingleChoice(answer.getAnswer(), cb.getChoiceAnswer())) {
+                answer.setScore(cb.getScore());
+            } else {
+                answer.setScore(0.0);
+            }
+        }
+        if (cb.getStructType().intValue() == 2) {
+            ExamCacheBean ec = getExamCacheBeanNative(ExamRecordCacheUtil.getExamId(recordId));
+            int r = checkMultipleChoice(answer.getAnswer(), cb.getChoiceAnswer());
+            if (ec.getObjectiveScorePolicy().equals(ObjectiveScorePolicyEnum.EQUAL)) {// 全对给分
+                if (r == 1) {
+                    answer.setScore(cb.getScore());
+                } else {
+                    answer.setScore(0.0);
+                }
+            }
+            if (ec.getObjectiveScorePolicy().equals(ObjectiveScorePolicyEnum.PARTIAL)) {// 漏选半分
+                if (r == 1) {
+                    answer.setScore(cb.getScore());
+                } else if (r == 0) {
+                    BigDecimal b = new BigDecimal(cb.getScore()).divide(new BigDecimal("2"), 1, BigDecimal.ROUND_UP);
+                    answer.setScore(b.doubleValue());
+                } else {
+                    answer.setScore(0.0);
+                }
+            }
+        }
+        if (cb.getStructType().intValue() == 3) {
+            if (StringUtils.isNotBlank(answer.getAnswer())) {
+                String answerStr = answer.getAnswer().replaceAll("\n", "");
+                if (StringUtils.isNotBlank(answerStr)) {
+                    if (Boolean.parseBoolean(answerStr) == cb.getBoolAnswer().booleanValue()) {
+                        answer.setScore(cb.getScore());
+                    } else {
+                        answer.setScore(0.0);
+                    }
+                }
+            }
+        }
+    }
+    private boolean checkSingleChoice(String answerArray, JSONArray ar) {
+        JSONArray an = JSONArray.parseArray(answerArray);
+        if (an.size() == 0) {
+            return false;
+        }
+        int a = an.getIntValue(0);
+        return checkSingleChoice(a, ar);
+    }
+
+    private boolean checkSingleChoice(int answer, JSONArray ar) {
+        for (int i = 0; i < ar.size(); i++) {
+            if (answer == ar.getIntValue(i)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @param answer
+     * @param ar
+     * @return -1 有错选项,0-无措选项 未答全 1-全对
+     */
+    private int checkMultipleChoice(String answer, JSONArray ar) {
+        int yes = 0;
+        int no = 0;
+        JSONArray an = JSONArray.parseArray(answer);
+        for (int i = 0; i < an.size(); i++) {
+            if (checkSingleChoice(an.getIntValue(i), ar)) {
+                yes++;
+                continue;
+            } else {
+                no++;
+            }
+        }
+        if (yes == ar.size()) {
+            return 1;
+        }
+        if (yes != 0 && no == 0) {
+            return 0;
+        }
+        return -1;
+    }
     private boolean checkIsObjective(Long paperId, Integer mainNumber, Integer subNumber, Integer subIndex) {
         Map<String, ObjectiveAnswerCacheBean> map = examPaperService.getObjectiveAnswerCacheBean(paperId);
         if (map == null || map.size() == 0) {
@@ -651,19 +812,6 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         return true;
     }
 
-    //考试中计算客观分消息
-    private void calculateObjectiveScoreMsg(Long recordId, Integer mainNumber, Integer subNumber, Integer subIndex) {
-        Map<String, Object> transMap = new HashMap<String, Object>();
-        transMap.put("recordId", recordId);
-        transMap.put("mainNumber", mainNumber);
-        transMap.put("subNumber", subNumber);
-        transMap.put("subIndex", subIndex);
-        // mq发送消息start
-        MqDto mqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.CALCULATE_OBJECTIVE_SCORE.name(),
-                transMap, MqTagEnum.CALCULATE_OBJECTIVE_SCORE, recordId.toString(), recordId.toString());
-        mqDtoService.assembleSendOneWayMsg(mqDto);
-    }
-
     /**
      * 更新音频剩余播放次数
      */
@@ -926,18 +1074,30 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
             redisUtil.releaseLock(lockKey);
         }
     }
+    
+    @SuppressWarnings("unchecked")
+	private Double calculateTotalObjectiveScore(Long recordId) {
+    	Double total = 0.0;
+    	Map<String, ExamStudentAnswerCacheBean> as = redisUtil.getHashEntries(RedisKeyHelper.examAnswerKey(recordId));
+        if (as != null && as.size() > 0) {
+            for (ExamStudentAnswerCacheBean sa : as.values()) {
+                if (sa.getObjective() != null && sa.getObjective()) {//是客观题
+                    if (sa.getScore() != null) {//有分值
+                        total = total + sa.getScore();
+                    } 
+                }
+            }
+        }
+        ExamRecordCacheUtil.setObjectiveScore(recordId, total);
+        return total;
+    }
 
     private ExamFinishBean diposeFinish(Long studentId, Long recordId, String type, Integer durationSeconds) {
-        Date now = new Date();
-        ExamFinishBean ret = new ExamFinishBean();
-        ret.setFinishTime(now.getTime());
-        ret.setObjectiveScore(ExamRecordCacheUtil.getObjectiveScore(recordId));
-        Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
-        if (ExamRecordCacheUtil.getId(recordId) == null || examStudentId == null) {
-            ret.setStatus(FinishExamResultEnum.NORMAL);
-            return ret;
-        }
+    	Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
         // 校验当前登录用户和参数一致性
+    	if(examStudentId==null) {
+    		throw new BusinessException(ExceptionResultEnum.NOT_FOUND_EXAM_RECORD);
+    	}
         ExamStudentCacheBean es = (ExamStudentCacheBean) redisUtil
                 .get(RedisKeyHelper.examStudentCacheKey(examStudentId));
         if (es == null) {
@@ -946,6 +1106,17 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         if (!studentId.equals(es.getStudentId())) {
             throw new BusinessException(ExceptionResultEnum.EXAM_ID_NOT_EQUALY);
         }
+        
+        Date now = new Date();
+        ExamFinishBean ret = new ExamFinishBean();
+        ret.setFinishTime(now.getTime());
+        ret.setObjectiveScore(calculateTotalObjectiveScore(recordId));
+        
+        if (ExamRecordCacheUtil.getId(recordId) == null || examStudentId == null) {
+            ret.setStatus(FinishExamResultEnum.NORMAL);
+            return ret;
+        }
+
 
         ExamCacheBean exam = getExamCacheBeanNative(es.getExamId());
         ret.setPostNotice(exam.getPostNotice());
@@ -967,9 +1138,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
                             1 :
                             ExamRecordCacheUtil.getBreachStatus(recordId);
                     if (warningCount != null && warningCount.intValue() > 0) {//有预警
-                        if (breachStatus == null) {//无违纪结果
-                            ret.setStatus(FinishExamResultEnum.AUDITING);
-                        } else if (breachStatus.intValue() == 0) {//违纪结果是false
+                        if (breachStatus.intValue() == 0) {//违纪结果是false
                             ret.setReviewResult(ReviewResultEnum.PASS);
                             if (ret.getObjectiveScore() == null) {
                                 ret.setStatus(FinishExamResultEnum.SCORE_CALCULATE);
@@ -1206,7 +1375,8 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
      * @param orgId
      * @return
      */
-    @Override
+    @SuppressWarnings("rawtypes")
+	@Override
     public List<Map> examList(Long userId, Long orgId) {
         return teExamMapper.examList(userId, orgId);
     }

+ 169 - 162
themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeExamRecordServiceImpl.java

@@ -99,7 +99,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      * @param orgId
      * @return
      */
-    @Override
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+	@Override
     public Map getUnFinishExam(Long studentId, Long examId, Long orgId) {
         TEExamUnFinishDto teExamUnFinishDto = tOeExamRecordMapper.getUnFinishExam(studentId, examId, orgId);
         if (Objects.nonNull(teExamUnFinishDto)) {
@@ -192,163 +193,163 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
     /**
      * 计算客观分
      */
-    @Override
-    public void calculateObjectiveScore(Map<String, Object> param) {
-        Long recordId = (Long) param.get("recordId");
-        Integer mainNumber = (Integer) param.get("mainNumber");
-        Integer subNumber = (Integer) param.get("subNumber");
-        Integer subIndex = (Integer) param.get("subIndex");
-        Long paperId = ExamRecordCacheUtil.getPaperId(recordId);
-        String key = RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex);
-
-        //考生作答缓存
-        ExamStudentAnswerCacheBean answer = (ExamStudentAnswerCacheBean) redisUtil
-                .get(RedisKeyHelper.examAnswerKey(recordId), key);
-        if (answer == null) {
-            log.error("no ExamStudentAnswerCacheBean for calculateObjectiveScore recordId:" + recordId + " key:" + key);
-
-            // 计算客观分总分
-            calculateTotalObjectiveScore(param);
-            return;
-        }
-        //整卷客观题标答缓存集合
-        Map<String, ObjectiveAnswerCacheBean> map = examPaperService.getObjectiveAnswerCacheBean(paperId);
-        if (map == null || map.size() == 0) {
-            log.info("no ObjectiveAnswerCacheBean map for calculateObjectiveScore recordId:" + recordId + " paperId:"
-                    + paperId);
-            // 更新分数
-            answer.setScore(0.0);
-            redisUtil.set(RedisKeyHelper.examAnswerKey(recordId), key, answer);
-
-            // 计算客观分总分
-            calculateTotalObjectiveScore(param);
-            return;
-        }
-        //客观题标答缓存
-        ObjectiveAnswerCacheBean cb = map.get(key);
-        if (cb == null) {
-            log.info("no ObjectiveAnswerCacheBean for calculateObjectiveScore recordId:" + recordId + " key:" + key);
-            // 更新分数
-            answer.setScore(0.0);
-            redisUtil.set(RedisKeyHelper.examAnswerKey(recordId), key, answer);
-
-            // 计算客观分总分
-            calculateTotalObjectiveScore(param);
-            return;
-        }
-
-        if (cb.getStructType().intValue() == 1) {
-            if (checkSingleChoice(answer.getAnswer(), cb.getChoiceAnswer())) {
-                answer.setScore(cb.getScore());
-            } else {
-                answer.setScore(0.0);
-            }
-        }
-        if (cb.getStructType().intValue() == 2) {
-            ExamCacheBean ec = examService.getExamCacheBean(ExamRecordCacheUtil.getExamId(recordId));
-            int r = checkMultipleChoice(answer.getAnswer(), cb.getChoiceAnswer());
-            if (ec.getObjectiveScorePolicy().equals(ObjectiveScorePolicyEnum.EQUAL)) {// 全对给分
-                if (r == 1) {
-                    answer.setScore(cb.getScore());
-                } else {
-                    answer.setScore(0.0);
-                }
-            }
-            if (ec.getObjectiveScorePolicy().equals(ObjectiveScorePolicyEnum.PARTIAL)) {// 漏选半分
-                if (r == 1) {
-                    answer.setScore(cb.getScore());
-                } else if (r == 0) {
-                    BigDecimal b = new BigDecimal(cb.getScore()).divide(new BigDecimal("2"), 1, BigDecimal.ROUND_UP);
-                    answer.setScore(b.doubleValue());
-                } else {
-                    answer.setScore(0.0);
-                }
-            }
-        }
-        if (cb.getStructType().intValue() == 3) {
-            if (StringUtils.isNotBlank(answer.getAnswer())) {
-                String answerStr = answer.getAnswer().replaceAll("\n", "");
-                if (StringUtils.isNotBlank(answerStr)) {
-                    if (Boolean.parseBoolean(answerStr) == cb.getBoolAnswer().booleanValue()) {
-                        answer.setScore(cb.getScore());
-                    } else {
-                        answer.setScore(0.0);
-                    }
-                }
-            }
-        }
-
-        if (answer.getScore() == null) {
-            log.info("小题分数为空,得分置为0," + "recordId:" + recordId + " questionKey:" + key);
-            answer.setScore(0.0);
-        }
-        // 更新分数
-        redisUtil.set(RedisKeyHelper.examAnswerKey(recordId), key, answer);
-
-        // 计算客观分总分
-        calculateTotalObjectiveScore(param);
-    }
-
-    @SuppressWarnings("unchecked")
-    private void calculateTotalObjectiveScore(Map<String, Object> param) {
-        Long recordId = (Long) param.get("recordId");
-        Integer mainNumber = (Integer) param.get("mainNumber");
-        Integer subNumber = (Integer) param.get("subNumber");
-        Integer subIndex = (Integer) param.get("subIndex");
-        String lockKey = SystemConstant.REDIS_LOCK_TOTAL_OBJECTIVE_SCORE_PREFIX + recordId;
-        try {
-            String key = RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex);
-            Boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_CACHE_TIME_OUT);
-            if (lock) {
-                log.info("calculateTotalObjectiveScore get lock sucss recordId:" + recordId + " key:" + key);
-                Map<String, ExamStudentAnswerCacheBean> as = redisUtil
-                        .getHashEntries(RedisKeyHelper.examAnswerKey(recordId));
-                if (as != null && as.size() > 0) {
-                    Double total = 0.0;
-                    for (ExamStudentAnswerCacheBean sa : as.values()) {
-                        if (sa.getObjective() != null && sa.getObjective()) {//是客观题
-                            if (sa.getScore() != null) {//有分值
-                                total = total + sa.getScore();
-                            } else {
-                                log.info(
-                                        "calculateTotalObjectiveScore ExamStudentAnswerCacheBean Score is null recordId:"
-                                                + recordId + " key:" + RedisKeyHelper
-                                                .examAnswerHashKey(sa.getMainNumber(), sa.getSubNumber(),
-                                                        sa.getSubIndex()));
-                                total = null;
-                                break;
-                            }
-                        }
-                    }
-                    if (total != null) {
-                        ExamRecordCacheUtil.setObjectiveScore(recordId, total);
-                        this.sendExamRecordDataSaveMq(recordId, System.currentTimeMillis());
-                    } else {
-                        log.info("calculateTotalObjectiveScore total Score is null recordId:" + recordId + " key:"
-                                + key);
-                    }
-                }
-            } else {
-                log.info("calculateTotalObjectiveScore get lock faild recordId:" + recordId + " key:" + key);
-                calculateObjectiveScoreMsg(recordId, mainNumber, subNumber, subIndex);
-            }
-        } finally {
-            redisUtil.releaseLock(lockKey);
-        }
-    }
+//    @Override
+//    public void calculateObjectiveScore(Map<String, Object> param) {
+//        Long recordId = (Long) param.get("recordId");
+//        Integer mainNumber = (Integer) param.get("mainNumber");
+//        Integer subNumber = (Integer) param.get("subNumber");
+//        Integer subIndex = (Integer) param.get("subIndex");
+//        Long paperId = ExamRecordCacheUtil.getPaperId(recordId);
+//        String key = RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex);
+//
+//        //考生作答缓存
+//        ExamStudentAnswerCacheBean answer = (ExamStudentAnswerCacheBean) redisUtil
+//                .get(RedisKeyHelper.examAnswerKey(recordId), key);
+//        if (answer == null) {
+//            log.error("no ExamStudentAnswerCacheBean for calculateObjectiveScore recordId:" + recordId + " key:" + key);
+//
+//            // 计算客观分总分
+//            calculateTotalObjectiveScore(param);
+//            return;
+//        }
+//        //整卷客观题标答缓存集合
+//        Map<String, ObjectiveAnswerCacheBean> map = examPaperService.getObjectiveAnswerCacheBean(paperId);
+//        if (map == null || map.size() == 0) {
+//            log.info("no ObjectiveAnswerCacheBean map for calculateObjectiveScore recordId:" + recordId + " paperId:"
+//                    + paperId);
+//            // 更新分数
+//            answer.setScore(0.0);
+//            redisUtil.set(RedisKeyHelper.examAnswerKey(recordId), key, answer);
+//
+//            // 计算客观分总分
+//            calculateTotalObjectiveScore(param);
+//            return;
+//        }
+//        //客观题标答缓存
+//        ObjectiveAnswerCacheBean cb = map.get(key);
+//        if (cb == null) {
+//            log.info("no ObjectiveAnswerCacheBean for calculateObjectiveScore recordId:" + recordId + " key:" + key);
+//            // 更新分数
+//            answer.setScore(0.0);
+//            redisUtil.set(RedisKeyHelper.examAnswerKey(recordId), key, answer);
+//
+//            // 计算客观分总分
+//            calculateTotalObjectiveScore(param);
+//            return;
+//        }
+//
+//        if (cb.getStructType().intValue() == 1) {
+//            if (checkSingleChoice(answer.getAnswer(), cb.getChoiceAnswer())) {
+//                answer.setScore(cb.getScore());
+//            } else {
+//                answer.setScore(0.0);
+//            }
+//        }
+//        if (cb.getStructType().intValue() == 2) {
+//            ExamCacheBean ec = examService.getExamCacheBean(ExamRecordCacheUtil.getExamId(recordId));
+//            int r = checkMultipleChoice(answer.getAnswer(), cb.getChoiceAnswer());
+//            if (ec.getObjectiveScorePolicy().equals(ObjectiveScorePolicyEnum.EQUAL)) {// 全对给分
+//                if (r == 1) {
+//                    answer.setScore(cb.getScore());
+//                } else {
+//                    answer.setScore(0.0);
+//                }
+//            }
+//            if (ec.getObjectiveScorePolicy().equals(ObjectiveScorePolicyEnum.PARTIAL)) {// 漏选半分
+//                if (r == 1) {
+//                    answer.setScore(cb.getScore());
+//                } else if (r == 0) {
+//                    BigDecimal b = new BigDecimal(cb.getScore()).divide(new BigDecimal("2"), 1, BigDecimal.ROUND_UP);
+//                    answer.setScore(b.doubleValue());
+//                } else {
+//                    answer.setScore(0.0);
+//                }
+//            }
+//        }
+//        if (cb.getStructType().intValue() == 3) {
+//            if (StringUtils.isNotBlank(answer.getAnswer())) {
+//                String answerStr = answer.getAnswer().replaceAll("\n", "");
+//                if (StringUtils.isNotBlank(answerStr)) {
+//                    if (Boolean.parseBoolean(answerStr) == cb.getBoolAnswer().booleanValue()) {
+//                        answer.setScore(cb.getScore());
+//                    } else {
+//                        answer.setScore(0.0);
+//                    }
+//                }
+//            }
+//        }
+//
+//        if (answer.getScore() == null) {
+//            log.info("小题分数为空,得分置为0," + "recordId:" + recordId + " questionKey:" + key);
+//            answer.setScore(0.0);
+//        }
+//        // 更新分数
+//        redisUtil.set(RedisKeyHelper.examAnswerKey(recordId), key, answer);
+//
+//        // 计算客观分总分
+//        calculateTotalObjectiveScore(param);
+//    }
+
+//    @SuppressWarnings("unchecked")
+//    private void calculateTotalObjectiveScore(Map<String, Object> param) {
+//        Long recordId = (Long) param.get("recordId");
+//        Integer mainNumber = (Integer) param.get("mainNumber");
+//        Integer subNumber = (Integer) param.get("subNumber");
+//        Integer subIndex = (Integer) param.get("subIndex");
+//        String lockKey = SystemConstant.REDIS_LOCK_TOTAL_OBJECTIVE_SCORE_PREFIX + recordId;
+//        try {
+//            String key = RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex);
+//            Boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_CACHE_TIME_OUT);
+//            if (lock) {
+//                log.info("calculateTotalObjectiveScore get lock sucss recordId:" + recordId + " key:" + key);
+//                Map<String, ExamStudentAnswerCacheBean> as = redisUtil
+//                        .getHashEntries(RedisKeyHelper.examAnswerKey(recordId));
+//                if (as != null && as.size() > 0) {
+//                    Double total = 0.0;
+//                    for (ExamStudentAnswerCacheBean sa : as.values()) {
+//                        if (sa.getObjective() != null && sa.getObjective()) {//是客观题
+//                            if (sa.getScore() != null) {//有分值
+//                                total = total + sa.getScore();
+//                            } else {
+//                                log.info(
+//                                        "calculateTotalObjectiveScore ExamStudentAnswerCacheBean Score is null recordId:"
+//                                                + recordId + " key:" + RedisKeyHelper
+//                                                .examAnswerHashKey(sa.getMainNumber(), sa.getSubNumber(),
+//                                                        sa.getSubIndex()));
+//                                total = null;
+//                                break;
+//                            }
+//                        }
+//                    }
+//                    if (total != null) {
+//                        ExamRecordCacheUtil.setObjectiveScore(recordId, total);
+//                        this.sendExamRecordDataSaveMq(recordId, System.currentTimeMillis());
+//                    } else {
+//                        log.info("calculateTotalObjectiveScore total Score is null recordId:" + recordId + " key:"
+//                                + key);
+//                    }
+//                }
+//            } else {
+//                log.info("calculateTotalObjectiveScore get lock faild recordId:" + recordId + " key:" + key);
+//                calculateObjectiveScoreMsg(recordId, mainNumber, subNumber, subIndex);
+//            }
+//        } finally {
+//            redisUtil.releaseLock(lockKey);
+//        }
+//    }
 
     //考试中计算客观分消息
-    private void calculateObjectiveScoreMsg(Long recordId, Integer mainNumber, Integer subNumber, Integer subIndex) {
-        Map<String, Object> transMap = new HashMap<String, Object>();
-        transMap.put("recordId", recordId);
-        transMap.put("mainNumber", mainNumber);
-        transMap.put("subNumber", subNumber);
-        transMap.put("subIndex", subIndex);
-        Map<String, Object> propMap = mqDtoService.buildMqDelayMsg("1s");
-        MqDto mqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.CALCULATE_OBJECTIVE_SCORE.name(),
-                transMap, MqTagEnum.CALCULATE_OBJECTIVE_SCORE, recordId.toString(), propMap, recordId.toString());
-        mqDtoService.assembleSendAsyncDelayMsg(mqDto);
-    }
+//    private void calculateObjectiveScoreMsg(Long recordId, Integer mainNumber, Integer subNumber, Integer subIndex) {
+//        Map<String, Object> transMap = new HashMap<String, Object>();
+//        transMap.put("recordId", recordId);
+//        transMap.put("mainNumber", mainNumber);
+//        transMap.put("subNumber", subNumber);
+//        transMap.put("subIndex", subIndex);
+//        Map<String, Object> propMap = mqDtoService.buildMqDelayMsg("1s");
+//        MqDto mqDto = new MqDto(mqUtil.getMqGroupDomain().getTopic(), MqTagEnum.CALCULATE_OBJECTIVE_SCORE.name(),
+//                transMap, MqTagEnum.CALCULATE_OBJECTIVE_SCORE, recordId.toString(), propMap, recordId.toString());
+//        mqDtoService.assembleSendAsyncDelayMsg(mqDto);
+//    }
 
     private boolean checkSingleChoice(String answerArray, JSONArray ar) {
         JSONArray an = JSONArray.parseArray(answerArray);
@@ -614,7 +615,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      * @param userId
      * @return
      */
-    @Override
+    @SuppressWarnings("rawtypes")
+	@Override
     public IPage<InvigilateListBean> invigilatePageList(IPage<Map> iPage, Long examId, Long examActivityId,
                                                         String roomCode, Integer paperDownload, String status, String name, String identity,
                                                         Integer minWarningCount, Integer maxWarningCount, String clientWebsocketStatus, String monitorStatusSource,
@@ -642,7 +644,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      * @param orgId
      * @return
      */
-    @Override
+    @SuppressWarnings("rawtypes")
+	@Override
     public IPage<InvigilateListVideoBean> invigilatePageListVideo(IPage<Map> iPage, Long examId, Long examActivityId,
                                                                   String roomCode, Integer paperDownload, String status, String name, String identity,
                                                                   Integer minWarningCount, Integer maxWarningCount, String clientWebsocketStatus, Long userId, Long orgId) {
@@ -687,7 +690,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      * @param orgId
      * @return
      */
-    @Override
+    @SuppressWarnings("rawtypes")
+	@Override
     public IPage<InvigilateListPatrolBean> invigilatePagePatrolList(IPage<Map> iPage, Long examId, Long examActivityId,
                                                                     String roomCode, String status, String name, String identity, Integer minMultipleFaceCount,
                                                                     Integer maxMultipleFaceCount, Integer minExceptionCount, Integer maxExceptionCount, Integer minWarningCount,
@@ -718,7 +722,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      * @param orgId
      * @return
      */
-    @Override
+    @SuppressWarnings("rawtypes")
+	@Override
     public IPage<InvigilateListWarningBean> invigilatePageWarningList(IPage<Map> iPage, Long examId,
                                                                       Long examActivityId, String roomCode, Integer approveStatus, String name, String identity,
                                                                       Integer minMultipleFaceCount, Integer maxMultipleFaceCount, Integer minExceptionCount,
@@ -772,7 +777,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      * @param userId
      * @return
      */
-    @Override
+    @SuppressWarnings("rawtypes")
+	@Override
     public IPage<InvigilateListProgressBean> invigilatePageProgressList(IPage<Map> iPage, Long examId,
                                                                         Long examActivityId, String roomCode, String courseCode, String name, String identity, Long userId,
                                                                         Long orgId) {
@@ -825,7 +831,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
      * @param orgId
      * @return
      */
-    @Override
+    @SuppressWarnings("rawtypes")
+	@Override
     public IPage<InvigilateListHistoryBean> invigilatePageListHistory(IPage<Map> iPage, Long examId,
                                                                       Long examActivityId, String roomCode, String courseCode, String status, Integer breachStatus,
                                                                       String finishType, String name, String identity, Integer minMultipleFaceCount, Integer maxMultipleFaceCount,

+ 7 - 2
themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskExamPaperImportTemplete.java

@@ -168,10 +168,11 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
         if (course == null) {
             throw new BusinessException("科目编码不存在 " + courseCode);
         }
+        List<Long> paperIds=new ArrayList<Long>();
         File[] childs = courseDir.listFiles();
         for (File paperDir : childs) {
             if (paperDir.isDirectory()) {
-                disposePaperDir(rootDir, teExam, course, paperDir, map);
+                disposePaperDir(rootDir, teExam, course, paperDir, map,paperIds);
             }
         }
         Boolean objectiveShuffle = (Boolean) map.get("objectiveShuffle");
@@ -207,6 +208,9 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
         teExamCourseService.saveOrUpdate(course);
         // 设置调卷比例
         teExamPaperService.savePaperWeight(paperWeight(list));
+        
+        //处理试卷缓存
+        teExamPaperService.disposePaperCacheOnPaperImport(teExam.getId(),paperIds);
 
         //清除考试科目缓存
         teExamCourseService.deleteExamCourseCacheBean(teExam.getId(), courseCode);
@@ -230,7 +234,7 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
     }
 
     private void disposePaperDir(String rootDir, TEExam teExam, TEExamCourse course, File paperDir,
-                                 Map<String, Object> map) throws IOException {
+                                 Map<String, Object> map,List<Long> paperIds) throws IOException {
         String paperCode = paperDir.getName();
         File[] childs = paperDir.listFiles();
 
@@ -289,6 +293,7 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
         Integer audioPlayCount = (Integer) map.get("audioPlayCount");
         paper.setAudioPlayCount(audioPlayCount);
         teExamPaperService.saveOrUpdate(paper);
+        paperIds.add(paper.getId());
     }
 
     private void disposeViewPaper(String rootDir, TEExamPaper paper, File paperFile, File attachmentDir)

+ 8 - 0
themis-business/src/main/java/com/qmth/themis/business/util/RedisUtil.java

@@ -372,6 +372,14 @@ public class RedisUtil {
     public void expire(String key, int timeOutSecond) {
         redisTemplate.expire(key, timeOutSecond, TimeUnit.SECONDS);
     }
+    
+    /**设置过期时间
+     * @param key
+     * @param timeOut
+     */
+    public void expire(String key, long timeOut,TimeUnit timeUnit) {
+        redisTemplate.expire(key, timeOut, timeUnit);
+    }
 
     /**
      * 获取redis序列

+ 12 - 0
themis-business/src/main/resources/mapper/TEExamActivityMapper.xml

@@ -197,4 +197,16 @@
             and f.org_id=#{orgId}
         </if>
     </select>
+    
+    <select id="findMinStartTimeAndMaxFinshTimeByExamId" resultType="java.util.Map" >
+        select min(t.start_time) startTime,max(t.finish_time) finishTime from t_e_exam_activity t
+        where t.exam_id=#{examId} and t.enable=1
+    </select>
+    
+    <select id="findExamIdToday" resultType="java.lang.Long" >
+        select distinct t.exam_id  from t_e_exam_activity t
+        left join t_e_exam f on t.exam_id=f.id
+        where t.start_time&gt;=#{startTime} and t.start_time&lt;=#{endTime} and t.enable=1 and f.enable=1
+    </select>
+    
 </mapper>

+ 6 - 0
themis-business/src/main/resources/mapper/TEExamPaperMapper.xml

@@ -21,4 +21,10 @@
 		update t_e_exam_paper t set t.weight=#{weight} where t.id=#{id}
 	</update>
 	
+	<select id="findByExamId"
+		resultType="com.qmth.themis.business.entity.TEExamPaper">
+		select * from t_e_exam_paper t
+		where t.exam_id=#{examId}
+	</select>
+	
 </mapper>

+ 4 - 0
themis-common/src/main/java/com/qmth/themis/common/enums/ExceptionResultEnum.java

@@ -52,6 +52,10 @@ public enum ExceptionResultEnum {
     MOBILE_FIRST_OFFLINE(500,500039,"移动端第一机位离线"),
 
     MOBILE_SECOND_OFFLINE(500,500040,"移动端第二机位离线"),
+    
+    NOT_FOUND_PAPER_OBJECTIVE_ANSWER(500,500041,"未找到试卷客观题答案"),
+    
+    NOT_FOUND_QUESTION_OBJECTIVE_ANSWER(500,500042,"未找到小题客观题答案"),
 
     /**
      * 系统预置

+ 1 - 1
themis-mq/src/main/java/com/qmth/themis/mq/service/MqLogicService.java

@@ -63,7 +63,7 @@ public interface MqLogicService {
      * @param mqDto
      * @param key
      */
-    public void execMqCalculateObjectiveScoreLogic(MqDto mqDto, String key);
+//    public void execMqCalculateObjectiveScoreLogic(MqDto mqDto, String key);
 
     /**
      * 人脸验证保存

+ 12 - 12
themis-mq/src/main/java/com/qmth/themis/mq/service/impl/MqLogicServiceImpl.java

@@ -306,18 +306,18 @@ public class MqLogicServiceImpl implements MqLogicService {
      * @param mqDto
      * @param key
      */
-    @Override
-    @Transactional
-    public void execMqCalculateObjectiveScoreLogic(MqDto mqDto, String key) {
-        Gson gson = new Gson();
-        Map<String, Object> param = (Map<String, Object>) mqDto.getBody();
-        examRecordService.calculateObjectiveScore(param);
-        mqDto.setAck(SystemConstant.STANDARD_ACK_TYPE);
-        TMRocketMessage tmRocketMessage = gson.fromJson(gson.toJson(mqDto), TMRocketMessage.class);
-        tmRocketMessage.setBody(JacksonUtil.parseJson(tmRocketMessage.getBody()));
-        tmRocketMessageService.saveOrUpdate(tmRocketMessage);
-        redisUtil.delete(key, mqDto.getId());
-    }
+//    @Override
+//    @Transactional
+//    public void execMqCalculateObjectiveScoreLogic(MqDto mqDto, String key) {
+//        Gson gson = new Gson();
+//        Map<String, Object> param = (Map<String, Object>) mqDto.getBody();
+//        examRecordService.calculateObjectiveScore(param);
+//        mqDto.setAck(SystemConstant.STANDARD_ACK_TYPE);
+//        TMRocketMessage tmRocketMessage = gson.fromJson(gson.toJson(mqDto), TMRocketMessage.class);
+//        tmRocketMessage.setBody(JacksonUtil.parseJson(tmRocketMessage.getBody()));
+//        tmRocketMessageService.saveOrUpdate(tmRocketMessage);
+//        redisUtil.delete(key, mqDto.getId());
+//    }
 
     /**
      * 人脸验证保存

+ 7 - 7
themis-mq/src/main/java/com/qmth/themis/mq/templete/impl/CalculateObjectiveScoreConcurrentlyImpl.java

@@ -1,15 +1,13 @@
 package com.qmth.themis.mq.templete.impl;
 
-import com.qmth.themis.business.constant.SpringContextHolder;
-import com.qmth.themis.business.enums.MqExecTypeEnum;
-import com.qmth.themis.mq.service.MqLogicService;
-import com.qmth.themis.mq.templete.Concurrently;
+import java.util.List;
+
 import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
 import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
 import org.apache.rocketmq.common.message.MessageExt;
 import org.springframework.stereotype.Service;
 
-import java.util.List;
+import com.qmth.themis.mq.templete.Concurrently;
 
 /**
  * 计算客观分
@@ -18,12 +16,14 @@ import java.util.List;
  * @Author: xiatian
  * @Date: 2020-07-30
  */
+@Deprecated
 @Service
 public class CalculateObjectiveScoreConcurrentlyImpl implements Concurrently {
 
     @Override
     public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
-        MqLogicService mqLogicService = SpringContextHolder.getBean(MqLogicService.class);
-        return mqLogicService.consumeMessage(msgs, consumeConcurrentlyContext, MqExecTypeEnum.EXEC_MQ_CALCULATE_OBJECTIVE_SCORE_LOGIC);
+//        MqLogicService mqLogicService = SpringContextHolder.getBean(MqLogicService.class);
+//        return mqLogicService.consumeMessage(msgs, consumeConcurrentlyContext, MqExecTypeEnum.EXEC_MQ_CALCULATE_OBJECTIVE_SCORE_LOGIC);
+    	return null;
     }
 }

+ 6 - 1
themis-task/src/main/java/com/qmth/themis/task/enums/QuartzTaskEnum.java

@@ -19,7 +19,12 @@ public enum QuartzTaskEnum {
 
     MQ_ACTIVITY_JOB_NAME("mq_activity_job"),
 
-    MQ_ACTIVITY_JOB_GROUP_NAME("mq_activity_job group"),;
+    MQ_ACTIVITY_JOB_GROUP_NAME("mq_activity_job group"),
+    
+    OBJECTIVE_ANSWER_CACHE_LOAD_JOB_NAME("客观题缓存加载"),
+    
+    OBJECTIVE_ANSWER_CACHE_LOAD_JOB_GROUP_NAME("客观题缓存加载")
+    ;
 
     private QuartzTaskEnum(String code) {
         this.code = code;

+ 42 - 0
themis-task/src/main/java/com/qmth/themis/task/quartz/ObjectiveAnswerCacheLoadJob.java

@@ -0,0 +1,42 @@
+package com.qmth.themis.task.quartz;
+
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+import org.springframework.stereotype.Component;
+
+import com.qmth.themis.business.service.TEExamActivityService;
+import com.qmth.themis.business.service.TEExamPaperService;
+
+/**
+ * @Description: 客观题缓存加载
+ * @Author: xiatian
+ * @Date: 2020-12-30
+ */
+@Component
+public class ObjectiveAnswerCacheLoadJob extends QuartzJobBean {
+    private final static Logger log = LoggerFactory.getLogger(ObjectiveAnswerCacheLoadJob.class);
+
+    @Resource
+    TEExamActivityService examActivityService;
+    
+    @Resource
+    TEExamPaperService examPaperService;
+
+    @Override
+    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
+        log.info("ObjectiveAnswerCacheLoadJob进来了,context:{}", context);
+        List<Long> examids=examActivityService.findExamIdToday();
+        if(examids!=null&&examids.size()>0) {
+        	for(Long examId:examids) {
+        		examPaperService.disposeObjectiveAnswer(examId);
+        	}
+        }
+    }
+}

+ 11 - 2
themis-task/src/main/java/com/qmth/themis/task/start/StartRunning.java

@@ -11,6 +11,7 @@ import com.qmth.themis.task.enums.QuartzTaskEnum;
 import com.qmth.themis.task.listener.QuartzOrderlyImpl;
 import com.qmth.themis.task.quartz.MqActivityJob;
 import com.qmth.themis.task.quartz.MqJob;
+import com.qmth.themis.task.quartz.ObjectiveAnswerCacheLoadJob;
 import com.qmth.themis.task.service.QuartzService;
 import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
 import org.slf4j.Logger;
@@ -46,7 +47,8 @@ public class StartRunning implements CommandLineRunner {
     @Resource
     DictionaryConfig dictionaryConfig;
 
-    @Override
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+	@Override
     public void run(String... args) throws Exception {
         log.info("服务器启动时执行 start");
         log.info("增加mqjob start");
@@ -62,6 +64,13 @@ public class StartRunning implements CommandLineRunner {
         quartzService.deleteJob(QuartzTaskEnum.MQ_ACTIVITY_JOB_NAME.name(), QuartzTaskEnum.MQ_ACTIVITY_JOB_GROUP_NAME.name());
         quartzService.addJob(MqActivityJob.class, QuartzTaskEnum.MQ_ACTIVITY_JOB_NAME.name(), QuartzTaskEnum.MQ_ACTIVITY_JOB_GROUP_NAME.name(), "0 0 0 * * ?", mqMap);
         log.info("增加mqActivityjob end");
+        
+        log.info("增加客观题答案缓存加载job start");
+        Map objectiveAnswerCacheMap = new HashMap();
+        objectiveAnswerCacheMap.put("name", ObjectiveAnswerCacheLoadJob.class.getName());
+        quartzService.deleteJob(QuartzTaskEnum.OBJECTIVE_ANSWER_CACHE_LOAD_JOB_NAME.name(), QuartzTaskEnum.OBJECTIVE_ANSWER_CACHE_LOAD_JOB_GROUP_NAME.name());
+        quartzService.addJob(ObjectiveAnswerCacheLoadJob.class, QuartzTaskEnum.OBJECTIVE_ANSWER_CACHE_LOAD_JOB_NAME.name(), QuartzTaskEnum.OBJECTIVE_ANSWER_CACHE_LOAD_JOB_GROUP_NAME.name(), "0 0 1 * * ?", objectiveAnswerCacheMap);
+        log.info("增加客观题答案缓存加载job end");
 
         /**
          * session
@@ -88,7 +97,7 @@ public class StartRunning implements CommandLineRunner {
          */
         rocketMessageConsumer.setRocketMQConsumer(nameServer, dictionaryConfig.mqConfigDomain().getMap().get(MqGroupEnum.QUARTZ_GROUP.name()), dictionaryConfig.mqConfigDomain().getTopic(), MqTagEnum.EXAM_ACTIVITY.name() + "||" + MqTagEnum.EXAM_STUDENT.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(QuartzOrderlyImpl.class));
         //计算客观分
-        rocketMessageConsumer.setRocketMQConsumer(nameServer, dictionaryConfig.mqConfigDomain().getMap().get(MqGroupEnum.CALCULATE_OBJECTIVE_SCORE_GROUP.name()), dictionaryConfig.mqConfigDomain().getTopic(), MqTagEnum.CALCULATE_OBJECTIVE_SCORE.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(CalculateObjectiveScoreConcurrentlyImpl.class));
+//        rocketMessageConsumer.setRocketMQConsumer(nameServer, dictionaryConfig.mqConfigDomain().getMap().get(MqGroupEnum.CALCULATE_OBJECTIVE_SCORE_GROUP.name()), dictionaryConfig.mqConfigDomain().getTopic(), MqTagEnum.CALCULATE_OBJECTIVE_SCORE.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(CalculateObjectiveScoreConcurrentlyImpl.class));
         //重新算分
         rocketMessageConsumer.setRocketMQConsumer(nameServer, dictionaryConfig.mqConfigDomain().getMap().get(MqGroupEnum.SCORE_CALCULATE_GROUP.name()), dictionaryConfig.mqConfigDomain().getTopic(), MqTagEnum.EXAM_SCORE_CALCULATE.name(), MessageModel.CLUSTERING, SpringContextHolder.getBean(CalculateScoreConcurrentlyImpl.class));
         //人脸验证保存