ソースを参照

异步任务终止

xiatian 2 年 前
コミット
3e4fc2c9ca
14 ファイル変更1038 行追加909 行削除
  1. 8 2
      examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExportTaskController.java
  2. 12 0
      examcloud-core-oe-admin-base/src/main/java/cn/com/qmth/examcloud/core/oe/admin/base/ExportTaskStopException.java
  3. 3 1
      examcloud-core-oe-admin-dao/src/main/java/cn/com/qmth/examcloud/core/oe/admin/dao/enums/ExportTaskStatus.java
  4. 4 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/ExportTaskService.java
  5. 208 199
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditQuery.java
  6. 306 298
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordQuery.java
  7. 8 1
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreQuery.java
  8. 11 0
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examstudent/ExamStudentQuery.java
  9. 409 392
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/AsyncExportServiceImpl.java
  10. 4 1
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamAuditServiceImpl.java
  11. 4 1
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordServiceImpl.java
  12. 3 1
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamScoreServiceImpl.java
  13. 3 1
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamStudentServiceImpl.java
  14. 55 12
      examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExportTaskServiceImpl.java

+ 8 - 2
examcloud-core-oe-admin-api-provider/src/main/java/cn/com/qmth/examcloud/core/oe/admin/api/controller/ExportTaskController.java

