Переглянути джерело

断点次数、考试次数、考试时间需求变动

xiatian 4 роки тому
батько
коміт
70586f2c7e

+ 36 - 10
themis-business/src/main/java/com/qmth/themis/business/cache/ExamRecordCacheUtil.java

@@ -1,14 +1,19 @@
 package com.qmth.themis.business.cache;
 
+import java.util.Date;
+import java.util.Objects;
+
 import com.qmth.themis.business.constant.SpringContextHolder;
 import com.qmth.themis.business.constant.SystemConstant;
-import com.qmth.themis.business.enums.*;
+import com.qmth.themis.business.enums.ExamRecordStatusEnum;
+import com.qmth.themis.business.enums.FinishTypeEnum;
+import com.qmth.themis.business.enums.MonitorCallStatusSourceEnum;
+import com.qmth.themis.business.enums.MonitorStatusSourceEnum;
+import com.qmth.themis.business.enums.MonitorVideoSourceEnum;
+import com.qmth.themis.business.enums.VerifyExceptionEnum;
+import com.qmth.themis.business.enums.WebsocketStatusEnum;
 import com.qmth.themis.business.service.TOeExamRecordService;
 import com.qmth.themis.business.util.RedisUtil;
-import org.bouncycastle.util.Strings;
-
-import java.util.Date;
-import java.util.Objects;
 
 /**
  * 考试记录缓存hash值操作
@@ -88,13 +93,13 @@ public class ExamRecordCacheUtil {
         return (Date) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "clientLastSyncTime");
     }
 
-    public static Integer getLeftBreakResumeCount(Long recordId) {
-        return (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "leftBreakResumeCount");
+    public static Integer getAlreadyBreakCount(Long recordId) {
+        return (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "alreadyBreakCount");
     }
 
-    public static void setLeftBreakResumeCount(Long recordId, Integer leftBreakResumeCount) {
-        redisUtil.set(RedisKeyHelper.examRecordCacheKey(recordId), "leftBreakResumeCount", leftBreakResumeCount);
-        examRecordService.dataUpdateMq(recordId, "left_break_resume_count", leftBreakResumeCount);
+    public static void setAlreadyBreakCount(Long recordId, Integer alreadyBreakCount) {
+        redisUtil.set(RedisKeyHelper.examRecordCacheKey(recordId), "alreadyBreakCount", alreadyBreakCount);
+        examRecordService.dataUpdateMq(recordId, "already_break_count", alreadyBreakCount);
     }
 
     public static ExamRecordStatusEnum getStatus(Long recordId) {
@@ -259,4 +264,25 @@ public class ExamRecordCacheUtil {
     public static Date getLastStartTime(Long recordId) {
         return (Date) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "lastStartTime");
     }
+    
+    public static Date getStartTime(Long recordId) {
+        return (Date) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "startTime");
+    }
+    
+    public static Date getEndTime(Long recordId) {
+        return (Date) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "endTime");
+    }
+    
+    public static Integer getOpeningSeconds(Long recordId) {
+        return (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "openingSeconds");
+    }
+    public static Integer getMinDurationSeconds(Long recordId) {
+        return (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "minDurationSeconds");
+    }
+    public static Integer getMaxDurationSeconds(Long recordId) {
+        return (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "maxDurationSeconds");
+    }
+    public static Integer getForceFinish(Long recordId) {
+        return (Integer) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId), "forceFinish");
+    }
 }

+ 12 - 9
themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamStudentCacheBean.java

@@ -33,8 +33,8 @@ public class ExamStudentCacheBean implements Serializable {
     // 考场名称
     private String roomName;
 
-    // 剩余考试次数
-    private Integer leftExamCount;
+    //已考考试次数
+    private Integer alreadyExamCount;
     // 当前考试是第几次
     private Integer currentSerialNumber;
     // 当前考试记录ID
@@ -128,15 +128,17 @@ public class ExamStudentCacheBean implements Serializable {
         this.roomCode = roomCode;
     }
 
-    public Integer getLeftExamCount() {
-        return leftExamCount;
-    }
 
-    public void setLeftExamCount(Integer leftExamCount) {
-        this.leftExamCount = leftExamCount;
-    }
+    
+    public Integer getAlreadyExamCount() {
+		return alreadyExamCount;
+	}
+
+	public void setAlreadyExamCount(Integer alreadyExamCount) {
+		this.alreadyExamCount = alreadyExamCount;
+	}
 
-    public Long getCurrentRecordId() {
+	public Long getCurrentRecordId() {
         return currentRecordId;
     }
 
@@ -152,4 +154,5 @@ public class ExamStudentCacheBean implements Serializable {
         this.id = id;
     }
 
+    
 }

+ 9 - 4
themis-business/src/main/java/com/qmth/themis/business/dto/response/TEExamActivityDto.java

@@ -1,5 +1,12 @@
 package com.qmth.themis.business.dto.response;
 
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
 import com.qmth.themis.business.cache.bean.ExamActivityCacheBean;
 import com.qmth.themis.business.cache.bean.ExamCacheBean;
 import com.qmth.themis.business.cache.bean.ExamCourseCacheBean;
@@ -8,10 +15,8 @@ import com.qmth.themis.business.enums.EntryAuthenticationPolicyEnum;
 import com.qmth.themis.business.enums.ExamModeEnum;
 import com.qmth.themis.business.enums.HardwareTestEnum;
 import com.qmth.themis.business.enums.MonitorVideoSourceEnum;
-import io.swagger.annotations.ApiModelProperty;
 
-import java.io.Serializable;
-import java.util.*;
+import io.swagger.annotations.ApiModelProperty;
 
 /**
  * @Description: 考试场次dto
@@ -207,7 +212,7 @@ public class TEExamActivityDto implements Serializable {
         this.inProcessLivenessJudgePolicy = ec.getInProcessLivenessJudgePolicy().name();
         this.startTime = examActivityCacheBean.getStartTime();
         this.finishTime = examActivityCacheBean.getFinishTime();
-        this.leftExamCount = examStudentCacheBean.getLeftExamCount();
+        this.leftExamCount = ec.getExamCount()-examStudentCacheBean.getAlreadyExamCount();
         this.mobilePhotoUpload = ec.getMobilePhotoUpload();
         this.examId = ec.getId();
     }

+ 24 - 12
themis-business/src/main/java/com/qmth/themis/business/entity/TEExamStudent.java

@@ -15,7 +15,12 @@ import io.swagger.annotations.ApiModelProperty;
 @ApiModel(value = "t_e_exam_student", description = "考生库")
 public class TEExamStudent extends BaseEntity {
 
-    @ApiModelProperty(value = "批次id")
+    /**
+	 * 
+	 */
+	private static final long serialVersionUID = 4720242896522208986L;
+
+	@ApiModelProperty(value = "批次id")
     @TableField(value = "exam_id")
     private Long examId;
 
