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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.app.service.IDetailService;
import org.jeecg.modules.app.vo.CourseInfoVO;
import org.jeecg.modules.app.vo.PlaceInfoVO;
import org.jeecg.modules.app.vo.course.CourseRequestVo;
import org.jeecg.modules.app.vo.course.CourseResponseVo;
import org.jeecg.modules.app.vo.stadium.ConcertsDetailsVO;
import org.jeecg.modules.app.vo.stadium.ConcertsVO;
import org.jeecg.modules.app.vo.stadium.StadiumConcertsResponseVO;
import org.jeecg.modules.app.vo.stadium.StadiumConcertsVO;
import org.jeecg.modules.system.app.entity.*;
import org.jeecg.modules.system.app.mapper.*;
import org.jeecg.modules.system.app.utils.TimeRangeFinder;
import org.jeecg.modules.system.app.vo.time.NextSevenDayVo;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.mapper.SysDictMapper;
import org.jeecg.modules.system.mapper.SysUserMapper;
import org.jeecg.modules.system.util.PositionUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static org.jeecg.modules.system.constant.DefIndexConst.FACILITY_INFO;

@Service
@Log4j2
public class DetailServiceImpl implements IDetailService {
    @Resource
    private AppSiteMapper appSiteMapper;
    @Resource
    private AppCoursesPriceRulesMapper appCoursesPriceRulesMapper;
    @Resource
    private AppCoursesMapper appCoursesMapper;
    @Autowired
    private AppOrderProInfoMapper appOrderProInfoMapper;
    @Resource
    private AppSitePriceRulesMapper appSitePriceRulesMapper;
    @Resource
    private AppInstructorMapper appInstructorMapper;
    @Resource
    private AppOrderMapper appOrderMapper;
    @Resource
    private AppSitePlaceMapper appSitePlaceMapper;
    @Resource
    private AppCategoryMapper appCategoryMapper;
    @Resource
    private SysUserMapper sysUserMapper;
    @Resource
    private SysDictMapper sysDictMapper;

    @Resource
    private EvaluateMapper evaluateMapper;

    @Autowired
    private AppInsureMapper appInsureMapper;
    @Autowired
    InsurePriceMapper insurePriceMapper;

    public static BigDecimal calculateAverage(Long scoreSum, Long scoreNum) {
        if (scoreNum == 0L) {
            return  BigDecimal.valueOf(0).setScale(1 , RoundingMode.DOWN);
        }

        // 将分子和分母都转换为BigDecimal
        BigDecimal sum = new BigDecimal(scoreSum);
        BigDecimal num = new BigDecimal(scoreNum);

        // 计算平均数，保留1位小数并四舍五入
        return sum.divide(num, 1, RoundingMode.HALF_UP);
    }