@@ -24,7 +24,7 @@ public class ExportTaskController extends ControllerSupport {
     @Autowired
     private ExportTaskService exportTaskService;
 
-    @PostMapping("/list")
+    @PostMapping("list")
     @ApiOperation(value = "查询导出任务列表(分页)")
     public Page<ExportTaskListResp> list(@RequestBody ExportTaskListReq req) {
         User user = getAccessUser();
@@ -33,10 +33,16 @@ public class ExportTaskController extends ControllerSupport {
         return exportTaskService.getExportTaskList(req, user.getUserId());
     }
 
-    @PostMapping("/delete")
+    @PostMapping("delete")
     @ApiOperation(value = "删除某个导出任务")
     public void delete(@RequestParam Long taskId) {
         exportTaskService.deleteExportTaskById(taskId);
     }
+    
+    @PostMapping("stop")
+    @ApiOperation(value = "终止某个导出任务")
+    public void stop(@RequestParam Long taskId) {
+        exportTaskService.stopExportTaskById(taskId);
+    }
 
 }

+ 12 - 0
examcloud-core-oe-admin-base/src/main/java/cn/com/qmth/examcloud/core/oe/admin/base/ExportTaskStopException.java

@@ -0,0 +1,12 @@
+package cn.com.qmth.examcloud.core.oe.admin.base;
+
+public class ExportTaskStopException extends RuntimeException {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -8668354677070051226L;
+
+
+
+}

+ 3 - 1
examcloud-core-oe-admin-dao/src/main/java/cn/com/qmth/examcloud/core/oe/admin/dao/enums/ExportTaskStatus.java

@@ -18,7 +18,9 @@ public enum ExportTaskStatus {
 
     ERROR("失败"),
     
-    TERMINATE("终止")
+    TERMINATING("终止中"),
+    
+    TERMINATE("已终止"),
     ;
 
     ExportTaskStatus(String desc) {

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

@@ -40,4 +40,8 @@ public interface ExportTaskService {
     
     ExportTaskEntity findById(Long taskId);
 
+	void stopExportTaskById(Long taskId);
+
+	void checkStopExportTaskById(Long taskId);
+
 }

+ 208 - 199
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examaudit/ExamAuditQuery.java

@@ -21,270 +21,272 @@ import io.swagger.annotations.ApiModelProperty;
 @ApiModel
 public class ExamAuditQuery implements JsonSerializable {
 
-    @ApiModelProperty("当前页数")
-    private Integer pageNo;
+	@ApiModelProperty("当前页数")
+	private Integer pageNo;
 
-    @ApiModelProperty("每页条数")
-    private Integer pageSize;
+	@ApiModelProperty("每页条数")
+	private Integer pageSize;
 
-    @ApiModelProperty("学习中心ID")
-    private Long orgId;
+	@ApiModelProperty("学习中心ID")
+	private Long orgId;
 
-    @ApiModelProperty("机构ID")
-    private Long rootOrgId;
+	@ApiModelProperty("机构ID")
+	private Long rootOrgId;
 
-    @ApiModelProperty("考试ID")
-    private Long examId;
+	@ApiModelProperty("考试ID")
+	private Long examId;
 
-    @ApiModelProperty("场次ID")
-    private Long examStageId;
+	@ApiModelProperty("场次ID")
+	private Long examStageId;
 
-    @ApiModelProperty("学号")
-    private String studentCode;
+	@ApiModelProperty("学号")
+	private String studentCode;
 
-    @ApiModelProperty("学生姓名")
-    private String studentName;
+	@ApiModelProperty("学生姓名")
+	private String studentName;
 
-    @ApiModelProperty("身份证号")
-    private String identityNumber;
+	@ApiModelProperty("身份证号")
+	private String identityNumber;
 
-    @ApiModelProperty("课程ID")
-    private Long courseId;
+	@ApiModelProperty("课程ID")
+	private Long courseId;
 
-    @ApiModelProperty("课程层次")
-    private String courseLevel;
+	@ApiModelProperty("课程层次")
+	private String courseLevel;
 
-    @ApiModelProperty("考试记录ID")
-    private Long examRecordDataId;
+	@ApiModelProperty("考试记录ID")
+	private Long examRecordDataId;
 
-    @ApiModelProperty("审核状态")
-    private String status;
+	@ApiModelProperty("审核状态")
+	private String status;
 
-    @ApiModelProperty("违纪类型")
-    private String disciplineType;
+	@ApiModelProperty("违纪类型")
+	private String disciplineType;
 
-    @ApiModelProperty("审核人")
-    private String auditUserName;
+	@ApiModelProperty("审核人")
+	private String auditUserName;
 
-    @ApiModelProperty("开考起始时间")
-    private String startTime;
+	@ApiModelProperty("开考起始时间")
+	private String startTime;
 
-    @ApiModelProperty("开考截止时间")
-    private String endTime;
+	@ApiModelProperty("开考截止时间")
+	private String endTime;
 
-    @ApiModelProperty("交卷起始时间")
-    private String submitStartTime;
+	@ApiModelProperty("交卷起始时间")
+	private String submitStartTime;
 
-    @ApiModelProperty("交卷截止时间")
-    private String submitEndTime;
+	@ApiModelProperty("交卷截止时间")
+	private String submitEndTime;
 
-    @ApiModelProperty("审核起始时间")
-    private String auditStartTime;
+	@ApiModelProperty("审核起始时间")
+	private String auditStartTime;
 
-    @ApiModelProperty("审核截止时间")
-    private String auditEndTime;
+	@ApiModelProperty("审核截止时间")
+	private String auditEndTime;
 
-    @ApiModelProperty("Ip")
-    private String ip;
+	@ApiModelProperty("Ip")
+	private String ip;
 
-    /**
-     * 查询或导出  select  export
-     */
-    private SelectType selectType;
-    
-    @ApiModelProperty("切屏次数起始")
-    private Integer switchScreenCountStart;
-    
-    @ApiModelProperty("切屏次数截止")
-    private Integer switchScreenCountEnd;
+	/**
+	 * 查询或导出 select export
+	 */
+	private SelectType selectType;
 
-    public Integer getPageNo() {
-        return pageNo;
-    }
+	@ApiModelProperty("切屏次数起始")
+	private Integer switchScreenCountStart;
 
-    public void setPageNo(Integer pageNo) {
-        this.pageNo = pageNo;
-    }
+	@ApiModelProperty("切屏次数截止")
+	private Integer switchScreenCountEnd;
+	@ApiModelProperty(hidden = true)
+	private Long taskId;
 
-    public Integer getPageSize() {
-        return pageSize;
-    }
+	public Integer getPageNo() {
+		return pageNo;
+	}
+
+	public void setPageNo(Integer pageNo) {
+		this.pageNo = pageNo;
+	}
+
+	public Integer getPageSize() {
+		return pageSize;
+	}
+
+	public void setPageSize(Integer pageSize) {
+		this.pageSize = pageSize;
+	}
 
-    public void setPageSize(Integer pageSize) {
-        this.pageSize = pageSize;
-    }
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
 
-    public Long getOrgId() {
-        return orgId;
-    }
+	public Long getExamId() {
+		return examId;
+	}
 
-    public void setOrgId(Long orgId) {
-        this.orgId = orgId;
-    }
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	public String getStudentCode() {
+		return studentCode;
+	}
 
-    public Long getExamId() {
-        return examId;
-    }
+	public void setStudentCode(String studentCode) {
+		this.studentCode = studentCode;
+	}
 
-    public void setExamId(Long examId) {
-        this.examId = examId;
-    }
+	public String getStudentName() {
+		return studentName;
+	}
 
-    public String getStudentCode() {
-        return studentCode;
-    }
+	public void setStudentName(String studentName) {
+		this.studentName = studentName;
+	}
 
-    public void setStudentCode(String studentCode) {
-        this.studentCode = studentCode;
-    }
+	public String getIdentityNumber() {
+		return identityNumber;
+	}
 
-    public String getStudentName() {
-        return studentName;
-    }
+	public void setIdentityNumber(String identityNumber) {
+		this.identityNumber = identityNumber;
+	}
 
-    public void setStudentName(String studentName) {
-        this.studentName = studentName;
-    }
+	public Long getCourseId() {
+		return courseId;
+	}
 
-    public String getIdentityNumber() {
-        return identityNumber;
-    }
+	public void setCourseId(Long courseId) {
+		this.courseId = courseId;
+	}
 
-    public void setIdentityNumber(String identityNumber) {
-        this.identityNumber = identityNumber;
-    }
+	public String getCourseLevel() {
+		return courseLevel;
+	}
 
-    public Long getCourseId() {
-        return courseId;
-    }
+	public void setCourseLevel(String courseLevel) {
+		this.courseLevel = courseLevel;
+	}
 
-    public void setCourseId(Long courseId) {
-        this.courseId = courseId;
-    }
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
 
-    public String getCourseLevel() {
-        return courseLevel;
-    }
+	public void setExamRecordId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
 
-    public void setCourseLevel(String courseLevel) {
-        this.courseLevel = courseLevel;
-    }
+	public String getStatus() {
+		return status;
+	}
 
-    public Long getExamRecordDataId() {
-        return examRecordDataId;
-    }
+	public void setStatus(String status) {
+		this.status = status;
+	}
 
-    public void setExamRecordId(Long examRecordDataId) {
-        this.examRecordDataId = examRecordDataId;
-    }
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
 
-    public String getStatus() {
-        return status;
-    }
-
-    public void setStatus(String status) {
-        this.status = status;
-    }
-
-    public Long getRootOrgId() {
-        return rootOrgId;
-    }
-
-    public void setRootOrgId(Long rootOrgId) {
-        this.rootOrgId = rootOrgId;
-    }
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
 
-    public SelectType getSelectType() {
-        return selectType;
-    }
+	public SelectType getSelectType() {
+		return selectType;
+	}
 
-    public void setSelectType(SelectType selectType) {
-        this.selectType = selectType;
-    }
+	public void setSelectType(SelectType selectType) {
+		this.selectType = selectType;
+	}
 
-    public String getDisciplineType() {
-        return disciplineType;
-    }
+	public String getDisciplineType() {
+		return disciplineType;
+	}
 
-    public void setDisciplineType(String disciplineType) {
-        this.disciplineType = disciplineType;
-    }
+	public void setDisciplineType(String disciplineType) {
+		this.disciplineType = disciplineType;
+	}
 
-    public void setExamRecordDataId(Long examRecordDataId) {
-        this.examRecordDataId = examRecordDataId;
-    }
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
 
-    public String getAuditUserName() {
-        return auditUserName;
-    }
+	public String getAuditUserName() {
+		return auditUserName;
+	}
 
-    public void setAuditUserName(String auditUserName) {
-        this.auditUserName = auditUserName;
-    }
+	public void setAuditUserName(String auditUserName) {
+		this.auditUserName = auditUserName;
+	}
 
-    public Long getExamStageId() {
-        return examStageId;
-    }
+	public Long getExamStageId() {
+		return examStageId;
+	}
 
-    public void setExamStageId(Long examStageId) {
-        this.examStageId = examStageId;
-    }
+	public void setExamStageId(Long examStageId) {
+		this.examStageId = examStageId;
+	}
 
-    public String getStartTime() {
-        return startTime;
-    }
+	public String getStartTime() {
+		return startTime;
+	}
 
-    public void setStartTime(String startTime) {
-        this.startTime = startTime;
-    }
+	public void setStartTime(String startTime) {
+		this.startTime = startTime;
+	}
 
-    public String getEndTime() {
-        return endTime;
-    }
+	public String getEndTime() {
+		return endTime;
+	}
 
-    public void setEndTime(String endTime) {
-        this.endTime = endTime;
-    }
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
 
-    public String getSubmitStartTime() {
-        return submitStartTime;
-    }
+	public String getSubmitStartTime() {
+		return submitStartTime;
+	}
 
-    public void setSubmitStartTime(String submitStartTime) {
-        this.submitStartTime = submitStartTime;
-    }
+	public void setSubmitStartTime(String submitStartTime) {
+		this.submitStartTime = submitStartTime;
+	}
 
-    public String getSubmitEndTime() {
-        return submitEndTime;
-    }
+	public String getSubmitEndTime() {
+		return submitEndTime;
+	}
 
-    public void setSubmitEndTime(String submitEndTime) {
-        this.submitEndTime = submitEndTime;
-    }
+	public void setSubmitEndTime(String submitEndTime) {
+		this.submitEndTime = submitEndTime;
+	}
 
-    public String getAuditStartTime() {
-        return auditStartTime;
-    }
+	public String getAuditStartTime() {
+		return auditStartTime;
+	}
 
-    public void setAuditStartTime(String auditStartTime) {
-        this.auditStartTime = auditStartTime;
-    }
+	public void setAuditStartTime(String auditStartTime) {
+		this.auditStartTime = auditStartTime;
+	}
 
-    public String getAuditEndTime() {
-        return auditEndTime;
-    }
+	public String getAuditEndTime() {
+		return auditEndTime;
+	}
 
-    public void setAuditEndTime(String auditEndTime) {
-        this.auditEndTime = auditEndTime;
-    }
+	public void setAuditEndTime(String auditEndTime) {
+		this.auditEndTime = auditEndTime;
+	}
 
-    public String getIp() {
-        return ip;
-    }
+	public String getIp() {
+		return ip;
+	}
 
-    public void setIp(String ip) {
-        this.ip = ip;
-    }
+	public void setIp(String ip) {
+		this.ip = ip;
+	}
 
 	public Integer getSwitchScreenCountStart() {
 		return switchScreenCountStart;
@@ -302,5 +304,12 @@ public class ExamAuditQuery implements JsonSerializable {
 		this.switchScreenCountEnd = switchScreenCountEnd;
 	}
 
-    
+	public Long getTaskId() {
+		return taskId;
+	}
+
+	public void setTaskId(Long taskId) {
+		this.taskId = taskId;
+	}
+
 }

+ 306 - 298
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examrecord/ExamRecordQuery.java

@@ -25,380 +25,381 @@ import java.util.List;
 @ApiModel
 public class ExamRecordQuery implements JsonSerializable {
 
-    private static final long serialVersionUID = 4227973258005786636L;
+	private static final long serialVersionUID = 4227973258005786636L;
 
-    private Long rootOrgId;
+	private Long rootOrgId;
 
-    private Long examRecordDataId;
+	private Long examRecordDataId;
 
-    @ApiModelProperty("当前页数")
-    private Integer pageNo;
+	@ApiModelProperty("当前页数")
+	private Integer pageNo;
 
-    @ApiModelProperty("每页条数")
-    private Integer pageSize;
+	@ApiModelProperty("每页条数")
+	private Integer pageSize;
 
-    @ApiModelProperty("学习中心ID")
-    private Long orgId;
+	@ApiModelProperty("学习中心ID")
+	private Long orgId;
 
-    @ApiModelProperty("考试ID")
-    private Long examId;
+	@ApiModelProperty("考试ID")
+	private Long examId;
 
-    @ApiModelProperty("场次ID")
-    private Long examStageId;
+	@ApiModelProperty("场次ID")
+	private Long examStageId;
 
-    @ApiModelProperty("学号")
-    private String studentCode;
+	@ApiModelProperty("学号")
+	private String studentCode;
 
-    @ApiModelProperty("学生姓名")
-    private String studentName;
+	@ApiModelProperty("学生姓名")
+	private String studentName;
 
-    @ApiModelProperty("身份证号")
-    private String identityNumber;
+	@ApiModelProperty("身份证号")
+	private String identityNumber;
 
-    @ApiModelProperty("课程ID")
-    private Long courseId;
+	@ApiModelProperty("课程ID")
+	private Long courseId;
 
-    @ApiModelProperty("课程层次")
-    private String courseLevel;
+	@ApiModelProperty("课程层次")
+	private String courseLevel;
 
-    @ApiModelProperty("考试记录ID")
-    private Long examRecordId;
+	@ApiModelProperty("考试记录ID")
+	private Long examRecordId;
 
-    @ApiModelProperty("审核状态")
-    private Boolean isAudit;
+	@ApiModelProperty("审核状态")
+	private Boolean isAudit;
 
-    @ApiModelProperty("是否需要审核")
-    private Boolean isWarn;
+	@ApiModelProperty("是否需要审核")
+	private Boolean isWarn;
 
-    @ApiModelProperty("是否有陌生人脸")
-    private Boolean hasStranger;
+	@ApiModelProperty("是否有陌生人脸")
+	private Boolean hasStranger;
 
-    @ApiModelProperty("人脸识别成功率下限")
-    private Double faceSuccessPercentLower;
+	@ApiModelProperty("人脸识别成功率下限")
+	private Double faceSuccessPercentLower;
 
-    @ApiModelProperty("人脸识别成功率上限")
-    private Double faceSuccessPercentUpper;
+	@ApiModelProperty("人脸识别成功率上限")
+	private Double faceSuccessPercentUpper;
 
-    @ApiModelProperty("人脸真实性比率下限")
-    private Double livenessSuccessPercentLower;
+	@ApiModelProperty("人脸真实性比率下限")
+	private Double livenessSuccessPercentLower;
 
-    @ApiModelProperty("人脸真实性比率上限")
-    private Double livenessSuccessPercentUpper;
+	@ApiModelProperty("人脸真实性比率上限")
+	private Double livenessSuccessPercentUpper;
 
-    @ApiModelProperty("考试记录状态:EXAM_ING、EXAM_END、EXAM_OVERDUE、EXAM_INVALID")
-    private List<String> recordStatuses;
+	@ApiModelProperty("考试记录状态:EXAM_ING、EXAM_END、EXAM_OVERDUE、EXAM_INVALID")
+	private List<String> recordStatuses;
 
-    @ApiModelProperty("采集人")
-    private String infoCollector;
+	@ApiModelProperty("采集人")
+	private String infoCollector;
 
-    @ApiModelProperty("开考起始时间")
-    private String startTime;
+	@ApiModelProperty("开考起始时间")
+	private String startTime;
 
-    @ApiModelProperty("开考截止时间")
-    private String endTime;
+	@ApiModelProperty("开考截止时间")
+	private String endTime;
 
-    @ApiModelProperty("是否有虚拟设备")
-    private Boolean hasVirtual;
+	@ApiModelProperty("是否有虚拟设备")
+	private Boolean hasVirtual;
 
-    @ApiModelProperty("交卷起始时间")
-    private String submitStartTime;
+	@ApiModelProperty("交卷起始时间")
+	private String submitStartTime;
 
-    @ApiModelProperty("交卷截止时间")
-    private String submitEndTime;
+	@ApiModelProperty("交卷截止时间")
+	private String submitEndTime;
 
-    @ApiModelProperty("是否违纪")
-    private Boolean isIllegality;
+	@ApiModelProperty("是否违纪")
+	private Boolean isIllegality;
 
-    @ApiModelProperty("创建人ID")
-    private Long creator;
+	@ApiModelProperty("创建人ID")
+	private Long creator;
 
-    @ApiModelProperty("Ip")
-    private String ip;
-    
-    @ApiModelProperty("虚拟设备名")
-    private String virtualName;
+	@ApiModelProperty("Ip")
+	private String ip;
 
-    @ApiModelProperty("审核人")
-    private String auditUserName;
-    
-    @ApiModelProperty("切屏次数起始")
-    private Integer switchScreenCountStart;
-    
-    @ApiModelProperty("切屏次数截止")
-    private Integer switchScreenCountEnd;
+	@ApiModelProperty("虚拟设备名")
+	private String virtualName;
 
-    public ExamRecordQuery addRecordStatus(String recordStatus) {
-        if (recordStatuses == null) {
-            recordStatuses = new ArrayList<>();
-        }
-        if (StringUtils.isNotBlank(recordStatus)) {
-            recordStatuses.add(recordStatus);
-        }
-        return this;
-    }
+	@ApiModelProperty("审核人")
+	private String auditUserName;
 
-    public List<ExamRecordStatus> getRecordStatusList() {
-        List<ExamRecordStatus> list = new ArrayList<>();
-        if (recordStatuses != null) {
-            for (String name : recordStatuses) {
-                ExamRecordStatus status = ExamRecordStatus.getByName(name);
-                if (status != null) {
-                    list.add(status);
-                }
-            }
-        }
-        return list;
-    }
+	@ApiModelProperty("切屏次数起始")
+	private Integer switchScreenCountStart;
 
-    public Long getRootOrgId() {
-        return rootOrgId;
-    }
+	@ApiModelProperty("切屏次数截止")
+	private Integer switchScreenCountEnd;
 
-    public void setRootOrgId(Long rootOrgId) {
-        this.rootOrgId = rootOrgId;
-    }
+	@ApiModelProperty(hidden = true)
+	private Long taskId;
 
-    public List<String> getRecordStatuses() {
-        return recordStatuses;
-    }
-
-    public void setRecordStatuses(List<String> recordStatuses) {
-        this.recordStatuses = recordStatuses;
-    }
-
-    public Integer getPageNo() {
-        return pageNo;
-    }
-
-    public void setPageNo(Integer pageNo) {
-        this.pageNo = pageNo;
-    }
-
-    public Integer getPageSize() {
-        return pageSize;
-    }
-
-    public void setPageSize(Integer pageSize) {
-        this.pageSize = pageSize;
-    }
-
-    public Long getOrgId() {
-        return orgId;
-    }
-
-    public void setOrgId(Long orgId) {
-        this.orgId = orgId;
-    }
-
-    public Long getExamId() {
-        return examId;
-    }
-
-    public void setExamId(Long examId) {
-        this.examId = examId;
-    }
-
-    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 getCourseLevel() {
-        return courseLevel;
-    }
-
-    public void setCourseLevel(String courseLevel) {
-        this.courseLevel = courseLevel;
-    }
-
-    public Long getExamRecordId() {
-        return examRecordId;
-    }
+	public ExamRecordQuery addRecordStatus(String recordStatus) {
+		if (recordStatuses == null) {
+			recordStatuses = new ArrayList<>();
+		}
+		if (StringUtils.isNotBlank(recordStatus)) {
+			recordStatuses.add(recordStatus);
+		}
+		return this;
+	}
+
+	public List<ExamRecordStatus> getRecordStatusList() {
+		List<ExamRecordStatus> list = new ArrayList<>();
+		if (recordStatuses != null) {
+			for (String name : recordStatuses) {
+				ExamRecordStatus status = ExamRecordStatus.getByName(name);
+				if (status != null) {
+					list.add(status);
+				}
+			}
+		}
+		return list;
+	}
+
+	public Long getRootOrgId() {
+		return rootOrgId;
+	}
+
+	public void setRootOrgId(Long rootOrgId) {
+		this.rootOrgId = rootOrgId;
+	}
+
+	public List<String> getRecordStatuses() {
+		return recordStatuses;
+	}
+
+	public void setRecordStatuses(List<String> recordStatuses) {
+		this.recordStatuses = recordStatuses;
+	}
+
+	public Integer getPageNo() {
+		return pageNo;
+	}
+
+	public void setPageNo(Integer pageNo) {
+		this.pageNo = pageNo;
+	}
+
+	public Integer getPageSize() {
+		return pageSize;
+	}
+
+	public void setPageSize(Integer pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	public Long getOrgId() {
+		return orgId;
+	}
+
+	public void setOrgId(Long orgId) {
+		this.orgId = orgId;
+	}
+
+	public Long getExamId() {
+		return examId;
+	}
+
+	public void setExamId(Long examId) {
+		this.examId = examId;
+	}
+
+	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 getCourseLevel() {
+		return courseLevel;
+	}
 
-    public void setExamRecordId(Long examRecordId) {
-        this.examRecordId = examRecordId;
-    }
+	public void setCourseLevel(String courseLevel) {
+		this.courseLevel = courseLevel;
+	}
 
-    public Boolean getHasStranger() {
-        return hasStranger;
-    }
+	public Long getExamRecordId() {
+		return examRecordId;
+	}
 
-    public void setHasStranger(Boolean hasStranger) {
-        this.hasStranger = hasStranger;
-    }
+	public void setExamRecordId(Long examRecordId) {
+		this.examRecordId = examRecordId;
+	}
 
-    public Double getFaceSuccessPercentLower() {
-        return faceSuccessPercentLower;
-    }
+	public Boolean getHasStranger() {
+		return hasStranger;
+	}
 
-    public void setFaceSuccessPercentLower(Double faceSuccessPercentLower) {
-        this.faceSuccessPercentLower = faceSuccessPercentLower;
-    }
+	public void setHasStranger(Boolean hasStranger) {
+		this.hasStranger = hasStranger;
+	}
 
-    public Double getFaceSuccessPercentUpper() {
-        return faceSuccessPercentUpper;
-    }
+	public Double getFaceSuccessPercentLower() {
+		return faceSuccessPercentLower;
+	}
 
-    public void setFaceSuccessPercentUpper(Double faceSuccessPercentUpper) {
-        this.faceSuccessPercentUpper = faceSuccessPercentUpper;
-    }
+	public void setFaceSuccessPercentLower(Double faceSuccessPercentLower) {
+		this.faceSuccessPercentLower = faceSuccessPercentLower;
+	}
 
-    public Double getLivenessSuccessPercentLower() {
-        return livenessSuccessPercentLower;
-    }
+	public Double getFaceSuccessPercentUpper() {
+		return faceSuccessPercentUpper;
+	}
 
-    public void setLivenessSuccessPercentLower(Double livenessSuccessPercentLower) {
-        this.livenessSuccessPercentLower = livenessSuccessPercentLower;
-    }
+	public void setFaceSuccessPercentUpper(Double faceSuccessPercentUpper) {
+		this.faceSuccessPercentUpper = faceSuccessPercentUpper;
+	}
 
-    public Double getLivenessSuccessPercentUpper() {
-        return livenessSuccessPercentUpper;
-    }
+	public Double getLivenessSuccessPercentLower() {
+		return livenessSuccessPercentLower;
+	}
 
-    public void setLivenessSuccessPercentUpper(Double livenessSuccessPercentUpper) {
-        this.livenessSuccessPercentUpper = livenessSuccessPercentUpper;
-    }
+	public void setLivenessSuccessPercentLower(Double livenessSuccessPercentLower) {
+		this.livenessSuccessPercentLower = livenessSuccessPercentLower;
+	}
 
-    public String getInfoCollector() {
-        return infoCollector;
-    }
+	public Double getLivenessSuccessPercentUpper() {
+		return livenessSuccessPercentUpper;
+	}
 
-    public void setInfoCollector(String infoCollector) {
-        this.infoCollector = infoCollector;
-    }
+	public void setLivenessSuccessPercentUpper(Double livenessSuccessPercentUpper) {
+		this.livenessSuccessPercentUpper = livenessSuccessPercentUpper;
+	}
 
-    public String getStartTime() {
-        return startTime;
-    }
+	public String getInfoCollector() {
+		return infoCollector;
+	}
 
-    public void setStartTime(String startTime) {
-        this.startTime = startTime;
-    }
+	public void setInfoCollector(String infoCollector) {
+		this.infoCollector = infoCollector;
+	}
 
-    public String getEndTime() {
-        return endTime;
-    }
+	public String getStartTime() {
+		return startTime;
+	}
 
-    public void setEndTime(String endTime) {
-        this.endTime = endTime;
-    }
+	public void setStartTime(String startTime) {
+		this.startTime = startTime;
+	}
 
-    public Long getExamRecordDataId() {
-        return examRecordDataId;
-    }
+	public String getEndTime() {
+		return endTime;
+	}
 
-    public void setExamRecordDataId(Long examRecordDataId) {
-        this.examRecordDataId = examRecordDataId;
-    }
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
 
-    public Boolean getIsAudit() {
-        return isAudit;
-    }
+	public Long getExamRecordDataId() {
+		return examRecordDataId;
+	}
 
-    public void setIsAudit(Boolean isAudit) {
-        this.isAudit = isAudit;
-    }
+	public void setExamRecordDataId(Long examRecordDataId) {
+		this.examRecordDataId = examRecordDataId;
+	}
 
-    public Boolean getIsWarn() {
-        return isWarn;
-    }
+	public Boolean getIsAudit() {
+		return isAudit;
+	}
 
-    public void setIsWarn(Boolean isWarn) {
-        this.isWarn = isWarn;
-    }
+	public void setIsAudit(Boolean isAudit) {
+		this.isAudit = isAudit;
+	}
 
+	public Boolean getIsWarn() {
+		return isWarn;
+	}
 
-    public Boolean getHasVirtual() {
-        return hasVirtual;
-    }
+	public void setIsWarn(Boolean isWarn) {
+		this.isWarn = isWarn;
+	}
 
+	public Boolean getHasVirtual() {
+		return hasVirtual;
+	}
 
-    public void setHasVirtual(Boolean hasVirtual) {
-        this.hasVirtual = hasVirtual;
-    }
+	public void setHasVirtual(Boolean hasVirtual) {
+		this.hasVirtual = hasVirtual;
+	}
 
-    public Long getExamStageId() {
-        return examStageId;
-    }
+	public Long getExamStageId() {
+		return examStageId;
+	}
 
-    public void setExamStageId(Long examStageId) {
-        this.examStageId = examStageId;
-    }
+	public void setExamStageId(Long examStageId) {
+		this.examStageId = examStageId;
+	}
 
-    public String getSubmitStartTime() {
-        return submitStartTime;
-    }
+	public String getSubmitStartTime() {
+		return submitStartTime;
+	}
 
-    public void setSubmitStartTime(String submitStartTime) {
-        this.submitStartTime = submitStartTime;
-    }
+	public void setSubmitStartTime(String submitStartTime) {
+		this.submitStartTime = submitStartTime;
+	}
 
-    public String getSubmitEndTime() {
-        return submitEndTime;
-    }
+	public String getSubmitEndTime() {
+		return submitEndTime;
+	}
 
-    public void setSubmitEndTime(String submitEndTime) {
-        this.submitEndTime = submitEndTime;
-    }
+	public void setSubmitEndTime(String submitEndTime) {
+		this.submitEndTime = submitEndTime;
+	}
 
-    public Long getCreator() {
-        return creator;
-    }
+	public Long getCreator() {
+		return creator;
+	}
 
-    public void setCreator(Long creator) {
-        this.creator = creator;
-    }
+	public void setCreator(Long creator) {
+		this.creator = creator;
+	}
 
-    public Boolean getIsIllegality() {
-        return isIllegality;
-    }
+	public Boolean getIsIllegality() {
+		return isIllegality;
+	}
 
-    public void setIsIllegality(Boolean isIllegality) {
-        this.isIllegality = isIllegality;
-    }
+	public void setIsIllegality(Boolean isIllegality) {
+		this.isIllegality = isIllegality;
+	}
 
-    public String getIp() {
-        return ip;
-    }
+	public String getIp() {
+		return ip;
+	}
 
-    public void setIp(String ip) {
-        this.ip = ip;
-    }
+	public void setIp(String ip) {
+		this.ip = ip;
+	}
 
-    public String getAuditUserName() {
-        return auditUserName;
-    }
+	public String getAuditUserName() {
+		return auditUserName;
+	}
 
-    public void setAuditUserName(String auditUserName) {
-        this.auditUserName = auditUserName;
-    }
+	public void setAuditUserName(String auditUserName) {
+		this.auditUserName = auditUserName;
+	}
 
 	public Integer getSwitchScreenCountStart() {
 		return switchScreenCountStart;
@@ -424,5 +425,12 @@ public class ExamRecordQuery implements JsonSerializable {
 		this.virtualName = virtualName;
 	}
 
-    
+	public Long getTaskId() {
+		return taskId;
+	}
+
+	public void setTaskId(Long taskId) {
+		this.taskId = taskId;
+	}
+
 }

+ 8 - 1
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/bean/examscore/ExamScoreQuery.java

@@ -53,7 +53,8 @@ public class ExamScoreQuery implements JsonSerializable {
 
     @ApiModelProperty("创建人ID")
     private Long creator;
-
+	@ApiModelProperty(hidden = true)
+	private Long taskId;
     public Long getRootOrgId() {
         return rootOrgId;
     }
@@ -173,6 +174,12 @@ public class ExamScoreQuery implements JsonSerializable {
     public void setCreator(Long creator) {
         this.creator = creator;
     }
+	public Long getTaskId() {
+		return taskId;
+	}
 
+	public void setTaskId(Long taskId) {
+		this.taskId = taskId;
+	}
 
 }

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

@@ -75,6 +75,9 @@ public class ExamStudentQuery implements JsonSerializable {
 
     //是否忽略是否上传离线考试的答案
     private Boolean ignoreUploadOfflineAnswer;
+    
+	@ApiModelProperty(hidden = true)
+	private Long taskId;
 
     public Integer getPageNo() {
         return pageNo;
@@ -212,5 +215,13 @@ public class ExamStudentQuery implements JsonSerializable {
         this.rootOrgId = rootOrgId;
     }
 
+	public Long getTaskId() {
+		return taskId;
+	}
+
+	public void setTaskId(Long taskId) {
+		this.taskId = taskId;
+	}
+
 
 }

+ 409 - 392
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/AsyncExportServiceImpl.java

@@ -32,6 +32,7 @@ import cn.com.qmth.examcloud.core.basic.api.UserCloudService;
 import cn.com.qmth.examcloud.core.basic.api.bean.UserBean;
 import cn.com.qmth.examcloud.core.basic.api.request.GetUserReq;
 import cn.com.qmth.examcloud.core.basic.api.response.GetUserResp;
+import cn.com.qmth.examcloud.core.oe.admin.base.ExportTaskStopException;
 import cn.com.qmth.examcloud.core.oe.admin.base.utils.Check;
 import cn.com.qmth.examcloud.core.oe.admin.base.utils.RoleUtil;
 import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExportTaskEntity;
@@ -73,399 +74,415 @@ import cn.com.qmth.examcloud.web.security.ResourceManager;
 @Service
 public class AsyncExportServiceImpl implements AsyncExportService {
 
-    private static final Logger LOG = LoggerFactory.getLogger(AsyncExportServiceImpl.class);
-
-    private static final String TASK_EXPORT_DIR = "task_export";
-
-    @Autowired
-    private ExportTaskService exportTaskService;
-
-    @Autowired
-    private ExamRecordService examRecordService;
-
-    @Autowired
-    private ExamScoreService examScoreService;
-
-    @Autowired
-    private SystemProperties systemConfig;
-
-    @Autowired
-    private ExamStudentService examStudentService;
-
-    @Autowired
-    private ExamAuditService examAuditService;
-    
-    @Autowired
-    private  UserCloudService userCloudService;
-    
-    @Autowired
-    private ResourceManager resourceManager;
-
-    @Override
-    public void exportExamScheduling(String jsonParams, User user) {
-        ExamStudentQuery req = new JsonMapper().parseJson(jsonParams, ExamStudentQuery.class);
-        req.setRootOrgId(user.getRootOrgId());
-        Check.isNull(req, "请求参数不能为空!");
-        Check.isNull(req.getExamId(), "请先选择考试批次!");
-
-        // 创建导出任务
-        ExportTaskInfo task = new ExportTaskInfo();
-        task.setRootOrgId(user.getRootOrgId());
-        task.setExamId(req.getExamId());
-        task.setType(ExportTaskType.EXAM_SCHEDULING);
-        task.setStatus(ExportTaskStatus.WAITING);
-        task.setCreator(user.getUserId());
-        task.setJsonParams(jsonParams);
-        exportTaskService.addExportTask(task);
-    }
-
-    @Override
-    public void asyncExportExamScheduling(Long taskId, ExamStudentQuery req) {
-        List<ExamStudentExcel> examRecords;
-        List<ExamStudentInfo> examStudentInfoList;
-        try {
-        	ExportTaskEntity task=exportTaskService.findById(taskId);
-        	UserDataRules uds=new UserDataRules(getUserDataRule(task.getCreator(),DataRuleType.ORG), getUserDataRule(task.getCreator(),DataRuleType.COURSE));
-            examStudentInfoList = examStudentService.getExamStudentInfoListForAsync(uds,req);
-            if (CollectionUtils.isEmpty(examStudentInfoList)) {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "当前条件暂无数据,任务终止");
-                return;
-            }
-            examRecords = ExamStudentEntityConvert.ofExcel(examStudentInfoList);
-            fillStage(examRecords);
-
-        } catch (Exception e) {
-            LOG.error(e.getMessage(), e);
-            if (e instanceof StatusException) {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR,
-                        ((StatusException) e).getDesc());
-            } else {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "导出数据异常");
-            }
-            return;
-        }
-        // 导出文件的存储路径
-        Long examId = examStudentInfoList.get(0).getExamId();
-        ExamSettingsCacheBean exam = CacheHelper.getExamSettings(examId);
-        final String filePath = String.format("/%s/%s/%s/%s/%s_考试进度详情.xlsx", TASK_EXPORT_DIR, req.getRootOrgId(),
-                dateDir(), randomUUID(), exam.getName());
-        String tempFilePath = systemConfig.getTempDataDir() + File.separator + filePath;
-        File tempFile = new File(tempFilePath);
-        try {
-            tempFile.getParentFile().mkdirs();
-            tempFile.createNewFile();
-        } catch (IOException e) {
-            LOG.error(e.getMessage(), e);
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "生成导出文件异常");
-            return;
-        }
-        try (FileOutputStream out = new FileOutputStream(tempFile);) {
-
-            ExcelExportUtil.exportExcel(ExamStudentExcel.class, examRecords, out);
-
-            // 上传至文件服务器
-            FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
-            env.setRootOrgId(String.valueOf(req.getRootOrgId()));
-            env.setRelativePath(filePath);
-            YunPathInfo oss = FileStorageUtil.saveFile(TASK_EXPORT_DIR, env, tempFile, null);
-
-            LOG.info("asyncExportExamScheduling finished... " + oss.getRelativePath());
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.SUCCESS, null, oss.getRelativePath());
-        } catch (Exception e) {
-            LOG.error("asyncExportExamScheduling error... " + e);
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "上传至文件服务器异常");
-        } finally {
-            FileUtils.deleteQuietly(tempFile);
-        }
-    }
-    
-    private void fillStage(List<ExamStudentExcel> list) {
-    	if(CollectionUtils.isEmpty(list)) {
-    		return;
-    	}
-    	for(ExamStudentExcel info:list) {
-    		if(info.getExamStageId()!=null) {
-    			ExamStageCacheBean stage=CacheHelper.getExamStage(info.getExamId(), info.getExamStageId());
-    			info.setExamStageOrder(stage.getStageOrder());
-    			info.setStageStartTime(stage.getStartTime());
-    			info.setStageEndTime(stage.getEndTime());
-    			info.setExamStage(stage.getStageOrder() + "("
+	private static final Logger LOG = LoggerFactory.getLogger(AsyncExportServiceImpl.class);
+
+	private static final String TASK_EXPORT_DIR = "task_export";
+
+	@Autowired
+	private ExportTaskService exportTaskService;
+
+	@Autowired
+	private ExamRecordService examRecordService;
+
+	@Autowired
+	private ExamScoreService examScoreService;
+
+	@Autowired
+	private SystemProperties systemConfig;
+
+	@Autowired
+	private ExamStudentService examStudentService;
+
+	@Autowired
+	private ExamAuditService examAuditService;
+
+	@Autowired
+	private UserCloudService userCloudService;
+
+	@Autowired
+	private ResourceManager resourceManager;
+
+	@Override
+	public void exportExamScheduling(String jsonParams, User user) {
+		ExamStudentQuery req = new JsonMapper().parseJson(jsonParams, ExamStudentQuery.class);
+		req.setRootOrgId(user.getRootOrgId());
+		Check.isNull(req, "请求参数不能为空!");
+		Check.isNull(req.getExamId(), "请先选择考试批次!");
+
+		// 创建导出任务
+		ExportTaskInfo task = new ExportTaskInfo();
+		task.setRootOrgId(user.getRootOrgId());
+		task.setExamId(req.getExamId());
+		task.setType(ExportTaskType.EXAM_SCHEDULING);
+		task.setStatus(ExportTaskStatus.WAITING);
+		task.setCreator(user.getUserId());
+		task.setJsonParams(jsonParams);
+		exportTaskService.addExportTask(task);
+	}
+
+	@Override
+	public void asyncExportExamScheduling(Long taskId, ExamStudentQuery req) {
+		List<ExamStudentExcel> examRecords;
+		List<ExamStudentInfo> examStudentInfoList;
+		try {
+			ExportTaskEntity task = exportTaskService.findById(taskId);
+			UserDataRules uds = new UserDataRules(getUserDataRule(task.getCreator(), DataRuleType.ORG),
+					getUserDataRule(task.getCreator(), DataRuleType.COURSE));
+			req.setTaskId(taskId);
+			examStudentInfoList = examStudentService.getExamStudentInfoListForAsync(uds, req);
+			if (CollectionUtils.isEmpty(examStudentInfoList)) {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "当前条件暂无数据,任务终止");
+				return;
+			}
+			examRecords = ExamStudentEntityConvert.ofExcel(examStudentInfoList);
+			fillStage(examRecords);
+
+		} catch (ExportTaskStopException e) {
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.TERMINATE);
+			return;
+		} catch (Exception e) {
+			LOG.error(e.getMessage(), e);
+			if (e instanceof StatusException) {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR,
+						((StatusException) e).getDesc());
+			} else {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "导出数据异常");
+			}
+			return;
+		}
+		// 导出文件的存储路径
+		Long examId = examStudentInfoList.get(0).getExamId();
+		ExamSettingsCacheBean exam = CacheHelper.getExamSettings(examId);
+		final String filePath = String.format("/%s/%s/%s/%s/%s_考试进度详情.xlsx", TASK_EXPORT_DIR, req.getRootOrgId(),
+				dateDir(), randomUUID(), exam.getName());
+		String tempFilePath = systemConfig.getTempDataDir() + File.separator + filePath;
+		File tempFile = new File(tempFilePath);
+		try {
+			tempFile.getParentFile().mkdirs();
+			tempFile.createNewFile();
+		} catch (IOException e) {
+			LOG.error(e.getMessage(), e);
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "生成导出文件异常");
+			return;
+		}
+		try (FileOutputStream out = new FileOutputStream(tempFile);) {
+
+			ExcelExportUtil.exportExcel(ExamStudentExcel.class, examRecords, out);
+
+			// 上传至文件服务器
+			FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
+			env.setRootOrgId(String.valueOf(req.getRootOrgId()));
+			env.setRelativePath(filePath);
+			YunPathInfo oss = FileStorageUtil.saveFile(TASK_EXPORT_DIR, env, tempFile, null);
+
+			LOG.info("asyncExportExamScheduling finished... " + oss.getRelativePath());
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.SUCCESS, null, oss.getRelativePath());
+		} catch (Exception e) {
+			LOG.error("asyncExportExamScheduling error... " + e);
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "上传至文件服务器异常");
+		} finally {
+			FileUtils.deleteQuietly(tempFile);
+		}
+	}
+
+	private void fillStage(List<ExamStudentExcel> list) {
+		if (CollectionUtils.isEmpty(list)) {
+			return;
+		}
+		for (ExamStudentExcel info : list) {
+			if (info.getExamStageId() != null) {
+				ExamStageCacheBean stage = CacheHelper.getExamStage(info.getExamId(), info.getExamStageId());
+				info.setExamStageOrder(stage.getStageOrder());
+				info.setStageStartTime(stage.getStartTime());
+				info.setStageEndTime(stage.getEndTime());
+				info.setExamStage(stage.getStageOrder() + "("
 						+ DateUtil.format(stage.getStartTime(), DateUtil.DatePatterns.CHINA_DEFAULT) + "至"
 						+ DateUtil.format(stage.getEndTime(), DateUtil.DatePatterns.CHINA_DEFAULT) + ")");
-    		}
-    	}
-    }
-
-    @Override
-    public void exportExamRecordDetails(String jsonParams, User user) {
-        ExamRecordQuery req = new JsonMapper().parseJson(jsonParams, ExamRecordQuery.class);
-        req.setRootOrgId(user.getRootOrgId());
-        Check.isNull(req, "请求参数不能为空!");
-        Check.isNull(req.getRootOrgId(), "学校ID不能为空!");
-        Check.isNull(req.getExamId(), "考试ID不能为空!");
-        Check.isNull(req.getCreator(), "创建人ID不能为空!");
-
-
-        // 创建导出任务
-        ExportTaskInfo task = new ExportTaskInfo();
-        task.setRootOrgId(req.getRootOrgId());
-        task.setExamId(req.getExamId());
-        task.setType(ExportTaskType.EXAM_DETAIL);
-        task.setStatus(ExportTaskStatus.WAITING);
-        task.setCreator(req.getCreator());
-        task.setJsonParams(jsonParams);
-        exportTaskService.addExportTask(task);
-    }
-
-    @Override
-    public void asyncExportExamRecordDetails(Long taskId, ExamRecordQuery req) {
-        List<ExamRecordInfo> examRecords;
-        try {
-        	ExportTaskEntity task=exportTaskService.findById(taskId);
-        	UserDataRules uds=new UserDataRules(getUserDataRule(task.getCreator(),DataRuleType.ORG), getUserDataRule(task.getCreator(),DataRuleType.COURSE));
-            
-            examRecords = examRecordService.getExamRecordDetailListForAsync(uds,req);
-
-            if (CollectionUtils.isEmpty(examRecords)) {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "当前条件暂无数据,任务终止");
-                return;
-            }
-
-        } catch (Exception e) {
-            LOG.error(e.getMessage(), e);
-            if (e instanceof StatusException) {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR,
-                        ((StatusException) e).getDesc());
-            } else {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "导出数据异常");
-            }
-            return;
-        }
-        // 导出文件的存储路径
-        String examName = examRecords.get(0).getExamName();
-        final String filePath = String.format("/%s/%s/%s/%s/%s_考试明细.xlsx", TASK_EXPORT_DIR, req.getRootOrgId(),
-                dateDir(), randomUUID(), examName);
-        String tempFilePath = systemConfig.getTempDataDir() + File.separator + filePath;
-        File tempFile = new File(tempFilePath);
-        try {
-            tempFile.getParentFile().mkdirs();
-            tempFile.createNewFile();
-        } catch (IOException e) {
-            LOG.error(e.getMessage(), e);
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "生成导出文件异常");
-            return;
-        }
-        try (FileOutputStream out = new FileOutputStream(tempFile);) {
-
-            ExcelExportUtil.exportExcel(ExamRecordInfo.class, examRecords, out);
-
-            // 上传至文件服务器
-            FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
-            env.setRootOrgId(String.valueOf(req.getRootOrgId()));
-            env.setRelativePath(filePath);
-            YunPathInfo oss = FileStorageUtil.saveFile(TASK_EXPORT_DIR, env, tempFile, null);
-
-            LOG.info("asyncExportExamRecordDetails finished... " + oss.getRelativePath());
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.SUCCESS, null, oss.getRelativePath());
-        } catch (Exception e) {
-            LOG.info("asyncExportExamRecordDetails error... " + e.getMessage());
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "上传至文件服务器异常");
-        } finally {
-            FileUtils.deleteQuietly(tempFile);
-        }
-    }
-
-    @Override
-    public void exportExamScoreStatistics(String jsonParams, User user) {
-        ExamScoreQuery req = new JsonMapper().parseJson(jsonParams, ExamScoreQuery.class);
-        req.setRootOrgId(user.getRootOrgId());
-        Check.isNull(req, "请求参数不能为空!");
-        Check.isNull(req.getRootOrgId(), "学校ID不能为空!");
-        Check.isNull(req.getExamId(), "考试ID不能为空!");
-        Check.isNull(req.getCreator(), "创建人ID不能为空!");
-
-        // 创建导出任务
-        ExportTaskInfo task = new ExportTaskInfo();
-        task.setRootOrgId(req.getRootOrgId());
-        task.setExamId(req.getExamId());
-        task.setType(ExportTaskType.SCORE_STATISTIC);
-        task.setStatus(ExportTaskStatus.WAITING);
-        task.setCreator(req.getCreator());
-        task.setJsonParams(jsonParams);
-        exportTaskService.addExportTask(task);
-    }
-
-    @Override
-    public void asyncExportExamScoreStatistics(Long taskId, ExamScoreQuery req) {
-        List<ExamScoreInfo> examScores;
-        try {
-        	ExportTaskEntity task=exportTaskService.findById(taskId);
-        	UserDataRules uds=new UserDataRules(getUserDataRule(task.getCreator(),DataRuleType.ORG), getUserDataRule(task.getCreator(),DataRuleType.COURSE));
-            
-            examScores = examScoreService.exportExamScoreListForAsync(uds,req);
-            if (CollectionUtils.isEmpty(examScores)) {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "当前条件暂无数据,任务终止");
-                return;
-            }
-        } catch (Exception e) {
-            LOG.error(e.getMessage(), e);
-            if (e instanceof StatusException) {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR,
-                        ((StatusException) e).getDesc());
-            } else {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "导出数据异常");
-            }
-            return;
-        }
-        // 导出文件的存储路径
-        String examName = examScores.get(0).getExamName();
-        final String filePath = String.format("/%s/%s/%s/%s/%s_成绩统计.xlsx", TASK_EXPORT_DIR, req.getRootOrgId(),
-                dateDir(), randomUUID(), examName);
-        String tempFilePath = systemConfig.getTempDataDir() + File.separator + filePath;
-        File tempFile = new File(tempFilePath);
-        try {
-            tempFile.getParentFile().mkdirs();
-            tempFile.createNewFile();
-        } catch (IOException e) {
-            LOG.error(e.getMessage(), e);
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "生成导出文件异常");
-            return;
-        }
-        try (FileOutputStream out = new FileOutputStream(tempFile);) {
-            ExcelExportUtil.exportExcel(ExamScoreInfo.class, examScores, out);
-
-            // 上传至文件服务器
-            FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
-            env.setRootOrgId(String.valueOf(req.getRootOrgId()));
-            env.setRelativePath(filePath);
-            YunPathInfo oss = FileStorageUtil.saveFile(TASK_EXPORT_DIR, env, tempFile, null);
-
-            LOG.info("asyncExportExamScoreStatistics finished... " + oss.getRelativePath());
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.SUCCESS, null, oss.getRelativePath());
-        } catch (Exception e) {
-            LOG.info("asyncExportExamScoreStatistics error... " + e.getMessage());
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "上传至文件服务器异常");
-        } finally {
-            FileUtils.deleteQuietly(tempFile);
-        }
-    }
-
-    @Override
-    public void asyncExportExamAuditList(Long taskId, ExamAuditQuery req) {
-        List<ExamAuditInfo> auditInfos;
-        try {
-        	ExportTaskEntity task=exportTaskService.findById(taskId);
-        	UserDataRules uds=new UserDataRules(getUserDataRule(task.getCreator(),DataRuleType.ORG), getUserDataRule(task.getCreator(),DataRuleType.COURSE));
-            
-            auditInfos = examAuditService.getExamAudit(uds,req);
-
-            if (CollectionUtils.isEmpty(auditInfos)) {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "当前条件暂无数据,任务终止");
-                return;
-            }
-            for (ExamAuditInfo examAuditInfo : auditInfos) {
-                examAuditInfo.setIdentityNumber(
-                        IdentityNumberHelper.conceal(req.getRootOrgId(), examAuditInfo.getIdentityNumber()));
-            }
-
-        } catch (Exception e) {
-            LOG.error(e.getMessage(), e);
-            if (e instanceof StatusException) {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR,
-                        ((StatusException) e).getDesc());
-            } else {
-                exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "导出数据异常");
-            }
-            return;
-        }
-        // 导出文件的存储路径
-        String examName = auditInfos.get(0).getExamName();
-        final String filePath = String.format("/%s/%s/%s/%s/%s_监考已审.xlsx", TASK_EXPORT_DIR, req.getRootOrgId(),
-                dateDir(), randomUUID(), examName);
-        String tempFilePath = systemConfig.getTempDataDir() + File.separator + filePath;
-        File tempFile = new File(tempFilePath);
-        try {
-            tempFile.getParentFile().mkdirs();
-            tempFile.createNewFile();
-        } catch (IOException e) {
-            LOG.error(e.getMessage(), e);
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "生成导出文件异常");
-            return;
-        }
-        try (FileOutputStream out = new FileOutputStream(tempFile);) {
-
-            ExcelExportUtil.exportExcel(ExamAuditInfo.class, auditInfos, out);
-
-            // 上传至文件服务器
-            FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
-            env.setRootOrgId(String.valueOf(req.getRootOrgId()));
-            env.setRelativePath(filePath);
-            YunPathInfo oss = FileStorageUtil.saveFile(TASK_EXPORT_DIR, env, tempFile, null);
-
-            LOG.info("asyncExportExamAuditList finished... " + oss.getRelativePath());
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.SUCCESS, null, oss.getRelativePath());
-        } catch (Exception e) {
-            LOG.info("asyncExportExamAuditList error... " + e.getMessage());
-            exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "上传至文件服务器异常");
-        } finally {
-            FileUtils.deleteQuietly(tempFile);
-        }
-    }
-
-    @Override
-    public void exportExamAuditList(String query, User user) {
-        ExamStudentQuery req = new JsonMapper().parseJson(query, ExamStudentQuery.class);
-        req.setRootOrgId(user.getRootOrgId());
-        Check.isNull(req, "请求参数不能为空!");
-        Check.isNull(req.getExamId(), "请先选择考试批次!");
-
-        // 创建导出任务
-        ExportTaskInfo task = new ExportTaskInfo();
-        task.setRootOrgId(user.getRootOrgId());
-        task.setExamId(req.getExamId());
-        task.setType(ExportTaskType.AUDIT);
-        task.setStatus(ExportTaskStatus.WAITING);
-        task.setCreator(user.getUserId());
-        task.setJsonParams(query);
-        exportTaskService.addExportTask(task);
-    }
-
-    private String randomUUID() {
-        return UUID.randomUUID().toString().replace("-", "");
-    }
-
-    private String dateDir() {
-        final String pattern = "yyyyMM";
-        return new SimpleDateFormat(pattern).format(new Date());
-    }
-    
-    private UserDataRule getUserDataRule(Long userId,DataRuleType dataRuleType) {
-    	UserDataRule userDataRule= new UserDataRule();
-        userDataRule.setGlobalStatus(true);
-        userDataRule.setRefIds(new HashSet<>());
-    	boolean isEnable = PropertyHolder.getBoolean("examcloud.data.rule.enable", false);
-        if (!isEnable) {
-            // 未启用数据权限
-            return userDataRule;
-        }
-
-        GetUserReq getUserReq = new GetUserReq();
-        getUserReq.setUserId(userId);
-        GetUserResp getUserResp = userCloudService.getUser(getUserReq);
-        UserBean userBean = getUserResp.getUserBean();
-        if (userBean == null) {
-            throw new StatusException("未找到用户");
-        }
-
-        boolean isAdmimUser = RoleUtil.hasAnyRoles(userBean, RoleMeta.SUPER_ADMIN, RoleMeta.ORG_ADMIN);
-
-        if (isAdmimUser) {
-            // “超管、机构管理员、学生” 暂例外
-        	return userDataRule;
-        } else {
-            userDataRule = resourceManager.loadUserDataRule(userId, dataRuleType);
-        }
-        return userDataRule;
-    }
+			}
+		}
+	}
+
+	@Override
+	public void exportExamRecordDetails(String jsonParams, User user) {
+		ExamRecordQuery req = new JsonMapper().parseJson(jsonParams, ExamRecordQuery.class);
+		req.setRootOrgId(user.getRootOrgId());
+		Check.isNull(req, "请求参数不能为空!");
+		Check.isNull(req.getRootOrgId(), "学校ID不能为空!");
+		Check.isNull(req.getExamId(), "考试ID不能为空!");
+		Check.isNull(req.getCreator(), "创建人ID不能为空!");
+
+		// 创建导出任务
+		ExportTaskInfo task = new ExportTaskInfo();
+		task.setRootOrgId(req.getRootOrgId());
+		task.setExamId(req.getExamId());
+		task.setType(ExportTaskType.EXAM_DETAIL);
+		task.setStatus(ExportTaskStatus.WAITING);
+		task.setCreator(req.getCreator());
+		task.setJsonParams(jsonParams);
+		exportTaskService.addExportTask(task);
+	}
+
+	@Override
+	public void asyncExportExamRecordDetails(Long taskId, ExamRecordQuery req) {
+		List<ExamRecordInfo> examRecords;
+		try {
+			ExportTaskEntity task = exportTaskService.findById(taskId);
+			UserDataRules uds = new UserDataRules(getUserDataRule(task.getCreator(), DataRuleType.ORG),
+					getUserDataRule(task.getCreator(), DataRuleType.COURSE));
+			req.setTaskId(taskId);
+			examRecords = examRecordService.getExamRecordDetailListForAsync(uds, req);
+
+			if (CollectionUtils.isEmpty(examRecords)) {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "当前条件暂无数据,任务终止");
+				return;
+			}
+
+		} catch (ExportTaskStopException e) {
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.TERMINATE);
+			return;
+		}  catch (Exception e) {
+			LOG.error(e.getMessage(), e);
+			if (e instanceof StatusException) {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR,
+						((StatusException) e).getDesc());
+			} else {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "导出数据异常");
+			}
+			return;
+		}
+		// 导出文件的存储路径
+		String examName = examRecords.get(0).getExamName();
+		final String filePath = String.format("/%s/%s/%s/%s/%s_考试明细.xlsx", TASK_EXPORT_DIR, req.getRootOrgId(),
+				dateDir(), randomUUID(), examName);
+		String tempFilePath = systemConfig.getTempDataDir() + File.separator + filePath;
+		File tempFile = new File(tempFilePath);
+		try {
+			tempFile.getParentFile().mkdirs();
+			tempFile.createNewFile();
+		} catch (IOException e) {
+			LOG.error(e.getMessage(), e);
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "生成导出文件异常");
+			return;
+		}
+		try (FileOutputStream out = new FileOutputStream(tempFile);) {
+
+			ExcelExportUtil.exportExcel(ExamRecordInfo.class, examRecords, out);
+
+			// 上传至文件服务器
+			FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
+			env.setRootOrgId(String.valueOf(req.getRootOrgId()));
+			env.setRelativePath(filePath);
+			YunPathInfo oss = FileStorageUtil.saveFile(TASK_EXPORT_DIR, env, tempFile, null);
+
+			LOG.info("asyncExportExamRecordDetails finished... " + oss.getRelativePath());
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.SUCCESS, null, oss.getRelativePath());
+		} catch (Exception e) {
+			LOG.info("asyncExportExamRecordDetails error... " + e.getMessage());
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "上传至文件服务器异常");
+		} finally {
+			FileUtils.deleteQuietly(tempFile);
+		}
+	}
+
+	@Override
+	public void exportExamScoreStatistics(String jsonParams, User user) {
+		ExamScoreQuery req = new JsonMapper().parseJson(jsonParams, ExamScoreQuery.class);
+		req.setRootOrgId(user.getRootOrgId());
+		Check.isNull(req, "请求参数不能为空!");
+		Check.isNull(req.getRootOrgId(), "学校ID不能为空!");
+		Check.isNull(req.getExamId(), "考试ID不能为空!");
+		Check.isNull(req.getCreator(), "创建人ID不能为空!");
+
+		// 创建导出任务
+		ExportTaskInfo task = new ExportTaskInfo();
+		task.setRootOrgId(req.getRootOrgId());
+		task.setExamId(req.getExamId());
+		task.setType(ExportTaskType.SCORE_STATISTIC);
+		task.setStatus(ExportTaskStatus.WAITING);
+		task.setCreator(req.getCreator());
+		task.setJsonParams(jsonParams);
+		exportTaskService.addExportTask(task);
+	}
+
+	@Override
+	public void asyncExportExamScoreStatistics(Long taskId, ExamScoreQuery req) {
+		List<ExamScoreInfo> examScores;
+		try {
+			ExportTaskEntity task = exportTaskService.findById(taskId);
+			UserDataRules uds = new UserDataRules(getUserDataRule(task.getCreator(), DataRuleType.ORG),
+					getUserDataRule(task.getCreator(), DataRuleType.COURSE));
+			req.setTaskId(taskId);
+			examScores = examScoreService.exportExamScoreListForAsync(uds, req);
+			if (CollectionUtils.isEmpty(examScores)) {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "当前条件暂无数据,任务终止");
+				return;
+			}
+		} catch (ExportTaskStopException e) {
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.TERMINATE);
+			return;
+		}  catch (Exception e) {
+			LOG.error(e.getMessage(), e);
+			if (e instanceof StatusException) {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR,
+						((StatusException) e).getDesc());
+			} else {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "导出数据异常");
+			}
+			return;
+		}
+		// 导出文件的存储路径
+		String examName = examScores.get(0).getExamName();
+		final String filePath = String.format("/%s/%s/%s/%s/%s_成绩统计.xlsx", TASK_EXPORT_DIR, req.getRootOrgId(),
+				dateDir(), randomUUID(), examName);
+		String tempFilePath = systemConfig.getTempDataDir() + File.separator + filePath;
+		File tempFile = new File(tempFilePath);
+		try {
+			tempFile.getParentFile().mkdirs();
+			tempFile.createNewFile();
+		} catch (IOException e) {
+			LOG.error(e.getMessage(), e);
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "生成导出文件异常");
+			return;
+		}
+		try (FileOutputStream out = new FileOutputStream(tempFile);) {
+			ExcelExportUtil.exportExcel(ExamScoreInfo.class, examScores, out);
+
+			// 上传至文件服务器
+			FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
+			env.setRootOrgId(String.valueOf(req.getRootOrgId()));
+			env.setRelativePath(filePath);
+			YunPathInfo oss = FileStorageUtil.saveFile(TASK_EXPORT_DIR, env, tempFile, null);
+
+			LOG.info("asyncExportExamScoreStatistics finished... " + oss.getRelativePath());
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.SUCCESS, null, oss.getRelativePath());
+		} catch (Exception e) {
+			LOG.info("asyncExportExamScoreStatistics error... " + e.getMessage());
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "上传至文件服务器异常");
+		} finally {
+			FileUtils.deleteQuietly(tempFile);
+		}
+	}
+
+	@Override
+	public void asyncExportExamAuditList(Long taskId, ExamAuditQuery req) {
+		List<ExamAuditInfo> auditInfos;
+		try {
+			ExportTaskEntity task = exportTaskService.findById(taskId);
+			UserDataRules uds = new UserDataRules(getUserDataRule(task.getCreator(), DataRuleType.ORG),
+					getUserDataRule(task.getCreator(), DataRuleType.COURSE));
+			req.setTaskId(taskId);
+			auditInfos = examAuditService.getExamAudit(uds, req);
+
+			if (CollectionUtils.isEmpty(auditInfos)) {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "当前条件暂无数据,任务终止");
+				return;
+			}
+			for (ExamAuditInfo examAuditInfo : auditInfos) {
+				examAuditInfo.setIdentityNumber(
+						IdentityNumberHelper.conceal(req.getRootOrgId(), examAuditInfo.getIdentityNumber()));
+			}
+
+		} catch (ExportTaskStopException e) {
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.TERMINATE);
+			return;
+		} catch (Exception e) {
+			LOG.error(e.getMessage(), e);
+			if (e instanceof StatusException) {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR,
+						((StatusException) e).getDesc());
+			} else {
+				exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "导出数据异常");
+			}
+			return;
+		}
+		// 导出文件的存储路径
+		String examName = auditInfos.get(0).getExamName();
+		final String filePath = String.format("/%s/%s/%s/%s/%s_监考已审.xlsx", TASK_EXPORT_DIR, req.getRootOrgId(),
+				dateDir(), randomUUID(), examName);
+		String tempFilePath = systemConfig.getTempDataDir() + File.separator + filePath;
+		File tempFile = new File(tempFilePath);
+		try {
+			tempFile.getParentFile().mkdirs();
+			tempFile.createNewFile();
+		} catch (IOException e) {
+			LOG.error(e.getMessage(), e);
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "生成导出文件异常");
+			return;
+		}
+		try (FileOutputStream out = new FileOutputStream(tempFile);) {
+
+			ExcelExportUtil.exportExcel(ExamAuditInfo.class, auditInfos, out);
+
+			// 上传至文件服务器
+			FileStoragePathEnvInfo env = new FileStoragePathEnvInfo();
+			env.setRootOrgId(String.valueOf(req.getRootOrgId()));
+			env.setRelativePath(filePath);
+			YunPathInfo oss = FileStorageUtil.saveFile(TASK_EXPORT_DIR, env, tempFile, null);
+
+			LOG.info("asyncExportExamAuditList finished... " + oss.getRelativePath());
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.SUCCESS, null, oss.getRelativePath());
+		} catch (Exception e) {
+			LOG.info("asyncExportExamAuditList error... " + e.getMessage());
+			exportTaskService.updateExportTaskStatus(taskId, ExportTaskStatus.ERROR, "上传至文件服务器异常");
+		} finally {
+			FileUtils.deleteQuietly(tempFile);
+		}
+	}
+
+	@Override
+	public void exportExamAuditList(String query, User user) {
+		ExamStudentQuery req = new JsonMapper().parseJson(query, ExamStudentQuery.class);
+		req.setRootOrgId(user.getRootOrgId());
+		Check.isNull(req, "请求参数不能为空!");
+		Check.isNull(req.getExamId(), "请先选择考试批次!");
+
+		// 创建导出任务
+		ExportTaskInfo task = new ExportTaskInfo();
+		task.setRootOrgId(user.getRootOrgId());
+		task.setExamId(req.getExamId());
+		task.setType(ExportTaskType.AUDIT);
+		task.setStatus(ExportTaskStatus.WAITING);
+		task.setCreator(user.getUserId());
+		task.setJsonParams(query);
+		exportTaskService.addExportTask(task);
+	}
+
+	private String randomUUID() {
+		return UUID.randomUUID().toString().replace("-", "");
+	}
+
+	private String dateDir() {
+		final String pattern = "yyyyMM";
+		return new SimpleDateFormat(pattern).format(new Date());
+	}
+
+	private UserDataRule getUserDataRule(Long userId, DataRuleType dataRuleType) {
+		UserDataRule userDataRule = new UserDataRule();
+		userDataRule.setGlobalStatus(true);
+		userDataRule.setRefIds(new HashSet<>());
+		boolean isEnable = PropertyHolder.getBoolean("examcloud.data.rule.enable", false);
+		if (!isEnable) {
+			// 未启用数据权限
+			return userDataRule;
+		}
+
+		GetUserReq getUserReq = new GetUserReq();
+		getUserReq.setUserId(userId);
+		GetUserResp getUserResp = userCloudService.getUser(getUserReq);
+		UserBean userBean = getUserResp.getUserBean();
+		if (userBean == null) {
+			throw new StatusException("未找到用户");
+		}
+
+		boolean isAdmimUser = RoleUtil.hasAnyRoles(userBean, RoleMeta.SUPER_ADMIN, RoleMeta.ORG_ADMIN);
+
+		if (isAdmimUser) {
+			// “超管、机构管理员、学生” 暂例外
+			return userDataRule;
+		} else {
+			userDataRule = resourceManager.loadUserDataRule(userId, dataRuleType);
+		}
+		return userDataRule;
+	}
 
 }

