Browse Source

Merge branch 'master' of http://git.qmth.com.cn/ExamCloud-3/examcloud-core-oe-admin.git

wangwei 5 năm trước cách đây
mục cha
commit
830809396f
12 tập tin đã thay đổi với 1715 bổ sung22 xóa
  1. 144 0
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/OfflineExamController.java
  2. 30 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordDataService.java
  3. 9 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordForMarkingService.java
  4. 6 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamScoreService.java
  5. 42 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/OfflineExamService.java
  6. 203 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/OfflineExamCourseInfo.java
  7. 492 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordDataBean.java
  8. 286 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentBean.java
  9. 62 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordDataServiceImpl.java
  10. 41 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordForMarkingServiceImpl.java
  11. 33 22
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamScoreServiceImpl.java
  12. 367 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/OfflineExamServiceImpl.java

+ 144 - 0
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/OfflineExamController.java

@@ -0,0 +1,144 @@
+package cn.com.qmth.examcloud.core.oe.admin.api.controller;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+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.multipart.MultipartFile;
+
+import cn.com.qmth.examcloud.api.commons.security.bean.User;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.oe.admin.base.utils.Check;
+import cn.com.qmth.examcloud.core.oe.admin.dao.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamProperties;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamType;
+import cn.com.qmth.examcloud.core.oe.admin.service.OfflineExamService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.OfflineExamCourseInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.others.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import cn.com.qmth.examcloud.web.support.ControllerSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月5日 下午3:33:26
+ * @company 	QMTH
+ * @description 离线考试Controller
+ */
+@Api(tags = "离线考试控制")
+@RestController
+@RequestMapping("${app.api.oe.student}/offlineExam")
+public class OfflineExamController extends ControllerSupport{
+
+	@Autowired
+	private OfflineExamService offlineExamService;
+
+	@Autowired
+	private ExamRecordDataRepo examRecordDataRepo;
+	/**
+	 * 答案文件最大限制
+	 * 单位:M
+	 */
+	private static int answerMaxsize = 30;
+	
+	
+	public static final String TEMP_FILE_EXP = "offlineExam/";
+	
+	
+	/**
+	 * 获取离线考试列表
+	 * @return
+	 */
+	@ApiOperation(value = "获取离线考试列表")
+	@GetMapping("/getOfflineCourse")
+	public List<OfflineExamCourseInfo> getOfflineCourse(){
+		User user = getAccessUser();
+		List<OfflineExamCourseInfo> offlineExamCourseInfos = offlineExamService.getOfflineCourse(user.getUserId());
+		return offlineExamCourseInfos;
+	}
+	
+	/**
+	 * 开始考试
+	 * @param examStudentId
+	 */
+	@ApiOperation(value = "离线考试:开始考试")
+	@GetMapping("/startOfflineExam")
+	public void startOfflineExam(@RequestParam long examStudentId){
+		Check.isNull(examStudentId, "examStudentId不能为空");
+		offlineExamService.startOfflineExam(examStudentId);
+	}
+	
+	/**
+	 * 交卷
+	 */
+	@ApiOperation(value = "离线考试:交卷")
+	@PostMapping("/submitPaper")
+	public void submitPaper(@RequestParam(value = "file") MultipartFile file,
+							@RequestParam long examRecordDataId)  throws Exception{
+		Check.isNull(file, "file不能为空");
+		Check.isNull(examRecordDataId, "examRecordDataId不能为空");
+		String fileName = file.getOriginalFilename();
+		int index = fileName.lastIndexOf(".");
+		String fileSuffix = fileName.substring(index+1, fileName.length()).toUpperCase();
+		if(!"PDF".equals(fileSuffix) && !"ZIP".equals(fileSuffix)){
+			throw new StatusException("OfflineExamController-submitPaper-001","文件格式不正确"); 
+		}
+		
+		ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo,examRecordDataId,ExamRecordDataEntity.class);
+		if(examRecordData.getExamType() != ExamType.OFFLINE){
+			throw new StatusException("OfflineExamController-submitPaper-002","非离线考试"); 
+		}
+		String offlineUploadFileType = ExamCacheTransferHelper.getCachedExamProperty(examRecordData.getExamId(),
+				examRecordData.getStudentId(),
+				ExamProperties.OFFLINE_UPLOAD_FILE_TYPE.name()).getValue();
+		if(StringUtils.isBlank(offlineUploadFileType) || "[]".equals(offlineUploadFileType)){
+			throw new StatusException("OfflineExamController-submitPaper-003","当前考试设置不允许上传附件"); 
+		}
+		if(offlineUploadFileType.indexOf(fileSuffix)<0){
+			throw new StatusException("OfflineExamController-submitPaper-004","当前考试允许上传文件格式为:" + offlineUploadFileType); 
+		}
+		//判断文件大小
+		long fileSize = file.getSize();
+		if(fileSize > answerMaxsize * 1048576){
+			throw new StatusException("OfflineExamController-submitPaper-005","文件大小不能超过"+answerMaxsize+"M"); 
+		}
+		offlineExamService.submitPaper(examRecordDataId,getUploadFile(file));
+	}
+	
+	private File getUploadFile(MultipartFile file){
+        //建临时文件夹
+		File dirFile = new File(TEMP_FILE_EXP);
+		if(!dirFile.exists()){
+			dirFile.mkdirs();
+		}
+        String fileName = file.getOriginalFilename();
+        File tempFile = new File(TEMP_FILE_EXP + fileName);
+        OutputStream os = null;
+        try {
+			os = new FileOutputStream(tempFile);
+			IOUtils.copyLarge(file.getInputStream(), os);
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}finally{
+			IOUtils.closeQuietly(os);
+		}
+        return tempFile;
+    }
+}

+ 30 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordDataService.java

@@ -0,0 +1,30 @@
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentBean;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+
+/**
+ * @author chenken
+ * @date 2018/8/15 11:16
+ * @company QMTH
+ * @description 考试记录数据服务接口
+ */
+public interface ExamRecordDataService {
+
+
+    /**
+     * 创建离线考试记录
+     *
+     * @param examRecord
+     * @param fullyObjective
+     * @return
+     */
+    ExamRecordDataEntity createOfflineExamRecordData(ExamStudentBean bean,
+                                                     ExamBean examBean,
+                                                     CourseBean courseBean,
+                                                     String basePaperId,
+                                                     String paperStructId, Boolean fullyObjective);
+
+}

+ 9 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamRecordForMarkingService.java

