Browse Source

Merge remote-tracking branch 'origin/dev' into dev

wangliang 5 years ago
parent
commit
87337d7c93
22 changed files with 1392 additions and 151 deletions
  1. 10 0
      themis-business/src/main/java/com/qmth/themis/business/cache/RedisKeyHelper.java
  2. 9 11
      themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamActivityCacheBean.java
  3. 134 0
      themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamCourseCacheBean.java
  4. 185 0
      themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamPaperCacheBean.java
  5. 476 0
      themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamRecordCacheBean.java
  6. 131 0
      themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamStudentCacheBean.java
  7. 1 1
      themis-business/src/main/java/com/qmth/themis/business/dao/TEExamCourseMapper.java
  8. 5 0
      themis-business/src/main/java/com/qmth/themis/business/dao/TEExamPaperMapper.java
  9. 5 1
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamCourseService.java
  10. 3 0
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamPaperService.java
  11. 4 5
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamService.java
  12. 3 0
      themis-business/src/main/java/com/qmth/themis/business/service/TEExamStudentService.java
  13. 2 0
      themis-business/src/main/java/com/qmth/themis/business/service/TOeExamRecordService.java
  14. 73 27
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamCourseServiceImpl.java
  15. 30 0
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamPaperServiceImpl.java
  16. 185 52
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamServiceImpl.java
  17. 32 4
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamStudentServiceImpl.java
  18. 39 4
      themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeExamRecordServiceImpl.java
  19. 4 4
      themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskExamPaperImportTemplete.java
  20. 51 40
      themis-business/src/main/resources/mapper/TEExamCourseMapper.xml
  21. 8 0
      themis-business/src/main/resources/mapper/TEExamPaperMapper.xml
  22. 2 2
      themis-exam/src/main/java/com/qmth/themis/exam/api/TEExamController.java

+ 10 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/RedisKeyHelper.java

@@ -2,8 +2,18 @@ package com.qmth.themis.business.cache;
 
 public class RedisKeyHelper {
 	private static String examActivityKeyPrefix = "exam_activity::";
+	private static String examStudentKeyPrefix = "exam_student::";
+	private static String examRecordKeyPrefix = "exam_record::";
 
 	public static String examActivityCacheKey(Long activityId) {
 		return examActivityKeyPrefix + activityId;
 	}
+
+	public static String examStudentCacheKey(Long examStudentId) {
+		return examStudentKeyPrefix + examStudentId;
+	}
+	
+	public static String examRecordCacheKey(Long examRecordId) {
+		return examRecordKeyPrefix + examRecordId;
+	}
 }

+ 9 - 11
themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamActivityCacheBean.java

@@ -3,38 +3,36 @@ package com.qmth.themis.business.cache.bean;
 import java.io.Serializable;
 import java.util.Date;
 
-import io.swagger.annotations.ApiModelProperty;
-
 public class ExamActivityCacheBean implements Serializable {
 	/**
 	 * 
 	 */
 	private static final long serialVersionUID = -5888311300472703230L;
 
-	@ApiModelProperty(value = "主键")
+	//主键
 	private Long id;
-	@ApiModelProperty(value = "批次id")
+	//批次id
 	private Long examId;
 
-	@ApiModelProperty(value = "场次代码")
+	//场次代码
 	private String code;
 
-	@ApiModelProperty(value = "提前多长时间开始候考(分钟)")
+	//提前多长时间开始候考(分钟)
 	private Integer prepareSeconds;
 
-	@ApiModelProperty(value = "最大考试时长")
+	//最大考试时长
 	private Integer maxDurationSeconds;
 
-	@ApiModelProperty(value = "是否启用,0:停用,1:启用")
+	//是否启用,0:停用,1:启用
 	private Integer enable;
 
-	@ApiModelProperty(value = "允许开考时长(分钟)")
+	//允许开考时长(分钟)
 	private Integer openingSeconds;
 
-	@ApiModelProperty(value = "开考时间")
+	//开考时间
 	private Date startTime;
 
-	@ApiModelProperty(value = "结束时间")
+	//结束时间
 	private Date finishTime;
 
 	public Long getExamId() {

+ 134 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamCourseCacheBean.java

@@ -0,0 +1,134 @@
+package com.qmth.themis.business.cache.bean;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class ExamCourseCacheBean implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -9033695063460046875L;
+
+	// 主键
+	private Long id;
+
+	// 考试ID
+	private Long examId;
+
+	// 科目编码
+	private String courseCode;
+
+	// 科目名称
+	private String courseName;
+
+	// 是否开启客观题乱序,0:不开启,1:开启
+	private Integer objectiveShuffle;
+
+	// 是否选项乱序,针对一个科目进行设置,0:不开启,1:开启
+	private Integer optionShuffle;
+
+	// 已绑定试卷数量
+	private Integer paperCount;
+
+	// 是否已补齐标答,0:否,1:是
+	private Integer hasAnswer;
+
+	// 音频播放次数
+	private Integer audioPlayTime;
+
+	// 试卷id
+	private List<Long> paperIds;
+
+	// 调卷比例
+	private List<Double> paperWeight;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	public String getCourseName() {
+		return courseName;
+	}
+
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+
+	public Integer getObjectiveShuffle() {
+		return objectiveShuffle;
+	}
+
+	public void setObjectiveShuffle(Integer objectiveShuffle) {
+		this.objectiveShuffle = objectiveShuffle;
+	}
+
+	public Integer getOptionShuffle() {
+		return optionShuffle;
+	}
+
+	public void setOptionShuffle(Integer optionShuffle) {
+		this.optionShuffle = optionShuffle;
+	}
+
+	public Integer getPaperCount() {
+		return paperCount;
+	}
+
+	public void setPaperCount(Integer paperCount) {
+		this.paperCount = paperCount;
+	}
+
+	public Integer getHasAnswer() {
+		return hasAnswer;
+	}
+
+	public void setHasAnswer(Integer hasAnswer) {
+		this.hasAnswer = hasAnswer;
+	}
+
+	public Integer getAudioPlayTime() {
+		return audioPlayTime;
+	}
+
+	public void setAudioPlayTime(Integer audioPlayTime) {
+		this.audioPlayTime = audioPlayTime;
+	}
+
+	public List<Long> getPaperIds() {
+		return paperIds;
+	}
+
+	public void setPaperIds(List<Long> paperIds) {
+		this.paperIds = paperIds;
+	}
+
+	public List<Double> getPaperWeight() {
+		return paperWeight;
+	}
+
+	public void setPaperWeight(List<Double> paperWeight) {
+		this.paperWeight = paperWeight;
+	}
+
+}

+ 185 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamPaperCacheBean.java

@@ -0,0 +1,185 @@
+package com.qmth.themis.business.cache.bean;
+
+import java.io.Serializable;
+
+public class ExamPaperCacheBean implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 5040597530688212719L;
+
+	// 主键
+	private Long id;
+	// 批次id
+	private Long examId;
+
+	// 科目编码
+	private String courseCode;
+
+	// 试卷编码
+	private String code;
+	// 试卷名称
+	private String name;
+
+	// 试卷总分
+	private Double totalScore;
+
+	// 题干路径
+	private String paperPath;
+	// 结构文件路径
+	private String structPath;
+	// 标答路径
+	private String answerPath;
+	// 解密密钥
+	private String decryptSecret;
+
+	// 解密向量
+	private String decryptVector;
+
+	// 0:自动,1:手动,2:不加密
+	private Integer encryptMode;
+
+	// 是否需要语音作答,0:不需要,1:需要
+	private Integer needVoiceAnswer;
+
+	// 题干包含音频,0:不包含,1:包含
+	private Integer hasAudio;
+
+	// 调卷比例
+	private Double weight;
+
+	// 音频播放次数
+	private Integer audioPlayTime;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Double getTotalScore() {
+		return totalScore;
+	}
+
+	public void setTotalScore(Double totalScore) {
+		this.totalScore = totalScore;
+	}
+
+	public String getPaperPath() {
+		return paperPath;
+	}
+
+	public void setPaperPath(String paperPath) {
+		this.paperPath = paperPath;
+	}
+
+	public String getStructPath() {
+		return structPath;
+	}
+
+	public void setStructPath(String structPath) {
+		this.structPath = structPath;
+	}
+
+	public String getAnswerPath() {
+		return answerPath;
+	}
+
+	public void setAnswerPath(String answerPath) {
+		this.answerPath = answerPath;
+	}
+
+	public String getDecryptSecret() {
+		return decryptSecret;
+	}
+
+	public void setDecryptSecret(String decryptSecret) {
+		this.decryptSecret = decryptSecret;
+	}
+
+	public String getDecryptVector() {
+		return decryptVector;
+	}
+
+	public void setDecryptVector(String decryptVector) {
+		this.decryptVector = decryptVector;
+	}
+
+	public Integer getEncryptMode() {
+		return encryptMode;
+	}
+
+	public void setEncryptMode(Integer encryptMode) {
+		this.encryptMode = encryptMode;
+	}
+
+	public Integer getNeedVoiceAnswer() {
+		return needVoiceAnswer;
+	}
+
+	public void setNeedVoiceAnswer(Integer needVoiceAnswer) {
+		this.needVoiceAnswer = needVoiceAnswer;
+	}
+
+	public Integer getHasAudio() {
+		return hasAudio;
+	}
+
+	public void setHasAudio(Integer hasAudio) {
+		this.hasAudio = hasAudio;
+	}
+
+	public Double getWeight() {
+		return weight;
+	}
+
+	public void setWeight(Double weight) {
+		this.weight = weight;
+	}
+
+	public Integer getAudioPlayTime() {
+		return audioPlayTime;
+	}
+
+	public void setAudioPlayTime(Integer audioPlayTime) {
+		this.audioPlayTime = audioPlayTime;
+	}
+	
+	
+
+}

