Răsfoiți Sursa

pms 生产异常避免重复通知

zhangcl 3 săptămâni în urmă
părinte
comite
21e1e93edb
13 a modificat fișierele cu 1047 adăugiri și 394 ștergeri
  1. 1 0
      yudao-module-pms/yudao-module-pms-api/src/main/java/cn/iocoder/yudao/module/pms/enums/ErrorCodeConstant.java
  2. 93 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotproductionexception/IotProductionExceptionController.java
  3. 52 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotproductionexception/vo/IotProductionExceptionPageReqVO.java
  4. 59 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotproductionexception/vo/IotProductionExceptionRespVO.java
  5. 42 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotproductionexception/vo/IotProductionExceptionSaveReqVO.java
  6. 68 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotproductionexception/IotProductionExceptionDO.java
  7. 54 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotproductionexception/IotProductionExceptionMapper.java
  8. 142 100
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotfivedailyreport/IotFiveDailyReportServiceImpl.java
  9. 55 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotproductionexception/IotProductionExceptionService.java
  10. 71 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotproductionexception/IotProductionExceptionServiceImpl.java
  11. 138 98
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrddailyreport/IotRdDailyReportServiceImpl.java
  12. 137 99
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrhdailyreport/IotRhDailyReportServiceImpl.java
  13. 135 97
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrydailyreport/IotRyDailyReportServiceImpl.java

+ 1 - 0
yudao-module-pms/yudao-module-pms-api/src/main/java/cn/iocoder/yudao/module/pms/enums/ErrorCodeConstant.java

