package com.yonge.cooleshow.biz.dal.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
import com.yonge.cooleshow.auth.api.entity.SysUser;
import com.yonge.cooleshow.biz.dal.constant.CourseConstant;
import com.yonge.cooleshow.biz.dal.dao.CourseGroupDao;
import com.yonge.cooleshow.biz.dal.dto.CheckCourseTimeDto;
import com.yonge.cooleshow.biz.dal.dto.LiveCourseGroupDto;
import com.yonge.cooleshow.biz.dal.dto.LiveCourseGroupDto.CoursePlanDto;
import com.yonge.cooleshow.biz.dal.dto.req.OrderReq;
import com.yonge.cooleshow.biz.dal.dto.search.LiveCourseGroupSearch;
import com.yonge.cooleshow.biz.dal.dto.search.LiveCourseGroupStudentCourseSearch;
import com.yonge.cooleshow.biz.dal.dto.search.LiveCourseGroupStudentSearch;
import com.yonge.cooleshow.biz.dal.entity.*;
import com.yonge.cooleshow.biz.dal.enums.*;
import com.yonge.cooleshow.biz.dal.service.*;
import com.yonge.cooleshow.biz.dal.support.PageUtil;
import com.yonge.cooleshow.biz.dal.support.WrapperUtil;
import com.yonge.cooleshow.biz.dal.vo.*;
import com.yonge.cooleshow.biz.dal.vo.res.OrderCreateRes;
import com.yonge.cooleshow.common.constant.SysConfigConstant;
import com.yonge.cooleshow.common.entity.HttpResponseResult;
import com.yonge.cooleshow.common.exception.BizException;
import com.yonge.toolset.mybatis.page.PageInfo;
import com.yonge.toolset.utils.date.DateUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 课程组表(CourseGroup)表服务实现类
 *
 * @author hgw
 * @since 2022-03-18 15:29:10
 */
@Service("courseGroupService")
public class CourseGroupServiceImpl extends ServiceImpl<CourseGroupDao, CourseGroup> implements CourseGroupService {

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

    @Autowired
    private SysUserFeignService sysUserFeignService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private CourseScheduleService courseScheduleService;
    @Autowired
    private CoursePlanService coursePlanService;
    @Autowired
    private SysConfigService sysConfigService;
    @Autowired
    private SubjectService subjectService;
    @Autowired
    private CourseScheduleStudentPaymentService courseScheduleStudentPaymentService;
    @Autowired
    private TeacherFreeTimeService teacherFreeTimeService;
    @Autowired
    private CourseScheduleTeacherSalaryService courseScheduleTeacherSalaryService;

    @Override
    public CourseGroupDao getDao() {
        return this.baseMapper;
    }

    /**
     * 查询课程组详情-直播课详情
     *
     * @param groupId 课程组id
     */
    @Override
    public LiveCourseInfoVo queryLiveCourseInfo(Long groupId) {
        CourseGroup group = Optional.ofNullable(groupId).map(this::getById)
                .orElseThrow(() -> new BizException("课程组信息不存在"));

        LiveCourseInfoVo result = new LiveCourseInfoVo();
        result.setCourseGroupId(group.getId());
        result.setCourseGroupName(group.getName());
        result.setCourseStartTime(group.getCourseStartTime());
        result.setSingleCourseMinutes(group.getSingleCourseMinutes());
        result.setStatus(group.getStatus());
        result.setStudentCount(group.getPreStudentNum());
        result.setBackgroundPic(group.getBackgroundPic());
        result.setTeacherId(group.getTeacherId());
        result.setCoursePrice(group.getCoursePrice());
        result.setCourseNum(group.getCourseNum());
        result.setCourseIntroduce(group.getCourseIntroduce());
        result.setSalesStartDate(group.getSalesStartDate());
        result.setSalesEndDate(group.getSalesEndDate());
        result.setMixStudentNum(group.getMixStudentNum());
        Optional.ofNullable(group.getTeacherId()).map(this::getSysUser)
                .ifPresent(sysUser -> result.setTeacherName(sysUser.getRealName()));
        Optional.ofNullable(group.getSubjectId()).map(subjectService::get)
                .ifPresent(subject -> result.setSubjectName(subject.getName()));
        //课程组计划
        result.setPlanList(coursePlanService.queryCoursePlanByGroupId(groupId));
        //课程组学员信息
        result.setStudentList(courseScheduleStudentPaymentService.queryStudentInfoByGroupId(groupId));

        return result;
    }

