瀏覽代碼

微信调用接口相关代码优化,未完成,先提交

lideyin 5 年之前
父節點
當前提交
76c25faf58
共有 17 個文件被更改,包括 1059 次插入46 次删除
  1. 204 22
      examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/api/controller/ExamControlController.java
  2. 104 0
      examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/ExamRecordDataEntity.java
  3. 28 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/BatchGetUpyunSignDomain.java
  4. 28 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/BatchGetUpyunSignDomainQuery.java
  5. 90 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/CheckQrCodeInfo.java
  6. 59 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/EndExamInfo.java
  7. 22 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/GetUploadedFileAcknowledgeStatusReq.java
  8. 61 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/GetUpyunSignDomain.java
  9. 35 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/GetUpyunSignDomainQuery.java
  10. 61 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/GetUpyunSignatureReq.java
  11. 60 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/SaveUploadedFileAcknowledgeStatusReq.java
  12. 61 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/SaveUploadedFileReq.java
  13. 53 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/UpyunSignatureInfo.java
  14. 35 4
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/ExamControlService.java
  15. 38 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/ExamFileAnswerService.java
  16. 68 20
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamControlServiceImpl.java
  17. 52 0
      examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamFileAnswerServiceImpl.java

+ 204 - 22
examcloud-core-oe-student-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/student/api/controller/ExamControlController.java

@@ -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;
+    }
+
 }

+ 104 - 0
examcloud-core-oe-student-dao/src/main/java/cn/com/qmth/examcloud/core/oe/student/dao/entity/ExamRecordDataEntity.java

