package cn.com.qmth.stmms.biz.mark.service.Impl;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.com.qmth.stmms.biz.exam.dao.MarkGroupDao;
import cn.com.qmth.stmms.biz.exam.dao.MarkGroupStudentDao;
import cn.com.qmth.stmms.biz.exam.dao.MarkerDao;
import cn.com.qmth.stmms.biz.exam.dao.SelectiveStudentDao;
import cn.com.qmth.stmms.biz.exam.dao.SubjectiveScoreDao;
import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
import cn.com.qmth.stmms.biz.exam.model.MarkGroupStudent;
import cn.com.qmth.stmms.biz.exam.model.Marker;
import cn.com.qmth.stmms.biz.exam.model.SelectiveGroup;
import cn.com.qmth.stmms.biz.exam.model.SelectiveStudent;
import cn.com.qmth.stmms.biz.exam.model.SubjectiveScore;
import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
import cn.com.qmth.stmms.biz.exam.service.ExamService;
import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
import cn.com.qmth.stmms.biz.exam.service.InspectHistoryService;
import cn.com.qmth.stmms.biz.exam.service.InspectedService;
import cn.com.qmth.stmms.biz.exam.service.MarkerService;
import cn.com.qmth.stmms.biz.exam.service.SelectiveGroupService;
import cn.com.qmth.stmms.biz.exam.service.SubjectiveScoreService;
import cn.com.qmth.stmms.biz.lock.LockService;
import cn.com.qmth.stmms.biz.mark.dao.ArbitrateHistoryDao;
import cn.com.qmth.stmms.biz.mark.dao.HeaderTagDao;
import cn.com.qmth.stmms.biz.mark.dao.HeaderTrackDao;
import cn.com.qmth.stmms.biz.mark.dao.MarkLibraryDao;
import cn.com.qmth.stmms.biz.mark.dao.MarkSpecialTagDao;
import cn.com.qmth.stmms.biz.mark.dao.MarkTrackDao;
import cn.com.qmth.stmms.biz.mark.dao.ProblemHistoryDao;
import cn.com.qmth.stmms.biz.mark.dao.RejectHistoryDao;
import cn.com.qmth.stmms.biz.mark.dao.TrialLibraryDao;
import cn.com.qmth.stmms.biz.mark.dao.TrialTagDao;
import cn.com.qmth.stmms.biz.mark.dao.TrialTrackDao;
import cn.com.qmth.stmms.biz.mark.model.ArbitrateHistory;
import cn.com.qmth.stmms.biz.mark.model.HeaderTrack;
import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
import cn.com.qmth.stmms.biz.mark.model.MarkResult;
import cn.com.qmth.stmms.biz.mark.model.MarkStepDTO;
import cn.com.qmth.stmms.biz.mark.model.MarkTrack;
import cn.com.qmth.stmms.biz.mark.model.ProblemHistory;
import cn.com.qmth.stmms.biz.mark.model.RejectHistory;
import cn.com.qmth.stmms.biz.mark.model.SubmitResult;
import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
import cn.com.qmth.stmms.biz.mark.service.MarkService;
import cn.com.qmth.stmms.biz.report.service.ReportSubjectService;
import cn.com.qmth.stmms.biz.utils.ScoreItem;
import cn.com.qmth.stmms.biz.utils.TaskLock;
import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
import cn.com.qmth.stmms.common.enums.HistoryStatus;
import cn.com.qmth.stmms.common.enums.LibraryStatus;
import cn.com.qmth.stmms.common.enums.LockType;
import cn.com.qmth.stmms.common.enums.MarkStatus;
import cn.com.qmth.stmms.common.enums.ObjectiveStatus;
import cn.com.qmth.stmms.common.enums.ScorePolicy;
import cn.com.qmth.stmms.common.enums.SubjectiveStatus;
import cn.com.qmth.stmms.common.enums.ThirdPolicy;
import cn.com.qmth.stmms.common.utils.BigDecimalUtils;

/**
 * 与评卷相关的所有修改操作(非传播性的新增操作除外)，全部汇总到这里进行集中控制
 *
 * @author luoshi
 */
@Service("markService")
public class MarkServiceImpl implements MarkService {

    protected static final Logger log = LoggerFactory.getLogger(MarkServiceImpl.class);

    public static final int UN_SELECTIVE_SCORE = -1;

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private ExamSubjectService subjectService;

    @Autowired
    private MarkGroupDao groupDao;

    @Autowired
    private MarkGroupStudentDao groupStudentDao;

    @Autowired
    private SubjectiveScoreDao scoreDao;

    @Autowired
    private ExamQuestionService questionService;

    @Autowired
    private MarkerDao markerDao;

    @Autowired
    private MarkLibraryDao libraryDao;

    @Autowired
    private ArbitrateHistoryDao arbitrateDao;

    @Autowired
    private MarkTrackDao trackDao;

    @Autowired
    private MarkSpecialTagDao specialTagDao;

    @Autowired
    private TrialLibraryDao trialLibraryDao;

    @Autowired
    private TrialTrackDao trialTrackDao;

    @Autowired
    private TrialTagDao trialTagDao;

    @Autowired
    private HeaderTagDao headerTagDao;

    @Autowired
    private HeaderTrackDao headerTrackDao;

    @Autowired
    private ProblemHistoryDao problemHistoryDao;

    @Autowired
    private ExamService examService;

    @Autowired
    private MarkerService markerService;

    @Autowired
    private SelectiveGroupService selectiveGroupService;

    @Autowired
    private SelectiveStudentDao selectiveStudentDao;

    @Autowired
    private InspectHistoryService inspectHistoryService;

    @Autowired
    private RejectHistoryDao rejectHistoryDao;

    @Autowired
    private SubjectiveScoreService scoreService;

    @Autowired
    private LockService lockService;

    private Map<Integer, Long> markerLastUpdateTime = new ConcurrentHashMap<>();

    @Autowired
    private InspectedService inspectedService;

    @Autowired
    private ReportSubjectService reportSubjectService;

    /**
     * 某个评卷分组已申请的评卷任务数量
     *
     * @param group
     *            - 评卷分组
     * @return int
     */
    @Override
    public int applyCount(MarkGroup group) {
        TaskLock taskLock = getTaskLock(group);
        int count = 0;
        if (taskLock != null) {
            count = taskLock.count();
        }
        return count;
    }

    /**
     * 某个评卷员已申请的评卷任务数量
     *
     * @param marker
     *            - 评卷员
     * @return int
     */
    @Override
    public int applyCount(Marker marker) {
        int count = 0;
        MarkGroup group = marker.getGroup();
        if (group == null) {
            group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
        }
        TaskLock taskLock = getTaskLock(group);
        if (taskLock != null) {
            count = taskLock.count(marker.getId());
        }
        return count;
    }

    private TaskLock getTaskLock(MarkGroup group) {
        if (group.getStatus() == MarkStatus.FORMAL) {
            return TaskLockUtil.getFormalTask(getGroupKey(group));

        } else if (group.getStatus() == MarkStatus.TRIAL) {
            return TaskLockUtil.getTrialTask(getGroupKey(group));
        }
        return null;
    }

    /**
     * 某个评卷员已完成的评卷任务数量
     *
     * @param marker
     *            - 评卷员
     * @return long
     */
    @Override
    public long markedCount(Marker marker) {
        MarkGroup group = marker.getGroup();
        if (group == null) {
            group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
        }
        if (group != null) {
            if (group.getStatus() == MarkStatus.TRIAL) {
                return trialLibraryDao.countByMarkerId(marker.getId());
            } else {
                return libraryDao.countByMarkerAndStatus(marker.getId(), LibraryStatus.MARKED,
                        LibraryStatus.ARBITRATED, LibraryStatus.INSPECTED);
            }
        }
        return 0;
    }

    /**
     * 释放某个评卷分组的锁定任务
     *
     * @param group
     *            - 评卷分组
     */
    @Override
    public void releaseByGroup(MarkGroup group) {
        TaskLock taskLock = getTaskLock(group);
        taskLock.clear();
    }

    /**
     * 重置某个评卷分组的所有评卷任务
     *
     * @param group
     *            - 评卷分组
     */
    @Override
    @Transactional
    public void resetByGroup(MarkGroup group) {
        resetGroup(group);
    }

