package jnpf.cultivate.v2.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jnpf.base.UserInfo;
import jnpf.base.vo.PageListVO;
import jnpf.base.vo.PaginationVO;
import jnpf.cultivate.service.FtbCultivatePositionService;
import jnpf.cultivate.service.FtbCultivatePromotionNewService;
import jnpf.cultivate.utils.UserApiV2Util;
import jnpf.cultivate.v2.service.*;
import jnpf.cultivate.v2.util.CultivateCourseStudyUtil;
import jnpf.model.cultivate.CultivatePage;
import jnpf.model.cultivate.dto.learn.BatchCommonCountDto;
import jnpf.model.cultivate.entiy.BaseEntity;
import jnpf.model.cultivate.po.position.FtbCultivatePosition;
import jnpf.model.cultivate.po.position.FtbCultivatePositionLog;
import jnpf.model.cultivate.po.position.FtbCultivatePositionSetting;
import jnpf.model.cultivate.po.promotion.*;
import jnpf.model.cultivate.v2.position.vo.AppCultivatePositionDetailVo;
import jnpf.model.cultivate.v2.position.vo.V2CultivatePositionDetailForApp;
import jnpf.model.cultivate.v2.promotion.dto.PersonStatisticsDataDto;
import jnpf.model.cultivate.v2.promotion.req.*;
import jnpf.model.cultivate.v2.promotion.vo.*;
import jnpf.model.cultivate.vo.common.InnerPowerUserVO;
import jnpf.model.enums.SelfrowingEnum;
import jnpf.permission.vo.v2.grades.GradeVO;
import jnpf.permission.vo.v2.organzie.OrganizeGeneralDetailVO;
import jnpf.permission.vo.v2.position.PositionAndGradesVO;
import jnpf.permission.vo.v2.position.PositionVO;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.permission.vo.v2.user.UserPageListVO;
import jnpf.util.SelfGrowthUtil;
import jnpf.util.UserProvider;
import jnpf.util.context.ThreadContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Service
@Slf4j
public class V2CultivatePromotionServiceImpl implements V2CultivatePromotionService {

    @Autowired
    private FtbCultivatePromotionPostNewService ftbCultivatePromotionPostNewService;

    @Autowired
    private FtbCultivatePromotionMemberNewService ftbCultivatePromotionMemberNewService;

    @Autowired
    private FtbCultivatePromotionNewService ftbCultivatePromotionNewService;

    @Autowired
    private FtbCultivatePromotionSettingService ftbCultivatePromotionSettingService;


    @Autowired
    private FtbCultivatePromotionNewMessageService ftbCultivatePromotionNewMessageService;

    @Autowired
    private UserApiV2Util userApiV2Util;

    @Resource
    private ThreadPoolExecutor cultivateThreadPool;

    @Autowired
    private FtbCultivatePromotionUserService ftbCultivatePromotionUserService;

    @Autowired
    private FtbCultivatePositionService ftbCultivatePositionService;

    @Autowired
    private FtbCultivatePositionSettingService ftbCultivatePositionSettingService;

    @Autowired
    private V2CultivatePositionService v2CultivatePositionService;

    @Autowired
    private FtbCultivatePromotionLogService ftbCultivatePromotionLogService;

    @Autowired
    private FtbCultivatePositionLogService ftbCultivatePositionLogService;

    @Autowired
    private CultivateCourseStudyUtil courseStudyUtil;

    @Autowired
    private V2CultivateBatchQueryService v2CultivateBatchQueryService;

    /**
     * 查询学习地图列表
     *
     * @param cultivatePage 分页参数对象，用于封装分页信息
     * @param dto           查询条件对象，用于传递查询参数
     * @return 返回分页结果对象，包含学习地图列表数据
     */
    @Override
    public PageListVO<WebCultivatePromotionListVo> webList(CultivatePage cultivatePage, FtbCultivatePromotionReq dto) {
        // 将分页参数转换为Page对象
        Page<WebCultivatePromotionListVo> page = cultivatePage.coverCultivatePage();
        // 调用服务层方法查询学习地图列表
        page = ftbCultivatePromotionNewService.webList(page, dto);
        // 将Page对象转换为PageListVO对象并返回
        return CultivatePage.coverPageList(page);
    }

    /**
     * 新增学习地图
     *
     * @param req 创建学习地图的请求参数对象
     * @return 返回操作结果字符串，通常为成功或失败信息
     */
    @Override
    @Transactional
    public String add(V2CultivatePromotionCreateReq req) {
        // 检查标签名称是否重复
        LambdaQueryWrapper<FtbCultivatePromotionNew> checkWrapper = new LambdaQueryWrapper<>();
        checkWrapper.eq(FtbCultivatePromotionNew::getPromotion, req.getPromotion());
        checkWrapper.eq(FtbCultivatePromotionNew::getEnableMark, 0);
        if (ftbCultivatePromotionNewService.count(checkWrapper) > 0) {
            throw new RuntimeException("地图已存在，请勿重复添加！");
        }
        // 对传入参数进行前置校验
        req.preParamCheck();
        // 执行实际的学习地图创建逻辑
        return realDealPromotion(req);
    }


    /**
     * 实际执行学习地图创建逻辑
     *
     * @param req 创建学习地图的请求参数对象
     * @return 创建结果字符串，通常为成功或失败信息
     */
    private String realDealPromotion(V2CultivatePromotionCreateReq req) {
        // 添加学习地图主表
        FtbCultivatePromotionNew promotionNew = V2CultivatePromotionCreateReq.coverFtbCultivatePromotionCreatDto(req, UserProvider.getLoginUserId());
        // 设置默认启用状态
        if (promotionNew.getEnableMark() == null) {
            promotionNew.setEnableMark(0); // 0表示启用，1表示禁用
        }
        if (StringUtils.isNotEmpty(req.getId())) {
            ftbCultivatePromotionNewService.updateById(promotionNew);
        } else {
            promotionNew.setPromotionBusinessId(SelfGrowthUtil.provideACustomIDBasedOnTheModule(SelfrowingEnum.PROMOTION));
            ftbCultivatePromotionNewService.save(promotionNew);
        }


        String promotionId = promotionNew.getId(); // 获取插入后的主键ID
        // 添加学习地图通道
        List<FtbCultivatePromotionPostNew> promotionPostNewList = new java.util.ArrayList<>();
        for (V2CultivatePromotionPostReq postReq : req.getPhaseChannel()) {
            if (postReq.getPostAndGradeVoList() != null && !postReq.getPostAndGradeVoList().isEmpty()) {
                for (V2CultivatePostAndGradeVo postGradeVo : postReq.getPostAndGradeVoList()) {
                    promotionPostNewList.add(buildFtbCultivatePromotionPostNew(postReq, postGradeVo, promotionId));
                }
            }
        }
        ftbCultivatePromotionPostNewService.saveBatch(promotionPostNewList);
        // 添加参加学习地图的人员和岗位
        List<FtbCultivatePromotionSetting> settingList = new ArrayList<>();
        for (V2CultivatePromotionScopeReq v2CultivatePromotionScopeReq : req.getScope()) {
            settingList.add(buildFtbCultivatePromotionSetting(v2CultivatePromotionScopeReq, promotionId));
        }
        ftbCultivatePromotionSettingService.saveBatch(settingList);
        return promotionId;
    }

    @NotNull
    private static FtbCultivatePromotionSetting buildFtbCultivatePromotionSetting(V2CultivatePromotionScopeReq v2CultivatePromotionScopeReq, String promotionId) {
        FtbCultivatePromotionSetting setting = new FtbCultivatePromotionSetting();
        setting.setPromotionId(promotionId);
        setting.setScope(v2CultivatePromotionScopeReq.getScope());
        setting.setPostId(v2CultivatePromotionScopeReq.getPostId());
        setting.setScopeId(v2CultivatePromotionScopeReq.getScopeId());
        return setting;
    }

    @NotNull
    private static FtbCultivatePromotionPostNew buildFtbCultivatePromotionPostNew(V2CultivatePromotionPostReq postReq, V2CultivatePostAndGradeVo postGradeVo, String promotionId) {
        FtbCultivatePromotionPostNew promotionPostNew = new FtbCultivatePromotionPostNew();
        promotionPostNew.setPromotionId(promotionId);
        promotionPostNew.setLevel(postReq.getChannelLevel()); // 使用通道级别作为level
        promotionPostNew.setSelectCourseNumber(postReq.getSelectCourseNumber());
        promotionPostNew.setPostId(postGradeVo.getPostId());
        promotionPostNew.setPostName(postGradeVo.getPostName());
        promotionPostNew.setGradeId(postGradeVo.getGradeId());
        promotionPostNew.setGradeName(postGradeVo.getGradeName());
        promotionPostNew.setDeleteMark(0);
        return promotionPostNew;
    }


    /**
     * 修改学习地图
     *
     * @param creatDto 修改学习地图的请求参数对象
     * @return 创建结果字符串，通常为成功或失败信息
     */
    @Override
    @Transactional
    public String update(V2CultivatePromotionCreateReq creatDto) {
        FtbCultivatePromotionNew promotionNewEntity = ftbCultivatePromotionNewService.getById(creatDto.getId());
        if (promotionNewEntity == null || promotionNewEntity.getEnableMark() == 1) {
            throw new RuntimeException("该学习地图不存在");
        }

        // 检查标签名称是否重复
        LambdaQueryWrapper<FtbCultivatePromotionNew> checkWrapper = new LambdaQueryWrapper<>();
        checkWrapper.eq(FtbCultivatePromotionNew::getPromotion, creatDto.getPromotion()).eq(FtbCultivatePromotionNew::getEnableMark, 0).ne(FtbCultivatePromotionNew::getId, creatDto.getId());
        if (ftbCultivatePromotionNewService.count(checkWrapper) > 0) {
            throw new RuntimeException("地图名称已存在！");
        }

        // 参数检查
        creatDto.preParamCheck();
        String promotionId = creatDto.getId();
        LambdaQueryWrapper<FtbCultivatePromotionMemberNew> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery.eq(FtbCultivatePromotionMemberNew::getPromotionId, promotionId);
        List<FtbCultivatePromotionMemberNew> memberNews = ftbCultivatePromotionMemberNewService.list(lambdaQuery);
        if (CollUtil.isNotEmpty(memberNews)) {
            List<FtbCultivatePromotionNewMessage> collect = memberNews.stream().map(item -> {
                FtbCultivatePromotionNewMessage message = new FtbCultivatePromotionNewMessage();
                message.setPromotionId(promotionId);
                message.setUserId(item.getUserId());
                return message;
            }).collect(Collectors.toList());
            ftbCultivatePromotionNewMessageService.saveBatch(collect);
        }
        realDeletePromotion(promotionId);
        return realDealPromotion(creatDto);
    }