    @Override
    public PlaceInfoVO getPlaceInfo(String id) {
        AppSite appSite = appSiteMapper.selectById(id);
        PlaceInfoVO placeInfo = appSiteMapper.getPlaceInfo(id);
        Long scoreNum = evaluateMapper.findByScoreNum(id);
        if(null == scoreNum){
            scoreNum = 0L;
        }
        Long scoreSum = evaluateMapper.findByAverageScore(id);//评论总评分
        if(null == scoreSum){
            scoreSum = 0L;
        }
        placeInfo.setGoodRate(calculateAverage(scoreSum, scoreNum));
        List<DictModel> dictModels = sysDictMapper.queryDictItemsByCode(FACILITY_INFO);
        List<String> facilityInfo = new ArrayList<>();
        if(null != placeInfo.getFacility()){
            Arrays.stream(placeInfo.getFacility().split(",")).forEach(facility -> {
                dictModels.forEach(dictModel -> {
                    if(facility.equals(dictModel.getValue())){
                        facilityInfo.add(dictModel.getTitle());
                    }
                });
            });
        }
        placeInfo.setFacilityInfo(facilityInfo);
        List<PlaceInfoVO.InstructorVO> instructorVOList = new ArrayList<>();
        List<AppInstructor> appInstructors = appInstructorMapper.selectList(Wrappers.<AppInstructor>lambdaQuery()
                .eq(AppInstructor::getOrgCode, appSite.getOrgCode()));
        if (CollUtil.isNotEmpty(appInstructors)) {
            for (AppInstructor instructor : appInstructors) {
                PlaceInfoVO.InstructorVO vo = new PlaceInfoVO.InstructorVO();
                BeanUtils.copyProperties(instructor,vo); // 注意参数顺序
                List<String> category = new ArrayList<>();
                Arrays.stream(instructor.getTrainingPrograms().split(",")).forEach(categoryId -> {
                    AppCategory appCategory = appCategoryMapper.selectById(categoryId);
                    if (appCategory != null) {
                        category.add(appCategory.getName());
                    }
                });
                vo.setCategory(category);
                SysUser sysUser = sysUserMapper.selectById(instructor.getUserId());
                vo.setName(sysUser.getRealname()).setAvatar(sysUser.getAvatar());
                vo.setId(sysUser.getId());
                instructorVOList.add(vo);
            }
        }
        placeInfo.setInstructorVOList(instructorVOList);
        placeInfo.setRunStatus(false);
        if (appSite.getRunStatus() == 0 && appSite.getRunType() == 0) {
            placeInfo.setRunStatus(true);
        } else if (appSite.getRunStatus() == 0) {
            if (appSite.getStartTime() != null && appSite.getEndTime() != null) {
                try {
                    // 解析时间
                    LocalTime now = LocalTime.now();
                    LocalTime startTime = LocalTime.parse(DateUtil.format(appSite.getStartTime(), "HH:mm:ss"));
                    LocalTime endTime = LocalTime.parse(DateUtil.format(appSite.getEndTime(), "HH:mm:ss"));
                    // 判断逻辑
                    if (!now.isBefore(startTime) && !now.isAfter(endTime)) {
                        placeInfo.setRunStatus(true);
                    }
                } catch (Exception e) {
                    log.error("时间解析失败", e);
                }
            }
        }
        if(appSite.getType() == 0){
            List<PlaceInfoVO.PlaceInfoMsgVO> placeInfoMsgVOS = new ArrayList<>();
            appSitePlaceMapper.selectList(Wrappers.<AppSitePlace>lambdaQuery().eq(AppSitePlace::getSiteId, id)
                    .eq(AppSitePlace::getType,0)).forEach(appSitePlace -> {
                AppSitePriceRules appSitePriceRules = appSitePriceRulesMapper.selectOne(Wrappers.<AppSitePriceRules>lambdaQuery()
                        .eq(AppSitePriceRules::getSitePlaceId, appSitePlace.getId())
                        .last("limit 1").orderByDesc(AppSitePriceRules::getOriginalPrice));
                AppSitePriceRules appSitePriceRules1 = appSitePriceRulesMapper.selectOne(Wrappers.<AppSitePriceRules>lambdaQuery()
                        .eq(AppSitePriceRules::getSitePlaceId, appSitePlace.getId())
                        .last("limit 1").orderByDesc(AppSitePriceRules::getSellingPrice));
                PlaceInfoVO.PlaceInfoMsgVO placeInfoMsgVO = new PlaceInfoVO.PlaceInfoMsgVO();
                int salesCount = 0;
                List<String> orderIdList = appSitePriceRulesMapper.selectList(Wrappers.<AppSitePriceRules>lambdaQuery()
                        .eq(AppSitePriceRules::getSitePlaceId, appSitePlace.getId())
                        .eq(AppSitePriceRules::getStatus, 0))
                        .stream().map(AppSitePriceRules::getId).collect(Collectors.toList());
                if (!orderIdList.isEmpty()) {
                    salesCount = Math.toIntExact(appOrderProInfoMapper.selectCount(Wrappers.<AppOrderProInfo>lambdaQuery()
                            .in(AppOrderProInfo::getProductId, orderIdList)));
                }
                placeInfoMsgVO.setName(appSitePlace.getName())
                        .setId(appSitePlace.getId())
                        .setCategory(getCategoryName(appSite.getCategoryId()))
                        .setCover(appSitePlace.getCover().split(",")[0])
                        .setReminder(appSitePlace.getReminder())
                        .setSales(salesCount)
                        .setRefundType(appSitePlace.getRefundType());
                if(appSitePriceRules!=null){
                    placeInfoMsgVO.setOriginalPrice(appSitePriceRules.getOriginalPrice());
                }else {
                    placeInfoMsgVO.setOriginalPrice(BigDecimal.ZERO);
                }
                if(appSitePriceRules1!=null){
                    placeInfoMsgVO.setSellingPrice(appSitePriceRules1.getSellingPrice());
                }else {
                    placeInfoMsgVO.setSellingPrice(BigDecimal.ZERO);
                }
                placeInfoMsgVOS.add(placeInfoMsgVO);
            });
            placeInfo.setPlaceInfoMsgVO(placeInfoMsgVOS);
            return placeInfo;
        }else if(appSite.getType() == 1){
            List<PlaceInfoVO.PlaceInfoGymMsgVO> placeInfoMsgVO = appSitePlaceMapper.getPlaceInfoNoFixation(appSite.getId());
            placeInfo.setPlaceInfoMsgVO(placeInfoMsgVO);
            return placeInfo;
        }else {
            return null;
        }
    }