    /**
     * 删除某个评卷分组
     *
     * @param group
     *            - 评卷分组
     */
    @Override
    @Transactional
    public void deleteGroup(MarkGroup group) {
        // 试评相关数据
        trialTrackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        trialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
        trialLibraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        // 正评相关数据
        trackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
        headerTrackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        headerTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
        arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        problemHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        rejectHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        // 评卷员数据
        markerDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        // 小题数据
        questionService.resetByExamIdAndSubjectCodeAndObjectiveAndGroupNumber(group.getExamId(),
                group.getSubjectCode(), false, group.getNumber());
        // 考生分组状态与得分明细
        groupStudentDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        scoreDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        selectiveStudentDao.deleteByExamIdAndSubjectCode(group.getExamId(), group.getSubjectCode());
        // 删除分组
        releaseByGroup(group);
        groupDao.delete(group);
        // 未分组的题目
        long unGroupQuestionCount = questionService.countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(
                group.getExamId(), group.getSubjectCode(), false);
        // 考生整体状态与总分更新
        long groupCount = groupDao.countByExamIdAndSubjectCode(group.getExamId(), group.getSubjectCode());
        if (groupCount == 0) {
            studentService.resetTrial(group.getExamId(), group.getSubjectCode(), false);
        }
        if (groupCount == 0 || unGroupQuestionCount > 0) {
            studentService.updateSubjectiveStatusAndScore(group.getExamId(), group.getSubjectCode(),
                    SubjectiveStatus.UNMARK, 0, null);
            inspectHistoryService.deleteByExamIdAndSubjectCode(group.getExamId(), group.getSubjectCode());
        } else {
            List<Integer> studentList = studentService.findIdByExamIdAndSubjectCodeAndSubjectiveStatus(
                    group.getExamId(), group.getSubjectCode(), SubjectiveStatus.UNMARK, SubjectiveStatus.MARKED);
            for (Integer studentId : studentList) {
                checkStudentSubjective(studentId, groupCount, unGroupQuestionCount);
            }
        }
    }

    /**
     * 修改某个评卷分组给分步骤，并重置评卷任务
     *
     * @param group
     *            - 评卷分组
     */
    @Override
    @Transactional
    public void updateGroup(MarkGroup group, List<ExamQuestion> questionList, ScorePolicy policy, ThirdPolicy third,
            boolean selective) {
        List<ExamQuestion> old = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(group.getExamId(),
                group.getSubjectCode(), false, group.getNumber());
        for (ExamQuestion question : old) {
            question.setGroupNumber(null);
            questionService.saveAndFlush(question);
        }
        BigDecimal totalScore = BigDecimal.ZERO;
        for (ExamQuestion question : questionList) {
            totalScore = totalScore.add(BigDecimal.valueOf(question.getTotalScore()));
            question.setGroupNumber(group.getNumber());
            questionService.saveAndFlush(question);
        }
        groupDao.updateTotalScore(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                totalScore.doubleValue());
        groupDao.updateScorePolicy(group.getExamId(), group.getSubjectCode(), group.getNumber(), policy);
        groupDao.updateThirdPolicy(group.getExamId(), group.getSubjectCode(), group.getNumber(), third);
        groupDao.updateSelective(group.getExamId(), group.getSubjectCode(), group.getNumber(), selective);

        resetGroup(group);
        if (group.getStatus() == MarkStatus.FORMAL) {
            libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
        }
        if (group.getStatus() == MarkStatus.TRIAL) {
            trialLibraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
        }
        updateLibraryCount(group);
        groupDao.updateBuildTime(group.getExamId(), group.getSubjectCode(), group.getNumber(), null);
    }

    /**
     * 评卷员申请领取某个正式评卷任务
     *
     * @param library
     *            - 正评任务
     * @param marker
     *            - 评卷员
     */
    @Override
    public boolean applyLibrary(MarkLibrary library, Marker marker) {
        // 查询待领取任务时，已经做了多评同一studentId互斥处理
        TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
        boolean lock = taskLock.add(library.getStudentId(), library.getTaskNumber(), marker.getId());
        // 上锁失败直接返回
        if (!lock) {
            return false;
        }
        // 重复校验任务状态
        if (libraryDao.countByIdAndStatus(library.getId(), library.getStatus()) == 1) {
            return true;
        } else {
            taskLock.remove(library.getStudentId(), library.getTaskNumber(), marker.getId());
            return false;
        }
    }

    /**
     * 评卷员申请领取某个试评评卷任务
     *
     * @param library
     *            - 试评任务
     * @param marker
     *            - 评卷员
     * @return boolean
     */
    @Override
    public boolean applyLibrary(TrialLibrary library, Marker marker) {
        TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
        return taskLock.add(library.getStudentId(), 1, marker.getId());
    }

    /**
     * 评卷员是否已领取了某个正式评卷任务
     *
     * @param library
     *            - 正评任务
     * @param marker
     *            - 评卷员
     * @return boolean
     */
    @Override
    public boolean hasApplied(MarkLibrary library, Marker marker) {
        TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
        return taskLock.exist(library.getStudentId(), library.getTaskNumber(), marker.getId());
    }

    /**
     * 评卷员是否已领取了某个试评评卷任务
     *
     * @param library
     *            - 试评任务
     * @param marker
     *            - 评卷员
     * @return boolean
     */
    @Override
    public boolean hasApplied(TrialLibrary library, Marker marker) {
        TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
        return taskLock.exist(library.getStudentId(), 1, marker.getId());
    }

    /**
     * 释放某个评卷员已完成的评卷任务
     *
     * @param result
     *            - 评卷结果
     * @param marker
     *            - 评卷员
     */
    @Override
    public void releaseTask(SubmitResult result, Marker marker) {
        if (result.getMarkLibrary() != null) {
            TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
            taskLock.remove(result.getMarkLibrary().getStudentId(), result.getMarkLibrary().getTaskNumber(),
                    marker.getId());
            taskLock.refresh(marker.getId());
        } else if (result.getTrialLibrary() != null) {
            TaskLock taskLock = TaskLockUtil.getTrialTask(getGroupKey(marker));
            taskLock.remove(result.getTrialLibrary().getStudentId(), 1, marker.getId());
        }
    }

    /**
     * 释放某个评卷员的所有锁定任务
     *
     * @param marker
     *            - 评卷员
     */
    @Override
    public void releaseByMarker(Marker marker) {
        TaskLock taskLock = TaskLockUtil.getFormalTask(getGroupKey(marker));
        taskLock.clear(marker.getId());
        TaskLock lock = TaskLockUtil.getTrialTask(getGroupKey(marker));
        lock.clear(marker.getId());
    }

    /**
     * 重置某个评卷员
     *
     * @param marker
     *            - 评卷员
     */
    @Override
    @Transactional
    public void resetMarker(Marker marker) {
        MarkGroup group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
        if (group == null || !markerDao.exists(marker.getId())) {
            return;
        }
        if (group.getStatus() == MarkStatus.FORMAL) {
            // 仲裁和等待仲裁的任务不被重置
            // 只选取评卷完成状态的记录重新检查
            // 遍历相关评卷任务的模式
            List<MarkLibrary> list = libraryDao.findByMarkerIdAndStatusNotIn(marker.getId(), LibraryStatus.ARBITRATED,
                    LibraryStatus.WAIT_ARBITRATE, LibraryStatus.PROBLEM);
            for (MarkLibrary library : list) {
                lockService.waitlock(LockType.STUDENT, library.getStudentId());
                try {
                    trackDao.deleteByLibraryId(library.getId());
                    specialTagDao.deleteByLibraryId(library.getId());
                    libraryDao.resetById(library.getId(), null, null, null, null, LibraryStatus.WAITING,
                            library.getStatus());
                    updateStudentGroupStatus(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
                            library.getGroupNumber(), SubjectiveStatus.UNMARK);
                    studentService.updateSubjectiveStatusAndScore(library.getStudentId(), SubjectiveStatus.UNMARK, 0,
                            null);
                    inspectedService.clearByStudent(library.getStudentId());
                } finally {
                    lockService.unlock(LockType.STUDENT, library.getStudentId());
                }
            }
            markerDao.resetById(marker.getId());
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            List<TrialLibrary> list = trialLibraryDao.findByMarkerId(marker.getId());
            for (TrialLibrary library : list) {
                lockService.waitlock(LockType.STUDENT, library.getStudentId());
                updateStudentGroupStatus(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
                        library.getGroupNumber(), SubjectiveStatus.UNMARK);
                studentService.updateSubjectiveStatusAndScore(library.getStudentId(), SubjectiveStatus.UNMARK, 0, null);
                inspectedService.clearByStudent(library.getStudentId());
                lockService.unlock(LockType.STUDENT, library.getStudentId());
            }
            trialTagDao.deleteByMarkerId(marker.getId());
            trialTrackDao.deleteByMarkerId(marker.getId());
            trialLibraryDao.resetByMarkerId(marker.getId());
        }
        updateMarkedCount(group);
        releaseByMarker(marker);
//        inspectHistoryService.deleteByExamIdAndSubjectCode(marker.getExamId(), marker.getSubjectCode());
    }

    /**
     * 根据考生删除评卷任务
     *
     * @param student
     *            - 考生
     */
    @Override
    @Transactional
    public void deleteByStudent(ExamStudent student) {
        // 正评相关数据
        trackDao.deleteByStudentId(student.getId());
        specialTagDao.deleteByStudentId(student.getId());
        headerTagDao.deleteByStudentId(student.getId());
        headerTrackDao.deleteByPkStudentId(student.getId());
        arbitrateDao.deleteByStudentId(student.getId());
        problemHistoryDao.deleteByStudentId(student.getId());
        libraryDao.deleteByStudentId(student.getId());
        // 试评相关数据
        trialTrackDao.deleteByStudentId(student.getId());
        trialTagDao.deleteByStudentId(student.getId());
        trialLibraryDao.deleteByStudentId(student.getId());
        // 主观状态与得分明细
        groupStudentDao.deleteByStudentId(student.getId());
        scoreDao.deleteByStudentId(student.getId());
        selectiveStudentDao.deleteByStudentId(student.getId());
        updateAllCount(student.getExamId(), student.getSubjectCode());
        // 复核记录
        inspectedService.clearByStudent(student.getId());
        // 打回记录
        rejectHistoryDao.deleteByStudentId(student.getId());
    }

