package com.ym.mec.biz.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;

import com.ym.mec.auth.api.client.SysUserFeignService;
import com.ym.mec.auth.api.entity.SysUser;
import com.ym.mec.biz.dal.dao.ClassGroupDao;
import com.ym.mec.biz.dal.dao.ClassGroupStudentMapperDao;
import com.ym.mec.biz.dal.dao.CourseScheduleDao;
import com.ym.mec.biz.dal.dao.CourseScheduleStudentPaymentDao;
import com.ym.mec.biz.dal.dao.CourseScheduleTeacherSalaryDao;
import com.ym.mec.biz.dal.dao.MusicGroupPaymentCalenderDao;
import com.ym.mec.biz.dal.dao.MusicGroupQuitDao;
import com.ym.mec.biz.dal.dao.MusicGroupStudentFeeDao;
import com.ym.mec.biz.dal.dao.SchoolDao;
import com.ym.mec.biz.dal.dao.StudentAttendanceDao;
import com.ym.mec.biz.dal.dao.StudentDao;
import com.ym.mec.biz.dal.dao.SysConfigDao;
import com.ym.mec.biz.dal.dao.TeacherAttendanceDao;
import com.ym.mec.biz.dal.dto.CourseScheduleResponse;
import com.ym.mec.biz.dal.dto.ExportStudentAttendanceDto;
import com.ym.mec.biz.dal.dto.StudentAttendanceDto;
import com.ym.mec.biz.dal.dto.StudentAttendanceResponse;
import com.ym.mec.biz.dal.dto.StudentAttendanceStatisticsResponse;
import com.ym.mec.biz.dal.dto.StudentAttendanceStatusCountDto;
import com.ym.mec.biz.dal.dto.StudentPersonalAttendanceDto;
import com.ym.mec.biz.dal.dto.StudentStatusCountUtilEntity;
import com.ym.mec.biz.dal.entity.ClassGroup;
import com.ym.mec.biz.dal.entity.ClassGroupStudentMapper;
import com.ym.mec.biz.dal.entity.CourseSchedule;
import com.ym.mec.biz.dal.entity.CourseScheduleTeacherSalary;
import com.ym.mec.biz.dal.entity.School;
import com.ym.mec.biz.dal.entity.StudentAttendance;
import com.ym.mec.biz.dal.entity.SysConfig;
import com.ym.mec.biz.dal.entity.TeacherAttendance;
import com.ym.mec.biz.dal.enums.CourseStatusEnum;
import com.ym.mec.biz.dal.enums.GroupType;
import com.ym.mec.biz.dal.enums.MessageTypeEnum;
import com.ym.mec.biz.dal.enums.StudentAttendanceStatusEnum;
import com.ym.mec.biz.dal.enums.TeachModeEnum;
import com.ym.mec.biz.dal.enums.YesOrNoEnum;
import com.ym.mec.biz.dal.page.CourseHomeworkQueryInfo;
import com.ym.mec.biz.dal.page.ExportStudentAttendanceQueryInfo;
import com.ym.mec.biz.dal.page.StudentAttendanceQueryInfo;
import com.ym.mec.biz.service.StudentAttendanceService;
import com.ym.mec.biz.service.SysConfigService;
import com.ym.mec.biz.service.SysMessageService;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.page.QueryInfo;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;

@Service
public class StudentAttendanceServiceImpl extends BaseServiceImpl<Long, StudentAttendance> implements StudentAttendanceService {

    @Autowired
    private StudentAttendanceDao studentAttendanceDao;
    @Autowired
    private MusicGroupStudentFeeDao studentFeeDao;
    @Autowired
    private CourseScheduleDao courseScheduleDao;
    @Autowired
    private CourseScheduleStudentPaymentDao courseScheduleStudentPaymentDao;
    @Autowired
    private CourseScheduleTeacherSalaryDao courseScheduleTeacherSalaryDao;
    @Autowired
    private ClassGroupDao classGroupDao;
    @Autowired
    private ClassGroupStudentMapperDao classGroupStudentMapperDao;
    @Autowired
    private SysUserFeignService sysUserFeignService;
    @Autowired
    private MusicGroupStudentFeeDao musicGroupStudentFeeDao;
    @Autowired
    private MusicGroupQuitDao musicGroupQuitDao;
    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private SysConfigDao sysConfigDao;
    @Autowired
    private SchoolDao schoolDao;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private TeacherAttendanceDao teacherAttendanceDao;
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private MusicGroupPaymentCalenderDao musicGroupPaymentCalenderDao;

    @Override
    public BaseDAO<Long, StudentAttendance> getDAO() {
        return studentAttendanceDao;
    }

