package cn.iocoder.yudao.module.pms.service.iotprojecttaskplan;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingattrs.vo.IotOperationMeetingAttrsPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanStatisticsItemRespVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanStatisticsRespVO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeetingattrs.IotOperationMeetingAttrsDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskplan.IotProjectTaskPlanDO;
import cn.iocoder.yudao.module.pms.dal.mysql.iotoperationmeetingattrs.IotOperationMeetingAttrsMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttaskplan.IotProjectTaskPlanMapper;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.dict.DictDataService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PROJECT_TASK_PLAN_EXISTS;
import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PROJECT_TASK_PLAN_NOT_EXISTS;

/**
 * 日报任务计划(工作量) Service 实现类
 *
 * @author ruqi
 */
@Service
@Validated
public class IotProjectTaskPlanServiceImpl implements IotProjectTaskPlanService {

    @Resource
    private IotProjectTaskPlanMapper iotProjectTaskPlanMapper;

    @Resource
    private IotOperationMeetingAttrsMapper iotOperationMeetingAttrsMapper;

    @Resource
    private DeptMapper deptMapper;

    @Resource
    private DeptService deptService;

    @Resource
    private DictDataService dictDataService;

    @Override
    public Long createIotProjectTaskPlan(IotProjectTaskPlanSaveReqVO createReqVO) {
        // 插入
        IotProjectTaskPlanDO iotProjectTaskPlan = BeanUtils.toBean(createReqVO, IotProjectTaskPlanDO.class);
        if (ObjUtil.isEmpty(createReqVO.getDeptId()) || ObjUtil.isEmpty(createReqVO.getStartTime())) {
            throw exception(IOT_PROJECT_TASK_PLAN_NOT_EXISTS);
        }
        // 校验当前队伍部门 指定时间所属月份 是否已经存在 工作量计划记录
        IotProjectTaskPlanDO existPlan = iotProjectTaskPlanMapper.selectOneByDeptIdAndMonth(createReqVO.getDeptId(), createReqVO.getStartTime(), createReqVO.getBusiness(), null);
        if (ObjUtil.isNotEmpty(existPlan)) {
            throw exception(IOT_PROJECT_TASK_PLAN_EXISTS);
        }
        iotProjectTaskPlanMapper.insert(iotProjectTaskPlan);
        // 返回
        return iotProjectTaskPlan.getId();
    }

    @Override
    public void updateIotProjectTaskPlan(IotProjectTaskPlanSaveReqVO updateReqVO) {
        // 校验存在
        validateIotProjectTaskPlanExists(updateReqVO.getId());
        // 校验当前队伍部门 指定时间所属月份 是否已经存在 工作量计划记录
        IotProjectTaskPlanDO existPlan = iotProjectTaskPlanMapper.selectOneByDeptIdAndMonth(updateReqVO.getDeptId(), updateReqVO.getStartTime(), updateReqVO.getBusiness(), updateReqVO.getId());
        if (ObjUtil.isNotEmpty(existPlan)) {
            throw exception(IOT_PROJECT_TASK_PLAN_EXISTS);
        }
        // 更新
        IotProjectTaskPlanDO updateObj = BeanUtils.toBean(updateReqVO, IotProjectTaskPlanDO.class);
        iotProjectTaskPlanMapper.updateById(updateObj);
    }

    @Override
    public void deleteIotProjectTaskPlan(Long id) {
        // 校验存在
        validateIotProjectTaskPlanExists(id);
        // 删除
        iotProjectTaskPlanMapper.deleteById(id);
    }

    private void validateIotProjectTaskPlanExists(Long id) {
        if (iotProjectTaskPlanMapper.selectById(id) == null) {
            throw exception(IOT_PROJECT_TASK_PLAN_NOT_EXISTS);
        }
    }

    @Override
    public IotProjectTaskPlanDO getIotProjectTaskPlan(Long id) {
        return iotProjectTaskPlanMapper.selectById(id);
    }

    @Override
    public PageResult<IotProjectTaskPlanDO> getIotProjectTaskPlanPage(IotProjectTaskPlanPageReqVO pageReqVO) {
        return iotProjectTaskPlanMapper.selectPage(pageReqVO);
    }

    @Override
    public List<IotTaskAttrModelProperty> planExtProperties(IotProjectTaskPlanPageReqVO pageReqVO) {
        // deptId 需要传递公司级的部门id
        Long deptId = pageReqVO.getDeptId();
        if (ObjUtil.isEmpty(deptId)) {
            return Collections.emptyList();
        }
        // 如果 deptId 是集团级部门 科瑞石油技术 则查询所有公司的工作量扩展属性列表
        DeptDO currentDept = deptMapper.selectById(deptId);
        boolean groupFlag = false;
        if ("0".equals(currentDept.getType())) {
            groupFlag = true;
        }
        // 根据任务井所属的 公司id 查询工作量扩展属性
        IotOperationMeetingAttrsPageReqVO reqVO = new IotOperationMeetingAttrsPageReqVO();
        if (!groupFlag) {
            reqVO.setDeptId(deptId);
        }
        reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        PageResult<IotOperationMeetingAttrsDO> pageAttrs = iotOperationMeetingAttrsMapper.selectPage(reqVO);
        List<IotOperationMeetingAttrsDO> workloadAttrs = pageAttrs.getList();
        if (CollUtil.isEmpty(workloadAttrs)) {
            return Collections.emptyList();
        }

        // 4. Java8 Stream 一站式处理：过滤符合条件属性 → 按name去重 → 按sort升序排序
        Map<String, IotTaskAttrModelProperty> uniqueAttrMap = workloadAttrs.stream()
                // 提取扩展属性，过滤空对象
                .map(IotOperationMeetingAttrsDO::getExtProperty)
                .filter(ObjUtil::isNotEmpty)
                // 过滤条件：名称包含“计划”、默认值为空
                .filter(property -> property.getName().contains("计划") && StrUtil.isBlank(property.getDefaultValue()))
                // 按name分组，同名只保留第一条数据
                .collect(Collectors.toMap(
                        IotTaskAttrModelProperty::getName,
                        property -> property,
                        (existing, replacement) -> existing // 冲突时保留先出现的第一条
                ));

        // map转list，按sort升序排序（保证前端展示顺序统一）
        List<IotTaskAttrModelProperty> result = new ArrayList<>(uniqueAttrMap.values());
        result.sort(Comparator.comparing(IotTaskAttrModelProperty::getSort));

        return result;
    }