    /**
     * 删除学习地图
     *
     * @param id 要删除的学学习地图的ID
     */
    @Override
    @Transactional
    public void delete(String id) {
        realDeletePromotion(id);
    }

    /**
     * 根据ID获取学习地图信息
     *
     * @param id 要获取的学学习地图的ID
     * @return 返回一个包含学习地图信息的对象
     */
    private void realDeletePromotion(String id) {
        if (id == null || id.trim().isEmpty()) {
            throw new IllegalArgumentException("学习地图ID不能为空");
        }
        LambdaUpdateWrapper<FtbCultivatePromotionNew> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(FtbCultivatePromotionNew::getEnableMark, "1");
        updateWrapper.eq(BaseEntity::getId, id);
        ftbCultivatePromotionNewService.update(new FtbCultivatePromotionNew(), updateWrapper);

        LambdaUpdateWrapper<FtbCultivatePromotionPostNew> wrapper = Wrappers.lambdaUpdate();
        // 移除晋升通道岗位信息
        wrapper.eq(FtbCultivatePromotionPostNew::getPromotionId, id);
        ftbCultivatePromotionPostNewService.remove(wrapper);
        // 移除关联数据 地图成员管理关联数据
        LambdaUpdateWrapper<FtbCultivatePromotionMemberNew> memberLambdaUpdateWrapper = Wrappers.lambdaUpdate();
        memberLambdaUpdateWrapper.eq(FtbCultivatePromotionMemberNew::getPromotionId, id);
        ftbCultivatePromotionMemberNewService.remove(memberLambdaUpdateWrapper);

        //删除配置
        LambdaUpdateWrapper<FtbCultivatePromotionSetting> settingLambdaUpdateWrapper = Wrappers.lambdaUpdate();
        settingLambdaUpdateWrapper.eq(FtbCultivatePromotionSetting::getPromotionId, id);
        ftbCultivatePromotionSettingService.remove(settingLambdaUpdateWrapper);

        //删除通道的成员
        LambdaUpdateWrapper<FtbCultivatePromotionUser> promotionUserLambdaUpdateWrapper = Wrappers.lambdaUpdate();
        promotionUserLambdaUpdateWrapper.eq(FtbCultivatePromotionUser::getPromotionId, id);
        ftbCultivatePromotionUserService.remove(promotionUserLambdaUpdateWrapper);
    }

    /**
     * 获取学习地图详情
     *
     * @param id 学习地图id
     * @return 学习地图信息
     */
    @Override
    public V2CultivatePromotionVo get(String id) {
        // 查询学习地图信息 ftb_cultivate_promotion_new
        FtbCultivatePromotionNew promotionNew = ftbCultivatePromotionNewService.getById(id);
        if (promotionNew == null || promotionNew.getEnableMark() == 1) {
            throw new RuntimeException("该学习地图不存在");
        }

        // 将基础信息转换为VO
        V2CultivatePromotionVo vo = V2CultivatePromotionVo.coverFtbCultivateStudyMemberVO(promotionNew);

        // 查询学习地图岗位信息 ftb_cultivate_promotion_post_new
        LambdaQueryWrapper<FtbCultivatePromotionPostNew> postWrapper = Wrappers.lambdaQuery();
        postWrapper.eq(FtbCultivatePromotionPostNew::getPromotionId, id)
                .eq(FtbCultivatePromotionPostNew::getDeleteMark, 0); // 只查询未删除的记录
        List<FtbCultivatePromotionPostNew> postList = ftbCultivatePromotionPostNewService.list(postWrapper);

        List<String> allPostIds = new ArrayList<>();
        Map<String, PositionAndGradesVO> allPostInfoMap = new HashMap<>();
        if (CollUtil.isNotEmpty(postList)) {
            allPostIds = postList.stream().map(FtbCultivatePromotionPostNew::getPostId).collect(Collectors.toList());
            List<PositionAndGradesVO> positionAndGradesVOS = userApiV2Util.listPositionAndGradeByPositionIds(allPostIds, null);
            if (CollUtil.isNotEmpty(positionAndGradesVOS)) {
                allPostInfoMap = positionAndGradesVOS.stream().collect(Collectors.toMap(PositionAndGradesVO::getId, item -> item));
            }
        }
        // 按level分组并组织岗位信息
        Map<Integer, List<FtbCultivatePromotionPostNew>> postGroupByLevel = postList.stream().collect(Collectors.groupingBy(FtbCultivatePromotionPostNew::getLevel));

        List<V2CultivatePromotionLevel> levelList = new ArrayList<>();
        for (Map.Entry<Integer, List<FtbCultivatePromotionPostNew>> entry : postGroupByLevel.entrySet()) {
            Integer level = entry.getKey();
            List<FtbCultivatePromotionPostNew> levelPosts = entry.getValue();

            V2CultivatePromotionLevel promotionLevel = new V2CultivatePromotionLevel();
            promotionLevel.setLevel(level);

            // 使用第一个岗位的选学数量作为该级别的选学数量（假设同一级别的岗位选学数量相同）
            if (!levelPosts.isEmpty()) {
                promotionLevel.setSelectCourseNumber(levelPosts.get(0).getSelectCourseNumber());
            }
            List<V2CultivatePromotionPostNewVO> postVOList = new ArrayList<>();
            for (FtbCultivatePromotionPostNew post : levelPosts) {

                PositionAndGradesVO positionAndGradesVO = allPostInfoMap.get(post.getPostId());

                // 设置状态：如果岗位信息不存在或职级不匹配则设置为1(无效)，否则为0(有效)
                int status = determineStatus(positionAndGradesVO, post.getGradeId());
                postVOList.add(buildV2CultivatePromotionPostNewVO(post, status, positionAndGradesVO));
            }
            promotionLevel.setPostList(postVOList);
            levelList.add(promotionLevel);
        }

        // 按level排序
        levelList.sort(Comparator.comparing(V2CultivatePromotionLevel::getLevel));
        vo.setLevelList(levelList);

        // 查询学习地图配置 ftb_cultivate_promotion_setting
        LambdaQueryWrapper<FtbCultivatePromotionSetting> settingWrapper = Wrappers.lambdaQuery();
        settingWrapper.eq(FtbCultivatePromotionSetting::getPromotionId, id);
        List<FtbCultivatePromotionSetting> settingList = ftbCultivatePromotionSettingService.list(settingWrapper);

        // 转换配置信息
        List<V2CultivatePromotionScopeVo> scopeVoList = settingList.stream().map(setting -> {
            V2CultivatePromotionScopeVo scopeVo = new V2CultivatePromotionScopeVo();
            scopeVo.setId(setting.getId());
            scopeVo.setScope(setting.getScope());
            scopeVo.setPostId(setting.getPostId());
            scopeVo.setScopeId(setting.getScopeId());
            return scopeVo;
        }).collect(Collectors.toList());

        vo.setScopeVoList(scopeVoList);

        return vo;
    }

    @NotNull
    private static V2CultivatePromotionPostNewVO buildV2CultivatePromotionPostNewVO(FtbCultivatePromotionPostNew post, int status, PositionAndGradesVO positionAndGradesVO) {
        V2CultivatePromotionPostNewVO postVO = new V2CultivatePromotionPostNewVO();
        postVO.setStatus(status);
        postVO.setPromotionId(post.getPromotionId());
        postVO.setPostId(post.getPostId());
        postVO.setPostName(post.getPostName());
        postVO.setChannelLevel(post.getLevel());
        postVO.setGradeId(post.getGradeId());
        postVO.setGradeName(post.getGradeName());
        postVO.setSelectCourseNumber(post.getSelectCourseNumber());
        if (status == 0) {
            postVO.fillPositionAndGradesName(positionAndGradesVO);
        }
        return postVO;
    }

    /**
     * 确定岗位职级状态
     *
     * @param positionAndGradesVO 岗位及职级信息
     * @param gradeId             职级ID
     * @return 状态码：0-有效，1-无效
     */
    private int determineStatus(PositionAndGradesVO positionAndGradesVO, String gradeId) {
        // 如果岗位信息不存在，则状态为无效(1)
        if (positionAndGradesVO == null) {
            return 1;
        }

        // 如果没有指定职级ID，则认为是有效的
        if (StringUtils.isEmpty(gradeId)) {
            return 0; // 或根据业务需求决定默认值
        }

        // 如果岗位存在但没有关联的职级列表，则状态为无效(1)
        if (CollUtil.isEmpty(positionAndGradesVO.getList())) {
            return 1;
        }

        // 检查指定的职级ID是否存在于岗位的职级列表中
        boolean gradeExists = positionAndGradesVO.getList().stream().anyMatch(item -> item.getId().equals(gradeId));

        // 如果职级存在则返回0(有效)，否则返回1(无效)
        return gradeExists ? 0 : 1;
    }

