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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ym.mec.auth.api.client.SysUserFeignService;
import com.ym.mec.auth.api.entity.SysUser;
import com.ym.mec.biz.dal.dao.*;
import com.ym.mec.biz.dal.dto.*;
import com.ym.mec.biz.dal.entity.*;
import com.ym.mec.biz.dal.enums.*;
import com.ym.mec.biz.dal.page.PracticeGroupQueryInfo;
import com.ym.mec.biz.dal.page.StudentBuyPracticeQueryInfo;
import com.ym.mec.biz.dal.page.StudentQueryInfo;
import com.ym.mec.biz.service.*;
import com.ym.mec.common.constant.CommonConstants;
import com.ym.mec.common.controller.BaseController;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.entity.HttpResponseResult;
import com.ym.mec.common.entity.ImGroupMember;
import com.ym.mec.common.entity.ImGroupModel;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.service.IdGeneratorService;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.im.ImFeignService;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;
import com.ym.mec.util.http.HttpUtil;
import com.ym.mec.util.string.MessageFormatter;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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 java.math.BigDecimal;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.stream.Collectors;

import static com.ym.mec.biz.dal.enums.SignInStatusEnum.*;

@Service
public class PracticeGroupServiceImpl extends BaseServiceImpl<Long, PracticeGroup> implements PracticeGroupService {

    @Autowired
    private PracticeGroupDao practiceGroupDao;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private SubjectDao subjectDao;
    @Autowired
    private OrganizationDao organizationDao;
    @Autowired
    private TeacherService teacherService;
    @Autowired
    private EmployeeDao employeeDao;
    @Autowired
    private ClassGroupDao classGroupDao;
    @Autowired
    private ClassGroupTeacherMapperDao classGroupTeacherMapperDao;
    @Autowired
    private ClassGroupTeacherSalaryDao classGroupTeacherSalaryDao;
    @Autowired
    private CourseScheduleDao courseScheduleDao;
    @Autowired
    private StudentAttendanceDao studentAttendanceDao;
    @Autowired
    private CourseScheduleTeacherSalaryDao courseScheduleTeacherSalaryDao;
    @Autowired
    private CourseScheduleStudentPaymentDao courseScheduleStudentPaymentDao;
    @Autowired
    private TeacherAttendanceDao teacherAttendanceDao;
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private ClassGroupStudentMapperDao classGroupStudentMapperDao;
    @Autowired
    private SysUserFeignService sysUserFeignService;
    @Autowired
    private TeacherDao teacherDao;
    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private MusicGroupDao musicGroupDao;
    @Autowired
    private ImFeignService imFeignService;
    @Autowired
    private ImUserFriendService imUserFriendService;
    @Autowired
    private TeacherFreeTimeDao teacherFreeTimeDao;
    @Autowired
    private CourseScheduleService courseScheduleService;
    @Autowired
    private IdGeneratorService idGeneratorService;
    @Autowired
    private PayService payService;
    @Autowired
    private StudentPaymentOrderService studentPaymentOrderService;
    @Autowired
    private SysUserCashAccountService sysUserCashAccountService;
    @Autowired
    private SysUserCashAccountDetailService sysUserCashAccountDetailService;
    @Autowired
    private SysConfigDao sysConfigDao;
    @Autowired
    private StudentPaymentOrderDao studentPaymentOrderDao;
    @Autowired
    private GroupClassService groupService;
    @Autowired
    private PracticeGroupSellPriceDao practiceGroupSellPriceDao;
    @Autowired
    private TeacherDefaultPracticeGroupSalaryDao teacherDefaultPracticeGroupSalaryDao;
    @Autowired
    private ContractService contractService;
    @Autowired
    private PracticeLessonApplyDao practiceLessonApplyDao;
    @Autowired
    private CourseScheduleEvaluateDao courseScheduleEvaluateDao;
    @Autowired
    private StudentRegistrationDao studentRegistrationDao;
    @Autowired
    private TeacherCourseRewardService teacherCourseRewardService;
    @Autowired
    private StudentPaymentRouteOrderService studentPaymentRouteOrderService;
    @Autowired
    private StudentGoodsSellDao studentGoodsSellDao;

    private static Map<Integer, Map<Integer, List<Integer>>> schoolSubjectTeachersMap;

    private static final Set<Integer> GUANGZHOU_SUBJECTIDS = new HashSet<Integer>(Arrays.asList(new Integer[]{2, 4, 5, 12, 13, 15, 23}));
    //广州分部编号
    private static final Integer GUANGZHOU_ORGAN_ID = 3;
    //大连分部编号
    private static final Integer DALIAN_ORGAN_ID = 27;
    //沈阳分部编号
    private static final Integer SHENYANG_ORGAN_ID = 6;

    private static Date activityStartDate, activityEndDate, applyStartDay, courseExpireDate;

    private static final Logger LOGGER = LoggerFactory
            .getLogger(PracticeGroupService.class);

    private static List<String> applyDayTimes = new ArrayList<>();

    private static final Set<Integer> ENABLE_APPLY_ORGANIDS = new HashSet<>(Arrays.asList(new Integer[]{28, 34, 37}));

    static {
        applyStartDay = DateUtil.stringToDate("2020-03-25 00:00:00");
        activityStartDate = DateUtil.stringToDate("2020-03-25 00:00:00");
        activityEndDate = DateUtil.stringToDate("2020-05-25 00:00:00");
        courseExpireDate = DateUtil.stringToDate("2020-06-01 00:00:00");

        schoolSubjectTeachersMap = new HashMap<>();
        Map<Integer, List<Integer>> subjectTeachersMap1 = new HashMap<>();
        subjectTeachersMap1.put(2, Arrays.asList(new Integer[]{100887, 100353, 100854, 102227}));
        subjectTeachersMap1.put(4, Arrays.asList(new Integer[]{102209, 101110, 102084, 102141, 100753}));
        subjectTeachersMap1.put(5, Arrays.asList(new Integer[]{102140, 100368, 100354}));
        subjectTeachersMap1.put(12, Arrays.asList(new Integer[]{101088, 100360, 100366}));
        subjectTeachersMap1.put(13, Arrays.asList(new Integer[]{100363, 100873}));
        subjectTeachersMap1.put(15, Arrays.asList(new Integer[]{100361, 100358, 102127, 100736}));
        subjectTeachersMap1.put(23, Arrays.asList(new Integer[]{100966, 102143, 101161}));
        Integer[] schoolIds1 = new Integer[]{258, 271, 780, 408, 262, 240, 270, 274, 242, 259, 508, 507, 241, 739, 272, 248, 427, 847, 991};
        for (Integer schoolId : schoolIds1) {
            schoolSubjectTeachersMap.put(schoolId, subjectTeachersMap1);
        }

        Map<Integer, List<Integer>> subjectTeachersMap2 = new HashMap<>();
        subjectTeachersMap2.put(2, Arrays.asList(new Integer[]{100812, 100401, 101683, 100362, 102147}));
        subjectTeachersMap2.put(4, Arrays.asList(new Integer[]{102096, 102145, 101386}));
        subjectTeachersMap2.put(5, Arrays.asList(new Integer[]{100939, 101797, 101809, 102121}));
        subjectTeachersMap2.put(12, Arrays.asList(new Integer[]{100375, 102210, 100698}));
        subjectTeachersMap2.put(13, Arrays.asList(new Integer[]{100873, 100876}));
        subjectTeachersMap2.put(15, Arrays.asList(new Integer[]{102294, 102081, 101111}));
        subjectTeachersMap2.put(23, Arrays.asList(new Integer[]{102142, 102122, 101188}));
        Integer[] schoolIds2 = new Integer[]{928, 1044, 243, 269, 250, 932, 502, 943, 954, 846, 266, 428, 819, 961, 492};
        for (Integer schoolId : schoolIds2) {
            schoolSubjectTeachersMap.put(schoolId, subjectTeachersMap2);
        }

        Map<Integer, List<Integer>> subjectTeachersMap3 = new HashMap<>();
        subjectTeachersMap3.put(2, Arrays.asList(new Integer[]{102139, 101650}));
        subjectTeachersMap3.put(4, Arrays.asList(new Integer[]{101796, 101387}));
        subjectTeachersMap3.put(5, Arrays.asList(new Integer[]{102229, 101388}));
        subjectTeachersMap3.put(12, Arrays.asList(new Integer[]{100806, 101950}));
        subjectTeachersMap3.put(13, Arrays.asList(new Integer[]{100806, 101950}));
        subjectTeachersMap3.put(15, Arrays.asList(new Integer[]{102203, 100640}));
        subjectTeachersMap3.put(23, Arrays.asList(new Integer[]{101915}));
        Integer[] schoolIds3 = new Integer[]{852, 267, 245, 848, 845, 862, 863, 854, 781, 864, 861, 931, 875};
        for (Integer schoolId : schoolIds3) {
            schoolSubjectTeachersMap.put(schoolId, subjectTeachersMap3);
        }

        applyDayTimes.add("09:00:00");
        applyDayTimes.add("09:30:00");
        applyDayTimes.add("10:00:00");
        applyDayTimes.add("11:00:00");
        applyDayTimes.add("11:30:00");
        applyDayTimes.add("12:00:00");
        applyDayTimes.add("14:00:00");
        applyDayTimes.add("14:30:00");
        applyDayTimes.add("15:00:00");
        applyDayTimes.add("16:00:00");
        applyDayTimes.add("16:30:00");
        applyDayTimes.add("17:00:00");
        applyDayTimes.add("19:00:00");
        applyDayTimes.add("19:30:00");
        applyDayTimes.add("20:00:00");
    }

    @Override
    public BaseDAO<Long, PracticeGroup> getDAO() {
        return practiceGroupDao;
    }

    @Override
    public List<CourseScheduleDto> findUserPracticeCourses(Integer userId) {
        List<CourseScheduleDto> studentPracticeCourses = courseScheduleDao.findStudentPracticeCourses(userId);
        if (!CollectionUtils.isEmpty(studentPracticeCourses)) {
            studentPracticeCourses.sort(Comparator.comparing(CourseScheduleDto::getStartClassTime));
            for (CourseScheduleDto studentPracticeCours : studentPracticeCourses) {
                if (studentPracticeCours.getCreateTime().after(applyStartDay)) {
                    studentPracticeCours.setIsNew(1);
                }
            }
        }
        return studentPracticeCourses;
    }

