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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
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.framework.datapermission.core.util.DataPermissionUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.pms.constant.PmsConstants;
import cn.iocoder.yudao.module.pms.controller.admin.iotattachment.vo.IotAttachmentSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotdailyreportfuel.vo.IotDailyReportFuelSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotdeviceassociate.vo.IotDeviceAssociatePageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotprojectinfo.vo.IotProjectInfoPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotProjectTaskPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.*;
import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreportdetail.vo.IotRdDailyReportDetailSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDevicePageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.vo.IotProductClassifyListReqVO;
import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.IotProductClassifyDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotattachment.IotAttachmentDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotdailyreportfuel.IotDailyReportFuelDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotdeviceassociate.IotDeviceAssociateDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojectinfo.IotProjectInfoDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttask.IotProjectTaskDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotrddailyreport.IotRdDailyReportDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotrddailyreportdetail.IotRdDailyReportDetailDO;
import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.IotProductClassifyMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotattachment.IotAttachmentMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotdailyreportfuel.IotDailyReportFuelMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotdeviceassociate.IotDeviceAssociateMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotprojectinfo.IotProjectInfoMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttask.IotProjectTaskMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotrddailyreport.IotRdDailyReportMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotrddailyreportdetail.IotRdDailyReportDetailMapper;
import cn.iocoder.yudao.module.pms.enums.AttachmentCategoryEnum;
import cn.iocoder.yudao.module.pms.enums.AttachmentTypeEnum;
import cn.iocoder.yudao.module.pms.message.PmsMessage;
import cn.iocoder.yudao.module.pms.util.LocalTimeUtils;
import cn.iocoder.yudao.module.supplier.enums.common.SupplierAuditStatusEnum;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
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.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.dict.DictDataService;
import cn.iocoder.yudao.module.system.service.permission.RoleService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.ImmutableMap;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.*;
import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;

/**
 * 瑞都日报 Service 实现类
 *
 * @author ruiqi
 */
@Service
@Validated
public class IotRdDailyReportServiceImpl implements IotRdDailyReportService {

    @Resource
    private IotRdDailyReportMapper iotRdDailyReportMapper;
    @Resource
    private IotProjectInfoMapper iotProjectInfoMapper;
    @Resource
    private IotProjectTaskMapper iotProjectTaskMapper;
    @Resource
    private IotAttachmentMapper iotAttachmentMapper;
    @Resource
    private DictDataService dictDataService;
    @Resource
    private DeptService deptService;
    @Resource
    private AdminUserService adminUserService;
    @Resource
    private RoleService roleService;
    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource(name = PMS_THREAD_POOL_TASK_EXECUTOR)
    private ThreadPoolTaskExecutor pmsThreadPoolTaskExecutor;
    @Resource
    private PmsMessage pmsMessage;
    @Resource
    private IotDeviceMapper iotDeviceMapper;
    @Resource
    private IotDailyReportFuelMapper iotDailyReportFuelMapper;
    @Resource
    private IotProductClassifyMapper iotProductClassifyMapper;
    @Resource
    private IotRdDailyReportDetailMapper iotRdDailyReportDetailMapper;
    @Resource
    private IotDeviceAssociateMapper iotDeviceAssociateMapper;

    @Override
    public Long createIotRdDailyReport(IotRdDailyReportSaveReqVO createReqVO) {
        // 插入
        IotRdDailyReportDO iotRdDailyReport = BeanUtils.toBean(createReqVO, IotRdDailyReportDO.class);
        // 如果没有传递 deptId 则默认使用当前登录人的 部门
        if (ObjUtil.isEmpty(createReqVO.getDeptId())) {
            Long deptId = SecurityFrameworkUtils.getLoginUserDeptId();
            if (ObjUtil.isNotEmpty(deptId)) {
                createReqVO.setDeptId(deptId);
            }
        }
        LocalDateTime createTime = createReqVO.getCreateTime();
        // 根据部门名称 当前日期 生成日报标题 新增日报时生成标题 编辑时标题不变
        if (ObjUtil.isNotEmpty(createReqVO.getDeptId()) && ObjUtil.isEmpty(createReqVO.getId())) {
            DeptDO dept = deptService.getDept(createReqVO.getDeptId());
            LocalDateTime tempDateTime = ObjUtil.isEmpty(createTime) ? LocalDateTime.now() : createTime;
            if (ObjUtil.isNotEmpty(dept)) {
                String todayDateStr = LocalDateTimeUtil.format(tempDateTime, DatePattern.NORM_DATE_PATTERN);
                iotRdDailyReport.setReportName(dept.getName() + "/" + todayDateStr + "日报");

                // 唯一性校验 每个施工队伍 每天只能有1份无工作量日报
                if (ObjUtil.isEmpty(createReqVO.getProjectId()) || ObjUtil.isEmpty(createReqVO.getTaskId())) {
                    // 查询瑞都日报列表中 当前施工队伍 当前日期todayDateStr 是否存在记录
                    IotRdDailyReportDO existedReport = iotRdDailyReportMapper.selectExistReport(createReqVO.getDeptId(), null, todayDateStr);
                    if (ObjUtil.isNotEmpty(existedReport) && ObjUtil.isEmpty(existedReport.getProjectId())
                            && ObjUtil.isEmpty(existedReport.getTaskId()) ) {
                        // 施工队伍 指定日期 已经存在了无工作量的日报
                        throw exception(IOT_RD_NO_WORK_DAILY_REPORT_EXISTS);
                    }
                }
            }
        }
        // 临时新建的日报 审批状态 默认为 审批通过 status 赋值为1 填写完成
        iotRdDailyReport.setAuditStatus(SupplierAuditStatusEnum.APPROVE.getStatus());
        iotRdDailyReport.setStatus(1);
        if (ObjUtil.isNotEmpty(createReqVO.getId())) {
            // 编辑现有数据
            iotRdDailyReportMapper.updateById(iotRdDailyReport);
        } else {
            // 新建日报记录
            iotRdDailyReportMapper.insert(iotRdDailyReport);
        }
        // 保存生产动态明细
        if (CollUtil.isNotEmpty(createReqVO.getReportDetails())) {
            // 先删除明细 再新增
            List<IotRdDailyReportDetailSaveReqVO> reportDetails = createReqVO.getReportDetails();
            int count = iotRdDailyReportDetailMapper.deleteByMap(ImmutableMap.of(
                    "report_id", iotRdDailyReport.getId()
            ));
            // 新增生产动态明细
            List<IotRdDailyReportDetailDO> reportDOs = new ArrayList<>();
            reportDetails.forEach(detail -> {
                String detailEndTime = detail.getEndTime();
                if (StrUtil.isNotBlank(detailEndTime)) {
                    String[] timeAttr = detailEndTime.split(":");
                    if ("24".equals(timeAttr[0])) {
                        detail.setEndTime("23:59:59");
                    }
                }
                IotRdDailyReportDetailDO tempDetail = BeanUtils.toBean(detail, IotRdDailyReportDetailDO.class);
                tempDetail.setDeptId(createReqVO.getDeptId());
                // 临时新建的日报 不关联项目 任务
                // tempDetail.setTaskId(updateObj.getTaskId());
                tempDetail.setReportId(iotRdDailyReport.getId());
                tempDetail.setReportDate(LocalDateTime.now());
                reportDOs.add(tempDetail);
            });
            // 插入日报明细记录
            if (CollUtil.isNotEmpty(reportDOs)) {
                iotRdDailyReportDetailMapper.insertBatch(reportDOs);
            }
        }
        // 保存附件
        if (CollUtil.isNotEmpty(createReqVO.getAttachments())) {
            List<IotAttachmentSaveReqVO> attachments = createReqVO.getAttachments();
            List<IotAttachmentDO> tobeAddedAttachments = new ArrayList<>();
            attachments.forEach(att -> {
                IotAttachmentDO attachment = new IotAttachmentDO();
                attachment.setBizId(iotRdDailyReport.getId());
                attachment.setCategory(AttachmentCategoryEnum.DAILY_REPORT.getCode());
                attachment.setType(AttachmentTypeEnum.EXTERNAL_RENTAL.getCode());
                attachment.setFilePath(att.getFilePath());
                attachment.setFileSize(att.getFileSize());
                attachment.setFilename(att.getFilename());
                attachment.setFileType(att.getFileType());
                attachment.setRemark(att.getRemark());
                tobeAddedAttachments.add(attachment);
            });
            if (CollUtil.isNotEmpty(tobeAddedAttachments)) {
                // 先删除再新增
                int count = iotAttachmentMapper.deleteByMap(ImmutableMap.of(
                        "category", AttachmentCategoryEnum.DAILY_REPORT.getCode(),
                        "biz_id", iotRdDailyReport.getId(),
                        "type", AttachmentTypeEnum.EXTERNAL_RENTAL.getCode()
                ));
                iotAttachmentMapper.insertBatch(tobeAddedAttachments);
            }
        } else {
            // 如果传递的附件列表为空 则删除已有的附件 有可能编辑已有数据
            int count = iotAttachmentMapper.deleteByMap(ImmutableMap.of(
                    "category", AttachmentCategoryEnum.DAILY_REPORT.getCode(),
                    "biz_id", iotRdDailyReport.getId(),
                    "type", AttachmentTypeEnum.EXTERNAL_RENTAL.getCode()
            ));
        }
        // 返回
        return iotRdDailyReport.getId();
    }

    @Override
    public IotRdDailyReportDO updateIotRdDailyReport(IotRdDailyReportSaveReqVO updateReqVO) {
        // 校验存在
        IotRdDailyReportDO dailyReport = validateIotRdDailyReportExists(updateReqVO.getId());
        // 如果 endTime = 24:00 转换成 23:59:59 否则会引发异常 Parse [2025-11-22 24:00] with format [yyyy-MM-dd HH:mm] error!
        String endTime = updateReqVO.getEndTime();
        if (StrUtil.isNotBlank(endTime)) {
            String[] timeAttr = endTime.split(":");
            if ("24".equals(timeAttr[0])) {
                updateReqVO.setEndTime("23:59:59");
            }
        }
        // 更新
        IotRdDailyReportDO updateObj = BeanUtils.toBean(updateReqVO, IotRdDailyReportDO.class);
        // 如果是修改 非生产时间 ，则不必再发起审批
        if (StrUtil.isBlank(updateReqVO.getNonProduct())) {
            // 正常填写4小时周报 不必设置日报状态为 已完成 可以每4小时填报1闪
            // updateObj.setStatus(1);
            // 设置 日报审批状态为 审批中 auditStatus = 10
            updateObj.setAuditStatus(10);
        } else {
            // 生产运营部填写了 非生产时间 施工简报 可以设置 日报状态为 已完成 不允许再次填报
            updateObj.setStatus(1);
        }
        // 如果 修改 了时效 只要保存 不管是否 填写了 非生产时间 都确认为已经 维护了 非生产时间
        if (StrUtil.isNotBlank(updateReqVO.getNonProduct())) {
            updateObj.setProcessInstanceId("2");
        }
        iotRdDailyReportMapper.updateById(updateObj);
        // 同步更新日报关联任务的 施工状态
        if (ObjUtil.isNotEmpty(dailyReport.getTaskId())) {
            // 查询任务记录 更新任务的施工状态
            IotProjectTaskDO task = iotProjectTaskMapper.selectById(dailyReport.getTaskId());
            if (ObjUtil.isNotEmpty(task)) {
                task.setRdStatus(updateObj.getRdStatus());
                task.setStatus(updateObj.getRdStatus());
                int num = iotProjectTaskMapper.updateById(task);
            }
        }
        // 保存生产动态明细
        if (CollUtil.isNotEmpty(updateReqVO.getReportDetails())) {
            // 先删除明细 再新增
            List<IotRdDailyReportDetailSaveReqVO> reportDetails = updateReqVO.getReportDetails();
            int count = iotRdDailyReportDetailMapper.deleteByMap(ImmutableMap.of(
                    "report_id", updateObj.getId()
            ));
            // 新增生产动态明细
            List<IotRdDailyReportDetailDO> reportDOs = new ArrayList<>();
            reportDetails.forEach(detail -> {
                String detailEndTime = detail.getEndTime();
                if (StrUtil.isNotBlank(detailEndTime)) {
                    String[] timeAttr = detailEndTime.split(":");
                    if ("24".equals(timeAttr[0])) {
                        detail.setEndTime("23:59:59");
                    }
                }
                IotRdDailyReportDetailDO tempDetail = BeanUtils.toBean(detail, IotRdDailyReportDetailDO.class);
                tempDetail.setDeptId(updateObj.getDeptId());
                tempDetail.setTaskId(updateObj.getTaskId());
                tempDetail.setReportId(updateObj.getId());
                tempDetail.setReportDate(ObjUtil.isEmpty(detail.getReportDate())?dailyReport.getCreateTime():detail.getReportDate());
                reportDOs.add(tempDetail);
            });
            // 插入日报明细记录
            if (CollUtil.isNotEmpty(reportDOs)) {
                iotRdDailyReportDetailMapper.insertBatch(reportDOs);
            }
        }
        // 保存油耗信息
        if (CollUtil.isNotEmpty(updateReqVO.getReportFuels())) {
            // 新增 or 更新 油耗子表数据
            AtomicBoolean addFlag = new AtomicBoolean(false);
            List<IotDailyReportFuelDO> tobeAddedOrUpdatedFuels = new ArrayList<>();
            // 如果有油耗记录 id 更新 如果没有 id 新增
            List<IotDailyReportFuelSaveReqVO> reportFuels = updateReqVO.getReportFuels();
            reportFuels.forEach(fuel -> {
                IotDailyReportFuelDO reportFuel = BeanUtils.toBean(fuel, IotDailyReportFuelDO.class);
                tobeAddedOrUpdatedFuels.add(reportFuel);
                if (ObjUtil.isNotEmpty(fuel.getId())) {
                    // 已有油耗数据 更新
                    addFlag.set(false);
                } else {
                    // 新增油耗数据
                    addFlag.set(true);
                }
            });
            if (CollUtil.isNotEmpty(tobeAddedOrUpdatedFuels)) {
                if (addFlag.get()) {
                    // 新增油耗数据
                    iotDailyReportFuelMapper.insertBatch(tobeAddedOrUpdatedFuels);
                } else {
                    // 更新现有油耗数据
                    iotDailyReportFuelMapper.updateBatch(tobeAddedOrUpdatedFuels);
                }
            }
        }
        // 如果上传了附件 保存附件
        if (CollUtil.isNotEmpty(updateReqVO.getAttachments())) {
            List<IotAttachmentSaveReqVO> attachments = updateReqVO.getAttachments();
            List<IotAttachmentDO> tobeAddedAttachments = new ArrayList<>();
            attachments.forEach(att -> {
                IotAttachmentDO attachment = new IotAttachmentDO();
                attachment.setBizId(att.getBizId());
                attachment.setCategory(AttachmentCategoryEnum.DAILY_REPORT.getCode());
                attachment.setType(AttachmentTypeEnum.EXTERNAL_RENTAL.getCode());
                attachment.setFilePath(att.getFilePath());
                attachment.setFileSize(att.getFileSize());
                attachment.setFilename(att.getFilename());
                attachment.setFileType(att.getFileType());
                attachment.setRemark(att.getRemark());
                tobeAddedAttachments.add(attachment);
            });
            if (CollUtil.isNotEmpty(tobeAddedAttachments)) {
                // 先删除再新增
                int count = iotAttachmentMapper.deleteByMap(ImmutableMap.of(
                        "category", AttachmentCategoryEnum.DAILY_REPORT.getCode(),
                        "biz_id", updateObj.getId(),
                        "type", AttachmentTypeEnum.EXTERNAL_RENTAL.getCode()
                ));
                iotAttachmentMapper.insertBatch(tobeAddedAttachments);
            }
        } else {
            // 如果传递的附件列表为空 则删除已有的附件
            // 先删除再新增
            int count = iotAttachmentMapper.deleteByMap(ImmutableMap.of(
                    "category", AttachmentCategoryEnum.DAILY_REPORT.getCode(),
                    "biz_id", updateObj.getId(),
                    "type", AttachmentTypeEnum.EXTERNAL_RENTAL.getCode()
            ));
        }
        updateObj.setReportName(dailyReport.getReportName());
        updateObj.setPlatformWell(dailyReport.getPlatformWell());
        updateObj.setPlatformGroup(dailyReport.getPlatformGroup());
        return updateObj;
    }

    @Override
    public void approvalRdDailyReport(IotRdDailyReportSaveReqVO updateReqVO) {
        // 如果审批拒绝 修改日报 填报状态 status = 0 填写中  审批状态 auditStatus = 审批不通过
        // 审批通过 设置 日报审批状态 auditStatus = 审批通过
        // 校验存在
        IotRdDailyReportDO dailyReport = validateIotRdDailyReportExists(updateReqVO.getId());
        dailyReport.setAuditStatus(updateReqVO.getAuditStatus());
        // 平台井的关联井 同步更新审核状态
        List<IotRdDailyReportDO> relatedPlatformWellReports = new ArrayList<>();
        // 平台井需要更新任务施工状态的关联井
        List<IotProjectTaskDO> relatedPlatformTasks = new ArrayList<>();
        if (SupplierAuditStatusEnum.REJECT.getStatus().equals(updateReqVO.getAuditStatus())) {
            dailyReport.setStatus(0);
        } else {
            // 审核通过 更新关联任务状态
            // 判断如果当前井为平台井 且 关联井也设置了 施工状态 也要同步更新关联井任务的施工状态
            Integer platformWell = updateReqVO.getPlatformWell();
            Long taskId = updateReqVO.getTaskId();
            if (ObjUtil.isNotEmpty(taskId)) {
                // 查找关联的任务 更新任务的 施工状态 + 更新非平台井的任务状态
                IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
                if (!"wg".equals(task.getStatus())) {
                    // 兼容平台井关联的任务已经完工，没有填报数据，但是关联井没有完工的情况
                    // 此时没有填写日报数据主井任务不必更新状态
                    task.setStatus(updateReqVO.getRdStatus());
                    task.setRdStatus(updateReqVO.getRdStatus());
                    iotProjectTaskMapper.updateById(task);
                }
                // 如果是平台井 且 关联井也设置了 施工状态 也要同步更新关联井任务的施工状态
                if (1 == platformWell) {
                    // key平台井关联井的任务taskId   value平台井关联井的任务施工状态数据字典value
                    Map<Long, String> relatedPlatformTaskPair = new HashMap<>();
                    // 查询当前日报关联平台井日报
                    String platformGroup = dailyReport.getPlatformGroup();
                    if (StrUtil.isNotBlank(platformGroup)) {
                        IotRdDailyReportPageReqVO reqVO = new IotRdDailyReportPageReqVO();
                        reqVO.setPlatformGroup(platformGroup);
                        // 查询平台井关联的所有井
                        List<IotRdDailyReportDO> platformRelatedReports = iotRdDailyReportMapper.dailyReports(reqVO);
                        if (CollUtil.isNotEmpty(platformRelatedReports)) {
                            List<Long> relatedPlatformTaskIds = new ArrayList<>();
                            platformRelatedReports.forEach(report -> {
                                if (2 == report.getPlatformWell()) {
                                    if (StrUtil.isNotBlank(report.getRdStatus())) {
                                        // 记录每个平台关联井的 施工状态
                                        relatedPlatformTaskPair.put(report.getTaskId(), report.getRdStatus());
                                        relatedPlatformTaskIds.add(report.getTaskId());
                                    }
                                    // 设置平台关联井的审核状态为 同意
                                    report.setAuditStatus(SupplierAuditStatusEnum.APPROVE.getStatus());
                                    relatedPlatformWellReports.add(report);
                                }
                            });
                            // 查询平台关联井列表
                            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
                            taskReqVO.setTaskIds(relatedPlatformTaskIds);
                            List<IotProjectTaskDO> relatedTasks = iotProjectTaskMapper.selectList(taskReqVO);
                            if (CollUtil.isNotEmpty(relatedTasks)) {
                                relatedTasks.forEach(relatedTask -> {
                                    if (relatedPlatformTaskPair.containsKey(relatedTask.getId())) {
                                        relatedTask.setStatus(relatedPlatformTaskPair.get(relatedTask.getId()));
                                        relatedTask.setRdStatus(relatedPlatformTaskPair.get(relatedTask.getId()));
                                        relatedPlatformTasks.add(relatedTask);
                                    }
                                });
                            }
                        }
                    }
                }
            }
        }
        dailyReport.setOpinion(updateReqVO.getOpinion());
        // 更新平台井 主井信息
        iotRdDailyReportMapper.updateById(dailyReport);
        // 更新平台井 关联井信息 审核状态
        if (CollUtil.isNotEmpty(relatedPlatformWellReports)) {
            iotRdDailyReportMapper.updateBatch(relatedPlatformWellReports);
        }
        // 更新平台井 关联井任务 施工状态
        if (CollUtil.isNotEmpty(relatedPlatformTasks)) {
            iotProjectTaskMapper.updateBatch(relatedPlatformTasks);
        }
    }

    @Override
    public void deleteIotRdDailyReport(Long id) {
        // 校验存在
        validateIotRdDailyReportExists(id);
        // 删除
        iotRdDailyReportMapper.deleteById(id);
    }

    private IotRdDailyReportDO validateIotRdDailyReportExists(Long id) {
        IotRdDailyReportDO dailyReport = iotRdDailyReportMapper.selectById(id);
        if (ObjUtil.isEmpty(dailyReport)) {
            throw exception(IOT_RD_DAILY_REPORT_NOT_EXISTS);
        }
        return dailyReport;
    }

    @Override
    public IotRdDailyReportDO getIotRdDailyReport(Long id) {
        return iotRdDailyReportMapper.selectById(id);
    }

    @Override
    public PageResult<IotRdDailyReportDO> getIotRdDailyReportPage(IotRdDailyReportPageReqVO pageReqVO) {
        // 查询选择部门下面所有子部门
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        // searchKey 兼容移动端查询的情况
        if (StrUtil.isNotBlank(pageReqVO.getSearchKey())) {
            if (CollUtil.isEmpty(pageReqVO.getProjectIds()) && CollUtil.isEmpty(pageReqVO.getTaskIds())) {
                return new PageResult<>(Collections.emptyList(), 0L);
            }
        } else {
            // PC 端 AND 检查：各自条件必须匹配到对应 ID
            if (StrUtil.isNotBlank(pageReqVO.getContractName()) && CollUtil.isEmpty(pageReqVO.getProjectIds())) {
                return new PageResult<>(Collections.emptyList(), 0L);
            }
            if (StrUtil.isNotBlank(pageReqVO.getTaskName()) && CollUtil.isEmpty(pageReqVO.getTaskIds())) {
                return new PageResult<>(Collections.emptyList(), 0L);
            }
        }

        return iotRdDailyReportMapper.selectPage(pageReqVO);
    }

    @Override
    public List<IotRdDailyReportDO> dailyReports(IotRdDailyReportPageReqVO pageReqVO) {
        return iotRdDailyReportMapper.dailyReports(pageReqVO);
    }

    @Override
    public void batchAddDailyReports(List<IotRdDailyReportDO> reports) {
        iotRdDailyReportMapper.insertBatch(reports);
    }

