浏览代码

打回卷和问题卷

yin 14 小时之前
父节点
当前提交
0a5d58dd14

+ 175 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/bean/ProblemHistoryVo.java

@@ -0,0 +1,175 @@
+package cn.com.qmth.stmms.biz.exam.bean;
+
+import java.util.Date;
+
+import cn.com.qmth.stmms.biz.mark.model.ProblemHistory;
+import cn.com.qmth.stmms.common.annotation.ExcelField;
+import io.swagger.annotations.ApiModelProperty;
+
+public class ProblemHistoryVo {
+
+    private String subjectCode;
+
+    private String subjectName;
+
+    @ExcelField(title = "科目", align = 2, sort = 10)
+    @ApiModelProperty("科目")
+    private String subjectText;
+    @ExcelField(title = "分组序号", align = 2, sort = 20)
+    private Integer groupNumber;
+
+    @ExcelField(title = "状态", align = 2, sort = 30)
+    private String status;
+
+    @ExcelField(title = "准考证号", align = 2, sort = 30)
+    @ApiModelProperty("准考证号")
+    private String examNumber;
+
+    @ExcelField(title = "密号", align = 2, sort = 40)
+    @ApiModelProperty("密号")
+    private String secretNumber;
+
+    @ExcelField(title = "姓名", align = 2, sort = 50)
+    @ApiModelProperty("姓名")
+    private String name;
+
+    @ExcelField(title = "评卷员", align = 2, sort = 60)
+    @ApiModelProperty("评卷员")
+    private String markerName;
+
+
+    @ApiModelProperty("提交时间")
+    @ExcelField(title = "提交时间", align = 2, sort = 90)
+    private Date createTime;
+
+    @ExcelField(title = "类型", align = 2, sort = 100)
+    private String problemType;
+
+    @ExcelField(title = "处理人", align = 2, sort = 110)
+    private String headerName;
+
+    @ApiModelProperty("处理时间")
+    @ExcelField(title = "处理时间", align = 2, sort = 120)
+    private Date updateTime;
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public String getSubjectText() {
+        return subjectText;
+    }
+
+    public void setSubjectText(String subjectText) {
+        this.subjectText = subjectText;
+    }
+
+    public Integer getGroupNumber() {
+        return groupNumber;
+    }
+
+    public void setGroupNumber(Integer groupNumber) {
+        this.groupNumber = groupNumber;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public String getSecretNumber() {
+        return secretNumber;
+    }
+
+    public void setSecretNumber(String secretNumber) {
+        this.secretNumber = secretNumber;
+    }
+
+
+    public String getMarkerName() {
+        return markerName;
+    }
+
+    public void setMarkerName(String markerName) {
+        this.markerName = markerName;
+    }
+
+
+    public String getHeaderName() {
+        return headerName;
+    }
+
+    public void setHeaderName(String headerName) {
+        this.headerName = headerName;
+    }
+
+    public String getSubjectName() {
+        return subjectName;
+    }
+
+    public void setSubjectName(String subjectName) {
+        this.subjectName = subjectName;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public String getProblemType() {
+        return problemType;
+    }
+
+    public void setProblemType(String problemType) {
+        this.problemType = problemType;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public static ProblemHistoryVo of(ProblemHistory from) {
+        if (from == null) {
+            return null;
+        }
+        ProblemHistoryVo ret = new ProblemHistoryVo();
+        ret.setSubjectCode(from.getSubjectCode());
+        ret.setGroupNumber(from.getGroupNumber());
+        ret.setExamNumber(from.getExamNumber());
+        ret.setSecretNumber(from.getSecretNumber());
+        ret.setStatus(from.getStatus().getName());
+        ret.setCreateTime(from.getCreateTime());
+        ret.setUpdateTime(from.getUpdateTime());
+        return ret;
+    }
+}

+ 177 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/bean/RejectHistoryVo.java

@@ -0,0 +1,177 @@
+package cn.com.qmth.stmms.biz.exam.bean;
+
+import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
+import cn.com.qmth.stmms.biz.mark.model.RejectHistory;
+import cn.com.qmth.stmms.common.annotation.ExcelField;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Date;
+
+public class RejectHistoryVo {
+
+    public static final String UN_SELECTIVE_SCORE = "-1";
+
+    private String subjectCode;
+    private String subjectName;
+
+    @ExcelField(title = "科目", align = 2, sort = 10)
+    @ApiModelProperty("科目")
+    private String subjectText;
+
+    private Integer groupNumber;
+
+    private String groupName;
+
+    @ExcelField(title = "分组", align = 2, sort = 20)
+    @ApiModelProperty("分组")
+    private String groupText;
+
+    @ExcelField(title = "准考证号", align = 2, sort = 30)
+    @ApiModelProperty("准考证号")
+    private String examNumber;
+
+    @ExcelField(title = "密号", align = 2, sort = 40)
+    @ApiModelProperty("密号")
+    private String secretNumber;
+
+    @ExcelField(title = "打回原因", align = 2, sort = 50)
+    @ApiModelProperty("打回原因")
+    private String reason;
+
+    @ExcelField(title = "评卷员", align = 2, sort = 60)
+    @ApiModelProperty("评卷员")
+    private String markerName;
+
+    @ApiModelProperty("给分明细")
+    @ExcelField(title = "给分明细", align = 2, sort = 70)
+    private String scoreList;
+
+    @ApiModelProperty("打回人")
+    @ExcelField(title = "打回人", align = 2, sort = 80)
+    private String headerName;
+
+    @ApiModelProperty("打回时间")
+    @ExcelField(title = "打回时间", align = 2, sort = 90)
+    private Date rejectTime;
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public String getSubjectText() {
+        return subjectText;
+    }
+
+    public void setSubjectText(String subjectText) {
+        this.subjectText = subjectText;
+    }
+
+    public Integer getGroupNumber() {
+        return groupNumber;
+    }
+
+    public void setGroupNumber(Integer groupNumber) {
+        this.groupNumber = groupNumber;
+    }
+
+    public String getGroupName() {
+        return groupName;
+    }
+
+    public void setGroupName(String groupName) {
+        this.groupName = groupName;
+    }
+
+    public String getGroupText() {
+        return groupText;
+    }
+
+    public void setGroupText(String groupText) {
+        this.groupText = groupText;
+    }
+
+    public String getExamNumber() {
+        return examNumber;
+    }
+
+    public void setExamNumber(String examNumber) {
+        this.examNumber = examNumber;
+    }
+
+    public String getSecretNumber() {
+        return secretNumber;
+    }
+
+    public void setSecretNumber(String secretNumber) {
+        this.secretNumber = secretNumber;
+    }
+
+    public String getReason() {
+        return reason;
+    }
+
+    public void setReason(String reason) {
+        this.reason = reason;
+    }
+
+    public String getMarkerName() {
+        return markerName;
+    }
+
+    public void setMarkerName(String markerName) {
+        this.markerName = markerName;
+    }
+
+    public String getScoreList() {
+        return scoreList;
+    }
+
+    public void setScoreList(String scoreList) {
+        this.scoreList = scoreList;
+    }
+
+    public String getHeaderName() {
+        return headerName;
+    }
+
+    public void setHeaderName(String headerName) {
+        this.headerName = headerName;
+    }
+
+    public String getSubjectName() {
+        return subjectName;
+    }
+
+    public void setSubjectName(String subjectName) {
+        this.subjectName = subjectName;
+    }
+
+    public Date getRejectTime() {
+        return rejectTime;
+    }
+
+    public void setRejectTime(Date rejectTime) {
+        this.rejectTime = rejectTime;
+    }
+
+    public static RejectHistoryVo of(RejectHistory from) {
+        if (from == null) {
+            return null;
+        }
+        RejectHistoryVo ret = new RejectHistoryVo();
+        ret.setSubjectCode(from.getSubjectCode());
+        ret.setGroupNumber(from.getGroupNumber());
+        ret.setExamNumber(from.getExamNumber());
+        ret.setSecretNumber(from.getSecretNumber());
+        ret.setReason(from.getReason());
+        if(from.getScoreList()!=null){
+            ret.setScoreList(from.getScoreList().replace(UN_SELECTIVE_SCORE, "/"));
+        }
+        ret.setRejectTime(from.getCreateTime());
+        return ret;
+    }
+}

+ 120 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/bean/SubjectRejectVo.java

@@ -0,0 +1,120 @@
+package cn.com.qmth.stmms.biz.exam.bean;
+
+import cn.com.qmth.stmms.common.annotation.ExcelField;
+import io.swagger.annotations.ApiModelProperty;
+
+public class SubjectRejectVo {
+
+    private String subjectCode;
+
+    @ExcelField(title = "科目", align = 2, sort = 10)
+    @ApiModelProperty("科目")
+    private String subjectText;
+
+    private Integer groupNumber;
+
+    private String groupName;
+
+    @ExcelField(title = "分组", align = 2, sort = 20)
+    @ApiModelProperty("分组")
+    private String groupText;
+
+    @ExcelField(title = "任务总量", align = 2, sort = 30)
+    @ApiModelProperty("任务总量")
+    private Integer totalCount = 0;
+
+    @ExcelField(title = "已完成", align = 2, sort = 40)
+    @ApiModelProperty("已完成")
+    private Integer finishCount = 0;
+
+    @ExcelField(title = "打回数", align = 2, sort = 50)
+    @ApiModelProperty("打回数")
+    private Long rejectCount = 0L;
+
+    @ExcelField(title = "打回率", align = 2, sort = 60)
+    @ApiModelProperty("打回率")
+    private String rejectRatio;
+
+    @ApiModelProperty(hidden = true)
+    private String subjectName;
+
+    public String getSubjectCode() {
+        return subjectCode;
+    }
+
+    public void setSubjectCode(String subjectCode) {
+        this.subjectCode = subjectCode;
+    }
+
+    public String getSubjectText() {
+        return subjectText;
+    }
+
+    public void setSubjectText(String subjectText) {
+        this.subjectText = subjectText;
+    }
+
+    public Integer getGroupNumber() {
+        return groupNumber;
+    }
+
+    public void setGroupNumber(Integer groupNumber) {
+        this.groupNumber = groupNumber;
+    }
+
+    public String getGroupName() {
+        return groupName;
+    }
+
+    public void setGroupName(String groupName) {
+        this.groupName = groupName;
+    }
+
+    public String getGroupText() {
+        return groupText;
+    }
+
+    public void setGroupText(String groupText) {
+        this.groupText = groupText;
+    }
+
+    public Integer getTotalCount() {
+        return totalCount;
+    }
+
+    public void setTotalCount(Integer totalCount) {
+        this.totalCount = totalCount;
+    }
+
+    public Integer getFinishCount() {
+        return finishCount;
+    }
+
+    public void setFinishCount(Integer finishCount) {
+        this.finishCount = finishCount;
+    }
+
+    public String getSubjectName() {
+        return subjectName;
+    }
+
+    public void setSubjectName(String subjectName) {
+        this.subjectName = subjectName;
+    }
+
+    public Long getRejectCount() {
+        return rejectCount;
+    }
+
+    public void setRejectCount(Long rejectCount) {
+        this.rejectCount = rejectCount;
+    }
+
+    public String getRejectRatio() {
+        return rejectRatio;
+    }
+
+    public void setRejectRatio(String rejectRatio) {
+        this.rejectRatio = rejectRatio;
+    }
+}

+ 3 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/dao/RejectHistoryDao.java

@@ -26,4 +26,7 @@ public interface RejectHistoryDao extends JpaRepository<RejectHistory, Integer>,
     List<Integer> getUserIdByExamIdAndSubjectCode(Integer examId, String subjectCode);
     @Query("select count(*) from RejectHistory f where f.markerId=?1")
     long countByMarkerId(Integer markerId);
+
+    @Query("select count(distinct s.examNumber) from RejectHistory s where s.examId=?1 and s.subjectCode=?2 and s.groupNumber=?3 ")
+    long countDistinctByExamIdAndSubjectCodeAndGroup(int examId, String subjectCode, Integer number);
 }

+ 5 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/RejectHistoryServiceImpl.java

@@ -110,4 +110,9 @@ public class RejectHistoryServiceImpl extends BaseQueryService<RejectHistory> im
         return historyDao.countByMarkerId(markerId);
     }
 
+    @Override
+    public long countDistinctByExamIdAndSubjectCodeAndGroup(int examId, String subjectCode, Integer number) {
+        return this.historyDao.countDistinctByExamIdAndSubjectCodeAndGroup(examId,subjectCode,number);
+    }
+
 }