@@ -3,6 +3,7 @@ package cn.com.qmth.examcloud.core.oe.admin.service;
 import java.util.List;
 
 import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordForMarkingEntity;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordDataBean;
 
 /**
  * 
@@ -31,4 +32,12 @@ public interface ExamRecordForMarkingService {
 	List<ExamRecordForMarkingEntity> queryValidExamRecordInfoByStuIds(Long examId, Long courseId,
 			List<Long> examStudentIds,String batchNum);
 	
+	/**
+     * 离线考试-保存阅卷相关数据-对内
+     * @param examRecordData
+     * @param fileName
+     * @param fileUrl
+     */
+    public void saveOffLineExamRecordForMarking(ExamRecordDataBean examRecordData,String fileName,String fileUrl);
+	
 }

+ 6 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExamScoreService.java

@@ -35,4 +35,10 @@ public interface ExamScoreService {
      * @return ExamScoreEntity
      */
     List<ExamScoreEntity> getAllExamScoreList(Long examId, String identityNumber, Long courseId);
+
+    /**
+     * 离线考试初始化得分
+     * @param examRecordDataId
+     */
+    public void createExamScoreWithOffline(Long examRecordDataId);
 }

+ 42 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/OfflineExamService.java

@@ -0,0 +1,42 @@
+package cn.com.qmth.examcloud.core.oe.admin.service;
+
+import java.io.File;
+import java.util.List;
+
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.OfflineExamCourseInfo;
+
+
+/**
+ * 
+ * @author  	chenken
+ * @date    	2018年9月5日 上午10:35:18
+ * @company 	QMTH
+ * @description 离线考试服务接口
+ */
+public interface OfflineExamService {
+	
+	/**
+	 * 获取待考课程列表
+	 * @param studentId
+	 * @return
+	 */
+	public List<OfflineExamCourseInfo> getOfflineCourse(Long studentId);
+
+	/**
+	 * 开始离线考试
+	 * @param token
+	 * @param examStudentId
+	 * @param studentId
+	 * @return
+	 */
+	public void startOfflineExam(Long examStudentId);
+	
+	/**
+	 * 上传作答
+	 * @param examRecordDataId
+	 * @param tempFile
+	 * @param fileType
+	 */
+	public void submitPaper(Long examRecordDataId,File tempFile)  throws Exception;
+
+}

+ 203 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/OfflineExamCourseInfo.java

@@ -0,0 +1,203 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamRecordStatus;
+
+
+
+
+/**
+ * 离线考试课程
+ * @author ting.yin
+ *
+ */
+public class OfflineExamCourseInfo implements Serializable {
+
+	private static final long serialVersionUID = 4102608764528925537L;
+
+	private Long examId;
+
+	private String examName;
+	
+	private String orgName;//学校名称
+
+	private Long examStudentId;
+
+	private String courseName;
+
+	private String courseCode;
+
+	private String courseLevel;
+
+	private String studentCode;
+
+	private String studentName;
+
+	private Date startTime;// 考试时间范围开始时间
+
+	private Date endTime; // 考试时间范围结束时间
+
+	private String specialtyName;
+
+	private Long examRecordDataId;
+	
+	private ExamRecordStatus status;//用于判断是否上传
+	
+	private String paperId;
+	
+	/**
+	 * 是否有效
+	 */
+	private Boolean isvalid;
+	
+	private String offlineFileUrl;//答卷地址
+	
+	private String fileName;//答卷文件名
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	public Long getExamStudentId() {
+		return examStudentId;
+	}
+
+	public void setExamStudentId(Long examStudentId) {
+		this.examStudentId = examStudentId;
+	}
+
+	public String getCourseName() {
+		return courseName;
+	}
+
+	public void setCourseName(String courseName) {
+		this.courseName = courseName;
+	}
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	public String getStudentCode() {
+		return studentCode;
+	}
+
+	public void setStudentCode(String studentCode) {
+		this.studentCode = studentCode;
+	}
+
+	public String getStudentName() {
+		return studentName;
+	}
+
+	public void setStudentName(String studentName) {
+		this.studentName = studentName;
+	}
+
+	public Date getStartTime() {
+		return startTime;
+	}
+
+	public void setStartTime(Date startTime) {
+		this.startTime = startTime;
+	}
+
+	public Date getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(Date endTime) {
+		this.endTime = endTime;
+	}
+
+	public String getExamName() {
+		return examName;
+	}
+
+	public void setExamName(String examName) {
+		this.examName = examName;
+	}
+
+	public String getCourseLevel() {
+		return courseLevel;
+	}
+
+	public void setCourseLevel(String courseLevel) {
+		this.courseLevel = courseLevel;
+	}
+
+	public String getSpecialtyName() {
+		return specialtyName;
+	}
+
+	public void setSpecialtyName(String specialtyName) {
+		this.specialtyName = specialtyName;
+	}
+
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
+
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
+
+	public Boolean getIsvalid() {
+		return isvalid;
+	}
+
+	public void setIsvalid(Boolean isvalid) {
+		this.isvalid = isvalid;
+	}
+
+	public String getPaperId() {
+		return paperId;
+	}
+
+	public void setPaperId(String paperId) {
+		this.paperId = paperId;
+	}
+
+	public ExamRecordStatus getStatus() {
+		return status;
+	}
+
+	public void setStatus(ExamRecordStatus status) {
+		this.status = status;
+	}
+
+	public String getOrgName() {
+		return orgName;
+	}
+
+	public void setOrgName(String orgName) {
+		this.orgName = orgName;
+	}
+
+	public String getOfflineFileUrl() {
+		return offlineFileUrl;
+	}
+
+	public void setOfflineFileUrl(String offlineFileUrl) {
+		this.offlineFileUrl = offlineFileUrl;
+	}
+
+	public String getFileName() {
+		return fileName;
+	}
+
+	public void setFileName(String fileName) {
+		this.fileName = fileName;
+	}
+
+}

+ 492 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordDataBean.java

