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.PracticeDrillTimeDto;
import com.ym.mec.biz.dal.dto.PracticeGroupBuyDto;
import com.ym.mec.biz.dal.dto.SimpleUserDto;
import com.ym.mec.biz.dal.entity.*;
import com.ym.mec.biz.dal.enums.*;
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.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.service.IdGeneratorService;
import com.ym.mec.im.ImFeignService;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.util.date.DateUtil;
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.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;

/**
 * @Author Joburgess
 * @Date 2020/3/6
 */
@Service
public class EduPracticeGroupServiceImpl implements EduPracticeGroupService{

    @Autowired
    private PracticeGroupDao practiceGroupDao;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private SubjectDao subjectDao;
    @Autowired
    private TeacherService teacherService;
    @Autowired
    private ClassGroupDao classGroupDao;
    @Autowired
    private ClassGroupTeacherMapperDao classGroupTeacherMapperDao;
    @Autowired
    private ClassGroupTeacherSalaryDao classGroupTeacherSalaryDao;
    @Autowired
    private CourseScheduleDao courseScheduleDao;
    @Autowired
    private CourseScheduleTeacherSalaryDao courseScheduleTeacherSalaryDao;
    @Autowired
    private CourseScheduleStudentPaymentDao courseScheduleStudentPaymentDao;
    @Autowired
    private TeacherAttendanceDao teacherAttendanceDao;
    @Autowired
    private ClassGroupStudentMapperDao classGroupStudentMapperDao;
    @Autowired
    private SysUserFeignService sysUserFeignService;
    @Autowired
    private TeacherDao teacherDao;
    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private ImFeignService imFeignService;
    @Autowired
    private ImUserFriendService imUserFriendService;
    @Autowired
    private TeacherFreeTimeDao teacherFreeTimeDao;
    @Autowired
    private CourseScheduleService courseScheduleService;
    @Autowired
    private IdGeneratorService idGeneratorService;
    @Autowired
    private StudentPaymentOrderService studentPaymentOrderService;
    @Autowired
    private SysUserCashAccountService sysUserCashAccountService;
    @Autowired
    private SysUserCashAccountDetailService sysUserCashAccountDetailService;
    @Autowired
    private StudentPaymentOrderDao studentPaymentOrderDao;
    @Autowired
    private GroupClassService groupService;
    @Autowired
    private PracticeGroupSellPriceDao practiceGroupSellPriceDao;
    @Autowired
    private TeacherDefaultPracticeGroupSalaryDao teacherDefaultPracticeGroupSalaryDao;
    @Autowired
    private ContractService contractService;
    @Autowired
    private StudentDao studentDao;
    @Autowired
    private TeacherCourseRewardService teacherCourseRewardService;
    @Autowired
    private StudentPaymentRouteOrderService studentPaymentRouteOrderService;

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

    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 HttpResponseResult buyPracticeGroup(PracticeGroupBuyDto practiceGroupBuyParams, String operatorInfo) {
        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());
            }else{
                return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "需要续费的课程组不存在");
            }
        }

        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<>();

        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;
        BigDecimal oneMonthOriginalPrice=practiceGroupBuyParams.getDrillTimesOnWeek()==1?practiceGroupSellPrice.getOnceOriginalPrice():practiceGroupSellPrice.getTwiceOriginalPrice();
        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 originalAmount = oneMonthOriginalPrice.multiply(new BigDecimal(practiceGroupBuyParams.getBuyMonths()));
        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.setMemo(operatorInfo+"，教务代买");
        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 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);
        } 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.setMemo(operatorInfo+"，教务代买");
        studentPaymentOrder.setVersion(0);
        studentPaymentOrderService.insert(studentPaymentOrder);

        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(), studentPaymentOrder.getExpectAmount());
                // 更新订单信息
                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,operatorInfo+"，教务代买");

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

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

                return BaseController.succeed(result);
            }else{
                throw new BizException("账户余额不足，请完成账户充值");
            }
        }else{
            throw new BizException("当前购买方式暂时只支持用户余额购买");
        }
    }

    @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, "网管课支付失败，退还余额");
            }
            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);

        //插入缴费明细
        //收入
        if(order.getActualAmount().compareTo(BigDecimal.ZERO)!=0){
            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){
                sysUserExpendCashAccountDetail.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);
            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 Map getTrialPracticeApplyParams(Integer userId) {
        Map result = new HashMap();
        SysConfig practiceSubjectIdListConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_SUBJECT_ID_LIST);
        SysConfig practiceApplyIntervalTimeConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_APPLY_INTERVAL_TIME);
        SysConfig practiceCourseMinutesConfig = sysConfigService.findByParamName(SysConfigService.PRACTICE_COURSE_MINUTES);

        List<Subject> subjects = subjectDao.findBySubjectByIdList(practiceSubjectIdListConfig.getParanValue());
        Integer userDefaultSubjectId = practiceGroupDao.findLastPracticeSubject(userId);

        Collections.swap(subjects,6,7);
        result.put("subjects", subjects);
        result.put("userDefaultSubjectId", userDefaultSubjectId);
        result.put("practiceApplyIntervalMinutes", practiceApplyIntervalTimeConfig.getParanValue(Integer.class));
        result.put("practiceCourseMinutes", practiceCourseMinutesConfig.getParanValue(Integer.class));
        return result;
    }

    @Override
    public List<SimpleUserDto> findEnableAssignTeachers(Integer subjectId, Integer studentId) {
        SysUser student = teacherDao.getUser(studentId);
        return practiceGroupDao.findEnableAssignTeachers(student.getOrganId(), subjectId, studentId);
    }

    @Override
    public Map<Integer, List<String>> getEnableApplyDatesWithWeek(int startWeekDay) {
        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 = startWeekDay; i <= 7; i++) {
            List<String> tempTimes = new ArrayList<>();
            for (String applyDayTime : dayApplyTimes) {
                tempTimes.add(applyDayTime);
            }
            result.put(i, tempTimes);
        }
        return result;
    }

    @Override
    public Map getTrialPracticeTeacherFreeTimes(Integer userId, Integer teacherId, String mondayStr) {
        if (Objects.isNull(teacherId)) {
            throw new BizException("请选择教师");
        }
        if(StringUtils.isBlank(mondayStr)){
            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<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();
        int holiday = 0;
        if (Objects.nonNull(teacherFreeTime)) {
            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 monday = LocalDate.parse(mondayStr, ddf);
        if(monday.compareTo(LocalDate.now())<=0){
            monday=LocalDate.now().plusDays(1);
        }
        LocalDate sunday = monday.with(weekFields.dayOfWeek(), DayOfWeek.SUNDAY.getValue());

        Map<Integer, List<String>> weekNumApplyTimesMap = getEnableApplyDatesWithWeek(monday.getDayOfWeek().getValue());
        List<CourseSchedule> allTeacherCourses = courseScheduleDao.findTeacherCoursesWithDateRangeWithoutMusicGroup(teacherId, Date.from(monday.atStartOfDay(zoneId).toInstant()), Date.from(sunday.atStartOfDay(zoneId).toInstant()));
        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);
                LocalDate tempDate = LocalDate.parse(dateStr, ddf);
                if (tempDate.compareTo(monday)<0
                        ||tempDate.compareTo(tempDate)>=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;
        }

        for (CourseSchedule teacherCourse : allTeacherCourses) {
            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);
            }
        }
        result.put("teacherFreeDays", weekNumApplyTimesMap);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public HttpResponseResult createTrialPracticeGroup(PracticeGroupBuyDto practiceGroupBuyParams, String operatorInfo) {
        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.getFirstCourseTime())&&Objects.isNull(practiceGroupBuyParams.getSecondCourseTime())){
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "请确定上课时间");
        }
        if(Objects.nonNull(practiceGroupBuyParams.getSecondCourseTime())
                &&DateUtil.daysBetween(practiceGroupBuyParams.getFirstCourseTime(),practiceGroupBuyParams.getSecondCourseTime())>7){
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "两次课程的时间需安排在七天内");
        }

        studentDao.lockUser(practiceGroupBuyParams.getStudentId());

        int studentTrialPracticeNum = practiceGroupDao.countStudentTrialPractices(practiceGroupBuyParams.getStudentId());
        if(studentTrialPracticeNum>=3){
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "指定的学生已存在三组试听课");
        }

        int studentIngTrialPracticeNum = practiceGroupDao.countStudentIngTrialPractices(practiceGroupBuyParams.getStudentId());
        if(studentIngTrialPracticeNum>0){
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "该学员存在进行中的试听课，无法创建");
        }

        int stn = practiceGroupDao.countStudentAndTeacherTrialPractices(practiceGroupBuyParams.getStudentId(), practiceGroupBuyParams.getUserId());
        if(stn>0){
            return BaseController.failed(HttpStatus.EXPECTATION_FAILED, "请给该学生分配不同的指导老师");
        }

        SysUser student = teacherDao.getUser(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);

        List<Date> courseDates=new ArrayList<>();
        courseDates.add(practiceGroupBuyParams.getFirstCourseTime());
        if(Objects.nonNull(practiceGroupBuyParams.getSecondCourseTime())){
            courseDates.add(practiceGroupBuyParams.getSecondCourseTime());
        }
        courseDates.sort(Comparator.comparing(Date::getTime));

        Date now=new Date();
        if(courseDates.get(0).before(now)){
            throw new BizException("课程开始时间不得小于当前时间");
        }

        practiceGroupBuyParams.setName(subject.getName() + "•" + student.getUsername());

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

        practiceGroupBuyParams.setCoursesStartDate(courseDates.get(0));
        practiceGroupBuyParams.setCoursesExpireDate(DateUtil.addMinutes(courseDates.get(courseDates.size()-1),practiceCourseMinutes));
        practiceGroupBuyParams.setOrganId(student.getOrganId());
        practiceGroupBuyParams.setSingleClassMinutes(practiceCourseMinutes);
        practiceGroupBuyParams.setGroupStatus(GroupStatusEnum.NORMAL);
        practiceGroupBuyParams.setType(PracticeGroupType.TRIAL);
        practiceGroupDao.insert(practiceGroupBuyParams);

        //创建班级信息
        ClassGroup classGroup = new ClassGroup();
        classGroup.setSubjectIdList(practiceGroupBuyParams.getSubjectId().toString());
        classGroup.setExpectStudentNum(1);
        classGroup.setStudentNum(1);
        classGroup.setName(practiceGroupBuyParams.getName());
        classGroup.setTotalClassTimes(courseDates.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(BigDecimal.ZERO);
        classGroupTeacherSalary.setOnlineClassesSalary(BigDecimal.ZERO);
        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<>();

        List<CourseSchedule> practiceCourses = new ArrayList<>();
        for (Date courseDate : courseDates) {
            CourseSchedule courseSchedule=new CourseSchedule();
            LocalDateTime courseStartDateTime = LocalDateTime.ofInstant(courseDate.toInstant(), DateUtil.zoneId);
            LocalDateTime courseEndDateTime = courseStartDateTime.plusMinutes(practiceCourseMinutes);
            courseSchedule.setClassDate(Date.from(courseStartDateTime.atZone(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.setGroupType(GroupType.PRACTICE);
            courseSchedule.setType(CourseSchedule.CourseScheduleType.PRACTICE);
            courseSchedule.setStatus(CourseStatusEnum.NOT_START);
            courseSchedule.setMusicGroupId(practiceGroupBuyParams.getId().toString());
            courseSchedule.setClassGroupId(classGroup.getId());
            courseSchedule.setStatus(CourseStatusEnum.NOT_START);
            courseSchedule.setCreateTime(now);
            courseSchedule.setUpdateTime(now);
            courseSchedule.setIsLock(0);
            courseSchedule.setOrganId(practiceGroupBuyParams.getOrganId());
            practiceCourses.add(courseSchedule);
        }
        practiceCourses.sort(Comparator.comparing(CourseSchedule::getStartClassTime));
        courseScheduleDao.batchAddCourseSchedules(practiceCourses);

        BigDecimal teacherDefaultSalary=BigDecimal.ZERO;
        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(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(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);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            String errMessage;
            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);
        }

        try{
            DateTimeFormatter mmdd = DateTimeFormatter.ofPattern("M月d日");
            DateTimeFormatter hhmm = DateTimeFormatter.ofPattern("H:mm");
            LocalDateTime oneCourseStartTime = LocalDateTime.ofInstant(courseDates.get(0).toInstant(), DateUtil.zoneId);
            String oneCourseMMdd=mmdd.format(oneCourseStartTime);
            String oneCourseHHmm=hhmm.format(oneCourseStartTime);
            String oneCourseStartTimeDayOfWeekName = DateUtil.weekNumNormalWeekNameMap.get(oneCourseStartTime.getDayOfWeek().getValue());
            String oneCourseTimeStr = oneCourseMMdd + "(" + oneCourseStartTimeDayOfWeekName + ")" + oneCourseHHmm;
            LocalDateTime twoCourseStartTime = LocalDateTime.ofInstant(courseDates.get(1).toInstant(), DateUtil.zoneId);
            String twoCourseMMdd=mmdd.format(twoCourseStartTime);
            String twoCourseHHmm=hhmm.format(twoCourseStartTime);
            String twoCourseStartTimeDayOfWeekName = DateUtil.weekNumNormalWeekNameMap.get(twoCourseStartTime.getDayOfWeek().getValue());
            String twoCourseTimeStr = twoCourseMMdd + "(" + twoCourseStartTimeDayOfWeekName + ")" + twoCourseHHmm;

            Map<Integer, String> userMap = new HashMap<>();
            userMap.put(practiceGroupBuyParams.getStudentId(), practiceGroupBuyParams.getStudentId().toString());
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.TRIAL_PRACTICE_CREATE_STUDENT_PUSH,
                    userMap, null, 0, null, "STUDENT", student.getUsername(), teacher.getRealName(),
                    oneCourseTimeStr, twoCourseTimeStr);
//            Map<Integer, String> userPhoneMap = new HashMap<>();
//            userPhoneMap.put(practiceGroupBuyParams.getStudentId(), student.getPhone());
//            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.YIMEI, MessageTypeEnum.TRIAL_PRACTICE_CREATE_STUDENT_PUSH,
//                    userPhoneMap, null, 0, null, "STUDENT", student.getUsername(), teacher.getRealName(),
//                    oneCourseTimeStr, twoCourseTimeStr);

            Map<Integer, String> teacherMap = new HashMap<>();
            teacherMap.put(practiceGroupBuyParams.getUserId(), practiceGroupBuyParams.getUserId().toString());
            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.TRIAL_PRACTICE_CREATE_TEACHER_PUSH,
                    teacherMap, null, 0, null, "TEACHER", teacher.getRealName(), practiceGroupBuyParams.getName(),
                    oneCourseTimeStr, twoCourseTimeStr);
//            Map<Integer, String> teacherPhoneMap = new HashMap<>();
//            teacherPhoneMap.put(practiceGroupBuyParams.getUserId(), teacher.getPhone());
//            sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.YIMEI, MessageTypeEnum.TRIAL_PRACTICE_CREATE_TEACHER_PUSH,
//                    teacherPhoneMap, null, 0, null, "TEACHER", teacher.getRealName(), practiceGroupBuyParams.getName(),
//                    oneCourseTimeStr, twoCourseTimeStr);
        }catch (Exception e){
            e.printStackTrace();
        }

        studentDao.updateStudentServiceTag(practiceGroupBuyParams.getStudentId(),null,YesOrNoEnum.YES.getCode());
        imUserFriendService.refreshGroupImUserFriend(practiceGroupBuyParams.getId().toString(),GroupType.PRACTICE);
        return BaseController.succeed();
    }

    @Override
    public Map<String, Object> getStudentTrialPractices(Integer studentId) {
        List<PracticeGroup> studentTrialPractices = practiceGroupDao.findStudentTrialPractices(studentId);
        Integer existNoMemoGroup = 0;
        for (PracticeGroup studentTrialPractice : studentTrialPractices) {
            if (StringUtils.isBlank(studentTrialPractice.getMemo())){
                existNoMemoGroup=1;
                break;
            }
        }
        Map<String, Object> result=new HashMap<>();
        result.put("existNoMemoGroup", existNoMemoGroup);
        result.put("practices", studentTrialPractices);
        int studentIngTrialPracticeNum = practiceGroupDao.countStudentIngTrialPractices(studentId);
        result.put("existIngTrialPractices", studentIngTrialPracticeNum>0?1:0);
        return result;
    }
}