    /**
     * 分页查询课程组列表
     *
     * @param param 传入参数
     *              <p> - teacherId    老师id
     *              <p> - groupStatus  课程组状态  ING(进行中)  NOT_SALE(未开售,未上架) APPLY(报名中,销售中) COMPLETE(已完成)
     *              <p> - row 条数
     *              <p> - page 页数
     *              <p> - search 模糊搜索关键字
     */
    public PageInfo<CourseGroupVo> queryPageLiveCourseGroup(Map<String, Object> param) {
        //查询该月的所有课程
        param.put("type", CourseScheduleEnum.LIVE.getCode());
        Page<CourseGroupVo> pageInfo = PageUtil.getPageInfo(param);
        pageInfo.setAsc("b.created_time_");
        IPage<CourseGroupVo> page = baseMapper.queryTeacherCourseGroup(pageInfo, param);
        return PageUtil.pageInfo(page);
    }

    /**
     * 新增课程组
     *
     * @param dto 创建直播课课程组参数
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addLiveCourse(LiveCourseGroupDto dto) {
        //批量检查老师课时在数据库是否重复
        batchCheckTeacherCourseTime(dto.getTeacherId(), dto.getCoursePlanList(), CoursePlanDto::getStartTime, CoursePlanDto::getEndTime);
        //获取锁定课时缓存，没有问题就刷新缓存
        RMap<Long, List<CourseTimeEntity>> cacheTime = getExpireLiveLockTimeCache(dto.getTeacherId());
        List<CourseTimeEntity> timeEntities = new ArrayList<>();
        dto.getCoursePlanList().forEach(o -> {
            CourseTimeEntity time = new CourseTimeEntity();
            time.setStartTime(o.getStartTime());
            time.setEndTime(o.getEndTime());
            timeEntities.add(time);
        });
        cacheTime.fastPut(dto.getTeacherId(), timeEntities);
        Date now = new Date();
        String live = CourseScheduleEnum.LIVE.getCode();
        //写入课程组表
        CourseGroup group = new CourseGroup();
        group.setType(live);
        group.setTeacherId(dto.getTeacherId());
        group.setName(dto.getName());
        group.setSubjectId(dto.getSubjectId());
        group.setSingleCourseMinutes(dto.getSingleCourseMinutes());
        group.setCourseNum(dto.getCourseNum());
        group.setCourseIntroduce(dto.getCourseIntroduce());
        group.setCoursePrice(dto.getCoursePrice());
        group.setStatus(CourseGroupEnum.NOT_SALE.getCode());
        group.setCourseStartTime(dto.getStartTime());
        group.setSalesStartDate(dto.getSalesStartDate());
        group.setSalesEndDate(dto.getSalesEndDate());
        group.setBackgroundPic(dto.getBackgroundPic());
        group.setMixStudentNum(dto.getMixStudentNum());
        group.setCreatedBy(dto.getTeacherId());
        group.setCreatedTime(now);
        this.save(group);

        //写入课程表及课程计划表
        List<CourseSchedule> courseList = new ArrayList<>();
        List<CoursePlan> planList = new ArrayList<>();
        dto.getCoursePlanList().forEach(o -> {
            CourseSchedule course = new CourseSchedule();
            course.setCourseGroupId(group.getId());
            course.setType(live);
            course.setClassNum(o.getClassNum());
            course.setTeacherId(dto.getTeacherId());
            course.setClassDate(o.getStartTime());
            course.setStartTime(o.getStartTime());
            course.setEndTime(o.getEndTime());
            course.setCreatedBy(dto.getTeacherId());
            course.setCreatedTime(now);
            course.setLock(0);
            course.setStatus(CourseScheduleEnum.NOT_START.getCode());
            courseList.add(course);

            CoursePlan plan = new CoursePlan();
            plan.setCourseGroupId(group.getId());
            plan.setClassNum(o.getClassNum());
            plan.setPlan(o.getPlan());
            plan.setCreatedTime(now);
            planList.add(plan);
        });
        courseScheduleService.getDao().insertBatch(courseList);
        coursePlanService.getDao().insertBatch(planList);

        //课程组完成删除缓存
        cacheTime.delete();
    }

    /**
     * 创建课时写到缓存锁定时间
     */
    public List<CourseTimeEntity> lockCourseToCache(CheckCourseTimeDto dto) {
        //获取课程类型
        CourseScheduleEnum courseType = CourseScheduleEnum.existCourseType(dto.getCourseType(), "课程类型不正确！");
        //先自校验传入时间是否交集
        List<CourseTimeEntity> timeList = dto.getTimeList();
        if (timeList.size() > 1) {
            for (int i = 0; i < timeList.size(); i++) {
                if (i == timeList.size() - 1) {
                    break;
                }
                CourseTimeEntity o = timeList.get(i);
                List<CourseTimeEntity> newList = timeList.subList(i + 1, timeList.size());
                boolean checkParamTime = courseScheduleService.checkCourseTime(newList, CourseTimeEntity::getStartTime, CourseTimeEntity::getEndTime, o.getStartTime(), o.getEndTime());
                if (checkParamTime) {
                    throw new BizException(DateUtil.dateToString(o.getStartTime(), "yyyy年MM月dd号 HH点mm分") + "的课程时间重复!");
                }
            }
        }
        //批量检查老师课时在数据库是否重复
        batchCheckTeacherCourseTime(dto.getTeacherId(), timeList, CourseTimeEntity::getStartTime, CourseTimeEntity::getEndTime);
        //获取老师锁课缓存并添加课时数据
        RMap<Long, List<CourseTimeEntity>> map = getExpireLiveLockTimeCache(dto.getTeacherId());
        map.fastPut(dto.getTeacherId(), timeList);

        //需要自动补全课时
        if (dto.getLoop() == 1) {
            //自动排课，获取排课后所有的课程时间
            List<CourseTimeEntity> allCourseTime = autoPlanningLiveCourseTime(dto.getTeacherId(), dto.getCourseNum(), timeList, courseType);
            allCourseTime.sort(Comparator.comparing(CourseTimeEntity::getStartTime));
            //替换掉原有的课时
            dto.setTimeList(allCourseTime);
            //将自动排课后的课时写入缓存覆盖原有的
            map.fastPut(dto.getTeacherId(), dto.getTimeList());
        }
        return dto.getTimeList();
    }