    /**
     * 根据地图主键id 获取地图成员信息
     *
     * @param req     请求参数对象，包含查询条件
     * @param reqPage 分页查询参数对象，包含分页信息如页码、每页大小等
     * @return V2WebCultivateStudyMemberListVo 获取结果对象，包含学习地图成员信息
     */
    @Override
    public V2WebCultivateStudyMemberListVo queryPromotionUserList(V2WebCultivateStudyMemberListReq req, CultivatePage reqPage) {
        Page<V2CultivatePromotionMemberVo> page = reqPage.coverCultivatePage();
        V2WebCultivateStudyMemberListVo returnVo = new V2WebCultivateStudyMemberListVo();

        // 查询学习地图基本信息
        FtbCultivatePromotionNew promotionNew = ftbCultivatePromotionNewService.getById(req.getPromotionId());
        if (promotionNew == null || promotionNew.getEnableMark() == 1) {
            return returnVo;
        }

        // 设置返回的头部信息
        returnVo.setPromotion(promotionNew.getPromotion());
        returnVo.setPromotionBusinessId(promotionNew.getPromotionBusinessId());

        // 查询学习地图岗位职级信息
        LambdaQueryWrapper<FtbCultivatePromotionPostNew> postWrapper = Wrappers.lambdaQuery();
        postWrapper.eq(FtbCultivatePromotionPostNew::getPromotionId, req.getPromotionId());
        postWrapper.eq(FtbCultivatePromotionPostNew::getDeleteMark, 0);
        List<FtbCultivatePromotionPostNew> postList = ftbCultivatePromotionPostNewService.list(postWrapper);
        if (CollUtil.isEmpty(postList)) {
            return returnVo;
        }


        Map<String, FtbCultivatePromotionPostNew> postMap = new HashMap<>();
        List<String> postIds = new ArrayList<>();
        List<String> gradeIds = new ArrayList<>();
        int maxPhses = 0;
        for (FtbCultivatePromotionPostNew ftbCultivatePromotionPostNew : postList) {
            postIds.add(ftbCultivatePromotionPostNew.getPostId());
            if (StringUtils.isNotEmpty(ftbCultivatePromotionPostNew.getGradeId())) {
                gradeIds.add(ftbCultivatePromotionPostNew.getGradeId());
            }
            postMap.put(ftbCultivatePromotionPostNew.getId(), ftbCultivatePromotionPostNew);
            maxPhses = Math.max(maxPhses, ftbCultivatePromotionPostNew.getLevel());
        }
        returnVo.setLearningPhase(maxPhses);
        returnVo.setLearningScope(postList.size());


        Map<String, GradeVO> stringGradeVOMap = userApiV2Util.listGradeByIdsReturnMap(gradeIds, null);
        Map<String, PositionVO> stringPositionVOMap = userApiV2Util.listPositionDetailInfoByIdsReturnMap(postIds, null);

        // 如果传入有关键词【用户姓名】 先查询V2UserApi的接口过滤用户id 再在ftb_cultivate_promotion_user这个表中查
        List<String> queryUserIds = new ArrayList<>();
        if (StringUtils.isNotEmpty(req.getKeyWord())) {
            List<UserPageListVO> filteredUsers = userApiV2Util.getUserForNameOrPhone(req.getKeyWord(), null, null);
            if (CollUtil.isNotEmpty(filteredUsers)) {
                queryUserIds = filteredUsers.stream().map(UserPageListVO::getId).collect(Collectors.toList());

            } else {
                // 如果关键词搜索结果为空，返回空结果
                return getV2WebCultivateStudyMemberListVo(page, returnVo);
            }
        }


        // 使用分页查询获取当前页的用户
        Page<V2CultivatePromotionMemberVo> pagedUserList = ftbCultivatePromotionUserService.queryUserForPage(page, queryUserIds, req.getPromotionId());

        List<V2CultivatePromotionMemberVo> userList = pagedUserList.getRecords();
        List<String> userIds = userList.stream().map(V2CultivatePromotionMemberVo::getUserId).collect(Collectors.toList());

        if (CollUtil.isEmpty(userIds)) {
            // 如果没有用户，返回空结果
            return getV2WebCultivateStudyMemberListVo(page, returnVo);
        }

        // 查询每个人对这个学习地图每个阶段选择的岗位
        LambdaQueryWrapper<FtbCultivatePromotionMemberNew> memberWrapper = Wrappers.lambdaQuery();
        memberWrapper.select(FtbCultivatePromotionMemberNew::getUserId, FtbCultivatePromotionMemberNew::getPromotionId, FtbCultivatePromotionMemberNew::getCurrentLearningStage, FtbCultivatePromotionMemberNew::getPromotionPostId);
        memberWrapper.eq(FtbCultivatePromotionMemberNew::getPromotionId, req.getPromotionId());
        memberWrapper.in(FtbCultivatePromotionMemberNew::getUserId, userIds);
        List<FtbCultivatePromotionMemberNew> memberList = ftbCultivatePromotionMemberNewService.list(memberWrapper);

        // 按用户ID分组成员信息
        Map<String, List<FtbCultivatePromotionMemberNew>> memberGroupByUser = memberList.stream().collect(Collectors.groupingBy(FtbCultivatePromotionMemberNew::getUserId));

        // 获取用户详细信息
        Map<String, UserBoundVO> userMap = userApiV2Util.getUserPrimaryBoundBatch(userIds, null);

        // 构建VO对象
        for (V2CultivatePromotionMemberVo vo : userList) {  // 遍历分页后的用户列表，保持正确的顺序
            String userId = vo.getUserId();
            UserBoundVO userBound = userMap.get(userId);
            if (userBound == null) continue;
            vo.setUserId(userBound.getId());
            vo.setUserName(userBound.getUserName());
            vo.setOrgId(userBound.getOrganizeId());
            vo.setOrgName(userBound.getOrganizeName());
            vo.setPositionId(userBound.getPositionId());
            vo.setPositionName(userBound.getPositionName());
            vo.setGradeId(userBound.getGradeId());
            vo.setGradeName(userBound.getGradeName());

            // 查找该用户在该学习地图中的记录
            List<FtbCultivatePromotionMemberNew> userMembers = memberGroupByUser.get(userId);

            if (CollUtil.isNotEmpty(userMembers)) {
                Integer maxStage = 1;
                for (FtbCultivatePromotionMemberNew userMember : userMembers) {
                    if (userMember.getCurrentLearningStage() > maxStage) {
                        maxStage = userMember.getCurrentLearningStage();
                    }
                }
                vo.setCurrentLearningStage(maxStage);

                // 获取已选学岗位
                List<FtbCultivatePromotionPostNew> selectedStudyPositions = new ArrayList<>();
                for (FtbCultivatePromotionMemberNew userMember : userMembers) {
                    if (userMember.getCurrentLearningStage().equals(maxStage)) {
                        FtbCultivatePromotionPostNew ftbCultivatePromotionPostNew = postMap.get(userMember.getPromotionPostId());
                        if (ftbCultivatePromotionPostNew != null) {
                            selectedStudyPositions.add(ftbCultivatePromotionPostNew);
                        }
                    }
                }
                vo.setSelectedStudyPosition(convertToPositionAndGrade(selectedStudyPositions, stringGradeVOMap, stringPositionVOMap));
                // 当前学习岗位 todo 暂时不知道怎么获取
                vo.setCurrStudyPosition(convertToPositionAndGrade(selectedStudyPositions, stringGradeVOMap, stringPositionVOMap));
            }
        }

        returnVo.setMemberList(CultivatePage.coverPageList(pagedUserList));
        return returnVo;
    }

    @NotNull
    private V2WebCultivateStudyMemberListVo getV2WebCultivateStudyMemberListVo(Page<V2CultivatePromotionMemberVo> page, V2WebCultivateStudyMemberListVo returnVo) {
        PageListVO<V2CultivatePromotionMemberVo> emptyResult = new PageListVO<>();
        PaginationVO pagination = new PaginationVO();
        pagination.setCurrentPage(page.getCurrent());
        pagination.setPageSize(page.getSize());
        pagination.setTotal(0);
        emptyResult.setPagination(pagination);
        emptyResult.setList(new ArrayList<>());
        returnVo.setMemberList(emptyResult);
        return returnVo;
    }

    private List<V2CultivatePostAndGradeVo> convertToPositionAndGrade(List<FtbCultivatePromotionPostNew> selectedStudyPositions, Map<String, GradeVO> stringGradeVOMap, Map<String, PositionVO> stringPositionVOMap) {
        if (CollUtil.isEmpty(selectedStudyPositions)) {
            return new ArrayList<>();
        }
        List<V2CultivatePostAndGradeVo> ret = new ArrayList<>();
        for (FtbCultivatePromotionPostNew selectedStudyPosition : selectedStudyPositions) {


            V2CultivatePostAndGradeVo vo = new V2CultivatePostAndGradeVo();
            if (StringUtils.isNotEmpty(selectedStudyPosition.getGradeId())) {
                GradeVO gradeVO = stringGradeVOMap.get(selectedStudyPosition.getGradeId());
                if (gradeVO != null) {
                    vo.setGradeId(gradeVO.getId());
                    vo.setGradeName(gradeVO.getFullName());
                }
            }
            PositionVO positionVO = stringPositionVOMap.get(selectedStudyPosition.getPostId());
            if (positionVO != null) {
                vo.setPostId(positionVO.getId());
                vo.setPostName(positionVO.getFullName());
            }
            ret.add(vo);
        }
        return ret;
    }

