package jnpf.personnels.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import jnpf.base.ActionResult;
import jnpf.base.vo.PageListVO;
import jnpf.base.vo.PaginationVO;
import jnpf.model.cultivate.CultivatePage;
import jnpf.model.personnels.dto.analysis.FtbPersonnlesAnalysisDTO;
import jnpf.model.personnels.dto.range.RangeDTO;
import jnpf.model.personnels.dto.staff.roster.WorkerGroupDataDto;
import jnpf.model.personnels.po.FtbPersonnelsRuleConfig;
import jnpf.model.personnels.vo.analysis.*;
import jnpf.model.personnels.vo.range.FtbRangeConfigDIYVO;
import jnpf.model.personnels.vo.range.FtbRangeConfigVO;
import jnpf.model.personnels.vo.turnover.FtbPersonnelsTurnoverManagementVO;
import jnpf.permission.V2UserApi;
import jnpf.permission.dto.v2.user.QueryListUserByLevelDTO;
import jnpf.permission.dto.v2.user.QueryPageUserDTO;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.personnels.mapper.FtbPersonnelsRuleConfigMapper;
import jnpf.personnels.mapper.FtbPersonnelsStaffRegistrationFormDataMapper;
import jnpf.personnels.mapper.FtbPersonnelsTurnoverAnalysisMapper;
import jnpf.personnels.mapper.FtbPersonnelsTurnoverManagementMapper;
import jnpf.personnels.service.FtbPersonnelsTurnoverAnalysisService;
import jnpf.personnels.service.FtbPersonnlesInfoConfigService;
import jnpf.personnels.utils.CompanyAgeUtil;
import jnpf.personnels.utils.CovertDateUtils;
import jnpf.util.RangeConfigUtil;
import jnpf.util.excel.EasyExcelUtils;
import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @Author: peng.hao
 * @create: 2024/10/14
 */
@Service
public class FtbPersonnelsTurnoverAnalysisServiceImpl implements FtbPersonnelsTurnoverAnalysisService {

    @Resource
    FtbPersonnelsTurnoverAnalysisMapper ftbPersonnelsTurnoverAnalysisMapper;

    @Autowired
    private V2UserApi v2UserApi;

    // 在职状态
    private static final List<String> ON_THE_JOB_STATUS = List.of("302", "303", "304", "306");
    // 首月
    private static final Integer FIRST_MONTH = 30;
    // 首周
    private static final Integer FIRST_WEEK = 7;
    // 首年
    private static final Integer FIRST_YEAR = 365;

    @Resource
    private FtbPersonnelsRuleConfigMapper ftbPersonnelsRuleConfigMapper;

    @Resource
    FtbPersonnelsTurnoverManagementMapper turnoverManagementMapper;

    @Resource
    private FtbPersonnlesInfoConfigService personnlesInfoConfigService;

    @Resource
    private FtbPersonnelsStaffRegistrationFormDataMapper ftbPersonnelsStaffRegistrationFormDataMapper;


    @Override
    public FtbPersonnelsTurnoverWithAnalysisInfoVO getLeavePeopleMonth(FtbPersonnlesAnalysisDTO dto) {
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new FtbPersonnelsTurnoverWithAnalysisInfoVO();
        }
        // 当前时间
        Integer peopleMonth = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleMonth(dto.getStartTime(),dto.getEndTime(),userIdListByOrganizeIds);
        FtbPersonnelsTurnoverWithAnalysisInfoVO infoVO = new FtbPersonnelsTurnoverWithAnalysisInfoVO();
        // 当月离职
        infoVO.setPercentageOfHeadcount(peopleMonth);
        // 同比 去年 这个月
        // 同比 （本期数 - 同期数） / 同期数 × 100%
        Date lastYearStartTime = DateUtil.offset(dto.getStartTime(), DateField.YEAR, -1).toJdkDate();
        Date lastYearEndTime = DateUtil.offset(dto.getEndTime(), DateField.YEAR, -1).toJdkDate();
        // 同期数
        Integer yearOnYear = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleMonth(lastYearStartTime,lastYearEndTime, userIdListByOrganizeIds);
        // 同比 （本期数 - 同期数） / 同期数 × 100%
        BigDecimal setScale = yearOnYearAndMonthOnMonth(peopleMonth, yearOnYear);
        infoVO.setYearOnYear(setScale);
        // 环比 上月
        Date ringStartTime = DateUtil.offset(dto.getStartTime(), DateField.MONTH, -1).toJdkDate();
        Date ringEndTime = DateUtil.offset(dto.getEndTime(), DateField.MONTH, -1).toJdkDate();
        // 环比 （本期数 - 上期数） / 上期数 × 100%
        Integer leavePeopleMonth = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleMonth(ringStartTime, ringEndTime, userIdListByOrganizeIds);
        BigDecimal decimal = yearOnYearAndMonthOnMonth(peopleMonth, leavePeopleMonth);
        infoVO.setAnnulus(decimal);
        return infoVO;
    }

    @Override
    public FtbPersonnelsTurnoverPeopleSexVO getLeavePeopleSexMonth(FtbPersonnlesAnalysisDTO dto) {
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new FtbPersonnelsTurnoverPeopleSexVO();
        }
        // 离职人数
        Integer peopleMonth = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleMonth(dto.getStartTime(),dto.getEndTime(),userIdListByOrganizeIds);
        //0	workerSex	未知
        //1	workerSex	男
        //2	workerSex	女
        FtbPersonnelsTurnoverPeopleSexVO ftbPersonnelsTurnoverPeopleSexVO = new FtbPersonnelsTurnoverPeopleSexVO();
        // 离职人员的数据项
        List<String> femaleUserIds = dynamicallyObtainTheRequiredLogFieldValueWithWorkSex(dto, userIdListByOrganizeIds, "2");
        // 女
        Integer femaleCount =  0;
        if (CollUtil.isNotEmpty(femaleUserIds)) femaleCount = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleSexMonth(dto.getStartTime(),dto.getEndTime(),femaleUserIds);
        ftbPersonnelsTurnoverPeopleSexVO.setMale(femaleCount);
        // 男
        List<String> maleUserIds = dynamicallyObtainTheRequiredLogFieldValueWithWorkSex(dto, userIdListByOrganizeIds, "1");
        Integer maleCount = 0;
        if (CollUtil.isNotEmpty(maleUserIds)) maleCount =  ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleSexMonth(dto.getStartTime(),dto.getEndTime(), maleUserIds);
        ftbPersonnelsTurnoverPeopleSexVO.setFemale(maleCount);