    /**
     * 批量检查老师课时在数据库是否重复
     *
     * @param teacherId 老师id
     * @param timeList  时间集合
     */
    private <T> void batchCheckTeacherCourseTime(Long teacherId, List<T> timeList, Function<T, Date> startTimeFun, Function<T, Date> endTimeFun) {
        //再校验数据库中课程时间和传入时间是否有交集
        timeList.forEach(o -> {
            boolean checkDataTime = courseScheduleService.checkTeacherCourseTime(teacherId, startTimeFun.apply(o), endTimeFun.apply(o));
            if (checkDataTime) {
                throw new BizException("预计安排在" + DateUtil.dateToString(startTimeFun.apply(o), "yyyy年MM月dd号 HH点mm分") + "的课程已被学员选择!");
            }
        });
    }

    /**
     * 批量检查学生课时在数据库是否重复
     *
     * @param studentId 老师id
     * @param timeList  时间集合
     */
    private <T> void batchCheckStudentCourseTime(Long studentId, List<T> timeList, Function<T, Date> startTimeFun, Function<T, Date> endTimeFun) {
        //再校验数据库中课程时间和传入时间是否有交集
        timeList.forEach(o -> {
            boolean checkDataTime = courseScheduleService.checkStudentCourseTime(studentId, startTimeFun.apply(o), endTimeFun.apply(o));
            if (checkDataTime) {
                throw new BizException("预计安排在" + DateUtil.dateToString(startTimeFun.apply(o), "yyyy年MM月dd号 HH点mm分") + "的课程时间存在冲突!");
            }
        });
    }