    @Override
    public List<IotRdDailyReportStatisticsRespVO> statistics(IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportStatisticsRespVO> result = new ArrayList<>();
        // 检查contractName不为空但projectIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getContractName()) && (CollUtil.isEmpty(pageReqVO.getProjectIds()))) {
            return result;
        }
        // 检查taskName不为空但taskIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getTaskName()) && (CollUtil.isEmpty(pageReqVO.getTaskIds()))) {
            return result;
        }
        // 按照项目部统计日报 先查询瑞都所有部门 163l
        Set<Long> rdChildDeptIds = deptService.getChildDeptIdListFromCache(163l);
        List<DeptDO> depts =deptService.getDeptList(rdChildDeptIds);
        // key队伍id      value队伍所属项目部id
        Map<Long, Long> teamProjectPair = new HashMap<>();
        Set<Long> projectDeptIds = new HashSet<>();
        // key项目部id     value项目部名称
        Map<Long, String> projectDeptPair = new HashMap<>();
        // key队伍id     value队伍名称
        Map<Long, String> teamDeptPair = new HashMap<>();
        if (CollUtil.isNotEmpty(depts)) {
            depts.forEach(dept -> {
                if (dept.getName().contains("项目部")) {
                    projectDeptIds.add(dept.getId());
                    projectDeptPair.put(dept.getId(), dept.getName());
                } else {
                    teamProjectPair.put(dept.getId(), dept.getParentId());
                    teamDeptPair.put(dept.getId(), dept.getName());
                }
            });
        }
        // 查询所有瑞都日报 设置组织机构查询条件
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.dailyReports(pageReqVO);
        // key项目id   value项目甲方名称
        Map<Long, String> projectPair = new HashMap<>();
        //  key任务id     value任务井号
        Map<Long, String> taskPair = new HashMap<>();
        //  key任务id     value任务施工状态
        Map<Long, String> taskStatusPair = new HashMap<>();
        //  施工工艺 key字典键值     value字典标签
        Map<String, String> techniqueDictPair = new HashMap<>();
        //  key任务id     value任务工作量
        Map<Long, BigDecimal> taskWorkloadPair = new HashMap<>();
        //  key任务id     value任务工艺名称
        Map<Long, String> taskTechniquePair = new HashMap<>();
        // key任务id   value任务的施工周期天数
        Map<Long, String> taskPeriodPair = new HashMap<>();
        // key任务id  value每个任务的施工开始日期
        Map<Long, LocalDateTime> taskStartDatePair = new HashMap<>();
        // 已经完工的任务id集合
        Set<Long> finishedTaskIds = new HashSet<>();
        if (CollUtil.isNotEmpty(dailyReports)) {
            // 遍历日报 找到每个日报关联的任务的 施工队伍
            List<Long> taskIds = convertList(dailyReports, IotRdDailyReportDO::getTaskId);
            Set<Long> uniqueTaskIds = new HashSet<>(taskIds);
            // 查询所有任务关联的队伍信息 如果任务选择了多个队伍 队伍名称逗号分隔
            // key任务id      value任务选择的施工队伍名称集合
            Map<Long, String> taskDeptNamePair = new HashMap<>();
            // key任务id      value任务创建时间集合
            Map<Long, LocalDateTime> taskCreateTimePair = new HashMap<>();
            // key任务id      value任务选择的施工队伍对应的项目部名称集合
            Map<Long, String> taskProjectDeptPair = new HashMap<>();
            IotProjectTaskPageReqVO relatedTaskReqVO = new IotProjectTaskPageReqVO();
            relatedTaskReqVO.setTaskIds(new ArrayList<>(uniqueTaskIds));
            List<IotProjectTaskDO> relatedTasks = iotProjectTaskMapper.selectList(relatedTaskReqVO);
            if (CollUtil.isNotEmpty(relatedTasks)) {
                relatedTasks.forEach(task -> {
                    Set<Long> teamIds = task.getDeptIds();
                    // 非空判断，避免空指针
                    if (CollUtil.isNotEmpty(teamIds)) {
                        // 映射队伍ID为名称，过滤空值，用逗号拼接
                        String teamNames = teamIds.stream()
                                .map(teamDeptPair::get) // 从已有映射中获取队伍名称
                                .filter(Objects::nonNull) // 过滤未匹配到名称的情况
                                .collect(Collectors.joining(",")); // 多队伍用逗号分隔
                        // 存入任务-队伍名称映射
                        taskDeptNamePair.put(task.getId(), teamNames);
                        // 处理项目部名称集合
                        String projectNames = teamIds.stream()
                                .map(teamId -> teamProjectPair.get(teamId)) // 队伍ID→项目部ID
                                .filter(Objects::nonNull) // 过滤无效的项目部ID
                                .map(projectId -> projectDeptPair.get(projectId)) // 项目部ID→项目部名称
                                .filter(Objects::nonNull) // 过滤无效的项目部名称
                                .distinct() // 去重（避免同一项目部重复出现）
                                .collect(Collectors.joining(",")); // 逗号分隔多项目部
                        taskProjectDeptPair.put(task.getId(), projectNames);
                    }
                    // 筛选出 完工 的任务集合 任务完工 施工井数 +1
                    if ("wg".equals(task.getRdStatus())) {
                        finishedTaskIds.add(task.getId());
                    }
                });
            }

            // 查询施工工艺字典数据
            List<DictDataDO> rdTechniques = dictDataService.getDictDataListByDictType("rq_iot_project_technology_rd");
            if (CollUtil.isNotEmpty(rdTechniques)) {
                rdTechniques.forEach(tech -> {
                    techniqueDictPair.put(tech.getValue(), tech.getLabel());
                });
            }

            // 查询日报关联的项目信息
            IotProjectInfoPageReqVO projectReqVO = new IotProjectInfoPageReqVO();
            projectReqVO.setProjectIds(convertList(dailyReports, IotRdDailyReportDO::getProjectId));
            List<IotProjectInfoDO> projects = iotProjectInfoMapper.selectList(projectReqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectPair.put(project.getId(), project.getManufactureName());
                });
            }
            // 查询日报关联的任务信息
            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
            taskReqVO.setTaskIds(convertList(dailyReports, IotRdDailyReportDO::getTaskId));
            List<IotProjectTaskDO> tasks = iotProjectTaskMapper.selectList(taskReqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskPair.put(task.getId(), task.getWellName());
                    taskStatusPair.put(task.getId(), task.getStatus());
                    taskCreateTimePair.put(task.getId(), task.getCreateTime());
                    // 获取所有任务的工作量数据
                    taskWorkloadPair.put(task.getId(), new BigDecimal(task.getWorkloadDesign()));
                    if (techniqueDictPair.containsKey(task.getTechnique())) {
                        String techniqueName = techniqueDictPair.get(task.getTechnique());
                        taskTechniquePair.put(task.getId(), techniqueName);
                    }
                });
            }

            // 查询所有日报关联任务的施工周期
            List<IotRdDailyReportTaskPeriodVO> taskPeriods = iotRdDailyReportMapper.taskConstructionPeriod();
            if (CollUtil.isNotEmpty(taskPeriods)) {
                // 定义日期格式化器，匹配"yyyy-MM-dd HH:mm:ss"格式
                DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                taskPeriods.forEach(period -> {
                    // 如果 startDate endDate 都存在 计算施工周期天数 考虑到日报审批后才会更新任务状态
                    // 所以计算任务施工周期时 要筛选出施工完成的任务
                    String taskStatus = StrUtil.EMPTY;
                    if (ObjUtil.isNotEmpty(period) && taskStatusPair.containsKey(period.getTaskId())) {
                        taskStatus = taskStatusPair.get(period.getTaskId());
                    }
                    if (ObjUtil.isNotEmpty(period) && StrUtil.isNotBlank(period.getStartDate())
                            && StrUtil.isNotBlank(period.getEndDate()) && "wg".equals(taskStatus)) {
                        // 计算 startDate endDate 之间的差值
                        try {
                            // 1. 解析日期字符串为LocalDateTime
                            LocalDateTime startLdt = LocalDateTime.parse(period.getStartDate(), dtf);
                            LocalDateTime endLdt = LocalDateTime.parse(period.getEndDate(), dtf);

                            // 2. 截断时分秒，仅保留日期部分
                            LocalDate startDate = startLdt.toLocalDate();
                            LocalDate endDate = endLdt.toLocalDate();

                            // 3. 计算日期差值（天），+1满足「同一天算1天」的规则
                            long daysDiff = ChronoUnit.DAYS.between(startDate, endDate);
                            long constructionDays = daysDiff + 1;

                            // 4. 存入任务-施工周期映射（key=任务ID，value=天数字符串）
                            taskPeriodPair.put(period.getTaskId(), String.valueOf(constructionDays));
                        } catch (Exception e) {
                            // 解析失败时默认周期为1天（可根据业务调整，也可记录日志）
                            taskPeriodPair.put(period.getTaskId(), "1");
                            // 可选：添加日志打印
                            // log.error("解析任务施工周期日期失败，taskId={}, startDate={}, endDate={}", period.getTaskId(), period.getStartDate(), period.getEndDate(), e);
                        }
                    }
                });
            }

            // 以任务为单位组装任务关联的日报的当日油耗总和
            // key任务id   value任务关联的日报油耗总和
            Map<Long, BigDecimal> taskFuelPair = new HashMap<>();
            // key任务id  value多个非生产时间累计值
            Map<Long, List<BigDecimal>> taskNonProductValuesPair = new HashMap<>();

            // 设置 小队 任务已经完成的工作量集合 （按照不同的单位统计任务的不同工作量）
            // key任务井id      value队伍工作量数据
            Map<Long, BigDecimal> bridgePlugPair = new HashMap<>();
            Map<Long, BigDecimal> runCountPair = new HashMap<>();
            Map<Long, BigDecimal> cumulativeWorkingWellPair = new HashMap<>();
            Map<Long, BigDecimal> hourCountPair = new HashMap<>();
            Map<Long, BigDecimal> waterVolumePair = new HashMap<>();
            Map<Long, BigDecimal> pumpTripsPair = new HashMap<>();
            Map<Long, BigDecimal> cumulativeWorkingLayersPair = new HashMap<>();
            Map<Long, BigDecimal> mixSandPair = new HashMap<>();

            dailyReports.forEach(report -> {
                // 筛选每个日报关联任务的施工开始日期
                Long taskId = report.getTaskId();
                LocalDateTime createTime = report.getCreateTime();
                if (taskStartDatePair.containsKey(taskId)) {
                    LocalDateTime tempStartDate = taskStartDatePair.get(taskId);
                    // 找出当前日报关联任务的最早日报日期
                    if (createTime.isBefore(tempStartDate)) {
                        taskStartDatePair.put(taskId, createTime);
                    }
                } else {
                    taskStartDatePair.put(taskId, createTime);
                }

                BigDecimal dailyFuel = report.getDailyFuel();
                dailyFuel = ObjUtil.isEmpty(dailyFuel) ? BigDecimal.ZERO : dailyFuel;
                if (taskFuelPair.containsKey(report.getTaskId())) {
                    BigDecimal existTotalFuel = taskFuelPair.get(report.getTaskId());
                    BigDecimal tempTotalFuel = existTotalFuel.add(dailyFuel);
                    taskFuelPair.put(report.getTaskId(), tempTotalFuel);
                } else {
                    taskFuelPair.put(report.getTaskId(), dailyFuel);
                }

                // 统计每个日报的 非生产时效 任务的非生产时效 任务关联的日报的 sum(非生产时间)/24n
                BigDecimal sumNpt = calculateNonProductTime(report);
                if (taskNonProductValuesPair.containsKey(report.getTaskId())) {
                    List<BigDecimal> tempNptValues = taskNonProductValuesPair.get(report.getTaskId());
                    tempNptValues.add(sumNpt);
                    taskNonProductValuesPair.put(report.getTaskId(), tempNptValues);
                } else {
                    List<BigDecimal> tempNptValues = new ArrayList<>();
                    tempNptValues.add(sumNpt);
                    taskNonProductValuesPair.put(report.getTaskId(), tempNptValues);
                }

                // 设置每个任务的工作量数据  单位相同的工作量值作合并处理
                List<IotTaskAttrModelProperty> taskAttrs = report.getExtProperty();
                // 暂存不同单位的工作量属性值
                BigDecimal tempTotalBridgePlug = BigDecimal.ZERO;               // 桥塞(个数)
                BigDecimal tempTotalRunCount = BigDecimal.ZERO;                 // 趟数
                BigDecimal tempTotalCumulativeWorkingWell = BigDecimal.ZERO;    // 井数
                BigDecimal tempTotalHourCount = BigDecimal.ZERO;                // 小时H
                BigDecimal tempTotalWaterVolume = BigDecimal.ZERO;              // 水方量(方)
                BigDecimal tempTotalPumpTrips = BigDecimal.ZERO;
                BigDecimal tempTotalCumulativeWorkingLayers = BigDecimal.ZERO;  // 段数  累计施工-层
                BigDecimal tempTotalMixSand = BigDecimal.ZERO;                  // 台次 当日泵车台次 当日仪表/混砂
                if (CollUtil.isNotEmpty(taskAttrs)) {
                    for (IotTaskAttrModelProperty attr : taskAttrs) {
                        String unit = attr.getUnit();   // 工作量单位
                        String actualValueStr = attr.getActualValue();  // 工作量属性的实际值
                        // 处理实际值：避免null或非数字字符串导致的异常
                        BigDecimal actualValue = BigDecimal.ZERO;
                        if (StrUtil.isNotBlank(actualValueStr)) {  // 假设使用Hutool的StrUtil，或自行判断null/空
                            try {
                                actualValue = new BigDecimal(actualValueStr);
                            } catch (NumberFormatException e) {
                                // 若字符串格式错误，默认按0处理（可根据业务调整）
                                actualValue = BigDecimal.ZERO;
                            }
                        }
                        if ("个数".equals(unit)) {
                            // 钻可溶桥塞  钻复合桥塞
                            tempTotalBridgePlug = tempTotalBridgePlug.add(actualValue);
                            if (bridgePlugPair.containsKey(report.getTaskId())) {
                                BigDecimal tempBridgePlug = bridgePlugPair.get(report.getTaskId());
                                bridgePlugPair.put(report.getTaskId(), tempTotalBridgePlug.add(tempBridgePlug));
                            } else {
                                bridgePlugPair.put(report.getTaskId(), tempTotalBridgePlug);
                            }
                        }
                        if ("趟数".equals(unit)) {
                            tempTotalRunCount = tempTotalRunCount.add(actualValue);
                            if (runCountPair.containsKey(report.getTaskId())) {
                                BigDecimal tempRunCount = runCountPair.get(report.getTaskId());
                                runCountPair.put(report.getTaskId(), tempTotalRunCount.add(tempRunCount));
                            } else {
                                runCountPair.put(report.getTaskId(), tempTotalRunCount);
                            }
                        }
                        if ("小时".equals(unit)) {
                            tempTotalHourCount = tempTotalHourCount.add(actualValue);
                            if (hourCountPair.containsKey(report.getTaskId())) {
                                BigDecimal tempHourCount = hourCountPair.get(report.getTaskId());
                                hourCountPair.put(report.getTaskId(), tempTotalHourCount.add(tempHourCount));
                            } else {
                                hourCountPair.put(report.getTaskId(), tempTotalHourCount);
                            }
                        }
                        if ("天数".equals(unit)) {
                            // 将 actualValue 换算成 H
                            BigDecimal hours = actualValue.multiply(new BigDecimal("24"));
                            tempTotalHourCount = tempTotalHourCount.add(hours);
                            if (hourCountPair.containsKey(report.getTaskId())) {
                                BigDecimal tempHourCount = hourCountPair.get(report.getTaskId());
                                hourCountPair.put(report.getTaskId(), tempTotalHourCount.add(tempHourCount));
                            } else {
                                hourCountPair.put(report.getTaskId(), tempTotalHourCount);
                            }
                        }
                        if ("方".equals(unit)) {
                            tempTotalWaterVolume = tempTotalWaterVolume.add(actualValue);
                            if (waterVolumePair.containsKey(report.getTaskId())) {
                                BigDecimal tempWaterVolume = waterVolumePair.get(report.getTaskId());
                                waterVolumePair.put(report.getTaskId(), tempTotalWaterVolume.add(tempWaterVolume));
                            } else {
                                waterVolumePair.put(report.getTaskId(), tempTotalWaterVolume);
                            }
                        }
                        if ("井数".equals(unit)) {
                            tempTotalCumulativeWorkingWell = tempTotalCumulativeWorkingWell.add(actualValue);
                            if (cumulativeWorkingWellPair.containsKey(report.getTaskId())) {
                                BigDecimal tempWorkingWell = cumulativeWorkingWellPair.get(report.getTaskId());
                                cumulativeWorkingWellPair.put(report.getTaskId(), tempTotalCumulativeWorkingWell.add(tempWorkingWell));
                            } else {
                                cumulativeWorkingWellPair.put(report.getTaskId(), tempTotalCumulativeWorkingWell);
                            }
                        }
                        if ("段数".equals(unit)) {
                            // 累计施工层
                            tempTotalCumulativeWorkingLayers = tempTotalCumulativeWorkingLayers.add(actualValue);
                            if (cumulativeWorkingLayersPair.containsKey(report.getTaskId())) {
                                BigDecimal tempWorkingLayer = cumulativeWorkingLayersPair.get(report.getTaskId());
                                cumulativeWorkingLayersPair.put(report.getTaskId(), tempTotalCumulativeWorkingLayers.add(tempWorkingLayer));
                            } else {
                                cumulativeWorkingLayersPair.put(report.getTaskId(), tempTotalCumulativeWorkingLayers);
                            }
                        }
                        if ("台次".equals(unit) && "当日泵车台次".equals(attr.getName())) {
                            tempTotalPumpTrips = tempTotalPumpTrips.add(actualValue);
                            if (pumpTripsPair.containsKey(report.getTaskId())) {
                                BigDecimal tempPumpTrips = pumpTripsPair.get(report.getTaskId());
                                pumpTripsPair.put(report.getTaskId(), tempTotalPumpTrips.add(tempPumpTrips));
                            } else {
                                pumpTripsPair.put(report.getTaskId(), tempTotalPumpTrips);
                            }
                        }
                        if ("台次".equals(unit) && ("当日仪表/混砂".equals(attr.getName())
                                || "当日混砂".equals(attr.getName()) || "当日仪表".equals(attr.getName()))) {
                            tempTotalMixSand = tempTotalMixSand.add(actualValue);
                            if (mixSandPair.containsKey(report.getTaskId())) {
                                BigDecimal tempMixSand = mixSandPair.get(report.getTaskId());
                                mixSandPair.put(report.getTaskId(), actualValue.add(tempMixSand));
                            } else {
                                mixSandPair.put(report.getTaskId(), tempTotalMixSand);
                            }
                        }
                    }
                }
            });
            // 循环遍历所有瑞都日报后 重新组装每个队伍的工作量数据
            // 将 deptId-projectId-taskId 作为唯一键 生成汇总统计报表
            Set<String> deptIdTaskIds = new HashSet<>();
            dailyReports.forEach(report -> {
                // uniqueKey = deptId-taskId
                String uniqueKey = StrUtil.join("-", report.getDeptId(), report.getProjectId(), report.getTaskId());
                deptIdTaskIds.add(uniqueKey);
            });
            // deptIdTaskIds 集合中就是要显示的汇总报表数据
            if (CollUtil.isNotEmpty(deptIdTaskIds)) {
                deptIdTaskIds.forEach(dt -> {
                    // 拆分 uniqueKey
                    String[] deptIdTaskIdArray = dt.split("-");
                    Long deptId = Long.valueOf(deptIdTaskIdArray[0]);
                    if (!"null".equals(deptIdTaskIdArray[1]) && !"null".equals(deptIdTaskIdArray[2])) {
                        Long projectId = Long.valueOf(deptIdTaskIdArray[1]);
                        Long taskId = Long.valueOf(deptIdTaskIdArray[2]);
                        IotRdDailyReportStatisticsRespVO uniqueReport = new IotRdDailyReportStatisticsRespVO();
                        List<IotRdDailyReportStatisticsItemVO> items = new ArrayList<>();
                        /* if (teamDeptPair.containsKey(deptId)) {
                            uniqueReport.setDeptName(teamDeptPair.get(deptId));
                        } */
                        // 任务 非生产时间 加和 相除
                        if (taskNonProductValuesPair.containsKey(taskId)) {
                            List<BigDecimal> tempNptValues = taskNonProductValuesPair.get(taskId);
                            // 根据集合 tempNptValues 计算 分母天数
                            // 1. 计算分子：集合中所有BigDecimal元素的总和
                            BigDecimal numerator = tempNptValues.stream()
                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                            // 2. 计算分母：size * 24（转为BigDecimal避免类型问题）
                            int denominatorInt = tempNptValues.size() * 24;
                            BigDecimal denominator = BigDecimal.valueOf(denominatorInt);
                            // 3. 计算比率：处理分母为0的情况，避免除零异常
                            BigDecimal nonProductionRate;
                            if (denominator.compareTo(BigDecimal.ZERO) == 0) {
                                nonProductionRate = BigDecimal.ZERO; // 分母为0时比率设为0
                            } else {
                                // 除法：指定精度（保留4位小数）和舍入模式（四舍五入），可根据业务调整
                                nonProductionRate = numerator.divide(denominator, 4, RoundingMode.HALF_UP);
                            }
                            uniqueReport.setNonProductionRate(nonProductionRate);
                        }
                        uniqueReport.setTaskId(taskId);
                        if (taskDeptNamePair.containsKey(taskId)) {
                            uniqueReport.setDeptName(taskDeptNamePair.get(taskId));
                        }
                        if (taskCreateTimePair.containsKey(taskId)) {
                            uniqueReport.setCreateTime(taskCreateTimePair.get(taskId));
                        }
                        if (taskFuelPair.containsKey(taskId)) {
                            uniqueReport.setTotalDailyFuel(taskFuelPair.get(taskId));
                        }
                        if (taskProjectDeptPair.containsKey(taskId)) {
                            uniqueReport.setProjectDeptName(taskProjectDeptPair.get(taskId));
                        }
                        if (projectPair.containsKey(projectId)) {
                            uniqueReport.setManufactureName(projectPair.get(projectId));
                        }
                        if (taskPair.containsKey(taskId)) {
                            uniqueReport.setWellName(taskPair.get(taskId));
                        }
                        // 施工周期
                        if (taskPeriodPair.containsKey(taskId)) {
                            uniqueReport.setPeriod(taskPeriodPair.get(taskId));
                        }
                        // 任务开始日期
                        if (taskStartDatePair.containsKey(taskId)) {
                            LocalDateTime startDate = taskStartDatePair.get(taskId);
                            uniqueReport.setTaskStartDate(LocalDateTimeUtil.format(startDate, DatePattern.NORM_DATE_PATTERN));
                        }
                        if (taskStatusPair.containsKey(taskId)) {
                            uniqueReport.setRdStatus(taskStatusPair.get(taskId));
                        }
                        if (taskTechniquePair.containsKey(taskId)) {
                            uniqueReport.setTechniques(taskTechniquePair.get(taskId));
                        }
                        // 总工作量
                        if (taskWorkloadPair.containsKey(taskId)) {
                            uniqueReport.setWorkloadDesign(taskWorkloadPair.get(taskId));
                        }
                        // 以队伍为维度 设置每种施工工艺的工作量 总和
                        if (bridgePlugPair.containsKey(taskId)) {
                            // 钻可溶桥塞  钻复合桥塞
                            IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
                            item.setUnit("个数");
                            item.setWorkload(bridgePlugPair.get(taskId));
                            items.add(item);
                        }
                        if (runCountPair.containsKey(taskId)) {
                            // 通刮洗  冲砂
                            IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
                            item.setUnit("趟数");
                            item.setWorkload(runCountPair.get(taskId));
                            items.add(item);
                        }
                        if (hourCountPair.containsKey(taskId)) {
                            // 液氮泵车(时间D)  千型泵车(时间H)
                            IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
                            item.setUnit("小时");
                            item.setWorkload(hourCountPair.get(taskId));
                            items.add(item);
                        }
                        if (waterVolumePair.containsKey(taskId)) {
                            // 注水
                            IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
                            item.setUnit("方");
                            item.setWorkload(waterVolumePair.get(taskId));
                            items.add(item);
                        }
                        if (finishedTaskIds.contains(taskId)) {
                            // 涉及到 井数 工作量 只有完工的任务 井数 +1
                            IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
                            item.setUnit("井数");
                            item.setWorkload(new BigDecimal(1));
                            items.add(item);
                        }
                        if (cumulativeWorkingLayersPair.containsKey(taskId)) {
                            // 压裂大包 压裂总包
                            IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
                            item.setUnit("段数");
                            item.setWorkload(cumulativeWorkingLayersPair.get(taskId));
                            items.add(item);
                        }
                        if (pumpTripsPair.containsKey(taskId) || mixSandPair.containsKey(taskId)) {
                            // 主压裂车 当日泵车台次
                            IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
                            item.setUnit("台次");
                            BigDecimal tempPumpTrip = pumpTripsPair.get(taskId);
                            BigDecimal tempMixSand = mixSandPair.get(taskId);
                            item.setWorkload((ObjUtil.isEmpty(tempPumpTrip)?BigDecimal.ZERO:tempPumpTrip).add(ObjUtil.isEmpty(tempMixSand)?BigDecimal.ZERO:tempMixSand));
                            items.add(item);
                        }
                    /* if (mixSandPair.containsKey(taskId)) {
                        // 当日仪表/混砂 台次
                        IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
                        item.setUnit("台次");
                        item.setWorkload(mixSandPair.get(taskId));
                        items.add(item);
                    } */
                        uniqueReport.setItems(items);
                        result.add(uniqueReport);
                    }
                });
            }
        }
        // 将 result 集合按照 集合中元素的 属性 创建时间 倒序排列
        if (CollUtil.isNotEmpty(result)) {
            result.sort(Comparator.comparing(
                    IotRdDailyReportStatisticsRespVO::getCreateTime,
                    Comparator.nullsLast(Comparator.reverseOrder())
            ));
        }
        return result;
    }

    /**
     *  计算非生产时间累加值 计算非生产时效
     *  NPT 归类 设备故障repair_time 工程质量accident_time 技术受限complexity_time 生产组织rectification_time 不可抗力waiting_stop_time
     *      甲方设计partya_design 甲方准备partya_prepare 甲方资源partya_resource 其他非生产时间other_npt_time
     * @param dailyReport
     * @return
     */
    private BigDecimal calculateNonProductTime(IotRdDailyReportDO dailyReport){
        return Stream.of(
                        dailyReport.getAccidentTime(),
                        dailyReport.getRepairTime(),
                        dailyReport.getComplexityTime(),
                        dailyReport.getRectificationTime(),
                        dailyReport.getWaitingStopTime(),
                        dailyReport.getPartyaDesign(),    // 注：字段名虽无Time但属于非生产时间，保留
                        dailyReport.getPartyaPrepare(),
                        dailyReport.getPartyaResource(),
                        dailyReport.getOtherNptTime()
                )
                // 处理null值，避免调用add()时抛出NullPointerException
                .map(bd -> Objects.isNull(bd) ? BigDecimal.ZERO : bd)
                // Stream.reduce：从初始值0开始，依次累加所有元素
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    @Override
    public List<IotRdDailyReportStatisticsRespVO> summaryStatistics(IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportStatisticsRespVO> result = new ArrayList<>();
        // 不分页统计所有数据
        Set<Long> ids = new HashSet<>();
        if (ObjUtil.isEmpty(pageReqVO.getDeptId())) {
            pageReqVO.setDeptId(163l);
        }
        ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
        // 找到所有子部门对象集合
        ids.add(pageReqVO.getDeptId());
        pageReqVO.setDeptIds(ids);
        // 检查contractName不为空但projectIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getContractName()) && (CollUtil.isEmpty(pageReqVO.getProjectIds()))) {
            return new ArrayList<>();
        }
        // 检查taskName不为空但taskIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getTaskName()) && (CollUtil.isEmpty(pageReqVO.getTaskIds()))) {
            return new ArrayList<>();
        }
        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.dailyReports(pageReqVO);

        // 默认显示所有项目部的汇总数据(新疆分公司也展示 下属各项目部的数据)
        // 点击项目部 显示 下属队伍的数据
        // 首先判断点击的部门是属于 公司 还是 队伍 如果没有点击任何部门 默认查询所有项目部数据
        if (ObjUtil.isEmpty(pageReqVO.getDeptId())) {
            result = statisticsByProjectDept(dailyReports, 163L, pageReqVO);
        } else {
            // 判断点击的组织树中的部门类型 类型(公司级1 项目部2 队伍3)
            DeptDO selectedDept = deptService.getDept(pageReqVO.getDeptId());
            if ("1".equals(selectedDept.getType())) {
                // 以项目部为维度汇总数据
                result = statisticsByProjectDept(dailyReports, pageReqVO.getDeptId(), pageReqVO);
            } else if ("2".equals(selectedDept.getType())) {
                // 以队伍为维度汇总数据
                result = statisticsByProjectDepartment(dailyReports, pageReqVO);
            } else if ("3".equals(selectedDept.getType())) {
                // 显示单个队伍的汇总数据
                result = statisticsByProjectDepartment(dailyReports, pageReqVO);
            } else {
                // 点击的部门没有类型 判断部门下的是否包含 项目部类型部门 新疆分公司
                // 以项目部为维度汇总数据
                result = statisticsByProjectDept(dailyReports, pageReqVO.getDeptId(), pageReqVO);
            }
        }
        // 根据result集合内对象的 sort 属性正序排列 sort 类型为 integer 类型
        if (CollUtil.isNotEmpty(result)) {
            result.sort(Comparator.comparing(
                    IotRdDailyReportStatisticsRespVO::getSort,
                    Comparator.nullsLast(Comparator.naturalOrder())
            ));
        }
        return result;
    }

    @Override
    public List<IotRdDailyReportStatisticsRespVO> totalWorkload(IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportStatisticsRespVO> result = new ArrayList<>();
        // 不分页统计所有数据
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            // 找到所有子部门对象集合
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        // 检查contractName不为空但projectIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getContractName()) && (CollUtil.isEmpty(pageReqVO.getProjectIds()))) {
            return new ArrayList<>();
        }
        // 检查taskName不为空但taskIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getTaskName()) && (CollUtil.isEmpty(pageReqVO.getTaskIds()))) {
            return new ArrayList<>();
        }

        // key工作量标识   value累计 桥塞(个数)
        Map<String, BigDecimal> cumulativeBridgePlugPair = new HashMap<>();
        // key工作量标识   value累计 趟数
        Map<String, BigDecimal> cumulativeRunCountPair = new HashMap<>();
        // key工作量标识   value累计 井数
        Map<String, BigDecimal> cumulativeWorkingWellPair = new HashMap<>();
        // key工作量标识   value累计 小时H
        Map<String, BigDecimal> cumulativeHourCountPair = new HashMap<>();
        // key工作量标识   value累计 油耗
        Map<String, BigDecimal> cumulativeFuelsPair = new HashMap<>();
        // key工作量标识   value累计 水方量(方)
        Map<String, BigDecimal> cumulativeWaterVolumePair = new HashMap<>();
        // key工作量标识   value累计 段数 累计施工-层
        Map<String, BigDecimal> cumulativeWorkingLayersPair = new HashMap<>();
        // key工作量标识   value累计 台次 当日仪表/混砂
        Map<String, BigDecimal> cumulativeMixSandPair = new HashMap<>();
        // key工作量标识   value累计 台次 当日泵车台次
        Map<String, BigDecimal> cumulativePumpTripsPair = new HashMap<>();

        // 查询 瑞都 施工状态 字典数据 筛选出施工设备为 LY 的记录
        Set<String> lyStatuses = new HashSet<>();
        List<DictDataDO> rdStatuses = dictDataService.getDictDataListByDictType("rdStatus");
        if (CollUtil.isNotEmpty(rdStatuses)) {
            rdStatuses.forEach(tech -> {
                if ("LY".equals(tech.getRemark())) {
                    lyStatuses.add(tech.getValue());
                }
            });
        }

        // 查询瑞都所有查询到的设备
        // 过滤掉 封存 的 压裂 连油 主设备
        Set<Long> abDeviceIds = new HashSet<>();

        // 20260604 修改设备利用率统计逻辑 统计 压裂 连油 主设备 rq_iot_device_associate
        // 队伍日报中关联的主设备数量 /（队伍下所有主设备数量-封存设备数量）
        Set<Long> mainDeviceIds = new HashSet<>();
        Set<Long> fcMainDeviceIds = new HashSet<>();
        // key设备id   value设备所属部门id
        Map<Long, Long> mainDeviceIdPair = new HashMap<>();
        IotDeviceAssociatePageReqVO deviceAssocReqVO = new IotDeviceAssociatePageReqVO();
        deviceAssocReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        deviceAssocReqVO.setDeptIds(ids);
        PageResult<IotDeviceAssociateDO> pageResult = iotDeviceAssociateMapper.selectPage(deviceAssocReqVO);
        if (ObjUtil.isNotEmpty(pageResult)) {
            List<IotDeviceAssociateDO> deviceAssociates = pageResult.getList();
            // 查询出所有瑞都压裂 连油主设备
            deviceAssociates.forEach(assoc -> {
                mainDeviceIds.add(assoc.getDeviceId());
                mainDeviceIdPair.put(assoc.getDeviceId(), assoc.getDeptId());
            });
        }

        // 查询瑞都 压裂 连油 主设备详情信息
        IotDevicePageReqVO deviceReqVO = new IotDevicePageReqVO();
        deviceReqVO.setDeviceIds(new ArrayList<>(mainDeviceIds));
        List<IotDeviceDO> mainDevices = iotDeviceMapper.selectListAlone(deviceReqVO);
        // 过滤掉 封存 的 压裂 连油 主设备
        if (CollUtil.isNotEmpty(mainDevices)) {
            mainDevices.forEach(device -> {
                /* if ("fc".equals(device.getDeviceStatus())) {
                    fcMainDeviceIds.add(device.getId());
                } else { */
                    abDeviceIds.add(device.getId());
                // }
            });
        }

        // 计算指定时间区间内包含的天数
        long daysCount;
        if (ObjUtil.isNotEmpty(pageReqVO.getCreateTime()) && pageReqVO.getCreateTime().length >= 2) {
            LocalDateTime start = pageReqVO.getCreateTime()[0];
            LocalDateTime end = pageReqVO.getCreateTime()[1];

            if (ObjUtil.isNotEmpty(start) && ObjUtil.isNotEmpty(end) && !end.isBefore(start)) {
                // 使用ChronoUnit.DAYS.between计算天数差，并+1包含首尾两天
                daysCount = ChronoUnit.DAYS.between(
                        start.toLocalDate(),
                        end.toLocalDate()
                ) + 1;
            } else {
                daysCount = 0L;
            }
        } else {
            daysCount = 0L;
        }

        // 日报涉及到的施工设备id集合
        Set<Long> reportDeviceIds = new HashSet<>();
        // 日报用到的施工主设备id集合
        List<Long> reportMainDeviceIds = new ArrayList<>();

        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.dailyReports(pageReqVO);
        // 累加所有日报的 工作量 数据 油耗 桥塞 趟数 井数...
        if (CollUtil.isNotEmpty(dailyReports)) {
            // 筛选出所有日报关联的任务集合
            dailyReports.forEach(report -> {
                // 筛选出日报中施工的 主设备id 集合
                Set<Long> currentReportDeviceIds = report.getDeviceIds();
                if (CollUtil.isNotEmpty(currentReportDeviceIds)) {
                    // 筛选出属于 abDeviceIds 的设备 ID，全部加入 reportMainDeviceIds（不去重）
                    currentReportDeviceIds.stream()
                            .filter(abDeviceIds::contains)
                            .forEach(reportMainDeviceIds::add);
                }
                /* if (CollUtil.isNotEmpty(report.getDeviceIds())) {
                    reportDeviceIds.addAll(report.getDeviceIds());
                } */
                BigDecimal dailyFuel = report.getDailyFuel();
                dailyFuel = ObjUtil.isEmpty(dailyFuel) ? BigDecimal.ZERO : dailyFuel;
                if (cumulativeFuelsPair.containsKey("cumulativeFuels")) {
                    BigDecimal existTotalFuel = cumulativeFuelsPair.get("cumulativeFuels");
                    BigDecimal tempTotalFuel = existTotalFuel.add(dailyFuel);
                    cumulativeFuelsPair.put("cumulativeFuels", tempTotalFuel);
                } else {
                    cumulativeFuelsPair.put("cumulativeFuels", dailyFuel);
                }
                // 设置每个任务的工作量数据  单位相同的工作量值作合并处理
                List<IotTaskAttrModelProperty> taskAttrs = report.getExtProperty();
                // 暂存不同单位的工作量属性值
                BigDecimal tempTotalBridgePlug = BigDecimal.ZERO;               // 桥塞(个数)
                BigDecimal tempTotalRunCount = BigDecimal.ZERO;                 // 趟数
                BigDecimal tempTotalCumulativeWorkingWell = BigDecimal.ZERO;    // 井数
                BigDecimal tempTotalHourCount = BigDecimal.ZERO;                // 小时H
                BigDecimal tempTotalWaterVolume = BigDecimal.ZERO;              // 水方量(方)
                BigDecimal tempTotalPumpTrips = BigDecimal.ZERO;                // 泵车 台次
                BigDecimal tempTotalCumulativeWorkingLayers = BigDecimal.ZERO;  // 段数 累计施工-层
                BigDecimal tempTotalMixSand = BigDecimal.ZERO;                  // 台次 当日仪表/混砂
                if (CollUtil.isNotEmpty(taskAttrs)) {
                    for (IotTaskAttrModelProperty attr : taskAttrs) {
                        String unit = attr.getUnit();   // 工作量单位
                        String actualValueStr = attr.getActualValue();  // 工作量属性的实际值
                        // 处理实际值：避免null或非数字字符串导致的异常
                        BigDecimal actualValue = BigDecimal.ZERO;
                        if (StrUtil.isNotBlank(actualValueStr)) {
                            try {
                                actualValue = new BigDecimal(actualValueStr);
                            } catch (NumberFormatException e) {
                                // 若字符串格式错误，默认按0处理（可根据业务调整）
                                actualValue = BigDecimal.ZERO;
                            }
                        }
                        if ("个数".equals(unit)) {
                            // 钻可溶桥塞  钻复合桥塞
                            tempTotalBridgePlug = tempTotalBridgePlug.add(actualValue);
                            if (cumulativeBridgePlugPair.containsKey("cumulativeBridgePlug")) {
                                BigDecimal tempBridgePlug = cumulativeBridgePlugPair.get("cumulativeBridgePlug");
                                cumulativeBridgePlugPair.put("cumulativeBridgePlug", tempTotalBridgePlug.add(tempBridgePlug));
                            } else {
                                cumulativeBridgePlugPair.put("cumulativeBridgePlug", tempTotalBridgePlug);
                            }
                        }
                        if ("趟数".equals(unit)) {
                            tempTotalRunCount = tempTotalRunCount.add(actualValue);
                            if (cumulativeRunCountPair.containsKey("cumulativeRunCount")) {
                                BigDecimal tempRunCount = cumulativeRunCountPair.get("cumulativeRunCount");
                                cumulativeRunCountPair.put("cumulativeRunCount", tempTotalRunCount.add(tempRunCount));
                            } else {
                                cumulativeRunCountPair.put("cumulativeRunCount", tempTotalRunCount);
                            }
                        }
                        if ("小时".equals(unit)) {
                            tempTotalHourCount = tempTotalHourCount.add(actualValue);
                            if (cumulativeHourCountPair.containsKey("cumulativeHourCount")) {
                                BigDecimal tempHourCount = cumulativeHourCountPair.get("cumulativeHourCount");
                                cumulativeHourCountPair.put("cumulativeHourCount", tempTotalHourCount.add(tempHourCount));
                            } else {
                                cumulativeHourCountPair.put("cumulativeHourCount", tempTotalHourCount);
                            }
                        }
                        if ("天数".equals(unit)) {
                            // 将 actualValue 换算成 H
                            BigDecimal hours = actualValue.multiply(new BigDecimal("24"));
                            tempTotalHourCount = tempTotalHourCount.add(hours);
                            if (cumulativeHourCountPair.containsKey("cumulativeHourCount")) {
                                BigDecimal tempHourCount = cumulativeHourCountPair.get("cumulativeHourCount");
                                cumulativeHourCountPair.put("cumulativeHourCount", tempTotalHourCount.add(tempHourCount));
                            } else {
                                cumulativeHourCountPair.put("cumulativeHourCount", tempTotalHourCount);
                            }
                        }
                        if ("方".equals(unit)) {
                            tempTotalWaterVolume = tempTotalWaterVolume.add(actualValue);
                            if (cumulativeWaterVolumePair.containsKey("cumulativeWaterVolume")) {
                                BigDecimal tempWaterVolume = cumulativeWaterVolumePair.get("cumulativeWaterVolume");
                                cumulativeWaterVolumePair.put("cumulativeWaterVolume", tempTotalWaterVolume.add(tempWaterVolume));
                            } else {
                                cumulativeWaterVolumePair.put("cumulativeWaterVolume", tempTotalWaterVolume);
                            }
                        }
                        // 井数 日报关联任务 完工 井数 +1
                        /* if ("井数".equals(unit)) {
                            tempTotalCumulativeWorkingWell = tempTotalCumulativeWorkingWell.add(actualValue);
                            if (cumulativeWorkingWellPair.containsKey("cumulativeWorkingWell")) {
                                BigDecimal tempWorkingWell = cumulativeWorkingWellPair.get("cumulativeWorkingWell");
                                cumulativeWorkingWellPair.put("cumulativeWorkingWell", tempTotalCumulativeWorkingWell.add(tempWorkingWell));
                            } else {
                                cumulativeWorkingWellPair.put("cumulativeWorkingWell", tempTotalCumulativeWorkingWell);
                            }
                        } */
                        if ("段数".equals(unit)) {
                            // 累计施工层
                            tempTotalCumulativeWorkingLayers = tempTotalCumulativeWorkingLayers.add(actualValue);
                            if (cumulativeWorkingLayersPair.containsKey("cumulativeWorkingLayers")) {
                                BigDecimal tempWorkingLayer = cumulativeWorkingLayersPair.get("cumulativeWorkingLayers");
                                cumulativeWorkingLayersPair.put("cumulativeWorkingLayers", tempTotalCumulativeWorkingLayers.add(tempWorkingLayer));
                            } else {
                                cumulativeWorkingLayersPair.put("cumulativeWorkingLayers", tempTotalCumulativeWorkingLayers);
                            }
                        }
                        if ("台次".equals(unit) && "当日泵车台次".equals(attr.getName())) {
                            tempTotalPumpTrips = tempTotalPumpTrips.add(actualValue);
                            if (cumulativePumpTripsPair.containsKey("cumulativePumpTrips")) {
                                BigDecimal tempPumpTrips = cumulativePumpTripsPair.get("cumulativePumpTrips");
                                cumulativePumpTripsPair.put("cumulativePumpTrips", tempTotalPumpTrips.add(tempPumpTrips));
                            } else {
                                cumulativePumpTripsPair.put("cumulativePumpTrips", tempTotalPumpTrips);
                            }
                        }
                        if ("台次".equals(unit) && ("当日仪表/混砂".equals(attr.getName())
                                || "当日混砂".equals(attr.getName()) || "当日仪表".equals(attr.getName()))) {
                            tempTotalMixSand = tempTotalMixSand.add(actualValue);
                            if (cumulativeMixSandPair.containsKey("cumulativeMixSand")) {
                                BigDecimal tempMixSand = cumulativeMixSandPair.get("cumulativeMixSand");
                                cumulativeMixSandPair.put("cumulativeMixSand", actualValue.add(tempMixSand));
                            } else {
                                cumulativeMixSandPair.put("cumulativeMixSand", tempTotalMixSand);
                            }
                        }
                    }
                }
            });
            // 指定时间范围内 日报中所有施工的主设备数量/总主设备数量*N天 (A B类)
            // abDeviceIds所有AB类设备  reportDeviceIds日报中用到的所有设备 交集求出日报中使用到的AB类设备
            if (CollUtil.isNotEmpty(abDeviceIds) && CollUtil.isNotEmpty(reportMainDeviceIds)) {
                // 1. 求交集：实际使用的A B类设备
                Set<Long> utilizedDeviceIds = reportDeviceIds.stream()
                        .filter(abDeviceIds::contains)
                        .collect(Collectors.toSet());
                // 2. 计算设备利用率（实际使用设备数 / 总设备数）
                BigDecimal deviceUtilization = BigDecimal.ZERO;
                // 转换为BigDecimal进行计算，保留4位小数
                deviceUtilization = BigDecimal.valueOf(reportMainDeviceIds.size())
                        .divide(BigDecimal.valueOf(abDeviceIds.size() * daysCount), 4, RoundingMode.HALF_UP);
                IotRdDailyReportStatisticsRespVO utilizationResp = new IotRdDailyReportStatisticsRespVO();
                utilizationResp.setUtilizationRate(deviceUtilization);
                utilizationResp.setDeptName("utilizationRate");
                result.add(utilizationResp);
                System.out.println("瑞都设备利用率-分子：" + reportMainDeviceIds.size());
                System.out.println("瑞都设备利用率-分母：" + abDeviceIds.size());
            }
        }
        if (CollUtil.isNotEmpty(cumulativeBridgePlugPair)) {
            Collection<BigDecimal> cumulativeBridgePlugs = cumulativeBridgePlugPair.values();
            Optional<BigDecimal> bridgePlugs = cumulativeBridgePlugs.stream().findAny();
            BigDecimal bridgePlug = bridgePlugs.get();
            IotRdDailyReportStatisticsRespVO bridgePlugResp = new IotRdDailyReportStatisticsRespVO();
            bridgePlugResp.setCumulativeBridgePlug(bridgePlug);
            bridgePlugResp.setDeptName("cumulativeBridgePlug");
            result.add(bridgePlugResp);
        }
        if (CollUtil.isNotEmpty(cumulativeRunCountPair)) {
            Collection<BigDecimal> cumulativeRunCounts = cumulativeRunCountPair.values();
            Optional<BigDecimal> runCounts = cumulativeRunCounts.stream().findAny();
            BigDecimal runCount = runCounts.get();
            IotRdDailyReportStatisticsRespVO runCountResp = new IotRdDailyReportStatisticsRespVO();
            runCountResp.setCumulativeRunCount(runCount);
            runCountResp.setDeptName("cumulativeRunCount");
            result.add(runCountResp);
        }
        if (CollUtil.isNotEmpty(cumulativeHourCountPair)) {
            Collection<BigDecimal> cumulativeHourCounts = cumulativeHourCountPair.values();
            Optional<BigDecimal> hourCounts = cumulativeHourCounts.stream().findAny();
            BigDecimal hourCount = hourCounts.get();
            IotRdDailyReportStatisticsRespVO hourCountResp = new IotRdDailyReportStatisticsRespVO();
            hourCountResp.setCumulativeHourCount(hourCount);
            hourCountResp.setDeptName("cumulativeHourCount");
            result.add(hourCountResp);
        }
        if (CollUtil.isNotEmpty(cumulativeWaterVolumePair)) {
            Collection<BigDecimal> cumulativeWaterVolumes = cumulativeWaterVolumePair.values();
            Optional<BigDecimal> waterVolumes = cumulativeWaterVolumes.stream().findAny();
            BigDecimal waterVolume = waterVolumes.get();
            IotRdDailyReportStatisticsRespVO waterVolumeResp = new IotRdDailyReportStatisticsRespVO();
            waterVolumeResp.setCumulativeWaterVolume(waterVolume);
            waterVolumeResp.setDeptName("cumulativeWaterVolume");
            result.add(waterVolumeResp);
        }
        if (CollUtil.isNotEmpty(cumulativeWorkingWellPair)) {
            Collection<BigDecimal> cumulativeWorkingWells = cumulativeWorkingWellPair.values();
            Optional<BigDecimal> workingWells = cumulativeWorkingWells.stream().findAny();
            BigDecimal workingWell = workingWells.get();
            IotRdDailyReportStatisticsRespVO workingWellResp = new IotRdDailyReportStatisticsRespVO();
            workingWellResp.setCumulativeWorkingWell(workingWell);
            workingWellResp.setDeptName("cumulativeWorkingWell");
            result.add(workingWellResp);
        }
        if (CollUtil.isNotEmpty(cumulativeWorkingLayersPair)) {
            Collection<BigDecimal> cumulativeWorkingLayers = cumulativeWorkingLayersPair.values();
            Optional<BigDecimal> workingLayers = cumulativeWorkingLayers.stream().findAny();
            BigDecimal workingLayer = workingLayers.get();
            IotRdDailyReportStatisticsRespVO workingLayerResp = new IotRdDailyReportStatisticsRespVO();
            workingLayerResp.setCumulativeWorkingLayers(workingLayer);
            workingLayerResp.setDeptName("cumulativeWorkingLayers");
            result.add(workingLayerResp);
        }
        if (CollUtil.isNotEmpty(cumulativePumpTripsPair)) {
            Collection<BigDecimal> cumulativePumpTrips = cumulativePumpTripsPair.values();
            Optional<BigDecimal> pumpTrips = cumulativePumpTrips.stream().findAny();
            BigDecimal pumpTrip = pumpTrips.get();
            IotRdDailyReportStatisticsRespVO pumpTripResp = new IotRdDailyReportStatisticsRespVO();
            pumpTripResp.setCumulativePumpTrips(pumpTrip);
            pumpTripResp.setDeptName("cumulativePumpTrips");
            result.add(pumpTripResp);
        }
        if (CollUtil.isNotEmpty(cumulativeMixSandPair)) {
            Collection<BigDecimal> cumulativeMixSands = cumulativeMixSandPair.values();
            Optional<BigDecimal> mixSands = cumulativeMixSands.stream().findAny();
            BigDecimal mixSand = mixSands.get();
            IotRdDailyReportStatisticsRespVO mixSandResp = new IotRdDailyReportStatisticsRespVO();
            mixSandResp.setCumulativeMixSand(mixSand);
            mixSandResp.setDeptName("cumulativeMixSand");
            result.add(mixSandResp);
        }
        if (CollUtil.isNotEmpty(cumulativeFuelsPair)) {
            Collection<BigDecimal> cumulativeFuels = cumulativeFuelsPair.values();
            Optional<BigDecimal> Fuels = cumulativeFuels.stream().findAny();
            BigDecimal fuels = Fuels.get();
            IotRdDailyReportStatisticsRespVO fuelsResp = new IotRdDailyReportStatisticsRespVO();
            fuelsResp.setTotalDailyFuel(fuels);
            fuelsResp.setDeptName("cumulativeFuels");
            result.add(fuelsResp);
        }
        return result;
    }

    /**
     * 按项目部维度 汇总 统计数据
     * @param dailyReports 日报数据列表
     * @param rootDeptId 根部门ID（如 163L为 瑞都 根部门，或其他公司级部门ID）
     * @return 项目部维度统计结果列表
     */
    private List<IotRdDailyReportStatisticsRespVO> statisticsByProjectDept(List<IotRdDailyReportDO> dailyReports,
                                                                           Long rootDeptId, IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportStatisticsRespVO> 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> utilizationRatePair = new HashMap<>();

        // key队伍id/项目部id   value累计 桥塞(个数)
        Map<Long, BigDecimal> cumulativeBridgePlugPair = new HashMap<>();
        // key队伍id/项目部id   value累计 趟数
        Map<Long, BigDecimal> cumulativeRunCountPair = new HashMap<>();
        // key队伍id/项目部id   value累计 井数
        Map<Long, BigDecimal> cumulativeWorkingWellPair = new HashMap<>();
        // key队伍id/项目部id   value累计 小时H
        Map<Long, BigDecimal> cumulativeHourCountPair = new HashMap<>();
        // key队伍id/项目部id   value累计 油耗
        Map<Long, BigDecimal> cumulativeFuelsPair = new HashMap<>();
        // key队伍id/项目部id   value累计 水方量(方)
        Map<Long, BigDecimal> cumulativeWaterVolumePair = new HashMap<>();
        // key队伍id/项目部id   value累计 段数 累计施工-层
        Map<Long, BigDecimal> cumulativeWorkingLayersPair = new HashMap<>();
        // key队伍id/项目部id   value累计 台次 当日仪表/混砂
        Map<Long, BigDecimal> cumulativeMixSandPair = new HashMap<>();
        // key队伍id/项目部id   value累计 台次 当日泵车台次
        Map<Long, BigDecimal> cumulativePumpTripsPair = new HashMap<>();
        // key项目部id  value 连油井数
        Map<Long, Set<Long>> lyWellsPair = new HashMap<>();
        // key项目部id  value 压裂井数
        Map<Long, Set<Long>> ylWellsPair = new HashMap<>();

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

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

        Set<Long> lyTeamIds = new HashSet<>();
        Set<Long> ylTeamIds = new HashSet<>();

        // 构建项目部映射和父子部门关系 瑞都存在日报部门是项目部的情况
        depts.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);
            }
            // 可能是项目部 也可能是队伍
            teamProjectIdPair.put(dept.getId(), dept.getParentId());
            // 筛选出 连油队伍 压裂队伍
            if ("LY".equals(dept.getEmail())) {
                lyTeamIds.add(dept.getId());
            }
            if ("YL".equals(dept.getEmail())) {
                ylTeamIds.add(dept.getId());
            }
        });
        // key项目部id   value 日报中A+B类设备id集合
        Map<Long, Set<Long>> projectReportABDeviceIdPair = new HashMap<>();
        // key项目部id   value 日报中A+B类设备id集合(不去重)
        Map<Long, List<Long>> projectReportABDeviceIdsPair = new HashMap<>();

        // key项目部id   value A+B类设备id集合
        Map<Long, Set<Long>> projectABDeviceIdPair = new HashMap<>();

        // 设备利用率逻辑调整 只筛选 压裂 连油 主设备 rq_iot_device_associate
        // 队伍日报中关联的主设备数量 /（队伍下所有主设备数量-封存设备数量）
        Set<Long> mainDeviceIds = new HashSet<>();
        Set<Long> fcMainDeviceIds = new HashSet<>();
        // key设备id   value设备所属部门id
        Map<Long, Long> mainDeviceIdPair = new HashMap<>();
        IotDeviceAssociatePageReqVO deviceAssocReqVO = new IotDeviceAssociatePageReqVO();
        deviceAssocReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        deviceAssocReqVO.setDeptIds(allRhChildDeptIds);
        PageResult<IotDeviceAssociateDO> pageResult = iotDeviceAssociateMapper.selectPage(deviceAssocReqVO);
        if (ObjUtil.isNotEmpty(pageResult)) {
            List<IotDeviceAssociateDO> deviceAssociates = pageResult.getList();
            // 查询出所有瑞都压裂 连油主设备
            deviceAssociates.forEach(assoc -> {
                mainDeviceIds.add(assoc.getDeviceId());
                mainDeviceIdPair.put(assoc.getDeviceId(), assoc.getDeptId());
            });
        }

        // 以项目部为维度统计 设备利用率 日报中用到AB类设备/
        // 瑞都 A B 类设备id 集合
        Set<Long> abDeviceCategoryIds = new HashSet<>();
        // 查询所有瑞都产品类别包含 A B 类
        IotProductClassifyListReqVO deviceCategoryReqVO = new IotProductClassifyListReqVO();
        List<IotProductClassifyDO> allDeviceCategories = iotProductClassifyMapper.selectList(deviceCategoryReqVO);
        if (CollUtil.isNotEmpty(allDeviceCategories)) {
            allDeviceCategories.forEach(category -> {
                if ("A".equals(category.getRemark())) {
                    abDeviceCategoryIds.add(category.getId());
                }
            });
        }
        // 查询 瑞都 施工状态 字典数据 筛选出施工设备为 LY 的记录
        Set<String> lyStatuses = new HashSet<>();
        List<DictDataDO> rdStatuses = dictDataService.getDictDataListByDictType("rdStatus");
        if (CollUtil.isNotEmpty(rdStatuses)) {
            rdStatuses.forEach(tech -> {
                if ("LY".equals(tech.getRemark())) {
                    lyStatuses.add(tech.getValue());
                }
            });
        }

        // 查询瑞都 压裂 连油 主设备详情信息
        IotDevicePageReqVO deviceReqVO = new IotDevicePageReqVO();
        deviceReqVO.setDeviceIds(new ArrayList<>(mainDeviceIds));
        List<IotDeviceDO> mainDevices = iotDeviceMapper.selectListAlone(deviceReqVO);
        // 筛选出 封存 的主设备
        if (CollUtil.isNotEmpty(mainDevices)) {
            mainDevices.forEach(device -> {
                /* if ("fc".equals(device.getDeviceStatus())) {
                    fcMainDeviceIds.add(device.getId());
                }*/
            });
        }

        // 设置各项目部 压裂 连油 主设备集合
        if (CollUtil.isNotEmpty(mainDeviceIdPair)) {
            // key主设备id  value主设备所属部门id
            mainDeviceIdPair.forEach((deviceId, deptId) -> {
                // 过滤掉封存的压裂 连油主设备
                if (!fcMainDeviceIds.contains(deviceId)) {
                    Long projectId = 0l;
                    if (teamProjectIdPair.containsKey(deptId)) {
                        projectId = teamProjectIdPair.get(deptId);
                    }
                    if (projectDeptIds.contains(deptId)) {
                        projectId = deptId;
                    }
                    if (projectABDeviceIdPair.containsKey(projectId)) {
                        Set<Long> tempDeviceIds = projectABDeviceIdPair.get(projectId);
                        tempDeviceIds.add(deviceId);
                        projectABDeviceIdPair.put(projectId, tempDeviceIds);
                    } else {
                        Set<Long> tempDeviceIds = new HashSet<>();
                        tempDeviceIds.add(deviceId);
                        projectABDeviceIdPair.put(projectId, tempDeviceIds);
                    }
                }
            });
        }

        // 计算指定时间区间内包含的天数
        long daysCount;
        if (ObjUtil.isNotEmpty(pageReqVO.getCreateTime()) && pageReqVO.getCreateTime().length >= 2) {
            LocalDateTime start = pageReqVO.getCreateTime()[0];
            LocalDateTime end = pageReqVO.getCreateTime()[1];

            if (ObjUtil.isNotEmpty(start) && ObjUtil.isNotEmpty(end) && !end.isBefore(start)) {
                // 使用ChronoUnit.DAYS.between计算天数差，并+1包含首尾两天
                daysCount = ChronoUnit.DAYS.between(
                        start.toLocalDate(),
                        end.toLocalDate()
                ) + 1;
            } else {
                daysCount = 0L;
            }
        } else {
            daysCount = 0L;
        }

        // 累计计算各项 工作量
        if (CollUtil.isNotEmpty(dailyReports)) {
            dailyReports.forEach(report -> {
                // 统计日报包含施工AB类设备的利用率
                // 利用率暂时不考虑日报状态
                // if (lyStatuses.contains(report.getRdStatus())) {
                Set<Long> reportDeviceIds = report.getDeviceIds();
                // 获得当前日报施工队伍的上级项目部
                if (teamProjectIdPair.containsKey(report.getDeptId())) {
                    Long projectId = teamProjectIdPair.get(report.getDeptId());
                    if (projectABDeviceIdPair.containsKey(projectId)) {
                        // 项目部下所有的AB类设备id集合
                        Set<Long> tempReportDeviceIds = projectABDeviceIdPair.get(projectId);
                        Set<Long> crossDeviceIds = CollUtil.isNotEmpty(reportDeviceIds)
                                && CollUtil.isNotEmpty(tempReportDeviceIds)
                                ? reportDeviceIds.stream() // 流式处理reportDeviceIds
                                .filter(tempReportDeviceIds::contains) // 筛选出在tempReportDeviceIds中存在的元素
                                .filter(deviceId -> !fcMainDeviceIds.contains(deviceId)) // 过滤掉 封存的主设备
                                .collect(Collectors.toSet()) // 收集为新的Set集合
                                : Collections.emptySet(); // 任意集合为空则返回空Set（避免返回null）
                        if (projectReportABDeviceIdsPair.containsKey(projectId)) {
                            List<Long> tempDeviceIds = projectReportABDeviceIdsPair.get(projectId);
                            tempDeviceIds.addAll(crossDeviceIds);
                            projectReportABDeviceIdsPair.put(projectId, tempDeviceIds);
                        } else {
                            List<Long> tempDeviceIds = new ArrayList<>();
                            tempDeviceIds.addAll(crossDeviceIds);
                            projectReportABDeviceIdsPair.put(projectId, tempDeviceIds);
                        }
                    }
                }
                // }

                BigDecimal dailyFuel = report.getDailyFuel();
                dailyFuel = ObjUtil.isEmpty(dailyFuel) ? BigDecimal.ZERO : dailyFuel;

                if (ObjUtil.isNotEmpty(report.getDeptId()) && teamProjectIdPair.containsKey(report.getDeptId())) {
                    // projectDeptId可能是项目部 也可能是项目的上级
                    Long projectDeptId = 0l;
                    if (teamDeptPair.containsKey(report.getDeptId())) {
                        // 日报deptId如果是队伍 获取队伍的上级项目部
                        projectDeptId = teamProjectIdPair.get(report.getDeptId());
                    }
                    if (projectDeptPair.containsKey(report.getDeptId())) {
                        // 日报deptId如果是项目部 使用当前项目部
                        projectDeptId = report.getDeptId();
                    }
                    if (ObjUtil.isNotEmpty(projectDeptId) && (projectDeptId > 0)) {
                        if (cumulativeFuelsPair.containsKey(projectDeptId)) {
                            BigDecimal existTotalFuel = cumulativeFuelsPair.get(projectDeptId);
                            BigDecimal tempTotalFuel = existTotalFuel.add(dailyFuel);
                            cumulativeFuelsPair.put(projectDeptId, tempTotalFuel);
                        } else {
                            cumulativeFuelsPair.put(projectDeptId, dailyFuel);
                        }

                        // 设置每个任务的工作量数据  单位相同的工作量值作合并处理
                        List<IotTaskAttrModelProperty> taskAttrs = report.getExtProperty();
                        // 暂存不同单位的工作量属性值
                        BigDecimal tempTotalBridgePlug = BigDecimal.ZERO;               // 桥塞(个数)
                        BigDecimal tempTotalRunCount = BigDecimal.ZERO;                 // 趟数
                        BigDecimal tempTotalCumulativeWorkingWell = BigDecimal.ZERO;    // 井数
                        BigDecimal tempTotalHourCount = BigDecimal.ZERO;                // 小时H
                        BigDecimal tempTotalWaterVolume = BigDecimal.ZERO;              // 水方量(方)
                        BigDecimal tempTotalPumpTrips = BigDecimal.ZERO;                // 泵车 台次
                        BigDecimal tempTotalCumulativeWorkingLayers = BigDecimal.ZERO;  // 段数 累计施工-层
                        BigDecimal tempTotalMixSand = BigDecimal.ZERO;                  // 台次 当日仪表/混砂
                        if (CollUtil.isNotEmpty(taskAttrs)) {
                            for (IotTaskAttrModelProperty attr : taskAttrs) {
                                String unit = attr.getUnit();   // 工作量单位
                                String actualValueStr = attr.getActualValue();  // 工作量属性的实际值
                                // 处理实际值：避免null或非数字字符串导致的异常
                                BigDecimal actualValue = BigDecimal.ZERO;
                                if (StrUtil.isNotBlank(actualValueStr)) {  // 假设使用Hutool的StrUtil，或自行判断null/空
                                    try {
                                        actualValue = new BigDecimal(actualValueStr);
                                    } catch (NumberFormatException e) {
                                        // 若字符串格式错误，默认按0处理（可根据业务调整）
                                        actualValue = BigDecimal.ZERO;
                                    }
                                }
                                if (lyTeamIds.contains(report.getDeptId())) {
                                    // 连油井数
                                    if (lyWellsPair.containsKey(projectDeptId)) {
                                        Set<Long> lyWellIds = lyWellsPair.get(projectDeptId);
                                        lyWellIds.add(report.getTaskId());
                                        lyWellsPair.put(projectDeptId, lyWellIds);
                                    } else {
                                        Set<Long> lyWellIds = new HashSet<>();
                                        lyWellIds.add(report.getTaskId());
                                        lyWellsPair.put(projectDeptId, lyWellIds);
                                    }
                                }
                                if (ylTeamIds.contains(report.getDeptId())) {
                                    // 压裂井数
                                    if (ylWellsPair.containsKey(projectDeptId)) {
                                        Set<Long> ylWellIds = ylWellsPair.get(projectDeptId);
                                        ylWellIds.add(report.getTaskId());
                                        ylWellsPair.put(projectDeptId, ylWellIds);
                                    } else {
                                        Set<Long> ylWellIds = new HashSet<>();
                                        ylWellIds.add(report.getTaskId());
                                        ylWellsPair.put(projectDeptId, ylWellIds);
                                    }
                                }
                                if ("个数".equals(unit)) {
                                    // 钻可溶桥塞  钻复合桥塞
                                    tempTotalBridgePlug = tempTotalBridgePlug.add(actualValue);
                                    if (cumulativeBridgePlugPair.containsKey(projectDeptId)) {
                                        BigDecimal tempBridgePlug = cumulativeBridgePlugPair.get(projectDeptId);
                                        cumulativeBridgePlugPair.put(projectDeptId, tempTotalBridgePlug.add(tempBridgePlug));
                                    } else {
                                        cumulativeBridgePlugPair.put(projectDeptId, tempTotalBridgePlug);
                                    }
                                }
                                if ("趟数".equals(unit)) {
                                    tempTotalRunCount = tempTotalRunCount.add(actualValue);
                                    if (cumulativeRunCountPair.containsKey(projectDeptId)) {
                                        BigDecimal tempRunCount = cumulativeRunCountPair.get(projectDeptId);
                                        cumulativeRunCountPair.put(projectDeptId, tempTotalRunCount.add(tempRunCount));
                                    } else {
                                        cumulativeRunCountPair.put(projectDeptId, tempTotalRunCount);
                                    }
                                }
                                if ("小时".equals(unit)) {
                                    tempTotalHourCount = tempTotalHourCount.add(actualValue);
                                    if (cumulativeHourCountPair.containsKey(projectDeptId)) {
                                        BigDecimal tempHourCount = cumulativeHourCountPair.get(projectDeptId);
                                        cumulativeHourCountPair.put(projectDeptId, tempTotalHourCount.add(tempHourCount));
                                    } else {
                                        cumulativeHourCountPair.put(projectDeptId, tempTotalHourCount);
                                    }
                                }
                                if ("天数".equals(unit)) {
                                    // 将 actualValue 换算成 H
                                    BigDecimal hours = actualValue.multiply(new BigDecimal("24"));
                                    tempTotalHourCount = tempTotalHourCount.add(hours);
                                    if (cumulativeHourCountPair.containsKey(projectDeptId)) {
                                        BigDecimal tempHourCount = cumulativeHourCountPair.get(projectDeptId);
                                        cumulativeHourCountPair.put(projectDeptId, tempTotalHourCount.add(tempHourCount));
                                    } else {
                                        cumulativeHourCountPair.put(projectDeptId, tempTotalHourCount);
                                    }
                                }
                                if ("方".equals(unit)) {
                                    tempTotalWaterVolume = tempTotalWaterVolume.add(actualValue);
                                    if (cumulativeWaterVolumePair.containsKey(projectDeptId)) {
                                        BigDecimal tempWaterVolume = cumulativeWaterVolumePair.get(projectDeptId);
                                        cumulativeWaterVolumePair.put(projectDeptId, tempTotalWaterVolume.add(tempWaterVolume));
                                    } else {
                                        cumulativeWaterVolumePair.put(projectDeptId, tempTotalWaterVolume);
                                    }
                                }
                                if ("井数".equals(unit)) {
                                    tempTotalCumulativeWorkingWell = tempTotalCumulativeWorkingWell.add(actualValue);
                                    if (cumulativeWorkingWellPair.containsKey(projectDeptId)) {
                                        BigDecimal tempWorkingWell = cumulativeWorkingWellPair.get(projectDeptId);
                                        cumulativeWorkingWellPair.put(projectDeptId, tempTotalCumulativeWorkingWell.add(tempWorkingWell));
                                    } else {
                                        cumulativeWorkingWellPair.put(projectDeptId, tempTotalCumulativeWorkingWell);
                                    }
                                }
                                if ("段数".equals(unit)) {
                                    // 压裂 累计施工层
                                    tempTotalCumulativeWorkingLayers = tempTotalCumulativeWorkingLayers.add(actualValue);
                                    if (cumulativeWorkingLayersPair.containsKey(projectDeptId)) {
                                        BigDecimal tempWorkingLayer = cumulativeWorkingLayersPair.get(projectDeptId);
                                        cumulativeWorkingLayersPair.put(projectDeptId, tempTotalCumulativeWorkingLayers.add(tempWorkingLayer));
                                    } else {
                                        cumulativeWorkingLayersPair.put(projectDeptId, tempTotalCumulativeWorkingLayers);
                                    }
                                }
                                if ("台次".equals(unit) && "当日泵车台次".equals(attr.getName())) {
                                    tempTotalPumpTrips = tempTotalPumpTrips.add(actualValue);
                                    if (cumulativePumpTripsPair.containsKey(projectDeptId)) {
                                        BigDecimal tempPumpTrips = cumulativePumpTripsPair.get(projectDeptId);
                                        cumulativePumpTripsPair.put(projectDeptId, tempTotalPumpTrips.add(tempPumpTrips));
                                    } else {
                                        cumulativePumpTripsPair.put(projectDeptId, tempTotalPumpTrips);
                                    }
                                }
                                if ("台次".equals(unit) && ("当日仪表/混砂".equals(attr.getName())
                                        || "当日混砂".equals(attr.getName()) || "当日仪表".equals(attr.getName()))) {
                                    tempTotalMixSand = tempTotalMixSand.add(actualValue);
                                    if (cumulativeMixSandPair.containsKey(projectDeptId)) {
                                        BigDecimal tempMixSand = cumulativeMixSandPair.get(projectDeptId);
                                        cumulativeMixSandPair.put(projectDeptId, actualValue.add(tempMixSand));
                                    } else {
                                        cumulativeMixSandPair.put(projectDeptId, tempTotalMixSand);
                                    }
                                }
                            }
                        }
                    }
                }
            });
            // 计算每个项目部的设备利用率 施工的设备数量/设备总数量
            // 队伍日报中关联的主设备数量(不去重)/(队伍下所有主设备数量-封存设备数量)*N天
            if (CollUtil.isNotEmpty(projectABDeviceIdPair)) {
                projectABDeviceIdPair.forEach((projectDeptId, deviceIds) -> {
                    // 项目部下所有压裂连油主设备
                    Integer projectDeviceCount = deviceIds.size();
                    // 项目部日报中施工的压裂连油主设备
                    Integer reportDeviceCount = 0;
                    if (projectReportABDeviceIdsPair.containsKey(projectDeptId)) {
                        List<Long> reportDeviceIds = projectReportABDeviceIdsPair.get(projectDeptId);
                        reportDeviceCount = reportDeviceIds.size();
                    }
                    double rate = 0.0;
                    // 计算设备利用率（处理除数为0的情况）
                    if (projectDeviceCount > 0 && reportDeviceCount > 0 && daysCount > 0) {
                        rate = new BigDecimal((double) reportDeviceCount / (projectDeviceCount * daysCount))
                                .setScale(4, RoundingMode.HALF_UP)  // 保留4位小数，四舍五入
                                .doubleValue();;
                    } else {
                        rate = 0;
                    }
                    utilizationRatePair.put(projectDeptId, BigDecimal.valueOf(rate));
                });
            }
        }

        // 生成返回的数据列表集合
        projectDeptPair.forEach((deptId, dept) -> {
            IotRdDailyReportStatisticsRespVO statistics = new IotRdDailyReportStatisticsRespVO();
            statistics.setProjectDeptId(deptId);
            statistics.setProjectDeptName(dept.getName());
            statistics.setSort(dept.getSort());
            statistics.setType("2");
            statistics.setUtilizationRate(utilizationRatePair.get(deptId));
            statistics.setCumulativeBridgePlug(cumulativeBridgePlugPair.get(deptId));
            statistics.setCumulativeRunCount(cumulativeRunCountPair.get(deptId));
            statistics.setCumulativeWorkingWell(cumulativeWorkingWellPair.get(deptId));
            statistics.setCumulativeHourCount(cumulativeHourCountPair.get(deptId));
            statistics.setCumulativeWaterVolume(cumulativeWaterVolumePair.get(deptId));
            statistics.setCumulativeWorkingLayers(cumulativeWorkingLayersPair.get(deptId));
            statistics.setCumulativeMixSand(cumulativeMixSandPair.get(deptId));
            statistics.setCumulativePumpTrips(cumulativePumpTripsPair.get(deptId));
            BigDecimal cumulativeFuel = cumulativeFuelsPair.get(deptId);
            if (ObjUtil.isNotEmpty(cumulativeFuel)) {
                // 油耗单位转换成 万升
                BigDecimal FuelWan = cumulativeFuel
                        .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
                statistics.setTotalDailyFuel(FuelWan);
            }
            // 连油井数
            Set<Long> lyWellIds = lyWellsPair.get(deptId);
            statistics.setLyWellCount(CollUtil.isNotEmpty(lyWellIds) ? lyWellIds.size() : 0);
            // 压裂井数
            Set<Long> ylWellIds = ylWellsPair.get(deptId);
            statistics.setYlWellCount(CollUtil.isNotEmpty(ylWellIds) ? ylWellIds.size() : 0);
            result.add(statistics);
        });

        return result;
    }

    /**
     * 按 队伍 维度统计 汇总 数据
     * @param dailyReports 日报数据列表
     * @param pageReqVO (deptId)项目部ID 或 队伍id
     * @return 队伍 维度统计结果列表
     */
    private List<IotRdDailyReportStatisticsRespVO> statisticsByProjectDepartment(List<IotRdDailyReportDO> dailyReports, IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportStatisticsRespVO> 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> utilizationRatePair = new HashMap<>();

        // key队伍id/项目部id   value累计 桥塞(个数)
        Map<Long, BigDecimal> cumulativeBridgePlugPair = new HashMap<>();
        // key队伍id/项目部id   value累计 趟数
        Map<Long, BigDecimal> cumulativeRunCountPair = new HashMap<>();
        // key队伍id/项目部id   value累计 井数
        Map<Long, BigDecimal> cumulativeWorkingWellPair = new HashMap<>();
        // key队伍id/项目部id   value累计 小时H
        Map<Long, BigDecimal> cumulativeHourCountPair = new HashMap<>();
        // key队伍id/项目部id   value累计 油耗
        Map<Long, BigDecimal> cumulativeFuelsPair = new HashMap<>();
        // key队伍id/项目部id   value累计 水方量(方)
        Map<Long, BigDecimal> cumulativeWaterVolumePair = new HashMap<>();
        // key队伍id/项目部id   value累计 段数 累计施工-层
        Map<Long, BigDecimal> cumulativeWorkingLayersPair = new HashMap<>();
        // key队伍id/项目部id   value累计 台次 当日仪表/混砂
        Map<Long, BigDecimal> cumulativeMixSandPair = new HashMap<>();
        // key队伍id/项目部id   value累计 台次 当日泵车台次
        Map<Long, BigDecimal> cumulativePumpTripsPair = new HashMap<>();

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

        // 构建项目部映射和父子部门关系
        depts.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());
        });

        // 统计各队伍的设备利用率
        // 查询 瑞都 施工状态 字典数据 筛选出施工设备为 LY 的记录
        Set<String> lyStatuses = new HashSet<>();
        List<DictDataDO> rdStatuses = dictDataService.getDictDataListByDictType("rdStatus");
        if (CollUtil.isNotEmpty(rdStatuses)) {
            rdStatuses.forEach(tech -> {
                if ("LY".equals(tech.getRemark())) {
                    lyStatuses.add(tech.getValue());
                }
            });
        }

        // key队伍id   value 日报中A+B类设备id集合
        Map<Long, Set<Long>> teamReportABDeviceIdPair = new HashMap<>();
        // key队伍id   value 日报中A+B类设备id集合(不去重)
        Map<Long, List<Long>> teamReportABDeviceIdsPair = new HashMap<>();

        // key队伍id   value A+B类设备id集合
        Map<Long, Set<Long>> teamABDeviceIdPair = new HashMap<>();

        // 设备利用率逻辑调整 只筛选 压裂 连油 主设备 rq_iot_device_associate
        // 队伍日报中关联的主设备数量 /（队伍下所有主设备数量-封存设备数量）
        Set<Long> mainDeviceIds = new HashSet<>();
        Set<Long> fcMainDeviceIds = new HashSet<>();
        // key设备id   value设备所属部门id
        Map<Long, Long> mainDeviceIdPair = new HashMap<>();
        IotDeviceAssociatePageReqVO deviceAssocReqVO = new IotDeviceAssociatePageReqVO();
        deviceAssocReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        deviceAssocReqVO.setDeptIds(allRhChildDeptIds);
        PageResult<IotDeviceAssociateDO> pageResult = iotDeviceAssociateMapper.selectPage(deviceAssocReqVO);
        if (ObjUtil.isNotEmpty(pageResult)) {
            List<IotDeviceAssociateDO> deviceAssociates = pageResult.getList();
            // 查询出所有瑞都压裂 连油主设备
            deviceAssociates.forEach(assoc -> {
                // 过滤掉封存的设备
                mainDeviceIds.add(assoc.getDeviceId());
                mainDeviceIdPair.put(assoc.getDeviceId(), assoc.getDeptId());
            });
        }

        // 查询瑞都 压裂 连油 主设备详情信息
        IotDevicePageReqVO deviceReqVO = new IotDevicePageReqVO();
        deviceReqVO.setDeviceIds(new ArrayList<>(mainDeviceIds));
        List<IotDeviceDO> mainDevices = iotDeviceMapper.selectListAlone(deviceReqVO);
        // 筛选出 封存 的主设备
        if (CollUtil.isNotEmpty(mainDevices)) {
            mainDevices.forEach(device -> {
                /* if ("fc".equals(device.getDeviceStatus())) {
                    fcMainDeviceIds.add(device.getId());
                } */
            });
        }

        // 筛选每个项目部 压裂 连油 主设备id集合
        if (CollUtil.isNotEmpty(mainDeviceIdPair)) {
            mainDeviceIdPair.forEach((deviceId, departmentId) -> {
                // 过滤掉封存的压裂 连油主设备
                if (!fcMainDeviceIds.contains(deviceId)) {
                    Long teamId = 0l;
                    if (teamDeptIds.contains(departmentId)) {
                        teamId = departmentId;
                    }
                    if (teamABDeviceIdPair.containsKey(teamId)) {
                        Set<Long> tempDeviceIds = teamABDeviceIdPair.get(teamId);
                        tempDeviceIds.add(deviceId);
                        teamABDeviceIdPair.put(teamId, tempDeviceIds);
                    } else {
                        Set<Long> tempDeviceIds = new HashSet<>();
                        tempDeviceIds.add(deviceId);
                        teamABDeviceIdPair.put(teamId, tempDeviceIds);
                    }
                }
            });
        }

        LocalDateTime[] createTime = pageReqVO.getCreateTime();
        List<Long> daysCounts = new ArrayList<>();
        // 如果 createTime 包含的时间是 1天则查询 这1天时间内的 队伍总数 施工队伍数量 施工准备队伍数量 驻地待命队伍数量
        if (ObjUtil.isNotEmpty(createTime)) {
            LocalDateTime startTime = createTime[0];
            LocalDateTime endTime = createTime[1];
            if (ObjUtil.isNotEmpty(startTime) && ObjUtil.isNotEmpty(endTime) && !endTime.isBefore(startTime)) {
                // 查询时间区间包含的天数
                // 使用ChronoUnit.DAYS.between计算天数差，并+1包含首尾两天
                long daysCount = ChronoUnit.DAYS.between(
                        startTime.toLocalDate(),
                        endTime.toLocalDate()
                ) + 1;
                daysCounts.add(daysCount);
            }
        }

        // 累计计算 工作量
        if (CollUtil.isNotEmpty(dailyReports)) {
            dailyReports.forEach(report -> {
                // 统计日报关联施工AB类设备的利用率
                Set<Long> reportDeviceIds = report.getDeviceIds();
                // 获得当前日报施工队伍的上级项目部
                if (teamDeptIds.contains(report.getDeptId())) {
                    if (teamABDeviceIdPair.containsKey(report.getDeptId())) {
                        // 下所有的AB类设备id集合
                        Set<Long> tempReportDeviceIds = teamABDeviceIdPair.get(report.getDeptId());
                        Set<Long> crossDeviceIds = CollUtil.isNotEmpty(reportDeviceIds)
                                && CollUtil.isNotEmpty(tempReportDeviceIds)
                                ? reportDeviceIds.stream() // 流式处理reportDeviceIds
                                .filter(tempReportDeviceIds::contains) // 筛选出在tempReportDeviceIds中存在的元素
                                .filter(deviceId -> !fcMainDeviceIds.contains(deviceId)) // 过滤掉 封存的主设备
                                .collect(Collectors.toSet()) // 收集为新的Set集合
                                : Collections.emptySet(); // 任意集合为空则返回空Set（避免返回null）
                        if (teamReportABDeviceIdsPair.containsKey(report.getDeptId())) {
                            List<Long> tempDeviceIds = teamReportABDeviceIdsPair.get(report.getDeptId());
                            tempDeviceIds.addAll(crossDeviceIds);
                            teamReportABDeviceIdsPair.put(report.getDeptId(), tempDeviceIds);
                        } else {
                            List<Long> tempDeviceIds = new ArrayList<>();
                            tempDeviceIds.addAll(crossDeviceIds);
                            teamReportABDeviceIdsPair.put(report.getDeptId(), tempDeviceIds);
                        }
                    }
                }

                BigDecimal dailyFuel = report.getDailyFuel();
                dailyFuel = ObjUtil.isEmpty(dailyFuel) ? BigDecimal.ZERO : dailyFuel;
                if (ObjUtil.isNotEmpty(report.getDeptId()) && teamProjectIdPair.containsKey(report.getDeptId())) {
                    // projectDeptId可能是项目部 也可能是项目的上级
                    Long detailDeptId = 0l;
                    detailDeptId = report.getDeptId();
                    /* if (teamDeptPair.containsKey(report.getDeptId())) {
                        // 日报deptId如果是队伍 获取队伍的上级项目部
                        detailDeptId = teamProjectIdPair.get(report.getDeptId());
                    }
                    if (projectDeptPair.containsKey(report.getDeptId())) {
                        // 日报deptId如果是项目部 使用当前项目部
                        detailDeptId = report.getDeptId();
                    } */
                    if (ObjUtil.isNotEmpty(detailDeptId) && (detailDeptId > 0)) {
                        if (cumulativeFuelsPair.containsKey(detailDeptId)) {
                            BigDecimal existTotalFuel = cumulativeFuelsPair.get(detailDeptId);
                            BigDecimal tempTotalFuel = existTotalFuel.add(dailyFuel);
                            cumulativeFuelsPair.put(detailDeptId, tempTotalFuel);
                        } else {
                            cumulativeFuelsPair.put(detailDeptId, dailyFuel);
                        }

                        // 设置每个任务的工作量数据  单位相同的工作量值作合并处理
                        List<IotTaskAttrModelProperty> taskAttrs = report.getExtProperty();
                        // 暂存不同单位的工作量属性值
                        BigDecimal tempTotalBridgePlug = BigDecimal.ZERO;               // 桥塞(个数)
                        BigDecimal tempTotalRunCount = BigDecimal.ZERO;                 // 趟数
                        BigDecimal tempTotalCumulativeWorkingWell = BigDecimal.ZERO;    // 井数
                        BigDecimal tempTotalHourCount = BigDecimal.ZERO;                // 小时H
                        BigDecimal tempTotalWaterVolume = BigDecimal.ZERO;              // 水方量(方)
                        BigDecimal tempTotalPumpTrips = BigDecimal.ZERO;                // 泵车 台次
                        BigDecimal tempTotalCumulativeWorkingLayers = BigDecimal.ZERO;  // 段数 累计施工-层
                        BigDecimal tempTotalMixSand = BigDecimal.ZERO;                  // 台次 当日仪表/混砂
                        if (CollUtil.isNotEmpty(taskAttrs)) {
                            for (IotTaskAttrModelProperty attr : taskAttrs) {
                                String unit = attr.getUnit();   // 工作量单位
                                String actualValueStr = attr.getActualValue();  // 工作量属性的实际值
                                // 处理实际值：避免null或非数字字符串导致的异常
                                BigDecimal actualValue = BigDecimal.ZERO;
                                if (StrUtil.isNotBlank(actualValueStr)) {
                                    try {
                                        actualValue = new BigDecimal(actualValueStr);
                                    } catch (NumberFormatException e) {
                                        // 若字符串格式错误，默认按0处理（可根据业务调整）
                                        actualValue = BigDecimal.ZERO;
                                    }
                                }
                                if ("个数".equals(unit)) {
                                    // 钻可溶桥塞  钻复合桥塞
                                    tempTotalBridgePlug = tempTotalBridgePlug.add(actualValue);
                                    if (cumulativeBridgePlugPair.containsKey(detailDeptId)) {
                                        BigDecimal tempBridgePlug = cumulativeBridgePlugPair.get(detailDeptId);
                                        cumulativeBridgePlugPair.put(detailDeptId, tempTotalBridgePlug.add(tempBridgePlug));
                                    } else {
                                        cumulativeBridgePlugPair.put(detailDeptId, tempTotalBridgePlug);
                                    }
                                }
                                if ("趟数".equals(unit)) {
                                    tempTotalRunCount = tempTotalRunCount.add(actualValue);
                                    if (cumulativeRunCountPair.containsKey(detailDeptId)) {
                                        BigDecimal tempRunCount = cumulativeRunCountPair.get(detailDeptId);
                                        cumulativeRunCountPair.put(detailDeptId, tempTotalRunCount.add(tempRunCount));
                                    } else {
                                        cumulativeRunCountPair.put(detailDeptId, tempTotalRunCount);
                                    }
                                }
                                if ("小时".equals(unit)) {
                                    tempTotalHourCount = tempTotalHourCount.add(actualValue);
                                    if (cumulativeHourCountPair.containsKey(detailDeptId)) {
                                        BigDecimal tempHourCount = cumulativeHourCountPair.get(detailDeptId);
                                        cumulativeHourCountPair.put(detailDeptId, tempTotalHourCount.add(tempHourCount));
                                    } else {
                                        cumulativeHourCountPair.put(detailDeptId, tempTotalHourCount);
                                    }
                                }
                                if ("天数".equals(unit)) {
                                    // 将 actualValue 换算成 H
                                    BigDecimal hours = actualValue.multiply(new BigDecimal("24"));
                                    tempTotalHourCount = tempTotalHourCount.add(hours);
                                    if (cumulativeHourCountPair.containsKey(detailDeptId)) {
                                        BigDecimal tempHourCount = cumulativeHourCountPair.get(detailDeptId);
                                        cumulativeHourCountPair.put(detailDeptId, tempTotalHourCount.add(tempHourCount));
                                    } else {
                                        cumulativeHourCountPair.put(detailDeptId, tempTotalHourCount);
                                    }
                                }
                                if ("方".equals(unit)) {
                                    tempTotalWaterVolume = tempTotalWaterVolume.add(actualValue);
                                    if (cumulativeWaterVolumePair.containsKey(detailDeptId)) {
                                        BigDecimal tempWaterVolume = cumulativeWaterVolumePair.get(detailDeptId);
                                        cumulativeWaterVolumePair.put(detailDeptId, tempTotalWaterVolume.add(tempWaterVolume));
                                    } else {
                                        cumulativeWaterVolumePair.put(detailDeptId, tempTotalWaterVolume);
                                    }
                                }
                                if ("井数".equals(unit)) {
                                    tempTotalCumulativeWorkingWell = tempTotalCumulativeWorkingWell.add(actualValue);
                                    if (cumulativeWorkingWellPair.containsKey(detailDeptId)) {
                                        BigDecimal tempWorkingWell = cumulativeWorkingWellPair.get(detailDeptId);
                                        cumulativeWorkingWellPair.put(detailDeptId, tempTotalCumulativeWorkingWell.add(tempWorkingWell));
                                    } else {
                                        cumulativeWorkingWellPair.put(detailDeptId, tempTotalCumulativeWorkingWell);
                                    }
                                }
                                if ("段数".equals(unit)) {
                                    // 累计施工层
                                    tempTotalCumulativeWorkingLayers = tempTotalCumulativeWorkingLayers.add(actualValue);
                                    if (cumulativeWorkingLayersPair.containsKey(detailDeptId)) {
                                        BigDecimal tempWorkingLayer = cumulativeWorkingLayersPair.get(detailDeptId);
                                        cumulativeWorkingLayersPair.put(detailDeptId, tempTotalCumulativeWorkingLayers.add(tempWorkingLayer));
                                    } else {
                                        cumulativeWorkingLayersPair.put(detailDeptId, tempTotalCumulativeWorkingLayers);
                                    }
                                }
                                if ("台次".equals(unit) && "当日泵车台次".equals(attr.getName())) {
                                    tempTotalPumpTrips = tempTotalPumpTrips.add(actualValue);
                                    if (cumulativePumpTripsPair.containsKey(detailDeptId)) {
                                        BigDecimal tempPumpTrips = cumulativePumpTripsPair.get(detailDeptId);
                                        cumulativePumpTripsPair.put(detailDeptId, tempTotalPumpTrips.add(tempPumpTrips));
                                    } else {
                                        cumulativePumpTripsPair.put(detailDeptId, tempTotalPumpTrips);
                                    }
                                }
                                if ("台次".equals(unit) && ("当日仪表/混砂".equals(attr.getName())
                                        || "当日混砂".equals(attr.getName()) || "当日仪表".equals(attr.getName()))) {
                                    tempTotalMixSand = tempTotalMixSand.add(actualValue);
                                    if (cumulativeMixSandPair.containsKey(detailDeptId)) {
                                        BigDecimal tempMixSand = cumulativeMixSandPair.get(detailDeptId);
                                        cumulativeMixSandPair.put(detailDeptId, actualValue.add(tempMixSand));
                                    } else {
                                        cumulativeMixSandPair.put(detailDeptId, tempTotalMixSand);
                                    }
                                }
                            }
                        }
                    }
                }
            });
            // 计算每个队伍的设备利用率 (日报中施工的主设备数量) / (主设备总数量)*N天
            if (CollUtil.isNotEmpty(teamABDeviceIdPair)) {
                teamABDeviceIdPair.forEach((teamDeptId, deviceIds) -> {
                    // 队伍下所有AB类设备
                    Integer projectDeviceCount = deviceIds.size();
                    // 队伍日报中施工的AB类设备
                    Integer reportDeviceCount = 0;
                    if (teamReportABDeviceIdsPair.containsKey(teamDeptId)) {
                        List<Long> reportDeviceIds = teamReportABDeviceIdsPair.get(teamDeptId);
                        reportDeviceCount = reportDeviceIds.size();
                    }
                    double rate = 0.0;
                    // 计算设备利用率（处理除数为0的情况）
                    if (projectDeviceCount > 0 && reportDeviceCount > 0 && CollUtil.isNotEmpty(daysCounts)) {
                        rate = new BigDecimal((double) reportDeviceCount / (projectDeviceCount * daysCounts.get(0)))
                                .setScale(4, RoundingMode.HALF_UP)  // 保留4位小数，四舍五入
                                .doubleValue();;
                    } else {
                        rate = 0;
                    }
                    utilizationRatePair.put(teamDeptId, BigDecimal.valueOf(rate));
                });
            }
        }

        // 队伍数据  生成返回的数据列表集合
        teamDeptPair.forEach((teamDeptId, dept) -> {
            IotRdDailyReportStatisticsRespVO statistics = new IotRdDailyReportStatisticsRespVO();
            statistics.setTeamId(teamDeptId);
            statistics.setTeamName(dept.getName());
            statistics.setSort(dept.getSort());
            if (teamDeptPair.containsKey(teamDeptId)) {
                statistics.setType("3");    // 最好是 项目部小队同时展示 因为有的瑞都日报挂在了项目部下
            }
            statistics.setUtilizationRate(utilizationRatePair.get(teamDeptId));
            statistics.setCumulativeBridgePlug(cumulativeBridgePlugPair.get(teamDeptId));
            statistics.setCumulativeRunCount(cumulativeRunCountPair.get(teamDeptId));
            statistics.setCumulativeWorkingWell(cumulativeWorkingWellPair.get(teamDeptId));
            statistics.setCumulativeHourCount(cumulativeHourCountPair.get(teamDeptId));
            statistics.setCumulativeWaterVolume(cumulativeWaterVolumePair.get(teamDeptId));
            statistics.setCumulativeWorkingLayers(cumulativeWorkingLayersPair.get(teamDeptId));
            statistics.setCumulativeMixSand(cumulativeMixSandPair.get(teamDeptId));
            statistics.setCumulativePumpTrips(cumulativePumpTripsPair.get(teamDeptId));
            statistics.setTotalDailyFuel(cumulativeFuelsPair.get(teamDeptId));
            result.add(statistics);
        });

        return result;
    }

    @Override
    public List<IotRdDailyReportPolylineVO> polylineStatistics(IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportPolylineVO> result = new ArrayList<>();
        // 查询折线趋势图必须 传递时间参数
        /* if (ObjUtil.isEmpty(pageReqVO.getCreateTime())) {
            throw exception(IOT_DAILY_REPORT_TIME_NOT_EXISTS);
        } */
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            // 找到所有子部门对象集合
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        // 检查contractName不为空但projectIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getContractName()) && (CollUtil.isEmpty(pageReqVO.getProjectIds()))) {
            return new ArrayList<>();
        }
        // 检查taskName不为空但taskIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getTaskName()) && (CollUtil.isEmpty(pageReqVO.getTaskIds()))) {
            return new ArrayList<>();
        }
        // 筛选审核通过的数据
        // pageReqVO.setAuditStatus(20);
        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.dailyReports(pageReqVO);
        if (CollUtil.isNotEmpty(dailyReports)) {
            Long rootDeptId = 163l;
            if (ObjUtil.isEmpty(pageReqVO.getDeptId())) {
                pageReqVO.setDeptId(rootDeptId);
            }
            result = polylineStatisticsByProjectDept(dailyReports, pageReqVO.getDeptId(), pageReqVO.getCreateTime());
        }
        return result;
    }

    @Override
    public List<IotRdDailyReportPolylineVO> workloadKanban(IotRdDailyReportPageReqVO pageReqVO) {
        // 看板 工作量折线图 以公司为维度统计 不区分项目部 施工队伍
        List<IotRdDailyReportPolylineVO> result = new ArrayList<>();

        Set<Long> ids = new HashSet<>();
        ids = deptService.getChildDeptIdListFromCache(163l);
        // 找到所有子部门对象集合
        ids.add(163l);
        pageReqVO.setDeptIds(ids);
        // 筛选审核通过的数据
        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.dailyReports(pageReqVO);
        if (CollUtil.isNotEmpty(dailyReports)) {
            result = polylineStatisticsByCompany(dailyReports, pageReqVO, pageReqVO.getCreateTime());
        }
        return result;
    }

    /**
     * 按项 日期 维度统计 折线 工作量 数据
     * @param dailyReports 日报数据列表
     * @param rootDeptId 部门ID（如163L为瑞恒根部门，或其他项目部 ID）
     * @return
     */
    private List<IotRdDailyReportPolylineVO> polylineStatisticsByProjectDept(List<IotRdDailyReportDO> dailyReports,
                                                                             Long rootDeptId, LocalDateTime[] createTimes) {
        List<IotRdDailyReportPolylineVO> 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日期yyyy-MM-dd   value 累计 桥塞(个数)
        Map<String, BigDecimal> dateBridgePlugPair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 趟数
        Map<String, BigDecimal> dateRunCountPair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 井数
        Map<String, BigDecimal> dateWorkingWellPair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 小时H
        Map<String, BigDecimal> dateHourCountPair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 水方量(方)
        Map<String, BigDecimal> dateWaterVolumePair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 台次 泵车
        Map<String, BigDecimal> datePumpTripsPair = new HashMap<>();
        // key日期yyyy-MM-dd  value 累计 段数 累计施工-层
        Map<String, BigDecimal> dateWorkingLayersPair = new HashMap<>();
        // key日期yyyy-MM-dd  value 累计 台次 当日仪表/混砂
        Map<String, BigDecimal> dateMixSandPair = new HashMap<>();
        // key日期yyyy-MM-dd  value 累计 油耗
        Map<String, BigDecimal> dateFuelsPair = new HashMap<>();
        // key日期yyyy-MM-dd  value 累计的压裂连油主设备id列表
        Map<String, List<Long>> dateDeviceIdsPair = new HashMap<>();

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

        // 构建项目部映射和父子部门关系
        depts.forEach(dept -> {
            if ("2".equals(dept.getType())) {
                projectDeptIds.add(dept.getId());
                projectDeptPair.put(dept.getId(), dept);
            }
            teamProjectIdPair.put(dept.getId(), dept.getParentId());
        });

        Set<Long> mainDeviceIds = new HashSet<>();
        // 瑞都 压裂 连油 主设备
        IotDeviceAssociatePageReqVO deviceAssocReqVO = new IotDeviceAssociatePageReqVO();
        deviceAssocReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        deviceAssocReqVO.setDeptIds(allRhChildDeptIds);
        PageResult<IotDeviceAssociateDO> pageResult = iotDeviceAssociateMapper.selectPage(deviceAssocReqVO);
        if (ObjUtil.isNotEmpty(pageResult)) {
            List<IotDeviceAssociateDO> deviceAssociates = pageResult.getList();
            // 查询出选择部门内 瑞都压裂 连油主设备
            deviceAssociates.forEach(assoc -> {
                mainDeviceIds.add(assoc.getDeviceId());
            });
        }

        // 累计计算各项指标
        if (CollUtil.isNotEmpty(dailyReports)) {
            dailyReports.forEach(report -> {
                BigDecimal dailyFuel = report.getDailyFuel();
                dailyFuel = ObjUtil.isEmpty(dailyFuel) ? BigDecimal.ZERO : dailyFuel;

                // 按照日期维度统计各 工作量数据 累计用电量 累计油耗 累计注气量 累计注水量
                LocalDateTime reportLocalDate = report.getCreateTime();
                // 将日期格式转换成 字符串
                String reportDateStr = LocalDateTimeUtil.format(reportLocalDate, DatePattern.NORM_DATE_PATTERN);
                if (dateFuelsPair.containsKey(reportDateStr)) {
                    BigDecimal existTotalFuel = dateFuelsPair.get(reportDateStr);
                    BigDecimal tempTotalFuel = existTotalFuel.add(dailyFuel);
                    dateFuelsPair.put(reportDateStr, tempTotalFuel);
                } else {
                    dateFuelsPair.put(reportDateStr, dailyFuel);
                }

                // 计算查询时间区间内每天的设备利用率 组装每天内所有队伍使用到的 压裂 连油 主设备列表
                Set<Long> currentReportDeviceIds = report.getDeviceIds();
                if (CollUtil.isNotEmpty(currentReportDeviceIds)) {
                    // 1. 求交集：实际使用的A B类设备
                    Set<Long> utilizedDeviceIds = currentReportDeviceIds.stream()
                            .filter(mainDeviceIds::contains)
                            .collect(Collectors.toSet());
                    if (dateDeviceIdsPair.containsKey(reportDateStr)) {
                        List<Long> tempDeviceIds = dateDeviceIdsPair.get(reportDateStr);
                        tempDeviceIds.addAll(new ArrayList<>(utilizedDeviceIds));
                        dateDeviceIdsPair.put(reportDateStr, tempDeviceIds);
                    } else {
                        List<Long> tempDeviceIds = new ArrayList<>();
                        tempDeviceIds.addAll(new ArrayList<>(utilizedDeviceIds));
                        dateDeviceIdsPair.put(reportDateStr, tempDeviceIds);
                    }
                }

                // 设置每个任务的工作量数据  单位相同的工作量值作合并处理
                List<IotTaskAttrModelProperty> taskAttrs = report.getExtProperty();
                // 暂存不同单位的工作量属性值
                BigDecimal tempTotalBridgePlug = BigDecimal.ZERO;               // 桥塞(个数)
                BigDecimal tempTotalRunCount = BigDecimal.ZERO;                 // 趟数
                BigDecimal tempTotalCumulativeWorkingWell = BigDecimal.ZERO;    // 井数
                BigDecimal tempTotalHourCount = BigDecimal.ZERO;                // 小时H
                BigDecimal tempTotalWaterVolume = BigDecimal.ZERO;              // 水方量(方)
                BigDecimal tempTotalPumpTrips = BigDecimal.ZERO;                // 泵车 台次
                BigDecimal tempTotalCumulativeWorkingLayers = BigDecimal.ZERO;  // 段数 累计施工-层
                BigDecimal tempTotalMixSand = BigDecimal.ZERO;                  // 台次 当日仪表/混砂

                if (CollUtil.isNotEmpty(taskAttrs)) {
                    for (IotTaskAttrModelProperty attr : taskAttrs) {
                        String unit = attr.getUnit();   // 工作量单位
                        String actualValueStr = attr.getActualValue();  // 工作量属性的实际值
                        // 处理实际值：避免null或非数字字符串导致的异常
                        BigDecimal actualValue = BigDecimal.ZERO;
                        if (StrUtil.isNotBlank(actualValueStr)) {  // 假设使用Hutool的StrUtil，或自行判断null/空
                            try {
                                actualValue = new BigDecimal(actualValueStr);
                            } catch (NumberFormatException e) {
                                // 若字符串格式错误，默认按0处理（可根据业务调整）
                                actualValue = BigDecimal.ZERO;
                            }
                        }
                        if ("个数".equals(unit)) {
                            // 钻可溶桥塞  钻复合桥塞
                            tempTotalBridgePlug = tempTotalBridgePlug.add(actualValue);
                            if (dateBridgePlugPair.containsKey(reportDateStr)) {
                                BigDecimal tempBridgePlug = dateBridgePlugPair.get(reportDateStr);
                                dateBridgePlugPair.put(reportDateStr, tempTotalBridgePlug.add(tempBridgePlug));
                            } else {
                                dateBridgePlugPair.put(reportDateStr, tempTotalBridgePlug);
                            }
                        }
                        if ("趟数".equals(unit)) {
                            tempTotalRunCount = tempTotalRunCount.add(actualValue);
                            if (dateRunCountPair.containsKey(reportDateStr)) {
                                BigDecimal tempRunCount = dateRunCountPair.get(reportDateStr);
                                dateRunCountPair.put(reportDateStr, tempTotalRunCount.add(tempRunCount));
                            } else {
                                dateRunCountPair.put(reportDateStr, tempTotalRunCount);
                            }
                        }
                        if ("小时".equals(unit)) {
                            tempTotalHourCount = tempTotalHourCount.add(actualValue);
                            if (dateHourCountPair.containsKey(reportDateStr)) {
                                BigDecimal tempHourCount = dateHourCountPair.get(reportDateStr);
                                dateHourCountPair.put(reportDateStr, tempTotalHourCount.add(tempHourCount));
                            } else {
                                dateHourCountPair.put(reportDateStr, tempTotalHourCount);
                            }
                        }
                        if ("天数".equals(unit)) {
                            // 将 actualValue 换算成 H
                            BigDecimal hours = actualValue.multiply(new BigDecimal("24"));
                            tempTotalHourCount = tempTotalHourCount.add(hours);
                            if (dateHourCountPair.containsKey(reportDateStr)) {
                                BigDecimal tempHourCount = dateHourCountPair.get(reportDateStr);
                                dateHourCountPair.put(reportDateStr, tempTotalHourCount.add(tempHourCount));
                            } else {
                                dateHourCountPair.put(reportDateStr, tempTotalHourCount);
                            }
                        }
                        if ("方".equals(unit)) {
                            tempTotalWaterVolume = tempTotalWaterVolume.add(actualValue);
                            if (dateWaterVolumePair.containsKey(reportDateStr)) {
                                BigDecimal tempWaterVolume = dateWaterVolumePair.get(reportDateStr);
                                dateWaterVolumePair.put(reportDateStr, tempTotalWaterVolume.add(tempWaterVolume));
                            } else {
                                dateWaterVolumePair.put(reportDateStr, tempTotalWaterVolume);
                            }
                        }
                        if ("井数".equals(unit)) {
                            tempTotalCumulativeWorkingWell = tempTotalCumulativeWorkingWell.add(actualValue);
                            if (dateWorkingWellPair.containsKey(reportDateStr)) {
                                BigDecimal tempWorkingWell = dateWorkingWellPair.get(reportDateStr);
                                dateWorkingWellPair.put(reportDateStr, tempTotalCumulativeWorkingWell.add(tempWorkingWell));
                            } else {
                                dateWorkingWellPair.put(reportDateStr, tempTotalCumulativeWorkingWell);
                            }
                        }
                        if ("段数".equals(unit)) {
                            // 累计施工层
                            tempTotalCumulativeWorkingLayers = tempTotalCumulativeWorkingLayers.add(actualValue);
                            if (dateWorkingLayersPair.containsKey(reportDateStr)) {
                                BigDecimal tempWorkingLayer = dateWorkingLayersPair.get(reportDateStr);
                                dateWorkingLayersPair.put(reportDateStr, tempTotalCumulativeWorkingLayers.add(tempWorkingLayer));
                            } else {
                                dateWorkingLayersPair.put(reportDateStr, tempTotalCumulativeWorkingLayers);
                            }
                        }
                        if ("台次".equals(unit) && "当日泵车台次".equals(attr.getName())) {
                            tempTotalPumpTrips = tempTotalPumpTrips.add(actualValue);
                            if (datePumpTripsPair.containsKey(reportDateStr)) {
                                BigDecimal tempPumpTrips = datePumpTripsPair.get(reportDateStr);
                                datePumpTripsPair.put(reportDateStr, tempTotalPumpTrips.add(tempPumpTrips));
                            } else {
                                datePumpTripsPair.put(reportDateStr, tempTotalPumpTrips);
                            }
                        }
                        if ("台次".equals(unit) && ("当日仪表/混砂".equals(attr.getName())
                                || "当日混砂".equals(attr.getName()) || "当日仪表".equals(attr.getName()))) {
                            tempTotalMixSand = tempTotalMixSand.add(actualValue);
                            if (dateMixSandPair.containsKey(reportDateStr)) {
                                BigDecimal tempMixSand = dateMixSandPair.get(reportDateStr);
                                dateMixSandPair.put(reportDateStr, actualValue.add(tempMixSand));
                            } else {
                                dateMixSandPair.put(reportDateStr, tempTotalMixSand);
                            }
                        }
                    }
                }
            });
        }

        // 新增：日期字符串集合
        List<String> dateRangeList = new ArrayList<>();

        if (ObjUtil.isNotEmpty(createTimes) && createTimes.length >= 2) {
            LocalDate startDate = createTimes[0].toLocalDate();
            LocalDate endDate = createTimes[1].toLocalDate();
            // 生成从起始日期到结束日期的所有日期
            for (LocalDate date = startDate;
                 !date.isAfter(endDate);  // 使用 isAfter 而不是 <= 判断
                 date = date.plusDays(1)) {
                dateRangeList.add(date.format(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));
            }
        }

        if (CollUtil.isNotEmpty(dateRangeList)) {
            dateRangeList.forEach(date -> {
                IotRdDailyReportPolylineVO statistics = new IotRdDailyReportPolylineVO();
                statistics.setReportDate(date);
                statistics.setCumulativeBridgePlug(dateBridgePlugPair.get(date));
                statistics.setCumulativeRunCount(dateRunCountPair.get(date));
                statistics.setCumulativeWorkingWell(dateWorkingWellPair.get(date));
                statistics.setCumulativeHourCount(dateHourCountPair.get(date));
                statistics.setCumulativeFuels(dateFuelsPair.get(date));
                statistics.setCumulativeWaterVolume(dateWaterVolumePair.get(date));
                statistics.setCumulativeWorkingLayers(dateWorkingLayersPair.get(date));
                BigDecimal taici = BigDecimal.ZERO;
                // 合并 仪表混砂 泵车 的台次
                if (CollUtil.isNotEmpty(dateMixSandPair)) {
                    BigDecimal mixSand = dateMixSandPair.get(date);
                    mixSand = ObjUtil.isNotEmpty(mixSand) ? mixSand : BigDecimal.ZERO;
                    taici = taici.add(mixSand);
                }
                if (CollUtil.isNotEmpty(datePumpTripsPair)) {
                    BigDecimal pumpTrips = datePumpTripsPair.get(date);
                    pumpTrips = ObjUtil.isNotEmpty(pumpTrips) ? pumpTrips : BigDecimal.ZERO;
                    taici = taici.add(pumpTrips);
                }
                statistics.setCumulativeMixSand(dateMixSandPair.get(date));
                statistics.setCumulativePumpTrips(datePumpTripsPair.get(date));
                statistics.setTaici(taici);
                // 计算查询周期内每天的设备利用率
                if (dateDeviceIdsPair.containsKey(date)) {
                    List<Long> dateDeviceIds = dateDeviceIdsPair.get(date);
                    Integer dateDeviceIdCount = dateDeviceIds.size();
                    Integer mainDeviceIdCount = mainDeviceIds.size();
                    BigDecimal rate = BigDecimal.ZERO;
                    if (mainDeviceIdCount > 0) {
                        BigDecimal total = new BigDecimal(mainDeviceIdCount);
                        BigDecimal current = new BigDecimal(dateDeviceIdCount);
                        rate = current.divide(total, 4, RoundingMode.HALF_UP);
                    }
                    statistics.setUtilizationRate(rate);
                }

                result.add(statistics);
            });
        }
        return result;
    }

    /**
     * 瑞都 看板 日报统计折线图 以公司为维度 不区分 项目部 队伍
     * @param dailyReports 日报数据列表
     * @param pageReqVO 瑞都 163 timeType month quarter year
     * @return
     */
    private List<IotRdDailyReportPolylineVO> polylineStatisticsByCompany(List<IotRdDailyReportDO> dailyReports,
                                                                         IotRdDailyReportPageReqVO pageReqVO, LocalDateTime[] createTimes) {
        List<IotRdDailyReportPolylineVO> 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日期yyyy-MM-dd   value 累计 桥塞(个数)
        Map<String, BigDecimal> dateBridgePlugPair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 趟数
        Map<String, BigDecimal> dateRunCountPair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 井数
        Map<String, BigDecimal> dateWorkingWellPair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 小时H
        Map<String, BigDecimal> dateHourCountPair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 水方量(方)
        Map<String, BigDecimal> dateWaterVolumePair = new HashMap<>();
        // key日期yyyy-MM-dd   value 累计 台次 泵车
        Map<String, BigDecimal> datePumpTripsPair = new HashMap<>();
        // key日期MM  value 累计 压裂 段数 累计施工-层
        Map<String, BigDecimal> dateWorkingLayersPair = new HashMap<>();
        // key日期yyyy-MM-dd  value 累计 台次 当日仪表/混砂
        Map<String, BigDecimal> dateMixSandPair = new HashMap<>();
        // key日期yyyy-MM-dd  value 累计 油耗
        Map<String, BigDecimal> dateFuelsPair = new HashMap<>();
        // key日期MM  value 连油井数
        Map<String, Set<Long>> lyWellsPair = new HashMap<>();
        // key日期MM  value 压裂井数
        Map<String, Set<Long>> ylWellsPair = new HashMap<>();

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

        Set<Long> lyTeamIds = new HashSet<>();
        Set<Long> ylTeamIds = new HashSet<>();
        // 构建项目部映射和父子部门关系
        depts.forEach(dept -> {
            if ("2".equals(dept.getType())) {
                projectDeptIds.add(dept.getId());
                projectDeptPair.put(dept.getId(), dept);
            }
            teamProjectIdPair.put(dept.getId(), dept.getParentId());
            // 筛选出 连油队伍 压裂队伍
            if ("LY".equals(dept.getEmail())) {
                lyTeamIds.add(dept.getId());
            }
            if ("YL".equals(dept.getEmail())) {
                ylTeamIds.add(dept.getId());
            }
        });

        // 累计计算各项指标
        if (CollUtil.isNotEmpty(dailyReports)) {
            dailyReports.forEach(report -> {
                BigDecimal dailyFuel = report.getDailyFuel();
                dailyFuel = ObjUtil.isEmpty(dailyFuel) ? BigDecimal.ZERO : dailyFuel;

                // 按照日期维度统计各 工作量数据 累计用电量 累计油耗 累计注气量 累计注水量
                LocalDateTime reportLocalDate = report.getCreateTime();
                // 获取月份 (1-12)
                int monthValue = reportLocalDate.getMonthValue();
                String reportDateStr = monthValue + "月";
                // 将日期格式转换成 字符串
                // String reportDateStr = LocalDateTimeUtil.format(reportLocalDate, DatePattern.NORM_DATE_PATTERN);
                if (dateFuelsPair.containsKey(reportDateStr)) {
                    BigDecimal existTotalFuel = dateFuelsPair.get(reportDateStr);
                    BigDecimal tempTotalFuel = existTotalFuel.add(dailyFuel);
                    dateFuelsPair.put(reportDateStr, tempTotalFuel);
                } else {
                    dateFuelsPair.put(reportDateStr, dailyFuel);
                }

                // 设置每个任务的工作量数据  单位相同的工作量值作合并处理
                List<IotTaskAttrModelProperty> taskAttrs = report.getExtProperty();
                // 暂存不同单位的工作量属性值
                BigDecimal tempTotalBridgePlug = BigDecimal.ZERO;               // 桥塞(个数)
                BigDecimal tempTotalRunCount = BigDecimal.ZERO;                 // 趟数
                BigDecimal tempTotalCumulativeWorkingWell = BigDecimal.ZERO;    // 井数
                BigDecimal tempTotalHourCount = BigDecimal.ZERO;                // 小时H
                BigDecimal tempTotalWaterVolume = BigDecimal.ZERO;              // 水方量(方)
                BigDecimal tempTotalPumpTrips = BigDecimal.ZERO;                // 泵车 台次
                BigDecimal tempTotalCumulativeWorkingLayers = BigDecimal.ZERO;  // 段数 累计施工-层
                BigDecimal tempTotalMixSand = BigDecimal.ZERO;                  // 台次 当日仪表/混砂

                if (CollUtil.isNotEmpty(taskAttrs)) {
                    for (IotTaskAttrModelProperty attr : taskAttrs) {
                        String unit = attr.getUnit();   // 工作量单位
                        String actualValueStr = attr.getActualValue();  // 工作量属性的实际值
                        // 处理实际值：避免null或非数字字符串导致的异常
                        BigDecimal actualValue = BigDecimal.ZERO;
                        if (StrUtil.isNotBlank(actualValueStr)) {  // 假设使用Hutool的StrUtil，或自行判断null/空
                            try {
                                actualValue = new BigDecimal(actualValueStr);
                            } catch (NumberFormatException e) {
                                // 若字符串格式错误，默认按0处理（可根据业务调整）
                                actualValue = BigDecimal.ZERO;
                            }
                        }
                        if (lyTeamIds.contains(report.getDeptId())) {
                            // 连油井数
                            if (lyWellsPair.containsKey(reportDateStr)) {
                                Set<Long> lyWellIds = lyWellsPair.get(reportDateStr);
                                lyWellIds.add(report.getTaskId());
                                lyWellsPair.put(reportDateStr, lyWellIds);
                            } else {
                                Set<Long> lyWellIds = new HashSet<>();
                                lyWellIds.add(report.getTaskId());
                                lyWellsPair.put(reportDateStr, lyWellIds);
                            }
                        }
                        if (ylTeamIds.contains(report.getDeptId())) {
                            // 压裂井数
                            if (ylWellsPair.containsKey(reportDateStr)) {
                                Set<Long> ylWellIds = ylWellsPair.get(reportDateStr);
                                ylWellIds.add(report.getTaskId());
                                ylWellsPair.put(reportDateStr, ylWellIds);
                            } else {
                                Set<Long> ylWellIds = new HashSet<>();
                                ylWellIds.add(report.getTaskId());
                                ylWellsPair.put(reportDateStr, ylWellIds);
                            }
                        }
                        if ("个数".equals(unit)) {
                            // 钻可溶桥塞  钻复合桥塞
                            tempTotalBridgePlug = tempTotalBridgePlug.add(actualValue);
                            if (dateBridgePlugPair.containsKey(reportDateStr)) {
                                BigDecimal tempBridgePlug = dateBridgePlugPair.get(reportDateStr);
                                dateBridgePlugPair.put(reportDateStr, tempTotalBridgePlug.add(tempBridgePlug));
                            } else {
                                dateBridgePlugPair.put(reportDateStr, tempTotalBridgePlug);
                            }
                        }
                        if ("趟数".equals(unit)) {
                            tempTotalRunCount = tempTotalRunCount.add(actualValue);
                            if (dateRunCountPair.containsKey(reportDateStr)) {
                                BigDecimal tempRunCount = dateRunCountPair.get(reportDateStr);
                                dateRunCountPair.put(reportDateStr, tempTotalRunCount.add(tempRunCount));
                            } else {
                                dateRunCountPair.put(reportDateStr, tempTotalRunCount);
                            }
                        }
                        if ("小时".equals(unit)) {
                            tempTotalHourCount = tempTotalHourCount.add(actualValue);
                            if (dateHourCountPair.containsKey(reportDateStr)) {
                                BigDecimal tempHourCount = dateHourCountPair.get(reportDateStr);
                                dateHourCountPair.put(reportDateStr, tempTotalHourCount.add(tempHourCount));
                            } else {
                                dateHourCountPair.put(reportDateStr, tempTotalHourCount);
                            }
                        }
                        if ("天数".equals(unit)) {
                            // 将 actualValue 换算成 H
                            BigDecimal hours = actualValue.multiply(new BigDecimal("24"));
                            tempTotalHourCount = tempTotalHourCount.add(hours);
                            if (dateHourCountPair.containsKey(reportDateStr)) {
                                BigDecimal tempHourCount = dateHourCountPair.get(reportDateStr);
                                dateHourCountPair.put(reportDateStr, tempTotalHourCount.add(tempHourCount));
                            } else {
                                dateHourCountPair.put(reportDateStr, tempTotalHourCount);
                            }
                        }
                        if ("方".equals(unit)) {
                            tempTotalWaterVolume = tempTotalWaterVolume.add(actualValue);
                            if (dateWaterVolumePair.containsKey(reportDateStr)) {
                                BigDecimal tempWaterVolume = dateWaterVolumePair.get(reportDateStr);
                                dateWaterVolumePair.put(reportDateStr, tempTotalWaterVolume.add(tempWaterVolume));
                            } else {
                                dateWaterVolumePair.put(reportDateStr, tempTotalWaterVolume);
                            }
                        }
                        if ("井数".equals(unit)) {
                            tempTotalCumulativeWorkingWell = tempTotalCumulativeWorkingWell.add(actualValue);
                            if (dateWorkingWellPair.containsKey(reportDateStr)) {
                                BigDecimal tempWorkingWell = dateWorkingWellPair.get(reportDateStr);
                                dateWorkingWellPair.put(reportDateStr, tempTotalCumulativeWorkingWell.add(tempWorkingWell));
                            } else {
                                dateWorkingWellPair.put(reportDateStr, tempTotalCumulativeWorkingWell);
                            }
                        }
                        if ("段数".equals(unit) && ylTeamIds.contains(report.getDeptId())) {
                            // 压裂 累计施工层
                            tempTotalCumulativeWorkingLayers = tempTotalCumulativeWorkingLayers.add(actualValue);
                            if (dateWorkingLayersPair.containsKey(reportDateStr)) {
                                BigDecimal tempWorkingLayer = dateWorkingLayersPair.get(reportDateStr);
                                dateWorkingLayersPair.put(reportDateStr, tempTotalCumulativeWorkingLayers.add(tempWorkingLayer));
                            } else {
                                dateWorkingLayersPair.put(reportDateStr, tempTotalCumulativeWorkingLayers);
                            }
                        }
                        if ("台次".equals(unit) && "当日泵车台次".equals(attr.getName())) {
                            // 核心工作量 泵车台次
                            tempTotalPumpTrips = tempTotalPumpTrips.add(actualValue);
                            if (datePumpTripsPair.containsKey(reportDateStr)) {
                                BigDecimal tempPumpTrips = datePumpTripsPair.get(reportDateStr);
                                datePumpTripsPair.put(reportDateStr, tempTotalPumpTrips.add(tempPumpTrips));
                            } else {
                                datePumpTripsPair.put(reportDateStr, tempTotalPumpTrips);
                            }
                        }
                        if ("台次".equals(unit) && ("当日仪表/混砂".equals(attr.getName())
                                || "当日混砂".equals(attr.getName()) || "当日仪表".equals(attr.getName()))) {
                            tempTotalMixSand = tempTotalMixSand.add(actualValue);
                            if (dateMixSandPair.containsKey(reportDateStr)) {
                                BigDecimal tempMixSand = dateMixSandPair.get(reportDateStr);
                                dateMixSandPair.put(reportDateStr, actualValue.add(tempMixSand));
                            } else {
                                dateMixSandPair.put(reportDateStr, tempTotalMixSand);
                            }
                        }
                    }
                }
            });
        }

        // 日期字符串集合
        List<String> dateRangeList = new ArrayList<>();

        if (ObjUtil.isNotEmpty(createTimes) && createTimes.length >= 2) {
            // 取起始年份
            int year = createTimes[0].getYear();

            // 生成1-12月
            for (int month = 1; month <= 12; month++) {
                dateRangeList.add(month + "月");
            }
        }
        // 全年 展示每个月的数据
        if (CollUtil.isNotEmpty(dateRangeList)) {
            dateRangeList.forEach(date -> {
                IotRdDailyReportPolylineVO statistics = new IotRdDailyReportPolylineVO();
                statistics.setReportDate(date);
                statistics.setCumulativeBridgePlug(dateBridgePlugPair.get(date));
                statistics.setCumulativeRunCount(dateRunCountPair.get(date));
                statistics.setCumulativeWorkingWell(dateWorkingWellPair.get(date));
                statistics.setCumulativeHourCount(dateHourCountPair.get(date));
                statistics.setCumulativeFuels(dateFuelsPair.get(date));
                statistics.setCumulativeWaterVolume(dateWaterVolumePair.get(date));
                statistics.setCumulativeWorkingLayers(dateWorkingLayersPair.get(date));
                BigDecimal taici = BigDecimal.ZERO;
                // 合并 仪表混砂 泵车 的台次
                if (CollUtil.isNotEmpty(dateMixSandPair)) {
                    BigDecimal mixSand = dateMixSandPair.get(date);
                    mixSand = ObjUtil.isNotEmpty(mixSand) ? mixSand : BigDecimal.ZERO;
                    taici = taici.add(mixSand);
                }
                if (CollUtil.isNotEmpty(datePumpTripsPair)) {
                    BigDecimal pumpTrips = datePumpTripsPair.get(date);
                    pumpTrips = ObjUtil.isNotEmpty(pumpTrips) ? pumpTrips : BigDecimal.ZERO;
                    taici = taici.add(pumpTrips);
                }
                statistics.setCumulativeMixSand(dateMixSandPair.get(date));
                statistics.setCumulativePumpTrips(datePumpTripsPair.get(date));
                statistics.setTaici(taici);
                // 连油井数
                Set<Long> lyWellIds = lyWellsPair.get(date);
                statistics.setLyWellCount(CollUtil.isNotEmpty(lyWellIds) ? lyWellIds.size() : 0);
                // 压裂井数
                Set<Long> ylWellIds = ylWellsPair.get(date);
                statistics.setYlWellCount(CollUtil.isNotEmpty(ylWellIds) ? ylWellIds.size() : 0);
                result.add(statistics);
            });
        }
        return result;
    }

    @Override
    public void saveBatch(List<IotRdDailyReportSaveReqVO> reports) {
        if (CollUtil.isEmpty(reports)) {
            throw exception(IOT_RD_DAILY_REPORT_NOT_EXISTS);
        }
        // 需要发送提醒消息的日报对象
        List<IotRdDailyReportDO> tobeSendMsgReports = new ArrayList<>();
        // 主井日报
        List<IotRdDailyReportDO> mainPlatformReports = new ArrayList<>();
        // 关联井日报
        List<IotRdDailyReportDO> relatePlatformReports = new ArrayList<>();
        // 维护非生产时间标识 nonProduct
        AtomicReference<String> nonProduct = new AtomicReference<>(StrUtil.EMPTY);
        reports.forEach(report -> {
            if (StrUtil.isNotBlank(report.getNonProduct())) {
                nonProduct.set(report.getNonProduct());
            }
            IotRdDailyReportDO updatedReport = updateIotRdDailyReport(report);
            // 标识当前任务井是 普通井 或 平台井主井 关联的任务审批人发送提醒消息
            if (1 == updatedReport.getPlatformWell() || 0 == updatedReport.getPlatformWell()) {
                tobeSendMsgReports.add(updatedReport);
            }
            // 记录关联井集合
            if (2 == updatedReport.getPlatformWell()) {
                relatePlatformReports.add(updatedReport);
            }
            // 记录平台井主井集合
            if (1 == updatedReport.getPlatformWell()) {
                mainPlatformReports.add(updatedReport);
            }
        });

        // 兼容平台井任务 主井任务已经施工完成 但是关联井仍在施工的情况 此时 平台井 下拉列表中 选择不到主井
        // 此时应该同步修改主井任务的 填写状态 审批状态
        if (CollUtil.isNotEmpty(relatePlatformReports) && CollUtil.isEmpty(mainPlatformReports)) {
            // 找到关联井对应的主井 并把 主井 添加到 准备发送消息提醒的集合 tobeSendMsgReports
            IotRdDailyReportDO randomRelatedReport = relatePlatformReports.get(0);
            String platformGroup = randomRelatedReport.getPlatformGroup();
            // 根据 platformGroup 找到主井日报
            IotRdDailyReportPageReqVO reqVO = new IotRdDailyReportPageReqVO();
            reqVO.setPlatformGroup(platformGroup);
            reqVO.setPlatformWell(1);
            List<IotRdDailyReportDO> mainWellReports = iotRdDailyReportMapper.dailyReports(reqVO);
            if (CollUtil.isNotEmpty(mainWellReports)) {
                IotRdDailyReportDO mainWellReport = mainWellReports.get(0);
                if (StrUtil.isBlank(nonProduct.get())) {
                    // 不是在维护非生产时间
                    // 设置 日报审批状态为 审批中 auditStatus = 10
                    mainWellReport.setAuditStatus(10);
                    // 设置 日报填写状态 保存 已完成
                    // mainWellReport.setStatus(1);
                } else {
                    mainWellReport.setProcessInstanceId("2");
                    // 设置 日报填写状态 保存 已完成
                    mainWellReport.setStatus(1);
                }
                iotRdDailyReportMapper.updateById(mainWellReport);
            }
        }

        // 考虑到平台井的情况 只向有审批权限的人发送一次 钉钉提醒及站内信
        // 任务为平台井时 只向主井的任务的 填报人所属部门或上级项目部下 有审批权限的人 发送消息提醒
        // 维护 非生产时间 施工简报 时 不发送消息提醒
        if (CollUtil.isNotEmpty(tobeSendMsgReports) && StrUtil.isBlank(nonProduct.get())) {
            DataPermissionUtils.executeIgnore(() -> {
                Set<Long> deptIds = new HashSet<>();
                IotRdDailyReportDO tobeSendMsgReport = tobeSendMsgReports.get(0);
                Long deptId = tobeSendMsgReport.getDeptId();
                DeptDO dept = deptService.getDept(deptId);
                if (ObjUtil.isNotEmpty(dept)) {
                    deptIds.add(dept.getId());
                    String type = dept.getType();
                    // 类型(公司级1 项目部2 队伍3)
                    // 如果当前部门是 队伍 查找上级 项目部
                    if ("3".equals(type)) {
                        // 项目部
                        DeptDO projectDept = deptService.getDept(dept.getParentId());
                        if (ObjUtil.isNotEmpty(projectDept)) {
                            deptIds.add(projectDept.getId());
                            if ("2".equals(projectDept.getType())) {
                                // 查找 项目部 的上一级 通知 生产运营部人员
                                DeptDO companyDept = deptService.getDept(projectDept.getParentId());
                                if (ObjUtil.isNotEmpty(companyDept)) {
                                    deptIds.add(companyDept.getId());
                                }
                            }
                        }
                    }
                    // 查找当前部门及项目部下的人员
                    List<AdminUserDO> receivedMsgUsers = adminUserService.getUserListByDeptIds(deptIds);
                    if (CollUtil.isNotEmpty(receivedMsgUsers)) {
                        Set<Long> userIds = new HashSet<>();
                        receivedMsgUsers.forEach(user -> {
                            userIds.add(user.getId());
                        });
                        RoleDO role = roleService.getRoleByCode("项目部日报审批RD");
                        // 日报查阅 瑞都 日报抄送
                        RoleDO detailRole = roleService.getRoleByCode("日报查阅RD");
                        if (ObjUtil.isNotEmpty(role)) {
                            Set<Long> roleIds = new HashSet<>();
                            roleIds.add(role.getId());
                            List<UserRoleDO> userRoles = userRoleMapper.selectListByRoleIds(roleIds);
                            if (CollUtil.isNotEmpty(userRoles)) {
                                // 提取有审批角色的所有用户ID（去重+空值过滤）
                                Set<Long> roleUserIds = userRoles.stream()
                                        .map(UserRoleDO::getUserId)
                                        .filter(Objects::nonNull) // 过滤空userId
                                        .collect(Collectors.toSet());

                                // 计算两个集合的交集，得到最终目标用户ID
                                Set<Long> targetUserIds = userIds.stream()
                                        .filter(roleUserIds::contains)
                                        .collect(Collectors.toSet());

                                // 异步发送 站内信 钉钉消息
                                if (CollUtil.isNotEmpty(targetUserIds)) {
                                    Map<Long, AdminUserRespDTO> users = adminUserApi.getUserMap(targetUserIds);
                                    // 给多个用户发送 相同 的 提醒消息
                                    if (CollUtil.isNotEmpty(users)) {
                                        CountDownLatch latch = new CountDownLatch(users.size());
                                        users.forEach((userId, user) -> {
                                            pmsThreadPoolTaskExecutor.execute(() -> {
                                                try {
                                                    String mobile = user.getMobile();
                                                    if (StrUtil.isNotBlank(mobile) && StrUtil.isNotBlank(tobeSendMsgReport.getReportName())) {
                                                        pmsMessage.sendMessage(tobeSendMsgReport.getId(), tobeSendMsgReport.getReportName(), PmsConstants.DAILY_REPORT_APPROVAL,
                                                                userId, mobile);
                                                    }
                                                } finally {
                                                    latch.countDown();
                                                }
                                            });
                                        });
                                    }
                                }
                            }
                        }
                        // 给日报抄送人发送 4小时日报更新的 钉钉及站内信消息
                        if (ObjUtil.isNotEmpty(detailRole)) {
                            // 拥有日报查阅权限的人员列表
                            Set<Long> detailRoleIds = new HashSet<>();
                            detailRoleIds.add(detailRole.getId());
                            List<UserRoleDO> userRoles = userRoleMapper.selectListByRoleIds(detailRoleIds);
                            if (CollUtil.isNotEmpty(userRoles)) {
                                // 提取有审批角色的所有用户ID（去重+空值过滤）
                                Set<Long> roleUserIds = userRoles.stream()
                                        .map(UserRoleDO::getUserId)
                                        .filter(Objects::nonNull) // 过滤空userId
                                        .collect(Collectors.toSet());
                                // 计算两个集合的交集，得到最终目标用户ID
                                Set<Long> targetUserIds = userIds.stream()
                                        .filter(roleUserIds::contains)
                                        .collect(Collectors.toSet());
                                // 异步发送 站内信 钉钉消息
                                if (CollUtil.isNotEmpty(targetUserIds)) {
                                    Map<Long, AdminUserRespDTO> users = adminUserApi.getUserMap(targetUserIds);
                                    // 给多个用户发送 相同的 提醒消息
                                    if (CollUtil.isNotEmpty(users)) {
                                        CountDownLatch latch = new CountDownLatch(users.size());
                                        users.forEach((userId, user) -> {
                                            pmsThreadPoolTaskExecutor.execute(() -> {
                                                try {
                                                    String mobile = user.getMobile();
                                                    if (StrUtil.isNotBlank(mobile) && StrUtil.isNotBlank(tobeSendMsgReport.getReportName())) {
                                                        pmsMessage.sendMessage(tobeSendMsgReport.getId(), tobeSendMsgReport.getReportName(), PmsConstants.DAILY_REPORT_DETAIL,
                                                                userId, mobile);
                                                    }
                                                } finally {
                                                    latch.countDown();
                                                }
                                            });
                                        });
                                    }
                                }
                            }
                        }
                    }
                }
            });
        }
    }

    @Override
    public List<IotRdDailyReportDO> taskActualProgress(IotRdDailyReportPageReqVO reqVO) {
        if (ObjUtil.isEmpty(reqVO.getTaskId())) {
            throw exception(IOT_PROJECT_TASK_NOT_EXISTS);
        }
        // 根据 taskId 任务提取日报中的施工状态进度节点信息
        IotRdDailyReportPageReqVO reportReqVO = new IotRdDailyReportPageReqVO();
        reportReqVO.setTaskId(reqVO.getTaskId());
        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.selectListGrouped(reportReqVO);
        return dailyReports;
    }

    @Override
    public PageResult<IotRdDailyReportDO> teamReports(IotRdDailyReportPageReqVO pageReqVO) {
        // 查询选择部门下面所有子部门
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        // 检查contractName不为空但projectIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getContractName()) && (CollUtil.isEmpty(pageReqVO.getProjectIds()))) {
            return new PageResult<>(Collections.emptyList(), 0L);
        }
        // 检查taskName不为空但taskIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getTaskName()) && (CollUtil.isEmpty(pageReqVO.getTaskIds()))) {
            return new PageResult<>(Collections.emptyList(), 0L);
        }
        IPage<IotRdDailyReportDO> page = iotRdDailyReportMapper.rdWellNameDailyReports(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO,
                pageReqVO.getTaskIds(), pageReqVO.getProjectIds(), ids);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public List<IotRdDailyReportDO> rdDeptDailyReportsGroupIds(IotRdDailyReportPageReqVO pageReqVO) {
        // 查询选择部门下面所有子部门
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        List<IotRdDailyReportDO> groupIds = iotRdDailyReportMapper.rdDeptDailyReportsGroupIds(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO,
                pageReqVO.getTaskIds(), pageReqVO.getProjectIds(), ids);
        return groupIds;
    }

    @Override
    public List<IotRdDailyReportDO> rdWellNameDailyReportsGroupIds(IotRdDailyReportPageReqVO pageReqVO) {
        // 查询选择部门下面所有子部门
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        List<IotRdDailyReportDO> groupIds = iotRdDailyReportMapper.rdWellNameDailyReportsGroupIds(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO,
                pageReqVO.getTaskIds(), pageReqVO.getProjectIds(), ids);
        return groupIds;
    }

    @Override
    public PageResult<IotRdDailyReportDO> wellReports(IotRdDailyReportPageReqVO pageReqVO) {
        // 查询选择部门下面所有子部门
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        // 检查contractName不为空但projectIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getContractName()) && (CollUtil.isEmpty(pageReqVO.getProjectIds()))) {
            return new PageResult<>(Collections.emptyList(), 0L);
        }
        // 检查taskName不为空但taskIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getTaskName()) && (CollUtil.isEmpty(pageReqVO.getTaskIds()))) {
            return new PageResult<>(Collections.emptyList(), 0L);
        }
        IPage<IotRdDailyReportDO> page = iotRdDailyReportMapper.rdDeptDailyReports(
                new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO,
                pageReqVO.getTaskIds(), pageReqVO.getProjectIds(), ids);

        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    public List<IotRdDailyReportBriefRespVO> constructionBriefing(IotRdDailyReportPageReqVO pageReqVO) {
        // 瑞都施工简报 查询昨天 和前天的日报数据 井 队伍 项目部
        // 瑞都所有子部门
        Set<Long> ids = new HashSet<>();
        ids = deptService.getChildDeptIdListFromCache(163l);
        DeptListReqVO reqVO = new DeptListReqVO();
        reqVO.setDeptIds(ids);
        List<DeptDO> depts = deptService.getDeptList(reqVO);
        // 筛选队伍与项目部的对应关系
        // key部门id   value部门parentId
        Map<Long, Long> teamProjectIdPair = new HashMap<>();
        Set<Long> projectDeptIds = new HashSet<>();
        // key项目部id    value项目部名称
        Map<Long, DeptDO> projectDeptPair = new HashMap<>();
        // key队伍id    value队伍名称
        Map<Long, DeptDO> teamDeptPair = new HashMap<>();
        depts.forEach(dept -> {
            if ("2".equals(dept.getType())) {
                projectDeptIds.add(dept.getId());
                projectDeptPair.put(dept.getId(), dept);
            }
            if ("3".equals(dept.getType())) {
                teamDeptPair.put(dept.getId(), dept);
                teamProjectIdPair.put(dept.getId(), dept.getParentId());
            }
        });
        // 查询 昨天 和 前天 创建的日报
        LocalDateTime nowDate = LocalDateTime.now();
        LocalDateTime yesterday = nowDate.minusDays(1);
        LocalDateTime beforeYesterday = nowDate.minusDays(2);
        String yesterdayStr = LocalDateTimeUtil.format(yesterday, DatePattern.NORM_DATE_PATTERN);
        String beforeYesterdayStr = LocalDateTimeUtil.format(beforeYesterday, DatePattern.NORM_DATE_PATTERN);
        IotRdDailyReportPageReqVO reportReqVO = new IotRdDailyReportPageReqVO();
        // 查询昨天的数据
        LocalDateTime[] createTime = new LocalDateTime[2];
        createTime[0] = LocalDateTimeUtil.parse(StrUtil.join(" ", yesterdayStr, "00:00:00"), DatePattern.NORM_DATETIME_PATTERN);
        createTime[1] = LocalDateTimeUtil.parse(StrUtil.join(" ", yesterdayStr, "23:59:59"), DatePattern.NORM_DATETIME_PATTERN);
        reportReqVO.setCreateTime(createTime);
        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.dailyReports(reportReqVO);

        // 查询前天的日报数据
        createTime[0] = LocalDateTimeUtil.parse(StrUtil.join(" ", beforeYesterdayStr, "00:00:00"), DatePattern.NORM_DATETIME_PATTERN);
        createTime[1] = LocalDateTimeUtil.parse(StrUtil.join(" ", beforeYesterdayStr, "23:59:59"), DatePattern.NORM_DATETIME_PATTERN);
        reportReqVO.setCreateTime(createTime);
        List<IotRdDailyReportDO> beforeYesterdayDailyReports = iotRdDailyReportMapper.dailyReports(reportReqVO);

        // 包含昨天 前天的日报对应的任务id集合
        Set<Long> taskIds = new HashSet<>();
        Set<Long> deviceIds = new HashSet<>();
        Map<Long, String> taskWellPair = new HashMap<>();
        //  施工工艺 key字典键值     value字典标签
        Map<String, String> techniqueDictPair = new HashMap<>();
        //  施工状态 key字典键值     value字典标签
        Map<String, String> rdStatusDictPair = new HashMap<>();
        //  key设备id     value设备编码-名称
        Map<Long, String> deviceIdCodeNamePair = new HashMap<>();
        // 前天的日报 与 昨天日报通过 任务井 taskId 对应
        if (CollUtil.isNotEmpty(beforeYesterdayDailyReports)) {
            beforeYesterdayDailyReports.forEach(report -> {
                // 收集所有 日报对应的任务井
                taskIds.add(report.getTaskId());
                if (CollUtil.isNotEmpty(report.getDeviceIds())) {
                    deviceIds.addAll(report.getDeviceIds());
                }
            });
        }
        // 瑞都日报按井为维度生成 所以每个日报与井是一一对应的
        if (CollUtil.isNotEmpty(dailyReports)) {
            dailyReports.forEach(report -> {
                // 收集所有 日报对应的任务井
                taskIds.add(report.getTaskId());
                if (CollUtil.isNotEmpty(report.getDeviceIds())) {
                    deviceIds.addAll(report.getDeviceIds());
                }
            });
        }
        if (CollUtil.isNotEmpty(taskIds)) {
            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
            taskReqVO.setTaskIds(new ArrayList<>(taskIds));
            List<IotProjectTaskDO> tasks = iotProjectTaskMapper.selectList(taskReqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskWellPair.put(task.getId(), task.getWellName());
                });
            }
        }
        // 查询施工工艺字典数据
        List<DictDataDO> rdTechniques = dictDataService.getDictDataListByDictType("rq_iot_project_technology_rd");
        if (CollUtil.isNotEmpty(rdTechniques)) {
            rdTechniques.forEach(tech -> {
                techniqueDictPair.put(tech.getValue(), tech.getLabel());
            });
        }
        // 查询施工状态字典数据
        List<DictDataDO> rdStatuses = dictDataService.getDictDataListByDictType("rdStatus");
        if (CollUtil.isNotEmpty(rdStatuses)) {
            rdStatuses.forEach(tech -> {
                rdStatusDictPair.put(tech.getValue(), tech.getLabel());
            });
        }

        // 查询所有 设备信息
        IotDevicePageReqVO deviceReqVO = new IotDevicePageReqVO();
        deviceReqVO.setDeviceIds(new ArrayList<>(deviceIds));
        List<IotDeviceDO> devices = iotDeviceMapper.selectListAlone(deviceReqVO);
        if (CollUtil.isNotEmpty(devices)) {
            devices.forEach(device -> {
                deviceIdCodeNamePair.put(device.getId(), StrUtil.join("——", device.getDeviceCode(), device.getDeviceName()));
            });
        }
        // key任务井id   value施工状态
        Map<Long, String> beforeYesterdayStatus = new HashMap<>();
        // key任务井id   value前天的施工动态
        Map<Long, String> beforeYesterdayProduct = new HashMap<>();
        // 上个工作日的 工作量
        Map<Long, BigDecimal> beforeYestBridgePlugPair = new HashMap<>();
        Map<Long, BigDecimal> beforeYestRunCountPair = new HashMap<>();
        Map<Long, BigDecimal> beforeYestHourCountPair = new HashMap<>();
        Map<Long, BigDecimal> beforeYestWaterVolumePair = new HashMap<>();
        Map<Long, BigDecimal> beforeYestPumpTripsPair = new HashMap<>();
        Map<Long, BigDecimal> beforeYestLayersPair = new HashMap<>();
        Map<Long, BigDecimal> beforeYestMixSandPair = new HashMap<>();
        Map<Long, BigDecimal> beforeYestFuelPair = new HashMap<>();
        if (CollUtil.isNotEmpty(beforeYesterdayDailyReports)) {
            beforeYesterdayDailyReports.forEach(report -> {
                // 前天的日报施工数据  通过 任务井 与 昨天 的日报关联
                if (rdStatusDictPair.containsKey(report.getRdStatus())) {
                    beforeYesterdayStatus.put(report.getTaskId(), rdStatusDictPair.get(report.getRdStatus()));
                }
                beforeYesterdayProduct.put(report.getTaskId(), report.getProductionStatus());
                // 设置前天的工作量数据
                beforeYestFuelPair.put(report.getTaskId(), report.getDailyFuel());
                // 工作量属性集合
                List<IotTaskAttrModelProperty> workloadAttrs = report.getExtProperty();
                // 暂存不同单位的工作量属性值
                BigDecimal tempTotalBridgePlug = BigDecimal.ZERO;               // 桥塞(个数)
                BigDecimal tempTotalRunCount = BigDecimal.ZERO;                 // 趟数
                BigDecimal tempTotalCumulativeWorkingWell = BigDecimal.ZERO;    // 井数
                BigDecimal tempTotalHourCount = BigDecimal.ZERO;                // 小时H
                BigDecimal tempTotalWaterVolume = BigDecimal.ZERO;              // 水方量(方)
                BigDecimal tempTotalPumpTrips = BigDecimal.ZERO;
                BigDecimal tempTotalCumulativeWorkingLayers = BigDecimal.ZERO;  // 段数  累计施工-层
                BigDecimal tempTotalMixSand = BigDecimal.ZERO;                  // 台次 当日泵车台次 当日仪表/混砂
                if (CollUtil.isNotEmpty(workloadAttrs)) {
                    for (IotTaskAttrModelProperty attr : workloadAttrs) {
                        String unit = attr.getUnit();   // 工作量单位
                        String actualValueStr = attr.getActualValue();  // 工作量属性的实际值
                        // 处理实际值：避免null或非数字字符串导致的异常
                        BigDecimal actualValue = BigDecimal.ZERO;
                        if (StrUtil.isNotBlank(actualValueStr)) {  // 假设使用Hutool的StrUtil，或自行判断null/空
                            try {
                                actualValue = new BigDecimal(actualValueStr);
                            } catch (NumberFormatException e) {
                                // 若字符串格式错误，默认按0处理（可根据业务调整）
                                actualValue = BigDecimal.ZERO;
                            }
                            if ("个数".equals(unit)) {
                                tempTotalBridgePlug = tempTotalBridgePlug.add(actualValue);
                            }
                            if ("趟数".equals(unit)) {
                                tempTotalRunCount = tempTotalRunCount.add(actualValue);
                            }
                            if ("小时".equals(unit)) {
                                tempTotalHourCount = tempTotalHourCount.add(actualValue);
                            }
                            if ("天数".equals(unit)) {
                                // 将 actualValue 换算成 H
                                BigDecimal hours = actualValue.multiply(new BigDecimal("24"));
                                tempTotalHourCount = tempTotalHourCount.add(hours);
                            }
                            if ("方".equals(unit)) {
                                tempTotalWaterVolume = tempTotalWaterVolume.add(actualValue);
                            }
                            if ("段数".equals(unit)) {
                                // 累计施工层
                                tempTotalCumulativeWorkingLayers = tempTotalCumulativeWorkingLayers.add(actualValue);
                            }
                            if ("台次".equals(unit) && "当日泵车台次".equals(attr.getName())) {
                                tempTotalPumpTrips = tempTotalPumpTrips.add(actualValue);
                            }
                            if ("台次".equals(unit) && ("当日仪表/混砂".equals(attr.getName())
                                    || "当日混砂".equals(attr.getName()) || "当日仪表".equals(attr.getName()))) {
                                tempTotalMixSand = tempTotalMixSand.add(actualValue);
                            }
                        }
                    }
                }
                beforeYestBridgePlugPair.put(report.getTaskId(), tempTotalBridgePlug);
                beforeYestRunCountPair.put(report.getTaskId(), tempTotalRunCount);
                beforeYestHourCountPair.put(report.getTaskId(), tempTotalHourCount);
                beforeYestWaterVolumePair.put(report.getTaskId(), tempTotalWaterVolume);
                beforeYestLayersPair.put(report.getTaskId(), tempTotalCumulativeWorkingLayers);
                beforeYestPumpTripsPair.put(report.getTaskId(), tempTotalPumpTrips);
                beforeYestMixSandPair.put(report.getTaskId(), tempTotalMixSand);
            });
        }

        // key日报id  value日报设备
        Map<Long, String> reportDevicesPair = new HashMap<>();
        List<IotRdDailyReportBriefRespVO> reportBriefs = new ArrayList<>();
        if (CollUtil.isNotEmpty(dailyReports)) {
            dailyReports.forEach(report -> {
                // 设置每个日报对应的设备编码-名称 逗号分隔
                IotRdDailyReportBriefRespVO reportBrief = new IotRdDailyReportBriefRespVO();
                if (rdStatusDictPair.containsKey(report.getRdStatus())) {
                    reportBrief.setRdStatus(rdStatusDictPair.get(report.getRdStatus()));
                }
                if (teamDeptPair.containsKey(report.getDeptId())) {
                    reportBrief.setDeptName(teamDeptPair.get(report.getDeptId()).getName());
                }
                if (teamProjectIdPair.containsKey(report.getDeptId())) {
                    Long parentId = teamProjectIdPair.get(report.getDeptId());
                    if (projectDeptPair.containsKey(parentId)) {
                        reportBrief.setProjectDeptName(projectDeptPair.get(parentId).getName());
                    }
                }
                if (taskWellPair.containsKey(report.getTaskId())) {
                    reportBrief.setWellName(taskWellPair.get(report.getTaskId()));
                }
                // 设置每个 日报 的设备编码-名称 字符串
                Set<Long> thisDeviceIds = report.getDeviceIds();
                String thisDeviceNames = Optional.ofNullable(thisDeviceIds)
                        .filter(CollUtil::isNotEmpty)
                        .map(devIds -> devIds.stream()
                                .map(deviceIdCodeNamePair::get)
                                .filter(Objects::nonNull)
                                .filter(StrUtil::isNotBlank)
                                .collect(Collectors.joining(",")))
                        .orElse(""); // 如果为空，返回空字符串
                reportBrief.setDeviceNames(thisDeviceNames);
                Set<Long> thisTechniqueIds = report.getTechniqueIds();
                String thisTechniqueNames = Optional.ofNullable(thisTechniqueIds)
                        .filter(CollUtil::isNotEmpty)
                        .map(techniqueIds -> techniqueIds.stream()
                                .map(String::valueOf)  // 将 Long 转为 String
                                .map(techniqueDictPair::get)  // 从字典获取名称
                                .filter(Objects::nonNull)  // 过滤空值
                                .filter(StrUtil::isNotBlank)  // 过滤空白值
                                .collect(Collectors.joining(",")))  // 用逗号连接
                        .orElse("");  // 如果为空，返回空字符串

                reportBrief.setTechniques(thisTechniqueNames);
                reportBrief.setProductionBrief(report.getProductionStatus());
                reportBrief.setTotalDailyFuel(report.getDailyFuel());
                // 工作量属性集合
                List<IotTaskAttrModelProperty> workloadAttrs = report.getExtProperty();
                // 暂存不同单位的工作量属性值
                BigDecimal tempTotalBridgePlug = BigDecimal.ZERO;               // 桥塞(个数)
                BigDecimal tempTotalRunCount = BigDecimal.ZERO;                 // 趟数
                BigDecimal tempTotalCumulativeWorkingWell = BigDecimal.ZERO;    // 井数
                BigDecimal tempTotalHourCount = BigDecimal.ZERO;                // 小时H
                BigDecimal tempTotalWaterVolume = BigDecimal.ZERO;              // 水方量(方)
                BigDecimal tempTotalPumpTrips = BigDecimal.ZERO;
                BigDecimal tempTotalCumulativeWorkingLayers = BigDecimal.ZERO;  // 段数  累计施工-层
                BigDecimal tempTotalMixSand = BigDecimal.ZERO;                  // 台次 当日泵车台次 当日仪表/混砂
                if (CollUtil.isNotEmpty(workloadAttrs)) {
                    for (IotTaskAttrModelProperty attr : workloadAttrs) {
                        String unit = attr.getUnit();   // 工作量单位
                        String actualValueStr = attr.getActualValue();  // 工作量属性的实际值
                        // 处理实际值：避免null或非数字字符串导致的异常
                        BigDecimal actualValue = BigDecimal.ZERO;
                        if (StrUtil.isNotBlank(actualValueStr)) {  // 假设使用Hutool的StrUtil，或自行判断null/空
                            try {
                                actualValue = new BigDecimal(actualValueStr);
                            } catch (NumberFormatException e) {
                                // 若字符串格式错误，默认按0处理（可根据业务调整）
                                actualValue = BigDecimal.ZERO;
                            }
                            if ("个数".equals(unit)) {
                                tempTotalBridgePlug = tempTotalBridgePlug.add(actualValue);
                            }
                            if ("趟数".equals(unit)) {
                                tempTotalRunCount = tempTotalRunCount.add(actualValue);
                            }
                            if ("小时".equals(unit)) {
                                tempTotalHourCount = tempTotalHourCount.add(actualValue);
                            }
                            if ("天数".equals(unit)) {
                                // 将 actualValue 换算成 H
                                BigDecimal hours = actualValue.multiply(new BigDecimal("24"));
                                tempTotalHourCount = tempTotalHourCount.add(hours);
                            }
                            if ("方".equals(unit)) {
                                tempTotalWaterVolume = tempTotalWaterVolume.add(actualValue);
                            }
                            if ("段数".equals(unit)) {
                                // 累计施工层
                                tempTotalCumulativeWorkingLayers = tempTotalCumulativeWorkingLayers.add(actualValue);
                            }
                            if ("台次".equals(unit) && "当日泵车台次".equals(attr.getName())) {
                                tempTotalPumpTrips = tempTotalPumpTrips.add(actualValue);
                            }
                            if ("台次".equals(unit) && ("当日仪表/混砂".equals(attr.getName())
                                    || "当日混砂".equals(attr.getName()) || "当日仪表".equals(attr.getName()))) {
                                tempTotalMixSand = tempTotalMixSand.add(actualValue);
                            }
                        }
                    }
                }
                reportBrief.setCumulativeBridgePlug(tempTotalBridgePlug);
                reportBrief.setCumulativeRunCount(tempTotalRunCount);
                reportBrief.setCumulativeHourCount(tempTotalHourCount);
                reportBrief.setCumulativeWaterVolume(tempTotalWaterVolume);
                reportBrief.setCumulativeWorkingLayers(tempTotalCumulativeWorkingLayers);
                reportBrief.setCumulativePumpTrips(tempTotalPumpTrips);
                reportBrief.setCumulativeMixSand(tempTotalMixSand);
                // 设置上一天的 工作量数据
                if (beforeYestBridgePlugPair.containsKey(report.getTaskId())) {
                    reportBrief.setYesterdayBridgePlug(beforeYestBridgePlugPair.get(report.getTaskId()));
                }
                if (beforeYestRunCountPair.containsKey(report.getTaskId())) {
                    reportBrief.setYesterdayRunCount(beforeYestRunCountPair.get(report.getTaskId()));
                }
                if (beforeYestHourCountPair.containsKey(report.getTaskId())) {
                    reportBrief.setYesterdayHourCount(beforeYestHourCountPair.get(report.getTaskId()));
                }
                if (beforeYestWaterVolumePair.containsKey(report.getTaskId())) {
                    reportBrief.setYesterdayWaterVolume(beforeYestWaterVolumePair.get(report.getTaskId()));
                }
                if (beforeYestLayersPair.containsKey(report.getTaskId())) {
                    reportBrief.setYesterdayWorkingLayers(beforeYestLayersPair.get(report.getTaskId()));
                }
                if (beforeYestPumpTripsPair.containsKey(report.getTaskId())) {
                    reportBrief.setYesterdayPumpTrips(beforeYestPumpTripsPair.get(report.getTaskId()));
                }
                if (beforeYestMixSandPair.containsKey(report.getTaskId())) {
                    reportBrief.setYesterdayMixSand(beforeYestMixSandPair.get(report.getTaskId()));
                }
                if (beforeYesterdayStatus.containsKey(report.getTaskId())) {
                    reportBrief.setYesterdayStatus(beforeYesterdayStatus.get(report.getTaskId()));
                }
                if (beforeYesterdayProduct.containsKey(report.getTaskId())) {
                    reportBrief.setYesterdayProduct(beforeYesterdayProduct.get(report.getTaskId()));
                }
                reportBriefs.add(reportBrief);
            });
        }
        return reportBriefs;
    }

    @Override
    public List<IotRdDailyReportAbnormalRespVO> abnormalAlert(IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportAbnormalRespVO> result = new ArrayList<>();
        // 查询指定时间范围内的异常警示信息
        // 待命 物资 三方 维修 人员 甲方 异常 天气 井口
        IotRdDailyReportPageReqVO reportReqVO = new IotRdDailyReportPageReqVO();
        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.dailyReports(reportReqVO);
        //  施工状态 key字典键值     value字典标签
        Map<String, String> rdStatusDictPair = new HashMap<>();
        Map<String, Set<Long>> abnormalReportPair = new HashMap<>();
        if (CollUtil.isNotEmpty(dailyReports)) {
            // 查询施工状态字典数据
            List<DictDataDO> rdStatuses = dictDataService.getDictDataListByDictType("rdStatus");
            if (CollUtil.isNotEmpty(rdStatuses)) {
                rdStatuses.forEach(tech -> {
                    // 筛选出停待状态 的施工状态
                    if ("TD".equals(tech.getRemark())) {
                        rdStatusDictPair.put(tech.getValue(), tech.getLabel());
                    }
                });
            }
            dailyReports.forEach(report -> {
                if (rdStatusDictPair.containsKey(report.getRdStatus())) {
                    String statusLabel = rdStatusDictPair.get(report.getRdStatus());
                    if (abnormalReportPair.containsKey(statusLabel)) {
                        Set<Long> tempIds = abnormalReportPair.get(statusLabel);
                        tempIds.add(report.getId());
                        abnormalReportPair.put(statusLabel, tempIds);
                    } else {
                        Set<Long> tempIds = new HashSet<>();
                        tempIds.add(report.getId());
                        abnormalReportPair.put(statusLabel, tempIds);
                    }
                }
            });
        }
        if (CollUtil.isNotEmpty(abnormalReportPair)) {
            abnormalReportPair.forEach((statusLabel, reportIds) -> {
                IotRdDailyReportAbnormalRespVO resp = new IotRdDailyReportAbnormalRespVO();
                resp.setName(statusLabel);
                resp.setCount(reportIds.size());
                result.add(resp);
            });
        }
        return result;
    }

    @Override
    public void repairHistoryProdStatus() {
        // 修复历史生产动态数据 生产动态拆分明细后 需要把  productionStatus 值赋值到 明细表 rq_iot_rd_daily_report_detail
        // 查询所有 日报数据 筛选出 production_status 有值的记录
        IotRdDailyReportPageReqVO reqVO = new IotRdDailyReportPageReqVO();
        List<IotRdDailyReportDO> reports = iotRdDailyReportMapper.dailyReports(reqVO);
        List<IotRdDailyReportDetailDO> reportDetails = new ArrayList<>();
        if (CollUtil.isNotEmpty(reports)) {
            reports.forEach(report -> {
                if (StrUtil.isNotBlank(report.getProductionStatus())) {
                    IotRdDailyReportDetailDO reportDetail = new IotRdDailyReportDetailDO();
                    reportDetail.setDeptId(report.getDeptId());
                    reportDetail.setReportId(report.getId());
                    reportDetail.setReportDate(report.getCreateTime());
                    reportDetail.setStartTime(report.getStartTime());
                    reportDetail.setEndTime(report.getEndTime());
                    // 计算2个时间之前间隔的小时数
                    reportDetail.setDuration(LocalTimeUtils.calculateDurationHours(report.getStartTime(), report.getEndTime()));
                    reportDetail.setConstructionDetail(report.getProductionStatus());
                    reportDetails.add(reportDetail);
                }
            });
            if (CollUtil.isNotEmpty(reportDetails)) {
                iotRdDailyReportDetailMapper.insertBatch(reportDetails);
            }
        }
    }

    @Override
    public List<IotRdDailyReportNptStatisticsRespVO> nptStatistics(IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportNptStatisticsRespVO> result = new ArrayList<>();
        // 不分页统计所有数据
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            // 找到所有子部门对象集合
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        // 检查contractName不为空但projectIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getContractName()) && (CollUtil.isEmpty(pageReqVO.getProjectIds()))) {
            return new ArrayList<>();
        }
        // 检查taskName不为空但taskIds为空的情况
        if (StrUtil.isNotBlank(pageReqVO.getTaskName()) && (CollUtil.isEmpty(pageReqVO.getTaskIds()))) {
            return new ArrayList<>();
        }
        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.dailyReports(pageReqVO);
        // 默认显示所有项目部的汇总数据
        // 点击项目部 显示 下属队伍的数据
        // 首先判断点击的部门是属于 公司 还是 队伍 如果没有点击任何部门 默认查询所有项目部数据
        if (ObjUtil.isEmpty(pageReqVO.getDeptId())) {
            // result = nptStatisticsByProjectDept(dailyReports, 163L, pageReqVO);
            result = nptStatisticsByProjectDepartment(dailyReports, 163L, pageReqVO);
        } else {
            // 判断点击的组织树中的部门类型 类型(公司级1 项目部2 队伍3)
            DeptDO selectedDept = deptService.getDept(pageReqVO.getDeptId());
            if ("1".equals(selectedDept.getType())) {
                // 以项目部为维度汇总数据
                // result = nptStatisticsByProjectDept(dailyReports, pageReqVO.getDeptId(), pageReqVO);
                result = nptStatisticsByProjectDepartment(dailyReports, pageReqVO.getDeptId(), pageReqVO);
            } else if ("2".equals(selectedDept.getType())) {
                // 以队伍为维度汇总数据
                result = nptStatisticsByProjectDepartment(dailyReports, pageReqVO.getDeptId(), pageReqVO);
            } else if ("3".equals(selectedDept.getType())) {
                // 显示单个队伍的汇总数据
                result = nptStatisticsByProjectDepartment(dailyReports, pageReqVO.getDeptId(), pageReqVO);
            } else {
                // 点击的部门没有类型 判断部门下的是否包含 项目部类型部门 新疆分公司 以项目部为维度汇总数据
                // result = nptStatisticsByProjectDept(dailyReports, pageReqVO.getDeptId(), pageReqVO);
                result = nptStatisticsByProjectDepartment(dailyReports, pageReqVO.getDeptId(), pageReqVO);
            }
        }
        // 根据 result 集合内对象的 sort 属性正序排列 sort 类型为 integer 类型
        if (CollUtil.isNotEmpty(result)) {
            result.sort(Comparator.comparing(
                    IotRdDailyReportNptStatisticsRespVO::getSort,
                    Comparator.nullsLast(Comparator.naturalOrder())
            ));
        }
        return result;
    }

    @Override
    public List<IotRdDailyReportSummaryYearRespVO> reportSummary(IotRdDailyReportPageReqVO pageReqVO) {
        // 查询选择部门下面所有子部门
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        // searchKey 兼容移动端查询的情况
        if (StrUtil.isNotBlank(pageReqVO.getSearchKey())) {
            if (CollUtil.isEmpty(pageReqVO.getProjectIds()) && CollUtil.isEmpty(pageReqVO.getTaskIds())) {
                return new ArrayList<>();
            }
        } else {
            // PC 端 AND 检查：各自条件必须匹配到对应 ID
            if (StrUtil.isNotBlank(pageReqVO.getContractName()) && CollUtil.isEmpty(pageReqVO.getProjectIds())) {
                return new ArrayList<>();
            }
            if (StrUtil.isNotBlank(pageReqVO.getTaskName()) && CollUtil.isEmpty(pageReqVO.getTaskIds())) {
                return new ArrayList<>();
            }
        }
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        PageResult<IotRdDailyReportDO> pageReports = iotRdDailyReportMapper.selectPage(pageReqVO);
        List<IotRdDailyReportDO> dailyReports = pageReports.getList();
        if (CollUtil.isEmpty(dailyReports)) {
            return Collections.emptyList();
        }
        // 按照 年 月 日 汇总统计 日报数据
        List<IotRdDailyReportSummaryYearRespVO> result = new ArrayList<>();
        // 1. 按年 -> 月 -> 日 分组，过滤 createTime 为空的数据
        Map<String, Map<String, Map<String, List<IotRdDailyReportDO>>>> grouped = dailyReports.stream()
                .filter(r -> r.getCreateTime() != null)
                .collect(Collectors.groupingBy(
                        r -> String.valueOf(r.getCreateTime().getYear()),
                        Collectors.groupingBy(
                                r -> String.valueOf(r.getCreateTime().getMonthValue()),
                                Collectors.groupingBy(
                                        r -> String.valueOf(r.getCreateTime().getDayOfMonth())
                                )
                        )
                ));
        // 2. 遍历年份
        for (Map.Entry<String, Map<String, Map<String, List<IotRdDailyReportDO>>>> yearEntry : grouped.entrySet()) {
            String year = yearEntry.getKey();
            Map<String, Map<String, List<IotRdDailyReportDO>>> monthMap = yearEntry.getValue();

            IotRdDailyReportSummaryYearRespVO yearVO = new IotRdDailyReportSummaryYearRespVO();
            yearVO.setYear(year);
            yearVO.setLevel("year");
            List<IotRdDailyReportSummaryMonthRespVO> monthVOList = new ArrayList<>();
            int yearReportCount = 0;

            // 3. 遍历月份
            for (Map.Entry<String, Map<String, List<IotRdDailyReportDO>>> monthEntry : monthMap.entrySet()) {
                String month = monthEntry.getKey();
                Map<String, List<IotRdDailyReportDO>> dayMap = monthEntry.getValue();

                IotRdDailyReportSummaryMonthRespVO monthVO = new IotRdDailyReportSummaryMonthRespVO();
                monthVO.setMonth(month);
                monthVO.setLevel("month");
                List<IotRdDailyReportSummaryDayRespVO> dayVOList = new ArrayList<>();
                int monthReportCount = 0;

                // 4. 遍历日
                for (Map.Entry<String, List<IotRdDailyReportDO>> dayEntry : dayMap.entrySet()) {
                    String day = dayEntry.getKey();
                    List<IotRdDailyReportDO> dayReports = dayEntry.getValue();

                    IotRdDailyReportSummaryDayRespVO dayVO = new IotRdDailyReportSummaryDayRespVO();
                    dayVO.setDay(day);
                    dayVO.setLevel("day");

                    // 统计已填报（status=1）和未填报（status=0）数量
                    long filled = dayReports.stream()
                            .filter(r -> r.getStatus() != null && r.getStatus() == 1)
                            .count();
                    long unfilled = dayReports.stream()
                            .filter(r -> r.getStatus() != null && r.getStatus() == 0)
                            .count();
                    dayVO.setFilledCount(String.valueOf(filled));
                    dayVO.setUnfilledCount(String.valueOf(unfilled));
                    dayVO.setReportCount(dayReports.size());

                    // 计算星期几（基于该天第一个日报的 createTime）
                    if (!dayReports.isEmpty()) {
                        LocalDateTime dateTime = dayReports.get(0).getCreateTime();
                        // 转换为中文星期几，如 "星期一"
                        String weekDay = dateTime.getDayOfWeek().getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.CHINESE);
                        dayVO.setWeekDay(weekDay);
                    }

                    // 构建日报简要列表（仅 id 和 reportName）
                    List<IotRdDailyReportShortRespVO> reportVOList = dayReports.stream()
                            .map(r -> {
                                IotRdDailyReportShortRespVO respVO = new IotRdDailyReportShortRespVO();
                                respVO.setId(r.getId());
                                respVO.setReportName(r.getReportName());
                                return respVO;
                            })
                            .collect(Collectors.toList());
                    dayVO.setReports(reportVOList);

                    dayVOList.add(dayVO);
                    monthReportCount += dayReports.size();
                }

                monthVO.setDays(dayVOList);
                monthVO.setDayCount(dayVOList.size());      // 该月下有日报的天数
                monthVO.setReportCount(monthReportCount);   // 该月日报总数
                monthVOList.add(monthVO);
                yearReportCount += monthReportCount;
            }

            yearVO.setMonths(monthVOList);
            yearVO.setMonthCount(monthVOList.size());       // 该年下有日报的月份数
            yearVO.setReportCount(yearReportCount);         // 该年日报总数
            result.add(yearVO);
        }

        // 5. 排序：年份降序、月份降序、日降序（最新在前）—— 使用数字比较
        result.sort((y1, y2) -> Integer.compare(Integer.parseInt(y2.getYear()), Integer.parseInt(y1.getYear())));
        for (IotRdDailyReportSummaryYearRespVO yearVO : result) {
            if (CollUtil.isNotEmpty(yearVO.getMonths())) {
                yearVO.getMonths().sort((m1, m2) -> Integer.compare(Integer.parseInt(m2.getMonth()), Integer.parseInt(m1.getMonth())));
                for (IotRdDailyReportSummaryMonthRespVO monthVO : yearVO.getMonths()) {
                    if (CollUtil.isNotEmpty(monthVO.getDays())) {
                        // 日排序：降序，数字比较
                        monthVO.getDays().sort((d1, d2) -> Integer.compare(Integer.parseInt(d2.getDay()), Integer.parseInt(d1.getDay())));
                    }
                }
            }
        }
        return result;
    }

    /**
     * 按项目部维度 汇总 统计数据
     * @param dailyReports 日报数据列表
     * @param rootDeptId 根部门ID（如 163L为 瑞都 根部门，或其他公司级部门ID）
     * @return 项目部维度统计结果列表
     */
    private List<IotRdDailyReportNptStatisticsRespVO> nptStatisticsByProjectDept(List<IotRdDailyReportDO> dailyReports,
                                                                           Long rootDeptId, IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportNptStatisticsRespVO> 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累计 工程质量npt
        Map<Long, BigDecimal> subtotalAccidentTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 设备故障npt
        Map<Long, BigDecimal> subtotalRepairTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 设备保养npt
        Map<Long, BigDecimal> subtotalSelfStopTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 技术受限npt
        Map<Long, BigDecimal> subtotalComplexityTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 生产配合npt
        Map<Long, BigDecimal> subtotalRelocationTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 生产组织npt
        Map<Long, BigDecimal> subtotalRectificationTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 不可抗力npt
        Map<Long, BigDecimal> subtotalWaitingStopTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 待命npt
        Map<Long, BigDecimal> subtotalWinterBreakTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 甲方设计npt
        Map<Long, BigDecimal> subtotalPartyaDesignPair = new HashMap<>();
        // key队伍id/项目部id   value累计 甲方准备npt
        Map<Long, BigDecimal> subtotalPartyaPreparePair = new HashMap<>();
        // key队伍id/项目部id   value累计 甲方资源npt
        Map<Long, BigDecimal> subtotalPartyaResourcePair = new HashMap<>();
        // key队伍id/项目部id   value累计 其它非生产时间npt
        Map<Long, BigDecimal> subtotalOtherNptTimePair = new HashMap<>();

        // key队伍id/项目部id   value npt小计
        Map<Long, BigDecimal> subtotalNptTimePair = new HashMap<>();
        // key队伍id/项目部id   value npt时效%
        Map<Long, BigDecimal> subtotalNptRatePair = new HashMap<>();

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

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

        // 构建项目部映射和父子部门关系 瑞都存在日报部门是项目部的情况
        depts.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);
            }
            // 可能是项目部 也可能是队伍
            teamProjectIdPair.put(dept.getId(), dept.getParentId());
        });

        // 计算日期查询区间有多少小时
        LocalDateTime[] createTimes = pageReqVO.getCreateTime();
        List<Long> totalHours = new ArrayList<>();
        if (ObjUtil.isNotEmpty(createTimes) && createTimes.length >= 2) {
            LocalDate startDate = createTimes[0].toLocalDate();
            LocalDate endDate = createTimes[1].toLocalDate();
            // 核心：包含起止两天 → 天数差 +1 再 ×24
            long days = ChronoUnit.DAYS.between(startDate, endDate) + 1;
            Long tempTotalHours = days * 24L;
            totalHours.add(tempTotalHours);
        }

        // 累计计算各项 工作量
        if (CollUtil.isNotEmpty(dailyReports)) {
            dailyReports.forEach(report -> {
                if (ObjUtil.isNotEmpty(report.getDeptId()) && teamProjectIdPair.containsKey(report.getDeptId())) {
                    // projectDeptId可能是项目部 也可能是项目的上级
                    Long projectDeptId = 0l;
                    if (teamDeptPair.containsKey(report.getDeptId())) {
                        // 日报deptId如果是队伍 获取队伍的上级项目部
                        projectDeptId = teamProjectIdPair.get(report.getDeptId());
                    }
                    if (projectDeptPair.containsKey(report.getDeptId())) {
                        // 日报deptId如果是项目部 使用当前项目部
                        projectDeptId = report.getDeptId();
                    }
                    if (ObjUtil.isNotEmpty(projectDeptId) && (projectDeptId > 0)) {
                        // 工程质量 npt
                        BigDecimal accidentTime = report.getAccidentTime();
                        if (ObjUtil.isNotEmpty(accidentTime)) {
                            if (subtotalAccidentTimePair.containsKey(projectDeptId)) {
                                BigDecimal existAccidentTime = subtotalAccidentTimePair.get(projectDeptId);
                                BigDecimal tempAccidentTime = existAccidentTime.add(accidentTime);
                                subtotalAccidentTimePair.put(projectDeptId, tempAccidentTime);
                            } else {
                                subtotalAccidentTimePair.put(projectDeptId, accidentTime);
                            }
                        }
                        // 设备故障 npt
                        BigDecimal repairTime = report.getRepairTime();
                        if (ObjUtil.isNotEmpty(repairTime)) {
                            if (subtotalRepairTimePair.containsKey(projectDeptId)) {
                                BigDecimal existRepairTime = subtotalRepairTimePair.get(projectDeptId);
                                BigDecimal tempRepairTime = existRepairTime.add(repairTime);
                                subtotalRepairTimePair.put(projectDeptId, tempRepairTime);
                            } else {
                                subtotalRepairTimePair.put(projectDeptId, repairTime);
                            }
                        }
                        // 设备保养 npt
                        BigDecimal selfStopTime = report.getSelfStopTime();
                        if (ObjUtil.isNotEmpty(selfStopTime)) {
                            if (subtotalSelfStopTimePair.containsKey(projectDeptId)) {
                                BigDecimal existSelfStopTime = subtotalSelfStopTimePair.get(projectDeptId);
                                BigDecimal tempSelfStopTime = existSelfStopTime.add(selfStopTime);
                                subtotalSelfStopTimePair.put(projectDeptId, tempSelfStopTime);
                            } else {
                                subtotalSelfStopTimePair.put(projectDeptId, selfStopTime);
                            }
                        }
                        // 技术受限 npt
                        BigDecimal complexityTime = report.getComplexityTime();
                        if (ObjUtil.isNotEmpty(complexityTime)) {
                            if (subtotalComplexityTimePair.containsKey(projectDeptId)) {
                                BigDecimal existComplexityTime = subtotalComplexityTimePair.get(projectDeptId);
                                BigDecimal tempComplexityTime = existComplexityTime.add(complexityTime);
                                subtotalComplexityTimePair.put(projectDeptId, tempComplexityTime);
                            } else {
                                subtotalComplexityTimePair.put(projectDeptId, complexityTime);
                            }
                        }
                        // 生产配合 npt
                        BigDecimal relocationTime = report.getRelocationTime();
                        if (ObjUtil.isNotEmpty(relocationTime)) {
                            if (subtotalRelocationTimePair.containsKey(projectDeptId)) {
                                BigDecimal existRelocationTime = subtotalRelocationTimePair.get(projectDeptId);
                                BigDecimal tempRelocationTime = existRelocationTime.add(relocationTime);
                                subtotalRelocationTimePair.put(projectDeptId, tempRelocationTime);
                            } else {
                                subtotalRelocationTimePair.put(projectDeptId, relocationTime);
                            }
                        }
                        // 生产组织 npt
                        BigDecimal rectificationTime = report.getRectificationTime();
                        if (ObjUtil.isNotEmpty(rectificationTime)) {
                            if (subtotalRectificationTimePair.containsKey(projectDeptId)) {
                                BigDecimal existRectificationTime = subtotalRectificationTimePair.get(projectDeptId);
                                BigDecimal tempRectificationTime = existRectificationTime.add(rectificationTime);
                                subtotalRectificationTimePair.put(projectDeptId, tempRectificationTime);
                            } else {
                                subtotalRectificationTimePair.put(projectDeptId, rectificationTime);
                            }
                        }
                        // 不可抗力 npt
                        BigDecimal waitingStopTime = report.getWaitingStopTime();
                        if (ObjUtil.isNotEmpty(waitingStopTime)) {
                            if (subtotalWaitingStopTimePair.containsKey(projectDeptId)) {
                                BigDecimal existWaitingStopTime = subtotalWaitingStopTimePair.get(projectDeptId);
                                BigDecimal tempWaitingStopTime = existWaitingStopTime.add(waitingStopTime);
                                subtotalWaitingStopTimePair.put(projectDeptId, tempWaitingStopTime);
                            } else {
                                subtotalWaitingStopTimePair.put(projectDeptId, waitingStopTime);
                            }
                        }
                        // 待命 npt
                        BigDecimal winterBreakTime = report.getWinterBreakTime();
                        if (ObjUtil.isNotEmpty(winterBreakTime)) {
                            if (subtotalWinterBreakTimePair.containsKey(projectDeptId)) {
                                BigDecimal existWinterBreakTime = subtotalWinterBreakTimePair.get(projectDeptId);
                                BigDecimal tempWinterBreakTime = existWinterBreakTime.add(winterBreakTime);
                                subtotalWinterBreakTimePair.put(projectDeptId, tempWinterBreakTime);
                            } else {
                                subtotalWinterBreakTimePair.put(projectDeptId, winterBreakTime);
                            }
                        }
                        // 甲方设计 npt
                        BigDecimal partyaDesign = report.getPartyaDesign();
                        if (ObjUtil.isNotEmpty(partyaDesign)) {
                            if (subtotalPartyaDesignPair.containsKey(projectDeptId)) {
                                BigDecimal existPartyaDesign = subtotalPartyaDesignPair.get(projectDeptId);
                                BigDecimal tempPartyaDesign = existPartyaDesign.add(partyaDesign);
                                subtotalPartyaDesignPair.put(projectDeptId, tempPartyaDesign);
                            } else {
                                subtotalPartyaDesignPair.put(projectDeptId, partyaDesign);
                            }
                        }
                        // 甲方准备 npt
                        BigDecimal partyaPrepare = report.getPartyaPrepare();
                        if (ObjUtil.isNotEmpty(partyaPrepare)) {
                            if (subtotalPartyaPreparePair.containsKey(projectDeptId)) {
                                BigDecimal existPartyaPrepare = subtotalPartyaPreparePair.get(projectDeptId);
                                BigDecimal tempPartyaPrepare = existPartyaPrepare.add(partyaPrepare);
                                subtotalPartyaPreparePair.put(projectDeptId, tempPartyaPrepare);
                            } else {
                                subtotalPartyaPreparePair.put(projectDeptId, partyaPrepare);
                            }
                        }
                        // 甲方资源 npt
                        BigDecimal partyaResource = report.getPartyaResource();
                        if (ObjUtil.isNotEmpty(partyaResource)) {
                            if (subtotalPartyaResourcePair.containsKey(projectDeptId)) {
                                BigDecimal existPartyaResource = subtotalPartyaResourcePair.get(projectDeptId);
                                BigDecimal tempPartyaResource = existPartyaResource.add(partyaResource);
                                subtotalPartyaResourcePair.put(projectDeptId, tempPartyaResource);
                            } else {
                                subtotalPartyaResourcePair.put(projectDeptId, partyaResource);
                            }
                        }
                        // 其它非生产时间 npt
                        BigDecimal otherNptTime = report.getOtherNptTime();
                        if (ObjUtil.isNotEmpty(otherNptTime)) {
                            if (subtotalOtherNptTimePair.containsKey(projectDeptId)) {
                                BigDecimal existOtherNptTime = subtotalOtherNptTimePair.get(projectDeptId);
                                BigDecimal tempOtherNptTime = existOtherNptTime.add(otherNptTime);
                                subtotalOtherNptTimePair.put(projectDeptId, tempOtherNptTime);
                            } else {
                                subtotalOtherNptTimePair.put(projectDeptId, otherNptTime);
                            }
                        }
                    }
                }

            });

            // 计算每个项目部的 npt小计 npt时效
            if (CollUtil.isNotEmpty(projectDeptPair)) {
                projectDeptPair.forEach((projectDeptId, dept) -> {
                    // 统计当前项目部所有 npt 总和、时效
                    BigDecimal tempSubtotalNpt = BigDecimal.ZERO;
                    // 计入 npt时效 的非生产时间小计
                    BigDecimal tempNptSum = BigDecimal.ZERO;
                    if (subtotalAccidentTimePair.containsKey(projectDeptId)) {
                        BigDecimal accidentTime = subtotalAccidentTimePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(accidentTime);
                        tempNptSum = tempNptSum.add(accidentTime);
                    }
                    if (subtotalRepairTimePair.containsKey(projectDeptId)) {
                        BigDecimal repairTime = subtotalRepairTimePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(repairTime);
                        tempNptSum = tempNptSum.add(repairTime);
                    }
                    if (subtotalSelfStopTimePair.containsKey(projectDeptId)) {
                        BigDecimal selfStopTime = subtotalSelfStopTimePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(selfStopTime);
                    }
                    if (subtotalComplexityTimePair.containsKey(projectDeptId)) {
                        BigDecimal complexityTime = subtotalComplexityTimePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(complexityTime);
                        tempNptSum = tempNptSum.add(complexityTime);
                    }
                    if (subtotalRelocationTimePair.containsKey(projectDeptId)) {
                        BigDecimal relocationTime = subtotalRelocationTimePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(relocationTime);
                    }
                    if (subtotalRectificationTimePair.containsKey(projectDeptId)) {
                        BigDecimal rectificationTime = subtotalRectificationTimePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(rectificationTime);
                        tempNptSum = tempNptSum.add(rectificationTime);
                    }
                    if (subtotalWaitingStopTimePair.containsKey(projectDeptId)) {
                        BigDecimal waitingStopTime = subtotalWaitingStopTimePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(waitingStopTime);
                        tempNptSum = tempNptSum.add(waitingStopTime);
                    }
                    if (subtotalWinterBreakTimePair.containsKey(projectDeptId)) {
                        BigDecimal winterBreakTime = subtotalWinterBreakTimePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(winterBreakTime);
                    }
                    if (subtotalPartyaDesignPair.containsKey(projectDeptId)) {
                        BigDecimal partyaDesign = subtotalPartyaDesignPair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(partyaDesign);
                        tempNptSum = tempNptSum.add(partyaDesign);
                    }
                    if (subtotalPartyaPreparePair.containsKey(projectDeptId)) {
                        BigDecimal partyaPrepare = subtotalPartyaPreparePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(partyaPrepare);
                        tempNptSum = tempNptSum.add(partyaPrepare);
                    }
                    if (subtotalPartyaResourcePair.containsKey(projectDeptId)) {
                        BigDecimal partyaResource = subtotalPartyaResourcePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(partyaResource);
                        tempNptSum = tempNptSum.add(partyaResource);
                    }
                    if (subtotalOtherNptTimePair.containsKey(projectDeptId)) {
                        BigDecimal otherNptTime = subtotalOtherNptTimePair.get(projectDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(otherNptTime);
                        tempNptSum = tempNptSum.add(otherNptTime);
                    }
                    // 项目部 npt 小计
                    subtotalNptTimePair.put(projectDeptId, tempNptSum);
                    // 项目部 npt 时效 以下非生产时间计入非生产时效
                    // 设备故障repair_time
                    // 工程质量accident_time
                    // 技术受限complexity_time
                    // 生产组织rectification_time
                    // 不可抗力waiting_stop_time
                    // 甲方设计partya_design
                    // 甲方准备partya_prepare
                    // 甲方资源partya_resource
                    // 其他非生产时间other_npt_time
                    if (CollUtil.isNotEmpty(totalHours)) {
                        Long totalHour = totalHours.get(0);
                        if (ObjUtil.isNotEmpty(totalHour) && totalHour > 0) {
                            BigDecimal nptRate = tempNptSum.divide(new BigDecimal(totalHour), 4, RoundingMode.HALF_UP);
                            subtotalNptRatePair.put(projectDeptId, nptRate);
                        }
                    }
                });
            }
        }

        // 生成返回的数据列表集合
        projectDeptPair.forEach((deptId, dept) -> {
            IotRdDailyReportNptStatisticsRespVO statistics = new IotRdDailyReportNptStatisticsRespVO();
            statistics.setProjectDeptId(deptId);
            statistics.setProjectDeptName(dept.getName());
            statistics.setSort(dept.getSort());
            statistics.setType("2");
            statistics.setAccidentTime(subtotalAccidentTimePair.get(deptId));
            statistics.setRepairTime(subtotalRepairTimePair.get(deptId));
            statistics.setSelfStopTime(subtotalSelfStopTimePair.get(deptId));
            statistics.setComplexityTime(subtotalComplexityTimePair.get(deptId));
            statistics.setRelocationTime(subtotalRelocationTimePair.get(deptId));
            statistics.setRectificationTime(subtotalRectificationTimePair.get(deptId));
            statistics.setWaitingStopTime(subtotalWaitingStopTimePair.get(deptId));
            statistics.setWinterBreakTime(subtotalWinterBreakTimePair.get(deptId));
            statistics.setPartyaDesign(subtotalPartyaDesignPair.get(deptId));
            statistics.setPartyaPrepare(subtotalPartyaPreparePair.get(deptId));
            statistics.setPartyaResource(subtotalPartyaResourcePair.get(deptId));
            statistics.setOtherNptTime(subtotalOtherNptTimePair.get(deptId));
            statistics.setNptTotal(subtotalNptTimePair.get(deptId));
            statistics.setNptRate(subtotalNptRatePair.get(deptId));
            if (CollUtil.isNotEmpty(totalHours)) {
                Long totalHour = totalHours.get(0);
                statistics.setCalendarTime(new BigDecimal(totalHour));
            }
            result.add(statistics);
        });

        return result;
    }

    /**
     * 按 队伍 维度统计 非生产时效
     * @param dailyReports 瑞都日报数据列表
     * @param deptId 项目部ID 或 队伍id
     * @return 队伍 维度统计 非生产时效数据
     */
    private List<IotRdDailyReportNptStatisticsRespVO> nptStatisticsByProjectDepartment(List<IotRdDailyReportDO> dailyReports, Long deptId, IotRdDailyReportPageReqVO pageReqVO) {
        List<IotRdDailyReportNptStatisticsRespVO> 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> utilizationRatePair = new HashMap<>();

        // key队伍id/项目部id   value累计 工程质量npt
        Map<Long, BigDecimal> subtotalAccidentTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 设备故障npt
        Map<Long, BigDecimal> subtotalRepairTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 设备保养npt
        Map<Long, BigDecimal> subtotalSelfStopTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 技术受限npt
        Map<Long, BigDecimal> subtotalComplexityTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 生产配合npt
        Map<Long, BigDecimal> subtotalRelocationTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 生产组织npt
        Map<Long, BigDecimal> subtotalRectificationTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 不可抗力npt
        Map<Long, BigDecimal> subtotalWaitingStopTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 待命npt
        Map<Long, BigDecimal> subtotalWinterBreakTimePair = new HashMap<>();
        // key队伍id/项目部id   value累计 甲方设计npt
        Map<Long, BigDecimal> subtotalPartyaDesignPair = new HashMap<>();
        // key队伍id/项目部id   value累计 甲方准备npt
        Map<Long, BigDecimal> subtotalPartyaPreparePair = new HashMap<>();
        // key队伍id/项目部id   value累计 甲方资源npt
        Map<Long, BigDecimal> subtotalPartyaResourcePair = new HashMap<>();
        // key队伍id/项目部id   value累计 其它非生产时间npt
        Map<Long, BigDecimal> subtotalOtherNptTimePair = new HashMap<>();

        // key队伍id   value npt小计
        Map<Long, BigDecimal> subtotalNptTimePair = new HashMap<>();
        // key队伍id   value npt时效%
        Map<Long, BigDecimal> subtotalNptRatePair = new HashMap<>();

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

        // 构建项目部映射和父子部门关系
        depts.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());
        });

        // 计算日期查询区间有多少小时
        LocalDateTime[] createTimes = pageReqVO.getCreateTime();
        List<Long> totalHours = new ArrayList<>();
        if (ObjUtil.isNotEmpty(createTimes) && createTimes.length >= 2) {
            LocalDate startDate = createTimes[0].toLocalDate();
            LocalDate endDate = createTimes[1].toLocalDate();
            // 核心：包含起止两天 → 天数差 +1 再 ×24
            long days = ChronoUnit.DAYS.between(startDate, endDate) + 1;
            Long tempTotalHours = days * 24L;
            totalHours.add(tempTotalHours);
        }

        // 累计计算 工作量
        if (CollUtil.isNotEmpty(dailyReports)) {
            dailyReports.forEach(report -> {
                // 统计日报 每个队伍的非生产时间集合
                Long reportDeptId = report.getDeptId();
                if (ObjUtil.isNotEmpty(reportDeptId) && teamDeptIds.contains(reportDeptId)) {
                    // 工程质量 npt
                    BigDecimal accidentTime = report.getAccidentTime();
                    if (ObjUtil.isNotEmpty(accidentTime)) {
                        if (subtotalAccidentTimePair.containsKey(reportDeptId)) {
                            BigDecimal existAccidentTime = subtotalAccidentTimePair.get(reportDeptId);
                            BigDecimal tempAccidentTime = existAccidentTime.add(accidentTime);
                            subtotalAccidentTimePair.put(reportDeptId, tempAccidentTime);
                        } else {
                            subtotalAccidentTimePair.put(reportDeptId, accidentTime);
                        }
                    }

                    // 设备故障 npt
                    BigDecimal repairTime = report.getRepairTime();
                    if (ObjUtil.isNotEmpty(repairTime)) {
                        if (subtotalRepairTimePair.containsKey(reportDeptId)) {
                            BigDecimal existRepairTime = subtotalRepairTimePair.get(reportDeptId);
                            BigDecimal tempRepairTime = existRepairTime.add(repairTime);
                            subtotalRepairTimePair.put(reportDeptId, tempRepairTime);
                        } else {
                            subtotalRepairTimePair.put(reportDeptId, repairTime);
                        }
                    }
                    // 设备保养 npt
                    BigDecimal selfStopTime = report.getSelfStopTime();
                    if (ObjUtil.isNotEmpty(selfStopTime)) {
                        if (subtotalSelfStopTimePair.containsKey(reportDeptId)) {
                            BigDecimal existSelfStopTime = subtotalSelfStopTimePair.get(reportDeptId);
                            BigDecimal tempSelfStopTime = existSelfStopTime.add(selfStopTime);
                            subtotalSelfStopTimePair.put(reportDeptId, tempSelfStopTime);
                        } else {
                            subtotalSelfStopTimePair.put(reportDeptId, selfStopTime);
                        }
                    }
                    // 技术受限 npt
                    BigDecimal complexityTime = report.getComplexityTime();
                    if (ObjUtil.isNotEmpty(complexityTime)) {
                        if (subtotalComplexityTimePair.containsKey(reportDeptId)) {
                            BigDecimal existComplexityTime = subtotalComplexityTimePair.get(reportDeptId);
                            BigDecimal tempComplexityTime = existComplexityTime.add(complexityTime);
                            subtotalComplexityTimePair.put(reportDeptId, tempComplexityTime);
                        } else {
                            subtotalComplexityTimePair.put(reportDeptId, complexityTime);
                        }
                    }
                    // 生产配合 npt
                    BigDecimal relocationTime = report.getRelocationTime();
                    if (ObjUtil.isNotEmpty(relocationTime)) {
                        if (subtotalRelocationTimePair.containsKey(reportDeptId)) {
                            BigDecimal existRelocationTime = subtotalRelocationTimePair.get(reportDeptId);
                            BigDecimal tempRelocationTime = existRelocationTime.add(relocationTime);
                            subtotalRelocationTimePair.put(reportDeptId, tempRelocationTime);
                        } else {
                            subtotalRelocationTimePair.put(reportDeptId, relocationTime);
                        }
                    }
                    // 生产组织 npt
                    BigDecimal rectificationTime = report.getRectificationTime();
                    if (ObjUtil.isNotEmpty(rectificationTime)) {
                        if (subtotalRectificationTimePair.containsKey(reportDeptId)) {
                            BigDecimal existRectificationTime = subtotalRectificationTimePair.get(reportDeptId);
                            BigDecimal tempRectificationTime = existRectificationTime.add(rectificationTime);
                            subtotalRectificationTimePair.put(reportDeptId, tempRectificationTime);
                        } else {
                            subtotalRectificationTimePair.put(reportDeptId, rectificationTime);
                        }
                    }
                    // 不可抗力 npt
                    BigDecimal waitingStopTime = report.getWaitingStopTime();
                    if (ObjUtil.isNotEmpty(waitingStopTime)) {
                        if (subtotalWaitingStopTimePair.containsKey(reportDeptId)) {
                            BigDecimal existWaitingStopTime = subtotalWaitingStopTimePair.get(reportDeptId);
                            BigDecimal tempWaitingStopTime = existWaitingStopTime.add(waitingStopTime);
                            subtotalWaitingStopTimePair.put(reportDeptId, tempWaitingStopTime);
                        } else {
                            subtotalWaitingStopTimePair.put(reportDeptId, waitingStopTime);
                        }
                    }
                    // 待命 npt
                    BigDecimal winterBreakTime = report.getWinterBreakTime();
                    if (ObjUtil.isNotEmpty(winterBreakTime)) {
                        if (subtotalWinterBreakTimePair.containsKey(reportDeptId)) {
                            BigDecimal existWinterBreakTime = subtotalWinterBreakTimePair.get(reportDeptId);
                            BigDecimal tempWinterBreakTime = existWinterBreakTime.add(winterBreakTime);
                            subtotalWinterBreakTimePair.put(reportDeptId, tempWinterBreakTime);
                        } else {
                            subtotalWinterBreakTimePair.put(reportDeptId, winterBreakTime);
                        }
                    }
                    // 甲方设计 npt
                    BigDecimal partyaDesign = report.getPartyaDesign();
                    if (ObjUtil.isNotEmpty(partyaDesign)) {
                        if (subtotalPartyaDesignPair.containsKey(reportDeptId)) {
                            BigDecimal existPartyaDesign = subtotalPartyaDesignPair.get(reportDeptId);
                            BigDecimal tempPartyaDesign = existPartyaDesign.add(partyaDesign);
                            subtotalPartyaDesignPair.put(reportDeptId, tempPartyaDesign);
                        } else {
                            subtotalPartyaDesignPair.put(reportDeptId, partyaDesign);
                        }
                    }
                    // 甲方准备 npt
                    BigDecimal partyaPrepare = report.getPartyaPrepare();
                    if (ObjUtil.isNotEmpty(partyaPrepare)) {
                        if (subtotalPartyaPreparePair.containsKey(reportDeptId)) {
                            BigDecimal existPartyaPrepare = subtotalPartyaPreparePair.get(reportDeptId);
                            BigDecimal tempPartyaPrepare = existPartyaPrepare.add(partyaPrepare);
                            subtotalPartyaPreparePair.put(reportDeptId, tempPartyaPrepare);
                        } else {
                            subtotalPartyaPreparePair.put(reportDeptId, partyaPrepare);
                        }
                    }
                    // 甲方资源 npt
                    BigDecimal partyaResource = report.getPartyaResource();
                    if (ObjUtil.isNotEmpty(partyaResource)) {
                        if (subtotalPartyaResourcePair.containsKey(reportDeptId)) {
                            BigDecimal existPartyaResource = subtotalPartyaResourcePair.get(reportDeptId);
                            BigDecimal tempPartyaResource = existPartyaResource.add(partyaResource);
                            subtotalPartyaResourcePair.put(reportDeptId, tempPartyaResource);
                        } else {
                            subtotalPartyaResourcePair.put(reportDeptId, partyaResource);
                        }
                    }
                    // 其它非生产时间 npt
                    BigDecimal otherNptTime = report.getOtherNptTime();
                    if (ObjUtil.isNotEmpty(otherNptTime)) {
                        if (subtotalOtherNptTimePair.containsKey(reportDeptId)) {
                            BigDecimal existOtherNptTime = subtotalOtherNptTimePair.get(reportDeptId);
                            BigDecimal tempOtherNptTime = existOtherNptTime.add(otherNptTime);
                            subtotalOtherNptTimePair.put(reportDeptId, tempOtherNptTime);
                        } else {
                            subtotalOtherNptTimePair.put(reportDeptId, otherNptTime);
                        }
                    }
                }
            });

            // 计算每个 施工队伍 的 npt小计 npt时效
            if (CollUtil.isNotEmpty(teamDeptPair)) {
                teamDeptPair.forEach((teamDeptId, dept) -> {
                    // 统计当前 施工队伍 所有 npt总和 npt时效
                    BigDecimal tempSubtotalNpt = BigDecimal.ZERO;
                    // 计入 npt时效 的非生产时间小计
                    BigDecimal tempNptSum = BigDecimal.ZERO;
                    if (subtotalAccidentTimePair.containsKey(teamDeptId)) {
                        BigDecimal accidentTime = subtotalAccidentTimePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(accidentTime);
                        tempNptSum = tempNptSum.add(accidentTime);
                    }
                    if (subtotalRepairTimePair.containsKey(teamDeptId)) {
                        BigDecimal repairTime = subtotalRepairTimePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(repairTime);
                        tempNptSum = tempNptSum.add(repairTime);
                    }
                    // 设备保养 不计入 非生产时效
                    if (subtotalSelfStopTimePair.containsKey(teamDeptId)) {
                        BigDecimal selfStopTime = subtotalSelfStopTimePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(selfStopTime);
                    }
                    if (subtotalComplexityTimePair.containsKey(teamDeptId)) {
                        BigDecimal complexityTime = subtotalComplexityTimePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(complexityTime);
                        tempNptSum = tempNptSum.add(complexityTime);
                    }
                    // 生产配合 不计入 非生产时效
                    if (subtotalRelocationTimePair.containsKey(teamDeptId)) {
                        BigDecimal relocationTime = subtotalRelocationTimePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(relocationTime);
                    }
                    if (subtotalRectificationTimePair.containsKey(teamDeptId)) {
                        BigDecimal rectificationTime = subtotalRectificationTimePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(rectificationTime);
                        tempNptSum = tempNptSum.add(rectificationTime);
                    }
                    if (subtotalWaitingStopTimePair.containsKey(teamDeptId)) {
                        BigDecimal waitingStopTime = subtotalWaitingStopTimePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(waitingStopTime);
                        tempNptSum = tempNptSum.add(waitingStopTime);
                    }
                    // 待命 不计入 非生产时效
                    if (subtotalWinterBreakTimePair.containsKey(teamDeptId)) {
                        BigDecimal winterBreakTime = subtotalWinterBreakTimePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(winterBreakTime);
                    }
                    if (subtotalPartyaDesignPair.containsKey(teamDeptId)) {
                        BigDecimal partyaDesign = subtotalPartyaDesignPair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(partyaDesign);
                        tempNptSum = tempNptSum.add(partyaDesign);
                    }
                    if (subtotalPartyaPreparePair.containsKey(teamDeptId)) {
                        BigDecimal partyaPrepare = subtotalPartyaPreparePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(partyaPrepare);
                        tempNptSum = tempNptSum.add(partyaPrepare);
                    }
                    if (subtotalPartyaResourcePair.containsKey(teamDeptId)) {
                        BigDecimal partyaResource = subtotalPartyaResourcePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(partyaResource);
                        tempNptSum = tempNptSum.add(partyaResource);
                    }
                    if (subtotalOtherNptTimePair.containsKey(teamDeptId)) {
                        BigDecimal otherNptTime = subtotalOtherNptTimePair.get(teamDeptId);
                        tempSubtotalNpt = tempSubtotalNpt.add(otherNptTime);
                        tempNptSum = tempNptSum.add(otherNptTime);
                    }
                    // 施工队伍 npt 小计
                    subtotalNptTimePair.put(teamDeptId, tempNptSum);
                    // 施工队伍 npt 时效 以下非生产时间计入非生产时效
                    // 设备故障repair_time
                    // 工程质量accident_time
                    // 技术受限complexity_time
                    // 生产组织rectification_time
                    // 不可抗力waiting_stop_time
                    // 甲方设计partya_design
                    // 甲方准备partya_prepare
                    // 甲方资源partya_resource
                    // 其他非生产时间other_npt_time
                    if (CollUtil.isNotEmpty(totalHours)) {
                        Long totalHour = totalHours.get(0);
                        if (ObjUtil.isNotEmpty(totalHour) && totalHour > 0) {
                            BigDecimal nptRate = tempNptSum.divide(new BigDecimal(totalHour), 4, RoundingMode.HALF_UP);
                            subtotalNptRatePair.put(teamDeptId, nptRate);
                        }
                    }
                });
            }

        }

        // 队伍数据  生成返回的数据列表集合
        teamDeptPair.forEach((teamDeptId, dept) -> {
            IotRdDailyReportNptStatisticsRespVO statistics = new IotRdDailyReportNptStatisticsRespVO();
            statistics.setTeamId(teamDeptId);
            statistics.setTeamName(dept.getName());
            statistics.setSort(dept.getSort());
            if (teamDeptIds.contains(teamDeptId)) {
                statistics.setType("3");
            }
            statistics.setAccidentTime(subtotalAccidentTimePair.get(teamDeptId));
            statistics.setRepairTime(subtotalRepairTimePair.get(teamDeptId));
            statistics.setSelfStopTime(subtotalSelfStopTimePair.get(teamDeptId));
            statistics.setComplexityTime(subtotalComplexityTimePair.get(teamDeptId));
            statistics.setRelocationTime(subtotalRelocationTimePair.get(teamDeptId));
            statistics.setRectificationTime(subtotalRectificationTimePair.get(teamDeptId));
            statistics.setWaitingStopTime(subtotalWaitingStopTimePair.get(teamDeptId));
            statistics.setWinterBreakTime(subtotalWinterBreakTimePair.get(teamDeptId));
            statistics.setPartyaDesign(subtotalPartyaDesignPair.get(teamDeptId));
            statistics.setPartyaPrepare(subtotalPartyaPreparePair.get(teamDeptId));
            statistics.setPartyaResource(subtotalPartyaResourcePair.get(teamDeptId));
            statistics.setOtherNptTime(subtotalOtherNptTimePair.get(teamDeptId));
            statistics.setNptTotal(subtotalNptTimePair.get(teamDeptId));
            statistics.setNptRate(subtotalNptRatePair.get(teamDeptId));
            if (CollUtil.isNotEmpty(totalHours)) {
                Long totalHour = totalHours.get(0);
                statistics.setCalendarTime(new BigDecimal(totalHour));
            }
            result.add(statistics);
        });

        return result;
    }

    /**
     * 计算两个LocalTime之间的间隔小时数（处理空值、跨天情况）
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 间隔小时数（保留2位小数），空值返回0
     */
    private BigDecimal calculateDurationHours(LocalTime startTime, LocalTime endTime) {
        // 处理空值情况：任意一个时间为空，返回0
        if (startTime == null || endTime == null) {
            return BigDecimal.ZERO;
        }

        // 计算时间间隔（秒）
        long seconds;
        if (endTime.isAfter(startTime)) {
            // 正常情况：结束时间在开始时间之后
            seconds = Duration.between(startTime, endTime).getSeconds();
        } else {
            // 跨天情况：结束时间在开始时间之前（如23:00到01:00）
            // 先计算到当天结束的秒数，再加次日开始到结束时间的秒数
            seconds = Duration.between(startTime, LocalTime.MAX).getSeconds() + 1; // 23:59:59到startTime的秒数+1秒=当天剩余秒数
            seconds += Duration.between(LocalTime.MIN, endTime).getSeconds();     // 次日00:00到endTime的秒数
        }

        // 转换为小时数（秒数 / 3600），保留2位小数
        BigDecimal hours = BigDecimal.valueOf(seconds).divide(BigDecimal.valueOf(3600), 2, BigDecimal.ROUND_HALF_UP);

        return hours;
    }

}