@@ -47,9 +52,14 @@ public class TEExamStudent extends BaseEntity {
     @TableField(value = "parameter")
     private String parameter;
 
-    @ApiModelProperty(value = "剩余考试次数")
-    @TableField(value = "left_exam_count")
-    private Integer leftExamCount;
+//    @ApiModelProperty(value = "剩余考试次数")
+//    @TableField(value = "left_exam_count")
+//    private Integer leftExamCount;
+    
+    @ApiModelProperty(value = "已考考试次数")
+    @TableField(value = "already_exam_count")
+    private Integer alreadyExamCount;
+    
 
     @ApiModelProperty(value = "当前考试记录ID")
     @TableField(value = "current_record_id")
@@ -183,14 +193,6 @@ public class TEExamStudent extends BaseEntity {
         this.parameter = parameter;
     }
 
-    public Integer getLeftExamCount() {
-        return leftExamCount;
-    }
-
-    public void setLeftExamCount(Integer leftExamCount) {
-        this.leftExamCount = leftExamCount;
-    }
-
     public Long getCurrentRecordId() {
         return currentRecordId;
     }
@@ -206,4 +208,14 @@ public class TEExamStudent extends BaseEntity {
     public void setSelectRecordId(Long selectRecordId) {
         this.selectRecordId = selectRecordId;
     }
+
+	public Integer getAlreadyExamCount() {
+		return alreadyExamCount;
+	}
+
+	public void setAlreadyExamCount(Integer alreadyExamCount) {
+		this.alreadyExamCount = alreadyExamCount;
+	}
+    
+    
 }

+ 86 - 10
themis-business/src/main/java/com/qmth/themis/business/entity/TOeExamRecord.java

@@ -64,9 +64,9 @@ public class TOeExamRecord implements Serializable {
     @TableField(value = "last_start_time")
     private Date lastStartTime;
 
-    @ApiModelProperty(value = "剩余断点续考次数")
-    @TableField(value = "left_break_resume_count")
-    private Integer leftBreakResumeCount;
+    @ApiModelProperty(value = "断点续考次数")
+    @TableField(value = "already_break_count")
+    private Integer alreadyBreakCount;
 
     @ApiModelProperty(value = "客户端当前IP地址")
     @TableField(value = "client_current_ip")
@@ -171,6 +171,31 @@ public class TOeExamRecord implements Serializable {
     @ApiModelProperty(value = "算分状态,never:从未算分,calculating:正在算分,finish:算分完成")
     @TableField(value = "score_status")
     private ScoreStatusEnum scoreStatus;
+    
+    
+    @ApiModelProperty(value = "考试开始时间")
+    @TableField(value = "start_time")
+    private Date startTime;
+    
+    @ApiModelProperty(value = "考试结束时间")
+    @TableField(value = "end_time")
+    private Date endTime;
+    
+    @ApiModelProperty(value = "允许开考开放时长,相当于迟到时间")
+    @TableField(value = "opening_seconds")
+    private Integer openingSeconds;
+    
+    @ApiModelProperty(value = "最短考试时长,相当于考试冻结时间")
+    @TableField(value = "min_duration_seconds")
+    private Integer minDurationSeconds;
+    
+    @ApiModelProperty(value = "最大考试时长")
+    @TableField(value = "max_duration_seconds")
+    private Integer maxDurationSeconds;
+    
+    @ApiModelProperty(value = "是否在结束时间集中强制收卷,0:不强制,1:强制")
+    @TableField(value = "force_finish")
+    private Integer forceFinish;
 
     public ScoreStatusEnum getScoreStatus() {
         return scoreStatus;
@@ -296,13 +321,6 @@ public class TOeExamRecord implements Serializable {
         this.lastStartTime = lastStartTime;
     }
 
-    public Integer getLeftBreakResumeCount() {
-        return leftBreakResumeCount;
-    }
-
-    public void setLeftBreakResumeCount(Integer leftBreakResumeCount) {
-        this.leftBreakResumeCount = leftBreakResumeCount;
-    }
 
     public String getClientCurrentIp() {
         return clientCurrentIp;
@@ -479,4 +497,62 @@ public class TOeExamRecord implements Serializable {
     public void setInProcessLivenessVerifyCount(Integer inProcessLivenessVerifyCount) {
         this.inProcessLivenessVerifyCount = inProcessLivenessVerifyCount;
     }
+
+	public Integer getAlreadyBreakCount() {
+		return alreadyBreakCount;
+	}
+
+	public void setAlreadyBreakCount(Integer alreadyBreakCount) {
+		this.alreadyBreakCount = alreadyBreakCount;
+	}
+
+	public Date getStartTime() {
+		return startTime;
+	}
+
+	public void setStartTime(Date startTime) {
+		this.startTime = startTime;
+	}
+
+	public Date getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(Date endTime) {
+		this.endTime = endTime;
+	}
+
+	public Integer getOpeningSeconds() {
+		return openingSeconds;
+	}
+
+	public void setOpeningSeconds(Integer openingSeconds) {
+		this.openingSeconds = openingSeconds;
+	}
+
+	public Integer getMinDurationSeconds() {
+		return minDurationSeconds;
+	}
+
+	public void setMinDurationSeconds(Integer minDurationSeconds) {
+		this.minDurationSeconds = minDurationSeconds;
+	}
+
+	public Integer getMaxDurationSeconds() {
+		return maxDurationSeconds;
+	}
+
+	public void setMaxDurationSeconds(Integer maxDurationSeconds) {
+		this.maxDurationSeconds = maxDurationSeconds;
+	}
+
+	public Integer getForceFinish() {
+		return forceFinish;
+	}
+
+	public void setForceFinish(Integer forceFinish) {
+		this.forceFinish = forceFinish;
+	}
+    
+    
 }

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

@@ -163,6 +163,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
     public List<TEExamDto> getWaitingExam(Long studentId, Long examId, Long orgId) {
         List<TEExamDto> list = teExamMapper.getWaitingExam(studentId, examId, orgId);
         if (Objects.nonNull(list) && list.size() > 0) {
+        	ExamCacheBean examCache=getExamCacheBean(examId);
             list.forEach(s -> {
                 List<TEExamActivityDto> teExamActivityList = teExamActivityService.getWaitingExam(studentId, s.getId(), s.getExamActivityId(), s.getMode());
                 teExamActivityList.forEach(v -> {
@@ -203,7 +204,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
                         v.setMonitorVideoSource(null);
                     }
                     ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(v.getExamStudentId());
-                    v.setLeftExamCount(examStudentCacheBean.getLeftExamCount());
+                    v.setLeftExamCount(examCache.getExamCount()-examStudentCacheBean.getAlreadyExamCount());
                 });
                 s.setActivities(teExamActivityList);
             });
@@ -226,6 +227,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         if (!studentId.equals(es.getStudentId())) {
             throw new BusinessException("考生Id和当前登录用户不一致");
         }
+        ExamCacheBean examCache=getExamCacheBean(es.getExamId());
         TEStudentCacheDto teStudentCacheDto = (TEStudentCacheDto) redisUtil.getStudent(es.getStudentId());
         if (teStudentCacheDto.getUnFinishedRecordId() != null) {
             Long recordId = teStudentCacheDto.getUnFinishedRecordId();
@@ -250,7 +252,7 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
 	            return prepare;
             }
         }
-        if (es.getLeftExamCount() == 0) {
+        if (examCache.getExamCount().intValue()<=es.getAlreadyExamCount().intValue()) {
             throw new BusinessException("没有剩余考试次数");
         }
         ExamCacheBean exam = getExamCacheBean(es.getExamId());
@@ -310,8 +312,8 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         Long recordId = toeExamRecordService.saveByPrepare(es.getExamId(), es.getExamActivityId(), examStudentId,
                 paperId, es.getCurrentSerialNumber());
 
-        Integer leftExamCount = es.getLeftExamCount();
-        es.setLeftExamCount(leftExamCount - 1);
+        Integer alreadyExamCount = es.getAlreadyExamCount();
+        es.setAlreadyExamCount(alreadyExamCount + 1);
 
         es.setCurrentRecordId(recordId);
 
@@ -332,14 +334,12 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
         prepare.setMonitorUserSig(tencentYunUtil.getSign(prepare.getMonitorUserId(), SystemConstant.TENCENT_EXPIRE_TIME));
 
         
-        Integer breakResumeCount = exam.getBreakResumeCount();
         // 更新考生缓存
         redisUtil.set(RedisKeyHelper.examStudentCacheKey(examStudentId), es);
         //更新场次-考试记录缓存
         ExamActivityRecordCacheUtil.setExamRecordStatus(activityId, recordId, ExamRecordCacheUtil.getStatus(recordId));
         teStudentCacheDto.setUnFinishedRecordId(recordId);
         redisUtil.setStudent(studentId, teStudentCacheDto);
-        ExamRecordCacheUtil.setLeftBreakResumeCount(recordId, breakResumeCount);
         //mq发送消息start
         MqDto mqDto = new MqDto(MqTopicEnum.themisTopic.getCode(), MqTagEnum.STUDENT.name(), SystemOperationEnum.PREPARE, MqTagEnum.STUDENT, String.valueOf(teStudentCacheDto.getId()), teStudentCacheDto.getIdentity());
         this.sendOeLogMessage(SystemOperationEnum.PREPARE, examStudentId, recordId, mqDto);

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

@@ -117,7 +117,7 @@ public class TEExamStudentServiceImpl extends ServiceImpl<TEExamStudentMapper, T
         ret.setCourseName(es.getCourseName());
         ret.setRoomCode(es.getRoomCode());
         ret.setRoomName(es.getRoomName());
-        ret.setLeftExamCount(es.getLeftExamCount());
+        ret.setAlreadyExamCount(es.getAlreadyExamCount());
         ret.setCurrentRecordId(es.getCurrentRecordId());
         ret.setCurrentSerialNumber(null);
         ret.setIdentity(es.getIdentity());

+ 21 - 0
themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeExamRecordServiceImpl.java

@@ -30,6 +30,7 @@ import com.qmth.themis.business.bean.backend.InvigilateListVideoBean;
 import com.qmth.themis.business.bean.backend.InvigilateListWarningBean;
 import com.qmth.themis.business.cache.ExamRecordCacheUtil;
 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.ExamStudentAnswerCacheBean;
 import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
@@ -52,6 +53,7 @@ import com.qmth.themis.business.enums.MqTopicEnum;
 import com.qmth.themis.business.enums.ObjectiveScorePolicyEnum;
 import com.qmth.themis.business.enums.VerifyExceptionEnum;
 import com.qmth.themis.business.service.MqDtoService;
+import com.qmth.themis.business.service.TEExamActivityService;
 import com.qmth.themis.business.service.TEExamPaperService;
 import com.qmth.themis.business.service.TEExamService;
 import com.qmth.themis.business.service.TEExamStudentService;
@@ -96,6 +98,9 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
 
     @Resource
     TEExamStudentService examStudentService;
+    
+    @Resource
+    TEExamActivityService examActivityService;
 
     /**
      * 获取考试未完列表
@@ -150,6 +155,8 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
     @Override
     public Long saveByPrepare(Long examId, Long examActivityId, Long examStudentId, Long paperId,
                               Integer serialNumber) {
+    	ExamActivityCacheBean ac=examActivityService.getExamActivityCacheBean(examActivityId);
+    	ExamCacheBean exam=examService.getExamCacheBean(examId);
         TOeExamRecord er = new TOeExamRecord();
         er.setId(Constants.idGen.next());
         er.setExamId(examId);
@@ -161,6 +168,13 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
         er.setStatus(ExamRecordStatusEnum.FIRST_PREPARE);
         er.setObjectiveScore(0.0);
         er.setMonitorKey(String.valueOf(redisUtil.getRedisSequence()));
+        er.setAlreadyBreakCount(0);
+        er.setStartTime(ac.getStartTime());
+        er.setEndTime(ac.getFinishTime());
+        er.setOpeningSeconds(ac.getOpeningSeconds());
+        er.setMinDurationSeconds(exam.getMinDurationSeconds());
+        er.setMaxDurationSeconds(ac.getMaxDurationSeconds());
+        er.setForceFinish(exam.getForceFinish());
         Map<String, Object> map = SimpleBeanUtil.objectToMap(er);
         redisUtil.setForHash(RedisKeyHelper.examRecordCacheKey(er.getId()), map);
         dataInitMq(map);
@@ -434,6 +448,13 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
         tr.setFirstPrepareTime(new Date((Long) param.get("firstPrepareTime")));
         tr.setStatus(ExamRecordStatusEnum.valueOf((String) param.get("status")));
         tr.setObjectiveScore(0.0);
+        tr.setAlreadyBreakCount(0);
+        tr.setStartTime(new Date((Long) param.get("startTime")));
+        tr.setEndTime(new Date((Long) param.get("endTime")));
+        tr.setOpeningSeconds((Integer) param.get("openingSeconds"));
+        tr.setMinDurationSeconds((Integer) param.get("minDurationSeconds"));
+        tr.setMaxDurationSeconds((Integer) param.get("maxDurationSeconds"));
+        tr.setForceFinish((Integer) param.get("forceFinish"));
         saveOrUpdate(tr);
     }
 

+ 2 - 2
themis-business/src/main/java/com/qmth/themis/business/templete/service/impl/TempleteLogicServiceImpl.java

@@ -163,7 +163,7 @@ public class TempleteLogicServiceImpl implements TempleteLogicService {
                         teExamStudent.setCreateId(createId);
                         teExamStudent.setCourseCode(examStudentImportDto.getCourseCode());
                         teExamStudent.setCourseName(examStudentImportDto.getCourseName());
-                        teExamStudent.setLeftExamCount(examCount);
+                        teExamStudent.setAlreadyExamCount(0);
                     } else {
                         teExamStudent.setUpdateId(createId);
                         teExamStudent.setName(examStudentImportDto.getName());
@@ -175,7 +175,7 @@ public class TempleteLogicServiceImpl implements TempleteLogicService {
                         teExamStudent.setCourseCode(examStudentImportDto.getCourseCode());
                         teExamStudent.setCourseName(examStudentImportDto.getCourseName());
                         teExamStudent.setExamActivityId(teExamActivity.getId());
-                        teExamStudent.setLeftExamCount(examCount);
+                        teExamStudent.setAlreadyExamCount(0);
 
                         UpdateWrapper<TEStudent> teStudentUpdateWrapper = new UpdateWrapper<>();
                         teStudentUpdateWrapper.lambda().set(TEStudent::getName, examStudentImportDto.getName())

+ 8 - 2
themis-business/src/main/resources/db/init.sql

@@ -1121,7 +1121,7 @@ CREATE TABLE `t_e_exam_student` (
   `identity` varchar(50) NOT NULL COMMENT '证件号,机构内唯一',
   `name` varchar(50) NOT NULL COMMENT '姓名',
   `parameter` varchar(1000) DEFAULT NULL COMMENT '扩展字段',
-  `left_exam_count` int DEFAULT NULL COMMENT '剩余考试次数',
+  `already_exam_count` int DEFAULT NULL COMMENT '已考考试次数',
   `current_record_id` bigint DEFAULT NULL COMMENT '当前考试记录ID',
   `select_record_id` bigint DEFAULT NULL COMMENT '最终生效的记录ID',
   `room_name` varchar(100) DEFAULT NULL COMMENT '考场名称',
@@ -1468,7 +1468,7 @@ CREATE TABLE `t_oe_exam_record` (
   `last_break_time` timestamp NULL COMMENT '最近断点时间',
   `last_prepare_time` timestamp NULL COMMENT '最近恢复候考时间',
   `last_start_time` timestamp NULL COMMENT '最近恢复开考时间',
-  `left_break_resume_count` int DEFAULT NULL COMMENT '剩余断点续考次数',
+  `already_break_count` int DEFAULT NULL COMMENT '已断点续考次数',
   `client_current_ip` varchar(30) DEFAULT NULL COMMENT '客户端当前IP地址',
   `client_websocket_status` varchar(30) DEFAULT NULL COMMENT '客户端websocket状态',
   `client_websocket_id` varchar(50) DEFAULT NULL COMMENT '客户端websocket连接标识',
@@ -1495,6 +1495,12 @@ CREATE TABLE `t_oe_exam_record` (
   `monitor_status_source` varchar(30) DEFAULT NULL COMMENT '监控状态,stop:停止,start:正常',
   `monitor_live_url` varchar(100) DEFAULT NULL COMMENT '监控观看地址',
   `score_status` varchar(30) DEFAULT NULL COMMENT '算分状态,never:从未算分,calculating:正在算分,finish:算分完成',
+  `start_time` timestamp NULL COMMENT '考试开始时间',
+  `end_time` timestamp NULL COMMENT '考试结束时间',
+  `opening_seconds` int DEFAULT NULL COMMENT '允许开考开放时长,相当于迟到时间',
+  `min_duration_seconds` int DEFAULT NULL COMMENT '最短考试时长,相当于考试冻结时间',
+  `max_duration_seconds` int DEFAULT NULL COMMENT '最大考试时长',
+  `force_finish` int DEFAULT NULL COMMENT '是否在结束时间集中强制收卷,0:不强制,1:强制',
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='考试记录';
 

+ 6 - 3
themis-exam/src/main/java/com/qmth/themis/exam/api/TEExamController.java

@@ -5,6 +5,7 @@ import com.qmth.themis.business.annotation.ApiJsonObject;
 import com.qmth.themis.business.annotation.ApiJsonProperty;
 import com.qmth.themis.business.bean.exam.*;
 import com.qmth.themis.business.cache.ExamRecordCacheUtil;
+import com.qmth.themis.business.cache.bean.ExamCacheBean;
 import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
 import com.qmth.themis.business.constant.SystemConstant;
 import com.qmth.themis.business.dto.MqDto;
@@ -128,17 +129,19 @@ public class TEExamController {
                 Long recordId = param.getRecordId();
                 Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
                 ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
-                Integer leftBreakResumeCount = Objects.isNull(ExamRecordCacheUtil.getLeftBreakResumeCount(recordId)) ? 0 : ExamRecordCacheUtil.getLeftBreakResumeCount(recordId);
-                leftBreakResumeCount--;
+                ExamCacheBean exam=teExamService.getExamCacheBean(examStudentCacheBean.getExamId());
+                Integer alreadyBreakCount=ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
+                Integer leftBreakResumeCount = exam.getBreakResumeCount()-alreadyBreakCount;
                 leftBreakResumeCount = leftBreakResumeCount <= 0 ? 0 : leftBreakResumeCount;
                 if (leftBreakResumeCount <= 0) {
                     Integer durationSeconds = Objects.isNull(ExamRecordCacheUtil.getDurationSeconds(recordId)) ? 0 : ExamRecordCacheUtil.getDurationSeconds(recordId);
                     teExamService.finish(examStudentCacheBean.getStudentId(), recordId, FinishTypeEnum.AUTO.name(), durationSeconds);
                 } else {
+                	alreadyBreakCount++;
                     ExamRecordCacheUtil.setLastBreakId(param.getRecordId(), Constants.idGen.next());
                     ExamRecordCacheUtil.setStatus(recordId, ExamRecordStatusEnum.RESUME_PREPARE);
                     ExamRecordCacheUtil.setLastBreakTime(recordId);
-                    ExamRecordCacheUtil.setLeftBreakResumeCount(recordId, leftBreakResumeCount);
+                    ExamRecordCacheUtil.setAlreadyBreakCount(recordId, alreadyBreakCount);
                     ExamRecordCacheUtil.setLastStartTime(recordId);
                     //考试断点异常原因 发送mq start
                     MqDto mqDto = new MqDto(MqTopicEnum.themisTopic.getCode(), MqTagEnum.EXCEPTION_LOG.name(), JacksonUtil.parseJson(param), MqTagEnum.EXCEPTION_LOG, String.valueOf(param.getRecordId()), param.getReason());

+ 3 - 1
themis-exam/src/main/java/com/qmth/themis/exam/api/TEStudentController.java

@@ -217,7 +217,9 @@ public class TEStudentController {
             //获取最近同步时间
             Date clientLastSyncTime = ExamRecordCacheUtil.getClientLastSyncTime(recordId);
             //获取剩余断点次数
-            Integer leftBreakResumeCount = Objects.isNull(ExamRecordCacheUtil.getLeftBreakResumeCount(recordId)) ? 0 : ExamRecordCacheUtil.getLeftBreakResumeCount(recordId);
+            Integer alreadyBreakCount=ExamRecordCacheUtil.getAlreadyBreakCount(recordId);
+            Integer leftBreakResumeCount = ec.getBreakResumeCount()-alreadyBreakCount;
+            
             //如果断点时间大于整体断点时间,则强制交卷
             if (Objects.equals(status, ExamRecordStatusEnum.ANSWERING) || Objects.equals(status, ExamRecordStatusEnum.BREAK_OFF) || Objects.equals(status, ExamRecordStatusEnum.RESUME_PREPARE)) {
                 if (Objects.nonNull(clientLastSyncTime) && (System.currentTimeMillis() - clientLastSyncTime.getTime()) / 1000 > breakExpireSeconds) {