@@ -151,6 +151,46 @@ public class ExamRecordDataEntity extends JpaEntity {
     @Enumerated(EnumType.STRING)
     private IsSuccess faceVerifyResult;
 
+    /**
+     * 是否异常数据
+     */
+    private Boolean isWarn;
+    /**
+     * 是否被审核过
+     */
+    private Boolean isAudit;
+    /**
+     * 是否违纪
+     */
+    @Column(name = "is_illegality")
+    private Boolean isIllegality;
+
+    /**
+     * 总分
+     */
+    private Double totalScore;
+
+    /**
+     * 客观题得分总分
+     */
+    private Double objectiveScore;
+
+    /**
+     * 客观题答对的比率
+     * (客观题答对的题数/客观题总题数)*100  取2位小数
+     */
+    private Double objectiveAccuracy;
+
+    /**
+     * 主观题得分总分
+     */
+    private Double subjectiveScore;
+
+    /**
+     * 答题正确率
+     */
+    private Double succPercent;
+
     public Long getId() {
         return id;
     }
@@ -342,4 +382,68 @@ public class ExamRecordDataEntity extends JpaEntity {
     public void setCleanTime(Date cleanTime) {
         this.cleanTime = cleanTime;
     }
+
+    public Double getTotalScore() {
+        return totalScore;
+    }
+
+    public void setTotalScore(Double totalScore) {
+        this.totalScore = totalScore;
+    }
+
+    public Double getObjectiveScore() {
+        return objectiveScore;
+    }
+
+    public void setObjectiveScore(Double objectiveScore) {
+        this.objectiveScore = objectiveScore;
+    }
+
+    public Double getObjectiveAccuracy() {
+        return objectiveAccuracy;
+    }
+
+    public void setObjectiveAccuracy(Double objectiveAccuracy) {
+        this.objectiveAccuracy = objectiveAccuracy;
+    }
+
+    public Double getSubjectiveScore() {
+        return subjectiveScore;
+    }
+
+    public void setSubjectiveScore(Double subjectiveScore) {
+        this.subjectiveScore = subjectiveScore;
+    }
+
+    public Double getSuccPercent() {
+        return succPercent;
+    }
+
+    public void setSuccPercent(Double succPercent) {
+        this.succPercent = succPercent;
+    }
+
+    public Boolean getIsWarn() {
+        return isWarn;
+    }
+
+    public void setIsWarn(Boolean warn) {
+        isWarn = warn;
+    }
+
+    public Boolean getIsAudit() {
+        return isAudit;
+    }
+
+    public void setIsAudit(Boolean audit) {
+        isAudit = audit;
+    }
+
+    public Boolean getIsIllegality() {
+        return isIllegality;
+    }
+
+    public void setIsIllegality(Boolean illegality) {
+        isIllegality = illegality;
+    }
 }

+ 28 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/BatchGetUpyunSignDomain.java

@@ -0,0 +1,28 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.List;
+
+/**
+ * @Description 获取又拍云签名实体
+ * @Author lideyin
+ * @Date 2019/7/29 13:51
+ * @Version 1.0
+ */
+public class BatchGetUpyunSignDomain implements JsonSerializable {
+
+	private static final long serialVersionUID = -1590654532824096979L;
+
+	@ApiModelProperty("又拍云签名集合")
+	private List<GetUpyunSignDomain> list;
+
+	public List<GetUpyunSignDomain> getList() {
+		return list;
+	}
+
+	public void setList(List<GetUpyunSignDomain> list) {
+		this.list = list;
+	}
+}

+ 28 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/BatchGetUpyunSignDomainQuery.java

@@ -0,0 +1,28 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @Description 获取又拍云签名查询实体
+ * @Author lideyin
+ * @Date 2019/7/29 13:52
+ * @Version 1.0
+ */
+public class BatchGetUpyunSignDomainQuery implements Serializable {
+
+    private static final long serialVersionUID = 9042153672752879732L;
+
+    @ApiModelProperty("又拍云签名查询参数集合")
+    private List<GetUpyunSignDomainQuery> queryList;
+
+    public List<GetUpyunSignDomainQuery> getQueryList() {
+        return queryList;
+    }
+
+    public void setQueryList(List<GetUpyunSignDomainQuery> queryList) {
+        this.queryList = queryList;
+    }
+}

+ 90 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/CheckQrCodeInfo.java

@@ -0,0 +1,90 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class CheckQrCodeInfo implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 7830558100815856011L;
+	@ApiModelProperty(value = "登录信息key")
+	private String key;
+	@ApiModelProperty(value = "登录信息token")
+	private String token;
+	@ApiModelProperty(value = "考生ID")
+	private Long examStudentId;
+	@ApiModelProperty(value = "考试记录DataID")
+	private Long examRecordDataId;
+	@ApiModelProperty(value = "考试试题题序")
+	private Integer questionOrder;
+	@ApiModelProperty(value = "考试试题大题号")
+    private Integer questionMainNumber;
+	@ApiModelProperty(value = "课程ID")
+	private Long courseId;
+	@ApiModelProperty(value = "课程名称")
+	private String courseName;
+	@ApiModelProperty(value = "小题显示序号")
+    private Integer subNumber;
+	public String getKey() {
+		return key;
+	}
+	public void setKey(String key) {
+		this.key = key;
+	}
+	public String getToken() {
+		return token;
+	}
+	public void setToken(String token) {
+		this.token = token;
+	}
+
+	public Long getExamStudentId() {
+		return examStudentId;
+	}
+
+	public void setExamStudentId(Long examStudentId) {
+		this.examStudentId = examStudentId;
+	}
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public Long getCourseId() {
+		return courseId;
+	}
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+	public String getCourseName() {
+		return courseName;
+	}
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+	public Integer getQuestionOrder() {
+		return questionOrder;
+	}
+	public void setQuestionOrder(Integer questionOrder) {
+		this.questionOrder = questionOrder;
+	}
+	public Integer getQuestionMainNumber() {
+		return questionMainNumber;
+	}
+	public void setQuestionMainNumber(Integer questionMainNumber) {
+		this.questionMainNumber = questionMainNumber;
+	}
+	public Integer getSubNumber() {
+		return subNumber;
+	}
+	public void setSubNumber(Integer subNumber) {
+		this.subNumber = subNumber;
+	}
+	
+}