    /**
     * 自动排课
     * <p>自动排课规则及场景：总5节课，填入2节，需要自动补3节
     * <p>1.把前面2节课的时间循环+1周直到填满5节课为止
     * <p>2.如果自动排课时的时间和未来课程时间有冲突则继续往后面延续一周
     *
     * @param teacherId      老师id
     * @param totalCourseNum 总课程数量
     * @param paramTimeList  当前课程的时间段
     * @param courseType     PRACTICE陪练课  LIVE直播课
     * @return 自动排课后的全部课时
     */
    private List<CourseTimeEntity> autoPlanningLiveCourseTime(Long teacherId, int totalCourseNum, List<CourseTimeEntity> paramTimeList, CourseScheduleEnum courseType) {
        //是否跳过节假日
        boolean skipHoliday = false;
        //true 陪练课
        boolean courseTypeFlag = courseType.equals(CourseScheduleEnum.PRACTICE);
        if (courseTypeFlag) {
            //查询老师陪练课设置
            TeacherFreeTime teacherTime = teacherFreeTimeService.getOne(Wrappers.<TeacherFreeTime>lambdaQuery()
                    .eq(TeacherFreeTime::getDefaultFlag, true)
                    .eq(TeacherFreeTime::getTeacherId, teacherId)
            );
            Optional.ofNullable(teacherTime).orElseThrow(() -> new BizException("未查询到老师陪练课设置！"));
            skipHoliday = teacherTime.getSkipHolidayFlag();
        }

        //获取当前课程
        int nowCourseNum = paramTimeList.size();
        //获取总课程数量 - 获取当前选择的课程数量 = 要自动排课的课程数量
        int diffCourse = totalCourseNum - nowCourseNum;
        //获取课程时间，并按开始时间排序
        List<CourseTimeEntity> resultCourseTime = paramTimeList.stream()
                .sorted(Comparator.comparing(CourseTimeEntity::getStartTime))
                .collect(Collectors.toList());
        //获取最大排课周
        String maxWeekStr = sysConfigService.findConfigValue(SysConfigConstant.AUTO_PLANNING_COURSE_MAX_WEEK);
        int maxWeek = 26;//自动生成课时最大的生成周数  默认 26周
        if (StringUtils.isBlank(maxWeekStr)) {
            maxWeek = Integer.parseInt(maxWeekStr);
        }
        //从第index课程开始生成时间
        int index = 0;
        //节假日缓存数据，避免多次查询
        Map<Integer, List<String>> holidayMap = new HashMap<>();
        //开始自动补全课时
        for (int i = 0; i < diffCourse; i++) {
            //需要增加的周数
            AtomicInteger week = new AtomicInteger(1);
            //是否要一直生成课程 true：一直增加周数并生成到不冲突的时间为止
            AtomicBoolean flag = new AtomicBoolean(true);
            //生成课程
            while (flag.get()) {
                if (index == nowCourseNum) {
                    index = 0;
                    //进入新的循环周数+1
                    week.getAndIncrement();
                }
                CourseTimeEntity timeDto = resultCourseTime.get(index);
                if (week.get() > maxWeek) {
                    throw new BizException("系统自动排课时发现当前时间往后" + maxWeek + "周的课程时间已排满，请手动排课！");
                }
                Date autoStartDate = DateUtil.addWeeks(timeDto.getStartTime(), week.get());
                Date autoEndDate = DateUtil.addWeeks(timeDto.getEndTime(), week.get());

                //传入 ->true ：周数+1   ->false：停止生成时间
                Consumer<Boolean> con = (check) -> {
                    //check = true ,有交集延续1周后继续生成时间
                    if (check) {
                        week.getAndIncrement();
                        flag.set(true);
                    } else {
                        flag.set(false);
                    }
                };

                //是否跳过节假日
                if (skipHoliday) {
                    //将自动生成的开始日期转换为字符串
                    String dateStr = DateUtil.dateToString(autoStartDate);
                    //取年份
                    int calendarYear = LocalDate.parse(dateStr).getYear();
                    //获取节假日信息
                    List<String> holiday = Optional.of(calendarYear)
                            .map(holidayMap::get)
                            .filter(CollectionUtils::isNotEmpty)
                            .orElseGet(() -> {
                                List<String> list = courseScheduleService.getHoliday(calendarYear);
                                holidayMap.put(calendarYear, list);
                                return list;
                            });

                    //如果生成的日期是节假日 则跳过当前日期
                    con.accept(holiday.contains(dateStr));
                    if (flag.get()) {
                        continue;
                    }
                }

                boolean checkTime;
                //若:传入时间是1号10点和8号10点，然后1号10点自动生成的课时是8号10点那么就和传入的8号10点冲突了，这种情况需要继续往后延续1周
                checkTime = courseScheduleService.checkCourseTime(resultCourseTime, CourseTimeEntity::getStartTime, CourseTimeEntity::getEndTime, autoStartDate, autoEndDate);
                con.accept(checkTime);
                //如果和传入时间冲突则跳过
                if (flag.get()) {
                    continue;
                }

                //校验当前生成时间是否和未来的课程时间是否交集
                checkTime = courseScheduleService.checkTeacherCourseTime(teacherId, autoStartDate, autoEndDate);
                con.accept(checkTime);
                //如果和未来时间冲突则跳过
                if (flag.get()) {
                    continue;
                }

                //将自动生成时间收入集合
                CourseTimeEntity autoTimeDto = new CourseTimeEntity();
                autoTimeDto.setStartTime(autoStartDate);
                autoTimeDto.setEndTime(autoEndDate);
                resultCourseTime.add(autoTimeDto);
            }
            index++;
        }
        return resultCourseTime;
    }