    @Override
    public CourseInfoVO getCourseInfo(String id, double latitude, double longitude) {
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        CourseInfoVO courseInfoVO = appCoursesMapper.getCourseInfo(id);
        AppSite appSite = appSiteMapper.selectById(courseInfoVO.getAddressSiteId());
        if (appSite!=null&& ObjectUtils.isNotEmpty(appSite.getLatitude())&& ObjectUtils.isNotEmpty(appSite.getLongitude())){
            courseInfoVO.setKm(PositionUtil.calculateDistance(latitude, longitude, appSite.getLatitude().doubleValue(), appSite.getLongitude().doubleValue()));
            courseInfoVO.setAddress(appSite.getName());
            courseInfoVO.setLatitude(appSite.getLatitude()).setLongitude(appSite.getLongitude());
        }
        courseInfoVO.setSales(getCourseSales(id));
        List<CourseInfoVO.CourseDetailVO> courseDetailVOList = new ArrayList<>();
        appCoursesPriceRulesMapper.selectList(Wrappers.<AppCoursesPriceRules>lambdaQuery()
                .eq(AppCoursesPriceRules::getCoursesId, courseInfoVO.getId())).forEach(appCourses -> {
            CourseInfoVO.CourseDetailVO courseDetailVO = new CourseInfoVO.CourseDetailVO();
            BeanUtils.copyProperties(appCourses,courseDetailVO);
            courseDetailVOList.add(courseDetailVO);
        });

        courseInfoVO.setCourseDetail(courseDetailVOList);
        if(null != loginUser &&  loginUser.getId() != null){
            courseInfoVO.setHasDiscount(checkOrderOrFree(loginUser.getId(),courseInfoVO.getCategoryId(),id));
        }else {
            courseInfoVO.setHasDiscount(false);
        }
        return courseInfoVO;
    }