+ 59 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/EndExamInfo.java

@@ -0,0 +1,59 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+/**
+ * @Description 考试结束信息
+ * @Author lideyin
+ * @Date 2019/12/13 14:17
+ * @Version 1.0
+ */
+public class EndExamInfo implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 3567311334163339241L;
+
+	private Long examRecordDataId;
+	
+	private Boolean isWarn;
+	
+	private Double objectiveScore;
+	
+	private Double objectiveAccuracy;
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public Boolean getIsWarn() {
+		return isWarn;
+	}
+
+	public void setIsWarn(Boolean isWarn) {
+		this.isWarn = isWarn;
+	}
+
+	public Double getObjectiveScore() {
+		return objectiveScore;
+	}
+
+	public void setObjectiveScore(Double objectiveScore) {
+		this.objectiveScore = objectiveScore;
+	}
+
+	public Double getObjectiveAccuracy() {
+		return objectiveAccuracy;
+	}
+
+	public void setObjectiveAccuracy(Double objectiveAccuracy) {
+		this.objectiveAccuracy = objectiveAccuracy;
+	}
+	
+	
+}

+ 22 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/GetUploadedFileAcknowledgeStatusReq.java

@@ -0,0 +1,22 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotNull;
+
+public class GetUploadedFileAcknowledgeStatusReq implements JsonSerializable {
+
+    private static final long serialVersionUID = 2015767610417418141L;
+    @NotNull(message = "响应id不允许为空")
+    @ApiModelProperty(required = true, value = "响应id不允许为空")
+    private String acknowledgeId;
+
+    public String getAcknowledgeId() {
+        return acknowledgeId;
+    }
+
+    public void setAcknowledgeId(String acknowledgeId) {
+        this.acknowledgeId = acknowledgeId;
+    }
+}

+ 61 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/GetUpyunSignDomain.java

@@ -0,0 +1,61 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Map;
+
+/**
+ * @Description 获取又拍云签名实体
+ * @Author lideyin
+ * @Date 2019/7/29 13:51
+ * @Version 1.0
+ */
+public class GetUpyunSignDomain implements JsonSerializable {
+	private static final long serialVersionUID = -7428336128716146142L;
+
+	@ApiModelProperty("又拍云读取文件地址")
+	private String accessUrl;
+
+	@ApiModelProperty("又拍云签名唯一标识")
+	private String signIdentifier;
+
+	@ApiModelProperty("又拍云上传form请求地址 POST")
+	private String formUrl;
+
+	@ApiModelProperty("form表单参数(上传文件的参数名为'file')")
+	private Map<String, String> formParams;
+
+	public String getAccessUrl() {
+		return accessUrl;
+	}
+
+	public void setAccessUrl(String accessUrl) {
+		this.accessUrl = accessUrl;
+	}
+
+	public String getSignIdentifier() {
+		return signIdentifier;
+	}
+
+	public void setSignIdentifier(String signIdentifier) {
+		this.signIdentifier = signIdentifier;
+	}
+
+	public String getFormUrl() {
+		return formUrl;
+	}
+
+	public void setFormUrl(String formUrl) {
+		this.formUrl = formUrl;
+	}
+
+	public Map<String, String> getFormParams() {
+		return formParams;
+	}
+
+	public void setFormParams(Map<String, String> formParams) {
+		this.formParams = formParams;
+	}
+
+}

+ 35 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/GetUpyunSignDomainQuery.java

