xiatian 4 жил өмнө
parent
commit
3632b92776

+ 60 - 1
themis-business/src/main/java/com/qmth/themis/business/cache/RedisKeyHelper.java

@@ -1,19 +1,78 @@
 package com.qmth.themis.business.cache;
 
 public class RedisKeyHelper {
+	private static String underLine = "_";
+
 	private static String examActivityKeyPrefix = "exam_activity::";
 	private static String examStudentKeyPrefix = "exam_student::";
 	private static String examRecordKeyPrefix = "exam_record::";
+	private static String studentPaperStructKeyPrefix = "student_paper_struct::";
+	private static String studentAnswerKeyPrefix = "student_answer::";
 
+	/**
+	 * 场次
+	 * 
+	 * @param activityId
+	 * @return
+	 */
 	public static String examActivityCacheKey(Long activityId) {
 		return examActivityKeyPrefix + activityId;
 	}
 
+	/**
+	 * 考生
+	 * 
+	 * @param examStudentId
+	 * @return
+	 */
 	public static String examStudentCacheKey(Long examStudentId) {
 		return examStudentKeyPrefix + examStudentId;
 	}
-	
+
+	/**
+	 * 考试记录
+	 * 
+	 * @param examRecordId
+	 * @return
+	 */
 	public static String examRecordCacheKey(Long examRecordId) {
 		return examRecordKeyPrefix + examRecordId;
 	}
+
+	/**
+	 * 考生试卷结构
+	 * 
+	 * @param examRecordId
+	 * @return
+	 */
+	public static String studentPaperStructKey(Long examRecordId) {
+		return studentPaperStructKeyPrefix + examRecordId;
+	}
+
+	/**
+	 * 考生作答
+	 * 
+	 * @param examRecordId
+	 * @return
+	 */
+	public static String examAnswerKey(Long examRecordId) {
+		return studentAnswerKeyPrefix + examRecordId;
+	}
+
+	/**
+	 * 考生作答 hashKey
+	 * 
+	 * @param mainNumber
+	 * @param subNumber
+	 * @param subIndex
+	 * @return
+	 */
+	public static String examAnswerHashKey(Integer mainNumber, Integer subNumber, Integer subIndex) {
+		if (subIndex == null) {
+			return mainNumber + underLine + subNumber;
+		} else {
+			return mainNumber + underLine + subNumber + underLine + subIndex + underLine;
+		}
+	}
+
 }

+ 49 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamStudentAnswerCacheBean.java

@@ -0,0 +1,49 @@
+package com.qmth.themis.business.cache.bean;
+
+import java.io.Serializable;
+
+public class ExamStudentAnswerCacheBean implements Serializable {
+
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -7505986290431178431L;
+
+	// 作答内容
+	private String content;
+
+	// 版本时间戳
+	private Long version;
+	
+	//作答时长 秒
+	private Integer duration;
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public Long getVersion() {
+		return version;
+	}
+
+	public void setVersion(Long version) {
+		this.version = version;
+	}
+
+	public Integer getDuration() {
+		return duration;
+	}
+
+	public void setDuration(Integer duration) {
+		this.duration = duration;
+	}
+
+
+
+}

+ 36 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamStudentPaperStructCacheBean.java

@@ -0,0 +1,36 @@
+package com.qmth.themis.business.cache.bean;
+
+import java.io.Serializable;
+
+public class ExamStudentPaperStructCacheBean implements Serializable {
+
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 6940234295672823984L;
+
+	// 内容
+	private String content;
+
+	// 上传时间
+	private Long time;
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public Long getTime() {
+		return time;
+	}
+
+	public void setTime(Long time) {
+		this.time = time;
+	}
+
+
+}

+ 14 - 0
themis-business/src/main/java/com/qmth/themis/business/service/TEExamService.java

@@ -71,4 +71,18 @@ public interface TEExamService extends IService<TEExam> {
 	 * @return
 	 */
 	public Long studentPaperStruct(Long studentId, Long recordId,String content);
+	
+	/**提交作答结果
+	 * @param studentId
+	 * @param recordId
+	 * @param mainNumber
+	 * @param subNumber
+	 * @param subIndex
+	 * @param answer
+	 * @param version
+	 * @param durationSeconds
+	 * @return
+	 */
+	public Long answerSubmit(Long studentId, Long recordId, Integer mainNumber, Integer subNumber, Integer subIndex,
+			String answer, Long version, Integer durationSeconds);
 }

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

