package org.jeecg.modules.system.app.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONObject;
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.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.app.form.ClassPostponeForm;
import org.jeecg.modules.app.form.TemporaryCourseForm;
import org.jeecg.modules.app.vo.AppCoursesPageVO;
import org.jeecg.modules.app.vo.CoursesPriceRulesVO;
import org.jeecg.modules.app.vo.FamilyUserVO;
import org.jeecg.modules.app.wxNotification.WxNotificationService;
import org.jeecg.modules.quartz.vo.JobExtendedClassNoticeVo;
import org.jeecg.modules.system.app.dto.*;
import org.jeecg.modules.system.app.entity.*;
import org.jeecg.modules.system.app.mapper.*;
import org.jeecg.modules.system.app.service.IAppCoureseService;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.mapper.SysDepartMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static org.jeecg.common.constant.CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
import static org.jeecg.modules.hikiot.HikiotTool.addFace;
import static org.jeecg.modules.hikiot.HikiotTool.addUser;

/**
 * @Description: 课程/培训表
 * @Author: jeecg-boot
 * @Date: 2025-07-03
 * @Version: V1.0
 */
@Service
public class AppCoureseServiceImpl extends ServiceImpl<AppCoursesMapper, AppCourses> implements IAppCoureseService {
    @Resource
    private AppCoursesPriceRulesMapper priceRulesMapper;
    @Resource
    private AppSiteMapper siteMapper;
    @Resource
    private AppCategoryMapper categoryMapper;
    @Resource
    private AppCoursesVerificationRecordMapper appCoursesVerificationRecordMapper;
    @Resource
    private FamilyMembersMapper familyMembersMapper;
    @Resource
    private AppOrderMapper appOrderMapper;
    @Autowired
    WxNotificationService wxNotificationService;
    @Resource
    private AppCoursesMapper appCoursesMapper;
    @Resource
    private AppDeviceMapper appDeviceMapper;
    @Resource
    private SysDepartMapper sysDepartMapper;
    @Resource
    private SeparateAccountsMapper separateAccountsMapper;
    @Resource
    private AppClassGroupingMapper appClassGroupingMapper;
    @Resource
    private AppClassUserMapper appClassUserMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveWitchPriceRules(AppCoursesDTO appCoursesDTO) {
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        SysDepart depart = sysDepartMapper.selectOne(Wrappers.lambdaQuery(SysDepart.class).eq(SysDepart::getOrgCode, loginUser.getOrgCode()).last("limit 1"));
        String deptId = depart.getId();
        if(depart.getSystemType() == 2){
            deptId = depart.getParentId();
        }
        if (depart.getSystemType() != 0) {
            SeparateAccounts separateAccounts = separateAccountsMapper.selectOne(Wrappers.lambdaQuery(SeparateAccounts.class).eq(SeparateAccounts::getDeptId, deptId));
            if (ObjectUtil.isEmpty(separateAccounts)) {
                throw new JeecgBootException("商户分账比例信息未配置！");
            }
        }
        AppCoursesCuDTO coursesCuDTO = appCoursesDTO.getCourses();
        if (null == coursesCuDTO.getCategoryId()) throw new JeecgBootException("请选择课程类别");
        AppSite site = siteMapper.selectOne(Wrappers.<AppSite>lambdaQuery().eq(AppSite::getOrgCode, coursesCuDTO.getOrgCode()));
        AppCourses appCourses = new AppCourses();
        BeanUtils.copyProperties(coursesCuDTO, appCourses);
        if (ObjUtil.isNotEmpty(site)){
            appCourses.setOrgCode(site.getOrgCode());
            appCourses.setTenantId(site.getTenantId());
            appCourses.setSiteId(site.getId());
        }
        if (appCoursesDTO.getCourses().getSellingPrice() == null ||
                appCoursesDTO.getCourses().getSellingPrice().compareTo(BigDecimal.ZERO) == 0) {
            appCourses.setPriceType(1);
        }else {
            appCourses.setPriceType(0);
        }

        if (ObjUtil.isEmpty(appCourses.getOriginalPrice())){
            appCourses.setOriginalPrice(appCourses.getSellingPrice());
        }

        appCourses.setUserId(coursesCuDTO.getUserId());
        appCourses.setAddressSiteId(coursesCuDTO.getAddressSiteId());
        List<AppCoursesRuleDTO> coursesRuleDTOS = appCoursesDTO.getPriceRulesList();
        //起止时间计算
        Optional<Date> minStartTime = coursesRuleDTOS.stream()
                .map(AppCoursesRuleDTO::getStartTime)
                .filter(Objects::nonNull)
                .min(Date::compareTo);

        Optional<Date> maxEndTime = coursesRuleDTOS.stream()
                .map(AppCoursesRuleDTO::getEndTime)
                .filter(Objects::nonNull)
                .max(Date::compareTo);
        appCourses.setStartTime(minStartTime.orElse(null));
        appCourses.setEndTime(maxEndTime.orElse(null));
        appCourses.setClassNum(coursesRuleDTOS.size());
        int saveCourseResult = baseMapper.insert(appCourses);
        if (saveCourseResult < 1) {
            throw new JeecgBootException("课程信息保存失败");
        }
        for (AppCoursesRuleDTO rule : coursesRuleDTOS) {
            AppCoursesPriceRules appCoursesPriceRules = new AppCoursesPriceRules();
            BeanUtils.copyProperties(rule, appCoursesPriceRules);
            appCoursesPriceRules.setCoursesId(appCourses.getId());
            appCoursesPriceRules.setOrgCode(appCourses.getOrgCode());
            appCoursesPriceRules.setTenantId(appCourses.getTenantId());
            int savePriceResult = priceRulesMapper.insert(appCoursesPriceRules); // 保证每个操作都在事务中
            if (savePriceResult < 1) {
                throw new JeecgBootException("课程价格规则保存失败");
            }
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editWitchPriceRules(AppCoursesDTO appCoursesDTO) {
        AppCoursesCuDTO coursesCuDTO = appCoursesDTO.getCourses();
        if (null == coursesCuDTO.getCategoryId()) throw new JeecgBootException("请选择课程类别");
        if(null == appCoursesDTO.getCourses().getOriginalPrice()) appCoursesDTO.getCourses().setOriginalPrice(BigDecimal.ZERO);
        AppCourses appCourses = baseMapper.selectById(coursesCuDTO.getId());
        BeanUtils.copyProperties(coursesCuDTO, appCourses);
        appCourses.setAddressSiteId(coursesCuDTO.getAddressSiteId());
        List<AppCoursesRuleDTO> coursesRuleDTOS = appCoursesDTO.getPriceRulesList();
        //起止时间计算
        Optional<Date> minStartTime = coursesRuleDTOS.stream()
                .map(AppCoursesRuleDTO::getStartTime)
                .filter(Objects::nonNull)
                .min(Date::compareTo);

        Optional<Date> maxEndTime = coursesRuleDTOS.stream()
                .map(AppCoursesRuleDTO::getEndTime)
                .filter(Objects::nonNull)
                .max(Date::compareTo);
        appCourses.setStartTime(minStartTime.orElse(null));
        appCourses.setEndTime(maxEndTime.orElse(null));
        appCourses.setClassNum(coursesRuleDTOS.size());
        if (appCoursesDTO.getCourses().getSellingPrice() == null ||
                appCoursesDTO.getCourses().getSellingPrice().compareTo(BigDecimal.ZERO) == 0) {
            appCourses.setPriceType(1);
        }else {
            appCourses.setPriceType(0);
        }
        int updateResult = baseMapper.updateById(appCourses);
        if (updateResult < 1) {
            throw new JeecgBootException("课程信息保存失败");
        }

        //查历史本地数据
        List<AppCoursesPriceRules> coursesPriceRules = priceRulesMapper.selectList(Wrappers.lambdaQuery(AppCoursesPriceRules.class).eq(AppCoursesPriceRules::getCoursesId, appCourses.getId()));
        List<String> localList = coursesPriceRules.stream().map(AppCoursesPriceRules::getId).collect(Collectors.toList());
        //表单数据
        List<String> formList = coursesRuleDTOS.stream().filter(e -> ObjectUtils.isNotEmpty(e.getId())).map(AppCoursesRuleDTO::getId).collect(Collectors.toList());
        //有ID的做更新
        coursesRuleDTOS.stream().filter(e -> ObjectUtils.isNotEmpty(e.getId())).forEach(coursesRule -> {
            AppCoursesPriceRules appCoursesPriceRules = new AppCoursesPriceRules();
            BeanUtils.copyProperties(coursesRule, appCoursesPriceRules);
            priceRulesMapper.updateById(appCoursesPriceRules);
        });
        //差集直接做删除
        List<String> differ = getDifferListByMap(formList, localList);
        //修改状态
        for (String id : differ) {
            AppCoursesPriceRules appCoursesPriceRules = priceRulesMapper.selectById(id);
            if (ObjectUtils.isNotEmpty(appCoursesPriceRules)) {
//                appCoursesPriceRules.setStatus(CommonConstant.STATUS_1_INT);
//                priceRulesMapper.updateById(appCoursesPriceRules);
                priceRulesMapper.deleteById(id);
            }
        }
        //id为空做新增
        List<AppCoursesRuleDTO> nullList = coursesRuleDTOS.stream().filter(e -> ObjectUtils.isEmpty(e.getId())).collect(Collectors.toList());
        for (AppCoursesRuleDTO rule : nullList) {
            //做新增
            AppCoursesPriceRules appCoursesPriceRules = new AppCoursesPriceRules();
            BeanUtils.copyProperties(rule, appCoursesPriceRules);
            appCoursesPriceRules.setCoursesId(appCourses.getId());
            appCoursesPriceRules.setOrgCode(appCourses.getOrgCode());
            appCoursesPriceRules.setTenantId(appCourses.getTenantId());
            int savePriceResult = priceRulesMapper.insert(appCoursesPriceRules); // 保证每个操作都在事务中
            if (savePriceResult < 1) {
                throw new JeecgBootException("课程价格规则保存失败");
            }
        }
        return Boolean.TRUE;
    }


    public static List<String> getDifferListByMap(List<String> listA, List<String> listB) {
        List<String> differList = new ArrayList<>();
        Map<String, Integer> map = new HashMap<>();
        for (String strA : listA) {
            map.put(strA, 1);
        }
        for (String strB : listB) {
            Integer value = map.get(strB);
            if (value != null) {
                map.put(strB, ++value);
                continue;
            }
            map.put(strB, 1);
        }
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            if (entry.getValue() == 1) {
                differList.add(entry.getKey());
            }
        }
        return differList;
    }

    @Override
    public AppCoursesDTO queryWitchPriceRulesById(String id) {
        AppCourses dbCourse = baseMapper.selectById(id);
        if (null == dbCourse) throw new JeecgBootException("未找到对应数据", SC_INTERNAL_SERVER_ERROR_500);
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
//        checkPermission(sysUser,dbCourse);
        List<AppCoursesPriceRules> priceRulesList =
                priceRulesMapper.selectList(Wrappers.<AppCoursesPriceRules>lambdaQuery().eq(AppCoursesPriceRules::getCoursesId, id).eq(AppCoursesPriceRules::getCoursesType,0));
        AppCoursesCuDTO coursesCuDTO = new AppCoursesCuDTO();
        BeanUtils.copyProperties(dbCourse, coursesCuDTO);
        List<AppCoursesRuleDTO> priceRules = new ArrayList<>();
        priceRulesList.forEach(rule -> {
            AppCoursesRuleDTO appCoursesRuleDTO = new AppCoursesRuleDTO();
            BeanUtils.copyProperties(rule, appCoursesRuleDTO);
            priceRules.add(appCoursesRuleDTO);
        });

        return new AppCoursesDTO(coursesCuDTO, priceRules);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWitchPriceRulesById(String id) {
        AppCourses dbCourse = baseMapper.selectById(id);
        if (null == dbCourse) throw new JeecgBootException("未找到对应数据", SC_INTERNAL_SERVER_ERROR_500);
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
//        checkPermission(sysUser,dbCourse);
        int deleteCourseResult = baseMapper.deleteById(id);
        if (deleteCourseResult < 1) {
            throw new JeecgBootException("课程信息删除失败", SC_INTERNAL_SERVER_ERROR_500);
        }
        int deletePriceResult = priceRulesMapper.delete(Wrappers.<AppCoursesPriceRules>lambdaQuery().eq(AppCoursesPriceRules::getCoursesId, id));
        if (deletePriceResult < 1) throw new JeecgBootException("课程价格规则删除失败", SC_INTERNAL_SERVER_ERROR_500);
        return Boolean.TRUE;
    }

    @Override
    public IPage<AppCoursesInfoPageDTO> queryWitchPriceRulesPage(AppCoursesPageDTO dto) {
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        dto.setOrgCode(loginUser.getOrgCode());
        Page<AppCourses> page = new Page<>(dto.getPageNo(), dto.getPageSize());
        IPage<AppCoursesInfoPageDTO> resultPage = baseMapper.queryWitchPriceRulesPage(page, dto);

        resultPage.getRecords().forEach(r -> {
            String[] split = r.getCategoryId().split(",");
            List<String> ids = Arrays.stream(split).collect(Collectors.toList());
            r.setIfThereIsAnOrder(appOrderMapper.selectCount(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getProductIds, r.getId())) > 0);
            Long count = priceRulesMapper.selectCount(Wrappers.<AppCoursesPriceRules>lambdaQuery().eq(AppCoursesPriceRules::getCoursesId, r.getId()).eq(AppCoursesPriceRules::getCoursesType,0));

            r.setTotalNum(Math.toIntExact(count));

            StringJoiner stringJoiner = new StringJoiner(",");

            for (String id : ids) {
                AppCategory category = categoryMapper.selectById(id);
                stringJoiner.add(category.getName());
            }
            r.setCategoryIds(stringJoiner.toString());
        });
        return resultPage;
    }

    @Override
    public List<AppSiteAddressDTO> queryAddressList() {
        List<AppSite> appSites = siteMapper.selectList(Wrappers.<AppSite>lambdaQuery().select(AppSite::getId, AppSite::getAddress));
        List<AppSiteAddressDTO> dtoList = Optional.ofNullable(appSites.stream().map(e -> {
            AppSiteAddressDTO appSiteAddressDTO = new AppSiteAddressDTO();
            appSiteAddressDTO.setSiteId(e.getId());
            appSiteAddressDTO.setAddress(e.getAddress());
            return appSiteAddressDTO;
        }).collect(Collectors.toList())).orElse(List.of());
        return dtoList;
    }

    /** 自定义去重工具方法
     * 通过 Predicate结合 ConcurrentHashMap记录已出现的字段值，动态过滤重复项
     * @param keyExtractor
     * @return
     * @param <T>
     */
    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t -> seen.add(keyExtractor.apply(t));
    }

