Browse Source

pms 5#国 瑞鹰 瑞都 生产异常提醒

zhangcl 1 day ago
parent
commit
db557210bb

+ 132 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotfivedailyreport/IotFiveDailyReportServiceImpl.java

@@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.pms.constant.PmsConstants;
 import cn.iocoder.yudao.module.pms.controller.admin.iotfivedailyreport.vo.IotFiveDailyReportPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotfivedailyreport.vo.IotFiveDailyReportSaveReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotfivedailyreport.vo.IotFiveDailyReportStatisticsRespVO;
@@ -13,9 +14,19 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.iotfivedailyreport.IotFiveDail
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttask.IotProjectTaskDO;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotfivedailyreport.IotFiveDailyReportMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttask.IotProjectTaskMapper;
+import cn.iocoder.yudao.module.pms.message.PmsMessage;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.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 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
@@ -25,9 +36,12 @@ import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_FIVE_DAILY_REPORT_NOT_EXISTS;
+import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;
 
 /**
  * 5#国日报 Service 实现类
@@ -44,6 +58,18 @@ public class IotFiveDailyReportServiceImpl implements IotFiveDailyReportService
     private DeptService deptService;
     @Resource
     private IotProjectTaskMapper iotProjectTaskMapper;
+    @Resource
+    private AdminUserService adminUserService;
+    @Resource
+    private RoleService roleService;
+    @Resource
+    private UserRoleMapper userRoleMapper;
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource(name = PMS_THREAD_POOL_TASK_EXECUTOR)
+    private ThreadPoolTaskExecutor pmsThreadPoolTaskExecutor;
+    @Resource
+    private PmsMessage pmsMessage;
 
     @Override
     public Long createIotFiveDailyReport(IotFiveDailyReportSaveReqVO createReqVO) {
@@ -64,6 +90,7 @@ public class IotFiveDailyReportServiceImpl implements IotFiveDailyReportService
         updateObj.setStatus(1);
 
         Long taskId = updateReqVO.getTaskId();
+        Long deptId = updateObj.getDeptId();
         // 关联更新相关任务的 施工状态
         if (ObjUtil.isNotEmpty(taskId)) {
             IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
@@ -77,6 +104,111 @@ public class IotFiveDailyReportServiceImpl implements IotFiveDailyReportService
         }
 
         iotFiveDailyReportMapper.updateById(updateObj);
+        // 将生产异常情况 (npt 工程质量 > 0) 发送到拥有 生产异常通知 角色的人员
+        BigDecimal accidentTime = updateObj.getAccidentTime();
+        if (ObjUtil.isNotEmpty(accidentTime) && accidentTime.compareTo(BigDecimal.ZERO) > 0) {
+
+            String companyName = "5#国";
+            BigDecimal day = accidentTime.divide(new BigDecimal("24"), 2, RoundingMode.HALF_UP);
+            String accidentDay = day + "T";
+            // 异常情况
+            String malfunction = StrUtil.blankToDefault(updateObj.getMalfunction(), "无");
+            DeptDO dept = deptService.getDept(deptId);
+            // 查询当前队伍的上级项目部
+            DeptDO projectDept = deptService.getDept(dept.getParentId());
+            // 队伍名称
+            String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
+            // 项目部名称
+            String projectDeptName = ObjUtil.isNotEmpty(projectDept) ? projectDept.getName() : StrUtil.EMPTY;
+
+            String wellName = "无";
+            if (ObjUtil.isNotEmpty(taskId)) {
+                IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
+                if (ObjUtil.isNotEmpty(task)) {
+                    wellName = task.getWellName();
+                }
+            }
+            // 当前处理进度 当日施工简报
+            String brief = StrUtil.blankToDefault(updateObj.getProductionStatus(), "无");
+            // 下步计划
+            String nextPlan = StrUtil.blankToDefault(updateObj.getNextPlan(), "无");
+            // 故障误工时间 accidentTime H
+
+            String PRODUCTION_EXCEPTION_MSG_TEMPLATE =
+                    "[公司]:{}\n" +
+                    "[项目部]:{}\n" +
+                    "[队伍]:{}\n" +
+                    "[井号]:{}\n" +
+                    "[故障描述]:{}\n" +
+                    "[当前处理进度]:{}\n" +
+                    "[下步处理计划]:{}\n" +
+                    "[累计误工时间]:{}\n";
+
+            String finalMsgTitle = StrUtil.format(PRODUCTION_EXCEPTION_MSG_TEMPLATE,
+                    companyName, projectDeptName, deptName, wellName,
+                    malfunction, brief, nextPlan, accidentDay
+            );
+
+            // 封装以上消息内容 发送到公司 角色 (生产异常通知)
+            // 查询瑞恒 157l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+            Set<Long> userIds = new HashSet<>();
+            Set<Long> deptIds = new HashSet<>();
+            deptIds.add(388l);
+            List<AdminUserDO> companyUsers = adminUserService.getUserListByDeptIds(deptIds);
+            if (CollUtil.isNotEmpty(companyUsers)) {
+                companyUsers.forEach(user -> {
+                    userIds.add(user.getId());
+                });
+            }
+            deptIds.clear();
+            deptIds.add(156l);
+            List<AdminUserDO> groupUsers = adminUserService.getUserListByDeptIds(deptIds);
+            if (CollUtil.isNotEmpty(groupUsers)) {
+                groupUsers.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(updateObj.getId(), finalMsgTitle, PmsConstants.PRODUCTION_EXCEPTION,
+                                                    userId, mobile);
+                                        }
+                                    } finally {
+                                        latch.countDown();
+                                    }
+                                });
+                            });
+                        }
+                    }
+                }
+            }
+        }
     }
 
     @Override

+ 115 - 7
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrddailyreport/IotRdDailyReportServiceImpl.java

@@ -252,7 +252,11 @@ public class IotRdDailyReportServiceImpl implements IotRdDailyReportService {
         }
         // 更新
         IotRdDailyReportDO updateObj = BeanUtils.toBean(updateReqVO, IotRdDailyReportDO.class);
-        // 如果是修改 非生产时间 ,则不必再发起审批
+
+        Long deptId = dailyReport.getDeptId();
+        Long taskId = dailyReport.getTaskId();
+
+        // 如果是修改 非生产时间 ,则不必再发起审批 生产运营部 维护 时效
         if (StrUtil.isBlank(updateReqVO.getNonProduct())) {
             // 正常填写4小时周报 不必设置日报状态为 已完成 可以每4小时填报1闪
             // updateObj.setStatus(1);
@@ -261,16 +265,119 @@ public class IotRdDailyReportServiceImpl implements IotRdDailyReportService {
         } else {
             // 生产运营部填写了 非生产时间 施工简报 可以设置 日报状态为 已完成 不允许再次填报
             updateObj.setStatus(1);
-        }
-        // 如果 修改 了时效 只要保存 不管是否 填写了 非生产时间 都确认为已经 维护了 非生产时间
-        if (StrUtil.isNotBlank(updateReqVO.getNonProduct())) {
+            // 如果 修改 了时效 只要保存 不管是否 填写了 非生产时间 都确认为已经 维护了 非生产时间
             updateObj.setProcessInstanceId("2");
+
+            // 审核同意 将生产异常情况 (npt 工程质量) 发送到拥有 生产异常通知 角色的人员
+            BigDecimal accidentTime = dailyReport.getAccidentTime();
+            if (ObjUtil.isNotEmpty(accidentTime) && accidentTime.compareTo(BigDecimal.ZERO) > 0) {
+                String companyName = "四川瑞都";
+                BigDecimal day = accidentTime.divide(new BigDecimal("24"), 2, RoundingMode.HALF_UP);
+                String accidentDay = day + "T";
+                // 异常情况
+                String malfunction = StrUtil.blankToDefault(dailyReport.getMalfunction(), "无");
+                DeptDO dept = deptService.getDept(deptId);
+                // 查询当前队伍的上级项目部
+                DeptDO projectDept = deptService.getDept(dept.getParentId());
+                // 队伍名称
+                String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
+                // 项目部名称
+                String projectDeptName = ObjUtil.isNotEmpty(projectDept) ? projectDept.getName() : StrUtil.EMPTY;
+                String wellName = "无";
+                if (ObjUtil.isNotEmpty(taskId)) {
+                    IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
+                    if (ObjUtil.isNotEmpty(task)) {
+                        wellName = task.getWellName();
+                    }
+                }
+                // 当前处理进度 当日施工简报
+                String brief = StrUtil.blankToDefault(dailyReport.getConstructionBrief(), "无");
+                // 下步计划
+                String nextPlan = StrUtil.blankToDefault(dailyReport.getNextPlan(), "无");
+                // 故障误工时间 accidentTime H
+
+                String PRODUCTION_EXCEPTION_MSG_TEMPLATE =
+                        "[公司]:{}\n" +
+                        "[项目部]:{}\n" +
+                        "[队伍]:{}\n" +
+                        "[井号]:{}\n" +
+                        "[故障描述]:{}\n" +
+                        "[当前处理进度]:{}\n" +
+                        "[下步处理计划]:{}\n" +
+                        "[累计误工时间]:{}\n";
+
+                String finalMsgTitle = StrUtil.format(PRODUCTION_EXCEPTION_MSG_TEMPLATE,
+                        companyName, projectDeptName, deptName, wellName,
+                        malfunction, brief, nextPlan, accidentDay
+                );
+
+                // 封装以上消息内容 发送到公司 角色 (生产异常通知)
+                // 查询瑞恒 157l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+                Set<Long> userIds = new HashSet<>();
+                Set<Long> deptIds = new HashSet<>();
+                deptIds.add(163l);
+                List<AdminUserDO> companyUsers = adminUserService.getUserListByDeptIds(deptIds);
+                if (CollUtil.isNotEmpty(companyUsers)) {
+                    companyUsers.forEach(user -> {
+                        userIds.add(user.getId());
+                    });
+                }
+                deptIds.clear();
+                deptIds.add(156l);
+                List<AdminUserDO> groupUsers = adminUserService.getUserListByDeptIds(deptIds);
+                if (CollUtil.isNotEmpty(groupUsers)) {
+                    groupUsers.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(dailyReport.getId(), finalMsgTitle, PmsConstants.PRODUCTION_EXCEPTION,
+                                                        userId, mobile);
+                                            }
+                                        } finally {
+                                            latch.countDown();
+                                        }
+                                    });
+                                });
+                            }
+                        }
+                    }
+                }
+            }
         }
+
         iotRdDailyReportMapper.updateById(updateObj);
         // 同步更新日报关联任务的 施工状态
-        if (ObjUtil.isNotEmpty(dailyReport.getTaskId())) {
+        if (ObjUtil.isNotEmpty(taskId)) {
             // 查询任务记录 更新任务的施工状态
-            IotProjectTaskDO task = iotProjectTaskMapper.selectById(dailyReport.getTaskId());
+            IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
             if (ObjUtil.isNotEmpty(task)) {
                 task.setRdStatus(updateObj.getRdStatus());
                 task.setStatus(updateObj.getRdStatus());
@@ -2895,10 +3002,11 @@ public class IotRdDailyReportServiceImpl implements IotRdDailyReportService {
         List<IotRdDailyReportDO> mainPlatformReports = new ArrayList<>();
         // 关联井日报
         List<IotRdDailyReportDO> relatePlatformReports = new ArrayList<>();
-        // 维护非生产时间标识 nonProduct
+        // 维护非生产时间标识 nonProduct 生产运营部维护时效
         AtomicReference<String> nonProduct = new AtomicReference<>(StrUtil.EMPTY);
         reports.forEach(report -> {
             if (StrUtil.isNotBlank(report.getNonProduct())) {
+                // 生产运营部维护时效
                 nonProduct.set(report.getNonProduct());
             }
             IotRdDailyReportDO updatedReport = updateIotRdDailyReport(report);

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

@@ -905,9 +905,6 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
             // 下步计划
             String nextPlan = StrUtil.blankToDefault(dailyReport.getNextPlan(), "无");
             // 故障误工时间 accidentTime H
-
-            String msgTemplate = "[公司]:{} - [队伍]:{} - [项目]:{} - [井号]:{}  -[故障描述]:{} -[当前处理进度]:{}-[下步处理计划]:{}-[累计误工时间]:{}";
-
             String PRODUCTION_EXCEPTION_MSG_TEMPLATE =
                     "[公司]:{}\n" +
                     "[项目部]:{}\n" +

+ 105 - 2
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrydailyreport/IotRyDailyReportServiceImpl.java

@@ -945,11 +945,11 @@ public class IotRyDailyReportServiceImpl implements IotRyDailyReportService {
         dailyReport.setAuditStatus(updateReqVO.getAuditStatus());
         dailyReport.setOpinion(updateReqVO.getOpinion());
         dailyReport.setConstructionBrief(updateReqVO.getConstructionBrief());
+        Long deptId = dailyReport.getDeptId();
+        Long taskId = dailyReport.getTaskId();
         if (SupplierAuditStatusEnum.REJECT.getStatus().equals(updateReqVO.getAuditStatus())) {
             dailyReport.setStatus(0);
             // 审批拒绝后需要向 日报填报人 发送消息提醒 驳回到日报创建人
-            Long deptId = dailyReport.getDeptId();
-            Long taskId = dailyReport.getTaskId();
             DeptDO dept = deptService.getDept(deptId);
             // 当前日报创建人
             String creator = dailyReport.getCreator();
@@ -983,6 +983,109 @@ public class IotRyDailyReportServiceImpl implements IotRyDailyReportService {
                 }
             }
         }
+        // 审核同意 将生产异常情况 (npt 工程质量) 发送到拥有 生产异常通知 角色的人员
+        BigDecimal accidentTime = dailyReport.getAccidentTime();
+        if (ObjUtil.isNotEmpty(accidentTime) && accidentTime.compareTo(BigDecimal.ZERO) > 0) {
+
+            String companyName = "瑞鹰国际";
+            BigDecimal day = accidentTime.divide(new BigDecimal("24"), 2, RoundingMode.HALF_UP);
+            String accidentDay = day + "T";
+            // 异常情况
+            String malfunction = StrUtil.blankToDefault(dailyReport.getMalfunction(), "无");
+            DeptDO dept = deptService.getDept(deptId);
+            // 查询当前队伍的上级项目部
+            DeptDO projectDept = deptService.getDept(dept.getParentId());
+            // 队伍名称
+            String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
+            // 项目部名称
+            String projectDeptName = ObjUtil.isNotEmpty(projectDept) ? projectDept.getName() : StrUtil.EMPTY;
+            String wellName = "无";
+            if (ObjUtil.isNotEmpty(taskId)) {
+                IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
+                if (ObjUtil.isNotEmpty(task)) {
+                    wellName = task.getWellName();
+                }
+            }
+            // 当前处理进度 当日施工简报
+            String brief = StrUtil.blankToDefault(dailyReport.getConstructionBrief(), "无");
+            // 下步计划
+            String nextPlan = StrUtil.blankToDefault(dailyReport.getNextPlan(), "无");
+            // 故障误工时间 accidentTime H
+            String PRODUCTION_EXCEPTION_MSG_TEMPLATE =
+                    "[公司]:{}\n" +
+                    "[项目部]:{}\n" +
+                    "[队伍]:{}\n" +
+                    "[井号]:{}\n" +
+                    "[故障描述]:{}\n" +
+                    "[当前处理进度]:{}\n" +
+                    "[下步处理计划]:{}\n" +
+                    "[累计误工时间]:{}\n";
+
+            String finalMsgTitle = StrUtil.format(PRODUCTION_EXCEPTION_MSG_TEMPLATE,
+                    companyName, projectDeptName, deptName, wellName,
+                    malfunction, brief, nextPlan, accidentDay
+            );
+
+            // 封装以上消息内容 发送到公司 角色 (生产异常通知)
+            // 查询瑞鹰 158l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+            Set<Long> userIds = new HashSet<>();
+            Set<Long> deptIds = new HashSet<>();
+            deptIds.add(158l);
+            List<AdminUserDO> companyUsers = adminUserService.getUserListByDeptIds(deptIds);
+            if (CollUtil.isNotEmpty(companyUsers)) {
+                companyUsers.forEach(user -> {
+                    userIds.add(user.getId());
+                });
+            }
+            deptIds.clear();
+            deptIds.add(156l);
+            List<AdminUserDO> groupUsers = adminUserService.getUserListByDeptIds(deptIds);
+            if (CollUtil.isNotEmpty(groupUsers)) {
+                groupUsers.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(dailyReport.getId(), finalMsgTitle, PmsConstants.PRODUCTION_EXCEPTION,
+                                                    userId, mobile);
+                                        }
+                                    } finally {
+                                        latch.countDown();
+                                    }
+                                });
+                            });
+                        }
+                    }
+                }
+            }
+        }
         // 更新瑞恒日报审核状态
         iotRyDailyReportMapper.updateById(dailyReport);
     }