//        // 截止月份在职员工
//        BigDecimal employedInTheMonth = ftbPersonnelsOverviewAnalysisMapper.employeesEmployedInTheMonth(DatePattern.NORM_MONTH_FORMAT.format(dto.getEndTime())
//                , ON_THE_JOB_STATUS, userIdListByOrganizeIds);
        ftbPersonnelsTurnoverPeopleSexVO.setMaleRatio(calculateTheScale(femaleCount, BigDecimal.valueOf(peopleMonth)));
        ftbPersonnelsTurnoverPeopleSexVO.setFemaleRatio(calculateTheScale(maleCount, BigDecimal.valueOf(peopleMonth)));
        return ftbPersonnelsTurnoverPeopleSexVO;
    }

    @Override
    public FtbPersonnelsTurnoverFirstMonthLeaveRateVO getFirstMonthLeaveRate(FtbPersonnlesAnalysisDTO dto) {
        //  首月离职率
        FtbPersonnelsTurnoverFirstMonthLeaveRateVO ftbPersonnelsTurnoverFirstMonthLeaveRateVO = new FtbPersonnelsTurnoverFirstMonthLeaveRateVO();
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return ftbPersonnelsTurnoverFirstMonthLeaveRateVO;
        }
        // 这个月时间离职率
        Date monthStartTime = dto.getStartTime();
        Date monthEndTime = dto.getEndTime();
        DateTime startTime = DateUtil.beginOfMonth(monthStartTime);
        DateTime endTime = DateUtil.endOfMonth(monthEndTime);
        BigDecimal firstMonthLeaveRate = turnoverRateSequentially(startTime, endTime, userIdListByOrganizeIds,FIRST_MONTH,dto.getOrganizationId());
        ftbPersonnelsTurnoverFirstMonthLeaveRateVO.setFirstMonthLeaveRate(firstMonthLeaveRate);
        // 同比 去年 这个月
        Date lastYearStartTime = DateUtil.offset(startTime, DateField.YEAR, -1).toJdkDate();
        Date lastYearEndTime = DateUtil.offset(endTime, DateField.YEAR, -1).toJdkDate();
        // 同期数
        BigDecimal turnoverRate = turnoverRateSequentially(lastYearStartTime, lastYearEndTime, userIdListByOrganizeIds, FIRST_MONTH, dto.getOrganizationId());
        // 同比 （本期数 - 同期数） / 同期数 × 100%
        BigDecimal calculatedValue = yearOnYearAndMonthOnMonth(firstMonthLeaveRate, turnoverRate);
        ftbPersonnelsTurnoverFirstMonthLeaveRateVO.setFirstMonthLeaveRateYearOnYear(calculatedValue);
        // 环比 （本期数 - 上期数） / 上期数 × 100%
        // 环比 上月
        Date ringStartTime = DateUtil.offset(startTime, DateField.MONTH, -1).toJdkDate();
        Date ringEndTime = DateUtil.offset(endTime, DateField.MONTH, -1).toJdkDate();
        // 上期数
        BigDecimal previousPeriodRatio = turnoverRateSequentially(ringStartTime, ringEndTime, userIdListByOrganizeIds, FIRST_MONTH, dto.getOrganizationId());
        // 环比
        BigDecimal annulus = yearOnYearAndMonthOnMonth(firstMonthLeaveRate, previousPeriodRatio);
        ftbPersonnelsTurnoverFirstMonthLeaveRateVO.setFirstMonthLeaveRateAnnulus(annulus);
        return ftbPersonnelsTurnoverFirstMonthLeaveRateVO;
    }

    @Override
    public FtbPersonnelsTurnoverFirstWeekLeaveRateVO getFirstWeekLeaveRate(FtbPersonnlesAnalysisDTO dto) {
        // 首周
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new FtbPersonnelsTurnoverFirstWeekLeaveRateVO();
        }
        FtbPersonnelsTurnoverFirstWeekLeaveRateVO firstWeekLeaveRateVO = new FtbPersonnelsTurnoverFirstWeekLeaveRateVO();
        BigDecimal firstWeekLeaveRate = turnoverRateSequentially(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds, FIRST_WEEK, dto.getOrganizationId());
        // 首周离职率
        firstWeekLeaveRateVO.setFirstWeekLeaveRate(firstWeekLeaveRate);
        // 首周离职率同比
        firstWeekLeaveRateVO.setFirstWeekLeaveRateYearOnYear(
                yearOnYearAndMonthOnMonth(firstWeekLeaveRate,
                        turnoverRateSequentially(
                                DateUtil.offset(dto.getStartTime(), DateField.YEAR, -1).toJdkDate(),
                                DateUtil.offset(dto.getEndTime(), DateField.YEAR, -1).toJdkDate(),
                                userIdListByOrganizeIds,
                                FIRST_WEEK, dto.getOrganizationId())));
        // 首周离职率环比
        firstWeekLeaveRateVO.setFirstWeekLeaveRateAnnulus(
                yearOnYearAndMonthOnMonth(firstWeekLeaveRate,
                        turnoverRateSequentially(
                                DateUtil.offset(dto.getStartTime(), DateField.MONTH, -1).toJdkDate(),
                                DateUtil.offset(dto.getEndTime(), DateField.MONTH, -1).toJdkDate(),
                                userIdListByOrganizeIds,
                                FIRST_WEEK, dto.getOrganizationId())));
        return firstWeekLeaveRateVO;
    }

    @Override
    public FtbPersonnelsTurnoverFirstYearLeaveRateVO getFirstYearLeaveRate(FtbPersonnlesAnalysisDTO dto) {
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new FtbPersonnelsTurnoverFirstYearLeaveRateVO();
        }
        FtbPersonnelsTurnoverFirstYearLeaveRateVO leaveRateVO = new FtbPersonnelsTurnoverFirstYearLeaveRateVO();
        // 离职率
        Date startTime = dto.getStartTime();
        Date endTime = dto.getEndTime();
        DateTime monthStartTime = DateUtil.beginOfMonth(startTime);
        DateTime monthEndTime = DateUtil.endOfMonth(endTime);
        BigDecimal firstYearLeaveRate = turnoverRateSequentially(monthStartTime, monthEndTime, userIdListByOrganizeIds, FIRST_YEAR, dto.getOrganizationId());
        leaveRateVO.setFirstYearLeaveRate(firstYearLeaveRate);
        // 同比
        leaveRateVO.setFirstYearLeaveRateYearOnYear(yearOnYearAndMonthOnMonth(firstYearLeaveRate,
                turnoverRateSequentially(
                        DateUtil.offset(monthStartTime, DateField.YEAR, -1).toJdkDate(),
                        DateUtil.offset(monthEndTime, DateField.YEAR, -1).toJdkDate(),
                        userIdListByOrganizeIds,
                        FIRST_YEAR, dto.getOrganizationId())));
        // 环比
        leaveRateVO.setFirstYearLeaveRateAnnulus(yearOnYearAndMonthOnMonth(firstYearLeaveRate,
                turnoverRateSequentially(
                        DateUtil.offset(monthStartTime, DateField.MONTH, -1).toJdkDate(),
                        DateUtil.offset(monthEndTime, DateField.MONTH, -1).toJdkDate(),
                        userIdListByOrganizeIds,
                        FIRST_YEAR, dto.getOrganizationId())));
        return leaveRateVO;
    }

    @Override
    public FtbPersonnelsTurnoverLeaveRateVO getNewEmployeeLeaveRate(FtbPersonnlesAnalysisDTO dto) {
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new FtbPersonnelsTurnoverLeaveRateVO();
        }
        // 新员工定义配置
        FtbPersonnelsRuleConfig ruleConfig = ftbPersonnelsRuleConfigMapper.selectById(1);
        FtbPersonnelsTurnoverLeaveRateVO leaveRateVO = new FtbPersonnelsTurnoverLeaveRateVO();
        // 新员工离职比例
        // 离职总人数
        Date startTime = dto.getStartTime();
        Date endTime = dto.getEndTime();
        Integer lastInstallment = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleMonth(startTime, endTime, userIdListByOrganizeIds);
        leaveRateVO.setLeaveCount(lastInstallment);
        // 新员工离职人数
        Integer timeRule = ruleConfig.getTimeRule();
        Integer type = ruleConfig.getType();
        // 天数
        timeRule = type == 2 ? timeRule * 30 : timeRule;
        Integer leaveRate = ftbPersonnelsTurnoverAnalysisMapper.getFirstMonthLeaveRate(startTime, endTime, userIdListByOrganizeIds, timeRule);
        leaveRateVO.setLeaveTotal(leaveRate);
        // 新员工离职比例
        leaveRateVO.setLeaveRate(calculateTheScale(leaveRate, lastInstallment));
        // 新员工离职比例 同比
        leaveRateVO.setLeaveRateYearOnYear(yearOnYearAndMonthOnMonth(calculateTheScale(leaveRate, lastInstallment),
                turnoverRateSequentially(
                        DateUtil.offset(dto.getStartTime(), DateField.YEAR, -1).toJdkDate(),
                        DateUtil.offset(dto.getEndTime(), DateField.YEAR, -1).toJdkDate(),
                        userIdListByOrganizeIds,
                        timeRule, dto.getOrganizationId())));
        // 新员工离职比例 环比
        leaveRateVO.setLeaveRateAnnulus(yearOnYearAndMonthOnMonth(calculateTheScale(leaveRate, lastInstallment),
                turnoverRateSequentially(
                        DateUtil.offset(dto.getStartTime(), DateField.MONTH, -1).toJdkDate(),
                        DateUtil.offset(dto.getEndTime(), DateField.MONTH, -1).toJdkDate(),
                        userIdListByOrganizeIds,
                        timeRule, dto.getOrganizationId())));
        return leaveRateVO;
    }

    @Override
    public List<FtbPersonnelsTurnoverLeaveReasonDistributionVO> getLeaveReasonDistribution(FtbPersonnlesAnalysisDTO dto) {
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new ArrayList<>();
        }
        // 离职原因分布
        List<Map<String,String>>  result =  ftbPersonnelsTurnoverAnalysisMapper.getLeaveReasonDistribution(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        // 将数据转化为map
        List<List<FtbPersonnelsTurnoverLeaveReasonDistributionVO>> list = result.stream().map(item -> {
            List<String> reasons = List.of(item.get("reason").split(","));
            return reasons.stream().map(reason -> {
                FtbPersonnelsTurnoverLeaveReasonDistributionVO distributionVO = new FtbPersonnelsTurnoverLeaveReasonDistributionVO();
                distributionVO.setLeaveReason(reason);
                distributionVO.setCount(1);
                return distributionVO;
            }).collect(Collectors.toList());
        }).collect(Collectors.toList());
        Map<String, Integer> stringIntegerMap = list.stream().flatMap(List::stream).collect(
                Collectors.groupingBy(FtbPersonnelsTurnoverLeaveReasonDistributionVO::getLeaveReason,
                        Collectors.summingInt(FtbPersonnelsTurnoverLeaveReasonDistributionVO::getCount)));
        // 原因统计
        return stringIntegerMap.entrySet().stream().map(item -> {
            FtbPersonnelsTurnoverLeaveReasonDistributionVO leaveReasonDistributionVO = new FtbPersonnelsTurnoverLeaveReasonDistributionVO();
            leaveReasonDistributionVO.setLeaveReason(item.getKey());
            leaveReasonDistributionVO.setCount(item.getValue());
            return leaveReasonDistributionVO;
        }).collect(Collectors.toList());
    }

    @Override
    public List<FtbPersonnelsTurnoverLeavePeoplePostRatioVO> getLeavePeoplePostRatio(FtbPersonnlesAnalysisDTO dto) {
        List<String> positionIds = dto.getPostId();
        String[] str = new String[0];
        if (CollUtil.isNotEmpty(positionIds)) {
            str = positionIds.toArray(String[]::new);
        }
        List<String> organizationId = dto.getOrganizationId();
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(organizationId, str);
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new ArrayList<>();
        }
        // 离职人员岗位信息对应
        List<Map<String,String>>  postRatioList = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeoplePostRatio(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        // 转化为map
        List<List<FtbPersonnelsTurnoverLeavePeoplePostRatioVO>> lists = postRatioList.stream().map(item -> {
            List<WorkerGroupDataDto> workerGroupDataDtos = JSONObject.parseArray(item.get("organizeInfo"), WorkerGroupDataDto.class);
            // 做数据过滤
            return workerGroupDataDtos.stream().filter(vo->{
                List<String> dtoOrganizationIds = dto.getOrganizationId();
                List<String> dtoPostIds = dto.getPostId();
                if (CollUtil.isNotEmpty(dtoOrganizationIds)
                        && CollUtil.isNotEmpty(dtoPostIds)
                        && dtoOrganizationIds.contains(vo.getAffiliatedOrg())
                        && dtoPostIds.contains(vo.getAffiliatedPosition())){
                    return true;
                }
                if (CollUtil.isNotEmpty(dtoOrganizationIds)
                        && CollUtil.isEmpty(dtoPostIds)
                        && dtoOrganizationIds.contains(vo.getAffiliatedOrg())){
                    return true;
                }
                if ( CollUtil.isEmpty(dtoOrganizationIds)
                        && CollUtil.isNotEmpty(dtoPostIds)
                        && dtoPostIds.contains(vo.getAffiliatedPosition())){
                    return true;
                }
                if (CollUtil.isEmpty(dtoOrganizationIds)
                        && CollUtil.isEmpty(dtoPostIds)){
                    return true;
                }
                return false;
            }).map(vo -> {
                FtbPersonnelsTurnoverLeavePeoplePostRatioVO ratioVO = new FtbPersonnelsTurnoverLeavePeoplePostRatioVO();
                ratioVO.setPostName(vo.getAffiliatedPositionName());
                ratioVO.setPostId(vo.getAffiliatedPosition());
                ratioVO.setCount(1);
                return ratioVO;
            }).collect(Collectors.toList());
        }).collect(Collectors.toList());
        // 统计
        Map<String, Integer> stringIntegerMap = lists.stream().flatMap(List::stream).collect(
                Collectors.groupingBy(vo->vo.getPostId()+"::"+vo.getPostName(),
                        Collectors.summingInt(FtbPersonnelsTurnoverLeavePeoplePostRatioVO::getCount)));
        //  总人数表示：指定岗位下截至筛选月以前的在职人数+筛选月当月的岗位离职人数
        return stringIntegerMap.entrySet().stream().map(item -> {
            FtbPersonnelsTurnoverLeavePeoplePostRatioVO postRatioVO = new FtbPersonnelsTurnoverLeavePeoplePostRatioVO();
            String itemKey = item.getKey();
            String[] split = itemKey.split("::");
            String postId = split[0];
            postRatioVO.setPostId(postId);
            String postName = split[1];
            postRatioVO.setPostName(postName);
            // 离职人数
            Integer count = item.getValue();
            postRatioVO.setCount(count);
            // 获取总人数
            // 截止月份在职员工
            Integer totalCount1 = getTotalCount(postId, dto.getEndTime(),organizationId);
            //  总人数表示：指定岗位下截至筛选月以前的在职人数+筛选月当月的岗位离职人数
            int totalCount = BigDecimal.valueOf(totalCount1).add(BigDecimal.valueOf(count)).intValue();
            postRatioVO.setTotalCount(totalCount);
            // 占比率
            postRatioVO.setRatio(calculateTheScale(count,totalCount));
            return postRatioVO;
        }).collect(Collectors.toList());
    }
    public Integer getTotalCount(String postId, Date startTime, List<String> organizationId) {
        QueryListUserByLevelDTO userBatchDTO = new QueryListUserByLevelDTO();
        userBatchDTO.setPositionIds(List.of(postId));
        userBatchDTO.setOrganizeIds(organizationId);
        ActionResult<List<UserBoundVO>> userInfoBatch = v2UserApi.listUserByLevel(userBatchDTO);
        if (userInfoBatch.getCode() == 200) {
            DateTime dateTime = DateUtil.endOfMonth(startTime);
            return Math.toIntExact(userInfoBatch.getData().stream().filter(v -> (v.getWorkStatusEnums() != null && v.getEntryDate() != null &&
                    ON_THE_JOB_STATUS.contains(v.getWorkStatusEnums().getCode())) &&
                    dateTime.after(v.getEntryDate())).count());
        }
        return 0;
    }

    // 离职人员司龄类型
    // 1 不满3个月
    // 2 3-6个月
    // 3 7个月～1年
    // 4 1～3年
    // 5 4～5年
    // 6 6～10年
    // 7 10年以上
    private static final List<Integer> LEAVE_RANGE = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

    @Override
    public List<FtbPersonnelsTurnoverLeavePeopleAgeDistributionVO> getLeavePeopleAgeDistribution(FtbPersonnlesAnalysisDTO dto) {
        // 初始化司龄类型
        List<FtbPersonnelsTurnoverLeavePeopleAgeDistributionVO> list = LEAVE_RANGE.stream().map(item -> {
            FtbPersonnelsTurnoverLeavePeopleAgeDistributionVO leavePeopleAgeDistributionVO = new FtbPersonnelsTurnoverLeavePeopleAgeDistributionVO();
            leavePeopleAgeDistributionVO.setLeavePeopleAgeDistributionType(item);
            leavePeopleAgeDistributionVO.setCount(0);
            leavePeopleAgeDistributionVO.setRatio(BigDecimal.ZERO);
            return leavePeopleAgeDistributionVO;
        }).collect(Collectors.toList());
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return list;
        }
        // 离职总人数
        Integer lastInstallment = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleMonth(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        List<Map<String,Object>> mapList = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleAgeDistribution(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        list.forEach(item->{
            // 计算不同的司龄
            Integer distributionType = item.getLeavePeopleAgeDistributionType();
            Predicate<Map<String, Object>> predicate = null;
            switch (distributionType) {
                // 1 不满3个月
                case 1:
                    predicate =  vo ->Double.parseDouble(vo.get("deff").toString())  <= 30 * 3;
                    break;
                // 2 3-6个月
                case 2:
                    predicate =  vo ->Double.parseDouble(vo.get("deff").toString())  > 30 * 3 && Double.parseDouble(vo.get("deff").toString())  <= 30 * 6;
                    break;
                // 3 7个月～1年
                case 3:
                    predicate =  vo ->Double.parseDouble(vo.get("deff").toString())  > 30 * 6 && Double.parseDouble(vo.get("deff").toString())  <= 365;
                    break;
                // 4 1～3年
                case 4:
                    predicate =  vo ->Double.parseDouble(vo.get("deff").toString())  > 365 && Double.parseDouble(vo.get("deff").toString())  <= 365 * 3;
                    break;
                // 5 4～5年
                case 5:
                    predicate =  vo ->Double.parseDouble(vo.get("deff").toString())  > 365 * 3 && Double.parseDouble(vo.get("deff").toString())  <= 365 * 5;
                    break;
                // 6 6～10年
                case 6:
                    predicate =  vo ->Double.parseDouble(vo.get("deff").toString())  > 365 * 5 && Double.parseDouble(vo.get("deff").toString())  <= 365 * 10;
                    break;
                // 7 10年以上
                case 7:
                    predicate =  vo ->Double.parseDouble(vo.get("deff").toString())  > 365 * 10;
                    break;
            }
            Long count = mapList.stream().filter(v->ObjectUtil.isNotNull(v.get("deff"))).filter(predicate).count();
            item.setCount(count.intValue());
            // 计算比例
            item.setRatio( calculateTheScale(count.intValue(), lastInstallment));
        });
        return list;
    }

    @Override
    public List<FtbPersonnelsTurnoverLeavePeopleAgeRatioVO> getLeavePeopleAgeRatio(FtbPersonnlesAnalysisDTO dto) {
        //离职人员类型
        Integer configType = personnlesInfoConfigService.queryType(1);
        if (configType == null) {
            throw new RuntimeException("获取年龄配置失败");
        }
        // 1平均 2自定义
        List<RangeDTO> rangeDTOS;
        if (configType  == 1){
            FtbRangeConfigVO configVO = personnlesInfoConfigService.queryInfo(1);
            rangeDTOS = RangeConfigUtil.generateRanges(configVO.getFirstGear(), configVO.getIntervalValue(), configVO.getNumberOfGears(), configVO.getPreviewValue());
        }else {
            List<FtbRangeConfigDIYVO> rangeConfigDIYVOS = personnlesInfoConfigService.queryDiyInfo(1);
            rangeDTOS = rangeConfigDIYVOS.stream().map(v->{
                RangeDTO rangeDTO = new RangeDTO();
                rangeDTO.setStartPrice(v.getStartInterval());
                rangeDTO.setEndPrice(v.getEndInterval());
                rangeDTO.setPriceBandDisplayStr(v.getPreviewValue());
                return rangeDTO;
            }).collect(Collectors.toList());
        }
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return rangeDTOS.stream().map(v->{
                FtbPersonnelsTurnoverLeavePeopleAgeRatioVO ageRatioVO = new FtbPersonnelsTurnoverLeavePeopleAgeRatioVO();
                ageRatioVO.setLeavePeopleAgeRatioName(v.getPriceBandDisplayStr());
                ageRatioVO.setCount(0);
                ageRatioVO.setRatio(BigDecimal.ZERO);
                return ageRatioVO;
            }).collect(Collectors.toList());
        }
        // 离职总人数
        //Integer lastInstallment = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleMonth(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        List<Map<String, Object>> mapList = dynamicallyObtainTheRequiredLogFieldValue(dto, userIdListByOrganizeIds);
        return rangeDTOS.stream().map(a->{
            FtbPersonnelsTurnoverLeavePeopleAgeRatioVO ageRatioVO = new FtbPersonnelsTurnoverLeavePeopleAgeRatioVO();
            ageRatioVO.setLeavePeopleAgeRatioName(a.getPriceBandDisplayStr());
            Integer startPrice = a.getStartPrice();
            Integer endPrice = a.getEndPrice();
            long count = mapList.stream().filter(v -> {
                if (StringUtils.isEmpty(v.get("age").toString())) return false;
                BigDecimal decimal = BigDecimal.valueOf(Double.parseDouble(v.get("age").toString()));
                BigDecimal startPriceDecimal = BigDecimal.valueOf(startPrice);
                if (endPrice == null) {
                    if (decimal.compareTo(startPriceDecimal) >= 0) {
                        return true;
                    }
                }else {
                    BigDecimal endPriceDecimal = BigDecimal.valueOf(endPrice);
                    if ( decimal.compareTo(startPriceDecimal) >= 0 && decimal.compareTo(endPriceDecimal) < 0) {
                        // 条件成立时的逻辑
                        return true;
                    }
                }
                // 判断是否在范围内
                return false;
            }).count();
            ageRatioVO.setRatio(calculateTheScale((int) count, mapList.size()));
            ageRatioVO.setCount((int) count);
            return ageRatioVO;
        }).collect(Collectors.toList());
    }

    @NotNull
    private List<Map<String, Object>> dynamicallyObtainTheRequiredLogFieldValue(FtbPersonnlesAnalysisDTO dto, List<String> userIdListByOrganizeIds) {
        // 离职人员的数据项
        List<Map<String, Object>> mapListWithLog = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleAgeLogRatio(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds, "birthday");
        List<Map<String, Object>> mapList = mapListWithLog.stream().map(
                item -> {
                    if (ObjectUtil.isEmpty( item.get("data")) || StringUtils.isEmpty((String) item.get("data"))) return null;
                    String data = item.get("data").toString();
                    Map<String, Object> map = new HashMap<>();
                    if (StringUtils.isNotEmpty(data) ) {
                        // 将字符串转换为 Date 对象
                        long betweenYear = DateUtil.ageOfNow(data);
                        map.put("age", betweenYear);
                        map.put("userId", item.get("userId"));
                    }
                    return map;
                }
        ).filter(ObjectUtil::isNotEmpty).collect(Collectors.toList());
        return mapList;
    }
    @NotNull
    private List<String> dynamicallyObtainTheRequiredLogFieldValueWithWorkSex(FtbPersonnlesAnalysisDTO dto, List<String> userIdListByOrganizeIds,String number) {
        // 离职人员的数据项
        List<Map<String, Object>> mapListWithLog = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleAgeLogRatio(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds,"workerSex");
        List<String> mapList = mapListWithLog.stream().map(
                item -> {
                    if (ObjectUtil.isEmpty( item.get("data")) || StringUtils.isEmpty((String) item.get("data"))) return null;
                    String data = item.get("data").toString();
                    // 0 未知 1 男 2 女
                    if (StringUtils.isNotEmpty(data) && number.equals(data) ) {
                        // 将字符串转换为 Date 对象
                        return item.get("userId").toString();
                    }
                    return null;
                }
        ).filter(ObjectUtil::isNotEmpty).collect(Collectors.toList());
        return mapList;
    }

    @Override
    public FtbPersonnelsTurnoverRateVO ftbPersonnelsTurnoverAnalysisService(FtbPersonnlesAnalysisDTO dto) {
        FtbPersonnelsTurnoverRateVO turnoverRateVO = new FtbPersonnelsTurnoverRateVO();
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new FtbPersonnelsTurnoverRateVO();
        }
        List<String> listOfDates = new LinkedList<>();
        // 当月离职人数
        List<Integer> theNumberOfDeparturesInTheMonth = new LinkedList<>();
        // 首年离职率
        List<BigDecimal> firstYearTurnoverRate = new LinkedList<>();;
        // 首月离职率
        List<BigDecimal> turnoverRateInTheFirstMonth = new LinkedList<>();
        // 日期列表
        long betweenMonth = DateUtil.betweenMonth(dto.getStartTime(), dto.getEndTime(), true);
        for (int i = 0; i <= betweenMonth; i++) {
            // 月份
            Date offsetTime = DateUtil.offset(dto.getStartTime(), DateField.MONTH, i).toJdkDate();
            // 构建时间
            DateTime monthStartTime = DateUtil.beginOfMonth(offsetTime);
            DateTime monthEndTime = DateUtil.endOfMonth(offsetTime);
            Integer peopleMonth = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleMonth(monthStartTime,monthEndTime,userIdListByOrganizeIds);
            // 当月离职人数
            theNumberOfDeparturesInTheMonth.add(peopleMonth);
            // 首月离职率
            BigDecimal firstMonthLeaveRate = turnoverRateSequentially(monthStartTime, monthEndTime, userIdListByOrganizeIds,FIRST_MONTH, dto.getOrganizationId());
            turnoverRateInTheFirstMonth.add(firstMonthLeaveRate);
            // 首年离职率
            BigDecimal sequentially = turnoverRateSequentially(monthStartTime, monthEndTime, userIdListByOrganizeIds, FIRST_YEAR, dto.getOrganizationId());
            firstYearTurnoverRate.add(sequentially);
            // 封装
            String month = DatePattern.NORM_MONTH_FORMAT.format(offsetTime);
            listOfDates.add(month);
        }
        // 日期列表
        turnoverRateVO.setListOfDates(listOfDates);
        turnoverRateVO.setTheNumberOfDeparturesInTheMonth(theNumberOfDeparturesInTheMonth);
        turnoverRateVO.setTurnoverRateInTheFirstMonth(turnoverRateInTheFirstMonth);
        turnoverRateVO.setFirstYearTurnoverRate(firstYearTurnoverRate);
        return turnoverRateVO;
    }
    @Override
    public PageListVO<FtbPersonnelsTurnoverDetailVO> getLeaveRateDetail(FtbPersonnlesAnalysisDTO dto) {
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new PageListVO<>();
        }
        // 构建时间
        DateTime monthStartTime = DateUtil.beginOfMonth(dto.getStartTime());
        DateTime monthEndTime = DateUtil.endOfMonth(dto.getEndTime());
        // 获取离职人员明细数据
        List<Map<String, Object>> leaveRateDetail = ftbPersonnelsTurnoverAnalysisMapper.getLeaveRateDetail(monthStartTime, monthEndTime, userIdListByOrganizeIds);
        // 转换为VO对象列表
        List<FtbPersonnelsTurnoverDetailVO> collect = leaveRateDetail.stream().map(item -> {
            FtbPersonnelsTurnoverDetailVO detailVO = new FtbPersonnelsTurnoverDetailVO();
            detailVO.setName((String) item.get("name"));
            detailVO.setEmployeeId((String) item.get("employeeId"));
            detailVO.setEntryDate(CovertDateUtils.convertToStringDate( item.get("entryDate")));
            detailVO.setLeaveDate( CovertDateUtils.convertToStringDate(item.get("leaveDate")));

            // 解析组织信息
            if (item.containsKey("organizationInfo")) {
                Object o = item.get("organizationInfo");
                String organizationInfo = (String) o;
                List<WorkerGroupDataDto> workerGroupDataDtos = JSONObject.parseArray(organizationInfo, WorkerGroupDataDto.class);
                if (CollUtil.isNotEmpty(workerGroupDataDtos)) {
                    WorkerGroupDataDto firstOrg = workerGroupDataDtos.get(0);
                    detailVO.setOrganization(firstOrg.getAffiliatedOrgName());
                    detailVO.setJobLevel(firstOrg.getAffiliatedPositionName());
                    detailVO.setJobGrade(firstOrg.getAffiliatedRankName());
                }
            }

            return detailVO;
        }).collect(Collectors.toList());
        PaginationVO paginationVO = new PaginationVO();
        paginationVO.setCurrentPage(dto.getCurrentPage());
        paginationVO.setPageSize(dto.getPageSize());
        paginationVO.setTotal(collect.size());
        return CultivatePage.paginate( collect,paginationVO);
    }

    @Override
    public FtbPersonnelsTurnoverRateComparisonVO getLeaveRateComparison(FtbPersonnlesAnalysisDTO dto) {
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return new FtbPersonnelsTurnoverRateComparisonVO();
        }
        // 离职人员岗位信息对应
        List<Map<String,String>>  postRatioList = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeoplePostRatio(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        //  1部门 2 门店 3岗位
        Integer type = dto.getType();
        List<Map<String, String>> maps = postRatioList.stream().map(item -> {
            Map<String, String> map = new HashMap<>();
            List<WorkerGroupDataDto> workerGroupDataDtos = JSONObject.parseArray(item.get("organizeInfo"), WorkerGroupDataDto.class);
            map.put("userId", item.get("userId"));
            Map<String, String> mapList = workerGroupDataDtos.stream().map(vo -> {
                Map<String, String> hashMap = new HashMap<>();
                String id = null;
                String name = null;
                // 部门
                if (type == 1) {
                    id = vo.getDepartmentId();
                    name = vo.getDepartmentName();
                } else if (type == 2) {
                    // 2 门店
                    id = vo.getShopId();
                    name = vo.getShopName();
                } else if (type == 3) {
                    // 3 岗位
                    id = vo.getAffiliatedPosition();
                    name = vo.getAffiliatedPositionName();
                }
                hashMap.put("id", id);
                hashMap.put("name", name);
                return hashMap;
            }).filter(ObjectUtil::isNotEmpty).findFirst().orElse(new HashMap<>());
            map.put("workerGroupDataDtos", JSONObject.toJSONString(mapList));
            return map;
        }).collect(Collectors.toList());
        // 根据过滤数据进行分组 计算人数
        List<FtbPersonnelsTurnoverRateInfoVO> rateInfoVOS = maps.stream().collect(Collectors.groupingBy(item -> item.get("workerGroupDataDtos"),
                        Collectors.mapping(item -> item.get("userId"), Collectors.toList())))
                .entrySet().stream().map(vo -> {
                    FtbPersonnelsTurnoverRateInfoVO rateInfoVO = new FtbPersonnelsTurnoverRateInfoVO();
                    Object name = JSONObject.parseObject(vo.getKey(), Map.class).get("name");
                    // 对应分类名称
                    rateInfoVO.setName(String.valueOf(name));
                    // 离职人数
                    int totalNumberOfPeople = postRatioList.size();
                    rateInfoVO.setTotal(totalNumberOfPeople);
                    int percentageOfPeople = vo.getValue().size();
                    rateInfoVO.setTurnOverRate(calculateTheScale(percentageOfPeople, totalNumberOfPeople));
                    return rateInfoVO;
                }).collect(Collectors.toList());
        FtbPersonnelsTurnoverRateComparisonVO ftbPersonnelsTurnoverRateComparisonVO = new FtbPersonnelsTurnoverRateComparisonVO();
        ftbPersonnelsTurnoverRateComparisonVO.setList(rateInfoVOS);
        ftbPersonnelsTurnoverRateComparisonVO.setType(type);
        return ftbPersonnelsTurnoverRateComparisonVO;
    }



    @Override
    public PageListVO<FtbPersonnelsTurnoverNewEmployeeDetailVO> getNewEmployeeLeaveRateDetail(FtbPersonnlesAnalysisDTO dto) {
        List<FtbPersonnelsTurnoverNewEmployeeDetailVO> results = new ArrayList<>();
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return CultivatePage.paginate(results, new PaginationVO());
        }
        // 新员工定义配置
        FtbPersonnelsRuleConfig ruleConfig = ftbPersonnelsRuleConfigMapper.selectById(1);
        Integer timeRule = ruleConfig.getTimeRule();
        Integer type = ruleConfig.getType();
        // 天数
        timeRule = type == 2 ? timeRule * 30 : timeRule;
        // 获取新员工离职人员
        List<Map<String, Object>> leaveRateDetail = ftbPersonnelsTurnoverAnalysisMapper.getLeaveRateDetail(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        // 转换为 VO 对象列表
        for (Map<String, Object> item : leaveRateDetail) {
            Date entryDate = CovertDateUtils.convertToDate(item.get("entryDate"));
            Date leaveDate = CovertDateUtils.convertToDate(item.get("leaveDate"));
            if (entryDate != null && leaveDate != null) {
                long days = DateUtil.betweenDay(entryDate, leaveDate, true);
                if (days <= timeRule) {
                    FtbPersonnelsTurnoverNewEmployeeDetailVO detailVO = new FtbPersonnelsTurnoverNewEmployeeDetailVO();
                    detailVO.setName((String) item.get("name"));
                    detailVO.setEmployeeId((String) item.get("employeeId"));
                    String toStringDate = CovertDateUtils.convertToStringDate(item.get("leaveDate"));
                    detailVO.setLeaveDate(toStringDate);
                    // 计算司龄
                    detailVO.setSeniority(CompanyAgeUtil.calculateSeniority(entryDate, leaveDate));
                    // 解析组织信息
                    String organizationInfo = (String) item.get("organizationInfo");
                    if (StringUtils.isNotEmpty(organizationInfo)) {
                        List<WorkerGroupDataDto> workerGroupDataDtos = JSONObject.parseArray(organizationInfo, WorkerGroupDataDto.class);
                        if (CollUtil.isNotEmpty(workerGroupDataDtos)) {
                            WorkerGroupDataDto firstOrg = workerGroupDataDtos.get(0);
                            detailVO.setOrganization(firstOrg.getAffiliatedOrgName());
                            detailVO.setJobLevel(firstOrg.getAffiliatedPositionName());
                            detailVO.setJobGrade(firstOrg.getAffiliatedRankName());
                        }
                    }

                    results.add(detailVO);
                }
            }
        }
        results.sort(Comparator.comparing(FtbPersonnelsTurnoverNewEmployeeDetailVO::getOrganization));
        PaginationVO pagination = new PaginationVO();
        pagination.setCurrentPage(dto.getCurrentPage());
        pagination.setPageSize(dto.getPageSize());
        pagination.setTotal(results.size());
        return CultivatePage.paginate(results, pagination);
    }

    @Override
    public PageListVO<FtbPersonnelsTurnoverLeaveReasonDetailVO> getLeaveReasonDistributionDetail(FtbPersonnlesAnalysisDTO dto) {
        List<FtbPersonnelsTurnoverLeaveReasonDetailVO> results = new ArrayList<>();
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return CultivatePage.paginate(results, new PaginationVO());
        }
        // 获取离职人员明细数据
        List<Map<String, Object>> leaveRateDetail = ftbPersonnelsTurnoverAnalysisMapper.getLeaveRateDetail(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        // 转换为 VO 对象列表
        results = leaveRateDetail.stream().map(item -> {
            FtbPersonnelsTurnoverLeaveReasonDetailVO detailVO = new FtbPersonnelsTurnoverLeaveReasonDetailVO();
            detailVO.setName((String) item.get("name"));
            detailVO.setEmployeeId((String) item.get("employeeId"));
            detailVO.setLeaveDate(CovertDateUtils.convertToStringDate(item.get("leaveDate")));

            // 解析组织信息
            String organizationInfo = (String) item.get("organizationInfo");
            if (StringUtils.isNotEmpty(organizationInfo)) {
                List<WorkerGroupDataDto> workerGroupDataDtos = JSONObject.parseArray(organizationInfo, WorkerGroupDataDto.class);
                if (CollUtil.isNotEmpty(workerGroupDataDtos)) {
                    WorkerGroupDataDto firstOrg = workerGroupDataDtos.get(0);
                    detailVO.setOrganization(firstOrg.getAffiliatedOrgName());
                    detailVO.setJobLevel(firstOrg.getAffiliatedPositionName());
                    detailVO.setJobGrade(firstOrg.getAffiliatedRankName());
                }
            }

            // 获取离职原因
            detailVO.setLeaveReason((String) item.get("reason"));
            return detailVO;
        }).collect(Collectors.toList());
        results.sort(Comparator.comparing(FtbPersonnelsTurnoverLeaveReasonDetailVO::getOrganization));
        PaginationVO pagination = new PaginationVO();
        pagination.setCurrentPage(dto.getCurrentPage());
        pagination.setPageSize(dto.getPageSize());
        pagination.setTotal(results.size());
        return CultivatePage.paginate(results, pagination);
    }


    @Override
    public PageListVO<FtbPersonnelsTurnoverPostRatioDetailVO> getLeavePeoplePostRatioDetail(FtbPersonnlesAnalysisDTO dto) {
        List<FtbPersonnelsTurnoverPostRatioDetailVO> results = new ArrayList<>();
        List<String> positionIds = dto.getPostId();
        String[] str = new String[0];
        if (CollUtil.isNotEmpty(positionIds)) {
            str = positionIds.toArray(String[]::new);
        }
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId(),str);
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return CultivatePage.paginate(results, new PaginationVO());
        }
        // 获取离职人员明细数据
        List<Map<String, Object>> leaveRateDetail = ftbPersonnelsTurnoverAnalysisMapper.getLeaveRateDetail(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        // 转换为 VO 对象列表
        results = leaveRateDetail.stream().filter(item -> item.get("organizationInfo") != null).map(item -> {
            FtbPersonnelsTurnoverPostRatioDetailVO detailVO = new FtbPersonnelsTurnoverPostRatioDetailVO();
            detailVO.setName((String) item.get("name"));
            detailVO.setEmployeeId((String) item.get("employeeId"));
            detailVO.setLeaveDate(CovertDateUtils.convertToStringDate(item.get("leaveDate")));

            // 解析组织信息
            String organizationInfo = (String) item.get("organizationInfo");
            if (StringUtils.isNotEmpty(organizationInfo)) {
                List<WorkerGroupDataDto> workerGroupDataDtos = JSONObject.parseArray(organizationInfo, WorkerGroupDataDto.class);
                if (CollUtil.isNotEmpty(workerGroupDataDtos)) {
                    WorkerGroupDataDto firstOrg = workerGroupDataDtos.get(0);
                    detailVO.setOrganization(firstOrg.getAffiliatedOrgName());
                    detailVO.setJobLevel(firstOrg.getAffiliatedPositionName());
                    detailVO.setJobGrade(firstOrg.getAffiliatedRankName());
                }
            }

            return detailVO;
        }).collect(Collectors.toList());
        results.sort(Comparator.comparing(FtbPersonnelsTurnoverPostRatioDetailVO::getOrganization));
        PaginationVO pagination = new PaginationVO();
        pagination.setCurrentPage(dto.getCurrentPage());
        pagination.setPageSize(dto.getPageSize());
        pagination.setTotal(results.size());
        return CultivatePage.paginate(results, pagination);
    }


    @Override
    public PageListVO<FtbPersonnelsTurnoverSeniorityDetailVO> getLeavePeopleAgeDistributionDetail(FtbPersonnlesAnalysisDTO dto) {
        List<FtbPersonnelsTurnoverSeniorityDetailVO> results = new ArrayList<>();
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return CultivatePage.paginate(results, new PaginationVO());
        }
        // 获取离职人员明细数据
        List<Map<String, Object>> leaveRateDetail = ftbPersonnelsTurnoverAnalysisMapper.getLeaveRateDetail(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        // 转换为 VO 对象列表
        results = leaveRateDetail.stream().map(item -> {
            FtbPersonnelsTurnoverSeniorityDetailVO detailVO = new FtbPersonnelsTurnoverSeniorityDetailVO();
            detailVO.setName((String) item.get("name"));
            detailVO.setEmployeeId((String) item.get("employeeId"));
            detailVO.setLeaveDate(CovertDateUtils.convertToStringDate(item.get("leaveDate")));

            // 计算司龄
            Date entryDate = CovertDateUtils.convertToDate(item.get("entryDate"));
            Date leaveDate = CovertDateUtils.convertToDate(item.get("leaveDate"));
            if (entryDate != null && leaveDate != null) {
                detailVO.setSeniority(CompanyAgeUtil.calculateSeniority(entryDate, leaveDate));
            }

            // 解析组织信息
            String organizationInfo = (String) item.get("organizationInfo");
            if (StringUtils.isNotEmpty(organizationInfo)) {
                List<WorkerGroupDataDto> workerGroupDataDtos = JSONObject.parseArray(organizationInfo, WorkerGroupDataDto.class);
                if (CollUtil.isNotEmpty(workerGroupDataDtos)) {
                    WorkerGroupDataDto firstOrg = workerGroupDataDtos.get(0);
                    detailVO.setOrganization(firstOrg.getAffiliatedOrgName());
                    detailVO.setJobLevel(firstOrg.getAffiliatedPositionName());
                    detailVO.setJobGrade(firstOrg.getAffiliatedRankName());
                }
            }

            return detailVO;
        }).collect(Collectors.toList());
        results.sort(Comparator.comparing(FtbPersonnelsTurnoverSeniorityDetailVO::getOrganization));
        PaginationVO pagination = new PaginationVO();
        pagination.setCurrentPage(dto.getCurrentPage());
        pagination.setPageSize(dto.getPageSize());
        pagination.setTotal(results.size());
        return CultivatePage.paginate(results, pagination);
    }


    @Override
    public PageListVO<FtbPersonnelsTurnoverAgeDetailVO> getLeavePeopleAgeRatioDetail(FtbPersonnlesAnalysisDTO dto) {
        List<FtbPersonnelsTurnoverAgeDetailVO> results = new ArrayList<>();
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return CultivatePage.paginate(results, new PaginationVO());
        }
        // 获取离职人员年龄信息
        List<Map<String, Object>> mapList = dynamicallyObtainTheRequiredLogFieldValue(dto, userIdListByOrganizeIds);
        // 获取离职人员明细数据
        List<Map<String, Object>> leaveRateDetail = ftbPersonnelsTurnoverAnalysisMapper.getLeaveRateDetail(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds);
        Map<String, Map<String, Object>> leaveRateMap = leaveRateDetail.stream().collect(Collectors.toMap(item -> (String) item.get("userId"), item -> item));

        // 转换为VO对象列表
        for (Map<String, Object> item : mapList) {
            String userId = (String) item.get("userId");
            Map<String, Object> leaveInfo = leaveRateMap.get(userId);
            if (leaveInfo != null) {
                FtbPersonnelsTurnoverAgeDetailVO detailVO = new FtbPersonnelsTurnoverAgeDetailVO();
                detailVO.setName((String) leaveInfo.get("name"));
                detailVO.setEmployeeId((String) leaveInfo.get("employeeId"));
                detailVO.setLeaveDate(CovertDateUtils.convertToStringDate(leaveInfo.get("leaveDate")));
                detailVO.setAge(Integer.parseInt(item.get("age").toString()));

                // 解析组织信息
                String organizationInfo = (String) leaveInfo.get("organizationInfo");
                if (StringUtils.isNotEmpty(organizationInfo)) {
                    List<WorkerGroupDataDto> workerGroupDataDtos = JSONObject.parseArray(organizationInfo, WorkerGroupDataDto.class);
                    if (CollUtil.isNotEmpty(workerGroupDataDtos)) {
                        WorkerGroupDataDto firstOrg = workerGroupDataDtos.get(0);
                        detailVO.setOrganization(firstOrg.getAffiliatedOrgName());
                        detailVO.setJobLevel(firstOrg.getAffiliatedPositionName());
                        detailVO.setJobGrade(firstOrg.getAffiliatedRankName());
                    }
                }

                results.add(detailVO);
            }
        }
        results.sort(Comparator.comparing(FtbPersonnelsTurnoverAgeDetailVO::getOrganization));
        PaginationVO pagination = new PaginationVO();
        pagination.setCurrentPage(dto.getCurrentPage());
        pagination.setPageSize(dto.getPageSize());
        pagination.setTotal(results.size());
        return CultivatePage.paginate(results, pagination);
    }

    @Override
    public PageListVO<FtbPersonnelsTurnoverGenderDetailVO> breakdownOfGenderDistributionOfResignation(FtbPersonnlesAnalysisDTO dto) {
        List<FtbPersonnelsTurnoverGenderDetailVO> results = new ArrayList<>();
        List<String> userIdListByOrganizeIds = doEmployeesOfYourOrganization(dto.getOrganizationId());
        if (CollUtil.isEmpty(userIdListByOrganizeIds)) {
            return CultivatePage.paginate(results, new PaginationVO());
        }
        // 获取离职人员年龄信息
        List<Map<String, Object>> mapListWithList = ftbPersonnelsTurnoverAnalysisMapper.
                getLeavePeopleAgeLogRatio(dto.getStartTime(), dto.getEndTime(), userIdListByOrganizeIds,"workerSex");
        List<String> genderList = List.of("0","1","2");
        mapListWithList =  mapListWithList.stream().filter(v->{
           if (ObjectUtil.isEmpty( v.get("data")) || StringUtils.isEmpty( v.get("data").toString())) return false;
            String string = v.get("data").toString();
            if (genderList.contains(string)) return true;
            return false;
        }).collect(Collectors.toList());
        if (CollUtil.isEmpty(mapListWithList)) {
            return CultivatePage.paginate(results, new PaginationVO());
        }
        // 获取离职人员明细数据
        Map<String, Map<String, Object>> leaveRateMap = mapListWithList.stream().collect(Collectors.toMap(item -> (String) item.get("userId"), item -> item));
        List<String> strings = mapListWithList.stream().map(v -> (String) v.get("phone")).collect(Collectors.toList());
        // 转换为VO对象列表
        for (Map<String, Object> item : mapListWithList) {
            String userId = (String) item.get("userId");
            Map<String, Object> leaveInfo = leaveRateMap.get(userId);
            if (leaveInfo != null) {
                FtbPersonnelsTurnoverGenderDetailVO detailVO = new FtbPersonnelsTurnoverGenderDetailVO();
                detailVO.setName((String) leaveInfo.get("name"));
                detailVO.setEmployeeId((String) leaveInfo.get("employeeId"));
                detailVO.setLeaveDate(CovertDateUtils.convertToStringDate(leaveInfo.get("leaveDate")));
                String phone = (String)item.get("phone");
                if (ObjectUtil.isNotEmpty(item.get("data"))) {
                    String value = item.get("data").toString();
                    // 0 未知 1 男 2 女
                    String gender = null;
                     switch (value){
                         case "0":
                             gender = "未知";
                             break;
                         case "1":
                             gender = "男";
                             break;
                         case "2":
                             gender = "女";
                             break;
                     }
                    detailVO.setGender(gender);
                }
                // 解析组织信息
                String organizationInfo = (String) leaveInfo.get("organizationInfo");
                if (StringUtils.isNotEmpty(organizationInfo)) {
                    List<WorkerGroupDataDto> workerGroupDataDtos = JSONObject.parseArray(organizationInfo, WorkerGroupDataDto.class);
                    if (CollUtil.isNotEmpty(workerGroupDataDtos)) {
                        WorkerGroupDataDto firstOrg = workerGroupDataDtos.get(0);
                        detailVO.setOrganization(firstOrg.getAffiliatedOrgName());
                        detailVO.setJobLevel(firstOrg.getAffiliatedPositionName());
                        detailVO.setJobGrade(firstOrg.getAffiliatedRankName());
                    }
                }
                results.add(detailVO);
            }
        }
        results.sort(Comparator.comparing(FtbPersonnelsTurnoverGenderDetailVO::getOrganization));
        PaginationVO pagination = new PaginationVO();
        pagination.setCurrentPage(dto.getCurrentPage());
        pagination.setPageSize(dto.getPageSize());
        pagination.setTotal(results.size());
        return CultivatePage.paginate(results, pagination);
    }
    /**
     * 计算同比环比
     * @return
     */
    private  BigDecimal yearOnYearAndMonthOnMonth(Object a, Object b) {
        if (a == null || b == null) {
            return BigDecimal.ZERO;
        }
        // a/b 除数为0
        if (b instanceof Integer) {
            Integer b0 = (Integer) b;
            if (b0 == 0) {
                return BigDecimal.ZERO;
            }
        }
        if (b instanceof Long) {
            Long b0 = (Long) b;
            if (b0 == 0) {
                return BigDecimal.ZERO;
            }
        }
        if (b instanceof BigDecimal) {
            if (((BigDecimal) b).compareTo(BigDecimal.ZERO) == 0) {
                return BigDecimal.ZERO;
            }
        }
        BigDecimal a0 = new BigDecimal(String.valueOf(a));
        BigDecimal b0 = new BigDecimal(String.valueOf(b));
        return a0.subtract(b0).divide(b0,4, BigDecimal.ROUND_HALF_UP)
                .multiply(BigDecimal.valueOf(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 获取组织下员工ID集合
     * @param orgIds
     * @return
     */
    private List<String> doEmployeesOfYourOrganization(List<String> orgIds, String[]... postId) {
        if (CollUtil.isNotEmpty(orgIds)){
            List<FtbPersonnelsTurnoverManagementVO> managements = turnoverManagementMapper.queryTurnoverListWithStatics();
            List<String> postIds;
            if (ObjectUtil.isNotEmpty(postId)){
                postIds = Arrays.stream(postId).flatMap(Arrays::stream).collect(Collectors.toList());
            } else {
                postIds = null;
            }
            if (CollUtil.isNotEmpty(orgIds) && CollUtil.isEmpty(postIds))
                return managements.stream().filter(vo -> StringUtils.isNotEmpty(vo.getOrganizationInfo()))
                        .filter(vo -> {
                                    List<String> org = JSONObject.parseArray(vo.getOrganizationInfo(), WorkerGroupDataDto.class)
                                            .stream().map(WorkerGroupDataDto::getAffiliatedOrg).collect(Collectors.toList());
                                    if (org.stream().anyMatch(orgIds::contains))return true;
                                    return false;
                                }
                        ).map(FtbPersonnelsTurnoverManagementVO::getUserId).collect(Collectors.toList());

            if (CollUtil.isNotEmpty(orgIds) && CollUtil.isNotEmpty(postIds)) return managements.stream().filter(vo -> StringUtils.isNotEmpty(vo.getOrganizationInfo()))
                    .filter(vo ->CollUtil.isNotEmpty(
                            JSONObject.parseArray(
                                            vo.getOrganizationInfo(), WorkerGroupDataDto.class)
                                    .stream().filter(po->orgIds.contains(po.getAffiliatedOrg()) && postIds.contains(po.getAffiliatedPosition())).collect(Collectors.toList()))
                    ).map(FtbPersonnelsTurnoverManagementVO::getUserId).collect(Collectors.toList());
        }
        return null;
    }
    /**
     * 比率计算
     * @param a 分子
     * @param b  分母
     * @return
     */
    private  BigDecimal calculateTheScale(Object a, Object b) {
        if (a == null || b == null) {
            return BigDecimal.ZERO;
        }
        // a/b 除数为0
        if (b instanceof Integer) {
            Integer b0 = (Integer) b;
            if (b0 == 0) {
                return BigDecimal.ZERO;
            }
        }
        if (b instanceof Long) {
            Long b0 = (Long) b;
            if (b0 == 0) {
                return BigDecimal.ZERO;
            }
        }
        if (b instanceof BigDecimal) {
            if (((BigDecimal) b).compareTo(BigDecimal.ZERO) == 0) {
                return BigDecimal.ZERO;
            }
        }
        BigDecimal a0 = new BigDecimal(String.valueOf(a));
        BigDecimal b0 = new BigDecimal(String.valueOf(b));
        return a0.divide(b0, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(2, RoundingMode.HALF_UP);
    }


    /**
     * 首月 / 首周 / 首年 离职率
     *
     * @param ringStartTime
     * @param ringEndTime
     * @param userIdListByOrganizeIds
     * @param firstMonth
     * @param organizationId
     * @return
     */
    private BigDecimal turnoverRateSequentially(Date ringStartTime, Date ringEndTime, List<String> userIdListByOrganizeIds, Integer firstMonth, List<String> organizationId) {
        // Integer lastInstallment = ftbPersonnelsTurnoverAnalysisMapper.getLeavePeopleMonth(ringStartTime, ringEndTime, userIdListByOrganizeIds);
        // 当月入职人数 取实际入职日期

        QueryPageUserDTO dto = new QueryPageUserDTO();
        dto.setOrganizeIds(organizationId);
        dto.setIsPage(false);
        ActionResult<PageListVO<UserBoundVO>> listByLevel = v2UserApi.pagePost(dto);
        List<String> userSubQueryList = listByLevel.getCode() == 200 ?
                listByLevel.getData().getList().stream().map(UserBoundVO::getId).collect(Collectors.toList())
                : new ArrayList<>();
        userIdListByOrganizeIds.addAll(userSubQueryList);
        Integer lastInstallment =  ftbPersonnelsTurnoverAnalysisMapper.numberOfOnboardedEmployees(ringStartTime, ringEndTime, userIdListByOrganizeIds);
        Integer leaveRate = ftbPersonnelsTurnoverAnalysisMapper.getFirstMonthLeaveRate(ringStartTime, ringEndTime, userIdListByOrganizeIds,firstMonth);
        return calculateTheScale( leaveRate, lastInstallment);
    }

    @Override
    public void getNewEmployeeLeaveRateDetailExport(FtbPersonnlesAnalysisDTO dto, HttpServletResponse response) {
        dto.setPageSize(-1);
        List<FtbPersonnelsTurnoverNewEmployeeDetailVO> dataList = getNewEmployeeLeaveRateDetail(dto).getList();
        exportExcel(response, "新员工离职比例明细", dataList, FtbPersonnelsTurnoverNewEmployeeDetailVO.class);
    }

    @Override
    public void getLeaveReasonDistributionDetailExport(FtbPersonnlesAnalysisDTO dto, HttpServletResponse response) {
        dto.setPageSize(-1);
        List<FtbPersonnelsTurnoverLeaveReasonDetailVO> dataList = getLeaveReasonDistributionDetail(dto).getList();
        exportExcel(response, "离职原因分布明细", dataList, FtbPersonnelsTurnoverLeaveReasonDetailVO.class);
    }

    @Override
    public void getLeavePeoplePostRatioDetailExport(FtbPersonnlesAnalysisDTO dto, HttpServletResponse response) {
        dto.setPageSize(-1);
        List<FtbPersonnelsTurnoverPostRatioDetailVO> dataList = getLeavePeoplePostRatioDetail(dto).getList();
        exportExcel(response, "各岗位人数占比明细", dataList, FtbPersonnelsTurnoverPostRatioDetailVO.class);
    }

    @Override
    public void getLeavePeopleAgeDistributionDetailExport(FtbPersonnlesAnalysisDTO dto, HttpServletResponse response) {
        dto.setPageSize(-1);
        List<FtbPersonnelsTurnoverSeniorityDetailVO> dataList = getLeavePeopleAgeDistributionDetail(dto).getList();
        exportExcel(response, "离职人员司龄分布明细", dataList, FtbPersonnelsTurnoverSeniorityDetailVO.class);
    }

    @Override
    public void getLeavePeopleAgeRatioDetailExport(FtbPersonnlesAnalysisDTO dto, HttpServletResponse response) {
        dto.setPageSize(-1);
        List<FtbPersonnelsTurnoverAgeDetailVO> dataList = getLeavePeopleAgeRatioDetail(dto).getList();
        exportExcel(response, "离职人员年龄分布占比明细", dataList, FtbPersonnelsTurnoverAgeDetailVO.class);
    }


    @Override
    public void breakdownOfGenderDistributionOfResignationExprot(FtbPersonnlesAnalysisDTO dto, HttpServletResponse response) {
        dto.setPageSize(-1);
        List<FtbPersonnelsTurnoverGenderDetailVO> dataList = breakdownOfGenderDistributionOfResignation(dto).getList();
        exportExcel(response, "离职人员性别分布明细", dataList, FtbPersonnelsTurnoverGenderDetailVO.class);
    }


    /**
     * 导出 Excel 文件
     *
     * @param response HTTP 响应
     * @param fileName 文件名
     * @param dataList 数据列表
     * @param clazz VO 类
     */
    @SneakyThrows
    private <T> void exportExcel(HttpServletResponse response, String fileName, List<T> dataList, Class<T> clazz) {
        EasyExcelUtils.exportExcel(response, fileName, dataList, clazz);
    }

}