    @Override
    public List<IotProjectTaskPlanStatisticsRespVO> summaryStatistics(IotProjectTaskPlanPageReqVO pageReqVO) {
        List<IotProjectTaskPlanStatisticsRespVO> result = new ArrayList<>();
        // 不分页统计所有数据
        Set<Long> ids = new HashSet<>();
        if (ObjUtil.isEmpty(pageReqVO.getDeptId())) {
            // 如果没有传递 deptId 参数 默认根据当前登录人所属公司
            return result;
        }
        ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
        // 找到所有根部门子部门对象集合
        ids.add(pageReqVO.getDeptId());
        // 查询所有工作量计划列表
        pageReqVO.setDeptIds(ids);
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        PageResult<IotProjectTaskPlanDO> pagePlans = iotProjectTaskPlanMapper.selectPage(pageReqVO);
        List<IotProjectTaskPlanDO> plans = pagePlans.getList();
        /* if (CollUtil.isEmpty(plans)) {
            return result;
        } */
        // 默认显示所有公司的汇总数据 点击公司 显示各项目部数据
        // 点击项目部 显示 下属队伍的数据
        // 首先判断点击的部门是属于 公司 还是 队伍 如果没有点击任何部门 默认选中最顶级部门
        // 判断点击的组织树中的部门类型 类型(公司级1 项目部2 队伍3)
        DeptDO selectedDept = deptService.getDept(pageReqVO.getDeptId());
        if ("0".equals(selectedDept.getType())) {
            // 选中集团 查询所有公司的汇总数据
            result = statisticsByCompany(plans, pageReqVO);
        } else if ("1".equals(selectedDept.getType())) {
            // 选中公司 查询公司下所有项目部的汇总数据
            result = statisticsByProjectDept(plans, pageReqVO);
        } else if ("2".equals(selectedDept.getType())) {
            // 选中项目部 查询项目部下所有队伍的汇总数据
            result = statisticsByProjectTeam(plans, pageReqVO);
        } else if ("3".equals(selectedDept.getType())) {
            // 选中队伍 查询单个队伍的汇总数据
            result = statisticsByProjectTeam(plans, pageReqVO);
        } else {
            // 点击的部门没有类型 如 瑞恒 新疆分公司 查询下属项目部的汇总数据
            result = statisticsByProjectDept(plans, pageReqVO);
        }

        // 根据result集合内对象的 sort 属性正序排列 sort 类型为 integer 类型
        if (CollUtil.isNotEmpty(result)) {
            result.sort(Comparator.comparing(
                    IotProjectTaskPlanStatisticsRespVO::getSort,
                    Comparator.nullsLast(Comparator.naturalOrder())
            ));
        }

        return result;
    }