+ 2 - 0
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/RejectHistoryService.java

@@ -15,4 +15,6 @@ public interface RejectHistoryService {
     List<User> getUserIdByExamIdAndSubjectCode(int examId, String subjectCode);
 
     long countByMarkerId(Integer markerId);
+
+    long countDistinctByExamIdAndSubjectCodeAndGroup(int examId, String subjectCode, Integer number);
 }

+ 52 - 2
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/admin/ProblemHistoryController.java

@@ -7,6 +7,8 @@ import java.util.Map;
 
 import javax.servlet.http.HttpServletResponse;
 
+import cn.com.qmth.stmms.biz.exam.bean.ProblemHistoryVo;
+import cn.com.qmth.stmms.biz.utils.PageUtil;
 import com.qmth.boot.core.collection.PageResult;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
@@ -93,8 +95,56 @@ public class ProblemHistoryController extends BaseApiController {
     @Logging(menu = "查询问题卷", type = LogType.QUERY)
     @RequestMapping(value = "query", method = RequestMethod.POST)
     @ResponseBody
-    public PageResult<ProblemHistory> query(ProblemHistorySearchQuery query) {
-        return null;
+    public PageResult<ProblemHistoryDTO> query(ProblemHistorySearchQuery query) {
+        ApiUser user = getApiUser();
+        int examId = getSessionExamId();
+        Exam exam = examService.findById(examId);
+        List<ProblemType> problemTypes = problemService.findByExamId(examId);
+        Map<Integer, ProblemType> problemMap = new HashMap<Integer, ProblemType>();
+        for (ProblemType problemType : problemTypes) {
+            problemMap.put(problemType.getId(), problemType);
+        }
+        if (user.isSubjectHeader() && StringUtils.isBlank(query.getSubjectCode())) {
+            String subjectCodeIn = StringUtils.join(user.getSubjectCodeSet(), ",");
+            query.setSubjectCodeIn(subjectCodeIn);
+        }
+        if (query.getStatus() == null) {
+            query.setStatus(HistoryStatus.WAITING);
+        }
+        query.setExamId(examId);
+        query.orderByExamNumber();
+        query = historyService.findByQuery(query);
+        List<ProblemHistoryDTO> list = new LinkedList<ProblemHistoryDTO>();
+        for (ProblemHistory history : query.getResult()) {
+            ExamStudent student = studentService.findById(history.getStudentId());
+            ProblemHistoryDTO dto = new ProblemHistoryDTO();
+            dto.setSubjectCode(student.getSubjectCode());
+            dto.setSubjectName(student.getSubjectName());
+            dto.setStatus(query.getStatus().getName());
+            dto.setGroupNumber(history.getGroupNumber());
+            if(user.isSubjectHeader() && exam.isForbiddenInfo()){
+                dto.setExamNumber(DEFAULT_SECRET_NUMBER);
+                dto.setSecretNumber(DEFAULT_SECRET_NUMBER);
+                dto.setName(DEFAULT_SECRET_NUMBER);
+            }else{
+                dto.setExamNumber(student.getExamNumber());
+                dto.setSecretNumber(student.getSecretNumber());
+                dto.setName(student.getName());
+            }
+            dto.setProblemType(problemMap.get(history.getProblemId()).getName());
+            if (history.getMarkerId() != null) {
+                User u = userService.findByMarkerId(history.getMarkerId());
+                dto.setMarkLogin(u.getLoginName() + " " + u.getName());
+                dto.setMarkTime(DateUtils.formatDateTime(history.getCreateTime()));
+            }
+            if (history.getUserId() != null) {
+                User u = userService.findById(history.getUserId());
+                dto.setProblemUserName(u.getLoginName() + " " + u.getName());
+                dto.setProblemRestTime(DateUtils.formatDateTime(history.getUpdateTime()));
+            }
+            list.add(dto);
+        }
+        return PageUtil.of(list, query);
     }
 
     @ApiOperation(value = "问题卷重置")

+ 276 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/api/controller/admin/RejectController.java

@@ -0,0 +1,276 @@
+package cn.com.qmth.stmms.api.controller.admin;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import cn.com.qmth.stmms.biz.exam.bean.RejectHistoryVo;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.qmth.boot.core.collection.PageResult;
+
+import cn.com.qmth.stmms.api.controller.BaseApiController;
+import cn.com.qmth.stmms.biz.exam.bean.SubjectRejectVo;
+import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
+import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
+import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
+import cn.com.qmth.stmms.biz.exam.model.Marker;
+import cn.com.qmth.stmms.biz.exam.query.SubjectQualitySearchQuery;
+import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
+import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
+import cn.com.qmth.stmms.biz.exam.service.MarkerService;
+import cn.com.qmth.stmms.biz.exception.StatusException;
+import cn.com.qmth.stmms.biz.mark.model.RejectHistory;
+import cn.com.qmth.stmms.biz.mark.query.RejectHistorySearchQuery;
+import cn.com.qmth.stmms.biz.mark.service.RejectHistoryService;
+import cn.com.qmth.stmms.biz.user.model.User;
+import cn.com.qmth.stmms.biz.user.service.UserService;
+import cn.com.qmth.stmms.biz.utils.Calculator;
+import cn.com.qmth.stmms.biz.utils.PageUtil;
+import cn.com.qmth.stmms.common.annotation.Logging;
+import cn.com.qmth.stmms.common.domain.ApiUser;
+import cn.com.qmth.stmms.common.enums.LogType;
+import cn.com.qmth.stmms.common.utils.ExportExcel;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+@Api(tags = "打回试卷")
+@Controller("adminRejectController")
+@RequestMapping("/api/admin/exam/reject")
+public class RejectController extends BaseApiController {
+
+    protected static Logger log = LoggerFactory.getLogger(RejectController.class);
+
+    @Autowired
+    private MarkGroupService groupService;
+
+    @Autowired
+    private MarkerService markerService;
+
+    @Autowired
+    private ExamSubjectService subjectService;
+
+    @Autowired
+    private RejectHistoryService rejectHistoryService;
+
+    @Autowired
+    private ExamQuestionService questionService;
+
+    @Autowired
+    private UserService userService;
+
+    public static final String UN_SELECTIVE_SCORE = "-1";
+
+    @Logging(menu = "打回统计查询", type = LogType.QUERY)
+    @ApiOperation(value = "打回统计按科目查询")
+    @RequestMapping(value = "list/subject", method = RequestMethod.POST)
+    @ResponseBody
+    public PageResult<SubjectRejectVo> listSubject(SubjectQualitySearchQuery query) {
+        return pageSubject(query);
+    }
+
+    private PageResult<SubjectRejectVo> pageSubject(SubjectQualitySearchQuery query) {
+        int examId = getSessionExamId();
+        ApiUser wu = getApiUser();
+        List<ExamSubject> subjectList = getExamSubject(examId, wu);
+        query.setTotalCount(subjectList == null ? 0 : subjectList.size());
+        List<ExamSubject> pageList = PageUtil.of(subjectList, query).getResult();
+        List<SubjectRejectVo> ret = new ArrayList<>();
+        for (ExamSubject sub : pageList) {
+            SubjectRejectVo vo = new SubjectRejectVo();
+            vo.setSubjectCode(sub.getCode());
+            vo.setSubjectName(sub.getName());
+            vo.setSubjectText(sub.getCode() + "-" + sub.getName());
+            ret.add(vo);
+        }
+        List<MarkGroup> gs = null;
+        if (StringUtils.isBlank(query.getSubjectCode())) {
+            gs = groupService.findByExam(examId);
+        } else {
+            gs = groupService.findByExamAndSubject(examId, query.getSubjectCode());
+        }
+        if (!CollectionUtils.isEmpty(gs)) {
+            fillTotalCount(ret, gs);
+
+        }
+        return PageUtil.of(ret, query);
+    }
+
+    @ApiOperation(value = "打回统计按科目导出")
+    @RequestMapping(value = "list/subject/export", method = RequestMethod.POST)
+    public void exportSubjectFile(SubjectQualitySearchQuery query, HttpServletResponse response) {
+        String fileName = "打回统计数据";
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        PageResult<SubjectRejectVo> page = pageSubject(query);
+        try {
+            new ExportExcel("打回统计数据", SubjectRejectVo.class).setDataList(page.getResult()).write(response, fileName)
+                    .dispose();
+        } catch (IOException e) {
+            throw new StatusException("打回统计数据导出出错", e);
+        }
+    }
+
+    private void fillTotalCount(List<SubjectRejectVo> ret, List<MarkGroup> gs) {
+        Map<String, SubjectRejectVo> map = new HashMap<>();
+        for (SubjectRejectVo vo : ret) {
+            map.put(vo.getSubjectCode(), vo);
+        }
+        for (MarkGroup g : gs) {
+            SubjectRejectVo vo = map.get(g.getSubjectCode());
+            vo.setTotalCount(vo.getTotalCount() + g.getLibraryCount());
+            vo.setFinishCount(vo.getFinishCount() + g.getMarkedCount());
+            long rejectCount = rejectHistoryService.countDistinctByExamIdAndSubjectCodeAndGroup(g.getExamId(),
+                    g.getSubjectCode(), g.getNumber());
+            vo.setRejectCount(vo.getRejectCount() + rejectCount);
+            vo.setRejectRatio(Calculator.percentage(vo.getRejectCount(), vo.getFinishCount(), 2));
+        }
+    }
+
+    @Logging(menu = "打回统计按分组查询", type = LogType.QUERY)
+    @ApiOperation(value = "打回统计按分组分页查询")
+    @RequestMapping(value = "list/group", method = RequestMethod.POST)
+    @ResponseBody
+    public PageResult<SubjectRejectVo> listGroup(SubjectQualitySearchQuery query) {
+        return pageGroup(query);
+    }
+
+    private PageResult<SubjectRejectVo> pageGroup(SubjectQualitySearchQuery query) {
+        int examId = getSessionExamId();
+        List<MarkGroup> gs = null;
+        if (StringUtils.isBlank(query.getSubjectCode())) {
+            gs = groupService.findByExam(examId);
+        } else {
+            gs = groupService.findByExamAndSubject(examId, query.getSubjectCode());
+        }
+        List<SubjectRejectVo> ret = new ArrayList<>();
+        query.setTotalCount(gs == null ? 0 : gs.size());
+        if (!CollectionUtils.isEmpty(gs)) {
+            Map<String, String> subjectNames = new HashMap<>();
+            List<MarkGroup> pageList = PageUtil.of(gs, query).getResult();
+            for (MarkGroup group : pageList) {
+                List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
+                        group.getSubjectCode(), false, group.getNumber());
+                group.setQuestionList(questions);
+                SubjectRejectVo vo = new SubjectRejectVo();
+                vo.setSubjectCode(group.getSubjectCode());
+                vo.setSubjectName(getSubjectName(subjectNames, examId, group.getSubjectCode()));
+                vo.setSubjectText(vo.getSubjectCode() + "-" + vo.getSubjectName());
+                vo.setGroupNumber(group.getNumber());
+                vo.setGroupName(group.getTitle());
+                vo.setGroupText(vo.getGroupNumber() + "-" + vo.getGroupName());
+                vo.setTotalCount(group.getLibraryCount());
+                vo.setFinishCount(group.getMarkedCount());
+                long rejectCount = rejectHistoryService.countDistinctByExamIdAndSubjectCodeAndGroup(group.getExamId(),
+                        group.getSubjectCode(), group.getNumber());
+                vo.setRejectCount(rejectCount);
+                vo.setRejectRatio(Calculator.percentage(vo.getRejectCount(), vo.getFinishCount(), 2));
+                ret.add(vo);
+            }
+        }
+        return PageUtil.of(ret, query);
+    }
+
+    @ApiOperation(value = "打回统计按分组导出")
+    @Logging(menu = "打回统计按分组到处", type = LogType.EXPORT)
+    @RequestMapping(value = "list/group/export", method = RequestMethod.POST)
+    public void exportGroupFile(SubjectQualitySearchQuery query, HttpServletResponse response) {
+        String fileName = "分组质量监控数据";
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        PageResult<SubjectRejectVo> page = pageGroup(query);
+        try {
+            new ExportExcel("分组质量监控数据", SubjectRejectVo.class).setDataList(page.getResult()).write(response, fileName)
+                    .dispose();
+        } catch (IOException e) {
+            throw new StatusException("分组质量监控数据导出出错", e);
+        }
+    }
+
+    private String getSubjectName(Map<String, String> subjectNames, int examId, String code) {
+        String name = subjectNames.get(code);
+        if (name != null) {
+            return name;
+        }
+        ExamSubject es = subjectService.find(examId, code);
+        subjectNames.put(code, es.getName());
+        return es.getName();
+    }
+
+    @ApiOperation(value = "打回历史查询")
+    @Logging(menu = "打回历史查询", type = LogType.QUERY)
+    @RequestMapping(value = "list/history", method = RequestMethod.POST)
+    @ResponseBody
+    public PageResult<RejectHistoryVo> listHistory(RejectHistorySearchQuery query) {
+        return pageHistory(query);
+    }
+
+    @ApiOperation(value = "打回历史导出")
+    @Logging(menu = "打回历史到处", type = LogType.EXPORT)
+    @RequestMapping(value = "list/history/export", method = RequestMethod.POST)
+    public void exportHistoryFile(RejectHistorySearchQuery query, HttpServletResponse response) {
+        String fileName = "打回历史数据";
+        query.setPageNumber(1);
+        query.setPageSize(Integer.MAX_VALUE);
+        List<RejectHistoryVo> page = pageHistory(query).getResult();
+        try {
+            new ExportExcel("打回历史数据", RejectHistoryVo.class).setDataList(page).write(response, fileName)
+                    .dispose();
+        } catch (IOException e) {
+            throw new StatusException("打回历史数据导出出错", e);
+        }
+    }
+
+    private PageResult<RejectHistoryVo> pageHistory(RejectHistorySearchQuery query) {
+        int examId = getSessionExamId();
+        ApiUser wu = getApiUser();
+        List<ExamSubject> subjectList = getExamSubject(examId, wu);
+        if (subjectList.isEmpty()) {
+            return PageUtil.emptyPage();
+        }
+        query.setExamId(examId);
+        if (wu.isSubjectHeader() && StringUtils.isBlank(query.getSubjectCode())) {
+            String subjectCodeIn = StringUtils.join(wu.getSubjectCodeSet(), ",");
+            query.setSubjectCodeIn(subjectCodeIn);
+        }
+        query = rejectHistoryService.findByQuery(query);
+        List<RejectHistoryVo> list =new ArrayList<>();
+        Map<String, String> subjectNames = new HashMap<>();
+        for (RejectHistory h : query.getResult()) {
+            RejectHistoryVo vo = RejectHistoryVo.of(h);
+            Marker marker = markerService.findById(h.getMarkerId());
+            User user = userService.findById(marker.getUserId());
+            vo.setMarkerName(user.getLoginName() + "/" + user.getName());
+
+            User header = userService.findById(h.getUserId());
+            vo.setHeaderName(header.getLoginName() + "/" + header.getName());
+
+            vo.setSubjectName(getSubjectName(subjectNames, examId, h.getSubjectCode()));
+            vo.setSubjectText(vo.getSubjectCode() + "-" + vo.getSubjectName());
+            MarkGroup group = groupService.findOne(examId, h.getSubjectCode(), h.getGroupNumber());
+                group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
+                        group.getSubjectCode(), false, group.getNumber()));
+            vo.setGroupName(group.getTitle());
+            vo.setGroupText(vo.getGroupNumber() + "-" + vo.getGroupName());
+
+            group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
+                    group.getSubjectCode(), false, group.getNumber()));
+            h.setTitle(group.getTitle());
+        }
+        return PageUtil.of(list, query);
+    }
+
+}