    /**
     * 评卷员提交评卷任务
     *
     * @param result
     *            - 评卷结果
     * @param marker
     *            - 评卷员
     * @return boolean
     */
    @Override
    @Transactional
    public SubmitResult submitTask(MarkResult result, Marker marker) {
        // 判断评卷分组是否存在/评卷是否结束
        MarkGroup group = groupDao.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
        if (group == null || group.getStatus() == MarkStatus.FINISH
                || !group.getStatus().toString().equals(result.getStatusValue())) {
            return SubmitResult.faile();
        }
        Date now = new Date();
        // 根据评卷状态选择读取不同的评卷任务
        if (group.getStatus() == MarkStatus.FORMAL) {
            MarkLibrary library = libraryDao.findOne(result.getLibraryId());
            if (library != null && library.getExamId().equals(group.getExamId())
                    && library.getSubjectCode().equals(group.getSubjectCode())
                    && library.getGroupNumber().equals(group.getNumber())) {
                // 问题卷
                if (result.isProblem()) {
                    // 状态更新
                    if (libraryDao.updateProblemResult(library.getId(), LibraryStatus.PROBLEM, marker.getId(), now,
                            result.getSpent(), 0d, null, LibraryStatus.WAITING, LibraryStatus.MARKED,
                            LibraryStatus.INSPECTED, LibraryStatus.REJECTED) != 0) {
                        saveProblemHistory(result, library, marker);
                        updateMarkedCount(group);
                        // 未评完
                        resetStudentGroup(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
                                library.getGroupNumber());
                        trackDao.deleteByLibraryId(library.getId());
                        specialTagDao.deleteByLibraryId(library.getId());
                        return SubmitResult.success(library);
                    }
                }
                if (result.getMarkerScore() <= group.getTotalScore()) {
                    if (submitLibrary(library, marker, group, result)) {
                        updateMarkedCount(group);
                        return SubmitResult.success(library);
                    }
                }
            }
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            TrialLibrary library = trialLibraryDao.findOne(result.getLibraryId());
            if (library != null && library.getExamId().equals(group.getExamId())
                    && library.getSubjectCode().equals(group.getSubjectCode())
                    && library.getGroupNumber().equals(group.getNumber())
                    && result.getMarkerScore() <= group.getTotalScore()) {
                if ((library.getMarkerId() == null && !hasApplied(library, marker))
                        || (library.getMarkerId() != null && !library.getMarkerId().equals(marker.getId()))) {
                    return SubmitResult.faile();
                }
                library.setMarkerId(marker.getId());
                library.setMarkerTime(now);
                // 未选做
                if (result.isUnselective() && group.isSelective()) {
                    result.setMarkerScore(UN_SELECTIVE_SCORE);
                    result.setScoreList(new Double[0]);
                    trialTrackDao.deleteByLibraryIdAndMarkerId(library.getId(), library.getMarkerId());
                    trialTagDao.deleteByLibraryIdAndMarkerId(library.getId(), library.getMarkerId());
                }
                library.setMarkerScore(result.getMarkerScore());
                library.setMarkerScoreList(result.getScoreList());
                if (result.getTrackList() != null && !result.isUnselective()) {
                    trialTrackDao.deleteByLibraryIdAndMarkerId(library.getId(), library.getMarkerId());
                    trialTrackDao.save(result.getTrackList(library));
                }
                library = trialLibraryDao.save(library);
                if (result.getSpecialTagList() != null && !result.isUnselective()) {
                    trialTagDao.deleteByLibraryIdAndMarkerId(library.getId(), library.getMarkerId());
                    trialTagDao.save(result.getTagList(library));
                }
                checkStudentTrial(library, group);
                updateMarkedCount(group);
                inspectedService.cancelByStudent(library.getStudentId());
                return SubmitResult.success(library);
            }
        }
        return SubmitResult.faile();
    }

    private void saveProblemHistory(MarkResult result, MarkLibrary library, Marker marker) {
        ProblemHistory history = problemHistoryDao.findByLibraryIdAndStatus(library.getId(), HistoryStatus.WAITING);
        if (history == null) {
            history = new ProblemHistory();
        }
        history.setCreateTime(new Date());
        history.setExamId(library.getExamId());
        history.setExamNumber(library.getExamNumber());
        history.setSecretNumber(library.getSecretNumber());
        history.setStudentId(library.getStudentId());
        history.setLibraryId(library.getId());
        history.setSubjectCode(library.getSubjectCode());
        history.setGroupNumber(library.getGroupNumber());
        history.setProblemId(result.getProblemTypeId());
        history.setStatus(HistoryStatus.WAITING);
        history.setMarkerId(marker.getId());
        problemHistoryDao.save(history);
    }