    /**
     * 按专业公司维度 汇总 统计数据
     * @param plans 月度计划工作量列表
     * @param pageReqVO 包含根部门ID（如 156L为 集团 根部门，或其他公司级部门ID）
     * @return 公司维度统计结果列表
     */
    private List<IotProjectTaskPlanStatisticsRespVO> statisticsByCompany(List<IotProjectTaskPlanDO> plans, IotProjectTaskPlanPageReqVO pageReqVO) {
        List<IotProjectTaskPlanStatisticsRespVO> result = new ArrayList<>();

        Set<Long> projectDeptIds = new HashSet<>();
        // key公司id    value公司级部门
        Map<Long, DeptDO> companyDeptPair = new HashMap<>();
        // key项目部id    value项目部名称
        Map<Long, DeptDO> projectDeptPair = new HashMap<>();
        // key部门id   value部门parentId
        Map<Long, Long> teamProjectIdPair = new HashMap<>();
        // key队伍id    value队伍部门
        Map<Long, DeptDO> teamDeptPair = new HashMap<>();

        // key公司id   value累计 注气量(万方)
        Map<Long, BigDecimal> cumulativeGasInjectionPair = new HashMap<>();
        // key公司id   value累计 进尺(米)
        Map<Long, BigDecimal> cumulativeFootagePair = new HashMap<>();
        // key公司id   value累计 井次(井)    修井 井下 连油 井次
        Map<Long, BigDecimal> cumulativeWellCountPair = new HashMap<>();
        // key公司id   value累计 压裂(层)    瑞都压裂层  5#国压裂层
        Map<Long, BigDecimal> cumulativeFracLayerPair = new HashMap<>();
        // key公司id   value业务类型
        Map<Long, String> projectBusinessPair = new HashMap<>();

        // 月度统计（公司 -> 年月 -> 统计值）
        Map<Long, Map<String, MonthlyStats>> projectMonthStatsMap = new HashMap<>();

        //  计划工作量 项目业务类型 key字典键值     value字典标签
        Map<String, String> workloadPlanDictPair = new HashMap<>();

        // key项目业务类型     value项目部id集合
        Map<String, Set<Long>> projectBizPair = new HashMap<>();

        // 队伍id 集合
        Set<Long> teamIds = new HashSet<>();

        // 查询3个专业公司的所有子部门 组装队伍与公司的关联关系
        Set<Long> allRhChildDeptIds = deptService.getChildDeptIdListFromCache(157l);
        Set<Long> allRyChildDeptIds = deptService.getChildDeptIdListFromCache(158l);
        Set<Long> allRdChildDeptIds = deptService.getChildDeptIdListFromCache(163l);
        Set<Long> allFiveChildDeptIds = deptService.getChildDeptIdListFromCache(388l);
        // 公司ID -> 公司全部下属子部门集合
        Map<Long, Set<Long>> companyAllChildDeptMap = new HashMap<>(4);
        companyAllChildDeptMap.put(157L, allRhChildDeptIds);
        companyAllChildDeptMap.put(158L, allRyChildDeptIds);
        companyAllChildDeptMap.put(163L, allRdChildDeptIds);
        companyAllChildDeptMap.put(388L, allFiveChildDeptIds);

        // 以项目部为维度统计数据
        // 找到所有项目部与队伍的对应关系
        // 查询指定根部门下的所有子部门
        Set<Long> allJtChildDeptIds = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
        DeptListReqVO reqVO = new DeptListReqVO();
        reqVO.setDeptIds(allJtChildDeptIds);
        List<DeptDO> departments = deptService.getDeptList(reqVO);

        // 查询 计划工作量 项目业务类型 项目业务(注气/钻井/修井/压裂/连油/井下)
        List<DictDataDO> workloadPlanBizData = dictDataService.getDictDataListByDictType("rq_iot_workload_plan_biz");
        if (CollUtil.isNotEmpty(workloadPlanBizData)) {
            workloadPlanBizData.forEach(tech -> {
                String label = tech.getLabel();
                workloadPlanDictPair.put(tech.getValue(), label);
                // 设置每个项目部的业务类型
                String projectDeptIdsStr = tech.getRemark();
                if (StrUtil.isNotBlank(projectDeptIdsStr)) {
                    // 数据格式 234,124,832
                    // 逗号分割、转Long、过滤空字符串、收集到Set
                    Set<Long> deptIdSet = StrUtil.splitTrim(projectDeptIdsStr, ",")
                            .stream()
                            .filter(StrUtil::isNotBlank)
                            .map(Long::valueOf)
                            .collect(Collectors.toSet());

                    // 存入map：存在则合并集合，不存在则新建
                    projectBizPair.computeIfAbsent(label, k -> new HashSet<>()).addAll(deptIdSet);
                }
            });
        }

        // 构建项目部映射和父子部门关系
        departments.forEach(dept -> {
            if ("1".equals(dept.getType()) && dept.getStatus() == 0) {
                // 公司
                companyDeptPair.put(dept.getId(), dept);
            }
            if ("2".equals(dept.getType())) {
                // 项目部
                projectDeptIds.add(dept.getId());
                projectDeptPair.put(dept.getId(), dept);
            }
            if ("3".equals(dept.getType())) {
                // 队伍
                teamIds.add(dept.getId());
                teamDeptPair.put(dept.getId(), dept);
            }
            // 所有部门与上级部门的 id 对应关系
            teamProjectIdPair.put(dept.getId(), dept.getParentId());

        });
        // 遍历 计划工作量列表 以公司为维度组装 工作量数据
        plans.forEach(plan -> {
            Long teamDeptId = plan.getDeptId();
            BigDecimal gasInjection = ObjUtil.isNotEmpty(plan.getPlanGasInjection()) ? plan.getPlanGasInjection() : BigDecimal.ZERO;
            BigDecimal footage = ObjUtil.isNotEmpty(plan.getPlanFootage()) ? plan.getPlanFootage() : BigDecimal.ZERO;
            BigDecimal wellTrips = ObjUtil.isNotEmpty(plan.getPlanWellTrips()) ? plan.getPlanWellTrips() : BigDecimal.ZERO;
            BigDecimal layers = ObjUtil.isNotEmpty(plan.getPlanLayers()) ? plan.getPlanLayers() : BigDecimal.ZERO;
            String business = plan.getBusiness();
            if (ObjUtil.isNotEmpty(teamDeptId) && teamProjectIdPair.containsKey(teamDeptId)) {
                // 公司id
                Long companyDeptId = 0l;
                // 判断当前队伍所属的专业公司
                if (allRhChildDeptIds.contains(teamDeptId)) {
                    // 瑞恒公司下的队伍
                    companyDeptId = 157l;
                } else if (allRyChildDeptIds.contains(teamDeptId)) {
                    // 瑞鹰公司下的队伍
                    companyDeptId = 158l;
                } else if (allRdChildDeptIds.contains(teamDeptId)) {
                    // 瑞都公司下的队伍
                    companyDeptId = 163l;
                } else {
                    // 5#国的队伍
                    companyDeptId = 388l;
                }
                if (ObjUtil.isNotEmpty(companyDeptId) && (companyDeptId > 0)) {
                    // 累计注气量
                    cumulativeGasInjectionPair.compute(companyDeptId, (k, v) -> ObjUtil.isEmpty(v) ? gasInjection : v.add(gasInjection));
                    // 累计进尺
                    cumulativeFootagePair.compute(companyDeptId, (k, v) -> ObjUtil.isEmpty(v) ? footage : v.add(footage));
                    // 累计井次
                    cumulativeWellCountPair.compute(companyDeptId, (k, v) -> ObjUtil.isEmpty(v) ? wellTrips : v.add(wellTrips));
                    // 累计压裂层
                    cumulativeFracLayerPair.compute(companyDeptId, (k, v) -> ObjUtil.isEmpty(v) ? layers : v.add(layers));
                    // 工作量业务类型
                    if (StrUtil.isNotBlank(business) && workloadPlanDictPair.containsKey(business)) {
                        projectBusinessPair.put(companyDeptId, workloadPlanDictPair.get(business));
                    }
                }

                // 按照 年 月 汇总统计 计划工作量 如 2025-03
                LocalDateTime startTime = plan.getStartTime();
                if (ObjUtil.isNotEmpty(startTime)) {
                    // 提取出时间中的 年 月
                    Integer year = startTime.getYear();
                    Integer month = startTime.getMonthValue();
                    String uniqueKey = StrUtil.join("-", year, month);
                    // 统计各公司下包含的按照 yyyy-MM 统计的各项工作量
                    // 获取该公司的月度Map
                    Map<String, MonthlyStats> monthMap = projectMonthStatsMap.computeIfAbsent(companyDeptId, k -> new HashMap<>());
                    // 获取该年月的统计对象
                    MonthlyStats stats = monthMap.computeIfAbsent(uniqueKey, k -> new MonthlyStats());
                    // 累加
                    stats.gasInjection = stats.gasInjection.add(gasInjection);
                    stats.footage = stats.footage.add(footage);
                    stats.wellCount = stats.wellCount.add(wellTrips);
                    stats.fracLayer = stats.fracLayer.add(layers);
                }
            }
        });

        // 组装查询时间区间内包含的 年 月
        LocalDateTime[] startTimeArr = pageReqVO.getStartTime();
        if (ObjUtil.isEmpty(startTimeArr)) {
            return result;
        }
        if (startTimeArr.length < 1) {
            return result;
        }
        List<String> allMonths = generateMonthRange(startTimeArr[0], startTimeArr[1]);

        // 生成返回的数据列表集合
        companyDeptPair.forEach((deptId, dept) -> {
            IotProjectTaskPlanStatisticsRespVO statistics = new IotProjectTaskPlanStatisticsRespVO();
            statistics.setCompanyId(deptId);
            statistics.setCompanyName(dept.getName());
            statistics.setSort(dept.getSort());
            statistics.setType("1");
            statistics.setCumulativeGasInjection(cumulativeGasInjectionPair.getOrDefault(deptId, BigDecimal.ZERO));
            statistics.setCumulativeFootage(cumulativeFootagePair.getOrDefault(deptId, BigDecimal.ZERO));
            statistics.setCumulativeWellCount(cumulativeWellCountPair.getOrDefault(deptId, BigDecimal.ZERO));
            statistics.setCumulativeFracLayer(cumulativeFracLayerPair.getOrDefault(deptId, BigDecimal.ZERO));

            // 设置每个公司的 业务类型
            Set<Long> companyChildDepts = companyAllChildDeptMap.getOrDefault(deptId, Collections.emptySet());
            List<String> bizNameList = projectBizPair.entrySet().stream()
                    .filter(entry -> CollUtil.isNotEmpty(entry.getValue())
                            && CollUtil.intersection(entry.getValue(), companyChildDepts).size() > 0)
                    .map(Map.Entry::getKey)
                    .distinct()
                    .collect(Collectors.toList());
            String businessName = bizNameList.isEmpty() ? StrUtil.EMPTY : CollUtil.join(bizNameList, ",");
            statistics.setBusiness(businessName);

            // 处理月度明细
            Map<String, MonthlyStats> existingMonthMap = projectMonthStatsMap.get(deptId);
            List<IotProjectTaskPlanStatisticsItemRespVO> items = new ArrayList<>();
            if (allMonths.isEmpty()) {
                // 无时间范围时，仅输出已有的月度（兼容原逻辑）
                if (CollUtil.isNotEmpty(existingMonthMap)) {
                    items = existingMonthMap.entrySet().stream()
                            .sorted(Map.Entry.comparingByKey())
                            .map(entry -> {
                                IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
                                item.setYearMonth(entry.getKey());
                                MonthlyStats ms = entry.getValue();
                                item.setCumulativeGasInjection(ms.gasInjection);
                                item.setCumulativeFootage(ms.footage);
                                item.setCumulativeWellCount(ms.wellCount);
                                item.setCumulativeFracLayer(ms.fracLayer);
                                return item;
                            })
                            .collect(Collectors.toList());
                }
            } else {
                // ★ 补全所有月份
                for (String month : allMonths) {
                    MonthlyStats stats = CollUtil.isNotEmpty(existingMonthMap) ? existingMonthMap.get(month) : null;
                    if (stats == null) {
                        stats = new MonthlyStats(); // 默认所有字段为 0
                    }
                    IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
                    item.setYearMonth(month);
                    item.setCumulativeGasInjection(stats.gasInjection);
                    item.setCumulativeFootage(stats.footage);
                    item.setCumulativeWellCount(stats.wellCount);
                    item.setCumulativeFracLayer(stats.fracLayer);
                    items.add(item);
                }
            }

            if (!items.isEmpty()) {
                statistics.setItems(items);
            }

            result.add(statistics);
        });

        return result;
    }

