package com.yida.data.attendance.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yida.data.attendance.mapper.*;
import com.yida.data.attendance.service.EduAttendanceStasticService;
import com.yida.data.attendance.vo.*;
import com.yida.data.common.core.entity.CurrentUser;
import com.yida.data.common.core.entity.attendance.*;
import com.yida.data.common.core.entity.school.EduDormitory;
import com.yida.data.common.core.entity.school.EduDormitoryRoom;
import com.yida.data.common.core.entity.system.Dept;
import com.yida.data.common.core.entity.system.enums.RoleEnum;
import com.yida.data.common.core.entity.user.EduStaff;
import com.yida.data.common.core.entity.user.EduStaffDept;
import com.yida.data.common.core.entity.user.EduStudent;
import com.yida.data.common.core.enums.AttendanceResult;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.EnumUtils;
import com.yida.data.common.core.utils.ExcelUtil;
import com.yida.data.common.core.utils.FebsUtil;
import com.yida.data.common.service.CommonService;
import com.yida.data.attendance.dto.ListResultStatisticsDTO;
import com.yida.data.attendance.dto.StasticStaffExcepByDayDTO;
import com.yida.data.attendance.dto.StasticStaffResultDTO;
import com.yida.data.school.dto.index.ListDormRoomDTO;
import com.yida.data.school.dto.leave.ListStudentRequestDetailDTO;
import com.yida.data.school.feign.facility.RemoteDormitoryService;
import com.yida.data.school.feign.leave.RemoteLeaveRequestService;
import com.yida.data.user.dto.ListStaffDTO;
import com.yida.data.user.dto.ListStudentDTO;
import com.yida.data.user.feign.RemoteStaffService;
import com.yida.data.user.feign.RemoteStudentService;