@@ -121,6 +121,7 @@ public interface ErrorCodeConstant{
     ErrorCode IOT_FIVE_DAILY_REPORT_NOT_EXISTS = new ErrorCode(600, "5#国日报不存在");
     ErrorCode IOT_PROJECT_TASK_PLAN_NOT_EXISTS = new ErrorCode(700, "日报任务计划(工作量)不存在");
     ErrorCode IOT_PROJECT_TASK_PLAN_EXISTS = new ErrorCode(700, "部门当前月份的工作量计划已经存在");
+    ErrorCode IOT_PRODUCTION_EXCEPTION_NOT_EXISTS = new ErrorCode(800, "生产异常日志不存在");
     /***
      * 数字油藏
      */

+ 93 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotproductionexception/IotProductionExceptionController.java

@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionRespVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotproductionexception.IotProductionExceptionDO;
+import cn.iocoder.yudao.module.pms.service.iotproductionexception.IotProductionExceptionService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 生产异常")
+@RestController
+@RequestMapping("/pms/iot-production-exception")
+@Validated
+public class IotProductionExceptionController {
+
+    @Resource
+    private IotProductionExceptionService iotProductionExceptionService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建生产异常")
+    @PreAuthorize("@ss.hasPermission('pms:iot-production-exception:create')")
+    public CommonResult<Long> createIotProductionException(@Valid @RequestBody IotProductionExceptionSaveReqVO createReqVO) {
+        return success(iotProductionExceptionService.createIotProductionException(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新生产异常")
+    @PreAuthorize("@ss.hasPermission('pms:iot-production-exception:update')")
+    public CommonResult<Boolean> updateIotProductionException(@Valid @RequestBody IotProductionExceptionSaveReqVO updateReqVO) {
+        iotProductionExceptionService.updateIotProductionException(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除生产异常")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('pms:iot-production-exception:delete')")
+    public CommonResult<Boolean> deleteIotProductionException(@RequestParam("id") Long id) {
+        iotProductionExceptionService.deleteIotProductionException(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得生产异常")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('pms:iot-production-exception:query')")
+    public CommonResult<IotProductionExceptionRespVO> getIotProductionException(@RequestParam("id") Long id) {
+        IotProductionExceptionDO iotProductionException = iotProductionExceptionService.getIotProductionException(id);
+        return success(BeanUtils.toBean(iotProductionException, IotProductionExceptionRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得生产异常分页")
+    @PreAuthorize("@ss.hasPermission('pms:iot-production-exception:query')")
+    public CommonResult<PageResult<IotProductionExceptionRespVO>> getIotProductionExceptionPage(@Valid IotProductionExceptionPageReqVO pageReqVO) {
+        PageResult<IotProductionExceptionDO> pageResult = iotProductionExceptionService.getIotProductionExceptionPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, IotProductionExceptionRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出生产异常 Excel")
+    @PreAuthorize("@ss.hasPermission('pms:iot-production-exception:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportIotProductionExceptionExcel(@Valid IotProductionExceptionPageReqVO pageReqVO,
+              HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<IotProductionExceptionDO> list = iotProductionExceptionService.getIotProductionExceptionPage(pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "生产异常.xls", "数据", IotProductionExceptionRespVO.class,
+                        BeanUtils.toBean(list, IotProductionExceptionRespVO.class));
+    }
+
+}

+ 52 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotproductionexception/vo/IotProductionExceptionPageReqVO.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 生产异常分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class IotProductionExceptionPageReqVO extends PageParam {
+
+    @Schema(description = "公司id", example = "13910")
+    private Long companyId;
+
+    @Schema(description = "部门id", example = "2718")
+    private Long deptId;
+
+    @Schema(description = "任务井id", example = "21971")
+    private Long taskId;
+
+    @Schema(description = "日报id", example = "5108")
+    private Long reportId;
+
+    @Schema(description = "生产异常发生日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] occurrenceDate;
+
+    @Schema(description = "发送通知标识 0不需要发送 1需要发送", example = "1")
+    private Integer status;
+
+    @Schema(description = "组标识 相同组的日报异常时间叠加")
+    private String groupFlag;
+
+    @Schema(description = "分类排序")
+    private Integer sort;
+
+    @Schema(description = "备注扩展字段", example = "你说的对")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] createTime;
+
+}

+ 59 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotproductionexception/vo/IotProductionExceptionRespVO.java

@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 生产异常 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class IotProductionExceptionRespVO {
+
+    @Schema(description = "主键id", requiredMode = Schema.RequiredMode.REQUIRED, example = "2991")
+    @ExcelProperty("主键id")
+    private Long id;
+
+    @Schema(description = "公司id", example = "13910")
+    @ExcelProperty("公司id")
+    private Long companyId;
+
+    @Schema(description = "部门id", example = "2718")
+    @ExcelProperty("部门id")
+    private Long deptId;
+
+    @Schema(description = "任务井id", example = "21971")
+    @ExcelProperty("任务井id")
+    private Long taskId;
+
+    @Schema(description = "日报id", example = "5108")
+    @ExcelProperty("日报id")
+    private Long reportId;
+
+    @Schema(description = "生产异常发生日期")
+    @ExcelProperty("生产异常发生日期")
+    private LocalDateTime occurrenceDate;
+
+    @Schema(description = "发送通知标识 0不需要发送 1需要发送", example = "1")
+    @ExcelProperty("发送通知标识 0不需要发送 1需要发送")
+    private Integer status;
+
+    @Schema(description = "组标识 相同组的日报异常时间叠加")
+    @ExcelProperty("组标识 相同组的日报异常时间叠加")
+    private String groupFlag;
+
+    @Schema(description = "分类排序")
+    @ExcelProperty("分类排序")
+    private Integer sort;
+
+    @Schema(description = "备注扩展字段", example = "你说的对")
+    @ExcelProperty("备注扩展字段")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    @ExcelProperty("创建时间")
+    private LocalDateTime createTime;
+
+}

+ 42 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotproductionexception/vo/IotProductionExceptionSaveReqVO.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 生产异常新增/修改 Request VO")
+@Data
+public class IotProductionExceptionSaveReqVO {
+
+    @Schema(description = "主键id", requiredMode = Schema.RequiredMode.REQUIRED, example = "2991")
+    private Long id;
+
+    @Schema(description = "公司id", example = "13910")
+    private Long companyId;
+
+    @Schema(description = "部门id", example = "2718")
+    private Long deptId;
+
+    @Schema(description = "任务井id", example = "21971")
+    private Long taskId;
+
+    @Schema(description = "日报id", example = "5108")
+    private Long reportId;
+
+    @Schema(description = "生产异常发生日期")
+    private LocalDateTime occurrenceDate;
+
+    @Schema(description = "发送通知标识 0不需要发送 1需要发送", example = "1")
+    private Integer status;
+
+    @Schema(description = "组标识 相同组的日报异常时间叠加")
+    private String groupFlag;
+
+    @Schema(description = "分类排序")
+    private Integer sort;
+
+    @Schema(description = "备注扩展字段", example = "你说的对")
+    private String remark;
+
+}

+ 68 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotproductionexception/IotProductionExceptionDO.java

@@ -0,0 +1,68 @@
+package cn.iocoder.yudao.module.pms.dal.dataobject.iotproductionexception;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+/**
+ * 生产异常 DO
+ *
+ * @author ruiqi
+ */
+@TableName("rq_iot_production_exception")
+@KeySequence("rq_iot_production_exception_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class IotProductionExceptionDO extends BaseDO {
+
+    /**
+     * 主键id
+     */
+    @TableId
+    private Long id;
+    /**
+     * 公司id
+     */
+    private Long companyId;
+    /**
+     * 部门id
+     */
+    private Long deptId;
+    /**
+     * 任务井id
+     */
+    private Long taskId;
+    /**
+     * 日报id
+     */
+    private Long reportId;
+    /**
+     * 生产异常发生日期
+     */
+    private LocalDateTime occurrenceDate;
+    /**
+     * 发送通知标识 0不需要发送 1需要发送
+     */
+    private Integer status;
+    /**
+     * 组标识 相同组的日报异常时间叠加
+     */
+    private String groupFlag;
+    /**
+     * 分类排序
+     */
+    private Integer sort;
+    /**
+     * 备注扩展字段
+     */
+    private String remark;
+
+}

+ 54 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotproductionexception/IotProductionExceptionMapper.java

@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.module.pms.dal.mysql.iotproductionexception;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionPageReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotproductionexception.IotProductionExceptionDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 生产异常 Mapper
+ *
+ * @author ruiqi
+ */
+@Mapper
+public interface IotProductionExceptionMapper extends BaseMapperX<IotProductionExceptionDO> {
+
+    default PageResult<IotProductionExceptionDO> selectPage(IotProductionExceptionPageReqVO reqVO) {
+        return selectPage(reqVO, buildQueryWrapper(reqVO));
+    }
+
+    /**
+     * 根据查询条件,获取ID最大的一条记录
+     * @param reqVO 查询条件
+     * @return id最大记录,无数据返回null
+     */
+    default IotProductionExceptionDO selectMaxIdOne(IotProductionExceptionPageReqVO reqVO) {
+        LambdaQueryWrapperX<IotProductionExceptionDO> wrapper = buildQueryWrapper(reqVO)
+                .orderByDesc(IotProductionExceptionDO::getId)
+                .last("LIMIT 1"); // mysql语法,追加limit 1
+
+        return selectOne(wrapper);
+    }
+
+    /**
+     * 抽取公共查询条件,复用,避免重复代码
+     */
+    default LambdaQueryWrapperX<IotProductionExceptionDO> buildQueryWrapper(IotProductionExceptionPageReqVO reqVO) {
+        return new LambdaQueryWrapperX<IotProductionExceptionDO>()
+                .eqIfPresent(IotProductionExceptionDO::getCompanyId, reqVO.getCompanyId())
+                .eqIfPresent(IotProductionExceptionDO::getDeptId, reqVO.getDeptId())
+                .eqIfPresent(IotProductionExceptionDO::getTaskId, reqVO.getTaskId())
+                .eqIfPresent(IotProductionExceptionDO::getReportId, reqVO.getReportId())
+                .betweenIfPresent(IotProductionExceptionDO::getOccurrenceDate, reqVO.getOccurrenceDate())
+                .eqIfPresent(IotProductionExceptionDO::getStatus, reqVO.getStatus())
+                .eqIfPresent(IotProductionExceptionDO::getGroupFlag, reqVO.getGroupFlag())
+                .eqIfPresent(IotProductionExceptionDO::getSort, reqVO.getSort())
+                // ⚠️原代码remark用eq等于,大概率业务是模糊匹配 likeIfPresent,按需修改
+                .eqIfPresent(IotProductionExceptionDO::getRemark, reqVO.getRemark())
+                .betweenIfPresent(IotProductionExceptionDO::getCreateTime, reqVO.getCreateTime())
+                .orderByDesc(IotProductionExceptionDO::getId);
+    }
+
+}

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

@@ -11,9 +11,12 @@ 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;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionPageReqVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotfivedailyreport.IotFiveDailyReportDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotproductionexception.IotProductionExceptionDO;
 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.iotproductionexception.IotProductionExceptionMapper;
 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;
@@ -34,6 +37,7 @@ import org.springframework.validation.annotation.Validated;
 import javax.annotation.Resource;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
@@ -71,6 +75,8 @@ public class IotFiveDailyReportServiceImpl implements IotFiveDailyReportService
     private ThreadPoolTaskExecutor pmsThreadPoolTaskExecutor;
     @Resource
     private PmsMessage pmsMessage;
+    @Resource
+    private IotProductionExceptionMapper iotProductionExceptionMapper;
 
     @Override
     public Long createIotFiveDailyReport(IotFiveDailyReportSaveReqVO createReqVO) {
@@ -92,6 +98,9 @@ public class IotFiveDailyReportServiceImpl implements IotFiveDailyReportService
 
         Long taskId = updateReqVO.getTaskId();
         Long deptId = updateObj.getDeptId();
+        // 日报所属日期
+        LocalDateTime reportDate = updateObj.getCreateTime();
+
         // 关联更新相关任务的 施工状态
         if (ObjUtil.isNotEmpty(taskId)) {
             IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
@@ -112,115 +121,148 @@ public class IotFiveDailyReportServiceImpl implements IotFiveDailyReportService
         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);
-
-            List<String> projectDeptNames = new ArrayList<>();
-            // 查询瑞鹰 163l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
-            Set<Long> userIds = new HashSet<>();
-            Set<Long> deptIds = new HashSet<>();
-            DataPermissionUtils.executeIgnore(() -> {
-                // 封装以上消息内容 发送到公司 角色 (生产异常通知)
-                // 查询瑞恒 157l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
-                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());
-                    });
-                }
-                // 查询当前队伍的上级项目部 过滤掉数据权限
-                DeptDO projectDept = deptService.getDept(dept.getParentId());
-                if (ObjUtil.isNotEmpty(projectDept)) {
-                    projectDeptNames.add(projectDept.getName());
-                }
-            });
-
-            // 队伍名称
-            String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
-            // 项目部名称
-            String projectDeptName = CollUtil.isNotEmpty(projectDeptNames) ? projectDeptNames.get(0) : StrUtil.EMPTY;
+            // 查询表 rq_iot_production_exception 判断是否需要发送生产异常消息提醒
+            // 当前公司 taskId deptId reportId reportDate
+            // 查询表中相同 taskId 是否存在记录 记录日期yyyy-MM-dd如果与当前日期相关 <= 1天则不发送通知
+            IotProductionExceptionPageReqVO reqVO = new IotProductionExceptionPageReqVO();
+            reqVO.setTaskId(taskId);
+            IotProductionExceptionDO productionException = iotProductionExceptionMapper.selectMaxIdOne(reqVO);
+            boolean skipExceptionNotify = false;
+            if (ObjUtil.isEmpty(productionException)) {
+                // 没有当前任务关联的异常记录 直接保存发送消息提醒
 
-            String wellName = "无";
-            if (ObjUtil.isNotEmpty(taskId)) {
-                IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
-                if (ObjUtil.isNotEmpty(task)) {
-                    wellName = task.getWellName();
+            } else {
+                // 存在当前任务的异常记录 记录日期 yyyy-MM-dd 与日报日期比较 如果相关 <= 1天 则不发送通知
+                LocalDateTime occurrenceDate = productionException.getOccurrenceDate();
+                if (ObjUtil.isNotEmpty(occurrenceDate)) {
+                    LocalDate occurDay = productionException.getOccurrenceDate().toLocalDate();
+                    LocalDate reportDay = reportDate.toLocalDate();
+                    long dayDiff = Math.abs(ChronoUnit.DAYS.between(occurDay, reportDay));
+                    skipExceptionNotify = dayDiff <= 1;
                 }
             }
-            // 当前处理进度 当日施工简报
-            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
-            );
-
-            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);
+
+            // 保存生产异常记录日志
+            IotProductionExceptionDO tobeAddedException = new IotProductionExceptionDO();
+            tobeAddedException.setCompanyId(388l);
+            tobeAddedException.setTaskId(taskId);
+            tobeAddedException.setReportId(updateObj.getId());
+            tobeAddedException.setDeptId(deptId);
+            tobeAddedException.setOccurrenceDate(updateObj.getCreateTime());
+            Integer num = iotProductionExceptionMapper.insert(tobeAddedException);
+
+            if (!skipExceptionNotify) {
+                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);
+
+                List<String> projectDeptNames = new ArrayList<>();
+                // 查询瑞鹰 163l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+                Set<Long> userIds = new HashSet<>();
+                Set<Long> deptIds = new HashSet<>();
+                DataPermissionUtils.executeIgnore(() -> {
+                    // 封装以上消息内容 发送到公司 角色 (生产异常通知)
+                    // 查询瑞恒 157l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+                    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());
+                        });
+                    }
+                    // 查询当前队伍的上级项目部 过滤掉数据权限
+                    DeptDO projectDept = deptService.getDept(dept.getParentId());
+                    if (ObjUtil.isNotEmpty(projectDept)) {
+                        projectDeptNames.add(projectDept.getName());
+                    }
+                });
+
+                // 队伍名称
+                String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
+                // 项目部名称
+                String projectDeptName = CollUtil.isNotEmpty(projectDeptNames) ? projectDeptNames.get(0) : 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
+                );
+
+                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();
                                         }
-                                    } finally {
-                                        latch.countDown();
-                                    }
+                                    });
                                 });