    private static final Logger log = LoggerFactory.getLogger(StudentAttendanceServiceImpl.class);

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public Map<String, Object> addStudentAttendances(StudentAttendanceDto studentAttendanceInfos) {
        List<StudentAttendance> studentAttendances = studentAttendanceInfos.getStudentAttendances();
        SysUser sysUser = sysUserFeignService.queryUserInfo();
        if (Objects.isNull(sysUser)) {
            throw new BizException("请登录");
        }
        studentDao.lockUser(sysUser.getId());
        if (CollectionUtils.isEmpty(studentAttendances)) {
            throw new BizException("无点名信息");
        }
        Long courseScheduleId = studentAttendances.get(0).getCourseScheduleId();
        CourseSchedule courseSchedule = courseScheduleDao.get(courseScheduleId);
        if (Objects.isNull(courseSchedule)) {
            throw new BizException("请指定课程");
        }
        if (TeachModeEnum.ONLINE.equals(courseSchedule.getTeachMode())) {
            throw new BizException("线上课程请进入房间授课");
        }
        Date date = new Date();
        SysConfig attendanceTimeRange;
        if (courseSchedule.getGroupType().equals(GroupType.MUSIC)) {
            TeacherAttendance teacherAttendanceInfo = teacherAttendanceDao.findByTeacherAttendanceInfo(sysUser.getId().longValue(), courseSchedule.getId());
            if(Objects.isNull(teacherAttendanceInfo)||Objects.isNull(teacherAttendanceInfo.getSignInStatus())){
                throw new BizException("您还未签到");
            }
            if (!DateUtil.isSameDay(courseSchedule.getEndClassTime(), date)) {
                throw new BizException("乐团课禁止跨天点名");
            }
        } else {
            SysConfig beforeAttendanceTimeRange = sysConfigService.findByParamName(SysConfigService.ENABLE_STUDENT_ATTENDANCE_TIME_RANGE_VIP);
            int courseStartTime = DateUtil.minutesBetween(date, courseSchedule.getStartClassTime());
            if (date.before(courseSchedule.getStartClassTime()) && courseStartTime > beforeAttendanceTimeRange.getParanValue(Integer.class)) {
                throw new BizException("VIP课开课前{}分钟禁止点名", beforeAttendanceTimeRange.getParanValue(Integer.class));
            }
            attendanceTimeRange = sysConfigService.findByParamName(SysConfigService.ENABLE_STUDENT_ATTENDANCE_TIME_RANGE_VIP);
            int courseEndTime = DateUtil.minutesBetween(courseSchedule.getEndClassTime(), date);
            if (date.after(courseSchedule.getEndClassTime()) && courseEndTime > attendanceTimeRange.getParanValue(Integer.class)) {
                throw new BizException("VIP课结束{}分钟后禁止点名",attendanceTimeRange.getParanValue(Integer.class));
            }
        }

        studentAttendanceDao.deleteStudentAttendancesByCourse(courseScheduleId);
        int classTimes = courseScheduleDao.countClassTimes(courseSchedule.getClassGroupId().longValue(), courseSchedule.getStartClassTime());
        studentAttendances.forEach(studentAttendance -> {
            studentAttendance.setTeacherId(sysUser.getId());
            studentAttendance.setCurrentClassTimes(classTimes + 1);
            StudentAttendance studentAttendanceInfo = studentAttendanceDao.getStudentAttendanceInfo(studentAttendance);
            studentAttendanceInfo.setGroupType(courseSchedule.getGroupType());
            studentAttendanceInfo.setMusicGroupId(courseSchedule.getMusicGroupId());
            if (studentAttendance.getStatus() != StudentAttendanceStatusEnum.DROP_OUT) {
                //判断是否为连续旷课
                if (StudentAttendanceStatusEnum.TRUANT.equals(studentAttendance.getStatus())
                        || StudentAttendanceStatusEnum.LEAVE.equals(studentAttendance.getStatus())) {
                    StudentAttendance studentLatestAttendanceInfo = studentAttendanceDao.getStudentLatestAttendanceInfo(studentAttendanceInfo);
                    if (Objects.nonNull(studentLatestAttendanceInfo)) {
                        if (StudentAttendanceStatusEnum.TRUANT.equals(studentLatestAttendanceInfo.getStatus())
                                || StudentAttendanceStatusEnum.LEAVE.equals(studentLatestAttendanceInfo.getStatus())) {
                            studentFeeDao.updateStudentAbsenteeismTimes(studentAttendanceInfo.getUserId(),
                                    studentAttendanceInfo.getMusicGroupId(),
                                    YesOrNoEnum.YES.getCode());
                        }
                    } else {
                        studentFeeDao.updateStudentAbsenteeismTimes(studentAttendanceInfo.getUserId(),
                                studentAttendanceInfo.getMusicGroupId(),
                                YesOrNoEnum.YES.getCode());
                    }
                } else {
                    studentFeeDao.updateStudentAbsenteeismTimes(studentAttendanceInfo.getUserId(),
                            studentAttendanceInfo.getMusicGroupId(),
                            YesOrNoEnum.NO.getCode());
                }
                if (studentAttendanceInfo.getStatus() == StudentAttendanceStatusEnum.NORMAL) {
                    studentAttendanceInfo.setSignInTime(courseSchedule.getStartClassTime());
                    studentAttendanceInfo.setSignOutTime(courseSchedule.getEndClassTime());
                }
                studentAttendanceDao.insert(studentAttendanceInfo);
            } else {
                studentFeeDao.updateStudentAbsenteeismTimes(studentAttendanceInfo.getUserId(),
                        studentAttendanceInfo.getMusicGroupId(),
                        YesOrNoEnum.NO.getCode());
            }
        });
        //获取正常签到的学生信息
        List<StudentAttendance> normal = studentAttendances.stream().filter(studentAttendance -> studentAttendance.getStatus() == StudentAttendanceStatusEnum.NORMAL).collect(Collectors.toList());
        if (normal != null && normal.size() > 0) {
            Map<Integer, String> receivers = new HashMap<>();
            normal.forEach(e -> {
                receivers.put(e.getUserId(), e.getUserId().toString());
            });
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.STUDENT_PUSH_NAMES_ACHIEVE,
                    receivers, null, 0, "2", "STUDENT", DateUtil.format(new Date(), DateUtil.DATE_FORMAT_MIN));
        }
        Map<StudentAttendanceStatusEnum, List<StudentAttendance>> studentAttendanceGroupByStatus = studentAttendances.stream().collect(Collectors.groupingBy(StudentAttendance::getStatus));
        Integer studentNum = studentAttendanceGroupByStatus.get(StudentAttendanceStatusEnum.NORMAL) == null ? 0 : studentAttendanceGroupByStatus.get(StudentAttendanceStatusEnum.NORMAL).size();
        Integer leaveStudentNum = studentAttendanceGroupByStatus.get(StudentAttendanceStatusEnum.LEAVE) == null ? 0 : studentAttendanceGroupByStatus.get(StudentAttendanceStatusEnum.LEAVE).size();
        courseSchedule.setStudentNum(studentNum);
        courseSchedule.setLeaveStudentNum(leaveStudentNum);
        courseScheduleDao.update(courseSchedule);