+ 476 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamRecordCacheBean.java

@@ -0,0 +1,476 @@
+package com.qmth.themis.business.cache.bean;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public class ExamRecordCacheBean implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 4130111410312390960L;
+
+	//主键
+    private Long id;
+
+    //考试批次ID
+    private Long examId;
+
+    //考试场次ID
+    private Long examActivityId;
+
+    //考生ID
+    private Long examStudentId;
+
+    //实际使用的试卷ID
+    private Long paperId;
+
+    //全部作答保存地址
+    private String answerPath;
+
+    //当前状态
+    private Integer status;
+
+    //首次进入候考时间
+    private Date firstPrepareTime;
+
+    //首次开考时间
+    private Date firstStartTime;
+
+    //最近断点时间
+    private Date lastBreakTime;
+
+    //最近恢复候考时间
+    private Date lastPrepareTime;
+
+    //最近恢复开考时间
+    private Date lastStartTime;
+
+    //剩余断点续考次数
+    private Integer leftBreakResumeCount;
+
+    //客户端当前IP地址
+    private String clientCurrentIp;
+
+    //客户端websocket状态
+    private Integer clientWebsocketStatus;
+
+    //客户端websocket连接标识
+    private String clientWebsocketId;
+
+    //客户端最近同步时间
+    private Date clientLastSyncTime;
+
+    //微信小程序websocket状态
+    private Integer wxappWebsocketStatus;
+
+    //微信小程序websocket连接标识
+    private String wxappWebsocketId;
+
+    //微信小程序最近同步时间
+    private Date wxappLastSyncTime;
+
+    //客户端推流状态
+    private Integer clientVideoPushStatus;
+
+    //客户端推流地址
+    private String clientVideoPushKey;
+
+    //微信小程序推流状态
+    private Integer wxappVideoPushStatus;
+
+    //微信小程序推流地址
+    private String wxappVideoPushKey;
+
+    //答题进度
+    private Double answerProgress;
+
+    //累计考试用时
+    private Integer durationSeconds;
+
+    //交卷时间
+    private Date finishTime;
+
+    //交卷原因
+    private Integer finishType;
+
+    //预警次数
+    private Integer warningCount;
+
+    //审核结果
+    private Integer reviewResult;
+
+    //客观题分数
+    private Double objectiveScore;
+
+    //试题下载,0:是,1:不是
+    private Integer paperDownload;
+
+    //是否违纪,0:是,1:不是
+    private Integer breachStatus;
+
+    //个人试卷结构
+    private String paperStruct;
+
+    //个人试卷结构是否已上传,0:未上传,1:已上传
+    private Integer paperStructUpload;
+
+    //第几次考试
+    private Integer serialNumber;
+
+    //最近断点记录ID
+    private Long lastBreakId;
+
+    //开考身份验证结果
+    private Integer entryAuthenticationResult;
+
+    //开考身份验证记录ID
+    private Long entryAuthenticationId;
+
+    //当前过程人脸检测状态
+    private Integer inProcessFaceVerifyStatus;
+
+    //当前过程活体验证状态
+    private Integer inProcessLivenessVerifyStatus;
+
+    //已完成过程活体验证次数
+    private Integer inProcessLivenessVerifyCount;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	public Long getExamActivityId() {
+		return examActivityId;
+	}
+
+	public void setExamActivityId(Long examActivityId) {
+		this.examActivityId = examActivityId;
+	}
+
+	public Long getExamStudentId() {
+		return examStudentId;
+	}
+
+	public void setExamStudentId(Long examStudentId) {
+		this.examStudentId = examStudentId;
+	}
+
+	public Long getPaperId() {
+		return paperId;
+	}
+
+	public void setPaperId(Long paperId) {
+		this.paperId = paperId;
+	}
+
+	public String getAnswerPath() {
+		return answerPath;
+	}
+
+	public void setAnswerPath(String answerPath) {
+		this.answerPath = answerPath;
+	}
+
+	public Integer getStatus() {
+		return status;
+	}
+
+	public void setStatus(Integer status) {
+		this.status = status;
+	}
+
+	public Date getFirstPrepareTime() {
+		return firstPrepareTime;
+	}
+
+	public void setFirstPrepareTime(Date firstPrepareTime) {
+		this.firstPrepareTime = firstPrepareTime;
+	}
+
+	public Date getFirstStartTime() {
+		return firstStartTime;
+	}
+
+	public void setFirstStartTime(Date firstStartTime) {
+		this.firstStartTime = firstStartTime;
+	}
+
+	public Date getLastBreakTime() {
+		return lastBreakTime;
+	}
+
+	public void setLastBreakTime(Date lastBreakTime) {
+		this.lastBreakTime = lastBreakTime;
+	}
+
+	public Date getLastPrepareTime() {
+		return lastPrepareTime;
+	}
+
+	public void setLastPrepareTime(Date lastPrepareTime) {
+		this.lastPrepareTime = lastPrepareTime;
+	}
+
+	public Date getLastStartTime() {
+		return lastStartTime;
+	}
+
+	public void setLastStartTime(Date lastStartTime) {
+		this.lastStartTime = lastStartTime;
+	}
+
+	public Integer getLeftBreakResumeCount() {
+		return leftBreakResumeCount;
+	}
+
+	public void setLeftBreakResumeCount(Integer leftBreakResumeCount) {
+		this.leftBreakResumeCount = leftBreakResumeCount;
+	}
+
+	public String getClientCurrentIp() {
+		return clientCurrentIp;
+	}
+
+	public void setClientCurrentIp(String clientCurrentIp) {
+		this.clientCurrentIp = clientCurrentIp;
+	}
+
+	public Integer getClientWebsocketStatus() {
+		return clientWebsocketStatus;
+	}
+
+	public void setClientWebsocketStatus(Integer clientWebsocketStatus) {
+		this.clientWebsocketStatus = clientWebsocketStatus;
+	}
+
+	public String getClientWebsocketId() {
+		return clientWebsocketId;
+	}
+
+	public void setClientWebsocketId(String clientWebsocketId) {
+		this.clientWebsocketId = clientWebsocketId;
+	}
+
+	public Date getClientLastSyncTime() {
+		return clientLastSyncTime;
+	}
+
+	public void setClientLastSyncTime(Date clientLastSyncTime) {
+		this.clientLastSyncTime = clientLastSyncTime;
+	}
+
+	public Integer getWxappWebsocketStatus() {
+		return wxappWebsocketStatus;
+	}
+
+	public void setWxappWebsocketStatus(Integer wxappWebsocketStatus) {
+		this.wxappWebsocketStatus = wxappWebsocketStatus;
+	}
+
+	public String getWxappWebsocketId() {
+		return wxappWebsocketId;
+	}
+
+	public void setWxappWebsocketId(String wxappWebsocketId) {
+		this.wxappWebsocketId = wxappWebsocketId;
+	}
+
+	public Date getWxappLastSyncTime() {
+		return wxappLastSyncTime;
+	}
+
+	public void setWxappLastSyncTime(Date wxappLastSyncTime) {
+		this.wxappLastSyncTime = wxappLastSyncTime;
+	}
+
+	public Integer getClientVideoPushStatus() {
+		return clientVideoPushStatus;
+	}
+
+	public void setClientVideoPushStatus(Integer clientVideoPushStatus) {
+		this.clientVideoPushStatus = clientVideoPushStatus;
+	}
+
+	public String getClientVideoPushKey() {
+		return clientVideoPushKey;
+	}
+
+	public void setClientVideoPushKey(String clientVideoPushKey) {
+		this.clientVideoPushKey = clientVideoPushKey;
+	}
+
+	public Integer getWxappVideoPushStatus() {
+		return wxappVideoPushStatus;
+	}
+
+	public void setWxappVideoPushStatus(Integer wxappVideoPushStatus) {
+		this.wxappVideoPushStatus = wxappVideoPushStatus;
+	}
+
+	public String getWxappVideoPushKey() {
+		return wxappVideoPushKey;
+	}
+
+	public void setWxappVideoPushKey(String wxappVideoPushKey) {
+		this.wxappVideoPushKey = wxappVideoPushKey;
+	}
+
+	public Double getAnswerProgress() {
+		return answerProgress;
+	}
+
+	public void setAnswerProgress(Double answerProgress) {
+		this.answerProgress = answerProgress;
+	}
+
+	public Integer getDurationSeconds() {
+		return durationSeconds;
+	}
+
+	public void setDurationSeconds(Integer durationSeconds) {
+		this.durationSeconds = durationSeconds;
+	}
+
+	public Date getFinishTime() {
+		return finishTime;
+	}
+
+	public void setFinishTime(Date finishTime) {
+		this.finishTime = finishTime;
+	}
+
+	public Integer getFinishType() {
+		return finishType;
+	}
+
+	public void setFinishType(Integer finishType) {
+		this.finishType = finishType;
+	}
+
+	public Integer getWarningCount() {
+		return warningCount;
+	}
+
+	public void setWarningCount(Integer warningCount) {
+		this.warningCount = warningCount;
+	}
+
+	public Integer getReviewResult() {
+		return reviewResult;
+	}
+
+	public void setReviewResult(Integer reviewResult) {
+		this.reviewResult = reviewResult;
+	}
+
+	public Double getObjectiveScore() {
+		return objectiveScore;
+	}
+
+	public void setObjectiveScore(Double objectiveScore) {
+		this.objectiveScore = objectiveScore;
+	}
+
+	public Integer getPaperDownload() {
+		return paperDownload;
+	}
+
+	public void setPaperDownload(Integer paperDownload) {
+		this.paperDownload = paperDownload;
+	}
+
+	public Integer getBreachStatus() {
+		return breachStatus;
+	}
+
+	public void setBreachStatus(Integer breachStatus) {
+		this.breachStatus = breachStatus;
+	}
+
+	public String getPaperStruct() {
+		return paperStruct;
+	}
+
+	public void setPaperStruct(String paperStruct) {
+		this.paperStruct = paperStruct;
+	}
+
+	public Integer getPaperStructUpload() {
+		return paperStructUpload;
+	}
+
+	public void setPaperStructUpload(Integer paperStructUpload) {
+		this.paperStructUpload = paperStructUpload;
+	}
+
+	public Integer getSerialNumber() {
+		return serialNumber;
+	}
+
+	public void setSerialNumber(Integer serialNumber) {
+		this.serialNumber = serialNumber;
+	}
+
+	public Long getLastBreakId() {
+		return lastBreakId;
+	}
+
+	public void setLastBreakId(Long lastBreakId) {
+		this.lastBreakId = lastBreakId;
+	}
+
+	public Integer getEntryAuthenticationResult() {
+		return entryAuthenticationResult;
+	}
+
+	public void setEntryAuthenticationResult(Integer entryAuthenticationResult) {
+		this.entryAuthenticationResult = entryAuthenticationResult;
+	}
+
+	public Long getEntryAuthenticationId() {
+		return entryAuthenticationId;
+	}
+
+	public void setEntryAuthenticationId(Long entryAuthenticationId) {
+		this.entryAuthenticationId = entryAuthenticationId;
+	}
+
+	public Integer getInProcessFaceVerifyStatus() {
+		return inProcessFaceVerifyStatus;
+	}
+
+	public void setInProcessFaceVerifyStatus(Integer inProcessFaceVerifyStatus) {
+		this.inProcessFaceVerifyStatus = inProcessFaceVerifyStatus;
+	}
+
+	public Integer getInProcessLivenessVerifyStatus() {
+		return inProcessLivenessVerifyStatus;
+	}
+
+	public void setInProcessLivenessVerifyStatus(Integer inProcessLivenessVerifyStatus) {
+		this.inProcessLivenessVerifyStatus = inProcessLivenessVerifyStatus;
+	}
+
+	public Integer getInProcessLivenessVerifyCount() {
+		return inProcessLivenessVerifyCount;
+	}
+
+	public void setInProcessLivenessVerifyCount(Integer inProcessLivenessVerifyCount) {
+		this.inProcessLivenessVerifyCount = inProcessLivenessVerifyCount;
+	}
+
+    
+}

