|
@@ -2,23 +2,36 @@ package cn.com.qmth.examcloud.core.oe.student.api.controller;
|
|
|
|
|
|
import cn.com.qmth.examcloud.api.commons.security.bean.User;
|
|
|
import cn.com.qmth.examcloud.commons.exception.StatusException;
|
|
|
+import cn.com.qmth.examcloud.commons.util.Util;
|
|
|
import cn.com.qmth.examcloud.core.oe.student.base.utils.Check;
|
|
|
-import cn.com.qmth.examcloud.core.oe.student.bean.CheckExamInProgressInfo;
|
|
|
-import cn.com.qmth.examcloud.core.oe.student.bean.ExamProcessResultInfo;
|
|
|
-import cn.com.qmth.examcloud.core.oe.student.bean.StartExamInfo;
|
|
|
+import cn.com.qmth.examcloud.core.oe.student.bean.*;
|
|
|
import cn.com.qmth.examcloud.core.oe.student.service.ExamControlService;
|
|
|
+import cn.com.qmth.examcloud.core.oe.student.service.ExamFileAnswerService;
|
|
|
+import cn.com.qmth.examcloud.core.oe.student.service.ExamingSessionService;
|
|
|
import cn.com.qmth.examcloud.support.Constants;
|
|
|
+import cn.com.qmth.examcloud.support.enums.FileAnswerAcknowledgeStatus;
|
|
|
import cn.com.qmth.examcloud.support.enums.HandInExamType;
|
|
|
+import cn.com.qmth.examcloud.support.examing.ExamFileAnswer;
|
|
|
import cn.com.qmth.examcloud.support.examing.ExamingSession;
|
|
|
+import cn.com.qmth.examcloud.support.redis.RedisKeyHelper;
|
|
|
+import cn.com.qmth.examcloud.web.bootstrap.PropertyHolder;
|
|
|
import cn.com.qmth.examcloud.web.helpers.SequenceLockHelper;
|
|
|
+import cn.com.qmth.examcloud.web.redis.RedisClient;
|
|
|
import cn.com.qmth.examcloud.web.support.ControllerSupport;
|
|
|
+import cn.com.qmth.examcloud.web.support.Naked;
|
|
|
+import cn.com.qmth.examcloud.web.upyun.UpYunHttpRequest;
|
|
|
+import cn.com.qmth.examcloud.web.upyun.UpyunPathEnvironmentInfo;
|
|
|
+import cn.com.qmth.examcloud.web.upyun.UpyunService;
|
|
|
+import com.mysql.cj.util.StringUtils;
|
|
|
import io.swagger.annotations.Api;
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-import org.springframework.web.bind.annotation.GetMapping;
|
|
|
-import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
-import org.springframework.web.bind.annotation.RequestParam;
|
|
|
-import org.springframework.web.bind.annotation.RestController;
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
+
|
|
|
+import javax.validation.Valid;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
@Api(tags = "在线考试控制")
|
|
|
@RestController
|
|
@@ -27,6 +40,14 @@ public class ExamControlController extends ControllerSupport {
|
|
|
|
|
|
@Autowired
|
|
|
private ExamControlService examControlService;
|
|
|
+ @Autowired
|
|
|
+ private ExamingSessionService examingSessionService;
|
|
|
+ @Autowired
|
|
|
+ private ExamFileAnswerService examFileAnswerService;
|
|
|
+ @Autowired
|
|
|
+ private UpyunService upyunService;
|
|
|
+ @Autowired
|
|
|
+ private RedisClient redisClient;
|
|
|
|
|
|
/**
|
|
|
* 开始考试
|
|
@@ -72,6 +93,18 @@ public class ExamControlController extends ControllerSupport {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 考试心跳
|
|
|
+ *
|
|
|
+ * @return 剩余时间
|
|
|
+ */
|
|
|
+ @ApiOperation(value = "考试心跳")
|
|
|
+ @GetMapping("/examHeartbeat")
|
|
|
+ public Long examHeartbeat() {
|
|
|
+ User user = getAccessUser();
|
|
|
+ return examControlService.examHeartbeat(user);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 结束考试:交卷..
|
|
|
*/
|
|
@@ -87,13 +120,7 @@ public class ExamControlController extends ControllerSupport {
|
|
|
long st = System.currentTimeMillis();
|
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
|
|
- ExamingSession examingSession = null;
|
|
|
-// // 获取考试会话,判断考生是否已结束考试
|
|
|
-// ExamSessionInfo examSessionInfo = examSessionInfoService.getExamSessionInfo(studentId);
|
|
|
-// if (examSessionInfo == null) {
|
|
|
-// throw new StatusException("oestudent-100100", "考试会话已过期");
|
|
|
-// }
|
|
|
-
|
|
|
+ ExamingSession examingSession = examingSessionService.getExamingSession(studentId);
|
|
|
|
|
|
if (log.isDebugEnabled()) {
|
|
|
log.debug("0 [END_EXAM] 交卷前处理耗时:" + (System.currentTimeMillis() - startTime) + " ms");
|
|
@@ -103,17 +130,172 @@ public class ExamControlController extends ControllerSupport {
|
|
|
log.debug("1 [END_EXAM]合计 耗时:" + (System.currentTimeMillis() - st) + " ms");
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
- * 考试心跳
|
|
|
+ * 获取考试记录信息
|
|
|
*
|
|
|
- * @return 剩余时间
|
|
|
+ * @param examRecordDataId
|
|
|
+ * @return
|
|
|
*/
|
|
|
- @ApiOperation(value = "考试心跳")
|
|
|
- @GetMapping("/examHeartbeat")
|
|
|
- public Long examHeartbeat() {
|
|
|
+ @ApiOperation(value = "获取考试记录信息")
|
|
|
+ @GetMapping("/getEndExamInfo")
|
|
|
+ public EndExamInfo getEndExamInfo(@RequestParam Long examRecordDataId) {
|
|
|
+ return examControlService.getEndExamInfo(examRecordDataId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取又拍云文件上传签名(微信小程序调用)
|
|
|
+ */
|
|
|
+ @ApiOperation(value = "获取又拍云文件上传签名(微信小程序调用)")
|
|
|
+ @PostMapping("/upyunSignature")
|
|
|
+ public UpyunSignatureInfo getUpyunSignature(@ModelAttribute @Valid GetUpyunSignatureReq req) {
|
|
|
+ return examControlService.getUpyunSignature(req);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验二维码(微信小程序调用)
|
|
|
+ */
|
|
|
+ @Naked
|
|
|
+ @ApiOperation(value = "校验二维码(微信小程序调用)")
|
|
|
+ @PostMapping("/checkQrCode")
|
|
|
+ public CheckQrCodeInfo checkQrCode(@RequestParam(required = true) String qrCode) {
|
|
|
+ return examControlService.checkQrCode(qrCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存上传的文件(微信小程序调用)
|
|
|
+ */
|
|
|
+ @ApiOperation(value = "保存上传的文件(微信小程序调用)")
|
|
|
+ @PostMapping("/saveUploadedFile")
|
|
|
+ public String saveUploadedFile(@ModelAttribute @Valid SaveUploadedFileReq req) {
|
|
|
User user = getAccessUser();
|
|
|
- return examControlService.examHeartbeat(user);
|
|
|
+ ExamFileAnswer examFileAnswer = new ExamFileAnswer();
|
|
|
+ examFileAnswer.setExamRecordDataId(req.getExamRecordDataId());
|
|
|
+ examFileAnswer.setExamStudentId(req.getExamStudentId());
|
|
|
+ examFileAnswer.setQuestionOrder(req.getOrder());
|
|
|
+ examFileAnswer.setFilePath(req.getFilePath());
|
|
|
+ examFileAnswer.setStatus(FileAnswerAcknowledgeStatus.UNCONFIRMED);
|
|
|
+ examFileAnswer.setTransferFileType(req.getTransferFileType());
|
|
|
+ String fileAnswerId = RedisKeyHelper.getBuilder().studentFileAnswerKey(req.getExamRecordDataId(), req.getOrder());
|
|
|
+ examFileAnswerService.saveFileAnswer(fileAnswerId, examFileAnswer);
|
|
|
+
|
|
|
+ try {
|
|
|
+ String upyunFileUrl = PropertyHolder.getString("$upyun.site.1.domain");
|
|
|
+ String fileUrl = "";
|
|
|
+ if (req.getFilePath().indexOf(",") > -1) {
|
|
|
+ for (String url : req.getFilePath().split(",")) {
|
|
|
+ fileUrl += upyunFileUrl + url + ",";
|
|
|
+ }
|
|
|
+ fileUrl = fileUrl.substring(0, fileUrl.length() - 1);
|
|
|
+ } else {
|
|
|
+ fileUrl = upyunFileUrl + req.getFilePath();
|
|
|
+ }
|
|
|
+ examControlService.sendFileAnswerToWebSocket(req.getExamRecordDataId(), req.getOrder(),
|
|
|
+ fileUrl, req.getTransferFileType(), user.getUserId());
|
|
|
+ } catch (Exception e) {
|
|
|
+ examFileAnswerService.deleteFileAnswer(fileAnswerId);
|
|
|
+ throw new StatusException("100009", "消息通知失败", e);
|
|
|
+ }
|
|
|
+ return fileAnswerId;
|
|
|
+ }
|
|
|
+
|
|
|
+ //TODO 此方法有修改,微信需要修改代码
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询客户端对上传的文件的响应状态(微信小程序调用)
|
|
|
+ */
|
|
|
+ @ApiOperation(value = "查询客户端对上传的文件的响应状态(微信小程序调用)")
|
|
|
+ @PostMapping("/getUploadedFileAcknowledgeStatus")
|
|
|
+ public String getUploadedFileAcknowledgeStatus(@ModelAttribute @Valid GetUploadedFileAcknowledgeStatusReq req) {
|
|
|
+ ExamFileAnswer fileAnswer = examFileAnswerService.getFileAnswer(req.getAcknowledgeId());
|
|
|
+ if (null != fileAnswer) {
|
|
|
+ return fileAnswer.getStatus().toString();
|
|
|
+ }
|
|
|
+ return FileAnswerAcknowledgeStatus.UNCONFIRMED.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 修改上传音频结果推送状态
|
|
|
+ */
|
|
|
+ @ApiOperation(value = "修改上传音频结果推送状态")
|
|
|
+ @PostMapping("/saveUploadedFileAcknowledgeStatus")
|
|
|
+ public void saveUploadedFileAcknowledgeStatus(@RequestBody @Valid SaveUploadedFileAcknowledgeStatusReq req) {
|
|
|
+ String acknowledgeId = RedisKeyHelper.getBuilder().studentFileAnswerKey(req.getExamRecordDataId(),req.getOrder());
|
|
|
+ ExamFileAnswer fileAnswer = examFileAnswerService.getFileAnswer(acknowledgeId);
|
|
|
+ if (null != fileAnswer) {
|
|
|
+ throw new StatusException("100010", "无效的数据");
|
|
|
+ }
|
|
|
+
|
|
|
+ fileAnswer.setStatus(FileAnswerAcknowledgeStatus.valueOf(req.getAcknowledgeStatus()));
|
|
|
+ examFileAnswerService.saveFileAnswer(acknowledgeId,fileAnswer);
|
|
|
}
|
|
|
+
|
|
|
+ @ApiOperation(value = "获取抓拍照片的又拍云签名")
|
|
|
+ @GetMapping("/getCapturePhotoUpYunSign")
|
|
|
+ public GetUpyunSignDomain getCapturePhotoUpYunSign(GetUpyunSignDomainQuery query) {
|
|
|
+ String fileSuffix = query.getFileSuffix();
|
|
|
+ if (StringUtils.isNullOrEmpty(fileSuffix)) {
|
|
|
+ throw new StatusException("200001", "文件后缀名不允许为空");
|
|
|
+ }
|
|
|
+ fileSuffix = fileSuffix.indexOf(".") == -1 ? "." + fileSuffix : fileSuffix;
|
|
|
+
|
|
|
+ GetUpyunSignDomain result = new GetUpyunSignDomain();
|
|
|
+ User accessUser = this.getAccessUser();
|
|
|
+ String signIdentifier = String.valueOf(System.currentTimeMillis());
|
|
|
+ String upyunSignRedisKey = Constants.EXAM_CAPTURE_PHOTO_UPYUN_SIGN_PREFIX
|
|
|
+ + accessUser.getUserId() + "_" + signIdentifier;
|
|
|
+
|
|
|
+ UpyunPathEnvironmentInfo env = new UpyunPathEnvironmentInfo();
|
|
|
+ env.setRootOrgId(accessUser.getRootOrgId().toString());
|
|
|
+ env.setUserId(accessUser.getUserId().toString());
|
|
|
+ env.setFileSuffix(fileSuffix);
|
|
|
+ UpYunHttpRequest upYunHttpRequest = upyunService.buildUpYunHttpRequest(Constants.CAPTURE_PHOTO_UPYUN_SITEID, env, query.getFileMd5());
|
|
|
+ redisClient.set(upyunSignRedisKey, upYunHttpRequest, 60);
|
|
|
+ result.setAccessUrl(upYunHttpRequest.getAccessUrl());
|
|
|
+ result.setFormUrl(upYunHttpRequest.getFormUrl());
|
|
|
+ result.setFormParams(upYunHttpRequest.getFormParams());
|
|
|
+ result.setSignIdentifier(signIdentifier);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @ApiOperation(value = "批量获取抓拍照片的又拍云签名")
|
|
|
+ @PostMapping("/batchGetCapturePhotoUpYunSign")
|
|
|
+ public BatchGetUpyunSignDomain batchGetCapturePhotoUpYunSign(@RequestBody BatchGetUpyunSignDomainQuery batchQuery) {
|
|
|
+ if (batchQuery.getQueryList() == null || batchQuery.getQueryList().isEmpty()) {
|
|
|
+ throw new StatusException("200001", "查询条件不允许为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<GetUpyunSignDomain> signDomainList = new ArrayList<>();
|
|
|
+ for (GetUpyunSignDomainQuery query : batchQuery.getQueryList()) {
|
|
|
+ String fileSuffix = query.getFileSuffix();
|
|
|
+ if (StringUtils.isNullOrEmpty(fileSuffix)) {
|
|
|
+ throw new StatusException("", "文件后缀名不允许为空");
|
|
|
+ }
|
|
|
+ fileSuffix = fileSuffix.indexOf(".") == -1 ? "." + fileSuffix : fileSuffix;
|
|
|
+
|
|
|
+ GetUpyunSignDomain upyunSignDomain = new GetUpyunSignDomain();
|
|
|
+ User accessUser = this.getAccessUser();
|
|
|
+ String signIdentifier = String.valueOf(System.currentTimeMillis());
|
|
|
+ String upyunSignRedisKey = Constants.EXAM_CAPTURE_PHOTO_UPYUN_SIGN_PREFIX
|
|
|
+ + accessUser.getUserId() + "_" + signIdentifier;
|
|
|
+
|
|
|
+ UpyunPathEnvironmentInfo env = new UpyunPathEnvironmentInfo();
|
|
|
+ env.setRootOrgId(accessUser.getRootOrgId().toString());
|
|
|
+ env.setUserId(accessUser.getUserId().toString());
|
|
|
+ env.setFileSuffix(fileSuffix);
|
|
|
+ UpYunHttpRequest upYunHttpRequest = upyunService.buildUpYunHttpRequest(Constants.CAPTURE_PHOTO_UPYUN_SITEID, env, query.getFileMd5());
|
|
|
+ redisClient.set(upyunSignRedisKey, upYunHttpRequest, 60);
|
|
|
+ upyunSignDomain.setAccessUrl(upYunHttpRequest.getAccessUrl());
|
|
|
+ upyunSignDomain.setFormUrl(upYunHttpRequest.getFormUrl());
|
|
|
+ upyunSignDomain.setFormParams(upYunHttpRequest.getFormParams());
|
|
|
+ upyunSignDomain.setSignIdentifier(signIdentifier);
|
|
|
+
|
|
|
+ signDomainList.add(upyunSignDomain);
|
|
|
+ Util.sleep(TimeUnit.MILLISECONDS, 1);
|
|
|
+ }
|
|
|
+ BatchGetUpyunSignDomain result = new BatchGetUpyunSignDomain();
|
|
|
+ result.setList(signDomainList);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
}
|