+ 4 - 1
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamAuditServiceImpl.java

@@ -71,6 +71,7 @@ import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordQuestionsService;
 import cn.com.qmth.examcloud.core.oe.admin.service.ExamScoreObtainQueueService;
 import cn.com.qmth.examcloud.core.oe.admin.service.ExamScorePushQueueService;
 import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentFinalScoreService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExportTaskService;
 import cn.com.qmth.examcloud.core.oe.admin.service.GainBaseDataService;
 import cn.com.qmth.examcloud.core.oe.admin.service.IllegallyTypeService;
 import cn.com.qmth.examcloud.core.oe.admin.service.bean.UserDataRules;
@@ -99,7 +100,8 @@ import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
  */
 @Service
 public class ExamAuditServiceImpl implements ExamAuditService {
-
+    @Autowired
+    private ExportTaskService exportTaskService;
     @Autowired
     private ExamAuditRepo examAuditRepo;
 
@@ -263,6 +265,7 @@ public class ExamAuditServiceImpl implements ExamAuditService {
         int pageNo = 1;
         int pageSize = 5000;
         for (; ; ) {
+        	exportTaskService.checkStopExportTaskById(query.getTaskId());
             List tem = getExamAuditForAsyncPage(uds,query,pageNo, pageSize);
             if (tem == null || tem.size() == 0) {
                 break;

+ 4 - 1
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamRecordServiceImpl.java

@@ -24,6 +24,7 @@ import cn.com.qmth.examcloud.core.oe.admin.dao.entity.*;
 import cn.com.qmth.examcloud.core.oe.admin.dao.enums.*;
 import cn.com.qmth.examcloud.core.oe.admin.service.ExamRecordService;
 import cn.com.qmth.examcloud.core.oe.admin.service.ExamStudentService;
+import cn.com.qmth.examcloud.core.oe.admin.service.ExportTaskService;
 import cn.com.qmth.examcloud.core.oe.admin.service.GainBaseDataService;
 import cn.com.qmth.examcloud.core.oe.admin.service.bean.ExamStudentEffectiveScoreInfo;
 import cn.com.qmth.examcloud.core.oe.admin.service.bean.UserDataRules;
@@ -66,7 +67,8 @@ import java.util.stream.Collectors;
  */
 @Service
 public class ExamRecordServiceImpl implements ExamRecordService {
-
+    @Autowired
+    private ExportTaskService exportTaskService;
     @Autowired
     private ExamScoreRepo examScoreRepo;
 
@@ -760,6 +762,7 @@ public class ExamRecordServiceImpl implements ExamRecordService {
         int pageNo = 1;
         int pageSize = 5000;
         for (; ; ) {
+        	exportTaskService.checkStopExportTaskById(query.getTaskId());
             List<ExamRecordInfo> tem = getExamRecordDetailPageForExport(uds, query, pageNo, pageSize);
             if (tem == null || tem.size() == 0) {
                 break;

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

@@ -66,7 +66,8 @@ import java.util.stream.Collectors;
 public class ExamScoreServiceImpl implements ExamScoreService {
 
 	private static final Logger log = LoggerFactory.getLogger(ExamScoreServiceImpl.class);
-
+    @Autowired
+    private ExportTaskService exportTaskService;
 	@Autowired
 	private ExamScoreRepo examScoreRepo;
 
@@ -204,6 +205,7 @@ public class ExamScoreServiceImpl implements ExamScoreService {
 
 			@Override
 			protected List<ExamStudentEntity> getData(List<Long> paramList) {
+				exportTaskService.checkStopExportTaskById(query.getTaskId());
 				return getExamStudentInfoListOfScoreExportByPage(uds, query, markingType,paramList);
 			}
 		}.getDataForBatch(ids, 1000);

+ 3 - 1
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExamStudentServiceImpl.java

@@ -92,7 +92,8 @@ import static cn.com.qmth.examcloud.core.oe.admin.service.bean.examstudent.ExamS
 public class ExamStudentServiceImpl implements ExamStudentService {
 
 	private static final Logger log = LoggerFactory.getLogger(ExamStudentServiceImpl.class);
-
+    @Autowired
+    private ExportTaskService exportTaskService;
 	@Autowired
 	private ExamStudentRepo examStudentRepo;
 
@@ -328,6 +329,7 @@ public class ExamStudentServiceImpl implements ExamStudentService {
 
 			@Override
 			protected List<ExamStudentEntity> getData(List<Long> paramList) {
+				exportTaskService.checkStopExportTaskById(query.getTaskId());
 				return getExamStudentInfoListByPage(uds, query, examBean, paramList);
 			}
 		}.getDataForBatch(ids, 1000);

+ 55 - 12
examcloud-core-oe-admin-service/src/main/java/cn/com/qmth/examcloud/core/oe/admin/service/impl/ExportTaskServiceImpl.java

@@ -5,10 +5,34 @@
 
 package cn.com.qmth.examcloud.core.oe.admin.service.impl;
 
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.persistence.criteria.Predicate;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import cn.com.qmth.examcloud.commons.exception.StatusException;
 import cn.com.qmth.examcloud.core.basic.api.UserCloudService;
 import cn.com.qmth.examcloud.core.basic.api.request.GetUserReq;
 import cn.com.qmth.examcloud.core.basic.api.response.GetUserResp;
+import cn.com.qmth.examcloud.core.oe.admin.base.ExportTaskStopException;
 import cn.com.qmth.examcloud.core.oe.admin.base.utils.Check;
 import cn.com.qmth.examcloud.core.oe.admin.dao.ExportTaskRepo;
 import cn.com.qmth.examcloud.core.oe.admin.dao.entity.ExportTaskEntity;
@@ -22,26 +46,19 @@ import cn.com.qmth.examcloud.examwork.api.ExamCloudService;
 import cn.com.qmth.examcloud.examwork.api.bean.ExamBean;
 import cn.com.qmth.examcloud.examwork.api.request.GetExamMapsReq;
 import cn.com.qmth.examcloud.examwork.api.response.GetExamMapsResp;
+import cn.com.qmth.examcloud.support.CacheConstants;
 import cn.com.qmth.examcloud.support.filestorage.FileStorageUtil;
 import cn.com.qmth.examcloud.web.helpers.GlobalHelper;
-
-import org.apache.commons.collections.CollectionUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.domain.*;
-import org.springframework.data.jpa.domain.Specification;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import javax.persistence.criteria.Predicate;
-import java.util.*;
-import java.util.stream.Collectors;
+import cn.com.qmth.examcloud.web.redis.RedisClient;
 
 /**
  * 导出任务相关接口
  */
 @Service
 public class ExportTaskServiceImpl implements ExportTaskService {
-
+	private static int cacheTimeOut = 60 * 60;
+	@Autowired
+	private RedisClient redisClient;
     @Autowired
     private ExportTaskRepo exportTaskRepo;
 
@@ -266,4 +283,30 @@ public class ExportTaskServiceImpl implements ExportTaskService {
 		return GlobalHelper.getEntity(exportTaskRepo, taskId, ExportTaskEntity.class);
 	}
 
+	@Transactional
+	@Override
+	public void stopExportTaskById(Long taskId) {
+		ExportTaskEntity e=findById(taskId);
+		if(e==null) {
+			throw new StatusException("未找到任务");
+		}
+		if(!ExportTaskStatus.EXPORTING.equals(e.getStatus())) {
+			throw new StatusException("只能终止导出中的任务");
+		}
+		e.setStatus(ExportTaskStatus.TERMINATING);
+		exportTaskRepo.save(e);
+		String key = CacheConstants.CACHE_OE_EXPORT_TASK_STOP + e.getId();
+		redisClient.set(key, e.getId(), cacheTimeOut);
+	}
+	
+	@Override
+	public void checkStopExportTaskById(Long taskId) {
+		String key = CacheConstants.CACHE_OE_EXPORT_TASK_STOP + taskId;
+		Long id=redisClient.get(key,Long.class);
+		if(id!=null) {
+			redisClient.delete(key);
+			throw new ExportTaskStopException();
+		}
+	}
+
 }