+ 131 - 0
themis-business/src/main/java/com/qmth/themis/business/cache/bean/ExamStudentCacheBean.java

@@ -0,0 +1,131 @@
+package com.qmth.themis.business.cache.bean;
+
+import java.io.Serializable;
+
+public class ExamStudentCacheBean implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -7154063361894874051L;
+
+	// 主键
+	private Long id;
+
+	// 批次id
+	private Long examId;
+
+	// 场次id
+	private Long examActivityId;
+
+	// 学生id
+	private Long studentId;
+
+	// 科目代码
+	private String courseCode;
+
+	// 科目名称
+	private String courseName;
+
+	// 虚拟考场代码,考试唯一
+	private String roomCode;
+
+	// 考场名称
+	private String roomName;
+
+	// 剩余考试次数
+	private Integer leftExamCount;
+	// 当前考试是第几次
+	private Integer currentSerialNumber;
+	// 当前考试记录ID
+	private Long currentRecordId;
+
+	public Integer getCurrentSerialNumber() {
+		return currentSerialNumber;
+	}
+
+	public void setCurrentSerialNumber(Integer currentSerialNumber) {
+		this.currentSerialNumber = currentSerialNumber;
+	}
+
+	public String getCourseName() {
+		return courseName;
+	}
+
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+
+	public String getRoomName() {
+		return roomName;
+	}
+
+	public void setRoomName(String roomName) {
+		this.roomName = roomName;
+	}
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	public Long getExamActivityId() {
+		return examActivityId;
+	}
+
+	public void setExamActivityId(Long examActivityId) {
+		this.examActivityId = examActivityId;
+	}
+
+	public Long getStudentId() {
+		return studentId;
+	}
+
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	public String getRoomCode() {
+		return roomCode;
+	}
+
+	public void setRoomCode(String roomCode) {
+		this.roomCode = roomCode;
+	}
+
+	public Integer getLeftExamCount() {
+		return leftExamCount;
+	}
+
+	public void setLeftExamCount(Integer leftExamCount) {
+		this.leftExamCount = leftExamCount;
+	}
+
+	public Long getCurrentRecordId() {
+		return currentRecordId;
+	}
+
+	public void setCurrentRecordId(Long currentRecordId) {
+		this.currentRecordId = currentRecordId;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+}