    /**
     * 查询在岗位学习中完成的配置的岗位&职级
     *
     * @param req 请求参数对象，包含排除条件
     * @return List<V2CultivatePositionVo> 查询结果列表，包含岗位&职级信息
     */
    @Override
    public List<V2CultivatePositionVo> queryCultivatePosition(V2CultivatePositionExcludeReq req) {
        // 获取所有有效的岗位配置
        LambdaQueryWrapper<FtbCultivatePosition> positionWrapper = Wrappers.lambdaQuery();
        positionWrapper.eq(FtbCultivatePosition::getIsGrounding, 1);
        positionWrapper.eq(FtbCultivatePosition::getEnabledMark, 0); // 仅查询有效的岗位
        List<FtbCultivatePosition> positionList = ftbCultivatePositionService.list(positionWrapper);

        if (CollUtil.isEmpty(positionList)) {
            return List.of();
        }

        // 提取ID列表并在一次遍历中完成
        List<String> learnIds = new ArrayList<>(positionList.size());
        List<String> postIds = new ArrayList<>(positionList.size());
        for (FtbCultivatePosition ftbCultivatePosition : positionList) {
            postIds.add(ftbCultivatePosition.getPostId());
            learnIds.add(ftbCultivatePosition.getId());
        }

        // 获取所有岗位对应的职级配置
        LambdaQueryWrapper<FtbCultivatePositionSetting> settingWrapper = Wrappers.lambdaQuery();
        settingWrapper.in(FtbCultivatePositionSetting::getPostLearnId, learnIds);
        List<FtbCultivatePositionSetting> settingList = ftbCultivatePositionSettingService.list(settingWrapper);

        // 构建岗位学习ID到职级列表的映射
        Map<String, List<FtbCultivatePositionSetting>> positionSettingMap = settingList.stream().collect(Collectors.groupingBy(FtbCultivatePositionSetting::getPostLearnId, Collectors.toList()));

        // 提取职级ID列表
        List<String> gradeIds = settingList.stream().map(FtbCultivatePositionSetting::getGradeId).distinct().collect(Collectors.toList());

        Map<String, PositionVO> stringPositionVOMap = userApiV2Util.listPositionDetailInfoByIdsReturnMap(postIds, null);
        Map<String, GradeVO> stringGradeVOMap = userApiV2Util.listGradeByIdsReturnMap(gradeIds, null);

        // 构建最终结果
        List<V2CultivatePositionVo> result = new ArrayList<>();
        List<V2CultivatePostAndGradeVo> excludes = req.getPostAndGradeVoList();

        // 预处理排除项，建立postId到排除职级ID集合的映射，用于快速查找
        Set<String> excludedPostIds = new HashSet<>();
        Map<String, Set<String>> excludedGradeIdsByPost = new HashMap<>();
        if (CollUtil.isNotEmpty(excludes)) {
            for (V2CultivatePostAndGradeVo exclude : excludes) {
                if (StringUtils.isEmpty(exclude.getGradeId())) {
                    excludedPostIds.add(exclude.getPostId());
                } else {
                    excludedGradeIdsByPost.computeIfAbsent(exclude.getPostId(), k -> new HashSet<>()).add(exclude.getGradeId());
                }
            }
        }

        for (FtbCultivatePosition position : positionList) {
            // 检查岗位是否被完全排除
            if (excludedPostIds.contains(position.getPostId())) {
                continue;
            }

            V2CultivatePositionVo positionVo = new V2CultivatePositionVo();
            result.add(positionVo);
            positionVo.setPostId(position.getPostId());
            PositionVO positionVO = stringPositionVOMap.get(position.getPostId());
            if (positionVO != null) {
                positionVo.setPostName(positionVO.getFullName());
            }

            // 获取该岗位下的职级配置
            List<FtbCultivatePositionSetting> gradeSettings = positionSettingMap.get(position.getId());
            List<V2CultivatePositionGradeVo> gradeList = new ArrayList<>();

            if (CollUtil.isNotEmpty(gradeSettings)) {
                Set<String> excludedGradesForCurrentPost = excludedGradeIdsByPost.get(position.getPostId());
                for (FtbCultivatePositionSetting setting : gradeSettings) {
                    // 检查当前职级是否被排除
                    if (excludedGradesForCurrentPost != null && excludedGradesForCurrentPost.contains(setting.getGradeId())) {
                        continue;
                    }

                    V2CultivatePositionGradeVo gradeVo = new V2CultivatePositionGradeVo();
                    GradeVO gradeVO = stringGradeVOMap.get(setting.getGradeId());
                    if (gradeVO != null) { // 只有当职级名称存在时才添加
                        gradeVo.setGradeId(gradeVO.getId());
                        gradeVo.setGradeName(gradeVO.getName());
                        gradeList.add(gradeVo);
                    }
                }
            }
            positionVo.setGradeList(gradeList);
        }

        return result;
    }

    /**
     * 获取我的所有学习地图列表
     *
     * @param userId 用户ID
     * @return List<MyCultivatePromotionListVo> 查询结果列表，包含用户的学习地图信息
     */
    @Override
    public List<MyCultivatePromotionListVo> queryMyAllPromotionList(String userId) {
        List<MyCultivatePromotionListVo> list = ftbCultivatePromotionUserService.queryMyAllPromotionList(userId);
        if (CollUtil.isNotEmpty(list)) {
            List<String> promotionIds = list.stream().map(MyCultivatePromotionListVo::getId).collect(Collectors.toList());
            //查询最大阶段
            List<BatchCommonCountDto> maxList = ftbCultivatePromotionNewService.queryMaxLevel(promotionIds);
            Map<String, BatchCommonCountDto> promotionIdMap = new HashMap<>();
            if (CollUtil.isNotEmpty(maxList)) {
                promotionIdMap = maxList.stream().collect(Collectors.toMap(BatchCommonCountDto::getSelectKey, v -> v));
            }
            List<FtbCultivatePromotionLog> completePromotionList = ftbCultivatePromotionLogService.queryMyCompletePromotion(userId, 2);
            //按照推广ID进行分组
            Map<String, List<FtbCultivatePromotionLog>> completePromotionMap = new HashMap<>();
            if (CollUtil.isNotEmpty(completePromotionList)) {
                completePromotionMap = completePromotionList.stream().collect(Collectors.groupingBy(FtbCultivatePromotionLog::getPromotionId));
            }
            //按照
            //用户所在阶段
            List<BatchCommonCountDto> currentPhaseList = ftbCultivatePromotionMemberNewService.queryMyCurrentPhase(userId, promotionIds);
            Map<String, BatchCommonCountDto> currentPhaseMap = new HashMap<>();
            if (CollUtil.isNotEmpty(currentPhaseList)) {
                currentPhaseMap = currentPhaseList.stream().collect(Collectors.toMap(BatchCommonCountDto::getSelectKey, v -> v));
            }
            for (MyCultivatePromotionListVo item : list) {
                BatchCommonCountDto max = promotionIdMap.get(item.getId());
                if (max != null) {
                    item.setPhaseNumber(max.getNum());
                } else {
                    item.setPhaseNumber(0);
                }
                BatchCommonCountDto currentPhase = currentPhaseMap.get(item.getId());
                if (currentPhase != null) {
                    item.setDoingPhaseNumber(currentPhase.getNum());
                } else {
                    item.setDoingPhaseNumber(1);
                }
                List<FtbCultivatePromotionLog> ftbCultivatePromotionLogs = completePromotionMap.get(item.getId());
                if (CollUtil.isNotEmpty(ftbCultivatePromotionLogs)) {
                    item.setCompletePhaseList(ftbCultivatePromotionLogs.stream().map(FtbCultivatePromotionLog::getLevel).collect(Collectors.toList()));
                }
            }
        }
        return list;
    }

    /**
     * 查询学习地图的阶段信息
     *
     * @param userId      用户ID
     * @param promotionId 学习地图ID
     * @param level       阶段编号
     * @return PromotionPhaseVo 阶段信息
     */
    @Override
    public PromotionPhaseVo queryPromotionInfoForLevel(String userId, String promotionId, Integer level) {

        PromotionPhaseVo resultVo = new PromotionPhaseVo();
        LambdaQueryWrapper<FtbCultivatePromotionUser> resetWrapper = Wrappers.lambdaQuery();
        resetWrapper.eq(FtbCultivatePromotionUser::getUserId, userId).eq(FtbCultivatePromotionUser::getPromotionId, promotionId);
        List<FtbCultivatePromotionUser> userList = ftbCultivatePromotionUserService.list(resetWrapper);
        if (CollUtil.isEmpty(userList)) {
            throw new RuntimeException("您没有加入该学习地图");
        }

        // 查询学习地图信息 ftb_cultivate_promotion_new
        FtbCultivatePromotionNew promotionNew = ftbCultivatePromotionNewService.getById(promotionId);
        if (promotionNew == null || promotionNew.getEnableMark() == 1) {
            throw new RuntimeException("该学习地图不存在");
        }
        if (promotionNew.getIsError().equals(1)) {
            throw new RuntimeException("该学习地图异常");
        }

        resultVo.setLevel(level);


        LambdaQueryWrapper<FtbCultivatePromotionPostNew> postWrapper = Wrappers.lambdaQuery();
        postWrapper.eq(FtbCultivatePromotionPostNew::getPromotionId, promotionId);
        postWrapper.eq(FtbCultivatePromotionPostNew::getLevel, level);
        postWrapper.eq(FtbCultivatePromotionPostNew::getDeleteMark, 0); // 只查询未删除的记录
        List<FtbCultivatePromotionPostNew> postList = ftbCultivatePromotionPostNewService.list(postWrapper);
        if (CollUtil.isEmpty(postList)) {
            return resultVo;
        }

        Map<String, PositionProgressVo> progressVoMap = v2CultivateBatchQueryService.batchQueryPositionProgress(postList, userId);
        LambdaQueryWrapper<FtbCultivatePromotionMemberNew> memberWrapper = Wrappers.lambdaQuery();
        memberWrapper.eq(FtbCultivatePromotionMemberNew::getPromotionId, promotionId).eq(FtbCultivatePromotionMemberNew::getCurrentLearningStage, level)
                .eq(FtbCultivatePromotionMemberNew::getUserId, userId).eq(FtbCultivatePromotionMemberNew::getDeleteMark, 0); // 只查询未删除的记录
        List<FtbCultivatePromotionMemberNew> memberNewList = ftbCultivatePromotionMemberNewService.list(memberWrapper);


        List<String> postIds = new ArrayList<>();
        List<String> gradeIds = new ArrayList<>();
        Map<String, FtbCultivatePromotionPostNew> postIdMap = new HashMap<>();
        for (FtbCultivatePromotionPostNew ftbCultivatePromotionPostNew : postList) {
            postIdMap.put(ftbCultivatePromotionPostNew.getId(), ftbCultivatePromotionPostNew);
            postIds.add(ftbCultivatePromotionPostNew.getPostId());
            if (StringUtils.isNotEmpty(ftbCultivatePromotionPostNew.getGradeId())) {
                gradeIds.add(ftbCultivatePromotionPostNew.getGradeId());
            }
        }
        Map<String, PositionVO> stringPositionVOMap = userApiV2Util.listPositionDetailInfoByIdsReturnMap(postIds, null);
        Map<String, GradeVO> stringGradeVOMap = userApiV2Util.listGradeByIdsReturnMap(gradeIds, null);
        //设置配置的岗位学习
        resultVo.setPostAndGradeList(PromotionLevelInfoVo.convertToPositionAndGrade(postList, stringGradeVOMap, stringPositionVOMap, memberNewList, progressVoMap));

        resultVo.setSelectCourseNumber(postList.get(0).getSelectCourseNumber());


        if (CollUtil.isNotEmpty(memberNewList)) {
            resultVo.setUserSelect(1);
        } else {
            resultVo.setUserSelect(0);
        }
        courseStudyUtil.checkCultivateLearning(userId, UserProvider.getUser().getTenantId());
        return resultVo;
    }