    @Override
    public Map<String, Object> checkCanApplyFreePracticeGroup(Integer userId) {
        Map<String, Object> result = new HashMap<>();
        SysConfig practiceFreeApplyExpireDateConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_FREE_APPLY_EXPIRE_DATE);
        result.put("practiceFreeApplyExpireDate", practiceFreeApplyExpireDateConfig.getParanValue());
        return result;
    }

    @Override
    public Integer searchTeacherId(PracticeGroup practiceGroup) {
        if (Objects.isNull(practiceGroup.getSubjectId())) {
            return null;
        }
        SysUser sysUser = sysUserFeignService.queryUserById(practiceGroup.getStudentId());
        if (Objects.isNull(sysUser)) {
            return null;
        }
        if (Objects.isNull(sysUser.getOrganId())) {
            return null;
        }

        //如果是广州分部的,并且声部满足条件的需要特殊处理
        if (sysUser.getOrganId().equals(GUANGZHOU_ORGAN_ID) && GUANGZHOU_SUBJECTIDS.contains(practiceGroup.getSubjectId())) {
            Integer teacherId = searchTeacherIdForGuangZhou(practiceGroup);
            if (Objects.nonNull(teacherId)) {
                return teacherId;
            }
        } else if (sysUser.getOrganId().equals(DALIAN_ORGAN_ID)) {
            sysUser.setOrganId(SHENYANG_ORGAN_ID);
        }

        List<TeacherBasicDto> organAndSubjectTeachers = teacherDao.findTeaTeachersByOrganAndSubject(sysUser.getOrganId(), practiceGroup.getSubjectId());
        if (CollectionUtils.isEmpty(organAndSubjectTeachers)) {
            return null;
        }

        boolean checkTeacherLeaveDate = true;
        SysConfig allTeacherLeaveDataConfig = sysConfigService.findByParamName(SysConfigService.TEACHER_LEAVE_DATA);
        JSONObject allTeacherLeaveData = JSONObject.parseObject(allTeacherLeaveDataConfig.getParanValue());
        JSONObject organTeacherLeaveData = null;
        if (Objects.nonNull(allTeacherLeaveData)) {
            organTeacherLeaveData = allTeacherLeaveData.getJSONObject(sysUser.getOrganId().toString());
        }
        if (Objects.isNull(organTeacherLeaveData)) {
            checkTeacherLeaveDate = false;
        }

        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        Integer practiceCourseMinutes = practiceCourseMinutesConfig.getParanValue(Integer.class);
        List<Integer> teacherIds = organAndSubjectTeachers.stream().map(TeacherBasicDto::getId).collect(Collectors.toList());
        teacherDao.lockTeachers(teacherIds);
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        if (now.before(applyStartDay)) {
            now = applyStartDay;
        }
        calendar.setTime(now);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        List<Integer> firstTeacherIds = new ArrayList<>();
        List<Integer> secondTeacherIds = new ArrayList<>();
        while (calendar.getTime().before(activityEndDate)) {
            calendar.add(Calendar.DATE, 1);
            Date applyStartDay = calendar.getTime();
            calendar.add(Calendar.DATE, 6);
            Date applyEndDay = calendar.getTime();
            if (applyEndDay.after(activityEndDate)) {
                applyEndDay = activityEndDate;
            }

            boolean excludeFirstTime = practiceGroup.getFirstCourseTime().before(applyStartDay) || (practiceGroup.getFirstCourseTime().after(applyEndDay) && !DateUtil.isSameDay(practiceGroup.getFirstCourseTime(), applyEndDay));
            boolean excludeSecondTime = practiceGroup.getSecondCourseTime().before(applyStartDay) || (practiceGroup.getSecondCourseTime().after(applyEndDay) && !DateUtil.isSameDay(practiceGroup.getFirstCourseTime(), applyEndDay));
            if (excludeFirstTime && excludeSecondTime) {
                continue;
            }

            Date firstMonday = DateUtil.getWeekDayWithDate(applyStartDay, Calendar.MONDAY);
            Date secondMonday = DateUtil.getWeekDayWithDate(applyEndDay, Calendar.MONDAY);
            Date secondSunday = DateUtil.getWeekDayWithDate(applyEndDay, Calendar.SUNDAY);

            List<CourseSchedule> allTeacherCourses = courseScheduleDao.findTeachersCoursesWithDateRange(teacherIds, firstMonday, secondSunday);

            Date firstClassEndTime = DateUtil.addMinutes(practiceGroup.getFirstCourseTime(), practiceCourseMinutes);
            Date secondClassEndTime = DateUtil.addMinutes(practiceGroup.getSecondCourseTime(), practiceCourseMinutes);

            Map<Integer, List<CourseSchedule>> teacherCoursesMap = allTeacherCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getActualTeacherId));
            for (Integer teacherId : teacherIds) {
                if (Objects.nonNull(organTeacherLeaveData)) {
                    checkTeacherLeaveDate = true;
                }
                JSONObject teacherLeaveData = null;
                if (checkTeacherLeaveDate) {
                    teacherLeaveData = organTeacherLeaveData.getJSONObject(teacherId.toString());
                }
                if (Objects.isNull(teacherLeaveData)) {
                    checkTeacherLeaveDate = false;
                }

                if (teacherId == 100473) {
                    checkTeacherLeaveDate = false;
                    Calendar tempCalendar = Calendar.getInstance();
                    tempCalendar.setFirstDayOfWeek(Calendar.MONDAY);
                    tempCalendar.setTime(practiceGroup.getFirstCourseTime());
                    if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY
                            || tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                        String date_str = DateUtil.dateToString(practiceGroup.getFirstCourseTime(), "yyyy-MM-dd");
                        Date date1 = DateUtil.stringToDate(date_str + " 11:30:00");
                        Date date2 = DateUtil.stringToDate(date_str + " 14:30:00");
                        Date date3 = DateUtil.stringToDate(date_str + " 20:30:00");
                        if (practiceGroup.getFirstCourseTime().before(date2)
                                && firstClassEndTime.after(date1)) {
                            continue;
                        }
                        if (date3.before(firstClassEndTime)) {
                            continue;
                        }
                    }
                    if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
                        continue;
                    }

                    tempCalendar.setTime(practiceGroup.getSecondCourseTime());
                    if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY
                            || tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                        String date_str = DateUtil.dateToString(practiceGroup.getSecondCourseTime(), "yyyy-MM-dd");
                        Date date1 = DateUtil.stringToDate(date_str + " 11:30:00");
                        Date date2 = DateUtil.stringToDate(date_str + " 14:30:00");
                        Date date3 = DateUtil.stringToDate(date_str + " 20:30:00");
                        if (practiceGroup.getSecondCourseTime().before(date2)
                                && firstClassEndTime.after(date1)) {
                            continue;
                        }
                        if (date3.before(secondClassEndTime)) {
                            continue;
                        }
                    }
                    if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
                        continue;
                    }
                }

                if (checkTeacherLeaveDate) {
                    String dateStr = DateUtil.dateToString(practiceGroup.getFirstCourseTime(), "yyyy-MM-dd");
                    JSONObject leaveDateData = teacherLeaveData.getJSONObject(dateStr);
                    if (Objects.nonNull(leaveDateData)) {
                        Date leaveStartTime = leaveDateData.getDate("leave_start_time");
                        Date leaveEndTime = leaveDateData.getDate("leave_end_time");
                        if (practiceGroup.getFirstCourseTime().before(leaveEndTime)
                                && firstClassEndTime.after(leaveStartTime)) {
                            continue;
                        }
                    }
                    String dateStr2 = DateUtil.dateToString(practiceGroup.getSecondCourseTime(), "yyyy-MM-dd");
                    JSONObject leaveDateData2 = teacherLeaveData.getJSONObject(dateStr2);
                    if (Objects.nonNull(leaveDateData2)) {
                        Date leaveStartTime = leaveDateData2.getDate("leave_start_time");
                        Date leaveEndTime = leaveDateData2.getDate("leave_end_time");
                        if (practiceGroup.getSecondCourseTime().before(leaveEndTime)
                                && secondClassEndTime.after(leaveStartTime)) {
                            continue;
                        }
                    }
                }

                List<CourseSchedule> teacherCourses = teacherCoursesMap.get(teacherId);
                if (CollectionUtils.isEmpty(teacherCourses)) {
                    if (!excludeFirstTime) {
                        firstTeacherIds.add(teacherId);
                    }
                    if (!excludeSecondTime) {
                        secondTeacherIds.add(teacherId);
                    }
                    continue;
                }

                List<CourseSchedule> firstWeekCourses = new ArrayList<>();
                List<CourseSchedule> secondWeekCourses = new ArrayList<>();
                List<CourseSchedule> applyDateRangeCourses = new ArrayList<>();
                for (CourseSchedule teacherCours : teacherCourses) {
                    if (teacherCours.getClassDate().before(secondMonday)) {
                        firstWeekCourses.add(teacherCours);
                    } else {
                        secondWeekCourses.add(teacherCours);
                    }
                    if (!teacherCours.getClassDate().before(applyStartDay)
                            && (teacherCours.getClassDate().before(applyEndDay) || DateUtil.isSameDay(teacherCours.getClassDate(), applyEndDay))) {
                        applyDateRangeCourses.add(teacherCours);
                    }
                }

                boolean firstWeekEnableApply = true;
                boolean secondWeekEnableApply = true;

                if (!CollectionUtils.isEmpty(firstWeekCourses)) {
                    Map<GroupType, Long> groupTypeCountFirstMap = firstWeekCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getGroupType, Collectors.counting()));
                    if (Objects.nonNull(groupTypeCountFirstMap.get(GroupType.PRACTICE)) && groupTypeCountFirstMap.get(GroupType.PRACTICE) >= 80) {
                        firstWeekEnableApply = false;
                    }
                }

                if (!CollectionUtils.isEmpty(firstWeekCourses)) {
                    Map<GroupType, Long> groupTypeCountSecondMap = secondWeekCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getGroupType, Collectors.counting()));
                    if (Objects.nonNull(groupTypeCountSecondMap.get(GroupType.PRACTICE)) && groupTypeCountSecondMap.get(GroupType.PRACTICE) >= 80) {
                        secondWeekEnableApply = false;
                    }
                }

                Date enableApplyStartDate = applyStartDay;
                Date enableApplyEndDate = secondSunday;
                if (!firstWeekEnableApply) {
                    enableApplyStartDate = secondMonday;
                }
                if (!secondWeekEnableApply) {
                    enableApplyEndDate = secondMonday;
                }

                if (DateUtil.isSameDay(enableApplyStartDate, enableApplyEndDate)) {
                    continue;
                }

                teacherCourses.sort(Comparator.comparing(CourseSchedule::getStartClassTime));
                boolean enableCreateFirstCourse = true;
                if (practiceGroup.getFirstCourseTime().before(enableApplyStartDate)
                        || practiceGroup.getFirstCourseTime().after(enableApplyEndDate)) {
                    enableCreateFirstCourse = false;
                }
                boolean enableCreateSecondCourse = true;
                if (practiceGroup.getSecondCourseTime().before(enableApplyStartDate)
                        || practiceGroup.getSecondCourseTime().after(enableApplyEndDate)) {
                    enableCreateSecondCourse = false;
                }
                for (int i = 0; i < applyDateRangeCourses.size(); i++) {
                    CourseSchedule preCourseSchedule = applyDateRangeCourses.get(i);

                    if (practiceGroup.getFirstCourseTime().before(preCourseSchedule.getEndClassTime())
                            && firstClassEndTime.after(preCourseSchedule.getStartClassTime())) {
                        enableCreateFirstCourse = false;
                    }

                    if (practiceGroup.getSecondCourseTime().before(preCourseSchedule.getEndClassTime())
                            && secondClassEndTime.after(preCourseSchedule.getStartClassTime())) {
                        enableCreateSecondCourse = false;
                    }
                }
                if (enableCreateFirstCourse) {
                    firstTeacherIds.add(teacherId);
                }
                if (enableCreateSecondCourse) {
                    secondTeacherIds.add(teacherId);
                }
            }
            if (!CollectionUtils.isEmpty(firstTeacherIds) && !CollectionUtils.isEmpty(secondTeacherIds)) {
                break;
            }
        }
        if (CollectionUtils.isEmpty(firstTeacherIds) || CollectionUtils.isEmpty(secondTeacherIds)) {
            return null;
        }

        List<Integer> includeTeacherIds = firstTeacherIds.stream().filter(firstTeacherId -> secondTeacherIds.contains(firstTeacherId)).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(includeTeacherIds)) {
            return null;
        }

        List<CourseScheduleTeacherSalary> studentRelateTeachers = courseScheduleTeacherSalaryDao.findStudentRelateTeachers(practiceGroup.getStudentId(), includeTeacherIds);
        Random random = new Random();
        if (!CollectionUtils.isEmpty(studentRelateTeachers)) {
            Map<GroupType, List<CourseScheduleTeacherSalary>> groupTypeTeachersMap = studentRelateTeachers.stream().collect(Collectors.groupingBy(CourseScheduleTeacherSalary::getGroupType));
            List<CourseScheduleTeacherSalary> vipGroupTeachers = groupTypeTeachersMap.get(GroupType.VIP);
            if (!CollectionUtils.isEmpty(vipGroupTeachers)) {
                int randomIndex = random.nextInt(vipGroupTeachers.size());
                return vipGroupTeachers.get(randomIndex).getUserId();
            }
            List<CourseScheduleTeacherSalary> musicGroupTeachers = groupTypeTeachersMap.get(GroupType.MUSIC);
            if (!CollectionUtils.isEmpty(musicGroupTeachers)) {
                int randomIndex = random.nextInt(musicGroupTeachers.size());
                return musicGroupTeachers.get(randomIndex).getUserId();
            }
        }

        List<Map<Integer, Integer>> teacherCourseNumMaps = courseScheduleTeacherSalaryDao.countTeacherCourses(includeTeacherIds, GroupType.PRACTICE);
        HashMap<Integer, Integer> teacherCourseNumMap = (HashMap<Integer, Integer>) MapUtil.convertIntegerMap(teacherCourseNumMaps);
        for (Integer includeTeacherId : includeTeacherIds) {
            if (!teacherCourseNumMap.containsKey(includeTeacherId)) {
                teacherCourseNumMap.put(includeTeacherId, 0);
            }
        }
        List<Integer> temp = new ArrayList<>();
        teacherCourseNumMap.entrySet().stream()
                .sorted((r1, r2) -> r1.getValue().compareTo(r2.getValue()))
                .forEach(result -> temp.add(result.getKey()));
        return temp.get(0);
    }

    @Override
    public Integer searchTeacherIdForGuangZhou(PracticeGroup practiceGroup) {
        List<MusicGroup> userMusicGroups = musicGroupDao.findUserMusicGroups(practiceGroup.getStudentId());
        if (CollectionUtils.isEmpty(userMusicGroups)) {
            return null;
        }

        List<Integer> userSchoolIds = userMusicGroups.stream()
                .filter(musicGroup -> Objects.nonNull(musicGroup.getSchoolId()) && schoolSubjectTeachersMap.containsKey(musicGroup.getSchoolId()))
                .map(MusicGroup::getSchoolId).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(userSchoolIds)) {
            return null;
        }

        Collections.shuffle(userSchoolIds);
        Integer schoolId = userMusicGroups.get(0).getSchoolId();

        List<Integer> teacherIds = schoolSubjectTeachersMap.get(schoolId).get(practiceGroup.getSubjectId());
        teacherDao.lockTeachers(teacherIds);

        boolean checkTeacherLeaveDate = true;
        SysConfig allTeacherLeaveDataConfig = sysConfigService.findByParamName(SysConfigService.TEACHER_LEAVE_DATA);
        JSONObject allTeacherLeaveData = JSONObject.parseObject(allTeacherLeaveDataConfig.getParanValue());
        JSONObject organTeacherLeaveData = null;
        if (Objects.nonNull(allTeacherLeaveData)) {
            organTeacherLeaveData = allTeacherLeaveData.getJSONObject(GUANGZHOU_ORGAN_ID.toString());
        }
        if (Objects.isNull(organTeacherLeaveData)) {
            checkTeacherLeaveDate = false;
        }

        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        Integer practiceCourseMinutes = practiceCourseMinutesConfig.getParanValue(Integer.class);
        Date now = new Date();
        if (now.before(applyStartDay)) {
            now = applyStartDay;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        List<Integer> firstTeacherIds = new ArrayList<>();
        List<Integer> secondTeacherIds = new ArrayList<>();
        while (calendar.getTime().before(activityEndDate)) {
            calendar.add(Calendar.DATE, 1);
            Date applyStartDay = calendar.getTime();
            calendar.add(Calendar.DATE, 6);
            Date applyEndDay = calendar.getTime();
            if (applyEndDay.after(activityEndDate)) {
                applyEndDay = activityEndDate;
            }
            boolean excludeFirstTime = practiceGroup.getFirstCourseTime().before(applyStartDay) || (practiceGroup.getFirstCourseTime().after(applyEndDay) && !DateUtil.isSameDay(practiceGroup.getFirstCourseTime(), applyEndDay));
            boolean excludeSecondTime = practiceGroup.getSecondCourseTime().before(applyStartDay) || (practiceGroup.getSecondCourseTime().after(applyEndDay) && !DateUtil.isSameDay(practiceGroup.getFirstCourseTime(), applyEndDay));
            if (excludeFirstTime && excludeSecondTime) {
                continue;
            }

            Date firstMonday = DateUtil.getWeekDayWithDate(applyStartDay, Calendar.MONDAY);
            Date secondMonday = DateUtil.getWeekDayWithDate(applyEndDay, Calendar.MONDAY);
            Date secondSunday = DateUtil.getWeekDayWithDate(applyEndDay, Calendar.SUNDAY);

            List<CourseSchedule> allTeacherCourses = courseScheduleDao.findTeachersCoursesWithDateRange(teacherIds, firstMonday, secondSunday);

            Date firstClassEndTime = DateUtil.addMinutes(practiceGroup.getFirstCourseTime(), practiceCourseMinutes);
            Date secondClassEndTime = DateUtil.addMinutes(practiceGroup.getSecondCourseTime(), practiceCourseMinutes);

            Map<Integer, List<CourseSchedule>> teacherCoursesMap = allTeacherCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getActualTeacherId));
            for (Integer teacherId : teacherIds) {
                if (Objects.nonNull(organTeacherLeaveData)) {
                    checkTeacherLeaveDate = true;
                }
                JSONObject teacherLeaveData = null;
                if (checkTeacherLeaveDate) {
                    teacherLeaveData = organTeacherLeaveData.getJSONObject(teacherId.toString());
                }
                if (Objects.isNull(teacherLeaveData)) {
                    checkTeacherLeaveDate = false;
                }

                if (checkTeacherLeaveDate) {
                    String dateStr = DateUtil.dateToString(practiceGroup.getFirstCourseTime(), "yyyy-MM-dd");
                    JSONObject leaveDateData = teacherLeaveData.getJSONObject(dateStr);
                    if (Objects.nonNull(leaveDateData)) {
                        Date leaveStartTime = leaveDateData.getDate("leave_start_time");
                        Date leaveEndTime = leaveDateData.getDate("leave_end_time");
                        if (practiceGroup.getFirstCourseTime().before(leaveEndTime)
                                && firstClassEndTime.after(leaveStartTime)) {
                            continue;
                        }
                    }
                    String dateStr2 = DateUtil.dateToString(practiceGroup.getSecondCourseTime(), "yyyy-MM-dd");
                    JSONObject leaveDateData2 = teacherLeaveData.getJSONObject(dateStr2);
                    if (Objects.nonNull(leaveDateData2)) {
                        Date leaveStartTime = leaveDateData2.getDate("leave_start_time");
                        Date leaveEndTime = leaveDateData2.getDate("leave_end_time");
                        if (practiceGroup.getSecondCourseTime().before(leaveEndTime)
                                && secondClassEndTime.after(leaveStartTime)) {
                            continue;
                        }
                    }
                }

                List<CourseSchedule> teacherCourses = teacherCoursesMap.get(teacherId);
                if (CollectionUtils.isEmpty(teacherCourses)) {
                    if (!excludeFirstTime) {
                        firstTeacherIds.add(teacherId);
                    }
                    if (!excludeSecondTime) {
                        secondTeacherIds.add(teacherId);
                    }
                    continue;
                }

                List<CourseSchedule> firstWeekCourses = new ArrayList<>();
                List<CourseSchedule> secondWeekCourses = new ArrayList<>();
                List<CourseSchedule> applyDateRangeCourses = new ArrayList<>();
                for (CourseSchedule teacherCours : teacherCourses) {
                    if (teacherCours.getClassDate().before(secondMonday)) {
                        firstWeekCourses.add(teacherCours);
                    } else {
                        secondWeekCourses.add(teacherCours);
                    }
                    if (!teacherCours.getClassDate().before(applyStartDay)
                            && (teacherCours.getClassDate().before(applyEndDay) || DateUtil.isSameDay(teacherCours.getClassDate(), applyEndDay))) {
                        applyDateRangeCourses.add(teacherCours);
                    }
                }

                boolean firstWeekEnableApply = true;
                boolean secondWeekEnableApply = true;

                if (!CollectionUtils.isEmpty(firstWeekCourses)) {
                    Map<GroupType, Long> groupTypeCountFirstMap = firstWeekCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getGroupType, Collectors.counting()));
                    if (Objects.nonNull(groupTypeCountFirstMap.get(GroupType.PRACTICE)) && groupTypeCountFirstMap.get(GroupType.PRACTICE) >= 80) {
                        firstWeekEnableApply = false;
                    }
                }

                if (!CollectionUtils.isEmpty(firstWeekCourses)) {
                    Map<GroupType, Long> groupTypeCountSecondMap = secondWeekCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getGroupType, Collectors.counting()));
                    if (Objects.nonNull(groupTypeCountSecondMap.get(GroupType.PRACTICE)) && groupTypeCountSecondMap.get(GroupType.PRACTICE) >= 80) {
                        secondWeekEnableApply = false;
                    }
                }

                Date enableApplyStartDate = applyStartDay;
                Date enableApplyEndDate = secondSunday;
                if (!firstWeekEnableApply) {
                    enableApplyStartDate = secondMonday;
                }
                if (!secondWeekEnableApply) {
                    enableApplyEndDate = secondMonday;
                }

                if (DateUtil.isSameDay(enableApplyStartDate, enableApplyEndDate)) {
                    continue;
                }

                teacherCourses.sort(Comparator.comparing(CourseSchedule::getStartClassTime));
                boolean enableCreateFirstCourse = true;
                if (practiceGroup.getFirstCourseTime().before(enableApplyStartDate)
                        || practiceGroup.getFirstCourseTime().after(enableApplyEndDate)) {
                    enableCreateFirstCourse = false;
                }
                boolean enableCreateSecondCourse = true;
                if (practiceGroup.getSecondCourseTime().before(enableApplyStartDate)
                        || practiceGroup.getSecondCourseTime().after(enableApplyEndDate)) {
                    enableCreateSecondCourse = false;
                }

                for (int i = 0; i < applyDateRangeCourses.size(); i++) {
                    CourseSchedule preCourseSchedule = applyDateRangeCourses.get(i);

                    if (practiceGroup.getFirstCourseTime().before(preCourseSchedule.getEndClassTime())
                            && firstClassEndTime.after(preCourseSchedule.getStartClassTime())) {
                        enableCreateFirstCourse = false;
                    }

                    if (practiceGroup.getSecondCourseTime().before(preCourseSchedule.getEndClassTime())
                            && secondClassEndTime.after(preCourseSchedule.getStartClassTime())) {
                        enableCreateSecondCourse = false;
                    }
                }
                if (enableCreateFirstCourse) {
                    firstTeacherIds.add(teacherId);
                }
                if (enableCreateSecondCourse) {
                    secondTeacherIds.add(teacherId);
                }
            }
            if (!CollectionUtils.isEmpty(firstTeacherIds) && !CollectionUtils.isEmpty(secondTeacherIds)) {
                break;
            }
        }
        if (CollectionUtils.isEmpty(firstTeacherIds) || CollectionUtils.isEmpty(secondTeacherIds)) {
            return null;
        }

        List<Integer> includeTeacherIds = firstTeacherIds.stream().filter(firstTeacherId -> secondTeacherIds.contains(firstTeacherId)).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(includeTeacherIds)) {
            return null;
        }

        List<Map<Integer, Integer>> teacherCourseNumMaps = courseScheduleTeacherSalaryDao.countTeacherCourses(includeTeacherIds, GroupType.PRACTICE);
        HashMap<Integer, Integer> teacherCourseNumMap = (HashMap<Integer, Integer>) MapUtil.convertIntegerMap(teacherCourseNumMaps);
        for (Integer includeTeacherId : includeTeacherIds) {
            if (!teacherCourseNumMap.containsKey(includeTeacherId)) {
                teacherCourseNumMap.put(includeTeacherId, 0);
            }
        }
        List<Integer> temp = new ArrayList<>();
        teacherCourseNumMap.entrySet().stream()
                .sorted((r1, r2) -> r1.getValue().compareTo(r2.getValue()))
                .forEach(result -> temp.add(result.getKey()));
        return temp.get(0);
    }

    @Override
    public Map getPracticeApplyParams(Integer userId) {
        Map result = new HashMap();
        SysConfig practiceSubjectIdListConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_SUBJECT_ID_LIST);
        SysConfig practiceApplyStartTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_START_TIME);
        SysConfig practiceApplyEndTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_END_TIME);
        SysConfig practiceApplyIntervalTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_INTERVAL_TIME);
        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        SysConfig practiceFreeApplyExpireDateConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_FREE_APPLY_EXPIRE_DATE);
        Student student = studentDao.get(userId);
        List<Subject> subjects = subjectDao.findBySubjectByIdList(practiceSubjectIdListConfig.getParanValue());
        List<Integer> subjectIds = Arrays.asList(practiceSubjectIdListConfig.getParanValue().split(",")).stream().mapToInt(idStr -> Integer.valueOf(idStr)).boxed().collect(Collectors.toList());
        List<Integer> userDefaultSubjectIds = Objects.isNull(student) || Objects.isNull(student.getSubjectIdList()) ? null : Arrays.asList(student.getSubjectIdList().split(",")).stream().mapToInt(idStr -> Integer.valueOf(idStr)).boxed().collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(userDefaultSubjectIds) && userDefaultSubjectIds.size() <= 1) {
            userDefaultSubjectIds = userDefaultSubjectIds.stream().filter(userDefaultSubjectId -> subjectIds.contains(userDefaultSubjectId)).collect(Collectors.toList());
        } else {
            userDefaultSubjectIds = new ArrayList<>();
        }

        Integer applyTimes = practiceGroupDao.countUserPracticeApplyRecord(userId);

        result.put("subjects", subjects);
        result.put("practiceApplyStartTime", practiceApplyStartTimeConfig.getParanValue());
        result.put("practiceApplyEndTime", practiceApplyEndTimeConfig.getParanValue());
        result.put("userDefaultSubjectIds", StringUtils.join(userDefaultSubjectIds.toArray(), ","));
        result.put("practiceApplyIntervalMinutes", practiceApplyIntervalTimeConfig.getParanValue(Integer.class));
        result.put("practiceCourseMinutes", practiceCourseMinutesConfig.getParanValue(Integer.class));
        result.put("practiceFreeApplyExpireDate", practiceFreeApplyExpireDateConfig.getParanValue());
        return result;
    }

    @Override
    public Object getPracticeApplySubjects() {
        SysConfig practiceSubjectIdListConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_SUBJECT_ID_LIST);
        List<Subject> subjects = subjectDao.findBySubjectByIdList(practiceSubjectIdListConfig.getParanValue());
        return subjects;
    }

    @Override
    public List<SimpleUserDto> getEnableApplyTeachers(Integer userId, Integer subjectId) {
        if (Objects.isNull(subjectId)) {
            throw new BizException("请选择声部");
        }
        SysUser sysUser = sysUserFeignService.queryUserById(userId);
        if (Objects.isNull(sysUser)) {
            throw new BizException("用户不存在");
        }
        if (Objects.isNull(sysUser.getOrganId())) {
            throw new BizException("未找到用户分部属性");
        }

        if (sysUser.getOrganId().equals(DALIAN_ORGAN_ID)) {
            sysUser.setOrganId(SHENYANG_ORGAN_ID);
        }

        List<Integer> includeTeacherIds = new ArrayList<>();
        List<ExtendTeacherBasicDto> organAndSubjectTeachers = teacherDao.findTeaTeachersByOrganAndSubject1(sysUser.getOrganId(), subjectId);
        if (CollectionUtils.isEmpty(organAndSubjectTeachers)) {
            return new ArrayList<>();
        }

        List<Integer> allTeacherIds = organAndSubjectTeachers.stream().map(TeacherBasicDto::getId).collect(Collectors.toList());

        List<CourseSchedule> userCourses = courseScheduleDao.findUserCourses(userId, allTeacherIds);

        if (!CollectionUtils.isEmpty(userCourses)) {
            Map<CourseSchedule.CourseScheduleType, List<CourseSchedule>> courseTypeCourseMap = userCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getType));
            List<CourseSchedule> vipCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.VIP);
            if (!CollectionUtils.isEmpty(vipCourses)) {
                for (CourseSchedule vipCourse : vipCourses) {
                    if (!includeTeacherIds.contains(vipCourse.getTeacherId())) {
                        includeTeacherIds.add(vipCourse.getTeacherId());
                    }
                }
            }
            List<CourseSchedule> singleCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.SINGLE);
            if (!CollectionUtils.isEmpty(singleCourses)) {
                for (CourseSchedule singleCourse : singleCourses) {
                    if (!includeTeacherIds.contains(singleCourse.getTeacherId())) {
                        includeTeacherIds.add(singleCourse.getTeacherId());
                    }
                }
            }
            List<CourseSchedule> highCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.HIGH);
            if (!CollectionUtils.isEmpty(highCourses)) {
                for (CourseSchedule highCourse : highCourses) {
                    if (!includeTeacherIds.contains(highCourse.getTeacherId())) {
                        includeTeacherIds.add(highCourse.getTeacherId());
                    }
                }
            }
        }

        for (ExtendTeacherBasicDto organAndSubjectTeacher : organAndSubjectTeachers) {
            if (!includeTeacherIds.contains(organAndSubjectTeacher.getId())) {
                includeTeacherIds.add(organAndSubjectTeacher.getId());
            }
        }

        if (CollectionUtils.isEmpty(new ArrayList<>(includeTeacherIds))) {
            return new ArrayList<>();
        }

        List<SimpleUserDto> userInfos = new ArrayList<>();
        Map<Integer, ExtendTeacherBasicDto> idTeacherMap = organAndSubjectTeachers.stream().collect(Collectors.toMap(ExtendTeacherBasicDto::getId, teacher -> teacher));
        for (Integer includeTeacherId : includeTeacherIds) {
            ExtendTeacherBasicDto teacher = idTeacherMap.get(includeTeacherId);
            userInfos.add(new SimpleUserDto(includeTeacherId, teacher.getRealName()));
        }

        return userInfos;
    }

    @Override
    public Map getTeacherFreeTimes(Integer userId, Integer teacherId, Date firstClassTime) {
        if (Objects.isNull(teacherId)) {
            throw new BizException("请选择教师");
        }
        SysUser student = sysUserFeignService.queryUserById(userId);
        if (Objects.isNull(student)) {
            throw new BizException("用户不存在");
        }
        if (Objects.isNull(student.getOrganId())) {
            throw new BizException("未找到用户分部属性");
        }
        Teacher teacher = teacherDao.get(teacherId);
        if (Objects.isNull(teacher)) {
            throw new BizException("教师不存在");
        }
        Map result = new HashMap();
        Set<Date> allTeacherFreeDates = new HashSet<>();

        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        Integer practiceCourseMinutes = practiceCourseMinutesConfig.getParanValue(Integer.class);

        boolean checkTeacherLeaveDate = true;
        SysConfig allTeacherLeaveDataConfig = sysConfigService.findByParamName(SysConfigService.TEACHER_LEAVE_DATA);
        JSONObject allTeacherLeaveData = JSONObject.parseObject(allTeacherLeaveDataConfig.getParanValue());
        if (Objects.isNull(allTeacherLeaveData)) {
            checkTeacherLeaveDate = false;
        }

        Set<Integer> disableApplyWeekNum = new HashSet<>();
        Set<Integer> disableApplyWeekDay = new HashSet<>();

        TeacherFreeTime teacherFreeTime = teacherFreeTimeDao.findTeacherFreeTime(teacherId);
        JSONObject teacherFreeTimes = new JSONObject();
        Integer maxTeacherCourses = null;
        int holiday = 0;
        if (Objects.nonNull(teacherFreeTime)) {
            if (Objects.nonNull(teacherFreeTime.getTotalTimes())) {
                maxTeacherCourses = teacherFreeTime.getTotalTimes();
            }
            if (Objects.nonNull(teacherFreeTime.getHoliday())) {
                holiday = DateUtil.normalWeekNumCalendarWeekNumMap.get(teacherFreeTime.getHoliday());
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getMonday())) {
                teacherFreeTimes.put(String.valueOf(Calendar.MONDAY), JSON.parseArray(teacherFreeTime.getMonday()));
            } else {
                disableApplyWeekDay.add(Calendar.MONDAY);
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getTuesday())) {
                teacherFreeTimes.put(String.valueOf(Calendar.TUESDAY), JSON.parseArray(teacherFreeTime.getTuesday()));
            } else {
                disableApplyWeekDay.add(Calendar.TUESDAY);
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getWednesday())) {
                teacherFreeTimes.put(String.valueOf(Calendar.WEDNESDAY), JSON.parseArray(teacherFreeTime.getWednesday()));
            } else {
                disableApplyWeekDay.add(Calendar.WEDNESDAY);
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getThursday())) {
                teacherFreeTimes.put(String.valueOf(Calendar.THURSDAY), JSON.parseArray(teacherFreeTime.getThursday()));
            } else {
                disableApplyWeekDay.add(Calendar.THURSDAY);
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getFriday())) {
                teacherFreeTimes.put(String.valueOf(Calendar.FRIDAY), JSON.parseArray(teacherFreeTime.getFriday()));
            } else {
                disableApplyWeekDay.add(Calendar.FRIDAY);
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getSaturday())) {
                teacherFreeTimes.put(String.valueOf(Calendar.SATURDAY), JSON.parseArray(teacherFreeTime.getSaturday()));
            } else {
                disableApplyWeekDay.add(Calendar.SATURDAY);
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getSunday())) {
                teacherFreeTimes.put(String.valueOf(Calendar.SUNDAY), JSON.parseArray(teacherFreeTime.getSunday()));
            } else {
                disableApplyWeekDay.add(Calendar.SUNDAY);
            }
        } else {
            result.put("teacherFreeDays", new ArrayList<>());
            return result;
        }

        SysConfig practiceFreeApplyExpireDateConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_FREE_APPLY_EXPIRE_DATE);
        Date practiceFreeApplyExpireDate = DateUtil.stringToDate(practiceFreeApplyExpireDateConfig.getParanValue());

        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.setTime(now);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DATE, 1);
        Date applyStartDate = calendar.getTime();
        Date applyEndDate = practiceFreeApplyExpireDate;
        Date firstMonday = DateUtil.getWeekDayWithDate(applyStartDate, Calendar.MONDAY);
        Date secondSunday = DateUtil.getWeekDayWithDate(applyEndDate, Calendar.SUNDAY);

        Date yesterday = null;
        Date tomorrow = null;
        if (Objects.nonNull(firstClassTime)) {
            Calendar tempCalendar = Calendar.getInstance();
            tempCalendar.setTime(firstClassTime);
            tempCalendar.add(Calendar.DATE, -1);
            yesterday = tempCalendar.getTime();
            tempCalendar.add(Calendar.DATE, 2);
            tomorrow = tempCalendar.getTime();
        }

        List<Date> enableApplyDates = getEnableApplyDates(applyStartDate, applyEndDate);
        List<CourseSchedule> allTeacherCourses = courseScheduleDao.findTeacherCoursesWithDateRange(teacherId, firstMonday, secondSunday);
        allTeacherCourses.sort(Comparator.comparing(CourseSchedule::getStartClassTime));

        List<CourseSchedule> applyDateRangeCourses = new ArrayList<>();
        Calendar courseCalendar = Calendar.getInstance();
        courseCalendar.setFirstDayOfWeek(Calendar.MONDAY);
        int courseWeekNum = 0;
        int practiceCourseNum = 0;
        for (int i = 0; i < allTeacherCourses.size(); i++) {
            CourseSchedule teacherCourse = allTeacherCourses.get(i);
            if ((!teacherCourse.getClassDate().before(applyStartDate))
                    && (teacherCourse.getClassDate().before(applyEndDate) || DateUtil.isSameDay(teacherCourse.getClassDate(), applyEndDate))) {
                applyDateRangeCourses.add(teacherCourse);
            }
            courseCalendar.setTime(teacherCourse.getClassDate());
            if (i == 0 && teacherCourse.getGroupType().equals(GroupType.PRACTICE)) {
                practiceCourseNum += 1;
                courseWeekNum = courseCalendar.get(Calendar.WEEK_OF_YEAR);
            }
            if (Objects.nonNull(maxTeacherCourses) && practiceCourseNum >= maxTeacherCourses) {
                disableApplyWeekNum.add(courseWeekNum);
            }
            if (courseCalendar.get(Calendar.WEEK_OF_YEAR) != courseWeekNum) {
                courseWeekNum = courseCalendar.get(Calendar.WEEK_OF_YEAR);
                practiceCourseNum = 0;
            }
            if (teacherCourse.getGroupType().equals(GroupType.PRACTICE)) {
                practiceCourseNum += 1;
            }
        }

        JSONObject teacherLeaveData = null;
        if (checkTeacherLeaveDate) {
            teacherLeaveData = allTeacherLeaveData.getJSONObject(teacherId.toString());
        }

        if (Objects.isNull(teacherLeaveData)) {
            checkTeacherLeaveDate = false;
        }

        if (CollectionUtils.isEmpty(applyDateRangeCourses)) {
            for (Date enableApplyDate : enableApplyDates) {
                if (Objects.nonNull(firstClassTime)) {
                    if (DateUtil.isSameDay(enableApplyDate, firstClassTime)) {
                        continue;
                    }
                    if (DateUtil.isSameDay(enableApplyDate, tomorrow)) {
                        continue;
                    }
                    if (DateUtil.isSameDay(enableApplyDate, yesterday)) {
                        continue;
                    }
                }
                Date enableApplyDateCourseEndTime = DateUtil.addMinutes(enableApplyDate, practiceCourseMinutes);
                if (teacherId == 100473) {
                    checkTeacherLeaveDate = false;
                    Calendar tempCalendar = Calendar.getInstance();
                    tempCalendar.setFirstDayOfWeek(Calendar.MONDAY);
                    tempCalendar.setTime(enableApplyDate);
                    if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY
                            || tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                        String date_str = DateUtil.dateToString(enableApplyDate, "yyyy-MM-dd");
                        Date date1 = DateUtil.stringToDate(date_str + " 11:30:00");
                        Date date2 = DateUtil.stringToDate(date_str + " 14:30:00");
                        Date date3 = DateUtil.stringToDate(date_str + " 20:30:00");
                        if (enableApplyDate.before(date2)
                                && enableApplyDateCourseEndTime.after(date1)) {
                            continue;
                        }
                        if (date3.before(enableApplyDateCourseEndTime)) {
                            continue;
                        }
                    }
                    if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
                        continue;
                    }
                }
                if (checkTeacherLeaveDate) {
                    String dateStr = DateUtil.dateToString(enableApplyDate, "yyyy-MM-dd");
                    JSONObject leaveDateData = teacherLeaveData.getJSONObject(dateStr);
                    if (Objects.nonNull(leaveDateData)) {
                        Date leaveStartTime = leaveDateData.getDate("leave_start_time");
                        Date leaveEndTime = leaveDateData.getDate("leave_end_time");
                        if (enableApplyDateCourseEndTime.after(leaveStartTime)
                                && enableApplyDate.before(leaveEndTime)) {
                            continue;
                        }
                    }
                }
                calendar.setTime(enableApplyDate);
                int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
                if (disableApplyWeekDay.contains(dayOfWeek)) {
                    continue;
                }
                JSONArray teacherWeekDayFreeTimes = teacherFreeTimes.getJSONArray(String.valueOf(dayOfWeek));
                if (Objects.nonNull(teacherWeekDayFreeTimes)) {
                    LocalTime enableApplyStartTime = LocalDateTime.ofInstant(enableApplyDate.toInstant(), DateUtil.zoneId).toLocalTime();
                    LocalTime enableApplyEndTime = LocalDateTime.ofInstant(enableApplyDateCourseEndTime.toInstant(), DateUtil.zoneId).toLocalTime();
                    boolean isInclude = false;
                    for (Object teacherWeekDayFreeTimeObject : teacherWeekDayFreeTimes) {
                        JSONObject teacherWeekDayFreeTime = JSONObject.parseObject(teacherWeekDayFreeTimeObject.toString());
                        LocalTime teacherFreeStartTime = LocalTime.parse(teacherWeekDayFreeTime.getString("startTime"), DateUtil.timeFormatter);
                        LocalTime teacherFreeEndTime = LocalTime.parse(teacherWeekDayFreeTime.getString("endTime"), DateUtil.timeFormatter);
                        if (enableApplyEndTime.compareTo(teacherFreeEndTime) <= 0
                                && enableApplyStartTime.compareTo(teacherFreeStartTime) >= 0) {
                            isInclude = true;
                            break;
                        }
                    }
                    if (!isInclude) {
                        continue;
                    }
                }
                if (dayOfWeek != holiday) {
                    allTeacherFreeDates.add(enableApplyDate);
                }
            }
            if (!CollectionUtils.isEmpty(allTeacherFreeDates)) {
                List<Date> tempDates = new ArrayList<>();
                if (Objects.nonNull(firstClassTime)) {
                    calendar.setTime(firstClassTime);
                    calendar.add(Calendar.DATE, -6);
                    calendar.set(Calendar.HOUR_OF_DAY, 0);
                    calendar.set(Calendar.MINUTE, 0);
                    calendar.set(Calendar.SECOND, 0);
                    calendar.set(Calendar.MILLISECOND, 0);
                    applyStartDate = calendar.getTime();
                    calendar.setTime(firstClassTime);
                    calendar.add(Calendar.DATE, 7);
                    calendar.set(Calendar.HOUR_OF_DAY, 0);
                    calendar.set(Calendar.MINUTE, 0);
                    calendar.set(Calendar.SECOND, 0);
                    calendar.set(Calendar.MILLISECOND, 0);
                    applyEndDate = calendar.getTime();
                    for (Date teacherFreeDate : allTeacherFreeDates) {
                        if (!teacherFreeDate.before(applyStartDate)
                                && !teacherFreeDate.after(applyEndDate)) {
                            tempDates.add(teacherFreeDate);
                        }
                    }
                }
                if (CollectionUtils.isEmpty(tempDates)) {
                    tempDates = new ArrayList<>(allTeacherFreeDates);
                }
                tempDates.sort(Comparator.comparing(Date::getTime));
                Set<String> days = tempDates.stream()
                        .map(date -> DateUtil.dateToString(date, "yyyy-MM-dd")).collect(Collectors.toSet());
                List<Date> tempDays = days.stream().map(dateStr -> DateUtil.stringToDate(dateStr, "yyyy-MM-dd")).collect(Collectors.toList());
                tempDays.sort(Comparator.comparing(Date::getTime));
                result.put("teacherFreeDays", tempDays.stream().map(day -> DateUtil.dateToString(day, "yyyy-MM-dd")).collect(Collectors.toList()));
                result.put("teacherFreeDates", tempDates);
            } else {
                result.put("teacherFreeDays", new ArrayList<>());
            }
            return result;
        }

        List<Date> tempEnableApplyDates = new ArrayList<>();
        for (Date enableApplyDate : enableApplyDates) {
            Date enableApplyDateCourseEndTime = DateUtil.addMinutes(enableApplyDate, practiceCourseMinutes);
            if (Objects.nonNull(firstClassTime)) {
                if (DateUtil.isSameDay(enableApplyDate, firstClassTime)) {
                    continue;
                }
                if (DateUtil.isSameDay(enableApplyDate, tomorrow)) {
                    continue;
                }
                if (DateUtil.isSameDay(enableApplyDate, yesterday)) {
                    continue;
                }
            }

            if (teacherId == 100473) {
                checkTeacherLeaveDate = false;
                Calendar tempCalendar = Calendar.getInstance();
                tempCalendar.setFirstDayOfWeek(Calendar.MONDAY);
                tempCalendar.setTime(enableApplyDate);
                if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY
                        || tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                    String date_str = DateUtil.dateToString(enableApplyDate, "yyyy-MM-dd");
                    Date date1 = DateUtil.stringToDate(date_str + " 11:30:00");
                    Date date2 = DateUtil.stringToDate(date_str + " 14:30:00");
                    Date date3 = DateUtil.stringToDate(date_str + " 20:30:00");
                    if (enableApplyDate.before(date2)
                            && enableApplyDateCourseEndTime.after(date1)) {
                        continue;
                    }
                    if (date3.before(enableApplyDateCourseEndTime)) {
                        continue;
                    }
                }
                if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
                    continue;
                }
            }
            if (checkTeacherLeaveDate) {
                String dateStr = DateUtil.dateToString(enableApplyDate, "yyyy-MM-dd");
                JSONObject leaveDateData = teacherLeaveData.getJSONObject(dateStr);
                if (Objects.nonNull(leaveDateData)) {
                    Date leaveStartTime = leaveDateData.getDate("leave_start_time");
                    Date leaveEndTime = leaveDateData.getDate("leave_end_time");
                    if (enableApplyDateCourseEndTime.after(leaveStartTime)
                            && enableApplyDate.before(leaveEndTime)) {
                        continue;
                    }
                }
            }
            calendar.setTime(enableApplyDate);

            int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
            if (disableApplyWeekDay.contains(dayOfWeek)) {
                continue;
            }
            JSONArray teacherWeekDayFreeTimes = teacherFreeTimes.getJSONArray(String.valueOf(dayOfWeek));
            if (Objects.nonNull(teacherWeekDayFreeTimes)) {
                LocalTime enableApplyStartTime = LocalDateTime.ofInstant(enableApplyDate.toInstant(), DateUtil.zoneId).toLocalTime();
                LocalTime enableApplyEndTime = LocalDateTime.ofInstant(enableApplyDateCourseEndTime.toInstant(), DateUtil.zoneId).toLocalTime();
                boolean isInclude = false;
                for (Object teacherWeekDayFreeTimeObject : teacherWeekDayFreeTimes) {
                    JSONObject teacherWeekDayFreeTime = JSONObject.parseObject(teacherWeekDayFreeTimeObject.toString());
                    LocalTime teacherFreeStartTime = LocalTime.parse(teacherWeekDayFreeTime.getString("startTime"), DateUtil.timeFormatter);
                    LocalTime teacherFreeEndTime = LocalTime.parse(teacherWeekDayFreeTime.getString("endTime"), DateUtil.timeFormatter);
                    if (enableApplyEndTime.compareTo(teacherFreeEndTime) <= 0
                            && enableApplyStartTime.compareTo(teacherFreeStartTime) >= 0) {
                        isInclude = true;
                        break;
                    }
                }
                if (!isInclude) {
                    continue;
                }
            }

            if (dayOfWeek == holiday) {
                continue;
            }

            if (!disableApplyWeekNum.contains(calendar.get(Calendar.WEEK_OF_YEAR))) {
                tempEnableApplyDates.add(enableApplyDate);
            }
        }

        applyDateRangeCourses.sort(Comparator.comparing(CourseSchedule::getStartClassTime));
        for (int j = 0; j < applyDateRangeCourses.size(); j++) {
            CourseSchedule preCourseSchedule = applyDateRangeCourses.get(j);
            CourseSchedule backCourseSchedule = j >= applyDateRangeCourses.size() - 1 ? preCourseSchedule : applyDateRangeCourses.get(j + 1);
            if (preCourseSchedule.getEndClassTime().after(backCourseSchedule.getEndClassTime())) {
                backCourseSchedule.setEndClassTime(preCourseSchedule.getEndClassTime());
            }
            if (j == 0) {
                for (Date enableApplyDate : tempEnableApplyDates) {
                    if (Objects.nonNull(firstClassTime) && DateUtil.isSameDay(enableApplyDate, firstClassTime)) {
                        continue;
                    }
                    Date enableApplyDateCourseEndTime = DateUtil.addMinutes(enableApplyDate, practiceCourseMinutes);
                    if (!enableApplyDateCourseEndTime.after(preCourseSchedule.getStartClassTime())) {
                        allTeacherFreeDates.add(enableApplyDate);
                    }
                }
            }
            if (j == applyDateRangeCourses.size() - 1) {
                for (Date enableApplyDate : tempEnableApplyDates) {
                    if (Objects.nonNull(firstClassTime) && DateUtil.isSameDay(enableApplyDate, firstClassTime)) {
                        continue;
                    }
                    if (!enableApplyDate.before(backCourseSchedule.getEndClassTime())) {
                        allTeacherFreeDates.add(enableApplyDate);
                    }
                }
            }
            if (j >= applyDateRangeCourses.size() - 1) {
                break;
            }
            for (Date enableApplyDate : tempEnableApplyDates) {
                if (Objects.nonNull(firstClassTime) && DateUtil.isSameDay(enableApplyDate, firstClassTime)) {
                    continue;
                }
                Date enableApplyDateCourseEndTime = DateUtil.addMinutes(enableApplyDate, practiceCourseMinutes);
                if (!enableApplyDate.before(preCourseSchedule.getEndClassTime())
                        && !enableApplyDateCourseEndTime.after(backCourseSchedule.getStartClassTime())) {
                    allTeacherFreeDates.add(enableApplyDate);
                }
            }
        }
        if (!CollectionUtils.isEmpty(allTeacherFreeDates)) {
            List<Date> tempDates = new ArrayList<>();
            if (Objects.nonNull(firstClassTime)) {
                calendar.setTime(firstClassTime);
                calendar.add(Calendar.DATE, -6);
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                applyStartDate = calendar.getTime();
                calendar.setTime(firstClassTime);
                calendar.add(Calendar.DATE, 7);
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                applyEndDate = calendar.getTime();
                for (Date teacherFreeDate : allTeacherFreeDates) {
                    if (!teacherFreeDate.before(applyStartDate)
                            && !teacherFreeDate.after(applyEndDate)) {
                        tempDates.add(teacherFreeDate);
                    }
                }
            }
            if (CollectionUtils.isEmpty(tempDates)) {
                tempDates = new ArrayList<>(allTeacherFreeDates);
            }
            tempDates.sort(Comparator.comparing(Date::getTime));
            Set<String> days = tempDates.stream()
                    .map(date -> DateUtil.dateToString(date, "yyyy-MM-dd")).collect(Collectors.toSet());
            List<Date> tempDays = days.stream().map(dateStr -> DateUtil.stringToDate(dateStr, "yyyy-MM-dd")).collect(Collectors.toList());
            tempDays.sort(Comparator.comparing(Date::getTime));
            result.put("teacherFreeDays", tempDays.stream().map(day -> DateUtil.dateToString(day, "yyyy-MM-dd")).collect(Collectors.toList()));
            result.put("teacherFreeDates", tempDates);
        }
        return result;
    }

    @Override
    public Map getTeachersFreeTimes(Integer userId, Integer subjectId, Date firstClassTime) {
        if (Objects.isNull(subjectId)) {
            throw new BizException("请选择声部");
        }
        SysUser sysUser = teacherDao.getUser(userId);
        if (Objects.isNull(sysUser)) {
            throw new BizException("用户不存在");
        }
        if (Objects.isNull(sysUser.getOrganId())) {
            throw new BizException("未找到用户分部属性");
        }
        Map result = new HashMap();
        Set<Date> allTeacherFreeDates = new HashSet<>();
        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        Integer practiceCourseMinutes = practiceCourseMinutesConfig.getParanValue(Integer.class);
        List<Integer> teacherIds = new ArrayList<>();

        //如果是广州分部的,并且声部满足条件的需要特殊处理
        if (sysUser.getOrganId().equals(GUANGZHOU_ORGAN_ID) && GUANGZHOU_SUBJECTIDS.contains(subjectId)) {
            List<MusicGroup> userMusicGroups = musicGroupDao.findUserMusicGroups(userId);
            if (!CollectionUtils.isEmpty(userMusicGroups)) {
                Collections.shuffle(userMusicGroups);
                Integer schoolId = userMusicGroups.get(0).getSchoolId();
                if (schoolSubjectTeachersMap.containsKey(schoolId)) {
                    teacherIds = schoolSubjectTeachersMap.get(schoolId).get(subjectId);
                }
            }
        } else if (sysUser.getOrganId().equals(DALIAN_ORGAN_ID)) {
            sysUser.setOrganId(SHENYANG_ORGAN_ID);
        }

        if (CollectionUtils.isEmpty(teacherIds)) {
            List<TeacherBasicDto> organAndSubjectTeachers = teacherDao.findTeaTeachersByOrganAndSubject(sysUser.getOrganId(), subjectId);
            if (CollectionUtils.isEmpty(organAndSubjectTeachers)) {
                result.put("teacherFreeDates", new ArrayList<>());
                return result;
            }
            teacherIds = organAndSubjectTeachers.stream().map(TeacherBasicDto::getId).collect(Collectors.toList());
        }
        teacherDao.lockTeachers(teacherIds);

        boolean checkTeacherLeaveDate = true;
        SysConfig allTeacherLeaveDataConfig = sysConfigService.findByParamName(SysConfigService.TEACHER_LEAVE_DATA);
        JSONObject allTeacherLeaveData = JSONObject.parseObject(allTeacherLeaveDataConfig.getParanValue());
        JSONObject organTeacherLeaveData = null;
        if (Objects.nonNull(allTeacherLeaveData)) {
            organTeacherLeaveData = allTeacherLeaveData.getJSONObject(sysUser.getOrganId().toString());
        }
        if (Objects.isNull(organTeacherLeaveData)) {
            checkTeacherLeaveDate = false;
        }

        SysConfig practiceFreeApplyExpireDateConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_FREE_APPLY_EXPIRE_DATE);
        Date practiceFreeApplyExpireDate = DateUtil.stringToDate(practiceFreeApplyExpireDateConfig.getParanValue());

        Date now = new Date();
        if (now.before(applyStartDay)) {
            now = applyStartDay;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        while (calendar.getTime().before(practiceFreeApplyExpireDate)) {
            calendar.add(Calendar.DATE, 1);
            Date applyStartDay = calendar.getTime();
            calendar.add(Calendar.DATE, 6);
            Date applyEndDay = calendar.getTime();
            if (applyEndDay.after(practiceFreeApplyExpireDate)) {
                applyEndDay = practiceFreeApplyExpireDate;
            }
            Date firstMonday = DateUtil.getWeekDayWithDate(applyStartDay, Calendar.MONDAY);
            Date secondMonday = DateUtil.getWeekDayWithDate(applyEndDay, Calendar.MONDAY);
            Date secondSunday = DateUtil.getWeekDayWithDate(applyEndDay, Calendar.SUNDAY);

            List<Date> enableApplyDates = getEnableApplyDates(applyStartDay, applyEndDay);
            List<CourseSchedule> allTeacherCourses = courseScheduleDao.findTeachersCoursesWithDateRange(teacherIds, firstMonday, secondSunday);
            HashSet<Integer> excludeTeacherIds = new HashSet<>();
            if (Objects.nonNull(firstClassTime)) {
                Date firstClassEndTime = DateUtil.addMinutes(firstClassTime, practiceCourseMinutes);
                for (CourseSchedule teacherCourse : allTeacherCourses) {
                    if (firstClassTime.before(teacherCourse.getEndClassTime())
                            && firstClassEndTime.after(teacherCourse.getStartClassTime())) {
                        excludeTeacherIds.add(teacherCourse.getActualTeacherId());
                    }
                }
            }
            Map<Integer, List<CourseSchedule>> teacherCoursesMap = allTeacherCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getActualTeacherId));
            for (Integer teacherId : teacherIds) {
                if (excludeTeacherIds.contains(teacherId)) {
                    continue;
                }
                if (Objects.nonNull(organTeacherLeaveData)) {
                    checkTeacherLeaveDate = true;
                }
                JSONObject teacherLeaveData = null;
                if (checkTeacherLeaveDate) {
                    teacherLeaveData = organTeacherLeaveData.getJSONObject(teacherId.toString());
                }

                if (Objects.isNull(teacherLeaveData)) {
                    checkTeacherLeaveDate = false;
                }
                List<CourseSchedule> teacherCourses = teacherCoursesMap.get(teacherId);
                if (CollectionUtils.isEmpty(teacherCourses)) {
                    for (Date enableApplyDate : enableApplyDates) {
                        if (Objects.nonNull(firstClassTime) && DateUtil.isSameDay(enableApplyDate, firstClassTime)) {
                            continue;
                        }
                        Date enableApplyDateCourseEndTime = DateUtil.addMinutes(enableApplyDate, practiceCourseMinutes);
                        if (teacherId == 100473) {
                            checkTeacherLeaveDate = false;
                            Calendar tempCalendar = Calendar.getInstance();
                            tempCalendar.setFirstDayOfWeek(Calendar.MONDAY);
                            tempCalendar.setTime(enableApplyDate);
                            if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY
                                    || tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                                String date_str = DateUtil.dateToString(enableApplyDate, "yyyy-MM-dd");
                                Date date1 = DateUtil.stringToDate(date_str + " 11:30:00");
                                Date date2 = DateUtil.stringToDate(date_str + " 14:30:00");
                                Date date3 = DateUtil.stringToDate(date_str + " 20:30:00");
                                if (enableApplyDate.before(date2)
                                        && enableApplyDateCourseEndTime.after(date1)) {
                                    continue;
                                }
                                if (date3.before(enableApplyDateCourseEndTime)) {
                                    continue;
                                }
                            }
                            if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
                                continue;
                            }
                        }
                        if (checkTeacherLeaveDate) {
                            String dateStr = DateUtil.dateToString(enableApplyDate, "yyyy-MM-dd");
                            JSONObject leaveDateData = teacherLeaveData.getJSONObject(dateStr);
                            if (Objects.nonNull(leaveDateData)) {
                                Date leaveStartTime = leaveDateData.getDate("leave_start_time");
                                Date leaveEndTime = leaveDateData.getDate("leave_end_time");
                                if (enableApplyDateCourseEndTime.after(leaveStartTime)
                                        && enableApplyDate.before(leaveEndTime)) {
                                    continue;
                                }
                            }
                        }
                        allTeacherFreeDates.add(enableApplyDate);
                    }
                    break;
                }

                List<CourseSchedule> firstWeekCourses = new ArrayList<>();
                List<CourseSchedule> secondWeekCourses = new ArrayList<>();
                List<CourseSchedule> applyDateRangeCourses = new ArrayList<>();
                for (CourseSchedule teacherCours : teacherCourses) {
                    if (teacherCours.getClassDate().before(secondMonday)) {
                        firstWeekCourses.add(teacherCours);
                    } else {
                        secondWeekCourses.add(teacherCours);
                    }
                    if ((!teacherCours.getClassDate().before(applyStartDay))
                            && (teacherCours.getClassDate().before(applyEndDay) || DateUtil.isSameDay(teacherCours.getClassDate(), applyEndDay))) {
                        applyDateRangeCourses.add(teacherCours);
                    }
                }

                boolean firstWeekEnableApply = true;
                boolean secondWeekEnableApply = true;

                if (!CollectionUtils.isEmpty(firstWeekCourses)) {
                    Map<GroupType, Long> groupTypeCountFirstMap = firstWeekCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getGroupType, Collectors.counting()));
                    if (Objects.nonNull(groupTypeCountFirstMap.get(GroupType.PRACTICE)) && groupTypeCountFirstMap.get(GroupType.PRACTICE) >= 80) {
                        firstWeekEnableApply = false;
                    }
                }

                if (!CollectionUtils.isEmpty(secondWeekCourses)) {
                    Map<GroupType, Long> groupTypeCountSecondMap = secondWeekCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getGroupType, Collectors.counting()));
                    if (Objects.nonNull(groupTypeCountSecondMap.get(GroupType.PRACTICE)) && groupTypeCountSecondMap.get(GroupType.PRACTICE) >= 80) {
                        secondWeekEnableApply = false;
                    }
                }

                Date enableApplyStartDate = applyStartDay;
                Date enableApplyEndDate = secondSunday;
                if (!firstWeekEnableApply) {
                    enableApplyStartDate = secondMonday;
                }
                if (!secondWeekEnableApply) {
                    enableApplyEndDate = secondMonday;
                }

                if (DateUtil.isSameDay(enableApplyStartDate, enableApplyEndDate)) {
                    continue;
                }

                List<Date> tempEnableApplyDates = new ArrayList<>();
                for (Date enableApplyDate : enableApplyDates) {
                    Date enableApplyDateCourseEndTime = DateUtil.addMinutes(enableApplyDate, practiceCourseMinutes);
                    if (teacherId == 100473) {
                        checkTeacherLeaveDate = false;
                        Calendar tempCalendar = Calendar.getInstance();
                        tempCalendar.setFirstDayOfWeek(Calendar.MONDAY);
                        tempCalendar.setTime(enableApplyDate);
                        if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY
                                || tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                            String date_str = DateUtil.dateToString(enableApplyDate, "yyyy-MM-dd");
                            Date date1 = DateUtil.stringToDate(date_str + " 11:30:00");
                            Date date2 = DateUtil.stringToDate(date_str + " 14:30:00");
                            Date date3 = DateUtil.stringToDate(date_str + " 20:30:00");
                            if (enableApplyDate.before(date2)
                                    && enableApplyDateCourseEndTime.after(date1)) {
                                continue;
                            }
                            if (date3.before(enableApplyDateCourseEndTime)) {
                                continue;
                            }
                        }
                        if (tempCalendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
                            continue;
                        }
                    }
                    if (checkTeacherLeaveDate) {
                        String dateStr = DateUtil.dateToString(enableApplyDate, "yyyy-MM-dd");
                        JSONObject leaveDateData = teacherLeaveData.getJSONObject(dateStr);
                        if (Objects.nonNull(leaveDateData)) {
                            Date leaveStartTime = leaveDateData.getDate("leave_start_time");
                            Date leaveEndTime = leaveDateData.getDate("leave_end_time");
                            if (enableApplyDateCourseEndTime.after(leaveStartTime)
                                    && enableApplyDate.before(leaveEndTime)) {
                                continue;
                            }
                        }
                    }
                    if (!enableApplyDate.before(enableApplyStartDate) && !enableApplyEndDate.before(enableApplyDate)) {
                        tempEnableApplyDates.add(enableApplyDate);
                    }
                }

                if (CollectionUtils.isEmpty(applyDateRangeCourses)) {
                    allTeacherFreeDates = new HashSet<>(tempEnableApplyDates);
                    break;
                }

                applyDateRangeCourses.sort(Comparator.comparing(CourseSchedule::getStartClassTime));
                for (int j = 0; j < applyDateRangeCourses.size(); j++) {
                    CourseSchedule preCourseSchedule = applyDateRangeCourses.get(j);
                    CourseSchedule backCourseSchedule = j >= applyDateRangeCourses.size() - 1 ? preCourseSchedule : applyDateRangeCourses.get(j + 1);
                    if (preCourseSchedule.getEndClassTime().after(backCourseSchedule.getEndClassTime())) {
                        backCourseSchedule.setEndClassTime(preCourseSchedule.getEndClassTime());
                    }
                    if (j == 0) {
                        for (Date enableApplyDate : tempEnableApplyDates) {
                            if (Objects.nonNull(firstClassTime) && DateUtil.isSameDay(enableApplyDate, firstClassTime)) {
                                continue;
                            }
                            Date enableApplyDateCourseEndTime = DateUtil.addMinutes(enableApplyDate, practiceCourseMinutes);
                            if (!enableApplyDateCourseEndTime.after(preCourseSchedule.getStartClassTime())) {
                                allTeacherFreeDates.add(enableApplyDate);
                            }
                        }
                    }
                    if (j == applyDateRangeCourses.size() - 1) {
                        for (Date enableApplyDate : tempEnableApplyDates) {
                            if (Objects.nonNull(firstClassTime) && DateUtil.isSameDay(enableApplyDate, firstClassTime)) {
                                continue;
                            }
                            if (!enableApplyDate.before(backCourseSchedule.getEndClassTime())) {
                                allTeacherFreeDates.add(enableApplyDate);
                            }
                        }
                    }
                    if (j >= applyDateRangeCourses.size() - 1) {
                        break;
                    }
                    for (Date enableApplyDate : tempEnableApplyDates) {
                        if (Objects.nonNull(firstClassTime) && DateUtil.isSameDay(enableApplyDate, firstClassTime)) {
                            continue;
                        }
                        Date enableApplyDateCourseEndTime = DateUtil.addMinutes(enableApplyDate, practiceCourseMinutes);
                        if (!enableApplyDate.before(preCourseSchedule.getEndClassTime())
                                && !enableApplyDateCourseEndTime.after(backCourseSchedule.getStartClassTime())) {
                            allTeacherFreeDates.add(enableApplyDate);
                        }
                    }
                }
            }
            if (!CollectionUtils.isEmpty(allTeacherFreeDates)) {
                break;
            }
        }
        if (!CollectionUtils.isEmpty(allTeacherFreeDates)) {
            Set<String> days = allTeacherFreeDates.stream()
                    .map(date -> DateUtil.dateToString(date, "yyyy-MM-dd")).collect(Collectors.toSet());
            result.put("teacherFreeDays", days);
            allTeacherFreeDates = allTeacherFreeDates.stream().sorted().collect(Collectors.toSet());
            result.put("teacherFreeDates", allTeacherFreeDates);
        } else {
            result.put("teacherFreeDays", new ArrayList<>());
        }
        return result;
    }

    @Override
    public List<Date> getEnableApplyDates(Date startDay, Date endDay) {
        List<Date> result = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDay);
        calendar.add(Calendar.DATE, -1);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        SysConfig practiceApplyStartTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_START_TIME);
        SysConfig practiceApplyEndTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_END_TIME);
        SysConfig practiceApplyIntervalTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_INTERVAL_TIME);
        String enableStartTimeStr = practiceApplyStartTimeConfig.getParanValue();
        String enableEndTimeStr = practiceApplyEndTimeConfig.getParanValue();
        Integer practiceApplyIntervalMinutes = practiceApplyIntervalTimeConfig.getParanValue(Integer.class);
        while (!calendar.getTime().after(endDay)) {
            calendar.add(Calendar.DATE, 1);
            calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(enableStartTimeStr.split(":")[0]));
            calendar.set(Calendar.MINUTE, Integer.valueOf(enableStartTimeStr.split(":")[1]));
            Date enableApplyDayStartTime = calendar.getTime();
            calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(enableEndTimeStr.split(":")[0]));
            calendar.set(Calendar.MINUTE, Integer.valueOf(enableEndTimeStr.split(":")[1]));
            Date enableApplyDayEndTime = calendar.getTime();
            Calendar applyStartCalendar = Calendar.getInstance();
            applyStartCalendar.setTime(enableApplyDayStartTime);
            for (String applyDayTime : applyDayTimes) {
                String temp = DateUtil.dateToString(applyStartCalendar.getTime(), "yyyy-MM-dd");
                temp = temp + " " + applyDayTime;
                result.add(DateUtil.stringToDate(temp, "yyyy-MM-dd HH:mm:ss"));
            }
        }
        return result;
    }

    @Override
    public List<Date> getEnableApplyDatesIncludeAllTimes(Date startDay, Date endDay) {
        List<Date> result = new ArrayList<>();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDay);
        calendar.add(Calendar.DATE, -1);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        SysConfig practiceApplyStartTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_START_TIME);
        SysConfig practiceApplyEndTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_END_TIME);
        SysConfig practiceApplyIntervalTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_INTERVAL_TIME);
        String enableStartTimeStr = practiceApplyStartTimeConfig.getParanValue();
        String enableEndTimeStr = practiceApplyEndTimeConfig.getParanValue();
        Integer practiceApplyIntervalMinutes = practiceApplyIntervalTimeConfig.getParanValue(Integer.class);
        LocalTime dayStartTime = LocalTime.parse("00:00:00", DateUtil.timeFormatter);
        LocalTime dayEndTime = LocalTime.parse("00:00:00", DateUtil.timeFormatter);
        List<String> dayApplyTimes = new ArrayList<>();
        while (true) {
            dayApplyTimes.add(dayStartTime.format(DateUtil.timeFormatter));
            dayStartTime = dayStartTime.plusMinutes(practiceApplyIntervalMinutes);
            if (dayStartTime.compareTo(dayEndTime) == 0) {
                break;
            }
        }

        while (!calendar.getTime().after(endDay)) {
            calendar.add(Calendar.DATE, 1);
            calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(enableStartTimeStr.split(":")[0]));
            calendar.set(Calendar.MINUTE, Integer.valueOf(enableStartTimeStr.split(":")[1]));
            Date enableApplyDayStartTime = calendar.getTime();
            calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(enableEndTimeStr.split(":")[0]));
            calendar.set(Calendar.MINUTE, Integer.valueOf(enableEndTimeStr.split(":")[1]));
            Date enableApplyDayEndTime = calendar.getTime();
            Calendar applyStartCalendar = Calendar.getInstance();
            applyStartCalendar.setTime(enableApplyDayStartTime);
            for (String applyDayTime : dayApplyTimes) {
                String temp = DateUtil.dateToString(applyStartCalendar.getTime(), "yyyy-MM-dd");
                temp = temp + " " + applyDayTime;
                result.add(DateUtil.stringToDate(temp, "yyyy-MM-dd HH:mm:ss"));
            }
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public Map practiceApply(PracticeGroup practiceGroup) {
        if (Objects.isNull(practiceGroup.getUserId())) {
            throw new BizException("请选择老师");
        }
        Map result = new HashMap();
        SysConfig practiceSubjectIdListConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_SUBJECT_ID_LIST);
        if (Objects.isNull(practiceGroup.getSubjectId())
                || !Arrays.asList(practiceSubjectIdListConfig.getParanValue().split(",")).contains(practiceGroup.getSubjectId().toString())) {
            result.put("status", "DISABLE_SUBJECT");
            result.put("info", "预约失败，声部选择错误，请重试。");
            return result;
        }
        Integer practiceCourseMinutes = 25;
        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        if (Objects.nonNull(practiceCourseMinutesConfig)) {
            practiceCourseMinutes = practiceCourseMinutesConfig.getParanValue(Integer.class);
        }
        SysConfig practiceCourseSalaryConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_SALARY);
        Date now = new Date();
        if (practiceGroup.getFirstCourseTime().after(practiceGroup.getSecondCourseTime())) {
            Date tmp = practiceGroup.getSecondCourseTime();
            practiceGroup.setSecondCourseTime(practiceGroup.getFirstCourseTime());
            practiceGroup.setFirstCourseTime(tmp);
        }