import java.text.DateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class EduAttendanceStasticServiceImpl implements EduAttendanceStasticService {

    private final EduAttendanceResultMapper eduAttendanceResultMapper;
    private final EduAttendanceVerifyDormMapper eduAttendanceVerifyDormMapper;
    private final EduAttendanceStaffResultMapper eduAttendanceStaffResultMapper;
    private final EduAttendanceStaffRuleStaffMapper eduAttendanceStaffRuleStaffMapper;
    private final EduAttendanceStaffRuleAdminRangeMapper eduAttendanceStaffRuleAdminRangeMapper;

    private final EduAttendanceRuleTimeMapper eduAttendanceRuleTimeMapper;

    private final EduAttendanceRuleMapper eduAttendanceRuleMapper;

    private final EduAttendanceRecordMapper eduAttendanceRecordMapper;

    private final RemoteStudentService remoteStudentService;
    private final RemoteStaffService remoteStaffService;
    private final RemoteDormitoryService remoteDormitoryService;

    private final RemoteLeaveRequestService remoteLeaveRequestService;
    private final CommonService commonService;

    @Override
    public CountVO countStudentAttendance(Long studentId, LocalDate startDate, LocalDate endDate) {
        return eduAttendanceResultMapper.countStudentAttendance(studentId, null, startDate, endDate);
    }

    @Override
    public TeacherStasticVO statisticsClass(Long classId, String date, String month, Long timeId) {
        TeacherStasticVO vo = new TeacherStasticVO();
        List<EduStudent> studentList = remoteStudentService.listStudent(
            ListStudentDTO.builder().classId(classId).onlyId(true).build())
            .getData();
        // 总人数
        vo.setShouldNum(CollUtil.isNotEmpty(studentList) ? studentList.size() : 0);
        List<Long> studentIds = new ArrayList<>();
        // 住宿学生数量
        Integer dormStudentNum = 0;
        // 仅包含走读生数据
        if (CollUtil.isNotEmpty(studentList)) {
            studentIds = studentList.stream().filter(x -> x.getType() == 0).map(EduStudent::getId).collect(Collectors.toList());
            dormStudentNum = studentList.size() - studentIds.size();
        } else {
            studentIds.add(-1L);
        }
        // 实到人数 按日统计才计算
        if (StrUtil.isNotBlank(date)) {
            vo.setRealNum(eduAttendanceResultMapper.countRealStudentNum(studentIds, timeId, date) + dormStudentNum);
        }
        List<ResultDetailVO> resultList = eduAttendanceResultMapper.listResultStudent(studentIds, timeId, date, month, null);
        if (CollUtil.isNotEmpty(resultList)) {
            // 迟到人数
            List<Long> lateStudent = resultList.stream().filter(x -> AttendanceResult.STU_LATE.getValue().equals(x.getStatus()))
                .map(ResultDetailVO::getStudentId)
                .collect(Collectors.toList());
            vo.setLateNum(lateStudent.size());
            vo.setLateStudentNum((int) lateStudent.stream().distinct().count());
            // 早退人数
            List<Long> leaveStudent = resultList.stream().filter(x -> AttendanceResult.STU_LEAVE.getValue().equals(x.getStatus()))
                .map(ResultDetailVO::getStudentId)
                .collect(Collectors.toList());
            vo.setLeaveNum(leaveStudent.size());
            vo.setLeaveStudentNum((int) leaveStudent.stream().distinct().count());
            // 旷课人数
            List<Long> notArriveStudent = resultList.stream()
                .filter(x -> AttendanceResult.STU_NOTARRIVE.getValue().equals(x.getStatus())).map(ResultDetailVO::getStudentId)
                .collect(Collectors.toList());
            vo.setNotArriveNum(notArriveStudent.size());
            vo.setNotArriveStudentNum((int) notArriveStudent.stream().distinct().count());
            // 请假人数
            List<Long> requestStudent = resultList.stream()
                .filter(x -> AttendanceResult.STU_REQUEST.getValue().equals(x.getStatus())).map(ResultDetailVO::getStudentId)
                .collect(Collectors.toList());
            vo.setRequestNum(requestStudent.size());
            vo.setRequestStudentNum((int) requestStudent.stream().distinct().count());
            // 考勤天数  按月统计才算
            if (StrUtil.isNotBlank(month)) {
                vo.setAttendanceDateNum((int) resultList.stream().map(ResultDetailVO::getAttendanceDate).distinct().count());
            }
        }
        return vo;
    }

    @Override
    public TeacherStasticVO statisticsClassCopy(Long classId, String date, String month, Long timeId) {
        //根据timeId找到规则Id
        EduAttendanceRuleTime eduAttendanceRuleTime = eduAttendanceRuleTimeMapper.selectById(timeId);
        EduAttendanceRule eduAttendanceRule = eduAttendanceRuleMapper.selectById(eduAttendanceRuleTime.getRuleId());
        TeacherStasticVO vo = new TeacherStasticVO();
        vo.setType(eduAttendanceRule.getType());
        List<EduStudent> studentList = remoteStudentService.listStudentByClassId(
            ListStudentDTO.builder().classId(classId).onlyId(true).build())
            .getData();
        //正常打卡人数/迟到人数/旷课人数/早退人数/请假人数
        Integer realNum = 0;
        Integer normalNum = 0;
        Integer lateStudentNum = 0;
        Integer dormLateStudentNum = 0;
        Integer dormLateExpStudentNum = 0;
        Integer dormNotArriveStudentNum = 0;
        Integer leaveStudentNum = 0;
        Integer notArriveStudentNum = 0;
        Integer requestStudentNum = 0;
        List<StudentInfoVO> requestStudentList = new ArrayList<>();
        List<StudentInfoVO> normalStudentList = new ArrayList<>();
        List<StudentInfoVO> lateStudentList = new ArrayList<>();
        List<StudentInfoVO> dormLateStudentList = new ArrayList<>();
        List<StudentInfoVO> dormLateExpStudentList = new ArrayList<>();
        List<StudentInfoVO> dormNotArriveStudentList = new ArrayList<>();
        List<StudentInfoVO> leaveStudentList = new ArrayList<>();
        List<StudentInfoVO> notArriveStudentList = new ArrayList<>();
        //规则对应的考勤设备
        List<Long> deviceIdList = eduAttendanceRule.getDeviceList().stream().map(EduAttendanceRuleDevice::getId)
            .collect(Collectors.toList());

        if (eduAttendanceRule.getType() == 0) {
            //上学考勤
            if (eduAttendanceRule.getDormitoryFlag() != 1) {
                //住宿生不参与考勤
                studentList = studentList.stream().filter(
                    x -> x.getType() == 0
                ).collect(Collectors.toList());
            }
            //应到人数
            vo.setShouldNum(CollUtil.isNotEmpty(studentList) ? studentList.size() : 0);

            for (EduStudent eduStudent : studentList) {
                //区别请假与正常上学
                Boolean leaveRequestBoolean = remoteLeaveRequestService.existRequest(
                    ListStudentRequestDetailDTO.builder().studentId(eduStudent.getId())
                        .startTime(LocalDateTimeUtil.now().toLocalDate().atTime(eduAttendanceRuleTime.getTime1()))
                        .endTime(LocalDateTimeUtil.now().toLocalDate().atTime(eduAttendanceRuleTime.getTime2())).build())
                    .getData();
                if (leaveRequestBoolean) {
                    //请假学生
                    requestStudentNum++;
                    StudentInfoVO studentInfoVO = new StudentInfoVO();
                    BeanUtils.copyProperties(eduStudent, studentInfoVO);
                    studentInfoVO.setResult(5);
                    requestStudentList.add(studentInfoVO);
                } else {
                    //查询每个学生的通行记录、计算状态
                    if (eduStudent.getType() == 0 || eduAttendanceRule.getDormitoryFlag() == 1) {
                        //根据当前时间 与 timeId 查询学生今天的第一条通行记录，再判断
                        EduAttendanceRecord firstByStudentIdAndRuleTimeId = eduAttendanceRecordMapper
                            .getFirstByStudentIdAndRuleTimeId(eduStudent.getId(), timeId, date, deviceIdList);
                        EduAttendanceRecord lastByStudentIdAndRuleTimeId = eduAttendanceRecordMapper
                            .getLastByStudentIdAndRuleTimeId(eduStudent.getId(), timeId, date, deviceIdList);

                        if (ObjectUtils.isNotNull(firstByStudentIdAndRuleTimeId)) {
                            Integer currentStatus = firstByStudentIdAndRuleTimeId.getStatus();
                            StudentInfoVO studentInfoVO = new StudentInfoVO();
                            BeanUtils.copyProperties(eduStudent, studentInfoVO);
                            //正常的
                            if (currentStatus == 0) {
                                if (LocalDateTimeUtil.now().toLocalTime().isBefore(eduAttendanceRuleTime.getTime2())) {
                                    //1:查询时间在结束时间之前的话，只要开始考勤正常即可，如果查询时间是在结束时间之后的话，就必须要上下课考勤都正常
                                    studentInfoVO.setResult(0);
                                    normalStudentList.add(studentInfoVO);
                                    normalNum++;
                                } else {
                                    //1:查询时间在结束时间之后的话，需要开始考勤时间正常 与  结束考勤时间都正常 才为正常
                                    if (ObjectUtils.isNotNull(lastByStudentIdAndRuleTimeId) && !Objects
                                        .equals(lastByStudentIdAndRuleTimeId.getId(), firstByStudentIdAndRuleTimeId.getId())) {
                                        if (lastByStudentIdAndRuleTimeId.getStatus() == 0) {
                                            studentInfoVO.setResult(0);
                                            normalStudentList.add(studentInfoVO);
                                            normalNum++;
                                        }
                                    }
                                }
                            }
                            //迟到的
                            if (currentStatus == 1) {
                                studentInfoVO.setResult(1);
                                lateStudentList.add(studentInfoVO);
                                lateStudentNum++;
                            }
                            //早退的
                            if (ObjectUtils.isNotNull(lastByStudentIdAndRuleTimeId) && !Objects
                                .equals(lastByStudentIdAndRuleTimeId.getId(), firstByStudentIdAndRuleTimeId.getId())) {
                                if (lastByStudentIdAndRuleTimeId.getStatus() == 2) {
                                    studentInfoVO.setResult(2);
                                    leaveStudentList.add(studentInfoVO);
                                    leaveStudentNum++;
                                }
                            }
                            //实到人数
                            realNum++;
                        } else {
                            //旷课的
                            if (LocalDateTimeUtil.now().toLocalTime().isAfter(eduAttendanceRuleTime.getTime2())) {
                                StudentInfoVO studentInfoVO = new StudentInfoVO();
                                BeanUtils.copyProperties(eduStudent, studentInfoVO);
                                studentInfoVO.setResult(4);
                                notArriveStudentList.add(studentInfoVO);
                                notArriveStudentNum++;
                            } else {
                                StudentInfoVO studentInfoVO = new StudentInfoVO();
                                BeanUtils.copyProperties(eduStudent, studentInfoVO);
                                studentInfoVO.setResult(null);  //正常  未打卡
                                normalStudentList.add(studentInfoVO);
//                                notArriveStudentNum++;
                            }
                        }
                    }
                }
            }
        } else {
            //宿舍考勤

            EduAttendanceRuleTime dormEduAttendanceRuleTime = eduAttendanceRuleTimeMapper.selectById(timeId);

            studentList = studentList.stream().filter(
                x -> x.getType() == 1
            ).collect(Collectors.toList());

            vo.setShouldNum(CollUtil.isNotEmpty(studentList) ? studentList.size() : 0);

            for (EduStudent eduStudent : studentList) {
                EduAttendanceRecord firstByStudentIdAndRuleTimeId = eduAttendanceRecordMapper
                    .getFirstByStudentIdAndRuleTimeId(eduStudent.getId(), timeId, date, deviceIdList);
                if (ObjectUtils.isNotNull(firstByStudentIdAndRuleTimeId)) {
                    if (firstByStudentIdAndRuleTimeId.getStatus() == 0) {
                        //正常
                        StudentInfoVO studentInfoVO = new StudentInfoVO();
                        BeanUtils.copyProperties(eduStudent, studentInfoVO);
                        studentInfoVO.setResult(0);
                        normalStudentList.add(studentInfoVO);
                        normalNum++;
                    } else if (firstByStudentIdAndRuleTimeId.getStatus() == 4) {
                        //晚归
                        StudentInfoVO studentInfoVO = new StudentInfoVO();
                        BeanUtils.copyProperties(eduStudent, studentInfoVO);
                        studentInfoVO.setResult(1);
                        dormLateStudentList.add(studentInfoVO);
                        dormLateStudentNum++;
                    } else if (firstByStudentIdAndRuleTimeId.getStatus() == 5) {
                        //异常晚归
                        StudentInfoVO studentInfoVO = new StudentInfoVO();
                        BeanUtils.copyProperties(eduStudent, studentInfoVO);
                        studentInfoVO.setResult(2);
                        dormLateExpStudentList.add(studentInfoVO);
                        dormLateExpStudentNum++;
                    }
                    realNum++;
                } else {
                    //旷宿
                    if (LocalDateTimeUtil.now().toLocalTime().isAfter(dormEduAttendanceRuleTime.getTime3())) {
                        //大于晚归截至时间
                        StudentInfoVO studentInfoVO = new StudentInfoVO();
                        BeanUtils.copyProperties(eduStudent, studentInfoVO);
                        studentInfoVO.setResult(4);
                        dormNotArriveStudentList.add(studentInfoVO);
                        dormNotArriveStudentNum++;
                    } else if (LocalDateTimeUtil.now().toLocalTime().isBefore(dormEduAttendanceRuleTime.getTime2())
                        && LocalDateTimeUtil.now().toLocalTime().isAfter(dormEduAttendanceRuleTime.getTime1())) {
                        //结束时间与晚归截至时间之间
                        StudentInfoVO studentInfoVO = new StudentInfoVO();
                        BeanUtils.copyProperties(eduStudent, studentInfoVO);
                        studentInfoVO.setResult(null); //晚归
                        lateStudentList.add(studentInfoVO);
//                        notArriveStudentNum++;
                    } else if (LocalDateTimeUtil.now().toLocalTime().isBefore(dormEduAttendanceRuleTime.getTime1())) {
                        StudentInfoVO studentInfoVO = new StudentInfoVO();
                        BeanUtils.copyProperties(eduStudent, studentInfoVO);
                        studentInfoVO.setResult(null); //正常 未打卡
                        normalStudentList.add(studentInfoVO);
                    }
                }
            }
        }
        vo.setRealNum(realNum);
        vo.setRequestStudentNum(requestStudentNum);
        vo.setLateStudentNum(lateStudentNum);
        vo.setLeaveStudentNum(leaveStudentNum);
        vo.setNotArriveStudentNum(notArriveStudentNum);
        vo.setRequestStudentList(requestStudentList);
        vo.setLateStudentList(lateStudentList);
        vo.setLeaveStudentList(leaveStudentList);
        vo.setNotArriveStudentList(notArriveStudentList);
        vo.setNormalStudentList(normalStudentList);
        return vo;
    }

    @Override
    public TeacherStasticVO statisticsClassCopyByMonth(Long classId, String month) {
        //计算考勤天数，根据classId 查询出规则

        //根据timeId找到规则Id
        TeacherStasticVO vo = new TeacherStasticVO();
        List<EduStudent> studentList = remoteStudentService.listStudent(
            ListStudentDTO.builder().classId(classId).onlyId(true).build())
            .getData();
        //正常打卡人数/迟到人数/旷课人数/早退人数/请假人数
        Integer realNum = 0;
        Integer normalNum = 0;

        Integer lateNum = 0;
        Integer lateStudentNum = 0;
        Integer leaveNum = 0;
        Integer leaveStudentNum = 0;
        Integer notArriveStudentNum = 0;
        Integer requestStudentNum = 0;
        List<StudentInfoVO> requestStudentList = new ArrayList<>();
        List<StudentInfoVO> normalStudentList = new ArrayList<>();
        List<StudentInfoVO> lateStudentList = new ArrayList<>();
        List<StudentInfoVO> leaveStudentList = new ArrayList<>();
        List<StudentInfoVO> notArriveStudentList = new ArrayList<>();
        for (EduStudent eduStudent : studentList) {
            //迟到
            List<EduAttendanceRecord> lastRecordByStudentIdAndMonth = eduAttendanceRecordMapper
                .getRecordByStudentIdAndMonth(eduStudent.getId(), month, 1);
            if (ObjectUtils.isNotEmpty(lastRecordByStudentIdAndMonth)) {
                StudentInfoVO studentInfoVO = new StudentInfoVO();
                BeanUtils.copyProperties(eduStudent, studentInfoVO);
                studentInfoVO.setResult(1);
                lateStudentList.add(studentInfoVO);
                lateStudentNum++;
                lateNum += lastRecordByStudentIdAndMonth.size();
            }

            //早退
            List<EduAttendanceRecord> leaveRecordByStudentIdAndMonth = eduAttendanceRecordMapper
                .getRecordByStudentIdAndMonth(eduStudent.getId(), month, 2);
            if (ObjectUtils.isNotEmpty(leaveRecordByStudentIdAndMonth)) {
                StudentInfoVO studentInfoVO = new StudentInfoVO();
                BeanUtils.copyProperties(eduStudent, studentInfoVO);
                studentInfoVO.setResult(2);
                leaveStudentList.add(studentInfoVO);
                leaveStudentNum++;
                leaveNum += leaveRecordByStudentIdAndMonth.size();
            }

            //请假

        }
        return null;
    }


    public Integer getDays(Integer classId) {

        return null;
    }

    @Override
    public RateRankVO stasticRateRank(String date, Long schoolId, Long gradeId, Long timeId) {
        ListResultStatisticsDTO dto = new ListResultStatisticsDTO();
        dto.setDate(LocalDate.parse(date));
        dto.setSchoolId(schoolId);
        dto.setGradeId(gradeId);
        dto.setTimeId(timeId);
        dto.setType(0);
        List<RateVO> rateList = listRate(dto);
        rateList.sort(Comparator.comparingInt(x -> (int) (x.getRate() * 100)));
        RateRankVO res = new RateRankVO();
        if (rateList.size() > 4) {
            res.setTopFive(rateList.subList(0, 5));
            List<RateVO> newRateList = CollUtil.newArrayList(rateList);
            newRateList.sort((y, x) -> (int) (x.getRate() * 100) - (int) (y.getRate() * 100));
            res.setBottomFive(newRateList.subList(newRateList.size() - 3, newRateList.size()));
        } else if (rateList.size() > 0) {
            res.setTopFive(rateList);
            List<RateVO> newRateList = CollUtil.newArrayList(rateList);
            newRateList.sort((y, x) -> (int) (x.getRate() * 100) - (int) (y.getRate() * 100));
            if (rateList.size() > 2) {
                res.setBottomFive(newRateList.subList(newRateList.size() - 3, newRateList.size()));
            } else {
                res.setBottomFive(newRateList);
            }
        }
        return res;
    }

    @Override
    public List<RateVO> stasticDormRateRank(String date, Long schoolId, Integer duration) {
        // 符合而条件的考勤记录
        List<EduAttendanceResult> resultList = eduAttendanceResultMapper
            .listStudentResult(null, null, LocalDateTimeUtil.parseDate(date, "yyyy-MM" + "-dd"),
                schoolId, null, null, null, null, duration, 1, null);
        List<RateVO> res = new ArrayList<>();
        if (CollUtil.isNotEmpty(resultList)) {
            Map<Long, EduDormitory> dormCache = new HashMap<>();
            // 按宿舍楼分组
            Map<Long, List<EduAttendanceResult>> resultGroup = resultList.stream()
                .collect(Collectors.groupingBy(EduAttendanceResult::getDormId));
            for (Map.Entry<Long, List<EduAttendanceResult>> entry : resultGroup.entrySet()) {
                RateVO rateVO = new RateVO();
                EduDormitory dorm =
                    dormCache.containsKey(entry.getKey())
                        ? dormCache.get(entry.getKey())
                        : remoteDormitoryService.getDorm(entry.getKey()).getData();
                if (dorm == null) {
                    continue;
                }
                dormCache.put(dorm.getId(), dorm);
                rateVO.setDormName(dorm.getName());
                rateVO.setTotal(entry.getValue().size());
                rateVO.setNotArrive(0);
                // 遍历宿舍楼的考勤数据
                for (EduAttendanceResult result : entry.getValue()) {
                    if (AttendanceResult.STU_D_NOTARRIVE.getValue().equals(result.getStatus())) {
                        rateVO.setNotArrive(rateVO.getNotArrive() + 1);
                    }
                }
                rateVO.setRate((rateVO.getTotal() - rateVO.getNotArrive()) * 100D / rateVO.getTotal());
                res.add(rateVO);
            }
            res.sort((x, y) -> (int) (x.getRate() * 100 - y.getRate() * 100));
        }
        return res;
    }

    @Override
    public List<RateVO> listRate(ListResultStatisticsDTO dto) {
        List<Long> roomIds = null;
        if (dto.getType() == 1 && dto.getDormId() != null) {
            List<EduDormitoryRoom> data =
                remoteDormitoryService.listDormRoom(
                    ListDormRoomDTO.builder().dormIds(Collections.singletonList(dto.getDormId())).build())
                    .getData();
            if (CollUtil.isNotEmpty(data)) {
                roomIds = data.stream().map(EduDormitoryRoom::getId).collect(Collectors.toList());
            }
        }
        // 筛选出的通行纪录
        List<EduAttendanceResult> resultList = eduAttendanceResultMapper
            .listStudentResult(roomIds, null, dto.getDate(), dto.getSchoolId(), dto.getCampusId(),
                dto.getGradeId(), dto.getClassId(), dto.getTimeId(), dto.getDuration(), dto.getType(), null);

        List<RateVO> res = new ArrayList<>();

        Map<Long, List<EduAttendanceResult>> resultGroup = dto.getType() == 0 || dto.getDormId() == null
            ? resultList.stream().collect(Collectors.groupingBy(EduAttendanceResult::getClassId))
            : resultList.stream().collect(Collectors.groupingBy(EduAttendanceResult::getDormId));
        // 有班级的考勤数据
        if (CollUtil.isNotEmpty(resultGroup)) {
            for (Map.Entry<Long, List<EduAttendanceResult>> entry : resultGroup.entrySet()) {
                List<EduAttendanceResult> classResult = entry.getValue();
                RateVO classRate = new RateVO();
                EduAttendanceResult firstResult = classResult.get(0);
                if (dto.getType() == 0 || dto.getDormId() == null) {
                    classRate.setGradeClass(commonService.getUserDept(firstResult.getGradeId()).getDeptName() + commonService
                        .getUserDept(firstResult.getClassId()).getDeptName());
                    classRate.setCampus(commonService.getUserDept(firstResult.getCampusId()).getDeptName());
                } else {
                    EduDormitory dorm = remoteDormitoryService.getDorm(firstResult.getDormId()).getData();
                    classRate.setGradeClass(dorm != null ? dorm.getName() : firstResult.getDormName());
                }
                classRate.setDate(dto.getDate());
                classRate.setTotal(classResult.size());
                classRate.setLeave(0);
                classRate.setNormal(0);
                classRate.setNotArrive(0);
                classRate.setLate(0);
                classRate.setLateAndLeave(0);
                for (EduAttendanceResult studentResult : classResult) {
                    AttendanceResult status;
                    try {
                        status = EnumUtils.valueOf(AttendanceResult.class, studentResult.getStatus(),
                            AttendanceResult.class.getMethod("getValue"));
                    } catch (NoSuchMethodException e) {
                        log.error("考勤结果值映射失败:[{}]", studentResult.getStatus());
                        continue;
                    }
                    switch (status) {
                        // 正常
                        case NORMAL:
                            classRate.setNormal(classRate.getNormal() + 1);
                            break;
                        // 迟到，晚归，异常晚归
                        case STU_LATE:
                        case STU_D_LATE:
                        case STU_D_EX_LATE:
                            classRate.setLate(classRate.getLate() + 1);
                            break;
                        // 早退
                        case STU_LEAVE:
                            classRate.setLeave(classRate.getLeave() + 1);
                            break;
                        // 未到，旷宿
                        case STU_NOTARRIVE:
                        case STU_D_NOTARRIVE:
                            classRate.setNotArrive(classRate.getNotArrive() + 1);
                            break;
                        // 迟到早退
                        case STU_LATE_LEAVE:
                            classRate.setLateAndLeave(classRate.getLateAndLeave() + 1);
                            break;
                        default:
                            break;
                    }
                }
                // 添加班级数据
                classRate.setRate((classRate.getTotal() - classRate.getNotArrive()) * 100D / classRate.getTotal());
                res.add(classRate);
            }
        }
        return res;
    }

    @Override
    public void exportRateList(ListResultStatisticsDTO dto, HttpServletResponse response) {
        // 查询数据
        List<RateVO> rateVOList = listRate(dto);
        if (CollUtil.isNotEmpty(rateVOList)) {
            // 校门导出
            if (dto.getType() == 0) {
                List<ExportSchoolRateVO> rateList = new ArrayList<>();
                for (RateVO vo : rateVOList) {
                    ExportSchoolRateVO schoolRateVO = new ExportSchoolRateVO();
                    BeanUtils.copyProperties(vo, schoolRateVO);
                    schoolRateVO.setDate(vo.getDate().toString());
                    rateList.add(schoolRateVO);
                }
                ExcelUtil.export("上学考勤.xlsx", ExportSchoolRateVO.class, rateList, response);
            } else {
                // 宿舍导出
                List<ExportDormRateVO> rateList = new ArrayList<>();
                for (RateVO vo : rateVOList) {
                    ExportDormRateVO dormRateVO = new ExportDormRateVO();
                    BeanUtils.copyProperties(vo, dormRateVO);
                    dormRateVO.setDate(vo.getDate().toString());
                    rateList.add(dormRateVO);
                }
                ExcelUtil.export("归寝考勤.xlsx", ExportDormRateVO.class, rateList, response);
            }
        } else {
            throw new FebsException("暂无数据");
        }
    }

    @Override
    public List<StasticDormResultVO> stasticResultGroupByDorm(Long schoolId, Long staffId, Integer time, String date) {
        //查询职工管理的宿舍
        List<StasticDormResultVO> res = new ArrayList<>();
        List<EduDormitory> dormList = remoteDormitoryService.listDormByMaster(staffId).getData();
        if (CollUtil.isNotEmpty(dormList)) {
            // 查询宿舍楼下的住宿成员
            List<EduStudent> studentList = remoteStudentService.listStudent(ListStudentDTO.builder()
                .dormIds(dormList.stream().map(EduDormitory::getId).collect(Collectors.toList()))
                .type(1)
                .onlyId(true)
                .build()).getData();
            // 做出每个宿舍楼的数据
            for (EduDormitory dorm : dormList) {
                StasticDormResultVO data = new StasticDormResultVO();
                data.setDormId(dorm.getId());
                data.setDromName(dorm.getName());
                log.info("dorm:[{}]", dorm);
                long total = studentList.stream().filter(x -> dorm.getId().equals(x.getDormId())).count();
                if (total > 0) {
                    data.setTotal((int) total);
                    // 查询单个宿舍楼的异常数据
                    StasticDormResultVO countDorm = eduAttendanceResultMapper.countDormLate(dorm.getId(), date, time);
                    data.setLate(countDorm.getLate());
                    data.setNotArrive(countDorm.getNotArrive());
                }
                res.add(data);
            }
        }
        return res;
    }

    @Override
    public List<StasticDormResultVO> stasticResultByDormRoom(Long dormId, Integer verify, Integer status, Integer time,
        String date,
        Integer sort, String keyword) {
        List<StasticDormResultVO> res = new ArrayList<>();
        // 宿舍楼的宿舍
        List<EduDormitoryRoom> roomList = dormId != null
            ? remoteDormitoryService.listDormRoom(ListDormRoomDTO.builder().dormIds(Collections.singletonList(dormId)).build())
            .getData()
            : null;
        if (CollUtil.isNotEmpty(roomList)) {
            // 查询考勤规则
//            List<Long> deptIds = eduAttendanceRuleService.listValidRuleDept(null, 1);
//            List<Long> classIds = remoteDeptService.listClassByDept(deptIds.toArray(new Long[0])).getData();
            // 查询宿舍楼下的住宿成员
            List<EduStudent> studentList = remoteStudentService.listStudent(ListStudentDTO.builder()
                .dormRoomIds(roomList.stream().map(EduDormitoryRoom::getId).collect(Collectors.toList()))
                .type(1)
                .isQueryParent(false)
                .isQueryUserDept(false)
                .isQuerySchoolName(false)
                .build()).getData();
            // 遍历做每个寝室数据
            for (EduDormitoryRoom room : roomList) {
                List<EduStudent> dormStudentList =
                    studentList.stream().filter(x -> room.getId().equals(x.getDormRoomId()))
                        .collect(Collectors.toList());
                if (CollUtil.isNotEmpty(dormStudentList)) {
                    Integer isVerify =
                        eduAttendanceVerifyDormMapper.selectCount(Wrappers.<EduAttendanceVerifyDorm>lambdaQuery()
                            .eq(EduAttendanceVerifyDorm::getDormRoomId, room.getId())
                            .eq(EduAttendanceVerifyDorm::getVerifyDate, LocalDateTimeUtil.parseDate(date, "yyyy-MM-dd"))
                            .eq(EduAttendanceVerifyDorm::getTime, time)) > 0 ? 1 : 0;
                    // 筛选查寝状态
                    if (verify != null) {
                        if (!isVerify.equals(verify)) {
                            continue;
                        }
                    }
                    // 筛选关键词
                    if (StrUtil.isNotBlank(keyword)) {
                        boolean flag = room.getName().contains(keyword);
                        for (EduStudent student : dormStudentList) {
                            if (!flag) {
                                if (student.getStuName().contains(keyword)) {
                                    flag = true;
                                    break;
                                }
                            }
                        }
                        if (!flag) {
                            continue;
                        }
                    }
                    StasticDormResultVO data = new StasticDormResultVO();
                    data.setTotal(dormStudentList.size());
                    data.setStay(eduAttendanceResultMapper.countDormStay(room.getId(), date));
                    data.setNotStay(data.getTotal() - data.getStay());
                    data.setDormRoomId(room.getId());
                    data.setDormRoomName(room.getName());
                    data.setVerify(isVerify);
                    // 筛选归寝状态
                    if (status != null) {
                        if ((status == 0 && data.getNotStay() == 0) || (status == 1 && data.getStay() == 0)) {
                            break;
                        }
                    }
                    res.add(data);
                }
            }
        }
        if (CollUtil.isNotEmpty(res)) {
            if (sort == 0) {
                res.sort((x, y) -> StrUtil.compare(x.getDormRoomName(), y.getDormRoomName(), true));
            } else {
                res.sort((y, x) -> StrUtil.compare(x.getDormRoomName(), y.getDormRoomName(), true));
            }
        }
        return res;
    }

    @Override
    public IPage<StasticStaffResultByDayVO> stasticStaffResultByDay(CurrentUser currentUser,
        StasticStaffResultDTO dto) {
        IPage<StasticStaffResultByDayVO> resPage = null;
        List<EduStaff> staffList = listStaff(currentUser, dto);
        if (CollUtil.isNotEmpty(staffList)) {
            // 按id分组方便遍历
            Map<Long, List<EduStaff>> staffGroup = staffList.stream().collect(Collectors.groupingBy(EduStaff::getId));
            resPage = eduAttendanceStaffResultMapper.listStaffResultStasticDayPage(dto.toPage(), dto,
                staffList.stream().map(EduStaff::getId).collect(Collectors.toList()));
            if (CollUtil.isNotEmpty(resPage.getRecords())) {
                for (StasticStaffResultByDayVO record : resPage.getRecords()) {
                    EduStaff staff = staffGroup.get(record.getStaffId()).get(0);
                    // 遍历教职工部门做出部门数据
                    if (CollUtil.isNotEmpty(staff.getStaffDept())) {
                        StringBuilder sb = new StringBuilder();
                        for (EduStaffDept staffDept : staff.getStaffDept()) {
                            Dept dept = commonService.getDept(staffDept.getDeptId());
                            if (dept != null) {
                                sb.append(staffDept.getDeptName())
                                    .append(",");
                            }
                        }
                        if (sb.length() > 0) {
                            record.setDeptName(sb.substring(0, sb.length() - 1));
                        }
                    }
                    if (StrUtil.isBlank(record.getDeptName())) {
                        record.setDeptName("无");
                    }
                    record.setStaffName(staff.getName());
                    record.setMobile(staff.getMobile());
                    record.setWeek(FebsUtil.convertWeekCN(record.getDate().getDayOfWeek().getValue()));
                }
            }
        }
        return resPage;
    }

    @Override
    public void exportStasticStaffResultByDay(CurrentUser currentUser, StasticStaffResultDTO dto,
        HttpServletResponse response) {
        List<EduStaff> staffList = listStaff(currentUser, dto);
        if (CollUtil.isNotEmpty(staffList)) {
            // 按id分组方便遍历
            Map<Long, List<EduStaff>> staffGroup = staffList.stream().collect(Collectors.groupingBy(EduStaff::getId));
            List<StasticStaffResultByDayVO> res = eduAttendanceStaffResultMapper.listStaffResultStasticDay(dto,
                staffList.stream().map(EduStaff::getId).collect(Collectors.toList()));
            if (CollUtil.isNotEmpty(res)) {
                for (StasticStaffResultByDayVO record : res) {
                    EduStaff staff = staffGroup.get(record.getStaffId()).get(0);
                    if (CollUtil.isNotEmpty(staff.getStaffDept())) {
                        StringBuilder sb = new StringBuilder();
                        // 遍历教职工部门做出部门数据
                        for (EduStaffDept staffDept : staff.getStaffDept()) {
                            Dept dept = commonService.getDept(staffDept.getDeptId());
                            if (dept != null) {
                                sb.append(staffDept.getDeptName())
                                    .append(",");
                            }
                        }
                        if (sb.length() > 0) {
                            record.setDeptName(sb.substring(0, sb.length() - 1));
                        }
                    }
                    if (StrUtil.isBlank(record.getDeptName())) {
                        record.setDeptName("无");
                    }
                    record.setStaffName(staff.getName());
                    record.setMobile(staff.getMobile());
                    record.setWeek(FebsUtil.convertWeekCN(record.getDate().getDayOfWeek().getValue()));
                }
                ExcelUtil.export("按日统计数据.xlsx", StasticStaffResultByDayVO.class, res, response);
            }
        }
    }

    @Override
    public IPage<StasticStaffResultByMonthVO> stasticStaffResultByMonth(CurrentUser currentUser,
        StasticStaffResultDTO dto) {
        IPage<StasticStaffResultByMonthVO> resPage = null;
        List<EduStaff> staffList = listStaff(currentUser, dto);
        if (CollUtil.isNotEmpty(staffList)) {
            // 按id分组方便遍历
            Map<Long, List<EduStaff>> staffGroup = staffList.stream().collect(Collectors.groupingBy(EduStaff::getId));
            resPage = eduAttendanceStaffResultMapper.listStaffResultStasticMonthPage(dto.toPage(), dto,
                staffList.stream().map(EduStaff::getId).collect(Collectors.toList()));
            if (CollUtil.isNotEmpty(resPage.getRecords())) {
                for (StasticStaffResultByMonthVO record : resPage.getRecords()) {
                    EduStaff staff = staffGroup.get(record.getStaffId()).get(0);
                    if (CollUtil.isNotEmpty(staff.getStaffDept())) {
                        StringBuilder sb = new StringBuilder();
                        // 遍历教职工部门做出部门数据
                        for (EduStaffDept staffDept : staff.getStaffDept()) {
                            Dept dept = commonService.getDept(staffDept.getDeptId());
                            if (dept != null) {
                                sb.append(staffDept.getDeptName())
                                    .append(",");
                            }
                        }
                        if (sb.length() > 0) {
                            record.setDeptName(sb.substring(0, sb.length() - 1));
                        }
                    }
                    if (StrUtil.isBlank(record.getDeptName())) {
                        record.setDeptName("无");
                    }
                    record.setStaffName(staff.getName());
                    record.setMobile(staff.getMobile());
                }
            }
        }
        return resPage;
    }

    @Override
    public void exportStasticStaffResultByMonth(CurrentUser currentUser, StasticStaffResultDTO dto,
        HttpServletResponse response) {
        List<StasticStaffResultByMonthVO> resList = null;
        List<EduStaff> staffList = listStaff(currentUser, dto);
        if (CollUtil.isNotEmpty(staffList)) {
            // 按id分组方便遍历
            Map<Long, List<EduStaff>> staffGroup = staffList.stream().collect(Collectors.groupingBy(EduStaff::getId));
            resList = eduAttendanceStaffResultMapper.listStaffResultStasticMonth(dto,
                staffList.stream().map(EduStaff::getId).collect(Collectors.toList()));
            if (CollUtil.isNotEmpty(resList)) {
                for (StasticStaffResultByMonthVO record : resList) {
                    EduStaff staff = staffGroup.get(record.getStaffId()).get(0);
                    if (CollUtil.isNotEmpty(staff.getStaffDept())) {
                        StringBuilder sb = new StringBuilder();
                        // 遍历教职工部门做出部门数据
                        for (EduStaffDept staffDept : staff.getStaffDept()) {
                            Dept dept = commonService.getDept(staffDept.getDeptId());
                            if (dept != null) {
                                sb.append(staffDept.getDeptName())
                                    .append(",");
                            }
                        }
                        if (sb.length() > 0) {
                            record.setDeptName(sb.substring(0, sb.length() - 1));
                        }
                    }
                    record.setStaffName(staff.getName());
                    record.setMobile(staff.getMobile());
                }
            }
        }
        ExcelUtil.export("按月统计数据.xlsx", StasticStaffResultByMonthVO.class, resList, response);
    }

    @Override
    public StasticStaffExcepByDayVO stasticStaffExcepByDay(StasticStaffExcepByDayDTO dto) {
        List<EduAttendanceStaffResult> resultList = eduAttendanceStaffResultMapper
            .selectList(Wrappers.<EduAttendanceStaffResult>lambdaQuery()
                .between(EduAttendanceStaffResult::getAttendanceDate, dto.getStartDate(), dto.getEndDate())
                .eq(EduAttendanceStaffResult::getStaffId, dto.getStaffId())
                .orderByAsc(
                    Arrays.asList(EduAttendanceStaffResult::getAttendanceDate, EduAttendanceStaffResult::getStartStiTime)));
        StasticStaffExcepByDayVO vo = new StasticStaffExcepByDayVO();
        // 不为空才做数据
        if (CollUtil.isNotEmpty(resultList)) {
            vo.setDayList(
                resultList.stream().map(EduAttendanceStaffResult::getAttendanceDate).distinct().sorted()
                    .collect(Collectors.toList()));
            vo.setLateList(resultList.stream().filter(x -> x.getStatus().equals(1) || x.getStatus().equals(4))
                .collect(Collectors.toList()));
            vo.setLeaveList(resultList.stream().filter(x -> x.getStatus().equals(2) || x.getStatus().equals(4))
                .collect(Collectors.toList()));
            vo.setNotArriveList(resultList.stream().filter(x -> x.getStatus().equals(3)).collect(Collectors.toList()));
        }
        return vo;
    }

    @Override
    public CountVO countRangeExcepStaff(StasticStaffExcepByDayDTO dto) {
        return eduAttendanceStaffResultMapper.countRangeExcepStaff(dto);
    }

    @Override
    public StaRecordCountVO staRecordCount(LocalDate startDate, LocalDate endDate, List<Long> schoolIds) {
        StaRecordCountVO vo = new StaRecordCountVO();
        if (CollUtil.isNotEmpty(schoolIds)) {
            StasticStaffExcepByDayDTO staffCountDTO = new StasticStaffExcepByDayDTO();
            staffCountDTO.setStartDate(startDate);
            staffCountDTO.setEndDate(endDate);
            staffCountDTO.setSchoolIds(schoolIds);
            CountVO studentCount = eduAttendanceResultMapper.countStudentAttendance(null, schoolIds, startDate, endDate);
            CountVO staffCount = eduAttendanceStaffResultMapper.countRangeExcepStaff(staffCountDTO);
            vo.setStudentNormal(studentCount.getNormal());
            vo.setStudentLate(studentCount.getLate());
            vo.setStudentNotArrive(studentCount.getNotArrive());
            vo.setStudentRequest(studentCount.getRequest());
            vo.setStaffNormal(staffCount.getNormal());
            vo.setStaffNotArrive(staffCount.getNotArrive());
            vo.setStaffLate(staffCount.getLate());
        }
        return vo;
    }

    @Override
    public List<EduStudent> getStudentDormInfo(Long schoolId, Long classId, String date, Integer time) {
        if (LocalDateTimeUtil.parseDate(date, DateTimeFormatter.ISO_DATE).isAfter(LocalDateTimeUtil.now().toLocalDate())) {
            return null;
        }
        //找出所有的宿舍生
        List<EduStudent> studentList = remoteStudentService.listStudentByClassId(
            ListStudentDTO.builder().classId(classId).onlyId(true).build())
            .getData().stream().filter(
                x -> x.getType() == 1
            ).collect(Collectors.toList());
        //找出当前宿舍考勤规则

        //TODO 先处理一个考勤时间段的
        EduAttendanceRule eduAttendanceRule = eduAttendanceRuleMapper.selectList(Wrappers.lambdaQuery(new EduAttendanceRule())
            .eq(EduAttendanceRule::getType, 1)
            .eq(EduAttendanceRule::getStatus, 0)
            .eq(EduAttendanceRule::getSchoolId, schoolId)).get(0);

        if (ObjectUtils.isNull(eduAttendanceRule)) {
            return null;
        }

        List<EduAttendanceRuleTime> eduAttendanceRuleTimes = eduAttendanceRuleTimeMapper
            .selectList(Wrappers.lambdaQuery(new EduAttendanceRuleTime())
                .eq(EduAttendanceRuleTime::getRuleId, eduAttendanceRule.getId())
                .eq(EduAttendanceRuleTime::getDayOfWeek, LocalDateTimeUtil.parseDate(date).getDayOfWeek().getValue()));
        if (ObjectUtils.isEmpty(eduAttendanceRuleTimes)) {
            return null;
        }
        EduAttendanceRuleTime eduAttendanceRuleTime = eduAttendanceRuleTimes.get(0);

        for (EduStudent student : studentList) {
            //实时统计结果
            EduAttendanceRecord dormFirstByStudentIdAndRuleTimeId = eduAttendanceRecordMapper
                .getDormFirstByStudentIdAndRuleTimeId(student.getId(), date);

            if (ObjectUtils.isNotNull(dormFirstByStudentIdAndRuleTimeId)) {
                if (dormFirstByStudentIdAndRuleTimeId.getCreateTime().toLocalTime().isBefore(eduAttendanceRuleTime.getTime2())
                    && dormFirstByStudentIdAndRuleTimeId.getCreateTime().toLocalTime()
                    .isAfter(eduAttendanceRuleTime.getTime1())) {
                    //正常
                    student.setDormAttStatus(0);
                } else if (
                    dormFirstByStudentIdAndRuleTimeId.getCreateTime().toLocalTime().isAfter(eduAttendanceRuleTime.getTime2())
                        && dormFirstByStudentIdAndRuleTimeId.getCreateTime().toLocalTime()
                        .isBefore(eduAttendanceRuleTime.getTime3())) {
                    //晚归
                    student.setDormAttStatus(1);
                } else if (dormFirstByStudentIdAndRuleTimeId.getCreateTime().toLocalTime()
                    .isAfter(eduAttendanceRuleTime.getTime3())) {
                    //异常晚归
                    student.setDormAttStatus(2);
                } else {
                    //异常
                    student.setDormAttStatus(5);
                }
                student.setCheckInTime(dormFirstByStudentIdAndRuleTimeId.getCreateTime());
            } else {
                //旷宿
                if (LocalDateTimeUtil.parseDate(date, DateTimeFormatter.ISO_DATE).equals(LocalDateTimeUtil.now().toLocalDate())) {
                    //查询的就是今天的考勤  还未打卡
                    student.setDormAttStatus(4);
                } else {
                    student.setDormAttStatus(3);
                }
            }
        }
        return studentList;
    }

    private List<EduStaff> listStaff(CurrentUser currentUser, StasticStaffResultDTO dto) {
        ListStaffDTO.ListStaffDTOBuilder builder = ListStaffDTO.builder().schoolId(currentUser.getDeptId());
        if (dto.getDeptId() != null) {
            builder.deptId(dto.getDeptId());
        }
        List<EduStaff> staffList = remoteStaffService.listStaff(builder.build()).getData();
        if (CollUtil.isNotEmpty(staffList)) {
            // 判断关键词
            if (StrUtil.isNotBlank(dto.getKeyword())) {
                staffList = staffList.stream()
                    .filter(x -> (StrUtil.isNotBlank(x.getName()) && x.getName().contains(dto.getKeyword()))
                        || (StrUtil.isNotBlank(x.getMobile()) && x.getMobile().contains(dto.getKeyword())))
                    .collect(Collectors.toList());
            }
            // 判断规则
            if (dto.getRuleId() != null && CollUtil.isNotEmpty(staffList)) {
                // 该规则包含的教职工
                List<EduAttendanceStaffRuleStaff> ruleStaffs = eduAttendanceStaffRuleStaffMapper
                    .selectList(Wrappers.<EduAttendanceStaffRuleStaff>lambdaQuery()
                        .eq(EduAttendanceStaffRuleStaff::getRuleId, dto.getRuleId()));
                if (CollUtil.isNotEmpty(ruleStaffs)) {
                    List<Long> staffIds = ruleStaffs.stream().map(EduAttendanceStaffRuleStaff::getStaffId)
                        .collect(Collectors.toList());
                    staffList = staffList.stream().filter(x -> staffIds.contains(x.getId()))
                        .collect(Collectors.toList());
                } else {
                    staffList = null;
                }
            }
            // 判断管理员可见范围
            if (CollUtil.isNotEmpty(staffList) && currentUser.getDeptType() == 0 && !currentUser.getRolePerms()
                .contains(RoleEnum.ROLE_SCHOOL_PRINCIPAL.getValue())) {
                List<EduStaff> staffs = remoteStaffService.listStaff(ListStaffDTO.builder()
                    .mobile(currentUser.getUsername())
                    .schoolId(currentUser.getDeptId()).build()).getData();
                if (CollUtil.isNotEmpty(staffs)) {
                    List<Long> staffIds = eduAttendanceStaffRuleAdminRangeMapper
                        .listAdminStaffRange(staffs.get(0).getId());
                    if (CollUtil.isNotEmpty(staffIds)) {
                        staffList = staffList.stream().filter(x -> staffIds.contains(x.getId()))
                            .collect(Collectors.toList());
                    } else {
                        staffList = null;
                    }
                } else {
                    staffList = null;
                }
            }
        }
        return staffList;
    }
}