+ 1 - 1
themis-business/src/main/java/com/qmth/themis/business/dao/TEExamCourseMapper.java

@@ -30,5 +30,5 @@ public interface TEExamCourseMapper extends BaseMapper<TEExamCourse> {
      */
     public IPage<TEExamCourse> examCourseQuery(IPage<Map> iPage, @Param("examId") Long examId, @Param("courseCode") String courseCode, @Param("courseName") String courseName, @Param("hasPaper") Integer hasPaper);
 
-    public TEExamCourse findByCode(@Param("courseCode") String courseCode);
+    public TEExamCourse findByExamIdAndCourseCode(@Param("examId") Long examId,@Param("courseCode") String courseCode);
 }

+ 5 - 0
themis-business/src/main/java/com/qmth/themis/business/dao/TEExamPaperMapper.java

@@ -1,5 +1,7 @@
 package com.qmth.themis.business.dao;
 
+import java.util.List;
+
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -18,4 +20,7 @@ public interface TEExamPaperMapper extends BaseMapper<TEExamPaper> {
 
 	public TEExamPaper findByExamIdAndCourseCodeAndPaperCode(@Param("examId") Long examId,
 			@Param("courseCode") String courseCode, @Param("paperCode") String paperCode);
+
+	public List<TEExamPaper> findListByExamIdAndCourseCode(@Param("examId") Long examId,
+			@Param("courseCode") String courseCode);
 }

+ 5 - 1
themis-business/src/main/java/com/qmth/themis/business/service/TEExamCourseService.java

@@ -2,6 +2,7 @@ package com.qmth.themis.business.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.themis.business.cache.bean.ExamCourseCacheBean;
 import com.qmth.themis.business.entity.TEExamCourse;
 
 import java.util.Map;
@@ -27,5 +28,8 @@ public interface TEExamCourseService extends IService<TEExamCourse> {
      */
     public IPage<TEExamCourse> examCourseQuery(IPage<Map> iPage, Long examId, String courseCode, String courseName, Integer hasPaper);
 
-	TEExamCourse findByCode(String courseCode);
+
+	ExamCourseCacheBean getExamCourseCacheBean(Long examId, String courseCode);
+
+	TEExamCourse findByExamIdAndCourseCode(Long examId, String courseCode);
 }

+ 3 - 0
themis-business/src/main/java/com/qmth/themis/business/service/TEExamPaperService.java

@@ -1,6 +1,7 @@
 package com.qmth.themis.business.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.themis.business.cache.bean.ExamPaperCacheBean;
 import com.qmth.themis.business.entity.TEExamPaper;
 
 /**
@@ -14,4 +15,6 @@ public interface TEExamPaperService extends IService<TEExamPaper> {
 
 	TEExamPaper findByExamIdAndCourseCodeAndPaperCode(Long examId, String courseCode, String paperCode);
 
+	ExamPaperCacheBean getExamPaperCacheBean(Long paperId);
+
 }

+ 4 - 5
themis-business/src/main/java/com/qmth/themis/business/service/TEExamService.java

@@ -1,13 +1,12 @@
 package com.qmth.themis.business.service;
 
+import java.util.List;
+import java.util.Map;
+
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.qmth.themis.business.bean.exam.ExamPrepareBean;
 import com.qmth.themis.business.entity.TEExam;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.List;
-import java.util.Map;
 
 /**
  * @Description: 考试批次 服务类
@@ -44,5 +43,5 @@ public interface TEExamService extends IService<TEExam> {
 	 * @param activityId
 	 * @return
 	 */
-	public ExamPrepareBean prepare(Long studentId, Long activityId);
+	public ExamPrepareBean prepare(Long studentId, Long examStudentId);
 }

+ 3 - 0
themis-business/src/main/java/com/qmth/themis/business/service/TEExamStudentService.java