//        if (practiceGroup.getFirstCourseTime().before(now)
//                || practiceGroup.getFirstCourseTime().after(activityEndDate)) {
//            result.put("status", "APPLY_DATE_OVERFLOW");
//            result.put("info", "预约失败，网管课的时间超出活动时间范围（2020-5-31之前），请重试。");
//            return result;
//        }
//        if (practiceGroup.getSecondCourseTime().before(now)
//                || practiceGroup.getSecondCourseTime().after(activityEndDate)) {
//            result.put("status", "APPLY_DATE_OVERFLOW");
//            result.put("info", "预约失败，网管课的时间超出活动时间范围（2020-5-31之前），请重试。");
//            return result;
//        }
        if (DateUtil.isSameDay(practiceGroup.getFirstCourseTime(), practiceGroup.getSecondCourseTime())) {
            result.put("status", "TWO_DATE_ON_ONE_DAY");
            result.put("info", "预约失败，网管课选择时间重复，请重试。");
            return result;
        }

//        int recordNum = practiceLessonApplyDao.checkUserIsApply(practiceGroup.getStudentId());
//        if(recordNum>0){
//            LocalDateTime nowDateTime=LocalDateTime.now();
//            LocalDateTime applyExpireDateTime=LocalDateTime.parse("2020-02-29 00:00:00", DateUtil.dateTimeFormatter);
//            if(nowDateTime.compareTo(applyExpireDateTime)>0){
//                result.put("status", "APPLY_DATE_OVERFLOW");
//                result.put("info", "预约失败，超出可预约时间范围。");
//                return result;
//            }
//        }else{
//            LocalDateTime nowDateTime=LocalDateTime.now();
//            LocalDateTime applyExpireDateTime=LocalDateTime.parse("2020-02-22 20:00:00", DateUtil.dateTimeFormatter);
//            if(nowDateTime.compareTo(applyExpireDateTime)>0){
//                result.put("status", "APPLY_DATE_OVERFLOW");
//                result.put("info", "预约失败，超出可预约时间范围。");
//                return result;
//            }
//        }

        List<String> musicGroupIds = studentRegistrationDao.queryStudentMusicGroup(practiceGroup.getStudentId());
        if (!CollectionUtils.isEmpty(musicGroupIds) && musicGroupIds.size() <= 1) {
            MusicGroup musicGroup = musicGroupDao.get(musicGroupIds.get(0));
            if (Objects.nonNull(musicGroup)) {
                practiceGroup.setEducationalTeacherId(musicGroup.getEducationalTeacherId());
            }
        }

        List<Date> allCourseDates = new ArrayList<>();
        allCourseDates.add(practiceGroup.getFirstCourseTime());
        allCourseDates.add(practiceGroup.getSecondCourseTime());
        allCourseDates.add(DateUtil.addDays(practiceGroup.getFirstCourseTime(), 7));
        if (DateUtil.isSameDay(allCourseDates.get(1), allCourseDates.get(2))) {
            Date tempDate = allCourseDates.get(2);
            allCourseDates.remove(2);
            allCourseDates.add(DateUtil.addDays(tempDate, 7));
        }
        allCourseDates.add(DateUtil.addDays(practiceGroup.getSecondCourseTime(), 7));
        if (DateUtil.isSameDay(allCourseDates.get(2), allCourseDates.get(3))) {
            Date tempDate = allCourseDates.get(3);
            allCourseDates.remove(3);
            allCourseDates.add(DateUtil.addDays(tempDate, 7));
        }
