package cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreport;

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.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.pms.controller.admin.depttype.vo.IotDeptTypePageReqVO;
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.iotrydailyreport.vo.*;
import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreportdetail.vo.IotRyDailyReportDetailComboRespVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreportdetail.vo.IotRyDailyReportDetailPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreportdetail.vo.IotRyDailyReportDetailRespVO;
import cn.iocoder.yudao.module.pms.dal.dataobject.depttype.IotDeptTypeDO;
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.iotrydailyreport.IotRyDailyReportDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotrydailyreportdetail.IotRyDailyReportDetailDO;
import cn.iocoder.yudao.module.pms.dal.mysql.depttype.IotDeptTypeMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttask.IotProjectTaskMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotrydailyreport.IotRyDailyReportMapper;
import cn.iocoder.yudao.module.pms.dal.mysql.iotrydailyreportdetail.IotRyDailyReportDetailMapper;
import cn.iocoder.yudao.module.pms.service.iotprojectinfo.IotProjectInfoService;
import cn.iocoder.yudao.module.pms.service.iotprojecttask.IotProjectTaskService;
import cn.iocoder.yudao.module.pms.service.iotrydailyreport.IotRyDailyReportService;
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.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.dict.DictDataService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;

@Tag(name = "管理后台 - 瑞鹰日报")
@RestController
@RequestMapping("/pms/iot-ry-daily-report")
@Validated
public class IotRyDailyReportController {

    @Resource
    private IotRyDailyReportService iotRyDailyReportService;
    @Resource
    private DeptService deptService;
    @Resource
    private IotProjectInfoService iotProjectInfoService;
    @Resource
    private IotProjectTaskService iotProjectTaskService;
    @Resource
    private IotRyDailyReportMapper iotRyDailyReportMapper;
    @Resource
    private IotRyDailyReportDetailMapper iotRyDailyReportDetailMapper;
    @Resource
    private DictDataService dictDataService;
    @Resource
    private IotDeptTypeMapper iotDeptTypeMapper;
    @Resource
    private IotProjectTaskMapper iotProjectTaskMapper;

    @PostMapping("/create")
    @Operation(summary = "创建瑞鹰日报")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:create')")
    public CommonResult<Long> createIotRyDailyReport(@Valid @RequestBody IotRyDailyReportSaveReqVO createReqVO) {
        return success(iotRyDailyReportService.createIotRyDailyReport(createReqVO));
    }

    @PutMapping("/update")
    @Operation(summary = "更新瑞鹰日报")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:update')")
    public CommonResult<Boolean> updateIotRyDailyReport(@Valid @RequestBody IotRyDailyReportSaveReqVO updateReqVO) {
        iotRyDailyReportService.updateIotRyDailyReport(updateReqVO);
        return success(true);
    }

    @PostMapping("/repairHistoryProdStatus")
    @Operation(summary = "修复历史生产动态数据 生产动态明细拆分")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:update')")
    public CommonResult<Boolean> repairHistoryProdStatus() {
        iotRyDailyReportService.repairHistoryProdStatus();
        return success(true);
    }

    @PutMapping("/approval")
    @Operation(summary = "审批日报")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:update')")
    public CommonResult<Boolean> approvalRhDailyReport(@Valid @RequestBody IotRyDailyReportSaveReqVO updateReqVO) {
        iotRyDailyReportService.approvalRyDailyReport(updateReqVO);
        return success(true);
    }

    @DeleteMapping("/delete")
    @Operation(summary = "删除瑞鹰日报")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:delete')")
    public CommonResult<Boolean> deleteIotRyDailyReport(@RequestParam("id") Long id) {
        iotRyDailyReportService.deleteIotRyDailyReport(id);
        return success(true);
    }

    @GetMapping("/get")
    @Operation(summary = "获得瑞鹰日报")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<IotRyDailyReportRespVO> getIotRyDailyReport(@RequestParam("id") Long id) {
        IotRyDailyReportDO iotRyDailyReport = iotRyDailyReportService.getIotRyDailyReport(id);
        if (ObjUtil.isEmpty(iotRyDailyReport)) {
            return success(null);
        }
        IotRyDailyReportRespVO result = BeanUtils.toBean(iotRyDailyReport, IotRyDailyReportRespVO.class);
        if (ObjUtil.isNotEmpty(iotRyDailyReport.getDeptId())) {
            DeptDO dept = deptService.getDept(iotRyDailyReport.getDeptId());
            result.setDeptName(dept.getName());
        }
        // 查询日报对应的 项目 任务
        Long projectId = iotRyDailyReport.getProjectId();
        Long taskId = iotRyDailyReport.getTaskId();
        Long deptId = iotRyDailyReport.getDeptId();
        if (ObjUtil.isNotEmpty(projectId)) {
            IotProjectInfoDO project = iotProjectInfoService.getIotProjectInfo(projectId);
            result.setContractName(project.getContractName());
        }
        if (ObjUtil.isNotEmpty(taskId)) {
            IotProjectTaskDO task = iotProjectTaskService.getIotProjectTask(taskId);
            result.setTaskName(ObjUtil.isNotEmpty(task) ? task.getWellName() : StrUtil.EMPTY);
            result.setLocation(task.getLocation());
        }
        // 查询日报(deptId-taskId)上次填报的 当前井深
        IotRyDailyReportPageReqVO reqVO = new IotRyDailyReportPageReqVO();
        reqVO.setProjectClassification("1");
        reqVO.setDeptId(deptId);
        reqVO.setTaskId(taskId);
        List<IotRyDailyReportDO> currentTaskReports = iotRyDailyReportMapper.dailyReports(reqVO);
        // 查询日报的生产动态详情
        IotRyDailyReportDetailPageReqVO detailReqVO = new IotRyDailyReportDetailPageReqVO();
        detailReqVO.setReportId(iotRyDailyReport.getId());
        detailReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        PageResult<IotRyDailyReportDetailDO> reportDetailsPage = iotRyDailyReportDetailMapper.selectPage(detailReqVO);
        List<IotRyDailyReportDetailDO> reportDetails = reportDetailsPage.getList();
        if (CollUtil.isNotEmpty(reportDetails)) {
            List<IotRyDailyReportDetailRespVO> reportDetailsResp = BeanUtils.toBean(reportDetails, IotRyDailyReportDetailRespVO.class);
            // 按照生产动态明细 的 日期 时间段 正序排列
            reportDetailsResp.sort(Comparator.comparing(
                            // 处理 reportDate 为空的情况
                            (IotRyDailyReportDetailRespVO detail) -> {
                                if (detail.getReportDate() == null) {
                                    return null; // null 值会排在前面
                                }
                                return detail.getReportDate().toLocalDate();
                            },
                            // 自定义 null 值比较器：null 排在非 null 前面
                            Comparator.nullsFirst(Comparator.naturalOrder())
                    )
                    .thenComparing(
                            // 处理 startTime 为空的情况
                            IotRyDailyReportDetailRespVO::getStartTime,
                            // 自定义 null 值比较器：null 排在非 null 前面
                            Comparator.nullsFirst(Comparator.naturalOrder())
                    ));
            result.setReportDetails(reportDetailsResp);
        }
        // 按照创建时间倒序排列 取第2条记录对应的 当前井深 即 上次日报的当前井深
        if (CollUtil.isNotEmpty(currentTaskReports)) {
            currentTaskReports.sort(Comparator.comparing(IotRyDailyReportDO::getCreateTime));
            currentTaskReports.forEach(report -> {
                if (!id.equals(report.getId()) && (report.getId() < id)) {
                    result.setLastCurrentDepth(report.getCurrentDepth());
                }
            });
        }
        // 查询当前日报对应部门 未完工的任务列表 以方便 日报管理员 或 填报人员在审批拒绝后修改 任务井
        IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
        taskReqVO.setDeptId(iotRyDailyReport.getDeptId());
        taskReqVO.setJobFlag("Y");
        List<IotProjectTaskDO> tasks = iotProjectTaskMapper.selectList(taskReqVO);
        Map<Long, String> wellNamePair = new HashMap<>();
        // 没有关联任务井时返回空集合
        result.setWellNamePair(wellNamePair);
        if (CollUtil.isNotEmpty(tasks)) {
            tasks.forEach(task -> {
                wellNamePair.put(task.getId(), task.getWellName());
            });
        }
        if (ObjUtil.isNotEmpty(iotRyDailyReport.getTaskId())) {
            // 将当前日报已经关联的任务井也添加到返回的集合
            IotProjectTaskDO task = iotProjectTaskMapper.selectById(iotRyDailyReport.getTaskId());
            if (ObjUtil.isNotEmpty(task)) {
                wellNamePair.put(task.getId(), task.getWellName());
                if (CollUtil.isNotEmpty(task.getExtProperty())) {
                    List<IotTaskAttrModelProperty> taskAttrs = task.getExtProperty();
                    if (CollUtil.isNotEmpty(taskAttrs)) {
                        taskAttrs.forEach(attr -> {
                            if ("额定生产时间".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                result.setRatedProductionTime(new BigDecimal(attr.getActualValue()));
                            }
                        });
                    }
                }
            }
        }
        // 返回当前日报可以设置的任务井集合
        result.setWellNamePair(wellNamePair);
        return success(result);
    }

    @GetMapping("/ryDailyReportStatistics")
    @Operation(summary = "按照日期查询瑞鹰日报统计数据 已填报 未填报 数量")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<List<IotRyDailyReportStatisticsVO>> ryDailyReportStatistics(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        List<IotRyDailyReportStatisticsVO> statistics = iotRyDailyReportService.ryDailyReportStatistics(pageReqVO);
        return success(statistics);
    }

    @GetMapping("/ryUnReportDetails")
    @Operation(summary = "按照日期查询瑞鹰日报统计数据 未填报队伍明细")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<List<IotRyDailyReportStatisticsVO>> ryUnReportDetails(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        List<IotRyDailyReportStatisticsVO> statistics = iotRyDailyReportService.ryUnReportedDetails(pageReqVO);
        return success(statistics);
    }

    @GetMapping("/page")
    @Operation(summary = "获得瑞鹰日报分页")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<PageResult<IotRyDailyReportRespVO>> getIotRyDailyReportPage(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        // 移动端搜索条件 标识 合同名称 任务井号 ... 移动端兼容：searchKey 不为空时 自动填充未填的 contractName / taskName
        String searchKey = pageReqVO.getSearchKey();
        if (StrUtil.isNotBlank(searchKey)) {
            if (StrUtil.isBlank(pageReqVO.getContractName())) {
                pageReqVO.setContractName(searchKey);
            }
            if (StrUtil.isBlank(pageReqVO.getTaskName())) {
                pageReqVO.setTaskName(searchKey);
            }
        }

        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }

        PageResult<IotRyDailyReportDO> pageResult = iotRyDailyReportService.getIotRyDailyReportPage(pageReqVO);