@@ -2,6 +2,7 @@ package com.qmth.themis.business.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
 import com.qmth.themis.business.entity.TEExamStudent;
 
 import java.util.Map;
@@ -30,4 +31,6 @@ public interface TEExamStudentService extends IService<TEExamStudent> {
      * @return
      */
     public IPage<Map> examStudentQuery(IPage<Map> iPage, Long examId, Long activityId, String identity, String name, String roomCode, String courseCode, String grade, Integer enable);
+
+	ExamStudentCacheBean getExamStudnetCacheBean(Long examStudentId);
 }

+ 2 - 0
themis-business/src/main/java/com/qmth/themis/business/service/TOeExamRecordService.java

@@ -21,4 +21,6 @@ public interface TOeExamRecordService extends IService<TOeExamRecord> {
      * @return
      */
     public Map getUnFinishExam(Long studentId);
+
+	Long saveByPrepare(Long examId, Long examActivityId, Long examStudentId, Long paperId, Integer serialNumber);
 }

+ 73 - 27
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamCourseServiceImpl.java

@@ -1,16 +1,22 @@
 package com.qmth.themis.business.service.impl;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 import javax.annotation.Resource;
 
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.themis.business.cache.bean.ExamCourseCacheBean;
 import com.qmth.themis.business.dao.TEExamCourseMapper;
+import com.qmth.themis.business.dao.TEExamPaperMapper;
 import com.qmth.themis.business.entity.TEExamCourse;
+import com.qmth.themis.business.entity.TEExamPaper;
 import com.qmth.themis.business.service.TEExamCourseService;
 import com.qmth.themis.common.exception.BusinessException;
 
@@ -22,31 +28,71 @@ import com.qmth.themis.common.exception.BusinessException;
  * @Date: 2020/6/25
  */
 @Service
-public class TEExamCourseServiceImpl extends ServiceImpl<TEExamCourseMapper, TEExamCourse> implements TEExamCourseService {
-
-    @Resource
-    TEExamCourseMapper teExamCourseMapper;
-
-    /**
-     * 查询考试科目
-     *
-     * @param iPage
-     * @param examId
-     * @param courseCode
-     * @param courseName
-     * @param hasPaper
-     * @return
-     */
-    @Override
-    public IPage<TEExamCourse> examCourseQuery(IPage<Map> iPage, Long examId, String courseCode, String courseName, Integer hasPaper) {
-        return teExamCourseMapper.examCourseQuery(iPage, examId, courseCode, courseName, hasPaper);
-    }
-    
-    @Override
-    public TEExamCourse findByCode(String courseCode) {
-    	if(StringUtils.isBlank(courseCode)) {
-    		throw new BusinessException("courseCode is null");
-    	}
-    	return teExamCourseMapper.findByCode(courseCode);
-    }
+public class TEExamCourseServiceImpl extends ServiceImpl<TEExamCourseMapper, TEExamCourse>
+		implements TEExamCourseService {
+
+	@Resource
+	TEExamCourseMapper teExamCourseMapper;
+
+	@Resource
+	TEExamPaperMapper teExamPaperMapper;
+
+	/**
+	 * 查询考试科目
+	 *
+	 * @param iPage
+	 * @param examId
+	 * @param courseCode
+	 * @param courseName
+	 * @param hasPaper
+	 * @return
+	 */
+	@Override
+	public IPage<TEExamCourse> examCourseQuery(IPage<Map> iPage, Long examId, String courseCode, String courseName,
+			Integer hasPaper) {
+		return teExamCourseMapper.examCourseQuery(iPage, examId, courseCode, courseName, hasPaper);
+	}
+
+	@Override
+	public TEExamCourse findByExamIdAndCourseCode(Long examId, String courseCode) {
+		if (examId == null) {
+			throw new BusinessException("examId is null");
+		}
+		if (StringUtils.isBlank(courseCode)) {
+			throw new BusinessException("courseCode is null");
+		}
+		return teExamCourseMapper.findByExamIdAndCourseCode(examId, courseCode);
+	}
+
+	@Cacheable(value = "exam_course", key = "#examId+'_'+#courseCode", unless = "#result == null")
+	@Override
+	public ExamCourseCacheBean getExamCourseCacheBean(Long examId, String courseCode) {
+		ExamCourseCacheBean ret = null;
+		TEExamCourse ec = teExamCourseMapper.findByExamIdAndCourseCode(examId, courseCode);
+		if (ec == null) {
+			return ret;
+		}
+		ret = new ExamCourseCacheBean();
+		ret.setId(ec.getId());
+		ret.setExamId(ec.getExamId());
+		ret.setCourseCode(ec.getCourseCode());
+		ret.setCourseName(ec.getCourseName());
+		ret.setObjectiveShuffle(ec.getObjectiveShuffle());
+		ret.setOptionShuffle(ec.getOptionShuffle());
+		ret.setPaperCount(ec.getPaperCount());
+		ret.setHasAnswer(ec.getHasAnswer());
+		ret.setAudioPlayTime(ec.getAudioPlayTime());
+		List<TEExamPaper> papers = teExamPaperMapper.findListByExamIdAndCourseCode(examId, courseCode);
+		if (papers != null && papers.size() > 0) {
+			List<Long> paperIds=new ArrayList<Long>();
+			List<Double> weight=new ArrayList<Double>();
+			for(TEExamPaper paper:papers) {
+				paperIds.add(paper.getId());
+				weight.add(paper.getWeight());
+			}
+			ret.setPaperIds(paperIds);
+			ret.setPaperWeight(weight);
+		} 
+		return ret;
+	}
 }

+ 30 - 0
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamPaperServiceImpl.java

@@ -3,9 +3,11 @@ package com.qmth.themis.business.service.impl;
 import javax.annotation.Resource;
 
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.themis.business.cache.bean.ExamPaperCacheBean;
 import com.qmth.themis.business.dao.TEExamPaperMapper;
 import com.qmth.themis.business.entity.TEExamPaper;
 import com.qmth.themis.business.service.TEExamPaperService;
@@ -37,4 +39,32 @@ public class TEExamPaperServiceImpl extends ServiceImpl<TEExamPaperMapper, TEExa
 		}
 		return teExamPaperMapper.findByExamIdAndCourseCodeAndPaperCode(examId, courseCode, paperCode);
 	}
+	
+	@Cacheable(value = "exam_paper", key = "#paperId", unless = "#result == null")
+	@Override
+	public ExamPaperCacheBean getExamPaperCacheBean(Long paperId) {
+		ExamPaperCacheBean ret = null;
+		TEExamPaper ep = getById(paperId);
+		if (ep == null) {
+			return ret;
+		}
+		ret = new ExamPaperCacheBean();
+		ret.setId(ep.getId());
+		ret.setExamId(ep.getExamId());
+		ret.setCourseCode(ep.getCourseCode());
+		ret.setCode(ep.getCode());
+		ret.setName(ep.getName());
+		ret.setTotalScore(ep.getTotalScore());
+		ret.setPaperPath(ep.getPaperPath());
+		ret.setStructPath(ep.getStructPath());
+		ret.setAnswerPath(ep.getAnswerPath());
+		ret.setDecryptSecret(ep.getDecryptSecret());
+		ret.setDecryptVector(ep.getDecryptVector());
+		ret.setEncryptMode(ep.getEncryptMode());
+		ret.setNeedVoiceAnswer(ep.getNeedVoiceAnswer());
+		ret.setHasAudio(ep.getHasAudio());
+		ret.setWeight(ep.getWeight());
+		ret.setAudioPlayTime(ep.getAudioPlayTime());
+		return ret;
+	}
 }