@@ -0,0 +1,35 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * @Description 获取又拍云签名查询实体
+ * @Author lideyin
+ * @Date 2019/7/29 13:52
+ * @Version 1.0
+ */
+public class GetUpyunSignDomainQuery implements Serializable {
+    private static final long serialVersionUID = 6594663402697883755L;
+    @ApiModelProperty("文件后缀名,示例:jpg")
+    private String fileSuffix;
+    @ApiModelProperty("文件md5加密值")
+    private String fileMd5;
+
+    public String getFileSuffix() {
+        return fileSuffix;
+    }
+
+    public void setFileSuffix(String fileSuffix) {
+        this.fileSuffix = fileSuffix;
+    }
+
+    public String getFileMd5() {
+        return fileMd5;
+    }
+
+    public void setFileMd5(String fileMd5) {
+        this.fileMd5 = fileMd5;
+    }
+}

+ 61 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/GetUpyunSignatureReq.java

@@ -0,0 +1,61 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotNull;
+
+public class GetUpyunSignatureReq implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3761016079101373608L;
+	@NotNull(message = "考试记录DataID不能为空")
+	@ApiModelProperty(required = true,value = "考试记录DataID")
+	private Integer examRecordDataId;
+	@NotNull(message = "题号不能为空")
+	@ApiModelProperty(required = true,value = "考试试题号")
+	private Integer order;
+	@NotNull(message = "文件MD5不能为空")
+	@ApiModelProperty(required = true,value = "文件MD5")
+	private String fileMd5;
+	@NotNull(message = "文件后缀不能为空")
+	@ApiModelProperty(required = true,value = "文件后缀")
+	private String fileSuffix;
+	@ApiModelProperty(value = "文件名自定义参数")
+	private String ext;
+
+	public Integer getExamRecordDataId() {
+		return examRecordDataId;
+	}
+	public void setExamRecordDataId(Integer examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public Integer getOrder() {
+		return order;
+	}
+	public void setOrder(Integer order) {
+		this.order = order;
+	}
+	public String getFileMd5() {
+		return fileMd5;
+	}
+	public void setFileMd5(String fileMd5) {
+		this.fileMd5 = fileMd5;
+	}
+	public String getFileSuffix() {
+		return fileSuffix;
+	}
+	public void setFileSuffix(String fileSuffix) {
+		this.fileSuffix = fileSuffix;
+	}
+	public String getExt() {
+		return ext;
+	}
+	public void setExt(String ext) {
+		this.ext = ext;
+	}
+	
+}

+ 60 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/SaveUploadedFileAcknowledgeStatusReq.java

@@ -0,0 +1,60 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotNull;
+
+public class SaveUploadedFileAcknowledgeStatusReq implements JsonSerializable {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 2015767610417418141L;
+    @NotNull(message = "考试记录DataID不能为空")
+    @ApiModelProperty(required = true, value = "考试记录DataID")
+    private Long examRecordDataId;
+    @NotNull(message = "题号不能为空")
+    @ApiModelProperty(required = true, value = "考试试题号")
+    private Integer order;
+    @NotNull(message = "文件路径(含文件名)不能为空")
+    @ApiModelProperty(required = true, value = "文件路径(含文件名)")
+    private String filePath;
+
+    @NotNull(message = "答复状态不允许为空")
+    @ApiModelProperty(required = true,value = "答复状态(CONFIRMED:已确认,DISCARDED:已弃用)")
+    private String acknowledgeStatus;
+
+    public Long getExamRecordDataId() {
+        return examRecordDataId;
+    }
+
+    public void setExamRecordDataId(Long examRecordDataId) {
+        this.examRecordDataId = examRecordDataId;
+    }
+
+
+    public Integer getOrder() {
+        return order;
+    }
+
+    public void setOrder(Integer order) {
+        this.order = order;
+    }
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+    public String getAcknowledgeStatus() {
+        return acknowledgeStatus;
+    }
+
+    public void setAcknowledgeStatus(String acknowledgeStatus) {
+        this.acknowledgeStatus = acknowledgeStatus;
+    }
+}

+ 61 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/SaveUploadedFileReq.java

