package com.qmth.teachcloud.mark.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.jeffreyning.mybatisplus.service.MppServiceImpl;
import com.qmth.teachcloud.common.bean.dto.mark.MarkUser;
import com.qmth.teachcloud.common.bean.dto.mark.PictureConfig;
import com.qmth.teachcloud.common.bean.params.mark.setting.GroupPictureConfigParams;
import com.qmth.teachcloud.common.entity.MarkQuestion;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.mark.SubjectiveStatus;
import com.qmth.teachcloud.mark.bean.answercard.CardFile;
import com.qmth.teachcloud.mark.dto.mark.manage.*;
import com.qmth.teachcloud.mark.dto.mark.setting.MarkGroupDto;
import com.qmth.teachcloud.mark.dto.mark.setting.MarkGroupSingleDto;
import com.qmth.teachcloud.mark.dto.mark.setting.MarkGroupTaskDto;
import com.qmth.teachcloud.mark.entity.MarkGroup;
import com.qmth.teachcloud.mark.entity.MarkPaper;
import com.qmth.teachcloud.mark.entity.MarkTask;
import com.qmth.teachcloud.mark.entity.MarkUserGroup;
import com.qmth.teachcloud.mark.enums.LockType;
import com.qmth.teachcloud.mark.enums.ScorePolicy;
import com.qmth.teachcloud.mark.lock.LockService;
import com.qmth.teachcloud.mark.mapper.MarkGroupMapper;
import com.qmth.teachcloud.mark.service.*;
import com.qmth.teachcloud.mark.utils.Calculator;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 评卷分组表 服务实现类
 * </p>
 *
 * @author xf
 * @since 2023-09-22
 */
@Service
public class MarkGroupServiceImpl extends MppServiceImpl<MarkGroupMapper, MarkGroup> implements MarkGroupService {

    @Resource
    private MarkQuestionService markQuestionService;
    @Resource
    private MarkGroupService markGroupService;
    @Resource
    private MarkPaperService markPaperService;
    @Resource
    private MarkUserGroupService markUserGroupService;
    @Resource
    private MarkUserClassService markUserClassService;
    @Resource
    private MarkArbitrateHistoryService markArbitrateHistoryService;
    @Resource
    private MarkTaskService markTaskService;
    @Resource
    private MarkStudentService markStudentService;
    @Resource
    private MarkService markService;
    @Resource
    private MarkSyncService markSyncService;
    @Resource
    private LockService lockService;