    /**
     * 按项目部维度 汇总 统计数据
     * @param plans 月度计划工作量列表
     * @param pageReqVO 包含根部门ID（如 163L为 瑞都 根部门，或其他公司级部门ID）
     * @return 项目部维度统计结果列表
     */
    private List<IotProjectTaskPlanStatisticsRespVO> statisticsByProjectDept(List<IotProjectTaskPlanDO> plans, IotProjectTaskPlanPageReqVO pageReqVO) {
        List<IotProjectTaskPlanStatisticsRespVO> result = new ArrayList<>();

        Set<Long> projectDeptIds = new HashSet<>();
        // key项目部id    value项目部名称
        Map<Long, DeptDO> projectDeptPair = new HashMap<>();
        // key部门id   value部门parentId
        Map<Long, Long> teamProjectIdPair = new HashMap<>();
        // key队伍id    value队伍部门
        Map<Long, DeptDO> teamDeptPair = new HashMap<>();

        // key队伍id/项目部id   value累计 注气量(万方)
        Map<Long, BigDecimal> cumulativeGasInjectionPair = new HashMap<>();
        // key队伍id/项目部id   value累计 进尺(米)
        Map<Long, BigDecimal> cumulativeFootagePair = new HashMap<>();
        // key队伍id/项目部id   value累计 井次(井)    修井 井下 连油 井次
        Map<Long, BigDecimal> cumulativeWellCountPair = new HashMap<>();
        // key队伍id/项目部id   value累计 压裂(层)    瑞都压裂层  5#国压裂层
        Map<Long, BigDecimal> cumulativeFracLayerPair = new HashMap<>();
        // key队伍id/项目部id   value业务类型
        Map<Long, String> projectBusinessPair = new HashMap<>();

        // 月度统计（项目部 -> 年月 -> 统计值）
        Map<Long, Map<String, MonthlyStats>> projectMonthStatsMap = new HashMap<>();

        //  计划工作量 项目业务类型 key字典键值     value字典标签
        Map<String, String> workloadPlanDictPair = new HashMap<>();

        // key项目业务类型     value项目部id集合
        Map<String, Set<Long>> projectBizPair = new HashMap<>();

        // 队伍id 集合
        Set<Long> teamIds = new HashSet<>();

        // 以项目部为维度统计数据
        // 找到所有项目部与队伍的对应关系
        // 查询指定根部门下的所有子部门
        Set<Long> allRhChildDeptIds = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
        DeptListReqVO reqVO = new DeptListReqVO();
        reqVO.setDeptIds(allRhChildDeptIds);
        List<DeptDO> departments = deptService.getDeptList(reqVO);

        // 查询 计划工作量 项目业务类型 项目业务(注气/钻井/修井/压裂/连油/井下)
        List<DictDataDO> workloadPlanBizData = dictDataService.getDictDataListByDictType("rq_iot_workload_plan_biz");
        if (CollUtil.isNotEmpty(workloadPlanBizData)) {
            workloadPlanBizData.forEach(tech -> {
                String label = tech.getLabel();
                workloadPlanDictPair.put(tech.getValue(), label);
                // 设置每个项目部的业务类型
                String projectDeptIdsStr = tech.getRemark();
                if (StrUtil.isNotBlank(projectDeptIdsStr)) {
                    // 数据格式 234,124,832
                    // 逗号分割、转Long、过滤空字符串、收集到Set
                    Set<Long> deptIdSet = StrUtil.splitTrim(projectDeptIdsStr, ",")
                            .stream()
                            .filter(StrUtil::isNotBlank)
                            .map(Long::valueOf)
                            .collect(Collectors.toSet());

                    // 存入map：存在则合并集合，不存在则新建
                    projectBizPair.computeIfAbsent(label, k -> new HashSet<>()).addAll(deptIdSet);
                }
            });
        }

        // 构建项目部映射和父子部门关系
        departments.forEach(dept -> {
            if ("2".equals(dept.getType())) {
                // 项目部
                projectDeptIds.add(dept.getId());
                projectDeptPair.put(dept.getId(), dept);
            }
            if ("3".equals(dept.getType())) {
                // 队伍
                teamIds.add(dept.getId());
                teamDeptPair.put(dept.getId(), dept);
            }
            // 所有部门与上级部门的 id 对应关系
            teamProjectIdPair.put(dept.getId(), dept.getParentId());

        });
        // 遍历 计划工作量列表 以项目部为维度组装 工作量数据
        plans.forEach(plan -> {
            BigDecimal gasInjection = ObjUtil.isNotEmpty(plan.getPlanGasInjection()) ? plan.getPlanGasInjection() : BigDecimal.ZERO;
            BigDecimal footage = ObjUtil.isNotEmpty(plan.getPlanFootage()) ? plan.getPlanFootage() : BigDecimal.ZERO;
            BigDecimal wellTrips = ObjUtil.isNotEmpty(plan.getPlanWellTrips()) ? plan.getPlanWellTrips() : BigDecimal.ZERO;
            BigDecimal layers = ObjUtil.isNotEmpty(plan.getPlanLayers()) ? plan.getPlanLayers() : BigDecimal.ZERO;
            String business = plan.getBusiness();
            if (ObjUtil.isNotEmpty(plan.getDeptId()) && teamProjectIdPair.containsKey(plan.getDeptId())) {
                // 项目部id
                Long projectDeptId = 0l;
                if (teamDeptPair.containsKey(plan.getDeptId())) {
                    // 日报 deptId 如果是队伍 获取队伍的上级项目部
                    projectDeptId = teamProjectIdPair.get(plan.getDeptId());
                }
                if (projectDeptPair.containsKey(plan.getDeptId())) {
                    // 日报 deptId 如果是项目部 使用当前项目部
                    projectDeptId = plan.getDeptId();
                }

                if (ObjUtil.isNotEmpty(projectDeptId) && (projectDeptId > 0)) {
                    // 累计注气量
                    cumulativeGasInjectionPair.compute(projectDeptId, (k, v) -> ObjUtil.isEmpty(v) ? gasInjection : v.add(gasInjection));
                    // 累计进尺
                    cumulativeFootagePair.compute(projectDeptId, (k, v) -> ObjUtil.isEmpty(v) ? footage : v.add(footage));
                    // 累计井次
                    cumulativeWellCountPair.compute(projectDeptId, (k, v) -> ObjUtil.isEmpty(v) ? wellTrips : v.add(wellTrips));
                    // 累计压裂层
                    cumulativeFracLayerPair.compute(projectDeptId, (k, v) -> ObjUtil.isEmpty(v) ? layers : v.add(layers));
                    // 工作量业务类型
                    if (StrUtil.isNotBlank(business) && workloadPlanDictPair.containsKey(business)) {
                        projectBusinessPair.put(projectDeptId, workloadPlanDictPair.get(business));
                    }
                }

                // 按照 年 月 汇总统计 计划工作量 如 2025-03
                LocalDateTime startTime = plan.getStartTime();
                if (ObjUtil.isNotEmpty(startTime)) {
                    // 提取出时间中的 年 月
                    Integer year = startTime.getYear();
                    Integer month = startTime.getMonthValue();
                    String uniqueKey = StrUtil.join("-", year, month);
                    // 统计各项目下包含的按照 yyyy-MM 统计的各项工作量
                    // 获取该项目部的月度Map
                    Map<String, MonthlyStats> monthMap = projectMonthStatsMap.computeIfAbsent(projectDeptId, k -> new HashMap<>());
                    // 获取该年月的统计对象
                    MonthlyStats stats = monthMap.computeIfAbsent(uniqueKey, k -> new MonthlyStats());
                    // 累加
                    stats.gasInjection = stats.gasInjection.add(gasInjection);
                    stats.footage = stats.footage.add(footage);
                    stats.wellCount = stats.wellCount.add(wellTrips);
                    stats.fracLayer = stats.fracLayer.add(layers);
                }
            }
        });

        // 组装查询时间区间内包含的 年 月
        LocalDateTime[] startTimeArr = pageReqVO.getStartTime();
        if (ObjUtil.isEmpty(startTimeArr)) {
            return result;
        }
        if (startTimeArr.length < 1) {
            return result;
        }
        List<String> allMonths = generateMonthRange(startTimeArr[0], startTimeArr[1]);

        // 生成返回的数据列表集合
        projectDeptPair.forEach((deptId, dept) -> {
            IotProjectTaskPlanStatisticsRespVO statistics = new IotProjectTaskPlanStatisticsRespVO();
            statistics.setProjectDeptId(deptId);
            statistics.setProjectDeptName(dept.getName());
            statistics.setSort(dept.getSort());
            statistics.setType("2");
            statistics.setCumulativeGasInjection(cumulativeGasInjectionPair.getOrDefault(deptId, BigDecimal.ZERO));
            statistics.setCumulativeFootage(cumulativeFootagePair.getOrDefault(deptId, BigDecimal.ZERO));
            statistics.setCumulativeWellCount(cumulativeWellCountPair.getOrDefault(deptId, BigDecimal.ZERO));
            statistics.setCumulativeFracLayer(cumulativeFracLayerPair.getOrDefault(deptId, BigDecimal.ZERO));

            // 遍历全部业务映射，收集该项目部绑定的所有业务（支持多业务）
            List<String> bizNameList = new ArrayList<>();
            for (Map.Entry<String, Set<Long>> bizEntry : projectBizPair.entrySet()) {
                Set<Long> bindDeptIds = bizEntry.getValue();
                if (CollUtil.isNotEmpty(bindDeptIds) && bindDeptIds.contains(deptId)) {
                    bizNameList.add(bizEntry.getKey());
                }
            }
            // 多业务逗号拼接，无匹配则为null
            String businessName = CollUtil.isNotEmpty(bizNameList) ? CollUtil.join(bizNameList, ",") : StrUtil.EMPTY;
            statistics.setBusiness(businessName);

            // 处理月度明细
            Map<String, MonthlyStats> existingMonthMap = projectMonthStatsMap.get(deptId);
            List<IotProjectTaskPlanStatisticsItemRespVO> items = new ArrayList<>();
            if (allMonths.isEmpty()) {
                // 无时间范围时，仅输出已有的月度（兼容原逻辑）
                if (CollUtil.isNotEmpty(existingMonthMap)) {
                    items = existingMonthMap.entrySet().stream()
                            .sorted(Map.Entry.comparingByKey())
                            .map(entry -> {
                                IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
                                item.setYearMonth(entry.getKey());
                                MonthlyStats ms = entry.getValue();
                                item.setCumulativeGasInjection(ms.gasInjection);
                                item.setCumulativeFootage(ms.footage);
                                item.setCumulativeWellCount(ms.wellCount);
                                item.setCumulativeFracLayer(ms.fracLayer);
                                return item;
                            })
                            .collect(Collectors.toList());
                }
            } else {
                // ★ 补全所有月份
                for (String month : allMonths) {
                    MonthlyStats stats = CollUtil.isNotEmpty(existingMonthMap) ? existingMonthMap.get(month) : null;
                    if (stats == null) {
                        stats = new MonthlyStats(); // 默认所有字段为 0
                    }
                    IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
                    item.setYearMonth(month);
                    item.setCumulativeGasInjection(stats.gasInjection);
                    item.setCumulativeFootage(stats.footage);
                    item.setCumulativeWellCount(stats.wellCount);
                    item.setCumulativeFracLayer(stats.fracLayer);
                    items.add(item);
                }
            }

            if (!items.isEmpty()) {
                statistics.setItems(items);
            }

            result.add(statistics);
        });

        return result;
    }