-                            });
+                            }
                         }
                     }
                 }
             }
+
         }
     }
 

+ 55 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotproductionexception/IotProductionExceptionService.java

@@ -0,0 +1,55 @@
+package cn.iocoder.yudao.module.pms.service.iotproductionexception;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotproductionexception.IotProductionExceptionDO;
+
+import javax.validation.Valid;
+
+/**
+ * 生产异常 Service 接口
+ *
+ * @author ruiqi
+ */
+public interface IotProductionExceptionService {
+
+    /**
+     * 创建生产异常
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createIotProductionException(@Valid IotProductionExceptionSaveReqVO createReqVO);
+
+    /**
+     * 更新生产异常
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateIotProductionException(@Valid IotProductionExceptionSaveReqVO updateReqVO);
+
+    /**
+     * 删除生产异常
+     *
+     * @param id 编号
+     */
+    void deleteIotProductionException(Long id);
+
+    /**
+     * 获得生产异常
+     *
+     * @param id 编号
+     * @return 生产异常
+     */
+    IotProductionExceptionDO getIotProductionException(Long id);
+
+    /**
+     * 获得生产异常分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 生产异常分页
+     */
+    PageResult<IotProductionExceptionDO> getIotProductionExceptionPage(IotProductionExceptionPageReqVO pageReqVO);
+
+}