    /**
     * 评卷员提交某个正评任务
     *
     * @param library
     *            - 正评任务
     * @param marker
     *            - 评卷员
     * @param group
     *            - 评卷分组
     * @param result
     *            - 评卷结果
     */
    private boolean submitLibrary(MarkLibrary library, Marker marker, MarkGroup group, MarkResult result) {
        // 非本人领取的待评任务
        if ((library.getStatus() == LibraryStatus.WAITING || library.getStatus() == LibraryStatus.REJECTED)
                && !hasApplied(library, marker)) {
            return false;
        }
        // 非本人的回评任务
        if ((library.getStatus() == LibraryStatus.MARKED || library.getStatus() == LibraryStatus.INSPECTED)
                && !library.getMarkerId().equals(marker.getId())) {
            return false;
        }
        // 是否多评情况下已处理过该考生评卷任务
        if (libraryDao
                .countByStudentIdAndMarkerIdAndIdNotEqual(library.getStudentId(), marker.getId(), library.getId()) > 0) {
            return false;
        }
        // 未选做
        if (result.isUnselective() && group.isSelective()) {
            result.setMarkerScore(UN_SELECTIVE_SCORE);
            result.setScoreList(new Double[0]);
            trackDao.deleteByLibraryId(library.getId());
            specialTagDao.deleteByLibraryId(library.getId());
        }
        // 尝试提交评卷结果
        Date now = new Date();
        if (libraryDao.updateMarkerResult(library.getId(), LibraryStatus.MARKED, marker.getId(),
                result.getMarkerScore(), result.getScoreList(), now, result.getSpent(), null, null, null,
                LibraryStatus.WAITING, LibraryStatus.MARKED, LibraryStatus.INSPECTED, LibraryStatus.REJECTED) == 0) {
            // 条件不符更新失败，直接返回
            return false;
        }
        // 取消复核
        inspectedService.cancelByStudent(library.getStudentId());
        // 保存阅卷轨迹
        int unansweredCount = 0;
        if (result.getTrackList() != null && !result.isUnselective()) {
            // trackDao.deleteByLibraryId(library.getId());
            List<MarkTrack> oldTracks = trackDao.findByPkLibraryId(library.getId());
            trackDao.delete(oldTracks);
            List<MarkTrack> tracks = result.getTrackList(library, marker);
            for (MarkTrack markTrack : tracks) {
                trackDao.saveAndFlush(markTrack);
                if (markTrack.isUnanswered()) {
                    unansweredCount++;
                }
            }
            libraryDao.updateUnansweredCount(library.getId(), unansweredCount);
        }
        // 保存特殊标记
        if (result.getSpecialTagList() != null && !result.isUnselective()) {
            specialTagDao.deleteByLibraryId(library.getId());
            specialTagDao.save(result.getSpecialTagList(library, marker));
        }
        // 判断多评模式下是否需要仲裁
        ArbitrateHistory history = null;
        if (group.getArbitrateThreshold() != null && group.getArbitrateThreshold() > 0) {
            // 多评模式
            List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumberAndStatus(library.getStudentId(),
                    library.getGroupNumber(), LibraryStatus.MARKED, LibraryStatus.INSPECTED);
            for (MarkLibrary other : list) {
                // 本评卷任务或组长已打分，则跳过该任务
                if (other.getId().equals(library.getId()) || other.getHeaderScore() != null) {
                    continue;
                }
                // 均为未选做，跳过该任务
                if (other.getMarkerScore() == UN_SELECTIVE_SCORE && result.getMarkerScore() == UN_SELECTIVE_SCORE) {
                    continue;
                }
                // 其中一个有分另一个未选做 直接进入仲裁
                if ((other.getMarkerScore() != UN_SELECTIVE_SCORE && result.getMarkerScore() == UN_SELECTIVE_SCORE)
                        || (other.getMarkerScore() == UN_SELECTIVE_SCORE && result.getMarkerScore() != UN_SELECTIVE_SCORE)) {
                    history = buildArbitrateHistory(library, now);
                    break;
                }
                // 分差超过阀值
                if (Math.abs(other.getMarkerScore() - result.getMarkerScore()) > group.getArbitrateThreshold()) {
                    // 开启三评
                    if (group.getThirdPolicy().equals(ThirdPolicy.LOW_DIFF_HIGH_AVG)) {
                        if (libraryDao.countByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber()) == 2) {
                            buildThirdLibrary(library, group);
                        } else {
                            // 两两比较，触发仲裁
                            List<MarkLibrary> libraries = libraryDao.findByStudentIdAndGroupNumber(
                                    library.getStudentId(), library.getGroupNumber());
                            history = buildArbitrateHistory(libraries, group.getArbitrateThreshold(), now);
                        }
                    } else {
                        // 未开启三评，触发仲裁
                        history = buildArbitrateHistory(library, now);
                        break;
                    }
                }
            }
        }
        if (history != null) {
            // 保存仲裁记录
            arbitrateDao.save(history);
            // 触发仲裁后续状态更新
            libraryDao.updateByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber(),
                    LibraryStatus.WAIT_ARBITRATE);
            // 未评完
            resetStudentGroup(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
                    library.getGroupNumber());
        } else {
            // 判断当前分组是否已完成评卷
            checkStudentGroup(library.getStudentId(), group);
        }
        return true;
    }

    private ArbitrateHistory buildArbitrateHistory(List<MarkLibrary> list, Double arbitrateThreshold, Date now) {
        for (MarkLibrary library : list) {
            if (library.getStatus() != LibraryStatus.MARKED && library.getStatus() != LibraryStatus.ARBITRATED
                    && library.getStatus() != LibraryStatus.INSPECTED) {
                return null;
            }
        }
        int count = 0;
        for (int i = 0; i < list.size(); i++) {
            int next = i + 1;
            if (next == list.size()) {
                next = 0;
            }
            if (Math.abs(list.get(i).getMarkerScore() - list.get(next).getMarkerScore()) > arbitrateThreshold) {
                count++;
            }
        }
        if (count == list.size()) {
            return buildArbitrateHistory(list.get(0), now);
        }
        return null;
    }

    private ArbitrateHistory buildArbitrateHistory(MarkLibrary library, Date now) {
        ArbitrateHistory history = new ArbitrateHistory();
        history.setExamId(library.getExamId());
        history.setSubjectCode(library.getSubjectCode());
        history.setGroupNumber(library.getGroupNumber());
        history.setStudentId(library.getStudentId());
        history.setExamNumber(library.getExamNumber());
        history.setSecretNumber(library.getSecretNumber());
        history.setStatus(HistoryStatus.WAITING);
        history.setCreateTime(now);
        return history;
    }

    private void buildThirdLibrary(MarkLibrary markLibrary, MarkGroup group) {
        MarkLibrary library = new MarkLibrary();
        library.setExamId(markLibrary.getExamId());
        library.setSubjectCode(markLibrary.getSubjectCode());
        library.setGroupNumber(group.getNumber());
        library.setStudentId(markLibrary.getStudentId());
        library.setExamNumber(markLibrary.getExamNumber());
        library.setSecretNumber(markLibrary.getSecretNumber());
        library.setTaskNumber(3);
        library.setStatus(LibraryStatus.WAITING);
        libraryDao.save(library);
        updateLibraryCount(group);
    }

    /**
     * 管理员/组长打回某个评卷任务<br>
     * isRest为true时重置该评卷任务
     *
     * @param library
     *            - 正评任务
     */
    @Override
    @Transactional
    public boolean rejectLibrary(MarkLibrary library, Integer userId, String reason, boolean isRest) {
        MarkGroup group = groupDao.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
        if (group.getStatus() == MarkStatus.FINISH) {
            return false;
        }
        RejectHistory history = new RejectHistory(library);
        history.setUserId(userId);
        history.setReason(reason);
        Date now = new Date();
        if (libraryDao.resetById(library.getId(), null, reason, userId, now, isRest ? LibraryStatus.WAITING
                : LibraryStatus.REJECTED, LibraryStatus.MARKED, LibraryStatus.PROBLEM, LibraryStatus.INSPECTED) > 0) {
            if (!isRest) {
                markerService.updateRejectCountById(library.getMarkerId());
                rejectHistoryDao.save(history);
            }
            trackDao.deleteByLibraryId(library.getId());
            specialTagDao.deleteByLibraryId(library.getId());
            scoreDao.updateRejected(library.getStudentId(), library.getGroupNumber(), true);
            resetStudentGroup(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
                    library.getGroupNumber());
            problemHistoryDao.resetByLibraryId(library.getId(), HistoryStatus.WAITING, userId, HistoryStatus.BACK, now);
            // 开启三评时，打回1，2任务则删除第3条任务
            long count = libraryDao.countByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber());
            if (library.getTaskNumber() != 3 && count == 3) {
                MarkLibrary third = libraryDao.findByStudentIdAndGroupNumberAndTaskNumber(library.getStudentId(),
                        library.getGroupNumber(), 3);
                trackDao.deleteByLibraryId(third.getId());
                specialTagDao.deleteByLibraryId(third.getId());
                problemHistoryDao.deleteByLibraryId(third.getId());
                libraryDao.delete(third);
                updateLibraryCount(group);
            }
            updateMarkedCount(group);
            inspectedService.cancelByStudent(library.getStudentId());
            return true;
        } else {
            return false;
        }
    }

    /**
     * 管理员/组长重置某个试评任务
     *
     * @param library
     *            - 试评任务
     */
    @Override
    @Transactional
    public boolean resetLibrary(TrialLibrary library) {
        if (library != null) {
            trialLibraryDao.resetById(library.getId());
            trialTrackDao.deleteByLibraryId(library.getId());
            trialTagDao.deleteByLibraryId(library.getId());
            return true;
        } else {
            return false;
        }
    }

    /**
     * 管理员/组长处理仲裁卷
     *
     * @param history
     *            - 仲裁卷
     */
    @Override
    @Transactional
    public void processArbitrate(MarkResult result, Integer userId) {
        ArbitrateHistory history = arbitrateDao.findOne(result.getLibraryId());
        history.setUserId(userId);
        history.setTotalScore(result.isUnselective() ? UN_SELECTIVE_SCORE : result.getMarkerScore());
        history.setScoreList(result.isUnselective() ? null : result.getScoreList());
        history.setStatus(HistoryStatus.MARKED);
        history.setUpdateTime(new Date());
        if (arbitrateDao.exists(history.getId())) {
            // 保存阅卷轨迹
            int unansweredCount = 0;
            if (result.getTrackList() != null && !result.isUnselective()) {
                headerTrackDao.deleteByPkStudentIdAndGroupNumber(history.getStudentId(), history.getGroupNumber());
                List<HeaderTrack> tracks = result.getTrackList(history);
                for (HeaderTrack t : tracks) {
                    headerTrackDao.saveAndFlush(t);
                    if (t.isUnanswered()) {
                        unansweredCount++;
                    }
                }
                history.setUnansweredCount(unansweredCount);
            }
            // 保存特殊标记
            if (result.getSpecialTagList() != null && !result.isUnselective()) {
                headerTagDao.deleteByStudentIdAndGroupNumber(history.getStudentId(), history.getGroupNumber());
                headerTagDao.save(result.getHeaderTagList(history));
            }
            arbitrateDao.saveAndFlush(history);
            libraryDao.updateHeaderResult(history.getStudentId(), history.getGroupNumber(), history.getUserId(),
                    history.getTotalScore(), history.getScoreList(), history.getUpdateTime(), LibraryStatus.ARBITRATED);
            updateMarkedCount(history.getExamId(), history.getSubjectCode(), history.getGroupNumber());
            inspectedService.cancelByStudent(history.getStudentId());
            checkStudentGroup(history.getStudentId(),
                    groupDao.findOne(history.getExamId(), history.getSubjectCode(), history.getGroupNumber()));
        }
    }

    // /**
    // * 对某个考生某个科目进行主观题统分
    // *
    // * @param student - 考生
    // * @param groups - 所有评卷分组
    // */
    // @Override
    // @Transactional
    // public void scoreCalculate(ExamStudent student, List<MarkGroup> groups) {
    // if (student == null || groups == null || groups.isEmpty()) {
    // return;
    // }
    // List<ScoreItem> scoreList = new ArrayList<>();
    // double totalScore = 0.0;
    // // 循环所有评卷分组
    // for (MarkGroup group : groups) {
    // if (group.getStatus().equals(MarkStatus.TRIAL)) {
    // return;
    // }
    // if (calculateGroup(group, student.getId())) {
    // totalScore += group.getMarkScore();
    // scoreList.addAll(group.getMarkScoreDetail());
    // } else {
    // // 未评完直接返回
    // return;
    // }
    // }
    // // 全部评完，更新考生主观题得分
    // student.setSubjectiveScore(totalScore);
    // student.setScoreList(scoreList, false);
    // studentService
    // .updateSubjectiveScore(student.getId(), student.getSubjectiveScore(),
    // student.getSubjectiveScoreList());
    // }

    private void scoreCalculate(Integer studentId) {
        List<ScoreItem> scoreList = new ArrayList<>();
        Map<Integer, List<SubjectiveScore>> mainScoreMap = new HashMap<Integer, List<SubjectiveScore>>();
        Map<Integer, Double> scoreMap = new HashMap<Integer, Double>();
        Integer examId = null;
        String subjectCode = null;
        // 循环所有主观得分明细
        List<SubjectiveScore> list = scoreService.findByStudentId(studentId);
        for (SubjectiveScore ss : list) {
            List<SubjectiveScore> mainScoreList = mainScoreMap.get(ss.getMainNumber());
            if (mainScoreList == null) {
                mainScoreList = new ArrayList<SubjectiveScore>();
            }
            examId = ss.getExamId();
            subjectCode = ss.getSubjectCode();
            mainScoreList.add(ss);
            mainScoreMap.put(ss.getMainNumber(), mainScoreList);
            scoreList.add(new ScoreItem(ss));
        }
        // 计算大题分
        for (Integer mainNumber : mainScoreMap.keySet()) {
            List<SubjectiveScore> mainScoreList = mainScoreMap.get(mainNumber);
            BigDecimal mainScore = BigDecimal.ZERO;
            for (SubjectiveScore subjectiveScore : mainScoreList) {
                if (subjectiveScore.getScore() != UN_SELECTIVE_SCORE) {
                    mainScore = mainScore.add(BigDecimal.valueOf(subjectiveScore.getScore()));
                    subjectiveScore.setUncalculate(false);
                } else {
                    mainScore = new BigDecimal(UN_SELECTIVE_SCORE);
                    subjectiveScore.setUncalculate(true);
                    break;
                }
            }
            for (SubjectiveScore subjectiveScore : mainScoreList) {
                subjectiveScore.setMainScore(mainScore.doubleValue());
            }
            scoreMap.put(mainNumber, mainScore.doubleValue());
        }
        // 计算选做题分数
        BigDecimal totalScore = BigDecimal.ZERO;
        boolean lessSelective = false;
        boolean mutiSelective = false;
        boolean notSelective = false;
        Map<Integer, List<SelectiveGroup>> indexMap = selectiveGroupService.findGroupByExamIdAndSubjectCode(examId,
                subjectCode);
        for (Integer index : indexMap.keySet()) {
            // 循环选做题组
            List<SelectiveGroup> indexGroup = indexMap.get(index);
            Integer selectiveCount = 0;
            ScorePolicy policy = null;
            Map<Integer, List<SelectiveGroup>> partMap = new HashMap<Integer, List<SelectiveGroup>>();
            for (SelectiveGroup selectiveGroup : indexGroup) {
                List<SelectiveGroup> partGroup = partMap.get(selectiveGroup.getSelectivePart());
                if (partGroup == null) {
                    partGroup = new ArrayList<SelectiveGroup>();
                }
                partGroup.add(selectiveGroup);
                partMap.put(selectiveGroup.getSelectivePart(), partGroup);
                if (selectiveCount == 0) {
                    selectiveCount = selectiveGroup.getSelectiveCount();
                }
                if (policy == null) {
                    policy = selectiveGroup.getScorePolicy();
                }
            }
            List<Double> partScoreList = new ArrayList<Double>();
            Map<Integer, Double> partScoreMap = new HashMap<Integer, Double>();
            // 循环选做分区
            for (Integer part : partMap.keySet()) {
                List<SelectiveGroup> partList = partMap.get(part);
                BigDecimal partScore = BigDecimal.ZERO;
                boolean unselectivePart = false;
                for (SelectiveGroup selectiveGroup : partList) {
                    double mainScore = scoreMap.get(selectiveGroup.getMainNumber());
                    scoreMap.remove(selectiveGroup.getMainNumber());
                    if (mainScore == UN_SELECTIVE_SCORE || unselectivePart) {
                        unselectivePart = true;
                    } else {
                        partScore = partScore.add(BigDecimal.valueOf(mainScore));
                    }
                }
                // 当前part未选做
                if (unselectivePart) {
                    for (SelectiveGroup selectiveGroup : partList) {
                        List<SubjectiveScore> mainScoreList = mainScoreMap.get(selectiveGroup.getMainNumber());
                        for (SubjectiveScore ss : mainScoreList) {
                            ss.setUncalculate(true);
                        }
                    }
                } else {
                    partScoreList.add(partScore.doubleValue());
                    partScoreMap.put(part, partScore.doubleValue());
                }
            }
            Collections.sort(partScoreList);
            if (policy == null || ScorePolicy.MAX.equals(policy)) {
                Collections.reverse(partScoreList);
            }
            // 计算一个选做题分组得分
            for (int i = 0; i < partScoreList.size(); i++) {
                if (i < selectiveCount && partScoreList.get(i) != UN_SELECTIVE_SCORE) {
                    totalScore = totalScore.add(BigDecimal.valueOf(partScoreList.get(i)));
                    // 删除已经合分的part
                    Iterator<Entry<Integer, Double>> it = partScoreMap.entrySet().iterator();
                    while (it.hasNext()) {
                        Entry<Integer, Double> entry = it.next();
                        if (entry.getValue().doubleValue() == partScoreList.get(i).doubleValue()) {
                            it.remove();
                            break;
                        }

                    }
                }
            }
            for (Integer part : partScoreMap.keySet()) {
                List<SelectiveGroup> selectiveGroups = partMap.get(part);
                for (SelectiveGroup group : selectiveGroups) {
                    List<SubjectiveScore> mainScoreList = mainScoreMap.get(group.getMainNumber());
                    for (SubjectiveScore ss : mainScoreList) {
                        ss.setUncalculate(true);
                    }
                }
            }
            if (partScoreList.size() > 0 && partScoreList.size() < selectiveCount) {
                lessSelective = true;
            }
            if (partScoreList.size() > 0 && partScoreList.size() > selectiveCount) {
                mutiSelective = true;
            }
            if (partScoreList.size() == 0) {
                notSelective = true;
            }
        }
        // 统一更新得分详情
        for (Integer mainNumber : mainScoreMap.keySet()) {
            List<SubjectiveScore> mainScoreList = mainScoreMap.get(mainNumber);
            for (SubjectiveScore ss : mainScoreList) {
                scoreDao.saveAndFlush(ss);
            }
        }
        // 更新考生多选做少选做
        SelectiveStudent selectiveStudent = new SelectiveStudent();
        selectiveStudent.setExamId(examId);
        selectiveStudent.setSubjectCode(subjectCode);
        selectiveStudent.setStudentId(studentId);
        selectiveStudent.setLessSelective(lessSelective);
        selectiveStudent.setMutiSelective(mutiSelective);
        selectiveStudent.setNotSelective(notSelective);
        selectiveStudentDao.saveAndFlush(selectiveStudent);
        // 计算非选做题总分
        for (Integer mainNumber : scoreMap.keySet()) {
            totalScore = totalScore.add(BigDecimal.valueOf(scoreMap.get(mainNumber)));
        }
        // 全部评完，更新考生主观题得分
        studentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.MARKED, totalScore.doubleValue(),
                ExamStudent.buildScoreList(scoreList));
    }

    private boolean calculateGroup(MarkGroup group, Integer studentId) {
        double score = 0;
        List<ScoreItem> detail = new ArrayList<>();
        int count = 0;
        // 未设置算分策略的情况下，默认取平均分
        ScorePolicy policy = group.getScorePolicy() != null ? group.getScorePolicy() : ScorePolicy.AVG;
        List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumber(studentId, group.getNumber());
        if (list.isEmpty()) {
            return false;
        }
        boolean selectiveAll = false;
        for (MarkLibrary library : list) {
            if (library.getStatus() != LibraryStatus.MARKED && library.getStatus() != LibraryStatus.ARBITRATED
                    && library.getStatus() != LibraryStatus.INSPECTED) {
                // 有非完成状态的评卷任务，直接返回
                return false;
            }
            double markerScore = library.getStatus() == LibraryStatus.ARBITRATED ? library.getHeaderScore() : library
                    .getMarkerScore();
            if (markerScore == UN_SELECTIVE_SCORE) {
                selectiveAll = true;
            }
        }
        if (selectiveAll) {
            group.setMarkScore(UN_SELECTIVE_SCORE);
            return true;
        }
        // 开启三评
        if (ThirdPolicy.LOW_DIFF_HIGH_AVG.equals(group.getThirdPolicy()) && list.size() == 3) {
            // Collections.sort(list,
            // Comparator.comparing(MarkLibrary::getMarkerScore));
            // 从小到大排序
            list.sort((o1, o2) -> {
                if (o1.getMarkerScore() - o2.getMarkerScore() > 0) {
                    return 1;
                }
                return -1;
            });
            Double score1 = list.get(0).getHeaderScore() != null ? list.get(0).getHeaderScore() : list.get(0)
                    .getMarkerScore();
            Double score2 = list.get(1).getHeaderScore() != null ? list.get(1).getHeaderScore() : list.get(1)
                    .getMarkerScore();
            Double score3 = list.get(2).getHeaderScore() != null ? list.get(2).getHeaderScore() : list.get(2)
                    .getMarkerScore();
            if ((score3 - score2) <= (score2 - score1)) {
                list.remove(0);
            } else {
                list.remove(2);
            }
            policy = ScorePolicy.AVG;
        }
        for (MarkLibrary library : list) {
            count++;
            Double current = library.getHeaderScore() != null ? library.getHeaderScore() : library.getMarkerScore();
            List<ScoreItem> scores = library.getScoreList();
            if (count == 1) {
                // 首份评卷任务，直接取总分与明细
                score = current;
                detail = scores;
            } else {
                switch (policy) {
                case AVG:
                    // 直接累加
                    score = BigDecimalUtils.add(score, current);
                    for (int i = 0; i < detail.size(); i++) {
                        try {
                            ScoreItem item = detail.get(i);
                            ScoreItem other = scores.get(i);
                            item.setScore(BigDecimalUtils.add(item.getScore(), other.getScore()));
                        } catch (Exception e) {
                            continue;
                        }
                    }
                    break;
                case MAX:
                    // 高分优先
                    if (current > score) {
                        score = current;
                        detail = scores;
                    }
                    break;
                case MIN:
                    // 低分优先
                    if (current < score) {
                        score = current;
                        detail = scores;
                    }
                    break;
                default:
                    break;
                }
            }
        }
        // 取平均分策略下，累计分数需要重新计算一次
        if (policy == ScorePolicy.AVG && count > 1) {
            score = BigDecimalUtils.div(score, count);
            for (ScoreItem item : detail) {
                item.setScore(BigDecimalUtils.div(item.getScore(), count));
            }
        }
        group.setMarkScore(score);
        group.setMarkScoreDetail(detail);
        return true;
    }

    private void updateMarkedCount(Integer examId, String subjectCode, Integer groupNumber) {
        MarkGroup group = groupDao.findOne(examId, subjectCode, groupNumber);
        if (group != null) {
            updateMarkedCount(group);
        }
    }

    /**
     * 更新某个评卷分组已评任务数量
     *
     * @param group
     *            - 评卷分组
     */
    @Override
    @Transactional
    public void updateMarkedCount(MarkGroup group) {
        if (group.getStatus() == MarkStatus.FORMAL || group.getStatus() == MarkStatus.FINISH) {
            groupDao.updateMarkedCount(group.getExamId(), group.getSubjectCode(), group.getNumber(), (int) libraryDao
                    .countByExamIdAndSubjectCodeAndGroupNumberAndStatus(group.getExamId(), group.getSubjectCode(),
                            group.getNumber(), LibraryStatus.MARKED, LibraryStatus.ARBITRATED, LibraryStatus.INSPECTED));
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            groupDao.updateMarkedCount(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    (int) trialLibraryDao.countMarked(group.getExamId(), group.getSubjectCode(), group.getNumber()));
        }
    }

    /**
     * 更新某个评卷分组评卷任务总量
     *
     * @param group
     *            - 评卷分组
     */
    @Override
    @Transactional
    public void updateLibraryCount(MarkGroup group) {
        if (group.getStatus() == MarkStatus.FORMAL || group.getStatus() == MarkStatus.FINISH) {
            group.setLibraryCount((int) libraryDao.countByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(),
                    group.getSubjectCode(), group.getNumber()));
            groupDao.updateLibraryCount(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    group.getLibraryCount());
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            group.setLibraryCount((int) trialLibraryDao.countByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(),
                    group.getSubjectCode(), group.getNumber()));
            groupDao.updateLibraryCount(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    group.getLibraryCount());
        }
    }

    /**
     * 更新某个科目所有评卷分组评卷任务数量
     *
     * @param examId
     *            - 考试ID
     * @param subjectCode
     *            - 科目代码
     */
    @Transactional
    @Override
    public void updateAllCount(Integer examId, String subjectCode) {
        List<MarkGroup> groups = groupDao.findByExamIdAndSubjectCode(examId, subjectCode);
        for (MarkGroup group : groups) {
            updateMarkedCount(group);
            updateLibraryCount(group);
        }
    }

    private String getGroupKey(MarkGroup group) {
        return group.getExamId() + "_" + group.getSubjectCode() + "_" + group.getNumber();
    }

    private String getGroupKey(Marker marker) {
        return marker.getExamId() + "_" + marker.getSubjectCode() + "_" + marker.getGroupNumber();
    }

    /**
     * 重置评卷分组的连带操作
     *
     * @param group
     *            - 评卷分组
     */
    private void resetGroup(MarkGroup group) {
        if (group.getStatus() == MarkStatus.FORMAL) {
            trackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            specialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
            headerTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
            headerTrackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            arbitrateDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            problemHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            libraryDao.resetByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber(), LibraryStatus.WAITING);
            libraryDao.deleteByExamIdAndSubjectCodeAndGroupNumberAndTaskNumber(group.getExamId(),
                    group.getSubjectCode(), group.getNumber(), 3);
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            trialTrackDao.deleteByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            trialTagDao.deleteByExamAndSubjectAndGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
            trialLibraryDao.resetByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
        }
        markerDao.resetByExamIdAndSubjectCodeAndGroupNumber(group.getExamId(), group.getSubjectCode(),
                group.getNumber());
        resetStudentGroup(group.getExamId(), group.getSubjectCode(), group.getNumber());
        updateLibraryCount(group);
        groupDao.resetCount(group.getExamId(), group.getSubjectCode(), group.getNumber());
        releaseByGroup(group);
    }

    /**
     * 计算并更新指定评卷员的评卷质量指标
     *
     * @param marker
     *            - 评卷员
     */
    @Override
    @Transactional
    public void updateQuality(Marker marker) {
        List<MarkLibrary> list = libraryDao.findByMarkerId(marker.getId());
        int finishCount = 0;
        int validCount = 0;
        double sumScore = 0;
        double sumScore2 = 0;
        double avgScore = 0;
        double stdevScore = 0;
        double sumSpent = 0;
        double avgSpent = 0;
        for (MarkLibrary library : list) {
            finishCount++;
            if (library.getStatus() == LibraryStatus.MARKED || library.getStatus() == LibraryStatus.INSPECTED) {
                validCount++;
            }
            double score = library.getMarkerScore() != null && library.getMarkerScore() != UN_SELECTIVE_SCORE ? library
                    .getMarkerScore() : 0;
            int spent = library.getMarkerSpent() != null ? library.getMarkerSpent() : 0;

            sumScore += score;
            sumScore2 += Math.pow(score, 2);
            sumSpent += spent;
            if (finishCount > 0) {
                avgScore = sumScore / finishCount;
                avgSpent = sumSpent / finishCount;
                // 递归法计算标准差
                stdevScore = Math.sqrt(sumScore2 / finishCount - Math.pow(sumScore / finishCount, 2));
            }
        }
        markerDao.updateQualityById(marker.getId(), finishCount, validCount, avgSpent / 1000, avgScore, stdevScore);
        markerLastUpdateTime.put(marker.getId(), System.currentTimeMillis());
    }

    @Override
    public boolean needUpdateQuality(Marker marker, long expireMinutes) {
        if (marker == null) {
            return false;
        }
        Long lastUpdateTime = markerLastUpdateTime.get(marker.getId());
        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setPageNumber(1);
        query.setPageSize(1);
        query.setSort(new Sort(Sort.Direction.DESC, "markerTime", "examNumber"));
        List<MarkLibrary> list = libraryDao.findByMarkerIdAndStatus(marker.getId(), LibraryStatus.MARKED, query);
        MarkLibrary library = list.isEmpty() ? null : list.get(0);
        if (library != null && library.getMarkerTime() != null) {
            long lastMarkTime = library.getMarkerTime().getTime();
            if (lastUpdateTime != null && lastUpdateTime > lastMarkTime) {
                return false;
            } else {
                return (System.currentTimeMillis() - lastMarkTime) < (expireMinutes * 60 * 1000);
            }
        }
        return false;
    }

    /**
     * 考生判断是否主观题全部评卷完成
     *
     * @param studentId
     */
    @Override
    public void checkStudentSubjective(Integer studentId, long groupCount, long unGroupQuestionCount) {
        if (groupStudentDao.countByStudentIdAndStatus(studentId, SubjectiveStatus.MARKED) == groupCount
                && unGroupQuestionCount == 0) {
            scoreCalculate(studentId);
        } else {
            studentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.UNMARK, 0, null);
            inspectedService.cancelByStudent(studentId);
        }
    }

    /**
     * 考生分组判断是否评卷完成，以及后续的统一处理动作
     *
     * @param library
     * @param group
     */
    private void checkStudentTrial(TrialLibrary library, MarkGroup group) {
        updateStudentGroupStatus(library.getStudentId(), group.getExamId(), group.getSubjectCode(), group.getNumber(),
                SubjectiveStatus.MARKED);
        updateStudentGroupScore(library.getStudentId(), group.getExamId(), group.getSubjectCode(), group,
                library.getMarkerScore(), library.getScoreList());
        // 未分组的题目
        long unGroupQuestionCount = questionService.countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(
                group.getExamId(), group.getSubjectCode(), false);
        long groupCount = groupDao.countByExamIdAndSubjectCode(group.getExamId(), group.getSubjectCode());
        checkStudentSubjective(library.getStudentId(), groupCount, unGroupQuestionCount);
    }

    /**
     * 考生分组判断是否评卷完成，以及后续的统一处理动作
     *
     * @param studentId
     * @param group
     */
    private void checkStudentGroup(Integer studentId, MarkGroup group) {
        if (calculateGroup(group, studentId)) {
            updateStudentGroupStatus(studentId, group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    SubjectiveStatus.MARKED);
            updateStudentGroupScore(studentId, group.getExamId(), group.getSubjectCode(), group, group.getMarkScore(),
                    group.getMarkScoreDetail());
            // 未分组的题目
            long unGroupQuestionCount = questionService.countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(
                    group.getExamId(), group.getSubjectCode(), false);
            long groupCount = groupDao.countByExamIdAndSubjectCode(group.getExamId(), group.getSubjectCode());
            checkStudentSubjective(studentId, groupCount, unGroupQuestionCount);
        } else {
            updateStudentGroupStatus(studentId, group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    SubjectiveStatus.UNMARK);
            studentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.UNMARK, 0, null);
        }
    }

    /**
     * 重置考生某个分组的评卷状态，以及整体的主观题状态
     *
     * @param studentId
     * @param examId
     * @param subjectCode
     * @param groupNumber
     */
    private void resetStudentGroup(Integer studentId, Integer examId, String subjectCode, Integer groupNumber) {
        updateStudentGroupStatus(studentId, examId, subjectCode, groupNumber, SubjectiveStatus.UNMARK);
        studentService.updateSubjectiveStatusAndScore(studentId, SubjectiveStatus.UNMARK, 0, null);
    }

    /**
     * 重置某个分组所有考生的评卷状态，以及这些考生的主观题状态
     *
     * @param examId
     * @param subjectCode
     * @param groupNumber
     */
    private void resetStudentGroup(Integer examId, String subjectCode, Integer groupNumber) {
        groupStudentDao.updateStatusByExamIdAndSubjectCodeAndGroupNumber(examId, subjectCode, groupNumber,
                SubjectiveStatus.UNMARK);
        scoreDao.deleteByExamIdAndSubjectCodeAndGroupNumber(examId, subjectCode, groupNumber);
        rejectHistoryDao.deleteByExamIdAndSubjectCodeAndGroupNumber(examId, subjectCode, groupNumber);
        selectiveStudentDao.deleteByExamIdAndSubjectCode(examId, subjectCode);
        studentService.updateSubjectiveStatusAndScore(examId, subjectCode, SubjectiveStatus.UNMARK, 0, null);
        inspectHistoryService.deleteByExamIdAndSubjectCode(examId, subjectCode);
    }

    private void updateStudentGroupStatus(Integer studentId, Integer examId, String subjectCode, Integer groupNumber,
            SubjectiveStatus status) {
        MarkGroupStudent gs = new MarkGroupStudent();
        gs.setStudentId(studentId);
        gs.setExamId(examId);
        gs.setSubjectCode(subjectCode);
        gs.setGroupNumber(groupNumber);
        gs.setStatus(status);
        groupStudentDao.saveAndFlush(gs);
    }

    private void updateStudentGroupScore(Integer studentId, Integer examId, String subjectCode, MarkGroup group,
            double score, List<ScoreItem> scoreList) {
        // scoreDao.deleteByStudentIdAndGroupNumber(studentId, groupNumber);
        List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
                subjectCode, false, group.getNumber());
        List<SubjectiveScore> subjectiveScores = scoreService.findByStudentIdAndGroupNumber(studentId,
                group.getNumber());
        for (int i = 0; i < questions.size(); i++) {
            ExamQuestion question = questions.get(i);
            SubjectiveScore ss;
            if (!subjectiveScores.isEmpty() && subjectiveScores.size() == questions.size()) {
                ss = subjectiveScores.get(i);
            } else {
                ss = new SubjectiveScore();
                ss.setRejected(false);
            }
            ss.setStudentId(studentId);
            ss.setExamId(examId);
            ss.setSubjectCode(subjectCode);
            ss.setGroupNumber(group.getNumber());
            ss.setGroupScore(score);
            ss.setMainNumber(question.getMainNumber());
            ss.setSubNumber(question.getSubNumber());
            if (score != UN_SELECTIVE_SCORE) {
                ScoreItem item = scoreList.get(i);
                ss.setScore(item.getScore());
                ss.setMainScore(0.0);
                ss.setUncalculate(false);
            } else {
                ss.setScore(score);
                ss.setMainScore(score);
                ss.setUncalculate(true);
            }
            if (group.getArbitrateThreshold() != null && group.getArbitrateThreshold() > 0) {
                ss.setUnansweredCount(headerTrackDao.countByStudentIdAndQuestionNumberAndUnanswered(studentId,
                        question.getQuestionNumber(), true));
            } else {
                if (MarkStatus.TRIAL.equals(group.getStatus())) {
                    ss.setUnansweredCount(trialTrackDao.countByStudentIdAndQuestionNumberAndUnanswered(studentId,
                            question.getQuestionNumber(), true));
                } else {
                    ss.setUnansweredCount(trackDao.countByStudentIdAndQuestionNumberAndUnanswered(studentId,
                            question.getQuestionNumber(), true));
                }
            }
            scoreDao.saveAndFlush(ss);
        }
    }

    // @Override
    // @Transactional
    // public boolean backStudentByGroup(ExamStudent student, Integer
    // groupNumber, Integer userId) {
    // MarkGroup group = groupDao.findOne(student.getExamId(),
    // student.getSubjectCode(), groupNumber);
    // if (group.getStatus() == MarkStatus.FINISH) {
    // return false;
    // }
    // List<MarkLibrary> list =
    // libraryDao.findByStudentIdAndGroupNumber(student.getId(), groupNumber);
    // int count = 0;
    // for (MarkLibrary library : list) {
    // if (libraryDao.resetById(library.getId(), null, LibraryStatus.REJECTED,
    // LibraryStatus.MARKED,
    // LibraryStatus.PROBLEM, LibraryStatus.INSPECTED) > 0) {
    // count++;
    // trackDao.deleteByLibraryId(library.getId());
    // specialTagDao.deleteByLibraryId(library.getId());
    // problemHistoryDao.resetByLibraryId(library.getId(),
    // HistoryStatus.WAITING, userId, HistoryStatus.BACK,
    // new Date());
    // if (library.getTaskNumber() == 3) {
    // // 出现三评时删除第三条任务
    // problemHistoryDao.deleteByLibraryId(library.getId());
    // libraryDao.delete(library);
    // updateLibraryCount(group);
    // }
    // }
    // }
    // if (count > 0) {
    // updateMarkedCount(group);
    // resetStudentGroup(student.getId(), group.getExamId(),
    // group.getSubjectCode(), group.getNumber());
    // studentService.updateSubjectiveStatusAndTimeAndInspectorId(student.getId(),
    // SubjectiveStatus.UNMARK, null,
    // null);
    // return true;
    // }
    // return false;
    // }

    @Override
    @Transactional
    public boolean rejectedStudent(ExamStudent student, MarkStepDTO[] markStepDTOs, Integer userId, String reason) {
        Map<Integer, List<MarkStepDTO>> map = new HashMap<Integer, List<MarkStepDTO>>();

        for (MarkStepDTO markStepDTO : markStepDTOs) {
            List<MarkStepDTO> list = map.get(markStepDTO.getGroupNumber());
            if (list == null) {
                list = new ArrayList<MarkStepDTO>();
            }
            list.add(markStepDTO);
            map.put(markStepDTO.getGroupNumber(), list);
        }
        Date now = new Date();
        for (Integer groupNumber : map.keySet()) {
            MarkGroup group = groupDao.findOne(student.getExamId(), student.getSubjectCode(), groupNumber);
            if (group.getStatus() == MarkStatus.FINISH || group.getStatus() == MarkStatus.TRIAL) {
                return false;
            }
            List<MarkLibrary> list = libraryDao.findByStudentIdAndGroupNumber(student.getId(), groupNumber);
            List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(
                    student.getExamId(), student.getSubjectCode(), false, groupNumber);
            List<MarkStepDTO> qList = map.get(groupNumber);
            int count = 0;
            for (MarkLibrary library : list) {
                if (library.getTaskNumber() == 3) {
                    // 出现三评时删除第三条任务
                    problemHistoryDao.deleteByLibraryId(library.getId());
                    trackDao.deleteByLibraryId(library.getId());
                    specialTagDao.deleteByLibraryId(library.getId());
                    headerTagDao.deleteByStudentIdAndGroupNumber(library.getStudentId(), groupNumber);
                    headerTrackDao.deleteByPkStudentIdAndGroupNumber(library.getStudentId(), groupNumber);
                    libraryDao.delete(library);
                    updateLibraryCount(group);
                    count++;
                    continue;
                }
                RejectHistory history = new RejectHistory(library);
                history.setUserId(userId);
                history.setReason(reason);

                if (library.getStatus().equals(LibraryStatus.ARBITRATED) || qList.size() == questions.size()) {
                    // 仲裁任务直接重置 或者该分组下所有分数被打回也重置
                    markerService.updateRejectCountById(library.getMarkerId());
                    libraryDao.resetById(library.getId(), null, reason, userId, now, LibraryStatus.REJECTED,
                            LibraryStatus.MARKED, LibraryStatus.PROBLEM, LibraryStatus.INSPECTED,
                            LibraryStatus.ARBITRATED);
                    rejectHistoryDao.save(history);
                    trackDao.deleteByLibraryId(library.getId());
                    specialTagDao.deleteByLibraryId(library.getId());
                    headerTagDao.deleteByStudentIdAndGroupNumber(library.getStudentId(), groupNumber);
                    headerTrackDao.deleteByPkStudentIdAndGroupNumber(library.getStudentId(), groupNumber);
                    arbitrateDao.deleteByStudentIdAndGroupNumber(student.getId(), groupNumber);
                    count++;
                    continue;
                }
                if (library.getStatus().equals(LibraryStatus.MARKED)
                        || library.getStatus().equals(LibraryStatus.INSPECTED)) {
                    List<ScoreItem> sList = library.getMarkerScoreItem();
                    for (MarkStepDTO markStepDTO : qList) {
                        trackDao.deleteByLibraryIdAndQuestionNumber(library.getId(), markStepDTO.getMainNumber() + "."
                                + markStepDTO.getSubNumber());
                        scoreDao.updateRejected(library.getStudentId(), markStepDTO.getMainNumber(),
                                markStepDTO.getSubNumber(), true);
                        for (int i = 0; i < questions.size(); i++) {
                            ExamQuestion question = questions.get(i);
                            if (markStepDTO.getMainNumber() == question.getMainNumber()
                                    && markStepDTO.getSubNumber().equals(question.getSubNumber())) {
                                ScoreItem si = sList.get(i);
                                si.setScore(null);
                            }
                        }
                    }
                    List<Object> markerScoreList = new ArrayList<Object>();
                    for (int i = 0; i < sList.size(); i++) {
                        ScoreItem scoreItem = sList.get(i);
                        if (scoreItem.getScore() != null) {
                            markerScoreList.add(new DecimalFormat("####.###").format(scoreItem.getScore()));
                        } else {
                            markerScoreList.add("#");
                        }
                    }
                    if (libraryDao.updateMarkerResult(library.getId(), LibraryStatus.REJECTED, library.getMarkerId(),
                            null, StringUtils.join(markerScoreList, ","), null, null, reason, userId, now,
                            LibraryStatus.MARKED, LibraryStatus.INSPECTED) == 1) {
                        markerService.updateRejectCountById(library.getMarkerId());
                        history.setRejectScoreList(StringUtils.join(markerScoreList, ","));
                        rejectHistoryDao.save(history);
                        count++;
                    }
                }
            }
            if (count > 0) {
                updateMarkedCount(group);
                resetStudentGroup(student.getId(), student.getExamId(), student.getSubjectCode(), groupNumber);
                studentService.updateSubjectiveStatusAndTimeAndInspectorId(student.getId(), SubjectiveStatus.UNMARK,
                        null, null);
                inspectedService.cancelByStudent(student.getId());
            }
        }
        return true;
    }

    @Transactional
    @Override
    public boolean deleteByQuestion(List<ExamQuestion> list) {
        if (list == null || list.isEmpty()) {
            return false;
        }
        int examId = list.get(0).getExamId();
        String subjectCode = list.get(0).getSubjectCode();
        Boolean containObjective = false;
        Boolean containSubjective = false;
        for (ExamQuestion question : list) {
            SelectiveGroup selectiveGroup = selectiveGroupService.findOne(question.getExamId(),
                    question.getSubjectCode(), question.getMainNumber());
            if (selectiveGroup != null) {
                continue;
            }
            if (!question.isObjective() && question.getGroupNumber() != null) {
                continue;
            } else {
                boolean objective = question.isObjective();
                questionService.deleteById(question.getId());
                if (objective) {
                    containObjective = true;
                } else {
                    containSubjective = true;
                }
            }
        }
        if (containObjective) {
            examService.updateObjectiveStatus(examId, ObjectiveStatus.WAITING);
            subjectService.updateScore(examId, subjectCode, true,
                    questionService.sumTotalScore(examId, subjectCode, true));
        }
        if (containSubjective) {
            // 未分组的题目
            long unGroupQuestionCount = questionService.countByExamIdAndSubjectAndObjectiveAndGroupNumberIsNull(examId,
                    subjectCode, false);
            // 考生整体状态与总分更新
            long groupCount = groupDao.countByExamIdAndSubjectCode(examId, subjectCode);
            if (groupCount == 0 || unGroupQuestionCount > 0) {
                studentService.updateSubjectiveStatusAndScore(examId, subjectCode, SubjectiveStatus.UNMARK, 0, null);
                inspectHistoryService.deleteByExamIdAndSubjectCode(examId, subjectCode);
            } else {
                List<Integer> studentList = studentService.findIdByExamIdAndSubjectCodeAndSubjectiveStatus(examId,
                        subjectCode, SubjectiveStatus.UNMARK, SubjectiveStatus.MARKED);
                for (Integer studentId : studentList) {
                    checkStudentSubjective(studentId, groupCount, unGroupQuestionCount);
                }
            }
            subjectService.updateScore(examId, subjectCode, false,
                    questionService.sumTotalScore(examId, subjectCode, false));
        }
        return true;
    }

    @Override
    @Transactional
    public boolean rejectLibrary(MarkLibrary library, MarkStepDTO[] questionList, Integer userId, String reason) {
        List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(
                library.getExamId(), library.getSubjectCode(), false, library.getGroupNumber());
        if (questionList.length == questions.size()) {
            return rejectLibrary(library, userId, reason, false);
        }
        MarkGroup group = groupDao.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
        if (group.getStatus() == MarkStatus.FINISH) {
            return false;
        }
        RejectHistory history = new RejectHistory(library);
        history.setUserId(userId);
        history.setReason(reason);

        Date now = new Date();
        List<ScoreItem> sList = library.getMarkerScoreItem();
        for (MarkStepDTO markStepDTO : questionList) {
            trackDao.deleteByLibraryIdAndQuestionNumber(library.getId(), markStepDTO.getMainNumber() + "."
                    + markStepDTO.getSubNumber());
            scoreDao.updateRejected(library.getStudentId(), markStepDTO.getMainNumber(), markStepDTO.getSubNumber(),
                    true);
            for (int i = 0; i < questions.size(); i++) {
                ExamQuestion question = questions.get(i);
                if (markStepDTO.getMainNumber() == question.getMainNumber()
                        && markStepDTO.getSubNumber().equals(question.getSubNumber())) {
                    ScoreItem si = sList.get(i);
                    si.setScore(null);
                }
            }
        }
        List<Object> markerScoreList = new ArrayList<Object>();
        for (int i = 0; i < sList.size(); i++) {
            ScoreItem scoreItem = sList.get(i);
            if (scoreItem.getScore() != null) {
                markerScoreList.add(new DecimalFormat("####.###").format(scoreItem.getScore()));
            } else {
                markerScoreList.add("#");
            }
            // if (i < sList.size() - 1) {
            // markerScoreList.append(",");
            // }
        }
        if (libraryDao.updateMarkerResult(library.getId(), LibraryStatus.REJECTED, library.getMarkerId(), null,
                StringUtils.join(markerScoreList, ","), null, null, reason, userId, now, LibraryStatus.MARKED,
                LibraryStatus.INSPECTED) == 1) {
            markerService.updateRejectCountById(library.getMarkerId());
            history.setRejectScoreList(StringUtils.join(markerScoreList, ","));
            rejectHistoryDao.save(history);
            resetStudentGroup(library.getStudentId(), library.getExamId(), library.getSubjectCode(),
                    library.getGroupNumber());
            problemHistoryDao.resetByLibraryId(library.getId(), HistoryStatus.WAITING, userId, HistoryStatus.BACK, now);
            // 开启三评时，打回1，2任务则删除第3条任务
            long count = libraryDao.countByStudentIdAndGroupNumber(library.getStudentId(), library.getGroupNumber());
            if (library.getTaskNumber() != 3 && count == 3) {
                MarkLibrary third = libraryDao.findByStudentIdAndGroupNumberAndTaskNumber(library.getStudentId(),
                        library.getGroupNumber(), 3);
                trackDao.deleteByLibraryId(third.getId());
                specialTagDao.deleteByLibraryId(third.getId());
                problemHistoryDao.deleteByLibraryId(third.getId());
                libraryDao.delete(third);
                updateLibraryCount(group);
            }
            updateMarkedCount(group);
            inspectedService.cancelByStudent(library.getStudentId());
            return true;
        } else {
            return false;
        }
    }

    @Transactional
    @Override
    public void groupTrialToFormal(int examId, String subjectCode) {
        groupStudentDao.updateStatusByExamIdAndSubjectCode(examId, subjectCode, SubjectiveStatus.UNMARK);
        scoreDao.deleteByExamIdAndSubjectCode(examId, subjectCode);
        selectiveStudentDao.deleteByExamIdAndSubjectCode(examId, subjectCode);
        studentService.updateSubjectiveStatusAndScore(examId, subjectCode, SubjectiveStatus.UNMARK, 0, null);
        inspectHistoryService.deleteByExamIdAndSubjectCode(examId, subjectCode);
        reportSubjectService.deleteByExamIdAndSubjectCode(examId, subjectCode);
    }
}