    /**
     * 按队伍维度 汇总 统计数据
     * @param plans 月度计划工作量列表
     * @param pageReqVO 包含根部门ID（如 163L为 瑞都 根部门，或其他公司级部门ID）
     * @return 队伍维度统计结果列表
     */
    private List<IotProjectTaskPlanStatisticsRespVO> statisticsByProjectTeam(List<IotProjectTaskPlanDO> plans, IotProjectTaskPlanPageReqVO pageReqVO) {
        List<IotProjectTaskPlanStatisticsRespVO> result = new ArrayList<>();

        Set<Long> teamDeptIds = new HashSet<>();
        // key项目部id    value项目部名称
        Map<Long, DeptDO> projectDeptPair = new HashMap<>();
        // key队伍id    value队伍名称
        Map<Long, DeptDO> teamDeptPair = new HashMap<>();
        // key部门id   value部门parentId
        Map<Long, Long> teamProjectIdPair = new HashMap<>();

        // key队伍id/项目部id   value累计 注气量(万方)
        Map<Long, BigDecimal> cumulativeGasInjectionPair = new HashMap<>();
        // key队伍id/项目部id   value累计 进尺(米)
        Map<Long, BigDecimal> cumulativeFootagePair = new HashMap<>();
        // key队伍id/项目部id   value累计 井次(井)    修井 井下 连油 井次
        Map<Long, BigDecimal> cumulativeWellCountPair = new HashMap<>();
        // key队伍id/项目部id   value累计 压裂(层)    瑞都压裂层  5#国压裂层
        Map<Long, BigDecimal> cumulativeFracLayerPair = new HashMap<>();
        // key队伍id/项目部id   value业务类型
        Map<Long, String> projectBusinessPair = new HashMap<>();

        // 月度统计（队伍 -> 年月 -> 统计值）
        Map<Long, Map<String, MonthlyStats>> teamMonthStatsMap = new HashMap<>();

        //  计划工作量 项目业务类型 key字典键值     value字典标签
        Map<String, String> workloadPlanDictPair = new HashMap<>();

        // key项目业务类型     value项目部id集合
        Map<String, Set<Long>> projectBizPair = new HashMap<>();

        // 以 队伍 为维度统计数据
        // 查询指定根部门下的所有子部门
        Set<Long> allRhChildDeptIds = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
        DeptListReqVO reqVO = new DeptListReqVO();
        // 查询某支队伍 或 所属项目部的信息
        allRhChildDeptIds.add(pageReqVO.getDeptId());
        reqVO.setDeptIds(allRhChildDeptIds);
        List<DeptDO> departments = deptService.getDeptList(reqVO);

        // 查询 计划工作量 项目业务类型 项目业务(注气/钻井/修井/压裂/连油/井下)
        List<DictDataDO> workloadPlanBizData = dictDataService.getDictDataListByDictType("rq_iot_workload_plan_biz");
        if (CollUtil.isNotEmpty(workloadPlanBizData)) {
            workloadPlanBizData.forEach(tech -> {
                String label = tech.getLabel();
                workloadPlanDictPair.put(tech.getValue(), label);
                // 设置每个项目部的业务类型
                String projectDeptIdsStr = tech.getRemark();
                if (StrUtil.isNotBlank(projectDeptIdsStr)) {
                    // 数据格式 234,124,832
                    // 逗号分割、转Long、过滤空字符串、收集到Set
                    Set<Long> deptIdSet = StrUtil.splitTrim(projectDeptIdsStr, ",")
                            .stream()
                            .filter(StrUtil::isNotBlank)
                            .map(Long::valueOf)
                            .collect(Collectors.toSet());

                    // 存入map：存在则合并集合，不存在则新建
                    projectBizPair.computeIfAbsent(label, k -> new HashSet<>()).addAll(deptIdSet);
                }
            });
        }
        // 组装队伍集合
        departments.forEach(dept -> {
            if ("3".equals(dept.getType())) {
                // 队伍
                teamDeptIds.add(dept.getId());
                teamDeptPair.put(dept.getId(), dept);
            }
            if ("2".equals(dept.getType())) {
                // 项目部
                projectDeptPair.put(dept.getId(), dept);
            }
            teamProjectIdPair.put(dept.getId(), dept.getParentId());
        });

        // 遍历 计划工作量列表 以队伍为维度组装 工作量数据
        plans.forEach(plan -> {
            Long deptId = plan.getDeptId();
            BigDecimal gasInjection = ObjUtil.isNotEmpty(plan.getPlanGasInjection()) ? plan.getPlanGasInjection() : BigDecimal.ZERO;
            BigDecimal footage = ObjUtil.isNotEmpty(plan.getPlanFootage()) ? plan.getPlanFootage() : BigDecimal.ZERO;
            BigDecimal wellTrips = ObjUtil.isNotEmpty(plan.getPlanWellTrips()) ? plan.getPlanWellTrips() : BigDecimal.ZERO;
            BigDecimal layers = ObjUtil.isNotEmpty(plan.getPlanLayers()) ? plan.getPlanLayers() : BigDecimal.ZERO;
            String business = plan.getBusiness();
            if (ObjUtil.isNotEmpty(deptId) && teamProjectIdPair.containsKey(deptId)) {
                // 累计注气量
                cumulativeGasInjectionPair.compute(deptId, (k, v) -> ObjUtil.isEmpty(v) ? gasInjection : v.add(gasInjection));
                // 累计进尺
                cumulativeFootagePair.compute(deptId, (k, v) -> ObjUtil.isEmpty(v) ? footage : v.add(footage));
                // 累计井次
                cumulativeWellCountPair.compute(deptId, (k, v) -> ObjUtil.isEmpty(v) ? wellTrips : v.add(wellTrips));
                // 累计压裂层
                cumulativeFracLayerPair.compute(deptId, (k, v) -> ObjUtil.isEmpty(v) ? layers : v.add(layers));
                // 工作量业务类型
                if (StrUtil.isNotBlank(business) && workloadPlanDictPair.containsKey(business)) {
                    projectBusinessPair.put(deptId, workloadPlanDictPair.get(business));
                }

                // 按照 年 月 汇总统计 计划工作量 如 2025-03
                LocalDateTime startTime = plan.getStartTime();
                if (ObjUtil.isNotEmpty(startTime)) {
                    // 提取出时间中的 年 月
                    Integer year = startTime.getYear();
                    Integer month = startTime.getMonthValue();
                    String uniqueKey = StrUtil.join("-", year, month);
                    // 统计各项目下包含的按照 yyyy-MM 统计的各项工作量
                    // 获取该项目部的月度Map
                    Map<String, MonthlyStats> monthMap = teamMonthStatsMap.computeIfAbsent(deptId, k -> new HashMap<>());
                    // 获取该年月的统计对象
                    MonthlyStats stats = monthMap.computeIfAbsent(uniqueKey, k -> new MonthlyStats());
                    // 累加
                    stats.gasInjection = stats.gasInjection.add(gasInjection);
                    stats.footage = stats.footage.add(footage);
                    stats.wellCount = stats.wellCount.add(wellTrips);
                    stats.fracLayer = stats.fracLayer.add(layers);
                }
            }
        });

        // 组装查询时间区间内包含的 年 月
        LocalDateTime[] startTimeArr = pageReqVO.getStartTime();
        if (ObjUtil.isEmpty(startTimeArr)) {
            return result;
        }
        if (startTimeArr.length < 1) {
            return result;
        }
        List<String> allMonths = generateMonthRange(startTimeArr[0], startTimeArr[1]);

        // 队伍数据  生成返回的数据列表集合
        teamDeptPair.forEach((teamDeptId, dept) -> {
            IotProjectTaskPlanStatisticsRespVO statistics = new IotProjectTaskPlanStatisticsRespVO();
            statistics.setTeamId(teamDeptId);
            statistics.setTeamName(dept.getName());
            statistics.setSort(dept.getSort());
            if (teamDeptPair.containsKey(teamDeptId)) {
                statistics.setType("3");    // 最好是 项目部小队同时展示 因为有的瑞都日报挂在了项目部下
            }
            statistics.setCumulativeGasInjection(cumulativeGasInjectionPair.getOrDefault(teamDeptId, BigDecimal.ZERO));
            statistics.setCumulativeFootage(cumulativeFootagePair.getOrDefault(teamDeptId, BigDecimal.ZERO));
            statistics.setCumulativeWellCount(cumulativeWellCountPair.getOrDefault(teamDeptId, BigDecimal.ZERO));
            statistics.setCumulativeFracLayer(cumulativeFracLayerPair.getOrDefault(teamDeptId, BigDecimal.ZERO));

            // 遍历全部业务映射，收集该项目部绑定的所有业务（支持多业务）
            List<String> bizNameList = new ArrayList<>();
            if (teamProjectIdPair.containsKey(teamDeptId)) {
                // 当前队伍的上级项目部id
                Long projectDeptId = teamProjectIdPair.get(teamDeptId);
                for (Map.Entry<String, Set<Long>> bizEntry : projectBizPair.entrySet()) {
                    Set<Long> bindDeptIds = bizEntry.getValue();
                    if (CollUtil.isNotEmpty(bindDeptIds) && bindDeptIds.contains(projectDeptId)) {
                        bizNameList.add(bizEntry.getKey());
                    }
                }
            }
            // 多业务逗号拼接，无匹配则为null
            String businessName = CollUtil.isNotEmpty(bizNameList) ? CollUtil.join(bizNameList, ",") : StrUtil.EMPTY;
            statistics.setBusiness(businessName);

            // 处理月度明细
            Map<String, MonthlyStats> existingMonthMap = teamMonthStatsMap.get(teamDeptId);
            List<IotProjectTaskPlanStatisticsItemRespVO> items = new ArrayList<>();
            if (allMonths.isEmpty()) {
                // 无时间范围时，仅输出已有的月度（兼容原逻辑）
                if (CollUtil.isNotEmpty(existingMonthMap)) {
                    items = existingMonthMap.entrySet().stream()
                            .sorted(Map.Entry.comparingByKey())
                            .map(entry -> {
                                IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
                                item.setYearMonth(entry.getKey());
                                MonthlyStats ms = entry.getValue();
                                item.setCumulativeGasInjection(ms.gasInjection);
                                item.setCumulativeFootage(ms.footage);
                                item.setCumulativeWellCount(ms.wellCount);
                                item.setCumulativeFracLayer(ms.fracLayer);
                                return item;
                            })
                            .collect(Collectors.toList());
                }
            } else {
                // ★ 补全所有月份
                for (String month : allMonths) {
                    MonthlyStats stats = existingMonthMap != null ? existingMonthMap.get(month) : null;
                    if (stats == null) {
                        stats = new MonthlyStats(); // 默认所有字段为 0
                    }
                    IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
                    item.setYearMonth(month);
                    item.setCumulativeGasInjection(stats.gasInjection);
                    item.setCumulativeFootage(stats.footage);
                    item.setCumulativeWellCount(stats.wellCount);
                    item.setCumulativeFracLayer(stats.fracLayer);
                    items.add(item);
                }
            }

            if (!items.isEmpty()) {
                statistics.setItems(items);
            }

            result.add(statistics);
        });

        return result;
    }

    /**
     * 生成从 start 到 end 之间的所有月份字符串（格式：yyyy-M，无前导零）
     */
    private List<String> generateMonthRange(LocalDateTime start, LocalDateTime end) {
        if (ObjUtil.isEmpty(start) || ObjUtil.isEmpty(end)) {
            return Collections.emptyList();
        }
        List<String> months = new ArrayList<>();
        YearMonth startYm = YearMonth.from(start);
        YearMonth endYm = YearMonth.from(end);
        YearMonth current = startYm;
        while (!current.isAfter(endYm)) {
            months.add(current.getYear() + "-" + current.getMonthValue());
            current = current.plusMonths(1);
        }
        return months;
    }

    // 内部类：月度统计辅助类
    private static class MonthlyStats {
        BigDecimal gasInjection = BigDecimal.ZERO;
        BigDecimal footage = BigDecimal.ZERO;
        BigDecimal wellCount = BigDecimal.ZERO;
        BigDecimal fracLayer = BigDecimal.ZERO;
    }

}