|
@@ -18,6 +18,10 @@ import java.util.TimeZone;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
+import cn.com.qmth.examcloud.core.oe.admin.api.ExamRecordCloudService;
|
|
|
+import cn.com.qmth.examcloud.core.oe.admin.api.request.GetPartialExamRecordDataReq;
|
|
|
+import cn.com.qmth.examcloud.core.oe.admin.api.response.GetPartialExamRecordDataResp;
|
|
|
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
|
|
|
import org.apache.commons.collections.CollectionUtils;
|
|
|
import org.apache.commons.lang.math.RandomUtils;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
@@ -160,6 +164,9 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
@Autowired
|
|
|
private WsCloudService wsCloudService;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ ExamRecordCloudService examRecordCloudService;
|
|
|
+
|
|
|
private static final String SEPARATOR = "/";
|
|
|
|
|
|
private static final String UNDERLINE = "_";
|
|
@@ -424,7 +431,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
examingSession.setCourseCode(courseCacheBean.getCode());
|
|
|
examingSession.setCourseId(courseCacheBean.getId());
|
|
|
examingSession.setCreationTime(new Date());
|
|
|
- examingSession.setExamDuration((long) examSettingsCacheBean.getDuration()*60*1000);
|
|
|
+ examingSession.setExamDuration((long) examSettingsCacheBean.getDuration() * 60 * 1000);
|
|
|
examingSession.setExamId(examSettingsCacheBean.getId());
|
|
|
examingSession.setExamingStatus(ExamingStatus.INFORMAL);
|
|
|
|
|
@@ -458,10 +465,8 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
/**
|
|
|
* 交卷
|
|
|
*
|
|
|
- * @param examRecordDataId
|
|
|
- * 考试记录id
|
|
|
- * @param handInExamType
|
|
|
- * 交卷类型
|
|
|
+ * @param examRecordDataId 考试记录id
|
|
|
+ * @param handInExamType 交卷类型
|
|
|
*/
|
|
|
@Transactional
|
|
|
@Override
|
|
@@ -619,20 +624,16 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
/**
|
|
|
* 发送消息到websocket
|
|
|
*
|
|
|
- * @param examRecordDataId
|
|
|
- * 考试记录id
|
|
|
- * @param order
|
|
|
- * 题序号
|
|
|
- * @param fileUrl
|
|
|
- * 文件路径
|
|
|
- * @param transferFileType
|
|
|
- * 传输文件类型
|
|
|
+ * @param examRecordDataId 考试记录id
|
|
|
+ * @param order 题序号
|
|
|
+ * @param fileUrl 文件路径
|
|
|
+ * @param transferFileType 传输文件类型
|
|
|
* @param userId
|
|
|
* @throws Exception
|
|
|
*/
|
|
|
@Override
|
|
|
public void sendFileAnswerToWebSocket(Long examRecordDataId, Integer order, String fileUrl, String transferFileType,
|
|
|
- Long userId, Long rootOrgId) throws Exception {
|
|
|
+ Long userId, Long rootOrgId) throws Exception {
|
|
|
Map<String, Object> data = new HashMap<String, Object>();
|
|
|
data.put("examRecordDataId", examRecordDataId);
|
|
|
data.put("order", order);
|
|
@@ -673,7 +674,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
*/
|
|
|
@Override
|
|
|
public void sendScanQrCodeToWebSocket(String clientId, Long examRecordDataId, Integer order, Long userId,
|
|
|
- Long rootOrgId) throws Exception {
|
|
|
+ Long rootOrgId) throws Exception {
|
|
|
Map<String, Object> data = new HashMap<String, Object>();
|
|
|
data.put("examRecordDataId", examRecordDataId);
|
|
|
data.put("order", order);
|
|
@@ -753,14 +754,14 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
throw new StatusException("100002", "登录信息错误");
|
|
|
}
|
|
|
|
|
|
- //考生id为空时,需要给考生id重新赋值
|
|
|
- if (req.getExamStudentId() == null) {
|
|
|
- ExamRecordData examRecordData = examRecordDataService.getExamRecordDataCache(req.getExamRecordDataId());
|
|
|
- req.setExamStudentId(examRecordData.getExamStudentId());
|
|
|
- }
|
|
|
-
|
|
|
// 如果是调用环境监测的接口,不用做如下校验
|
|
|
if (!req.isTestEnv()) {
|
|
|
+ //考生id为空时,需要给考生id重新赋值
|
|
|
+ if (req.getExamStudentId() == null) {
|
|
|
+ ExamRecordData examRecordData = examRecordDataService.getExamRecordDataCache(req.getExamRecordDataId());
|
|
|
+ req.setExamStudentId(examRecordData.getExamStudentId());
|
|
|
+ }
|
|
|
+
|
|
|
ExamingSession examSessionInfo = examingSessionService.getExamingSession(user.getUserId());
|
|
|
|
|
|
ExamStudentCacheBean examStudent = CacheHelper.getExamStudent(req.getExamStudentId());
|
|
@@ -779,6 +780,40 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ //环境监测时,需要特殊处理考试记录和orderId
|
|
|
+ else {
|
|
|
+
|
|
|
+ //考试记录id
|
|
|
+ SysPropertyCacheBean examRecordDataIdObject = CacheHelper.getSysProperty("oe.testDev.examRecordDataId");
|
|
|
+ //题号id
|
|
|
+ SysPropertyCacheBean orderObject = CacheHelper.getSysProperty("oe.testDev.order");
|
|
|
+
|
|
|
+ Long examRecordDataId;
|
|
|
+ if (!examRecordDataIdObject.getHasValue()) {
|
|
|
+ throw new StatusException("100014", "未找到环境监测的考试记录id");
|
|
|
+ } else {
|
|
|
+ examRecordDataId = Long.valueOf(examRecordDataIdObject.getValue().toString());
|
|
|
+ req.setExamRecordDataId(examRecordDataId);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!orderObject.getHasValue()) {
|
|
|
+ throw new StatusException("100015", "未找到环境监测的小题序号");
|
|
|
+ } else {
|
|
|
+ req.setOrder(Integer.parseInt((orderObject).getValue().toString()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 环境检测时,需要重新给考生id赋值
|
|
|
+ GetPartialExamRecordDataReq gedReq = new GetPartialExamRecordDataReq();
|
|
|
+ gedReq.setExamRecordDataId(examRecordDataId);
|
|
|
+ GetPartialExamRecordDataResp gedResp = examRecordCloudService.getPartialExamRecordData(gedReq);
|
|
|
+
|
|
|
+ if (null == gedResp.getExamStudentId()) {
|
|
|
+ throw new StatusException("100016", "环境监测中配置的考试记录id不正确");
|
|
|
+ }
|
|
|
+
|
|
|
+ req.setExamStudentId(gedResp.getExamStudentId());
|
|
|
+ }
|
|
|
+
|
|
|
String key = user.getKey();
|
|
|
StringBuffer param = new StringBuffer();
|
|
|
String transferFileType = StringUtils.isBlank(req.getTransferFileType()) ? "" : req.getTransferFileType();
|
|
@@ -804,16 +839,11 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @param bucketName
|
|
|
- * //不能为空
|
|
|
- * @param expiration
|
|
|
- * //不能为空
|
|
|
- * @param filePath
|
|
|
- * //不能为空
|
|
|
- * @param date
|
|
|
- * 为空时,以又怕云时间和expiration比较,不为空时以此date和expiration比较
|
|
|
- * @param md5
|
|
|
- * //可以为空
|
|
|
+ * @param bucketName //不能为空
|
|
|
+ * @param expiration //不能为空
|
|
|
+ * @param filePath //不能为空
|
|
|
+ * @param date 为空时,以又怕云时间和expiration比较,不为空时以此date和expiration比较
|
|
|
+ * @param md5 //可以为空
|
|
|
* @return
|
|
|
*/
|
|
|
private String policy(String bucketName, Long expiration, String filePath, Date date, String md5) {
|
|
@@ -855,7 +885,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
* @throws UpException
|
|
|
*/
|
|
|
private String sign(String method, String date, String bucketName, String policy, String userName, String password,
|
|
|
- String md5) throws UpException {
|
|
|
+ String md5) throws UpException {
|
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
String sp = "&";
|
|
@@ -1003,10 +1033,8 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
/**
|
|
|
* 获取试卷结构 小题乱序、选项乱序
|
|
|
*
|
|
|
- * @param extractConfig
|
|
|
- * 调卷规则对象
|
|
|
- * @param paperStruct
|
|
|
- * 试卷结构对象
|
|
|
+ * @param extractConfig 调卷规则对象
|
|
|
+ * @param paperStruct 试卷结构对象
|
|
|
*/
|
|
|
private void reorderPaperStruct(ExtractConfigCacheBean extractConfig, ExtractConfigPaperCacheBean paperStruct) {
|
|
|
// 小题乱序
|
|
@@ -1091,7 +1119,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
}
|
|
|
|
|
|
private StartExamInfo buildStartExamInfo(Long examRecordDataId, ExamingSession examingSession,
|
|
|
- ExamSettingsCacheBean examBean, CourseCacheBean courseBean) {
|
|
|
+ ExamSettingsCacheBean examBean, CourseCacheBean courseBean) {
|
|
|
StartExamInfo startExamInfo = new StartExamInfo();
|
|
|
startExamInfo.setExamRecordDataId(examRecordDataId);
|
|
|
startExamInfo.setCourseName(courseBean.getName());
|
|
@@ -1138,7 +1166,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
* @param examBean
|
|
|
*/
|
|
|
public void initializeExamRecordSession(ExamingSession examSessionInfo, ExamRecordData examRecordData,
|
|
|
- final ExamSettingsCacheBean examBean) {
|
|
|
+ final ExamSettingsCacheBean examBean) {
|
|
|
examSessionInfo.setExamRecordDataId(examRecordData.getId());
|
|
|
examSessionInfo.setStartTime(examRecordData.getStartTime().getTime());
|
|
|
examSessionInfo.setExamType(examBean.getExamType());
|
|
@@ -1196,12 +1224,12 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
// 更新考试中的断点续考属性
|
|
|
examRecordDataService.saveExamRecordDataCache(examingRecord.getId(), examingRecord);
|
|
|
|
|
|
- String examingHeartbeatKey = RedisKeyHelper.getBuilder()
|
|
|
- .examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
|
|
|
- ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
|
|
|
- ExamingHeartbeat.class);
|
|
|
+ String examingHeartbeatKey = RedisKeyHelper.getBuilder()
|
|
|
+ .examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
|
|
|
+ ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
|
|
|
+ ExamingHeartbeat.class);
|
|
|
|
|
|
- long usedTime = null == examingHeartbeat ? 0 : examingHeartbeat.getCost();
|
|
|
+ long usedTime = null == examingHeartbeat ? 0 : examingHeartbeat.getCost();
|
|
|
|
|
|
checkExamInProgressInfo.setExamRecordDataId(examingRecord.getId());
|
|
|
checkExamInProgressInfo.setExamId(examSessionInfo.getExamId());
|
|
@@ -1219,7 +1247,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
} else {// 非新活检,默认使用旧的活检计算方式
|
|
|
faceVerifyMinute = examFaceLivenessVerifyService.getFaceLivenessVerifyMinute(
|
|
|
examSessionInfo.getRootOrgId(), examSessionInfo.getOrgId(), examSessionInfo.getExamId(),
|
|
|
- studentId, examSessionInfo.getExamRecordDataId(), (int) usedTime / 60);
|
|
|
+ studentId, examSessionInfo.getExamRecordDataId(), (int) usedTime / 60);
|
|
|
}
|
|
|
|
|
|
checkExamInProgressInfo.setFaceVerifyMinute(faceVerifyMinute);
|
|
@@ -1234,12 +1262,12 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- String examingHeartbeatKey = RedisKeyHelper.getBuilder()
|
|
|
- .examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
|
|
|
- ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
|
|
|
- ExamingHeartbeat.class);
|
|
|
+ String examingHeartbeatKey = RedisKeyHelper.getBuilder()
|
|
|
+ .examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
|
|
|
+ ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
|
|
|
+ ExamingHeartbeat.class);
|
|
|
|
|
|
- long usedTime = null == examingHeartbeat ? 0 : examingHeartbeat.getCost();
|
|
|
+ long usedTime = null == examingHeartbeat ? 0 : examingHeartbeat.getCost();
|
|
|
|
|
|
// 如果考试时间结束,自动交卷
|
|
|
if (examSessionInfo.getExamDuration() <= usedTime * 1000) {
|
|
@@ -1247,29 +1275,28 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- String examingActiveTimeKey = RedisKeyHelper.getBuilder()
|
|
|
- .examingActiveTimeKey(examSessionInfo.getExamRecordDataId());
|
|
|
- ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
|
|
|
- ExamingActivityTime.class);
|
|
|
+ String examingActiveTimeKey = RedisKeyHelper.getBuilder()
|
|
|
+ .examingActiveTimeKey(examSessionInfo.getExamRecordDataId());
|
|
|
+ ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
|
|
|
+ ExamingActivityTime.class);
|
|
|
|
|
|
- long activeTime = null == examingActiveTime
|
|
|
- ? System.currentTimeMillis()
|
|
|
- : examingActiveTime.getActiveTime();
|
|
|
+ long activeTime = null == examingActiveTime
|
|
|
+ ? System.currentTimeMillis()
|
|
|
+ : examingActiveTime.getActiveTime();
|
|
|
|
|
|
- // 如果已经过了断点续考时间,自动交卷
|
|
|
- long now = System.currentTimeMillis();
|
|
|
- if (now - activeTime >= examSessionInfo.getExamReconnectTime().intValue() * 60 * 1000) {
|
|
|
- delayHandInExamIfLocked(examingRecord.getId());
|
|
|
- return null;
|
|
|
- }
|
|
|
+ // 如果已经过了断点续考时间,自动交卷
|
|
|
+ long now = System.currentTimeMillis();
|
|
|
+ if (now - activeTime >= examSessionInfo.getExamReconnectTime().intValue() * 60 * 1000) {
|
|
|
+ delayHandInExamIfLocked(examingRecord.getId());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
return examingRecord;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 如果有序列化锁,则延迟交卷
|
|
|
*
|
|
|
- * @param examRecordDataId
|
|
|
- * 考试记录id
|
|
|
+ * @param examRecordDataId 考试记录id
|
|
|
* @return
|
|
|
*/
|
|
|
private void delayHandInExamIfLocked(Long examRecordDataId) {
|
|
@@ -1315,54 +1342,53 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
/**
|
|
|
* 考试心跳每分钟调用一次
|
|
|
*
|
|
|
- * @param user
|
|
|
- * 学生
|
|
|
+ * @param user 学生
|
|
|
*/
|
|
|
@Override
|
|
|
public long examHeartbeat(User user) {
|
|
|
Long studentId = user.getUserId();
|
|
|
ExamingSession examSessionInfo = examingSessionService.getExamingSession(studentId);
|
|
|
- if (examSessionInfo == null
|
|
|
- || examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)) {
|
|
|
- throw new StatusException("8001", "无效的会话,请离开考试");
|
|
|
- }
|
|
|
+ if (examSessionInfo == null
|
|
|
+ || examSessionInfo.getExamingStatus().equals(ExamingStatus.INFORMAL)) {
|
|
|
+ throw new StatusException("8001", "无效的会话,请离开考试");
|
|
|
+ }
|
|
|
|
|
|
String examingHeartbeatKey = RedisKeyHelper.getBuilder().examingHeartbeatKey(examSessionInfo.getExamRecordDataId());
|
|
|
- ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,ExamingHeartbeat.class);
|
|
|
+ ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey, ExamingHeartbeat.class);
|
|
|
|
|
|
- if (null != examingHeartbeat
|
|
|
- && examingHeartbeat.getCost() >= examSessionInfo.getExamDuration()) {
|
|
|
- throw new StatusException("8002", "考试会话已过期,请重新开考");
|
|
|
- }
|
|
|
+ if (null != examingHeartbeat
|
|
|
+ && examingHeartbeat.getCost() >= examSessionInfo.getExamDuration()) {
|
|
|
+ throw new StatusException("8002", "考试会话已过期,请重新开考");
|
|
|
+ }
|
|
|
|
|
|
- if (null==examingHeartbeat) {
|
|
|
- examingHeartbeat=new ExamingHeartbeat();
|
|
|
- examingHeartbeat.setCost(0L);
|
|
|
- examingHeartbeat.setTimes(0L);
|
|
|
- examingHeartbeat.setExamRecordDataId(examSessionInfo.getExamRecordDataId());
|
|
|
- }
|
|
|
+ if (null == examingHeartbeat) {
|
|
|
+ examingHeartbeat = new ExamingHeartbeat();
|
|
|
+ examingHeartbeat.setCost(0L);
|
|
|
+ examingHeartbeat.setTimes(0L);
|
|
|
+ examingHeartbeat.setExamRecordDataId(examSessionInfo.getExamRecordDataId());
|
|
|
+ }
|
|
|
|
|
|
- examingHeartbeat.setTimes(examingHeartbeat.getTimes()+1);
|
|
|
- examingHeartbeat.setCost(examingHeartbeat.getCost() + 60);
|
|
|
+ examingHeartbeat.setTimes(examingHeartbeat.getTimes() + 1);
|
|
|
+ examingHeartbeat.setCost(examingHeartbeat.getCost() + 60);
|
|
|
|
|
|
- redisClient.set(examingHeartbeatKey, examingHeartbeat);
|
|
|
+ redisClient.set(examingHeartbeatKey, examingHeartbeat);
|
|
|
|
|
|
- String examingActiveTimeKey = RedisKeyHelper.getBuilder()
|
|
|
- .examingActiveTimeKey(examSessionInfo.getExamRecordDataId());
|
|
|
- ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
|
|
|
- ExamingActivityTime.class);
|
|
|
- if (null==examingActiveTime) {
|
|
|
- examingActiveTime=new ExamingActivityTime();
|
|
|
- examingActiveTime.setActiveTime(System.currentTimeMillis());
|
|
|
- examingActiveTime.setExamRecordDataId(examSessionInfo.getExamRecordDataId());
|
|
|
- }
|
|
|
+ String examingActiveTimeKey = RedisKeyHelper.getBuilder()
|
|
|
+ .examingActiveTimeKey(examSessionInfo.getExamRecordDataId());
|
|
|
+ ExamingActivityTime examingActiveTime = redisClient.get(examingActiveTimeKey,
|
|
|
+ ExamingActivityTime.class);
|
|
|
+ if (null == examingActiveTime) {
|
|
|
+ examingActiveTime = new ExamingActivityTime();
|
|
|
+ examingActiveTime.setActiveTime(System.currentTimeMillis());
|
|
|
+ examingActiveTime.setExamRecordDataId(examSessionInfo.getExamRecordDataId());
|
|
|
+ }
|
|
|
|
|
|
- redisClient.set(examingActiveTimeKey, examingActiveTime);
|
|
|
+ redisClient.set(examingActiveTimeKey, examingActiveTime);
|
|
|
|
|
|
- long activeTime = examingActiveTime.getActiveTime();
|
|
|
+ long activeTime = examingActiveTime.getActiveTime();
|
|
|
|
|
|
long now = System.currentTimeMillis();
|
|
|
- if (now - activeTime>= examSessionInfo.getExamReconnectTime().intValue() * 60 * 1000) {
|
|
|
+ if (now - activeTime >= examSessionInfo.getExamReconnectTime().intValue() * 60 * 1000) {
|
|
|
delayHandInExamIfLocked(examSessionInfo.getExamRecordDataId());
|
|
|
return 0L;
|
|
|
}
|
|
@@ -1377,8 +1403,7 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
/**
|
|
|
* 计算考试时长 校验是否达到冻结时间
|
|
|
*
|
|
|
- * @param studentId
|
|
|
- * 学生id
|
|
|
+ * @param studentId 学生id
|
|
|
* @return
|
|
|
*/
|
|
|
private Long checkAndComputeExamDuration(Long studentId) {
|
|
@@ -1389,12 +1414,12 @@ public class ExamControlServiceImpl implements ExamControlService {
|
|
|
throw new StatusException("oestudent-100100", "考试会话已过期");
|
|
|
}
|
|
|
|
|
|
- String examingHeartbeatKey = RedisKeyHelper.getBuilder()
|
|
|
- .examingHeartbeatKey(examingSession.getExamRecordDataId());
|
|
|
- ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
|
|
|
- ExamingHeartbeat.class);
|
|
|
+ String examingHeartbeatKey = RedisKeyHelper.getBuilder()
|
|
|
+ .examingHeartbeatKey(examingSession.getExamRecordDataId());
|
|
|
+ ExamingHeartbeat examingHeartbeat = redisClient.get(examingHeartbeatKey,
|
|
|
+ ExamingHeartbeat.class);
|
|
|
|
|
|
- long cost = null == examingHeartbeat ? 0L : examingHeartbeat.getCost();
|
|
|
+ long cost = null == examingHeartbeat ? 0L : examingHeartbeat.getCost();
|
|
|
|
|
|
Long examUsedMilliSeconds = cost * 1000;
|
|
|
// 如果没有超过冻结时间,抛出异常
|