        return success(new PageResult<>(buildRyDailyReports(pageResult.getList(), pageReqVO, "list"), pageResult.getTotal()));
    }

    @GetMapping("/productionBriefs")
    @Operation(summary = "瑞鹰看板 组合钻井修井日报生产动态简报")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<List<IotRyDailyReportRespVO>> productionBriefs(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        // 不区分钻井 修井 查询全部日报后再组装
        pageReqVO.setProjectClassification(StrUtil.EMPTY);
        List<IotRyDailyReportDO> reports = iotRyDailyReportService.productionBriefs(pageReqVO);

        return success(buildProductionBriefs(reports, pageReqVO));
    }

    @GetMapping("/teamReports")
    @Operation(summary = "根据井号查询瑞鹰日报列表 按照队伍分组统计工作量")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<PageResult<IotRyDailyReportRespVO>> teamReports(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        PageResult<IotRyDailyReportDO> pageResult = iotRyDailyReportService.teamReports(pageReqVO);
        return success(new PageResult<>(buildSubtotalRyDailyReports(pageResult.getList(), pageReqVO), pageResult.getTotal()));
    }

    @GetMapping("/wellReports")
    @Operation(summary = "根据井号查询瑞鹰日报列表 按照队伍分组统计工作量")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<PageResult<IotRyDailyReportRespVO>> wellReports(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        PageResult<IotRyDailyReportDO> pageResult = iotRyDailyReportService.wellReports(pageReqVO);
        return success(new PageResult<>(buildWellRyDailyReports(pageResult.getList(), pageReqVO), pageResult.getTotal()));
    }

    @GetMapping("/statistics")
    @Operation(summary = "获得瑞鹰日报汇总统计")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<Map<String, Object>> statistics(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        Map<String, Object> resultMap = new HashMap<>();
        List<IotRyDailyReportStatisticsRespVO> result = new ArrayList<>();
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        List<IotRyDailyReportStatisticsRespVO> statistics = iotRyDailyReportService.statistics(pageReqVO);
        result.addAll(statistics);
        resultMap.put("list", result);
        resultMap.put("total", result.size());
        return success(resultMap);
    }

    @GetMapping("/nptStatistics")
    @Operation(summary = "获得瑞鹰日报 非生产时效 统计")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<Map<String, Object>> nptStatistics(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        Map<String, Object> resultMap = new HashMap<>();
        List<IotRyDailyReportNptStatisticsRespVO> result = new ArrayList<>();
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        List<IotRyDailyReportNptStatisticsRespVO> statistics = iotRyDailyReportService.nptStatistics(pageReqVO);
        result.addAll(statistics);
        resultMap.put("list", result);
        resultMap.put("total", result.size());
        return success(resultMap);
    }

    private List<IotRyDailyReportRespVO> buildProductionBriefs(List<IotRyDailyReportDO> reports, IotRyDailyReportPageReqVO pageReqVO) {
        if (CollUtil.isEmpty(reports)) {
            return Collections.emptyList();
        }
        // 设备部门信息
        Map<Long, DeptDO> deptMap = deptService.getDeptMap(convertList(reports, IotRyDailyReportDO::getDeptId));
        // key项目id   value项目合同号
        Map<Long, String> projectPair = new HashMap<>();
        //  key任务id     value任务井号
        Map<Long, String> taskPair = new HashMap<>();
        //  key任务id     value任务施工区域
        Map<Long, String> taskLocationPair = new HashMap<>();
        // key任务id   value设计井深
        Map<Long, BigDecimal> taskExtPropertyPair = new HashMap<>();
        // key任务id   value额定生产时间
        Map<Long, BigDecimal> taskRatedProductionTimePair = new HashMap<>();
        // key任务id   value套生段产管尺寸
        Map<Long, String> taskCasingPipeSizePair = new HashMap<>();
        // key任务id   value井控级别
        Map<Long, String> taskWellControlLevelPair = new HashMap<>();
        // key任务id   value施工工艺
        Map<Long, String> taskTechniquePair = new HashMap<>();
        // key任务id   value主设备数量
        Map<Long, Integer> taskDeviceNumPair = new HashMap<>();
        // key任务id   value井别
        Map<Long, String> taskWellCategoryPair = new HashMap<>();
        // key任务id   value设计井身结构
        Map<Long, String> taskWellStructPair = new HashMap<>();
        // key部门id  value设备型号编号
        AtomicReference<Map<Long, String>> equipmentTypePair = new AtomicReference<>(new HashMap<>());
        // key施工队伍id    value最近完工日期
        Map<Long, LocalDateTime> latestWellDoneTimePair = new HashMap<>();
        // key施工队伍id    value施工井数量
        Map<Long, Integer> totalTasksPair = new HashMap<>();
        // key施工队伍id    value完工井数量
        Map<Long, Integer> completedTasksPair = new HashMap<>();
        // key非生产时间原因数据字典value   value非生产时间原因数据字典label
        Map<String, String> nptReasonPair = new HashMap<>();
        // 钻井 key施工状态数据字典value   value施工状态数据字典label
        Map<String, String> constructStatusPair = new HashMap<>();
        // 修井 key施工状态数据字典value   value施工状态数据字典label
        Map<String, String> repairStatusPair = new HashMap<>();
        // key日报id   value生产动态明细
        Map<Long, List<IotRyDailyReportDetailRespVO>> reportDetailsPair = new HashMap<>();

        // 钻井 施工状态 字典数据
        List<DictDataDO> rigStatusDictData = dictDataService.getDictDataListByDictType("rigStatus");
        // 修井 施工状态 字典数据
        List<DictDataDO> repairStatusDictData = dictDataService.getDictDataListByDictType("repairStatus");
        // 非生产时间原因 字典数据
        List<DictDataDO> nptReasonDictData = dictDataService.getDictDataListByDictType("ryNptReason");

        if (CollUtil.isNotEmpty(nptReasonDictData)) {
            nptReasonDictData.forEach(data -> {
                nptReasonPair.put(data.getValue(), data.getLabel());
            });
        }
        // 钻井施工状态
        if (CollUtil.isNotEmpty(rigStatusDictData)) {
            rigStatusDictData.forEach(data -> {
                constructStatusPair.put(data.getValue(), data.getLabel());
            });
        }
        // 修井施工状态
        if (CollUtil.isNotEmpty(repairStatusDictData)) {
            repairStatusDictData.forEach(data -> {
                repairStatusPair.put(data.getValue(), data.getLabel());
            });
        }

        // 查询瑞鹰下所有部门 施工队伍及对应的项目部
        Set<Long> deptIds = deptService.getChildDeptIdListFromCache(158l);
        DeptListReqVO deptReqVO = new DeptListReqVO();
        deptReqVO.setDeptIds(deptIds);
        List<DeptDO> departments = deptService.getDeptList(deptReqVO);
        Map<Long, String> projectNamePair = new HashMap<>();
        Set<Long> teamIds = new HashSet<>();
        // key队伍id   value项目部id
        Map<Long, Long> teamProjectPair = new HashMap<>();
        // key队伍id   value项目部名称
        Map<Long, String> teamProjectNamePair = new HashMap<>();
        // 项目部排序值
        Map<Long, Integer> projectSortPair = new HashMap<>();
        // 队伍排序值
        Map<Long, Integer> teamSortPair = new HashMap<>();
        // 找到每个施工队伍的上级部门
        if (CollUtil.isNotEmpty(departments)) {
            departments.forEach(dept -> {
                if ("2".equals(dept.getType())) {
                    projectNamePair.put(dept.getId(), dept.getName());
                    projectSortPair.put(dept.getId(), dept.getSort());
                }
                if ("3".equals(dept.getType())) {
                    teamIds.add(dept.getId());
                    teamProjectPair.put(dept.getId(), dept.getParentId());
                    teamSortPair.put(dept.getId(), dept.getSort());
                }
            });
            if (CollUtil.isNotEmpty(teamProjectPair)) {
                teamProjectPair.forEach((teamId, projectDeptId) -> {
                    if (projectNamePair.containsKey(projectDeptId)) {
                        teamProjectNamePair.put(teamId, projectNamePair.get(projectDeptId));
                    }
                });
            }
        }

        // 查询施工队伍属于钻井或修井
        Set<Long> zjDeptIds = new HashSet<>();
        Set<Long> xjDeptIds = new HashSet<>();
        IotDeptTypePageReqVO deptTypeReqVO = new IotDeptTypePageReqVO();
        List<IotDeptTypeDO> deptTypes = iotDeptTypeMapper.selectList(deptTypeReqVO);
        if (CollUtil.isNotEmpty(deptTypes)) {
            deptTypes.forEach(deptType -> {
                if ("zj".equals(deptType.getType())) {
                    zjDeptIds.add(deptType.getDeptId());
                }
                if ("xj".equals(deptType.getType())) {
                    xjDeptIds.add(deptType.getDeptId());
                }
            });
        }

        // 查询每个日报的生产动态明细
        IotRyDailyReportDetailPageReqVO detailReqVO = new IotRyDailyReportDetailPageReqVO();
        detailReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        detailReqVO.setReportIds(convertList(reports, IotRyDailyReportDO::getId));
        PageResult<IotRyDailyReportDetailDO> reportDetailsPage = iotRyDailyReportDetailMapper.selectPage(detailReqVO);
        List<IotRyDailyReportDetailDO> reportDetails = reportDetailsPage.getList();
        if (CollUtil.isNotEmpty(reportDetails)) {
            // 设置每个日报的生产动态明细
            reportDetails.forEach(detail -> {
                if (reportDetailsPair.containsKey(detail.getReportId())) {
                    List<IotRyDailyReportDetailRespVO> tempDetails = reportDetailsPair.get(detail.getReportId());
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                } else {
                    List<IotRyDailyReportDetailRespVO> tempDetails = new ArrayList<>();
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                }
            });
        }
        DataPermissionUtils.executeIgnore(() -> {
            // 查询日报关联的项目信息
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setProjectIds(convertList(reports, IotRyDailyReportDO::getProjectId));
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectPair.put(project.getId(), project.getContractName());
                });
            }
            // 查询日报关联的任务信息
            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
            taskReqVO.setTaskIds(convertList(reports, IotRyDailyReportDO::getTaskId));
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(taskReqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskPair.put(task.getId(), task.getWellName());
                    taskLocationPair.put(task.getId(), task.getLocation());
                    taskTechniquePair.put(task.getId(), task.getTechnique());
                    taskDeviceNumPair.put(task.getId(), task.getDeviceIds().size());
                });
            }
            // 查询当前日报所属施工队伍中包含 井架 的型号
            equipmentTypePair.set(iotRyDailyReportService.queryEquipmentType(convertList(reports, IotRyDailyReportDO::getDeptId)));
            // 查询当前日报所属部门的 上井次完井时间 已经绑定任务的情况下
            List<IotRyDailyReportDO> latestWellDoneTimes = iotRyDailyReportService.latestWellDoneTimes(null);
            if (CollUtil.isNotEmpty(latestWellDoneTimes)) {
                latestWellDoneTimes.forEach(time -> {
                    latestWellDoneTimePair.put(time.getDeptId(), time.getConstructionStartDate());
                });
            }
            // 按施工队伍统计 钻井日报 施工井数 完工井数
            List<IotRyDailyReportTaskCountVO> deptTasks = iotRyDailyReportService.countTasksByDept();
            // 修井日报 不存在既填写钻井日报 又填写修井日报的队伍
            if (ObjUtil.isNotEmpty(pageReqVO.getCreateTime())) {
                List<IotRyDailyReportTaskCountVO> repairDeptTasks = iotRyDailyReportService.countRepairTasksByDept(pageReqVO);
                if (CollUtil.isNotEmpty(repairDeptTasks)) {
                    repairDeptTasks.forEach(task -> {
                        totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                        completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                    });
                }
            }
            if (CollUtil.isNotEmpty(deptTasks)) {
                deptTasks.forEach(task -> {
                    totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                    completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                });
            }
        });

        List<IotRyDailyReportRespVO> resultList =BeanUtils.toBean(reports, IotRyDailyReportRespVO.class, (reportVO) -> {
            // 油耗 精确到 2位小数
            if (reportVO.getDailyFuel().compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal fuel = reportVO.getDailyFuel()
                        .divide(BigDecimal.valueOf(1), 2, RoundingMode.HALF_UP);
                reportVO.setDailyFuel(fuel);
            }
            // 将 ‘生产配合’ 的值赋值给 ‘其他非生产时间’
            if (reportVO.getRelocationTime().compareTo(BigDecimal.ZERO) > 0) {
                reportVO.setOtherProductionTime(reportVO.getRelocationTime());
            }
            // 计算非生产时效
            BigDecimal sumNpt = calculateNonProductTime(reportVO);
            // 非生产时间加和
            reportVO.setNonProductionTime(sumNpt);
            reportVO.setNonProductionRate(sumNpt.divide(BigDecimal.valueOf(24), 4, RoundingMode.HALF_UP ));

            // 日报生成日期 格式化时间为 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getCreateTime())) {
                reportVO.setCreateTimeStr(LocalDateTimeUtil.format(reportVO.getCreateTime(), DatePattern.NORM_DATE_PATTERN));
            }
            // 上井次完井时间 格式化时间为 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getLatestWellDoneTime())) {
                reportVO.setLatestWellDoneTimeStr(LocalDateTimeUtil.format(reportVO.getLatestWellDoneTime(), DatePattern.NORM_DATE_PATTERN));
            }
            // 2.1 拼接部门信息
            findAndThen(deptMap, reportVO.getDeptId(), dept -> reportVO.setDeptName(dept.getName()));
            // 队伍排序值
            findAndThen(teamSortPair, reportVO.getDeptId(), sort -> reportVO.setTeamSort(sort));
            // 设置项目部排序值
            if (teamProjectPair.containsKey(reportVO.getDeptId())) {
                Long projectDeptId = teamProjectPair.get(reportVO.getDeptId());
                if (projectSortPair.containsKey(projectDeptId)) {
                    reportVO.setProjectSort(projectSortPair.get(projectDeptId));
                }
            }
            // 设置 钻井 or 修井
            if (zjDeptIds.contains(reportVO.getDeptId())) {
                reportVO.setProjectClassification("钻井");
            }
            if (xjDeptIds.contains(reportVO.getDeptId())) {
                reportVO.setProjectClassification("修井");
            }
            // 主设备数量
            findAndThen(taskDeviceNumPair, reportVO.getTaskId(), deviceNum -> reportVO.setDeviceNum(deviceNum));
            // 项目部名称
            findAndThen(teamProjectNamePair, reportVO.getDeptId(), projectName -> reportVO.setProjectName(projectName));
            // 2.2 日报关联的项目信息
            findAndThen(projectPair, reportVO.getProjectId(), contractName -> reportVO.setContractName(contractName));
            // 2.3 日报关联的任务信息
            findAndThen(taskPair, reportVO.getTaskId(), taskName -> reportVO.setTaskName(taskName));
            // 日报关联的任务施工地点
            findAndThen(taskLocationPair, reportVO.getTaskId(), location -> reportVO.setLocation(location));
            // 2.7 上井次完井时间
            findAndThen(latestWellDoneTimePair, reportVO.getDeptId(), latestWellDoneTime -> reportVO.setLatestWellDoneTime(latestWellDoneTime));
            // 2.8 总施工井数 完工井数
            findAndThen(totalTasksPair, reportVO.getDeptId(), totalTaskCount -> reportVO.setTotalConstructionWells(totalTaskCount));
            findAndThen(completedTasksPair, reportVO.getDeptId(), completedTaskCount -> reportVO.setCompletedWells(completedTaskCount));
            // 钻井 施工状态 数据字典
            findAndThen(constructStatusPair, reportVO.getRigStatus(), statusLabel -> {
                if ("钻井".equals(reportVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
            // 修井 施工状态 数据字典
            findAndThen(repairStatusPair, reportVO.getRepairStatus(), statusLabel -> {
                if ("修井".equals(reportVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
        });

        // 2. 拼接数据
        return resultList.stream()
                .sorted(Comparator.comparing(IotRyDailyReportRespVO::getProjectClassification,
                                Comparator.comparingInt(s -> "钻井".equals(s) ? 0 : 1))  // 1. 钻井在前
                        .thenComparing(IotRyDailyReportRespVO::getProjectSort)     // 2. 项目部排序升序
                        .thenComparing(IotRyDailyReportRespVO::getTeamSort))       // 3. 队伍排序升序
                .collect(Collectors.toList());
    }

    /**
     * 瑞恒日报分页 设置关联查询信息
     * @param reports
     * @return
     */
    private List<IotRyDailyReportRespVO> buildRyDailyReports(List<IotRyDailyReportDO> reports, IotRyDailyReportPageReqVO pageReqVO, String type) {
        if (CollUtil.isEmpty(reports)) {
            return Collections.emptyList();
        }
        // 设备部门信息
        Map<Long, DeptDO> deptMap = deptService.getDeptMap(convertList(reports, IotRyDailyReportDO::getDeptId));
        // key项目id   value项目合同号
        Map<Long, String> projectPair = new HashMap<>();
        //  key任务id     value任务井号
        Map<Long, String> taskPair = new HashMap<>();
        //  key任务id     value任务施工区域
        Map<Long, String> taskLocationPair = new HashMap<>();
        // key任务id   value设计井深
        Map<Long, BigDecimal> taskExtPropertyPair = new HashMap<>();
        // key任务id   value额定生产时间
        Map<Long, BigDecimal> taskRatedProductionTimePair = new HashMap<>();
        // key任务id   value套生段产管尺寸
        Map<Long, String> taskCasingPipeSizePair = new HashMap<>();
        // key任务id   value井控级别
        Map<Long, String> taskWellControlLevelPair = new HashMap<>();
        // key任务id   value施工工艺
        Map<Long, String> taskTechniquePair = new HashMap<>();
        // key任务id   value井别
        Map<Long, String> taskWellCategoryPair = new HashMap<>();
        // key任务id   value设计井身结构
        Map<Long, String> taskWellStructPair = new HashMap<>();
        // key部门id  value设备型号编号
        AtomicReference<Map<Long, String>> equipmentTypePair = new AtomicReference<>(new HashMap<>());
        // key施工队伍id    value最近完工日期
        Map<Long, LocalDateTime> latestWellDoneTimePair = new HashMap<>();
        // key施工队伍id    value施工井数量
        Map<Long, Integer> totalTasksPair = new HashMap<>();
        // key施工队伍id    value完工井数量
        Map<Long, Integer> completedTasksPair = new HashMap<>();
        // key非生产时间原因数据字典value   value非生产时间原因数据字典label
        Map<String, String> nptReasonPair = new HashMap<>();
        // key施工状态数据字典value   value施工状态数据字典label
        Map<String, String> constructStatusPair = new HashMap<>();
        // key日报id   value生产动态明细
        Map<Long, List<IotRyDailyReportDetailRespVO>> reportDetailsPair = new HashMap<>();

        // 钻井 施工状态 字典数据
        List<DictDataDO> rigStatusDictData = dictDataService.getDictDataListByDictType("rigStatus");
        // 修井 施工状态 字典数据
        List<DictDataDO> repairStatusDictData = dictDataService.getDictDataListByDictType("repairStatus");
        // 非生产时间原因 字典数据
        List<DictDataDO> nptReasonDictData = dictDataService.getDictDataListByDictType("ryNptReason");

        if (CollUtil.isNotEmpty(nptReasonDictData)) {
            nptReasonDictData.forEach(data -> {
                nptReasonPair.put(data.getValue(), data.getLabel());
            });
        }
        // 钻井施工状态
        if ("1".equals(pageReqVO.getProjectClassification()) && CollUtil.isNotEmpty(rigStatusDictData)) {
            rigStatusDictData.forEach(data -> {
                constructStatusPair.put(data.getValue(), data.getLabel());
            });
        }
        // 修井施工状态
        if ("2".equals(pageReqVO.getProjectClassification()) && CollUtil.isNotEmpty(repairStatusDictData)) {
            repairStatusDictData.forEach(data -> {
                constructStatusPair.put(data.getValue(), data.getLabel());
            });
        }
        // 查询每个日报的生产动态明细
        IotRyDailyReportDetailPageReqVO detailReqVO = new IotRyDailyReportDetailPageReqVO();
        detailReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        detailReqVO.setReportIds(convertList(reports, IotRyDailyReportDO::getId));
        PageResult<IotRyDailyReportDetailDO> reportDetailsPage = iotRyDailyReportDetailMapper.selectPage(detailReqVO);
        List<IotRyDailyReportDetailDO> reportDetails = reportDetailsPage.getList();
        if (CollUtil.isNotEmpty(reportDetails)) {
            // 设置每个日报的生产动态明细
            reportDetails.forEach(detail -> {
                if (reportDetailsPair.containsKey(detail.getReportId())) {
                    List<IotRyDailyReportDetailRespVO> tempDetails = reportDetailsPair.get(detail.getReportId());
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                } else {
                    List<IotRyDailyReportDetailRespVO> tempDetails = new ArrayList<>();
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                }
            });
        }
        DataPermissionUtils.executeIgnore(() -> {
            // 查询日报关联的项目信息
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setProjectIds(convertList(reports, IotRyDailyReportDO::getProjectId));
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectPair.put(project.getId(), project.getContractName());
                });
            }
            // 查询日报关联的任务信息
            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
            taskReqVO.setTaskIds(convertList(reports, IotRyDailyReportDO::getTaskId));
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(taskReqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskPair.put(task.getId(), task.getWellName());
                    taskLocationPair.put(task.getId(), task.getLocation());
                    taskTechniquePair.put(task.getId(), task.getTechnique());
                    if (CollUtil.isNotEmpty(task.getExtProperty())) {
                        List<IotTaskAttrModelProperty> taskAttrs = task.getExtProperty();
                        if (CollUtil.isNotEmpty(taskAttrs)) {
                            // 找到 设计注气量 属性 对应的值
                            taskAttrs.forEach(attr -> {
                                if ("设计井深".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskExtPropertyPair.put(task.getId(), new BigDecimal(attr.getActualValue()));
                                }
                                if ("设计井身结构".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellStructPair.put(task.getId(), attr.getActualValue());
                                }
                                if ("额定生产时间".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskRatedProductionTimePair.put(task.getId(), new BigDecimal(attr.getActualValue()));
                                }
                                if ("套生段产管尺寸".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskCasingPipeSizePair.put(task.getId(), attr.getActualValue());
                                }
                                if ("井控级别".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellControlLevelPair.put(task.getId(), attr.getActualValue());
                                }
                                if ("井别".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellCategoryPair.put(task.getId(), attr.getActualValue());
                                }
                            });
                        }
                    }
                });
            }
            // 查询当前日报所属施工队伍中包含 井架 的型号
            equipmentTypePair.set(iotRyDailyReportService.queryEquipmentType(convertList(reports, IotRyDailyReportDO::getDeptId)));
            // 查询当前日报所属部门的 上井次完井时间 已经绑定任务的情况下
            List<IotRyDailyReportDO> latestWellDoneTimes = iotRyDailyReportService.latestWellDoneTimes(null);
            if (CollUtil.isNotEmpty(latestWellDoneTimes)) {
                latestWellDoneTimes.forEach(time -> {
                    latestWellDoneTimePair.put(time.getDeptId(), time.getConstructionStartDate());
                });
            }
            // 按施工队伍统计 钻井日报 施工井数 完工井数
            List<IotRyDailyReportTaskCountVO> deptTasks = iotRyDailyReportService.countTasksByDept();
            // 修井日报 不存在既填写钻井日报 又填写修井日报的队伍
            if (ObjUtil.isNotEmpty(pageReqVO.getCreateTime())) {
                List<IotRyDailyReportTaskCountVO> repairDeptTasks = iotRyDailyReportService.countRepairTasksByDept(pageReqVO);
                if (CollUtil.isNotEmpty(repairDeptTasks)) {
                    repairDeptTasks.forEach(task -> {
                        totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                        completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                    });
                }
            }
            if (CollUtil.isNotEmpty(deptTasks)) {
                deptTasks.forEach(task -> {
                    totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                    completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                });
            }
        });
        // 2. 拼接数据
        return BeanUtils.toBean(reports, IotRyDailyReportRespVO.class, (reportVO) -> {
            // 油耗 精确到 2位小数
            if (reportVO.getDailyFuel().compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal fuel = reportVO.getDailyFuel()
                        .divide(BigDecimal.valueOf(1), 2, RoundingMode.HALF_UP);
                reportVO.setDailyFuel(fuel);
            }
            // 将 ‘生产配合’ 的值赋值给 ‘其他非生产时间’
            if (reportVO.getRelocationTime().compareTo(BigDecimal.ZERO) > 0) {
                reportVO.setOtherProductionTime(reportVO.getRelocationTime());
            }
            // 计算非生产时效
            BigDecimal sumNpt = calculateNonProductTime(reportVO);
            reportVO.setNonProductionRate(sumNpt.divide(BigDecimal.valueOf(24), 4, RoundingMode.HALF_UP ));

            // 非生产时效 百分比格式 导出使用
            if (ObjUtil.isNotEmpty(reportVO.getNonProductionRate())) {
                // 获取原始小数
                BigDecimal nptRate = reportVO.getNonProductionRate();
                // 乘以100转换为百分比数值
                BigDecimal percentage = nptRate.multiply(BigDecimal.valueOf(100));
                // 格式化保留2位小数
                DecimalFormat df = new DecimalFormat("0.00");
                String nonProductionRate = df.format(percentage) + "%";
                // 赋值
                reportVO.setNonProductionRateFormat(nonProductionRate);
            }

            // 日报生成日期 格式化时间为 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getCreateTime())) {
                reportVO.setCreateTimeStr(LocalDateTimeUtil.format(reportVO.getCreateTime(), DatePattern.NORM_DATE_PATTERN));
            }
            // 上井次完井时间 格式化时间为 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getLatestWellDoneTime())) {
                reportVO.setLatestWellDoneTimeStr(LocalDateTimeUtil.format(reportVO.getLatestWellDoneTime(), DatePattern.NORM_DATE_PATTERN));
            }
            // 2.1 拼接部门信息
            findAndThen(deptMap, reportVO.getDeptId(), dept -> reportVO.setDeptName(dept.getName()));
            // 2.2 日报关联的项目信息
            findAndThen(projectPair, reportVO.getProjectId(), contractName -> reportVO.setContractName(contractName));
            // 2.3 日报关联的任务信息
            findAndThen(taskPair, reportVO.getTaskId(), taskName -> reportVO.setTaskName(taskName));
            // 日报关联的任务施工地点
            findAndThen(taskLocationPair, reportVO.getTaskId(), location -> reportVO.setLocation(location));
            // 2.4 设计井深
            findAndThen(taskExtPropertyPair, reportVO.getTaskId(), wellDepth -> reportVO.setDesignWellDepth(wellDepth));
            // 生产动态明细
            findAndThen(reportDetailsPair, reportVO.getId(), details -> {
                // 每个日报的明细 根据 reportDate+startTime 正序排列
                details.sort(Comparator.comparing(
                                // 处理 reportDate 为空的情况
                                (IotRyDailyReportDetailRespVO detail) -> {
                                    if (detail.getReportDate() == null) {
                                        return null; // null 值会排在前面
                                    }
                                    return detail.getReportDate().toLocalDate();
                                },
                                // 自定义 null 值比较器：null 排在非 null 前面
                                Comparator.nullsFirst(Comparator.naturalOrder())
                        )
                        .thenComparing(
                                // 处理 startTime 为空的情况
                                IotRyDailyReportDetailRespVO::getStartTime,
                                // 自定义 null 值比较器：null 排在非 null 前面
                                Comparator.nullsFirst(Comparator.naturalOrder())
                        ));
                reportVO.setReportDetails(details);
            });
            // 施工工艺
            findAndThen(taskTechniquePair, reportVO.getTaskId(), technique -> reportVO.setTechnique(technique));
            // 井别
            findAndThen(taskWellCategoryPair, reportVO.getTaskId(), wellCategory -> reportVO.setWellCategory(wellCategory));
            // 额定生产时间
            findAndThen(taskRatedProductionTimePair, reportVO.getTaskId(), ratedProductionTime -> reportVO.setRatedProductionTime(ratedProductionTime));
            // 套生段产管尺寸
            findAndThen(taskCasingPipeSizePair, reportVO.getTaskId(), casingPipeSize -> reportVO.setCasingPipeSize(casingPipeSize));
            // 井控级别
            findAndThen(taskWellControlLevelPair, reportVO.getTaskId(), wellControlLevel -> reportVO.setWellControlLevel(wellControlLevel));
            // 2.5 设计井身结构
            findAndThen(taskWellStructPair, reportVO.getTaskId(), wellStruct -> reportVO.setDesignWellStruct(wellStruct));
            // 2.6 设备型号
            findAndThen(equipmentTypePair.get(), reportVO.getDeptId(), equipmentType -> reportVO.setEquipmentType(equipmentType));
            // 2.7 上井次完井时间
            findAndThen(latestWellDoneTimePair, reportVO.getDeptId(), latestWellDoneTime -> reportVO.setLatestWellDoneTime(latestWellDoneTime));
            // 2.8 总施工井数 完工井数
            findAndThen(totalTasksPair, reportVO.getDeptId(), totalTaskCount -> reportVO.setTotalConstructionWells(totalTaskCount));
            findAndThen(completedTasksPair, reportVO.getDeptId(), completedTaskCount -> reportVO.setCompletedWells(completedTaskCount));
            // 钻井 施工状态 数据字典
            findAndThen(constructStatusPair, reportVO.getRigStatus(), statusLabel -> {
                if ("1".equals(pageReqVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
            // 修井 施工状态 数据字典
            findAndThen(constructStatusPair, reportVO.getRepairStatus(), statusLabel -> {
                if ("2".equals(pageReqVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
            // 非生产时间原因 数据字典
            findAndThen(nptReasonPair, reportVO.getRyNptReason(), statusLabel -> reportVO.setRyNptReason(statusLabel));
        });
    }

    /**
     * 瑞恒日报分页 设置关联查询信息
     * @param reports
     * @return
     */
    private List<IotRyDailyReportDetailComboRespVO> buildRyDailyReportDetails(List<IotRyDailyReportDO> reports, IotRyDailyReportPageReqVO pageReqVO, String type) {
        if (CollUtil.isEmpty(reports)) {
            return Collections.emptyList();
        }
        // 设备部门信息
        Map<Long, DeptDO> deptMap = deptService.getDeptMap(convertList(reports, IotRyDailyReportDO::getDeptId));
        // key项目id   value项目合同号
        Map<Long, String> projectPair = new HashMap<>();
        //  key任务id     value任务井号
        Map<Long, String> taskPair = new HashMap<>();
        //  key任务id     value任务施工区域
        Map<Long, String> taskLocationPair = new HashMap<>();
        // key任务id   value设计井深
        Map<Long, BigDecimal> taskExtPropertyPair = new HashMap<>();
        // key任务id   value额定生产时间
        Map<Long, BigDecimal> taskRatedProductionTimePair = new HashMap<>();
        // key任务id   value套生段产管尺寸
        Map<Long, String> taskCasingPipeSizePair = new HashMap<>();
        // key任务id   value井控级别
        Map<Long, String> taskWellControlLevelPair = new HashMap<>();
        // key任务id   value施工工艺
        Map<Long, String> taskTechniquePair = new HashMap<>();
        // key任务id   value井别
        Map<Long, String> taskWellCategoryPair = new HashMap<>();
        // key任务id   value设计井身结构
        Map<Long, String> taskWellStructPair = new HashMap<>();
        // key部门id  value设备型号编号
        AtomicReference<Map<Long, String>> equipmentTypePair = new AtomicReference<>(new HashMap<>());
        // key施工队伍id    value最近完工日期
        Map<Long, LocalDateTime> latestWellDoneTimePair = new HashMap<>();
        // key施工队伍id    value施工井数量
        Map<Long, Integer> totalTasksPair = new HashMap<>();
        // key施工队伍id    value完工井数量
        Map<Long, Integer> completedTasksPair = new HashMap<>();
        // key非生产时间原因数据字典value   value非生产时间原因数据字典label
        Map<String, String> nptReasonPair = new HashMap<>();
        // key施工状态数据字典value   value施工状态数据字典label
        Map<String, String> constructStatusPair = new HashMap<>();
        // key日报id   value生产动态明细
        Map<Long, List<IotRyDailyReportDetailRespVO>> reportDetailsPair = new HashMap<>();

        // 钻井 施工状态 字典数据
        List<DictDataDO> rigStatusDictData = dictDataService.getDictDataListByDictType("rigStatus");
        // 修井 施工状态 字典数据
        List<DictDataDO> repairStatusDictData = dictDataService.getDictDataListByDictType("repairStatus");
        // 非生产时间原因 字典数据
        List<DictDataDO> nptReasonDictData = dictDataService.getDictDataListByDictType("ryNptReason");

        if (CollUtil.isNotEmpty(nptReasonDictData)) {
            nptReasonDictData.forEach(data -> {
                nptReasonPair.put(data.getValue(), data.getLabel());
            });
        }
        // 钻井施工状态
        if ("1".equals(pageReqVO.getProjectClassification()) && CollUtil.isNotEmpty(rigStatusDictData)) {
            rigStatusDictData.forEach(data -> {
                constructStatusPair.put(data.getValue(), data.getLabel());
            });
        }
        // 修井施工状态
        if ("2".equals(pageReqVO.getProjectClassification()) && CollUtil.isNotEmpty(repairStatusDictData)) {
            repairStatusDictData.forEach(data -> {
                constructStatusPair.put(data.getValue(), data.getLabel());
            });
        }
        // 查询每个日报的生产动态明细
        IotRyDailyReportDetailPageReqVO detailReqVO = new IotRyDailyReportDetailPageReqVO();
        detailReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        detailReqVO.setReportIds(convertList(reports, IotRyDailyReportDO::getId));
        PageResult<IotRyDailyReportDetailDO> reportDetailsPage = iotRyDailyReportDetailMapper.selectPage(detailReqVO);
        List<IotRyDailyReportDetailDO> reportDetails = reportDetailsPage.getList();
        if (CollUtil.isNotEmpty(reportDetails)) {
            // 设置每个日报的生产动态明细
            reportDetails.forEach(detail -> {
                if (reportDetailsPair.containsKey(detail.getReportId())) {
                    List<IotRyDailyReportDetailRespVO> tempDetails = reportDetailsPair.get(detail.getReportId());
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                } else {
                    List<IotRyDailyReportDetailRespVO> tempDetails = new ArrayList<>();
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                }
            });
        }
        DataPermissionUtils.executeIgnore(() -> {
            // 查询日报关联的项目信息
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setProjectIds(convertList(reports, IotRyDailyReportDO::getProjectId));
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectPair.put(project.getId(), project.getContractName());
                });
            }
            // 查询日报关联的任务信息
            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
            taskReqVO.setTaskIds(convertList(reports, IotRyDailyReportDO::getTaskId));
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(taskReqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskPair.put(task.getId(), task.getWellName());
                    taskLocationPair.put(task.getId(), task.getLocation());
                    taskTechniquePair.put(task.getId(), task.getTechnique());
                    if (CollUtil.isNotEmpty(task.getExtProperty())) {
                        List<IotTaskAttrModelProperty> taskAttrs = task.getExtProperty();
                        if (CollUtil.isNotEmpty(taskAttrs)) {
                            // 找到 设计注气量 属性 对应的值
                            taskAttrs.forEach(attr -> {
                                if ("设计井深".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskExtPropertyPair.put(task.getId(), new BigDecimal(attr.getActualValue()));
                                }
                                if ("设计井身结构".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellStructPair.put(task.getId(), attr.getActualValue());
                                }
                                if ("额定生产时间".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskRatedProductionTimePair.put(task.getId(), new BigDecimal(attr.getActualValue()));
                                }
                                if ("套生段产管尺寸".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskCasingPipeSizePair.put(task.getId(), attr.getActualValue());
                                }
                                if ("井控级别".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellControlLevelPair.put(task.getId(), attr.getActualValue());
                                }
                                if ("井别".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellCategoryPair.put(task.getId(), attr.getActualValue());
                                }
                            });
                        }
                    }
                });
            }
            // 查询当前日报所属施工队伍中包含 井架 的型号
            equipmentTypePair.set(iotRyDailyReportService.queryEquipmentType(convertList(reports, IotRyDailyReportDO::getDeptId)));
            // 查询当前日报所属部门的 上井次完井时间 已经绑定任务的情况下
            List<IotRyDailyReportDO> latestWellDoneTimes = iotRyDailyReportService.latestWellDoneTimes(null);
            if (CollUtil.isNotEmpty(latestWellDoneTimes)) {
                latestWellDoneTimes.forEach(time -> {
                    latestWellDoneTimePair.put(time.getDeptId(), time.getConstructionStartDate());
                });
            }
            // 按施工队伍统计 钻井日报 施工井数 完工井数
            List<IotRyDailyReportTaskCountVO> deptTasks = iotRyDailyReportService.countTasksByDept();
            // 修井日报 不存在既填写钻井日报 又填写修井日报的队伍
            if (ObjUtil.isNotEmpty(pageReqVO.getCreateTime())) {
                List<IotRyDailyReportTaskCountVO> repairDeptTasks = iotRyDailyReportService.countRepairTasksByDept(pageReqVO);
                if (CollUtil.isNotEmpty(repairDeptTasks)) {
                    repairDeptTasks.forEach(task -> {
                        totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                        completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                    });
                }
            }
            if (CollUtil.isNotEmpty(deptTasks)) {
                deptTasks.forEach(task -> {
                    totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                    completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                });
            }
        });

        // 以生产动态明细数据为维度 追加主表数据
        List<IotRyDailyReportDetailComboRespVO> reportDetailCombos = new ArrayList<>();
        if (CollUtil.isNotEmpty(reports)) {
            reports.forEach(report -> {
                // 查询每个日报对应的明细数据，按照时间段正序排列
                if (reportDetailsPair.containsKey(report.getId())) {
                    // 日报存在明细记录
                    List<IotRyDailyReportDetailRespVO> tempReportDetails = reportDetailsPair.get(report.getId());
                    // 将日报生产动态明细 按照 日期-时间段 正序排列
                    tempReportDetails.sort(Comparator.comparing(
                                    // 处理 reportDate 为空的情况
                                    (IotRyDailyReportDetailRespVO detail) -> {
                                        if (detail.getReportDate() == null) {
                                            return null; // null 值会排在前面
                                        }
                                        return detail.getReportDate().toLocalDate();
                                    },
                                    // 自定义 null 值比较器：null 排在非 null 前面
                                    Comparator.nullsFirst(Comparator.naturalOrder())
                            )
                            .thenComparing(
                                    // 处理 startTime 为空的情况
                                    IotRyDailyReportDetailRespVO::getStartTime,
                                    // 自定义 null 值比较器：null 排在非 null 前面
                                    Comparator.nullsFirst(Comparator.naturalOrder())
                            ));
                    // 遍历明细 追加主表数据
                    tempReportDetails.forEach(detail -> {
                        IotRyDailyReportDetailComboRespVO tempDetailCombo = new IotRyDailyReportDetailComboRespVO();
                        BeanUtils.copyProperties(detail, tempDetailCombo);
                        BeanUtils.copyProperties(report, tempDetailCombo);
                        // 转换生产动态明细中的 开始时间 结束时间
                        tempDetailCombo.setReportDateStr(LocalDateTimeUtil.format(detail.getReportDate(), DatePattern.NORM_DATE_PATTERN));
                        tempDetailCombo.setStartTimeStr(detail.getStartTime().toString());
                        tempDetailCombo.setEndTimeStr(detail.getEndTime().toString());
                        reportDetailCombos.add(tempDetailCombo);
                    });
                } else {
                    // 日报不存在明细记录 历史数据
                    IotRyDailyReportDetailComboRespVO tempDetailCombo = new IotRyDailyReportDetailComboRespVO();
                    BeanUtils.copyProperties(report, tempDetailCombo);
                    reportDetailCombos.add(tempDetailCombo);
                }
            });
        }

        // 2. 拼接数据
        return BeanUtils.toBean(reportDetailCombos, IotRyDailyReportDetailComboRespVO.class, (reportVO) -> {
            // 油耗 精确到 2位小数
            if (reportVO.getDailyFuel().compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal fuel = reportVO.getDailyFuel()
                        .divide(BigDecimal.valueOf(1), 2, RoundingMode.HALF_UP);
                reportVO.setDailyFuel(fuel);
            }
            // 将 ‘生产配合’ 的值赋值给 ‘其他非生产时间’
            if (reportVO.getRelocationTime().compareTo(BigDecimal.ZERO) > 0) {
                reportVO.setOtherProductionTime(reportVO.getRelocationTime());
            }
            // 计算非生产时效
            BigDecimal sumNpt = calDetailNonProductTime(reportVO);
            reportVO.setNonProductionRate(sumNpt.divide(BigDecimal.valueOf(24), 4, RoundingMode.HALF_UP ));

            // 非生产时效 百分比格式 导出使用
            if (ObjUtil.isNotEmpty(reportVO.getNonProductionRate())) {
                // 获取原始小数
                BigDecimal nptRate = reportVO.getNonProductionRate();
                // 乘以100转换为百分比数值
                BigDecimal percentage = nptRate.multiply(BigDecimal.valueOf(100));
                // 格式化保留2位小数
                DecimalFormat df = new DecimalFormat("0.00");
                String nonProductionRate = df.format(percentage) + "%";
                // 赋值
                reportVO.setNonProductionRateFormat(nonProductionRate);
            }
            // 定额时效
            if (ObjUtil.isNotEmpty(reportVO.getAllocatedEfficiency())) {
                BigDecimal percentage = reportVO.getAllocatedEfficiency().multiply(BigDecimal.valueOf(100));
                // 格式化保留2位小数
                DecimalFormat df = new DecimalFormat("0.00");
                String rate = df.format(percentage) + "%";
                reportVO.setAllocatedEfficiencyStr(rate);
            }
            // 日报生成日期 格式化时间为 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getCreateTime())) {
                reportVO.setCreateTimeStr(LocalDateTimeUtil.format(reportVO.getCreateTime(), DatePattern.NORM_DATE_PATTERN));
            }
            // 上井次完井时间 格式化时间为 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getLatestWellDoneTime())) {
                reportVO.setLatestWellDoneTimeStr(LocalDateTimeUtil.format(reportVO.getLatestWellDoneTime(), DatePattern.NORM_DATE_PATTERN));
            }
            // 2.1 拼接部门信息
            findAndThen(deptMap, reportVO.getDeptId(), dept -> reportVO.setDeptName(dept.getName()));
            // 2.2 日报关联的项目信息
            findAndThen(projectPair, reportVO.getProjectId(), contractName -> reportVO.setContractName(contractName));
            // 2.3 日报关联的任务信息
            findAndThen(taskPair, reportVO.getTaskId(), taskName -> reportVO.setTaskName(taskName));
            // 日报关联的任务施工地点
            findAndThen(taskLocationPair, reportVO.getTaskId(), location -> reportVO.setLocation(location));
            // 2.4 设计井深
            findAndThen(taskExtPropertyPair, reportVO.getTaskId(), wellDepth -> reportVO.setDesignWellDepth(wellDepth));

            // 额定生产时间
            findAndThen(taskRatedProductionTimePair, reportVO.getTaskId(), ratedProductionTime -> reportVO.setRatedProductionTime(ratedProductionTime));
            // 套生段产管尺寸
            findAndThen(taskCasingPipeSizePair, reportVO.getTaskId(), casingPipeSize -> reportVO.setCasingPipeSize(casingPipeSize));
            // 井控级别
            findAndThen(taskWellControlLevelPair, reportVO.getTaskId(), wellControlLevel -> reportVO.setWellControlLevel(wellControlLevel));
            // 2.5 设计井身结构
            findAndThen(taskWellStructPair, reportVO.getTaskId(), wellStruct -> reportVO.setDesignWellStruct(wellStruct));
            // 2.6 设备型号
            findAndThen(equipmentTypePair.get(), reportVO.getDeptId(), equipmentType -> reportVO.setEquipmentType(equipmentType));
            // 2.7 上井次完井时间
            findAndThen(latestWellDoneTimePair, reportVO.getDeptId(), latestWellDoneTime -> reportVO.setLatestWellDoneTime(latestWellDoneTime));
            // 2.8 总施工井数 完工井数
            findAndThen(totalTasksPair, reportVO.getDeptId(), totalTaskCount -> reportVO.setTotalConstructionWells(totalTaskCount));
            findAndThen(completedTasksPair, reportVO.getDeptId(), completedTaskCount -> reportVO.setCompletedWells(completedTaskCount));
            // 钻井 施工状态 数据字典
            findAndThen(constructStatusPair, reportVO.getRigStatus(), statusLabel -> {
                if ("1".equals(pageReqVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
            // 修井 施工状态 数据字典
            findAndThen(constructStatusPair, reportVO.getRepairStatus(), statusLabel -> {
                if ("2".equals(pageReqVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
            // 非生产时间原因 数据字典
            findAndThen(nptReasonPair, reportVO.getRyNptReason(), statusLabel -> reportVO.setRyNptReason(statusLabel));
        });
    }

    /**
     *  计算非生产时间累加值 计算非生产时效
     *  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(IotRyDailyReportRespVO 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);
    }

    /**
     *  计算非生产时间累加值 计算非生产时效
     *  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 calDetailNonProductTime(IotRyDailyReportDetailComboRespVO 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);
    }

    /**
     * 瑞鹰日报分页 设置关联查询信息
     * @param reports
     * @return
     */
    private List<IotRyDailyReportRespVO> buildSubtotalRyDailyReports(List<IotRyDailyReportDO> reports, IotRyDailyReportPageReqVO pageReqVO) {
        if (CollUtil.isEmpty(reports)) {
            return Collections.emptyList();
        }
        // 设备部门信息
        Map<Long, DeptDO> deptMap = deptService.getDeptMap(convertList(reports, IotRyDailyReportDO::getDeptId));
        // key项目id   value项目合同号
        Map<Long, String> projectPair = new HashMap<>();
        //  key任务id     value任务井号-施工区域
        Map<Long, String> taskPair = new HashMap<>();
        // key任务id   value设计井深
        Map<Long, BigDecimal> taskExtPropertyPair = new HashMap<>();
        // key任务id   value额定生产时间
        Map<Long, BigDecimal> taskRatedProductionTimePair = new HashMap<>();
        // key任务id   value套生段产管尺寸
        Map<Long, String> taskCasingPipeSizePair = new HashMap<>();
        // key任务id   value井控级别
        Map<Long, String> taskWellControlLevelPair = new HashMap<>();
        // key任务id   value施工工艺
        Map<Long, String> taskTechniquePair = new HashMap<>();
        // key任务id   value井别
        Map<Long, String> taskWellCategoryPair = new HashMap<>();
        // key任务id   value设计井身结构
        Map<Long, String> taskWellStructPair = new HashMap<>();
        // key部门id  value设备型号编号
        AtomicReference<Map<Long, String>> equipmentTypePair = new AtomicReference<>(new HashMap<>());
        // key施工队伍id    value最近完工日期
        Map<Long, LocalDateTime> latestWellDoneTimePair = new HashMap<>();
        // key施工队伍id    value施工井数量
        Map<Long, Integer> totalTasksPair = new HashMap<>();
        // key施工队伍id    value完工井数量
        Map<Long, Integer> completedTasksPair = new HashMap<>();

        // key部门id-队伍id   value 队伍累计进尺
        Map<String, BigDecimal> groupFootagePair = new HashMap<>();
        // key部门id-队伍id   value 队伍当年累计用电量
        Map<String, BigDecimal> groupPowerPair = new HashMap<>();
        // key部门id-队伍id   value 队伍当年累计油耗
        Map<String, BigDecimal> groupFuelPair = new HashMap<>();
        // key部门id-队伍id    value 进尺工作时间小计
        Map<String, BigDecimal> groupFootageTimePair = new HashMap<>();
        // key部门id-队伍id    value 钻井 其他生产时间小计
        Map<String, BigDecimal> groupOtherProductTimePair = new HashMap<>();
        // key部门id-队伍id    value 修井 生产时间小计
        Map<String, BigDecimal> groupProductTimePair = new HashMap<>();
        // key部门id-队伍id    value 修井 非生产时间小计
        Map<String, BigDecimal> groupNoProductTimePair = new HashMap<>();
        // key部门id-队伍id    value 钻井非生产时间小计
        Map<String, BigDecimal> groupZjNoProductTimePair = new HashMap<>();
        // key部门id-队伍id    value 小计平均运行时效
        Map<String, BigDecimal> groupTransTimePair = new HashMap<>();

        // key小组最后1条记录id   value 队伍累计进尺
        Map<Long, BigDecimal> groupIdFootagePair = new HashMap<>();
        // key小组最后1条记录id   value 队伍累计用电量
        Map<Long, BigDecimal> groupIdPowerPair = new HashMap<>();
        // key小组最后1条记录id   value 队伍累计油耗
        Map<Long, BigDecimal> groupIdFuelPair = new HashMap<>();
        // key小组最后1条记录id    value 进尺工作时间小计
        Map<Long, BigDecimal> groupIdFootageTimePair = new HashMap<>();
        // key小组最后1条记录id    value 钻井 其他生产时间小计
        Map<Long, BigDecimal> groupIdOtherProductTimePair = new HashMap<>();
        // key小组最后1条记录id    value 修井 生产时间小计
        Map<Long, BigDecimal> groupIdProductTimePair = new HashMap<>();
        // key小组最后1条记录id    value 修井 非生产时间小计
        Map<Long, BigDecimal> groupIdNoProductTimePair = new HashMap<>();
        // key小组最后1条记录id    value 钻井 非生产时间小计
        Map<Long, BigDecimal> groupIdZjNoProductTimePair = new HashMap<>();
        // key小组最后1条记录id    value 小计平均运行时效
        Map<Long, BigDecimal> groupIdTransTimePair = new HashMap<>();
        // key日报id   value生产动态明细
        Map<Long, List<IotRyDailyReportDetailRespVO>> reportDetailsPair = new HashMap<>();

        // 查询 当前分页 所有部门 任务井 日报数据 小计工作量
        IotRyDailyReportPageReqVO currentTaskReqVO = new IotRyDailyReportPageReqVO();
        currentTaskReqVO.setTaskIds(convertSet(reports, IotRyDailyReportDO::getTaskId));
        currentTaskReqVO.setDeptIds(convertSet(reports, IotRyDailyReportDO::getDeptId));
        currentTaskReqVO.setCreateTime(pageReqVO.getCreateTime());
        currentTaskReqVO.setProjectIds(pageReqVO.getProjectIds());
        currentTaskReqVO.setTaskIds(pageReqVO.getTaskIds());
        List<IotRyDailyReportDO> deptTaskDailyReports = iotRyDailyReportMapper.dailyReports(currentTaskReqVO);

        // 以 deptId-taskId 为唯一键 小计日报工作量
        if (CollUtil.isNotEmpty(deptTaskDailyReports)) {
            deptTaskDailyReports.forEach(report -> {
                String uniqueKey = StrUtil.join("-", report.getDeptId(), report.getTaskId());
                // 进尺
                if (groupFootagePair.containsKey(uniqueKey)) {
                    BigDecimal tempFootage = groupFootagePair.get(uniqueKey);
                    BigDecimal tempResult = tempFootage.add(report.getDailyFootage());
                    groupFootagePair.put(uniqueKey, tempResult);
                } else {
                    groupFootagePair.put(uniqueKey, report.getDailyFootage());
                }
                // 进尺工作时间
                if (groupFootageTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempFootageTime = groupFootageTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempFootageTime.add(report.getDrillingWorkingTime());
                    groupFootageTimePair.put(uniqueKey, tempResult);
                } else {
                    groupFootageTimePair.put(uniqueKey, report.getDrillingWorkingTime());
                }
                // 电量
                if (groupPowerPair.containsKey(uniqueKey)) {
                    BigDecimal tempPower = groupPowerPair.get(uniqueKey);
                    BigDecimal tempResult = tempPower.add(report.getDailyPowerUsage());
                    groupPowerPair.put(uniqueKey, tempResult);
                } else {
                    groupPowerPair.put(uniqueKey, report.getDailyPowerUsage());
                }
                // 油耗
                if (groupFuelPair.containsKey(uniqueKey)) {
                    BigDecimal tempFuel = groupFuelPair.get(uniqueKey);
                    BigDecimal tempResult = tempFuel.add(report.getDailyFuel());
                    groupFuelPair.put(uniqueKey, tempResult);
                } else {
                    groupFuelPair.put(uniqueKey, report.getDailyFuel());
                }
                // 其他生产时间 钻井
                if (groupOtherProductTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempNoProductTime = groupOtherProductTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempNoProductTime.add(report.getOtherProductionTime());
                    groupOtherProductTimePair.put(uniqueKey, tempResult);
                } else {
                    groupOtherProductTimePair.put(uniqueKey, report.getOtherProductionTime());
                }
                // 生产时间 修井
                if (groupProductTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempProductTime = groupProductTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempProductTime.add(report.getProductionTime());
                    groupProductTimePair.put(uniqueKey, tempResult);
                } else {
                    groupProductTimePair.put(uniqueKey, report.getProductionTime());
                }
                // 非生产时间 修井
                if (groupNoProductTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempNoProductTime = groupNoProductTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempNoProductTime.add(report.getNonProductionTime());
                    groupNoProductTimePair.put(uniqueKey, tempResult);
                } else {
                    groupNoProductTimePair.put(uniqueKey, report.getNonProductionTime());
                }
                // 钻井 非生产时间 事故 修理 自停 复杂 搬迁 整改 等停 冬休
                BigDecimal accidentTime = report.getAccidentTime();
                BigDecimal repairTime = report.getRepairTime();
                BigDecimal selfStopTime = report.getSelfStopTime();
                BigDecimal complexityTime = report.getComplexityTime();
                BigDecimal relocationTime = report.getRelocationTime();
                BigDecimal rectificationTime = report.getRectificationTime();
                BigDecimal waitingStopTime = report.getWaitingStopTime();
                BigDecimal winterBreakTime = report.getWinterBreakTime();
                BigDecimal totalNonProductionTime = Arrays.asList(
                                accidentTime,
                                repairTime,
                                selfStopTime,
                                complexityTime,
                                relocationTime,
                                rectificationTime,
                                waitingStopTime,
                                winterBreakTime
                        ).stream()
                // 将null转换为BigDecimal.ZERO，避免空指针
                        .map(bd -> bd == null ? BigDecimal.ZERO : bd)
                // 累加所有元素，初始值为BigDecimal.ZERO
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                // 非生产时间 钻井
                if (groupZjNoProductTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempZjNoProductTime = groupZjNoProductTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempZjNoProductTime.add(totalNonProductionTime);
                    groupZjNoProductTimePair.put(uniqueKey, tempResult);
                } else {
                    groupZjNoProductTimePair.put(uniqueKey, totalNonProductionTime);
                }
            });
        }

        TenantUtils.executeIgnore(() -> {
            currentTaskReqVO.setDeptId(pageReqVO.getDeptId());
            // 查询 deptId-taskId 分组内最后1条记录id 设置展开列标识
            List<IotRyDailyReportDO> currentGroupIds = iotRyDailyReportService.ryWellNameDailyReportsGroupIds(currentTaskReqVO);
            // key部门id-任务id   value小组内最后1条记录id
            Map<String, Long> groupIdPair = new HashMap<>();
            if (CollUtil.isNotEmpty(currentGroupIds)) {
                currentGroupIds.forEach(groupId -> {
                    String uniqueKey = StrUtil.join("-", groupId.getDeptId(), groupId.getTaskId());
                    groupIdPair.put(uniqueKey, groupId.getId());
                });
            }

            // 设置 分组内最后1条记录id 与 deptId-taskId 唯一键的对应关系
            if (CollUtil.isNotEmpty(groupIdPair)) {
                groupIdPair.forEach((uniqueKey, groupId) -> {
                    if (groupFootagePair.containsKey(uniqueKey)) {
                        groupIdFootagePair.put(groupId, groupFootagePair.get(uniqueKey));
                    }
                    if (groupFootageTimePair.containsKey(uniqueKey)) {
                        groupIdFootageTimePair.put(groupId, groupFootageTimePair.get(uniqueKey));
                    }
                    if (groupOtherProductTimePair.containsKey(uniqueKey)) {
                        groupIdOtherProductTimePair.put(groupId, groupOtherProductTimePair.get(uniqueKey));
                    }
                    if (groupProductTimePair.containsKey(uniqueKey)) {
                        groupIdProductTimePair.put(groupId, groupProductTimePair.get(uniqueKey));
                    }
                    if (groupFuelPair.containsKey(uniqueKey)) {
                        groupIdFuelPair.put(groupId, groupFuelPair.get(uniqueKey));
                    }
                    if (groupPowerPair.containsKey(uniqueKey)) {
                        groupIdPowerPair.put(groupId, groupPowerPair.get(uniqueKey));
                    }
                    if (groupNoProductTimePair.containsKey(uniqueKey)) {
                        groupIdNoProductTimePair.put(groupId, groupNoProductTimePair.get(uniqueKey));
                    }
                    if (groupZjNoProductTimePair.containsKey(uniqueKey)) {
                        groupIdZjNoProductTimePair.put(groupId, groupZjNoProductTimePair.get(uniqueKey));
                    }
                });
            }
        });

        // key施工状态数据字典value   value施工状态数据字典label
        Map<String, String> constructStatusPair = new HashMap<>();

        // 钻井 施工状态 字典数据
        List<DictDataDO> rigStatusDictData = dictDataService.getDictDataListByDictType("rigStatus");
        // 修井 施工状态 字典数据
        List<DictDataDO> repairStatusDictData = dictDataService.getDictDataListByDictType("repairStatus");
        // 钻井施工状态
        if ("1".equals(pageReqVO.getProjectClassification()) && CollUtil.isNotEmpty(rigStatusDictData)) {
            rigStatusDictData.forEach(data -> {
                constructStatusPair.put(data.getValue(), data.getLabel());
            });
        }
        // 修井施工状态
        if ("2".equals(pageReqVO.getProjectClassification()) && CollUtil.isNotEmpty(repairStatusDictData)) {
            repairStatusDictData.forEach(data -> {
                constructStatusPair.put(data.getValue(), data.getLabel());
            });
        }

        // 查询每个日报的生产动态明细
        IotRyDailyReportDetailPageReqVO detailReqVO = new IotRyDailyReportDetailPageReqVO();
        detailReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        detailReqVO.setReportIds(convertList(reports, IotRyDailyReportDO::getId));
        PageResult<IotRyDailyReportDetailDO> reportDetailsPage = iotRyDailyReportDetailMapper.selectPage(detailReqVO);
        List<IotRyDailyReportDetailDO> reportDetails = reportDetailsPage.getList();
        if (CollUtil.isNotEmpty(reportDetails)) {
            // 设置每个日报的生产动态明细
            reportDetails.forEach(detail -> {
                if (reportDetailsPair.containsKey(detail.getReportId())) {
                    List<IotRyDailyReportDetailRespVO> tempDetails = reportDetailsPair.get(detail.getReportId());
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                } else {
                    List<IotRyDailyReportDetailRespVO> tempDetails = new ArrayList<>();
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                }
            });
        }

        DataPermissionUtils.executeIgnore(() -> {
            // 查询日报关联的项目信息
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setProjectIds(convertList(reports, IotRyDailyReportDO::getProjectId));
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectPair.put(project.getId(), project.getContractName());
                });
            }
            // 查询日报关联的任务信息
            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
            taskReqVO.setTaskIds(convertList(reports, IotRyDailyReportDO::getTaskId));
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(taskReqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskPair.put(task.getId(), task.getWellName());
                    taskTechniquePair.put(task.getId(), task.getTechnique());
                    if (CollUtil.isNotEmpty(task.getExtProperty())) {
                        List<IotTaskAttrModelProperty> taskAttrs = task.getExtProperty();
                        if (CollUtil.isNotEmpty(taskAttrs)) {
                            // 找到 设计注气量 属性 对应的值
                            taskAttrs.forEach(attr -> {
                                if ("设计井深".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskExtPropertyPair.put(task.getId(), new BigDecimal(attr.getActualValue()));
                                }
                                if ("设计井身结构".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellStructPair.put(task.getId(), attr.getActualValue());
                                }
                                if ("额定生产时间".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskRatedProductionTimePair.put(task.getId(), new BigDecimal(attr.getActualValue()));
                                }
                                if ("套生段产管尺寸".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskCasingPipeSizePair.put(task.getId(), attr.getActualValue());
                                }
                                if ("井控级别".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellControlLevelPair.put(task.getId(), attr.getActualValue());
                                }
                                if ("井别".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellCategoryPair.put(task.getId(), attr.getActualValue());
                                }
                            });
                        }
                    }
                });
            }
            // 查询当前日报所属施工队伍中包含 井架 的型号
            equipmentTypePair.set(iotRyDailyReportService.queryEquipmentType(convertList(reports, IotRyDailyReportDO::getDeptId)));
            // 查询当前日报所属部门的 上井次完井时间 已经绑定任务的情况下
            List<IotRyDailyReportDO> latestWellDoneTimes = iotRyDailyReportService.latestWellDoneTimes(null);
            if (CollUtil.isNotEmpty(latestWellDoneTimes)) {
                latestWellDoneTimes.forEach(time -> {
                    latestWellDoneTimePair.put(time.getDeptId(), time.getConstructionStartDate());
                });
            }
            // 按施工队伍统计 钻井日报 施工井数 完工井数
            List<IotRyDailyReportTaskCountVO> deptTasks = iotRyDailyReportService.countTasksByDept();
            // 修井日报 不存在既填写钻井日报 又填写修井日报的队伍
            if (ObjUtil.isNotEmpty(pageReqVO.getCreateTime())) {
                List<IotRyDailyReportTaskCountVO> repairDeptTasks = iotRyDailyReportService.countRepairTasksByDept(pageReqVO);
                if (CollUtil.isNotEmpty(repairDeptTasks)) {
                    repairDeptTasks.forEach(task -> {
                        totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                        completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                    });
                }
            }
            if (CollUtil.isNotEmpty(deptTasks)) {
                deptTasks.forEach(task -> {
                    totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                    completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                });
            }
        });
        // 2. 拼接数据
        return BeanUtils.toBean(reports, IotRyDailyReportRespVO.class, (reportVO) -> {
            // 2.1 拼接部门信息
            findAndThen(deptMap, reportVO.getDeptId(), dept -> reportVO.setDeptName(dept.getName()));

            findAndThen(completedTasksPair, reportVO.getDeptId(), completedTaskCount -> reportVO.setCompletedWells(completedTaskCount));
            // 钻井 施工状态 数据字典
            findAndThen(constructStatusPair, reportVO.getRigStatus(), statusLabel -> {
                if ("1".equals(pageReqVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
            // 修井 施工状态 数据字典
            findAndThen(constructStatusPair, reportVO.getRepairStatus(), statusLabel -> {
                if ("2".equals(pageReqVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
            // 创建时间 格式化 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getCreateTime())) {
                reportVO.setCreateTimeStr(LocalDateTimeUtil.format(reportVO.getCreateTime(), DatePattern.NORM_DATE_PATTERN));
            }

            // 上井次完井时间 格式化时间为 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getLatestWellDoneTime())) {
                reportVO.setLatestWellDoneTimeStr(LocalDateTimeUtil.format(reportVO.getLatestWellDoneTime(), DatePattern.NORM_DATE_PATTERN));
            }

            // 转换用电量 单位 kWh 转换成 MWh
            if (reportVO.getDailyPowerUsage().compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal powerW = reportVO.getDailyPowerUsage()
                        .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
                reportVO.setDailyPowerUsage(powerW);
            }

            // 油耗 精确到2位小数
            if (reportVO.getDailyFuel().compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal fuel = reportVO.getDailyFuel()
                        .divide(BigDecimal.valueOf(1), 2, RoundingMode.HALF_UP);
                reportVO.setDailyFuel(fuel);
            }

            // 生产动态明细
            findAndThen(reportDetailsPair, reportVO.getId(), details -> {
                // 每个日报的明细 根据 reportDate+startTime 正序排列
                details.sort(Comparator.comparing(
                                // 处理 reportDate 为空的情况
                                (IotRyDailyReportDetailRespVO detail) -> {
                                    if (detail.getReportDate() == null) {
                                        return null; // null 值会排在前面
                                    }
                                    return detail.getReportDate().toLocalDate();
                                },
                                // 自定义 null 值比较器：null 排在非 null 前面
                                Comparator.nullsFirst(Comparator.naturalOrder())
                        )
                        .thenComparing(
                                // 处理 startTime 为空的情况
                                IotRyDailyReportDetailRespVO::getStartTime,
                                // 自定义 null 值比较器：null 排在非 null 前面
                                Comparator.nullsFirst(Comparator.naturalOrder())
                        ));
                reportVO.setReportDetails(details);
            });

            // 小组内最后1条记录标识
            findAndThen(groupIdFootagePair, reportVO.getId(), footage -> reportVO.setLastGroupIdFlag(true));
            // 小组内累计进尺
            findAndThen(groupIdFootagePair, reportVO.getId(), footage -> reportVO.setGroupIdFootage(footage));
            // 小组内累计 进尺工作时间
            findAndThen(groupIdFootageTimePair, reportVO.getId(), footageTime -> reportVO.setGroupIdFootageTime(footageTime));
            // 小组内累计 其他生产时间
            findAndThen(groupIdOtherProductTimePair, reportVO.getId(), otherProductTime -> reportVO.setGroupIdOtherProductTime(otherProductTime));
            // 小组内累计 钻井 非生产时间
            findAndThen(groupIdZjNoProductTimePair, reportVO.getId(), zjNoProductTime -> reportVO.setGroupIdZjNoProductTime(zjNoProductTime));
            // 小组内累计用电量
            findAndThen(groupIdPowerPair, reportVO.getId(), power -> {
                BigDecimal powerWanFang = power
                        .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
                reportVO.setGroupIdPower(powerWanFang);
            });
            // 小组内累计油耗 精确到2位小数
            findAndThen(groupIdFuelPair, reportVO.getId(), fuel -> {
                BigDecimal accurateFuel = fuel
                        .divide(BigDecimal.valueOf(1), 2, RoundingMode.HALF_UP);
                reportVO.setGroupIdFuel(accurateFuel);
            });
            // 修井 非生产时间
            findAndThen(groupIdNoProductTimePair, reportVO.getId(), noProductTime -> reportVO.setNonProductionTime(noProductTime));
            // 修井 生产时间
            findAndThen(groupIdProductTimePair, reportVO.getId(), productTime -> reportVO.setGroupIdProductTime(productTime));

            // 2.2 日报关联的项目信息
            findAndThen(projectPair, reportVO.getProjectId(), contractName -> reportVO.setContractName(contractName));
            // 2.3 日报关联的任务信息
            findAndThen(taskPair, reportVO.getTaskId(), taskName -> reportVO.setTaskName(taskName));
            // 2.4 设计井深
            findAndThen(taskExtPropertyPair, reportVO.getTaskId(), wellDepth -> reportVO.setDesignWellDepth(wellDepth));
            // 施工工艺
            findAndThen(taskTechniquePair, reportVO.getTaskId(), technique -> reportVO.setTechnique(technique));
            // 井别
            findAndThen(taskWellCategoryPair, reportVO.getTaskId(), wellCategory -> reportVO.setWellCategory(wellCategory));
            // 额定生产时间
            findAndThen(taskRatedProductionTimePair, reportVO.getTaskId(), ratedProductionTime -> reportVO.setRatedProductionTime(ratedProductionTime));
            // 套生段产管尺寸
            findAndThen(taskCasingPipeSizePair, reportVO.getTaskId(), casingPipeSize -> reportVO.setCasingPipeSize(casingPipeSize));
            // 井控级别
            findAndThen(taskWellControlLevelPair, reportVO.getTaskId(), wellControlLevel -> reportVO.setWellControlLevel(wellControlLevel));
            // 2.5 设计井身结构
            findAndThen(taskWellStructPair, reportVO.getTaskId(), wellStruct -> reportVO.setDesignWellStruct(wellStruct));
            // 2.6 设备型号
            findAndThen(equipmentTypePair.get(), reportVO.getDeptId(), equipmentType -> reportVO.setEquipmentType(equipmentType));
            // 2.7 上井次完井时间
            findAndThen(latestWellDoneTimePair, reportVO.getDeptId(), latestWellDoneTime -> reportVO.setLatestWellDoneTime(latestWellDoneTime));
            // 2.8 总施工井数 完工井数
            findAndThen(totalTasksPair, reportVO.getDeptId(), totalTaskCount -> reportVO.setTotalConstructionWells(totalTaskCount));
            findAndThen(completedTasksPair, reportVO.getDeptId(), completedTaskCount -> reportVO.setCompletedWells(completedTaskCount));
        });
    }

    /**
     * 瑞鹰日报分页 设置关联查询信息
     * @param reports
     * @return
     */
    private List<IotRyDailyReportRespVO> buildWellRyDailyReports(List<IotRyDailyReportDO> reports, IotRyDailyReportPageReqVO pageReqVO) {
        if (CollUtil.isEmpty(reports)) {
            return Collections.emptyList();
        }
        // 设备部门信息
        Map<Long, DeptDO> deptMap = deptService.getDeptMap(convertList(reports, IotRyDailyReportDO::getDeptId));
        // key项目id   value项目合同号
        Map<Long, String> projectPair = new HashMap<>();
        //  key任务id     value任务井号-施工区域
        Map<Long, String> taskPair = new HashMap<>();
        // key任务id   value设计井深
        Map<Long, BigDecimal> taskExtPropertyPair = new HashMap<>();
        // key任务id   value额定生产时间
        Map<Long, BigDecimal> taskRatedProductionTimePair = new HashMap<>();
        // key任务id   value套生段产管尺寸
        Map<Long, String> taskCasingPipeSizePair = new HashMap<>();
        // key任务id   value井控级别
        Map<Long, String> taskWellControlLevelPair = new HashMap<>();
        // key任务id   value施工工艺
        Map<Long, String> taskTechniquePair = new HashMap<>();
        // key任务id   value井别
        Map<Long, String> taskWellCategoryPair = new HashMap<>();
        // key任务id   value设计井身结构
        Map<Long, String> taskWellStructPair = new HashMap<>();
        // key部门id  value设备型号编号
        AtomicReference<Map<Long, String>> equipmentTypePair = new AtomicReference<>(new HashMap<>());
        // key施工队伍id    value最近完工日期
        Map<Long, LocalDateTime> latestWellDoneTimePair = new HashMap<>();
        // key施工队伍id    value施工井数量
        Map<Long, Integer> totalTasksPair = new HashMap<>();
        // key施工队伍id    value完工井数量
        Map<Long, Integer> completedTasksPair = new HashMap<>();

        // key部门id-队伍id   value 队伍累计进尺
        Map<String, BigDecimal> groupFootagePair = new HashMap<>();
        // key部门id-队伍id   value 队伍当年累计用电量
        Map<String, BigDecimal> groupPowerPair = new HashMap<>();
        // key部门id-队伍id   value 队伍当年累计油耗
        Map<String, BigDecimal> groupFuelPair = new HashMap<>();
        // key部门id-队伍id    value 进尺工作时间小计
        Map<String, BigDecimal> groupFootageTimePair = new HashMap<>();
        // key部门id-队伍id    value 钻井 其他生产时间小计
        Map<String, BigDecimal> groupOtherProductTimePair = new HashMap<>();
        // key部门id-队伍id    value 修井 生产时间小计
        Map<String, BigDecimal> groupProductTimePair = new HashMap<>();
        // key部门id-队伍id    value 修井 非生产时间小计
        Map<String, BigDecimal> groupNoProductTimePair = new HashMap<>();
        // key部门id-队伍id    value 钻井非生产时间小计
        Map<String, BigDecimal> groupZjNoProductTimePair = new HashMap<>();
        // key部门id-队伍id    value 小计平均运行时效
        Map<String, BigDecimal> groupTransTimePair = new HashMap<>();

        // key小组最后1条记录id   value 队伍累计进尺
        Map<Long, BigDecimal> groupIdFootagePair = new HashMap<>();
        // key小组最后1条记录id   value 队伍累计用电量
        Map<Long, BigDecimal> groupIdPowerPair = new HashMap<>();
        // key小组最后1条记录id   value 队伍累计油耗
        Map<Long, BigDecimal> groupIdFuelPair = new HashMap<>();
        // key小组最后1条记录id    value 进尺工作时间小计
        Map<Long, BigDecimal> groupIdFootageTimePair = new HashMap<>();
        // key小组最后1条记录id    value 钻井 其他生产时间小计
        Map<Long, BigDecimal> groupIdOtherProductTimePair = new HashMap<>();
        // key小组最后1条记录id    value 修井 生产时间小计
        Map<Long, BigDecimal> groupIdProductTimePair = new HashMap<>();
        // key小组最后1条记录id    value 修井 非生产时间小计
        Map<Long, BigDecimal> groupIdNoProductTimePair = new HashMap<>();
        // key小组最后1条记录id    value 钻井 非生产时间小计
        Map<Long, BigDecimal> groupIdZjNoProductTimePair = new HashMap<>();
        // key小组最后1条记录id    value 小计平均运行时效
        Map<Long, BigDecimal> groupIdTransTimePair = new HashMap<>();
        // key日报id   value生产动态明细
        Map<Long, List<IotRyDailyReportDetailRespVO>> reportDetailsPair = new HashMap<>();

        // 查询 当前分页 所有部门 任务井 日报数据 小计工作量
        IotRyDailyReportPageReqVO currentTaskReqVO = new IotRyDailyReportPageReqVO();
        currentTaskReqVO.setTaskIds(convertSet(reports, IotRyDailyReportDO::getTaskId));
        currentTaskReqVO.setDeptIds(convertSet(reports, IotRyDailyReportDO::getDeptId));
        currentTaskReqVO.setCreateTime(pageReqVO.getCreateTime());
        currentTaskReqVO.setProjectIds(pageReqVO.getProjectIds());
        currentTaskReqVO.setTaskIds(pageReqVO.getTaskIds());
        List<IotRyDailyReportDO> deptTaskDailyReports = iotRyDailyReportMapper.dailyReports(currentTaskReqVO);

        // 以 deptId-taskId 为唯一键 小计日报工作量
        if (CollUtil.isNotEmpty(deptTaskDailyReports)) {
            deptTaskDailyReports.forEach(report -> {
                String uniqueKey = StrUtil.join("-", report.getDeptId(), report.getTaskId());
                // 进尺
                if (groupFootagePair.containsKey(uniqueKey)) {
                    BigDecimal tempFootage = groupFootagePair.get(uniqueKey);
                    BigDecimal tempResult = tempFootage.add(report.getDailyFootage());
                    groupFootagePair.put(uniqueKey, tempResult);
                } else {
                    groupFootagePair.put(uniqueKey, report.getDailyFootage());
                }
                // 进尺工作时间
                if (groupFootageTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempFootageTime = groupFootageTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempFootageTime.add(report.getDrillingWorkingTime());
                    groupFootageTimePair.put(uniqueKey, tempResult);
                } else {
                    groupFootageTimePair.put(uniqueKey, report.getDrillingWorkingTime());
                }
                // 电量
                if (groupPowerPair.containsKey(uniqueKey)) {
                    BigDecimal tempPower = groupPowerPair.get(uniqueKey);
                    BigDecimal tempResult = tempPower.add(report.getDailyPowerUsage());
                    groupPowerPair.put(uniqueKey, tempResult);
                } else {
                    groupPowerPair.put(uniqueKey, report.getDailyPowerUsage());
                }
                // 油耗
                if (groupFuelPair.containsKey(uniqueKey)) {
                    BigDecimal tempFuel = groupFuelPair.get(uniqueKey);
                    BigDecimal tempResult = tempFuel.add(report.getDailyFuel());
                    groupFuelPair.put(uniqueKey, tempResult);
                } else {
                    groupFuelPair.put(uniqueKey, report.getDailyFuel());
                }
                // 其他生产时间 钻井
                if (groupOtherProductTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempNoProductTime = groupOtherProductTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempNoProductTime.add(report.getOtherProductionTime());
                    groupOtherProductTimePair.put(uniqueKey, tempResult);
                } else {
                    groupOtherProductTimePair.put(uniqueKey, report.getOtherProductionTime());
                }
                // 生产时间 修井
                if (groupProductTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempProductTime = groupProductTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempProductTime.add(report.getProductionTime());
                    groupProductTimePair.put(uniqueKey, tempResult);
                } else {
                    groupProductTimePair.put(uniqueKey, report.getProductionTime());
                }
                // 非生产时间 修井
                if (groupNoProductTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempNoProductTime = groupNoProductTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempNoProductTime.add(report.getNonProductionTime());
                    groupNoProductTimePair.put(uniqueKey, tempResult);
                } else {
                    groupNoProductTimePair.put(uniqueKey, report.getNonProductionTime());
                }
                // 钻井 非生产时间 事故 修理 自停 复杂 搬迁 整改 等停 冬休
                BigDecimal accidentTime = report.getAccidentTime();
                BigDecimal repairTime = report.getRepairTime();
                BigDecimal selfStopTime = report.getSelfStopTime();
                BigDecimal complexityTime = report.getComplexityTime();
                BigDecimal relocationTime = report.getRelocationTime();
                BigDecimal rectificationTime = report.getRectificationTime();
                BigDecimal waitingStopTime = report.getWaitingStopTime();
                BigDecimal winterBreakTime = report.getWinterBreakTime();
                BigDecimal totalNonProductionTime = Arrays.asList(
                                accidentTime,
                                repairTime,
                                selfStopTime,
                                complexityTime,
                                relocationTime,
                                rectificationTime,
                                waitingStopTime,
                                winterBreakTime
                        ).stream()
                        // 将null转换为BigDecimal.ZERO，避免空指针
                        .map(bd -> bd == null ? BigDecimal.ZERO : bd)
                        // 累加所有元素，初始值为BigDecimal.ZERO
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                // 非生产时间 钻井
                if (groupZjNoProductTimePair.containsKey(uniqueKey)) {
                    BigDecimal tempZjNoProductTime = groupZjNoProductTimePair.get(uniqueKey);
                    BigDecimal tempResult = tempZjNoProductTime.add(totalNonProductionTime);
                    groupZjNoProductTimePair.put(uniqueKey, tempResult);
                } else {
                    groupZjNoProductTimePair.put(uniqueKey, totalNonProductionTime);
                }
            });
        }

        // 查询 deptId-taskId 分组内最后1条记录id 设置展开列标识
        // 暂时过滤掉租户
        TenantUtils.executeIgnore(() -> {
            currentTaskReqVO.setDeptId(pageReqVO.getDeptId());
            List<IotRyDailyReportDO> currentGroupIds = iotRyDailyReportService.ryDeptDailyReportsGroupIds(currentTaskReqVO);
            // key部门id-任务id   value小组内最后1条记录id
            Map<String, Long> groupIdPair = new HashMap<>();
            if (CollUtil.isNotEmpty(currentGroupIds)) {
                currentGroupIds.forEach(groupId -> {
                    String uniqueKey = StrUtil.join("-", groupId.getDeptId(), groupId.getTaskId());
                    groupIdPair.put(uniqueKey, groupId.getId());
                });
            }

            // 设置 分组内最后1条记录id 与 deptId-taskId 唯一键的对应关系
            if (CollUtil.isNotEmpty(groupIdPair)) {
                groupIdPair.forEach((uniqueKey, groupId) -> {
                    if (groupFootagePair.containsKey(uniqueKey)) {
                        groupIdFootagePair.put(groupId, groupFootagePair.get(uniqueKey));
                    }
                    if (groupFootageTimePair.containsKey(uniqueKey)) {
                        groupIdFootageTimePair.put(groupId, groupFootageTimePair.get(uniqueKey));
                    }
                    if (groupOtherProductTimePair.containsKey(uniqueKey)) {
                        groupIdOtherProductTimePair.put(groupId, groupOtherProductTimePair.get(uniqueKey));
                    }
                    if (groupProductTimePair.containsKey(uniqueKey)) {
                        groupIdProductTimePair.put(groupId, groupProductTimePair.get(uniqueKey));
                    }
                    if (groupFuelPair.containsKey(uniqueKey)) {
                        groupIdFuelPair.put(groupId, groupFuelPair.get(uniqueKey));
                    }
                    if (groupPowerPair.containsKey(uniqueKey)) {
                        groupIdPowerPair.put(groupId, groupPowerPair.get(uniqueKey));
                    }
                    if (groupNoProductTimePair.containsKey(uniqueKey)) {
                        groupIdNoProductTimePair.put(groupId, groupNoProductTimePair.get(uniqueKey));
                    }
                    if (groupZjNoProductTimePair.containsKey(uniqueKey)) {
                        groupIdZjNoProductTimePair.put(groupId, groupZjNoProductTimePair.get(uniqueKey));
                    }
                });
            }
        });

        // key施工状态数据字典value   value施工状态数据字典label
        Map<String, String> constructStatusPair = new HashMap<>();
        // 钻井 施工状态 字典数据
        List<DictDataDO> rigStatusDictData = dictDataService.getDictDataListByDictType("rigStatus");
        // 修井 施工状态 字典数据
        List<DictDataDO> repairStatusDictData = dictDataService.getDictDataListByDictType("repairStatus");
        // 钻井施工状态
        if ("1".equals(pageReqVO.getProjectClassification()) && CollUtil.isNotEmpty(rigStatusDictData)) {
            rigStatusDictData.forEach(data -> {
                constructStatusPair.put(data.getValue(), data.getLabel());
            });
        }
        // 修井施工状态
        if ("2".equals(pageReqVO.getProjectClassification()) && CollUtil.isNotEmpty(repairStatusDictData)) {
            repairStatusDictData.forEach(data -> {
                constructStatusPair.put(data.getValue(), data.getLabel());
            });
        }

        // 查询每个日报的生产动态明细
        IotRyDailyReportDetailPageReqVO detailReqVO = new IotRyDailyReportDetailPageReqVO();
        detailReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        detailReqVO.setReportIds(convertList(reports, IotRyDailyReportDO::getId));
        PageResult<IotRyDailyReportDetailDO> reportDetailsPage = iotRyDailyReportDetailMapper.selectPage(detailReqVO);
        List<IotRyDailyReportDetailDO> reportDetails = reportDetailsPage.getList();
        if (CollUtil.isNotEmpty(reportDetails)) {
            // 设置每个日报的生产动态明细
            reportDetails.forEach(detail -> {
                if (reportDetailsPair.containsKey(detail.getReportId())) {
                    List<IotRyDailyReportDetailRespVO> tempDetails = reportDetailsPair.get(detail.getReportId());
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                } else {
                    List<IotRyDailyReportDetailRespVO> tempDetails = new ArrayList<>();
                    tempDetails.add(BeanUtils.toBean(detail, IotRyDailyReportDetailRespVO.class));
                    reportDetailsPair.put(detail.getReportId(), tempDetails);
                }
            });
        }

        DataPermissionUtils.executeIgnore(() -> {
            // 查询日报关联的项目信息
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setProjectIds(convertList(reports, IotRyDailyReportDO::getProjectId));
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectPair.put(project.getId(), project.getContractName());
                });
            }
            // 查询日报关联的任务信息
            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
            taskReqVO.setTaskIds(convertList(reports, IotRyDailyReportDO::getTaskId));
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(taskReqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskPair.put(task.getId(), task.getWellName());
                    taskTechniquePair.put(task.getId(), task.getTechnique());
                    if (CollUtil.isNotEmpty(task.getExtProperty())) {
                        List<IotTaskAttrModelProperty> taskAttrs = task.getExtProperty();
                        if (CollUtil.isNotEmpty(taskAttrs)) {
                            // 找到 设计注气量 属性 对应的值
                            taskAttrs.forEach(attr -> {
                                if ("设计井深".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskExtPropertyPair.put(task.getId(), new BigDecimal(attr.getActualValue()));
                                }
                                if ("设计井身结构".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellStructPair.put(task.getId(), attr.getActualValue());
                                }
                                if ("额定生产时间".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskRatedProductionTimePair.put(task.getId(), new BigDecimal(attr.getActualValue()));
                                }
                                if ("套生段产管尺寸".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskCasingPipeSizePair.put(task.getId(), attr.getActualValue());
                                }
                                if ("井控级别".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellControlLevelPair.put(task.getId(), attr.getActualValue());
                                }
                                if ("井别".equals(attr.getName()) && StrUtil.isNotBlank(attr.getActualValue())) {
                                    taskWellCategoryPair.put(task.getId(), attr.getActualValue());
                                }
                            });
                        }
                    }
                });
            }
            // 查询当前日报所属施工队伍中包含 井架 的型号
            equipmentTypePair.set(iotRyDailyReportService.queryEquipmentType(convertList(reports, IotRyDailyReportDO::getDeptId)));
            // 查询当前日报所属部门的 上井次完井时间 已经绑定任务的情况下
            List<IotRyDailyReportDO> latestWellDoneTimes = iotRyDailyReportService.latestWellDoneTimes(null);
            if (CollUtil.isNotEmpty(latestWellDoneTimes)) {
                latestWellDoneTimes.forEach(time -> {
                    latestWellDoneTimePair.put(time.getDeptId(), time.getConstructionStartDate());
                });
            }
            // 按施工队伍统计 钻井日报 施工井数 完工井数
            List<IotRyDailyReportTaskCountVO> deptTasks = iotRyDailyReportService.countTasksByDept();
            // 修井日报 不存在既填写钻井日报 又填写修井日报的队伍
            if (ObjUtil.isNotEmpty(pageReqVO.getCreateTime())) {
                List<IotRyDailyReportTaskCountVO> repairDeptTasks = iotRyDailyReportService.countRepairTasksByDept(pageReqVO);
                if (CollUtil.isNotEmpty(repairDeptTasks)) {
                    repairDeptTasks.forEach(task -> {
                        totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                        completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                    });
                }
            }
            if (CollUtil.isNotEmpty(deptTasks)) {
                deptTasks.forEach(task -> {
                    totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                    completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                });
            }
        });
        // 2. 拼接数据
        return BeanUtils.toBean(reports, IotRyDailyReportRespVO.class, (reportVO) -> {
            // 2.1 拼接部门信息
            findAndThen(deptMap, reportVO.getDeptId(), dept -> reportVO.setDeptName(dept.getName()));

            // 日报生成日期 格式化时间为 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getCreateTime())) {
                reportVO.setCreateTimeStr(LocalDateTimeUtil.format(reportVO.getCreateTime(), DatePattern.NORM_DATE_PATTERN));
            }
            // 上井次完井时间 格式化时间为 yyyy-MM-dd
            if (ObjUtil.isNotEmpty(reportVO.getLatestWellDoneTime())) {
                reportVO.setLatestWellDoneTimeStr(LocalDateTimeUtil.format(reportVO.getLatestWellDoneTime(), DatePattern.NORM_DATE_PATTERN));
            }

            // 转换用电量 单位 kWh 转换成 MWh
            if (reportVO.getDailyPowerUsage().compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal powerW = reportVO.getDailyPowerUsage()
                        .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
                reportVO.setDailyPowerUsage(powerW);
            }

            // 油耗 精确到2位小数
            if (reportVO.getDailyFuel().compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal fuel = reportVO.getDailyFuel()
                        .divide(BigDecimal.valueOf(1), 2, RoundingMode.HALF_UP);
                reportVO.setDailyFuel(fuel);
            }

            // 生产动态明细
            findAndThen(reportDetailsPair, reportVO.getId(), details -> {
                // 每个日报的明细 根据 reportDate+startTime 正序排列
                details.sort(Comparator.comparing(
                                // 处理 reportDate 为空的情况
                                (IotRyDailyReportDetailRespVO detail) -> {
                                    if (detail.getReportDate() == null) {
                                        return null; // null 值会排在前面
                                    }
                                    return detail.getReportDate().toLocalDate();
                                },
                                // 自定义 null 值比较器：null 排在非 null 前面
                                Comparator.nullsFirst(Comparator.naturalOrder())
                        )
                        .thenComparing(
                                // 处理 startTime 为空的情况
                                IotRyDailyReportDetailRespVO::getStartTime,
                                // 自定义 null 值比较器：null 排在非 null 前面
                                Comparator.nullsFirst(Comparator.naturalOrder())
                        ));
                reportVO.setReportDetails(details);
            });

            // 小组内最后1条记录标识
            findAndThen(groupIdFootagePair, reportVO.getId(), footage -> reportVO.setLastGroupIdFlag(true));
            // 小组内累计进尺
            findAndThen(groupIdFootagePair, reportVO.getId(), footage -> reportVO.setGroupIdFootage(footage));
            // 小组内累计 进尺工作时间
            findAndThen(groupIdFootageTimePair, reportVO.getId(), footageTime -> reportVO.setGroupIdFootageTime(footageTime));
            // 小组内累计 钻井 其他生产时间
            findAndThen(groupIdOtherProductTimePair, reportVO.getId(), otherProductTime -> reportVO.setGroupIdOtherProductTime(otherProductTime));
            // 小组内累计 钻井 非生产时间
            findAndThen(groupIdZjNoProductTimePair, reportVO.getId(), zjNoProductTime -> reportVO.setGroupIdZjNoProductTime(zjNoProductTime));
            // 小组内累计用电量
            findAndThen(groupIdPowerPair, reportVO.getId(), power -> {
                BigDecimal powerWanFang = power
                        .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
                reportVO.setGroupIdPower(powerWanFang);
            });
            // 小组内累计油耗 保留2位小数
            findAndThen(groupIdFuelPair, reportVO.getId(), fuel -> {
                BigDecimal accurateFuel = fuel
                        .divide(BigDecimal.valueOf(1), 2, RoundingMode.HALF_UP);
                reportVO.setGroupIdFuel(accurateFuel);
            });
            // 修井 非生产时间
            findAndThen(groupIdNoProductTimePair, reportVO.getId(), noProductTime -> reportVO.setNonProductionTime(noProductTime));
            // 修井 生产时间
            findAndThen(groupIdProductTimePair, reportVO.getId(), productTime -> reportVO.setGroupIdProductTime(productTime));

            // 2.2 日报关联的项目信息
            findAndThen(projectPair, reportVO.getProjectId(), contractName -> reportVO.setContractName(contractName));
            // 2.3 日报关联的任务信息
            findAndThen(taskPair, reportVO.getTaskId(), taskName -> reportVO.setTaskName(taskName));
            // 2.4 设计井深
            findAndThen(taskExtPropertyPair, reportVO.getTaskId(), wellDepth -> reportVO.setDesignWellDepth(wellDepth));
            // 施工工艺
            findAndThen(taskTechniquePair, reportVO.getTaskId(), technique -> reportVO.setTechnique(technique));
            // 井别
            findAndThen(taskWellCategoryPair, reportVO.getTaskId(), wellCategory -> reportVO.setWellCategory(wellCategory));
            // 额定生产时间
            findAndThen(taskRatedProductionTimePair, reportVO.getTaskId(), ratedProductionTime -> reportVO.setRatedProductionTime(ratedProductionTime));
            // 套生段产管尺寸
            findAndThen(taskCasingPipeSizePair, reportVO.getTaskId(), casingPipeSize -> reportVO.setCasingPipeSize(casingPipeSize));
            // 井控级别
            findAndThen(taskWellControlLevelPair, reportVO.getTaskId(), wellControlLevel -> reportVO.setWellControlLevel(wellControlLevel));
            // 2.5 设计井身结构
            findAndThen(taskWellStructPair, reportVO.getTaskId(), wellStruct -> reportVO.setDesignWellStruct(wellStruct));
            // 2.6 设备型号
            findAndThen(equipmentTypePair.get(), reportVO.getDeptId(), equipmentType -> reportVO.setEquipmentType(equipmentType));
            // 2.7 上井次完井时间
            findAndThen(latestWellDoneTimePair, reportVO.getDeptId(), latestWellDoneTime -> reportVO.setLatestWellDoneTime(latestWellDoneTime));
            // 2.8 总施工井数 完工井数
            findAndThen(totalTasksPair, reportVO.getDeptId(), totalTaskCount -> reportVO.setTotalConstructionWells(totalTaskCount));
            findAndThen(completedTasksPair, reportVO.getDeptId(), completedTaskCount -> reportVO.setCompletedWells(completedTaskCount));
            // 钻井 施工状态 数据字典
            findAndThen(constructStatusPair, reportVO.getRigStatus(), statusLabel -> {
                if ("1".equals(pageReqVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
            // 修井 施工状态 数据字典
            findAndThen(constructStatusPair, reportVO.getRepairStatus(), statusLabel -> {
                if ("2".equals(pageReqVO.getProjectClassification())) {
                    reportVO.setConstructionStatusName(statusLabel);
                }
            });
        });
    }

    @GetMapping("/polylineStatistics")
    @Operation(summary = "获得瑞鹰日报汇总统计 折线图")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<List<IotRyDailyReportPolylineVO>> polylineStatistics(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        List<IotRyDailyReportPolylineVO> result = new ArrayList<>();
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        List<IotRyDailyReportPolylineVO> statistics = iotRyDailyReportService.polylineStatistics(pageReqVO);
        result.addAll(statistics);
        return success(result);
    }

    @GetMapping("/totalWorkload")
    @Operation(summary = "累计工作量统计")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:query')")
    public CommonResult<Map<String, BigDecimal>> totalWorkload(@Valid IotRyDailyReportPageReqVO pageReqVO) {
        // 查询所有数据不分页
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }

        // 时间查询区间内如果包含 未发生的时间段(今年 本季度) 则设置 结束时间为 当前日期
        // 【调用公共方法】矫正未来时间区间
        LocalDateTimeUtils.correctFutureTimeRange(pageReqVO.getCreateTime());

        // 统计设备利用率
        Long deptId = pageReqVO.getDeptId();
        // 钻井或修井部门 队伍
        List<Long> zjOrXjDeptIds = new ArrayList<>();
        // 可用的部门id集合
        Set<Long> availableDeptIds = new HashSet<>();
        if (ObjUtil.isNotEmpty(deptId)) {
            // 查询指定部门下所有子部门
            Set<Long> childDeptIds = deptService.getChildDeptIdListFromCache(deptId);
            Map<Long, DeptDO> allDeptPair = deptService.getDeptMap(childDeptIds);
            // 过滤掉关闭的部门
            if (CollUtil.isNotEmpty(allDeptPair)) {
                allDeptPair.forEach((tempDeptId, dept) -> {
                    if (0 == dept.getStatus()) {
                        availableDeptIds.add(tempDeptId);
                    }
                });
            }
            // 钻井日报汇总 分母为 钻井施工队伍  修井日报汇总分母分母为修井施工队伍
            // 1钻井  2修井
            String projectClassification = pageReqVO.getProjectClassification();
            // 查询钻井部门 修井 部门
            IotDeptTypePageReqVO reqVO = new IotDeptTypePageReqVO();
            if ("1".equals(projectClassification)) {
                reqVO.setType("zj");
            } else {
                reqVO.setType("xj");
            }
            List<IotDeptTypeDO> deptTypes = iotDeptTypeMapper.selectList(reqVO);
            if (CollUtil.isNotEmpty(deptTypes)) {
                deptTypes.forEach(dept -> {
                    zjOrXjDeptIds.add(dept.getDeptId());
                });
            }
        }
        // 取 availableDeptIds zjOrXjDeptIds 2个集合的交集 当前查询条件下 的钻井 或 修井 部门id集合
        Set<Long> overlapDeptIds = Stream.of(availableDeptIds)
                .filter(Objects::nonNull)
                .flatMap(Set::stream)
                .filter(tempDeptId -> CollUtil.isNotEmpty(zjOrXjDeptIds) && zjOrXjDeptIds.contains(tempDeptId))
                .collect(Collectors.toSet());


        // 统计设备利用率 查询非 dt zx 状态的日报
        // 钻井：日报状态 过滤掉 ‘等停dt’    修井：日报状态过滤掉 ‘等停dt’、‘整修zx’
        List<String> statuses = new ArrayList<>();
        statuses.add("dt");
        statuses.add("zx");
        pageReqVO.setStatuses(statuses);
        double rate = 0.0;
        Integer reportCount = 0;
        List<IotRyDailyReportDO> dailyReports = iotRyDailyReportMapper.dailyReports(pageReqVO);
        if (CollUtil.isNotEmpty(dailyReports)) {
            for (IotRyDailyReportDO dailyReport : dailyReports) {
                if (overlapDeptIds.contains(dailyReport.getDeptId())) {
                    ++reportCount;
                }
            }
        }

        // 计算筛选时间范围内 指定部门的设备利用率
        long daysCount = 0l;
        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;
            }
        }

        // 所有项目部包含的队伍数量>0 并且 指定时间区间天数大于0
        if (daysCount > 0 && overlapDeptIds.size() > 0) {
            // 当前项目部 设备利用率 公式 reportCount/(currentTeamNum*daysCount)
            // 计算设备利用率（处理除数为0的情况）
            rate = new BigDecimal((double) reportCount / (overlapDeptIds.size() * daysCount))
                    .setScale(4, RoundingMode.HALF_UP)  // 保留4位小数，四舍五入
                    .doubleValue();
            System.out.println("瑞鹰设备利用率-分子：" + reportCount);
            System.out.println("瑞鹰设备利用率-分母：" + overlapDeptIds.size());
        }

        // 查询指定部门 指定时间范围内的日报
        List<IotRyDailyReportDO> list = iotRyDailyReportService.getIotRyDailyReportPage(pageReqVO).getList();
        Map<String, BigDecimal> result = new HashMap<>();
        // 总进尺
        BigDecimal totalFootage = BigDecimal.ZERO;
        // 总用电量
        BigDecimal totalPowerConsumption = BigDecimal.ZERO;
        // 总油耗
        BigDecimal totalFuelConsumption = BigDecimal.ZERO;
        // 平均油耗
        BigDecimal averageFuelConsumption = BigDecimal.ZERO;
        // key队伍id      value施工井数
        BigDecimal constructionWells = BigDecimal.ZERO;
        // key队伍id      value完工井数
        BigDecimal completedWells = BigDecimal.ZERO;
        // key施工队伍id    value施工井数量
        Map<Long, Integer> totalTasksPair = new HashMap<>();
        // key施工队伍id    value完工井数量
        Map<Long, Integer> completedTasksPair = new HashMap<>();
        // 瑞鹰修井 施工井数 完工井数 统计
        Set<Long> ids = new HashSet<>();
        if (Objects.nonNull(pageReqVO.getDeptId())) {
            ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
            ids.add(pageReqVO.getDeptId());
            pageReqVO.setDeptIds(ids);
        }
        if (ObjUtil.isNotEmpty(pageReqVO.getCreateTime())) {
            List<IotRyDailyReportTaskCountVO> repairDeptTasks = iotRyDailyReportService.countRepairTasksByDept(pageReqVO);
            if (CollUtil.isNotEmpty(repairDeptTasks)) {
                repairDeptTasks.forEach(task -> {
                    totalTasksPair.put(task.getDeptId(), task.getTotalTaskCount());
                    completedTasksPair.put(task.getDeptId(), task.getCompletedTaskCount());
                });
            }
        }

        if (CollUtil.isNotEmpty(list)) {
            for (IotRyDailyReportDO report : list) {
                BigDecimal dailyFootage = report.getDailyFootage();
                BigDecimal powerConsumption = report.getDailyPowerUsage();
                BigDecimal fuelConsumption = report.getDailyFuel();
                if (ObjUtil.isNotEmpty(dailyFootage)) {
                    totalFootage = totalFootage.add(dailyFootage);
                }
                if (ObjUtil.isNotEmpty(powerConsumption)) {
                    totalPowerConsumption = totalPowerConsumption.add(powerConsumption);
                }
                if (ObjUtil.isNotEmpty(fuelConsumption)) {
                    totalFuelConsumption = totalFuelConsumption.add(fuelConsumption);
                }
            }

            // 施工井数
            if (CollUtil.isNotEmpty(totalTasksPair)) {
                for (Integer wellCount : totalTasksPair.values()) {
                    constructionWells = constructionWells.add(new BigDecimal(wellCount));
                }
            }
            // 完工井数
            if (CollUtil.isNotEmpty(completedTasksPair)) {
                for (Integer wellCount : completedTasksPair.values()) {
                    completedWells = completedWells.add(new BigDecimal(wellCount));
                }
            }
            // 累计用电量
            result.put("totalPowerConsumption", totalPowerConsumption);
            // 累计油耗
            result.put("totalFuelConsumption", totalFuelConsumption);
            // 平均油耗
            if (ObjUtil.isNotEmpty(pageReqVO.getCreateTime())) {
                LocalDateTime[] createTimes = pageReqVO.getCreateTime();
                // 获取开始时间和结束时间
                LocalDateTime startTime = createTimes[0];
                LocalDateTime endTime = createTimes[1];
                // 截断到日期（忽略时分秒）
                LocalDate startDate = startTime.toLocalDate();
                LocalDate endDate = endTime.toLocalDate();
                // 计算天数：日期差 + 1（包含首尾两天）
                long days = ChronoUnit.DAYS.between(startDate, endDate) + 1;
                averageFuelConsumption = totalFuelConsumption.divide(new BigDecimal(days), 2, RoundingMode.HALF_UP);
                result.put("averageFuelConsumption", averageFuelConsumption);
            }

            // 汇总 指定搜索时间段内的 总进尺  累计用电量 累计油耗
            if ("1".equals(pageReqVO.getProjectClassification())) {
                // 钻井日报
                result.put("totalFootage", totalFootage);
            } else if ("2".equals(pageReqVO.getProjectClassification())) {
                // 累计施工井数
                result.put("constructionWells", constructionWells);
                // 累计完工井数
                result.put("completedWells", completedWells);
            }
        }
        // 设备利用率
        result.put("utilizationRate", BigDecimal.valueOf(rate));
        return success(result);
    }

    @GetMapping("/export-excel")
    @Operation(summary = "导出瑞鹰 钻井 修井 日报 Excel")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportIotRyDailyReportExcel(@Valid IotRyDailyReportPageReqVO pageReqVO,
              HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        List<IotRyDailyReportDO> list = iotRyDailyReportService.getIotRyDailyReportPage(pageReqVO).getList();
        try {
            // 导出 Excel
            ExcelUtils.write(response, "瑞鹰日报.xls", "数据", IotRyDailyReportDetailComboRespVO.class,
                    buildRyDailyReportDetails(list, pageReqVO, "export"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @GetMapping("/exportStatistics")
    @Operation(summary = "导出瑞鹰日报汇总 Excel")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportStatistics(@Valid IotRyDailyReportPageReqVO pageReqVO,
                                 HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        String sheetName = StrUtil.EMPTY;

        List<IotRyDailyReportStatisticsRespVO> statistics = iotRyDailyReportService.statistics(pageReqVO);
        if (CollUtil.isNotEmpty(statistics)) {
            statistics.forEach(stat -> {
                // 修井 平均时效
                BigDecimal transitTime = stat.getTransitTime();
                if (ObjUtil.isEmpty(transitTime) || transitTime.compareTo(BigDecimal.ZERO) == 0) {
                    stat.setTransitTimePercent("0.00%");
                } else {
                    // 转换逻辑：乘以100 → 保留2位小数（四舍五入）→ 拼接%
                    BigDecimal percent = transitTime.multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.HALF_UP);
                    stat.setTransitTimePercent(percent + "%");
                }
                // 非生产时效
                BigDecimal nonProductiveTime = stat.getNonProductiveTime();
                if (ObjUtil.isEmpty(nonProductiveTime) || nonProductiveTime.compareTo(BigDecimal.ZERO) == 0) {
                    stat.setNonProductiveTimeStr("0.00%");
                } else {
                    // 转换逻辑：乘以100 → 保留2位小数（四舍五入）→ 拼接%
                    BigDecimal percent = nonProductiveTime.multiply(new BigDecimal("100"))
                            .setScale(2, RoundingMode.HALF_UP);
                    stat.setNonProductiveTimeStr(percent + "%");
                }
                // 累计用电量 MWh
                BigDecimal cumulativePowerConsumption = stat.getCumulativePowerConsumption();
                if (ObjUtil.isNotEmpty(cumulativePowerConsumption)) {
                    BigDecimal powerWanFang = cumulativePowerConsumption
                            .divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
                    stat.setCumulativePowerConsumptionExport(powerWanFang);
                }
            });
        }

        if ("1".equals(pageReqVO.getProjectClassification())) {
            // 钻井日报汇总数据
            sheetName = "钻井日报汇总数据";
            // 修改 statistics 集合类型为 IotRyZjDailyReportStatisticsRespVO
            List<IotRyZjDailyReportStatisticsRespVO> zjStatistics = BeanUtils.toBean(statistics, IotRyZjDailyReportStatisticsRespVO.class);
            // 导出 Excel
            ExcelUtils.write(response, "瑞鹰日报汇总.xls", sheetName,
                    IotRyZjDailyReportStatisticsRespVO.class, zjStatistics);
        } else {
            // 修井日报汇总数据
            sheetName = "修井日报汇总数据";
            // 修改 statistics 集合类型为 IotRyXjDailyReportStatisticsRespVO
            List<IotRyXjDailyReportStatisticsRespVO> xjStatistics = BeanUtils.toBean(statistics, IotRyXjDailyReportStatisticsRespVO.class);
            // 导出 Excel
            ExcelUtils.write(response, "瑞鹰日报汇总.xls", sheetName,
                    IotRyXjDailyReportStatisticsRespVO.class, xjStatistics);
        }
    }

    @GetMapping("/exportSingleWells")
    @Operation(summary = "导出瑞鹰 日报 单井统计 Excel")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportSingleWells(@Valid IotRyDailyReportPageReqVO pageReqVO,
                                            HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        List<IotRyDailyReportDO> list = iotRyDailyReportService.teamReports(pageReqVO).getList();
        // 导出 Excel
        ExcelUtils.write(response, "瑞鹰日报单井统计.xls", "瑞鹰日报单井统计数据", IotRyDailyReportRespVO.class,
                buildSubtotalRyDailyReports(list, pageReqVO));
    }

    @GetMapping("/exportSingleTeams")
    @Operation(summary = "导出瑞鹰 日报 单队伍 统计 Excel")
    @PreAuthorize("@ss.hasPermission('pms:iot-ry-daily-report:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportSingleTeams(@Valid IotRyDailyReportPageReqVO pageReqVO,
                                  HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        // 根据查询参数筛选出 符合条件 的记录id 再传入 分页查询
        Set<Long> projectIds = new HashSet<>();
        Set<Long> taskIds = new HashSet<>();
        if (StrUtil.isNotBlank(pageReqVO.getContractName())) {
            IotProjectInfoPageReqVO reqVO = new IotProjectInfoPageReqVO();
            reqVO.setContractName(pageReqVO.getContractName());
            List<IotProjectInfoDO> projects = iotProjectInfoService.getIotProjectInfos(reqVO);
            if (CollUtil.isNotEmpty(projects)) {
                projects.forEach(project -> {
                    projectIds.add(project.getId());
                });
                pageReqVO.setProjectIds(projectIds);
            }
        }
        if (StrUtil.isNotBlank(pageReqVO.getTaskName())) {
            IotProjectTaskPageReqVO reqVO = new IotProjectTaskPageReqVO();
            reqVO.setSearchKey(pageReqVO.getTaskName());
            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(reqVO);
            if (CollUtil.isNotEmpty(tasks)) {
                tasks.forEach(task -> {
                    taskIds.add(task.getId());
                });
                pageReqVO.setTaskIds(taskIds);
            }
        }
        List<IotRyDailyReportDO> list = iotRyDailyReportService.wellReports(pageReqVO).getList();
        // 导出 Excel
        ExcelUtils.write(response, "瑞鹰日报单队伍统计.xls", "瑞鹰日报单队伍统计数据", IotRyDailyReportRespVO.class,
                buildWellRyDailyReports(list, pageReqVO));
    }

}