    /**
     * 创建直播课程组-解除锁定课程时间-删除写到缓存当作锁定的课时
     *
     * @param teacherId 老师id
     */
    @Override
    public void unlockCourseToCache(Long teacherId) {
        Optional.ofNullable(teacherId)
                .ifPresent(id -> getLiveLockTimeCache(id).delete());
    }

    /**
     * 获取老师锁定课时数据的缓存-设置过期时间
     *
     * @param teacherId 老师id
     * @return 缓存
     */
    private RMap<Long, List<CourseTimeEntity>> getExpireLiveLockTimeCache(Long teacherId) {
        String lockMinuteStr = sysConfigService.findConfigValue(SysConfigConstant.CREATE_LIVE_TIME_LOCK_MINUTE);
        long lockMinute = StringUtils.isBlank(lockMinuteStr) ? 15L : Long.parseLong(lockMinuteStr);
        RMap<Long, List<CourseTimeEntity>> cache = getLiveLockTimeCache(teacherId);
        cache.expire(lockMinute, TimeUnit.MINUTES);
        return cache;
    }

    /**
     * 获取老师锁定的直播课时数据的缓存
     *
     * @param teacherId 老师id
     * @return 缓存 key teacherId value List<CourseTimeEntity>
     */
    public RMap<Long, List<CourseTimeEntity>> getLiveLockTimeCache(Long teacherId) {
        String key = CourseConstant.LOCK_COURSE_TIME_INFO.replace(CourseConstant.TEACHER_ID, teacherId.toString());
        return redissonClient.getMap(key);
    }

    @Override
    public IPage<LiveCourseGroupVo> selectAdminLivePage(IPage<LiveCourseGroupVo> page, LiveCourseGroupSearch query) {

        IPage<LiveCourseGroupVo> liveCourseGroupVoIPage = baseMapper.selectAdminLivePage(page, query);
        List<LiveCourseGroupVo> records = liveCourseGroupVoIPage.getRecords();

        if (CollectionUtils.isEmpty(records)) {
            return liveCourseGroupVoIPage;
        }
        List<Long> courseGroupIdList = records.stream()
                .map(LiveCourseGroupVo::getCourseGroupId)
                .collect(Collectors.toList());

        // 获取课程组声部
        List<LiveCourseGroupVo> subjectList = baseMapper.selectCourseGroupSubject(courseGroupIdList);
        for (int i = 0; i < records.size(); i++) {
            records.get(i).setSubjectName(subjectList.get(i).getSubjectName());
        }

        return liveCourseGroupVoIPage;
    }