+ 71 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotproductionexception/IotProductionExceptionServiceImpl.java

@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.pms.service.iotproductionexception;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotproductionexception.IotProductionExceptionDO;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotproductionexception.IotProductionExceptionMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_PRODUCTION_EXCEPTION_NOT_EXISTS;
+
+/**
+ * 生产异常 Service 实现类
+ *
+ * @author ruiqi
+ */
+@Service
+@Validated
+public class IotProductionExceptionServiceImpl implements IotProductionExceptionService {
+
+    @Resource
+    private IotProductionExceptionMapper iotProductionExceptionMapper;
+
+    @Override
+    public Long createIotProductionException(IotProductionExceptionSaveReqVO createReqVO) {
+        // 插入
+        IotProductionExceptionDO iotProductionException = BeanUtils.toBean(createReqVO, IotProductionExceptionDO.class);
+        iotProductionExceptionMapper.insert(iotProductionException);
+        // 返回
+        return iotProductionException.getId();
+    }
+
+    @Override
+    public void updateIotProductionException(IotProductionExceptionSaveReqVO updateReqVO) {
+        // 校验存在
+        validateIotProductionExceptionExists(updateReqVO.getId());
+        // 更新
+        IotProductionExceptionDO updateObj = BeanUtils.toBean(updateReqVO, IotProductionExceptionDO.class);
+        iotProductionExceptionMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteIotProductionException(Long id) {
+        // 校验存在
+        validateIotProductionExceptionExists(id);
+        // 删除
+        iotProductionExceptionMapper.deleteById(id);
+    }
+
+    private void validateIotProductionExceptionExists(Long id) {
+        if (iotProductionExceptionMapper.selectById(id) == null) {
+            throw exception(IOT_PRODUCTION_EXCEPTION_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public IotProductionExceptionDO getIotProductionException(Long id) {
+        return iotProductionExceptionMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<IotProductionExceptionDO> getIotProductionExceptionPage(IotProductionExceptionPageReqVO pageReqVO) {
+        return iotProductionExceptionMapper.selectPage(pageReqVO);
+    }
+
+}

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

@@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.pms.constant.PmsConstants;
 import cn.iocoder.yudao.module.pms.controller.admin.iotattachment.vo.IotAttachmentSaveReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotdailyreportfuel.vo.IotDailyReportFuelSaveReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotdeviceassociate.vo.IotDeviceAssociatePageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprojectinfo.vo.IotProjectInfoPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotProjectTaskPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.*;
@@ -26,6 +27,7 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.IotProductClassifyDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotattachment.IotAttachmentDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotdailyreportfuel.IotDailyReportFuelDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotdeviceassociate.IotDeviceAssociateDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotproductionexception.IotProductionExceptionDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojectinfo.IotProjectInfoDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttask.IotProjectTaskDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
@@ -36,6 +38,7 @@ import cn.iocoder.yudao.module.pms.dal.mysql.IotProductClassifyMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotattachment.IotAttachmentMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotdailyreportfuel.IotDailyReportFuelMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotdeviceassociate.IotDeviceAssociateMapper;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotproductionexception.IotProductionExceptionMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotprojectinfo.IotProjectInfoMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttask.IotProjectTaskMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotrddailyreport.IotRdDailyReportMapper;
@@ -129,6 +132,8 @@ public class IotRdDailyReportServiceImpl implements IotRdDailyReportService {
     private IotRdDailyReportDetailMapper iotRdDailyReportDetailMapper;
     @Resource
     private IotDeviceAssociateMapper iotDeviceAssociateMapper;
+    @Resource
+    private IotProductionExceptionMapper iotProductionExceptionMapper;
 
     @Override
     public Long createIotRdDailyReport(IotRdDailyReportSaveReqVO createReqVO) {
@@ -256,6 +261,8 @@ public class IotRdDailyReportServiceImpl implements IotRdDailyReportService {
 
         Long deptId = dailyReport.getDeptId();
         Long taskId = dailyReport.getTaskId();
+        // 日报所属日期
+        LocalDateTime reportDate = dailyReport.getCreateTime();
 
         // 如果是修改 非生产时间 ,则不必再发起审批 生产运营部 维护 时效
         if (StrUtil.isBlank(updateReqVO.getNonProduct())) {
@@ -272,110 +279,143 @@ public class IotRdDailyReportServiceImpl implements IotRdDailyReportService {
             // 审核同意 将生产异常情况 (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);
 
-                List<String> projectDeptNames = new ArrayList<>();
-                // 查询瑞鹰 163l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
-                Set<Long> userIds = new HashSet<>();
-                Set<Long> deptIds = new HashSet<>();
-                DataPermissionUtils.executeIgnore(() -> {
-                    // 封装以上消息内容 发送到公司 角色 (生产异常通知)
-                    // 查询瑞恒 157l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
-                    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());
-                        });
-                    }
-                    // 查询当前队伍的上级项目部 过滤掉数据权限
-                    DeptDO projectDept = deptService.getDept(dept.getParentId());
-                    if (ObjUtil.isNotEmpty(projectDept)) {
-                        projectDeptNames.add(projectDept.getName());
-                    }
-                });
+                // 查询表 rq_iot_production_exception 判断是否需要发送生产异常消息提醒
+                // 当前公司 taskId deptId reportId reportDate
+                // 查询表中相同 taskId 是否存在记录 记录日期yyyy-MM-dd如果与当前日期相关 <= 1天则不发送通知
+                IotProductionExceptionPageReqVO reqVO = new IotProductionExceptionPageReqVO();
+                reqVO.setTaskId(taskId);
+                IotProductionExceptionDO productionException = iotProductionExceptionMapper.selectMaxIdOne(reqVO);
+                boolean skipExceptionNotify = false;
+                if (ObjUtil.isEmpty(productionException)) {
+                    // 没有当前任务关联的异常记录 直接保存发送消息提醒
 
-                // 队伍名称
-                String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
-                // 项目部名称
-                String projectDeptName = CollUtil.isNotEmpty(projectDeptNames) ? projectDeptNames.get(0) : StrUtil.EMPTY;
-                String wellName = "无";
-                if (ObjUtil.isNotEmpty(taskId)) {
-                    IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
-                    if (ObjUtil.isNotEmpty(task)) {
-                        wellName = task.getWellName();
+                } else {
+                    // 存在当前任务的异常记录 记录日期 yyyy-MM-dd 与日报日期比较 如果相关 <= 1天 则不发送通知
+                    LocalDateTime occurrenceDate = productionException.getOccurrenceDate();
+                    if (ObjUtil.isNotEmpty(occurrenceDate)) {
+                        LocalDate occurDay = productionException.getOccurrenceDate().toLocalDate();
+                        LocalDate reportDay = reportDate.toLocalDate();
+                        long dayDiff = Math.abs(ChronoUnit.DAYS.between(occurDay, reportDay));
+                        skipExceptionNotify = dayDiff <= 1;
                     }
                 }
-                // 当前处理进度 当日施工简报
-                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
-                );
-
-                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);
+
+                // 保存生产异常记录日志
+                IotProductionExceptionDO tobeAddedException = new IotProductionExceptionDO();
+                tobeAddedException.setCompanyId(163l);
+                tobeAddedException.setTaskId(taskId);
+                tobeAddedException.setReportId(dailyReport.getId());
+                tobeAddedException.setDeptId(dailyReport.getDeptId());
+                tobeAddedException.setOccurrenceDate(dailyReport.getCreateTime());
+                Integer num = iotProductionExceptionMapper.insert(tobeAddedException);
+
+                if (!skipExceptionNotify) {
+                    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);
+
+                    List<String> projectDeptNames = new ArrayList<>();
+                    // 查询瑞鹰 163l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+                    Set<Long> userIds = new HashSet<>();
+                    Set<Long> deptIds = new HashSet<>();
+                    DataPermissionUtils.executeIgnore(() -> {
+                        // 封装以上消息内容 发送到公司 角色 (生产异常通知)
+                        // 查询瑞恒 157l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+                        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());
+                            });
+                        }
+                        // 查询当前队伍的上级项目部 过滤掉数据权限
+                        DeptDO projectDept = deptService.getDept(dept.getParentId());
+                        if (ObjUtil.isNotEmpty(projectDept)) {
+                            projectDeptNames.add(projectDept.getName());
+                        }
+                    });
+
+                    // 队伍名称
+                    String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
+                    // 项目部名称
+                    String projectDeptName = CollUtil.isNotEmpty(projectDeptNames) ? projectDeptNames.get(0) : 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
+                    );
+
+                    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();
                                             }
-                                        } finally {
-                                            latch.countDown();
-                                        }
+                                        });
                                     });