@@ -0,0 +1,61 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotNull;
+
+public class SaveUploadedFileReq implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5983488494596822561L;
+	@NotNull(message = "考生ID不能为空")
+	@ApiModelProperty(required = true,value = "考生ID")
+	private Long examStudentId;
+	@NotNull(message = "考试记录DataID不能为空")
+	@ApiModelProperty(required = true,value = "考试记录DataID")
+	private Long examRecordDataId;
+	@NotNull(message = "题号不能为空")
+	@ApiModelProperty(required = true,value = "考试试题号")
+	private Integer order;
+	@NotNull(message = "文件路径(含文件名)不能为空")
+	@ApiModelProperty(required = true,value = "文件路径(含文件名)")
+	private String filePath;
+	@ApiModelProperty(required = true,value = "传输文件类型(用于标识,通过二维码传输文件的类型)")
+	private String transferFileType;
+	public Long getExamStudentId() {
+		return examStudentId;
+	}
+	public void setExamStudentId(Long examStudentId) {
+		this.examStudentId = examStudentId;
+	}
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public Integer getOrder() {
+		return order;
+	}
+	public void setOrder(Integer order) {
+		this.order = order;
+	}
+	public String getFilePath() {
+		return filePath;
+	}
+	public void setFilePath(String filePath) {
+		this.filePath = filePath;
+	}
+
+	public String getTransferFileType() {
+		return transferFileType;
+	}
+
+	public void setTransferFileType(String transferFileType) {
+		this.transferFileType = transferFileType;
+	}
+}

+ 53 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/bean/UpyunSignatureInfo.java

@@ -0,0 +1,53 @@
+package cn.com.qmth.examcloud.core.oe.student.bean;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import io.swagger.annotations.ApiModelProperty;
+
+public class UpyunSignatureInfo implements JsonSerializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -2401837848131443152L;
+	@ApiModelProperty(value = "上传到又拍云的url")
+	private String uploadUrl;
+	@ApiModelProperty(value = "又拍云签名")
+	private String signature;
+	@ApiModelProperty(value = "又拍云policy")
+	private String policy;
+	@ApiModelProperty(value = "上传到又拍云的文件路径(含文件名)")
+	private String filePath;
+	@ApiModelProperty(value = "取又拍云文件时的域名")
+	private String upyunFileDomain;
+	public String getSignature() {
+		return signature;
+	}
+	public void setSignature(String signature) {
+		this.signature = signature;
+	}
+	public String getPolicy() {
+		return policy;
+	}
+	public void setPolicy(String policy) {
+		this.policy = policy;
+	}
+	public String getFilePath() {
+		return filePath;
+	}
+	public void setFilePath(String filePath) {
+		this.filePath = filePath;
+	}
+	public String getUploadUrl() {
+		return uploadUrl;
+	}
+	public void setUploadUrl(String uploadUrl) {
+		this.uploadUrl = uploadUrl;
+	}
+	public String getUpyunFileDomain() {
+		return upyunFileDomain;
+	}
+	public void setUpyunFileDomain(String upyunFileDomain) {
+		this.upyunFileDomain = upyunFileDomain;
+	}
+	
+}

+ 35 - 4
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/ExamControlService.java

@@ -2,8 +2,7 @@ package cn.com.qmth.examcloud.core.oe.student.service;
 
 
 import cn.com.qmth.examcloud.api.commons.security.bean.User;
-import cn.com.qmth.examcloud.core.oe.student.bean.CheckExamInProgressInfo;
-import cn.com.qmth.examcloud.core.oe.student.bean.StartExamInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.*;
 import cn.com.qmth.examcloud.support.enums.HandInExamType;
 
 /**
@@ -35,12 +34,44 @@ public interface ExamControlService {
      *
      * @param studentId
      */
-    public CheckExamInProgressInfo checkExamInProgress(Long studentId);
+    CheckExamInProgressInfo checkExamInProgress(Long studentId);
 
     /**
      * 考试心跳
      *
      * @param
      */