    @Override
    public IPage<LiveCourseGroupStudentVo> selectAdminLiveStudentPage(IPage<LiveCourseGroupStudentVo> page,
                                                                      LiveCourseGroupStudentSearch query) {
        return baseMapper.selectAdminLiveStudentPage(page, query);
    }

    @Override
    public List<LiveCourseGroupPlanVo> selectAdminLivePlan(Long courseGroupId) {
        return baseMapper.selectAdminLivePlan(courseGroupId);
    }

    @Override
    public LiveCourseGroupVo detail(Long courseGroupId) {
        return baseMapper.selectAdminLiveCourseGroup(courseGroupId);

    }

    @Override
    public IPage<LiveCourseGroupStudentCourseVo> selectAdminLiveStudentCoursePage(IPage<LiveCourseGroupStudentCourseVo> page, LiveCourseGroupStudentCourseSearch query) {
        IPage<LiveCourseGroupStudentCourseVo> studentCourseVoIPage = baseMapper.selectAdminLiveStudentCoursePage(
                page, query);
        List<LiveCourseGroupStudentCourseVo> records = studentCourseVoIPage.getRecords();
        // 设置学生上课状态
        for (LiveCourseGroupStudentCourseVo record : records) {
            if (CourseScheduleEnum.NOT_START.getCode().equals(record.getCourseStatue().getCode())) {
                record.setStatus(StudentCourseEnum.NOTSTART);
            } else {
                if (record.getStudentAttendanceId() != null) {
                    record.setStatus(StudentCourseEnum.ATTENDCLASS);
                } else {
                    record.setStatus(StudentCourseEnum.TRUANT);
                }
            }
        }
        return studentCourseVoIPage;
    }

    /**
     * 学生购买直播课程组
     *
     * @param orderReqInfo 订单信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public HttpResponseResult<OrderCreateRes> buyLiveCourse(OrderReq.OrderReqInfo orderReqInfo) {
        log.info("buyLiveCourse  param:{}", JSON.toJSONString(orderReqInfo));
        Map<String, Object> param = WrapperUtil.toMap(orderReqInfo.getBizContent());
        Long groupId = WrapperUtil.toLong(param, "groupId", "课程组id不能为空！");
        Long studentId = orderReqInfo.getUserId();
        //校验学生信息
        getSysUser(studentId);
        //课程组信息
        CourseGroup courseGroup = this.getOne(Wrappers.<CourseGroup>lambdaQuery()
                .eq(CourseGroup::getId, groupId)
                .eq(CourseGroup::getType, CourseScheduleEnum.LIVE.getCode())
        );
        if (Objects.isNull(courseGroup)) {
            throw new BizException("课程组不存在！");
        }
        //课程信息
        List<CourseSchedule> courseList = courseScheduleService.list(Wrappers.<CourseSchedule>lambdaQuery()
                .eq(CourseSchedule::getCourseGroupId, groupId));
        if (CollectionUtils.isEmpty(courseList)) {
            throw new BizException("课程组课程不存在！");
        }
        //校验购买的课程组每节课时间是否和自己的课时冲突
        batchCheckStudentCourseTime(studentId, courseList, CourseSchedule::getStartTime, CourseSchedule::getEndTime);
        //写入学生购买课程记录
        buyLiveCourseAfter(orderReqInfo.getOrderNo(), studentId, courseList, courseGroup);

        OrderCreateRes orderCreateRes = new OrderCreateRes();
        orderCreateRes.setRes(true);
        orderCreateRes.setMerchId(courseGroup.getTeacherId());
        orderCreateRes.setBizId(courseGroup.getId());
        orderCreateRes.setOriginalPrice(courseGroup.getCoursePrice());
        orderCreateRes.setExpectPrice(courseGroup.getCoursePrice());
        orderCreateRes.setGoodNum(courseGroup.getCourseNum());
        orderCreateRes.setGoodType(GoodTypeEnum.LIVE);

        HttpResponseResult<OrderCreateRes> httpResponseResult = new HttpResponseResult<>();
        httpResponseResult.setData(orderCreateRes);
        log.info("buyLiveCourse  return {}", httpResponseResult);
        return httpResponseResult;
    }

    /**
     * 待订单创建完毕后继续后续操作
     *
     * @param orderNo     订单号
     * @param courseList  课程列表
     * @param studentId   学员id
     * @param courseGroup 直播课程组信息
     */
    private void buyLiveCourseAfter(String orderNo, Long studentId, List<CourseSchedule> courseList, CourseGroup courseGroup) {
        log.info("buyLiveCourse buyLiveCourseAfter -> order {} studentId {} courseList {} courseGroup {}", orderNo, studentId, JSON.toJSONString(courseList), JSON.toJSONString(courseGroup));
        //获取每节课购买的价格
        Map<Integer, BigDecimal> courseAveragePrice = WrapperUtil.getAveragePrice(courseGroup.getCourseNum(), courseGroup.getCoursePrice());
        //写course_schedule_student_payment表 作为记录锁定时间用，防止重复购买，如果支付失败则删除该数据
        List<CourseScheduleStudentPayment> studentPaymentList = new ArrayList<>();
        Date now = new Date();
        AtomicInteger i = new AtomicInteger(1);
        courseList.forEach(course -> {
            CourseScheduleStudentPayment studentPayment = new CourseScheduleStudentPayment();
            studentPayment.setUserId(studentId);
            studentPayment.setCourseGroupId(courseGroup.getId());
            studentPayment.setCourseId(course.getId());
            studentPayment.setOrderNo(orderNo);
            BigDecimal amount = courseAveragePrice.get(i.getAndIncrement());
            studentPayment.setOriginalPrice(amount);
            studentPayment.setExpectPrice(amount);
            studentPayment.setActualPrice(amount);
            studentPayment.setCreatedTime(now);
            studentPayment.setUpdatedTime(now);
            studentPayment.setCourseType(courseGroup.getType());
            studentPaymentList.add(studentPayment);
        });
        courseScheduleStudentPaymentService.getDao().insertBatch(studentPaymentList);
    }