-                                });
+                                }
                             }
                         }
                     }

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

@@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotcarzhbd.vo.IotCarZhbdPage
 import cn.iocoder.yudao.module.pms.controller.admin.iotdevicecategorytemplateattrs.vo.IotDeviceProperty;
 import cn.iocoder.yudao.module.pms.controller.admin.iotdevicerunlog.vo.IotDeviceRunLogPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeationFillPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionPageReqVO;
 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.*;
@@ -27,6 +28,7 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.iotcarzhbd.IotCarZhbdDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotdailyreportfuel.IotDailyReportFuelDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotproductionexception.IotProductionExceptionDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttask.IotProjectTaskDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotrhdailyreport.IotRhDailyReportDO;
 import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
@@ -34,6 +36,7 @@ import cn.iocoder.yudao.module.pms.dal.mysql.iotcarzhbd.IotCarZhbdMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotdailyreportfuel.IotDailyReportFuelMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotdevicerunlog.IotDeviceRunLogMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotopeationfill.IotOpeationFillMapper;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotproductionexception.IotProductionExceptionMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotprojectinfo.IotProjectInfoMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttask.IotProjectTaskMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotrhdailyreport.IotRhDailyReportMapper;
@@ -139,7 +142,8 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
     private IotProjectInfoMapper iotProjectInfoMapper;
     @Resource
     private IotProjectTaskService iotProjectTaskService;