    /**
     * 添加岗位到学习地图
     *
     * @param id       学习地图ID
     * @param creatDto 添加岗位信息
     */
    @Override
    public void addMemberToPromotion(String id, V2CultivatePromotionCreateReq creatDto) {
        UserInfo user = UserProvider.getUser();
        String tenantId = user.getTenantId();

        CompletableFuture.runAsync(ThreadContext.wrap(() -> {
            //检查任务开始并发消息提醒
            List<V2CultivatePromotionScopeReq> scope = creatDto.getScope();
            if (CollUtil.isEmpty(scope)) {
                return;
            }
            List<String> postIds = new ArrayList<>();
            List<String> userIds = new ArrayList<>();
            List<V2CultivatePromotionScopeReq> postScope = new ArrayList<>();
            for (V2CultivatePromotionScopeReq scopeReq : scope) {
                if (scopeReq.getScope().equals(3)) {
                    userIds.add(scopeReq.getScopeId());
                } else {
                    postIds.add(scopeReq.getPostId());
                    postScope.add(scopeReq);
                }
            }
            if (CollUtil.isNotEmpty(postIds)) {
                List<UserBoundVO> userListForPositions = userApiV2Util.getUserListForPositions(postIds, tenantId);
                List<String> positUserIds = new ArrayList<>();
                if (CollUtil.isNotEmpty(userListForPositions)) {
                    for (UserBoundVO userBoundVO : userListForPositions) {
                        for (V2CultivatePromotionScopeReq scopeReq : postScope) {
                            if (scopeReq.getScope().equals(1) && userBoundVO.getPositionId().equals(scopeReq.getPostId())) {
                                positUserIds.add(userBoundVO.getId());
                                break;
                            } else if (scopeReq.getScope().equals(2) && userBoundVO.getPositionId().equals(scopeReq.getPostId()) && userBoundVO.getGradeId().equals(scopeReq.getScopeId())) {
                                positUserIds.add(userBoundVO.getId());
                                break;
                            }
                        }
                    }
                }
                if (CollUtil.isNotEmpty(positUserIds)) {
                    userIds.addAll(positUserIds);
                }
            }
            //加入学习地图
            if (CollUtil.isNotEmpty(userIds)) {
                userApiV2Util.checkOutTenant(tenantId);
                List<FtbCultivatePromotionUser> userList = new ArrayList<>();
                for (String userId : userIds) {
                    FtbCultivatePromotionUser promotionUser = new FtbCultivatePromotionUser();
                    promotionUser.setPromotionId(id);
                    promotionUser.setUserId(userId);
                    promotionUser.setEnabledMark(0);
                    userList.add(promotionUser);
                }
                ftbCultivatePromotionUserService.saveBatch(userList);
            }

        }), cultivateThreadPool);
    }

    /**
     * 选择学习地图
     *
     * @param userId      用户id
     * @param promotionId 学习地图id
     */
    @Override
    @Transactional
    public void selectPromotion(String userId, String promotionId) {
        // 首先将用户的所有学习地图选择标记设为0
        LambdaUpdateWrapper<FtbCultivatePromotionUser> resetWrapper = Wrappers.lambdaUpdate();
        resetWrapper.eq(FtbCultivatePromotionUser::getUserId, userId).set(FtbCultivatePromotionUser::getUserSelect, 0);
        ftbCultivatePromotionUserService.update(resetWrapper);

        // 然后将指定的学习地图选择标记设为1
        LambdaUpdateWrapper<FtbCultivatePromotionUser> selectWrapper = Wrappers.lambdaUpdate();
        selectWrapper.eq(FtbCultivatePromotionUser::getUserId, userId).eq(FtbCultivatePromotionUser::getPromotionId, promotionId).set(FtbCultivatePromotionUser::getUserSelect, 1);
        ftbCultivatePromotionUserService.update(selectWrapper);
    }

    /**
     * 学习地图的一个阶段，选定学习的岗位
     *
     * @param req 请求参数对象，包含岗位选择信息
     */
    @Override
    @Transactional
    public void recordSelectPosition(V2CultivatePromotionSelectPositionReq req) {

        if (CollUtil.isEmpty(req.getPostAndGradeList())) {
            throw new RuntimeException("请选择需要学习的岗位");
        }
        String userId = UserProvider.getUser().getUserId();
        UserBoundVO userPrimaryBoundOne = userApiV2Util.getUserPrimaryBoundOne(userId, null);
        if (userPrimaryBoundOne == null) {
            log.error("查询用户信息失败：{}", userId);
            return;
        }
        // 查询学习地图信息 ftb_cultivate_promotion_new
        FtbCultivatePromotionNew promotionNew = ftbCultivatePromotionNewService.getById(req.getPromotionId());
        if (promotionNew == null || promotionNew.getEnableMark() == 1) {
            throw new RuntimeException("该学习地图不存在");
        }
        if (promotionNew.getIsError().equals(1)) {
            throw new RuntimeException("该学习地图已异常");
        }

        // 查询学习地图岗位信息 ftb_cultivate_promotion_post_new
        LambdaQueryWrapper<FtbCultivatePromotionPostNew> postWrapper = Wrappers.lambdaQuery();
        postWrapper.eq(FtbCultivatePromotionPostNew::getPromotionId, req.getPromotionId()).eq(FtbCultivatePromotionPostNew::getLevel, req.getLevel()).eq(FtbCultivatePromotionPostNew::getDeleteMark, 0); // 只查询未删除的记录
        List<FtbCultivatePromotionPostNew> postList = ftbCultivatePromotionPostNewService.list(postWrapper);
        if (CollUtil.isEmpty(postList)) {
            throw new RuntimeException("本学习地图该阶段没有可以选择的岗位");
        }

        Map<String, FtbCultivatePromotionPostNew> promotionPostNewMap = new HashMap<>();
        for (FtbCultivatePromotionPostNew postNew : postList) {
            promotionPostNewMap.put(postNew.getPostId() + "_" + postNew.getGradeId(), postNew);
        }

        LambdaQueryWrapper<FtbCultivatePromotionUser> resetWrapper = Wrappers.lambdaQuery();
        resetWrapper.eq(FtbCultivatePromotionUser::getUserId, userId).eq(FtbCultivatePromotionUser::getPromotionId, req.getPromotionId());
        List<FtbCultivatePromotionUser> userList = ftbCultivatePromotionUserService.list(resetWrapper);
        if (CollUtil.isEmpty(userList)) {
            throw new RuntimeException("该用户没有加入该学习地图");
        }

        List<FtbCultivatePromotionMemberNew> promotionMemberNewList = new ArrayList<>();
        for (V2CultivatePostAndGradeVo postAndGrade : req.getPostAndGradeList()) {
            FtbCultivatePromotionPostNew ftbCultivatePromotionPostNew = promotionPostNewMap.get(postAndGrade.getPostId() + "_" + postAndGrade.getGradeId());
            if (ftbCultivatePromotionPostNew == null) {
                continue;
            }
            FtbCultivatePromotionMemberNew promotionMemberNew = new FtbCultivatePromotionMemberNew();
            promotionMemberNew.setPromotionId(req.getPromotionId());
            promotionMemberNew.setPromotionPostId(ftbCultivatePromotionPostNew.getId());
            promotionMemberNew.setUserId(userId);
            promotionMemberNew.setCurrentLearningStage(req.getLevel());
            promotionMemberNew.setDeleteMark(0);
            promotionMemberNew.setCreatorTime(new Date());
            promotionMemberNew.setCreatorUserId(userId);
            promotionMemberNew.setOrgName(userPrimaryBoundOne.getOrganizeName());
            promotionMemberNew.setOrgId(userPrimaryBoundOne.getOrganizeId());
            promotionMemberNew.setPositionName(ftbCultivatePromotionPostNew.getPostName());
            promotionMemberNew.setPositionId(ftbCultivatePromotionPostNew.getPostId());
            promotionMemberNew.setGradeName(ftbCultivatePromotionPostNew.getGradeName());
            promotionMemberNew.setGradeId(ftbCultivatePromotionPostNew.getGradeId());
            promotionMemberNewList.add(promotionMemberNew);
        }
        if (CollUtil.isNotEmpty(promotionMemberNewList)) {
            ftbCultivatePromotionMemberNewService.saveBatch(promotionMemberNewList);
        }

        // 初始化学习地图阶段日志表 FtbCultivatePromotionLog
        initializePromotionLogIfNeeded(userId, req.getPromotionId(), req.getLevel());

    }