    /**
     * 校验是否为第一次购买当前类目的课程
     *
     * @param userId
     * @param categoryId
     * @return
     */
    private Boolean checkOrderOrFree(String userId, String categoryId, String coursesId) {
        Date startTime = appCoursesMapper.selectById(coursesId).getStartTime();
        if (startTime != null && startTime.before(new Date())) {
            return Boolean.FALSE; // 课程已过期，视为非首次购买
        }
        //查询当前用户的所有课程订单
        List<AppOrder> appOrders = appOrderMapper.selectList(Wrappers.<AppOrder>lambdaQuery().eq(AppOrder::getUserId, userId)
                .eq(AppOrder::getType, CommonConstant.ORDER_TYPE_2));
        if (CollUtil.isNotEmpty(appOrders)){
            for (AppOrder appOrder : appOrders) {
                if(null != appOrder){
                    AppCourses appCours = appCoursesMapper.selectById(appOrder.getProductIds());
                    if (null != appCours && appCours.getCategoryId().equals(categoryId)) {
                        return Boolean.FALSE;
                    }
                }
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public List<PlaceInfoVO.CourseInfoVO> courseInfoVOList(String categoryId,String id,double longitude,double latitude) {
        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        List<PlaceInfoVO.CourseInfoVO> courseInfoVOList = new ArrayList<>();
        appCoursesMapper.selectList(Wrappers.<AppCourses>lambdaQuery()
                .eq(AppCourses::getAddressSiteId, id)
                .like(AppCourses::getCategoryId, categoryId)
                .eq(AppCourses::getRackingStatus, 0)
                .gt(AppCourses::getEndTime, new Date())
                .eq(AppCourses::getDelFlag, 0)).forEach(appCourses -> {
            List<String> list = Arrays.asList(appCourses.getCover().split(","));
            PlaceInfoVO.CourseInfoVO courseInfoVO = new PlaceInfoVO.CourseInfoVO();
            BeanUtils.copyProperties(appCourses, courseInfoVO);
            courseInfoVO.setSales(getCourseSalesCount(appCourses.getId()));
            courseInfoVO.setSalesYear(getCourseSales(appCourses.getId()));
            courseInfoVO.setPriceType(isFirstPurchase(user.getId()));
            courseInfoVO.setCover(list.get(0));
            courseInfoVO.setPeriod(Math.toIntExact(appCoursesPriceRulesMapper.selectCount(Wrappers.<AppCoursesPriceRules>lambdaQuery()
                    .eq(AppCoursesPriceRules::getCoursesId, appCourses.getId()))));
            courseInfoVO.setPeriod(Math.toIntExact(appCoursesPriceRulesMapper.selectCount(Wrappers.<AppCoursesPriceRules>lambdaQuery().eq(AppCoursesPriceRules::getCoursesId, appCourses.getId()))));
            courseInfoVO.setHasDiscount(checkOrderOrFree(user.getId(), appCourses.getCategoryId(),appCourses.getId()));
            AppSite appSite = appSiteMapper.selectById(appCourses.getAddressSiteId());
            if (appSite!=null){
                courseInfoVO.setAddress(appSite.getName());
                double km = PositionUtil.calculateDistance(latitude, longitude, appSite.getLatitude().doubleValue(), appSite.getLongitude().doubleValue());
                courseInfoVO.setKm(new BigDecimal(String.valueOf(km)));
            }
            String positiveRating = evaluateMapper.findPositiveRating(courseInfoVO.getId());
            BigDecimal decimalValue = null;
            try {
                decimalValue = new BigDecimal(positiveRating);
            } catch (Exception e) {
                decimalValue=BigDecimal.ZERO;
            }

            courseInfoVO.setGoodRate(decimalValue);
            courseInfoVOList.add(courseInfoVO);
        });
        return courseInfoVOList;
    }

    @Override
    public PlaceInfoVO.theGymnasiumIsCharteredVO getPlaceInfoNoFixation(String id, String categoryId) {
        AppSitePlace appSitePlace = appSitePlaceMapper.selectOne(Wrappers.<AppSitePlace>lambdaQuery()
                .eq(AppSitePlace::getSiteId, id)
                .eq(AppSitePlace::getStatus, 0)
                .eq(AppSitePlace::getDelFlag, 0)
                .last("LIMIT 1"));
        List<PlaceInfoVO.theGymnasiumIsCharteredInfoVO> collect = new ArrayList<>();
        PlaceInfoVO.theGymnasiumIsCharteredVO theGymnasiumIsCharteredVO = new PlaceInfoVO.theGymnasiumIsCharteredVO()
                .setCategory(getCategoryName(categoryId));
        if(appSitePlace != null){
            collect = appSitePriceRulesMapper.theGymnasiumIsCharteredInfo(appSitePlace.getOrgCode(), categoryId);
            theGymnasiumIsCharteredVO.setEarlyRefundTime(Optional.ofNullable(appSitePlace.getEarlyRefundTime()).orElse(0));

        }
        if(!collect.isEmpty()){
            collect.forEach(theGymnasiumIsCharteredInfoVO -> {
                AppSitePriceRules appSitePriceRules = appSitePriceRulesMapper.selectById(theGymnasiumIsCharteredInfoVO.getId());
                theGymnasiumIsCharteredInfoVO.setSellingPrice(appSitePriceRulesMapper.selectOne(Wrappers.<AppSitePriceRules>lambdaQuery()
                        .eq(AppSitePriceRules::getOrgCode, appSitePriceRules.getOrgCode())
                        .eq(AppSitePriceRules::getCategoryId, categoryId)
                        .eq(AppSitePriceRules::getDayOfWeek, appSitePriceRules.getDayOfWeek())
                        .orderByAsc(AppSitePriceRules::getSellingPrice).last("LIMIT 1"))
                        .getSellingPrice());
            });
            theGymnasiumIsCharteredVO.setVr(appSiteMapper.selectById(id).getVr());
        }
        theGymnasiumIsCharteredVO.setTimeSlot(collect);
        return theGymnasiumIsCharteredVO;
    }

    @Override
    public Result<StadiumConcertsResponseVO> findConcerts(String siteId, String categoryId) {
        List<NextSevenDayVo> nextSevenDays = TimeRangeFinder.getNextSevenDays();
        StadiumConcertsResponseVO  stadiumConcertsResponseVO = appSitePlaceMapper.findBySidAndCid(siteId,categoryId);
        if (stadiumConcertsResponseVO==null){
            stadiumConcertsResponseVO = new StadiumConcertsResponseVO();
        }
        List<StadiumConcertsVO> stadiumConcertsVOS = new ArrayList<>();
        Boolean label = true;
        for (NextSevenDayVo nextSevenDay : nextSevenDays) {
            StadiumConcertsVO stadiumConcertsVO = new StadiumConcertsVO();
            BeanUtils.copyProperties(nextSevenDay,stadiumConcertsVO);
            List<ConcertsVO>  concertsVOList=null;
            //查询当天所有场地和场次及状态
            if (label){
                concertsVOList= appSitePlaceMapper.findByConcertsAndTime(stadiumConcertsVO.getWeekDayAndDate().replaceAll(".*\\(([^)]+)\\).*", "$1"),stadiumConcertsVO.getWeekDay(),siteId,categoryId);
            }else {
                concertsVOList= appSitePlaceMapper.findByConcerts(stadiumConcertsVO.getWeekDayDate(),stadiumConcertsVO.getWeekDay(),siteId,categoryId);
            }

            label=false;
            stadiumConcertsVO.setConcertsVOList(concertsVOList);
            long count=0L;
            if (concertsVOList!=null&&!concertsVOList.isEmpty()){

                for (ConcertsVO concertsVO : concertsVOList) {
                    List<ConcertsDetailsVO> concertsDetailsVOList = concertsVO.getConcertsDetailsVOList();
                    if (concertsDetailsVOList!=null&&!concertsDetailsVOList.isEmpty()){
                        count = count+concertsDetailsVOList.stream()
                                .filter(Objects::nonNull) // 过滤掉null元素
                                .filter(vo -> vo.getConcertStatus() == 0) // 精确匹配状态0
                                .count();
                    }
                }

            }
            stadiumConcertsVO.setNoDayConcertsReservationNum(count);


            stadiumConcertsVOS.add(stadiumConcertsVO);
        }
        stadiumConcertsResponseVO.setStadiumConcertsVOList(stadiumConcertsVOS);
        return Result.ok(stadiumConcertsResponseVO);
    }

    @Override
    public List<AppCategory> getAllCategory(String id) {
        List<AppCategory> appCategories = new ArrayList<>();
        List<AppSitePlace> appSitePlaceList = appSitePlaceMapper.selectList(Wrappers.<AppSitePlace>lambdaQuery()
                .eq(AppSitePlace::getSiteId, id));
        List<String> objects = appSitePlaceList.stream().map(AppSitePlace::getCategoryId).collect(Collectors.toList());
        AppSite appSite = appSiteMapper.selectById(id);
        if(null != appSite && appSite.getType() == 0){
            objects = Arrays.asList(appSite.getCategoryId().split(","));
        }
        if(objects.isEmpty()) return appCategories;
        appCategories = appCategoryMapper.selectList(Wrappers.<AppCategory>lambdaQuery()
                .eq(AppCategory::getStatus, 0).in(AppCategory::getId,objects)
                .eq(AppCategory::getDelFlag, 0));
        return appCategories;
    }

    @Override
    public List<AppCategory> getAllCourseCategory(String id) {
        List<AppCategory> appCategories = new ArrayList<>();
        AppSite appSite = appSiteMapper.selectById(id);
        List<String> objects = new ArrayList<>();
        if(null != appSite){
            objects = Arrays.asList(appSite.getCategoryId().split(","));
        }
        if(objects.isEmpty()) return appCategories;
        appCategories = appCategoryMapper.selectList(Wrappers.<AppCategory>lambdaQuery()
                .eq(AppCategory::getStatus, 0).in(AppCategory::getId,objects)
                .eq(AppCategory::getDelFlag, 0));
        return appCategories;
    }


    /**
     * 查询课程分页列表
     * @param courseRequestVo
     * @return
     */
    @Override
    public IPage<CourseResponseVo> findByCourseList(CourseRequestVo courseRequestVo) {
        Page<CourseResponseVo> page = new Page<CourseResponseVo>(courseRequestVo.getPageNo(), courseRequestVo.getPageSize());
        Date date = new Date();
        IPage<CourseResponseVo>  courseInfoVOIPage=null;
        if (ObjectUtil.isNotEmpty(courseRequestVo.getOrderByType())){
            if (ObjectUtil.isNotEmpty(courseRequestVo.getLongitude())
                    &&ObjectUtil.isNotEmpty(courseRequestVo.getLatitude())){
                courseInfoVOIPage= appCoursesMapper.findByLaLoList(page,courseRequestVo,date);
            }else {
                courseInfoVOIPage= appCoursesMapper.findByList(page,courseRequestVo,date);
            }
        }else {
            courseInfoVOIPage= appCoursesMapper.findByCourseList(page,courseRequestVo,date);
        }
        courseInfoVOIPage.getRecords().forEach(courseResponseVo -> {
            String string = courseResponseVo.getCover().split(",")[0];
            courseResponseVo.setCover(string);
            courseResponseVo.setAnnualSalesNum(getCourseSalesCountYear(courseResponseVo.getId()));
            courseResponseVo.setSaleNum(getCourseSalesCount(courseResponseVo.getId()));
            courseResponseVo.setGoodRate(evaluateMapper.findPositiveRating(courseResponseVo.getId()));
        });
        return courseInfoVOIPage;
    }


    /**
     * @Author SheepHy
     * @Description 计算当前课程年销售数
     * @Date 15:47 2025/7/8
     * @Param id 课程id
     * @return int 课程年销售数
     **/
    private int getCourseSales(String id) {
        // 获取当前年份的时间范围（如：2025-01-01 00:00:00 到 2026-01-01 00:00:00）
        LocalDate now = LocalDate.now();
        LocalDateTime startOfYear = now.withDayOfYear(1).atStartOfDay(); // 当前年份第一天
        LocalDateTime endOfYear = startOfYear.plusYears(1); // 下一年第一天

        return Math.toIntExact(appOrderProInfoMapper.selectCount(
                Wrappers.<AppOrderProInfo>lambdaQuery()
                        .eq(AppOrderProInfo::getProductId, id)
                        .eq(AppOrderProInfo::getStatus,0)
                        .eq(AppOrderProInfo::getDelFlag,0)
                        .ge(AppOrderProInfo::getCreateTime, Date.from(startOfYear.atZone(ZoneId.systemDefault()).toInstant())) // >= 2025-01-01
                        .lt(AppOrderProInfo::getCreateTime, Date.from(endOfYear.atZone(ZoneId.systemDefault()).toInstant())) // < 2026-01-01
        ));
    }

    /**
     * @Author SheepHy
     * @Description 计算当前课程销售数
     * @Date 15:47 2025/7/8
     * @Param id 课程id
     * @return int 课程年销售数
     **/
    private int getCourseSalesCount(String id) {
        return Math.toIntExact(appOrderProInfoMapper.selectCount(
                Wrappers.<AppOrderProInfo>lambdaQuery()
                        .eq(AppOrderProInfo::getProductId, id)
        ));
    }

    private int getCourseSalesCountYear(String id) {
        LocalDate now = LocalDate.now();
        LocalDateTime startOfYear = now.withDayOfYear(1).atStartOfDay(); // 当前年份第一天
        LocalDateTime endOfYear = startOfYear.plusYears(1); // 下一年第一天

        return Math.toIntExact(appOrderProInfoMapper.selectCount(
                Wrappers.<AppOrderProInfo>lambdaQuery()
                        .eq(AppOrderProInfo::getProductId, id)
                        .ge(AppOrderProInfo::getCreateTime, Date.from(startOfYear.atZone(ZoneId.systemDefault()).toInstant())) // >= 当年第一天
                        .lt(AppOrderProInfo::getCreateTime, Date.from(endOfYear.atZone(ZoneId.systemDefault()).toInstant())) // < 下一年第一天
        ));
    }

    /**
     * @Author SheepHy
     * @Description 查询用户是否为第一次购买
     * @Date 16:54 2025/7/8
     * @Param
     * @return
     **/
    private int isFirstPurchase(String userId) {
        AppOrder appOrder = appOrderMapper.selectOne(Wrappers.<AppOrder>lambdaQuery()
                .eq(AppOrder::getUpdateBy, userId).last("limit 1"));
        if(null == appOrder){
            return 1;
        }else {
            return 0;
        }
    }

    /**
     * @Author SheepHy
     * @Description 根据类目ID获取完整的类目名称
     * @Date 13:31 2025/7/7
     * @Param categoryId 类目ID
     * @return List<String>
     **/
    private List<String> getCategoryName(String categoryId) {
        List<String> list = new ArrayList<>();
        if(null != categoryId){
            String[] split = categoryId.split(",");
            Arrays.stream(split).forEach(id -> {
                list.add(appCategoryMapper.selectById(id).getName());
            });
        }
        return list;
    }
}