-
+    @Resource
+    private IotProductionExceptionMapper iotProductionExceptionMapper;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -854,6 +858,8 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
         dailyReport.setOpinion(updateReqVO.getOpinion());
         Long deptId = dailyReport.getDeptId();
         Long taskId = dailyReport.getTaskId();
+        // 日报所属日期
+        LocalDateTime reportDate = dailyReport.getCreateTime();
         if (SupplierAuditStatusEnum.REJECT.getStatus().equals(updateReqVO.getAuditStatus())) {
             // 审批拒绝
             dailyReport.setStatus(0);
@@ -884,109 +890,141 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
             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);
-
-                List<String> projectDeptNames = new ArrayList<>();
-                // 查询瑞鹰 163l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
-                Set<Long> userIds = new HashSet<>();
-                Set<Long> deptIds = new HashSet<>();
-                DataPermissionUtils.executeIgnore(() -> {
-                    // 封装以上消息内容 发送到公司 角色 (生产异常通知)
-                    // 查询瑞恒 157l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
-                    deptIds.add(157l);
-                    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());
-                        });
-                    }
-                    // 查询当前队伍的上级项目部 过滤掉数据权限
-                    DeptDO projectDept = deptService.getDept(dept.getParentId());
-                    if (ObjUtil.isNotEmpty(projectDept)) {
-                        projectDeptNames.add(projectDept.getName());
-                    }
-                });
+                // 查询表 rq_iot_production_exception 判断是否需要发送生产异常消息提醒
+                // 当前公司 taskId deptId reportId reportDate
+                // 查询表中相同 taskId 是否存在记录 记录日期yyyy-MM-dd如果与当前日期相关 <= 1天则不发送通知
+                IotProductionExceptionPageReqVO reqVO = new IotProductionExceptionPageReqVO();
+                reqVO.setTaskId(taskId);
+                IotProductionExceptionDO productionException = iotProductionExceptionMapper.selectMaxIdOne(reqVO);
+                boolean skipExceptionNotify = false;
+                if (ObjUtil.isEmpty(productionException)) {
+                    // 没有当前任务关联的异常记录 直接保存发送消息提醒
 
-                // 队伍名称
-                String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
-                // 项目部名称
-                String projectDeptName = CollUtil.isNotEmpty(projectDeptNames) ? projectDeptNames.get(0) : StrUtil.EMPTY;
-                String wellName = "无";
-                if (ObjUtil.isNotEmpty(taskId)) {
-                    IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
-                    if (ObjUtil.isNotEmpty(task)) {
-                        wellName = task.getWellName();
+                } else {
+                    // 存在当前任务的异常记录 记录日期 yyyy-MM-dd 与日报日期比较 如果相关 <= 1天 则不发送通知
+                    LocalDateTime occurrenceDate = productionException.getOccurrenceDate();
+                    if (ObjUtil.isNotEmpty(occurrenceDate)) {
+                        LocalDate occurDay = productionException.getOccurrenceDate().toLocalDate();
+                        LocalDate reportDay = reportDate.toLocalDate();
+                        long dayDiff = Math.abs(ChronoUnit.DAYS.between(occurDay, reportDay));
+                        skipExceptionNotify = dayDiff <= 1;
                     }
                 }
-                // 当前处理进度 当日施工简报
-                String brief = StrUtil.blankToDefault(dailyReport.getProductionStatus(), "无");
-                // 下步计划
-                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
-                );
-
-                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);
+
+                // 保存生产异常记录日志
+                IotProductionExceptionDO tobeAddedException = new IotProductionExceptionDO();
+                tobeAddedException.setCompanyId(157l);
+                tobeAddedException.setTaskId(taskId);
+                tobeAddedException.setReportId(dailyReport.getId());
+                tobeAddedException.setDeptId(dailyReport.getDeptId());
+                tobeAddedException.setOccurrenceDate(dailyReport.getCreateTime());
+                Integer num = iotProductionExceptionMapper.insert(tobeAddedException);
+
+                if (!skipExceptionNotify) {
+                    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);
+
+                    List<String> projectDeptNames = new ArrayList<>();
+                    // 查询瑞鹰 163l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+                    Set<Long> userIds = new HashSet<>();
+                    Set<Long> deptIds = new HashSet<>();
+                    DataPermissionUtils.executeIgnore(() -> {
+                        // 封装以上消息内容 发送到公司 角色 (生产异常通知)
+                        // 查询瑞恒 157l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+                        deptIds.add(157l);
+                        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());
+                            });
+                        }
+                        // 查询当前队伍的上级项目部 过滤掉数据权限
+                        DeptDO projectDept = deptService.getDept(dept.getParentId());
+                        if (ObjUtil.isNotEmpty(projectDept)) {
+                            projectDeptNames.add(projectDept.getName());
+                        }
+                    });
+
+                    // 队伍名称
+                    String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
+                    // 项目部名称
+                    String projectDeptName = CollUtil.isNotEmpty(projectDeptNames) ? projectDeptNames.get(0) : 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.getProductionStatus(), "无");
+                    // 下步计划
+                    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
+                    );
+
+                    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();
                                             }