-    public long examHeartbeat(User user);
+    long examHeartbeat(User user);
+
+    /**
+     * 获取考试结束后的相关信息
+     * @param examRecordDataId
+     * @return
+     */
+    EndExamInfo getEndExamInfo(Long examRecordDataId);
+
+    CheckQrCodeInfo checkQrCode(String qrCode);
+
+    /**
+     * 发送消息到websocket
+     *
+     * @param examRecordDataId 考试记录id
+     * @param order            题序号
+     * @param fileUrl          文件路径
+     * @param transferFileType 传输文件类型
+     * @throws Exception
+     */
+    void sendFileAnswerToWebSocket(Long examRecordDataId, Integer order,
+                                   String fileUrl, String transferFileType, Long userId) throws Exception;
+
+    /**
+     * 通过websocket发送二维码扫描信息
+     *
+     * @param examRecordDataId
+     * @param clientId
+     * @throws Exception
+     */
+    void sendScanQrCodeToWebSocket(String clientId, Long examRecordDataId, Integer order) throws Exception;
+
+    UpyunSignatureInfo getUpyunSignature(GetUpyunSignatureReq req);
 }

+ 38 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/ExamFileAnswerService.java

@@ -0,0 +1,38 @@
+package cn.com.qmth.examcloud.core.oe.student.service;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.core.oe.student.bean.FaceBiopsyInfo;
+import cn.com.qmth.examcloud.core.oe.student.bean.SaveFaceBiopsyResultReq;
+import cn.com.qmth.examcloud.core.oe.student.bean.SaveFaceBiopsyResultResp;
+import cn.com.qmth.examcloud.core.oe.student.bean.SaveUploadedFileReq;
+import cn.com.qmth.examcloud.core.oe.student.dao.enums.FaceBiopsyType;
+import cn.com.qmth.examcloud.support.examing.ExamFileAnswer;
+
+/**
+ * @Description 文件作答接口
+ * @Author lideyin
+ * @Date 2019/12/13 17:31
+ * @Version 1.0
+ */
+public interface ExamFileAnswerService {
+
+    /**
+     * 保存文件作答结果
+     * @param examFileAnswer
+     * @return 返回主键id
+     */
+    void saveFileAnswer(String fileAnswerId,ExamFileAnswer examFileAnswer);
+
+    /**
+     * 删除文件作答记录
+     * @param fileAnswerId
+     */
+    void deleteFileAnswer(String fileAnswerId);
+
+    /**
+     *  获取文件作答结果
+     * @param fileAnswerId
+     * @return
+     */
+    ExamFileAnswer getFileAnswer(String fileAnswerId);
+}

+ 68 - 20
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamControlServiceImpl.java

@@ -9,9 +9,7 @@ import cn.com.qmth.examcloud.commons.util.Util;
 import cn.com.qmth.examcloud.core.oe.student.base.bean.ExamRecordQuestions;
 import cn.com.qmth.examcloud.core.oe.student.base.utils.CommonUtil;
 import cn.com.qmth.examcloud.core.oe.student.base.utils.QuestionTypeUtil;
-import cn.com.qmth.examcloud.core.oe.student.bean.CheckExamInProgressInfo;
-import cn.com.qmth.examcloud.core.oe.student.bean.ExamRecordPaperStruct;
-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.*;
 import cn.com.qmth.examcloud.core.oe.task.api.ExamCaptureCloudService;
 import cn.com.qmth.examcloud.core.oe.task.api.request.SaveExamCaptureSyncCompareResultReq;
