Browse Source

识别对照

ting.yin 1 year ago
parent
commit
00e87e716b
16 changed files with 791 additions and 133 deletions
  1. 16 4
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/entity/MarkStudent.java
  2. 28 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/enums/ScanStatus.java
  3. 2 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/ScanAnswerCardService.java
  4. 22 2
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/ScanAnswerCardServiceImpl.java
  5. 33 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/OmrTaskDto.java
  6. 51 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/OmrTaskPageDto.java
  7. 5 5
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/ScanOmrTaskDto.java
  8. 6 6
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/ScanOmrTask.java
  9. 16 2
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/ScanPaperPage.java
  10. 6 6
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/ScanStudentPaper.java
  11. 19 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/ScanOmrTaskService.java
  12. 6 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/ScanPaperService.java
  13. 2 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/ScanStudentPaperService.java
  14. 513 105
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanOmrTaskServiceImpl.java
  15. 58 3
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanPaperServiceImpl.java
  16. 8 0
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanStudentPaperServiceImpl.java

+ 16 - 4
teachcloud-common/src/main/java/com/qmth/teachcloud/common/entity/MarkStudent.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.teachcloud.common.enums.ScanStatus;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -119,7 +120,7 @@ public class MarkStudent implements Serializable {
     private String teacher;
 
     @ApiModelProperty(value = "扫描状态")
-    private String scanStatus;
+    private ScanStatus scanStatus;
 
     private Boolean questionFilled;
 
@@ -132,6 +133,8 @@ public class MarkStudent implements Serializable {
     private String breachCode;
 
     private Boolean omrAbsent;
+    
+    private Integer cardNumber;
 
     public MarkStudent() {
     }
@@ -360,11 +363,11 @@ public class MarkStudent implements Serializable {
     public void setTeacher(String teacher) {
         this.teacher = teacher;
     }
-    public String getScanStatus() {
+    public ScanStatus getScanStatus() {
         return scanStatus;
     }
 
-    public void setScanStatus(String scanStatus) {
+    public void setScanStatus(ScanStatus scanStatus) {
         this.scanStatus = scanStatus;
     }
     public Boolean getQuestionFilled() {
@@ -411,7 +414,15 @@ public class MarkStudent implements Serializable {
         this.omrAbsent = omrAbsent;
     }
 
-    @Override
+    public Integer getCardNumber() {
+		return cardNumber;
+	}
+
+	public void setCardNumber(Integer cardNumber) {
+		this.cardNumber = cardNumber;
+	}
+
+	@Override
     public String toString() {
         return "MarkStudent{" +
             "id=" + id +
@@ -451,6 +462,7 @@ public class MarkStudent implements Serializable {
             ", incomplete=" + incomplete +
             ", breachCode=" + breachCode +
             ", omrAbsent=" + omrAbsent +
+            ", cardNumber=" + cardNumber +
         "}";
     }
 }

+ 28 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/enums/ScanStatus.java

@@ -0,0 +1,28 @@
+package com.qmth.teachcloud.common.enums;
+
+public enum ScanStatus {
+
+    SCANNED("已扫描"),
+
+    UNEXIST("未扫描"), MANUAL_ABSENT("人工指定缺考");
+
+    private String name;
+
+    ScanStatus(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static ScanStatus getByName(String name) {
+        for (ScanStatus r : ScanStatus.values()) {
+            if (r.getName().equals(name)) {
+                return r;
+            }
+        }
+        return null;
+    }
+
+}

+ 2 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/ScanAnswerCardService.java

@@ -13,4 +13,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface ScanAnswerCardService extends IService<ScanAnswerCard> {
 
+	ScanAnswerCard findByExamAndNumber(Long examId, Integer cardNumber);
+
 }

+ 22 - 2
teachcloud-common/src/main/java/com/qmth/teachcloud/common/service/impl/ScanAnswerCardServiceImpl.java

@@ -1,10 +1,14 @@
 package com.qmth.teachcloud.common.service.impl;
 
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.boot.core.exception.ParameterException;
 import com.qmth.teachcloud.common.entity.ScanAnswerCard;
 import com.qmth.teachcloud.common.mapper.ScanAnswerCardMapper;
 import com.qmth.teachcloud.common.service.ScanAnswerCardService;
-import org.springframework.stereotype.Service;
 
 /**
  * <p>
@@ -15,6 +19,22 @@ import org.springframework.stereotype.Service;
  * @since 2023-09-22
  */
 @Service
-public class ScanAnswerCardServiceImpl extends ServiceImpl<ScanAnswerCardMapper, ScanAnswerCard> implements ScanAnswerCardService {
+public class ScanAnswerCardServiceImpl extends ServiceImpl<ScanAnswerCardMapper, ScanAnswerCard>
+		implements ScanAnswerCardService {
+
+	@Override
+	public ScanAnswerCard findByExamAndNumber(Long examId, Integer cardNumber) {
+		if (examId == null) {
+			throw new ParameterException("examId不能为空");
+		}
+		if (cardNumber == null) {
+			throw new ParameterException("cardNumber不能为空");
+		}
+		QueryWrapper<ScanAnswerCard> wrapper = new QueryWrapper<>();
+		LambdaQueryWrapper<ScanAnswerCard> lw = wrapper.lambda();
+		lw.eq(ScanAnswerCard::getExamId, examId);
+		lw.eq(ScanAnswerCard::getNumber, cardNumber);
+		return baseMapper.selectOne(wrapper);
+	}
 
 }

+ 33 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/OmrTaskDto.java

@@ -0,0 +1,33 @@
+package com.qmth.teachcloud.mark.dto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.qmth.teachcloud.mark.entity.ScanOmrTask;
+
+public class OmrTaskDto {
+	private ScanOmrTask task;
+	List<OmrTaskPageDto> page = new ArrayList<>();
+
+	public OmrTaskDto(ScanOmrTask task) {
+		super();
+		this.task = task;
+	}
+
+	public ScanOmrTask getTask() {
+		return task;
+	}
+
+	public void setTask(ScanOmrTask task) {
+		this.task = task;
+	}
+
+	public List<OmrTaskPageDto> getPage() {
+		return page;
+	}
+
+	public void setPage(List<OmrTaskPageDto> page) {
+		this.page = page;
+	}
+
+}

+ 51 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/OmrTaskPageDto.java

@@ -0,0 +1,51 @@
+package com.qmth.teachcloud.mark.dto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.qmth.teachcloud.mark.bean.OmrTaskItem;
+
+public class OmrTaskPageDto {
+	private Integer pageIndex;
+	List<OmrTaskItem> items = new ArrayList<>();
+	List<OmrTaskItem> pageMultiBlankItems = new ArrayList<>();
+	List<OmrTaskItem> pageSingleBlankItems = new ArrayList<>();
+
+	public OmrTaskPageDto(Integer pageIndex) {
+		super();
+		this.pageIndex = pageIndex;
+	}
+
+	public Integer getPageIndex() {
+		return pageIndex;
+	}
+
+	public void setPageIndex(Integer pageIndex) {
+		this.pageIndex = pageIndex;
+	}
+
+	public List<OmrTaskItem> getItems() {
+		return items;
+	}
+
+	public void setItems(List<OmrTaskItem> items) {
+		this.items = items;
+	}
+
+	public List<OmrTaskItem> getPageMultiBlankItems() {
+		return pageMultiBlankItems;
+	}
+
+	public void setPageMultiBlankItems(List<OmrTaskItem> pageMultiBlankItems) {
+		this.pageMultiBlankItems = pageMultiBlankItems;
+	}
+
+	public List<OmrTaskItem> getPageSingleBlankItems() {
+		return pageSingleBlankItems;
+	}
+
+	public void setPageSingleBlankItems(List<OmrTaskItem> pageSingleBlankItems) {
+		this.pageSingleBlankItems = pageSingleBlankItems;
+	}
+
+}

+ 5 - 5
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/dto/ScanOmrTaskDto.java

@@ -18,7 +18,7 @@ public class ScanOmrTaskDto {
 
     private Long paperId;
 
-    private Integer paperNumber;
+    private Integer paperIndex;
 
     private List<ScanOmrTaskPageDto> pages;
     
@@ -80,12 +80,12 @@ public class ScanOmrTaskDto {
         this.paperId = paperId;
     }
 
-    public Integer getPaperNumber() {
-        return paperNumber;
+    public Integer getPaperIndex() {
+        return paperIndex;
     }
 
-    public void setPaperNumber(Integer paperNumber) {
-        this.paperNumber = paperNumber;
+    public void setPaperIndex(Integer paperIndex) {
+        this.paperIndex = paperIndex;
     }
 
     public List<ScanOmrTaskPageDto> getPages() {

+ 6 - 6
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/ScanOmrTask.java

@@ -45,7 +45,7 @@ public class ScanOmrTask implements Serializable {
     private Long studentId;
 
     @ApiModelProperty(value = "试卷页数")
-    private Integer paperNumber;
+    private Integer paperIndex;
 
     @JsonSerialize(using = ToStringSerializer.class)
     @ApiModelProperty(value = "试卷ID")
@@ -101,12 +101,12 @@ public class ScanOmrTask implements Serializable {
     public void setStudentId(Long studentId) {
         this.studentId = studentId;
     }
-    public Integer getPaperNumber() {
-        return paperNumber;
+    public Integer getPaperIndex() {
+        return paperIndex;
     }
 
-    public void setPaperNumber(Integer paperNumber) {
-        this.paperNumber = paperNumber;
+    public void setPaperIndex(Integer paperIndex) {
+        this.paperIndex = paperIndex;
     }
     public Long getPaperId() {
         return paperId;
@@ -180,7 +180,7 @@ public class ScanOmrTask implements Serializable {
             ", examId=" + examId +
             ", conditions=" + conditions +
             ", studentId=" + studentId +
-            ", paperNumber=" + paperNumber +
+            ", paperIndex=" + paperIndex +
             ", paperId=" + paperId +
             ", cardNumber=" + cardNumber +
             ", status=" + status +

+ 16 - 2
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/ScanPaperPage.java

@@ -2,6 +2,8 @@ package com.qmth.teachcloud.mark.entity;
 
 import java.io.Serializable;
 
+import org.apache.commons.collections4.CollectionUtils;
+
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -30,7 +32,6 @@ public class ScanPaperPage implements Serializable {
 	@JsonSerialize(using = ToStringSerializer.class)
 	@TableId(value = "paper_id")
 	private Long paperId;
-
 	private Integer pageIndex;
 
 	@TableField(value = "absent", typeHandler = JacksonTypeHandler.class)
@@ -154,7 +155,20 @@ public class ScanPaperPage implements Serializable {
 	public void setUpdateTime(Long updateTime) {
 		this.updateTime = updateTime;
 	}
-
+	 public boolean isQuestionFilled() {
+	        if (question != null) {
+	            if (CollectionUtils.isEmpty(question.getResult())) {
+	                return false;
+	            }
+	            for (String s : question.getResult()) {
+	                if (!"#".equals(s)) {
+	                    return true;
+	                }
+	            }
+	            return false;
+	        }
+	        return false;
+	    }
 	@Override
 	public String toString() {
 		return "ScanPaperPage{" + "paperId=" + paperId + ", pageIndex=" + pageIndex + ", absent=" + absent + ", breach="

+ 6 - 6
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/entity/ScanStudentPaper.java

@@ -27,7 +27,7 @@ public class ScanStudentPaper implements Serializable {
     @TableId(value = "student_id")
     private Long studentId;
 
-    private Integer paperNumber;
+    private Integer paperIndex;
 
     @JsonSerialize(using = ToStringSerializer.class)
     private Long paperId;
@@ -43,12 +43,12 @@ public class ScanStudentPaper implements Serializable {
     public void setStudentId(Long studentId) {
         this.studentId = studentId;
     }
-    public Integer getPaperNumber() {
-        return paperNumber;
+    public Integer getPaperIndex() {
+        return paperIndex;
     }
 
-    public void setPaperIndex(Integer paperNumber) {
-        this.paperNumber = paperNumber;
+    public void setPaperIndex(Integer paperIndex) {
+        this.paperIndex = paperIndex;
     }
     public Long getPaperId() {
         return paperId;
@@ -76,7 +76,7 @@ public class ScanStudentPaper implements Serializable {
     public String toString() {
         return "ScanStudentPaper{" +
             "studentId=" + studentId +
-            ", paperNumber=" + paperNumber +
+            ", paperIndex=" + paperIndex +
             ", paperId=" + paperId +
             ", createTime=" + createTime +
             ", updateTime=" + updateTime +

+ 19 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/ScanOmrTaskService.java

@@ -2,6 +2,8 @@ package com.qmth.teachcloud.mark.service;
 
 import java.util.List;
 
+import javax.validation.constraints.NotNull;
+
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.qmth.teachcloud.common.entity.SysUser;
 import com.qmth.teachcloud.common.enums.scan.ConditionType;
@@ -40,4 +42,21 @@ public interface ScanOmrTaskService extends IService<ScanOmrTask> {
 
 	boolean apply(ScanOmrTask task, String userId);
 
+	int getCountByExamAndStatusAndUserId(Long examId, Long userId, OmrTaskStatus... status);
+
+	int getCountByExamAndStatus(Long examId, OmrTaskStatus... status);
+
+	boolean hasApplied(ScanOmrTask t, Long userId);
+
+	void releaseByTask(ScanOmrTask t);
+
+	/**
+	 * 根据考生当前绑定的paper刷新考生状态,需要在外部调用处对考生上锁
+	 *
+	 * @param id
+	 */
+	void updateStudentByPaper(@NotNull Long userId, @NotNull Long studentId, @NotNull boolean updateOmrTask);
+
+	void deleteByStudentId(Long examId, Long studentId);
+
 }

+ 6 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/ScanPaperService.java

@@ -1,6 +1,10 @@
 package com.qmth.teachcloud.mark.service;
 
 import com.qmth.teachcloud.mark.entity.ScanPaper;
+import com.qmth.teachcloud.mark.entity.ScanPaperPage;
+
+import java.util.List;
+
 import com.baomidou.mybatisplus.extension.service.IService;
 
 /**
@@ -13,4 +17,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface ScanPaperService extends IService<ScanPaper> {
 
+	void savePaperAndPages(ScanPaper paper, List<ScanPaperPage> pages);
+
 }

+ 2 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/ScanStudentPaperService.java

@@ -18,4 +18,6 @@ public interface ScanStudentPaperService extends IService<ScanStudentPaper> {
 
 	List<ScanStudentPaper> findByStudentId(Long studentId);
 
+	ScanStudentPaper findByPaperId(Long paperId);
+
 }

+ 513 - 105
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanOmrTaskServiceImpl.java

@@ -5,32 +5,44 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.validation.constraints.NotNull;
+
 import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.boot.core.concurrent.service.ConcurrentService;
 import com.qmth.boot.core.exception.ParameterException;
 import com.qmth.teachcloud.common.entity.MarkStudent;
+import com.qmth.teachcloud.common.entity.ScanAnswerCard;
 import com.qmth.teachcloud.common.entity.SysUser;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.common.enums.ScanStatus;
 import com.qmth.teachcloud.common.enums.scan.ConditionType;
 import com.qmth.teachcloud.common.enums.scan.OmrField;
 import com.qmth.teachcloud.common.service.MarkStudentService;
+import com.qmth.teachcloud.common.service.ScanAnswerCardService;
 import com.qmth.teachcloud.mark.bean.OmrTaskItem;
 import com.qmth.teachcloud.mark.bean.OmrTaskPage;
+import com.qmth.teachcloud.mark.dto.OmrTaskDto;
+import com.qmth.teachcloud.mark.dto.OmrTaskPageDto;
 import com.qmth.teachcloud.mark.dto.ScanOmrTaskDto;
 import com.qmth.teachcloud.mark.dto.ScanOmrTaskPageDto;
 import com.qmth.teachcloud.mark.dto.ScanOmrTaskResultDto;
+import com.qmth.teachcloud.mark.dto.ScanOmrTaskResultPageDto;
 import com.qmth.teachcloud.mark.dto.ScanOmrTaskSaveDto;
 import com.qmth.teachcloud.mark.dto.ScanOmrTaskStatusDto;
 import com.qmth.teachcloud.mark.entity.ScanOmrTask;
 import com.qmth.teachcloud.mark.entity.ScanPaper;
 import com.qmth.teachcloud.mark.entity.ScanPaperPage;
 import com.qmth.teachcloud.mark.entity.ScanStudentPaper;
+import com.qmth.teachcloud.mark.enums.LockType;
 import com.qmth.teachcloud.mark.enums.OmrTaskStatus;
 import com.qmth.teachcloud.mark.mapper.ScanOmrTaskMapper;
 import com.qmth.teachcloud.mark.service.ScanOmrTaskService;
@@ -52,115 +64,276 @@ import com.qmth.teachcloud.mark.utils.TaskLockUtil;
 public class ScanOmrTaskServiceImpl extends ServiceImpl<ScanOmrTaskMapper, ScanOmrTask> implements ScanOmrTaskService {
 	@Autowired
 	private ScanStudentPaperService studentPaperService;
-	
 	@Autowired
 	private MarkStudentService studentService;
 	@Autowired
 	private ScanPaperService paperService;
 	@Autowired
 	private ScanPaperPageService pageService;
-	
-    private static final String OMR_SUSPECT = "?";
+	@Autowired
+	private ConcurrentService concurrentService;
+	@Autowired
+	private ScanAnswerCardService answerCardService;
+
+	private static final String OMR_SUSPECT = "?";
+
+	private static final String OMR_BLANK = "#";
 
-    private static final String OMR_BLANK = "#";
-	
 	@Override
 	public ScanOmrTask buildTask(ConditionType c, Long studentId) {
+		List<ScanStudentPaper> paperIds = clearAndToDispose(c, studentId);
+		if (CollectionUtils.isEmpty(paperIds)) {
+			return null;
+		}
+		MarkStudent student = studentService.getById(studentId);
+		// 所有客观题空白
+		boolean allObjectiveBlank = isAllBlank(paperIds);
+		List<OmrTaskDto> retList = new ArrayList<>();
+		boolean multi_blank = false;
+		boolean single_blank = false;
+		int multi_blank_count = 0;
+		int single_blank_count = 0;
+		for (ScanStudentPaper spe : paperIds) {
+			ScanPaper paper = paperService.getById(spe.getPaperId());
+			if (paper == null) {
+				throw new ParameterException("paper不存在");
+			}
+			Long paperId = spe.getPaperId();
+			ScanOmrTask task = new ScanOmrTask();
+			task.setCardNumber(paper.getCardNumber());
+			task.setExamId(paper.getExamId());
+			task.setConditions(c.toString());
+			task.setPaperId(paperId);
+			task.setPaperIndex(spe.getPaperIndex());
+			task.setStatus(OmrTaskStatus.WAITING);
+			task.setStudentId(spe.getStudentId());
+			task.setPages(new ArrayList<>());
+			List<ScanPaperPage> pageList = pageService.listByPaperId(paperId);
+			OmrTaskDto taskDto = new OmrTaskDto(task);
+			retList.add(taskDto);
+			// 循环试卷
+			for (ScanPaperPage pageEntity : pageList) {
+				OmrTaskPageDto pageDto = new OmrTaskPageDto(pageEntity.getPageIndex());
+				taskDto.getPage().add(pageDto);
+				List<OmrTaskItem> items = pageDto.getItems();
+				// 分组类型
+				// 识别嫌疑
+				if (ConditionType.FILL_SUSPECT.equals(c)) {
+					for (int i = 0; pageEntity.getQuestion() != null && pageEntity.getQuestion().getResult() != null
+							&& i < pageEntity.getQuestion().getResult().size(); i++) {
+						String result = pageEntity.getQuestion().getResult().get(i);
+						if (result != null && result.startsWith(OMR_SUSPECT)) {
+							OmrTaskItem item = new OmrTaskItem();
+							item.setIndex(i + 1);
+							item.setField(OmrField.QUESTION);
+							item.setOmrResult(result);
+							items.add(item);
+						}
+					}
+				}
+				// 卷型空选
+//                if (ConditionType.PAPER_TYPE_BLANK.equals(c)) {
+//                    if (pageEntity.getPaperType() != null
+//                            && pageEntity.getPaperType().getResult().contains(OMR_BLANK)) {
+//                        OmrTaskItem item = new OmrTaskItem();
+//                        item.setIndex(1);
+//                        item.setField(OmrField.PAPER_TYPE);
+//                        item.setOmrResult(pageEntity.getPaperType().getResult());
+//                        items.add(item);
+//                    }
+//                }
+				// 卷型多选
+//                if (ConditionType.PAPER_TYPE_EXCEED.equals(c)) {
+//                    if ((!pageEntity.getPaperType().getResult().contains(OMR_SUSPECT)
+//                            && pageEntity.getPaperType().getResult().length() > 2)
+//                            || (pageEntity.getPaperType().getResult().contains(OMR_SUSPECT)
+//                                    && pageEntity.getPaperType().getResult().length() > 1)) {
+//                        OmrTaskItem item = new OmrTaskItem();
+//                        item.setField(OmrField.PAPER_TYPE);
+//                        item.setOmrResult(pageEntity.getPaperType().getResult());
+//                        items.add(item);
+//                    }
+//                }
+//                if (ConditionType.QUESTION_MULTI_BLANK.equals(c)) {
+//                    for (int i = 0; pageEntity.getQuestion() != null && pageEntity.getQuestion().getResult() != null
+//                            && i < pageEntity.getQuestion().getResult().size(); i++) {
+//                        String result = pageEntity.getQuestion().getResult().get(i);
+//                        PaperStructure question = structureService.getPaperStructure(paper.getExamId(),
+//                                paper.getCardNumber(), spe.getPaperNumber(), pageEntity.getPageIndex(), i + 1,
+//                                OmrField.QUESTION);
+//                        if (!question.getSingle() && result != null && result.contains(OMR_BLANK)) {
+//                            OmrTaskItem item = new OmrTaskItem();
+//                            item.setIndex(i + 1);
+//                            item.setField(OmrField.QUESTION);
+//                            item.setOmrResult(result);
+//                            pageDto.getPageMultiBlankItems().add(item);
+//                            multi_blank_count++;
+//                            if (multi_blank_count > condition.getValue()) {
+//                                multi_blank = true;
+//                            }
+//                        }
+//                    }
+//                }
+//                if (ConditionType.QUESTION_SINGLE_BLANK.equals(c)
+//                        && ScanStatus.SCANNED.equals(student.getStatus()) && !allObjectiveBlank) {
+//                    for (int i = 0; pageEntity.getQuestion() != null && pageEntity.getQuestion().getResult() != null
+//                            && i < pageEntity.getQuestion().getResult().size(); i++) {
+//                        String result = pageEntity.getQuestion().getResult().get(i);
+//                        PaperStructure question = structureService.getPaperStructure(paper.getExamId(),
+//                                paper.getCardNumber(), spe.getPaperNumber(), pageEntity.getPageIndex(), i + 1,
+//                                OmrField.QUESTION);
+//                        if (question.getSingle() && result != null && result.contains(OMR_BLANK)) {
+//                            OmrTaskItem item = new OmrTaskItem();
+//                            item.setIndex(i + 1);
+//                            item.setField(OmrField.QUESTION);
+//                            item.setOmrResult(result);
+//                            pageDto.getPageSingleBlankItems().add(item);
+//                            single_blank_count++;
+//                            multi_blank_count++;
+//                            if (single_blank_count > condition.getValue()) {
+//                                single_blank = true;
+//                            }
+//                        }
+//                    }
+//                }
+//                if (ConditionType.QUESTION_SINGLE_EXCEED.equals(c)
+//                        && ScanStatus.SCANNED.equals(student.getStatus()) && !allObjectiveBlank) {
+//                    for (int i = 0; pageEntity.getQuestion() != null && pageEntity.getQuestion().getResult() != null
+//                            && i < pageEntity.getQuestion().getResult().size(); i++) {
+//                        String result = pageEntity.getQuestion().getResult().get(i);
+//                        PaperStructure question = structureService.getPaperStructure(paper.getExamId(),
+//                                paper.getCardNumber(), spe.getPaperNumber(), pageEntity.getPageIndex(), i + 1,
+//                                OmrField.QUESTION);
+//                        if (question.getSingle() && result != null && result.length() > 1) {
+//                            OmrTaskItem item = new OmrTaskItem();
+//                            item.setIndex(i + 1);
+//                            item.setField(OmrField.QUESTION);
+//                            item.setOmrResult(result);
+//                            items.add(item);
+//                        }
+//                    }
+//                }
+			}
+		}
+		if (retList.size() == 0) {
+			return null;
+		}
+		for (OmrTaskDto dto : retList) {
+			for (OmrTaskPageDto pageDto : dto.getPage()) {
+				if (multi_blank) {
+					pageDto.getItems().addAll(pageDto.getPageMultiBlankItems());
+				}
+				if (single_blank) {
+					pageDto.getItems().addAll(pageDto.getPageSingleBlankItems());
+				}
+				if (pageDto.getItems().size() > 0) {
+					OmrTaskPage page = new OmrTaskPage();
+					page.setItems(pageDto.getItems());
+					page.setIndex(pageDto.getPageIndex());
+					dto.getTask().getPages().add(page);
+				}
+			}
+			if (dto.getTask().getPages().size() > 0) {
+				return dto.getTask();
+			}
+		}
 		return null;
 	}
 
 	private List<ScanStudentPaper> clearAndToDispose(ConditionType c, Long studentId) {
-        List<ScanStudentPaper> spes = studentPaperService.findByStudentId(studentId);
-        if (CollectionUtils.isEmpty(spes)) {
-            return spes;
-        }
-        List<ScanOmrTask> tasks = getByStudent(c, studentId);
-        if (CollectionUtils.isEmpty(tasks)) {
-            return spes;
-        }
-        Map<Integer, Long> speMap = new HashMap<>();
-        for (ScanStudentPaper spe : spes) {
-            speMap.put(spe.getPaperNumber(), spe.getPaperId());
-        }
-        Map<Integer, Long> taskMap = new HashMap<>();
-        for (ScanOmrTask t : tasks) {
-            taskMap.put(t.getPaperNumber(), t.getPaperId());
-        }
-        if (ConditionType.FILL_SUSPECT.equals(c)) {
-            List<ScanStudentPaper> ret = new ArrayList<>();
-            for (ScanStudentPaper spe : spes) {
-                Long paperId = taskMap.get(spe.getPaperNumber());
-                if (paperId == null) {// 没有task的直接创建
-                    ret.add(spe);
-                } else if (paperId.longValue() != spe.getPaperId().longValue()) {// 和task不一致的删除并创建
-                    delete(c, studentId, paperId);
-                    ret.add(spe);
-                }
-            }
-            for (ScanOmrTask t : tasks) {
-                if (speMap.get(t.getPaperNumber()) == null) {// 不在绑定关系的task删除
-                    delete(c, studentId, t.getPaperId());
-                }
-            }
-            return ret;
-        } else {
-            if (spes.size() != tasks.size()) {// 数量不一致的删除重建
-                delete(c, studentId);
-                return spes;
-            }
-            for (ScanOmrTask t : tasks) {
-                if (!t.getPaperId().equals(speMap.get(t.getPaperNumber()))) {// 有一个不一致的删除重建
-                    delete(c, studentId);
-                    return spes;
-                }
-            }
-            return null;// 完全一致的不处理
-        }
+		List<ScanStudentPaper> spes = studentPaperService.findByStudentId(studentId);
+		if (CollectionUtils.isEmpty(spes)) {
+			return spes;
+		}
+		List<ScanOmrTask> tasks = getByStudent(c, studentId);
+		if (CollectionUtils.isEmpty(tasks)) {
+			return spes;
+		}
+		Map<Integer, Long> speMap = new HashMap<>();
+		for (ScanStudentPaper spe : spes) {
+			speMap.put(spe.getPaperIndex(), spe.getPaperId());
+		}
+		Map<Integer, Long> taskMap = new HashMap<>();
+		for (ScanOmrTask t : tasks) {
+			taskMap.put(t.getPaperIndex(), t.getPaperId());
+		}
+		if (ConditionType.FILL_SUSPECT.equals(c)) {
+			List<ScanStudentPaper> ret = new ArrayList<>();
+			for (ScanStudentPaper spe : spes) {
+				Long paperId = taskMap.get(spe.getPaperIndex());
+				if (paperId == null) {// 没有task的直接创建
+					ret.add(spe);
+				} else if (paperId.longValue() != spe.getPaperId().longValue()) {// 和task不一致的删除并创建
+					delete(c, studentId, paperId);
+					ret.add(spe);
+				}
+			}
+			for (ScanOmrTask t : tasks) {
+				if (speMap.get(t.getPaperIndex()) == null) {// 不在绑定关系的task删除
+					delete(c, studentId, t.getPaperId());
+				}
+			}
+			return ret;
+		} else {
+			if (spes.size() != tasks.size()) {// 数量不一致的删除重建
+				delete(c, studentId);
+				return spes;
+			}
+			for (ScanOmrTask t : tasks) {
+				if (!t.getPaperId().equals(speMap.get(t.getPaperIndex()))) {// 有一个不一致的删除重建
+					delete(c, studentId);
+					return spes;
+				}
+			}
+			return null;// 完全一致的不处理
+		}
+	}
+
+	private List<ScanOmrTask> getByStudent(ConditionType c, Long studentId) {
+		QueryWrapper<ScanOmrTask> wrapper = new QueryWrapper<>();
+		LambdaQueryWrapper<ScanOmrTask> lw = wrapper.lambda();
+		lw.eq(ScanOmrTask::getConditions, c);
+		lw.eq(ScanOmrTask::getStudentId, studentId);
+		return baseMapper.selectList(wrapper);
+	}
+
+	private void delete(ConditionType c, Long studentId, Long paperId) {
+		UpdateWrapper<ScanOmrTask> wrapper = new UpdateWrapper<>();
+		LambdaUpdateWrapper<ScanOmrTask> lw = wrapper.lambda();
+		lw.eq(ScanOmrTask::getConditions, c);
+		lw.eq(ScanOmrTask::getStudentId, studentId);
+		lw.eq(ScanOmrTask::getPaperId, paperId);
+		this.baseMapper.delete(wrapper);
+	}
+
+	private void delete(ConditionType c, Long studentId) {
+		UpdateWrapper<ScanOmrTask> wrapper = new UpdateWrapper<>();
+		LambdaUpdateWrapper<ScanOmrTask> lw = wrapper.lambda();
+		lw.eq(ScanOmrTask::getConditions, c);
+		lw.eq(ScanOmrTask::getStudentId, studentId);
+		this.baseMapper.delete(wrapper);
+	}
+
+	private boolean isAllBlank(List<ScanStudentPaper> paperIds) {
+		for (ScanStudentPaper spe : paperIds) {
+			ScanPaper paper = paperService.getById(spe.getPaperId());
+			if (paper == null) {
+				throw new ParameterException("paper不存在");
+			}
+			List<ScanPaperPage> pageList = pageService.listByPaperId(spe.getPaperId());
+			for (ScanPaperPage pageEntity : pageList) {
+				for (int i = 0; pageEntity.getQuestion() != null && pageEntity.getQuestion().getResult() != null
+						&& i < pageEntity.getQuestion().getResult().size(); i++) {
+					String result = pageEntity.getQuestion().getResult().get(i);
+					if (!"#".equals(result)) {
+						return false;
+					}
+				}
+			}
+		}
+		return true;
 	}
-	
-	 private List<ScanOmrTask> getByStudent(ConditionType c, Long studentId) {
-	        QueryWrapper<ScanOmrTask> wrapper = new QueryWrapper<>();
-	        LambdaQueryWrapper<ScanOmrTask> lw = wrapper.lambda();
-	        lw.eq(ScanOmrTask::getConditions, c);
-	        lw.eq(ScanOmrTask::getStudentId, studentId);
-	        return baseMapper.selectList(wrapper);
-	    }
-	 
-	 private void delete( ConditionType c, Long studentId, Long paperId) {
-	        UpdateWrapper<ScanOmrTask> wrapper = new UpdateWrapper<>();
-	        LambdaUpdateWrapper<ScanOmrTask> lw = wrapper.lambda();
-	        lw.eq(ScanOmrTask::getConditions, c);
-	        lw.eq(ScanOmrTask::getStudentId, studentId);
-	        lw.eq(ScanOmrTask::getPaperId, paperId);
-	        this.baseMapper.delete(wrapper);
-	    }
-	 private void delete( ConditionType c, Long studentId) {
-	        UpdateWrapper<ScanOmrTask> wrapper = new UpdateWrapper<>();
-	        LambdaUpdateWrapper<ScanOmrTask> lw = wrapper.lambda();
-	        lw.eq(ScanOmrTask::getConditions, c);
-	        lw.eq(ScanOmrTask::getStudentId, studentId);
-	        this.baseMapper.delete(wrapper);
-	    }
-	 
-	 private boolean isAllBlank(List<ScanStudentPaper> paperIds) {
-	        for (ScanStudentPaper spe : paperIds) {
-	        	ScanPaper paper = paperService.getById(spe.getPaperId());
-	            if (paper == null) {
-	                throw new ParameterException("paper不存在");
-	            }
-	            List<ScanPaperPage> pageList = pageService.listByPaperId(spe.getPaperId());
-	            for (ScanPaperPage pageEntity : pageList) {
-	                for (int i = 0; pageEntity.getQuestion() != null && pageEntity.getQuestion().getResult() != null
-	                        && i < pageEntity.getQuestion().getResult().size(); i++) {
-	                    String result = pageEntity.getQuestion().getResult().get(i);
-	                    if (!"#".equals(result)) {
-	                        return false;
-	                    }
-	                }
-	            }
-	        }
-	        return true;
-	    }
+
 	@Override
 	public ScanOmrTaskDto getTask(Long examId, Long userId) {
 		int retry = 0;
@@ -180,6 +353,9 @@ public class ScanOmrTaskServiceImpl extends ServiceImpl<ScanOmrTaskMapper, ScanO
 				retry++;
 			}
 		}
+		if (task == null) {
+			throw ExceptionResultEnum.ERROR.exception("没有识别对照任务");
+		}
 		return task;
 	}
 
@@ -191,7 +367,7 @@ public class ScanOmrTaskServiceImpl extends ServiceImpl<ScanOmrTaskMapper, ScanO
 		vo.setId(task.getId());
 		vo.setName(student.getStudentName());
 		vo.setPaperId(task.getPaperId());
-		vo.setPaperNumber(task.getPaperNumber());
+		vo.setPaperIndex(task.getPaperIndex());
 		vo.setSubjectCode(student.getCourseCode());
 		vo.setSubjectName(student.getCourseName());
 		List<ScanOmrTaskPageDto> pages = new ArrayList<>();
@@ -293,27 +469,259 @@ public class ScanOmrTaskServiceImpl extends ServiceImpl<ScanOmrTaskMapper, ScanO
 	}
 
 	@Override
-	public ScanOmrTaskSaveDto submitTask(ScanOmrTaskResultDto result, SysUser user) {
-		// TODO Auto-generated method stub
-		return null;
+	public ScanOmrTaskSaveDto submitTask(@NotNull ScanOmrTaskResultDto result, @NotNull SysUser user) {
+		ScanOmrTask task = this.getById(result.getId());
+		if (task == null) {
+			throw new ParameterException("任务不存在");
+		}
+		// if (TaskStatus.PROCESSED.equals(task.getStatus())) {
+		// throw new ParameterException("任务已处理");
+		// }
+		if (!task.getUserId().equals(user.getId())) {
+			throw new ParameterException("任务非本人领取");
+		}
+		if (OmrTaskStatus.WAITING.equals(task.getStatus()) && !hasApplied(task, user.getId())) {
+			throw new ParameterException("任务非本人领取");
+		}
+		concurrentService.getReadWriteLock(LockType.STUDENT + "-" + task.getStudentId()).writeLock().lock();
+
+		for (OmrTaskPage page : task.getPages()) {
+			ScanOmrTaskResultPageDto pageVo = result.findPage(page.getIndex());
+			if (pageVo == null) {
+				throw new ParameterException("page[" + page.getIndex() + "]不存在");
+			}
+			for (OmrTaskItem item : page.getItems()) {
+				if (OmrField.ABSENT.equals(item.getField())) {
+					if (pageVo.getAbsent() == null) {
+						throw new ParameterException("page[" + page.getIndex() + "].absent不存在");
+					}
+					item.setFirstResult(pageVo.getAbsent().toString());
+				}
+				if (OmrField.BREACH.equals(item.getField())) {
+					if (pageVo.getBreach() == null) {
+						throw new ParameterException("page[" + page.getIndex() + "].breach不存在");
+					}
+					item.setFirstResult(pageVo.getBreach().toString());
+				}
+				if (OmrField.QUESTION.equals(item.getField())) {
+					String content = pageVo.getQuestion() != null ? pageVo.getQuestion().get(item.getIndex()) : null;
+					if (content == null) {
+						throw new ParameterException(
+								"page[" + page.getIndex() + "].question[" + item.getIndex() + "]不存在");
+					}
+					item.setFirstResult(content);
+				}
+				if (OmrField.SELECTIVE.equals(item.getField())) {
+					String content = pageVo.getSelective() != null ? pageVo.getSelective().get(item.getIndex()) : null;
+					if (content == null) {
+						throw new ParameterException(
+								"page[" + page.getIndex() + "].selective[" + item.getIndex() + "]不存在");
+					}
+					item.setFirstResult(content);
+				}
+			}
+		}
+		task.setStatus(OmrTaskStatus.PROCESSED);
+
+		updatePaperResult(result, task.getPaperId(), user.getId());
+		task.setUserId(user.getId());
+		task.setUpdateTime(System.currentTimeMillis());
+		this.saveOrUpdate(task);
+		this.releaseByTask(task);
+		concurrentService.getReadWriteLock(LockType.STUDENT + "-" + task.getStudentId()).writeLock().unlock();
+		ScanOmrTaskSaveDto vo = new ScanOmrTaskSaveDto();
+		vo.setId(result.getId());
+		vo.setUpdateTime(System.currentTimeMillis());
+		vo.setStatus(this.getStatus(task.getExamId(), user.getId()));
+		return vo;
+	}
+
+	private void updatePaperResult(ScanOmrTaskResultDto result, Long paperId, Long userId) {
+		ScanPaper paper = paperService.getById(paperId);
+		ScanStudentPaper sp = studentPaperService.findByPaperId(paperId);
+		if (paper == null) {
+			throw new ParameterException("未找到paper信息");
+		}
+		if (sp == null) {
+			throw new ParameterException("paper未绑定考生");
+		}
+		List<ScanPaperPage> pages = pageService.listByPaperId(paperId);
+		if (CollectionUtils.isEmpty(pages)) {
+			throw new ParameterException("未找到page信息");
+		}
+		for (ScanPaperPage page : pages) {
+			ScanOmrTaskResultPageDto vo = result.findPage(page.getPageIndex());
+			if (vo != null) {
+				vo.update(page);
+			}
+		}
+		paperService.savePaperAndPages(paper, pages);
+		this.updateStudentByPaper(userId, sp.getStudentId(), false);
+	}
+
+	@Override
+	public boolean hasApplied(ScanOmrTask t, Long userId) {
+		TaskLock taskLock = TaskLockUtil.getOmrTask(t.getExamId().toString());
+		return taskLock.exist(t.getId(), 1, userId.toString());
+	}
+
+	@Override
+	public void releaseByTask(ScanOmrTask t) {
+		TaskLock taskLock = TaskLockUtil.getOmrTask(t.getExamId().toString());
+		taskLock.remove(t.getId(), 1);
 	}
 
 	@Override
 	public ScanOmrTaskStatusDto getStatus(Long examId, Long userId) {
-		// TODO Auto-generated method stub
-		return null;
+		ScanOmrTaskStatusDto status = new ScanOmrTaskStatusDto();
+		status.setFinishCount(this.getCountByExamAndStatusAndUserId(examId, userId, OmrTaskStatus.PROCESSED));
+		status.setTodoCount(this.getCountByExamAndStatus(examId, OmrTaskStatus.WAITING));
+		return status;
 	}
 
 	@Override
-	public void releaseByUser(Long examId, Long userId) {
-		// TODO Auto-generated method stub
+	public int getCountByExamAndStatusAndUserId(Long examId, Long userId, OmrTaskStatus... status) {
+		QueryWrapper<ScanOmrTask> wrapper = new QueryWrapper<>();
+		LambdaQueryWrapper<ScanOmrTask> lw = wrapper.lambda();
+		lw.and(param -> {
+			for (OmrTaskStatus taskStatus : status) {
+				param.or().eq(ScanOmrTask::getStatus, taskStatus);
+			}
+		});
+		lw.eq(ScanOmrTask::getUserId, userId);
+		lw.eq(ScanOmrTask::getExamId, examId);
+		return this.count(wrapper);
+	}
+
+	@Override
+	public int getCountByExamAndStatus(Long examId, OmrTaskStatus... status) {
+		QueryWrapper<ScanOmrTask> wrapper = new QueryWrapper<>();
+		LambdaQueryWrapper<ScanOmrTask> lw = wrapper.lambda();
+		lw.and(param -> {
+			for (OmrTaskStatus taskStatus : status) {
+				param.or().eq(ScanOmrTask::getStatus, taskStatus);
+			}
+		});
+		lw.eq(ScanOmrTask::getExamId, examId);
+		return this.count(wrapper);
+	}
 
+	@Override
+	public void releaseByUser(Long examId, Long userId) {
+		TaskLock taskLock = TaskLockUtil.getOmrTask(examId.toString());
+		taskLock.clear(userId.toString());
 	}
 
 	@Override
 	public ScanOmrTaskDto history(Long examId, Long taskId, Long userId, Boolean next) {
-		// TODO Auto-generated method stub
-		return null;
+		ScanOmrTask cur = null;
+		if (taskId != null) {
+			cur = this.getById(taskId);
+		}
+		QueryWrapper<ScanOmrTask> wrapper = new QueryWrapper<>();
+		LambdaQueryWrapper<ScanOmrTask> lw = wrapper.lambda();
+		lw.eq(ScanOmrTask::getExamId, examId);
+		lw.eq(ScanOmrTask::getUserId, userId);
+		lw.eq(ScanOmrTask::getStatus, OmrTaskStatus.PROCESSED);
+		if (next != null && next) {
+			if (taskId != null) {
+				lw.gt(ScanOmrTask::getId, cur.getId());
+			}
+			lw.orderByAsc(ScanOmrTask::getId);
+		} else {
+			if (taskId != null) {
+				lw.lt(ScanOmrTask::getId, cur.getId());
+			}
+			lw.orderByDesc(ScanOmrTask::getId);
+		}
+		wrapper.last("LIMIT 1");
+		ScanOmrTask task = this.baseMapper.selectOne(wrapper);
+		if (task == null) {
+			throw ExceptionResultEnum.ERROR.exception("没有识别对照任务");
+		}
+		return toTaskVo(task, true);
+	}
+
+	/**
+	 * 根据考生当前绑定的paper刷新考生状态,需要在外部调用处对考生上锁
+	 *
+	 * @param id
+	 */
+	@Override
+	@Transactional
+	public void updateStudentByPaper(@NotNull Long userId, @NotNull Long studentId, @NotNull boolean updateOmrTask) {
+		MarkStudent student = studentService.getById(studentId);
+		if (student == null) {
+			throw new ParameterException("找不到对应的考生");
+		}
+		// 重置状态
+		student.setIncomplete(false);
+		student.setAssigned(false);
+		student.setQuestionFilled(false);
+		student.setOmrAbsent(false);
+		int paperCount = 0;
+		List<ScanStudentPaper> studentPaperList = studentPaperService.findByStudentId(studentId);
+		for (ScanStudentPaper studentPaper : studentPaperList) {
+			paperCount++;
+			// 获取paper详情更新考生状态
+			ScanPaper paper = paperService.getById(studentPaper.getPaperId());
+			student.setAssigned(student.getAssigned() || paper.getAssigned());
+			student.setQuestionFilled(student.getQuestionFilled() || paper.getQuestionFilled());
+			student.setCardNumber(paper.getCardNumber());
+			// 单独判断首张纸正面的识别结果
+			if (studentPaper.getPaperIndex() == 1) {
+				// 根据识别结果更新考生属性
+				ScanPaperPage page = pageService.findPaperIdAndIndex(paper.getId(), 1);
+				student.setOmrAbsent(page.getAbsent() == null ? false : page.getAbsent().getResult());
+//                student.setDevice(batchService.findByPaperId(paper.getId()).getDevice());
+			}
+		}
+		// 更新考生状态
+		if (paperCount > 0) {
+			ScanAnswerCard answerCard = answerCardService.findByExamAndNumber(student.getExamId(),
+					student.getCardNumber());
+			student.setIncomplete(paperCount != answerCard.getPaperCount());
+			student.setScanStatus(ScanStatus.SCANNED);
+		} else {
+			student.setScanStatus(ScanStatus.UNEXIST);
+		}
+//        student.setUpdaterId(userId);
+//        student.setUpdateTime(System.currentTimeMillis());
+		studentService.saveOrUpdate(student);
+		// try {
+		// SignatureInfo signatureInfo = new SignatureInfo(SignatureType.TOKEN,
+		// user.getAccount(),
+		// user.getMarkingCloudToken());
+		// apiClient.studentFileDelete(signatureInfo.toString(),
+		// student.getExamId(), student.getExamNumber());
+		// } catch (RetrofitResponseError e) {
+		// e.printStackTrace();
+		// if (e.getCode() == 401) {
+		// throw AuthorizationException.SIGNATURE_INVALID;
+		// } else {
+		// throw e;
+		// }
+		// }
+		if (updateOmrTask) {
+			// 清除识别对照任务
+			this.deleteByStudentId(student.getExamId(), student.getId());
+			// 重新生成识别对照任务
+			this.buildTask(ConditionType.FILL_SUSPECT, student.getId());
+		}
 	}
 
+	@Transactional
+	@Override
+	public void deleteByStudentId(Long examId, Long studentId) {
+		QueryWrapper<ScanOmrTask> wrapper = new QueryWrapper<>();
+		LambdaQueryWrapper<ScanOmrTask> lw = wrapper.lambda();
+		lw.eq(ScanOmrTask::getStudentId, studentId);
+		List<ScanOmrTask> tasks = baseMapper.selectList(wrapper);
+		if (CollectionUtils.isEmpty(tasks)) {
+			return;
+		}
+		concurrentService.getReadWriteLock(LockType.STUDENT + "-" + studentId).writeLock().lock();
+		this.removeByIds(tasks);
+		concurrentService.getReadWriteLock(LockType.STUDENT + "-" + studentId).writeLock().unlock();
+	}
 }

+ 58 - 3
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanPaperServiceImpl.java

@@ -1,14 +1,26 @@
 package com.qmth.teachcloud.mark.service.impl;
 
+import java.util.List;
+
+import javax.validation.constraints.NotNull;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.teachcloud.common.entity.ScanAnswerCard;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
+import com.qmth.teachcloud.common.service.ScanAnswerCardService;
 import com.qmth.teachcloud.mark.entity.ScanPaper;
+import com.qmth.teachcloud.mark.entity.ScanPaperPage;
 import com.qmth.teachcloud.mark.mapper.ScanPaperMapper;
+import com.qmth.teachcloud.mark.service.ScanPaperPageService;
 import com.qmth.teachcloud.mark.service.ScanPaperService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.stereotype.Service;
 
 /**
  * <p>
- *  服务实现类
+ * 服务实现类
  * </p>
  *
  * @author xf
@@ -16,5 +28,48 @@ import org.springframework.stereotype.Service;
  */
 @Service
 public class ScanPaperServiceImpl extends ServiceImpl<ScanPaperMapper, ScanPaper> implements ScanPaperService {
+    @Autowired
+    private ScanAnswerCardService answerCardService;
+    @Autowired
+    private ScanPaperPageService paperPageService;
+	/**
+     * 全量更新paper和所属的page集合,并刷新paper相关属性
+     *
+     * @param paper
+     * @param pages
+     */
+    @Override
+    @Transactional
+    public void savePaperAndPages(@NotNull ScanPaper paper,@NotNull List<ScanPaperPage> pages ) {
+        // 与卡格式校验
+        ScanAnswerCard answerCard = answerCardService.findByExamAndNumber(paper.getExamId(), paper.getCardNumber());
+        if (answerCard == null) {
+        	throw ExceptionResultEnum.ERROR.exception("卡格式不存在, cardNumber=" + paper.getCardNumber());
+        }
+        if (pages.size() != (answerCard.getSinglePage() ? 1 : 2)) {
+        	throw ExceptionResultEnum.ERROR.exception("paper.page数量与卡格式不符, cardNumber=" + paper.getCardNumber());
+        }
+        // 更新paper属性
+        paper.setPageCount(pages.size());
+        paper.setQuestionFilled(false);
+        // 判断是否客观题已填涂
+        for (ScanPaperPage page : pages) {
+            paper.setQuestionFilled(paper.getQuestionFilled() || page.isQuestionFilled());
+        }
+        // 保存paper数据
+        saveOrUpdate(paper);
+        for (ScanPaperPage page : pages) {
+        	//FIXME 
+            // 验证原图与裁切图已上传
+//            if (!fileStore.exist(page.getSheetPath())) {
+//            	throw ExceptionResultEnum.ERROR.exception("原图不存在:" + page.getSheetPath());
+//            }
+            page.setPaperId(paper.getId());
+        }
+        // 保存page数据
+        paperPageService.saveOrUpdateBatch(pages);
+    }
+
+
 
 }

+ 8 - 0
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/ScanStudentPaperServiceImpl.java

@@ -32,4 +32,12 @@ public class ScanStudentPaperServiceImpl extends ServiceImpl<ScanStudentPaperMap
 		return this.list(wrapper);
 	}
 
+	@Override
+	public ScanStudentPaper findByPaperId(Long paperId) {
+		QueryWrapper<ScanStudentPaper> wrapper = new QueryWrapper<>();
+		LambdaQueryWrapper<ScanStudentPaper> lw = wrapper.lambda();
+		lw.eq(ScanStudentPaper::getPaperId, paperId);
+		return baseMapper.selectOne(wrapper);
+	}
+
 }