    /**
     * 初始化学习地图阶段日志
     *
     * @param userId      用户ID
     * @param promotionId 学习地图ID
     * @param level       阶段编号
     */
    private void initializePromotionLogIfNeeded(String userId, String promotionId, Integer level) {
        // 检查该用户在该学习地图的该阶段是否已经有日志记录
        LambdaQueryWrapper<FtbCultivatePromotionLog> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FtbCultivatePromotionLog::getUserId, userId)
                .eq(FtbCultivatePromotionLog::getPromotionId, promotionId)
                .eq(FtbCultivatePromotionLog::getLevel, level)
                .eq(FtbCultivatePromotionLog::getEnabledMark, 0);

        long count = ftbCultivatePromotionLogService.count(queryWrapper);

        // 如果不存在日志记录，则创建新的日志记录（状态为未完成）
        if (count == 0) {
            FtbCultivatePromotionLog promotionLog = new FtbCultivatePromotionLog();
            promotionLog.setUserId(userId);
            promotionLog.setPromotionId(promotionId);
            promotionLog.setLevel(level);
            promotionLog.setState(1); // 1-未完成
            promotionLog.setEnabledMark(0); // 0-有效
            ftbCultivatePromotionLogService.save(promotionLog);
        }
    }

    /**
     * 获取岗位学习详情
     *
     * @param req 请求参数
     * @return 岗位学习详情
     */
    @Override
    public AppCultivatePromotionPositionDetailVo getPositionStudyDetail(V2CultivatePromotionPositionDetailForApp req) {
        req.checkParams();
        AppCultivatePromotionPositionDetailVo resultVo = new AppCultivatePromotionPositionDetailVo();
        LambdaQueryWrapper<FtbCultivatePromotionUser> resetWrapper = Wrappers.lambdaQuery();
        resetWrapper.eq(FtbCultivatePromotionUser::getUserId, req.getUserId()).eq(FtbCultivatePromotionUser::getPromotionId, req.getPromotionId());
        List<FtbCultivatePromotionUser> userList = ftbCultivatePromotionUserService.list(resetWrapper);
        if (CollUtil.isEmpty(userList)) {
            throw new RuntimeException("您没有加入该学习地图");
        }

        // 查询学习地图信息 ftb_cultivate_promotion_new
        FtbCultivatePromotionNew promotionNew = ftbCultivatePromotionNewService.getById(req.getPromotionId());
        if (promotionNew == null || promotionNew.getEnableMark() == 1) {
            throw new RuntimeException("该学习地图不存在");
        }
        if (promotionNew.getIsError().equals(1)) {
            throw new RuntimeException("该学习地图异常");
        }


        LambdaQueryWrapper<FtbCultivatePromotionPostNew> postWrapper = Wrappers.lambdaQuery();
        postWrapper.eq(FtbCultivatePromotionPostNew::getPromotionId, req.getPromotionId());
        postWrapper.eq(FtbCultivatePromotionPostNew::getPostId, req.getPostId());
        postWrapper.eq(FtbCultivatePromotionPostNew::getLevel, req.getLevel());
        postWrapper.eq(FtbCultivatePromotionPostNew::getDeleteMark, 0); // 只查询未删除的记录
        List<FtbCultivatePromotionPostNew> postList = ftbCultivatePromotionPostNewService.list(postWrapper);
        if (CollUtil.isEmpty(postList)) {
            throw new RuntimeException("该学习地图,第+" + req.getLevel() + "阶段没有该岗位");
        }

        //查询岗位学习的基本详情
        V2CultivatePositionDetailForApp v2CultivatePositionDetailForAppReq = new V2CultivatePositionDetailForApp();
        v2CultivatePositionDetailForAppReq.setPostId(req.getPostId());
        v2CultivatePositionDetailForAppReq.setGradeId(req.getGradeId());
        v2CultivatePositionDetailForAppReq.setUserId(req.getUserId());
        v2CultivatePositionDetailForAppReq.setCompulsory(req.getCompulsory());
        AppCultivatePositionDetailVo appCultivatePositionDetailVo = v2CultivatePositionService.positionStudyDetail(v2CultivatePositionDetailForAppReq);
        resultVo.setPositionDetail(appCultivatePositionDetailVo);
        //查询该阶段是否完成
        V2CultivatePromotionPhaseStateForApp promotionPhaseStateForAppReq = new V2CultivatePromotionPhaseStateForApp();
        promotionPhaseStateForAppReq.setPromotionId(req.getPromotionId());
        promotionPhaseStateForAppReq.setLevel(req.getLevel());
        promotionPhaseStateForAppReq.setUserId(req.getUserId());
        resultVo.setPhaseStats(promotionLevelStatus(promotionPhaseStateForAppReq));
        return resultVo;
    }

    /**
     * 获取学习地图阶段状态
     *
     * @param req 请求参数
     * @return 阶段状态 状态 0-未完成 1-已完成
     */
    @Override
    public Integer promotionLevelStatus(V2CultivatePromotionPhaseStateForApp req) {
        LambdaQueryWrapper<FtbCultivatePromotionLog> logWrapper = Wrappers.lambdaQuery();
        logWrapper.eq(FtbCultivatePromotionLog::getPromotionId, req.getPromotionId())
                .eq(FtbCultivatePromotionLog::getLevel, req.getLevel())
                .eq(FtbCultivatePromotionLog::getUserId, req.getUserId())
                .eq(FtbCultivatePromotionLog::getEnabledMark, 0);
        List<FtbCultivatePromotionLog> list = ftbCultivatePromotionLogService.list(logWrapper);
        if (CollUtil.isNotEmpty(list)) {
            return list.get(0).getState().equals(2) ? 1 : 0;
        } else {
            initializePromotionLogIfNeeded(req.getUserId(), req.getPromotionId(), req.getLevel());
            return 0;
        }
    }

    /**
     * 修改学习地图异常信息
     *
     * @param list 异常信息
     */
    @Override
    public void updatePromotionError(List<FtbCultivatePromotionPostNew> list) {
        // 按学习地图ID进行分组
        Map<String, List<FtbCultivatePromotionPostNew>> promotionIdMap = list.stream().collect(Collectors.groupingBy(FtbCultivatePromotionPostNew::getPromotionId));

        for (Map.Entry<String, List<FtbCultivatePromotionPostNew>> entry : promotionIdMap.entrySet()) {
            String promotionId = entry.getKey();
            List<FtbCultivatePromotionPostNew> promotionPosts = entry.getValue();

            // 转换异常岗位信息
            List<V2CultivatePostAndGradeVo> jsonList = new ArrayList<>();
            if (CollUtil.isNotEmpty(promotionPosts)) {
                for (FtbCultivatePromotionPostNew promotionPost : promotionPosts) {
                    V2CultivatePostAndGradeVo json = new V2CultivatePostAndGradeVo();
                    json.setPostId(promotionPost.getPostId());
                    json.setPostName(promotionPost.getPostName());
                    json.setGradeId(promotionPost.getGradeId());
                    json.setGradeName(promotionPost.getGradeName());
                    jsonList.add(json);
                }
            }

            // 创建异常信息VO
            V2CultivatePromotionErrorVo vo = new V2CultivatePromotionErrorVo();
            vo.setPositionDel(jsonList);

            // 更新学习地图状态
            ftbCultivatePromotionNewService.update(new UpdateWrapper<FtbCultivatePromotionNew>().lambda().eq(FtbCultivatePromotionNew::getId, promotionId).set(FtbCultivatePromotionNew::getIsError, 1).set(FtbCultivatePromotionNew::getErrorMsg, JSONUtil.toJsonStr(vo)));
        }
    }


    /**
     * 获取学习地图人员统计信息
     *
     * @param req 统计信息请求参数
     * @return 学习地图人员统计信息
     */
    @Override
    public PageListVO<V2CultivatePromotionPersonStatisticVo> personStatistics(V2PromotionPersonStatisticReq req) {
        // 1. 获取符合条件的用户ID列表
        List<String> userIds = getUserIdsForStatistics(req);
        if (userIds == null) {
            return UserApiV2Util.returnEmptyListGeneric(req.getCurrentPage(), req.getPageSize());
        }

        // 2. 执行分页查询
        Page<V2CultivatePromotionPersonStatisticVo> page = req.coverCultivatePage();
        page = ftbCultivatePromotionNewService.personStatisticsV2(req, userIds, page);
        List<V2CultivatePromotionPersonStatisticVo> records = page.getRecords();

        if (CollUtil.isEmpty(records)) {
            return CultivatePage.coverPageList(page);
        }

        // 3. 批量处理统计数据
        enrichPersonStatisticsData(records);

        return CultivatePage.coverPageList(page);
    }

    /**
     * 获取当前组织下的所有组织
     *
     * @param orgIds 组织ID列表
     * @return 组织ID列表，包括当前组织及其所有子组织
     */
    public List<String> queryChildOrg(List<String> orgIds) {
        List<OrganizeGeneralDetailVO> organizeGeneralDetailVOS = userApiV2Util.organizesOrHaveChildByOrganizeIds(orgIds, true, null);
        if (ObjectUtil.isEmpty(organizeGeneralDetailVOS)) {
            return new ArrayList<>();
        }
        return organizeGeneralDetailVOS.stream().map(OrganizeGeneralDetailVO::getId).collect(Collectors.toList());
    }

    /**
     * 获取学习地图组织统计信息
     *
     * @param req 统计信息请求参数
     * @return 学习地图组织统计信息
     */
    @Override
    public Page<V2CultivatePromotionOrgStatisticVo> organizationStatistics(V2PromotionOrgStatisticReq req) {
        Page<V2CultivatePromotionOrgStatisticVo> page = req.coverCultivatePage();
        page = ftbCultivatePromotionNewService.mapStatisticPageV2(page, req, new ArrayList<>());
        if (CollUtil.isNotEmpty(page.getRecords())) {
            List<BatchCommonCountDto> maxList = ftbCultivatePromotionNewService.queryMaxLevel(page.getRecords().stream().map(V2CultivatePromotionOrgStatisticVo::getPromotionId).collect(Collectors.toList()));
            Map<String, BatchCommonCountDto> promotionIdMap = maxList.stream().collect(Collectors.toMap(BatchCommonCountDto::getSelectKey, v -> v));

            List<BatchCommonCountDto> positionList = ftbCultivatePromotionNewService.queryPositionNumForMap(page.getRecords().stream().map(V2CultivatePromotionOrgStatisticVo::getPromotionId).collect(Collectors.toList()));
            Map<String, BatchCommonCountDto> positionNumMap = positionList.stream().collect(Collectors.toMap(BatchCommonCountDto::getSelectKey, v -> v));

            for (V2CultivatePromotionOrgStatisticVo record : page.getRecords()) {
                record.setPhaseNum(promotionIdMap.get(record.getPromotionId()) == null ? 0 : promotionIdMap.get(record.getPromotionId()).getNum());
                record.setNumberOfMapPositions(positionNumMap.get(record.getPromotionId()) == null ? 0 : positionNumMap.get(record.getPromotionId()).getNum());
            }
        }

        List<String> orgIds = new ArrayList<>();
        List<String> powerOrgList = userApiV2Util.queryPowerOrgList();
        if (CollUtil.isEmpty(powerOrgList)) {
            return page;
        }

        if ("1".equals(req.getSelectLogo()) && CollUtil.isNotEmpty(req.getOrgIds())) {
            List<String> allOrganize = queryChildOrg(req.getOrgIds());
            List<String> intersection = UserApiV2Util.getIntersection(powerOrgList, allOrganize);
            if (CollUtil.isEmpty(intersection)) {
                return page;
            }
            orgIds = intersection;
        } else if (CollUtil.isNotEmpty(req.getOrgIds())) {
            List<String> intersection = UserApiV2Util.getIntersection(powerOrgList, req.getOrgIds());
            if (CollUtil.isEmpty(intersection)) {
                return page;
            }
            orgIds = intersection;
        } else if (CollUtil.isEmpty(req.getOrgIds())) {
            orgIds = powerOrgList;
        }

        List<UserPageListVO> userListForOrgIds = userApiV2Util.getUserListForOrgIds(orgIds, null);
        if (CollUtil.isEmpty(userListForOrgIds)) {
            return page;
        }
        List<String> userIds = userListForOrgIds.stream().map(UserPageListVO::getId).collect(Collectors.toList());


        List<BatchCommonCountDto> userCount = ftbCultivatePromotionNewService.userCountForPromotion(userIds);
        Map<String, BatchCommonCountDto> userNumMap = userCount.stream().collect(Collectors.toMap(BatchCommonCountDto::getSelectKey, v -> v));


        for (V2CultivatePromotionOrgStatisticVo record : page.getRecords()) {
            record.setStudyUserNum(userNumMap.get(record.getPromotionId()) == null ? 0 : userNumMap.get(record.getPromotionId()).getNum());
            record.setStudyUserIds(userNumMap.get(record.getPromotionId()) == null ? new ArrayList<>() : List.of(userNumMap.get(record.getPromotionId()).getUserIds().split(",")));
        }
        return page;
    }

    @Override
    public Map<String, List<V2CultivatePostAndGradeVo>> queryAllSelectPosition(String userId, List<String> promotionIds) {

        LambdaQueryWrapper<FtbCultivatePromotionPostNew> postWrapper = Wrappers.lambdaQuery();
        postWrapper.in(FtbCultivatePromotionPostNew::getPromotionId, promotionIds);
        postWrapper.eq(FtbCultivatePromotionPostNew::getDeleteMark, 0); // 只查询未删除的记录
        List<FtbCultivatePromotionPostNew> postList = ftbCultivatePromotionPostNewService.list(postWrapper);
        if (CollUtil.isEmpty(postList)) {
            return new HashMap<>();
        }

        LambdaQueryWrapper<FtbCultivatePromotionMemberNew> memberWrapper = Wrappers.lambdaQuery();
        memberWrapper.in(FtbCultivatePromotionMemberNew::getPromotionId, promotionIds).eq(FtbCultivatePromotionMemberNew::getUserId, userId).eq(FtbCultivatePromotionMemberNew::getDeleteMark, 0); // 只查询未删除的记录
        List<FtbCultivatePromotionMemberNew> memberNewList = ftbCultivatePromotionMemberNewService.list(memberWrapper);
        if (CollUtil.isEmpty(memberNewList)) {
            return new HashMap<>();
        }


        List<String> postIds = new ArrayList<>();
        List<String> gradeIds = new ArrayList<>();
        Map<String, FtbCultivatePromotionPostNew> postIdMap = new HashMap<>();
        for (FtbCultivatePromotionPostNew ftbCultivatePromotionPostNew : postList) {
            postIdMap.put(ftbCultivatePromotionPostNew.getId(), ftbCultivatePromotionPostNew);
            postIds.add(ftbCultivatePromotionPostNew.getPostId());
            if (StringUtils.isNotEmpty(ftbCultivatePromotionPostNew.getGradeId())) {
                gradeIds.add(ftbCultivatePromotionPostNew.getGradeId());
            }
        }
        Map<String, PositionVO> stringPositionVOMap = userApiV2Util.listPositionDetailInfoByIdsReturnMap(postIds, null);
        Map<String, GradeVO> stringGradeVOMap = userApiV2Util.listGradeByIdsReturnMap(gradeIds, null);
        //设置配置的岗位学习
        return PromotionLevelInfoVo.convertToPositionAndGradeNextUser(stringGradeVOMap, stringPositionVOMap, memberNewList);

    }


    private List<String> getUserIdsForStatistics(V2PromotionPersonStatisticReq req) {
        List<String> userIds = new ArrayList<>();

        // 处理组织机构筛选
        if (CollUtil.isNotEmpty(req.getOrgIds())) {
            List<UserPageListVO> orgUsers = userApiV2Util.getUserListForOrgIds(req.getOrgIds(), null);
            if (CollUtil.isEmpty(orgUsers)) {
                return null;
            }

            List<String> orgUserIds = orgUsers.stream().map(UserPageListVO::getId).collect(Collectors.toList());
            if (CollUtil.isEmpty(req.getUserIds())) {
                userIds = orgUserIds;
            } else {
                // 取交集：同时满足组织筛选和用户筛选
                List<String> intersection = UserApiV2Util.getIntersection(req.getUserIds(), orgUserIds);
                if (CollUtil.isEmpty(intersection)) {
                    return null;
                }
                userIds = intersection;
            }
        } else if (CollUtil.isNotEmpty(req.getUserIds())) {
            // 如果没有指定组织但指定了用户，直接使用指定的用户ID（去重）
            userIds = req.getUserIds().stream().distinct().collect(Collectors.toList());
        }

        // 处理权限控制
        InnerPowerUserVO powerUser = userApiV2Util.getLoginManagerUserIdsForEncode();
        if (powerUser.getCode() == 2) {
            // 无权限，返回null
            return null;
        }

        if (powerUser.getCode() == 1) {
            // 有部分权限，需要与权限用户取交集
            if (CollUtil.isNotEmpty(userIds)) {
                List<String> intersection = UserApiV2Util.getIntersection(powerUser.getUserIds(), userIds);
                return CollUtil.isNotEmpty(intersection) ? intersection : null;
            } else {
                // 未指定任何筛选条件，返回所有有权限的用户
                userIds = powerUser.getUserIds();
            }
        }
        // powerUser.getCode() == 0 表示完全权限，直接返回已筛选的userIds（可能为空列表）

        return userIds;
    }

    /**
     * 批量丰富人员统计数据
     */
    private void enrichPersonStatisticsData(List<V2CultivatePromotionPersonStatisticVo> records) {
        // 提取用户ID和学习地图ID
        Set<String> userIds = records.stream().map(V2CultivatePromotionPersonStatisticVo::getUserId).collect(Collectors.toSet());
        Set<String> promotionIds = records.stream().map(V2CultivatePromotionPersonStatisticVo::getPromotionId).collect(Collectors.toSet());

        List<BatchCommonCountDto> maxList = ftbCultivatePromotionNewService.queryMaxLevel(new ArrayList<>(promotionIds));
        Map<String, BatchCommonCountDto> promotionIdMap = maxList.stream().collect(Collectors.toMap(BatchCommonCountDto::getSelectKey, v -> v));


        // 并行获取所有需要的数据
        PersonStatisticsDataDto data = loadPersonStatisticsData(userIds, promotionIds);

        // 填充统计数据
        for (V2CultivatePromotionPersonStatisticVo record : records) {
            enrichSingleRecord(record, data);
            BatchCommonCountDto max = promotionIdMap.get(record.getPromotionId());
            if (max != null) {
                record.setPhaseNum(max.getNum());
            } else {
                record.setPhaseNum(0);
            }
        }
    }

    /**
     * 加载人员统计所需的所有数据
     */
    private PersonStatisticsDataDto loadPersonStatisticsData(Set<String> userIds, Set<String> promotionIds) {
        PersonStatisticsDataDto data = new PersonStatisticsDataDto();

        // 1. 获取学习地图岗位职级信息
        List<FtbCultivatePromotionPostNew> postList = getPromotionPosts(promotionIds);
        data.postMap = postList.stream().collect(Collectors.toMap(FtbCultivatePromotionPostNew::getId, p -> p));
        data.postGroupMap = postList.stream().collect(Collectors.groupingBy(FtbCultivatePromotionPostNew::getPromotionId));

        List<String> postIds = postList.stream().map(FtbCultivatePromotionPostNew::getPostId).distinct().collect(Collectors.toList());
        List<String> gradeIds = postList.stream().map(FtbCultivatePromotionPostNew::getGradeId).filter(StringUtils::isNotEmpty).distinct().collect(Collectors.toList());

        // 2. 批量获取基础数据
        data.gradeMap = userApiV2Util.listGradeByIdsReturnMap(gradeIds, null);
        data.positionMap = userApiV2Util.listPositionDetailInfoByIdsReturnMap(postIds, null);
        data.userBoundMap = userApiV2Util.getUserPrimaryBoundBatch(new ArrayList<>(userIds), null);

        // 3. 获取已完成的岗位记录
        List<FtbCultivatePositionLog> completePositions = ftbCultivatePositionLogService.batchQueryCompletePosition(new ArrayList<>(userIds));
        data.completePositionMap = completePositions.stream().collect(Collectors.groupingBy(FtbCultivatePositionLog::getUserId));

        // 4. 获取成员学习信息
        data.memberMap = getPromotionMembers(promotionIds, userIds);

        return data;
    }

    private List<FtbCultivatePromotionPostNew> getPromotionPosts(Set<String> promotionIds) {
        LambdaQueryWrapper<FtbCultivatePromotionPostNew> wrapper = Wrappers.lambdaQuery();
        wrapper.in(FtbCultivatePromotionPostNew::getPromotionId, promotionIds);
        wrapper.eq(FtbCultivatePromotionPostNew::getDeleteMark, 0);
        return ftbCultivatePromotionPostNewService.list(wrapper);
    }

    private Map<String, List<FtbCultivatePromotionMemberNew>> getPromotionMembers(Set<String> promotionIds, Set<String> userIds) {
        LambdaQueryWrapper<FtbCultivatePromotionMemberNew> wrapper = Wrappers.lambdaQuery();
        wrapper.select(FtbCultivatePromotionMemberNew::getUserId, FtbCultivatePromotionMemberNew::getPromotionId, FtbCultivatePromotionMemberNew::getCurrentLearningStage, FtbCultivatePromotionMemberNew::getPromotionPostId);
        wrapper.in(FtbCultivatePromotionMemberNew::getPromotionId, promotionIds);
        wrapper.in(FtbCultivatePromotionMemberNew::getUserId, userIds);
        List<FtbCultivatePromotionMemberNew> members = ftbCultivatePromotionMemberNewService.list(wrapper);
        return members.stream().collect(Collectors.groupingBy(m -> m.getUserId() + "-" + m.getPromotionId()));
    }

    private void enrichSingleRecord(V2CultivatePromotionPersonStatisticVo record, PersonStatisticsDataDto data) {
        UserBoundVO userBound = data.userBoundMap.get(record.getUserId());
        if (userBound == null) return;

        record.setFullName(userBound.getUserName());
        record.setOrganizeName(userBound.getOrganizeName());
        record.setOrganizeId(userBound.getOrganizeId());
        record.setSysEmployeeID(userBound.getSystemWorkerId());
        record.setEmployeeSCurrentPosition(buildCurrentPosition(userBound));

        List<FtbCultivatePromotionPostNew> posts = data.postGroupMap.get(record.getPromotionId());
        if (CollUtil.isNotEmpty(posts)) {
            record.setNumberOfMapPositions(posts.size());
        }

        List<FtbCultivatePromotionMemberNew> members = data.memberMap.get(record.getUserId() + "-" + record.getPromotionId());
        List<FtbCultivatePositionLog> completePositions = data.completePositionMap.get(record.getUserId());

        if (CollUtil.isNotEmpty(members) && CollUtil.isNotEmpty(completePositions)) {
            record.setPositionLearned(buildLearnedPositions(members, completePositions, data));
        }
    }

    private String buildCurrentPosition(UserBoundVO userBound) {
        StringBuilder sb = new StringBuilder();
        if (StringUtils.isNotEmpty(userBound.getPositionName())) {
            sb.append(userBound.getPositionName());
        }
        if (StringUtils.isNotEmpty(userBound.getGradeName())) {
            if (sb.length() > 0) sb.append("_");
            sb.append(userBound.getGradeName());
        }
        return sb.toString();
    }

    private String buildLearnedPositions(List<FtbCultivatePromotionMemberNew> members, List<FtbCultivatePositionLog> completePositions, PersonStatisticsDataDto data) {
        StringBuilder learnedPositions = new StringBuilder();
        Set<String> completedKeys = completePositions.stream().map(log -> StringUtils.isEmpty(log.getGradeId()) ? log.getPostId() : log.getPostId() + "-" + log.getGradeId()).collect(Collectors.toSet());

        for (FtbCultivatePromotionMemberNew member : members) {
            FtbCultivatePromotionPostNew post = data.postMap.get(member.getPromotionPostId());
            if (post == null) continue;

            String postKey = StringUtils.isEmpty(post.getGradeId()) ? post.getPostId() : post.getPostId() + "-" + post.getGradeId();
            if (completedKeys.contains(postKey)) {
                PositionVO position = data.positionMap.get(post.getPostId());
                if (position != null) {
                    StringBuilder sb = new StringBuilder(position.getFullName());
                    if (StringUtils.isNotEmpty(post.getGradeId())) {
                        GradeVO grade = data.gradeMap.get(post.getGradeId());
                        if (grade != null) {
                            sb.append("|").append(grade.getFullName());
                        }
                    }
                    learnedPositions.append(sb).append(" ");
                }
            }
        }
        return learnedPositions.toString().trim();
    }

    /**
     * 根据用户ID查询用户的所有学习地图及分阶段岗位信息
     *
     * @param userId 用户ID
     * @return List<UserPromotionDetailVo> 用户学习地图详情列表
     */
    @Override
    public UserPromotionDetailVo getUserPromotionDetail(String userId, String promotionId) {
        // 1. 获取指定学习地图信息
        MyCultivatePromotionListVo promotion = ftbCultivatePromotionUserService.queryMyPromotionById(userId, promotionId);
        if (promotion == null) {
            return null;
        }

        // 2. 查询该学习地图的岗位配置信息
        LambdaQueryWrapper<FtbCultivatePromotionPostNew> postWrapper = Wrappers.lambdaQuery();
        postWrapper.eq(FtbCultivatePromotionPostNew::getPromotionId, promotionId);
        postWrapper.eq(FtbCultivatePromotionPostNew::getDeleteMark, 0);
        List<FtbCultivatePromotionPostNew> allPostList = ftbCultivatePromotionPostNewService.list(postWrapper);

        // 3. 按level分组岗位配置
        Map<Integer, List<FtbCultivatePromotionPostNew>> levelPostMap = new HashMap<>();
        if (CollUtil.isNotEmpty(allPostList)) {
            levelPostMap = allPostList.stream()
                    .collect(Collectors.groupingBy(FtbCultivatePromotionPostNew::getLevel));
        }

        // 4. 收集所有需要的岗位ID和职级ID
        Set<String> allPostIds = new HashSet<>();
        Set<String> allGradeIds = new HashSet<>();
        for (FtbCultivatePromotionPostNew post : allPostList) {
            allPostIds.add(post.getPostId());
            if (StringUtils.isNotEmpty(post.getGradeId())) {
                allGradeIds.add(post.getGradeId());
            }
        }

        // 5. 批量查询岗位和职级信息
        Map<String, PositionVO> positionMap = userApiV2Util.listPositionDetailInfoByIdsReturnMap(new ArrayList<>(allPostIds), null);
        Map<String, GradeVO> gradeMap = userApiV2Util.listGradeByIdsReturnMap(new ArrayList<>(allGradeIds), null);

        // 6. 组装返回结果
        UserPromotionDetailVo detailVo = new UserPromotionDetailVo();
        detailVo.setId(promotion.getId());
        detailVo.setPromotion(promotion.getPromotion());
        detailVo.setPromotionBusinessId(promotion.getPromotionBusinessId());

        // 7. 组装该学习地图的分阶段岗位信息
        List<PromotionPhasePositionVo> phaseList = new ArrayList<>();

        if (CollUtil.isNotEmpty(levelPostMap)) {
            // 按阶段编号排序
            List<Integer> sortedLevels = levelPostMap.keySet().stream()
                    .sorted()
                    .collect(Collectors.toList());

            for (Integer level : sortedLevels) {
                PromotionPhasePositionVo phaseVo = new PromotionPhasePositionVo();
                phaseVo.setLevel(level);

                List<FtbCultivatePromotionPostNew> postList = levelPostMap.get(level);
                List<V2CultivatePostAndGradeVo> positionVoList = new ArrayList<>();

                for (FtbCultivatePromotionPostNew post : postList) {
                    V2CultivatePostAndGradeVo vo = new V2CultivatePostAndGradeVo();
                    vo.setId(post.getId());
                    vo.setPostId(post.getPostId());

                    PositionVO positionVO = positionMap.get(post.getPostId());
                    if (positionVO != null) {
                        vo.setPostName(positionVO.getFullName());
                    }

                    if (StringUtils.isNotEmpty(post.getGradeId())) {
                        vo.setGradeId(post.getGradeId());
                        GradeVO gradeVO = gradeMap.get(post.getGradeId());
                        if (gradeVO != null) {
                            vo.setGradeName(gradeVO.getFullName());
                        }
                    }

                    positionVoList.add(vo);
                }

                phaseVo.setPositionList(positionVoList);
                phaseList.add(phaseVo);
            }
        }

        detailVo.setPhaseList(phaseList);
        return detailVo;
    }
}