    /**
     * 学生购买直播课程-成功-回调
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void buyLiveCourseSuccess(UserOrderDetailVo orderParam) {
        log.info("buyLiveCourseSuccess param :{}", JSON.toJSONString(orderParam));
        String orderNo = orderParam.getOrderNo();
        //更新课程组的购买人数+1
        CourseScheduleStudentPayment studentPayment = courseScheduleStudentPaymentService.getOne(Wrappers.<CourseScheduleStudentPayment>lambdaQuery()
                .eq(CourseScheduleStudentPayment::getOrderNo, orderNo)
        );
        if (Objects.isNull(studentPayment)) {
            throw new BizException("订单不存在！");
        }
        //更新课程组的购买人数+1
        this.baseMapper.opsPreStudentNum(studentPayment.getCourseGroupId(), 1);
        //课程组信息
        CourseGroup courseGroup = this.getOne(Wrappers.<CourseGroup>lambdaQuery()
                .eq(CourseGroup::getId, studentPayment.getCourseGroupId()));
        //课程信息
        List<CourseSchedule> courseList = courseScheduleService.list(Wrappers.<CourseSchedule>lambdaQuery()
                .eq(CourseSchedule::getCourseGroupId, studentPayment.getCourseGroupId()));
        //计算课程组单节课价格 取CourseScheduleStudentPayment expectPrice
        BigDecimal expectPrice = studentPayment.getExpectPrice();
        //查询直播课服务费
        String liveServiceRateStr = sysConfigService.findConfigValue(SysConfigConstant.LIVE_SERVICE_RATE);
        BigDecimal liveServiceRate = new BigDecimal(liveServiceRateStr).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
        //总课酬  1 - (1 * 手续费率)
        BigDecimal totalRatePrice = expectPrice.subtract(expectPrice.multiply(liveServiceRate)).setScale(2, RoundingMode.HALF_UP);
        //获取每节课的课酬 key 课堂数  value 课酬
        Map<Integer, BigDecimal> singerCourseSalary = WrapperUtil.getAveragePrice(courseGroup.getCourseNum(), totalRatePrice);
        //写入课酬表计算-根据课程组总金额计算分配到每节课的金额
        Date now = new Date();
        List<CourseScheduleTeacherSalary> teacherSalaryList = new ArrayList<>();
        courseList.forEach(course -> {
            CourseScheduleTeacherSalary teacherSalary = new CourseScheduleTeacherSalary();
            teacherSalary.setTeacherId(course.getTeacherId());
            teacherSalary.setStudentId(studentPayment.getUserId());
            teacherSalary.setCourseScheduleId(course.getId());
            teacherSalary.setCourseGroupType(course.getType());
            teacherSalary.setCourseGroupId(course.getCourseGroupId());
            teacherSalary.setClassNum(course.getClassNum());
            teacherSalary.setExpectSalary(singerCourseSalary.get(course.getClassNum()));
            teacherSalary.setReduceSalary(BigDecimal.ZERO);
            teacherSalary.setCreateTime(now);
            teacherSalary.setStatus(TeacherSalaryEnum.NOT_START.getCode());
            teacherSalaryList.add(teacherSalary);
        });
        courseScheduleTeacherSalaryService.getDao().insertBatch(teacherSalaryList);
        log.info("buyLiveCourseSuccess ok");
    }

    @Override
    public void buyLiveCourseCancel(UserOrderDetailVo orderParam) {
        courseScheduleStudentPaymentService.remove(Wrappers.<CourseScheduleStudentPayment>lambdaQuery()
                .eq(CourseScheduleStudentPayment::getOrderNo, orderParam.getOrderNo()));
    }

    private SysUser getSysUser(Long userId) {
        return Optional.ofNullable(userId)
                .map(sysUserFeignService::queryUserById)
                .orElseThrow(() -> new BizException("用户不存在"));
    }

    private SysUser getSysUser() {
        return Optional.ofNullable(sysUserFeignService.queryUserInfo())
                .orElseThrow(() -> new BizException("用户不存在"));
    }

    /**
     * <p>定时将符合开售日期的未开售的直播课课程组修改为报名中状态-每隔10分钟执行一次
     * <p>定时将符合结束售卖日期的在售卖中的直播课课程组修改为取消或者成课状态状态-每隔10分钟执行一次
     */
    public void opsCourseGroup() {
        openCourseGroup();
        closeCourseGroup();
    }