-                                        } finally {
-                                            latch.countDown();
-                                        }
+                                        });
                                     });
-                                });
+                                }
                             }
                         }
                     }

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

@@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.pms.constant.PmsConstants;
 import cn.iocoder.yudao.module.pms.controller.admin.depttype.vo.IotDeptTypePageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotdevicecategorytemplateattrs.vo.IotDeviceProperty;
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeationFillPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotproductionexception.vo.IotProductionExceptionPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotProjectTaskPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreport.vo.*;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreportdetail.vo.IotRyDailyReportDetailPageReqVO;
@@ -23,6 +24,7 @@ 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.depttype.IotDeptTypeDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotproductionexception.IotProductionExceptionDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttask.IotProjectTaskDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotrydailyreport.IotRyDailyReportDO;
@@ -30,6 +32,7 @@ import cn.iocoder.yudao.module.pms.dal.dataobject.iotrydailyreportdetail.IotRyDa
 import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.depttype.IotDeptTypeMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotopeationfill.IotOpeationFillMapper;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotproductionexception.IotProductionExceptionMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttask.IotProjectTaskMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotrydailyreport.IotRyDailyReportMapper;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotrydailyreportdetail.IotRyDailyReportDetailMapper;
@@ -116,6 +119,8 @@ public class IotRyDailyReportServiceImpl implements IotRyDailyReportService {
     private IotOpeationFillMapper iotOpeationFillMapper;
     @Resource
     private IotRyDailyReportDetailMapper iotRyDailyReportDetailMapper;
+    @Resource
+    private IotProductionExceptionMapper iotProductionExceptionMapper;
 
 
     @Override
@@ -947,6 +952,8 @@ public class IotRyDailyReportServiceImpl implements IotRyDailyReportService {
         dailyReport.setConstructionBrief(updateReqVO.getConstructionBrief());
         Long deptId = dailyReport.getDeptId();
         Long taskId = dailyReport.getTaskId();
+        // 日报所属日期
+        LocalDateTime reportDate = dailyReport.getCreateTime();
         if (SupplierAuditStatusEnum.REJECT.getStatus().equals(updateReqVO.getAuditStatus())) {
             dailyReport.setStatus(0);
             // 审批拒绝后需要向 日报填报人 发送消息提醒 驳回到日报创建人
@@ -986,109 +993,140 @@ public class IotRyDailyReportServiceImpl implements IotRyDailyReportService {
             // 审核同意 将生产异常情况 (npt 工程质量) 发送到拥有 生产异常通知 角色的人员
             BigDecimal accidentTime = dailyReport.getAccidentTime();
             if (ObjUtil.isNotEmpty(accidentTime) && accidentTime.compareTo(BigDecimal.ZERO) > 0) {
+                // 查询表 rq_iot_production_exception 判断是否需要发送生产异常消息提醒
+                // 当前公司 taskId deptId reportId reportDate
+                // 查询表中相同 taskId 是否存在记录 记录日期yyyy-MM-dd如果与当前日期相关 <= 1天则不发送通知
+                IotProductionExceptionPageReqVO reqVO = new IotProductionExceptionPageReqVO();
+                reqVO.setTaskId(taskId);
+                IotProductionExceptionDO productionException = iotProductionExceptionMapper.selectMaxIdOne(reqVO);
+                boolean skipExceptionNotify = false;
+                if (ObjUtil.isEmpty(productionException)) {
+                    // 没有当前任务关联的异常记录 直接保存发送消息提醒
 
-                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);
-                List<String> projectDeptNames = new ArrayList<>();
-
-                // 封装以上消息内容 发送到公司 角色 (生产异常通知)
-                // 查询瑞鹰 158l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
-                Set<Long> userIds = new HashSet<>();
-                Set<Long> deptIds = new HashSet<>();
-                DataPermissionUtils.executeIgnore(() -> {
-                    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());
-                        });
-                    }
-                    // 查询当前队伍的上级项目部 过滤掉数据权限
-                    DeptDO projectDept = deptService.getDept(dept.getParentId());
-                    if (ObjUtil.isNotEmpty(projectDept)) {
-                        projectDeptNames.add(projectDept.getName());
+                } else {
+                    // 存在当前任务的异常记录 记录日期 yyyy-MM-dd 与日报日期比较 如果相关 <= 1天 则不发送通知
+                    LocalDateTime occurrenceDate = productionException.getOccurrenceDate();
+                    if (ObjUtil.isNotEmpty(occurrenceDate)) {
+                        LocalDate occurDay = productionException.getOccurrenceDate().toLocalDate();
+                        LocalDate reportDay = reportDate.toLocalDate();
+                        long dayDiff = Math.abs(ChronoUnit.DAYS.between(occurDay, reportDay));
+                        skipExceptionNotify = dayDiff <= 1;
                     }
-                });
+                }
 
-                // 队伍名称
-                String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
-                // 项目部名称
-                String projectDeptName = CollUtil.isNotEmpty(projectDeptNames) ? projectDeptNames.get(0) : StrUtil.EMPTY;
-                String wellName = "无";
-                if (ObjUtil.isNotEmpty(taskId)) {
-                    IotProjectTaskDO task = iotProjectTaskMapper.selectById(taskId);
-                    if (ObjUtil.isNotEmpty(task)) {
-                        wellName = task.getWellName();
+                // 保存生产异常记录日志
+                IotProductionExceptionDO tobeAddedException = new IotProductionExceptionDO();
+                tobeAddedException.setCompanyId(158l);
+                tobeAddedException.setTaskId(taskId);
+                tobeAddedException.setReportId(dailyReport.getId());
+                tobeAddedException.setDeptId(dailyReport.getDeptId());
+                tobeAddedException.setOccurrenceDate(dailyReport.getCreateTime());
+                Integer num = iotProductionExceptionMapper.insert(tobeAddedException);
+
+                if (!skipExceptionNotify) {
+                    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);
+                    List<String> projectDeptNames = new ArrayList<>();
+
+                    // 封装以上消息内容 发送到公司 角色 (生产异常通知)
+                    // 查询瑞鹰 158l 下具有以上角色的人员 查询集团(科瑞石油技术) 拥有以上角色的人员
+                    Set<Long> userIds = new HashSet<>();
+                    Set<Long> deptIds = new HashSet<>();
+                    DataPermissionUtils.executeIgnore(() -> {
+                        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());
+                            });
+                        }
+                        // 查询当前队伍的上级项目部 过滤掉数据权限
+                        DeptDO projectDept = deptService.getDept(dept.getParentId());
+                        if (ObjUtil.isNotEmpty(projectDept)) {
+                            projectDeptNames.add(projectDept.getName());
+                        }
+                    });
+
+                    // 队伍名称
+                    String deptName = ObjUtil.isNotEmpty(dept) ? dept.getName() : StrUtil.EMPTY;
+                    // 项目部名称
+                    String projectDeptName = CollUtil.isNotEmpty(projectDeptNames) ? projectDeptNames.get(0) : 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
-                );
-
-                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);
+                    // 当前处理进度 当日施工简报
+                    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
+                    );
+
+                    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();
                                             }
-                                        } finally {
-                                            latch.countDown();
-                                        }
+                                        });
                                     });
-                                });
+                                }
                             }
                         }
                     }