    @Override
    public List<CoursesPriceRulesVO> getCourseInfo(String courseId) {


        List<CoursesPriceRulesVO> verifyCourseInfoDTOList = new ArrayList<>();

        List<AppCoursesPriceRules> priceRulesList = priceRulesMapper.selectList(Wrappers.lambdaQuery(AppCoursesPriceRules.class)
                .eq(AppCoursesPriceRules::getCoursesId, courseId));

//        Map<Integer, List<AppCoursesPriceRules>> map = priceRulesList.stream().collect(Collectors.groupingBy(AppCoursesPriceRules::getCoursesType));

        for (AppCoursesPriceRules priceRules : priceRulesList) {
            CoursesPriceRulesVO coursesPriceRulesVO = new CoursesPriceRulesVO();
            coursesPriceRulesVO.setId(priceRules.getId());
            coursesPriceRulesVO.setCoursesId(priceRules.getCoursesId());
            coursesPriceRulesVO.setName(priceRules.getName());
            coursesPriceRulesVO.setStartTime(priceRules.getStartTime());
            coursesPriceRulesVO.setEndTime(priceRules.getEndTime());
            coursesPriceRulesVO.setCoursesType(priceRules.getCoursesType());

            List<AppCoursesVerificationRecord> verificationRecordList = appCoursesVerificationRecordMapper.getRecordList(priceRules.getId());
            ArrayList<AppCoursesVerificationRecord> verificationRecords = verificationRecordList.stream()
                    .collect(Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(AppCoursesVerificationRecord::getUseUserId))),
                            ArrayList::new
                    ));
            //共计
            coursesPriceRulesVO.setTotalNum(verificationRecords.size());
            //延期
            long postponeNum = verificationRecords.stream().filter(e -> e.getOrPostpone() == 1).filter(distinctByKey(AppCoursesVerificationRecord::getUseUserId)).count();
            coursesPriceRulesVO.setPostponeNum((int) postponeNum);
            //已核验
            long writtenOffNum = verificationRecords.stream().filter(e -> e.getVerifyStatus() == 1).filter(distinctByKey(AppCoursesVerificationRecord::getUseUserId)).count();
            coursesPriceRulesVO.setWrittenOffNum((int) writtenOffNum);
            //未核验
            long unwrittenOffNum =
                    verificationRecords.stream().filter(e -> e.getVerifyStatus() == 0 && e.getOrPostpone() == 0).filter(distinctByKey(AppCoursesVerificationRecord::getUseUserId)) .count();
            coursesPriceRulesVO.setUnwrittenOffNum((int) unwrittenOffNum);

            //判断是否为当日数据
            LocalDate today = LocalDate.now();
            LocalDate localDate = priceRules.getStartTime().toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate();
            if (coursesPriceRulesVO.getEndTime().after(new Date())) {
                coursesPriceRulesVO.setOrDone(false);
            }else {
                coursesPriceRulesVO.setOrDone(true);
            }
            if (!localDate.isEqual(today)) {
                coursesPriceRulesVO.setOrToday(false);
            } else {
                coursesPriceRulesVO.setOrToday(true);
            }
            verifyCourseInfoDTOList.add(coursesPriceRulesVO);
        }

        return verifyCourseInfoDTOList;
    }

    /**
     * 临时约课
     *
     * @param temporaryCourseForm
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean temporaryCourse(TemporaryCourseForm temporaryCourseForm) {

        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        //如果不是教练
        String roleCode = user.getRoleCode();
        if (!roleCode.contains("instructor")) {
            throw new JeecgBootException("当前用户不是教练，请勿操作!");
        }

        AppCoursesPriceRules appCoursesPriceRules = priceRulesMapper.selectById(temporaryCourseForm.getCoursePriceRulesId());

        for (String userId : temporaryCourseForm.getUserIds()) {
            FamilyMembers familyMembers = familyMembersMapper.selectById(userId);

            if (ObjUtil.isEmpty(familyMembers)) {
                throw new JeecgBootException("当前用户不存在，请确认后重新提交");
            }

            //判断是否已存在
            List<AppCoursesVerificationRecord> verificationRecords =
                    appCoursesVerificationRecordMapper.selectList(Wrappers.<AppCoursesVerificationRecord>lambdaQuery()
                            .eq(AppCoursesVerificationRecord::getCoursesPriceRuleId, appCoursesPriceRules.getId())
                            .eq(AppCoursesVerificationRecord::getUseUserId,userId)
                    );
            if (ObjUtil.isNotEmpty(verificationRecords)) {
                throw new JeecgBootException(familyMembers.getFullName()+"已经在课程中,请确认后重新提交");
            }

            AppCoursesVerificationRecord appCoursesVerificationRecord = new AppCoursesVerificationRecord();
            appCoursesVerificationRecord.setCoursesId(appCoursesPriceRules.getCoursesId());
            appCoursesVerificationRecord.setCoursesPriceRuleId(appCoursesPriceRules.getId());
            appCoursesVerificationRecord.setCoursesName(appCoursesPriceRules.getName());
            appCoursesVerificationRecord.setCoursesStartTime(appCoursesPriceRules.getStartTime());
            appCoursesVerificationRecord.setCoursesEndTime(appCoursesPriceRules.getEndTime());
            appCoursesVerificationRecord.setUseUserId(userId);
            appCoursesVerificationRecord.setUseUserName(familyMembers.getFullName());
            appCoursesVerificationRecord.setVerifyStatus(0);
            appCoursesVerificationRecord.setOrPostpone(0);
            appCoursesVerificationRecord.setOrTemporaryCourse(1);
            appCoursesVerificationRecord.setCoursesType(0);

            int insert = appCoursesVerificationRecordMapper.insert(appCoursesVerificationRecord);
            if (insert < 1) throw new JeecgBootException("临时约课创建失败", SC_INTERNAL_SERVER_ERROR_500);
            //默认加入教练第一个班
            List<AppClassGrouping> appClassGroupings = appClassGroupingMapper.selectList(Wrappers.<AppClassGrouping>lambdaQuery()
                    .eq(AppClassGrouping::getCoachUserId, user.getId())
                    .eq(AppClassGrouping::getCourseId, appCoursesPriceRules.getCoursesId()));
            if(CollUtil.isEmpty(appClassGroupings)){
                throw new JeecgBootException("请先创建课程分班");
            }
            AppClassUser appClassUser = new AppClassUser();
            appClassUser.setClassGroupingId(appClassGroupings.get(0).getId());
            appClassUser.setCourseId(appCoursesPriceRules.getCoursesId());
            appClassUser.setFamilyMemberId(userId);
            appClassUser.setOrTemp(1);
            appClassUserMapper.insert(appClassUser);
        }
        return Boolean.TRUE;
    }

    @Override
    public List<FamilyUserVO> getClassPostponeUsers(String coursePriceRulesId) {

        List<AppCoursesVerificationRecord> verificationRecordList = appCoursesVerificationRecordMapper.selectList(Wrappers.<AppCoursesVerificationRecord>lambdaQuery()
                .eq(AppCoursesVerificationRecord::getCoursesPriceRuleId, coursePriceRulesId)
                        .eq(AppCoursesVerificationRecord::getVerifyStatus, CommonConstant.STATUS_0_INT)
                .eq(AppCoursesVerificationRecord::getOrPostpone, CommonConstant.STATUS_0_INT));
        List<FamilyUserVO> list = verificationRecordList.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(AppCoursesVerificationRecord::getUseUserId))),
                        ArrayList::new
                )).stream().map(e -> {
            FamilyUserVO familyUserVO = new FamilyUserVO();
            familyUserVO.setFamilyUserId(e.getUseUserId()).setFamilyUserName(e.getUseUserName()).setFamilyUserImage(e.getUseUserImage());
            return familyUserVO;
        }).collect(Collectors.toList());

        return list;
    }

    @Override
    public Boolean classPostpone(ClassPostponeForm form) {

//        AppOrder appOrder = appOrderMapper.selectById(form.getOrderId());
        //原课时
        AppCoursesPriceRules coursesPriceRules = priceRulesMapper.selectById(form.getPriceRulesId());
        if (coursesPriceRules==null){
            throw new JeecgBootException("未查询到原课时");
        }
        //补课课时
        AppCoursesPriceRules appCoursesPriceRules = priceRulesMapper.selectById(form.getCoursePriceRulesId());
        List<JobExtendedClassNoticeVo> jobExtendedClassNoticeVos = new ArrayList<>();

        for (FamilyUserVO familyUserVO : form.getFamilyUserVOList()) {

            FamilyMembers familyMembers = familyMembersMapper.selectById(familyUserVO.getFamilyUserId());

            List<AppCoursesVerificationRecord> verificationRecordList = appCoursesVerificationRecordMapper.selectList(Wrappers.<AppCoursesVerificationRecord>lambdaQuery()
                    .eq(AppCoursesVerificationRecord::getCoursesPriceRuleId, form.getPriceRulesId())
                    .eq(AppCoursesVerificationRecord::getUseUserId,familyMembers.getId())
            );
            //查询到的所有记录修改为延期
            for (AppCoursesVerificationRecord appCoursesVerificationRecord : verificationRecordList) {
                appCoursesVerificationRecord
                        .setOrPostpone(CommonConstant.STATUS_1_INT)
                        .setPostponeReason(form.getPostponeReason());
                appCoursesVerificationRecordMapper.updateById(appCoursesVerificationRecord);
            }

            AppCoursesVerificationRecord verificationRecord = new AppCoursesVerificationRecord();
//            verificationRecord.setOrderId(appOrder.getId());
//            verificationRecord.setOrderCode(appOrder.getOrderCode());
            verificationRecord.setCoursesId(appCoursesPriceRules.getCoursesId());
            verificationRecord.setCoursesPriceRuleId(appCoursesPriceRules.getId());
            verificationRecord.setCoursesName(appCoursesPriceRules.getName());
            verificationRecord.setCoursesStartTime(appCoursesPriceRules.getStartTime());
            verificationRecord.setCoursesEndTime(appCoursesPriceRules.getEndTime());
            verificationRecord.setUseUserId(familyUserVO.getFamilyUserId());
            verificationRecord.setUseUserName(familyMembers.getFullName());
            verificationRecord.setUseUserImage(familyMembers.getRealNameImg());
            verificationRecord.setUseUserPhone(familyMembers.getPhone());
            verificationRecord.setVerifyStatus(CommonConstant.STATUS_0_INT);
            verificationRecord.setCoursesType(CommonConstant.STATUS_1_INT);
            if(appCoursesVerificationRecordMapper.insert(verificationRecord) > 0){
                AppSite appSite = siteMapper.selectById(appCoursesMapper.selectById(verificationRecord.getCoursesId()).getAddressSiteId());
                if(appSite.getType() == 0){
                    for (AppDevice appDevice : appDeviceMapper.selectList(Wrappers.<AppDevice>lambdaQuery().eq(AppDevice::getSiteId, appSite.getId()))){
                        if(null != appDevice){
                            JsonObject addUserJson = JsonParser.parseString(addUser(new DateTime(verificationRecord.getCoursesStartTime()),
                                    appDevice.getDeviceSerial(),
                                    verificationRecord.getUseUserName(),
                                    familyMembers.getId(),new DateTime(verificationRecord.getCoursesEndTime()), null)).getAsJsonObject();
                            JsonObject addFaceJson = JsonParser.parseString(addFace(appDevice.getDeviceSerial(), familyMembers.getId(),
                                    familyMembers.getRealNameImg())).getAsJsonObject();
                            if (addUserJson.get("code").getAsInt() != 0 && addFaceJson.get("code").getAsInt() != 0){
                                throw new JeecgBootException("设备录入用户信息失败！请联系管理员");
                            }
                        }
                    }
                }
            }
            log.warn("查询主用户信息：use_user_id:"+familyMembers.getId()+";courses_price_rule_id:"+coursesPriceRules.getId());
            JobExtendedClassNoticeVo jobExtendedClassNoticeVo =  priceRulesMapper.findByClassPrice(familyMembers.getId(),coursesPriceRules.getId());
            log.warn("查询主用户信息:"+ JSONObject.toJSONString(jobExtendedClassNoticeVo));
            if (jobExtendedClassNoticeVo==null||StringUtils.isEmpty(jobExtendedClassNoticeVo.getUserOpenId())|| StringUtils.isEmpty(jobExtendedClassNoticeVo.getSiteName())){
                continue;
            }

            String date = getDate(coursesPriceRules.getStartTime(), coursesPriceRules.getEndTime());
            jobExtendedClassNoticeVo.setLassHourTime(date);
            jobExtendedClassNoticeVo.setReasonClassExtension(form.getPostponeReason());
            jobExtendedClassNoticeVos.add(jobExtendedClassNoticeVo);

        }
        log.warn("需要通知列表:"+JSONObject.toJSONString(jobExtendedClassNoticeVos));
        if (!jobExtendedClassNoticeVos.isEmpty()){
            wxNotificationService.extendedClassesJob(jobExtendedClassNoticeVos);
        }

        return Boolean.TRUE;
    }

    @Override
    public IPage<AppCoursesPageVO> getPageCourse(Page<AppCoursesPageVO> page, String orgCode, String userId) {
        return baseMapper.getPageCourse(page,orgCode,userId);
    }

    @Override
    public List<AppCoursesPageVO> queryMakeUpClassTable( String orgCode, String userId) {
        return baseMapper.queryMakeUpClassTable(orgCode,userId);
    }

    /**
     * 校验权限
     *
     * @param loginUser
     * @param course
     */
    private void checkPermission(LoginUser loginUser, AppCourses course) {
        if (!loginUser.getOrgCode().equals(course.getOrgCode()))
            throw new JeecgBootException("无权限操作", SC_INTERNAL_SERVER_ERROR_500);
    }

    private String getDate(Date startTime,Date endTime){

        if (startTime==null|| endTime==null){
            return "请联系授课老师确认时间";
        }
        // 将 Date 转换为 LocalDateTime
        LocalDateTime startLdt = LocalDateTime.ofInstant(startTime.toInstant(), java.time.ZoneId.systemDefault());
        LocalDateTime endLdt = LocalDateTime.ofInstant(endTime.toInstant(), java.time.ZoneId.systemDefault());

        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM.dd");
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");

        String formattedDate = startLdt.format(dateFormatter);
        String formattedStartTime = startLdt.format(timeFormatter);
        String formattedEndTime = endLdt.format(timeFormatter);

        return   formattedDate + " " + formattedStartTime + "-" + formattedEndTime;
    }

}