@@ -0,0 +1,492 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord;
+
+import java.util.Date;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamType;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.IsSuccess;
+
+public class ExamRecordDataBean implements JsonSerializable {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 1341159150606432450L;
+    /**
+     * 主键
+     */
+    private Long id;
+    /**
+     * 考试ID
+     */
+    private Long examId;
+    /**
+     * 考试类型
+     */
+    private ExamType examType;
+    /**
+     * 考生ID
+     */
+    private Long examStudentId;
+    /**
+     * 学生ID
+     */
+    private Long studentId;
+    /**
+     * 学号
+     */
+    private String studentCode;
+    /**
+     * 学生姓名
+     */
+    private String studentName;
+    /**
+     * 身份证号
+     */
+    private String identityNumber;
+    /**
+     * 课程ID
+     */
+    private Long courseId;
+
+    private String courseLevel;
+    /**
+     * 学习中心ID
+     */
+    private Long orgId;
+    /**
+     * 顶级机构ID
+     */
+    private Long rootOrgId;
+    /**
+     * 基础试卷ID
+     */
+    private String basePaperId;
+
+    /**
+     * 试卷类型
+     */
+    private String paperType;
+    /**
+     * 试卷结构 ID
+     */
+    private String paperStructId;
+    /**
+     * 采集人
+     */
+    private String infoCollector;
+
+    /**
+     * 考试作答记录id
+     */
+    private String examRecordQuestionsId;
+    /**
+     * 考试记录状态(考试中,考试结束,考试过期,考试作废)
+     */
+    private ExamRecordStatus examRecordStatus;
+    /**
+     * 考试开始时间
+     */
+    private Date startTime;
+    /**
+     * 考试结束时间
+     */
+    private Date endTime;
+    /**
+     * 考试被清理时间
+     */
+    private Date cleanTime;
+    /**
+     * 是否异常数据
+     */
+    private Boolean isWarn;
+    /**
+     * 是否被审核过
+     */
+    private Boolean isAudit;
+    /**
+     * 是否违纪
+     */
+    private Boolean isIllegality;
+
+    /**
+     * 考试时长
+     */
+    private Long usedExamTime;
+    /**
+     * 第几次考试
+     */
+    private Integer examOrder;
+    /**
+     * 是否为重考
+     */
+    private Boolean isReexamine;
+    /**
+     * 是否断点续考
+     */
+    private Boolean isContinued;
+    /**
+     * 是否是全客观题卷  1:是   0:否
+     */
+    private Boolean isAllObjectivePaper;
+    /**
+     * 断点续考次数
+     */
+    private Integer continuedCount;
+    /**
+     * 是否达到最大断点限制
+     */
+    private Boolean isExceed;
+    /**
+     * 抓拍比对成功次数
+     */
+    private Integer faceSuccessCount;
+    /**
+     * 抓拍比对失败次数
+     */
+    private Integer faceFailedCount;
+    /**
+     * 抓拍存在陌生人的次数
+     */
+    private Integer faceStrangerCount;
+    /**
+     * 抓拍比对总次数
+     */
+    private Integer faceTotalCount;
+    /**
+     * 抓拍比对成功比率
+     */
+    private Double faceSuccessPercent;
+    /**
+     * @see cn.com.qmth.examcloud.core.oe.admin.dao.enums.IsSuccess
+     * 活体检测结果
+     */
+    private IsSuccess faceVerifyResult;
+
+    /**
+     * 百度人脸活体检测通过率
+     */
+    private Double baiduFaceLivenessSuccessPercent;
+    /**
+     * 人脸五官坐标比对值
+     */
+    private Double faceLandmarkVal;
+
+    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 ExamType getExamType() {
+        return examType;
+    }
+
+    public void setExamType(ExamType examType) {
+        this.examType = examType;
+    }
+
+    public Long getExamStudentId() {
+        return examStudentId;
+    }
+
+    public void setExamStudentId(Long examStudentId) {
+        this.examStudentId = examStudentId;
+    }
+
+    public Long getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Long studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getStudentCode() {
+        return studentCode;
+    }
+
+    public void setStudentCode(String studentCode) {
+        this.studentCode = studentCode;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getIdentityNumber() {
+        return identityNumber;
+    }
+
+    public void setIdentityNumber(String identityNumber) {
+        this.identityNumber = identityNumber;
+    }
+
+    public Long getCourseId() {
+        return courseId;
+    }
+
+    public void setCourseId(Long courseId) {
+        this.courseId = courseId;
+    }
+
+    public Long getOrgId() {
+        return orgId;
+    }
+
+    public void setOrgId(Long orgId) {
+        this.orgId = orgId;
+    }
+
+    public Long getRootOrgId() {
+        return rootOrgId;
+    }
+
+    public void setRootOrgId(Long rootOrgId) {
+        this.rootOrgId = rootOrgId;
+    }
+
+    public String getBasePaperId() {
+        return basePaperId;
+    }
+
+    public void setBasePaperId(String basePaperId) {
+        this.basePaperId = basePaperId;
+    }
+
+    public String getPaperStructId() {
+        return paperStructId;
+    }
+
+    public void setPaperStructId(String paperStructId) {
+        this.paperStructId = paperStructId;
+    }
+
+    public String getPaperType() {
+        return paperType;
+    }
+
+    public void setPaperType(String paperType) {
+        this.paperType = paperType;
+    }
+
+    public String getCourseLevel() {
+        return courseLevel;
+    }
+
+    public void setCourseLevel(String courseLevel) {
+        this.courseLevel = courseLevel;
+    }
+
+    public String getInfoCollector() {
+        return infoCollector;
+    }
+
+    public void setInfoCollector(String infoCollector) {
+        this.infoCollector = infoCollector;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Date getCleanTime() {
+        return cleanTime;
+    }
+
+    public void setCleanTime(Date cleanTime) {
+        this.cleanTime = cleanTime;
+    }
+
+    public Boolean getIsWarn() {
+        return isWarn;
+    }
+
+    public void setIsWarn(Boolean isWarn) {
+        this.isWarn = isWarn;
+    }
+
+    public Boolean getIsAudit() {
+        return isAudit;
+    }
+
+    public void setIsAudit(Boolean isAudit) {
+        this.isAudit = isAudit;
+    }
+
+    public Boolean getIsIllegality() {
+        return isIllegality;
+    }
+
+    /**
+     * 设置是否违纪
+     *
+     * @param isIllegality
+     */
+    public void setIsIllegality(Boolean isIllegality) {
+        this.isIllegality = isIllegality;
+    }
+
+    public ExamRecordStatus getExamRecordStatus() {
+        return examRecordStatus;
+    }
+
+    public void setExamRecordStatus(ExamRecordStatus examRecordStatus) {
+        this.examRecordStatus = examRecordStatus;
+    }
+
+    public Long getUsedExamTime() {
+        return usedExamTime;
+    }
+
+    public void setUsedExamTime(Long usedExamTime) {
+        this.usedExamTime = usedExamTime;
+    }
+
+    public Integer getExamOrder() {
+        return examOrder;
+    }
+
+    public void setExamOrder(Integer examOrder) {
+        this.examOrder = examOrder;
+    }
+
+    public Boolean getIsReexamine() {
+        return isReexamine;
+    }
+
+    public void setIsReexamine(Boolean isReexamine) {
+        this.isReexamine = isReexamine;
+    }
+
+    public Boolean getIsContinued() {
+        return isContinued;
+    }
+
+    public void setIsContinued(Boolean isContinued) {
+        this.isContinued = isContinued;
+    }
+
+    public Integer getContinuedCount() {
+        return continuedCount;
+    }
+
+    public void setContinuedCount(Integer continuedCount) {
+        this.continuedCount = continuedCount;
+    }
+
+    public Integer getFaceSuccessCount() {
+        return faceSuccessCount;
+    }
+
+    public void setFaceSuccessCount(Integer faceSuccessCount) {
+        this.faceSuccessCount = faceSuccessCount;
+    }
+
+    public Integer getFaceFailedCount() {
+        return faceFailedCount;
+    }
+
+    public void setFaceFailedCount(Integer faceFailedCount) {
+        this.faceFailedCount = faceFailedCount;
+    }
+
+    public Integer getFaceStrangerCount() {
+        return faceStrangerCount;
+    }
+
+    public void setFaceStrangerCount(Integer faceStrangerCount) {
+        this.faceStrangerCount = faceStrangerCount;
+    }
+
+    public Integer getFaceTotalCount() {
+        return faceTotalCount;
+    }
+
+    public void setFaceTotalCount(Integer faceTotalCount) {
+        this.faceTotalCount = faceTotalCount;
+    }
+
+    public Double getFaceSuccessPercent() {
+        return faceSuccessPercent;
+    }
+
+    public void setFaceSuccessPercent(Double faceSuccessPercent) {
+        this.faceSuccessPercent = faceSuccessPercent;
+    }
+
+    public IsSuccess getFaceVerifyResult() {
+        return faceVerifyResult;
+    }
+
+    public void setFaceVerifyResult(IsSuccess faceVerifyResult) {
+        this.faceVerifyResult = faceVerifyResult;
+    }
+
+    public Double getFaceLandmarkVal() {
+        return faceLandmarkVal;
+    }
+
+    public void setFaceLandmarkVal(Double faceLandmarkVal) {
+        this.faceLandmarkVal = faceLandmarkVal;
+    }
+
+    public Boolean getIsAllObjectivePaper() {
+        return isAllObjectivePaper;
+    }
+
+    public void setIsAllObjectivePaper(Boolean isAllObjectivePaper) {
+        this.isAllObjectivePaper = isAllObjectivePaper;
+    }
+
+    public Double getBaiduFaceLivenessSuccessPercent() {
+        return baiduFaceLivenessSuccessPercent;
+    }
+
+    public void setBaiduFaceLivenessSuccessPercent(
+            Double baiduFaceLivenessSuccessPercent) {
+        this.baiduFaceLivenessSuccessPercent = baiduFaceLivenessSuccessPercent;
+    }
+
+    public Boolean getIsExceed() {
+        return isExceed;
+    }
+
+    public void setIsExceed(Boolean isExceed) {
+        this.isExceed = isExceed;
+    }
+
+    public String getExamRecordQuestionsId() {
+        return examRecordQuestionsId;
+    }
+
+    public void setExamRecordQuestionsId(String examRecordQuestionsId) {
+        this.examRecordQuestionsId = examRecordQuestionsId;
+    }
+}

+ 286 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentBean.java

@@ -0,0 +1,286 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent;
+
+import cn.com.qmth.examcloud.api.commons.exchange.JsonSerializable;
+
+public class ExamStudentBean implements JsonSerializable {
+
+
+	/**
+     * 
+     */
+    private static final long serialVersionUID = -205239358290227432L;
+
+    /**
+	 * 不做业务字段使用
+	 */
+	private Long id;
+
+	/**
+	 * 考生ID(与考务的考生ID一致) 跟其他表关联使用
+	 */
+	private Long examStudentId;
+
+	/**
+	 * 考试ID
+	 */
+	private Long examId;
+
+	/**
+	 * 学生ID
+	 */
+	private Long studentId;
+
+	/**
+	 * 学号
+	 */
+	private String studentCode;
+
+	/**
+	 * 学生姓名
+	 */
+	private String studentName;
+
+	/**
+	 * 身份证号
+	 */
+	private String identityNumber;
+
+	/**
+	 * 课程ID
+	 */
+	private Long courseId;
+
+	/**
+	 * 课程Code
+	 */
+	private String courseCode;
+
+	/**
+	 * 课程层次
+	 */
+	private String courseLevel;
+
+	/**
+	 * 学习中心ID
+	 */
+	private Long orgId;
+
+	/**
+	 * 顶级机构ID
+	 */
+	private Long rootOrgId;
+
+	/**
+	 * 专业code
+	 */
+	private String specialtyCode;
+
+	/**
+	 * 专业名称
+	 */
+	private String specialtyName;
+
+	/**
+	 * 试卷类型
+	 */
+	private String paperType;
+
+	/**
+	 * 采集人
+	 */
+	private String infoCollector;
+
+	/**
+	 * 是否完成考试
+	 */
+	private Boolean finished;
+
+	/**
+	 * 已经考试的次数
+	 */
+	private Integer usedNum;
+
+	/**
+	 * 补加的考试次数
+	 */
+	private Integer extraNum;
+
+	/**
+	 * 年级
+	 */
+	private String grade;
+
+	/**
+	 * 是否可用
+	 */
+	private Boolean enable;
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Long getExamStudentId() {
+		return examStudentId;
+	}
+
+	public void setExamStudentId(Long examStudentId) {
+		this.examStudentId = examStudentId;
+	}
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	public Long getStudentId() {
+		return studentId;
+	}
+
+	public void setStudentId(Long studentId) {
+		this.studentId = studentId;
+	}
+
+	public String getStudentCode() {
+		return studentCode;
+	}
+
+	public void setStudentCode(String studentCode) {
+		this.studentCode = studentCode;
+	}
+
+	public String getStudentName() {
+		return studentName;
+	}
+
+	public void setStudentName(String studentName) {
+		this.studentName = studentName;
+	}
+
+	public String getIdentityNumber() {
+		return identityNumber;
+	}
+
+	public void setIdentityNumber(String identityNumber) {
+		this.identityNumber = identityNumber;
+	}
+
+	public Long getCourseId() {
+		return courseId;
+	}
+
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+
+	public String getCourseLevel() {
+		return courseLevel;
+	}
+
+	public void setCourseLevel(String courseLevel) {
+		this.courseLevel = courseLevel;
+	}
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public String getSpecialtyCode() {
+		return specialtyCode;
+	}
+
+	public void setSpecialtyCode(String specialtyCode) {
+		this.specialtyCode = specialtyCode;
+	}
+
+	public String getSpecialtyName() {
+		return specialtyName;
+	}
+
+	public void setSpecialtyName(String specialtyName) {
+		this.specialtyName = specialtyName;
+	}
+
+	public String getPaperType() {
+		return paperType;
+	}
+
+	public void setPaperType(String paperType) {
+		this.paperType = paperType;
+	}
+
+	public String getInfoCollector() {
+		return infoCollector;
+	}
+
+	public void setInfoCollector(String infoCollector) {
+		this.infoCollector = infoCollector;
+	}
+
+	public Boolean getFinished() {
+		return finished;
+	}
+
+	public void setFinished(Boolean finished) {
+		this.finished = finished;
+	}
+
+	public Integer getUsedNum() {
+		return usedNum;
+	}
+
+	public void setUsedNum(Integer usedNum) {
+		this.usedNum = usedNum;
+	}
+
+	public Integer getExtraNum() {
+		return extraNum;
+	}
+
+	public void setExtraNum(Integer extraNum) {
+		this.extraNum = extraNum;
+	}
+
+	public String getGrade() {
+		return grade;
+	}
+
+	public void setGrade(String grade) {
+		this.grade = grade;
+	}
+
+	public Boolean getEnable() {
+		return enable;
+	}
+
+	public void setEnable(Boolean enable) {
+		this.enable = enable;
+	}
+
+}

+ 62 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordDataServiceImpl.java

@@ -0,0 +1,62 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import java.util.Date;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.dao.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamType;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordDataService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentBean;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+
+/**
+ * @author chenken
+ * @date 2018/8/15 11:16
+ * @company QMTH
+ * @description 考试记录数据服务实现
+ */
+@Service("examRecordDataService")
+public class ExamRecordDataServiceImpl implements ExamRecordDataService {
+
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+
+    @Override
+    public ExamRecordDataEntity createOfflineExamRecordData(ExamStudentBean examStudent,
+                                                            ExamBean examBean,
+                                                            CourseBean courseBean,
+                                                            String basePaperId,
+                                                            String paperStructId, Boolean fullyObjective) {
+        ExamRecordDataEntity examRecordDataEntity = new ExamRecordDataEntity();
+        examRecordDataEntity.setExamId(examBean.getId());
+        examRecordDataEntity.setExamType(ExamType.strToEnum(examBean.getExamType()));
+
+        examRecordDataEntity.setExamStudentId(examStudent.getExamStudentId());
+        examRecordDataEntity.setStudentId(examStudent.getStudentId());
+        examRecordDataEntity.setStudentCode(examStudent.getStudentCode());
+        examRecordDataEntity.setStudentName(examStudent.getStudentName());
+        examRecordDataEntity.setIdentityNumber(examStudent.getIdentityNumber());
+        examRecordDataEntity.setOrgId(examStudent.getOrgId());
+        examRecordDataEntity.setRootOrgId(examStudent.getRootOrgId());
+
+        examRecordDataEntity.setCourseId(courseBean.getId());
+        examRecordDataEntity.setCourseLevel(examStudent.getCourseLevel());
+        examRecordDataEntity.setBasePaperId(basePaperId);
+
+        examRecordDataEntity.setPaperType(examStudent.getPaperType());
+        examRecordDataEntity.setPaperStructId(paperStructId);
+
+        examRecordDataEntity.setInfoCollector(examStudent.getInfoCollector());
+        examRecordDataEntity.setStartTime(new Date());
+        examRecordDataEntity.setExamRecordStatus(ExamRecordStatus.EXAM_ING);
+        examRecordDataEntity.setExamOrder(1);
+        examRecordDataEntity.setIsAllObjectivePaper(fullyObjective);
+        return examRecordDataRepo.save(examRecordDataEntity);
+    }
+
+}

+ 41 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordForMarkingServiceImpl.java

@@ -3,6 +3,7 @@ package cn.com.qmth.examcloud.core.oe.admin.service.impl;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -12,6 +13,7 @@ import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.stereotype.Service;
@@ -21,11 +23,13 @@ import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordForMarkingEntity
 import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamType;
 import cn.com.qmth.examcloud.core.oe.admin.dao.enums.MarkingType;
 import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordForMarkingService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordDataBean;
 import cn.com.qmth.examcloud.examwork.api.ExamCloudService;
 import cn.com.qmth.examcloud.examwork.api.request.GetExamPropertyReq;
 import cn.com.qmth.examcloud.examwork.api.request.GetExamReq;
 import cn.com.qmth.examcloud.examwork.api.response.GetExamPropertyResp;
 import cn.com.qmth.examcloud.examwork.api.response.GetExamResp;
+import main.java.com.UpYun;
 
 /**
  * @author chenken
@@ -44,7 +48,17 @@ public class ExamRecordForMarkingServiceImpl implements ExamRecordForMarkingServ
 
     @Autowired
     private JdbcTemplate jdbcTemplate;
+    @Value("${$upyun.site.1.bucketName}")
+    private String bucketName;
+    
+    @Value("${$upyun.site.1.userName}")
+    private String userName;
+    
+    @Value("${$upyun.site.1.password}")
+    private String password;
 
+    @Value("${$upyun.site.1.domain}")
+    private String upyunFileUrl;
     @Override
     public List<ExamRecordForMarkingEntity> queryValidExamRecordInfoByStuIds(
             Long examId, Long courseId, List<Long> examStudentIds, String batchNum) {
@@ -230,4 +244,31 @@ public class ExamRecordForMarkingServiceImpl implements ExamRecordForMarkingServ
         });
     }
 
+    @Override
+    public void saveOffLineExamRecordForMarking(ExamRecordDataBean examRecordData,String offlineFileName,String fileUrl) {
+        ExamRecordForMarkingEntity examRecordForMarking = examRecordForMarkingRepo.findByExamRecordDataId(examRecordData.getId());
+        if(examRecordForMarking == null){
+            examRecordForMarking = new ExamRecordForMarkingEntity();
+            examRecordForMarking.setCreationTime(new Date());
+        }else{
+            //将原文件删掉
+            UpYun upyun = new UpYun(bucketName,userName,password);
+            String offlineFileUrl = examRecordForMarking.getOfflineFileUrl();
+            offlineFileUrl = offlineFileUrl.replace(upyunFileUrl,"");
+            upyun.deleteFile(offlineFileUrl);
+        }
+
+        examRecordForMarking.setExamId(examRecordData.getExamId());
+        examRecordForMarking.setExamRecordDataId(examRecordData.getId());
+        examRecordForMarking.setExamStudentId(examRecordData.getExamStudentId());
+        examRecordForMarking.setCourseId(examRecordData.getCourseId());
+        examRecordForMarking.setOfflineFileUrl(fileUrl);
+        examRecordForMarking.setOfflineFileName(offlineFileName);
+        examRecordForMarking.setObjectiveScore(0D);
+        examRecordForMarking.setSubjectiveAnswerLength(0);
+        examRecordForMarking.setBasePaperId(examRecordData.getBasePaperId());
+        examRecordForMarking.setPaperType(examRecordData.getPaperType());
+        examRecordForMarking.setUpdateTime(new Date());
+        examRecordForMarkingRepo.save(examRecordForMarking);
+    }
 }

+ 33 - 22
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamScoreServiceImpl.java

@@ -7,6 +7,24 @@
 
 package cn.com.qmth.examcloud.core.oe.admin.service.impl;
 
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+import com.google.common.collect.Lists;
+
 import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
 import cn.com.qmth.examcloud.core.oe.admin.base.jpa.SpecUtils;
 import cn.com.qmth.examcloud.core.oe.admin.base.utils.Check;
@@ -19,9 +37,11 @@ import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamStudentEntity;
 import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamStudentFinalScoreEntity;
 import cn.com.qmth.examcloud.core.oe.admin.dao.enums.CourseLevel;
 import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamProperties;
-import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamRecordStatus;
-import cn.com.qmth.examcloud.core.oe.admin.dao.enums.MarkingType;
-import cn.com.qmth.examcloud.core.oe.admin.service.*;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScoreService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentFinalScoreService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.oe.admin.service.GainBaseDataService;
 import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreEntityConvert;
 import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreInfo;
 import cn.com.qmth.examcloud.core.oe.admin.service.bean.examscore.ExamScoreQuery;
@@ -30,23 +50,6 @@ import cn.com.qmth.examcloud.core.oe.admin.service.others.ExamCacheTransferHelpe
 import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
 import cn.com.qmth.examcloud.support.cache.bean.OrgCacheBean;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
-import com.google.common.collect.Lists;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageImpl;
-import org.springframework.data.domain.Pageable;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.RowMapper;
-import org.springframework.stereotype.Service;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 /**
  * 考试分数相关接口
@@ -65,8 +68,6 @@ public class ExamScoreServiceImpl implements ExamScoreService {
     @Autowired
     private ExamRecordService examRecordService;
     @Autowired
-    private ExamService examService;
-    @Autowired
     private GainBaseDataService gainBaseDataService;
     @Autowired
     private JdbcTemplate jdbcTemplate;
@@ -390,5 +391,15 @@ public class ExamScoreServiceImpl implements ExamScoreService {
         examStudentEntity.setGrade(rs.getString("grade"));
         return examStudentEntity;
     }
+    
+    @Override
+    public void createExamScoreWithOffline(Long examRecordDataId) {
+        ExamScoreEntity examScoreEntity = new ExamScoreEntity();
+        examScoreEntity.setExamRecordDataId(examRecordDataId);
+        examScoreEntity.setObjectiveScore(0D);
+        examScoreEntity.setSubjectiveScore(0D);
+        examScoreEntity.setTotalScore(0D);
+        examScoreRepo.save(examScoreEntity);
+    }
 
 }

+ 367 - 0
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/OfflineExamServiceImpl.java

@@ -0,0 +1,367 @@
+package cn.com.qmth.examcloud.core.oe.admin.service.impl;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.transaction.Transactional;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import cn.com.qmth.examcloud.api.commons.enums.ExamSpecialSettingsType;
+import cn.com.qmth.examcloud.commons.exception.StatusException;
+import cn.com.qmth.examcloud.core.basic.api.bean.CourseBean;
+import cn.com.qmth.examcloud.core.oe.admin.dao.ExamRecordDataRepo;
+import cn.com.qmth.examcloud.core.oe.admin.dao.ExamRecordForMarkingRepo;
+import cn.com.qmth.examcloud.core.oe.admin.dao.ExamStudentRepo;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordDataEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamRecordForMarkingEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExamStudentEntity;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamProperties;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamRecordStatus;
+import cn.com.qmth.examcloud.core.oe.admin.dao.enums.ExamType;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordDataService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordForMarkingService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExamScoreService;
+import cn.com.qmth.examcloud.core.oe.admin.service.GainBaseDataService;
+import cn.com.qmth.examcloud.core.oe.admin.service.OfflineExamService;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.OfflineExamCourseInfo;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examrecord.ExamRecordDataBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamStudentBean;
+import cn.com.qmth.examcloud.core.oe.admin.service.others.ExamCacheTransferHelper;
+import cn.com.qmth.examcloud.core.questions.api.ExtractConfigCloudService;
+import cn.com.qmth.examcloud.core.questions.api.request.GetPaperReq;
+import cn.com.qmth.examcloud.core.questions.api.response.GetPaperResp;
+import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
+import cn.com.qmth.examcloud.support.cache.CacheHelper;
+import cn.com.qmth.examcloud.support.cache.bean.ExamOrgSettingsCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamPropertyCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.ExamStudentSettingsCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.OrgCacheBean;
+import cn.com.qmth.examcloud.support.cache.bean.SysPropertyCacheBean;
+import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
+import main.java.com.UpYun;
+
+/**
+ * @author chenken
+ * @date 2018年9月5日 下午3:24:44
+ * @company QMTH
+ * @description 离线考试服务实现
+ */
+@Service("offlineExamService")
+public class OfflineExamServiceImpl implements OfflineExamService {
+
+    @Autowired
+    private ExamStudentRepo examStudentRepo;
+
+    @Autowired
+    private ExamRecordDataRepo examRecordDataRepo;
+
+    @Autowired
+    private GainBaseDataService gainBaseDataService;
+
+    @Autowired
+    private ExtractConfigCloudService extractConfigCloudService;
+
+    @Autowired
+    private ExamRecordDataService examRecordDataService;
+
+    @Autowired
+    private ExamScoreService examScoreService;
+
+    @Autowired
+    private ExamRecordForMarkingService examRecordForMarkingService;
+
+    @Autowired
+    private ExamRecordForMarkingRepo examRecordForMarkingRepo;
+
+    @Value("${$upyun.site.1.bucketName}")
+    private String bucketName;
+
+    @Value("${$upyun.site.1.userName}")
+    private String userName;
+
+    @Value("${$upyun.site.1.password}")
+    private String password;
+
+    @Value("${app.upyun.uploadUrl}")
+    private String upyunUploadUrl;
+
+    @Value("${$upyun.site.1.domain}")
+    private String upyunFileUrl;
+
+    @Override
+    public List<OfflineExamCourseInfo> getOfflineCourse(Long studentId) {
+        List<ExamStudentEntity> examStudents = examStudentRepo.findByStudentId(studentId);
+        List<OfflineExamCourseInfo> offlineExamCourseInfoList = new ArrayList<OfflineExamCourseInfo>();
+        for (ExamStudentEntity examStudent : examStudents) {
+            ExamBean examBean = ExamCacheTransferHelper.getCachedExam(examStudent.getExamId(),studentId);
+            if (!ExamType.OFFLINE.name().equals(examBean.getExamType())) {
+                continue;
+            }
+            if (!examBean.getEnable() || (examBean.getExamLimit() != null && examBean.getExamLimit())) {
+                continue;
+            }
+            if (new Date().before(examBean.getBeginTime()) || examBean.getEndTime().before(new Date())) {
+                continue;
+            }
+            offlineExamCourseInfoList.add(toOfflineExamCourse(examStudent, examBean));
+        }
+        return offlineExamCourseInfoList;
+    }
+
+    private OfflineExamCourseInfo toOfflineExamCourse(ExamStudentEntity examStudent, ExamBean examBean) {
+        OfflineExamCourseInfo offlineExamCourseInfo = new OfflineExamCourseInfo();
+
+        CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(examStudent.getCourseId());
+        offlineExamCourseInfo.setCourseCode(courseBean.getCode());
+        offlineExamCourseInfo.setCourseLevel(courseBean.getLevel());
+        offlineExamCourseInfo.setCourseName(courseBean.getName());
+        offlineExamCourseInfo.setExamId(examBean.getId());
+        offlineExamCourseInfo.setExamName(examBean.getName());
+        offlineExamCourseInfo.setSpecialtyName(examStudent.getSpecialtyName());
+        offlineExamCourseInfo.setExamStudentId(examStudent.getExamStudentId());
+        offlineExamCourseInfo.setStudentCode(examStudent.getStudentCode());
+        offlineExamCourseInfo.setStudentName(examStudent.getStudentName());
+        offlineExamCourseInfo.setStartTime(examBean.getBeginTime());
+        offlineExamCourseInfo.setEndTime(examBean.getEndTime());
+        Date nowDate = new Date();
+        if (nowDate.getTime() > offlineExamCourseInfo.getStartTime().getTime()
+                && nowDate.getTime() < offlineExamCourseInfo.getEndTime().getTime()) {
+            offlineExamCourseInfo.setIsvalid(true);
+        } else {
+            offlineExamCourseInfo.setIsvalid(false);
+        }
+
+        OrgCacheBean orgBean = gainBaseDataService.getOrgBean(examStudent.getOrgId());
+        offlineExamCourseInfo.setOrgName(orgBean.getName());
+
+        List<ExamRecordDataEntity> examRecords = examRecordDataRepo.findByExamStudentId(examStudent.getExamStudentId());
+        if (examRecords.size() > 0) {
+            ExamRecordDataEntity examRecordDataEntity = examRecords.get(0);
+            offlineExamCourseInfo.setExamRecordDataId(examRecordDataEntity.getId());
+            offlineExamCourseInfo.setStatus(examRecordDataEntity.getExamRecordStatus());
+            offlineExamCourseInfo.setPaperId(examRecordDataEntity.getBasePaperId());
+            if (examRecordDataEntity.getExamRecordStatus() == ExamRecordStatus.EXAM_END) {
+                ExamRecordForMarkingEntity examRecordForMarkingEntity = examRecordForMarkingRepo.findByExamRecordDataId(examRecordDataEntity.getId());
+                offlineExamCourseInfo.setOfflineFileUrl(examRecordForMarkingEntity.getOfflineFileUrl());
+                offlineExamCourseInfo.setFileName(examRecordForMarkingEntity.getOfflineFileName());
+            }
+        }
+        return offlineExamCourseInfo;
+    }
+
+    @Override
+    public void startOfflineExam(Long examStudentId) {
+        SysPropertyCacheBean stuClientLoginLimit = CacheHelper.getSysProperty("STU_CLIENT_LOGIN_LIMIT");
+        Boolean stuClientLoginLimitBoolean = false;
+        if (stuClientLoginLimit.getHasValue()) {
+            stuClientLoginLimitBoolean = Boolean.valueOf(stuClientLoginLimit.getValue().toString());
+        }
+        if (stuClientLoginLimitBoolean) {
+            throw new StatusException("1001", "系统维护中... ...");
+        }
+        List<ExamRecordDataEntity> examRecordList = examRecordDataRepo.findByExamStudentId(examStudentId);
+        if (examRecordList != null && examRecordList.size() > 0) {
+            throw new StatusException("1002", "已经存在examStudentId=" + examStudentId + "的离线考试记录");
+        }
+        //获取考生信息
+        ExamStudentEntity examStudentEntity = examStudentRepo.findByExamStudentId(examStudentId);
+        ExamStudentBean bean=of(examStudentEntity);
+        //检查并获取课程信息
+        CourseBean courseBean = checkCourse(bean);
+        //检查并获取考试信息
+        ExamBean examBean = checkExam(bean);
+        //获取题库试卷结构(由于存在随机抽卷,所以不能缓存 )
+        GetPaperReq getPaperReq = new GetPaperReq();
+        getPaperReq.setExamId(examStudentEntity.getExamId());
+        getPaperReq.setCourseCode(courseBean.getCode());
+        getPaperReq.setGroupCode(examStudentEntity.getPaperType());
+        GetPaperResp getPaperResp = extractConfigCloudService.getPaper(getPaperReq);
+
+        //生成考试记录
+        ExamRecordDataEntity examRecordData = examRecordDataService.createOfflineExamRecordData(bean,
+                examBean, courseBean, getPaperResp.getPaperId(),
+                null, getPaperResp.getDefaultPaper().getFullyObjective());
+        //生成分数
+        examScoreService.createExamScoreWithOffline(examRecordData.getId());
+        //更新考生
+        examStudentRepo.updateExamStudentFinished(examStudentId);
+    }
+
+    private CourseBean checkCourse(ExamStudentBean bean) {
+        CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(bean.getCourseId());
+        if (!courseBean.getEnable()) {
+            throw new StatusException("2001", "该课程已被禁用");
+        }
+        return courseBean;
+    }
+
+    private ExamBean checkExam(ExamStudentBean bean) {
+        Long examId = bean.getExamId();
+        Long studentId = bean.getStudentId();
+        ExamBean examBean = ExamCacheTransferHelper.getCachedExam(examId, studentId);
+
+        //如果启用了了特殊设置,并且无特殊设置时结束考试 配置 设置为true..且实际未设置特殊设置则不允许考试
+        ExamPropertyCacheBean limitedIfNoSpecialSettings = ExamCacheTransferHelper.getDefaultCachedExamProperty(examId,
+                ExamProperties.LIMITED_IF_NO_SPECIAL_SETTINGS.toString());
+        if (examBean.getSpecialSettingsEnabled() &&
+                (limitedIfNoSpecialSettings.getHasValue() && Boolean.valueOf(limitedIfNoSpecialSettings.getValue()))) {
+
+            //学生特殊设置开启未配置,不允许考试
+            if (examBean.getSpecialSettingsType() == ExamSpecialSettingsType.STUDENT_BASED) {
+                ExamStudentSettingsCacheBean specialSettings = CacheHelper.getExamStudentSettings(examId, studentId);
+                if (!specialSettings.getHasValue()) {
+                    throw new StatusException("3001", "考试配置未完成,不允许考试");
+                }
+            }
+
+            //机构特殊设置开启未配置,不允许考试
+            if (examBean.getSpecialSettingsType() == ExamSpecialSettingsType.ORG_BASED) {
+                //需求调整,所有的组织机构取学生表所关联的orgId
+                Long orgId = CacheHelper.getStudent(studentId).getOrgId();
+                ExamOrgSettingsCacheBean specialSettings = CacheHelper.getExamOrgSettings(examId, orgId);
+                if (!specialSettings.getHasValue()) {
+                    throw new StatusException("3002", "考试配置未完成,不允许考试");
+                }
+            }
+        }
+
+        if (!examBean.getEnable() || (examBean.getExamLimit() != null && examBean.getExamLimit())) {
+            throw new StatusException("3003", "暂无考试资格,请与学校老师联系");
+        }
+        if (new Date().before(examBean.getBeginTime())) {
+            throw new StatusException("3004", "考试未开始");
+        }
+        if (examBean.getEndTime().before(new Date())) {
+            throw new StatusException("3005", "本次考试已结束");
+        }
+        return examBean;
+    }
+
+    @Override
+    @Transactional
+    public void submitPaper(Long examRecordDataId, File tempFile) throws Exception {
+        SysPropertyCacheBean stuClientLoginLimit = CacheHelper.getSysProperty("STU_CLIENT_LOGIN_LIMIT");
+        Boolean stuClientLoginLimitBoolean = false;
+        if (stuClientLoginLimit.getHasValue()) {
+            stuClientLoginLimitBoolean = Boolean.valueOf(stuClientLoginLimit.getValue().toString());
+        }
+        if (stuClientLoginLimitBoolean) {
+            throw new StatusException("4001", "系统维护中... ...");
+        }
+        ExamRecordDataEntity examRecordDataEntity = GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId, ExamRecordDataEntity.class);
+        if (examRecordDataEntity == null) {
+            return;
+        }
+        ExamRecordDataBean bean=of(examRecordDataEntity);
+        String fileName = tempFile.getName();
+        String fileSuffix = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).toLowerCase();
+        //上传文件至又拍云
+        String fileNewName = createOfflineFileName(bean) + "." + fileSuffix;
+
+        ExamRecordDataEntity examRecordData = GlobalHelper.getEntity(examRecordDataRepo, examRecordDataId, ExamRecordDataEntity.class);
+        String upyunFilePath = upyunUploadUrl + examRecordData.getExamId() + "/" + fileNewName;
+        UpYun upyun = new UpYun(bucketName, userName, password);
+        upyun.writeFile(upyunFilePath, tempFile, true);
+        tempFile.delete();
+        //保存 文件信息
+        String fileUrl = upyunFileUrl + upyunFilePath;
+        examRecordForMarkingService.saveOffLineExamRecordForMarking(bean, fileNewName, fileUrl);
+        //更新考试记录状态
+        examRecordDataEntity.setExamRecordStatus(ExamRecordStatus.EXAM_END);
+        examRecordDataEntity.setEndTime(new Date());//交卷(上传)时间
+        examRecordDataRepo.save(examRecordDataEntity);
+    }
+
+
+
+    private String createOfflineFileName(ExamRecordDataBean examRecordData) {
+        long currentTime = System.currentTimeMillis();
+
+        long orgId = examRecordData.getOrgId();
+        OrgCacheBean orgBean = gainBaseDataService.getOrgBean(orgId);
+        long courseId = examRecordData.getCourseId();
+        CourseBean courseBean = ExamCacheTransferHelper.getCachedCourse(courseId);
+        return orgBean.getCode() + "_" +
+        examRecordData.getStudentCode() + "_" +
+        examRecordData.getStudentName() + "_" +
+                courseBean.getCode() + "_" + currentTime;
+    }
+
+    private ExamRecordDataBean of(ExamRecordDataEntity record) {
+        ExamRecordDataBean info = new ExamRecordDataBean();
+        //封装基础数据
+        info.setId(record.getId());
+        info.setExamId(record.getExamId());
+        info.setExamType(record.getExamType());
+        info.setExamStudentId(record.getExamStudentId());
+        info.setStudentId(record.getStudentId());
+        info.setStudentCode(record.getStudentCode());
+        info.setStudentName(record.getStudentName());
+        info.setIdentityNumber(record.getIdentityNumber());
+        info.setCourseId(record.getCourseId());
+        
+        info.setCourseLevel(record.getCourseLevel());
+        info.setOrgId(record.getOrgId());
+        
+        info.setRootOrgId(record.getRootOrgId());
+        info.setBasePaperId(record.getBasePaperId());
+        info.setPaperStructId(record.getPaperStructId());
+        info.setInfoCollector(record.getInfoCollector());
+        info.setExamRecordQuestionsId(record.getExamRecordQuestionsId());
+        info.setExamRecordStatus(record.getExamRecordStatus());
+        info.setStartTime(record.getStartTime());
+        info.setEndTime(record.getEndTime());
+        info.setCleanTime(record.getCleanTime());
+        info.setIsWarn(record.getIsWarn());
+        info.setIsAudit(record.getIsAudit());
+        info.setIsIllegality(record.getIsIllegality());
+
+        info.setUsedExamTime(record.getUsedExamTime());
+        info.setExamOrder(record.getExamOrder());
+        info.setIsReexamine(record.getIsReexamine());
+        info.setIsContinued(record.getIsContinued());
+        info.setContinuedCount(record.getContinuedCount());
+        info.setIsAllObjectivePaper(record.getIsAllObjectivePaper());
+        info.setIsExceed(record.getIsExceed());
+        info.setFaceSuccessCount(record.getFaceSuccessCount());
+        info.setFaceFailedCount(record.getFaceFailedCount());
+        info.setFaceStrangerCount(record.getFaceStrangerCount());
+        info.setFaceTotalCount(record.getFaceTotalCount());
+        info.setFaceSuccessPercent(record.getFaceSuccessPercent());
+        info.setBaiduFaceLivenessSuccessPercent(record.getBaiduFaceLivenessSuccessPercent());
+        info.setFaceVerifyResult(record.getFaceVerifyResult());
+        info.setFaceLandmarkVal(record.getFaceLandmarkVal());
+        return info;
+    }
+    
+    private ExamStudentBean of(ExamStudentEntity et) {
+        ExamStudentBean b=new ExamStudentBean();
+        b.setId(et.getId());
+        b.setExamStudentId(et.getExamStudentId());
+        b.setExamId(et.getExamId());
+        b.setStudentId(et.getStudentId());
+        b.setStudentCode(et.getStudentCode());
+        b.setStudentName(et.getStudentName());
+        b.setIdentityNumber(et.getIdentityNumber());
+        b.setCourseId(et.getCourseId());
+        b.setCourseCode(et.getCourseCode());
+        b.setCourseLevel(et.getCourseLevel());
+        b.setOrgId(et.getOrgId());
+        b.setRootOrgId(et.getRootOrgId());
+        b.setSpecialtyCode(et.getSpecialtyCode());
+        b.setSpecialtyName(et.getSpecialtyName());
+        b.setPaperType(et.getPaperType());
+        b.setInfoCollector(et.getInfoCollector());
+        b.setFinished(et.getFinished());
+        b.setUsedNum(et.getUsedNum());
+        b.setExtraNum(et.getExtraNum());
+        b.setGrade(et.getGrade());
+        b.setEnable(et.getEnable());
+        return b;
+    }
+}