//        if (allCourseDates.get(2).before(now)
//                || allCourseDates.get(2).after(courseExpireDate)) {
//            result.put("status", "APPLY_DATE_OVERFLOW");
//            result.put("info", "预约失败，网管课的时间超出活动时间范围（2020-5-31之前），请重试。");
//            return result;
//        }
//        if (allCourseDates.get(3).before(now)
//                || allCourseDates.get(3).after(courseExpireDate)) {
//            result.put("status", "APPLY_DATE_OVERFLOW");
//            result.put("info", "预约失败，网管课的时间超出活动时间范围（2020-5-31之前），请重试。");
//            return result;
//        }
        allCourseDates.sort(Comparator.comparing(Date::getTime));

        studentDao.lockUser(practiceGroup.getStudentId());
        studentDao.lockUser(practiceGroup.getUserId());

        Integer applyTimes = practiceGroupDao.countUserPracticeApplyRecord(practiceGroup.getStudentId());

        if (applyTimes >= 1) {
            result.put("status", "IS_APPLIED");
            result.put("info", "您已经完成网管课预约，请勿重复预约。");
            return result;
        }
        applyTimes += 1;

        SysUser sysUser = sysUserFeignService.queryUserById(practiceGroup.getStudentId());

        practiceGroup.setCoursesStartDate(allCourseDates.get(0));
        practiceGroup.setCoursesExpireDate(allCourseDates.get(3));
        Teacher teacher = teacherService.getDetail(practiceGroup.getUserId());
        if (Objects.isNull(teacher)) {
            throw new BizException("老师不存在");
        }

        List<SimpleUserDto> enableApplyTeachers = getEnableApplyTeachers(practiceGroup.getStudentId(), practiceGroup.getSubjectId());
        Set<Integer> enableApplyTeacherIds = enableApplyTeachers.stream().map(SimpleUserDto::getUserId).collect(Collectors.toSet());
        if (!enableApplyTeacherIds.contains(practiceGroup.getUserId())) {
            throw new BizException("预约失败，老师选择错误，请重试");
        }

        practiceGroup.setOrganId(sysUser.getOrganId());

        Subject subject = subjectDao.get(practiceGroup.getSubjectId());
        if (Objects.isNull(subject)) {
            result.put("status", "TEACHER_PRACTICE_REPEAT");
            result.put("info", "预约失败，声部选择错误，请重试。");
            return result;
        }

        TeacherFreeTime teacherFreeTime = teacherFreeTimeDao.findTeacherFreeTime(practiceGroup.getUserId());
        Integer maxTeacherCourses = null;
        if (Objects.nonNull(teacherFreeTime)) {
            if (Objects.nonNull(teacherFreeTime.getTotalTimes())) {
                maxTeacherCourses = teacherFreeTime.getTotalTimes();
            }
        } else {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.put("status", "TEACHER_PRACTICE_REPEAT");
            result.put("info", "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请重新选择时段或更换老师后重试。");
            return result;
        }

        Date monday1 = DateUtil.getWeekDayWithDate(allCourseDates.get(0), Calendar.MONDAY);
        Date sunday1 = DateUtil.getWeekDayWithDate(allCourseDates.get(0), Calendar.SUNDAY);
        int tp1 = courseScheduleDao.countTeacherPracticeCourse(practiceGroup.getUserId(), monday1, sunday1);
        if (Objects.nonNull(maxTeacherCourses) && tp1 >= maxTeacherCourses) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.put("status", "TEACHER_PRACTICE_REPEAT");
            result.put("info", "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请重新选择时段或更换老师后重试。");
            return result;
        }

        Date monday2 = DateUtil.getWeekDayWithDate(allCourseDates.get(1), Calendar.MONDAY);
        Date sunday2 = DateUtil.getWeekDayWithDate(allCourseDates.get(1), Calendar.SUNDAY);
        int tp2 = courseScheduleDao.countTeacherPracticeCourse(practiceGroup.getUserId(), monday2, sunday2);
        if (Objects.nonNull(maxTeacherCourses) && tp2 >= maxTeacherCourses) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.put("status", "TEACHER_PRACTICE_REPEAT");
            result.put("info", "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请重新选择时段或更换老师后重试。");
            return result;
        }

        Date monday3 = DateUtil.getWeekDayWithDate(allCourseDates.get(2), Calendar.MONDAY);
        Date sunday3 = DateUtil.getWeekDayWithDate(allCourseDates.get(2), Calendar.SUNDAY);
        int tp3 = courseScheduleDao.countTeacherPracticeCourse(practiceGroup.getUserId(), monday3, sunday3);
        if (Objects.nonNull(maxTeacherCourses) && tp3 >= maxTeacherCourses) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.put("status", "TEACHER_PRACTICE_REPEAT");
            result.put("info", "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请重新选择时段或更换老师后重试。");
            return result;
        }

        Date monday4 = DateUtil.getWeekDayWithDate(allCourseDates.get(3), Calendar.MONDAY);
        Date sunday4 = DateUtil.getWeekDayWithDate(allCourseDates.get(3), Calendar.SUNDAY);
        int tp4 = courseScheduleDao.countTeacherPracticeCourse(practiceGroup.getUserId(), monday4, sunday4);
        if (Objects.nonNull(maxTeacherCourses) && tp4 >= maxTeacherCourses) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.put("status", "TEACHER_PRACTICE_REPEAT");
            result.put("info", "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请重新选择时段或更换老师后重试。");
            return result;
        }

        List<CourseSchedule> courseSchedules6 = courseScheduleDao.findTeacherCoursesWithIncludeDateRange(practiceGroup.getUserId(), allCourseDates.get(0), DateUtil.addMinutes(allCourseDates.get(0), practiceCourseMinutes));
        List<CourseSchedule> courseSchedules7 = courseScheduleDao.findTeacherCoursesWithIncludeDateRange(practiceGroup.getUserId(), allCourseDates.get(1), DateUtil.addMinutes(allCourseDates.get(1), practiceCourseMinutes));
        List<CourseSchedule> courseSchedules8 = courseScheduleDao.findTeacherCoursesWithIncludeDateRange(practiceGroup.getUserId(), allCourseDates.get(2), DateUtil.addMinutes(allCourseDates.get(2), practiceCourseMinutes));
        List<CourseSchedule> courseSchedules9 = courseScheduleDao.findTeacherCoursesWithIncludeDateRange(practiceGroup.getUserId(), allCourseDates.get(3), DateUtil.addMinutes(allCourseDates.get(3), practiceCourseMinutes));

        if (!CollectionUtils.isEmpty(courseSchedules6) || !CollectionUtils.isEmpty(courseSchedules7)
                || !CollectionUtils.isEmpty(courseSchedules8) || !CollectionUtils.isEmpty(courseSchedules9)) {
            if (courseSchedules6 == null) {
                courseSchedules6 = new ArrayList<>();
            }
            if (courseSchedules7 == null) {
                courseSchedules7 = new ArrayList<>();
            }
            if (courseSchedules7 == null) {
                courseSchedules7 = new ArrayList<>();
            }
            if (courseSchedules8 == null) {
                courseSchedules8 = new ArrayList<>();
            }
            courseSchedules6.addAll(courseSchedules7);
            courseSchedules6.addAll(courseSchedules8);
            courseSchedules6.addAll(courseSchedules9);
            for (int i = 0; i < courseSchedules6.size(); i++) {
                if (courseSchedules6.get(i).getGroupType().equals(GroupType.PRACTICE)) {
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    result.put("status", "TEACHER_PRACTICE_REPEAT");
                    result.put("info", "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请选择其他时段或更换老师后重试。");
                    return result;
                }
            }
        }

        practiceGroup.setName(subject.getName() + "•" + sysUser.getUsername());
        practiceGroup.setSingleClassMinutes(practiceCourseMinutes);
        practiceGroup.setGroupStatus(GroupStatusEnum.NORMAL);
        practiceGroup.setType(PracticeGroupType.FREE);
        practiceGroupDao.insert(practiceGroup);

        //创建班级信息
        ClassGroup classGroup = new ClassGroup();
        classGroup.setSubjectIdList(practiceGroup.getSubjectId().toString());
        classGroup.setExpectStudentNum(1);
        classGroup.setStudentNum(1);
        classGroup.setName(practiceGroup.getName());
        classGroup.setTotalClassTimes(allCourseDates.size());
        classGroup.setType(ClassGroupTypeEnum.PRACTICE);
        classGroup.setDelFlag(0);
        classGroup.setGroupType(GroupType.PRACTICE);
        classGroup.setMusicGroupId(practiceGroup.getId().toString());
        classGroup.setCreateTime(now);
        classGroup.setUpdateTime(now);
        classGroupDao.insert(classGroup);

        //创建班级老师关联记录
        ClassGroupTeacherMapper classGroupTeacherMapper = new ClassGroupTeacherMapper();
        classGroupTeacherMapper.setMusicGroupId(practiceGroup.getId().toString());
        classGroupTeacherMapper.setClassGroupId(classGroup.getId());
        classGroupTeacherMapper.setTeacherRole(TeachTypeEnum.BISHOP);
        classGroupTeacherMapper.setUserId(practiceGroup.getUserId());
        classGroupTeacherMapper.setGroupType(GroupType.PRACTICE);
        classGroupTeacherMapper.setCreateTime(now);
        classGroupTeacherMapper.setUpdateTime(now);
        classGroupTeacherMapperDao.insert(classGroupTeacherMapper);

        //创建班级与老师课酬记录
        ClassGroupTeacherSalary classGroupTeacherSalary = new ClassGroupTeacherSalary();
        classGroupTeacherSalary.setMusicGroupId(practiceGroup.getId().toString());
        classGroupTeacherSalary.setClassGroupId(classGroup.getId());
        classGroupTeacherSalary.setTeacherRole(TeachTypeEnum.BISHOP);
        classGroupTeacherSalary.setUserId(practiceGroup.getUserId());
        classGroupTeacherSalary.setSalary(new BigDecimal(practiceCourseSalaryConfig.getParanValue()));
        classGroupTeacherSalary.setOnlineClassesSalary(new BigDecimal(practiceCourseSalaryConfig.getParanValue()));
        classGroupTeacherSalary.setGroupType(GroupType.PRACTICE);
        classGroupTeacherSalary.setCreateTime(now);
        classGroupTeacherSalary.setUpdateTime(now);
        classGroupTeacherSalaryDao.insert(classGroupTeacherSalary);

        //班级学生关联表
        ClassGroupStudentMapper classGroupStudentMapper = new ClassGroupStudentMapper();
        classGroupStudentMapper.setMusicGroupId(practiceGroup.getId().toString());
        classGroupStudentMapper.setClassGroupId(classGroup.getId());
        classGroupStudentMapper.setUserId(practiceGroup.getStudentId());
        classGroupStudentMapper.setCreateTime(now);
        classGroupStudentMapper.setStatus(ClassGroupStudentStatusEnum.NORMAL);
        classGroupStudentMapper.setGroupType(GroupType.PRACTICE);
        classGroupStudentMapperDao.insert(classGroupStudentMapper);

        List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries = new ArrayList<>();
        List<CourseScheduleStudentPayment> courseScheduleStudentPayments = new ArrayList<>();
        List<TeacherAttendance> teacherAttendances = new ArrayList<>();

        for (Date courseDate : allCourseDates) {
            //课表
            CourseSchedule courseSchedule = new CourseSchedule();
            courseSchedule.setMusicGroupId(practiceGroup.getId().toString());
            courseSchedule.setClassGroupId(classGroup.getId());
            courseSchedule.setStatus(CourseStatusEnum.NOT_START);
            courseSchedule.setClassDate(courseDate);
            courseSchedule.setStartClassTime(courseDate);
            courseSchedule.setEndClassTime(DateUtil.addMinutes(courseDate, practiceCourseMinutes));
            courseSchedule.setTeacherId(practiceGroup.getUserId());
            courseSchedule.setActualTeacherId(practiceGroup.getUserId());
            courseSchedule.setCreateTime(now);
            courseSchedule.setUpdateTime(now);
            courseSchedule.setTeachMode(TeachModeEnum.ONLINE);
            courseSchedule.setType(CourseSchedule.CourseScheduleType.PRACTICE);
            courseSchedule.setGroupType(GroupType.PRACTICE);
            courseSchedule.setName(practiceGroup.getName());
            courseSchedule.setOrganId(practiceGroup.getOrganId());
            courseScheduleDao.insert(courseSchedule);

            //课程与老师薪水表
            CourseScheduleTeacherSalary courseScheduleTeacherSalary = new CourseScheduleTeacherSalary();
            courseScheduleTeacherSalary.setCourseScheduleId(courseSchedule.getId());
            courseScheduleTeacherSalary.setGroupType(GroupType.PRACTICE);
            courseScheduleTeacherSalary.setMusicGroupId(practiceGroup.getId().toString());
            courseScheduleTeacherSalary.setTeacherRole(classGroupTeacherMapper.getTeacherRole());
            courseScheduleTeacherSalary.setUserId(practiceGroup.getUserId());
            courseScheduleTeacherSalary.setExpectSalary(new BigDecimal(practiceCourseSalaryConfig.getParanValue()));
            courseScheduleTeacherSalary.setCreateTime(now);
            courseScheduleTeacherSalary.setUpdateTime(now);
            courseScheduleTeacherSalary.setClassGroupId(classGroup.getId());
            courseScheduleTeacherSalaries.add(courseScheduleTeacherSalary);

            //学生缴费记录
            CourseScheduleStudentPayment courseScheduleStudentPayment = new CourseScheduleStudentPayment();
            courseScheduleStudentPayment.setGroupType(GroupType.PRACTICE);
            courseScheduleStudentPayment.setMusicGroupId(practiceGroup.getId().toString());
            courseScheduleStudentPayment.setCourseScheduleId(courseSchedule.getId());
            courseScheduleStudentPayment.setUserId(practiceGroup.getStudentId());
            courseScheduleStudentPayment.setExpectPrice(BigDecimal.ZERO);
            courseScheduleStudentPayment.setActualPrice(BigDecimal.ZERO);
            courseScheduleStudentPayment.setOriginalPrice(BigDecimal.ZERO);
            courseScheduleStudentPayment.setClassGroupId(classGroup.getId());
            courseScheduleStudentPayment.setCreateTime(now);
            courseScheduleStudentPayment.setUpdateTime(now);
            courseScheduleStudentPayments.add(courseScheduleStudentPayment);

            //教师签到记录
            TeacherAttendance teacherAttendance = new TeacherAttendance();
            teacherAttendance.setMusicGroupId(practiceGroup.getId().toString());
            teacherAttendance.setTeacherId(practiceGroup.getUserId());
            teacherAttendance.setClassGroupId(classGroup.getId());
            teacherAttendance.setGroupType(GroupType.PRACTICE);
            teacherAttendance.setCourseScheduleId(courseSchedule.getId());
            teacherAttendance.setCreateTime(now);
            teacherAttendances.add(teacherAttendance);
        }
        courseScheduleTeacherSalaryDao.batchInsert(courseScheduleTeacherSalaries);
        teacherAttendanceDao.batchInsert(teacherAttendances);

        Student student = studentDao.get(practiceGroup.getStudentId());
        if (Objects.isNull(student)) {
            student = new Student();
            student.setUserId(practiceGroup.getStudentId());
            student.setSubjectIdList(practiceGroup.getSubjectId().toString());
            student.setServiceTag(1);
            student.setOperatingTag(1);
            studentDao.insert(student);
        } else {
            if (Objects.isNull(student.getSubjectIdList())) {
                student.setSubjectIdList(practiceGroup.getSubjectId().toString());
            } else {
                String[] studentIdStrings = student.getSubjectIdList().split(",");
                List<String> studentIds = new ArrayList<>(Arrays.asList(studentIdStrings));
                if (!studentIds.contains(practiceGroup.getSubjectId().toString())) {
                    studentIds.add(practiceGroup.getSubjectId().toString());
                }
                student.setSubjectIdList(StringUtils.join(studentIds.toArray(), ","));
            }
            studentDao.update(student);
        }

        List<CourseSchedule> studentRepeatCourse1 = courseScheduleDao.findStudentCoursesWithIncludeDateRange(practiceGroup.getStudentId(), allCourseDates.get(0), DateUtil.addMinutes(allCourseDates.get(0), practiceCourseMinutes));
        if (!CollectionUtils.isEmpty(studentRepeatCourse1)) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.put("status", "STUDENT_COURSE_REPEAT");
            result.put("info", "抱歉啦，当前所选时段组合，与您现有课程「" + studentRepeatCourse1.get(0).getName() + "」时段冲突，请选择其他时段重试。");
            return result;
        }
        List<CourseSchedule> studentRepeatCourse2 = courseScheduleDao.findStudentCoursesWithIncludeDateRange(practiceGroup.getStudentId(), allCourseDates.get(1), DateUtil.addMinutes(allCourseDates.get(1), practiceCourseMinutes));
        if (!CollectionUtils.isEmpty(studentRepeatCourse2)) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.put("status", "STUDENT_COURSE_REPEAT");
            result.put("info", "抱歉啦，当前所选时段组合，与您现有课程「" + studentRepeatCourse2.get(0).getName() + "」时段冲突，请选择其他时段重试。");
            return result;
        }
        List<CourseSchedule> studentRepeatCourse3 = courseScheduleDao.findStudentCoursesWithIncludeDateRange(practiceGroup.getStudentId(), allCourseDates.get(2), DateUtil.addMinutes(allCourseDates.get(2), practiceCourseMinutes));
        if (!CollectionUtils.isEmpty(studentRepeatCourse3)) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.put("status", "STUDENT_COURSE_REPEAT");
            result.put("info", "抱歉啦，当前所选时段组合，与您现有课程「" + studentRepeatCourse3.get(0).getName() + "」时段冲突，请选择其他时段重试。");
            return result;
        }
        List<CourseSchedule> studentRepeatCourse4 = courseScheduleDao.findStudentCoursesWithIncludeDateRange(practiceGroup.getStudentId(), allCourseDates.get(3), DateUtil.addMinutes(allCourseDates.get(3), practiceCourseMinutes));
        if (!CollectionUtils.isEmpty(studentRepeatCourse4)) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            result.put("status", "STUDENT_COURSE_REPEAT");
            result.put("info", "抱歉啦，当前所选时段组合，与您现有课程「" + studentRepeatCourse4.get(0).getName() + "」时段冲突，请选择其他时段重试。");
            return result;
        }
        courseScheduleStudentPaymentDao.batchInsert(courseScheduleStudentPayments);

        //推送
        List<CourseSchedule> courseSchedules1 = courseScheduleDao.findTeacherCoursesWithIncludeDateRange(practiceGroup.getUserId(), allCourseDates.get(0), DateUtil.addMinutes(allCourseDates.get(0), practiceCourseMinutes));
        List<CourseSchedule> courseSchedules2 = courseScheduleDao.findTeacherCoursesWithIncludeDateRange(practiceGroup.getUserId(), allCourseDates.get(1), DateUtil.addMinutes(allCourseDates.get(1), practiceCourseMinutes));
        List<CourseSchedule> courseSchedules3 = courseScheduleDao.findTeacherCoursesWithIncludeDateRange(practiceGroup.getUserId(), allCourseDates.get(2), DateUtil.addMinutes(allCourseDates.get(2), practiceCourseMinutes));
        List<CourseSchedule> courseSchedules5 = courseScheduleDao.findTeacherCoursesWithIncludeDateRange(practiceGroup.getUserId(), allCourseDates.get(3), DateUtil.addMinutes(allCourseDates.get(3), practiceCourseMinutes));
        if (!CollectionUtils.isEmpty(courseSchedules1) || !CollectionUtils.isEmpty(courseSchedules2)
                || !CollectionUtils.isEmpty(courseSchedules3) || !CollectionUtils.isEmpty(courseSchedules5)) {
            if (courseSchedules1 == null) {
                courseSchedules1 = new ArrayList<>();
            }
            if (courseSchedules2 == null) {
                courseSchedules2 = new ArrayList<>();
            }
            if (courseSchedules3 == null) {
                courseSchedules3 = new ArrayList<>();
            }
            if (courseSchedules5 == null) {
                courseSchedules5 = new ArrayList<>();
            }
            courseSchedules1.addAll(courseSchedules2);
            courseSchedules1.addAll(courseSchedules3);
            courseSchedules1.addAll(courseSchedules5);
            List<String> courseDates = new ArrayList<>();
            List<String> courseNames = new ArrayList<>();
            for (int i = 0; i < courseSchedules1.size(); i++) {
                if (courseSchedules1.get(i).getGroupType().equals(GroupType.PRACTICE)) {
                    if (courseSchedules1.get(i).getMusicGroupId().equals(practiceGroup.getId().toString())) {
                        continue;
                    } else {
                        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                        result.put("status", "TEACHER_PRACTICE_REPEAT");
                        result.put("info", "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请选择其他时段或更换老师后重试。");
                        return result;
                    }
                }
                courseDates.add(DateUtil.dateToString(courseSchedules1.get(i).getStartClassTime(), "yyyy-MM-dd HH:mm:ss"));
                courseNames.add(courseSchedules1.get(i).getName());
            }
            if (!CollectionUtils.isEmpty(courseDates)) {
                String courseDatesStr = StringUtils.join(courseDates, "、");
                String courseNamesStr = StringUtils.join(courseNames, "、");
                Map<Integer, String> teacherMap = new HashMap<>();
                teacherMap.put(practiceGroup.getUserId(), practiceGroup.getUserId().toString());
                sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.PUSH_TEACHER_COURSE_COLLIDE,
                        teacherMap, null, 0, null, "TEACHER", Objects.isNull(sysUser.getUsername()) ? sysUser.getPhone() : sysUser.getUsername(), courseDatesStr, courseNamesStr, courseNamesStr);
            }
        }

        studentDao.updateStudentServiceTag(practiceGroup.getStudentId(), null, YesOrNoEnum.YES.getCode());

        imUserFriendService.refreshGroupImUserFriend(practiceGroup.getId().toString(), GroupType.PRACTICE);
        result.put("teacherName", teacher.getRealName());
        result.put("enableApply", applyTimes < 1 ? 1 : 0);
        result.put("status", "SUCCESS");
        return result;
    }

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

        List<PracticeGroupDto> dataList = null;
        int count = practiceGroupDao.countPracticeGroupByOrgan(params);
        if (queryInfo.getIsExport() && count > 50000) {
            throw new BizException("数据集太大，不能导出.最大数据集不能超过50000");
        }
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = practiceGroupDao.findAllByOrgan(params);
            if (dataList != null && dataList.size() > 0) {
                List<Integer> subjectIds = dataList.stream().map(practiceGroup -> practiceGroup.getSubjectId()).distinct().collect(Collectors.toList());
                Set<Integer> organIds = dataList.stream().map(practiceGroup -> practiceGroup.getOrganId()).collect(Collectors.toSet());
                List<String> practiceGroupIds = dataList.stream().map(e -> String.valueOf(e.getId())).distinct().collect(Collectors.toList());
                List<Integer> educationalTeacherIds = dataList.stream().filter(data -> Objects.nonNull(data.getEducationalTeacherId())).map(PracticeGroupDto::getEducationalTeacherId).collect(Collectors.toList());

                List<ClassGroup> classGroupsByVipGroups = classGroupDao
                        .findByMusicGroupsAndType(practiceGroupIds.stream().map(groupId -> String.valueOf(groupId)).collect(Collectors.toList()),
                                GroupType.PRACTICE.getCode());
                Map<String, ClassGroup> vipGroupClassGroupMap = classGroupsByVipGroups.stream().collect(Collectors.toMap(ClassGroup::getMusicGroupId, classGroup -> classGroup));
                //当前课时，总课时
//                List<Map<Long, Integer>> countGroupOverCourse = courseScheduleDao.countGroupOverCourse(practiceGroupIds,GroupType.PRACTICE.getCode());
                Map<String, Long> totalClassTimeMap = MapUtil.convertIntegerMap(courseScheduleDao.countTotalCourseScheduleNum(practiceGroupIds, GroupType.PRACTICE.getCode()));
                Map<String, Long> currentClassTimeMap = MapUtil.convertIntegerMap(courseScheduleDao.countCourseScheduleNum(practiceGroupIds, GroupType.PRACTICE.getCode()));
                //声部名称列表
                Map<Long, String> subjectNames = MapUtil.convertMybatisMap(subjectDao.findBySubjecIds(StringUtils.join(subjectIds, ",")));
                Map<Integer, String> organNames = MapUtil.convertMybatisMap(organizationDao.findOrganNameMapList(organIds));
//                List<Integer> collect = dataList.stream().map(e -> e.getEducationalTeacherId()).collect(Collectors.toList());
//                Map<Integer, String> educationNames = MapUtil.convertMybatisMap(teacherDao.queryNameByIds(StringUtils.join(collect,",")));

                Map<Integer, String> idNameMap = new HashMap<>();
                if (!CollectionUtils.isEmpty(educationalTeacherIds)) {
                    List<SimpleUserDto> educationalUsers = employeeDao.findByIds(educationalTeacherIds);
                    idNameMap = educationalUsers.stream().filter(e -> Objects.nonNull(e.getUserId()) && Objects.nonNull(e.getUserName())).collect(Collectors.toMap(SimpleUserDto::getUserId, user -> user.getUserName()));
                }

                for (PracticeGroupDto e : dataList) {
                    e.setSubjectName(subjectNames.get(e.getSubjectId().intValue()));
                    e.setOrganName(organNames.get(e.getOrganId()));
                    ClassGroup classGroup = vipGroupClassGroupMap.get(e.getId().toString());
                    if (Objects.nonNull(classGroup)) {
                        e.setStudentNum(classGroup.getStudentNum());
                    }
                    Long aLong = totalClassTimeMap.get(e.getId() + "");
                    e.setTotalClassTimes(aLong == null ? 0 : aLong.intValue());
                    Long aLong1 = currentClassTimeMap.get(e.getId() + "");
                    e.setCurrentClassTimes(aLong1 == null ? 0 : aLong1.intValue());
                    e.setEducationalTeacherName(idNameMap.get(e.getEducationalTeacherId()));
                    e.setSubClassTimes(e.getTotalClassTimes() == null ? 0 : e.getTotalClassTimes() - (e.getCurrentClassTimes() == null ? 0 : e.getCurrentClassTimes()));
                    if(e.getBuyMonths() == null || e.getBuyMonths() == 0){
                        e.setPayStatus("免费");
                    }else {
                        if(e.getBeRenewGroupId() == null || e.getBeRenewGroupId() == 0l){
                            e.setPayStatus("首充");
                        }else {
                            e.setPayStatus("续费");
                        }
                    }
                }
            }
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public Object findPracticeGroupCourseSchedules(PracticeGroupQueryInfo queryInfo) {
        PageInfo pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        if (Objects.isNull(queryInfo.getPracticeId())) {
            throw new BizException("请指定网管课课程");
        }

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

        PracticeGroup practiceGroup = practiceGroupDao.get(queryInfo.getPracticeId());
        if (Objects.isNull(practiceGroup)) {
            throw new BizException("指定的网管课不存在");
        }
        List<CourseSchedule> dataList = courseScheduleDao.findGroupCourseSchedules(practiceGroup.getId(), GroupType.PRACTICE.getCode());
        int count = dataList.size();
        if (count > 0) {
            pageInfo.setTotal(count);
            List<Long> courseScheduleIds = dataList.stream().map(courseSchedule -> courseSchedule.getId()).distinct().collect(Collectors.toList());
            List<TeacherAttendance> teacherAttendances = teacherAttendanceDao.findTeacherIdByCourseSchedule(courseScheduleIds);
            Map<Long, List<TeacherAttendance>> courseTeacherAttendanceMap = teacherAttendances.stream().collect(Collectors.groupingBy(TeacherAttendance::getCourseScheduleId));
            List<Map<Long, Integer>> courseSettlementMaps = courseScheduleTeacherSalaryDao.checkCoursesIsSettlement(courseScheduleIds);
            Map<Long, Long> courseSettlementMap = MapUtil.convertIntegerMap(courseSettlementMaps);
            Map<Integer, Long> studentNumCourseMap = MapUtil.convertIntegerMap(studentAttendanceDao.countStudentAttendancesByCourses(courseScheduleIds));
            //获取课程实际上课老师姓名
            Map<Long, String> teacherNames = MapUtil.convertIntegerMap(courseScheduleDao.queryTeacherName(courseScheduleIds));
            dataList.forEach(e -> {
                e.setCoursesExpireDate(practiceGroup.getCoursesExpireDate());
                Long isSettlement = courseSettlementMap.get(e.getId().longValue());
                e.setTeacherName(teacherNames.get(e.getId()));
                if (Objects.isNull(isSettlement) || isSettlement <= 0) {
                    e.setIsSettlement(0);
                } else {
                    e.setIsSettlement(1);
                }
                Long studentNum = studentNumCourseMap.get(e.getId());
                if (Objects.nonNull(studentNum)) {
                    e.setIsCallNames(studentNum > 0 ? YesOrNoEnum.YES : YesOrNoEnum.NO);
                } else {
                    e.setIsCallNames(YesOrNoEnum.NO);
                }
                List<TeacherAttendance> courseTeacherAttendances = courseTeacherAttendanceMap.get(e.getId());
                if (!CollectionUtils.isEmpty(courseTeacherAttendances)) {
                    TeacherAttendance teacherAttendance = courseTeacherAttendances.get(0);
                    e.setIsSignIn(Objects.isNull(teacherAttendance.getSignInStatus()) ? 3 : teacherAttendance.getSignInStatus().getCode());
                    e.setIsSignOut(Objects.isNull(teacherAttendance.getSignOutStatus()) ? 3 : teacherAttendance.getSignOutStatus().getCode());
                    e.setSignInStatusEnum(e.getIsSignIn() == 1 ? NORMAL : e.getIsSignIn() == 0 ? UNUSUAL : NO);
                    e.setSignOutStatusEnum(e.getIsSignOut() == 1 ? SignOutStatusEnum.NORMAL : e.getIsSignOut() == 0 ? SignOutStatusEnum.UNUSUAL : SignOutStatusEnum.NO);
                    e.setSignInTime(teacherAttendance.getSignInTime());
                    e.setSignOutTime(teacherAttendance.getSignOutTime());
                }
            });
        }
        pageInfo.setRows(dataList);
        Map<String, Object> result = new HashMap<>();
        result.put("pageInfo", pageInfo);
        return result;
    }

    @Override
    public PracticeGroup findUserLatestPracticeGroup(Integer userId, Long groupId) {
        if (Objects.isNull(groupId)) {
            throw new BizException("请选择需要续费的课程");
        }
        Date now = new Date();
        PracticeGroupDto userPracticeGroup = practiceGroupDao.findUserPracticeGroup2(userId, groupId);
        if (Objects.isNull(userPracticeGroup)) {
            throw new BizException("需要续费的课程组不存在");
        }
        if (userPracticeGroup.getCoursesExpireDate().before(now)) {
            throw new BizException("当前课程组已经无法续费，请选择购买");
        }
        return userPracticeGroup;
    }

    @Override
    public Map getPayPracticeApplyParams(Integer userId, Integer organId) {
        Map result = new HashMap();
        SysConfig practiceSubjectIdListConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_SUBJECT_ID_LIST);
        SysConfig practiceApplyStartTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_START_TIME);
        SysConfig practiceApplyEndTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_END_TIME);
        SysConfig practiceApplyIntervalTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_INTERVAL_TIME);
        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        SysConfig practicePayMaxMonthConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_PAY_MAX_MONTH);
        SysConfig practicePayDrillTimesOnOneWeekConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_PAY_DRILL_TIMES_ON_ONE_WEEK);
        SysConfig practicePromotionActivityStartDateConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_PROMOTION_ACTIVITY_START_DATE);
        SysConfig practiceBuyActivityExpireDateConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_BUY_ACTIVITY_EXPIRE_DATE);
        SysUserCashAccount userCashAccount = sysUserCashAccountService.getLocked(userId);
        Student student = studentDao.get(userId);
        List<Subject> subjects = subjectDao.findBySubjectByIdList(practiceSubjectIdListConfig.getParanValue());
        List<Integer> subjectIds = Arrays.asList(practiceSubjectIdListConfig.getParanValue().split(",")).stream().mapToInt(idStr -> Integer.valueOf(idStr)).boxed().collect(Collectors.toList());
        List<Integer> userDefaultSubjectIds = Objects.isNull(student) || Objects.isNull(student.getSubjectIdList()) ? null : Arrays.asList(student.getSubjectIdList().split(",")).stream().mapToInt(idStr -> Integer.valueOf(idStr)).boxed().collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(userDefaultSubjectIds) && userDefaultSubjectIds.size() <= 1) {
            userDefaultSubjectIds = userDefaultSubjectIds.stream().filter(userDefaultSubjectId -> subjectIds.contains(userDefaultSubjectId)).collect(Collectors.toList());
        } else {
            userDefaultSubjectIds = new ArrayList<>();
        }
        Collections.swap(subjects, 6, 7);
        PracticeGroupSellPrice practiceGroupSellPrice = practiceGroupSellPriceDao.get(organId);
        result.put("subjects", subjects);
        result.put("practiceApplyStartTime", practiceApplyStartTimeConfig.getParanValue());
        result.put("practiceApplyEndTime", practiceApplyEndTimeConfig.getParanValue());
        result.put("userDefaultSubjectIds", StringUtils.join(userDefaultSubjectIds.toArray(), ","));
        result.put("practiceApplyIntervalMinutes", practiceApplyIntervalTimeConfig.getParanValue(Integer.class));
        result.put("practiceCourseMinutes", practiceCourseMinutesConfig.getParanValue(Integer.class));
        result.put("practicePayMaxMonth", practicePayMaxMonthConfig.getParanValue(Integer.class));
        result.put("practicePayDrillTimesOnOneWeek", practicePayDrillTimesOnOneWeekConfig.getParanValue(Integer.class));
        result.put("practiceGroupSellPrice", practiceGroupSellPrice);
        result.put("balance", Objects.isNull(userCashAccount) ? 0 : userCashAccount.getBalance());
        result.put("practicePromotionActivityStartTime", practicePromotionActivityStartDateConfig.getParanValue());
        result.put("practiceActivityExpireDate", practiceBuyActivityExpireDateConfig.getParanValue());
        return result;
    }

    @Override
    public List<SimpleUserDto> getEnableApplyTeachersWithPay(Integer userId, Integer subjectId) {
        if (Objects.isNull(subjectId)) {
            throw new BizException("请选择声部");
        }
        SysUser sysUser = sysUserFeignService.queryUserById(userId);
        if (Objects.isNull(sysUser)) {
            throw new BizException("用户不存在");
        }
        if (Objects.isNull(sysUser.getOrganId())) {
            throw new BizException("未找到用户分部属性");
        }

//        if (sysUser.getOrganId().equals(DALIAN_ORGAN_ID)) {
//            sysUser.setOrganId(SHENYANG_ORGAN_ID);
//        }

        List<Integer> includeTeacherIds = new ArrayList<>();
        List<ExtendTeacherBasicDto> organAndSubjectTeachers = teacherDao.findTeaTeachersByOrganAndSubject2(sysUser.getOrganId(), subjectId);
        if (CollectionUtils.isEmpty(organAndSubjectTeachers)) {
            return new ArrayList<>();
        }

        List<Integer> allTeacherIds = organAndSubjectTeachers.stream().map(TeacherBasicDto::getId).collect(Collectors.toList());

        List<CourseSchedule> userCourses = courseScheduleDao.findUserCourses2(userId, allTeacherIds);

        if (!CollectionUtils.isEmpty(userCourses)) {
            Map<CourseSchedule.CourseScheduleType, List<CourseSchedule>> courseTypeCourseMap = userCourses.stream().collect(Collectors.groupingBy(CourseSchedule::getType));

            List<CourseSchedule> practiceCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.PRACTICE);
            if (!CollectionUtils.isEmpty(practiceCourses)) {
                for (CourseSchedule practiceCourse : practiceCourses) {
                    if (!includeTeacherIds.contains(practiceCourse.getTeacherId())) {
                        includeTeacherIds.add(practiceCourse.getTeacherId());
                    }
                }
            }

            List<CourseSchedule> vipCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.VIP);
            if (!CollectionUtils.isEmpty(vipCourses)) {
                for (CourseSchedule vipCourse : vipCourses) {
                    if (!includeTeacherIds.contains(vipCourse.getTeacherId())) {
                        includeTeacherIds.add(vipCourse.getTeacherId());
                    }
                }
            }
            List<CourseSchedule> singleCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.SINGLE);
            if (!CollectionUtils.isEmpty(singleCourses)) {
                for (CourseSchedule singleCourse : singleCourses) {
                    if (!includeTeacherIds.contains(singleCourse.getTeacherId())) {
                        includeTeacherIds.add(singleCourse.getTeacherId());
                    }
                }
            }
            List<CourseSchedule> highCourses = courseTypeCourseMap.get(CourseSchedule.CourseScheduleType.HIGH);
            if (!CollectionUtils.isEmpty(highCourses)) {
                for (CourseSchedule highCourse : highCourses) {
                    if (!includeTeacherIds.contains(highCourse.getTeacherId())) {
                        includeTeacherIds.add(highCourse.getTeacherId());
                    }
                }
            }
        }

        for (ExtendTeacherBasicDto organAndSubjectTeacher : organAndSubjectTeachers) {
            if (!includeTeacherIds.contains(organAndSubjectTeacher.getId())) {
                includeTeacherIds.add(organAndSubjectTeacher.getId());
            }
        }

        if (CollectionUtils.isEmpty(new ArrayList<>(includeTeacherIds))) {
            return new ArrayList<>();
        }

        List<SimpleUserDto> userInfos = new ArrayList<>();
        Map<Integer, ExtendTeacherBasicDto> idTeacherMap = organAndSubjectTeachers.stream().collect(Collectors.toMap(ExtendTeacherBasicDto::getId, teacher -> teacher));
        for (Integer includeTeacherId : includeTeacherIds) {
            ExtendTeacherBasicDto teacher = idTeacherMap.get(includeTeacherId);
            SimpleUserDto simpleUserDto = new SimpleUserDto(includeTeacherId, teacher.getRealName());
            simpleUserDto.setAvatar(teacher.getAvatar());
            simpleUserDto.setIntroduction(teacher.getIntroduction());
            simpleUserDto.setSubjectNames(teacher.getSubjectNames());
            userInfos.add(simpleUserDto);
        }

        return userInfos;
    }

    @Override
    public Map<Integer, List<String>> getEnableApplyDatesWithWeek() {
        Map<Integer, List<String>> result = new HashMap<>();
        SysConfig practiceApplyIntervalTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_INTERVAL_TIME);
        Integer practiceApplyIntervalMinutes = practiceApplyIntervalTimeConfig.getParanValue(Integer.class);
        LocalTime dayStartTime = LocalTime.parse("00:00:00", DateUtil.timeFormatter);
        LocalTime dayEndTime = LocalTime.parse("00:00:00", DateUtil.timeFormatter);
        List<String> dayApplyTimes = new ArrayList<>();
        while (true) {
            dayApplyTimes.add(dayStartTime.format(DateUtil.timeFormatter));
            dayStartTime = dayStartTime.plusMinutes(practiceApplyIntervalMinutes);
            if (dayStartTime.compareTo(dayEndTime) == 0) {
                break;
            }
        }
        for (int i = 1; i <= 7; i++) {
            List<String> tempTimes = new ArrayList<>();
            for (String applyDayTime : dayApplyTimes) {
                tempTimes.add(applyDayTime);
            }
            result.put(i, tempTimes);
        }
        return result;
    }

    @Override
    public Map getPayPracticeTeacherFreeTimes(Integer userId, Integer teacherId, Integer buyMonths, boolean renew, Long groupId) {
        if (Objects.isNull(teacherId)) {
            throw new BizException("请选择教师");
        }
        SysUser student = sysUserFeignService.queryUserById(userId);
        if (Objects.isNull(student)) {
            throw new BizException("用户不存在");
        }
        if (Objects.isNull(student.getOrganId())) {
            throw new BizException("未找到用户分部属性");
        }
        if (renew && Objects.isNull(groupId)) {
            throw new BizException("请选择需要续费的课程");
        }
        Teacher teacher = teacherDao.get(teacherId);
        if (Objects.isNull(teacher)) {
            throw new BizException("教师不存在");
        }
        Map result = new HashMap();
        Set<Integer> disableApplyWeekDay = new HashSet<>();

        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        Integer practiceCourseMinutes = practiceCourseMinutesConfig.getParanValue(Integer.class);

        boolean checkTeacherLeaveDate = true;
        SysConfig allTeacherLeaveDataConfig = sysConfigService.findByParamName(SysConfigService.TEACHER_LEAVE_DATA);
        JSONObject allTeacherLeaveData = JSONObject.parseObject(allTeacherLeaveDataConfig.getParanValue());
        if (Objects.isNull(allTeacherLeaveData)) {
            checkTeacherLeaveDate = false;
        }

        TeacherFreeTime teacherFreeTime = teacherFreeTimeDao.findTeacherFreeTime(teacherId);
        JSONObject teacherFreeTimes = new JSONObject();
        Integer maxTeacherCourses = null;
        int holiday = 0;
        if (Objects.nonNull(teacherFreeTime)) {
            if (Objects.nonNull(teacherFreeTime.getTotalTimes())) {
                maxTeacherCourses = teacherFreeTime.getTotalTimes();
            }
            if (Objects.nonNull(teacherFreeTime.getHoliday())) {
                holiday = teacherFreeTime.getHoliday();
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getMonday())) {
                teacherFreeTimes.put(String.valueOf(DayOfWeek.MONDAY.getValue()), JSON.parseArray(teacherFreeTime.getMonday()));
            } else {
                disableApplyWeekDay.add(DayOfWeek.MONDAY.getValue());
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getTuesday())) {
                teacherFreeTimes.put(String.valueOf(DayOfWeek.TUESDAY.getValue()), JSON.parseArray(teacherFreeTime.getTuesday()));
            } else {
                disableApplyWeekDay.add(DayOfWeek.TUESDAY.getValue());
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getWednesday())) {
                teacherFreeTimes.put(String.valueOf(DayOfWeek.WEDNESDAY.getValue()), JSON.parseArray(teacherFreeTime.getWednesday()));
            } else {
                disableApplyWeekDay.add(DayOfWeek.WEDNESDAY.getValue());
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getThursday())) {
                teacherFreeTimes.put(String.valueOf(DayOfWeek.THURSDAY.getValue()), JSON.parseArray(teacherFreeTime.getThursday()));
            } else {
                disableApplyWeekDay.add(DayOfWeek.THURSDAY.getValue());
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getFriday())) {
                teacherFreeTimes.put(String.valueOf(DayOfWeek.FRIDAY.getValue()), JSON.parseArray(teacherFreeTime.getFriday()));
            } else {
                disableApplyWeekDay.add(DayOfWeek.FRIDAY.getValue());
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getSaturday())) {
                teacherFreeTimes.put(String.valueOf(DayOfWeek.SATURDAY.getValue()), JSON.parseArray(teacherFreeTime.getSaturday()));
            } else {
                disableApplyWeekDay.add(DayOfWeek.SATURDAY.getValue());
            }
            if (StringUtils.isNotBlank(teacherFreeTime.getSunday())) {
                teacherFreeTimes.put(String.valueOf(DayOfWeek.SUNDAY.getValue()), JSON.parseArray(teacherFreeTime.getSunday()));
            } else {
                disableApplyWeekDay.add(DayOfWeek.SUNDAY.getValue());
            }
        }

        DateTimeFormatter ddtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        DateTimeFormatter ddf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("HH:mm:ss");
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY, 1);
        ZoneId zoneId = ZoneId.systemDefault();

        LocalDate now = LocalDate.now();
        LocalDate tempCourseDate = LocalDate.parse("2020-03-01", DateUtil.dateFormatter);
        if (now.isBefore(tempCourseDate)) {
            now = tempCourseDate;
        }
        if (renew) {
            PracticeGroup userLatestPracticeGroup = practiceGroupDao.findUserPracticeGroup(userId, groupId);
            if (Objects.nonNull(userLatestPracticeGroup)) {
                LocalDate lastExpiredDay = LocalDateTime.ofInstant(userLatestPracticeGroup.getCoursesExpireDate().toInstant(), DateUtil.zoneId).plusSeconds(1).toLocalDate();
                if (Objects.nonNull(lastExpiredDay) && lastExpiredDay.compareTo(now) >= 0) {
                    now = lastExpiredDay;
                }
            }
        }
        now = now.plusDays(1);
        Date applyStartDate = Date.from(now.atStartOfDay(zoneId).toInstant());
        Date applyEndDate = Date.from(now.plusMonths(buyMonths).atStartOfDay(zoneId).toInstant());
        Date firstMonday = DateUtil.getWeekDayWithDate(applyStartDate, Calendar.MONDAY);
        Date secondSunday = DateUtil.getWeekDayWithDate(applyEndDate, Calendar.SUNDAY);

        Map<Integer, List<String>> weekNumApplyTimesMap = getEnableApplyDatesWithWeek();