        Map<String, Object> result = new HashMap<>();

        if (!courseSchedule.getType().equals(CourseSchedule.CourseScheduleType.VIP)) {
            result.put("status",0);
            return result;
        }

        if (StringUtils.isBlank(studentAttendanceInfos.getSignInLongitudeLatitude())) {
            throw new BizException("未获取到您的位置");
        }

        School school = schoolDao.get(courseSchedule.getSchoolId());

        //是否在范围内
        boolean isInScore = true;
        String teacherAttendanceMemo = null;
        if (Objects.nonNull(school)) {
            if (StringUtils.isBlank(school.getLongitudeLatitude())) {
                if (studentAttendanceInfos.getUpdate().equals(YesOrNoEnum.YES.getCode())) {
                    school.setLongitudeLatitude(studentAttendanceInfos.getSignInLongitudeLatitude());
                    schoolDao.update(school);
                }
            } else {
                SysConfig sysConfig = sysConfigService.findByParamName(SysConfigService.ATTENDANCE_RANGE_VIP);
                double attendanceRange = Double.valueOf(sysConfig.getParanValue());
                double distance = MapUtil.distance(studentAttendanceInfos.getSignInLongitudeLatitude(),
                        school.getLongitudeLatitude());
                if (distance > attendanceRange) {
                    isInScore = false;
                    teacherAttendanceMemo = "打卡地点距离教学点" + distance + "米";
                }
            }
        }

        if(StringUtils.isEmpty(studentAttendanceInfos.getRemark())&&!isInScore){
            //当前为异常签到，请填写原因!
            result.put("status",2);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return result;
        }

        TeacherAttendance teacherAttendance = teacherAttendanceDao.findByTeacherAttendanceInfo(sysUser.getId().longValue(), courseSchedule.getId());

        teacherAttendance.setSignInLongitudeLatitude(studentAttendanceInfos.getSignInLongitudeLatitude());
        teacherAttendance.setSignOutLongitudeLatitude(studentAttendanceInfos.getSignInLongitudeLatitude());

        if (Objects.isNull(teacherAttendance)) {
            teacherAttendance = new TeacherAttendance();
            teacherAttendance.setTeacherId(sysUser.getId());
            teacherAttendance.setCreateTime(date);
        }

        teacherAttendance.setSignInTime(courseSchedule.getStartClassTime());
        teacherAttendance.setSignInStatus(isInScore?YesOrNoEnum.YES:YesOrNoEnum.NO);
        teacherAttendance.setSignOutTime(courseSchedule.getEndClassTime());
        teacherAttendance.setSignOutStatus(isInScore?YesOrNoEnum.YES:YesOrNoEnum.NO);
        teacherAttendance.setCurrentClassTimes(classTimes + 1);
        teacherAttendance.setRemark(studentAttendanceInfos.getRemark());
        teacherAttendance.setSignInDeviceNo(studentAttendanceInfos.getDeviceNum());
        teacherAttendance.setSignOutDeviceNo(studentAttendanceInfos.getDeviceNum());
        teacherAttendance.setSignOutAttachments(studentAttendanceInfos.getSignOutAttachments());
        if (Objects.nonNull(teacherAttendance.getId())) {
            teacherAttendanceDao.update(teacherAttendance);
        } else {
            teacherAttendanceDao.insert(teacherAttendance);
        }