    /**
     * 定时将符合开售日期的未开售的直播课课程组修改为报名中状态
     */
    private void openCourseGroup() {
        //查询今天未开售的课程组
        List<CourseGroup> courseGroupList = this.list(Wrappers.<CourseGroup>lambdaQuery()
                .eq(CourseGroup::getType, CourseScheduleEnum.LIVE.getCode())
                .eq(CourseGroup::getSalesStartDate, LocalDate.now())
                .eq(CourseGroup::getStatus, CourseGroupEnum.NOT_SALE.getCode()));
        if (CollectionUtils.isEmpty(courseGroupList)) {
            return;
        }
        //修改为报名中状态
        courseGroupList.forEach(courseGroup -> {
            courseGroup.setStatus(CourseGroupEnum.APPLY.getCode());
            try {
                this.updateById(courseGroup);
            } catch (Exception ignored) {
            }
        });
    }

    /**
     * 定时将符合结束售卖日期的在售卖中的直播课课程组修改为取消或者成课状态状态
     */
    private void closeCourseGroup() {
        //查询昨天要结束售卖的课程组
        List<CourseGroup> courseGroupList = this.list(Wrappers.<CourseGroup>lambdaQuery()
                .eq(CourseGroup::getType, CourseScheduleEnum.LIVE.getCode())
                .eq(CourseGroup::getSalesEndDate, LocalDate.now().plusDays(-1))
                .eq(CourseGroup::getStatus, CourseGroupEnum.APPLY.getCode()));
        if (CollectionUtils.isEmpty(courseGroupList)) {
            return;
        }
        courseGroupList.forEach(courseGroup -> {
            if (courseGroup.getPreStudentNum() >= courseGroup.getMixStudentNum()) {
                //人数达标则修改课程组为进行中状态
                courseGroup.setStatus(CourseGroupEnum.ING.getCode());
            } else {
                //人数未达标则修改课程组为取消状态
                courseGroup.setStatus(CourseGroupEnum.CANCEL.getCode());
            }
            try {
                this.updateById(courseGroup);
            } catch (Exception ignored) {
            }
        });
    }

}