@@ -39,6 +37,7 @@ import cn.com.qmth.examcloud.support.examing.ExamingStatus;
 import cn.com.qmth.examcloud.support.helper.ExamCacheTransferHelper;
 import cn.com.qmth.examcloud.support.helper.FaceBiopsyHelper;
 import cn.com.qmth.examcloud.web.exception.SequenceLockException;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
 import cn.com.qmth.examcloud.web.helpers.SequenceLockHelper;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -79,10 +78,10 @@ public class ExamControlServiceImpl implements ExamControlService {
 
     @Autowired
     private ExamCloudService examCloudService;
-    
+
     @Autowired
     private FaceBiopsyService faceBiopsyService;
-    
+
     @Autowired
     private ExamFaceLivenessVerifyService examFaceLivenessVerifyService;
 
@@ -189,7 +188,7 @@ public class ExamControlServiceImpl implements ExamControlService {
 
         // 创建考试作答记录
         startTime = System.currentTimeMillis();
-        ExamRecordQuestions examRecordQuestions=examRecordQuestionsService.createExamRecordQuestions(examRecordData.getId(),
+        ExamRecordQuestions examRecordQuestions = examRecordQuestionsService.createExamRecordQuestions(examRecordData.getId(),
                 extractConfigPaper.getDefaultPaper());
         //记录试卷题目数量
         examingSession.setQuestionCount(examRecordQuestions.getExamQuestions().size());
@@ -215,8 +214,7 @@ public class ExamControlServiceImpl implements ExamControlService {
 
     }
 
-    
-    
+
     /**
      * 交卷
      *
@@ -248,7 +246,7 @@ public class ExamControlServiceImpl implements ExamControlService {
 
             if (FaceBiopsyHelper.isFaceEnable(rootOrgId, examId, studentId)) {
                 // 更新照片抓拍队列优先级为高优先级
-                UpdateExamCaptureQueuePriorityReq req =new UpdateExamCaptureQueuePriorityReq();
+                UpdateExamCaptureQueuePriorityReq req = new UpdateExamCaptureQueuePriorityReq();
                 req.setExamRecordDataId(examRecordDataId);
                 req.setPriority(Constants.PROCESS_CAPTURE_HIGH_PRIORITY);
                 examCaptureCloudService.updateExamCaptureQueuePriority(req);
@@ -264,12 +262,66 @@ public class ExamControlServiceImpl implements ExamControlService {
         examRecordData.setExamRecordStatus(ExamRecordStatus.EXAM_HAND_IN);
 
         // 保存考试记录
-        examRecordDataService.saveExamRecordDataCache(examRecordDataId,examRecordData);
+        examRecordDataService.saveExamRecordDataCache(examRecordDataId, examRecordData);
 
         // 删除redis会话
         examingSessionService.deleteExamingSession(studentId);
     }
 
+    @Override
+    public EndExamInfo getEndExamInfo(Long examRecordDataId) {
+        ExamRecordData examRecordData = examRecordDataService.getExamRecordDataCache(examRecordDataId);
+        // 如果考试没有最终结束,则返回空值
+        if (examRecordData.getExamRecordStatus() != ExamRecordStatus.EXAM_END
+                && examRecordData.getExamRecordStatus() != ExamRecordStatus.EXAM_OVERDUE) {
+            return null;
+        }
+        EndExamInfo endExamInfo = new EndExamInfo();
+        endExamInfo.setExamRecordDataId(examRecordDataId);
+        endExamInfo.setIsWarn(examRecordData.getIsWarn());// 是否异常数据
+        endExamInfo.setObjectiveScore(examRecordData.getObjectiveScore());// 客观题总分
+        endExamInfo.setObjectiveAccuracy(examRecordData.getObjectiveAccuracy());// 客观点答对比率
+        return endExamInfo;
+    }
+
+    @Override
+    public CheckQrCodeInfo checkQrCode(String qrCode) {
+        return null;
+    }
+
+    /**
+     * 发送消息到websocket
+     *
+     * @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) throws Exception {
+
+    }
+
+    /**
+     * 通过websocket发送二维码扫描信息
+     *
+     * @param clientId
+     * @param examRecordDataId
+     * @param order
+     * @throws Exception
+     */
+    @Override
+    public void sendScanQrCodeToWebSocket(String clientId, Long examRecordDataId, Integer order) throws Exception {
+
+    }
+
+    @Override
+    public UpyunSignatureInfo getUpyunSignature(GetUpyunSignatureReq req) {
+        return null;
+    }
+
     /**
      * 检查并返回考试 开考条件 1.enable为true 2.开始时间和结束时间判断 3.examLimit为null或false
      * 4.剩余考试次数>0
@@ -386,10 +438,8 @@ public class ExamControlServiceImpl implements ExamControlService {
     /**
      * 获取试卷结构 小题乱序、选项乱序
      *
-     * @param extractConfig
-     *            调卷规则对象
-     * @param paperStruct
-     *            试卷结构对象
+     * @param extractConfig 调卷规则对象
+     * @param paperStruct   试卷结构对象
      */
     private void reorderPaperStruct(ExtractConfigCacheBean extractConfig, ExtractConfigPaperCacheBean paperStruct) {
         // 小题乱序
@@ -521,7 +571,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());
@@ -628,8 +678,7 @@ public class ExamControlServiceImpl implements ExamControlService {
     /**
      * 如果有序列化锁,则延迟交卷
      *
-     * @param examRecordDataId
-     *            考试记录id
+     * @param examRecordDataId 考试记录id
      * @return
      */
     private void delayHandInExamIfLocked(Long examRecordDataId) {
@@ -675,8 +724,7 @@ public class ExamControlServiceImpl implements ExamControlService {
     /**
      * 考试心跳每分钟调用一次
      *
-     * @param user
-     *            学生
+     * @param user 学生
      */
     @Override
     public long examHeartbeat(User user) {
@@ -718,7 +766,7 @@ public class ExamControlServiceImpl implements ExamControlService {
         Long examUsedMilliSeconds = examingSession.getCost() * 1000;
         // 如果没有超过冻结时间,抛出异常
         if (examingSession.getExamType().equals(ExamType.ONLINE.name())) {
-            ExamRecordData examRecordData  = examRecordDataService.getExamRecordDataCache(examingSession.getExamRecordDataId());
+            ExamRecordData examRecordData = examRecordDataService.getExamRecordDataCache(examingSession.getExamRecordDataId());
 
             if (examRecordData != null && examRecordData.getIsExceed() != null && examRecordData.getIsExceed()) {// 超过断点最大次数的不校验冻结时间
                 return examUsedMilliSeconds;

+ 52 - 0
examcloud-core-oe-student-service/src/main/java/cn/com/qmth/examcloud/core/oe/student/service/impl/ExamFileAnswerServiceImpl.java

@@ -0,0 +1,52 @@
+package cn.com.qmth.examcloud.core.oe.student.service.impl;
+
+import cn.com.qmth.examcloud.core.oe.student.service.ExamFileAnswerService;
+import cn.com.qmth.examcloud.support.examing.ExamFileAnswer;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @Description 文件作答实现类
+ * @Author lideyin
+ * @Date 2019/12/13 18:57
+ * @Version 1.0
+ */
+@Service("examFileAnswerService")
+public class ExamFileAnswerServiceImpl implements ExamFileAnswerService {
+    @Autowired
+    private RedisClient redisClient;
+
+    /**
+     * 保存文件作答结果
+     *
+     * @param fileAnswerId
+     * @param examFileAnswer
+     * @return 返回主键id
+     */
+    @Override
+    public void saveFileAnswer(String fileAnswerId, ExamFileAnswer examFileAnswer) {
+        redisClient.set(fileAnswerId, examFileAnswer, 24 * 60 * 60);
+    }
+
+    /**
+     * 删除文件作答记录
+     *
+     * @param fileAnswerId
+     */
+    @Override
+    public void deleteFileAnswer(String fileAnswerId) {
+        redisClient.delete(fileAnswerId);
+    }
+
+    /**
+     * 获取文件作答结果
+     *
+     * @param fileAnswerId
+     * @return
+     */
+    @Override
+    public ExamFileAnswer getFileAnswer(String fileAnswerId) {
+        return redisClient.get(fileAnswerId,ExamFileAnswer.class);
+    }
+}