        result.put("status",0);
        result.put("currentTime",date);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStudentAttendances(StudentAttendanceDto studentAttendanceInfo) {
        if (Objects.isNull(studentAttendanceInfo.getCourseScheduleId())) {
            throw new BizException("请指定课程编号");
        }
        List<StudentAttendance> studentAttendances = studentAttendanceInfo.getStudentAttendances();
        if (CollectionUtils.isEmpty(studentAttendances)) {
            throw new BizException("无点名信息");
        }
        CourseSchedule courseSchedule = courseScheduleDao.get(studentAttendanceInfo.getCourseScheduleId().longValue());
        if (Objects.isNull(courseSchedule)) {
            throw new BizException("课程不存在");
        }

        List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries = courseScheduleTeacherSalaryDao.findByCourseSchedule(studentAttendanceInfo.getCourseScheduleId());
        if (!CollectionUtils.isEmpty(courseScheduleTeacherSalaries) && Objects.nonNull(courseScheduleTeacherSalaries.get(0).getSettlementTime())) {
            throw new BizException("此课程已结算");
        }

        List<Integer> userIds = studentAttendances.stream().map(StudentAttendance::getUserId).collect(Collectors.toList());
        studentAttendanceDao.deleteStudentAttendancesByCourseAndUsers(studentAttendanceInfo.getCourseScheduleId().longValue(), userIds);
        for (StudentAttendance studentAttendance : studentAttendances) {
            studentAttendance.setGroupType(courseSchedule.getGroupType());
            studentAttendance.setMusicGroupId(courseSchedule.getMusicGroupId());
            studentAttendance.setClassGroupId(courseSchedule.getClassGroupId());
            studentAttendance.setTeacherId(courseSchedule.getActualTeacherId());
            studentAttendance.setCourseScheduleId(courseSchedule.getId());
        }
        studentAttendanceDao.addStudentAttendances(studentAttendances);
        List<StudentAttendance> allStudentAttendances = studentAttendanceDao.findByCourseId(studentAttendanceInfo.getCourseScheduleId());
        Map<StudentAttendanceStatusEnum, List<StudentAttendance>> studentAttendanceGroupByStatus = allStudentAttendances.stream().collect(Collectors.groupingBy(StudentAttendance::getStatus));
        Integer studentNum = studentAttendanceGroupByStatus.get(StudentAttendanceStatusEnum.NORMAL) == null ? 0 : studentAttendanceGroupByStatus.get(StudentAttendanceStatusEnum.NORMAL).size();
        Integer leaveStudentNum = studentAttendanceGroupByStatus.get(StudentAttendanceStatusEnum.LEAVE) == null ? 0 : studentAttendanceGroupByStatus.get(StudentAttendanceStatusEnum.LEAVE).size();
        courseSchedule.setStudentNum(studentNum);
        courseSchedule.setLeaveStudentNum(leaveStudentNum);
        courseScheduleDao.update(courseSchedule);
    }

    @Override
    public Map<String, Object> getCurrentCourseStudents(QueryInfo queryInfo) {
        Map<String, Object> result = new HashMap<>();

        PageInfo<StudentAttendance> pageInfo = super.queryPage(queryInfo);

        result.put("pageInfo", pageInfo);

        List<StudentStatusCountUtilEntity> stringIntegerMap = studentAttendanceDao.countStudentStatus(((StudentAttendanceQueryInfo) queryInfo).getClassGroupId());

        stringIntegerMap.forEach(studentStatusCount -> {
            switch (studentStatusCount.getStudentStatus()) {
                case LEAVE:
                    result.put("numberOfLeavePeoples", studentStatusCount.getNumberOfStudent());
                    break;
                default:
                    break;

            }
        });

        return result;
    }