//        List<CourseSchedule> allTeacherCourses = courseScheduleDao.findTeacherCoursesWithDateRange(teacherId, firstMonday, secondSunday);
        List<CourseSchedule> allTeacherCourses = courseScheduleDao.findTeacherCoursesWithDateRangeWithoutMusicGroup(teacherId, firstMonday, secondSunday);
        allTeacherCourses.sort(Comparator.comparing(CourseSchedule::getStartClassTime));

        JSONObject teacherLeaveData = null;
        if (checkTeacherLeaveDate) {
            teacherLeaveData = allTeacherLeaveData.getJSONObject(teacherId.toString());
        }

        if (Objects.isNull(teacherLeaveData)) {
            checkTeacherLeaveDate = false;
        }

        weekNumApplyTimesMap.remove(holiday);

        if (teacherId == 100473) {
            List<String> timeStrs = weekNumApplyTimesMap.get(DateUtil.calendarWeekNumNormalWeekNumMap.get(Calendar.MONDAY));
            LocalTime noonStartTime = LocalTime.parse("11:30:00", dtf);
            LocalTime noonEndTime = LocalTime.parse("14:30:00", dtf);
            LocalTime nightStartTime = LocalTime.parse("20:30:00", dtf);
            LocalTime defaultApplyTime;
            Iterator<String> iterator = timeStrs.iterator();
            while (iterator.hasNext()) {
                defaultApplyTime = LocalTime.parse(iterator.next(), dtf);
                LocalTime defaultApplyEndTime = defaultApplyTime.plusMinutes(practiceCourseMinutes);
                if (defaultApplyTime.compareTo(noonStartTime) <= 0
                        && defaultApplyEndTime.compareTo(noonEndTime) >= 0) {
                    iterator.remove();
                    continue;
                }
                if (defaultApplyEndTime.compareTo(nightStartTime) < 0) {
                    iterator.remove();
                    continue;
                }
            }
            weekNumApplyTimesMap.remove(DateUtil.calendarWeekNumNormalWeekNumMap.get(Calendar.SATURDAY));
            weekNumApplyTimesMap.put(DateUtil.calendarWeekNumNormalWeekNumMap.get(Calendar.MONDAY), timeStrs);
            weekNumApplyTimesMap.put(DateUtil.calendarWeekNumNormalWeekNumMap.get(Calendar.SUNDAY), timeStrs);
        } else if (checkTeacherLeaveDate) {
            for (String dateStr : teacherLeaveData.keySet()) {
                LocalDate parse = LocalDate.parse(dateStr, ddf);
                Date tempDate = DateUtil.stringToDate(dateStr, "yyyy-MM-dd");
                if (tempDate.compareTo(applyStartDate) < 0
                        || tempDate.compareTo(applyEndDate) >= 0) {
                    continue;
                }
                List<String> applyTimeStrs = weekNumApplyTimesMap.get(parse.getDayOfWeek().getValue());
                if (CollectionUtils.isEmpty(applyTimeStrs)) {
                    continue;
                }
                Iterator<String> iterator = applyTimeStrs.iterator();
                JSONObject leaveDateData = teacherLeaveData.getJSONObject(dateStr);
                LocalTime leaveStartTime = LocalDateTime.parse(leaveDateData.getString("leave_start_time"), ddtf).toLocalTime();
                LocalTime leaveEndTime = LocalDateTime.parse(leaveDateData.getString("leave_end_time"), ddtf).toLocalTime();
                LocalTime defaultApplyTime;
                while (iterator.hasNext()) {
                    defaultApplyTime = LocalTime.parse(iterator.next(), dtf);
                    LocalTime defaultApplyEndTime = defaultApplyTime.plusMinutes(practiceCourseMinutes);
                    if (defaultApplyTime.compareTo(leaveEndTime) <= 0
                            && defaultApplyEndTime.compareTo(leaveStartTime) >= 0) {
                        iterator.remove();
                        continue;
                    }
                }
                if (!CollectionUtils.isEmpty(applyTimeStrs)) {
                    weekNumApplyTimesMap.put(parse.getDayOfWeek().getValue(), applyTimeStrs);
                } else {
                    weekNumApplyTimesMap.remove(parse.getDayOfWeek().getValue());
                }
            }
        }

        Iterator<Integer> weekNumApplyTimeIterator = weekNumApplyTimesMap.keySet().iterator();
        while (weekNumApplyTimeIterator.hasNext()) {
            Integer weekNum = weekNumApplyTimeIterator.next();
            JSONArray teacherWeekDayFreeTimes = teacherFreeTimes.getJSONArray(String.valueOf(weekNum));
            if (Objects.nonNull(teacherWeekDayFreeTimes)) {
                List<String> applyTimeStrs = weekNumApplyTimesMap.get(weekNum);
                if (CollectionUtils.isEmpty(applyTimeStrs)) {
                    continue;
                }
                Iterator<String> iterator = applyTimeStrs.iterator();
                while (iterator.hasNext()) {
                    LocalTime enableApplyStartTime = LocalTime.parse(iterator.next(), dtf);
                    LocalTime enableApplyEndTime = enableApplyStartTime.plusMinutes(practiceCourseMinutes);
                    boolean isInclude = false;
                    for (Object teacherWeekDayFreeTimeObject : teacherWeekDayFreeTimes) {
                        JSONObject teacherWeekDayFreeTime = JSONObject.parseObject(teacherWeekDayFreeTimeObject.toString());
                        LocalTime teacherFreeStartTime = LocalTime.parse(teacherWeekDayFreeTime.getString("startTime"), DateUtil.timeFormatter);
                        LocalTime teacherFreeEndTime = LocalTime.parse(teacherWeekDayFreeTime.getString("endTime"), DateUtil.timeFormatter);
                        if (enableApplyEndTime.compareTo(teacherFreeEndTime) <= 0
                                && enableApplyStartTime.compareTo(teacherFreeStartTime) >= 0) {
                            isInclude = true;
                            break;
                        }
                    }
                    if (!isInclude) {
                        iterator.remove();
                    }
                }
                if (CollectionUtils.isEmpty(applyTimeStrs)) {
                    weekNumApplyTimeIterator.remove();
                } else {
                    weekNumApplyTimesMap.put(weekNum, applyTimeStrs);
                }
            } else {
                weekNumApplyTimeIterator.remove();
            }
        }

        if (CollectionUtils.isEmpty(allTeacherCourses)) {
            result.put("teacherFreeDays", weekNumApplyTimesMap);
            return result;
        }

        int courseWeekNum = 0;
        int practiceCourseNum = 0;
        Set<Integer> disableApplyWeekNum = new HashSet<>();
        LocalDateTime tempClassDateTime;
        for (int i = 0; i < allTeacherCourses.size(); i++) {
            CourseSchedule teacherCourse = allTeacherCourses.get(i);
            tempClassDateTime = LocalDateTime.ofInstant(teacherCourse.getClassDate().toInstant(), zoneId);
            if (tempClassDateTime.get(weekFields.weekOfYear()) != courseWeekNum) {
                courseWeekNum = tempClassDateTime.get(weekFields.weekOfYear());
                practiceCourseNum = 0;
                practiceCourseNum = 1;
            } else if (teacherCourse.getGroupType().equals(GroupType.PRACTICE)) {
                practiceCourseNum += 1;
            }
            if (Objects.nonNull(maxTeacherCourses) && practiceCourseNum >= maxTeacherCourses) {
                disableApplyWeekNum.add(courseWeekNum);
            }
        }

        if (disableApplyWeekNum.size() > 2) {
            result.put("teacherFreeDays", new HashMap<>());
            return result;
        }

        LocalDateTime applyStartDateTime = LocalDateTime.ofInstant(applyStartDate.toInstant(), DateUtil.zoneId);
        int applyStartDateWeekNum = applyStartDateTime.get(weekFields.weekOfYear());
        LocalDateTime applyEndDateTime = LocalDateTime.ofInstant(applyEndDate.toInstant(), DateUtil.zoneId);
        int applyEndDateWeekNum = applyEndDateTime.get(weekFields.weekOfYear());

        if (!CollectionUtils.isEmpty(disableApplyWeekNum) && !disableApplyWeekNum.contains(applyStartDateWeekNum) && !disableApplyWeekNum.contains(applyEndDateWeekNum)) {
            result.put("teacherFreeDays", new HashMap<>());
            return result;
        }

        if (disableApplyWeekNum.contains(applyStartDateWeekNum)) {
            LocalDateTime applyStartSunDay = applyStartDateTime.with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.SUNDAY.getValue());
            while (applyStartDateTime.compareTo(applyStartSunDay) <= 0) {
                weekNumApplyTimesMap.remove(applyStartDateTime.getDayOfWeek().getValue());
                applyStartDateTime = applyStartDateTime.plusDays(1);
            }
        }

        if (disableApplyWeekNum.contains(applyEndDateWeekNum)) {
            LocalDateTime applyEndMonDay = applyEndDateTime.with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue());
            while (applyEndMonDay.compareTo(applyEndDateTime) < 0) {
                weekNumApplyTimesMap.remove(applyEndMonDay.getDayOfWeek().getValue());
                applyEndMonDay = applyEndMonDay.plusDays(1);
            }
        }


        for (CourseSchedule teacherCourse : allTeacherCourses) {
            if (teacherCourse.getEndClassTime().before(applyStartDate) || teacherCourse.getStartClassTime().after(applyEndDate)) {
                continue;
            }
            LocalDateTime courseStartTime = LocalDateTime.ofInstant(teacherCourse.getStartClassTime().toInstant(), zoneId);
            LocalDateTime courseEndTime = LocalDateTime.ofInstant(teacherCourse.getEndClassTime().toInstant(), zoneId);
            List<String> applyTimeStrs = weekNumApplyTimesMap.get(courseStartTime.getDayOfWeek().getValue());
            if (CollectionUtils.isEmpty(applyTimeStrs)) {
                continue;
            }
            Iterator<String> iterator = applyTimeStrs.iterator();
            LocalTime defaultApplyTime;
            while (iterator.hasNext()) {
                defaultApplyTime = LocalTime.parse(iterator.next(), dtf);
                LocalTime defaultApplyEndTime = defaultApplyTime.plusMinutes(practiceCourseMinutes);
                if (defaultApplyTime.compareTo(courseEndTime.toLocalTime()) <= 0
                        && defaultApplyEndTime.compareTo(courseStartTime.toLocalTime()) >= 0) {
                    iterator.remove();
                    continue;
                }
            }
            if (CollectionUtils.isEmpty(applyTimeStrs)) {
                weekNumApplyTimesMap.remove(courseStartTime.getDayOfWeek().getValue());
            } else {
                weekNumApplyTimesMap.put(courseStartTime.getDayOfWeek().getValue(), applyTimeStrs);
            }
        }
        if (weekNumApplyTimesMap.size() <= 1) {
            weekNumApplyTimesMap = new HashMap<>();
        }
        result.put("teacherFreeDays", weekNumApplyTimesMap);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public HttpResponseResult buyPracticeGroup(PracticeGroupBuyDto practiceGroupBuyParams) {
        if (Objects.isNull(practiceGroupBuyParams.getUserId())) {
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "请选择教师");
        }
        if (Objects.isNull(practiceGroupBuyParams.getSubjectId())) {
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "请选择声部");
        }
        if (Objects.isNull(practiceGroupBuyParams.getBuyMonths())) {
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "请选择需要购买的月数");
        }
        if (Objects.isNull(practiceGroupBuyParams.getDrillTimesOnWeek())) {
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "请选择单周陪练次数");
        }
        if (CollectionUtils.isEmpty(practiceGroupBuyParams.getDrillTimes())) {
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "请选择训练时间");
        }
        if (practiceGroupBuyParams.getDrillTimesOnWeek() != practiceGroupBuyParams.getDrillTimes().size()) {
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "训练时间与陪练次数不匹配");
        }

        SysUser sysUser = sysUserFeignService.queryUserById(practiceGroupBuyParams.getStudentId());

        Subject subject = subjectDao.get(practiceGroupBuyParams.getSubjectId());
        if (Objects.isNull(subject)) {
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "预约失败，声部选择错误，请重试。");
        }

        Teacher teacher = teacherService.getDetail(practiceGroupBuyParams.getUserId());
        if (Objects.isNull(teacher)) {
            throw new BizException("老师不存在");
        }

        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        Integer practiceCourseMinutes = practiceCourseMinutesConfig.getParanValue(Integer.class);
        SysConfig practiceCourseSalaryConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_SALARY);
        SysConfig practiceBuyActivityExpireDateConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_BUY_ACTIVITY_EXPIRE_DATE);
        Date practiceBuyActivityExpireDate = DateUtil.stringToDate(practiceBuyActivityExpireDateConfig.getParanValue(), "yyyy-MM-dd HH:mm:ss");
        SysConfig practicePromotionActivityStartDateConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_PROMOTION_ACTIVITY_START_DATE);
        Date practicePromotionActivityStartDate = DateUtil.stringToDate(practicePromotionActivityStartDateConfig.getParanValue(), "yyyy-MM-dd HH:mm:ss");

        Date now = new Date();

        int studentExitChargePractices = practiceGroupDao.checkStudentExitChargePractice(practiceGroupBuyParams.getStudentId());

        LocalDate courseStartDay = LocalDate.now();
        LocalDate tempCourseLocalDate = LocalDate.parse("2020-03-01", DateUtil.dateFormatter);
        if (courseStartDay.isBefore(tempCourseLocalDate)) {
            courseStartDay = tempCourseLocalDate;
        }
        if (practiceGroupBuyParams.isRenew()) {
            if (Objects.isNull(practiceGroupBuyParams.getGroupId())) {
                return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "请选择需要续费的课程");
            }
            PracticeGroup userLatestPracticeGroup = practiceGroupDao.findUserPracticeGroup(practiceGroupBuyParams.getStudentId(), practiceGroupBuyParams.getGroupId());
            if (Objects.nonNull(userLatestPracticeGroup)) {
                if (!userLatestPracticeGroup.getGroupStatus().equals(GroupStatusEnum.NORMAL) || userLatestPracticeGroup.getCoursesExpireDate().before(now)) {
                    throw new BizException("当前课程组已经无法续费，请选择购买");
                }
                LocalDate lastExpiredDay = LocalDateTime.ofInstant(userLatestPracticeGroup.getCoursesExpireDate().toInstant(), DateUtil.zoneId).toLocalDate();
                if (Objects.nonNull(lastExpiredDay) && lastExpiredDay.compareTo(courseStartDay) >= 0) {
                    courseStartDay = lastExpiredDay;
                }
                practiceGroupBuyParams.setBeRenewGroupId(practiceGroupBuyParams.getGroupId());
                practiceGroupBuyParams.setEducationalTeacherId(userLatestPracticeGroup.getEducationalTeacherId());
            } else {
                return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "需要续费的课程组不存在");
            }
        }

        if (Objects.isNull(practiceGroupBuyParams.getEducationalTeacherId())) {
            List<String> musicGroupIds = studentRegistrationDao.queryStudentMusicGroup(practiceGroupBuyParams.getStudentId());
            if (!CollectionUtils.isEmpty(musicGroupIds) && musicGroupIds.size() <= 1) {
                MusicGroup musicGroup = musicGroupDao.get(musicGroupIds.get(0));
                if (Objects.nonNull(musicGroup)) {
                    practiceGroupBuyParams.setEducationalTeacherId(musicGroup.getEducationalTeacherId());
                }
            }
        }

        courseStartDay = courseStartDay.plusDays(1);
        Date courseStartDate = Date.from(courseStartDay.atStartOfDay(DateUtil.zoneId).toInstant());
        practiceGroupBuyParams.setCoursesStartDate(courseStartDate);
        LocalDate currentExpiredDay = courseStartDay.plusMonths(practiceGroupBuyParams.getBuyMonths());
        Date courseExpiredDate = Date.from(currentExpiredDay.atStartOfDay(DateUtil.zoneId).toInstant());
        courseExpiredDate = DateUtil.addSeconds(courseExpiredDate, -1);
        practiceGroupBuyParams.setCoursesExpireDate(courseExpiredDate);

