Просмотр исходного кода

pms 瑞恒日报汇总生产动态列表 消息推送

zhangcl 4 недель назад
Родитель
Сommit
2100366e97

+ 3 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/constant/PmsConstants.java

@@ -46,4 +46,7 @@ public interface PmsConstants {
 
     // 生产异常消息提醒
     String PRODUCTION_EXCEPTION = "production-exception";
+
+    // 瑞恒日报汇总生产动态消息提醒
+    String RH_PRODUCTION_STATUS = "rh-production-status";
 }

+ 5 - 711
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/stat/IotStaticController.java

@@ -28,7 +28,6 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotdevicerunlog.vo.IotDevice
 import cn.iocoder.yudao.module.pms.controller.admin.iotdevicerunlog.vo.IotDeviceRunLogRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotmainworkorder.vo.IotMainWorkOrderPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeationFillPageReqVO;
-import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotProjectTaskPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrhdailyreport.vo.IotRhDailyReportPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreport.vo.IotRyDailyReportPageReqVO;
@@ -50,7 +49,6 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.iotcountdata.IotCountDataDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotdeviceassociate.IotDeviceAssociateDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotmainworkorder.IotMainWorkOrderDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotoutbound.IotOutboundDO;
-import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttask.IotProjectTaskDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotrddailyreport.IotRdDailyReportDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotrhdailyreport.IotRhDailyReportDO;
@@ -83,6 +81,7 @@ import cn.iocoder.yudao.module.pms.service.DeviceServiceImpl;
 import cn.iocoder.yudao.module.pms.service.inspect.IotInspectOrderService;
 import cn.iocoder.yudao.module.pms.service.iotmainworkorder.IotMainWorkOrderService;
 import cn.iocoder.yudao.module.pms.service.iotprojecttask.IotProjectTaskService;
+import cn.iocoder.yudao.module.pms.service.iotrhdailyreport.IotRhDailyReportService;
 import cn.iocoder.yudao.module.pms.service.iotrydailyreport.IotRyDailyReportService;
 import cn.iocoder.yudao.module.pms.service.iotryhidailyreport.IotRyHiDailyReportService;
 import cn.iocoder.yudao.module.pms.service.maintain.IotMaintainService;
@@ -128,13 +127,11 @@ import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 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.date.DateUtils.*;
 import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_DAILY_REPORT_DEPT_NOT_EXISTS;
 import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_DAILY_REPORT_TIME_NOT_EXISTS;
