|
@@ -32,6 +32,7 @@ import cn.com.qmth.examcloud.core.oe.student.dao.ExamContinuedRecordRepo;
|
|
import cn.com.qmth.examcloud.core.oe.student.dao.ExamRecordDataRepo;
|
|
import cn.com.qmth.examcloud.core.oe.student.dao.ExamRecordDataRepo;
|
|
import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamContinuedRecordEntity;
|
|
import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamContinuedRecordEntity;
|
|
import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamRecordDataEntity;
|
|
import cn.com.qmth.examcloud.core.oe.student.dao.entity.ExamRecordDataEntity;
|
|
|
|
+import cn.com.qmth.examcloud.core.oe.student.report.ExamProcessRecordReport;
|
|
import cn.com.qmth.examcloud.core.oe.student.service.*;
|
|
import cn.com.qmth.examcloud.core.oe.student.service.*;
|
|
import cn.com.qmth.examcloud.core.oe.task.api.ExamCaptureCloudService;
|
|
import cn.com.qmth.examcloud.core.oe.task.api.ExamCaptureCloudService;
|
|
import cn.com.qmth.examcloud.core.oe.task.api.request.SaveExamCaptureSyncCompareResultReq;
|
|
import cn.com.qmth.examcloud.core.oe.task.api.request.SaveExamCaptureSyncCompareResultReq;
|
|
@@ -51,10 +52,7 @@ import cn.com.qmth.examcloud.reports.commons.util.ReportsUtil;
|
|
import cn.com.qmth.examcloud.support.Constants;
|
|
import cn.com.qmth.examcloud.support.Constants;
|
|
import cn.com.qmth.examcloud.support.cache.CacheHelper;
|
|
import cn.com.qmth.examcloud.support.cache.CacheHelper;
|
|
import cn.com.qmth.examcloud.support.cache.bean.*;
|
|
import cn.com.qmth.examcloud.support.cache.bean.*;
|
|
-import cn.com.qmth.examcloud.support.enums.ExamProperties;
|
|
|
|
-import cn.com.qmth.examcloud.support.enums.ExamRecordStatus;
|
|
|
|
-import cn.com.qmth.examcloud.support.enums.FaceBiopsyScheme;
|
|
|
|
-import cn.com.qmth.examcloud.support.enums.HandInExamType;
|
|
|
|
|
|
+import cn.com.qmth.examcloud.support.enums.*;
|
|
import cn.com.qmth.examcloud.support.examing.*;
|
|
import cn.com.qmth.examcloud.support.examing.*;
|
|
import cn.com.qmth.examcloud.support.helper.ExamCacheTransferHelper;
|
|
import cn.com.qmth.examcloud.support.helper.ExamCacheTransferHelper;
|
|
import cn.com.qmth.examcloud.support.helper.FaceBiopsyHelper;
|
|
import cn.com.qmth.examcloud.support.helper.FaceBiopsyHelper;
|
|
@@ -161,7 +159,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
|
|
|
@Transactional
|
|
@Transactional
|
|
@Override
|
|
@Override
|
|
- public StartExamInfo startExam(Long examStudentId, User user) {
|
|
|
|
|
|
+ public StartExamInfo startExam(Long examStudentId, User user, String ip) {
|
|
// 开考预处理
|
|
// 开考预处理
|
|
prepare4Exam(examStudentId, user);
|
|
prepare4Exam(examStudentId, user);
|
|
|
|
|
|
@@ -304,7 +302,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
}
|
|
}
|
|
|
|
|
|
//设置并保存上次活动时间
|
|
//设置并保存上次活动时间
|
|
- setAndSaveActiveTime(examRecordData.getId());
|
|
|
|
|
|
+ setAndSaveActiveTime(examRecordData.getId(), ip);
|
|
|
|
|
|
if (log.isDebugEnabled()) {
|
|
if (log.isDebugEnabled()) {
|
|
log.debug("10 合计 耗时:" + (System.currentTimeMillis() - st) + " ms");
|
|
log.debug("10 合计 耗时:" + (System.currentTimeMillis() - st) + " ms");
|
|
@@ -312,6 +310,11 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
// 在线考生开考打点
|
|
// 在线考生开考打点
|
|
ReportsUtil.report(
|
|
ReportsUtil.report(
|
|
new OnlineExamStudentReport(user.getRootOrgId(), user.getUserId(), examBean.getId(), examStudentId));
|
|
new OnlineExamStudentReport(user.getRootOrgId(), user.getUserId(), examBean.getId(), examStudentId));
|
|
|
|
+ //考试过程记录(开考)打点
|
|
|
|
+ ReportsUtil.report(
|
|
|
|
+ new ExamProcessRecordReport(examRecordData.getId(), ExamProcess.START, examRecordData.getEnterExamTime())
|
|
|
|
+ );
|
|
|
|
+
|
|
StartExamInfo startExamInfo = buildStartExamInfo(examRecordData.getId(), examingSession, examBean, courseBean);
|
|
StartExamInfo startExamInfo = buildStartExamInfo(examRecordData.getId(), examingSession, examBean, courseBean);
|
|
return startExamInfo;
|
|
return startExamInfo;
|
|
|
|
|
|
@@ -615,7 +618,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
*/
|
|
*/
|
|
@Transactional
|
|
@Transactional
|
|
@Override
|
|
@Override
|
|
- public void handInExam(Long examRecordDataId, HandInExamType handInExamType) {
|
|
|
|
|
|
+ public void handInExam(Long examRecordDataId, HandInExamType handInExamType, String ip) {
|
|
// 此锁是为了避免自动交卷服务与断点续考交卷或活检失败交卷,同一时刻交卷争抢资源导致死锁
|
|
// 此锁是为了避免自动交卷服务与断点续考交卷或活检失败交卷,同一时刻交卷争抢资源导致死锁
|
|
String sequenceLockKey = Constants.HAND_IN_EXAM_LOCK_PREFIX + examRecordDataId;
|
|
String sequenceLockKey = Constants.HAND_IN_EXAM_LOCK_PREFIX + examRecordDataId;
|
|
// 系统在请求结束后会,自动释放锁,无需手动解锁
|
|
// 系统在请求结束后会,自动释放锁,无需手动解锁
|
|
@@ -649,6 +652,8 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
log.error("[HAND_IN_EXAM-" + examRecordDataId + "]更新照片优先级时,出现异常", e);
|
|
log.error("[HAND_IN_EXAM-" + examRecordDataId + "]更新照片优先级时,出现异常", e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ setAndSaveActiveTime(examRecordDataId, ip);
|
|
} else if (handInExamType == HandInExamType.AUTO) {
|
|
} else if (handInExamType == HandInExamType.AUTO) {
|
|
examRecordData.setExamRecordStatus(ExamRecordStatus.EXAM_AUTO_HAND_IN);
|
|
examRecordData.setExamRecordStatus(ExamRecordStatus.EXAM_AUTO_HAND_IN);
|
|
examRecordData.setCleanTime(new Date());
|
|
examRecordData.setCleanTime(new Date());
|
|
@@ -657,15 +662,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
}
|
|
}
|
|
|
|
|
|
//交卷时,落地最近的上次活动时间字段
|
|
//交卷时,落地最近的上次活动时间字段
|
|
- String examingActiveTimeKey = RedisKeyHelper.getBuilder()
|
|
|
|
- .examingActiveTimeKey(examRecordDataId);
|
|
|
|
- ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
|
|
|
|
- ExamingActivityTime.class);
|
|
|
|
-
|
|
|
|
- long activeTime = null == examingActiveTime
|
|
|
|
- ? System.currentTimeMillis()
|
|
|
|
- : examingActiveTime.getActiveTime();
|
|
|
|
- examRecordData.setLastActiveTime(new Date(activeTime));
|
|
|
|
|
|
+ examRecordData.setLastActiveTime(new Date(getExamingActivityTime(examRecordDataId).getActiveTime()));
|
|
|
|
|
|
//特殊处理:如果考试类型为 在线练习,则需要将部分数据提前入库,并更新相关状态
|
|
//特殊处理:如果考试类型为 在线练习,则需要将部分数据提前入库,并更新相关状态
|
|
if (ExamType.PRACTICE == examRecordData.getExamType()) {
|
|
if (ExamType.PRACTICE == examRecordData.getExamType()) {
|
|
@@ -698,6 +695,11 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
|
|
|
// 删除redis会话
|
|
// 删除redis会话
|
|
examingSessionService.deleteExamingSession(studentId);
|
|
examingSessionService.deleteExamingSession(studentId);
|
|
|
|
+
|
|
|
|
+ //考试过程记录(交卷)打点
|
|
|
|
+ ReportsUtil.report(new ExamProcessRecordReport(examRecordDataId,
|
|
|
|
+ (HandInExamType.MANUAL == handInExamType ? ExamProcess.MANUAL_HAND_IN : ExamProcess.AUTO_HAND_IN),
|
|
|
|
+ new Date()));
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -1430,7 +1432,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public CheckExamInProgressInfo checkExamInProgress(Long studentId) {
|
|
|
|
|
|
+ public CheckExamInProgressInfo checkExamInProgress(Long studentId, String ip) {
|
|
ExamingSession examSessionInfo = examingSessionService.getExamingSession(studentId);
|
|
ExamingSession examSessionInfo = examingSessionService.getExamingSession(studentId);
|
|
if (examSessionInfo == null || ExamingStatus.INFORMAL.equals(examSessionInfo.getExamingStatus())) {
|
|
if (examSessionInfo == null || ExamingStatus.INFORMAL.equals(examSessionInfo.getExamingStatus())) {
|
|
return null;
|
|
return null;
|
|
@@ -1495,6 +1497,17 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
}
|
|
}
|
|
|
|
|
|
checkExamInProgressInfo.setFaceVerifyMinute(faceVerifyMinute);
|
|
checkExamInProgressInfo.setFaceVerifyMinute(faceVerifyMinute);
|
|
|
|
+
|
|
|
|
+ //考试过程记录(断点)打点
|
|
|
|
+ ExamingActivityTime lastExamingActivityTime = getExamingActivityTime(examingRecord.getId());
|
|
|
|
+ ReportsUtil.report(
|
|
|
|
+ new ExamProcessRecordReport(examingRecord.getId(), ExamProcess.BREAK_OFF, new Date(lastExamingActivityTime.getActiveTime()))
|
|
|
|
+ );
|
|
|
|
+ //考试过程记录(断点续考)打点
|
|
|
|
+ ReportsUtil.report(new ExamProcessRecordReport(examingRecord.getId(), ExamProcess.CONTINUE, new Date()));
|
|
|
|
+
|
|
|
|
+ setAndSaveActiveTime(examingRecord.getId(), ip);
|
|
|
|
+
|
|
return checkExamInProgressInfo;
|
|
return checkExamInProgressInfo;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -1533,14 +1546,9 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
return null;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
- String examingActiveTimeKey = RedisKeyHelper.getBuilder()
|
|
|
|
- .examingActiveTimeKey(examSessionInfo.getExamRecordDataId());
|
|
|
|
- ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
|
|
|
|
- ExamingActivityTime.class);
|
|
|
|
|
|
+ ExamingActivityTime examingActiveTime = getExamingActivityTime(examSessionInfo.getExamRecordDataId());
|
|
|
|
|
|
- long activeTime = null == examingActiveTime
|
|
|
|
- ? System.currentTimeMillis()
|
|
|
|
- : examingActiveTime.getActiveTime();
|
|
|
|
|
|
+ long activeTime = examingActiveTime.getActiveTime();
|
|
|
|
|
|
// 如果已经过了断点续考时间,自动交卷
|
|
// 如果已经过了断点续考时间,自动交卷
|
|
long now = System.currentTimeMillis();
|
|
long now = System.currentTimeMillis();
|
|
@@ -1559,7 +1567,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
*/
|
|
*/
|
|
private void delayHandInExamIfLocked(Long examRecordDataId) {
|
|
private void delayHandInExamIfLocked(Long examRecordDataId) {
|
|
try {
|
|
try {
|
|
- handInExam(examRecordDataId, HandInExamType.AUTO);
|
|
|
|
|
|
+ handInExam(examRecordDataId, HandInExamType.AUTO, null);
|
|
} catch (SequenceLockException e) {
|
|
} catch (SequenceLockException e) {
|
|
// 如果发现自动服务正在交卷,则重试1500毫秒获取考试记录状态,判断是否已交卷
|
|
// 如果发现自动服务正在交卷,则重试1500毫秒获取考试记录状态,判断是否已交卷
|
|
int loopTimes = 0;
|
|
int loopTimes = 0;
|
|
@@ -1603,7 +1611,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
* @param user 学生
|
|
* @param user 学生
|
|
*/
|
|
*/
|
|
@Override
|
|
@Override
|
|
- public long examHeartbeat(User user) {
|
|
|
|
|
|
+ public long examHeartbeat(User user, String ip) {
|
|
Long studentId = user.getUserId();
|
|
Long studentId = user.getUserId();
|
|
ExamingSession examSessionInfo = examingSessionService.getExamingSession(studentId);
|
|
ExamingSession examSessionInfo = examingSessionService.getExamingSession(studentId);
|
|
if (examSessionInfo == null
|
|
if (examSessionInfo == null
|
|
@@ -1662,7 +1670,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
examingHeartbeat.setCost(usedExamSeconds);
|
|
examingHeartbeat.setCost(usedExamSeconds);
|
|
redisClient.set(examingHeartbeatKey, examingHeartbeat);//更新心跳缓存
|
|
redisClient.set(examingHeartbeatKey, examingHeartbeat);//更新心跳缓存
|
|
|
|
|
|
- setAndSaveActiveTime(examSessionInfo.getExamRecordDataId());
|
|
|
|
|
|
+ setAndSaveActiveTime(examSessionInfo.getExamRecordDataId(), ip);
|
|
|
|
|
|
// 在线考生心跳打点
|
|
// 在线考生心跳打点
|
|
ReportsUtil.report(new OnlineExamStudentReport(user.getRootOrgId(), user.getUserId(),
|
|
ReportsUtil.report(new OnlineExamStudentReport(user.getRootOrgId(), user.getUserId(),
|
|
@@ -1677,7 +1685,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
*
|
|
*
|
|
* @param examRecrodDataId 考试记录id
|
|
* @param examRecrodDataId 考试记录id
|
|
*/
|
|
*/
|
|
- private void setAndSaveActiveTime(Long examRecrodDataId) {
|
|
|
|
|
|
+ private void setAndSaveActiveTime(Long examRecrodDataId, String ip) {
|
|
String examingActiveTimeKey = RedisKeyHelper.getBuilder()
|
|
String examingActiveTimeKey = RedisKeyHelper.getBuilder()
|
|
.examingActiveTimeKey(examRecrodDataId);
|
|
.examingActiveTimeKey(examRecrodDataId);
|
|
ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
|
|
ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
|
|
@@ -1686,11 +1694,41 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
examingActiveTime = new ExamingActivityTime();
|
|
examingActiveTime = new ExamingActivityTime();
|
|
examingActiveTime.setExamRecordDataId(examRecrodDataId);
|
|
examingActiveTime.setExamRecordDataId(examRecrodDataId);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ Long now = System.currentTimeMillis();
|
|
|
|
+
|
|
|
|
+ //ip如果发生变更,则记录ip变更记录
|
|
|
|
+ String lastIp = examingActiveTime.getRealIp();
|
|
|
|
+ if (StringUtils.isNotEmpty(ip) && !ip.equals(lastIp)) {
|
|
|
|
+ ReportsUtil.report(
|
|
|
|
+ new ExamProcessRecordReport(examRecrodDataId, ExamProcess.IP_CHANGE, new Date(now))
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
examingActiveTime.setActiveTime(System.currentTimeMillis());
|
|
examingActiveTime.setActiveTime(System.currentTimeMillis());
|
|
|
|
+ examingActiveTime.setRealIp(ip);
|
|
|
|
|
|
redisClient.set(examingActiveTimeKey, examingActiveTime);
|
|
redisClient.set(examingActiveTimeKey, examingActiveTime);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 获取上次活动缓存
|
|
|
|
+ *
|
|
|
|
+ * @param examRecrodDataId
|
|
|
|
+ * @return
|
|
|
|
+ */
|
|
|
|
+ private ExamingActivityTime getExamingActivityTime(Long examRecrodDataId) {
|
|
|
|
+ String examingActiveTimeKey = RedisKeyHelper.getBuilder()
|
|
|
|
+ .examingActiveTimeKey(examRecrodDataId);
|
|
|
|
+ ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey, ExamingActivityTime.class);
|
|
|
|
+ if (null == examingActiveTime) {
|
|
|
|
+ examingActiveTime = new ExamingActivityTime();
|
|
|
|
+ examingActiveTime.setExamRecordDataId(examRecrodDataId);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return examingActiveTime;
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* 计算考试时长 校验是否达到冻结时间
|
|
* 计算考试时长 校验是否达到冻结时间
|
|
*
|
|
*
|