+ 185 - 52
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamServiceImpl.java

@@ -1,19 +1,36 @@
 package com.qmth.themis.business.service.impl;
 
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.qmth.themis.business.bean.exam.ExamPrepareBean;
+import com.qmth.themis.business.cache.RedisKeyHelper;
+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.ExamStudentCacheBean;
+import com.qmth.themis.business.config.SystemConfig;
 import com.qmth.themis.business.dao.TEExamMapper;
 import com.qmth.themis.business.entity.TEExam;
 import com.qmth.themis.business.service.TEExamActivityService;
+import com.qmth.themis.business.service.TEExamCourseService;
+import com.qmth.themis.business.service.TEExamPaperService;
 import com.qmth.themis.business.service.TEExamService;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import javax.annotation.Resource;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import com.qmth.themis.business.service.TEExamStudentService;
+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;
 
 /**
  * @Description: 考试批次 服务实现类
@@ -25,51 +42,167 @@ import java.util.Objects;
 @Service
 public class TEExamServiceImpl extends ServiceImpl<TEExamMapper, TEExam> implements TEExamService {
 
-    @Resource
-    TEExamMapper teExamMapper;
-
-    @Resource
-    TEExamActivityService examActivityService;
-
-    /**
-     * 查询考试批次
-     *
-     * @param iPage
-     * @param id
-     * @param code
-     * @param name
-     * @param mode
-     * @param enable
-     * @return
-     */
-    @Override
-    public IPage<TEExam> examQuery(IPage<Map> iPage, Long id, String code, String name, Integer mode, Integer enable) {
-        return teExamMapper.examQuery(iPage, id, code, name, mode, enable);
-    }
-
-    /**
-     * 获取考试待考列表
-     *
-     * @param studentId
-     * @return
-     */
-    @Override
-    public List<Map> getWaitingExam(Long studentId) {
-        List<Map> list = teExamMapper.getWaitingExam(studentId);
-        if (Objects.nonNull(list) && list.size() > 0) {
-            for (int i = 0; i < list.size(); i++) {
-                Map m = list.get(i);
-                List<Map> teExamActivityList = examActivityService.getWaitingExam(studentId, Long.parseLong(String.valueOf(m.get("id"))), Long.parseLong(String.valueOf(m.get("examActivityId"))));
-                m.put("activities", teExamActivityList);
-            }
-        }
-        return list;
-    }
-
-    @Transactional
+	@Resource
+	TEExamMapper teExamMapper;
+
+	@Resource
+	TEExamActivityService teExamActivityService;
+
+	@Resource
+	TEExamStudentService teExamStudentService;
+
+	@Resource
+	TEExamCourseService teExamCourseService;
+
+	@Resource
+	TEExamPaperService teExamPaperService;
+
+	@Resource
+	TOeExamRecordService toeExamRecordService;
+
+	@Resource
+	RedisUtil redisUtil;
+
+	@Resource
+	SystemConfig systemConfig;
+
+	/**
+	 * 查询考试批次
+	 *
+	 * @param iPage
+	 * @param id
+	 * @param code
+	 * @param name
+	 * @param mode
+	 * @param enable
+	 * @return
+	 */
+	@Override
+	public IPage<TEExam> examQuery(IPage<Map> iPage, Long id, String code, String name, Integer mode, Integer enable) {
+		return teExamMapper.examQuery(iPage, id, code, name, mode, enable);
+	}
+
+	/**
+	 * 获取考试待考列表
+	 *
+	 * @param studentId
+	 * @return
+	 */
 	@Override
-	public ExamPrepareBean prepare(Long studentId, Long activityId) {
-		// TODO Auto-generated method stub
-		return null;
+	public List<Map> getWaitingExam(Long studentId) {
+		List<Map> list = teExamMapper.getWaitingExam(studentId);
+		if (Objects.nonNull(list) && list.size() > 0) {
+			for (int i = 0; i < list.size(); i++) {
+				Map m = list.get(i);
+				List<Map> teExamActivityList = teExamActivityService.getWaitingExam(studentId,
+						Long.parseLong(String.valueOf(m.get("id"))),
+						Long.parseLong(String.valueOf(m.get("examActivityId"))));
+				m.put("activities", teExamActivityList);
+			}
+		}
+		return list;
+	}
+
+	@Transactional
+	@Override
+	public ExamPrepareBean prepare(Long studentId, Long examStudentId) {
+		ExamPrepareBean ret = null;
+		ExamStudentCacheBean es = null;
+		es = (ExamStudentCacheBean) redisUtil.get(RedisKeyHelper.examStudentCacheKey(examStudentId));
+		if (es == null) {
+			es = teExamStudentService.getExamStudnetCacheBean(examStudentId);
+		}
+		if (es == null) {
+			throw new BusinessException("未找到考试");
+		}
+
+		if (studentId.equals(es.getStudentId())) {
+			throw new BusinessException("考生Id和当前登录用户不一致");
+		}
+		if (es.getLeftExamCount() == 0) {
+			throw new BusinessException("没有剩余考试次数");
+		}
+		Long activityId = es.getExamActivityId();
+		ExamActivityCacheBean ac = teExamActivityService.getExamActivityCacheBean(activityId);
+		if (ac == null) {
+			throw new BusinessException("未找到场次");
+		}
+		Date now = new Date();
+		Long start = ac.getStartTime().getTime() - (ac.getPrepareSeconds() * 1000);
+		Long end = ac.getStartTime().getTime() + (ac.getOpeningSeconds() * 1000);
+		if (now.getTime() < start) {
+			throw new BusinessException("没有到允许开考的时间");
+		}
+		if (now.getTime() > end) {
+			throw new BusinessException("允许开考的时间已结束");
+		}
+		ExamCourseCacheBean ec = teExamCourseService.getExamCourseCacheBean(es.getExamId(), es.getCourseCode());
+		if (ec == null) {
+			throw new BusinessException("未找到考试科目");
+		}
+		if (ec.getPaperIds() == null) {
+			throw new BusinessException("考试科目未绑定试卷");
+		}
+		if (ec.getHasAnswer() == null || ec.getHasAnswer().intValue() == 0) {
+			throw new BusinessException("考试科目答案未补全");
+		}
+		// 根据权重选中试卷
+		Long paperId = ec.getPaperIds().get(getPaperByWeight(ec.getPaperWeight()));
+		ExamPaperCacheBean ep = teExamPaperService.getExamPaperCacheBean(paperId);
+		if (ep == null) {
+			throw new BusinessException("未找到试卷:" + paperId);
+		}
+		if (StringUtils.isBlank(ep.getAnswerPath())) {
+			throw new BusinessException("试卷答案未上传:" + paperId);
+		}
+
+		// 写入次数
+		Integer serialNumber = es.getCurrentSerialNumber();
+		if (serialNumber == null) {
+			serialNumber = 0;
+		}
+		es.setCurrentSerialNumber(serialNumber + 1);
+
+		Long recordId = toeExamRecordService.saveByPrepare(es.getExamId(), es.getExamActivityId(), examStudentId,
+				paperId, es.getCurrentSerialNumber());
+
+		Integer leftExamCount = es.getLeftExamCount();
+		es.setLeftExamCount(leftExamCount - 1);
+
+		es.setCurrentRecordId(recordId);
+
+		ExamPrepareBean prepare = new ExamPrepareBean();
+		prepare.setRecordId(recordId);
+		prepare.setAudioPlayTime(ep.getAudioPlayTime());
+		prepare.setHasAudio((ep.getHasAudio() == null || ep.getHasAudio().intValue() == 0 ? false : true));
+		prepare.setObjectiveShuffle(
+				(ec.getObjectiveShuffle() == null || ec.getObjectiveShuffle().intValue() == 0 ? false : true));
+		prepare.setOptionShuffle(
+				(ec.getOptionShuffle() == null || ec.getOptionShuffle().intValue() == 0 ? false : true));
+		String paperurl = OssUtil.getUrlForPrivateBucket(systemConfig.getOssEnv(3), ep.getPaperPath());
+		String structurl = OssUtil.getUrlForPrivateBucket(systemConfig.getOssEnv(3), ep.getStructPath());
+		prepare.setPaperUrl(paperurl);
+		prepare.setStructUrl(structurl);
+
+		// 更新考生缓存
+		redisUtil.set(RedisKeyHelper.examStudentCacheKey(examStudentId), es);
+
+		return ret;
+	}
+
+	/**
+	 * 根据设定几率取出一套试卷
+	 */
+	private int getPaperByWeight(List<Double> paperWeight) {
+		// 从1开始
+		double r = Math.random();
+		for (int i = 0; i < paperWeight.size(); i++) {
+			r -= paperWeight.get(i);
+			if (r <= 0.0d) {
+				return i;// 选中
+			}
+		}
+
+		return -1;
 	}
 }