@@ -211,6 +208,8 @@ public class IotStaticController {
     private IotYfClassifyMapper iotYfClassifyMapper;
     @Resource
     private DictTypeService dictTypeService;
+    @Resource
+    private IotRhDailyReportService iotRhDailyReportService;
 
 
     @GetMapping("/main/day")
@@ -2575,7 +2574,7 @@ public class IotStaticController {
     @GetMapping("/rh/device/utilizationRate")
     @PermitAll
     public CommonResult<List<ProjectUtilizationRateVo>> utilizationRate(IotRhDailyReportPageReqVO reqVO) {
-        List<ProjectUtilizationRateVo> productionStatuses = rhProductionStatusList(reqVO);
+        List<ProjectUtilizationRateVo> productionStatuses = iotRhDailyReportService.rhProductionStatusList(reqVO);
         return success(productionStatuses);
     }
 
@@ -2585,7 +2584,7 @@ public class IotStaticController {
     @PreAuthorize("@ss.hasPermission('pms:iot-rh-daily-report:export')")
     @ApiAccessLog(operateType = EXPORT)
     public void exportRhDashboard(@Valid IotRhDailyReportPageReqVO reqVO, HttpServletResponse response) throws IOException {
-        List<ProjectUtilizationRateVo> productionStatuses = rhProductionStatusList(reqVO);
+        List<ProjectUtilizationRateVo> productionStatuses = iotRhDailyReportService.rhProductionStatusList(reqVO);
         // 汇总 注入量(日注气 累注气 日注水 累注水) 运行时效(当日运行时效 平均运行时效)
         if (CollUtil.isNotEmpty(productionStatuses)) {
             // 百分比格式化:保留2位小数,自动乘100加%
@@ -2637,711 +2636,6 @@ public class IotStaticController {
                 productionStatuses);
     }
 
-    /**
-     * 查询 瑞恒看板 生产动态列表
-     */
-    private List<ProjectUtilizationRateVo> rhProductionStatusList(IotRhDailyReportPageReqVO reqVO) {
-        if (ObjUtil.isEmpty(reqVO.getCreateTime())) {
-            throw exception(IOT_DAILY_REPORT_TIME_NOT_EXISTS);
-        }
-        if (reqVO.getCreateTime().length < 2) {
-            throw exception(IOT_DAILY_REPORT_TIME_NOT_EXISTS);
-        }
-        List<Long> sortedProjectIds = new ArrayList<>();
-        sortedProjectIds.add(157l);
-        sortedProjectIds.add(164l); // 塔河项目部
-        sortedProjectIds.add(165l); // 塔里木项目部
-        sortedProjectIds.add(160l); // 吐哈项目部
-        sortedProjectIds.add(256l); // 川庆项目部
-        sortedProjectIds.add(269l); // 非洲项目部
-        sortedProjectIds.add(273l); // 南美项目部
-        sortedProjectIds.add(275l); // 中东项目部
-        sortedProjectIds.add(282l); // 中亚项目部
-        // 各项目部的设备利用率集合
-        List<ProjectUtilizationRateVo> rates = new ArrayList<>();
-        // 查询瑞恒 157l 所有项目部
-        Set<Long> childDeptIds = deptService.getChildDeptIdListFromCache(157l);
-        Map<Long, DeptDO> allDeptPair = deptService.getDeptMap(childDeptIds);
-        Set<String> projectDeptNames = new HashSet<>();
-        Set<Long> projectDeptIds = new HashSet<>();
-        // key项目部id   value项目部名称
-        Map<Long, String> projectPair = new HashMap<>();
-        // key项目部id   value项目部包含的队伍id集合
-        Map<Long, Set<Long>> projectTeamPair = new HashMap<>();
-        // 队伍id 集合
-        Set<Long> teamDepartmentIds = new HashSet<>();
-
-        // key施工队伍id   value状态为LY的项目部id
-        Map<Long, Long> teamProjectIdPair = new HashMap<>();
-
-        // key施工队伍id   value队伍上级项目部id
-        Map<Long, Long> teamIdProjectIdPair = new HashMap<>();
-
-        // key项目部id     value项目部年累计注气量
-        Map<Long, BigDecimal> projectYearGasInjectionPair = new HashMap<>();
-        // key项目部id     value项目部年累计注水量
-        Map<Long, BigDecimal> projectYearWaterInjectionPair = new HashMap<>();
-
-        // key项目部id   value项目部下包含的 施工 队伍数量
-        Map<Long, Integer> sgTeamCountPair = new HashMap<>();
-        // key项目部id   value项目部下包含的 施工准备 队伍数量
-        Map<Long, Integer> zbTeamCountPair = new HashMap<>();
-        // key项目部id   value项目部下包含的 驻地待命 队伍数量
-        Map<Long, Integer> zddmTeamCountPair = new HashMap<>();
-
-        // 筛选瑞恒 主设备 rq_iot_rh_main_device_category
-        Set<Long> mainDeviceCategoryIds = new HashSet<>();
-        List<DictDataDO> constructionStatusDictData = dictDataService.getDictDataListByDictType("rq_iot_rh_main_device_category");
-        if (CollUtil.isNotEmpty(constructionStatusDictData)) {
-            constructionStatusDictData.forEach(data -> {
-                String value = data.getValue();
-                if (NumberUtil.isNumber(value)) {
-                    Long longValue = Long.valueOf(value);
-                    mainDeviceCategoryIds.add(longValue);
-                }
-            });
-        }
-
-        // 查询 瑞恒 所有设备 只统计包含 施工 状态设备的队伍
-        IotDevicePageReqVO deviceReqVO = new IotDevicePageReqVO();
-        deviceReqVO.setDeptIds(new ArrayList<>(childDeptIds));
-        List<IotDeviceDO> allRhDevices = iotDeviceMapper.selectListAlone(deviceReqVO);
-        Set<Long> haveDeviceDeptIds = new HashSet<>();
-        if (CollUtil.isNotEmpty(allRhDevices)) {
-            // 筛选出包含设备的队伍部门集合
-            // 20260608 修改成 包含瑞恒主设备的 部门队伍id集合
-            allRhDevices.forEach(device -> {
-                if (mainDeviceCategoryIds.contains(device.getAssetClass())) {
-                    haveDeviceDeptIds.add(device.getDeptId());
-                }
-            });
-        }
-        if (CollUtil.isNotEmpty(allDeptPair)) {
-            allDeptPair.forEach((deptId, dept) -> {
-                // 项目部
-                if ("2".equals(dept.getType())) {
-                    projectDeptIds.add(deptId);
-                    projectDeptNames.add(dept.getName());
-                    projectPair.put(deptId, dept.getName());
-                }
-                // 队伍
-                if ("3".equals(dept.getType())) {
-                    teamDepartmentIds.add(dept.getId());
-                }
-            });
-            // 遍历所有部门 只筛选出有设备的队伍
-            allDeptPair.forEach((deptId, dept) -> {
-                // 找出每个项目部下的队伍
-                if (projectPair.containsKey(dept.getParentId())) {
-                    // 获得当前部门的上级项目部
-                    projectPair.forEach((projectDeptId, projectDeptName) -> {
-                        if (projectDeptId.equals(dept.getParentId()) && haveDeviceDeptIds.contains(deptId) && teamDepartmentIds.contains(deptId)) {
-                            if (projectTeamPair.containsKey(projectDeptId)) {
-                                Set<Long> teamIds = projectTeamPair.get(projectDeptId);
-                                teamIds.add(deptId);
-                                projectTeamPair.put(projectDeptId, teamIds);
-                            } else {
-                                Set<Long> teamIds = new HashSet<>();
-                                teamIds.add(deptId);
-                                projectTeamPair.put(projectDeptId, teamIds);
-                            }
-                            // 设置队伍与上级项目部的关联关系集合
-                            teamProjectIdPair.put(dept.getId(), dept.getParentId());
-                        }
-                        if (projectDeptId.equals(dept.getParentId()) && teamDepartmentIds.contains(deptId)) {
-                            teamIdProjectIdPair.put(dept.getId(), dept.getParentId());
-                        }
-                    });
-                }
-            });
-        }
-        // 查询出指定时间区间内 已经填写的日报数量 存在注气时间是0 但是 注气量 不为0的情况
-        // reqVO.setStatisticFlag("Y");
-        List<IotRhDailyReportDO> dailyReports = iotRhDailyReportMapper.dailyReports(reqVO);
-
-        // 查询时间所在年份所有日报 计算年累计注气 年累计注水
-        IotRhDailyReportPageReqVO yearReqVO = new IotRhDailyReportPageReqVO();
-        LocalDateTime[] timeRange = reqVO.getCreateTime();
-        if (ObjUtil.isNotEmpty(timeRange) && timeRange.length >= 2) {
-            LocalDateTime originalStart = timeRange[0];
-            LocalDateTime originalEnd = timeRange[1];
-            int statYear = originalStart.getYear();
-            // 取当年1月1日零点 从去掉12月26日开始统计 工作量
-            // LocalDateTime yearBegin = LocalDateTime.of(originalStart.getYear(), 1, 1, 0, 0);
-            LocalDateTime yearBegin = LocalDateTime.of(statYear - 1, 12, 26, 0, 0);
-            yearReqVO.setCreateTime(new LocalDateTime[]{yearBegin, originalEnd});
-        }
-        List<IotRhDailyReportDO> yearDailyReports = iotRhDailyReportMapper.dailyReports(yearReqVO);
-
-        // key项目部id     value项目部下包含设备的队伍 年度 注气时间 或 注水时间 总和
-        Map<Long, BigDecimal> projectYearWorkTimePair = new HashMap<>();
-
-        if (CollUtil.isNotEmpty(yearDailyReports)) {
-            yearDailyReports.forEach(yearReport -> {
-                Long deptId = yearReport.getDeptId();
-                BigDecimal dailyGasInjection = yearReport.getDailyGasInjection();
-                BigDecimal dailyWaterInjection = yearReport.getDailyWaterInjection();
-
-                if (CollUtil.isNotEmpty(projectTeamPair)) {
-                    /* projectTeamPair.forEach((projectDeptId, teamDeptIds) -> {
-                        if (teamDeptIds.contains(deptId)) {
-                            // 各项目部年度 运行时效(按小时统计的设备利用率)
-                            // 按小时数 统计设备利用率
-                            BigDecimal gasInjectionTime = ObjUtil.defaultIfNull(yearReport.getDailyInjectGasTime(), BigDecimal.ZERO);
-                            BigDecimal waterInjectionTime = ObjUtil.defaultIfNull(yearReport.getDailyInjectWaterTime(), BigDecimal.ZERO);
-                            // 取 注气时间 注水时间 的较大者
-                            BigDecimal workTime = gasInjectionTime.compareTo(BigDecimal.ZERO) > 0
-                                    ? gasInjectionTime
-                                    : waterInjectionTime.compareTo(BigDecimal.ZERO) > 0
-                                    ? waterInjectionTime
-                                    : BigDecimal.ZERO;
-                            if (workTime.compareTo(BigDecimal.ZERO) > 0) {
-                                projectYearWorkTimePair.merge(projectDeptId, workTime, BigDecimal::add);
-                            }
-                        }
-                    }); */
-
-                    // 设置各项目部 按照时间统计的 设备利用率
-                    // 各项目部年度 运行时效(按小时统计的设备利用率)
-                    // 按小时数 统计设备利用率
-                    BigDecimal gasInjectionTime = ObjUtil.defaultIfNull(yearReport.getDailyInjectGasTime(), BigDecimal.ZERO);
-                    BigDecimal waterInjectionTime = ObjUtil.defaultIfNull(yearReport.getDailyInjectWaterTime(), BigDecimal.ZERO);
-                    // 取 注气时间 注水时间 的较大者
-                    BigDecimal workTime = gasInjectionTime.compareTo(BigDecimal.ZERO) > 0
-                            ? gasInjectionTime
-                            : waterInjectionTime.compareTo(BigDecimal.ZERO) > 0
-                            ? waterInjectionTime
-                            : BigDecimal.ZERO;
-                    if (workTime.compareTo(BigDecimal.ZERO) > 0) {
-                        Long projectDeptId = teamProjectIdPair.get(deptId);
-                        if (ObjUtil.isNotEmpty(projectDeptId) && projectTeamPair.containsKey(projectDeptId)) {
-                            projectYearWorkTimePair.merge(projectDeptId, workTime, BigDecimal::add);
-                        }
-                    }
-                }
-
-                // 年累计的工作量 注气 注水 运行时效 不考虑队伍是否包含主设备
-                if (teamIdProjectIdPair.containsKey(deptId)) {
-                    Long projectDeptId = teamIdProjectIdPair.get(deptId);
-                    // 项目部 年累计注气量
-                    if (projectYearGasInjectionPair.containsKey(projectDeptId)) {
-                        BigDecimal tempGasInjection = projectYearGasInjectionPair.get(projectDeptId);
-                        BigDecimal subtotal = tempGasInjection.add(dailyGasInjection);
-                        projectYearGasInjectionPair.put(projectDeptId, subtotal);
-                    } else {
-                        projectYearGasInjectionPair.put(projectDeptId, dailyGasInjection);
-                    }
-                    // 项目部 年累计注水量
-                    if (projectYearWaterInjectionPair.containsKey(projectDeptId)) {
-                        BigDecimal tempWaterInjection = projectYearWaterInjectionPair.get(projectDeptId);
-                        BigDecimal subtotal = tempWaterInjection.add(dailyWaterInjection);
-                        projectYearWaterInjectionPair.put(projectDeptId, subtotal);
-                    } else {
-                        projectYearWaterInjectionPair.put(projectDeptId, dailyWaterInjection);
-                    }
-                }
-            });
-        }
-
-        // key项目部id     value项目部包含的队伍指定时间区域内日报数量
-        Map<Long, Integer> projectReportPair = new HashMap<>();
-
-        // 以施工区域为维度统计 队伍总数 施工队伍数 准备队伍数
-        // key: 项目部ID -> key: 施工区域 -> Set<队伍ID>
-        Map<Long, Map<String, Set<Long>>> projectLocationTeamSet = new HashMap<>();   // 总队伍
-        Map<Long, Map<String, Set<Long>>> projectLocationSgTeamSet = new HashMap<>(); // 施工队伍(注气/注水>0)
-        Map<Long, Map<String, Set<Long>>> projectLocationZbTeamSet = new HashMap<>(); // 准备队伍(zb/dq/wg)
-
-        // key项目部id     value项目部注气量
-        Map<Long, BigDecimal> projectGasInjectionPair = new HashMap<>();
-        // key项目部id     value项目部注水量
-        Map<Long, BigDecimal> projectWaterInjectionPair = new HashMap<>();
-
-        // 查询所有日报关联的任务 井号 施工区域
-        Map<Long, String> taskWellNamePair = new HashMap<>();
-        Map<Long, String> taskLocationPair = new HashMap<>();
-
-        // 生产动态临时存储
-        Map<Long, Map<String, Map<String, List<String>>>> productionMap = new HashMap<>();
-
-        // key项目部id     value项目部下包含设备的队伍注气时间或注水时间总和
-        Map<Long, BigDecimal> projectWorkTimePair = new HashMap<>();
-
-        if (CollUtil.isNotEmpty(dailyReports)) {
-            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
-            taskReqVO.setTaskIds(convertList(dailyReports, IotRhDailyReportDO::getTaskId));
-            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(taskReqVO);
-            if (CollUtil.isNotEmpty(tasks)) {
-                tasks.forEach(task -> {
-                    taskWellNamePair.put(task.getId(), task.getWellName());
-                    taskLocationPair.put(task.getId(), task.getLocation());
-                });
-            }
-
-            // 整理出每个项目部下的队伍填报的日报数量
-            dailyReports.forEach(report -> {
-                Long deptId = report.getDeptId();
-                BigDecimal dailyGasInjection = report.getDailyGasInjection();
-                BigDecimal dailyWaterInjection = report.getDailyWaterInjection();
-
-                if (CollUtil.isNotEmpty(projectTeamPair)) {
-                    projectTeamPair.forEach((projectDeptId, teamDeptIds) -> {
-                        if (teamDeptIds.contains(deptId)) {
-                            // 以施工区域为维度统计 队伍总数 施工队伍数 准备队伍数 待命队伍数量
-                            Long taskId = report.getTaskId();
-                            String taskLocation = StrUtil.EMPTY;
-                            if (ObjUtil.isNotEmpty(taskId) && taskLocationPair.containsKey(taskId)) {
-                                taskLocation = taskLocationPair.get(taskId);
-                            }
-
-                            // 项目部下 施工区域内 队伍总数量
-                            if (StrUtil.isNotBlank(taskLocation)) {
-                                // 总队伍(去重)
-                                projectLocationTeamSet
-                                        .computeIfAbsent(projectDeptId, k -> new HashMap<>())
-                                        .computeIfAbsent(taskLocation, k -> new HashSet<>())
-                                        .add(deptId);
-                            }
-
-                            if (dailyGasInjection.compareTo(BigDecimal.ZERO) > 0 || dailyWaterInjection.compareTo(BigDecimal.ZERO) > 0) {
-                                // 项目部日报数量 20260608 统计 注气量 or 注水量 > 0 的日报数量
-                                if (projectReportPair.containsKey(projectDeptId)) {
-                                    Integer tempCount = projectReportPair.get(projectDeptId);
-                                    projectReportPair.put(projectDeptId, ++tempCount);
-                                } else {
-                                    projectReportPair.put(projectDeptId, 1);
-                                }
-                                // 施工队伍数量
-                                if (haveDeviceDeptIds.contains(deptId)) {
-                                    if (sgTeamCountPair.containsKey(projectDeptId)) {
-                                        Integer tempCount = sgTeamCountPair.get(projectDeptId);
-                                        sgTeamCountPair.put(projectDeptId, ++tempCount);
-                                    } else {
-                                        sgTeamCountPair.put(projectDeptId, 1);
-                                    }
-                                }
-                                // 项目部下 施工区域内 施工队伍数量
-                                if (StrUtil.isNotBlank(taskLocation)) {
-                                    projectLocationSgTeamSet
-                                            .computeIfAbsent(projectDeptId, k -> new HashMap<>())
-                                            .computeIfAbsent(taskLocation, k -> new HashSet<>())
-                                            .add(deptId);
-                                }
-                            }
-
-                            // 统计 施工 准备 待命 队伍数量
-                            // 准备
-                            if ("zb".equals(report.getConstructionStatus()) || "dq".equals(report.getConstructionStatus()) || "wg".equals(report.getConstructionStatus())) {
-                                if (zbTeamCountPair.containsKey(projectDeptId)) {
-                                    Integer tempCount = zbTeamCountPair.get(projectDeptId);
-                                    zbTeamCountPair.put(projectDeptId, ++tempCount);
-                                } else {
-                                    zbTeamCountPair.put(projectDeptId, 1);
-                                }
-                            }
-
-                            // 项目部 指定时间的注气量
-                            /* if (projectGasInjectionPair.containsKey(projectDeptId)) {
-                                BigDecimal tempGasInjection = projectGasInjectionPair.get(projectDeptId);
-                                BigDecimal subtotal = tempGasInjection.add(dailyGasInjection);
-                                projectGasInjectionPair.put(projectDeptId, subtotal);
-                            } else {
-                                projectGasInjectionPair.put(projectDeptId, dailyGasInjection);
-                            } */
-                            // 项目部 指定时间的注水量
-                            /* if (projectWaterInjectionPair.containsKey(projectDeptId)) {
-                                BigDecimal tempWaterInjection = projectWaterInjectionPair.get(projectDeptId);
-                                BigDecimal subtotal = tempWaterInjection.add(dailyWaterInjection);
-                                projectWaterInjectionPair.put(projectDeptId, subtotal);
-                            } else {
-                                projectWaterInjectionPair.put(projectDeptId, dailyWaterInjection);
-                            } */
-
-                            // 日报关联的任务井号
-                            Long reportTaskId = report.getTaskId();
-
-                            // 提取 所有日报非生产时间和 = 24H 的队伍
-                            List<BigDecimal> nptTimeList = Arrays.asList(
-                                    report.getAccidentTime(),
-                                    report.getRepairTime(),
-                                    report.getSelfStopTime(),
-                                    report.getComplexityTime(),
-                                    report.getRelocationTime(),
-                                    report.getRectificationTime(),
-                                    report.getWaitingStopTime(),
-                                    report.getWinterBreakTime(),
-                                    report.getPartyaDesign(),
-                                    report.getPartyaPrepare(),
-                                    report.getPartyaResource(),
-                                    report.getOtherNptTime()
-                            );
-                            BigDecimal totalNptTime = nptTimeList.stream()
-                                    .map(time -> Optional.ofNullable(time).orElse(BigDecimal.ZERO))
-                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
-                            BigDecimal twentyFour = new BigDecimal("24");
-                            if (totalNptTime.compareTo(twentyFour) == 0) {
-                                // 所有非生产时间和=24H 的队伍 提取生产动态
-                                if (ObjUtil.isNotEmpty(reportTaskId)) {
-                                    String wellName = taskWellNamePair.get(reportTaskId);
-                                    String location = taskLocationPair.get(reportTaskId);
-                                    if (StrUtil.isNotBlank(wellName) && StrUtil.isNotBlank(location)) {
-                                        String dynamic = report.getProductionStatus();
-                                        productionMap.computeIfAbsent(projectDeptId, k -> new HashMap<>())
-                                                .computeIfAbsent(location, k -> new HashMap<>())
-                                                .computeIfAbsent(wellName, k -> new ArrayList<>())
-                                                .add(dynamic);
-                                    }
-                                }
-                            }
-                            // 按小时数 统计设备利用率
-                            BigDecimal gasInjectionTime = ObjUtil.defaultIfNull(report.getDailyInjectGasTime(), BigDecimal.ZERO);
-                            BigDecimal waterInjectionTime = ObjUtil.defaultIfNull(report.getDailyInjectWaterTime(), BigDecimal.ZERO);
-                            // 取 注气时间 注水时间 的较大者
-                            BigDecimal workTime = gasInjectionTime.compareTo(BigDecimal.ZERO) > 0
-                                    ? gasInjectionTime
-                                    : waterInjectionTime.compareTo(BigDecimal.ZERO) > 0
-                                    ? waterInjectionTime
-                                    : BigDecimal.ZERO;
-                            if (workTime.compareTo(BigDecimal.ZERO) > 0) {
-                                projectWorkTimePair.merge(projectDeptId, workTime, BigDecimal::add);
-                            }
-                        }
-                    });
-                }
-                // 统计所有队伍的注气量 注水量 不区分是否包含主设备
-                if (teamIdProjectIdPair.containsKey(deptId)) {
-                    Long projectId = teamIdProjectIdPair.get(deptId);
-                    // 项目部 指定时间的注气量
-                    if (projectGasInjectionPair.containsKey(projectId)) {
-                        BigDecimal tempGasInjection = projectGasInjectionPair.get(projectId);
-                        BigDecimal subtotal = tempGasInjection.add(dailyGasInjection);
-                        projectGasInjectionPair.put(projectId, subtotal);
-                    } else {
-                        projectGasInjectionPair.put(projectId, dailyGasInjection);
-                    }
-                    // 项目部 指定时间的注水量
-                    if (projectWaterInjectionPair.containsKey(projectId)) {
-                        BigDecimal tempWaterInjection = projectWaterInjectionPair.get(projectId);
-                        BigDecimal subtotal = tempWaterInjection.add(dailyWaterInjection);
-                        projectWaterInjectionPair.put(projectId, subtotal);
-                    } else {
-                        projectWaterInjectionPair.put(projectId, dailyWaterInjection);
-                    }
-                }
-            });
-            // 计算指定时间区间内包含的天数
-            long daysCount;
-            if (ObjUtil.isNotEmpty(reqVO.getCreateTime()) && reqVO.getCreateTime().length >= 2) {
-                LocalDateTime start = reqVO.getCreateTime()[0];
-                LocalDateTime end = reqVO.getCreateTime()[1];
-
-                if (ObjUtil.isNotEmpty(start) && ObjUtil.isNotEmpty(end) && !end.isBefore(start)) {
-                    // 使用ChronoUnit.DAYS.between计算天数差,并+1包含首尾两天
-                    daysCount = ChronoUnit.DAYS.between(
-                            start.toLocalDate(),
-                            end.toLocalDate()
-                    ) + 1;
-                } else {
-                    daysCount = 0L;
-                }
-            } else {
-                daysCount = 0L;
-            }
-
-            // 计算年度开始 当前年1.1到指定查询时间区间内包含的天数
-            long yearTotalDay = 0L;
-            List<Long> yearTotalDays = new ArrayList<>();
-            if (ObjUtil.isNotEmpty(reqVO.getCreateTime()) && reqVO.getCreateTime().length >= 2) {
-                // 取查询结束日期
-                LocalDateTime queryEndTime = reqVO.getCreateTime()[1];
-                int startYear = queryEndTime.getYear();
-                // 当年1月1日 零点 从前一年12月26日统计 累计工作量
-                LocalDate yearStartDate = LocalDate.of(startYear - 1, 12, 26);
-                LocalDate queryEndDate = queryEndTime.toLocalDate();
-                // 首尾都包含,+1
-                yearTotalDay = ChronoUnit.DAYS.between(yearStartDate, queryEndDate) + 1;
-                yearTotalDays.add(yearTotalDay);
-            }
-
-            // 计算每个项目部的设备利用率
-            if (CollUtil.isNotEmpty(projectTeamPair)) {
-                projectTeamPair.forEach((projectDeptId, teamIds) -> {
-                    // 暂时不统计欧洲项目部 352
-                    if (352 != projectDeptId) {
-                        ProjectUtilizationRateVo rateVo = new ProjectUtilizationRateVo();
-                        rateVo.setProjectDeptId(projectDeptId);
-                        if (projectPair.containsKey(projectDeptId)) {
-                            rateVo.setProjectDeptName(projectPair.get(projectDeptId));
-                        }
-                        if (allDeptPair.containsKey(projectDeptId)) {
-                            DeptDO dept = allDeptPair.get(projectDeptId);
-                            rateVo.setSort(dept.getSort());
-                        }
-                        // 设置每个项目部的注气量
-                        if (projectGasInjectionPair.containsKey(projectDeptId)) {
-                            BigDecimal gasInjection = projectGasInjectionPair.get(projectDeptId);
-                            // 转换单位 万方
-                            BigDecimal gasInjectionWanFang = gasInjection
-                                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
-                            rateVo.setGasInjection(gasInjectionWanFang);
-                        }
-                        // 设置每个项目部的年累计注气量
-                        if (projectYearGasInjectionPair.containsKey(projectDeptId)) {
-                            BigDecimal yearGasInjection = projectYearGasInjectionPair.get(projectDeptId);
-                            // 转换单位 万方
-                            BigDecimal gasInjectionWanFang = yearGasInjection
-                                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
-                            rateVo.setYearGasInjection(gasInjectionWanFang);
-                        }
-                        // 设置每个项目部的注水量
-                        if (projectWaterInjectionPair.containsKey(projectDeptId)) {
-                            BigDecimal waterInjection = projectWaterInjectionPair.get(projectDeptId);
-                            // 转换单位 万方
-                            BigDecimal waterInjectionWanFang = waterInjection
-                                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
-                            rateVo.setWaterInjection(waterInjectionWanFang);
-                        }
-                        // 设置每个项目部的年累计注水量
-                        if (projectYearWaterInjectionPair.containsKey(projectDeptId)) {
-                            BigDecimal yearWaterInjection = projectYearWaterInjectionPair.get(projectDeptId);
-                            // 转换单位 万方
-                            BigDecimal waterInjectionWanFang = yearWaterInjection
-                                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
-                            rateVo.setYearWaterInjection(waterInjectionWanFang);
-                        }
-                        // 遍历每个项目部 获取每个项目部下队伍数量
-                        Integer currentTeamNum = teamIds.size();
-                        rateVo.setTeamCount(currentTeamNum);
-                        // 待命队伍数量 = 队伍总数 - 施工队伍数量 - 准备队伍数量
-                        // 施工队伍数量
-                        Integer sgTeamCount = sgTeamCountPair.getOrDefault(projectDeptId, 0);
-                        // 施工准备队伍数量
-                        Integer zbTeamCount = zbTeamCountPair.getOrDefault(projectDeptId, 0);
-                        // 驻地待命 队伍数量 = 队伍总数 - 施工队伍 -  准备队伍
-                        Integer dmTeamCount = Math.max(currentTeamNum - sgTeamCount - zbTeamCount, 0);
-                        zddmTeamCountPair.put(projectDeptId, dmTeamCount);
-                        rateVo.setSgTeamCount(sgTeamCount);
-                        rateVo.setZbTeamCount(zbTeamCount);
-                        rateVo.setDmTeamCount(dmTeamCount);
-
-                        rateVo.setCumulativeDays(currentTeamNum * daysCount);
-                        double rate = 0.0;
-                        // 匹配出当前项目部下 日报数量
-                        if (projectReportPair.containsKey(projectDeptId)) {
-                            Integer reportCount = projectReportPair.getOrDefault(projectDeptId, 0);
-                            rateVo.setConstructionDays(reportCount);
-                            // 当前项目部 设备利用率 公式 reportCount/(currentTeamNum*daysCount)
-                            // 计算设备利用率(处理除数为0的情况)
-                            if (currentTeamNum > 0 && daysCount > 0) {
-                                rate = new BigDecimal((double) reportCount / (currentTeamNum * daysCount))
-                                        .setScale(4, RoundingMode.HALF_UP)  // 保留4位小数,四舍五入
-                                        .doubleValue();
-                            }
-                        }
-                        rateVo.setUtilizationRate(rate); // 设备利用率
-                        // 计算项目部 按小时统计的设备利用率(运行时效)
-                        if (projectWorkTimePair.containsKey(projectDeptId)) {
-                            BigDecimal reportWorkTime = projectWorkTimePair.getOrDefault(projectDeptId, BigDecimal.ZERO);
-                            // 统计所有项目部 当日 总工作时间 以便于计算 公司整体的当日运行时效
-                            if (currentTeamNum > 0 && daysCount > 0) {
-                                // 分母 = 队伍数 * 天数 * 24小时
-                                BigDecimal denominator = new BigDecimal(currentTeamNum * daysCount * 24);
-                                // 安全计算,避免除0
-                                BigDecimal utilization = BigDecimal.ZERO;
-                                if (denominator.compareTo(BigDecimal.ZERO) > 0) {
-                                    utilization = reportWorkTime.divide(denominator, 4, RoundingMode.HALF_UP);
-                                    rateVo.setHourUtilizationRate(utilization);
-                                }
-                            }
-                        }
-                        // 计算项目部 年度运行时效 按小时统计的设备利用率(运行时效)
-                        if (projectYearWorkTimePair.containsKey(projectDeptId)) {
-                            BigDecimal reportWorkTime = projectYearWorkTimePair.getOrDefault(projectDeptId, BigDecimal.ZERO);
-                            // 统计所有项目部 年度的 总工作时间 以便于计算 公司整体的当日运行时效
-                            if (currentTeamNum > 0 && CollUtil.isNotEmpty(yearTotalDays)) {
-                                long tempYearTotalDay = yearTotalDays.get(0);
-                                // 分母 = 队伍数 * 天数 * 24小时
-                                BigDecimal denominator = new BigDecimal(currentTeamNum * tempYearTotalDay * 24);
-                                // 安全计算,避免除0
-                                BigDecimal utilization = BigDecimal.ZERO;
-                                if (denominator.compareTo(BigDecimal.ZERO) > 0) {
-                                    utilization = reportWorkTime.divide(denominator, 4, RoundingMode.HALF_UP);
-                                    rateVo.setYearHourUtilizationRate(utilization);
-                                }
-                            }
-                        }
-                        rates.add(rateVo);
-                    }
-                });
-            }
-
-            // 公司汇总的 数据
-            ProjectUtilizationRateVo rateVo = new ProjectUtilizationRateVo();
-            // 计算公司汇总的设备利用率 日平均时效  年平均时效
-            // teamProjectIdPair 有效的队伍集合
-            // 公司当日总运行工时
-            BigDecimal currentTotalWorkTime = projectWorkTimePair.values().stream()
-                    .filter(Objects::nonNull)
-                    .reduce(BigDecimal.ZERO, BigDecimal::add);
-
-            // 公司年度总运行工时
-            BigDecimal yearTotalWorkTime = projectYearWorkTimePair.values().stream()
-                    .filter(Objects::nonNull)
-                    .reduce(BigDecimal.ZERO, BigDecimal::add);
-
-            if (CollUtil.isNotEmpty(teamProjectIdPair)) {
-                int teamCount = teamProjectIdPair.size();
-                // 公司 当日运行时效 分母 = 队伍数 * 天数 * 24小时
-                BigDecimal denominator = new BigDecimal(teamCount * daysCount * 24);
-                // 安全计算,避免除0
-                BigDecimal utilization = BigDecimal.ZERO;
-                if (denominator.compareTo(BigDecimal.ZERO) > 0) {
-                    utilization = currentTotalWorkTime.divide(denominator, 4, RoundingMode.HALF_UP);
-                    rateVo.setHourUtilizationRate(utilization);
-                }
-
-                // 公司 年运行时效 分母 = 队伍数 * 天数 * 24小时
-                long tempYearTotalDay = yearTotalDays.get(0);
-                BigDecimal yearDenominator = new BigDecimal(teamCount * tempYearTotalDay * 24);
-                // 安全计算,避免除0
-                BigDecimal yearUtilization = BigDecimal.ZERO;
-                if (yearDenominator.compareTo(BigDecimal.ZERO) > 0) {
-                    yearUtilization = yearTotalWorkTime.divide(yearDenominator, 4, RoundingMode.HALF_UP);
-                    rateVo.setYearHourUtilizationRate(yearUtilization);
-                }
-            }
-
-            // 公司年累计注气
-            BigDecimal companyYearGasInjection = projectYearGasInjectionPair.values().stream()
-                    .filter(Objects::nonNull)
-                    .reduce(BigDecimal.ZERO, BigDecimal::add);
-            // 单位转换成 万方
-            BigDecimal companyGasInjectionWanFang = companyYearGasInjection
-                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
-
-            // 公司年累计注水
-            BigDecimal companyYearWaterInjection = projectYearWaterInjectionPair.values().stream()
-                    .filter(Objects::nonNull)
-                    .reduce(BigDecimal.ZERO, BigDecimal::add);
-            // 单位转换成 万方
-            BigDecimal companyWaterInjectionWanFang = companyYearWaterInjection
-                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
-
-            // 公司当日累计注气
-            BigDecimal companyCurrentGasInjection = projectGasInjectionPair.values().stream()
-                    .filter(Objects::nonNull)
-                    .reduce(BigDecimal.ZERO, BigDecimal::add);
-            // 单位转换成 万方
-            BigDecimal currentGasInjectionWanFang = companyCurrentGasInjection
-                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
-
-            // 公司当日累计注水
-            BigDecimal companyCurrentWaterInjection = projectWaterInjectionPair.values().stream()
-                    .filter(Objects::nonNull)
-                    .reduce(BigDecimal.ZERO, BigDecimal::add);
-            // 单位转换成 万方
-            BigDecimal currentWaterInjectionWanFang = companyCurrentWaterInjection
-                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
-
-
-            // 公司队伍总数
-            Integer companyTeamCount = teamProjectIdPair.size();
-            // 公司待命队伍总数
-            Integer companyDmTeamCount = zddmTeamCountPair.values().stream()
-                    .filter(Objects::nonNull)
-                    .reduce(0, Integer::sum);
-            // 公司施工准备队伍总数
-            Integer companyZbTeamCount = zbTeamCountPair.values().stream()
-                    .filter(Objects::nonNull)
-                    .reduce(0, Integer::sum);
-            // 公司施工队伍总数
-            Integer companySgTeamCount = sgTeamCountPair.values().stream()
-                    .filter(Objects::nonNull)
-                    .reduce(0, Integer::sum);
-            rateVo.setProjectDeptName("合计");
-            rateVo.setProjectDeptId(157l);
-            rateVo.setSort(0);
-            rateVo.setTeamCount(companyTeamCount);
-            rateVo.setDmTeamCount(companyDmTeamCount);
-            rateVo.setZbTeamCount(companyZbTeamCount);
-            rateVo.setSgTeamCount(companySgTeamCount);
-            rateVo.setGasInjection(currentGasInjectionWanFang);
-            rateVo.setWaterInjection(currentWaterInjectionWanFang);
-            rateVo.setYearGasInjection(companyGasInjectionWanFang);
-            rateVo.setYearWaterInjection(companyWaterInjectionWanFang);
-            rates.add(rateVo);
-
-        }
-        // rates.sort(Comparator.comparingDouble(ProjectUtilizationRateVo::getUtilizationRate).reversed());
-        // 克拉玛依 加入到 吐哈项目部,两个项目部合并
-        AtomicReference<Integer> mergeTeamCount = new AtomicReference<>(0);
-        AtomicReference<Integer> mergeConstructionDays = new AtomicReference<>(0);
-        AtomicReference<Long> mergeCumulativeDays = new AtomicReference<>(0l);
-        rates.forEach(rate -> {
-            // 克拉玛依 项目部数据
-            if (rate.getProjectDeptId() == 166) {
-                mergeTeamCount.set(rate.getTeamCount());
-                mergeCumulativeDays.set(ObjUtil.isNotEmpty(rate.getCumulativeDays()) ? rate.getCumulativeDays() : 0);
-                mergeConstructionDays.set(ObjUtil.isNotEmpty(rate.getConstructionDays()) ? rate.getConstructionDays() : 0);
-            }
-        });
-        rates.forEach(rate -> {
-            // 吐哈项目部 项目部数据
-            if (rate.getProjectDeptId() == 160) {
-                rate.setTeamCount((ObjUtil.isNotEmpty(rate.getTeamCount()) ? rate.getTeamCount() : 0) + mergeTeamCount.get());
-                rate.setCumulativeDays((ObjUtil.isNotEmpty(rate.getCumulativeDays()) ? rate.getCumulativeDays() : 0l) + mergeCumulativeDays.get());
-                rate.setConstructionDays((ObjUtil.isNotEmpty(rate.getConstructionDays()) ? rate.getConstructionDays() : 0) + mergeConstructionDays.get());
-            }
-
-            Map<String, Map<String, List<String>>> locationMap = productionMap.get(rate.getProjectDeptId());
-            // 组装各项目部 按照施工区域维度统计 的 队伍数量 生产动态明细
-            if (locationMap != null && !locationMap.isEmpty()) {
-                Map<String, Set<Long>> teamMap = projectLocationTeamSet.getOrDefault(rate.getProjectDeptId(), Collections.emptyMap());
-                Map<String, Set<Long>> sgTeamMap = projectLocationSgTeamSet.getOrDefault(rate.getProjectDeptId(), Collections.emptyMap());
-
-                StringBuilder sb = new StringBuilder();
-                List<String> sortedLocations = new ArrayList<>(locationMap.keySet());
-                Collections.sort(sortedLocations);
-                for (String location : sortedLocations) {
-                    int total = teamMap.getOrDefault(location, Collections.emptySet()).size();
-                    int sg = sgTeamMap.getOrDefault(location, Collections.emptySet()).size();
-                    int zb = Math.max(total - sg, 0);
-                    sb.append(location).append(":施工").append(sg).append("套设备,准备").append(zb).append("套设备(共").append(total).append("套设备)\n");
-
-                    Map<String, List<String>> wellMap = locationMap.get(location);
-                    List<String> sortedWells = new ArrayList<>(wellMap.keySet());
-                    Collections.sort(sortedWells);
-                    for (String well : sortedWells) {
-                        List<String> dynamics = wellMap.get(well);
-                        // 过滤掉空字符串(包括null和仅空白字符)
-                        List<String> nonEmptyDynamics = dynamics.stream()
-                                .filter(s -> s != null && !s.trim().isEmpty())
-                                .collect(Collectors.toList());
-                        if (nonEmptyDynamics.isEmpty()) {
-                            // 无有效动态时,只显示井名和一个分号
-                            sb.append(well).append(",;\n");
-                        } else {
-                            String dynamicsStr = String.join(";", nonEmptyDynamics);
-                            sb.append(well).append(",").append(dynamicsStr).append(";\n");
-                        }
-                    }
-                }
-                rate.setProductionSummary(sb.toString());
-            }
-        });
-        // 删除projectDeptId为166的对象 克拉玛依项目部
-        rates.removeIf(rate -> rate.getProjectDeptId() == 166);
-        // 暂时不统计欧洲项目部 352
-        rates.sort(Comparator.comparingInt(vo -> {
-            Long deptId = vo.getProjectDeptId();
-            // 获取当前projectDeptId 在 sortedProjectIds中 的索引
-            int index = sortedProjectIds.indexOf(deptId);
-            // 若不在集合中,用集合长度作为索引(排在所有指定项目部之后)
-            return index != -1 ? index : sortedProjectIds.size();
-        }));
-        return rates;
-    }
-
     /**
      * 瑞恒 公司总体 设备利用率
      * @return

+ 219 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/dailyreport/CreateRhDailyReportStatisticsJob.java

@@ -0,0 +1,219 @@
+package cn.iocoder.yudao.module.pms.job.dailyreport;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
+import cn.iocoder.yudao.module.pms.constant.PmsConstants;
+import cn.iocoder.yudao.module.pms.controller.admin.iotrhdailyreport.vo.IotRhDailyReportPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.stat.vo.ProjectUtilizationRateVo;
+import cn.iocoder.yudao.module.pms.message.PmsMessage;
+import cn.iocoder.yudao.module.pms.service.iotrhdailyreport.IotRhDailyReportService;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
+import cn.iocoder.yudao.module.system.service.dept.DeptService;
+import cn.iocoder.yudao.module.system.service.permission.RoleService;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;
+
+/**
+ * 瑞恒每天生成昨日的日报汇总 钉钉 推送消息
+ */
+@Component
+@Slf4j
+public class CreateRhDailyReportStatisticsJob implements JobHandler {
+
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private IotRhDailyReportService iotRhDailyReportService;
+    @Resource(name = PMS_THREAD_POOL_TASK_EXECUTOR)
+    private ThreadPoolTaskExecutor pmsThreadPoolTaskExecutor;
+    @Resource
+    private PmsMessage pmsMessage;
+    @Resource
+    private DeptService deptService;
+    @Resource
+    private AdminUserService adminUserService;
+    @Resource
+    private RoleService roleService;
+    @Resource
+    private UserRoleMapper userRoleMapper;
+
+    @Override
+    @TenantIgnore
+    public String execute(String param) throws Exception {
+        // 获取当前时间
+        LocalDateTime currentDate = LocalDateTime.now();
+        // 根据当前日期生成前一天的日期
+        LocalDateTime yesterday = currentDate.minusDays(1);
+
+        LocalDateTime createTimeStart = yesterday.toLocalDate().atStartOfDay();
+        LocalDateTime createTimeEnd = yesterday.toLocalDate().atTime(LocalTime.MAX);
+
+        // 查询瑞恒昨天的日报列表
+        IotRhDailyReportPageReqVO reqVO = new IotRhDailyReportPageReqVO();
+        reqVO.setCreateTime(new LocalDateTime[]{createTimeStart, createTimeEnd});
+        // 测试数据
+        /* reqVO.setCreateTime(new LocalDateTime[]{
+                LocalDate.of(2026, 6, 15).atStartOfDay(),
+                LocalDate.of(2026, 6, 15).atTime(23, 59, 59)
+        }); */
+        List<ProjectUtilizationRateVo> productionStatuses = iotRhDailyReportService.rhProductionStatusList(reqVO);
+
+        // 汇总 注入量(日注气 累注气 日注水 累注水) 运行时效(当日运行时效 平均运行时效)
+        if (CollUtil.isNotEmpty(productionStatuses)) {
+            // 百分比格式化:保留2位小数,自动乘100加%
+            DecimalFormat percentFormat = new DecimalFormat("#0.00%");
+            percentFormat.setRoundingMode(RoundingMode.HALF_UP);
+
+            productionStatuses.forEach(status -> {
+                List<String> injectLines = new ArrayList<>();
+                List<String> rateLines = new ArrayList<>();
+                // 将 日注气 累注气 日注水 累注水 汇总到 注入量injectionVolume
+                // 将 当日运行时效 平均运行时效 汇总到 运行时效runningAvailability
+                BigDecimal gasInjection = status.getGasInjection();
+                BigDecimal yearGasInjection = status.getYearGasInjection();
+                BigDecimal waterInjection = status.getWaterInjection();
+                BigDecimal yearWaterInjection = status.getYearWaterInjection();
+                BigDecimal hourRate = status.getHourUtilizationRate();
+                BigDecimal yearRate = status.getYearHourUtilizationRate();
+                if (ObjUtil.isNotEmpty(gasInjection) && gasInjection.compareTo(BigDecimal.ZERO) > 0) {
+                    injectLines.add("日注气:" + gasInjection);
+                }
+                if (ObjUtil.isNotEmpty(yearGasInjection) && yearGasInjection.compareTo(BigDecimal.ZERO) > 0) {
+                    injectLines.add("累注气:" + yearGasInjection);
+                }
+                if (ObjUtil.isNotEmpty(waterInjection) && waterInjection.compareTo(BigDecimal.ZERO) > 0) {
+                    injectLines.add("日注水:" + waterInjection);
+                }
+                if (ObjUtil.isNotEmpty(yearWaterInjection) && yearWaterInjection.compareTo(BigDecimal.ZERO) > 0) {
+                    injectLines.add("累注水:" + yearWaterInjection);
+                }
+                String injectStr = injectLines.stream().collect(Collectors.joining("  \n"));
+                status.setInjectionVolume(injectStr);
+
+                if (ObjUtil.isNotEmpty(hourRate) && hourRate.compareTo(BigDecimal.ZERO) > 0) {
+                    // 原始4位小数,转百分比保留两位
+                    String hourPercent = percentFormat.format(hourRate);
+                    rateLines.add("当日运行时效:" + hourPercent);
+                }
+                if (ObjUtil.isNotEmpty(yearRate) && yearRate.compareTo(BigDecimal.ZERO) > 0) {
+                    String yearPercent = percentFormat.format(yearRate);
+                    rateLines.add("平均运行时效:" + yearPercent);
+                }
+                // 拼接外层星号
+                String rateStr = rateLines.stream().collect(Collectors.joining("  \n"));
+                status.setRunningAvailability(rateStr);
+            });
+
+            // 构建包含所有项目部的消息(每个项目部一块)
+            StringBuilder finalMsgBuilder = new StringBuilder();
+
+            // 日报汇总生产动态精简内容
+            String PRODUCTION_STATUS_MSG_TEMPLATE =
+                    "**项目部**:{}  \n" +
+                    "**队伍总数**:{}  \n" +
+                    "**待命队伍**:{}  \n" +
+                    "**施工准备**:{}  \n" +
+                    "**施工队伍**:{}  \n" +
+                    "**注入量(万方)**:  \n{}  \n" +
+                    "**运行时效**:  \n{}  \n";
+
+            for (int i = 0; i < productionStatuses.size(); i++) {
+                ProjectUtilizationRateVo status = productionStatuses.get(i);
+                String block = StrUtil.format(PRODUCTION_STATUS_MSG_TEMPLATE,
+                        status.getProjectDeptName(),
+                        status.getTeamCount(),
+                        status.getDmTeamCount(),
+                        status.getZbTeamCount(),
+                        status.getSgTeamCount(),
+                        // 注入量和运行时效已包含换行,直接放入
+                        status.getInjectionVolume(),
+                        status.getRunningAvailability()
+                );
+                finalMsgBuilder.append(block);
+                // 块之间用分隔线隔开(最后一块不加)
+                // 块之间添加纯文本分隔线,避免被识别为Markdown水平线
+                if (i < productionStatuses.size() - 1) {
+                    finalMsgBuilder.append("  \n==========  \n");
+                }
+            }
+
+            String finalMsgTitle = finalMsgBuilder.toString();
+
+            Set<Long> deptIds = new HashSet<>();
+            Set<Long> userIds = new HashSet<>();
+
+            // 查询瑞恒 157l 下具有 瑞恒日报汇总推送 角色的人员
+            deptIds.add(157l);
+            List<AdminUserDO> companyUsers = adminUserService.getUserListByDeptIds(deptIds);
+            if (CollUtil.isNotEmpty(companyUsers)) {
+                companyUsers.forEach(user -> {
+                    userIds.add(user.getId());
+                });
+            }
+
+            RoleDO role = roleService.getRoleByCode("瑞恒日报汇总推送");
+            if (ObjUtil.isNotEmpty(role)) {
+                Set<Long> roleIds = new HashSet<>();
+                roleIds.add(role.getId());
+                List<UserRoleDO> userRoles = userRoleMapper.selectListByRoleIds(roleIds);
+                if (CollUtil.isNotEmpty(userRoles)) {
+                    // 提取有审批角色的所有用户ID(去重+空值过滤)
+                    Set<Long> roleUserIds = userRoles.stream()
+                            .map(UserRoleDO::getUserId)
+                            .filter(Objects::nonNull) // 过滤空userId
+                            .collect(Collectors.toSet());
+                    // 计算两个集合的交集,得到最终目标用户ID
+                    Set<Long> targetUserIds = userIds.stream()
+                            .filter(roleUserIds::contains)
+                            .collect(Collectors.toSet());
+                    if (CollUtil.isNotEmpty(targetUserIds)) {
+                        Map<Long, AdminUserRespDTO> users = adminUserApi.getUserMap(targetUserIds);
+                        // 给多个用户发送 相同 的 提醒消息
+                        if (CollUtil.isNotEmpty(users)) {
+                            // 生成消息提醒 标题内容
+                            CountDownLatch latch = new CountDownLatch(users.size());
+                            users.forEach((userId, user) -> {
+                                pmsThreadPoolTaskExecutor.execute(() -> {
+                                    try {
+                                        String mobile = user.getMobile();
+                                        // 没有手机号也发送站内信消息
+                                        if (StrUtil.isNotBlank(finalMsgTitle)) {
+                                            pmsMessage.sendMessage(Long.MIN_VALUE, finalMsgTitle, PmsConstants.RH_PRODUCTION_STATUS,
+                                                    userId, mobile);
+                                        }
+                                    } finally {
+                                        latch.countDown();
+                                    }
+                                });
+                            });
+                        }
+                    }
+                }
+            }
+        }
+        return "创建成功";
+    }
+}

+ 6 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/message/PmsMessage.java

@@ -115,6 +115,11 @@ public class PmsMessage {
                 msg = "生产异常  \n" + name + "["+ DateUtil.format(new Date(),"yyyy-MM-dd hh:mm:ss")+"]";
             }
 
+            // 瑞恒日报汇总生产动态提醒
+            if (PmsConstants.RH_PRODUCTION_STATUS.equals(businessType)) {
+                msg = "**瑞恒生产动态**  \n" + name + "["+ DateUtil.format(new Date(),"yyyy-MM-dd hh:mm:ss")+"]";
+            }
+
             // 瑞鹰项目启动设备整改
             if (PmsConstants.RY_IMPROVE_DAILY_REPORT.equals(businessType)) {
                 msg = "您有新的项目启动设备整改日报 " + name + "需要填报,请处理。"+"["+ DateUtil.format(new Date(),"yyyy-MM-dd hh:mm:ss")+"]";
@@ -162,7 +167,7 @@ public class PmsMessage {
                     || PmsConstants.GENERATE_QHSE_CERT.equals(businessType) || PmsConstants.WEEK_ALARM_MESSAGE.equals(businessType)) {
                 //通知设备状态变更为观察使用
                 dingtalkSendApi.send(mobile, msg, "text",url,  title);
-            } else if (PmsConstants.PRODUCTION_EXCEPTION.equals(businessType)) {
+            } else if (PmsConstants.PRODUCTION_EXCEPTION.equals(businessType) || PmsConstants.RH_PRODUCTION_STATUS.equals(businessType)) {
                 // markdown 类型消息 支持 文字加粗
                 dingtalkSendApi.send(mobile, msg, "markdown",url,  title);
             } else {

+ 9 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrhdailyreport/IotRhDailyReportService.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pms.service.iotrhdailyreport;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportSaveReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrhdailyreport.vo.*;
+import cn.iocoder.yudao.module.pms.controller.admin.stat.vo.ProjectUtilizationRateVo;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotrhdailyreport.IotRhDailyReportDO;
 
 import javax.validation.Valid;
@@ -173,4 +174,12 @@ public interface IotRhDailyReportService {
      * @return 瑞恒日报 非生产时效分析 列表
      */
     List<IotRhDailyReportNptStatisticsRespVO> nptStatistics(IotRhDailyReportPageReqVO pageReqVO);
+
+    /**
+     * 瑞恒看板 日报生产动态汇总
+     *
+     * @param pageReqVO 无分页查询
+     * @return 瑞恒日报 非生产时效分析 列表
+     */
+    List<ProjectUtilizationRateVo> rhProductionStatusList(IotRhDailyReportPageReqVO pageReqVO);
 }

+ 674 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrhdailyreport/IotRhDailyReportServiceImpl.java

@@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeati
 import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotProjectTaskPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportSaveReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrhdailyreport.vo.*;
+import cn.iocoder.yudao.module.pms.controller.admin.stat.vo.ProjectUtilizationRateVo;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDevicePageReqVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotcarzhbd.IotCarZhbdDO;
@@ -38,6 +39,7 @@ import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttask.IotProjectTaskMapper
 import cn.iocoder.yudao.module.pms.dal.mysql.iotrhdailyreport.IotRhDailyReportMapper;
 import cn.iocoder.yudao.module.pms.job.IotDeviceCarFuelVO;
 import cn.iocoder.yudao.module.pms.message.PmsMessage;
+import cn.iocoder.yudao.module.pms.service.iotprojecttask.IotProjectTaskService;
 import cn.iocoder.yudao.module.pms.util.ZHBDUtil;
 import cn.iocoder.yudao.module.supplier.enums.common.SupplierAuditStatusEnum;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -83,6 +85,7 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.*;
 import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;
 
@@ -134,6 +137,8 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
     private DeptMapper deptMapper;
     @Resource
     private IotProjectInfoMapper iotProjectInfoMapper;
+    @Resource
+    private IotProjectTaskService iotProjectTaskService;
 
 
     @Override
@@ -1050,6 +1055,675 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
         return result;
     }
 
+    @Override
+    public List<ProjectUtilizationRateVo> rhProductionStatusList(IotRhDailyReportPageReqVO reqVO) {
+        if (ObjUtil.isEmpty(reqVO.getCreateTime())) {
+            throw exception(IOT_DAILY_REPORT_TIME_NOT_EXISTS);
+        }
+        if (reqVO.getCreateTime().length < 2) {
+            throw exception(IOT_DAILY_REPORT_TIME_NOT_EXISTS);
+        }
+        List<Long> sortedProjectIds = new ArrayList<>();
+        sortedProjectIds.add(157l);
+        sortedProjectIds.add(164l); // 塔河项目部
+        sortedProjectIds.add(165l); // 塔里木项目部
+        sortedProjectIds.add(160l); // 吐哈项目部
+        sortedProjectIds.add(256l); // 川庆项目部
+        sortedProjectIds.add(269l); // 非洲项目部
+        sortedProjectIds.add(273l); // 南美项目部
+        sortedProjectIds.add(275l); // 中东项目部
+        sortedProjectIds.add(282l); // 中亚项目部
+        // 各项目部的设备利用率集合
+        List<ProjectUtilizationRateVo> rates = new ArrayList<>();
+        // 查询瑞恒 157l 所有项目部
+        Set<Long> childDeptIds = deptService.getChildDeptIdListFromCache(157l);
+        Map<Long, DeptDO> allDeptPair = deptService.getDeptMap(childDeptIds);
+        Set<String> projectDeptNames = new HashSet<>();
+        Set<Long> projectDeptIds = new HashSet<>();
+        // key项目部id   value项目部名称
+        Map<Long, String> projectPair = new HashMap<>();
+        // key项目部id   value项目部包含的队伍id集合
+        Map<Long, Set<Long>> projectTeamPair = new HashMap<>();
+        // 队伍id 集合
+        Set<Long> teamDepartmentIds = new HashSet<>();
+
+        // key施工队伍id   value状态为LY的项目部id
+        Map<Long, Long> teamProjectIdPair = new HashMap<>();
+
+        // key施工队伍id   value队伍上级项目部id
+        Map<Long, Long> teamIdProjectIdPair = new HashMap<>();
+
+        // key项目部id     value项目部年累计注气量
+        Map<Long, BigDecimal> projectYearGasInjectionPair = new HashMap<>();
+        // key项目部id     value项目部年累计注水量
+        Map<Long, BigDecimal> projectYearWaterInjectionPair = new HashMap<>();
+
+        // key项目部id   value项目部下包含的 施工 队伍数量
+        Map<Long, Integer> sgTeamCountPair = new HashMap<>();
+        // key项目部id   value项目部下包含的 施工准备 队伍数量
+        Map<Long, Integer> zbTeamCountPair = new HashMap<>();
+        // key项目部id   value项目部下包含的 驻地待命 队伍数量
+        Map<Long, Integer> zddmTeamCountPair = new HashMap<>();
+
+        // 筛选瑞恒 主设备 rq_iot_rh_main_device_category
+        Set<Long> mainDeviceCategoryIds = new HashSet<>();
+        List<DictDataDO> constructionStatusDictData = dictDataService.getDictDataListByDictType("rq_iot_rh_main_device_category");
+        if (CollUtil.isNotEmpty(constructionStatusDictData)) {
+            constructionStatusDictData.forEach(data -> {
+                String value = data.getValue();
+                if (NumberUtil.isNumber(value)) {
+                    Long longValue = Long.valueOf(value);
+                    mainDeviceCategoryIds.add(longValue);
+                }
+            });
+        }
+
+        // 查询 瑞恒 所有设备 只统计包含 施工 状态设备的队伍
+        IotDevicePageReqVO deviceReqVO = new IotDevicePageReqVO();
+        deviceReqVO.setDeptIds(new ArrayList<>(childDeptIds));
+        List<IotDeviceDO> allRhDevices = iotDeviceMapper.selectListAlone(deviceReqVO);
+        Set<Long> haveDeviceDeptIds = new HashSet<>();
+        if (CollUtil.isNotEmpty(allRhDevices)) {
+            // 筛选出包含设备的队伍部门集合
+            // 20260608 修改成 包含瑞恒主设备的 部门队伍id集合
+            allRhDevices.forEach(device -> {
+                if (mainDeviceCategoryIds.contains(device.getAssetClass())) {
+                    haveDeviceDeptIds.add(device.getDeptId());
+                }
+            });
+        }
+        if (CollUtil.isNotEmpty(allDeptPair)) {
+            allDeptPair.forEach((deptId, dept) -> {
+                // 项目部
+                if ("2".equals(dept.getType())) {
+                    projectDeptIds.add(deptId);
+                    projectDeptNames.add(dept.getName());
+                    projectPair.put(deptId, dept.getName());
+                }
+                // 队伍
+                if ("3".equals(dept.getType())) {
+                    teamDepartmentIds.add(dept.getId());
+                }
+            });
+            // 遍历所有部门 只筛选出有设备的队伍
+            allDeptPair.forEach((deptId, dept) -> {
+                // 找出每个项目部下的队伍
+                if (projectPair.containsKey(dept.getParentId())) {
+                    // 获得当前部门的上级项目部
+                    projectPair.forEach((projectDeptId, projectDeptName) -> {
+                        if (projectDeptId.equals(dept.getParentId()) && haveDeviceDeptIds.contains(deptId) && teamDepartmentIds.contains(deptId)) {
+                            if (projectTeamPair.containsKey(projectDeptId)) {
+                                Set<Long> teamIds = projectTeamPair.get(projectDeptId);
+                                teamIds.add(deptId);
+                                projectTeamPair.put(projectDeptId, teamIds);
+                            } else {
+                                Set<Long> teamIds = new HashSet<>();
+                                teamIds.add(deptId);
+                                projectTeamPair.put(projectDeptId, teamIds);
+                            }
+                            // 设置队伍与上级项目部的关联关系集合
+                            teamProjectIdPair.put(dept.getId(), dept.getParentId());
+                        }
+                        if (projectDeptId.equals(dept.getParentId()) && teamDepartmentIds.contains(deptId)) {
+                            teamIdProjectIdPair.put(dept.getId(), dept.getParentId());
+                        }
+                    });
+                }
+            });
+        }
+        // 查询出指定时间区间内 已经填写的日报数量 存在注气时间是0 但是 注气量 不为0的情况
+        // reqVO.setStatisticFlag("Y");
+        List<IotRhDailyReportDO> dailyReports = iotRhDailyReportMapper.dailyReports(reqVO);
+
+        // 查询时间所在年份所有日报 计算年累计注气 年累计注水
+        IotRhDailyReportPageReqVO yearReqVO = new IotRhDailyReportPageReqVO();
+        LocalDateTime[] timeRange = reqVO.getCreateTime();
+        if (ObjUtil.isNotEmpty(timeRange) && timeRange.length >= 2) {
+            LocalDateTime originalStart = timeRange[0];
+            LocalDateTime originalEnd = timeRange[1];
+            int statYear = originalStart.getYear();
+            // 取当年1月1日零点 从去掉12月26日开始统计 工作量
+            // LocalDateTime yearBegin = LocalDateTime.of(originalStart.getYear(), 1, 1, 0, 0);
+            LocalDateTime yearBegin = LocalDateTime.of(statYear - 1, 12, 26, 0, 0);
+            yearReqVO.setCreateTime(new LocalDateTime[]{yearBegin, originalEnd});
+        }
+        List<IotRhDailyReportDO> yearDailyReports = iotRhDailyReportMapper.dailyReports(yearReqVO);
+
+        // key项目部id     value项目部下包含设备的队伍 年度 注气时间 或 注水时间 总和
+        Map<Long, BigDecimal> projectYearWorkTimePair = new HashMap<>();
+
+        if (CollUtil.isNotEmpty(yearDailyReports)) {
+            yearDailyReports.forEach(yearReport -> {
+                Long deptId = yearReport.getDeptId();
+                BigDecimal dailyGasInjection = yearReport.getDailyGasInjection();
+                BigDecimal dailyWaterInjection = yearReport.getDailyWaterInjection();
+
+                if (CollUtil.isNotEmpty(projectTeamPair)) {
+
+                    // 设置各项目部 按照时间统计的 设备利用率
+                    // 各项目部年度 运行时效(按小时统计的设备利用率)
+                    // 按小时数 统计设备利用率
+                    BigDecimal gasInjectionTime = ObjUtil.defaultIfNull(yearReport.getDailyInjectGasTime(), BigDecimal.ZERO);
+                    BigDecimal waterInjectionTime = ObjUtil.defaultIfNull(yearReport.getDailyInjectWaterTime(), BigDecimal.ZERO);
+                    // 取 注气时间 注水时间 的较大者
+                    BigDecimal workTime = gasInjectionTime.compareTo(BigDecimal.ZERO) > 0
+                            ? gasInjectionTime
+                            : waterInjectionTime.compareTo(BigDecimal.ZERO) > 0
+                            ? waterInjectionTime
+                            : BigDecimal.ZERO;
+                    if (workTime.compareTo(BigDecimal.ZERO) > 0) {
+                        Long projectDeptId = teamProjectIdPair.get(deptId);
+                        if (ObjUtil.isNotEmpty(projectDeptId) && projectTeamPair.containsKey(projectDeptId)) {
+                            projectYearWorkTimePair.merge(projectDeptId, workTime, BigDecimal::add);
+                        }
+                    }
+                }
+
+                // 年累计的工作量 注气 注水 运行时效 不考虑队伍是否包含主设备
+                if (teamIdProjectIdPair.containsKey(deptId)) {
+                    Long projectDeptId = teamIdProjectIdPair.get(deptId);
+                    // 项目部 年累计注气量
+                    if (projectYearGasInjectionPair.containsKey(projectDeptId)) {
+                        BigDecimal tempGasInjection = projectYearGasInjectionPair.get(projectDeptId);
+                        BigDecimal subtotal = tempGasInjection.add(dailyGasInjection);
+                        projectYearGasInjectionPair.put(projectDeptId, subtotal);
+                    } else {
+                        projectYearGasInjectionPair.put(projectDeptId, dailyGasInjection);
+                    }
+                    // 项目部 年累计注水量
+                    if (projectYearWaterInjectionPair.containsKey(projectDeptId)) {
+                        BigDecimal tempWaterInjection = projectYearWaterInjectionPair.get(projectDeptId);
+                        BigDecimal subtotal = tempWaterInjection.add(dailyWaterInjection);
+                        projectYearWaterInjectionPair.put(projectDeptId, subtotal);
+                    } else {
+                        projectYearWaterInjectionPair.put(projectDeptId, dailyWaterInjection);
+                    }
+                }
+            });
+        }
+
+        // key项目部id     value项目部包含的队伍指定时间区域内日报数量
+        Map<Long, Integer> projectReportPair = new HashMap<>();
+
+        // 以施工区域为维度统计 队伍总数 施工队伍数 准备队伍数
+        // key: 项目部ID -> key: 施工区域 -> Set<队伍ID>
+        Map<Long, Map<String, Set<Long>>> projectLocationTeamSet = new HashMap<>();   // 总队伍
+        Map<Long, Map<String, Set<Long>>> projectLocationSgTeamSet = new HashMap<>(); // 施工队伍(注气/注水>0)
+        Map<Long, Map<String, Set<Long>>> projectLocationZbTeamSet = new HashMap<>(); // 准备队伍(zb/dq/wg)
+
+        // key项目部id     value项目部注气量
+        Map<Long, BigDecimal> projectGasInjectionPair = new HashMap<>();
+        // key项目部id     value项目部注水量
+        Map<Long, BigDecimal> projectWaterInjectionPair = new HashMap<>();
+
+        // 查询所有日报关联的任务 井号 施工区域
+        Map<Long, String> taskWellNamePair = new HashMap<>();
+        Map<Long, String> taskLocationPair = new HashMap<>();
+
+        // 生产动态临时存储
+        Map<Long, Map<String, Map<String, List<String>>>> productionMap = new HashMap<>();
+
+        // key项目部id     value项目部下包含设备的队伍注气时间或注水时间总和
+        Map<Long, BigDecimal> projectWorkTimePair = new HashMap<>();
+
+        if (CollUtil.isNotEmpty(dailyReports)) {
+            IotProjectTaskPageReqVO taskReqVO = new IotProjectTaskPageReqVO();
+            taskReqVO.setTaskIds(convertList(dailyReports, IotRhDailyReportDO::getTaskId));
+            List<IotProjectTaskDO> tasks = iotProjectTaskService.projectTasks(taskReqVO);
+            if (CollUtil.isNotEmpty(tasks)) {
+                tasks.forEach(task -> {
+                    taskWellNamePair.put(task.getId(), task.getWellName());
+                    taskLocationPair.put(task.getId(), task.getLocation());
+                });
+            }
+
+            // 整理出每个项目部下的队伍填报的日报数量
+            dailyReports.forEach(report -> {
+                Long deptId = report.getDeptId();
+                BigDecimal dailyGasInjection = report.getDailyGasInjection();
+                BigDecimal dailyWaterInjection = report.getDailyWaterInjection();
+
+                if (CollUtil.isNotEmpty(projectTeamPair)) {
+                    projectTeamPair.forEach((projectDeptId, teamDeptIds) -> {
+                        if (teamDeptIds.contains(deptId)) {
+                            // 以施工区域为维度统计 队伍总数 施工队伍数 准备队伍数 待命队伍数量
+                            Long taskId = report.getTaskId();
+                            String taskLocation = StrUtil.EMPTY;
+                            if (ObjUtil.isNotEmpty(taskId) && taskLocationPair.containsKey(taskId)) {
+                                taskLocation = taskLocationPair.get(taskId);
+                            }
+
+                            // 项目部下 施工区域内 队伍总数量
+                            if (StrUtil.isNotBlank(taskLocation)) {
+                                // 总队伍(去重)
+                                projectLocationTeamSet
+                                        .computeIfAbsent(projectDeptId, k -> new HashMap<>())
+                                        .computeIfAbsent(taskLocation, k -> new HashSet<>())
+                                        .add(deptId);
+                            }
+
+                            if (dailyGasInjection.compareTo(BigDecimal.ZERO) > 0 || dailyWaterInjection.compareTo(BigDecimal.ZERO) > 0) {
+                                // 项目部日报数量 20260608 统计 注气量 or 注水量 > 0 的日报数量
+                                if (projectReportPair.containsKey(projectDeptId)) {
+                                    Integer tempCount = projectReportPair.get(projectDeptId);
+                                    projectReportPair.put(projectDeptId, ++tempCount);
+                                } else {
+                                    projectReportPair.put(projectDeptId, 1);
+                                }
+                                // 施工队伍数量
+                                if (haveDeviceDeptIds.contains(deptId)) {
+                                    if (sgTeamCountPair.containsKey(projectDeptId)) {
+                                        Integer tempCount = sgTeamCountPair.get(projectDeptId);
+                                        sgTeamCountPair.put(projectDeptId, ++tempCount);
+                                    } else {
+                                        sgTeamCountPair.put(projectDeptId, 1);
+                                    }
+                                }
+                                // 项目部下 施工区域内 施工队伍数量
+                                if (StrUtil.isNotBlank(taskLocation)) {
+                                    projectLocationSgTeamSet
+                                            .computeIfAbsent(projectDeptId, k -> new HashMap<>())
+                                            .computeIfAbsent(taskLocation, k -> new HashSet<>())
+                                            .add(deptId);
+                                }
+                            }
+
+                            // 统计 施工 准备 待命 队伍数量
+                            // 准备
+                            if ("zb".equals(report.getConstructionStatus()) || "dq".equals(report.getConstructionStatus()) || "wg".equals(report.getConstructionStatus())) {
+                                if (zbTeamCountPair.containsKey(projectDeptId)) {
+                                    Integer tempCount = zbTeamCountPair.get(projectDeptId);
+                                    zbTeamCountPair.put(projectDeptId, ++tempCount);
+                                } else {
+                                    zbTeamCountPair.put(projectDeptId, 1);
+                                }
+                            }
+
+                            // 日报关联的任务井号
+                            Long reportTaskId = report.getTaskId();
+
+                            // 提取 所有日报非生产时间和 = 24H 的队伍
+                            List<BigDecimal> nptTimeList = Arrays.asList(
+                                    report.getAccidentTime(),
+                                    report.getRepairTime(),
+                                    report.getSelfStopTime(),
+                                    report.getComplexityTime(),
+                                    report.getRelocationTime(),
+                                    report.getRectificationTime(),
+                                    report.getWaitingStopTime(),
+                                    report.getWinterBreakTime(),
+                                    report.getPartyaDesign(),
+                                    report.getPartyaPrepare(),
+                                    report.getPartyaResource(),
+                                    report.getOtherNptTime()
+                            );
+                            BigDecimal totalNptTime = nptTimeList.stream()
+                                    .map(time -> Optional.ofNullable(time).orElse(BigDecimal.ZERO))
+                                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+                            BigDecimal twentyFour = new BigDecimal("24");
+                            if (totalNptTime.compareTo(twentyFour) == 0) {
+                                // 所有非生产时间和=24H 的队伍 提取生产动态
+                                if (ObjUtil.isNotEmpty(reportTaskId)) {
+                                    String wellName = taskWellNamePair.get(reportTaskId);
+                                    String location = taskLocationPair.get(reportTaskId);
+                                    if (StrUtil.isNotBlank(wellName) && StrUtil.isNotBlank(location)) {
+                                        String dynamic = report.getProductionStatus();
+                                        productionMap.computeIfAbsent(projectDeptId, k -> new HashMap<>())
+                                                .computeIfAbsent(location, k -> new HashMap<>())
+                                                .computeIfAbsent(wellName, k -> new ArrayList<>())
+                                                .add(dynamic);
+                                    }
+                                }
+                            }
+                            // 按小时数 统计设备利用率
+                            BigDecimal gasInjectionTime = ObjUtil.defaultIfNull(report.getDailyInjectGasTime(), BigDecimal.ZERO);
+                            BigDecimal waterInjectionTime = ObjUtil.defaultIfNull(report.getDailyInjectWaterTime(), BigDecimal.ZERO);
+                            // 取 注气时间 注水时间 的较大者
+                            BigDecimal workTime = gasInjectionTime.compareTo(BigDecimal.ZERO) > 0
+                                    ? gasInjectionTime
+                                    : waterInjectionTime.compareTo(BigDecimal.ZERO) > 0
+                                    ? waterInjectionTime
+                                    : BigDecimal.ZERO;
+                            if (workTime.compareTo(BigDecimal.ZERO) > 0) {
+                                projectWorkTimePair.merge(projectDeptId, workTime, BigDecimal::add);
+                            }
+                        }
+                    });
+                }
+                // 统计所有队伍的注气量 注水量 不区分是否包含主设备
+                if (teamIdProjectIdPair.containsKey(deptId)) {
+                    Long projectId = teamIdProjectIdPair.get(deptId);
+                    // 项目部 指定时间的注气量
+                    if (projectGasInjectionPair.containsKey(projectId)) {
+                        BigDecimal tempGasInjection = projectGasInjectionPair.get(projectId);
+                        BigDecimal subtotal = tempGasInjection.add(dailyGasInjection);
+                        projectGasInjectionPair.put(projectId, subtotal);
+                    } else {
+                        projectGasInjectionPair.put(projectId, dailyGasInjection);
+                    }
+                    // 项目部 指定时间的注水量
+                    if (projectWaterInjectionPair.containsKey(projectId)) {
+                        BigDecimal tempWaterInjection = projectWaterInjectionPair.get(projectId);
+                        BigDecimal subtotal = tempWaterInjection.add(dailyWaterInjection);
+                        projectWaterInjectionPair.put(projectId, subtotal);
+                    } else {
+                        projectWaterInjectionPair.put(projectId, dailyWaterInjection);
+                    }
+                }
+            });
+            // 计算指定时间区间内包含的天数
+            long daysCount;
+            if (ObjUtil.isNotEmpty(reqVO.getCreateTime()) && reqVO.getCreateTime().length >= 2) {
+                LocalDateTime start = reqVO.getCreateTime()[0];
+                LocalDateTime end = reqVO.getCreateTime()[1];
+
+                if (ObjUtil.isNotEmpty(start) && ObjUtil.isNotEmpty(end) && !end.isBefore(start)) {
+                    // 使用ChronoUnit.DAYS.between计算天数差,并+1包含首尾两天
+                    daysCount = ChronoUnit.DAYS.between(
+                            start.toLocalDate(),
+                            end.toLocalDate()
+                    ) + 1;
+                } else {
+                    daysCount = 0L;
+                }
+            } else {
+                daysCount = 0L;
+            }
+
+            // 计算年度开始 当前年1.1到指定查询时间区间内包含的天数
+            long yearTotalDay = 0L;
+            List<Long> yearTotalDays = new ArrayList<>();
+            if (ObjUtil.isNotEmpty(reqVO.getCreateTime()) && reqVO.getCreateTime().length >= 2) {
+                // 取查询结束日期
+                LocalDateTime queryEndTime = reqVO.getCreateTime()[1];
+                int startYear = queryEndTime.getYear();
+                // 当年1月1日 零点 从前一年12月26日统计 累计工作量
+                LocalDate yearStartDate = LocalDate.of(startYear - 1, 12, 26);
+                LocalDate queryEndDate = queryEndTime.toLocalDate();
+                // 首尾都包含,+1
+                yearTotalDay = ChronoUnit.DAYS.between(yearStartDate, queryEndDate) + 1;
+                yearTotalDays.add(yearTotalDay);
+            }
+
+            // 计算每个项目部的设备利用率
+            if (CollUtil.isNotEmpty(projectTeamPair)) {
+                projectTeamPair.forEach((projectDeptId, teamIds) -> {
+                    // 暂时不统计欧洲项目部 352
+                    if (352 != projectDeptId) {
+                        ProjectUtilizationRateVo rateVo = new ProjectUtilizationRateVo();
+                        rateVo.setProjectDeptId(projectDeptId);
+                        if (projectPair.containsKey(projectDeptId)) {
+                            rateVo.setProjectDeptName(projectPair.get(projectDeptId));
+                        }
+                        if (allDeptPair.containsKey(projectDeptId)) {
+                            DeptDO dept = allDeptPair.get(projectDeptId);
+                            rateVo.setSort(dept.getSort());
+                        }
+                        // 设置每个项目部的注气量
+                        if (projectGasInjectionPair.containsKey(projectDeptId)) {
+                            BigDecimal gasInjection = projectGasInjectionPair.get(projectDeptId);
+                            // 转换单位 万方
+                            BigDecimal gasInjectionWanFang = gasInjection
+                                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
+                            rateVo.setGasInjection(gasInjectionWanFang);
+                        }
+                        // 设置每个项目部的年累计注气量
+                        if (projectYearGasInjectionPair.containsKey(projectDeptId)) {
+                            BigDecimal yearGasInjection = projectYearGasInjectionPair.get(projectDeptId);
+                            // 转换单位 万方
+                            BigDecimal gasInjectionWanFang = yearGasInjection
+                                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
+                            rateVo.setYearGasInjection(gasInjectionWanFang);
+                        }
+                        // 设置每个项目部的注水量
+                        if (projectWaterInjectionPair.containsKey(projectDeptId)) {
+                            BigDecimal waterInjection = projectWaterInjectionPair.get(projectDeptId);
+                            // 转换单位 万方
+                            BigDecimal waterInjectionWanFang = waterInjection
+                                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
+                            rateVo.setWaterInjection(waterInjectionWanFang);
+                        }
+                        // 设置每个项目部的年累计注水量
+                        if (projectYearWaterInjectionPair.containsKey(projectDeptId)) {
+                            BigDecimal yearWaterInjection = projectYearWaterInjectionPair.get(projectDeptId);
+                            // 转换单位 万方
+                            BigDecimal waterInjectionWanFang = yearWaterInjection
+                                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
+                            rateVo.setYearWaterInjection(waterInjectionWanFang);
+                        }
+                        // 遍历每个项目部 获取每个项目部下队伍数量
+                        Integer currentTeamNum = teamIds.size();
+                        rateVo.setTeamCount(currentTeamNum);
+                        // 待命队伍数量 = 队伍总数 - 施工队伍数量 - 准备队伍数量
+                        // 施工队伍数量
+                        Integer sgTeamCount = sgTeamCountPair.getOrDefault(projectDeptId, 0);
+                        // 施工准备队伍数量
+                        Integer zbTeamCount = zbTeamCountPair.getOrDefault(projectDeptId, 0);
+                        // 驻地待命 队伍数量 = 队伍总数 - 施工队伍 -  准备队伍
+                        Integer dmTeamCount = Math.max(currentTeamNum - sgTeamCount - zbTeamCount, 0);
+                        zddmTeamCountPair.put(projectDeptId, dmTeamCount);
+                        rateVo.setSgTeamCount(sgTeamCount);
+                        rateVo.setZbTeamCount(zbTeamCount);
+                        rateVo.setDmTeamCount(dmTeamCount);
+
+                        rateVo.setCumulativeDays(currentTeamNum * daysCount);
+                        double rate = 0.0;
+                        // 匹配出当前项目部下 日报数量
+                        if (projectReportPair.containsKey(projectDeptId)) {
+                            Integer reportCount = projectReportPair.getOrDefault(projectDeptId, 0);
+                            rateVo.setConstructionDays(reportCount);
+                            // 当前项目部 设备利用率 公式 reportCount/(currentTeamNum*daysCount)
+                            // 计算设备利用率(处理除数为0的情况)
+                            if (currentTeamNum > 0 && daysCount > 0) {
+                                rate = new BigDecimal((double) reportCount / (currentTeamNum * daysCount))
+                                        .setScale(4, RoundingMode.HALF_UP)  // 保留4位小数,四舍五入
+                                        .doubleValue();
+                            }
+                        }
+                        rateVo.setUtilizationRate(rate); // 设备利用率
+                        // 计算项目部 按小时统计的设备利用率(运行时效)
+                        if (projectWorkTimePair.containsKey(projectDeptId)) {
+                            BigDecimal reportWorkTime = projectWorkTimePair.getOrDefault(projectDeptId, BigDecimal.ZERO);
+                            // 统计所有项目部 当日 总工作时间 以便于计算 公司整体的当日运行时效
+                            if (currentTeamNum > 0 && daysCount > 0) {
+                                // 分母 = 队伍数 * 天数 * 24小时
+                                BigDecimal denominator = new BigDecimal(currentTeamNum * daysCount * 24);
+                                // 安全计算,避免除0
+                                BigDecimal utilization = BigDecimal.ZERO;
+                                if (denominator.compareTo(BigDecimal.ZERO) > 0) {
+                                    utilization = reportWorkTime.divide(denominator, 4, RoundingMode.HALF_UP);
+                                    rateVo.setHourUtilizationRate(utilization);
+                                }
+                            }
+                        }
+                        // 计算项目部 年度运行时效 按小时统计的设备利用率(运行时效)
+                        if (projectYearWorkTimePair.containsKey(projectDeptId)) {
+                            BigDecimal reportWorkTime = projectYearWorkTimePair.getOrDefault(projectDeptId, BigDecimal.ZERO);
+                            // 统计所有项目部 年度的 总工作时间 以便于计算 公司整体的当日运行时效
+                            if (currentTeamNum > 0 && CollUtil.isNotEmpty(yearTotalDays)) {
+                                long tempYearTotalDay = yearTotalDays.get(0);
+                                // 分母 = 队伍数 * 天数 * 24小时
+                                BigDecimal denominator = new BigDecimal(currentTeamNum * tempYearTotalDay * 24);
+                                // 安全计算,避免除0
+                                BigDecimal utilization = BigDecimal.ZERO;
+                                if (denominator.compareTo(BigDecimal.ZERO) > 0) {
+                                    utilization = reportWorkTime.divide(denominator, 4, RoundingMode.HALF_UP);
+                                    rateVo.setYearHourUtilizationRate(utilization);
+                                }
+                            }
+                        }
+                        rates.add(rateVo);
+                    }
+                });
+            }
+
+            // 公司汇总的 数据
+            ProjectUtilizationRateVo rateVo = new ProjectUtilizationRateVo();
+            // 计算公司汇总的设备利用率 日平均时效  年平均时效
+            // teamProjectIdPair 有效的队伍集合
+            // 公司当日总运行工时
+            BigDecimal currentTotalWorkTime = projectWorkTimePair.values().stream()
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            // 公司年度总运行工时
+            BigDecimal yearTotalWorkTime = projectYearWorkTimePair.values().stream()
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+            if (CollUtil.isNotEmpty(teamProjectIdPair)) {
+                int teamCount = teamProjectIdPair.size();
+                // 公司 当日运行时效 分母 = 队伍数 * 天数 * 24小时
+                BigDecimal denominator = new BigDecimal(teamCount * daysCount * 24);
+                // 安全计算,避免除0
+                BigDecimal utilization = BigDecimal.ZERO;
+                if (denominator.compareTo(BigDecimal.ZERO) > 0) {
+                    utilization = currentTotalWorkTime.divide(denominator, 4, RoundingMode.HALF_UP);
+                    rateVo.setHourUtilizationRate(utilization);
+                }
+
+                // 公司 年运行时效 分母 = 队伍数 * 天数 * 24小时
+                long tempYearTotalDay = yearTotalDays.get(0);
+                BigDecimal yearDenominator = new BigDecimal(teamCount * tempYearTotalDay * 24);
+                // 安全计算,避免除0
+                BigDecimal yearUtilization = BigDecimal.ZERO;
+                if (yearDenominator.compareTo(BigDecimal.ZERO) > 0) {
+                    yearUtilization = yearTotalWorkTime.divide(yearDenominator, 4, RoundingMode.HALF_UP);
+                    rateVo.setYearHourUtilizationRate(yearUtilization);
+                }
+            }
+
+            // 公司年累计注气
+            BigDecimal companyYearGasInjection = projectYearGasInjectionPair.values().stream()
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            // 单位转换成 万方
+            BigDecimal companyGasInjectionWanFang = companyYearGasInjection
+                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
+
+            // 公司年累计注水
+            BigDecimal companyYearWaterInjection = projectYearWaterInjectionPair.values().stream()
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            // 单位转换成 万方
+            BigDecimal companyWaterInjectionWanFang = companyYearWaterInjection
+                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
+
+            // 公司当日累计注气
+            BigDecimal companyCurrentGasInjection = projectGasInjectionPair.values().stream()
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            // 单位转换成 万方
+            BigDecimal currentGasInjectionWanFang = companyCurrentGasInjection
+                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
+
+            // 公司当日累计注水
+            BigDecimal companyCurrentWaterInjection = projectWaterInjectionPair.values().stream()
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            // 单位转换成 万方
+            BigDecimal currentWaterInjectionWanFang = companyCurrentWaterInjection
+                    .divide(BigDecimal.valueOf(10000), 2, RoundingMode.HALF_UP);
+
+
+            // 公司队伍总数
+            Integer companyTeamCount = teamProjectIdPair.size();
+            // 公司待命队伍总数
+            Integer companyDmTeamCount = zddmTeamCountPair.values().stream()
+                    .filter(Objects::nonNull)
+                    .reduce(0, Integer::sum);
+            // 公司施工准备队伍总数
+            Integer companyZbTeamCount = zbTeamCountPair.values().stream()
+                    .filter(Objects::nonNull)
+                    .reduce(0, Integer::sum);
+            // 公司施工队伍总数
+            Integer companySgTeamCount = sgTeamCountPair.values().stream()
+                    .filter(Objects::nonNull)
+                    .reduce(0, Integer::sum);
+            rateVo.setProjectDeptName("合计");
+            rateVo.setProjectDeptId(157l);
+            rateVo.setSort(0);
+            rateVo.setTeamCount(companyTeamCount);
+            rateVo.setDmTeamCount(companyDmTeamCount);
+            rateVo.setZbTeamCount(companyZbTeamCount);
+            rateVo.setSgTeamCount(companySgTeamCount);
+            rateVo.setGasInjection(currentGasInjectionWanFang);
+            rateVo.setWaterInjection(currentWaterInjectionWanFang);
+            rateVo.setYearGasInjection(companyGasInjectionWanFang);
+            rateVo.setYearWaterInjection(companyWaterInjectionWanFang);
+            rates.add(rateVo);
+
+        }
+        // rates.sort(Comparator.comparingDouble(ProjectUtilizationRateVo::getUtilizationRate).reversed());
+        // 克拉玛依 加入到 吐哈项目部,两个项目部合并
+        AtomicReference<Integer> mergeTeamCount = new AtomicReference<>(0);
+        AtomicReference<Integer> mergeConstructionDays = new AtomicReference<>(0);
+        AtomicReference<Long> mergeCumulativeDays = new AtomicReference<>(0l);
+        rates.forEach(rate -> {
+            // 克拉玛依 项目部数据
+            if (rate.getProjectDeptId() == 166) {
+                mergeTeamCount.set(rate.getTeamCount());
+                mergeCumulativeDays.set(ObjUtil.isNotEmpty(rate.getCumulativeDays()) ? rate.getCumulativeDays() : 0);
+                mergeConstructionDays.set(ObjUtil.isNotEmpty(rate.getConstructionDays()) ? rate.getConstructionDays() : 0);
+            }
+        });
+        rates.forEach(rate -> {
+            // 吐哈项目部 项目部数据
+            if (rate.getProjectDeptId() == 160) {
+                rate.setTeamCount((ObjUtil.isNotEmpty(rate.getTeamCount()) ? rate.getTeamCount() : 0) + mergeTeamCount.get());
+                rate.setCumulativeDays((ObjUtil.isNotEmpty(rate.getCumulativeDays()) ? rate.getCumulativeDays() : 0l) + mergeCumulativeDays.get());
+                rate.setConstructionDays((ObjUtil.isNotEmpty(rate.getConstructionDays()) ? rate.getConstructionDays() : 0) + mergeConstructionDays.get());
+            }
+
+            Map<String, Map<String, List<String>>> locationMap = productionMap.get(rate.getProjectDeptId());
+            // 组装各项目部 按照施工区域维度统计 的 队伍数量 生产动态明细
+            if (locationMap != null && !locationMap.isEmpty()) {
+                Map<String, Set<Long>> teamMap = projectLocationTeamSet.getOrDefault(rate.getProjectDeptId(), Collections.emptyMap());
+                Map<String, Set<Long>> sgTeamMap = projectLocationSgTeamSet.getOrDefault(rate.getProjectDeptId(), Collections.emptyMap());
+
+                StringBuilder sb = new StringBuilder();
+                List<String> sortedLocations = new ArrayList<>(locationMap.keySet());
+                Collections.sort(sortedLocations);
+                for (String location : sortedLocations) {
+                    int total = teamMap.getOrDefault(location, Collections.emptySet()).size();
+                    int sg = sgTeamMap.getOrDefault(location, Collections.emptySet()).size();
+                    int zb = Math.max(total - sg, 0);
+                    sb.append(location).append(":施工").append(sg).append("套设备,准备").append(zb).append("套设备(共").append(total).append("套设备)\n");
+
+                    Map<String, List<String>> wellMap = locationMap.get(location);
+                    List<String> sortedWells = new ArrayList<>(wellMap.keySet());
+                    Collections.sort(sortedWells);
+                    for (String well : sortedWells) {
+                        List<String> dynamics = wellMap.get(well);
+                        // 过滤掉空字符串(包括null和仅空白字符)
+                        List<String> nonEmptyDynamics = dynamics.stream()
+                                .filter(s -> s != null && !s.trim().isEmpty())
+                                .collect(Collectors.toList());
+                        if (nonEmptyDynamics.isEmpty()) {
+                            // 无有效动态时,只显示井名和一个分号
+                            sb.append(well).append(",;\n");
+                        } else {
+                            String dynamicsStr = String.join(";", nonEmptyDynamics);
+                            sb.append(well).append(",").append(dynamicsStr).append(";\n");
+                        }
+                    }
+                }
+                rate.setProductionSummary(sb.toString());
+            }
+        });
+        // 删除projectDeptId为166的对象 克拉玛依项目部
+        rates.removeIf(rate -> rate.getProjectDeptId() == 166);
+        // 暂时不统计欧洲项目部 352
+        rates.sort(Comparator.comparingInt(vo -> {
+            Long deptId = vo.getProjectDeptId();
+            // 获取当前projectDeptId 在 sortedProjectIds中 的索引
+            int index = sortedProjectIds.indexOf(deptId);
+            // 若不在集合中,用集合长度作为索引(排在所有指定项目部之后)
+            return index != -1 ? index : sortedProjectIds.size();
+        }));
+        return rates;
+    }
+
     @Override
     public void updateIotRhDailyReport(IotRhDailyReportSaveReqVO updateReqVO) {
         // 校验存在