    @Override
    public PageInfo<StudentPersonalAttendanceDto> getStudentPersonalAttendances(QueryInfo queryInfo) {
        PageInfo<StudentPersonalAttendanceDto> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<>();
        MapUtil.populateMap(params, queryInfo);

        List<StudentPersonalAttendanceDto> dataList = null;
        int count = studentAttendanceDao.queryStudentPersonalAttendancesCount(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = studentAttendanceDao.queryStudentPersonalAttendances(params);
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean leave(Integer userId, Long courseScheduleId, String remark) {

        CourseSchedule courseSchedule = courseScheduleDao.get(courseScheduleId);
        if (courseSchedule == null) {
            throw new BizException("课程编号异常");
        }

        if (courseSchedule.getStatus() == CourseStatusEnum.OVER) {
            throw new BizException("课程已结束");
        }

        Date date = new Date();

        int hours = 4;
        String str = sysConfigDao.findConfigValue(SysConfigService.ADVANCE_LEAVE_HOURS);
        if (StringUtils.isNotBlank(str)) {
            hours = Integer.parseInt(str);
        }

        if (DateUtil.addHours(date, hours).after(courseSchedule.getStartClassTime())) {
            throw new BizException("开课{}小时之前才可以请假", hours);
        }

        StudentAttendance studentAttendance = studentAttendanceDao.findByStatusAndCourseScheduleId(userId, courseScheduleId.intValue());
        if (Objects.isNull(studentAttendance)) {
            studentAttendance = new StudentAttendance();
        }
        studentAttendance.setClassGroupId(courseSchedule.getClassGroupId());
        studentAttendance.setCourseScheduleId(courseScheduleId);
        studentAttendance.setCreateTime(date);
        studentAttendance.setCurrentClassTimes(0);
        studentAttendance.setRemark(remark);
        studentAttendance.setStatus(StudentAttendanceStatusEnum.LEAVE);
        studentAttendance.setUserId(userId);
        studentAttendance.setGroupType(courseSchedule.getGroupType());
        studentAttendance.setMusicGroupId(courseSchedule.getMusicGroupId());

        if (Objects.nonNull(studentAttendance.getId())) {
            studentAttendanceDao.update(studentAttendance);
        } else {
            studentAttendanceDao.insert(studentAttendance);
        }

        ClassGroupStudentMapper classGroupStudentMapper = classGroupStudentMapperDao.query(courseSchedule.getClassGroupId(), userId);
        if (Objects.isNull(classGroupStudentMapper)) {
            throw new BizException("您不在此课程对应班级上");
        }

        Map<Integer, String> sendArgs = new HashMap<>();
        sendArgs.put(courseSchedule.getActualTeacherId(), courseSchedule.getActualTeacherId() + "");
        SysUser sysUser = sysUserFeignService.queryUserInfo();
        //发送消息至老师
        sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG,
                MessageTypeEnum.TEACHER_PUSH_STUDENT_LEAVE,
                sendArgs,
                null, 0, "1", "TEACHER", DateUtil.getDate(courseSchedule.getClassDate()), DateUtil.getTime(courseSchedule.getStartClassTime()), courseSchedule.getName(), sysUser.getUsername());
        return true;
    }

    @Override
    public PageInfo statisticsList(CourseHomeworkQueryInfo queryInfo) {

        if (Objects.isNull(queryInfo.getClassGroupId())) {
            throw new BizException("请指定班级");
        }

        PageInfo pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<String, Object>();
        MapUtil.populateMap(params, queryInfo);

        List<StudentAttendanceStatisticsResponse> dataList = null;
        int count = courseScheduleStudentPaymentDao.countStudentByClassGroup(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            ClassGroup classGroup = classGroupDao.get(queryInfo.getClassGroupId().intValue());
            if (Objects.isNull(classGroup)) {
                throw new BizException("班级不存在");
            }
            dataList = courseScheduleStudentPaymentDao.findStudentByClassGroup(params);
            //学生编号列表
            List<Integer> userIds = dataList.stream().map(StudentAttendanceStatisticsResponse::getUserId).collect(Collectors.toList());
            //学生-旷课次数关联集合
            List<Map<Integer, Integer>> continuousAbsenteeismTimesByUsersAndMusicGroup = musicGroupStudentFeeDao.findContinuousAbsenteeismTimesByUsersAndMusicGroup(classGroup.getMusicGroupId(), userIds);
            Map<Integer, Integer> continuousAbsenteeismTimesWithUser = MapUtil.convertIntegerMap(continuousAbsenteeismTimesByUsersAndMusicGroup);
            //学生签到记录列表
            List<StudentAttendanceResponse> studentAttendances = studentAttendanceDao.findByClassGroupAndUsers(queryInfo.getClassGroupId(), userIds);
            //学生签到状态统计列表
            List<StudentAttendanceStatusCountDto> studentAttendanceStatusCountDtos = studentAttendanceDao.countStudentAttendanceStatus(queryInfo.getClassGroupId(), userIds);
            Map<Integer, List<StudentAttendanceStatusCountDto>> studentAttendanceStatusCountDtosByUser = studentAttendanceStatusCountDtos.stream().collect(Collectors.groupingBy(StudentAttendanceStatusCountDto::getUserId));

            if (!CollectionUtils.isEmpty(studentAttendances)) {
                //学生-签到记录关联集合
                Map<Integer, List<StudentAttendanceResponse>> studentAttendanceByUser = studentAttendances.stream().collect(Collectors.groupingBy(StudentAttendanceResponse::getUserId));

                dataList.forEach(student -> {
                    //判断是否是连续签到
                    Integer continuousAbsenteeismTimes = continuousAbsenteeismTimesWithUser.get(student.getUserId());
                    if (Objects.nonNull(continuousAbsenteeismTimes)) {
                        if (continuousAbsenteeismTimes >= 3) {
                            student.setTruant(true);
                        }
                    }
                    //当前学生的签到状态统计
                    List<StudentAttendanceStatusCountDto> currentStudentAttendanceStatusCountDtos = studentAttendanceStatusCountDtosByUser.get(student.getUserId());
                    if (!CollectionUtils.isEmpty(currentStudentAttendanceStatusCountDtos)) {
                        Map<StudentAttendanceStatusEnum, Integer> statusAndNumMap = currentStudentAttendanceStatusCountDtos.stream().collect(Collectors.toMap(StudentAttendanceStatusCountDto::getStatus, StudentAttendanceStatusCountDto::getNum));
                        student.setNormalDay(statusAndNumMap.get(StudentAttendanceStatusEnum.NORMAL));
                        student.setTruantDay(statusAndNumMap.get(StudentAttendanceStatusEnum.TRUANT));
                        student.setLeaveDay(statusAndNumMap.get(StudentAttendanceStatusEnum.LEAVE));
                    }

                    //当前学生签到记录
                    List<StudentAttendanceResponse> studentAttendanceResps = studentAttendanceByUser.get(student.getUserId());
                    if (Objects.nonNull(studentAttendanceResps)) {
                        student.setList(studentAttendanceResps);
                    }
                });
            }
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public CourseScheduleResponse getStatisticsInfo(Integer classGroupId) {
        if (Objects.isNull(classGroupId)) {
            throw new BizException("请指定班级");
        }
        ClassGroup classGroup = classGroupDao.get(classGroupId);
        CourseScheduleResponse courseScheduleResp = new CourseScheduleResponse();
        if (Objects.nonNull(classGroup)) {
            if (Objects.nonNull(classGroup.getCurrentClassTimes()) && Objects.nonNull(classGroup.getTotalClassTimes())) {
                courseScheduleResp.setAlreadyInClass(classGroup.getCurrentClassTimes() + "/" + classGroup.getTotalClassTimes());
            }
            if (Objects.nonNull(classGroup.getStudentNum())) {
                courseScheduleResp.setStudentNum(classGroup.getStudentNum());
            }
            Date latestSignInDate = studentAttendanceDao.findLatestSignInDate(classGroupId);
            if (Objects.nonNull(latestSignInDate)) {
                courseScheduleResp.setClassDate(DateUtil.format(latestSignInDate, DateUtil.DATE_FORMAT_MIN));
                courseScheduleResp.setClassTime(DateUtil.date2Week(latestSignInDate));
            }
            courseScheduleResp.setLeagueNum(musicGroupQuitDao.countMusicGroupQuitNum(classGroup.getMusicGroupId()));
        }
        return courseScheduleResp;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addStudentAttendanceSignIn(Long firstCourseId, Integer userId,Long currentScheduleId) {
        Date date = new Date();
        ClassGroup classGroup = classGroupDao.findByCourseSchedule(currentScheduleId.intValue(), 0);
        StudentAttendance studentAttendance = studentAttendanceDao.findByStatusAndCourseScheduleId(userId, currentScheduleId.intValue());
        if (studentAttendance == null) {
            studentAttendance = new StudentAttendance();
            studentAttendance.setClassGroupId(classGroup.getId());
            studentAttendance.setCourseScheduleId(currentScheduleId);
            studentAttendance.setCurrentClassTimes(classGroup.getCurrentClassTimes() + 1);
            studentAttendance.setGroupType(classGroup.getGroupType());
            studentAttendance.setMusicGroupId(classGroup.getMusicGroupId());
            studentAttendance.setUserId(userId);
            studentAttendance.setCurrentScheduleId(firstCourseId);
            studentAttendanceDao.insert(studentAttendance);
        } else {
            studentAttendance.setCurrentScheduleId(firstCourseId);
            studentAttendance.setUpdateTime(date);
        }
        if (studentAttendance.getSignInTime() == null){
            CourseSchedule courseSchedule = courseScheduleDao.get(currentScheduleId);
            if (courseSchedule.getStudentNum() == null) {
                courseSchedule.setStudentNum(0);
            }
            courseSchedule.setStudentNum(courseSchedule.getStudentNum() + 1);
            studentAttendance.setTeacherId(courseSchedule.getActualTeacherId());
            //课程结束前进入，算正常
            studentAttendance.setStatus(StudentAttendanceStatusEnum.NORMAL);
            studentAttendance.setSignInTime(date);

            courseSchedule.setUpdateTime(date);
            courseScheduleDao.update(courseSchedule);
            studentAttendanceDao.update(studentAttendance);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addStudentAttendanceSignOut(Long firstCourseId, Integer userId) {
        Date date = new Date();
        CourseSchedule courseSchedule = courseScheduleDao.get(firstCourseId);
        String continueCourseTime = sysConfigDao.findConfigValue(SysConfigService.ONLINE_CONTINUE_COURSE_TIME);
        if (StringUtils.isEmpty(continueCourseTime)) {
            continueCourseTime = "5";
        }
        List<CourseSchedule> courseSchedules = new ArrayList<>();
        courseSchedules.add(courseSchedule);
        CourseSchedule cs = courseSchedule;
        while (true) {
            //获取当前课程的所有连堂课列表
            String courseClassDate = DateUtil.format(cs.getClassDate(), DateUtil.DEFAULT_PATTERN);
            String courseEndDateTime = DateUtil.format(cs.getEndClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
            cs = courseScheduleDao.queryStudentContinueCourse(cs, continueCourseTime, courseClassDate + " " + courseEndDateTime);
            //存在连堂课
            if (cs != null) {
                courseSchedules.add(cs);
            } else {
                break;
            }
        }
        String courseClassDate;
        String courseStartDateTime;
        String courseEndDateTime;
        Boolean isBreak = false;
        //签退之前看是不是第一次进入这个连堂课
        for (int i = courseSchedules.size() - 1; i >= 0; i--) {
            if(isBreak){
                break;
            }
            CourseSchedule schedule = courseSchedules.get(i);
            courseClassDate = DateUtil.format(schedule.getClassDate(), DateUtil.DEFAULT_PATTERN);
            courseStartDateTime = DateUtil.format(schedule.getStartClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
            Date startDateTime = DateUtil.stringToDate(courseClassDate + " " + courseStartDateTime, DateUtil.EXPANDED_DATE_TIME_FORMAT);
            courseEndDateTime = DateUtil.format(schedule.getEndClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
            Date endDateTime = DateUtil.stringToDate(courseClassDate + " " + courseEndDateTime, DateUtil.EXPANDED_DATE_TIME_FORMAT);
            //不是最后一节课
            if(i == courseSchedules.size() - 1){
                isBreak = upsetAttendance(userId,startDateTime,date,schedule,firstCourseId);
            }else {
                isBreak = upsetAttendance(userId,startDateTime,endDateTime,schedule,firstCourseId);
            }
        }
    }

    public Boolean upsetAttendance(Integer userId,Date signInTime,Date signOutTime,CourseSchedule courseSchedule,Long courseScheduleId){
        StudentAttendance studentAttendance = studentAttendanceDao.findByStatusAndCourseScheduleId(userId, courseSchedule.getId().intValue());
        Boolean flag = false;
        Long courseId = studentAttendanceDao.findFirstSign(courseScheduleId,userId);
        //如果当前课程是第一次签到的课程那么不更新签到时间，并且退出下次循环
        if(courseId != null && courseId.equals(courseSchedule.getId())){
            flag = true;
        }
        if(studentAttendance == null){
            studentAttendance = new StudentAttendance();
            studentAttendance.setSignOutTime(signOutTime);
            studentAttendance.setStatus(StudentAttendanceStatusEnum.NORMAL);
            studentAttendance.setSignInTime(signInTime);
            studentAttendance.setUserId(userId);
            studentAttendance.setTeacherId(courseSchedule.getActualTeacherId());
            studentAttendance.setClassGroupId(courseSchedule.getClassGroupId());
            studentAttendance.setCourseScheduleId(courseSchedule.getId());
            studentAttendance.setMusicGroupId(courseSchedule.getMusicGroupId());
            studentAttendance.setGroupType(courseSchedule.getGroupType());
            studentAttendance.setCurrentScheduleId(courseScheduleId);
            studentAttendanceDao.insert(studentAttendance);
        }else {
            studentAttendance.setSignOutTime(signOutTime);
            studentAttendance.setCurrentScheduleId(courseScheduleId);
            studentAttendance.setStatus(StudentAttendanceStatusEnum.NORMAL);
            if(!flag){
                studentAttendance.setSignInTime(signInTime);
            }
            studentAttendanceDao.update(studentAttendance);
        }
        return flag;
    }

    @Override
    public PageInfo<StudentAttendance> findStudentAttendance(QueryInfo queryInfo) {
        PageInfo<StudentAttendance> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<>();
        MapUtil.populateMap(params, queryInfo);

        List<StudentAttendance> dataList = new ArrayList<StudentAttendance>();
        int count = studentAttendanceDao.countStudentAttendance(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = studentAttendanceDao.findStudentAttendance(params);
            if (dataList == null) {
                dataList = new ArrayList<>();
            }
            
            for(StudentAttendance sa : dataList){
            	if(sa.getGroupType() == GroupType.MUSIC){
            		Set<Integer> userId = new HashSet<Integer>();
            		userId.add(sa.getUserId());
            		Map<Integer,String> paymentStatusMap = MapUtil.convertIntegerMap(musicGroupPaymentCalenderDao.queryUserCoursePaymentStatus(userId,sa.getMusicGroupId()));
            		sa.setPaymentStatus(paymentStatusMap.get(sa.getUserId()));
                }
            }
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public void repairStudentAttendance(Integer month) {
        //获取所有有老师考勤的课程记录，正常签退，当月线上课,有学员签到记录
        List<CourseSchedule> courseScheduleList = courseScheduleDao.queryScheduleByAttendance(month);
        //是否是连堂课
        String continueCourseTime = sysConfigDao.findConfigValue(SysConfigService.ONLINE_CONTINUE_COURSE_TIME);
        if (StringUtils.isEmpty(continueCourseTime)) {
            continueCourseTime = "5";
        }
        Date date = new Date();
        for (CourseSchedule courseSchedule : courseScheduleList) {
            log.info("课程编号: {}", courseSchedule.getId());
            //只会修复签到的学员考勤
            List<StudentAttendance> byCourseId = studentAttendanceDao.findByCourseId(courseSchedule.getId());
            if (byCourseId == null || byCourseId.size() == 0) {
                break;
            }
            List<CourseSchedule> courseSchedules = new ArrayList<>();
            CourseSchedule cs = courseSchedule;
            while (true) {
                //获取当前课程的所有连堂课列表
                String courseClassDate = DateUtil.format(cs.getClassDate(), DateUtil.DEFAULT_PATTERN);
                String courseEndDateTime = DateUtil.format(cs.getEndClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
                cs = courseScheduleDao.queryStudentContinueCourse(cs, continueCourseTime, courseClassDate + " " + courseEndDateTime);
                //存在连堂课
                if (cs != null) {
                    courseSchedules.add(cs);
                } else {
                    break;
                }
            }

//			List<BasicUserDto> students = courseScheduleStudentPaymentDao.findStudents(courseSchedule.getId());
            if (courseSchedules.size() > 0) {
                //获取当前课程老师总上课时长
                int totalMinutes = teacherAttendanceDao.getTotalMinutes(courseSchedule.getId(), courseSchedule.getActualTeacherId());
                int firstMinutes = courseScheduleDao.getSingleClassMinutes(courseSchedule.getId());
                totalMinutes -= firstMinutes;
                if (totalMinutes > 0) {
                    String courseClassDate;
                    String courseStartDateTime;
                    String courseEndDateTime;
                    for (CourseSchedule e : courseSchedules) {
                        //获取当前课程的单节课时长
                        int signClassMinutes = courseScheduleDao.getSingleClassMinutes(e.getId());
                        totalMinutes -= signClassMinutes;
                        if (totalMinutes >= 0) {
                            //补充签到签退时间
                            courseClassDate = DateUtil.format(e.getClassDate(), DateUtil.DEFAULT_PATTERN);
                            courseStartDateTime = DateUtil.format(e.getStartClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
                            courseEndDateTime = DateUtil.format(e.getEndClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
                            Date startDateTime = DateUtil.stringToDate(courseClassDate + " " + courseStartDateTime, DateUtil.EXPANDED_DATE_TIME_FORMAT);
                            Date endDateTime = DateUtil.stringToDate(courseClassDate + " " + courseEndDateTime, DateUtil.EXPANDED_DATE_TIME_FORMAT);
                            byCourseId.forEach(student -> {
                                StudentAttendance byStatusAndCourseScheduleId = studentAttendanceDao.findByStatusAndCourseScheduleId(student.getUserId(), e.getId().intValue());
                                if (byStatusAndCourseScheduleId == null) {
                                    byStatusAndCourseScheduleId = new StudentAttendance();
                                    byStatusAndCourseScheduleId.setSignOutTime(endDateTime);
                                    byStatusAndCourseScheduleId.setStatus(StudentAttendanceStatusEnum.NORMAL);
                                    byStatusAndCourseScheduleId.setSignInTime(startDateTime);
                                    byStatusAndCourseScheduleId.setUpdateTime(date);
                                    byStatusAndCourseScheduleId.setUserId(student.getUserId());
                                    byStatusAndCourseScheduleId.setTeacherId(e.getActualTeacherId());
                                    byStatusAndCourseScheduleId.setClassGroupId(e.getClassGroupId());
                                    byStatusAndCourseScheduleId.setMusicGroupId(e.getMusicGroupId());
                                    byStatusAndCourseScheduleId.setGroupType(e.getGroupType());
                                    byStatusAndCourseScheduleId.setCourseScheduleId(e.getId());
                                    studentAttendanceDao.insert(byStatusAndCourseScheduleId);
                                }
                            });
                        }
                    }
                }
            }
        }
    }

    @Override
    public StudentAttendance findByStatusAndCourseScheduleId(Long courseId, Integer userId) {
        return studentAttendanceDao.findByStatusAndCourseScheduleId(userId, courseId.intValue());
    }

    @Override
    public void cleanCourseStudentSignOut(Long courseId, Integer userId) {
        studentAttendanceDao.cleanCourseStudentSignOut(courseId, userId);
    }

    @Override
    public PageInfo<ExportStudentAttendanceDto> exportStudentAttendancesQueryPage(ExportStudentAttendanceQueryInfo queryInfo) {
        PageInfo<ExportStudentAttendanceDto> pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<>();
        MapUtil.populateMap(params, queryInfo);

        List<ExportStudentAttendanceDto> dataList = null;
        int count = studentAttendanceDao.countExportStudentAttendancesQueryPage(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = studentAttendanceDao.exportStudentAttendancesQueryPage(params);
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }
}
