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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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.CourseSalaryQueryInfo4Web;
import com.ym.mec.biz.dal.page.CourseScheduleTeacherSalaryQueryInfo;
import com.ym.mec.biz.dal.page.ExportTeacherSalaryQueryInfo;
import com.ym.mec.biz.service.*;
import com.ym.mec.common.constant.CommonConstants;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class CourseScheduleTeacherSalaryServiceImpl extends BaseServiceImpl<Long, CourseScheduleTeacherSalary> implements CourseScheduleTeacherSalaryService {

    @Autowired
    private CourseScheduleTeacherSalaryDao courseScheduleTeacherSalaryDao;
    @Autowired
    private VipGroupActivityDao vipGroupActivityDao;
    @Autowired
    private CourseScheduleStudentPaymentDao courseScheduleStudentPaymentDao;
    @Autowired
    @Lazy
    private VipGroupService vipGroupService;
    @Autowired
    private SchoolService schoolService;
    @Autowired
    private CourseScheduleDao courseScheduleDao;
    @Autowired
    private TeacherDefaultMusicGroupSalaryDao teacherDefaultMusicGroupSalaryDao;
    @Autowired
    private SysConfigService sysConfigDao;
    @Autowired
    private TeacherAttendanceDao teacherAttendanceDao;
    @Autowired
    private ClassGroupDao classGroupDao;
    @Autowired
    private TeacherDao teacherDao;
    @Autowired
    private ClassGroupStudentMapperDao classGroupStudentMapperDao;
    @Autowired
    private TeacherDefaultVipGroupSalaryDao teacherDefaultVipGroupSalaryDao;
    @Autowired
    private CourseScheduleRewardsRulesService courseScheduleRewardsRulesService;
    @Autowired
    private StudentAttendanceDao studentAttendanceDao;
    @Autowired
    private TeacherSalaryComplaintsDao teacherSalaryComplaintsDao;
    @Autowired
    private TeacherCourseRewardService teacherCourseRewardService;
    @Autowired
    private SysMessageService sysMessageService;
    @Autowired
    private TeacherCourseRewardDao teacherCourseRewardDao;
    @Autowired
    private MusicGroupDao musicGroupDao;
    @Autowired
    private TeacherDefaultPracticeGroupSalaryDao teacherDefaultPracticeGroupSalaryDao;
    @Autowired
    private SchoolDao schoolDao;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private RedisTemplate redisTemplate;

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

    @Override
    public BaseDAO<Long, CourseScheduleTeacherSalary> getDAO() {
        return courseScheduleTeacherSalaryDao;
    }

    @Override
    public void createCourseScheduleTeacherVipSalary(VipGroup vipGroup,
                                                     List<CourseSchedule> vipCourseSchedules,
                                                     BigDecimal onlineTeacherSalary,
                                                     BigDecimal offlineTeacherSalary) {

        School school = schoolService.get(vipGroup.getTeacherSchoolId());
        BigDecimal subsidy = new BigDecimal(0);
        if (Objects.nonNull(school)) {
            subsidy=school.getSubsidy();
        }

        //获取活动信息
        VipGroupActivity vipGroupActivity = null;
        if(vipGroup.getVipGroupActivityId() != null){
            vipGroupActivity = vipGroupActivityDao.get(vipGroup.getVipGroupActivityId().intValue());
        }

        List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries = new ArrayList<>();
        Map<TeachModeEnum, List<CourseSchedule>> courseScheduleGroupByTeachMode = vipCourseSchedules.stream().collect(Collectors.groupingBy(CourseSchedule::getTeachMode));

        for (TeachModeEnum teachModeEnum : courseScheduleGroupByTeachMode.keySet()) {
            List<CourseSchedule> courseSchedules = courseScheduleGroupByTeachMode.get(teachModeEnum)
                    .stream()
                    .sorted(Comparator.comparing(CourseSchedule::getStartClassTime)).collect(Collectors.toList());
            for (int i = 0; i < courseSchedules.size(); i++) {
                //创建教师课程薪水记录
                CourseScheduleTeacherSalary courseScheduleTeacherSalary = courseScheduleTeacherSalaryDao.queryByCourseScheduleIdAndUserId(courseSchedules.get(i).getId(), courseSchedules.get(i).getActualTeacherId());
                if (Objects.isNull(courseScheduleTeacherSalary)) {
                    courseScheduleTeacherSalary = new CourseScheduleTeacherSalary();
                }
                courseScheduleTeacherSalary.setCourseScheduleId(courseSchedules.get(i).getId());
                courseScheduleTeacherSalary.setGroupType(courseSchedules.get(i).getGroupType());
                courseScheduleTeacherSalary.setMusicGroupId(courseSchedules.get(i).getMusicGroupId());
                courseScheduleTeacherSalary.setTeacherRole(TeachTypeEnum.BISHOP);
                courseScheduleTeacherSalary.setUserId(courseSchedules.get(i).getActualTeacherId().intValue());
                courseScheduleTeacherSalary.setClassGroupId(courseSchedules.get(i).getClassGroupId());
                courseScheduleTeacherSalary.setSubsidy(subsidy);

                if (Objects.nonNull(vipGroupActivity)
                        &&vipGroupActivity.getType() == VipGroupActivityTypeEnum.GIVE_CLASS
                        && vipGroup.getGiveTeachMode() == teachModeEnum
                        && i >= Integer.parseInt(vipGroupActivity.getAttribute1())
                        && "0".equals(vipGroupActivity.getGiveClassPaySalaryFlag())) {
                    courseScheduleTeacherSalary.setExpectSalary(new BigDecimal(0));
                } else {
                    if (teachModeEnum == TeachModeEnum.ONLINE) {
                        courseScheduleTeacherSalary.setExpectSalary(onlineTeacherSalary);
                    } else {
                        courseScheduleTeacherSalary.setExpectSalary(offlineTeacherSalary);
                    }
                }
                courseScheduleTeacherSalary.setActualSalary(null);
                if (Objects.isNull(courseScheduleTeacherSalary.getId())) {
                    courseScheduleTeacherSalaries.add(courseScheduleTeacherSalary);
                } else {
                    courseScheduleTeacherSalaryDao.update(courseScheduleTeacherSalary);
                }
            }
        }
        if (courseScheduleTeacherSalaries.size() > 0) {
            batchInsert(courseScheduleTeacherSalaries);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchInsert(List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries) {
        courseScheduleTeacherSalaryDao.batchInsert(courseScheduleTeacherSalaries);
        List<Long> courseScheduleIds = courseScheduleTeacherSalaries.stream()
                .map(CourseScheduleTeacherSalary::getCourseScheduleId)
                .collect(Collectors.toList());
        List<ClassGroup> byCourseSchedules = classGroupDao.findByCourseSchedules(courseScheduleIds);

        Map<Integer, ClassGroup> classGroupMap = new HashMap<Integer, ClassGroup>();
        for (ClassGroup classGroup : byCourseSchedules) {
            if (!classGroupMap.containsKey(classGroup.getId())) {
                classGroupMap.put(classGroup.getId(), classGroup);
            }
        }

        List<TeacherAttendance> teacherIdByCourseSchedule = teacherAttendanceDao.findTeacherIdByCourseSchedule(courseScheduleIds);
        Map<Long, List<TeacherAttendance>> courseScheduleTeacherAttendanceMap = teacherIdByCourseSchedule.stream()
                .collect(Collectors.groupingBy(TeacherAttendance::getCourseScheduleId));
        List<TeacherAttendance> teacherAttendances = new ArrayList<>();
        courseScheduleTeacherSalaries.forEach(courseScheduleTeacherSalary -> {
            List<TeacherAttendance> teacherAttendancesTemp = courseScheduleTeacherAttendanceMap.get(courseScheduleTeacherSalary.getCourseScheduleId());
            List<Integer> collect = new ArrayList<>();
            if (!CollectionUtils.isEmpty(teacherAttendancesTemp)) {
                collect = teacherAttendancesTemp.stream().map(TeacherAttendance::getTeacherId).collect(Collectors.toList());
            }
            TeacherAttendance teacherAttendance = new TeacherAttendance();
            teacherAttendance.setGroupType(courseScheduleTeacherSalary.getGroupType());
            teacherAttendance.setClassGroupId(courseScheduleTeacherSalary.getClassGroupId());
            teacherAttendance.setMusicGroupId(courseScheduleTeacherSalary.getMusicGroupId());
            if (CollectionUtils.isEmpty(collect) || !collect.contains(courseScheduleTeacherSalary.getUserId())) {
                teacherAttendance.setTeacherId(courseScheduleTeacherSalary.getUserId());
                teacherAttendance.setCourseScheduleId(courseScheduleTeacherSalary.getCourseScheduleId());
                teacherAttendances.add(teacherAttendance);
            }
        });
        if (!CollectionUtils.isEmpty(teacherAttendances)) {
            teacherAttendanceDao.batchInsert(teacherAttendances);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void practiceTeacherSalarySettlement() {
        //获取教师未结算课程记录
        Date now = new Date();
        Date date = DateUtil.addMonths(now, -1);
        String startDate = DateUtil.format(DateUtil.getFirstDayOfMonth(date),DateUtil.DEFAULT_PATTERN);
        String endDate = DateUtil.format(DateUtil.getLastDayOfMonth(date),DateUtil.DEFAULT_PATTERN);
        List<CourseScheduleTeacherSalary> someDayAgoTeacherCourseSalaryNoSettlement = courseScheduleTeacherSalaryDao.findTeacherCourseSalaryNoSettlement(startDate,endDate);

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

        Set<Long> courseScheduleIds = someDayAgoTeacherCourseSalaryNoSettlement.stream().map(CourseScheduleTeacherSalary::getCourseScheduleId).collect(Collectors.toSet());

        Set<Integer> teacherIds = someDayAgoTeacherCourseSalaryNoSettlement.stream().map(CourseScheduleTeacherSalary::getUserId).collect(Collectors.toSet());
        List<Teacher> teachers = teacherDao.findByTeacherIds(new ArrayList<>(teacherIds));
        Map<Integer, Teacher> idTeacherMap = teachers.stream().collect(Collectors.toMap(Teacher::getId, t -> t, (t1, t2) -> t1));

        //教师签到记录
        List<TeacherAttendance> allTeacherAttendances = teacherAttendanceDao.findByCourseSchedules(new ArrayList<>(courseScheduleIds));
        Map<String, List<TeacherAttendance>> teacherCourseAttendanceMap = allTeacherAttendances.stream().collect(Collectors.groupingBy(ta -> org.apache.commons.lang3.StringUtils.joinWith(":", ta.getCourseScheduleId(), ta.getTeacherId())));

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

        //获取课程对应教学点补贴
        List<Map<Long, BigDecimal>> courseSubsidyByCourses = courseScheduleTeacherSalaryDao.findCourseSubsidyByCourses(new ArrayList<>(courseScheduleIds));
        Map<Integer, BigDecimal> courseSubsidyMap = MapUtil.convertIntegerMap(courseSubsidyByCourses);

        someDayAgoTeacherCourseSalaryNoSettlement.forEach(courseScheduleTeacherSalary -> {
            List<String> deductReasons = new ArrayList<>();

            BigDecimal expectSalary = courseScheduleTeacherSalary.getExpectSalary();

            BigDecimal subsidy = courseSubsidyMap.get(courseScheduleTeacherSalary.getCourseScheduleId());
            if (Objects.isNull(subsidy)) {
                subsidy = new BigDecimal(0);
            }
            courseScheduleTeacherSalary.setSubsidy(subsidy);
            expectSalary = expectSalary.add(subsidy);

            if(BigDecimal.ZERO.compareTo(subsidy)!=0){
                deductReasons.add("教学点补贴：" + subsidy);
            }

            Teacher teacher = idTeacherMap.get(courseScheduleTeacherSalary.getUserId());

            boolean notPositive = Objects.nonNull(teacher)&&Objects.nonNull(teacher.getFormalStaffDate())&&courseScheduleTeacherSalary.getCourseSchedule().getClassDate().compareTo(teacher.getFormalStaffDate())<0;
            boolean isProbationPeriod = Objects.nonNull(teacher)&&Objects.isNull(teacher.getFormalStaffDate())&&ProbationPeriodEnum.TRY.equals(teacher.getIsProbationPeriod());

            //如果上课日期在转正日期之前的按80%结算
            if(Objects.isNull(teacher)||notPositive||isProbationPeriod){
                expectSalary = expectSalary.multiply(new BigDecimal("0.8"));
                deductReasons.add("未转正");
            }

            //扣除费用
            BigDecimal deductCost = new BigDecimal(0);

            List<TeacherAttendance> courseTeacherAttendances = teacherCourseAttendanceMap.get(org.apache.commons.lang3.StringUtils.joinWith(":", courseScheduleTeacherSalary.getCourseScheduleId(), courseScheduleTeacherSalary.getUserId()));

            TeacherAttendance teacherAttendance = courseTeacherAttendances.get(0);
            if(CollectionUtils.isEmpty(courseTeacherAttendances)||Objects.isNull(teacherAttendance.getSignInStatus())){
                //未签到扣除全部课酬
                deductCost = deductCost.add(expectSalary);
                deductReasons.add("未签到扣除全部课酬");
            }else if(YesOrNoEnum.NO.equals(teacherAttendance.getSignInStatus())){
                //异常签到
                int signCourseTimeBetweenSeconds = DateUtil.secondsBetween(teacherAttendance.getSignInTime(), courseScheduleTeacherSalary.getCourseSchedule().getStartClassTime());
                float signCourseTimeBetween = (float)signCourseTimeBetweenSeconds/(float)60;
                if(signCourseTimeBetween<=1&&signCourseTimeBetween>-3){
                    //课程开始前1分钟至开始后3分钟进入教室
                    deductCost = deductCost.add(expectSalary.divide(new BigDecimal(2)));
                    deductReasons.add("课程开始前1分钟至开始后3分钟进入教室扣除一半课酬");
                }else if(signCourseTimeBetween<=-3){
                    //课程开始后3分钟后进入教室
                    deductCost = deductCost.add(expectSalary);
                    deductReasons.add("课程开始后3分钟进入教室扣除全部课酬");
                }
            }

            if(CollectionUtils.isEmpty(courseTeacherAttendances)||Objects.isNull(teacherAttendance.getSignOutStatus())){
                //未签退扣除全部课酬
                deductCost = deductCost.add(expectSalary);
                deductReasons.add("未签退扣除全部课酬");
            }else if(YesOrNoEnum.NO.equals(teacherAttendance.getSignOutStatus())){
                //异常签退
                int signOutCourseTimeBetweenSeconds = DateUtil.secondsBetween(teacherAttendance.getSignOutTime(), courseScheduleTeacherSalary.getCourseSchedule().getEndClassTime());
                float signOutCourseTimeBetween = (float)signOutCourseTimeBetweenSeconds/(float)60;

                if(signOutCourseTimeBetween>3){
                    //课程开始前20分钟至开始后3分钟退出教室
                    deductCost = deductCost.add(expectSalary);
                    deductReasons.add("课程结束前3分钟之前退出教室扣除全部课酬");
                }else if(signOutCourseTimeBetween<=3&&signOutCourseTimeBetween>0){
                    //课程结束前3分钟后至课程结束前退出教室
                    deductCost = deductCost.add(expectSalary.divide(new BigDecimal(2)));
                    deductReasons.add("课程结束前3分钟后至课程结束前退出教室扣除一半课酬");
                }
            }

            BigDecimal finalSalary = expectSalary.subtract(deductCost).setScale(BigDecimal.ZERO.intValue(), BigDecimal.ROUND_HALF_UP);

            if(BigDecimal.ZERO.compareTo(deductCost)<0){
                deductReasons.add("扣除总费用：" + deductCost);
            }
            if(!CollectionUtils.isEmpty(deductReasons))
                courseScheduleTeacherSalary.setDeductionReason(StringUtils.join(deductReasons, "；"));
            courseScheduleTeacherSalary.setActualSalary(finalSalary.compareTo(BigDecimal.ZERO)<0?BigDecimal.ZERO:finalSalary);
            courseScheduleTeacherSalary.setSettlementTime(now);
            courseScheduleTeacherSalaryDao.update(courseScheduleTeacherSalary);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void teacherSalarySettlement() {
        Date now = new Date();
        Date date = DateUtil.addMonths(now, -1);
        String startDate = DateUtil.format(DateUtil.getFirstDayOfMonth(date),DateUtil.DEFAULT_PATTERN);
        String endDate = DateUtil.format(DateUtil.getLastDayOfMonth(date),DateUtil.DEFAULT_PATTERN);

        // 查询需要修改的课酬记录
        List<TeacherVipSalaryDto> teacherSalaryList = courseScheduleTeacherSalaryDao.queryOnlineCourseByGroupType(startDate, endDate);

        Set<Integer> teacherIds = teacherSalaryList.stream().map(TeacherVipSalaryDto::getUserId).collect(Collectors.toSet());
        List<Teacher> teachers = teacherDao.findByTeacherIds(new ArrayList<>(teacherIds));
        Map<Integer, Teacher> idTeacherMap = teachers.stream().collect(Collectors.toMap(Teacher::getId, t -> t, (t1, t2) -> t1));

        //获取教师未结算课程记录
        List<CourseScheduleTeacherSalary> someDayAgoTeacherCourseSalaryNoSettlement = courseScheduleTeacherSalaryDao.findSomeDayAgoTeacherCourseSalaryNoSettlement(startDate,endDate);
        if (CollectionUtils.isEmpty(someDayAgoTeacherCourseSalaryNoSettlement)) {
            LOGGER.info("未获取到未结算课酬记录!");
            return;
        }
        //获取未结算课程编号列表
        List<Long> courseScheduleIds = someDayAgoTeacherCourseSalaryNoSettlement.stream().map(CourseScheduleTeacherSalary::getCourseScheduleId).collect(Collectors.toList());

        //教师签到记录
        List<TeacherAttendance> allTeacherAttendances = teacherAttendanceDao.findByCourseSchedules(courseScheduleIds);
        Map<String, List<TeacherAttendance>> teacherCourseAttendanceMap = allTeacherAttendances.stream().collect(Collectors.groupingBy(ta -> org.apache.commons.lang3.StringUtils.joinWith(":", ta.getCourseScheduleId(), ta.getTeacherId())));

        //获取课程对应教学点补贴
        List<Map<Long, BigDecimal>> courseSubsidyByCourses = courseScheduleTeacherSalaryDao.findCourseSubsidyByCourses(courseScheduleIds);
        Map<Integer, BigDecimal> courseSubsidyMap = MapUtil.convertIntegerMap(courseSubsidyByCourses);

        //处理课酬信息
        someDayAgoTeacherCourseSalaryNoSettlement.forEach(courseScheduleTeacherSalary -> {
            List<String> deductReasons = new ArrayList<>();

            BigDecimal expectSalary = courseScheduleTeacherSalary.getExpectSalary();

            BigDecimal subsidy = courseSubsidyMap.get(courseScheduleTeacherSalary.getCourseScheduleId());
            if (Objects.isNull(subsidy)) {
                subsidy = new BigDecimal(0);
            }
            courseScheduleTeacherSalary.setSubsidy(subsidy);
            expectSalary = expectSalary.add(subsidy);
            if(BigDecimal.ZERO.compareTo(subsidy)!=0){
                deductReasons.add("教学点补贴：" + subsidy);
            }

            Teacher teacher = idTeacherMap.get(courseScheduleTeacherSalary.getUserId());

            boolean notPositive = Objects.nonNull(teacher)&&Objects.nonNull(teacher.getFormalStaffDate())&&courseScheduleTeacherSalary.getCourseSchedule().getClassDate().compareTo(teacher.getFormalStaffDate())<0;
            boolean isProbationPeriod = Objects.nonNull(teacher)&&Objects.isNull(teacher.getFormalStaffDate())&&ProbationPeriodEnum.TRY.equals(teacher.getIsProbationPeriod());

            if(Objects.isNull(teacher)||notPositive||isProbationPeriod){
                expectSalary = expectSalary.multiply(new BigDecimal("0.8"));
                deductReasons.add("未转正");
            }

            //扣除费用
            BigDecimal deductCost = new BigDecimal(0);
            List<TeacherAttendance> courseTeacherAttendances = teacherCourseAttendanceMap.get(org.apache.commons.lang3.StringUtils.joinWith(":", courseScheduleTeacherSalary.getCourseScheduleId(), courseScheduleTeacherSalary.getUserId()));

//            TeacherAttendance teacherAttendance = courseTeacherAttendances.get(0);
            if(CollectionUtils.isEmpty(courseTeacherAttendances)||Objects.isNull(courseTeacherAttendances.get(0).getSignInStatus())){
                //未签到扣除全部课酬
                deductCost = deductCost.add(expectSalary);
                deductReasons.add("未签到扣除全部课酬");
            }else if(YesOrNoEnum.NO.equals(courseTeacherAttendances.get(0).getSignInStatus())){
                //异常签到
                int signCourseTimeBetweenSeconds = DateUtil.secondsBetween(courseTeacherAttendances.get(0).getSignInTime(), courseScheduleTeacherSalary.getCourseSchedule().getStartClassTime());
                float signCourseTimeBetween = (float)signCourseTimeBetweenSeconds/(float)60;
                if(signCourseTimeBetween<=1&&signCourseTimeBetween>-3){
                    //课程开始前1分钟至开始后3分钟进入教室
                    deductCost = deductCost.add(expectSalary.divide(new BigDecimal(2)));
                    deductReasons.add("课程开始前1分钟至开始后3分钟进入教室扣除一半课酬");
                }else if(signCourseTimeBetween<=-3){
                    //课程开始后3分钟后进入教室
                    deductCost = deductCost.add(expectSalary);
                    deductReasons.add("课程开始后3分钟进入教室扣除全部课酬");
                }

                if(TeachModeEnum.OFFLINE.equals(courseScheduleTeacherSalary.getCourseSchedule().getTeachMode())){
                    deductCost = deductCost.add(expectSalary);
                    deductReasons.add("点名时经纬度不在范围内扣除全部课酬");
                }
            }

            if(CollectionUtils.isEmpty(courseTeacherAttendances)||Objects.isNull(courseTeacherAttendances.get(0).getSignOutStatus())){
                //未签退扣除全部课酬
                deductCost = deductCost.add(expectSalary);
                deductReasons.add("未签退扣除全部课酬");
            }else if(YesOrNoEnum.NO.equals(courseTeacherAttendances.get(0).getSignOutStatus())){
                //异常签退
                int signOutCourseTimeBetweenSeconds = DateUtil.secondsBetween(courseTeacherAttendances.get(0).getSignOutTime(), courseScheduleTeacherSalary.getCourseSchedule().getEndClassTime());
                float signOutCourseTimeBetween = (float)signOutCourseTimeBetweenSeconds/(float)60;

                if(signOutCourseTimeBetween>3){
                    //课程开始前20分钟至开始后3分钟退出教室
                    deductCost = deductCost.add(expectSalary);
                    deductReasons.add("课程结束前3分钟之前退出教室扣除全部课酬");
                }else if(signOutCourseTimeBetween<=3&&signOutCourseTimeBetween>0){
                    //课程结束前3分钟后至课程结束前退出教室
                    deductCost = deductCost.add(expectSalary.divide(new BigDecimal(2)));
                    deductReasons.add("课程结束前3分钟后至课程结束前退出教室扣除一半课酬");
                }

                if(TeachModeEnum.OFFLINE.equals(courseScheduleTeacherSalary.getCourseSchedule().getTeachMode())){
                    deductCost = deductCost.add(expectSalary);
                    deductReasons.add("点名时经纬度不在范围内扣除全部课酬");
                }
            }

            if(BigDecimal.ZERO.compareTo(deductCost)<0){
                deductReasons.add("扣除总费用：" + deductCost);
            }
            if(!CollectionUtils.isEmpty(deductReasons))
                courseScheduleTeacherSalary.setDeductionReason(StringUtils.join(deductReasons, "；"));

            BigDecimal finalSalary = expectSalary.subtract(deductCost).setScale(BigDecimal.ZERO.intValue(), BigDecimal.ROUND_HALF_UP);

            courseScheduleTeacherSalary.setActualSalary(finalSalary.compareTo(BigDecimal.ZERO)<0?BigDecimal.ZERO:finalSalary);
            courseScheduleTeacherSalary.setSettlementTime(now);
            courseScheduleTeacherSalaryDao.update(courseScheduleTeacherSalary);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void musicGroupTeacherSalarySettlement() {
        Date now = new Date();
        Date date = DateUtil.addMonths(now, -1);
        String startDate = DateUtil.format(DateUtil.getFirstDayOfMonth(date),DateUtil.DEFAULT_PATTERN);
        String endDate = DateUtil.format(DateUtil.getLastDayOfMonth(date),DateUtil.DEFAULT_PATTERN);

        //获取上个月的课程计划
        List<CourseSchedule> yesterdayCourseSchedules = courseScheduleDao.findCourseScheduleWithDate(startDate,endDate);
        //课程编号列表
        List<Long> courseScheduleIds = yesterdayCourseSchedules.stream().map(CourseSchedule::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(courseScheduleIds)) {
            return;
        }

        //获取课程对应教学点补贴
        List<Map<Long, BigDecimal>> courseSubsidyByCourses = courseScheduleTeacherSalaryDao.findCourseSubsidyByCourses(courseScheduleIds);
        Map<Integer, BigDecimal> courseSubsidyMap = MapUtil.convertIntegerMap(courseSubsidyByCourses);

        Set<Integer> schoolIds = yesterdayCourseSchedules.stream().map(CourseSchedule::getSchoolId).collect(Collectors.toSet());
        List<School> schools = schoolDao.getSchools(new ArrayList<>(schoolIds));
        Map<Integer, School> idSchoolMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(schools)){
            idSchoolMap = schools.stream().collect(Collectors.toMap(School::getId, s->s, (s1, s2)->s1));
        }

        //课程编号与课程对应集合
        Map<Long, CourseSchedule> courseScheduleIdMap = yesterdayCourseSchedules.stream()
                .collect(Collectors.toMap(CourseSchedule::getId, courseSchedule -> courseSchedule));

        //课程教师课酬
        List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries = courseScheduleTeacherSalaryDao.findByCourseSchedulesWithNoSettlement(courseScheduleIds);

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

        SysConfig sysConfig = sysConfigService.findByParamName(SysConfigService.ATTENDANCE_RANGE_VIP);
        double attendanceRange = 0;
        if(Objects.nonNull(sysConfig)){
            attendanceRange = Double.valueOf(sysConfig.getParanValue());
        }

        //教师签到记录
        List<TeacherAttendance> allTeacherAttendances = teacherAttendanceDao.findByCourseSchedules(courseScheduleIds);
        Map<String, List<TeacherAttendance>> teacherCourseAttendanceMap = allTeacherAttendances.stream().collect(Collectors.groupingBy(ta -> org.apache.commons.lang3.StringUtils.joinWith(":", ta.getCourseScheduleId(), ta.getTeacherId())));

        //学员点名记录
        List<StudentAttendance> allStudentAttendances = studentAttendanceDao.findByCourseIds(courseScheduleIds);
        Map<Long, List<StudentAttendance>> courseStudentAttendanceMap = allStudentAttendances.stream().collect(Collectors.groupingBy(StudentAttendance::getCourseScheduleId));

        //需要根据实际上课学员结算课酬的课程编号
        List<Long> needStudentAttendanceCourseScheduleIds = yesterdayCourseSchedules.stream()
                .filter(c -> CourseSchedule.CourseScheduleType.HIGH_ONLINE.equals(c.getType())
                        || CourseSchedule.CourseScheduleType.MUSIC_NETWORK.equals(c.getType())
                        || CourseSchedule.CourseScheduleType.HIGH.equals(c.getType()))
                .map(CourseSchedule::getId).collect(Collectors.toList());

        List<CourseScheduleStudentPayment> studentAttendances = null;
        if(!CollectionUtils.isEmpty(needStudentAttendanceCourseScheduleIds)){
            studentAttendances = courseScheduleStudentPaymentDao.findByCourseScheduleIds(needStudentAttendanceCourseScheduleIds);
        }
        Map<Long, Long> courseNormalStudentsMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(studentAttendances)){
            courseNormalStudentsMap =studentAttendances.stream()
//                    .filter(studentAttendance -> StudentAttendanceStatusEnum.NORMAL.equals(studentAttendance.getStatus()))
                    .collect(Collectors.groupingBy(CourseScheduleStudentPayment::getCourseScheduleId, Collectors.counting()));
        }

        //课程对应乐团结算方式集合
        List<Map<String, String>> musicGroupSettlementTypeByCourse = courseScheduleTeacherSalaryDao.findMusicGroupSettlementTypeByCourse(courseScheduleIds);
        Map<String, String> musicGroupSettlementsMap = MapUtil.convertMybatisMap(musicGroupSettlementTypeByCourse);

        //所有老师编号
        List<Integer> allTeacherIds = courseScheduleTeacherSalaries.stream()
                .map(CourseScheduleTeacherSalary::getUserId)
                .collect(Collectors.toList());

        //所有老师默认乐团课酬
        List<TeacherDefaultMusicGroupSalary> teacherDefaultMusicGroupSalaries = teacherDefaultMusicGroupSalaryDao.findByTeacher(allTeacherIds);
        Map<CourseSchedule.CourseScheduleType, List<TeacherDefaultMusicGroupSalary>> teacherDefaultMusicGroupSalariesGroupByCourseType = teacherDefaultMusicGroupSalaries.stream().collect(Collectors.groupingBy(TeacherDefaultMusicGroupSalary::getCourseScheduleType));

        Set<Integer> teacherIds = courseScheduleTeacherSalaries.stream().map(CourseScheduleTeacherSalary::getUserId).collect(Collectors.toSet());
        List<Teacher> teachers = teacherDao.findByTeacherIds(new ArrayList<>(teacherIds));
        Map<Integer, Teacher> idTeacherMap = teachers.stream().collect(Collectors.toMap(Teacher::getId, t -> t, (t1, t2) -> t1));

        //计算课酬
        for (CourseScheduleTeacherSalary courseScheduleTeacherSalary : courseScheduleTeacherSalaries) {
            //当前课酬对应的课程信息
            CourseSchedule courseSchedule = courseScheduleIdMap.get(courseScheduleTeacherSalary.getCourseScheduleId());

            //教师课酬
            BigDecimal teacherSalary = new BigDecimal(0);

            Teacher teacher = idTeacherMap.get(courseScheduleTeacherSalary.getUserId());

            if(CourseSchedule.CourseScheduleType.MUSIC_NETWORK.equals(courseSchedule.getType())||CourseSchedule.CourseScheduleType.HIGH_ONLINE.equals(courseSchedule.getType())){
                Long normalStudentNum = courseNormalStudentsMap.get(courseSchedule.getId());
                if(Objects.isNull(normalStudentNum)){
                    normalStudentNum = Long.valueOf(0);
                }
                if(normalStudentNum>5){
                    normalStudentNum=Long.valueOf(5);
                }

                List<String> deductReasons = new ArrayList<>();

                BigDecimal subsidy = courseSubsidyMap.get(courseSchedule.getId());
                if (Objects.isNull(subsidy)) {
                    subsidy = new BigDecimal(0);
                }
                teacherSalary = teacherSalary.add(subsidy);
                if(BigDecimal.ZERO.compareTo(subsidy)!=0){
                    deductReasons.add("教学点补贴：" + subsidy);
                }

                //未转正
                boolean notPositive = Objects.nonNull(teacher)&&Objects.nonNull(teacher.getFormalStaffDate())&&courseSchedule.getClassDate().compareTo(teacher.getFormalStaffDate())<0;
                //试用期
                boolean isProbationPeriod = Objects.nonNull(teacher)&&Objects.isNull(teacher.getFormalStaffDate())&&ProbationPeriodEnum.TRY.equals(teacher.getIsProbationPeriod());

                //如果上课日期在转正日期之前的按80%结算
                if(Objects.isNull(teacher)||notPositive||isProbationPeriod){
                    teacherSalary = teacherSalary.multiply(new BigDecimal("0.8"));
                    deductReasons.add("未转正");
                }

                //扣除费用
                BigDecimal deductCost = new BigDecimal(0);
                deductReasons.add("课程应到人数：" + normalStudentNum);
                List<TeacherAttendance> courseTeacherAttendances = teacherCourseAttendanceMap.get(org.apache.commons.lang3.StringUtils.joinWith(":", courseScheduleTeacherSalary.getCourseScheduleId(), courseScheduleTeacherSalary.getUserId()));

                TeacherAttendance teacherAttendance = courseTeacherAttendances.get(0);
                if(CollectionUtils.isEmpty(courseTeacherAttendances)||Objects.isNull(teacherAttendance.getSignInStatus())){
                    //未签到扣除全部课酬
                    deductCost = deductCost.add(teacherSalary);
                    deductReasons.add("未签到扣除全部课酬");
                }else if(YesOrNoEnum.NO.equals(teacherAttendance.getSignInStatus())){
                    //异常签到
                    int signCourseTimeBetweenSeconds = DateUtil.secondsBetween(teacherAttendance.getSignInTime(), courseSchedule.getStartClassTime());
                    float signCourseTimeBetween = (float)signCourseTimeBetweenSeconds/(float)60;
                    if(signCourseTimeBetween<=1&&signCourseTimeBetween>-3){
                        //课程开始前1分钟至开始后3分钟进入教室
                        deductCost = deductCost.add(teacherSalary.divide(new BigDecimal(2)));
                        deductReasons.add("课程开始前1分钟至开始后3分钟进入教室扣除一半课酬");
                    }else if(signCourseTimeBetween<=-3){
                        //课程开始后3分钟后进入教室
                        deductCost = deductCost.add(teacherSalary);
                        deductReasons.add("课程开始后3分钟进入教室扣除全部课酬");
                    }
                }

                if(CollectionUtils.isEmpty(courseTeacherAttendances)||Objects.isNull(teacherAttendance.getSignOutStatus())){
                    //未签退扣除一半课酬
                    deductCost = deductCost.add(teacherSalary.divide(new BigDecimal(2)));
                    deductReasons.add("未签退扣除一半课酬");
                }else if(YesOrNoEnum.NO.equals(teacherAttendance.getSignOutStatus())){
                    //异常签退
                    int signOutCourseTimeBetweenSeconds = DateUtil.secondsBetween(teacherAttendance.getSignOutTime(), courseSchedule.getEndClassTime());
                    float signOutCourseTimeBetween = (float)signOutCourseTimeBetweenSeconds/(float)60;

                    if(signOutCourseTimeBetween>3){
                        //课程开始前20分钟至开始后3分钟退出教室
                        deductCost = deductCost.add(teacherSalary);
                        deductReasons.add("课程结束前3分钟之前退出教室扣除全部课酬");
                    }else if(signOutCourseTimeBetween<=3&&signOutCourseTimeBetween>0){
                        //课程结束前3分钟后至课程结束前退出教室
                        deductCost = deductCost.add(teacherSalary.divide(new BigDecimal(2)));
                        deductReasons.add("课程结束前3分钟后至课程结束前退出教室扣除一半课酬");
                    }
                }

                BigDecimal finalSalary = teacherSalary.subtract(deductCost).setScale(BigDecimal.ZERO.intValue(), BigDecimal.ROUND_HALF_UP);

                if(BigDecimal.ZERO.compareTo(deductCost)<0){
                    deductReasons.add("扣除总费用：" + deductCost);
                }
                if(!CollectionUtils.isEmpty(deductReasons))
                    courseScheduleTeacherSalary.setDeductionReason(org.apache.commons.lang3.StringUtils.join(deductReasons, "；"));

                //更新教师结算信息
                courseScheduleTeacherSalary.setSubsidy(subsidy);
                courseScheduleTeacherSalary.setActualSalary(finalSalary.compareTo(BigDecimal.ZERO)<0?BigDecimal.ZERO:finalSalary);
                courseScheduleTeacherSalary.setSettlementTime(now);
                courseScheduleTeacherSalaryDao.update(courseScheduleTeacherSalary);

                continue;
            }else if(CourseSchedule.CourseScheduleType.HIGH.equals(courseSchedule.getType())){
                //根据课程类型获取对应的默认课酬设置列表
                List<TeacherDefaultMusicGroupSalary> teacherDefaultMusicGroupSalariesWithCourseType = teacherDefaultMusicGroupSalariesGroupByCourseType.get(courseSchedule.getType());
                if (CollectionUtils.isEmpty(teacherDefaultMusicGroupSalariesWithCourseType)) {
                    LOGGER.error("存在未指定结算方式的课程类型[{}]", courseScheduleTeacherSalary.getId());
                    continue;
                }

                //根据教师编号将对应结算方式下的默认课酬设置分组
                Map<Integer, TeacherDefaultMusicGroupSalary> teacherDefaultMusicGroupSalariesGroupByTeacherId = teacherDefaultMusicGroupSalariesWithCourseType.stream().collect(Collectors.toMap(TeacherDefaultMusicGroupSalary::getUserId, teacherDefaultMusicGroupSalary -> teacherDefaultMusicGroupSalary));

                TeacherDefaultMusicGroupSalary teacherDefaultMusicGroupSalaryWithTeacherId = teacherDefaultMusicGroupSalariesGroupByTeacherId.get(courseScheduleTeacherSalary.getUserId());

                Long normalStudentNum = courseNormalStudentsMap.get(courseSchedule.getId());
                if(Objects.isNull(normalStudentNum)){
                    normalStudentNum = Long.valueOf(0);
                }

                //结算方式
                String settlementType = musicGroupSettlementsMap.get(courseScheduleTeacherSalary.getCourseScheduleId());
                if(StringUtils.isEmpty(settlementType)){
                    LOGGER.error("未指定课酬结算方式[{}]", courseScheduleTeacherSalary.getId());
                    continue;
                }

                teacherSalary = TeachTypeEnum.BISHOP.equals(courseScheduleTeacherSalary.getTeacherRole()) ? teacherDefaultMusicGroupSalaryWithTeacherId.getMainTeacher30MinSalary() : teacherDefaultMusicGroupSalaryWithTeacherId.getAssistantTeacher30MinSalary();
                if (settlementType.equals(SalarySettlementTypeEnum.GRADIENT_SALARY)) {
                    teacherSalary = TeachTypeEnum.BISHOP.equals(courseScheduleTeacherSalary.getTeacherRole()) ? teacherDefaultMusicGroupSalaryWithTeacherId.getMainTeacher90MinSalary() : teacherDefaultMusicGroupSalaryWithTeacherId.getAssistantTeacher90MinSalary();
                }

                if(Objects.isNull(teacherSalary)){
                    LOGGER.error("教师课酬设置异常{}", courseSchedule.getId());
                    continue;
                }
                teacherSalary = teacherSalary.multiply(new BigDecimal(normalStudentNum));
            }else{
                teacherSalary=courseScheduleTeacherSalary.getExpectSalary();
            }

            List<String> deductReasons = new ArrayList<>();

            BigDecimal subsidy = courseSubsidyMap.get(courseSchedule.getId());
            if (Objects.isNull(subsidy)) {
                subsidy = new BigDecimal(0);
            }
            teacherSalary = teacherSalary.add(subsidy);
            if(BigDecimal.ZERO.compareTo(subsidy)!=0){
                deductReasons.add("教学点补贴：" + subsidy);
            }

            boolean notPositive = Objects.nonNull(teacher)&&Objects.nonNull(teacher.getFormalStaffDate())&&courseSchedule.getClassDate().compareTo(teacher.getFormalStaffDate())<0;
            boolean isProbationPeriod = Objects.nonNull(teacher)&&Objects.isNull(teacher.getFormalStaffDate())&&ProbationPeriodEnum.TRY.equals(teacher.getIsProbationPeriod());

            if(Objects.isNull(teacher)||notPositive||isProbationPeriod){
                teacherSalary = teacherSalary.multiply(new BigDecimal("0.8"));
                deductReasons.add("未转正");
            }

            //扣除费用
            BigDecimal deductCost = BigDecimal.ZERO;
            List<TeacherAttendance> courseTeacherAttendances = teacherCourseAttendanceMap.get(org.apache.commons.lang3.StringUtils.joinWith(":", courseScheduleTeacherSalary.getCourseScheduleId(), courseScheduleTeacherSalary.getUserId()));

            boolean isCallName = false;
            List<StudentAttendance> courseStudentAttendance = courseStudentAttendanceMap.get(courseScheduleTeacherSalary.getCourseScheduleId());
            if(!CollectionUtils.isEmpty(courseStudentAttendance)){
                long csaNum = courseStudentAttendance.stream().filter(csa -> DateUtil.hoursBetween(csa.getCreateTime(), courseSchedule.getStartClassTime()) < 4).count();
                if(csaNum>0){
                    isCallName = true;
                }
            }

            TeacherAttendance teacherAttendance = null;
            if(!CollectionUtils.isEmpty(courseTeacherAttendances)){
                teacherAttendance = courseTeacherAttendances.get(0);
            }

            School school = idSchoolMap.get(courseSchedule.getSchoolId());
            boolean signInInRange = false;
            boolean signOutInRange = false;
            if(Objects.nonNull(school)&&Objects.nonNull(school.getLongitudeLatitude())
                    &&StringUtils.isNotBlank(teacherAttendance.getSignInLongitudeLatitude())
                    &&StringUtils.isNotBlank(teacherAttendance.getSignOutLongitudeLatitude())){
                double signInDistance = MapUtil.distance(teacherAttendance.getSignInLongitudeLatitude(),
                        school.getLongitudeLatitude());
                if (signInDistance <= attendanceRange) {
                    signInInRange = true;
                }
                double signOutDistance = MapUtil.distance(teacherAttendance.getSignOutLongitudeLatitude(),
                        school.getLongitudeLatitude());
                if (signOutDistance <= attendanceRange) {
                    signOutInRange = true;
                }
            }

            if(Objects.isNull(teacherAttendance)||Objects.isNull(teacherAttendance.getSignInStatus())){
                //无签到记录扣除全部课酬
                deductCost = deductCost.add(teacherSalary);
                deductReasons.add("未签到扣除全部课酬");
            }else if(YesOrNoEnum.NO.equals(teacherAttendance.getSignInStatus())){
                int signCourseTimeBetweenSeconds = DateUtil.secondsBetween(teacherAttendance.getSignInTime(), courseSchedule.getStartClassTime());
                float signCourseTimeBetween = (float)signCourseTimeBetweenSeconds/(float)60;
                if(signCourseTimeBetween>0&&signCourseTimeBetween<=20){
                    //未提前20分钟打卡扣除50元
                    deductCost = deductCost.add(new BigDecimal(50));
                    deductReasons.add("未提前20分钟打卡扣除50元");
                }else if(signCourseTimeBetween<=0&&signCourseTimeBetween>-30){
                    //迟到30分钟内扣除一半课酬
                    deductCost = deductCost.add(teacherSalary.divide(new BigDecimal(2)));
                    deductReasons.add("迟到30分钟内扣除一半课酬");
                }else if(signCourseTimeBetween<=-30){
                    //迟到30分钟及以上扣除全部课酬
                    deductCost = deductCost.add(teacherSalary);
                    deductReasons.add("迟到30分钟及以上扣除全部课酬");
                }
                if(!signInInRange){
                    //签到经纬度异常扣除50
                    deductCost = deductCost.add(new BigDecimal(50));
                    deductReasons.add("签到经纬度异常扣除50");
                }
            }else{
                signInInRange = true;
            }

            if(Objects.isNull(teacherAttendance)||Objects.isNull(teacherAttendance.getSignOutStatus())){
                //未签退扣除全部课酬
                deductCost = deductCost.add(teacherSalary);
                deductReasons.add("未签退扣除全部课酬");
            }else if(YesOrNoEnum.NO.equals(teacherAttendance.getSignOutStatus())){
                int signOutCourseTimeBetweenSeconds = DateUtil.secondsBetween(teacherAttendance.getSignOutTime(), courseSchedule.getEndClassTime());
                float signOutCourseTimeBetween = (float)signOutCourseTimeBetweenSeconds/(float) 60;
                if(signOutCourseTimeBetween>3){
                    //早退
                    deductCost = deductCost.add(teacherSalary);
                    deductReasons.add("早退扣除全部课酬");
                }else if((signOutCourseTimeBetween<=3&&signOutCourseTimeBetween>0)||signOutCourseTimeBetween<=3600){
                    //异常签退，扣除50元
                    deductCost = deductCost.add(new BigDecimal(50));
                    deductReasons.add("异常签退，扣除50元");
                }else{
                    //签退经纬度异常，扣除50元
                    deductCost = deductCost.add(new BigDecimal(50));
                    deductReasons.add("签退经纬度异常，扣除50元");
                }
            }else{
                signOutInRange = true;
            }

            if(!signInInRange&&!signOutInRange){
                //签到签退GPS定位在指定距离外
                deductCost = deductCost.add(teacherSalary);
                deductReasons.add("签到签退GPS定位在指定距离外，扣除全部课酬");
            }

            BigDecimal finalSalary = teacherSalary.subtract(deductCost).setScale(BigDecimal.ZERO.intValue(), BigDecimal.ROUND_HALF_UP);

            if(BigDecimal.ZERO.compareTo(deductCost)<0){
                deductReasons.add("扣除总费用：" + deductCost);
            }
            if(!CollectionUtils.isEmpty(deductReasons)){
                courseScheduleTeacherSalary.setDeductionReason(org.apache.commons.lang3.StringUtils.join(deductReasons, "；"));
            }

            //更新教师结算信息
            courseScheduleTeacherSalary.setSubsidy(subsidy);
            courseScheduleTeacherSalary.setActualSalary(finalSalary.compareTo(BigDecimal.ZERO)<0?BigDecimal.ZERO:finalSalary);
            courseScheduleTeacherSalary.setSettlementTime(now);
            courseScheduleTeacherSalaryDao.update(courseScheduleTeacherSalary);
        }
    }

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

        List<TeacherSalaryDto> dataList = null;
        int count = courseScheduleTeacherSalaryDao.countSalaries(params);
        if(queryInfo.getIsExport() && count > 50000){
            throw new BizException("数据集太大，不能导出.最大数据集不能超过50000");
        }
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = courseScheduleTeacherSalaryDao.querySalaries(params);
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
    public void updateVipGroupCourseTeacherSalary(Integer vipGroupId, Integer oldTeacherId) {
        VipGroup vipGroup = vipGroupService.get(vipGroupId.longValue());

        BigDecimal onlineTeacherSalary=BigDecimal.ZERO,
                offlineTeacherSalary=BigDecimal.ZERO;

        Map<String, BigDecimal> salary = vipGroupService.countVipGroupPredictFee(vipGroup, vipGroup.getUserId(), null, null);

        if(Objects.nonNull(salary)){
            onlineTeacherSalary=salary.get("onlineTeacherSalary");
            offlineTeacherSalary=salary.get("offlineTeacherSalary");
        }

        List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries = courseScheduleTeacherSalaryDao.findByGroupWithNotStart(vipGroupId.toString(), GroupType.VIP.getCode());
        if(!CollectionUtils.isEmpty(courseScheduleTeacherSalaries)){
            List<Long> courseIds = courseScheduleTeacherSalaries.stream().map(CourseScheduleTeacherSalary::getCourseScheduleId).collect(Collectors.toList());
            List<Map<Integer, String>> teachModeById = courseScheduleDao.getTeachModeById(courseIds);
            Map<Integer, String> coureTeachModeMap = MapUtil.convertIntegerMap(teachModeById);
            for(int i=0;i<courseScheduleTeacherSalaries.size();i++){
                String courseType = coureTeachModeMap.get(courseScheduleTeacherSalaries.get(i).getCourseScheduleId());
                if(courseType.equals(TeachModeEnum.ONLINE.getCode())){
                    courseScheduleTeacherSalaries.get(i).setExpectSalary(onlineTeacherSalary);
                }else if(courseType.equals(TeachModeEnum.OFFLINE.getCode())){
                    courseScheduleTeacherSalaries.get(i).setExpectSalary(offlineTeacherSalary);
                }
            }
            courseScheduleTeacherSalaryDao.batchUpdateTeacherExpectSalarys(courseScheduleTeacherSalaries);
        }
    }

    @Override
    public List<ExportTeacherSalaryDto> exportTeacherSalary(ExportTeacherSalaryQueryInfo queryInfo) {
        //4月份之后的需要扣异常考勤的课酬
        //扣除有异常考勤的课酬
        Map<String, Object> params = new HashMap<>();
        MapUtil.populateMap(params, queryInfo);
        String firstDayOfMonth = DateUtil.format(DateUtil.getFirstDayOfMonth(queryInfo.getDate()),DateUtil.ISO_EXPANDED_DATE_FORMAT);
        String lastDayOfMonth =  DateUtil.format(DateUtil.getLastDayOfMonth(queryInfo.getDate()),DateUtil.ISO_EXPANDED_DATE_FORMAT);
        params.put("firstDayOfMonth",firstDayOfMonth);
        params.put("lastDayOfMonth",lastDayOfMonth);
        List<ExportTeacherSalaryDto> exportTeacherSalaryDtos = new ArrayList<>();
        //乐团课
        exportTeacherSalaryDtos.addAll(courseScheduleTeacherSalaryDao.exportMusicTeacherSalary(params));
        //vip课
        List<ExportTeacherSalaryDto> exportVipTeacherSalaryDtos = courseScheduleTeacherSalaryDao.exportVipTeacherSalary(params);
        //获取有奖励的vip课  .collect(Collectors.toMap(Group::getId, group -> group));
        List<TaskRewardsRulesDto> rewardsRulesDtos = courseScheduleRewardsRulesService.queryVipGroupTeacherClassTimesByMonth(firstDayOfMonth,lastDayOfMonth);
        Map<Integer,Double> rewardMap = new HashMap<>(1000);
        Map<Integer,String> rewardNameMap = new HashMap<>(1000);
        if(rewardsRulesDtos != null && rewardsRulesDtos.size() > 0){
            List<Integer> activeIds = rewardsRulesDtos.stream().map(e -> e.getActiveId()).collect(Collectors.toList());
            List<CourseScheduleRewardsRules> rewardsRules = courseScheduleRewardsRulesService.findByIds(activeIds);
            Map<Integer, List<CourseScheduleRewardsRules>> integerListMap = rewardsRules.stream().collect(Collectors.groupingBy(CourseScheduleRewardsRules::getId));
            rewardsRulesDtos.forEach(e->{
                CourseScheduleRewardsRules rewardsRule = integerListMap.get(e.getActiveId()).get(0);
                List<RewardsRulesJsonDto> rulesJsonDtos = JSONObject.parseArray(rewardsRule.getRewardsRulesJson(), RewardsRulesJsonDto.class);
                BigDecimal money = BigDecimal.ZERO;
                //STAIR 阶梯, PER 累计, TOTAL
                RewardsRulesJsonDto dto;
                int times = e.getTimes();
                switch (rewardsRule.getRewardMode()) {
                    case STAIR:
                        for (int i = 0;i< rulesJsonDtos.size();i++){
                            dto = rulesJsonDtos.get(i);
                            if (e.getTimes() >= dto.getMin()) {
                                //最后一个梯度
                                if(rulesJsonDtos.size()-1 == i){
                                    money = money.add(dto.getMoney().multiply(new BigDecimal(times)));
                                }else if(e.getTimes() >= dto.getMax()){
                                    times = Math.abs(dto.getMax() - times);
                                    money = money.add(dto.getMoney().multiply(new BigDecimal(dto.getMax()-dto.getMin()+1)));
                                }else {
                                    money = money.add(dto.getMoney().multiply(new BigDecimal(times)));
                                }
                            }
                        }
                        break;
                    case PER:
                        for (int i = 0;i< rulesJsonDtos.size();i++){
                            dto = rulesJsonDtos.get(i);
                            if (e.getTimes() >= dto.getMin()) {
                                money = dto.getMoney().multiply(new BigDecimal(times));
                            }
                        }
                        break;
                    case TOTAL:
                        break;
                }
                if (money.doubleValue() > 0) {
                    String[] split = e.getCourseScheduleIds().split(",");
                    switch (rewardsRule.getRewardMode()) {
                        case STAIR:
                            List<String> list = new ArrayList(Arrays.asList(split));
                            Collections.sort(list);
                            int fromIndex = 0;
                            List<String> subList;
                            int max = 0;
                            for (RewardsRulesJsonDto rulesJsonDto:rulesJsonDtos) {
                                if(list.size() < fromIndex){
                                    break;
                                }
                                max = rulesJsonDto.getMax();
                                if(list.size() < rulesJsonDto.getMax()){
                                    max = list.size();
                                }
                                subList = list.subList(fromIndex,max);
                                fromIndex = rulesJsonDto.getMax();
                                if(subList != null && subList.size() > 0){
                                    subList.forEach(courseId->{
                                        rewardMap.put(Integer.parseInt(courseId),rulesJsonDto.getMoney().doubleValue());
                                        rewardNameMap.put(Integer.parseInt(courseId),e.getActiveName());
                                    });
                                }
                            }
                            break;
                        case PER:
                            for (int i = 0; i < split.length; i++) {
                                rewardMap.put(Integer.parseInt(split[i]),money.doubleValue()/split.length);
                                rewardNameMap.put(Integer.parseInt(split[i]),e.getActiveName());
                            }
                            break;
                    }
                }
            });
        }
        exportVipTeacherSalaryDtos.forEach(e->{
            e.setRewards(rewardMap.get(e.getCourseScheduleId()));
            e.setActiveName(rewardNameMap.get(e.getCourseScheduleId()));
        });
        exportTeacherSalaryDtos.addAll(exportVipTeacherSalaryDtos);
        //vip课（梯度课酬）
        //陪练课
        exportTeacherSalaryDtos.addAll(courseScheduleTeacherSalaryDao.exportPracticeTeacherSalary(params));
        exportTeacherSalaryDtos.sort(Comparator.comparing(ExportTeacherSalaryDto::getTeacherId));
        return exportTeacherSalaryDtos;
    }


    @Override
    public List<Long> querySettlementScheduleId(String courseScheduleIds) {
        return courseScheduleTeacherSalaryDao.querySettlementScheduleId(courseScheduleIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmTeacherMonthSalary(Integer teacherId, String month) {
        if(Objects.isNull(month)){
            month=DateUtil.dateToString(DateUtil.addMonths(new Date(), -1), "yyyy-MM");
        }
        List<Long> teacherSalaryIds = courseScheduleTeacherSalaryDao.findTeacherSalaryIdsWithMonth(teacherId, month);
        if(CollectionUtils.isEmpty(teacherSalaryIds)){
            return;
        }
        courseScheduleTeacherSalaryDao.updateTeacherSalaryConfirmStatus(teacherSalaryIds,TeacherSalaryConfirmStatus.CONFIRMED);
        teacherCourseRewardDao.updateTeacherRewardConfirmStatusWithMonth(month, teacherId, TeacherSalaryConfirmStatus.CONFIRMED);
    }

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

        List<TeacherCourseSalaryDetail4WebDto> dataList = null;
        int count = courseScheduleTeacherSalaryDao.countIsSettlementCourseSalarys(params);
        if (count > 0) {
            pageInfo.setTotal(count);
            params.put("offset", pageInfo.getOffset());
            dataList = courseScheduleTeacherSalaryDao.findIsSettlementCourseSalarys(params);
            List<Long> courseScheduleIds = dataList.stream().map(TeacherCourseSalaryDetail4WebDto::getCourseScheduleId).collect(Collectors.toList());
            List<Map<Long, BigDecimal>> coursePaymentMaps = courseScheduleStudentPaymentDao.findCoursePayment(courseScheduleIds);
            Map<Long, BigDecimal> coursePaymentMap = MapUtil.convertIntegerMap(coursePaymentMaps);
            for (TeacherCourseSalaryDetail4WebDto teacherCourseSalaryDetail4WebDto : dataList) {
                teacherCourseSalaryDetail4WebDto.setFinalSalary(teacherCourseSalaryDetail4WebDto.getActualSalary());
                if(Objects.nonNull(teacherCourseSalaryDetail4WebDto.getReduceSalary())){
                    teacherCourseSalaryDetail4WebDto.setActualSalary(teacherCourseSalaryDetail4WebDto.getActualSalary().add(teacherCourseSalaryDetail4WebDto.getReduceSalary()));
                }
                BigDecimal paymentPrice = coursePaymentMap.get(teacherCourseSalaryDetail4WebDto.getCourseScheduleId());
                teacherCourseSalaryDetail4WebDto.setActualReceipts(paymentPrice);
            }
        }
        if (count == 0) {
            dataList = new ArrayList<>();
        }
        pageInfo.setRows(dataList);
        return pageInfo;
    }

    @Override
    public Map<String, Object> findTeacherSettlementCourseSalarys(CourseSalaryQueryInfo4Web queryInfo4Web) {
        if(Objects.isNull(queryInfo4Web.getMonth())){
            queryInfo4Web.setMonth(DateUtil.dateToString(DateUtil.addMonths(new Date(), -1), "yyyy-MM"));
        }
        PageInfo<TeacherCourseSalaryDetail4WebDto> pageInfo = findIsSettlementCourseSalarys(queryInfo4Web);
        Map<String, Object> result=new HashMap<>();
        result.put("pageInfo",pageInfo);
        List<CourseTeacherSalaryStatisticsDto> teacherGroupTypeSalary = new ArrayList<>();
        if(queryInfo4Web.getPage()<=1){
            teacherGroupTypeSalary = courseScheduleTeacherSalaryDao.getTeacherGroupTypeSalary(queryInfo4Web.getMonth(), queryInfo4Web.getTeacherId());
            CourseTeacherSalaryStatisticsDto courseTeacherSalaryStatisticsDto = teacherCourseRewardDao.teacherCourseRewardStatWithMonth(queryInfo4Web.getTeacherId(), queryInfo4Web.getMonth());
            if(Objects.isNull(courseTeacherSalaryStatisticsDto)){
                courseTeacherSalaryStatisticsDto=new CourseTeacherSalaryStatisticsDto();
            }
            courseTeacherSalaryStatisticsDto.setStatType("REWARD");
            teacherGroupTypeSalary.add(courseTeacherSalaryStatisticsDto);
        }
        result.put("stat", teacherGroupTypeSalary);
        if(!CollectionUtils.isEmpty(pageInfo.getRows())){
            if(TeacherSalaryConfirmStatus.CONFIRMED.equals(pageInfo.getRows().get(0).getConfirmStatus())||
                    TeacherSalaryConfirmStatus.COMPLETED.equals(pageInfo.getRows().get(0).getConfirmStatus())){
                result.put("confirmStatus",1);
            }else{
                result.put("confirmStatus",0);
            }
        }else{
            result.put("confirmStatus",0);
        }
        List<TeacherSalaryComplaints> teacherSalaryComplaintsWithMonth = teacherSalaryComplaintsDao.findTeacherSalaryComplaintsWithMonth(queryInfo4Web.getTeacherId(), queryInfo4Web.getMonth(), TeacherSalaryComplaintsStatusEnum.PENDING);
        if(CollectionUtils.isEmpty(teacherSalaryComplaintsWithMonth)){
            result.put("haveComplaints",0);
        }else{
            result.put("haveComplaints",1);
        }
        return result;
    }

    @Override
    public Map<String, Object> findTeacherYearSalarys(Integer teacherId, String year) {
        if(Objects.isNull(year)){
            year=DateUtil.getYear(new Date());
        }
        List<CourseTeacherSalaryStatisticsDto> teacherMonthSalarys = courseScheduleTeacherSalaryDao.getTeacherMonthSalarys(teacherId, year);
        BigDecimal totalSalary = BigDecimal.ZERO;
        for (CourseTeacherSalaryStatisticsDto teacherMonthSalary : teacherMonthSalarys) {
            totalSalary = totalSalary.add(teacherMonthSalary.getTotalActualSalary());
        }
        Map<String, Object> result=new HashMap<>();
        result.put("monthSalarys", teacherMonthSalarys);
        result.put("totalSalary", totalSalary);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void openSalaryConfirm(String month) {
        if(Objects.isNull(month)){
            month=DateUtil.dateToString(DateUtil.addMonths(new Date(), -1), "yyyy-MM");
        }
        int i = courseScheduleTeacherSalaryDao.countOpenConfirmSalarysWithMonth(month,null);
        if(i<=0){
            throw new BizException("课酬确认已开启");
        }
        List<Long> teacherSalaryIds = courseScheduleTeacherSalaryDao.findTeacherSalaryIdsWithMonth(null, month);
        if(CollectionUtils.isEmpty(teacherSalaryIds)){
            return;
        }
        courseScheduleTeacherSalaryDao.updateTeacherSalaryConfirmStatus(teacherSalaryIds,TeacherSalaryConfirmStatus.UNCONFIRMED);
        teacherCourseRewardDao.updateTeacherRewardConfirmStatusWithMonth(month, null, TeacherSalaryConfirmStatus.UNCONFIRMED);
        String teacherBaseUrl = sysConfigDao.findByParamName(SysConfigService.TEACHER_BASE_URL).getParanValue();
        String notifyUrl="8?"+teacherBaseUrl+"/#/remuneration?month=" + month;
        String monthStr = DateUtil.dateToString(DateUtil.stringToDate(month, "yyyy-MM"),"M月");
        String expireDateStr = DateUtil.dateToString(DateUtil.addDays(new Date(), 2), "M月d号");
        List<Integer> teacherIds = courseScheduleTeacherSalaryDao.findIsSettlementTeacherIdsWithMonth(month);
        Map<Integer, String> userMap = teacherIds.stream().collect(Collectors.toMap(Integer::valueOf, id->String.valueOf(id)));
        sysMessageService.batchSendMessage(MessageSenderPluginContext.MessageSender.JIGUANG, MessageTypeEnum.TEACHER_SALARY_CONFIRM_PUSH,
                userMap, null, 0, notifyUrl, "TEACHER", monthStr, expireDateStr);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeSalaryConfirm(String month) {
        if(Objects.isNull(month)){
            month=DateUtil.dateToString(DateUtil.addMonths(new Date(), -1), "yyyy-MM");
        }
        List<Long> teacherSalaryIds = courseScheduleTeacherSalaryDao.findTeacherSalaryIdsWithMonth(null, month);
        if(CollectionUtils.isEmpty(teacherSalaryIds)){
            return;
        }
        courseScheduleTeacherSalaryDao.updateTeacherSalaryConfirmStatusToComplied(teacherSalaryIds);
        teacherSalaryComplaintsDao.updateComplaintStatusTuDoneWithMonth(month);
        teacherCourseRewardDao.updateTeacherRewardConfirmStatusWithMonth(month, null, TeacherSalaryConfirmStatus.COMPLETED);
    }

    @Override
    public List<CourseScheduleTeacherSalary> createTeacherCourseSalary(CourseSchedule courseSchedule, List<Integer> teacherIds) {
        if(Objects.isNull(courseSchedule.getType())){
            throw new BizException("请指定课程类型");
        }

        List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries = new ArrayList<>();
        Date now = new Date();
        for (Integer teacherId : teacherIds) {
            CourseScheduleTeacherSalary courseScheduleTeacherSalary = new CourseScheduleTeacherSalary();
            courseScheduleTeacherSalary.setGroupType(courseSchedule.getGroupType());
            courseScheduleTeacherSalary.setMusicGroupId(courseSchedule.getMusicGroupId());
            courseScheduleTeacherSalary.setCourseScheduleId(courseSchedule.getId());
            courseScheduleTeacherSalary.setClassGroupId(courseSchedule.getClassGroupId());
            if(teacherId.equals(courseSchedule.getActualTeacherId())){
                courseScheduleTeacherSalary.setTeacherRole(TeachTypeEnum.BISHOP);
            }else{
                courseScheduleTeacherSalary.setTeacherRole(TeachTypeEnum.TEACHING);
            }
            courseScheduleTeacherSalary.setUserId(teacherId);
            courseScheduleTeacherSalary.setCreateTime(now);
            courseScheduleTeacherSalary.setUpdateTime(now);
        }
        switch (courseSchedule.getGroupType()){
            case MUSIC:
                //生成乐团课课酬
                createMusicGroupCourseTeacherSalaries(null, courseSchedule, courseScheduleTeacherSalaries);
                break;
            case VIP:
                //生成vip课课酬
                createVipGroupCourseTeacherSalaries(courseSchedule, courseScheduleTeacherSalaries);
                break;
            case PRACTICE:
                //生成网管课课酬
                createPracticeGroupCourseTeacherSalaries(courseSchedule, courseScheduleTeacherSalaries);
                break;
        }
        return courseScheduleTeacherSalaries;
    }

    @Override
    public void createMusicGroupCourseTeacherSalary(MusicGroup musicGroup, CourseSchedule courseSchedule, CourseScheduleTeacherSalary courseScheduleTeacherSalary) {
        List<CourseScheduleTeacherSalary> teacherSalaries = new ArrayList<>();
        teacherSalaries.add(courseScheduleTeacherSalary);
        createMusicGroupCourseTeacherSalaries(musicGroup,courseSchedule,teacherSalaries);
        courseScheduleTeacherSalary.setExpectSalary(teacherSalaries.get(0).getExpectSalary());
    }

    /**
     * @describe 创建课程指定教师的课酬记录-乐团课
     * @author Joburgess
     * @date 2020.10.22
     * @param courseSchedule: 课程计划
     * @param courseScheduleTeacherSalaries: 教师课酬记录基本信息
     * @return java.util.List<com.ym.mec.biz.dal.entity.CourseScheduleTeacherSalary>
     */
    private void createMusicGroupCourseTeacherSalaries(MusicGroup musicGroup, CourseSchedule courseSchedule, List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries) {
        if(Objects.isNull(musicGroup)){
            musicGroup = musicGroupDao.get(courseSchedule.getMusicGroupId());
        }

        List<Integer> teacherIds = courseScheduleTeacherSalaries.stream().map(CourseScheduleTeacherSalary::getUserId).collect(Collectors.toList());

        List<TeacherDefaultMusicGroupSalary> allTeacherDefaultMusicGroupSalary = teacherDefaultMusicGroupSalaryDao.findByTeacher(teacherIds);
        Map<Integer, List<TeacherDefaultMusicGroupSalary>> teacherSalaryMap = allTeacherDefaultMusicGroupSalary.stream().collect(Collectors.groupingBy(TeacherDefaultMusicGroupSalary::getUserId));

        long classCourseDuration = DateUtil.minutesBetween(courseSchedule.getStartClassTime(), courseSchedule.getEndClassTime());

        for (CourseScheduleTeacherSalary courseScheduleTeacherSalary : courseScheduleTeacherSalaries) {
            if(Objects.isNull(courseScheduleTeacherSalary.getTeacherRole())){
                throw new BizException("请指定教师类型");
            }
            List<TeacherDefaultMusicGroupSalary> teacherSalaryList = teacherSalaryMap.get(courseScheduleTeacherSalary.getUserId());

            BigDecimal salary;

            Map<CourseSchedule.CourseScheduleType, TeacherDefaultMusicGroupSalary> collect = teacherSalaryList.stream().collect(Collectors.toMap(TeacherDefaultMusicGroupSalary::getCourseScheduleType, teacherDefaultMusicGroupSalary -> teacherDefaultMusicGroupSalary));

            TeacherDefaultMusicGroupSalary teacherDefaultMusicGroupSalary = collect.get(courseSchedule.getType());

            if (teacherDefaultMusicGroupSalary == null) {
                Teacher teacher = teacherDao.get(courseScheduleTeacherSalary.getUserId());
                throw new BizException("请设置" + teacher.getRealName() + "老师的" + courseSchedule.getType().getMsg() + "课酬");
            }

            //对应基准课酬
            BigDecimal baseSalary = new BigDecimal(0), classTimeDuty = new BigDecimal(0);

            SalarySettlementTypeEnum settlementType = musicGroup.getSettlementType();

            //基准课酬
            if (settlementType.equals(SalarySettlementTypeEnum.TEACHER_DEFAULT) && teacherDefaultMusicGroupSalary != null) {
                //课程时长与结算单位时长占比
                classTimeDuty = new BigDecimal(classCourseDuration).divide(new BigDecimal(30), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN);
                baseSalary = TeachTypeEnum.BISHOP.equals(courseScheduleTeacherSalary.getTeacherRole()) ? teacherDefaultMusicGroupSalary.getMainTeacher30MinSalary() : teacherDefaultMusicGroupSalary.getAssistantTeacher30MinSalary();
            }
            //阶梯课酬
            if (settlementType.equals(SalarySettlementTypeEnum.GRADIENT_SALARY) && teacherDefaultMusicGroupSalary != null) {
                //课程时长与结算单位时长占比
                classTimeDuty = new BigDecimal(classCourseDuration).divide(new BigDecimal(90), CommonConstants.DECIMAL_PLACE, BigDecimal.ROUND_DOWN);
                baseSalary = TeachTypeEnum.BISHOP.equals(courseScheduleTeacherSalary.getTeacherRole()) ? teacherDefaultMusicGroupSalary.getMainTeacher90MinSalary() : teacherDefaultMusicGroupSalary.getAssistantTeacher90MinSalary();
            }

            //课堂课课酬改为按分钟数计算，并且时长占比向下取整
            if (courseSchedule.getType().equals(CourseSchedule.CourseScheduleType.CLASSROOM)){
                baseSalary = TeachTypeEnum.BISHOP.equals(courseScheduleTeacherSalary.getTeacherRole()) ? teacherDefaultMusicGroupSalary.getMainTeacher30MinSalary() : teacherDefaultMusicGroupSalary.getAssistantTeacher30MinSalary();
                classTimeDuty = new BigDecimal(classCourseDuration).divide(new BigDecimal(40), BigDecimal.ZERO.intValue(), BigDecimal.ROUND_DOWN);
            }

            salary = baseSalary.multiply(classTimeDuty).setScale(2, BigDecimal.ROUND_HALF_UP);

            //基础技能提高课
            if (courseSchedule.getType().equals(CourseSchedule.CourseScheduleType.HIGH)) {
                salary = TeachTypeEnum.BISHOP.equals(courseScheduleTeacherSalary.getTeacherRole()) ? teacherDefaultMusicGroupSalary.getMainTeacher30MinSalary() : teacherDefaultMusicGroupSalary.getAssistantTeacher30MinSalary();
                if (settlementType.equals(SalarySettlementTypeEnum.GRADIENT_SALARY)) {
                    salary = TeachTypeEnum.BISHOP.equals(courseScheduleTeacherSalary.getTeacherRole()) ? teacherDefaultMusicGroupSalary.getMainTeacher90MinSalary() : teacherDefaultMusicGroupSalary.getAssistantTeacher90MinSalary();
                }
                if(Objects.isNull(salary)){
                    throw new BizException("课酬设置异常");
                }
                Integer studentNum = classGroupStudentMapperDao.countClassGroupNormalStudentNum(courseSchedule.getClassGroupId());
                salary = salary.multiply(new BigDecimal(studentNum));
            }
            //线上小班课
            if (courseSchedule.getType().equals(CourseSchedule.CourseScheduleType.HIGH_ONLINE)) {
                Integer studentNum = classGroupStudentMapperDao.countClassGroupNormalStudentNum(courseSchedule.getClassGroupId());
                if(studentNum>5){
                    studentNum=5;
                }
                salary = JSON.parseObject(teacherDefaultMusicGroupSalary.getSalaryRuleJson()).getBigDecimal(studentNum.toString());
            }

            if (courseSchedule.getType().equals(CourseSchedule.CourseScheduleType.MUSIC_NETWORK)) {
                salary = JSON.parseObject(teacherDefaultMusicGroupSalary.getSalaryRuleJson()).getBigDecimal("1");
            }

            if(Objects.isNull(salary)){
                throw new BizException("课酬设置异常");
            }

            courseScheduleTeacherSalary.setExpectSalary(salary);
        }
    }

    /**
     * @describe 创建课程指定教师的课酬记录-VIP课
     * @author Joburgess
     * @date 2020.10.22
     * @param courseSchedule: 课程计划
     * @param courseScheduleTeacherSalaries: 教师课酬记录基本信息
     * @return java.util.List<com.ym.mec.biz.dal.entity.CourseScheduleTeacherSalary>
     */
    private void createVipGroupCourseTeacherSalaries(CourseSchedule courseSchedule, List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries){
        VipGroup vipGroup = vipGroupService.get(Long.valueOf(courseSchedule.getMusicGroupId()));
        for (CourseScheduleTeacherSalary courseScheduleTeacherSalary : courseScheduleTeacherSalaries) {
            Map<String, BigDecimal> salaryMap = vipGroupService.countVipGroupCoursePredictFee(vipGroup, courseSchedule.getActualTeacherId(), courseSchedule.getId());
            courseScheduleTeacherSalary.setExpectSalary(courseSchedule.getTeachMode() == TeachModeEnum.OFFLINE?salaryMap.get("offlineTeacherSalary"):salaryMap.get("onlineTeacherSalary"));
        }
    }

    /**
     * @describe 创建课程指定教师的课酬记录-网管课
     * @author Joburgess
     * @date 2020.10.22
     * @param courseSchedule: 课程计划
     * @param courseScheduleTeacherSalaries: 教师课酬记录基本信息
     * @return java.util.List<com.ym.mec.biz.dal.entity.CourseScheduleTeacherSalary>
     */
    private void createPracticeGroupCourseTeacherSalaries(CourseSchedule courseSchedule, List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries){
        List<Integer> teacherIds = courseScheduleTeacherSalaries.stream().map(CourseScheduleTeacherSalary::getUserId).collect(Collectors.toList());

        List<TeacherDefaultPracticeGroupSalary> allTeacherDefaultPracticeGroupSalaryList = teacherDefaultPracticeGroupSalaryDao.queryByUserIdList(teacherIds);
        Map<Integer, List<TeacherDefaultPracticeGroupSalary>> teacherSalaryMap = allTeacherDefaultPracticeGroupSalaryList.stream().collect(Collectors.groupingBy(TeacherDefaultPracticeGroupSalary::getUserId));
        for (CourseScheduleTeacherSalary courseScheduleTeacherSalary : courseScheduleTeacherSalaries) {
            final List<TeacherDefaultPracticeGroupSalary> teacherDefaultPracticeGroupSalaryList = teacherSalaryMap.get(courseScheduleTeacherSalary.getUserId());
            if (teacherDefaultPracticeGroupSalaryList != null && teacherDefaultPracticeGroupSalaryList.size() > 0) {
                courseScheduleTeacherSalary.setExpectSalary(teacherDefaultPracticeGroupSalaryList.get(0).getMainTeacherSalary());
            } else {
                throw new BizException("请设置老师 的网管课课酬");
            }
        }
    }

    @Override
    public List<TeacherAttendancesDto> getCourseTeachers(Long courseScheduleId) {
        List<TeacherAttendancesDto> result = new ArrayList<>();
        List<CourseScheduleTeacherSalary> courseScheduleTeacherSalaries = courseScheduleTeacherSalaryDao.findByCourseSchedule(courseScheduleId);
        if(CollectionUtils.isEmpty(courseScheduleTeacherSalaries)){
            return result;
        }
        Set<Integer> teacherIds = courseScheduleTeacherSalaries.stream().map(CourseScheduleTeacherSalary::getUserId).collect(Collectors.toSet());
        List<TeacherAttendance> teacherAttendances = teacherAttendanceDao.findByCourseSchedules(Arrays.asList(courseScheduleId));
        Map<Integer, TeacherAttendance> idTeacherAttendanceMap = teacherAttendances.stream().collect(Collectors.toMap(TeacherAttendance::getTeacherId, t -> t, (t1, t2) -> t1));
        List<SimpleUserDto> usersSimpleInfo = new ArrayList<>();
        if(!CollectionUtils.isEmpty(teacherIds)){
            usersSimpleInfo = teacherDao.getUsersSimpleInfo(new ArrayList<>(teacherIds));
        }
        Map<Integer, SimpleUserDto> idUserMap = usersSimpleInfo.stream().collect(Collectors.toMap(SimpleUserDto::getUserId, s -> s));

        CourseSchedule courseSchedule = courseScheduleDao.get(courseScheduleId);
        School school = schoolService.get(courseSchedule.getSchoolId());

        for (CourseScheduleTeacherSalary courseScheduleTeacherSalary : courseScheduleTeacherSalaries) {
            TeacherAttendancesDto t = new TeacherAttendancesDto();
            t.setTeacherId(courseScheduleTeacherSalary.getUserId());
            t.setTeacherName(idUserMap.containsKey(t.getTeacherId())?idUserMap.get(t.getTeacherId()).getUserName():"");
            t.setTeacherRole(courseScheduleTeacherSalary.getTeacherRole());
            if(idTeacherAttendanceMap.containsKey(t.getTeacherId())){
                TeacherAttendance teacherAttendance = idTeacherAttendanceMap.get(t.getTeacherId());
                t.setTeacherAttendance(teacherAttendance);
                t.setSignInTime(teacherAttendance.getSignInTime());
                t.setSignInStatus(Objects.nonNull(teacherAttendance.getSignInStatus())?(YesOrNoEnum.NO.equals(teacherAttendance.getSignInStatus())?SignInStatusEnum.UNUSUAL:SignInStatusEnum.NORMAL):SignInStatusEnum.NO);
                t.setSignOutTime(teacherAttendance.getSignOutTime());
                t.setSignOutStatus(Objects.nonNull(teacherAttendance.getSignOutStatus())?(YesOrNoEnum.NO.equals(teacherAttendance.getSignOutStatus())?SignOutStatusEnum.UNUSUAL:SignOutStatusEnum.NORMAL):SignOutStatusEnum.NO);
                t.setSignInLongitudeLatitude(teacherAttendance.getSignInLongitudeLatitude());
                t.setSignOutLongitudeLatitude(teacherAttendance.getSignOutLongitudeLatitude());
            }
            if(Objects.nonNull(school)){
                t.setSchoolLongitudeLatitude(school.getLongitudeLatitude());
            }
            result.add(t);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void salaryMark() {
        Date now = new Date();
        Date date = DateUtil.addMonths(now, -1);
        String startDate = DateUtil.format(DateUtil.getFirstDayOfMonth(date),DateUtil.DEFAULT_PATTERN);
        String endDate = DateUtil.format(DateUtil.getLastDayOfMonth(date),DateUtil.DEFAULT_PATTERN);

        int isSalaryGroupTypes = redisTemplate.opsForSet().size(CourseScheduleTeacherSalaryService.TASK_KEY).intValue();
        if(isSalaryGroupTypes<3){
            return;
        }
        redisTemplate.delete(CourseScheduleTeacherSalaryService.TASK_KEY);
        List<CourseScheduleTeacherSalary> salaries = courseScheduleTeacherSalaryDao.getIsSalaryWithDate(startDate, endDate);
        if(CollectionUtils.isEmpty(salaries)){
            return;
        }
        SysConfig dayaBaseSalaryAmountConfig = sysConfigService.findByParamName(SysConfigService.DAYA_BASE_SALARY_AMOUNT);
        BigDecimal dayaBaseSalaryAmount = new BigDecimal(4000);
        if(Objects.nonNull(dayaBaseSalaryAmountConfig)&&StringUtils.isNotBlank(dayaBaseSalaryAmountConfig.getParanValue())){
            dayaBaseSalaryAmount = new BigDecimal(dayaBaseSalaryAmountConfig.getParanValue());
        }

        Map<Integer, List<CourseScheduleTeacherSalary>> teacherSalariesMap = salaries.stream().collect(Collectors.groupingBy(CourseScheduleTeacherSalary::getUserId));
        List<CourseScheduleTeacherSalary> updateRecords = new ArrayList<>();
        for (Map.Entry<Integer, List<CourseScheduleTeacherSalary>> teacherSalariesMapEntry : teacherSalariesMap.entrySet()) {
            List<CourseScheduleTeacherSalary> teacherSalaries = teacherSalariesMapEntry.getValue();
            List<BigDecimal> amounts = teacherSalaries.stream().map(CourseScheduleTeacherSalary::getActualSalary).collect(Collectors.toList());
            List<BigDecimal> resultAmounts = salaryComposition(amounts, dayaBaseSalaryAmount);
            for (BigDecimal resultAmount : resultAmounts) {
                Iterator<CourseScheduleTeacherSalary> iterator = teacherSalaries.iterator();
                while (iterator.hasNext()){
                    CourseScheduleTeacherSalary teacherSalary = iterator.next();
                    if(teacherSalary.getActualSalary().compareTo(resultAmount)==0){
                        CourseScheduleTeacherSalary courseScheduleTeacherSalary = new CourseScheduleTeacherSalary();
                        courseScheduleTeacherSalary.setId(teacherSalary.getId());
                        courseScheduleTeacherSalary.setBelongToDaya(true);
                        updateRecords.add(courseScheduleTeacherSalary);
                        iterator.remove();
                        break;
                    }
                }
            }
        }
        courseScheduleTeacherSalaryDao.batchUpdate(updateRecords);
    }

    @Override
    public List<BigDecimal> salaryComposition(List<BigDecimal> salaries, BigDecimal targetSalary) {
        int target = targetSalary.intValue();
        BigDecimal zero = new BigDecimal(0);

        List<List<List<BigDecimal>>> result = new LinkedList<>();
        for(int j=0;j<2;j++){
            result.add(new ArrayList<>());
            for(int i=0;i<target+1;i++){
                List<List<BigDecimal>> t=new ArrayList<>();
                List<BigDecimal> b = new ArrayList<BigDecimal>();
                b.add(zero);
                t.add(b);
                result.get(j).addAll(t);
            }
        }

        int salary_index_pre = 0;
        int salary_index_cur = 1;

        for(int i=1;i<salaries.size()+1;i++){
            for(int j = 1;j<target+1;j++){
                if(j<salaries.get(i-1).intValue()){
                    result.get(salary_index_cur).get(j).clear();
                    result.get(salary_index_cur).get(j).addAll(result.get(salary_index_pre).get(j));
                }else{
                    BigDecimal pre = result.get(salary_index_pre).get(j).stream().reduce(BigDecimal.ZERO, BigDecimal::add);
                    BigDecimal t = result.get(salary_index_pre).get(j-salaries.get(i-1).intValue()).stream().reduce(BigDecimal.ZERO, BigDecimal::add).add(salaries.get(i-1));
                    if(t.compareTo(pre)>0){
                        result.get(salary_index_cur).get(j).clear();
                        result.get(salary_index_cur).get(j).addAll(result.get(salary_index_pre).get(j-salaries.get(i-1).intValue()));
                        result.get(salary_index_cur).get(j).add(salaries.get(i-1));
                    }else{
                        result.get(salary_index_cur).get(j).clear();
                        result.get(salary_index_cur).get(j).addAll(result.get(salary_index_pre).get(j));
                    }
                }
            }
            Collections.swap(result, 0, 1);
        }
        return result.get(0).get(target);
    }
}