+ 32 - 4
themis-business/src/main/java/com/qmth/themis/business/service/impl/TEExamStudentServiceImpl.java

@@ -1,14 +1,17 @@
 package com.qmth.themis.business.service.impl;
 
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.springframework.stereotype.Service;
+
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
 import com.qmth.themis.business.dao.TEExamStudentMapper;
 import com.qmth.themis.business.entity.TEExamStudent;
 import com.qmth.themis.business.service.TEExamStudentService;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.util.Map;
 
 /**
  * @Description: 考生库 服务实现类
@@ -41,4 +44,29 @@ public class TEExamStudentServiceImpl extends ServiceImpl<TEExamStudentMapper, T
     public IPage<Map> examStudentQuery(IPage<Map> iPage, Long examId, Long activityId, String identity, String name, String roomCode, String courseCode, String grade, Integer enable) {
         return teExamStudentMapper.examStudentQuery(iPage, examId, activityId, identity, name, roomCode, courseCode, grade, enable);
     }
+    
+    @Override
+    public ExamStudentCacheBean getExamStudnetCacheBean(Long examStudentId) {
+    	ExamStudentCacheBean ret=null;
+    	TEExamStudent es=getById(examStudentId);
+    	if(es==null) {
+    		return ret;
+    	}
+    	if(es.getEnable()!=null&&es.getEnable().intValue()==0) {
+    		return ret;
+    	}
+    	ret=new ExamStudentCacheBean();
+    	ret.setId(es.getId());
+    	ret.setExamId(es.getExamId());
+    	ret.setExamActivityId(es.getExamActivityId());
+    	ret.setStudentId(es.getStudentId());
+    	ret.setCourseCode(es.getCourseCode());
+    	ret.setCourseName(es.getCourseName());
+    	ret.setRoomCode(es.getRoomCode());
+    	ret.setRoomName(es.getRoomName());
+    	ret.setLeftExamCount(es.getLeftExamCount());
+    	ret.setCurrentRecordId(es.getCurrentRecordId());
+    	ret.setCurrentSerialNumber(null);
+        return ret;
+    }
 }

+ 39 - 4
themis-business/src/main/java/com/qmth/themis/business/service/impl/TOeExamRecordServiceImpl.java

@@ -1,13 +1,20 @@
 package com.qmth.themis.business.service.impl;
 
+import java.util.Date;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.themis.business.cache.RedisKeyHelper;
+import com.qmth.themis.business.cache.bean.ExamRecordCacheBean;
 import com.qmth.themis.business.dao.TOeExamRecordMapper;
 import com.qmth.themis.business.entity.TOeExamRecord;
 import com.qmth.themis.business.service.TOeExamRecordService;
-import org.springframework.stereotype.Service;
-
-import javax.annotation.Resource;
-import java.util.Map;
+import com.qmth.themis.business.util.RedisUtil;
 
 /**
  * @Description: 考试记录 服务实现类
@@ -21,9 +28,37 @@ public class TOeExamRecordServiceImpl extends ServiceImpl<TOeExamRecordMapper, T
 
     @Resource
     TOeExamRecordMapper tOeExamRecordMapper;
+    
+    @Resource
+    RedisUtil redisUtil;
 
     @Override
     public Map getUnFinishExam(Long studentId) {
         return tOeExamRecordMapper.getUnFinishExam(studentId);
     }
+    
+    @Transactional 
+    @Override
+    public Long saveByPrepare(Long examId,Long examActivityId,Long examStudentId,Long paperId,Integer serialNumber) {
+    	TOeExamRecord er=new TOeExamRecord();
+    	er.setExamId(examId);
+    	er.setExamActivityId(examActivityId);
+    	er.setExamStudentId(examStudentId);
+    	er.setPaperId(paperId);
+    	er.setSerialNumber(serialNumber);
+    	er.setFirstPrepareTime(new Date());
+    	er.setStatus(0);
+    	saveOrUpdate(er);
+    	ExamRecordCacheBean erCache=new ExamRecordCacheBean();
+    	erCache.setId(er.getId());
+    	erCache.setExamId(examId);
+    	erCache.setExamActivityId(examActivityId);
+    	erCache.setExamStudentId(examStudentId);
+    	erCache.setPaperId(paperId);
+    	erCache.setSerialNumber(serialNumber);
+    	erCache.setFirstPrepareTime(new Date());
+    	erCache.setStatus(0);
+    	redisUtil.set(RedisKeyHelper.examRecordCacheKey(er.getId()), erCache);
+    	return er.getId();
+    }
 }

+ 4 - 4
themis-business/src/main/java/com/qmth/themis/business/templete/impl/TaskExamPaperImportTemplete.java

@@ -104,7 +104,7 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
 
     private void disposeCourseDir(String rootDir, TEExam teExam, File courseDir, Map<String, Object> map) {
         String courseCode = courseDir.getName();
-        TEExamCourse course = teExamCourseService.findByCode(courseCode);
+        TEExamCourse course = teExamCourseService.findByExamIdAndCourseCode(teExam.getId(),courseCode);
         if (course == null) {
             throw new BusinessException("科目编码不存在 " + courseCode);
         }
@@ -127,7 +127,7 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
             course.setAudioPlayTime(audioPlayTime);
         }
         if (objectiveShuffle != null || optionShuffle != null || audioPlayTime != null) {
-            teExamCourseService.save(course);
+            teExamCourseService.saveOrUpdate(course);
         }
     }
 
@@ -173,7 +173,7 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
         disposeStruct(rootDir, paper, paperFile);
         disposeAnswer(rootDir, paper, answerFile);
 
-        teExamPaperService.save(paper);
+        teExamPaperService.saveOrUpdate(paper);
     }
 
     private void disposePaper(String rootDir, TEExamPaper paper, File paperFile, File attachmentDir) {
@@ -245,7 +245,7 @@ public class TaskExamPaperImportTemplete implements TaskImportTemplete {
         for (File courseDir : dfile.listFiles()) {// 校验每一个科目
             if (courseDir.isDirectory()) {
                 String courseCode = courseDir.getName();
-                TEExamCourse course = teExamCourseService.findByCode(courseCode);
+                TEExamCourse course = teExamCourseService.findByExamIdAndCourseCode(examId,courseCode);
                 if (course == null) {
                     addResult(result, "科目编码不存在 " + courseCode);
                     hasErr = true;

+ 51 - 40
themis-business/src/main/resources/mapper/TEExamCourseMapper.xml

@@ -1,44 +1,55 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.qmth.themis.business.dao.TEExamCourseMapper">
+<mapper
+	namespace="com.qmth.themis.business.dao.TEExamCourseMapper">
+
+	<select id="examCourseQuery"
+		resultType="com.qmth.themis.business.entity.TEExamCourse">
+		select * from t_e_exam_course teec
+		left join t_e_exam tee on teec.exam_id = tee.id
+		<where>
+			<if test="examId != null and examId != ''">
+				and teec.exam_id = #{examId}
+			</if>
+			<if test="courseCode != null and courseCode != ''">
+				and teec.course_code like concat('%', #{courseCode}, '%')
+			</if>
+			<if test="courseName != null and courseName != ''">
+				and teec.course_name like concat('%', #{courseName}, '%')
+			</if>
+			<if test="hasPaper != null and hasPaper != '' or hasPaper == 0">
+				<choose>
+					<when test="hasPaper == 0">
+						and (teec.paper_count = #{hasPaper} or teec.paper_count is null)
+					</when>
+					<otherwise>
+						and teec.paper_count <![CDATA[ > ]]>
+						0
+					</otherwise>
+				</choose>
+			</if>
+		</where>
+	</select>
+
+	<select id="findByCode"
+		resultType="com.qmth.themis.business.entity.TEExamCourse">
+		select * from t_e_exam_course teec
+		<where>
+			1=1
+			<if test="courseCode != null and courseCode != ''">
+				and teec.course_code =#{courseCode}
+			</if>
+			<if test="courseCode == null or courseCode == ''">
+				1=2
+			</if>
+		</where>
+	</select>
+
+	<select id="findByExamIdAndCourseCode"
+		resultType="com.qmth.themis.business.entity.TEExamCourse">
+		select * from t_e_exam_course t
+		where t.exam_id=#{examId} and t.course_code =#{courseCode}
+	</select>
+
 
-    <select id="examCourseQuery" resultType="com.qmth.themis.business.entity.TEExamCourse">
-        select * from t_e_exam_course teec
-        left join t_e_exam tee on teec.exam_id = tee.id
-        <where>
-            <if test="examId != null and examId != ''">
-                and teec.exam_id = #{examId}
-            </if>
-            <if test="courseCode != null and courseCode != ''">
-                and teec.course_code like concat('%', #{courseCode}, '%')
-            </if>
-            <if test="courseName != null and courseName != ''">
-                and teec.course_name like concat('%', #{courseName}, '%')
-            </if>
-            <if test="hasPaper != null and hasPaper != '' or hasPaper == 0">
-                <choose>
-                    <when test="hasPaper == 0">
-                        and (teec.paper_count = #{hasPaper} or teec.paper_count is null)
-                    </when>
-                    <otherwise>
-                        and teec.paper_count <![CDATA[ > ]]> 0
-                    </otherwise>
-                </choose>
-            </if>
-        </where>
-    </select>
-    
-    <select id="findByCode" resultType="com.qmth.themis.business.entity.TEExamCourse">
-        select * from t_e_exam_course teec
-        <where>
-        	1=1
-            <if test="courseCode != null and courseCode != ''">
-                and teec.course_code =#{courseCode}
-            </if>
-            <if test="courseCode == null or courseCode == ''">
-                1=2
-            </if>
-        </where>
-    </select>
-    
 </mapper>

+ 8 - 0
themis-business/src/main/resources/mapper/TEExamPaperMapper.xml

@@ -9,4 +9,12 @@
 		t.course_code=#{courseCode}
 		and t.code =#{paperCode}
 	</select>
+	
+	<select id="findListByExamIdAndCourseCode"
+		resultType="com.qmth.themis.business.entity.TEExamPaper">
+		select * from t_e_exam_paper t
+		where t.exam_id=#{examId} and
+		t.course_code=#{courseCode}
+	</select>
+	
 </mapper>

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

@@ -62,7 +62,7 @@ public class TEExamController {
 	@ApiOperation(value = "开始候考")
 	@RequestMapping(value = "/prepare", method = RequestMethod.POST)
 	@ApiResponses({ @ApiResponse(code = 200, message = "试卷信息") })
-	public ExamPrepareBean prepare(@ApiParam(value = "考试场次ID", required = true) @RequestParam Long activityId) {
+	public ExamPrepareBean prepare(@ApiParam(value = "考生ID", required = true) @RequestParam Long examStudentId) {
 		TEStudent teStudent = (TEStudent) ServletUtil.getRequestStudentAccount();
 		String lockKey=SystemConstant.REDIS_LOCK_STUDENT_PREFIX+teStudent.getId();
 		Boolean lock=redisUtil.lock(lockKey, SystemConstant.REDIS_CACHE_TIME_OUT);
@@ -70,7 +70,7 @@ public class TEExamController {
 			throw new BusinessException(ExceptionResultEnum.REQUEST_AWAIT);
 		}
 		try {
-			return teExamService.prepare(teStudent.getId(), activityId);
+			return teExamService.prepare(teStudent.getId(), examStudentId);
 		} finally {
 			redisUtil.releaseLock(lockKey);
 		}