    @Override
    public MarkGroupTaskDto listGroupTaskByExamIdAndPaperNumber(Long examId, String paperNumber) {
        MarkGroupTaskDto markGroupTaskDto = new MarkGroupTaskDto();
        markGroupTaskDto.setExamId(examId);
        markGroupTaskDto.setPaperNumber(paperNumber);

        // 分组信息
        List<MarkGroup> groupList = listGroupByExamIdAndPaperNumber(examId, paperNumber);
        if (CollectionUtils.isNotEmpty(groupList)) {
            List<MarkGroupDto> markGroupDtoList = new ArrayList<>();
            for (MarkGroup markGroup : groupList) {
                MarkGroupDto markGroupDto = new MarkGroupDto();
                markGroupDto.setGroupNumber(markGroup.getNumber());
                markGroupDto.setDoubleEnable(markGroup.getDoubleEnable());
                markGroupDto.setDoubleRate(markGroup.getDoubleRate());
                markGroupDto.setArbitrateThreshold(markGroup.getArbitrateThreshold());
                markGroupDto.setScorePolicy(markGroup.getScorePolicy());
                if (StringUtils.isNotBlank(markGroup.getPicList())) {
                    markGroupDto.setPictureConfigs(JSON.parseArray(markGroup.getPicList(), PictureConfig.class));
                }
                // 评卷员
                markGroupDto.setMarkers(markUserGroupService.listGroupUserByExamIdAndPaperNumberAndGroupNumberAndClassName(examId, paperNumber, markGroup.getNumber(), null));
                // 题目
                markGroupDto.setQuestions(markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, markGroup.getNumber(), false));
                markGroupDto.setGroupQuestions(markQuestionService.assembleGroupQuestionsByExamIdAndPaperNumberAndNumber(examId, paperNumber, markGroup.getNumber()));
                markGroupDtoList.add(markGroupDto);
            }
            markGroupTaskDto.setGroups(markGroupDtoList);
        }
        // 分班阅参数
        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
        markGroupTaskDto.setOpenMarkClass(markPaper != null && markPaper.getOpenMarkClass());
        markGroupTaskDto.setOpenDoubleMarking(markPaper != null && markPaper.getOpenDoubleMarking());
        return markGroupTaskDto;
    }

    @Override
    public List<MarkGroup> listGroupByExamIdAndPaperNumber(Long examId, String paperNumber) {
        QueryWrapper<MarkGroup> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MarkGroup::getExamId, examId)
                .eq(MarkGroup::getPaperNumber, paperNumber)
                .orderByAsc(MarkGroup::getNumber);
        return this.list(queryWrapper);
    }

    @Transactional
    @Override
    public void deleteGroupByExamIdAndPaperNumberAndGroupNumber(Long examId, String paperNumber, Integer groupNumber) {
        MarkGroup markGroup = this.getByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, groupNumber);
        if (lockService.trylock(LockType.GROUP_DELETE, examId, paperNumber, groupNumber)) {
            markSyncService.deleteMarkGroup(markGroup, true);
        }

    }

    @Override
    public void updatePicListByExamIdAndPaperNumberAndGroupNumber(GroupPictureConfigParams groupPictureConfigParams) {
        List<PictureConfig> pictureConfigs = groupPictureConfigParams.getPictureConfigs();
        if (CollectionUtils.isEmpty(pictureConfigs)) {
            throw ExceptionResultEnum.ERROR.exception("没有评卷区数据");
        }
        UpdateWrapper<MarkGroup> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(MarkGroup::getPicList, JSON.toJSONString(pictureConfigs))
                .eq(MarkGroup::getExamId, groupPictureConfigParams.getExamId())
                .eq(MarkGroup::getPaperNumber, groupPictureConfigParams.getPaperNumber())
                .eq(MarkGroup::getNumber, groupPictureConfigParams.getGroupNumber());
        this.update(updateWrapper);
    }

    @Transactional
    @Override
    public void updateOpenMarkClassByExamIdAndPaperNumber(Long examId, String paperNumber, Boolean openMarkClass) {
        UpdateWrapper<MarkPaper> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(MarkPaper::getOpenMarkClass, openMarkClass)
                .eq(MarkPaper::getExamId, examId)
                .eq(MarkPaper::getPaperNumber, paperNumber);
        markPaperService.update(updateWrapper);

        // 禁用，删除分班数据
        if (openMarkClass != null && !openMarkClass) {
            markUserClassService.deleteByExamIdAndPaperNumber(examId, paperNumber);
        }
    }

    @Override
    public MarkGroup getByExamIdAndPaperNumberAndGroupNumber(Long examId, String paperNumber, Integer groupNumber) {
        QueryWrapper<MarkGroup> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MarkGroup::getExamId, examId)
                .eq(MarkGroup::getPaperNumber, paperNumber)
                .eq(MarkGroup::getNumber, groupNumber);
        return this.getOne(queryWrapper);
    }

    @Override
    public MarkGroupSummaryProgressDto summaryGroupProgress(Long examId, String paperNumber) {
        MarkGroupSummaryProgressDto markGroupSummaryProgressDto = new MarkGroupSummaryProgressDto();

        // totalInfo
        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
        if (markPaper == null) {
            return markGroupSummaryProgressDto;
        }
        markGroupSummaryProgressDto.setOpenMarkClass(markPaper.getOpenMarkClass());
        MarkGroupTotalProgressDto markGroupTotalProgressDto = new MarkGroupTotalProgressDto();
        markGroupTotalProgressDto.setStudentCount(markPaper.getStudentCount());
        markGroupTotalProgressDto.setUploadCount(markPaper.getUploadCount());
        markGroupTotalProgressDto.setAbsentCount(markPaper.getAbsentCount());

        int totalCount = markStudentService.countByExamIdAndPaperNumberAndMarkStatus(examId, paperNumber, null);
        int markedCount = markStudentService.countByExamIdAndPaperNumberAndMarkStatus(examId, paperNumber, SubjectiveStatus.MARKED);
        markGroupTotalProgressDto.setPercent(Calculator.divide2String(Calculator.multiply(markedCount, 100), Double.valueOf(totalCount), 2));
        markGroupSummaryProgressDto.setTotalInfo(markGroupTotalProgressDto);

        // groupInfo
        List<MarkGroupProgressDto> markGroupProgressDtoList = new ArrayList<>();
        List<MarkGroup> markGroupList = this.listGroupByExamIdAndPaperNumber(examId, paperNumber);
        for (MarkGroup markGroup : markGroupList) {
            MarkGroupProgressDto markGroupProgressDto = new MarkGroupProgressDto();
            markGroupProgressDto.setGroupNumber(markGroup.getNumber());
            markGroupProgressDto.setGroupQuestions(markQuestionService.assembleGroupQuestionsByExamIdAndPaperNumberAndNumber(examId, paperNumber, markGroup.getNumber()));
            List<MarkUser> markUserList = markUserGroupService.listGroupUserByExamIdAndPaperNumberAndGroupNumberAndClassName(examId, paperNumber, markGroup.getNumber(), null);
            markGroupProgressDto.setMarkerCount(markUserList.size());
            markGroup.setQuestionList(markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, markGroup.getNumber(), false));
            markGroupProgressDto.setScoreList(markGroup.getScoreList());
            markGroupProgressDto.setTaskCount(markGroup.getTaskCount());
            markGroupProgressDto.setMarkedCount(markGroup.getMarkedCount());
            markGroupProgressDto.setLeftCount(markGroupProgressDto.getTaskCount() - markGroupProgressDto.getMarkedCount());
            markGroupProgressDto.setCurrentCount(markService.applyCurrentCount(markGroup));
            markGroupProgressDto.setPercent(Calculator.divide2String(Calculator.multiply(markGroupProgressDto.getMarkedCount(), 100), Double.valueOf(markGroupProgressDto.getTaskCount()), 2));
            markGroupProgressDto.setArbitrateCount(markArbitrateHistoryService.waitArbitrateCount(examId, paperNumber, markGroup.getNumber(), null));
            markGroupProgressDtoList.add(markGroupProgressDto);
        }
        markGroupSummaryProgressDto.setGroupInfo(markGroupProgressDtoList);
        return markGroupSummaryProgressDto;
    }

    @Override
    public IPage<MarkGroupClassProgressDto> summaryGroupClassProgress(Long examId, String paperNumber, String className, Integer pageNumber, Integer pageSize) {
        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
        if (markPaper.getOpenMarkClass()) {
            Page<MarkGroupClassProgressDto> page = new Page<>(pageNumber, pageSize);
            IPage<MarkGroupClassProgressDto> markGroupClassProgressDtoIPage = markUserClassService.pageClassByExamIdAndPaperNumber(page, examId, paperNumber, className);
            for (MarkGroupClassProgressDto markGroupClassProgressDto : markGroupClassProgressDtoIPage.getRecords()) {
                Integer groupNumber = markGroupClassProgressDto.getGroupNumber();
                List<MarkTask> totalMarkTaskList = new ArrayList<>();
                List<MarkUser> totalMarkUserList = new ArrayList<>();
                totalMarkTaskList.addAll(markTaskService.listByExamIdAndPaperNumberAndGroupNumberAndUserIdAndClassName(examId, paperNumber, groupNumber, null, markGroupClassProgressDto.getClassName()));
                totalMarkUserList.addAll(markUserGroupService.listGroupUserByExamIdAndPaperNumberAndGroupNumberAndClassName(examId, paperNumber, groupNumber, markGroupClassProgressDto.getClassName()));

                MarkGroup markGroup = this.getByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, groupNumber);
                markGroup.setQuestionList(markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, groupNumber, false));
                List<MarkUser> markUserList = markUserClassService.listClassMarkerByExamIdAndPaperNumberAndGroupNumberAndClassName(examId, paperNumber, groupNumber, markGroupClassProgressDto.getClassName());
                int count = 0;
                for (MarkUser markUser : markUserList) {
                    MarkUserGroup markUserGroup = markUserGroupService.getByExamIdAndPaperNumberAndGroupNumberAndUserId(examId, paperNumber, groupNumber, markUser.getUserId());
                    if (markUserGroup != null) {
                        int markerCount = markService.applyCurrentCount(markGroup, markUserGroup.getId());
                        count += markerCount;
                    }
                }

                // 待仲裁数量
                int arbitrateCount = markArbitrateHistoryService.waitArbitrateCount(examId, paperNumber, groupNumber, markGroupClassProgressDto.getClassName());
                markGroupClassProgressDto.setGroupQuestions(markGroup.getGroupQuestions());
                markGroupClassProgressDto.setMarkerCount(totalMarkUserList.size());
                markGroupClassProgressDto.setTaskCount(totalMarkTaskList.size());
                markGroupClassProgressDto.setMarkedCount(markTaskService.markedCount(totalMarkTaskList));
                markGroupClassProgressDto.setLeftCount(markGroupClassProgressDto.getTaskCount() - markGroupClassProgressDto.getMarkedCount());
                markGroupClassProgressDto.setCurrentCount(count);
                markGroupClassProgressDto.setPercent(Calculator.divide2String(Calculator.multiply(markGroupClassProgressDto.getMarkedCount(), 100), Double.valueOf(markGroupClassProgressDto.getTaskCount()), 2));
                markGroupClassProgressDto.setArbitrateCount(arbitrateCount);
            }
            return markGroupClassProgressDtoIPage;
        }
        return null;
    }

    @Override
    public void updateMarkedCount(Long examId, String paperNumber, Integer groupNumber, int count) {
        UpdateWrapper<MarkGroup> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(MarkGroup::getMarkedCount, count)
                .eq(MarkGroup::getExamId, examId)
                .eq(MarkGroup::getPaperNumber, paperNumber)
                .eq(MarkGroup::getNumber, groupNumber);
        this.update(updateWrapper);
    }

    @Transactional
    @Override
    public MarkGroup saveGroup(MarkGroupSingleDto markGroupSingleDto) {
        Long examId = markGroupSingleDto.getExamId();
        String paperNumber = markGroupSingleDto.getPaperNumber();
        MarkGroupDto markGroupDto = markGroupSingleDto.getGroupInfo();
        if (lockService.isLocked(LockType.GROUP_DELETE, examId, paperNumber, markGroupDto.getGroupNumber())) {
            throw ExceptionResultEnum.ERROR.exception("分组正删除中");
        }
        MarkGroup markGroup = this.getByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, markGroupDto.getGroupNumber());
        if (markGroup != null) {
            throw ExceptionResultEnum.ERROR.exception("数据已存在");
        }
        List<MarkQuestion> questions = markGroupDto.getQuestions();
        // 分组
        markGroup = new MarkGroup();
        markGroup.setExamId(examId);
        markGroup.setPaperNumber(paperNumber);
        MarkPaper markPaper = markPaperService.getByExamIdAndPaperNumber(examId, paperNumber);
        markGroup.setCourseCode(markPaper.getCourseCode());
        markGroup.setCourseName(markPaper.getCourseName());
        markGroup.setNumber(markGroupDto.getGroupNumber());
        markGroup.setPicList(CardFile.getMarkConfigAdd(markGroupDto.getPictureConfigs(), 0.015));
        Double totalScore = questions.stream().mapToDouble(MarkQuestion::getTotalScore).sum();
        markGroup.setTotalScore(totalScore);
        markGroup.setDoubleEnable(markGroupDto.getDoubleEnable());
        if (markGroupDto.getDoubleEnable()) {
            if (markGroupDto.getDoubleRate() == null) {
                throw ExceptionResultEnum.ERROR.exception("开启双评，双评比例必填");
            }
            if (markGroupDto.getArbitrateThreshold() == null) {
                throw ExceptionResultEnum.ERROR.exception("开启双评，仲裁阀值必填");
            } else if (markGroupDto.getArbitrateThreshold() < 0) {
                throw ExceptionResultEnum.ERROR.exception("开启双评，仲裁阀值大于等于0");
            }
            if (markGroupDto.getScorePolicy() == null) {
                throw ExceptionResultEnum.ERROR.exception("开启双评，合分规则必填");
            }
            markGroup.setDoubleRate(Calculator.round(markGroupDto.getDoubleRate(), 2));
            markGroup.setArbitrateThreshold(markGroupDto.getArbitrateThreshold());
            markGroup.setScorePolicy(markGroupDto.getScorePolicy());
        } else {
            markGroup.setDoubleRate(null);
            markGroup.setArbitrateThreshold(null);
            markGroup.setScorePolicy(ScorePolicy.AVG);
        }

        markGroup.setTaskCount(0);
        markGroup.setMarkedCount(0);
        this.saveOrUpdateByMultiId(markGroup);

        // 题目
        for (MarkQuestion markQuestion : questions) {
            MarkQuestion question = markQuestionService.getByExamIdAndPaperNumberAndMainNumberAndSubNumberAndObjective(examId, paperNumber, markQuestion.getMainNumber(), markQuestion.getSubNumber(), false);
            if (question == null) {
                throw ExceptionResultEnum.ERROR.exception("大题号[" + markQuestion.getMainNumber() + "],小题号[" + markQuestion.getSubNumber() + "]数据不存在");
            } else if (question.getGroupNumber() != null) {
                throw ExceptionResultEnum.ERROR.exception("大题号[" + markQuestion.getMainNumber() + "],小题号[" + markQuestion.getSubNumber() + "]存在评阅题目");
            }
            question.setGroupNumber(markGroupDto.getGroupNumber());
            markQuestionService.updateById(question);
        }

        // 评卷员
        List<MarkUser> markers = markGroupDto.getMarkers();
        if (CollectionUtils.isEmpty(markers)) {
            throw ExceptionResultEnum.ERROR.exception("请选择评卷员");
        }
        List<MarkUserGroup> markUserGroupList = markUserGroupService.listByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, markGroupDto.getGroupNumber());
        List<Long> userIds = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(markUserGroupList)) {
            userIds = markUserGroupList.stream().map(MarkUserGroup::getUserId).collect(Collectors.toList());
        }
        List<MarkUserGroup> markUserGroups = new ArrayList<>();
        for (MarkUser marker : markGroupDto.getMarkers()) {
            if (userIds.contains(marker.getUserId())) {
                continue;
            }
            markUserGroups.add(new MarkUserGroup(examId, paperNumber, markGroupDto.getGroupNumber(), marker.getUserId()));
        }
        if (CollectionUtils.isNotEmpty(markUserGroups)) {
            markUserGroupService.saveBatch(markUserGroups);
        }

        // 更新MarkPaper中groupStatus
        markService.updateMarkGroupStatus(examId, paperNumber);
        return markGroup;
    }

    @Transactional
    @Override
    public MarkGroup updateGroup(MarkGroupSingleDto markGroupSingleDto) {
        Long examId = markGroupSingleDto.getExamId();
        String paperNumber = markGroupSingleDto.getPaperNumber();
        MarkGroupDto markGroupDto = markGroupSingleDto.getGroupInfo();
        MarkGroup markGroup = this.getByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, markGroupDto.getGroupNumber());
        Boolean doubleEnable = markGroup.getDoubleEnable();
        Double doubleRate = markGroup.getDoubleRate();
        if (markGroup == null) {
            throw ExceptionResultEnum.ERROR.exception("数据不存在");
        }
        if (markGroupDto.getDoubleEnable()) {
            if (markGroupDto.getDoubleRate() == null) {
                throw ExceptionResultEnum.ERROR.exception("开启双评，双评比例必填");
            }
            if (markGroupDto.getArbitrateThreshold() == null) {
                throw ExceptionResultEnum.ERROR.exception("开启双评，仲裁阀值必填");
            } else if (markGroupDto.getArbitrateThreshold() < 0) {
                throw ExceptionResultEnum.ERROR.exception("开启双评，仲裁阀值大于等于0");
            }
            if (markGroupDto.getScorePolicy() == null) {
                throw ExceptionResultEnum.ERROR.exception("开启双评，合分规则必填");
            }
        }
        markGroup.setDoubleEnable(markGroupDto.getDoubleEnable());
        markGroup.setDoubleRate(markGroupDto.getDoubleRate());
        markGroup.setScorePolicy(markGroupDto.getScorePolicy());
        markGroup.setArbitrateThreshold(markGroupDto.getArbitrateThreshold());
        markGroup.setPicList(JSON.toJSONString(markGroupDto.getPictureConfigs()));
        this.updateByMultiId(markGroup);

        List<MarkQuestion> markQuestionList = markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, markGroupDto.getGroupNumber(), false);
        boolean isEqual = compareMarkGroupQuestion(markQuestionList, markGroupDto.getQuestions());
        if (!isEqual) {
            // 结构分组号
            markQuestionService.updateGroupNumberByExamIdAndPaperNumberAndGroupNumber(null, examId, paperNumber, markGroupDto.getGroupNumber());
            for (MarkQuestion markQuestion : markGroupDto.getQuestions()) {
                markQuestion.setGroupNumber(markGroupDto.getGroupNumber());
                markQuestionService.updateById(markQuestion);
            }

            // 修改分组题目总分
            List<MarkQuestion> updateMarkQuestionList = markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, markGroupDto.getGroupNumber(), false);
            Double totalScore = updateMarkQuestionList.stream().collect(Collectors.summingDouble(MarkQuestion::getTotalScore));
            markGroupService.updateTotalScore(examId, paperNumber, markGroupDto.getGroupNumber(), totalScore);
        }

        // 更新MarkPaper中groupStatus
        markService.updateMarkGroupStatus(examId, paperNumber);

        boolean isClear = !isEqual || !doubleEnable.equals(markGroupDto.getDoubleEnable()) || (markGroupDto.getDoubleEnable() && (doubleRate == null || markGroupDto.getDoubleRate().doubleValue() != doubleRate.doubleValue()));
        updateMarkUserGroup(isClear, examId, paperNumber, markGroupDto);

        // 增加或减少分组内题目数量和题目变动、单双评切换、开启双评且双评比例值修改过。则删除任务
        if (isClear) {
            this.updateMarkedCount(examId, paperNumber, markGroupDto.getGroupNumber(), 0);
            this.updateTaskCount(examId, paperNumber, markGroupDto.getGroupNumber(), 0);
            if (lockService.trylock(LockType.GROUP_DELETE, examId, paperNumber, markGroupDto.getGroupNumber())) {
                markSyncService.deleteMarkGroup(markGroup, false);
            }
        }
        return markGroup;
    }

    private void updateMarkUserGroup(boolean isClear, Long examId, String paperNumber, MarkGroupDto markGroupDto) {
        List<MarkUser> markUserList = markGroupDto.getMarkers();
        if (CollectionUtils.isEmpty(markUserList)) {
            throw ExceptionResultEnum.ERROR.exception("请选择评卷员");
        }
        List<MarkUserGroup> markUserGroupList = markUserGroupService.listByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, markGroupDto.getGroupNumber());
        if (CollectionUtils.isEmpty(markUserGroupList)) {
            markUserGroupList = new ArrayList<>();
        }
        List<Long> oldUserIds = markUserGroupList.stream().map(MarkUserGroup::getUserId).collect(Collectors.toList());
        List<Long> userIds = markUserList.stream().map(MarkUser::getUserId).collect(Collectors.toList());

        // 需要删除的用户
        Collection<Long> deleteUserIds = CollectionUtils.subtract(oldUserIds, userIds);
        if (CollectionUtils.isNotEmpty(deleteUserIds)) {
            markUserGroupList.stream().filter(m -> deleteUserIds.contains(m.getUserId())).forEach(m -> {
                markUserGroupService.deleteMarker(m.getId());
            });
        }

        // 需要新增的用户
        List<MarkUserGroup> markUserGroups = new ArrayList<>();
        Collection<Long> addUserIds = CollectionUtils.subtract(userIds, oldUserIds);
        for (Long addUserId : addUserIds) {
            markUserGroups.add(new MarkUserGroup(examId, paperNumber, markGroupDto.getGroupNumber(), addUserId));
        }
        if (CollectionUtils.isNotEmpty(markUserGroups)) {
            markUserGroupService.saveBatch(markUserGroups);
        }

        if (isClear) {
            // 需要修改的用户
            Collection<Long> updateUserIds = CollectionUtils.intersection(oldUserIds, userIds);
            if (CollectionUtils.isNotEmpty(updateUserIds)) {
                markUserGroupList.stream().filter(m -> updateUserIds.contains(m.getUserId())).forEach(m -> {
                    markUserGroupService.resetById(m.getId());
                });
            }
        }

    }

    /**
     * 校验修改前后分组内题目是否一致
     *
     * @param oldQuestions 修改前题目
     * @param questions    修改后题目
     * @return boolean
     */
    private boolean compareMarkGroupQuestion(List<MarkQuestion> oldQuestions, List<MarkQuestion> questions) {
        if (CollectionUtils.isEmpty(oldQuestions) || CollectionUtils.isEmpty(questions)) {
            throw ExceptionResultEnum.ERROR.exception("评卷题目异常");
        }
        List<String> oldQuestionNumbers = oldQuestions.stream().map(m -> m.getMainNumber() + "." + m.getSubNumber()).collect(Collectors.toList());
        List<String> questionNumbers = questions.stream().map(m -> m.getMainNumber() + "." + m.getSubNumber()).collect(Collectors.toList());
        // 不相同，返回false
        if (!CollectionUtils.isEqualCollection(oldQuestionNumbers, questionNumbers)) {
            return false;
        }
        // 取交集，与其中任意一个list不相同，返回false
        List<String> intersectionList = (List<String>) CollectionUtils.intersection(oldQuestionNumbers, questionNumbers);
        if (CollectionUtils.isEmpty(intersectionList) || !CollectionUtils.isEqualCollection(oldQuestionNumbers, intersectionList)) {
            return false;
        }
        return true;
    }

    @Override
    public void updateTaskCount(Long examId, String paperNumber, Integer groupNumber, int taskCount) {
        UpdateWrapper<MarkGroup> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().set(MarkGroup::getTaskCount, taskCount)
                .eq(MarkGroup::getExamId, examId)
                .eq(MarkGroup::getPaperNumber, paperNumber)
                .eq(MarkGroup::getNumber, groupNumber);
        this.update(updateWrapper);
    }

    @Override
    public List<MarkGroupQuestionsDto> listGroupQuestions(Long examId, String paperNumber) {
        List<MarkGroup> markGroupList = this.listGroupByExamIdAndPaperNumber(examId, paperNumber);
        List<MarkGroupQuestionsDto> markGroupQuestionsDtoList = new ArrayList<>();
        for (MarkGroup markGroup : markGroupList) {
            MarkGroupQuestionsDto markGroupQuestionsDto = new MarkGroupQuestionsDto();
            List<MarkQuestion> markQuestionList = markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(examId, paperNumber, markGroup.getNumber(), false);
            markGroup.setQuestionList(markQuestionList);
            markGroupQuestionsDto.setGroupNumber(markGroup.getNumber());
            markGroupQuestionsDto.setGroupQuestions(markGroup.getGroupQuestions());
            markGroupQuestionsDtoList.add(markGroupQuestionsDto);
        }
        markGroupQuestionsDtoList.sort(Comparator.comparingInt(MarkGroupQuestionsDto::getGroupNumber));
        return markGroupQuestionsDtoList;
    }

    @Override
    public void deleteByExamIdAndPaperNumberAndGroupNumber(Long examId, String paperNumber, Integer groupNumber) {
        UpdateWrapper<MarkGroup> markGroupUpdateWrapper = new UpdateWrapper<>();
        markGroupUpdateWrapper.lambda().eq(MarkGroup::getExamId, examId)
                .eq(MarkGroup::getPaperNumber, paperNumber)
                .eq(MarkGroup::getNumber, groupNumber);
        this.remove(markGroupUpdateWrapper);
    }

    @Override
    public long countByExamIdAndPaperNumber(Long examId, String paperNumber) {
        QueryWrapper<MarkGroup> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(MarkGroup::getExamId, examId)
                .eq(MarkGroup::getPaperNumber, paperNumber);
        return this.count(queryWrapper);
    }

    @Override
    public void updateTotalScore(Long examId, String paperNumber, Integer groupNumber, Double totalScore) {
        UpdateWrapper<MarkGroup> markGroupUpdateWrapper = new UpdateWrapper<>();
        markGroupUpdateWrapper.lambda().set(MarkGroup::getTotalScore, totalScore)
                .eq(MarkGroup::getExamId, examId)
                .eq(MarkGroup::getPaperNumber, paperNumber)
                .eq(MarkGroup::getNumber, groupNumber);
        this.update(markGroupUpdateWrapper);
    }
}