//        LocalDate courseStartMonday=courseStartDay.with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue());
        LocalDate courseExpiredSunday = currentExpiredDay.with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.SUNDAY.getValue());
//        Date courseStartMondayDate = Date.from(courseStartMonday.atStartOfDay(DateUtil.zoneId).toInstant());
        Date courseExpiredSundayDate = Date.from(courseExpiredSunday.atStartOfDay(DateUtil.zoneId).toInstant());

        practiceGroupBuyParams.setName(subject.getName() + "•" + sysUser.getUsername());
        List<CourseSchedule> practiceCourses = createPracticeCourses(practiceGroupBuyParams, practiceCourseMinutes);
        practiceCourses.sort(Comparator.comparing(CourseSchedule::getStartClassTime));

        LocalDate classStartDate = LocalDateTime.ofInstant(practiceCourses.get(0).getClassDate().toInstant(), DateUtil.zoneId).toLocalDate();
        LocalDate courseStartMonday = classStartDate.with(DateUtil.weekFields.dayOfWeek(), DayOfWeek.MONDAY.getValue());
        Date courseStartMondayDate = Date.from(courseStartMonday.atStartOfDay(DateUtil.zoneId).toInstant());

        List<CourseSchedule> allTeacherCourses = courseScheduleDao.findTeacherCoursesWithDateRange(practiceGroupBuyParams.getUserId(), courseStartMondayDate, courseExpiredSundayDate);

        TeacherFreeTime teacherFreeTime = teacherFreeTimeDao.findTeacherFreeTime(practiceGroupBuyParams.getUserId());
        Integer maxTeacherCourses = null;
        if (Objects.nonNull(teacherFreeTime)) {
            if (Objects.nonNull(teacherFreeTime.getTotalTimes())) {
                maxTeacherCourses = teacherFreeTime.getTotalTimes();
            }
        } else {
            return BaseController.failed(HttpStatus.MULTIPLE_CHOICES, "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请重新选择时段或更换老师后重试。");
        }

        int courseWeekNum = 0;
        int practiceCourseNum = 0;
        LocalDateTime tempClassDateTime;
        for (int i = 0; i < allTeacherCourses.size(); i++) {
            CourseSchedule teacherCourse = allTeacherCourses.get(i);
            tempClassDateTime = LocalDateTime.ofInstant(teacherCourse.getClassDate().toInstant(), DateUtil.zoneId);
            if (i == 0 && teacherCourse.getGroupType().equals(GroupType.PRACTICE)) {
                practiceCourseNum += 1;
                courseWeekNum = tempClassDateTime.get(DateUtil.weekFields.weekOfYear());
            }
            if (Objects.nonNull(maxTeacherCourses) && practiceCourseNum >= maxTeacherCourses) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return BaseController.failed(HttpStatus.MULTIPLE_CHOICES, "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请重新选择时段或更换老师后重试。");
            }
            if (tempClassDateTime.get(DateUtil.weekFields.weekOfYear()) != courseWeekNum) {
                courseWeekNum = tempClassDateTime.get(DateUtil.weekFields.weekOfYear());
                practiceCourseNum = 0;
            }
            if (teacherCourse.getGroupType().equals(GroupType.PRACTICE)) {
                practiceCourseNum += 1;
            }
        }
        allTeacherCourses = new ArrayList<>();