@@ -1,12 +1,10 @@
 package com.qmth.themis.business.service.impl;
 
-import java.io.File;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.UUID;
 
 import javax.annotation.Resource;
 
@@ -23,7 +21,9 @@ import com.qmth.themis.business.cache.bean.ExamActivityCacheBean;
 import com.qmth.themis.business.cache.bean.ExamCourseCacheBean;
 import com.qmth.themis.business.cache.bean.ExamPaperCacheBean;
 import com.qmth.themis.business.cache.bean.ExamRecordCacheBean;
+import com.qmth.themis.business.cache.bean.ExamStudentAnswerCacheBean;
 import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
+import com.qmth.themis.business.cache.bean.ExamStudentPaperStructCacheBean;
 import com.qmth.themis.business.config.SystemConfig;
 import com.qmth.themis.business.dao.TEExamMapper;
 import com.qmth.themis.business.entity.TEExam;
@@ -36,7 +36,6 @@ import com.qmth.themis.business.service.TOeExamRecordService;
 import com.qmth.themis.business.util.OssUtil;
 import com.qmth.themis.business.util.RedisUtil;
 import com.qmth.themis.common.exception.BusinessException;
-import com.qmth.themis.common.util.FileUtil;
 
 /**
  * @Description: 考试批次 服务实现类
@@ -276,38 +275,61 @@ public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> impleme
 
 
     @Transactional
-    @Override
-    public Long studentPaperStruct(Long studentId, Long recordId, String content) {
-        ExamRecordCacheBean er = (ExamRecordCacheBean) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId));
-        if (er == null) {
-            throw new BusinessException("未找到考试记录");
-        }
-        ExamStudentCacheBean es = (ExamStudentCacheBean) redisUtil
-                .get(RedisKeyHelper.examStudentCacheKey(er.getExamStudentId()));
-        if (es == null) {
-            throw new BusinessException("未找到考生");
-        }
-        if (studentId.equals(es.getStudentId())) {
-            throw new BusinessException("考试记录的学生Id和当前登录用户不一致");
-        }
-
-        String tempDir = systemConfig.getProperty("sys.config.tempDataDir");
-        File file = new File(tempDir + "/" + uuid() + ".json");
-        try {
-            FileUtil.saveAsFile(file.getAbsolutePath(), content);
-            String filePath = "upload/" + sdf.format(new Date()) + "/" + uuid() + ".json";
-            er.setPaperStruct(filePath);
-            er.setPaperStructUpload(1);
-            OssUtil.ossUpload(systemConfig.getOssEnv(3), filePath, file);
-            // 更新考试记录缓存
-            redisUtil.set(RedisKeyHelper.examRecordCacheKey(recordId), er);
-            return new Date().getTime();
-        } finally {
-            FileUtil.deleteFile(file);
-        }
-    }
-
-    private String uuid() {
-        return UUID.randomUUID().toString().replaceAll("-", "");
-    }
+	@Override
+	public Long studentPaperStruct(Long studentId, Long recordId, String content) {
+		ExamRecordCacheBean er = (ExamRecordCacheBean) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId));
+		if (er == null) {
+			throw new BusinessException("未找到考试记录");
+		}
+		ExamStudentCacheBean es = (ExamStudentCacheBean) redisUtil
+				.get(RedisKeyHelper.examStudentCacheKey(er.getExamStudentId()));
+		if (es == null) {
+			throw new BusinessException("未找到考生");
+		}
+		if (studentId.equals(es.getStudentId())) {
+			throw new BusinessException("考试记录的学生Id和当前登录用户不一致");
+		}
+		ExamStudentPaperStructCacheBean struct = new ExamStudentPaperStructCacheBean();
+		struct.setContent(content);
+		struct.setTime(new Date().getTime());
+		// 更新考生试卷结构
+		redisUtil.set(RedisKeyHelper.studentPaperStructKey(recordId), struct);
+		return struct.getTime();
+	}
+
+	@Transactional
+	@Override
+	public Long answerSubmit(Long studentId, Long recordId, Integer mainNumber, Integer subNumber, Integer subIndex,
+			String answer, Long version, Integer durationSeconds) {
+		ExamRecordCacheBean er = (ExamRecordCacheBean) redisUtil.get(RedisKeyHelper.examRecordCacheKey(recordId));
+		if (er == null) {
+			throw new BusinessException("未找到考试记录");
+		}
+		ExamStudentCacheBean es = (ExamStudentCacheBean) redisUtil
+				.get(RedisKeyHelper.examStudentCacheKey(er.getExamStudentId()));
+		if (es == null) {
+			throw new BusinessException("未找到考生");
+		}
+		if (studentId.equals(es.getStudentId())) {
+			throw new BusinessException("考试记录的学生Id和当前登录用户不一致");
+		}
+		ExamStudentAnswerCacheBean answerCache = (ExamStudentAnswerCacheBean) redisUtil
+				.get(RedisKeyHelper.examAnswerKey(er.getExamStudentId()),RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex));
+		if (answerCache == null) {
+			answerCache = new ExamStudentAnswerCacheBean();
+			answerCache.setContent(answer);
+			answerCache.setVersion(version);
+			answerCache.setDuration(durationSeconds);
+		}else {
+			if(version.longValue()<answerCache.getVersion().longValue()) {
+				throw new BusinessException("版本时间戳过期");
+			}
+			answerCache.setContent(answer);
+			answerCache.setVersion(version);
+			answerCache.setDuration(durationSeconds);
+		}
+		// 更新考生作答
+		redisUtil.set(RedisKeyHelper.examAnswerKey(er.getExamStudentId()),RedisKeyHelper.examAnswerHashKey(mainNumber, subNumber, subIndex), answerCache);
+		return version;
+	}
 }

+ 23 - 1
themis-exam/src/main/java/com/qmth/themis/exam/api/TEExamController.java

@@ -96,7 +96,7 @@ public class TEExamController {
 	}
 	
 	@ApiOperation(value = "上传个人试卷结构")
-	@RequestMapping(value = "/student_paper_struct", method = RequestMethod.POST)
+	@RequestMapping(value = "/student_paper_struct/upload", method = RequestMethod.POST)
 	@ApiResponses({ @ApiResponse(code = 200, message = "试卷信息") })
 	public Long studentPaperStruct(@ApiParam(value = "考生ID", required = true) @RequestParam Long recordId,
 			@ApiParam(value = "试卷结构json字符串", required = true) @RequestParam String content) {
@@ -113,5 +113,27 @@ public class TEExamController {
 		}
 	}
 	
+	@ApiOperation(value = "提交作答结果")
+	@RequestMapping(value = "/answer/submit", method = RequestMethod.POST)
+	@ApiResponses({ @ApiResponse(code = 200, message = "试卷信息") })
+	public Long answerSubmit(@ApiParam(value = "考生ID", required = true) @RequestParam Long recordId,
+			@ApiParam(value = "大题号", required = true) @RequestParam Integer mainNumber,
+			@ApiParam(value = "小题号", required = true) @RequestParam Integer subNumber,
+			@ApiParam(value = "套题子题序号", required = false) @RequestParam Integer subIndex,
+			@ApiParam(value = "答案json字符串", required = true) @RequestParam String answer,
+			@ApiParam(value = "时间戳", required = true) @RequestParam Long version,
+			@ApiParam(value = "作答累计秒数", required = false) @RequestParam Integer durationSeconds) {
+		TEStudent teStudent = (TEStudent) ServletUtil.getRequestStudentAccount();
+		String lockKey = SystemConstant.REDIS_LOCK_STUDENT_PREFIX + teStudent.getId();
+		Boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_CACHE_TIME_OUT);
+		if (!lock) {
+			throw new BusinessException(ExceptionResultEnum.REQUEST_AWAIT);
+		}
+		try {
+			return teExamService.answerSubmit(teStudent.getId(), recordId,mainNumber,subNumber,subIndex,answer,version,durationSeconds);
+		} finally {
+			redisUtil.releaseLock(lockKey);
+		}
+	}
 	
 }