//
//        practiceGroupBuyParams.setName(subject.getName() + "•" + sysUser.getUsername());
//
//        List<CourseSchedule> practiceCourses = createPracticeCourses(practiceGroupBuyParams, practiceCourseMinutes);

        JSONObject drillTimesObject = new JSONObject();
        for (PracticeDrillTimeDto drillTime : practiceGroupBuyParams.getDrillTimes()) {
            drillTimesObject.put(String.valueOf(drillTime.getWeekNum()), drillTime.getTimeStr());
        }

        PracticeGroupSellPrice practiceGroupSellPrice = practiceGroupSellPriceDao.get(sysUser.getOrganId());
        if (Objects.isNull(practiceGroupSellPrice)) {
            throw new BizException("所在城市暂不参与此活动");
        }
        BigDecimal oneMonthPrice;
        if (practiceBuyActivityExpireDate.after(now)) {
            oneMonthPrice = practiceGroupBuyParams.getDrillTimesOnWeek() == 1 ? practiceGroupSellPrice.getOnceActivityPrice() : practiceGroupSellPrice.getTwiceActivityPrice();
            if (!now.before(practicePromotionActivityStartDate) && practiceGroupBuyParams.getBuyMonths() >= 3 && studentExitChargePractices > 0) {
                oneMonthPrice = practiceGroupBuyParams.getDrillTimesOnWeek() == 1 ? practiceGroupSellPrice.getOnceQuartActivityPrice() : practiceGroupSellPrice.getTwiceQuartActivityPrice();
            } else if (!now.before(practicePromotionActivityStartDate) && studentExitChargePractices <= 0) {
                oneMonthPrice = practiceGroupBuyParams.getDrillTimesOnWeek() == 1 ? practiceGroupSellPrice.getOnceOriginalPrice() : practiceGroupSellPrice.getTwiceOriginalPrice();
            }
        } else {
            oneMonthPrice = practiceGroupBuyParams.getDrillTimesOnWeek() == 1 ? practiceGroupSellPrice.getOnceOriginalPrice() : practiceGroupSellPrice.getTwiceOriginalPrice();
        }
        BigDecimal amount = oneMonthPrice.multiply(new BigDecimal(practiceGroupBuyParams.getBuyMonths()));
        if (!now.before(practicePromotionActivityStartDate) && now.before(practiceBuyActivityExpireDate) && studentExitChargePractices <= 0) {
            amount = oneMonthPrice.multiply(new BigDecimal(1));
        }

        practiceGroupBuyParams.setDrillTimesJson(drillTimesObject.toJSONString());
        practiceGroupBuyParams.setOrganId(sysUser.getOrganId());
        practiceGroupBuyParams.setSingleClassMinutes(practiceCourseMinutes);
        practiceGroupBuyParams.setGroupStatus(GroupStatusEnum.LOCK);
        practiceGroupBuyParams.setType(PracticeGroupType.CHARGE);
        practiceGroupDao.insert(practiceGroupBuyParams);

        //创建班级信息
        ClassGroup classGroup = new ClassGroup();
        classGroup.setSubjectIdList(practiceGroupBuyParams.getSubjectId().toString());
        classGroup.setExpectStudentNum(1);
        classGroup.setStudentNum(1);
        classGroup.setName(practiceGroupBuyParams.getName());
        classGroup.setTotalClassTimes(practiceCourses.size());
        classGroup.setType(ClassGroupTypeEnum.PRACTICE);
        classGroup.setDelFlag(0);
        classGroup.setGroupType(GroupType.PRACTICE);
        classGroup.setMusicGroupId(practiceGroupBuyParams.getId().toString());
        classGroup.setCreateTime(now);
        classGroup.setUpdateTime(now);
        classGroupDao.insert(classGroup);

        //创建班级老师关联记录
        ClassGroupTeacherMapper classGroupTeacherMapper = new ClassGroupTeacherMapper();
        classGroupTeacherMapper.setMusicGroupId(practiceGroupBuyParams.getId().toString());
        classGroupTeacherMapper.setClassGroupId(classGroup.getId());
        classGroupTeacherMapper.setTeacherRole(TeachTypeEnum.BISHOP);
        classGroupTeacherMapper.setUserId(practiceGroupBuyParams.getUserId());
        classGroupTeacherMapper.setGroupType(GroupType.PRACTICE);
        classGroupTeacherMapper.setCreateTime(now);
        classGroupTeacherMapper.setUpdateTime(now);
        classGroupTeacherMapperDao.insert(classGroupTeacherMapper);

        //创建班级与老师课酬记录
        ClassGroupTeacherSalary classGroupTeacherSalary = new ClassGroupTeacherSalary();
        classGroupTeacherSalary.setMusicGroupId(practiceGroupBuyParams.getId().toString());
        classGroupTeacherSalary.setClassGroupId(classGroup.getId());
        classGroupTeacherSalary.setTeacherRole(TeachTypeEnum.BISHOP);
        classGroupTeacherSalary.setUserId(practiceGroupBuyParams.getUserId());
        classGroupTeacherSalary.setSalary(new BigDecimal(practiceCourseSalaryConfig.getParanValue()));
        classGroupTeacherSalary.setOnlineClassesSalary(new BigDecimal(practiceCourseSalaryConfig.getParanValue()));
        classGroupTeacherSalary.setGroupType(GroupType.PRACTICE);
        classGroupTeacherSalary.setCreateTime(now);
        classGroupTeacherSalary.setUpdateTime(now);
        classGroupTeacherSalaryDao.insert(classGroupTeacherSalary);

        //班级学生关联表
        ClassGroupStudentMapper classGroupStudentMapper = new ClassGroupStudentMapper();
        classGroupStudentMapper.setMusicGroupId(practiceGroupBuyParams.getId().toString());
        classGroupStudentMapper.setClassGroupId(classGroup.getId());
        classGroupStudentMapper.setUserId(practiceGroupBuyParams.getStudentId());
        classGroupStudentMapper.setCreateTime(now);
        classGroupStudentMapper.setStatus(ClassGroupStudentStatusEnum.NORMAL);
        classGroupStudentMapper.setGroupType(GroupType.PRACTICE);
        classGroupStudentMapperDao.insert(classGroupStudentMapper);

        List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries = new ArrayList<>();
        List<CourseScheduleStudentPayment> courseScheduleStudentPayments = new ArrayList<>();
        List<TeacherAttendance> teacherAttendances = new ArrayList<>();

        for (CourseSchedule courseSchedule : practiceCourses) {
            //课表
            courseSchedule.setMusicGroupId(practiceGroupBuyParams.getId().toString());
            courseSchedule.setClassGroupId(classGroup.getId());
            courseSchedule.setStatus(CourseStatusEnum.NOT_START);
            courseSchedule.setCreateTime(now);
            courseSchedule.setUpdateTime(now);
            courseSchedule.setTeachMode(TeachModeEnum.ONLINE);
            courseSchedule.setType(CourseSchedule.CourseScheduleType.PRACTICE);
            courseSchedule.setGroupType(GroupType.PRACTICE);
            courseSchedule.setIsLock(1);
            courseSchedule.setOrganId(practiceGroupBuyParams.getOrganId());
        }
        courseScheduleDao.batchAddCourseSchedules(practiceCourses);
        TeacherDefaultPracticeGroupSalary teacherDefaultPracticeGroupSalary = teacherDefaultPracticeGroupSalaryDao.findByTeacherAndCourseMinutes(practiceGroupBuyParams.getUserId(), practiceCourseMinutes);
        BigDecimal teacherDefaultSalary = BigDecimal.ZERO;
        if (Objects.nonNull(teacherDefaultPracticeGroupSalary)) {
            teacherDefaultSalary = teacherDefaultPracticeGroupSalary.getMainTeacherSalary();
        }
        BigDecimal studentSingleCourseCost = amount.divide(new BigDecimal(practiceCourses.size()), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN);

        BigDecimal oneMonthOriginalPrice=practiceGroupBuyParams.getDrillTimesOnWeek()==1?practiceGroupSellPrice.getOnceOriginalPrice():practiceGroupSellPrice.getTwiceOriginalPrice();
        BigDecimal originalAmount = oneMonthOriginalPrice.multiply(new BigDecimal(practiceGroupBuyParams.getBuyMonths()));
        BigDecimal studentSingleCourseOriginalCost=originalAmount.divide(new BigDecimal(practiceCourses.size()), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN);

        for (CourseSchedule courseSchedule : practiceCourses) {
            //课程与老师薪水表
            CourseScheduleTeacherSalary courseScheduleTeacherSalary = new CourseScheduleTeacherSalary();
            courseScheduleTeacherSalary.setCourseScheduleId(courseSchedule.getId());
            courseScheduleTeacherSalary.setGroupType(GroupType.PRACTICE);
            courseScheduleTeacherSalary.setMusicGroupId(practiceGroupBuyParams.getId().toString());
            courseScheduleTeacherSalary.setTeacherRole(classGroupTeacherMapper.getTeacherRole());
            courseScheduleTeacherSalary.setUserId(practiceGroupBuyParams.getUserId());
            courseScheduleTeacherSalary.setExpectSalary(teacherDefaultSalary);
            courseScheduleTeacherSalary.setCreateTime(now);
            courseScheduleTeacherSalary.setUpdateTime(now);
            courseScheduleTeacherSalary.setClassGroupId(classGroup.getId());
            courseScheduleTeacherSalaries.add(courseScheduleTeacherSalary);

            //学生缴费记录
            CourseScheduleStudentPayment courseScheduleStudentPayment = new CourseScheduleStudentPayment();
            courseScheduleStudentPayment.setGroupType(GroupType.PRACTICE);
            courseScheduleStudentPayment.setMusicGroupId(practiceGroupBuyParams.getId().toString());
            courseScheduleStudentPayment.setCourseScheduleId(courseSchedule.getId());
            courseScheduleStudentPayment.setUserId(practiceGroupBuyParams.getStudentId());
            courseScheduleStudentPayment.setExpectPrice(studentSingleCourseCost);
            courseScheduleStudentPayment.setActualPrice(studentSingleCourseCost);
            courseScheduleStudentPayment.setOriginalPrice(studentSingleCourseOriginalCost);
            courseScheduleStudentPayment.setClassGroupId(classGroup.getId());
            courseScheduleStudentPayment.setCreateTime(now);
            courseScheduleStudentPayment.setUpdateTime(now);
            courseScheduleStudentPayments.add(courseScheduleStudentPayment);

            //教师签到记录
            TeacherAttendance teacherAttendance = new TeacherAttendance();
            teacherAttendance.setMusicGroupId(practiceGroupBuyParams.getId().toString());
            teacherAttendance.setTeacherId(practiceGroupBuyParams.getUserId());
            teacherAttendance.setClassGroupId(classGroup.getId());
            teacherAttendance.setGroupType(GroupType.PRACTICE);
            teacherAttendance.setCourseScheduleId(courseSchedule.getId());
            teacherAttendance.setCreateTime(now);
            teacherAttendances.add(teacherAttendance);
        }
        courseScheduleTeacherSalaryDao.batchInsert(courseScheduleTeacherSalaries);
        courseScheduleStudentPaymentDao.batchInsert(courseScheduleStudentPayments);
        teacherAttendanceDao.batchInsert(teacherAttendances);

        try {
            courseScheduleService.checkNewCourseSchedules(practiceCourses, false,false);
//            courseScheduleService.checkNewCourseSchedulesWithoutMusicGroup(practiceCourses,false);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            String errMessage = new String();
            if (e.getMessage().indexOf("主教冲突") != -1) {
                errMessage = "抱歉啦，当前所选时段组合，「" + teacher.getRealName() + "」老师已被预约，请重新选择时段或更换老师后重试。";
            } else {
                String courseName = e.getMessage().substring(e.getMessage().indexOf(")-") + 2);
                courseName = courseName.substring(0, courseName.indexOf("("));
                errMessage = "抱歉啦，当前所选时段组合，与您现有课程「";
                errMessage += courseName;
                errMessage += "」时段冲突，请选择其他时段重试。";
            }
            return BaseController.failed(HttpStatus.FOUND, errMessage);
        }

        StudentPaymentOrder studentPaymentOrder = new StudentPaymentOrder();
        studentPaymentOrder.setUserId(practiceGroupBuyParams.getStudentId());
        studentPaymentOrder.setGroupType(GroupType.PRACTICE);
        String orderNo = idGeneratorService.generatorId("payment") + "";
        studentPaymentOrder.setOrderNo(orderNo);
        studentPaymentOrder.setStatus(DealStatusEnum.ING);
        studentPaymentOrder.setType(OrderTypeEnum.PRACTICE_GROUP_BUY);
        if (practiceGroupBuyParams.isRenew()) {
            studentPaymentOrder.setType(OrderTypeEnum.PRACTICE_GROUP_RENEW);
        }
        studentPaymentOrder.setExpectAmount(amount);
        studentPaymentOrder.setMusicGroupId(practiceGroupBuyParams.getId().toString());
        studentPaymentOrder.setActualAmount(studentPaymentOrder.getExpectAmount());
        studentPaymentOrder.setClassGroupId(classGroup.getId());
        studentPaymentOrder.setVersion(0);
        studentPaymentOrderService.insert(studentPaymentOrder);

        BigDecimal balance = BigDecimal.ZERO;
        if (practiceGroupBuyParams.isUseBalancePayment() || studentPaymentOrder.getExpectAmount().doubleValue() == 0) {
            SysUserCashAccount userCashAccount = sysUserCashAccountService.getLocked(practiceGroupBuyParams.getStudentId());
            if (userCashAccount == null) {
                throw new BizException("用户账户找不到");
            }
            studentPaymentOrder.setPaymentChannel("BALANCE");
            if (userCashAccount.getBalance().subtract(studentPaymentOrder.getExpectAmount()).doubleValue() >= 0) {
                studentPaymentRouteOrderService.addRouteOrder(orderNo, practiceGroupBuyParams.getOrganId(), amount);

                // 更新订单信息
                studentPaymentOrder.setActualAmount(new BigDecimal(0));
                studentPaymentOrder.setBalancePaymentAmount(studentPaymentOrder.getExpectAmount());
                studentPaymentOrder.setStatus(DealStatusEnum.SUCCESS);
                studentPaymentOrder.setUpdateTime(now);
                studentPaymentOrder.setOrganId(practiceGroupBuyParams.getOrganId());
                studentPaymentOrder.setRoutingOrganId(practiceGroupBuyParams.getOrganId());

                sysUserCashAccountService.updateBalance(practiceGroupBuyParams.getStudentId(), studentPaymentOrder.getExpectAmount().negate(), PlatformCashAccountDetailTypeEnum.PAY_FEE, "网管课购买");

                studentPaymentOrder.setPayTime(now);
                this.orderCallback(studentPaymentOrder);

                Map<String, Object> result = new HashMap<>();
                result.put("orderNo", studentPaymentOrder.getOrderNo());

                return BaseController.succeed(result);
            } else {
                if (userCashAccount.getBalance().doubleValue() > 0) {
                    balance = userCashAccount.getBalance();
                    sysUserCashAccountService.updateBalance(practiceGroupBuyParams.getStudentId(), balance.negate(), PlatformCashAccountDetailTypeEnum.PAY_FEE, "网管课购买");
                    amount = studentPaymentOrder.getExpectAmount().subtract(userCashAccount.getBalance());
                    studentPaymentOrder.setActualAmount(amount);
                    studentPaymentOrder.setBalancePaymentAmount(userCashAccount.getBalance());
                } else {
                    studentPaymentOrder.setBalancePaymentAmount(new BigDecimal(0));
                }
            }
        }

        String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");
        Map<String, BigDecimal> classFee = new HashMap<>();
        classFee.put("course", amount);
        classFee.put("instrument", BigDecimal.ZERO);
        classFee.put("accessories", BigDecimal.ZERO);
        classFee.put("other", BigDecimal.ZERO);
        try {
            Map<String, Object> payMap = payService.getPayMap(
                    amount,
                    balance,
                    orderNo,
                    baseApiUrl + "/api-student/studentOrder/notify",
                    baseApiUrl + "/api-student/studentOrder/paymentResult?orderNo=" + orderNo,
                    "网管课购买",
                    practiceGroupBuyParams.getName(),
                    practiceGroupBuyParams.getOrganId(),
                    "practiceBuy"
            );

            studentPaymentOrder.setOrganId(practiceGroupBuyParams.getOrganId());
            studentPaymentOrder.setMerNos((String) payMap.get("routingMerNos"));
            studentPaymentOrder.setPaymentChannel((String) payMap.get("type"));
            studentPaymentOrder.setUpdateTime(now);
            studentPaymentOrderService.update(studentPaymentOrder);

            return BaseController.succeed(payMap);
        } catch (Exception e) {
            LOGGER.error("网管课购买失败：", e.getCause());
            throw new BizException("订单提交超时，请尝试重新提交购买");
        }
    }

    private List<CourseSchedule> createPracticeCourses(PracticeGroupBuyDto practiceGroupBuyParams, Integer practiceCourseMinutes) {
        List<CourseSchedule> courseSchedules = new ArrayList<>();
        LocalDate courseStartDay = LocalDateTime.ofInstant(practiceGroupBuyParams.getCoursesStartDate().toInstant(), DateUtil.zoneId).toLocalDate();
        LocalDate courseEndDay = LocalDateTime.ofInstant(practiceGroupBuyParams.getCoursesExpireDate().toInstant(), DateUtil.zoneId).plusSeconds(1).toLocalDate();
        while (courseStartDay.compareTo(courseEndDay) < 0) {
            int dayOfWeek = courseStartDay.get(DateUtil.weekFields.dayOfWeek());
            for (PracticeDrillTimeDto drillTime : practiceGroupBuyParams.getDrillTimes()) {
                if (dayOfWeek == drillTime.getWeekNum()) {
                    CourseSchedule courseSchedule = new CourseSchedule();
                    String courseStartDayStr = DateUtil.dateFormatter.format(courseStartDay);
                    String courseStartDateTimeStr = courseStartDayStr + " " + drillTime.getTimeStr();
                    LocalDateTime courseStartDateTime = LocalDateTime.parse(courseStartDateTimeStr, DateUtil.dateTimeFormatter);
                    LocalDateTime courseEndDateTime = courseStartDateTime.plusMinutes(practiceCourseMinutes);
                    courseSchedule.setClassDate(Date.from(courseStartDay.atStartOfDay(DateUtil.zoneId).toInstant()));
                    courseSchedule.setStartClassTime(Date.from(courseStartDateTime.atZone(DateUtil.zoneId).toInstant()));
                    courseSchedule.setEndClassTime(Date.from(courseEndDateTime.atZone(DateUtil.zoneId).toInstant()));
                    courseSchedule.setName(practiceGroupBuyParams.getName());
                    courseSchedule.setTeacherId(practiceGroupBuyParams.getUserId());
                    courseSchedule.setActualTeacherId(practiceGroupBuyParams.getUserId());
                    courseSchedule.setTeachMode(TeachModeEnum.ONLINE);
                    courseSchedule.setType(CourseSchedule.CourseScheduleType.PRACTICE);
                    courseSchedule.setStatus(CourseStatusEnum.NOT_START);
                    courseSchedules.add(courseSchedule);
                }
            }
            courseStartDay = courseStartDay.plusDays(1);
        }

        return courseSchedules;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public void orderCallback(StudentPaymentOrder order) {
        StudentPaymentOrder studentPaymentOrder = studentPaymentOrderDao.get(order.getId());
        if (!studentPaymentOrder.getStatus().equals(DealStatusEnum.ING)) {
            return;
        }
        SysUserCashAccount sysUserCashAccount = sysUserCashAccountService.get(order.getUserId());
        List<StudentPaymentOrder> userGroupOrders = studentPaymentOrderDao.findUserGroupOrders(order.getUserId(), order.getMusicGroupId(), order.getGroupType(), null);
        Map<DealStatusEnum, Long> statusOrderNumMap = userGroupOrders.stream().collect(Collectors.groupingBy(StudentPaymentOrder::getStatus, Collectors.counting()));
        Long successOrderNum = statusOrderNumMap.get(DealStatusEnum.SUCCESS);
        if (Objects.nonNull(successOrderNum) && successOrderNum > 0) {
            if (order.getStatus().equals(DealStatusEnum.FAILED) && Objects.nonNull(order.getBalancePaymentAmount()) && order.getBalancePaymentAmount().compareTo(BigDecimal.ZERO) > 0) {
                sysUserCashAccountService.updateBalance(order.getUserId(), order.getBalancePaymentAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "网管课支付失败，退还余额");
            }
            if (order.getStatus().equals(DealStatusEnum.SUCCESS)) {
                sysUserCashAccountService.updateBalance(order.getUserId(), order.getExpectAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "重复支付，退还余额");
                order.setMemo("重复支付,进入余额");
            }
            studentPaymentOrderDao.update(order);
            return;
        }

        Long ingOrderNum = statusOrderNumMap.get(DealStatusEnum.ING);
        if (Objects.nonNull(ingOrderNum) && ingOrderNum > 1 && order.getStatus().equals(DealStatusEnum.FAILED)) {
            if (Objects.nonNull(order.getBalancePaymentAmount()) && order.getBalancePaymentAmount().compareTo(BigDecimal.ZERO) > 0) {
                sysUserCashAccountService.updateBalance(order.getUserId(), order.getBalancePaymentAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "网管课支付失败，退还余额");
            }
            studentPaymentOrderDao.update(order);
            return;
        }

        if (order.getStatus().equals(DealStatusEnum.SUCCESS)) {
            courseScheduleDao.updateGroupCourseLock(order.getMusicGroupId(), GroupType.PRACTICE, 0);
        } else {
            if (Objects.nonNull(order.getBalancePaymentAmount()) && order.getBalancePaymentAmount().compareTo(BigDecimal.ZERO) > 0) {
                sysUserCashAccountService.updateBalance(order.getUserId(), order.getBalancePaymentAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "网管课支付失败，退还余额");
            }
            studentPaymentOrderDao.update(order);
            PracticeGroup practiceGroup = practiceGroupDao.get(Long.valueOf(order.getMusicGroupId()));
            practiceGroup.setGroupStatus(GroupStatusEnum.CANCEL);
            practiceGroupDao.update(practiceGroup);
            groupService.deleteGroupOtherInfo(order.getMusicGroupId(), GroupType.PRACTICE);
            return;
        }
        studentPaymentOrderDao.update(order);

        //插入缴费明细
        //收入
        SysUserCashAccountDetail sysUserIncomeCashAccountDetail = new SysUserCashAccountDetail();
        sysUserIncomeCashAccountDetail.setUserId(order.getUserId());
        sysUserIncomeCashAccountDetail.setType(PlatformCashAccountDetailTypeEnum.RECHARGE);
        sysUserIncomeCashAccountDetail.setStatus(DealStatusEnum.SUCCESS);
        sysUserIncomeCashAccountDetail.setAmount(order.getActualAmount());
        sysUserIncomeCashAccountDetail.setBalance(sysUserCashAccount.getBalance().add(order.getActualAmount()));
        sysUserIncomeCashAccountDetail.setAttribute(order.getTransNo());
        sysUserIncomeCashAccountDetail.setChannel(studentPaymentOrder.getPaymentChannel());
        sysUserIncomeCashAccountDetail.setComAmount(studentPaymentOrder.getComAmount());
        sysUserIncomeCashAccountDetail.setPerAmount(studentPaymentOrder.getPerAmount());

        //支出
        SysUserCashAccountDetail sysUserExpendCashAccountDetail = new SysUserCashAccountDetail();
        sysUserExpendCashAccountDetail.setUserId(order.getUserId());
        sysUserExpendCashAccountDetail.setType(PlatformCashAccountDetailTypeEnum.PAY_FEE);
        sysUserExpendCashAccountDetail.setStatus(DealStatusEnum.SUCCESS);
        sysUserExpendCashAccountDetail.setAmount(order.getActualAmount().negate());
        sysUserExpendCashAccountDetail.setBalance(sysUserCashAccount.getBalance());
        sysUserExpendCashAccountDetail.setAttribute(order.getTransNo());
        sysUserExpendCashAccountDetail.setChannel(studentPaymentOrder.getPaymentChannel());

        if (studentPaymentOrder.getComAmount() != null) {
            sysUserIncomeCashAccountDetail.setComAmount(studentPaymentOrder.getComAmount().negate());
        }
        if (studentPaymentOrder.getPerAmount() != null) {
            sysUserExpendCashAccountDetail.setPerAmount(studentPaymentOrder.getPerAmount().negate());
        }

        sysUserCashAccountDetailService.insert(sysUserIncomeCashAccountDetail);
        sysUserCashAccountDetailService.insert(sysUserExpendCashAccountDetail);

        PracticeGroup practiceGroup = practiceGroupDao.get(Long.valueOf(order.getMusicGroupId()));
        if (practiceGroup.getGroupStatus().equals(GroupStatusEnum.CANCEL)) {
            order.setVersion(order.getVersion() + 1);
            order.setMemo("支付成功，在重新支付时，用户选择取消，金额进入账户");
            studentPaymentOrderDao.update(order);

            sysUserCashAccountService.updateBalance(order.getUserId(), order.getExpectAmount(), PlatformCashAccountDetailTypeEnum.REFUNDS, "支付成功，用户取消，退还余额");

            return;
        }
        ClassGroup classGroup = classGroupDao.findByMusicGroupAndType(order.getMusicGroupId(), GroupType.PRACTICE.getCode());

        practiceGroup.setGroupStatus(GroupStatusEnum.NORMAL);
        practiceGroupDao.update(practiceGroup);

        try {
            contractService.transferPracticeCoursesContract(order.getUserId(), classGroup.getTotalClassTimes(), practiceGroup.getCoursesStartDate(), practiceGroup.getCoursesExpireDate(), order.getExpectAmount());
        } catch (Exception e) {
            LOGGER.error(MessageFormatter.arrayFormat("网管课[{}]购买协议错误:{}", order.getMusicGroupId(), e.getMessage()), e);
        }

        try {
            teacherCourseRewardService.addConvertReward(practiceGroup.getStudentId(), practiceGroup.getUserId());
        } catch (Exception e) {
            e.printStackTrace();
        }

        Teacher teacher = teacherService.getDetail(practiceGroup.getUserId());

        String groupStartTime = DateUtil.dateToString(practiceGroup.getCoursesStartDate(), "yyyy年MM月dd日");
        String groupEndTime = DateUtil.dateToString(practiceGroup.getCoursesExpireDate(), "yyyy年MM月dd日");
        String teacherName = teacher.getRealName();
        String drillTimesOnWeek = practiceGroup.getDrillTimesOnWeek().toString();
        CourseSchedule firstCourseWithGroup = courseScheduleDao.findFirstCourseWithGroup(practiceGroup.getId().toString(), GroupType.PRACTICE);
        String firstCourseStartTime = DateUtil.dateToString(firstCourseWithGroup.getStartClassTime(), "yyyy年MM月dd日 HH时mm分");

        try {
            Map<Integer, String> userMap = new HashMap<>();
            userMap.put(practiceGroup.getStudentId(), practiceGroup.getStudentId().toString());
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.PAY_PRACTICE_BUY_SUCCESS,
                    userMap, null, 0, null, "STUDENT", groupStartTime, groupEndTime, teacherName, drillTimesOnWeek, firstCourseStartTime);
//            SysUser student = sysUserFeignService.queryUserById(practiceGroup.getStudentId());
            SysUser student = teacherDao.getUser(practiceGroup.getStudentId());
            Map<Integer, String> userPhoneMap = new HashMap<>();
            userPhoneMap.put(practiceGroup.getStudentId(), student.getPhone());
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.AWSMS, MessageTypeEnum.PAY_PRACTICE_BUY_SUCCESS,
                    userPhoneMap, null, 0, null, "STUDENT", groupStartTime, groupEndTime, teacherName, drillTimesOnWeek, firstCourseStartTime);
        } catch (Exception e) {
            e.printStackTrace();
        }

        studentDao.updateStudentServiceTag(practiceGroup.getStudentId(), null, YesOrNoEnum.YES.getCode());
        imUserFriendService.refreshGroupImUserFriend(practiceGroup.getId().toString(),GroupType.PRACTICE);
    }

    @Override
    public List<PracticeCourseDto> findUserHistoryBuyPracticeGroups(Integer userId, Long groupId) {
        List<PracticeCourseDto> userPracticeGroups;
        if (Objects.nonNull(groupId)) {
            PracticeGroup practiceGroup = practiceGroupDao.get(groupId);
            Date date = DateUtil.addSeconds(practiceGroup.getCoursesExpireDate(), 1);
            userPracticeGroups = practiceGroupDao.findUserBuyPracticeGroupsWithDate(userId, date);
        } else {
            userPracticeGroups = practiceGroupDao.findUserBuyPracticeGroups(userId);
        }

        if (CollectionUtils.isEmpty(userPracticeGroups)) {
            return new ArrayList<>();
        }
        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        Integer practiceCourseMinutes = practiceCourseMinutesConfig.getParanValue(Integer.class);
        for (PracticeCourseDto userPracticeGroup : userPracticeGroups) {
            JSONArray coursesArry = new JSONArray();
            JSONObject courseJson = JSON.parseObject(userPracticeGroup.getDrillTimesJson());
            for (String weekNumStr : courseJson.keySet()) {
                JSONObject newCourseJson = new JSONObject();
                newCourseJson.put("weekName", DateUtil.weekNumNormalWeekNameMap.get(Integer.parseInt(weekNumStr)));
                LocalTime courseStartTime = LocalTime.parse(courseJson.getString(weekNumStr), DateUtil.timeFormatter);
                LocalTime courseEndTime = courseStartTime.plusMinutes(practiceCourseMinutes);
                newCourseJson.put("startTime", courseStartTime.format(DateUtil.timeFormatter));
                newCourseJson.put("endTime", courseEndTime.format(DateUtil.timeFormatter));
                coursesArry.add(newCourseJson);
            }
            userPracticeGroup.setDrillTimesJson(coursesArry.toJSONString());
        }
        Map<GroupStatusEnum, List<PracticeCourseDto>> statusGroupMap = userPracticeGroups.stream().collect(Collectors.groupingBy(PracticeCourseDto::getGroupStatus));
        List<PracticeCourseDto> newPracticeGroups = new ArrayList<>();
        if (!CollectionUtils.isEmpty(statusGroupMap.get(GroupStatusEnum.LOCK))) {
            newPracticeGroups.addAll(statusGroupMap.get(GroupStatusEnum.LOCK));
        }
        for (PracticeCourseDto userPracticeGroup : userPracticeGroups) {
            if (!userPracticeGroup.getGroupStatus().equals(GroupStatusEnum.LOCK)) {
                newPracticeGroups.add(userPracticeGroup);
            }
        }
        return newPracticeGroups;
    }

    @Override
    public PracticeBuyResultDto queryOrderInfo(String orderNo) {
        if (StringUtils.isBlank(orderNo)) {
            throw new BizException("订单不存在");
        }
        StudentPaymentOrder order = studentPaymentOrderDao.findOrderByOrderNo(orderNo);
        if (Objects.isNull(order)) {
            throw new BizException("订单不存在");
        }
        PracticeBuyResultDto practiceBuyResult = new PracticeBuyResultDto();
        practiceBuyResult.setOrderNo(orderNo);
        practiceBuyResult.setStatus(order.getStatus());
        practiceBuyResult.setCreateTime(order.getCreateTime());
        practiceBuyResult.setPrice(order.getExpectAmount());
        practiceBuyResult.setType(order.getType().getCode());

        if(order.getGroupType().equals(GroupType.GOODS_SELL)){
            practiceBuyResult.setDetail(studentGoodsSellDao.getStudentGoodsSellDto(orderNo));
        }

        if (order.getStatus().equals(DealStatusEnum.FAILED) || order.getGroupType().equals(GroupType.REPAIR)||Objects.isNull(order.getMusicGroupId())) {
            return practiceBuyResult;
        }
        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);
        Integer practiceCourseMinutes = practiceCourseMinutesConfig.getParanValue(Integer.class);
        PracticeGroup practiceGroup = practiceGroupDao.get(Long.valueOf(order.getMusicGroupId()));
        JSONArray coursesArry = new JSONArray();
        JSONObject courseJson = JSON.parseObject(practiceGroup.getDrillTimesJson());
        for (String weekNumStr : courseJson.keySet()) {
            JSONObject newCourseJson = new JSONObject();
            newCourseJson.put("weekName", DateUtil.weekNumNormalWeekNameMap.get(Integer.parseInt(weekNumStr)));
            LocalTime courseStartTime = LocalTime.parse(courseJson.getString(weekNumStr), DateUtil.timeFormatter);
            LocalTime courseEndTime = courseStartTime.plusMinutes(practiceCourseMinutes);
            newCourseJson.put("startTime", courseStartTime.format(DateUtil.timeFormatter));
            newCourseJson.put("endTime", courseEndTime.format(DateUtil.timeFormatter));
            coursesArry.add(newCourseJson);
        }
        practiceBuyResult.setDrillTimesJson(coursesArry.toJSONString());
        practiceBuyResult.setCourseStartTime(practiceGroup.getCoursesStartDate());
        practiceBuyResult.setCourseEndTime(practiceGroup.getCoursesExpireDate());
        Teacher teacher = teacherDao.get(practiceGroup.getUserId());
        practiceBuyResult.setTeacherName(teacher.getRealName());
        return practiceBuyResult;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public HttpResponseResult repay(Integer userId, Integer practiceGroupId, boolean useBalancePayment) throws Exception {
        if (Objects.isNull(practiceGroupId)) {
            throw new BizException("请指定需要重新支付的课程组");
        }
        PracticeGroup practiceGroup = practiceGroupDao.lockPracticeGroup(practiceGroupId);
        if (Objects.isNull(practiceGroup)) {
            return BaseController.failed(HttpStatus.FAILED_DEPENDENCY, "该课程组已失效，请重新购买");
        }
//        if(practiceGroup.getGroupStatus().equals(GroupStatusEnum.NORMAL)){
//            return BaseController.failed(HttpStatus.CREATED, "该订单已经支付成功");
//        }
        if (practiceGroup.getGroupStatus().equals(GroupStatusEnum.CANCEL)) {
            return BaseController.failed(HttpStatus.FAILED_DEPENDENCY, "该课程组已失效，请重新购买");
        }
        List<StudentPaymentOrder> userGroupOrders = studentPaymentOrderDao.findUserGroupOrders(userId, practiceGroupId.toString(), GroupType.PRACTICE, null);
        Map<DealStatusEnum, List<StudentPaymentOrder>> statusOrderMap = userGroupOrders.stream().collect(Collectors.groupingBy(StudentPaymentOrder::getStatus));
        List<StudentPaymentOrder> successOrders = statusOrderMap.get(DealStatusEnum.SUCCESS);
        if (!CollectionUtils.isEmpty(successOrders) && successOrders.size() > 0) {
            Map<String, Object> result = new HashMap<>();
            result.put("orderNo", successOrders.get(0).getOrderNo());
            return BaseController.succeed(result);
        }

        StudentPaymentOrder latestOrder = userGroupOrders.stream().max(Comparator.comparing(StudentPaymentOrder::getId)).get();
        StudentPaymentOrder newOrder = new StudentPaymentOrder();
        BeanUtils.copyProperties(latestOrder, newOrder);
        newOrder.setId(null);

        List<StudentPaymentOrder> ingOrders = statusOrderMap.get(DealStatusEnum.ING);
        if (CollectionUtils.isEmpty(ingOrders) || ingOrders.size() <= 0) {
            return BaseController.failed(HttpStatus.FAILED_DEPENDENCY, "该课程组已失效，请重新购买");
        }

        String orderNo = idGeneratorService.generatorId("payment") + "";

        newOrder.setOrderNo(orderNo);
        newOrder.setActualAmount(newOrder.getExpectAmount());
        newOrder.setMerNos(null);
        newOrder.setBalancePaymentAmount(new BigDecimal(0));
        newOrder.setComAmount(null);
        newOrder.setPerAmount(null);
        newOrder.setStatus(DealStatusEnum.ING);
        newOrder.setVersion(0);
        studentPaymentOrderService.insert(newOrder);

        BigDecimal balance = BigDecimal.ZERO;
        if (useBalancePayment || newOrder.getExpectAmount().doubleValue() == 0) {
            SysUserCashAccount userCashAccount = sysUserCashAccountService.getLocked(userId);
            if (userCashAccount == null) {
                throw new BizException("用户账户找不到");
            }
            newOrder.setPaymentChannel("BALANCE");
            if (userCashAccount.getBalance().subtract(newOrder.getExpectAmount()).doubleValue() >= 0) {
                studentPaymentRouteOrderService.addRouteOrder(orderNo, practiceGroup.getOrganId(), newOrder.getExpectAmount());
                // 更新订单信息
                newOrder.setActualAmount(new BigDecimal(0));
                newOrder.setBalancePaymentAmount(newOrder.getExpectAmount());
                newOrder.setStatus(DealStatusEnum.SUCCESS);
                newOrder.setUpdateTime(new Date());
                newOrder.setOrganId(practiceGroup.getOrganId());
                newOrder.setRoutingOrganId(practiceGroup.getOrganId());

                sysUserCashAccountService.updateBalance(userId, newOrder.getExpectAmount().negate(), PlatformCashAccountDetailTypeEnum.PAY_FEE, "网管课购买");

                newOrder.setPayTime(new Date());
                this.orderCallback(newOrder);

                Map<String, Object> result = new HashMap<>();
                result.put("orderNo", newOrder.getOrderNo());

                return BaseController.succeed(result);
            } else {
                if (userCashAccount.getBalance().doubleValue() > 0) {
                    balance = userCashAccount.getBalance();
                    sysUserCashAccountService.updateBalance(userId, balance.negate(), PlatformCashAccountDetailTypeEnum.PAY_FEE, "网管课购买");
                    BigDecimal amount = newOrder.getExpectAmount().subtract(userCashAccount.getBalance());
                    newOrder.setActualAmount(amount);
                    newOrder.setBalancePaymentAmount(userCashAccount.getBalance());
                } else {
                    newOrder.setBalancePaymentAmount(new BigDecimal(0));
                }
            }
        }

        String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");
        Map<String, BigDecimal> classFee = new HashMap<>();
        classFee.put("course", newOrder.getActualAmount());
        classFee.put("instrument", BigDecimal.ZERO);
        classFee.put("accessories", BigDecimal.ZERO);
        classFee.put("other", BigDecimal.ZERO);

        Map<String, Object> payMap = payService.getPayMap(
                newOrder.getActualAmount(),
                balance,
                orderNo,
                baseApiUrl + "/api-student/studentOrder/notify",
                baseApiUrl + "/api-student/studentOrder/paymentResult?orderNo=" + orderNo,
                "网管课购买",
                practiceGroup.getName(),
                practiceGroup.getOrganId(),
                "practiceBuy"
        );

        newOrder.setStatus(DealStatusEnum.ING);
        newOrder.setOrganId(practiceGroup.getOrganId());
        newOrder.setMerNos((String) payMap.get("routingMerNos"));
        newOrder.setPaymentChannel((String) payMap.get("type"));
        newOrder.setUpdateTime(new Date());
        newOrder.setCreateTime(new Date());
        studentPaymentOrderService.update(newOrder);

        return BaseController.succeed(payMap);
    }

    @Override
    public void updateHistoryPracticeGroupStatus() {
        List<PracticeGroup> historyPracticeGroups = practiceGroupDao.findHistoryPracticeGroups();
        List<PracticeGroup> needUpdateGroups = new ArrayList<>();
        for (PracticeGroup historyPracticeGroup : historyPracticeGroups) {
            if (historyPracticeGroup.getGroupStatus().equals(GroupStatusEnum.NORMAL)) {
                historyPracticeGroup.setGroupStatus(GroupStatusEnum.FINISH);
                needUpdateGroups.add(historyPracticeGroup);
            }
        }
        if (!CollectionUtils.isEmpty(needUpdateGroups)) {
            practiceGroupDao.batchUpdate(needUpdateGroups);
        }
    }

    @Override
    public Map<String, Object> checkExistWaitPayOrder(Integer userId, Long groupId) {
        List<PracticeGroup> userLockGroups;
        Map<String, Object> result = new HashMap<>();
        if (Objects.nonNull(groupId)) {
            PracticeGroup practiceGroup = practiceGroupDao.get(groupId);
            if (Objects.isNull(practiceGroup) || !practiceGroup.getGroupStatus().equals(GroupStatusEnum.NORMAL) || PracticeGroupType.FREE.equals(practiceGroup.getType())) {
                result.put("existWaitPayOrder", 0);
                return result;
            }
            Date date = DateUtil.addSeconds(practiceGroup.getCoursesExpireDate(), 1);
            userLockGroups = practiceGroupDao.findUserLockPracticeGroupWithDate(userId, date);
        } else {
            LocalDate localDate = LocalDate.now();
            localDate = localDate.plusDays(1);
            Date date = Date.from(localDate.atStartOfDay(DateUtil.zoneId).toInstant());
            userLockGroups = practiceGroupDao.findUserLockPracticeGroupWithDateBefore(userId, date);
        }
        if (!CollectionUtils.isEmpty(userLockGroups)) {
            result.put("existWaitPayOrder", 1);
            result.put("groupId", userLockGroups.get(0).getId());
        } else {
            result.put("existWaitPayOrder", 0);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public HttpResponseResult cancelWaitPayOrder(Integer userId, Integer groupId) {
        PracticeGroup practiceGroup = practiceGroupDao.get(groupId.longValue());
        if (practiceGroup.getGroupStatus().equals(GroupStatusEnum.LOCK)) {
            groupService.deleteGroupOtherInfo(groupId.toString(), GroupType.PRACTICE);
            practiceGroup.setGroupStatus(GroupStatusEnum.CANCEL);
            practiceGroup.setMemo("用户手动取消");
            practiceGroupDao.update(practiceGroup);
        } else if (practiceGroup.getGroupStatus().equals(GroupStatusEnum.NORMAL)) {
            return BaseController.failed(HttpStatus.CREATED, "该订单已经支付成功");
        } else if (practiceGroup.getGroupStatus().equals(GroupStatusEnum.CANCEL)) {
            return BaseController.failed(HttpStatus.FAILED_DEPENDENCY, "该课程组已失效，请重新购买");
        }
        return BaseController.succeed();
    }

    @Override
    public void pushStudyReport(Date expiredDate, String pushType) {
        if (Objects.isNull(expiredDate)) {
            expiredDate = new Date();
        }

        if (StringUtils.isBlank(pushType)) {
            throw new BizException("请指定推送类型：JIGUANG、SMS、ALL");
        }

        List<CourseScheduleEvaluate> reports = courseScheduleEvaluateDao.findExpiredDateBeforeReport(expiredDate);

        if (CollectionUtils.isEmpty(reports)) {
            return;
        }

        String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");

        for (CourseScheduleEvaluate report : reports) {
            PracticeGroup userFreePracticeGroup = practiceGroupDao.get(Long.valueOf(report.getMusicGroupId()));
            if (Objects.isNull(userFreePracticeGroup)) {
                continue;
            }
//            ClassGroup classGroup = classGroupDao.findByMusicGroupAndType(userFreePracticeGroup.getId().toString(), GroupType.PRACTICE.getCode());
//
//            CourseScheduleEvaluate courseScheduleEvaluate = courseScheduleEvaluateDao.findByClassGroupId(classGroup.getId());
//            if(Objects.isNull(courseScheduleEvaluate)){
//                continue;
//            }

            String pushUrl = baseApiUrl + "/#/reportDetail?classGroupId=" + report.getClassGroupId();
            String smsUrl = baseApiUrl + "/#/transfer?url=" + baseApiUrl + "&hash=reportDetail&classGroupId=" + report.getClassGroupId();

            if (report.getVersion().equals(2)) {
                pushUrl = baseApiUrl + "/#/reportDetailNew?id=" + report.getId() + "&classGroupId=" + report.getClassGroupId();
                smsUrl = baseApiUrl + "/#/transfer?url=" + baseApiUrl + "&hash=reportDetailNew&id=" + report.getId() + "&classGroupId=" + report.getClassGroupId();
            }

//            SysUser student = sysUserFeignService.queryUserById(userFreePracticeGroup.getStudentId());
            SysUser student = teacherDao.getUser(userFreePracticeGroup.getStudentId());

            if (pushType.equals("ALL") || pushType.equals("JIGUANG")) {
                Map<Integer, String> userMap = new HashMap<>();
                userMap.put(userFreePracticeGroup.getStudentId(), userFreePracticeGroup.getStudentId().toString());
                sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.PRACTICE_COMPLETED_STUDY_REPORT,
                        userMap, null, 0, "5?" + pushUrl, "STUDENT", pushUrl);
            }

            if (pushType.equals("ALL") || pushType.equals("SMS")) {
                Map<Integer, String> userPhoneMap = new HashMap<>();
                userPhoneMap.put(userFreePracticeGroup.getStudentId(), student.getPhone());
                sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.AWSMS, MessageTypeEnum.PRACTICE_COMPLETED_STUDY_REPORT,
                        userPhoneMap, null, 0, null, "STUDENT", HttpUtil.getSortUrl(smsUrl));
            }
            report.setIsPushed(1);
            courseScheduleEvaluateDao.update(report);
        }
    }

    @Override
    public PageInfo<PracticeGroupDto> findPracticeGroupReviews(PracticeGroupQueryInfo queryInfo) {
        queryInfo.setMonth(queryInfo.getMonth());
        PageInfo pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<>();
        MapUtil.populateMap(params, queryInfo);

        List<PracticeGroupDto> dataList = null;
        int count = practiceGroupDao.countPracticeGroupReviews(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = practiceGroupDao.findPracticeGroupsReviews(params);
            if (dataList != null && dataList.size() > 0) {
                List<Integer> subjectIds = dataList.stream().map(practiceGroup -> practiceGroup.getSubjectId()).distinct().collect(Collectors.toList());
                Set<Integer> organIds = dataList.stream().map(practiceGroup -> practiceGroup.getOrganId()).collect(Collectors.toSet());
                List<String> practiceGroupIds = dataList.stream().map(e -> String.valueOf(e.getId())).distinct().collect(Collectors.toList());

                List<ClassGroup> classGroupsByVipGroups = classGroupDao
                        .findByMusicGroupsAndType(practiceGroupIds.stream().map(groupId -> String.valueOf(groupId)).collect(Collectors.toList()),
                                GroupType.PRACTICE.getCode());
                Map<String, ClassGroup> vipGroupClassGroupMap = classGroupsByVipGroups.stream().collect(Collectors.toMap(ClassGroup::getMusicGroupId, classGroup -> classGroup));
                //当前课时，总课时
//                List<Map<Long, Integer>> countGroupOverCourse = courseScheduleDao.countGroupOverCourse(practiceGroupIds,GroupType.PRACTICE.getCode());
                Map<String, Long> totalClassTimeMap = MapUtil.convertIntegerMap(courseScheduleDao.countTotalCourseScheduleNum(practiceGroupIds, GroupType.PRACTICE.getCode()));
                Map<String, Long> currentClassTimeMap = MapUtil.convertIntegerMap(courseScheduleDao.countCourseScheduleNum(practiceGroupIds, GroupType.PRACTICE.getCode()));
                //声部名称列表
                Map<Long, String> subjectNames = MapUtil.convertMybatisMap(subjectDao.findBySubjecIds(StringUtils.join(subjectIds, ",")));
                Map<Integer, String> organNames = MapUtil.convertMybatisMap(organizationDao.findOrganNameMapList(organIds));
                dataList.forEach(e -> {
                    e.setSubjectName(subjectNames.get(e.getSubjectId().intValue()));
                    e.setOrganName(organNames.get(e.getOrganId()));
                    ClassGroup classGroup = vipGroupClassGroupMap.get(e.getId().toString());
                    if (Objects.nonNull(classGroup)) {
                        e.setStudentNum(classGroup.getStudentNum());
                    }
                    Long aLong = totalClassTimeMap.get(e.getId() + "");
                    e.setTotalClassTimes(aLong == null ? 0 : aLong.intValue());
                    Long aLong1 = currentClassTimeMap.get(e.getId() + "");
                    e.setCurrentClassTimes(aLong1 == null ? 0 : aLong1.intValue());
                });
            }
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public PageInfo<StudentBuyPracticeDto> studentBuys(StudentBuyPracticeQueryInfo queryInfo) {
        PageInfo pageInfo = new PageInfo<>(queryInfo.getPage(), queryInfo.getRows());
        Map<String, Object> params = new HashMap<>();
        MapUtil.populateMap(params, queryInfo);
        List<StudentBuyPracticeDto> dataList = null;
        int count = practiceGroupDao.countStudentBuys(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = practiceGroupDao.queryStudentBuys(params);
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

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

        List<SimpleUserDto> dataList = new ArrayList<>();
        int count = practiceGroupDao.countConvertDefeatStudents(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = practiceGroupDao.findConvertDefeatStudents(params);
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public void updateMemo(Long groupId, String memo) {
        practiceGroupDao.updateMemo(groupId, memo);
    }

    @Override
    public boolean isPurchasedPracticeCourse(Integer userId) {
        int count = practiceGroupDao.checkStudentExitChargePractice(userId);
        if (count > 0) {
            return true;
        }
        return false;
    }

    @Override
    public void practiceGroupRenewRemind() {
        List<Integer> needRemindStudentIds = practiceGroupDao.getNeedRemindPracticeUsers();
        if(CollectionUtils.isEmpty(needRemindStudentIds)){
            return;
        }
        List<Map<Integer, String>> userNeedRemindPracticeIdMaps = practiceGroupDao.getUserNeedRemindPracticeIdMap(needRemindStudentIds);
        if (!CollectionUtils.isEmpty(userNeedRemindPracticeIdMaps)) {
            Map<Integer, String> userNeedRemindPracticeIdMap = MapUtil.convertIntegerMap(userNeedRemindPracticeIdMaps);
            String baseApiUrl = sysConfigDao.findConfigValue("base_api_url");
            String pushUrl = baseApiUrl + "/#/auditionActive/1?groupId=";
            for (Map.Entry<Integer, String> integerStringEntry : userNeedRemindPracticeIdMap.entrySet()) {
                Map<Integer, String> userMap = new HashMap<>();
                userMap.put(integerStringEntry.getKey(), integerStringEntry.getKey().toString());
                String[] params = integerStringEntry.getValue().split("-");
                sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.PRACTICE_RENEW_REMIND_PUSH,
                        userMap, null, 0, "5?" + pushUrl + (params[0].equals("CHARGE") ? params[1] : ""), "STUDENT");
            }
            List<Integer> practiceIds = userNeedRemindPracticeIdMap.values().stream().map(e -> Integer.valueOf(e.split("-")[1])).collect(Collectors.toList());
            practiceGroupDao.batchUpdatePracticeRemindStatus(practiceIds, YesOrNoEnum.YES.getCode());
        }
    }
}
