Sfoglia il codice sorgente

Merge remote-tracking branch 'origin/master'

lipenghui 2 giorni fa
parent
commit
796d509119
26 ha cambiato i file con 933 aggiunte e 83 eliminazioni
  1. 3 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotcarzhbd/vo/IotCarZhbdPageReqVO.java
  2. 6 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotdailyreportfuel/vo/IotDailyReportFuelPageReqVO.java
  3. 206 1
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttask/IotProjectTaskController.java
  4. 4 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttask/vo/IotProjectTaskPlatformVO.java
  5. 4 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttask/vo/IotProjectTaskRespVO.java
  6. 164 25
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrddailyreport/IotRdDailyReportController.java
  7. 12 2
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrddailyreport/vo/IotRdDailyReportRespVO.java
  8. 15 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrddailyreport/vo/IotRdDailyReportTaskProgressVO.java
  9. 9 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrhdailyreport/IotRhDailyReportController.java
  10. 3 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrhdailyreport/vo/IotRhDailyReportRespVO.java
  11. 3 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrhdailyreport/vo/IotRhDailyReportSaveReqVO.java
  12. 24 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/vo/IotCarDevicePlateVO.java
  13. 5 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotrhdailyreport/IotRhDailyReportDO.java
  14. 3 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/IotDeviceMapper.java
  15. 5 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotcarzhbd/IotCarZhbdMapper.java
  16. 1 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotdailyreportfuel/IotDailyReportFuelMapper.java
  17. 0 42
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotrydailyreport/IotRyDailyReportMapper.java
  18. 90 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/IotAllZhbdJob.java
  19. 158 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/dailyreport/CreateRyDailyReportOrderJob.java
  20. 4 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/httpEntity/ResponseOilData.java
  21. 1 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrddailyreport/IotRdDailyReportServiceImpl.java
  22. 8 0
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrhdailyreport/IotRhDailyReportService.java
  23. 120 5
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotrhdailyreport/IotRhDailyReportServiceImpl.java
  24. 68 8
      yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/util/ZHBDUtil.java
  25. 14 0
      yudao-module-pms/yudao-module-pms-biz/src/main/resources/mapper/static/IotDeviceMapper.xml
  26. 3 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java

+ 3 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotcarzhbd/vo/IotCarZhbdPageReqVO.java

@@ -45,4 +45,7 @@ public class IotCarZhbdPageReqVO extends PageParam {
 
     @Schema(description = "设备编码 与设备表额外匹配的编码", example = "10570")
     private Collection<String> customDeviceCodes;
+
+    @Schema(description = "中航北斗 carId 集合", example = "10570")
+    private Collection<String> carIds;
 }

+ 6 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotdailyreportfuel/vo/IotDailyReportFuelPageReqVO.java

@@ -9,6 +9,7 @@ import org.springframework.format.annotation.DateTimeFormat;
 
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
+import java.util.Collection;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 
@@ -56,4 +57,9 @@ public class IotDailyReportFuelPageReqVO extends PageParam {
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     private LocalDateTime[] createTime;
 
+    /**
+     * 扩展属性
+     */
+    @Schema(description = "日报id集合", example = "你猜")
+    private Collection<Long> reportIds;
 }

+ 206 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttask/IotProjectTaskController.java

@@ -14,10 +14,16 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotProject
 import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotProjectTaskPlatformVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotProjectTaskRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotTaskSaveVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportStatisticsItemVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojectinfo.IotProjectInfoDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttask.IotProjectTaskDO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotrddailyreport.IotRdDailyReportDO;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotrddailyreport.IotRdDailyReportMapper;
 import cn.iocoder.yudao.module.pms.service.iotprojectinfo.IotProjectInfoService;
 import cn.iocoder.yudao.module.pms.service.iotprojecttask.IotProjectTaskService;
+import cn.iocoder.yudao.module.pms.service.iotrddailyreport.IotRdDailyReportService;
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
 import cn.iocoder.yudao.module.system.service.dept.DeptService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -31,6 +37,7 @@ import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.util.*;
 
 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
@@ -52,6 +59,10 @@ public class IotProjectTaskController {
 
     @Resource
     private IotProjectInfoService iotProjectInfoService;
+    @Resource
+    private IotRdDailyReportMapper iotRdDailyReportMapper;
+    @Resource
+    private IotRdDailyReportService iotRdDailyReportService;
 
     @PostMapping("/create")
     @Operation(summary = "创建项目信息任务拆分")
@@ -110,6 +121,138 @@ public class IotProjectTaskController {
             return Collections.emptyList();
         }
         Map<Long, List<IotProjectTaskPlatformVO>> platformWellPair = new HashMap<>();
+        // 查询每口井的已完成工作量
+        IotRdDailyReportPageReqVO reportReqVO = new IotRdDailyReportPageReqVO();
+        reportReqVO.setTaskIds(convertList(pagedTasks, IotProjectTaskDO::getId));
+        List<IotRdDailyReportDO> dailyReports = iotRdDailyReportMapper.dailyReports(reportReqVO);
+        // key任务井id      value队伍工作量数据
+        Map<Long, BigDecimal> bridgePlugPair = new HashMap<>();
+        Map<Long, BigDecimal> runCountPair = new HashMap<>();
+        Map<Long, BigDecimal> cumulativeWorkingWellPair = new HashMap<>();
+        Map<Long, BigDecimal> hourCountPair = new HashMap<>();
+        Map<Long, BigDecimal> waterVolumePair = new HashMap<>();
+        Map<Long, BigDecimal> pumpTripsPair = new HashMap<>();
+        Map<Long, BigDecimal> cumulativeWorkingLayersPair = new HashMap<>();
+        Map<Long, BigDecimal> mixSandPair = new HashMap<>();
+        if (CollUtil.isNotEmpty(dailyReports)) {
+            dailyReports.forEach(report -> {
+                // 设置每个任务的工作量数据  单位相同的工作量值作合并处理
+                List<IotTaskAttrModelProperty> taskAttrs = report.getExtProperty();
+                // 暂存不同单位的工作量属性值
+                BigDecimal tempTotalBridgePlug = BigDecimal.ZERO;               // 桥塞(个数)
+                BigDecimal tempTotalRunCount = BigDecimal.ZERO;                 // 趟数
+                BigDecimal tempTotalCumulativeWorkingWell = BigDecimal.ZERO;    // 井数
+                BigDecimal tempTotalHourCount = BigDecimal.ZERO;                // 小时H
+                BigDecimal tempTotalWaterVolume = BigDecimal.ZERO;              // 水方量(方)
+                BigDecimal tempTotalPumpTrips = BigDecimal.ZERO;
+                BigDecimal tempTotalCumulativeWorkingLayers = BigDecimal.ZERO;  // 段数  累计施工-层
+                BigDecimal tempTotalMixSand = BigDecimal.ZERO;                  // 台次 当日泵车台次 当日仪表/混砂
+                if (CollUtil.isNotEmpty(taskAttrs)) {
+                    for (IotTaskAttrModelProperty attr : taskAttrs) {
+                        String unit = attr.getUnit();   // 工作量单位
+                        String actualValueStr = attr.getActualValue();  // 工作量属性的实际值
+                        // 处理实际值:避免null或非数字字符串导致的异常
+                        BigDecimal actualValue = BigDecimal.ZERO;
+                        if (StrUtil.isNotBlank(actualValueStr)) {  // 假设使用Hutool的StrUtil,或自行判断null/空
+                            try {
+                                actualValue = new BigDecimal(actualValueStr);
+                            } catch (NumberFormatException e) {
+                                // 若字符串格式错误,默认按0处理(可根据业务调整)
+                                actualValue = BigDecimal.ZERO;
+                            }
+                        }
+                        if ("个数".equals(unit)) {
+                            // 钻可溶桥塞  钻复合桥塞
+                            tempTotalBridgePlug = tempTotalBridgePlug.add(actualValue);
+                            if (bridgePlugPair.containsKey(report.getTaskId())) {
+                                BigDecimal tempBridgePlug = bridgePlugPair.get(report.getTaskId());
+                                bridgePlugPair.put(report.getTaskId(), tempTotalBridgePlug.add(tempBridgePlug));
+                            } else {
+                                bridgePlugPair.put(report.getTaskId(), tempTotalBridgePlug);
+                            }
+                        }
+                        if ("趟数".equals(unit)) {
+                            tempTotalRunCount = tempTotalRunCount.add(actualValue);
+                            if (runCountPair.containsKey(report.getTaskId())) {
+                                BigDecimal tempRunCount = runCountPair.get(report.getTaskId());
+                                runCountPair.put(report.getTaskId(), tempTotalRunCount.add(tempRunCount));
+                            } else {
+                                runCountPair.put(report.getTaskId(), tempTotalRunCount);
+                            }
+                        }
+                        if ("小时".equals(unit)) {
+                            tempTotalHourCount = tempTotalHourCount.add(actualValue);
+                            if (hourCountPair.containsKey(report.getTaskId())) {
+                                BigDecimal tempHourCount = hourCountPair.get(report.getTaskId());
+                                hourCountPair.put(report.getTaskId(), tempTotalHourCount.add(tempHourCount));
+                            } else {
+                                hourCountPair.put(report.getTaskId(), tempTotalHourCount);
+                            }
+                        }
+                        if ("天数".equals(unit)) {
+                            // 将 actualValue 换算成 H
+                            BigDecimal hours = actualValue.multiply(new BigDecimal("24"));
+                            tempTotalHourCount = tempTotalHourCount.add(hours);
+                            if (hourCountPair.containsKey(report.getTaskId())) {
+                                BigDecimal tempHourCount = hourCountPair.get(report.getTaskId());
+                                hourCountPair.put(report.getTaskId(), tempTotalHourCount.add(tempHourCount));
+                            } else {
+                                hourCountPair.put(report.getTaskId(), tempTotalHourCount);
+                            }
+                        }
+                        if ("方".equals(unit)) {
+                            tempTotalWaterVolume = tempTotalWaterVolume.add(actualValue);
+                            if (waterVolumePair.containsKey(report.getTaskId())) {
+                                BigDecimal tempWaterVolume = waterVolumePair.get(report.getTaskId());
+                                waterVolumePair.put(report.getTaskId(), tempTotalWaterVolume.add(tempWaterVolume));
+                            } else {
+                                waterVolumePair.put(report.getTaskId(), tempTotalWaterVolume);
+                            }
+                        }
+                        if ("井数".equals(unit)) {
+                            tempTotalCumulativeWorkingWell = tempTotalCumulativeWorkingWell.add(actualValue);
+                            if (cumulativeWorkingWellPair.containsKey(report.getTaskId())) {
+                                BigDecimal tempWorkingWell = cumulativeWorkingWellPair.get(report.getTaskId());
+                                cumulativeWorkingWellPair.put(report.getTaskId(), tempTotalCumulativeWorkingWell.add(tempWorkingWell));
+                            } else {
+                                cumulativeWorkingWellPair.put(report.getTaskId(), tempTotalCumulativeWorkingWell);
+                            }
+                        }
+                        if ("段数".equals(unit)) {
+                            // 累计施工层
+                            tempTotalCumulativeWorkingLayers = tempTotalCumulativeWorkingLayers.add(actualValue);
+                            if (cumulativeWorkingLayersPair.containsKey(report.getTaskId())) {
+                                BigDecimal tempWorkingLayer = cumulativeWorkingLayersPair.get(report.getTaskId());
+                                cumulativeWorkingLayersPair.put(report.getTaskId(), tempTotalCumulativeWorkingLayers.add(tempWorkingLayer));
+                            } else {
+                                cumulativeWorkingLayersPair.put(report.getTaskId(), tempTotalCumulativeWorkingLayers);
+                            }
+                        }
+                        if ("台次".equals(unit) && "当日泵车台次".equals(attr.getName())) {
+                            tempTotalPumpTrips = tempTotalPumpTrips.add(actualValue);
+                            if (pumpTripsPair.containsKey(report.getTaskId())) {
+                                BigDecimal tempPumpTrips = pumpTripsPair.get(report.getTaskId());
+                                pumpTripsPair.put(report.getTaskId(), tempTotalPumpTrips.add(tempPumpTrips));
+                            } else {
+                                pumpTripsPair.put(report.getTaskId(), tempTotalPumpTrips);
+                            }
+                        }
+                        if ("台次".equals(unit) && ("当日仪表/混砂".equals(attr.getName())
+                                || "当日混砂".equals(attr.getName()) || "当日仪表".equals(attr.getName()))) {
+                            tempTotalMixSand = tempTotalMixSand.add(actualValue);
+                            if (mixSandPair.containsKey(report.getTaskId())) {
+                                BigDecimal tempMixSand = mixSandPair.get(report.getTaskId());
+                                mixSandPair.put(report.getTaskId(), tempTotalMixSand.add(tempMixSand));
+                            } else {
+                                mixSandPair.put(report.getTaskId(), tempTotalMixSand);
+                            }
+                        }
+                    }
+                }
+            });
+        }
+        // key任务id   value任务已经完成的工作量集合
+        Map<Long, List<IotRdDailyReportStatisticsItemVO>> taskFinishedWorkloadPair = new HashMap<>();
         // 循环每个任务 找到任务 关联的平台井
         if (CollUtil.isNotEmpty(pagedTasks)) {
             pagedTasks.forEach(task -> {
@@ -131,12 +274,74 @@ public class IotProjectTaskController {
                     });
                     platformWellPair.put(task.getId(), platforms);
                 }
+                // 设置每个任务的已完成工作量信息
+                List<IotRdDailyReportStatisticsItemVO> items = new ArrayList<>();
+                // 以队伍为维度 设置每种施工工艺的工作量 总和
+                if (bridgePlugPair.containsKey(task.getId())) {
+                    // 钻可溶桥塞  钻复合桥塞
+                    IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
+                    item.setUnit("个数");
+                    item.setWorkload(bridgePlugPair.get(task.getId()));
+                    items.add(item);
+                }
+                if (runCountPair.containsKey(task.getId())) {
+                    // 通刮洗  冲砂
+                    IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
+                    item.setUnit("趟数");
+                    item.setWorkload(runCountPair.get(task.getId()));
+                    items.add(item);
+                }
+                if (hourCountPair.containsKey(task.getId())) {
+                    // 液氮泵车(时间D)  千型泵车(时间H)
+                    IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
+                    item.setUnit("小时");
+                    item.setWorkload(hourCountPair.get(task.getId()));
+                    items.add(item);
+                }
+                if (waterVolumePair.containsKey(task.getId())) {
+                    // 注水
+                    IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
+                    item.setUnit("方");
+                    item.setWorkload(waterVolumePair.get(task.getId()));
+                    items.add(item);
+                }
+                if (cumulativeWorkingWellPair.containsKey(task.getId())) {
+                    // 连续油管常规作业
+                    IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
+                    item.setUnit("井数");
+                    item.setWorkload(cumulativeWorkingWellPair.get(task.getId()));
+                    items.add(item);
+                }
+                if (cumulativeWorkingLayersPair.containsKey(task.getId())) {
+                    // 压裂大包 压裂总包
+                    IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
+                    item.setUnit("段数");
+                    item.setWorkload(cumulativeWorkingLayersPair.get(task.getId()));
+                    items.add(item);
+                }
+                if (pumpTripsPair.containsKey(task.getId())) {
+                    // 主压裂车 当日泵车台次
+                    IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
+                    item.setUnit("台次");
+                    item.setWorkload(pumpTripsPair.get(task.getId()));
+                    items.add(item);
+                }
+                if (mixSandPair.containsKey(task.getId())) {
+                    // 当日仪表/混砂 台次
+                    IotRdDailyReportStatisticsItemVO item = new IotRdDailyReportStatisticsItemVO();
+                    item.setUnit("台次");
+                    item.setWorkload(mixSandPair.get(task.getId()));
+                    items.add(item);
+                }
+                taskFinishedWorkloadPair.put(task.getId(), items);
             });
         }
         // 2. 拼接数据
         return BeanUtils.toBean(pagedTasks, IotProjectTaskRespVO.class, (taskVO) -> {
-            // 2.1 拼接平台井信息
+            // 拼接平台井信息
             findAndThen(platformWellPair, taskVO.getId(), tasks -> taskVO.setPlatformWells(tasks));
+            // 任务已经完成的工作量
+            findAndThen(taskFinishedWorkloadPair, taskVO.getId(), workloads -> taskVO.setItems(workloads));
         });
     }
 

+ 4 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttask/vo/IotProjectTaskPlatformVO.java

@@ -5,6 +5,7 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
+import java.math.BigDecimal;
 import java.util.List;
 import java.util.Set;
 
@@ -39,4 +40,7 @@ public class IotProjectTaskPlatformVO {
 
     @Schema(description = "任务状态 数据字典label值", example = "施工")
     private String rdStatusLabel;
+
+    @Schema(description = "关联井当日油耗", example = "12.3")
+    private BigDecimal dailyFuel;
 }

+ 4 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotprojecttask/vo/IotProjectTaskRespVO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo;
 
+import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportStatisticsItemVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;
@@ -122,4 +123,7 @@ public class IotProjectTaskRespVO {
 
     @Schema(description = "平台井明细", example = "[{\"井号\": #233, \"工作量\": 234}]")
     private List<IotProjectTaskPlatformVO> platformWells;
+
+    @Schema(description = "当前任务已经完成的工作量明细")
+    private List<IotRdDailyReportStatisticsItemVO> items;
 }

+ 164 - 25
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrddailyreport/IotRdDailyReportController.java

@@ -19,10 +19,7 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotdailyreportfuel.vo.IotDai
 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.iotprojecttask.vo.IotProjectTaskPlatformVO;
-import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportPageReqVO;
-import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportRespVO;
-import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportSaveReqVO;
-import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportStatisticsRespVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.*;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDevicePageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDeviceRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDeviceSimpleRespVO;
@@ -70,6 +67,7 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoUnit;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -205,14 +203,60 @@ public class IotRdDailyReportController {
         if (CollUtil.isNotEmpty(reportFuels)) {
             hasFuels = true;
             dailyReportVO.setReportedFuels(reportFuels);
-        } else {
         }
+        // 查询施工工艺字典数据
+        List<DictDataDO> rdTechniquesDicts = dictDataService.getDictDataListByDictType("rq_iot_project_technology_rd");
+        // 工作量单位 数据字典
+        List<DictDataDO> measureUnitDicts = dictDataService.getDictDataListByDictType("rq_iot_project_measure_unit");
+        // 施工状态 数据字典
+        List<DictDataDO> rdStatusDicts = dictDataService.getDictDataListByDictType("rdStatus");
+        //  施工状态 key字典键值     value字典标签
+        Map<String, String> statusDictPair = new HashMap<>();
+        // 停待状态 的字典数据集合
+        Set<String> tdStatuses = new HashSet<>();
+        //  施工工艺 key字典键值     value字典标签
+        Map<String, String> techniqueDictPair = new HashMap<>();
+        //  工作量单位 key字典键值     value字典标签
+        Map<String, String> unitDictPair = new HashMap<>();
+        if (CollUtil.isNotEmpty(rdTechniquesDicts)) {
+            rdTechniquesDicts.forEach(tech -> {
+                techniqueDictPair.put(tech.getValue(), tech.getLabel());
+            });
+        }
+        if (CollUtil.isNotEmpty(measureUnitDicts)) {
+            measureUnitDicts.forEach(tech -> {
+                unitDictPair.put(tech.getValue(), tech.getLabel());
+            });
+        }
+        if (CollUtil.isNotEmpty(rdStatusDicts)) {
+            rdStatusDicts.forEach(status -> {
+                statusDictPair.put(status.getValue(), status.getLabel());
+                // 找到属于 停待状态 的字典数据
+                if ("TD".equals(status.getRemark())) {
+                    tdStatuses.add(status.getValue());
+                }
+            });
+        }
+
         // 查询日报对应的任务信息
         if (ObjUtil.isNotEmpty(dailyReport.getTaskId())) {
             IotProjectTaskDO task = iotProjectTaskService.getIotProjectTask(dailyReport.getTaskId());
             dailyReportVO.setTaskName(StrUtil.join("-", task.getWellName(), task.getLocation()));
             dailyReportVO.setWellName(task.getWellName());
             dailyReportVO.setLocation(task.getLocation());
+            // 任务施工工艺
+            String techniqueValue = task.getTechnique();
+            if (StrUtil.isNotBlank(techniqueValue)) {
+                dailyReportVO.setTechniqueNames(techniqueDictPair.get(techniqueValue));
+            }
+            // 任务工作量
+            String workloadDesign = task.getWorkloadDesign();
+            // 任务工作量单位
+            String workloadDesignUnit = task.getWorkloadUnit();
+            if (StrUtil.isNotBlank(workloadDesignUnit)) {
+                dailyReportVO.setWorkloadDesign(StrUtil.join(" ", workloadDesign, unitDictPair.get(workloadDesignUnit)));
+            }
+
             Set<Long> deviceIds = task.getDeviceIds();
             Set<Long> responsiblePersonIds = task.getResponsiblePerson();
             // 日报对应的任务中的设备列表
@@ -281,6 +325,85 @@ public class IotRdDailyReportController {
                 List<IotDeviceSimpleRespVO> simpleDevices = BeanUtils.toBean(selectedDevices, IotDeviceSimpleRespVO.class);
                 dailyReportVO.setSelectedDevices(simpleDevices);
             }
+            // 查询相同任务id的日报 查询开工日期 完工日期 施工周期
+            IotRdDailyReportPageReqVO reportReqVO  = new IotRdDailyReportPageReqVO();
+            reportReqVO.setTaskId(dailyReport.getTaskId());
+            List<IotRdDailyReportDO> reports = iotRdDailyReportService.dailyReports(reportReqVO);
+            if (CollUtil.isNotEmpty(reports)) {
+                // 找出开工日期:最早创建时间
+                Optional<LocalDateTime> startDateOpt = reports.stream()
+                        .filter(report -> StrUtil.isNotBlank(report.getRdStatus()))
+                        .map(IotRdDailyReportDO::getCreateTime)
+                        .filter(Objects::nonNull)
+                        .min(LocalDateTime::compareTo);
+                // 找出完工日期:rdStatus='wg' 的日报创建时间(取最早的一个)
+                Optional<LocalDateTime> endDateOpt = reports.stream()
+                        .filter(report -> "wg".equals(report.getRdStatus()))
+                        .map(IotRdDailyReportDO::getCreateTime)
+                        .filter(Objects::nonNull)
+                        .min(LocalDateTime::compareTo);
+                startDateOpt.ifPresent(startDate ->
+                        dailyReportVO.setCommencementDate(LocalDateTimeUtil.format(startDate, DatePattern.NORM_DATE_PATTERN))
+                );
+                endDateOpt.ifPresent(finishDate ->
+                        dailyReportVO.setCompletionDate(LocalDateTimeUtil.format(finishDate, DatePattern.NORM_DATE_PATTERN))
+                );
+                // 计算施工周期(包含开工和完工日期)
+                if (startDateOpt.isPresent() && endDateOpt.isPresent()) {
+                    LocalDateTime startDate = startDateOpt.get();
+                    LocalDateTime endDate = endDateOpt.get();
+
+                    // 将LocalDateTime转换为LocalDate进行计算
+                    LocalDate startLocalDate = startDate.toLocalDate();
+                    LocalDate endLocalDate = endDate.toLocalDate();
+
+                    // 计算天数(包含起始和结束日期)
+                    long daysBetween = ChronoUnit.DAYS.between(startLocalDate, endLocalDate) + 1;
+                    dailyReportVO.setConstructionPeriod(String.valueOf(daysBetween));
+                }
+                // 找到处于停待状态 的日报数据
+                long tdStatusCount = reports.stream()
+                        .filter(report -> StrUtil.isNotBlank(report.getRdStatus()) && tdStatuses.contains(report.getRdStatus()))
+                        .count();
+                dailyReportVO.setIdleTime(String.valueOf(tdStatusCount));
+                // 当前日报关联任务的施工进度
+                if (CollUtil.isNotEmpty(statusDictPair)) {
+                    // 1. 过滤有效数据并排序(按时间升序)
+                    List<IotRdDailyReportDO> validReports = reports.stream()
+                            .filter(report -> StrUtil.isNotBlank(report.getRdStatus())
+                                    && ObjUtil.isNotEmpty(report.getCreateTime())
+                                    && statusDictPair.containsKey(report.getRdStatus()))
+                            .sorted(Comparator.comparing(IotRdDailyReportDO::getCreateTime))
+                            .collect(Collectors.toList());
+                    Map<String, List<IotRdDailyReportDO>> rdStatusReportPair = new HashMap<>();
+                    List<IotRdDailyReportDO> uniqueRdStatusReports = new ArrayList<>();
+                    if (CollUtil.isNotEmpty(validReports)) {
+                        validReports.forEach(report -> {
+                            // 获取集合中的最后一个元素 如果最后一个元素的rdStatus 与当前日报 rdStatus 不同
+                            // 则将 当前report 加入 集合 uniqueRdStatusReports
+                            if (CollUtil.isEmpty(uniqueRdStatusReports)) {
+                                uniqueRdStatusReports.add(report);
+                            } else {
+                                IotRdDailyReportDO lastElement = uniqueRdStatusReports.get(uniqueRdStatusReports.size() - 1);
+                                if (ObjUtil.isNotEmpty(lastElement) && !lastElement.getRdStatus().equals(report.getRdStatus())) {
+                                    uniqueRdStatusReports.add(report);
+                                }
+                            }
+                        });
+                    }
+                    List<IotRdDailyReportTaskProgressVO> progresses = new ArrayList<>();
+                    if (CollUtil.isNotEmpty(uniqueRdStatusReports)) {
+                        uniqueRdStatusReports.forEach(report -> {
+                            IotRdDailyReportTaskProgressVO progress = new IotRdDailyReportTaskProgressVO();
+                            progress.setRdStatusLabel(statusDictPair.get(report.getRdStatus()));
+                            progress.setCreateTime(LocalDateTimeUtil.format(report.getCreateTime(), DatePattern.NORM_DATE_PATTERN));
+                            progresses.add(progress);
+                        });
+                        dailyReportVO.setTaskProgresses(progresses);
+                    }
+                }
+            }
+
             if (CollUtil.isNotEmpty(responsiblePersonIds)) {
                 List<AdminUserRespDTO> users = adminUserApi.getUserList(responsiblePersonIds);
                 String userNames = users.stream()
@@ -330,7 +453,7 @@ public class IotRdDailyReportController {
                                     reportFuel.setZhbdFuel(tempOil);
                                     // 将字符串转换为LocalDateTime
                                     try {
-                                        LocalDateTime queryDate = LocalDate.parse(carFuel.getCreatetime(), formatter).atStartOfDay();;
+                                        LocalDateTime queryDate = LocalDate.parse(yesterdayDateStr, formatter).atStartOfDay();;
                                         reportFuel.setQueryDate(queryDate);
                                     } catch (DateTimeParseException e) {
                                         log.error("瑞都日报-中航北斗油耗-日期格式解析失败: {}", e);
@@ -362,24 +485,7 @@ public class IotRdDailyReportController {
             DeptDO dept = deptService.getDept(dailyReport.getDeptId());
             dailyReportVO.setDeptName(dept.getName());
         }
-        // 查询施工工艺字典数据
-        List<DictDataDO> rdStatusDicts = dictDataService.getDictDataListByDictType("rdStatus");
-        // 查询施工工艺字典数据
-        List<DictDataDO> rdTechniquesDicts = dictDataService.getDictDataListByDictType("rq_iot_project_technology_rd");
-        //  施工状态 key字典键值     value字典标签
-        Map<String, String> statusDictPair = new HashMap<>();
-        //  施工工艺 key字典键值     value字典标签
-        Map<String, String> techniqueDictPair = new HashMap<>();
-        if (CollUtil.isNotEmpty(rdStatusDicts)) {
-            rdStatusDicts.forEach(tech -> {
-                statusDictPair.put(tech.getValue(), tech.getLabel());
-            });
-        }
-        if (CollUtil.isNotEmpty(rdTechniquesDicts)) {
-            rdTechniquesDicts.forEach(tech -> {
-                techniqueDictPair.put(tech.getValue(), tech.getLabel());
-            });
-        }
+
         // 日报任务对应的平台井列表
         if (1 == dailyReport.getPlatformWell()) {
             // 查询未施工完成的平台井 关联井列表
@@ -393,15 +499,19 @@ public class IotRdDailyReportController {
             List<IotRdDailyReportDO> reports = iotRdDailyReportService.dailyReports(reportReqVO);
             // key任务id  value日报id
             Map<Long, Long> reportPair = new HashMap<>();
+            // key任务id  value任务井对应日报当日油耗
+            Map<Long, BigDecimal> reportFuelPair = new HashMap<>();
             // key任务id  value平台井日报已经设置的多种属性
             Map<Long, IotProjectTaskPlatformVO> platformPair = new HashMap<>();
+            Set<Long> reportIds = new HashSet<>();
             if (CollUtil.isNotEmpty(reports)) {
                 reports.forEach(report -> {
+                    reportIds.add(report.getId());
                     // 保养平台井关联的多个日报使用
                     reportPair.put(report.getTaskId(), report.getId());
                     // 设置 平台关联井 已经设置的 属性 施工状态 施工工艺 工作量属性
                     // 查询 平台关联井详情使用
-
+                    reportFuelPair.put(report.getTaskId(), report.getDailyFuel());
                     IotProjectTaskPlatformVO platformVO = new IotProjectTaskPlatformVO();
                     platformVO.setRdStatus(report.getRdStatus());
                     if (StrUtil.isNotBlank(report.getRdStatus()) && statusDictPair.containsKey(report.getRdStatus())) {
@@ -421,6 +531,27 @@ public class IotRdDailyReportController {
                     platformPair.put(report.getTaskId(), platformVO);
                 });
             }
+            // 查询多个日报id 对应的 已经保存的油耗记录
+            IotDailyReportFuelPageReqVO fuelReqVO = new IotDailyReportFuelPageReqVO();
+            fuelReqVO.setType("RD");
+            fuelReqVO.setReportIds(reportIds);
+            List<IotDailyReportFuelDO> relatedReportFuels = iotDailyReportFuelService.getIotDailyReportFuels(fuelReqVO);
+            Map<Long, List<IotDailyReportFuelDO>> reportFuelsPair = new HashMap<>();
+            if (CollUtil.isNotEmpty(relatedReportFuels)) {
+                relatedReportFuels.forEach(fuel -> {
+                    if (reportFuelsPair.containsKey(fuel.getReportId())) {
+                        List<IotDailyReportFuelDO> tempFuels = reportFuelsPair.get(fuel.getReportId());
+                        tempFuels.add(fuel);
+                        reportFuelsPair.put(fuel.getReportId(), tempFuels);
+                    } else {
+                        List<IotDailyReportFuelDO> tempFuels = new ArrayList<>();
+                        tempFuels.add(fuel);
+                        reportFuelsPair.put(fuel.getReportId(), tempFuels);
+                    }
+                });
+            }
+            // 返回已经填报的油耗记录
+
             List<IotProjectTaskPlatformVO> platforms = new ArrayList<>();
             if (CollUtil.isNotEmpty(tasks)) {
                 tasks.forEach(task -> {
@@ -431,6 +562,7 @@ public class IotRdDailyReportController {
                         platform.setWellName(task.getWellName());
                         platform.setWorkloadDesign(task.getWorkloadDesign());
                         platform.setReportId(reportPair.get(task.getId()));
+                        platform.setDailyFuel(reportFuelPair.get(task.getId()));
                         if (platformPair.containsKey(task.getId())) {
                             IotProjectTaskPlatformVO tempPlatform = platformPair.get(task.getId());
                             platform.setRdStatus(tempPlatform.getRdStatus());
@@ -448,6 +580,8 @@ public class IotRdDailyReportController {
                 List<IotProjectTaskPlatformVO> finishedPlatforms = new ArrayList<>();
                 // key任务井id   value任务井名称
                 Map<Long, String> wellNamePair = new HashMap<>();
+                // key任务id  value任务井对应日报当日油耗
+                Map<Long, BigDecimal> finishedReportFuelPair = new HashMap<>();
                 // 查询当前任务关联的平台井任务列表
                 IotProjectTaskPageReqVO currentPlatformTaskReqVO = new IotProjectTaskPageReqVO();
                 currentPlatformTaskReqVO.setPlatformGroup(dailyReport.getTaskPlatform());
@@ -469,9 +603,14 @@ public class IotRdDailyReportController {
                 }
                 if (CollUtil.isNotEmpty(currentRelatedReports)) {
                     currentRelatedReports.forEach(report -> {
+                        if (reportFuelsPair.containsKey(report.getId())) {
+                            List<IotDailyReportFuelDO> finishedReportFuels = reportFuelsPair.get(report.getId());
+                            dailyReportVO.setReportedFuels(finishedReportFuels);
+                        }
                         if (StrUtil.isNotBlank(report.getRdStatus())) {
                             IotProjectTaskPlatformVO platform = new IotProjectTaskPlatformVO();
                             platform.setId(report.getTaskId());
+                            platform.setDailyFuel(report.getDailyFuel());
                             if (wellNamePair.containsKey(report.getTaskId())) {
                                 platform.setWellName(wellNamePair.get(report.getTaskId()));
                             }

+ 12 - 2
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrddailyreport/vo/IotRdDailyReportRespVO.java

@@ -277,10 +277,14 @@ public class IotRdDailyReportRespVO {
     @ExcelProperty("完工日期")
     private String completionDate;
 
-    @Schema(description = "施工周期", example = "125")
-    @ExcelProperty("施工周期")
+    @Schema(description = "施工周期D", example = "125")
+    @ExcelProperty("施工周期D")
     private String constructionPeriod;
 
+    @Schema(description = "停待时间D", example = "12")
+    @ExcelProperty("停待时间D")
+    private String idleTime;
+
     /**
      * 扩展属性
      */
@@ -308,4 +312,10 @@ public class IotRdDailyReportRespVO {
 
     @Schema(description = "日报关联的已经填报的油耗记录")
     private List<IotDailyReportFuelDO> reportedFuels;
+
+    @Schema(description = "工作量", example = "1 井次")
+    private String workloadDesign;
+
+    @Schema(description = "任务施工进度", example = "施工准备2025-12-01 -> 施工2025-12-02")
+    private List<IotRdDailyReportTaskProgressVO> taskProgresses;
 }

+ 15 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrddailyreport/vo/IotRdDailyReportTaskProgressVO.java

@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 瑞都日报 任务施工进度 Response VO")
+@Data
+public class IotRdDailyReportTaskProgressVO {
+
+    @Schema(description = "施工进度", example = "施工")
+    private String rdStatusLabel;
+
+    @Schema(description = "日期")
+    private String createTime;
+}

+ 9 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrhdailyreport/IotRhDailyReportController.java

@@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotdailyreportfuel.vo.IotDai
 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.IotRdDailyReportPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportSaveReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrhdailyreport.vo.*;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreport.vo.IotRyDailyReportPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreport.vo.IotRyDailyReportStatisticsVO;
@@ -94,6 +95,14 @@ public class IotRhDailyReportController {
         return success(true);
     }
 
+    @PutMapping("/approval")
+    @Operation(summary = "审批日报")
+    @PreAuthorize("@ss.hasPermission('pms:iot-rh-daily-report:update')")
+    public CommonResult<Boolean> approvalRhDailyReport(@Valid @RequestBody IotRdDailyReportSaveReqVO updateReqVO) {
+        iotRhDailyReportService.approvalRhDailyReport(updateReqVO);
+        return success(true);
+    }
+
     @DeleteMapping("/delete")
     @Operation(summary = "删除瑞恒日报")
     @Parameter(name = "id", description = "编号", required = true)

+ 3 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrhdailyreport/vo/IotRhDailyReportRespVO.java

@@ -157,6 +157,9 @@ public class IotRhDailyReportRespVO {
     @Schema(description = "审批状态 未提交、审批中、审批通过、审批不通过、已取消", example = "1")
     private Integer auditStatus;
 
+    @Schema(description = "审批意见", example = "同意")
+    private String opinion;
+
     /**
      * 扩展字段
      */

+ 3 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotrhdailyreport/vo/IotRhDailyReportSaveReqVO.java

@@ -97,6 +97,9 @@ public class IotRhDailyReportSaveReqVO {
     @Schema(description = "审批状态 未提交、审批中、审批通过、审批不通过、已取消", example = "1")
     private Integer auditStatus;
 
+    @Schema(description = "审批意见", example = "同意")
+    private String opinion;
+
     /**
      * 扩展字段
      */

+ 24 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/vo/IotCarDevicePlateVO.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.pms.controller.admin.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 设备台账 车辆设备 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class IotCarDevicePlateVO {
+
+    @Schema(description = "设备id")
+    private Long deviceId;
+
+    @Schema(description = "资产编码")
+    private String deviceCode;
+
+    @Schema(description = "设备名称")
+    private String deviceName;
+
+    @Schema(description = "规格型号")
+    private String licensePlate;
+
+}

+ 5 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/dataobject/iotrhdailyreport/IotRhDailyReportDO.java

@@ -146,4 +146,9 @@ public class IotRhDailyReportDO extends BaseDO {
      */
     private Integer auditStatus;
 
+    /**
+     * 审批意见
+     */
+    private String opinion;
+
 }

+ 3 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/IotDeviceMapper.java

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
 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.iotmainworkorder.vo.IotMainWorkOrderPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.vo.IotCarDevicePlateVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDevicePageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDeviceRespVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
@@ -264,6 +265,8 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
 
     IPage<IotDeviceRespVO> deviceAssociateBomListPage(IPage<IotDeviceRespVO> page,
                                                       @Param("deviceIds") Collection<Long> deviceIds, @Param("bomFlag") String bomFlag);
+
+    List<IotCarDevicePlateVO> allCarsContainsPlates();
     default Long selectCountByTimeAndStatus(IotDevicePageReqVO reqVO) {
         return selectCount(new LambdaQueryWrapperX<IotDeviceDO>()
                 .eqIfPresent(IotDeviceDO::getDeviceStatus, reqVO.getDeviceStatus())

+ 5 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotcarzhbd/IotCarZhbdMapper.java

@@ -1,8 +1,10 @@
 package cn.iocoder.yudao.module.pms.dal.mysql.iotcarzhbd;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.pms.controller.admin.iotcarzhbd.vo.IotCarZhbdPageReqVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotcarzhbd.IotCarZhbdDO;
 import org.apache.ibatis.annotations.Mapper;
@@ -28,12 +30,15 @@ public interface IotCarZhbdMapper extends BaseMapperX<IotCarZhbdDO> {
                 .orderByDesc(IotCarZhbdDO::getId));
     }
 
+    @DataPermission(enable = false)
+    @TenantIgnore
     default List<IotCarZhbdDO> selectList(IotCarZhbdPageReqVO reqVO) {
         return selectList(new LambdaQueryWrapperX<IotCarZhbdDO>()
                 .eqIfPresent(IotCarZhbdDO::getDeviceCode, reqVO.getDeviceCode())
                 .inIfPresent(IotCarZhbdDO::getDeviceCode, reqVO.getDeviceCodes())
                 .likeIfPresent(IotCarZhbdDO::getDeviceName, reqVO.getDeviceName())
                 .eqIfPresent(IotCarZhbdDO::getCarId, reqVO.getCarId())
+                .inIfPresent(IotCarZhbdDO::getCarId, reqVO.getCarIds())
                 .betweenIfPresent(IotCarZhbdDO::getCreateTime, reqVO.getCreateTime())
                 .likeIfPresent(IotCarZhbdDO::getUserName, reqVO.getUserName())
                 .inIfPresent(IotCarZhbdDO::getUserName, reqVO.getCustomDeviceCodes())

+ 1 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotdailyreportfuel/IotDailyReportFuelMapper.java

@@ -38,6 +38,7 @@ public interface IotDailyReportFuelMapper extends BaseMapperX<IotDailyReportFuel
         return selectList(new LambdaQueryWrapperX<IotDailyReportFuelDO>()
                 .eqIfPresent(IotDailyReportFuelDO::getType, reqVO.getType())
                 .eqIfPresent(IotDailyReportFuelDO::getReportId, reqVO.getReportId())
+                .inIfPresent(IotDailyReportFuelDO::getReportId, reqVO.getReportIds())
                 .eqIfPresent(IotDailyReportFuelDO::getDeviceId, reqVO.getDeviceId())
                 .eqIfPresent(IotDailyReportFuelDO::getDeviceCode, reqVO.getDeviceCode())
                 .eqIfPresent(IotDailyReportFuelDO::getYfDeviceCode, reqVO.getYfDeviceCode())

+ 0 - 42
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotrydailyreport/IotRyDailyReportMapper.java

@@ -28,48 +28,6 @@ import java.util.Objects;
 @Mapper
 public interface IotRyDailyReportMapper extends BaseMapperX<IotRyDailyReportDO> {
 
-    /* default PageResult<IotRyDailyReportDO> selectPage(IotRyDailyReportPageReqVO reqVO) {
-        // 获取查询参数中的projectClassification值
-        String projectClassification = reqVO.getProjectClassification();
-        return selectPage(reqVO, new LambdaQueryWrapperX<IotRyDailyReportDO>()
-                .eqIfPresent(IotRyDailyReportDO::getDeptId, reqVO.getDeptId())
-                .eqIfPresent(IotRyDailyReportDO::getProjectId, reqVO.getProjectId())
-                .eqIfPresent(IotRyDailyReportDO::getTaskId, reqVO.getTaskId())
-                // .eqIfPresent(IotRyDailyReportDO::getProjectClassification, reqVO.getProjectClassification())
-                .eqIfPresent(IotRyDailyReportDO::getRelocationDays, reqVO.getRelocationDays())
-                .betweenIfPresent(IotRyDailyReportDO::getLatestWellDoneTime, reqVO.getLatestWellDoneTime())
-                .eqIfPresent(IotRyDailyReportDO::getCurrentDepth, reqVO.getCurrentDepth())
-                .eqIfPresent(IotRyDailyReportDO::getDailyFootage, reqVO.getDailyFootage())
-                .eqIfPresent(IotRyDailyReportDO::getMonthlyFootage, reqVO.getMonthlyFootage())
-                .eqIfPresent(IotRyDailyReportDO::getAnnualFootage, reqVO.getAnnualFootage())
-                .eqIfPresent(IotRyDailyReportDO::getDailyPowerUsage, reqVO.getDailyPowerUsage())
-                .eqIfPresent(IotRyDailyReportDO::getMonthlyPowerUsage, reqVO.getMonthlyPowerUsage())
-                .eqIfPresent(IotRyDailyReportDO::getDailyFuel, reqVO.getDailyFuel())
-                .eqIfPresent(IotRyDailyReportDO::getMonthlyFuel, reqVO.getMonthlyFuel())
-                .betweenIfPresent(IotRyDailyReportDO::getNonProductionTime, reqVO.getNonProductionTime())
-                .eqIfPresent(IotRyDailyReportDO::getRyNptReason, reqVO.getRyNptReason())
-                .betweenIfPresent(IotRyDailyReportDO::getConstructionStartDate, reqVO.getConstructionStartDate())
-                .betweenIfPresent(IotRyDailyReportDO::getConstructionEndDate, reqVO.getConstructionEndDate())
-                .eqIfPresent(IotRyDailyReportDO::getProductionStatus, reqVO.getProductionStatus())
-                .eqIfPresent(IotRyDailyReportDO::getNextPlan, reqVO.getNextPlan())
-                .eqIfPresent(IotRyDailyReportDO::getRigStatus, reqVO.getRigStatus())
-                .eqIfPresent(IotRyDailyReportDO::getPersonnel, reqVO.getPersonnel())
-                .eqIfPresent(IotRyDailyReportDO::getMudDensity, reqVO.getMudDensity())
-                .eqIfPresent(IotRyDailyReportDO::getMudViscosity, reqVO.getMudViscosity())
-                .eqIfPresent(IotRyDailyReportDO::getLateralLength, reqVO.getLateralLength())
-                .eqIfPresent(IotRyDailyReportDO::getWellInclination, reqVO.getWellInclination())
-                .eqIfPresent(IotRyDailyReportDO::getAzimuth, reqVO.getAzimuth())
-                .eqIfPresent(IotRyDailyReportDO::getExtProperty, reqVO.getExtProperty())
-                .eqIfPresent(IotRyDailyReportDO::getSort, reqVO.getSort())
-                .eqIfPresent(IotRyDailyReportDO::getRemark, reqVO.getRemark())
-                .eqIfPresent(IotRyDailyReportDO::getStatus, reqVO.getStatus())
-                .eqIfPresent(IotRyDailyReportDO::getProcessInstanceId, reqVO.getProcessInstanceId())
-                .eqIfPresent(IotRyDailyReportDO::getAuditStatus, reqVO.getAuditStatus())
-                .betweenIfPresent(IotRyDailyReportDO::getCreateTime, reqVO.getCreateTime())
-                .orderByDesc(IotRyDailyReportDO::getCreateTime)
-                .orderByAsc(IotRyDailyReportDO::getId));
-    } */
-
     default List<IotRyDailyReportDO> dailyReports(IotRyDailyReportPageReqVO reqVO) {
         // 获取查询参数中的 projectClassification 值
         String projectClassification = reqVO.getProjectClassification();

+ 90 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/IotAllZhbdJob.java

@@ -0,0 +1,90 @@
+package cn.iocoder.yudao.module.pms.job;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
+import cn.iocoder.yudao.module.pms.controller.admin.iotcarzhbd.vo.IotCarZhbdPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.vo.IotCarDevicePlateVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotcarzhbd.IotCarZhbdDO;
+import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
+import cn.iocoder.yudao.module.pms.dal.mysql.iotcarzhbd.IotCarZhbdMapper;
+import cn.iocoder.yudao.module.pms.util.ZHBDUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * 根据设备表中的 车牌号 对应的 中航北斗的 vehicle_id carId
+ */
+@Component
+@Slf4j
+public class IotAllZhbdJob implements JobHandler {
+
+    @Autowired
+    private IotDeviceMapper iotDeviceMapper;
+    @Autowired
+    private ZHBDUtil zhbdUtil;
+
+    @Autowired
+    private IotCarZhbdMapper iotCarZhbdMapper;
+
+    @Override
+    @TenantIgnore
+    public String execute(String param) throws Exception {
+        // 查询设备表中 所有设备名称包含车牌号的 车辆
+        List<IotCarDevicePlateVO> carContainsPlates = iotDeviceMapper.allCarsContainsPlates();
+        Set<String> uniquePlates = new HashSet<>();
+        // key车牌号   value车辆对象(设备编码、设备名称、设备id)
+        Map<String, IotCarDevicePlateVO> plateDevicePair = new HashMap<>();
+        if (CollUtil.isNotEmpty(carContainsPlates)) {
+            carContainsPlates.forEach(plate -> {
+                uniquePlates.add(plate.getLicensePlate());
+                plateDevicePair.put(plate.getLicensePlate(), plate);
+            });
+            // 将集合中的车辆牌号拼接成逗号分隔的字符串
+            if (CollUtil.isNotEmpty(uniquePlates)) {
+                // 查询出 rq_iot_car_zhbd 表中已经存在的所有 carId
+                IotCarZhbdPageReqVO reqVO = new IotCarZhbdPageReqVO();
+                List<IotCarZhbdDO> existCars = iotCarZhbdMapper.selectList(reqVO);
+                Set<String> existCarIds = new HashSet<>();
+                if (CollUtil.isNotEmpty(existCars)) {
+                    existCars.forEach(car -> {
+                        existCarIds.add(car.getCarId());
+                    });
+                }
+
+                String uniquePlatesStr = StrUtil.join(",", uniquePlates);
+                log.info("逗号分隔的车辆号:{}", uniquePlatesStr);
+                // key车牌号   value中航北斗carId
+                Map<String, Integer> carLocationPair = zhbdUtil.carLocations(uniquePlatesStr);
+                // 将接口返回的 在本地表不存在的车辆信息保存
+                List<IotCarZhbdDO> tobeAddedCars = new ArrayList<>();
+                // 筛选出 中航北斗 carId 不在表 rq_iot_car_zhbd 中存在的记录
+                if (CollUtil.isNotEmpty(carLocationPair)) {
+                    carLocationPair.forEach((plateNum, carId) -> {
+                        if (!existCarIds.contains(String.valueOf(carId))) {
+                            IotCarZhbdDO car = new IotCarZhbdDO();
+                            car.setCarId(String.valueOf(carId));
+                            if (plateDevicePair.containsKey(plateNum)) {
+                                IotCarDevicePlateVO carDevice = plateDevicePair.get(plateNum);
+                                if (ObjUtil.isNotEmpty(carDevice)) {
+                                    car.setDeviceCode(carDevice.getDeviceCode());
+                                    car.setDeviceName(carDevice.getDeviceName());
+                                }
+                            }
+                            tobeAddedCars.add(car);
+                        }
+                    });
+                    if (CollUtil.isNotEmpty(tobeAddedCars)) {
+                        iotCarZhbdMapper.insertBatch(tobeAddedCars);
+                    }
+                }
+            }
+        }
+        return "success";
+    }
+}

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

@@ -0,0 +1,158 @@
+package cn.iocoder.yudao.module.pms.job.dailyreport;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
+import cn.iocoder.yudao.module.pms.controller.admin.depttype.vo.IotDeptTypePageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreport.vo.IotRyDailyReportPageReqVO;
+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.iotrydailyreport.IotRyDailyReportDO;
+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.iotrydailyreport.IotRyDailyReportMapper;
+import cn.iocoder.yudao.module.pms.message.PmsMessage;
+import cn.iocoder.yudao.module.pms.service.iotdeviceperson.IotDevicePersonService;
+import cn.iocoder.yudao.module.pms.service.iotmainworkorderbom.IotMainWorkOrderBomService;
+import cn.iocoder.yudao.module.pms.service.iotprojecttask.IotProjectTaskService;
+import cn.iocoder.yudao.module.pms.service.iotrydailyreport.IotRyDailyReportService;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
+import cn.iocoder.yudao.module.system.service.dept.DeptService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;
+
+/**
+ * 瑞鹰挂靠队伍 生成日报
+ */
+@Component
+@Slf4j
+public class CreateRyDailyReportOrderJob implements JobHandler {
+    @Resource
+    private IotDevicePersonService iotDevicePersonService;
+    @Resource
+    private IotRyDailyReportMapper iotRyDailyReportMapper;
+    @Autowired
+    private IotRyDailyReportService iotRyDailyReportService;
+
+    @Autowired
+    private IotDeviceMapper iotDeviceMapper;
+    @Resource
+    private IotProjectTaskService iotProjectTaskService;
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private IotMainWorkOrderBomService iotMainWorkOrderBomService;
+    @Resource
+    private IotDeptTypeMapper iotDeptTypeMapper;
+    @Resource(name = PMS_THREAD_POOL_TASK_EXECUTOR)
+    private ThreadPoolTaskExecutor pmsThreadPoolTaskExecutor;
+    @Resource
+    private PmsMessage pmsMessage;
+    @Resource
+    private DeptService deptService;
+
+    @Override
+    @TenantIgnore
+    public String execute(String param) throws Exception {
+
+        // 查询瑞鹰下所有部门 筛选出 修井 队伍(不包含任何设备的队伍)
+        IotDeptTypePageReqVO deptTypeReqVO = new IotDeptTypePageReqVO();
+        // 查询 钻井 修井 类型的部门
+        deptTypeReqVO.setType("xj");
+        List<IotDeptTypeDO> deptTypes = iotDeptTypeMapper.selectList(deptTypeReqVO);
+        Set<Long> xjDeptIds = new HashSet<>();
+        if (CollUtil.isNotEmpty(deptTypes)) {
+            deptTypes.forEach(deptType -> {
+                xjDeptIds.add(deptType.getDeptId());
+            });
+        }
+        // 查询瑞鹰下所有队伍
+        Set<Long> ryTeamDeptIds = new HashSet<>();
+        // 查询瑞鹰所有 队伍 项目部
+        Set<Long> ryChildDeptIds = deptService.getChildDeptIdListFromCache(158l);
+        List<DeptDO> depts =deptService.getDeptList(ryChildDeptIds);
+        if (CollUtil.isNotEmpty(depts)) {
+            depts.forEach(dept -> {
+                if ("3".equals(dept.getType())) {
+                    ryTeamDeptIds.add(dept.getId());
+                }
+            });
+        }
+        // 查询瑞鹰所有队伍关联的设备 筛选出不包含任何设备的队伍
+        IotDevicePageReqVO reqVO = new IotDevicePageReqVO();
+        reqVO.setDeptIds(new ArrayList<>(ryTeamDeptIds));
+        List<IotDeviceDO> devices = iotDeviceMapper.selectListAlone(reqVO);
+        // 包含设备的部门id集合
+        Set<Long> containsDeviceDeptIds = new HashSet<>();
+        if (CollUtil.isNotEmpty(devices)) {
+            devices.forEach(device -> {
+                containsDeviceDeptIds.add(device.getDeptId());
+            });
+        }
+        // 不包含任何设备的队伍id集合
+        Set<Long> noDeviceXjDeptIds = xjDeptIds.stream()
+                .filter(deptId -> !containsDeviceDeptIds.contains(deptId))
+                .collect(Collectors.toSet());
+
+        // 查询无设备修井队伍日报列表
+        IotRyDailyReportPageReqVO ryReqVO = new IotRyDailyReportPageReqVO();
+        ryReqVO.setDeptIds(noDeviceXjDeptIds);
+        ryReqVO.setProjectClassification("2");
+        List<IotRyDailyReportDO> ryDailyReports = iotRyDailyReportMapper.dailyReports(ryReqVO);
+
+        // 查询所有瑞都的项目任务 以‘部门-时间’ 为唯一键 当天创建时间 内没有生成过日报 就自动生成
+        IotRdDailyReportPageReqVO pageReqVO = new IotRdDailyReportPageReqVO();
+        LocalDateTime currentDate = LocalDateTime.now();
+        String currentFormatDateStr = LocalDateTimeUtil.format(currentDate, "yyyy-MM-dd");
+        // 前一天的日期
+        LocalDateTime yesterday = currentDate.minusDays(1);
+        // 当天已经生成日报的修井队伍deptId集合
+        Set<Long> currentDayReportedDeptIds = new HashSet<>();
+        if (CollUtil.isNotEmpty(ryDailyReports)) {
+            ryDailyReports.forEach(report -> {
+                // 查询当天生成的日报
+                LocalDateTime createDate = report.getCreateTime();
+                String formatDateStr = LocalDateTimeUtil.format(createDate, "yyyy-MM-dd");
+                if (formatDateStr.equals(currentFormatDateStr)) {
+                    currentDayReportedDeptIds.add(report.getTaskId());
+                }
+            });
+        }
+
+        // 如果当天的修井日报记录中不包含 无设备的修井队伍部门id 则生成修井队伍日报记录
+        Set<Long> tobeReportXjDeptIds = noDeviceXjDeptIds.stream()
+                .filter(deptId -> !currentDayReportedDeptIds.contains(deptId))
+                .collect(Collectors.toSet());
+
+        if (CollUtil.isNotEmpty(tobeReportXjDeptIds)) {
+            List<IotRyDailyReportDO> tobeAddedReports = new ArrayList<>();
+            tobeReportXjDeptIds.forEach(deptId -> {
+                IotRyDailyReportDO noDeviceReport = new IotRyDailyReportDO();
+                noDeviceReport.setDeptId(deptId);
+                noDeviceReport.setProjectClassification("2");
+                tobeAddedReports.add(noDeviceReport);
+            });
+            if (CollUtil.isNotEmpty(tobeAddedReports)) {
+                iotRyDailyReportMapper.insertBatch(tobeAddedReports);
+            }
+        }
+
+        return "创建成功";
+    }
+}

+ 4 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/httpEntity/ResponseOilData.java

@@ -13,4 +13,8 @@ import java.util.Map;
 public class ResponseOilData {
     public List<Map<String,Object>> pageList;
     public long total;
+    // 查询车辆位置返回的数据集合
+    public List<Map<String,Object>> data;
+    // 时间戳
+    public long time;
 }

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

@@ -513,6 +513,7 @@ public class IotRdDailyReportServiceImpl implements IotRdDailyReportService {
 
             dailyReports.forEach(report -> {
                 BigDecimal dailyFuel = report.getDailyFuel();
+                dailyFuel = ObjUtil.isEmpty(dailyFuel) ? BigDecimal.ZERO : dailyFuel;
                 if (taskFuelPair.containsKey(report.getTaskId())) {
                     BigDecimal existTotalFuel = taskFuelPair.get(report.getTaskId());
                     BigDecimal tempTotalFuel = existTotalFuel.add(dailyFuel);

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

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.pms.service.iotrhdailyreport;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportSaveReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrhdailyreport.vo.*;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotrhdailyreport.IotRhDailyReportDO;
 
@@ -125,4 +126,11 @@ public interface IotRhDailyReportService {
      * @return
      */
     List<IotRhDailyReportPolylineVO> polylineStatistics(IotRhDailyReportPageReqVO pageReqVO);
+
+    /**
+     * 审批瑞恒日报
+     *
+     * @param updateReqVO 更新信息
+     */
+    void approvalRhDailyReport(IotRdDailyReportSaveReqVO updateReqVO);
 }

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

@@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.pms.controller.admin.iotcarzhbd.vo.IotCarZhbdPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotdevicecategorytemplateattrs.vo.IotDeviceProperty;
 import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttask.vo.IotProjectTaskPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotrddailyreport.vo.IotRdDailyReportSaveReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrhdailyreport.vo.*;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDevicePageReqVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
@@ -28,17 +29,28 @@ import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttask.IotProjectTaskMapper
 import cn.iocoder.yudao.module.pms.dal.mysql.iotrhdailyreport.IotRhDailyReportMapper;
 import cn.iocoder.yudao.module.pms.enums.common.FailureAuditStatusEnum;
 import cn.iocoder.yudao.module.pms.job.IotDeviceCarFuelVO;
+import cn.iocoder.yudao.module.pms.message.PmsMessage;
 import cn.iocoder.yudao.module.pms.util.ZHBDUtil;
+import cn.iocoder.yudao.module.supplier.enums.common.SupplierAuditStatusEnum;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
+import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
 import cn.iocoder.yudao.module.system.service.dept.DeptService;
 import cn.iocoder.yudao.module.system.service.dict.DictTypeService;
+import cn.iocoder.yudao.module.system.service.permission.RoleService;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
 import lombok.extern.log4j.Log4j;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -53,11 +65,13 @@ import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicReference;
 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.*;
+import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;
 
 /**
  * 瑞恒日报 Service 实现类
@@ -87,20 +101,36 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
     private DeptService deptService;
     @Resource
     private IotCarZhbdMapper iotCarZhbdMapper;
-
+    @Resource
+    private AdminUserService adminUserService;
     @Resource
     private IotDailyReportFuelMapper iotDailyReportFuelMapper;
+    @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
     @Transactional(rollbackFor = Exception.class)
     public Long createIotRhDailyReport(IotRhDailyReportSaveReqVO createReqVO) {
         // 插入
         IotRhDailyReportDO iotRhDailyReport = BeanUtils.toBean(createReqVO, IotRhDailyReportDO.class);
+        // 设置 日报填写状态 审核状态
+        iotRhDailyReport.setStatus(1);
+        // 设置 日报审批状态为 审批中 auditStatus = 10
+        iotRhDailyReport.setAuditStatus(10);
         LocalDateTime reportDate = createReqVO.getFillOrderCreateTime();
         if (ObjUtil.isEmpty(reportDate)) {
             throw exception(IOT_RH_DAILY_REPORT_NO_DATE);
         }
-        if (ObjUtil.isEmpty(createReqVO.getDeptId())) {
+        Long deptId = createReqVO.getDeptId();
+        if (ObjUtil.isEmpty(deptId)) {
             throw exception(IOT_RH_DAILY_REPORT_NO_DEPT);
         }
         // 根据日报的 施工队伍 deptId 查询队伍所在的 项目 任务
@@ -117,12 +147,15 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
         Map<String, IotDeviceDO> carIdDevicePair = new HashMap<>();
         // 中航北斗车辆id集合
         Set<String> zhbdCarIds = new HashSet<>();
+        // 井号
+        String wellName = StrUtil.EMPTY;
         if (CollUtil.isNotEmpty(tasks)) {
             IotProjectTaskDO task = tasks.get(0);
             // 暂时只考虑1个施工队伍只属于1个任务
             iotRhDailyReport.setProjectId(task.getProjectId());
             iotRhDailyReport.setTaskId(task.getId());
             taskId = task.getId();
+            wellName = task.getWellName();
             // 根据日报状态 查询 日报所属任务的状态
             if (ObjUtil.isNotEmpty(createReqVO.getConstructionStatus())) {
                 task.setStatus(createReqVO.getConstructionStatus());
@@ -264,7 +297,7 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
                                 reportFuel.setZhbdFuel(tempOil);
                                 // 将字符串转换为LocalDateTime
                                 try {
-                                    LocalDateTime queryDate = LocalDate.parse(carFuel.getCreatetime(), formatter).atStartOfDay();;
+                                    LocalDateTime queryDate = LocalDate.parse(yesterdayDateStr, formatter).atStartOfDay();;
                                     reportFuel.setQueryDate(queryDate);
                                 } catch (DateTimeParseException e) {
                                     log.error("瑞恒日报-中航北斗油耗-日期格式解析失败: {}", e);
@@ -292,6 +325,69 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
             iotRhDailyReport.setId(existReport.getId());
             iotRhDailyReportMapper.updateById(iotRhDailyReport);
         }
+        // 只要保存了记录就要发送审批提醒到 提交人对应的项目经理 角色 项目部日报审批RH
+        // 根据日报的 deptId 查询上级项目部经理 角色的审批人 发送站内信 钉钉提醒
+        DeptDO dept = deptService.getDept(deptId);
+        Long parentId = dept.getParentId();
+        if (ObjUtil.isNotEmpty(parentId)) {
+            DeptDO projectDept = deptService.getDept(parentId);
+            if (ObjUtil.isNotEmpty(projectDept) && "2".equals(projectDept.getType())) {
+                // 查询项目部下具有 项目部日报审批RH 角色的人员
+                Set<Long> projectIds = new HashSet<>();
+                projectIds.add(projectDept.getId());
+                List<AdminUserDO> receivedMsgUsers = adminUserService.getUserListByDeptIds(projectIds);
+                if (CollUtil.isNotEmpty(receivedMsgUsers)) {
+                    Set<Long> userIds = new HashSet<>();
+                    receivedMsgUsers.forEach(user -> {
+                        userIds.add(user.getId());
+                    });
+                    RoleDO role = roleService.getRoleByCode("项目部日报审批RH");
+                    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)) {
+                                    // 生成消息提醒标题 部门名称-井号
+                                    String msgTitle = dept.getName();
+                                    if (StrUtil.isNotBlank(wellName)) {
+                                        msgTitle = StrUtil.join("-", msgTitle, wellName);
+                                    }
+                                    CountDownLatch latch = new CountDownLatch(users.size());
+                                    String finalMsgTitle = msgTitle;
+                                    users.forEach((userId, user) -> {
+                                        pmsThreadPoolTaskExecutor.execute(() -> {
+                                            try {
+                                                String mobile = user.getMobile();
+                                                if (StrUtil.isNotBlank(mobile) && StrUtil.isNotBlank(finalMsgTitle)) {
+                                                    /* pmsMessage.sendMessage(iotRhDailyReport.getId(), finalMsgTitle, PmsConstants.RH_DAILY_REPORT_APPROVAL,
+                                                            userId, mobile); */
+                                                }
+                                            } finally {
+                                                latch.countDown();
+                                            }
+                                        });
+                                    });
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
         return iotRhDailyReport.getId();
     }
 
@@ -451,6 +547,23 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
         return result;
     }
 
+    @Override
+    public void approvalRhDailyReport(IotRdDailyReportSaveReqVO updateReqVO) {
+        // 审核瑞恒日报
+        // 如果审批拒绝 修改日报 填报状态 status = 0 填写中  审批状态 auditStatus = 30
+        // 审批通过 设置 日报审批状态 auditStatus = 20
+        IotRhDailyReportDO dailyReport = validateIotRhDailyReportExists(updateReqVO.getId());
+        dailyReport.setAuditStatus(updateReqVO.getAuditStatus());
+        dailyReport.setOpinion(updateReqVO.getOpinion());
+        if (SupplierAuditStatusEnum.REJECT.getStatus().equals(updateReqVO.getAuditStatus())) {
+            dailyReport.setStatus(0);
+        } else {
+
+        }
+        // 更新瑞恒日报审核状态
+        iotRhDailyReportMapper.updateById(dailyReport);
+    }
+
     @Override
     public void updateIotRhDailyReport(IotRhDailyReportSaveReqVO updateReqVO) {
         // 校验存在
@@ -470,10 +583,12 @@ public class IotRhDailyReportServiceImpl implements IotRhDailyReportService {
         iotRhDailyReportMapper.deleteById(id);
     }
 
-    private void validateIotRhDailyReportExists(Long id) {
-        if (iotRhDailyReportMapper.selectById(id) == null) {
+    private IotRhDailyReportDO validateIotRhDailyReportExists(Long id) {
+        IotRhDailyReportDO dailyReport = iotRhDailyReportMapper.selectById(id);
+        if (ObjUtil.isEmpty(dailyReport)) {
             throw exception(IOT_RH_DAILY_REPORT_NOT_EXISTS);
         }
+        return dailyReport;
     }
 
     @Override

+ 68 - 8
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/util/ZHBDUtil.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.pms.util;
 
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.module.pms.job.IotDeviceCarFuelVO;
 import cn.iocoder.yudao.module.pms.job.httpEntity.ApiOilResponse;
@@ -37,7 +38,12 @@ public class ZHBDUtil {
     private static final String ID = "NjI1LjUyNS42ODg";
     private static final String SECRET = "3897865b70d7bf29fcca5029147f7d0a";
     private static final String TOKEN_URL = "https://zhbdgps.cn/video/webapi/user/login";
-    private static final String LOCATION_URL="https://zhbdgps.cn/video/webapi/report/fuel/daily/detail";
+    // 日报表明细
+    private static final String LOCATION_URL = "https://zhbdgps.cn/video/webapi/report/fuel/daily/detail";
+    // 日报表统计
+    private static final String DAILY_FUEL_STATISTICS_URL = "https://zhbdgps.cn/video/webapi/report/fuel/daily/statistics";
+    // 查询位置(车牌号)
+    private static final String GET_LOCATION_USE_PLATES_URL = "https://zhbdgps.cn/video/webapi/location/get-location-use-plates";
     private static final String parameter = "lng,lat,today_distance,distance,todayoil,totaloil,online,oil1,oil2,oil3,oil4,vehicle_name";
 
     @Autowired
@@ -53,7 +59,8 @@ public class ZHBDUtil {
      */
     public Map<Integer, IotDeviceCarFuelVO> carFuels(String carIds, String startDate, String endDate) throws Exception {
         String zhbdtoken = redisTemplate.opsForValue().get("zhbdtoken");
-        if (StrUtil.isBlank(zhbdtoken)) {
+        String clientId = redisTemplate.opsForValue().get("zhdbClientId");
+        if (StrUtil.isBlank(zhbdtoken) || StrUtil.isBlank(clientId)) {
             Map<String, String> map = new HashMap<>();
             map.put("id", ID);
             map.put("secret", SECRET);
@@ -61,12 +68,15 @@ public class ZHBDUtil {
             if (Objects.nonNull(entries)&&entries.get("code") != null&&Integer.parseInt(String.valueOf(entries.get("code")))==200) {
                 JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(entries.get("data")));
                 zhbdtoken = jsonObject.get("token").toString();
+                // 缓存 client_id
+                clientId = jsonObject.get("client_id").toString();
                 redisTemplate.opsForValue().set("zhbdtoken", zhbdtoken, 80000, TimeUnit.SECONDS);
+                redisTemplate.opsForValue().set("zhdbClientId", clientId, 80000, TimeUnit.SECONDS);
             }
         }
         // 构建请求JSON
         Map<String, Object> requestMap = new HashMap<>();
-        requestMap.put("client_ids", ID);
+        requestMap.put("client_ids", clientId);
         requestMap.put("vehicle_ids", carIds);
         requestMap.put("begin_time", startDate);
         requestMap.put("end_time", endDate);
@@ -76,7 +86,7 @@ public class ZHBDUtil {
         Map<Integer, IotDeviceCarFuelVO> carOilFuelPair = new HashMap<>();
         if (StrUtil.isNotBlank(zhbdtoken)) {
             try {
-                String jsonResponse = sendPostRequest(requestMap, zhbdtoken);
+                String jsonResponse = sendPostRequest(requestMap, zhbdtoken, DAILY_FUEL_STATISTICS_URL);
                 ApiOilResponse response = parseResponse(jsonResponse);
                 if (response != null && response.code == 200) {
                     List<Map<String, Object>> details = response.data.pageList;
@@ -86,11 +96,11 @@ public class ZHBDUtil {
                         Integer carId = Integer.valueOf(detail.get("vehicle_id").toString());
                         Double oil = Double.valueOf(detail.get("oil").toString());
                         String vehicleName = String.valueOf(detail.get("vehicle_name"));
-                        String createtime = String.valueOf(detail.get("createtime"));
+                        // String createtime = String.valueOf(detail.get("createtime"));
                         carFuel.setVehicleId(carId);
                         carFuel.setVehicleName(vehicleName);
                         carFuel.setOil(oil);
-                        carFuel.setCreatetime(createtime);
+                        // carFuel.setCreatetime(createtime);
                         carOilFuelPair.put(carId, carFuel);
                     }
                 }
@@ -101,7 +111,57 @@ public class ZHBDUtil {
         return carOilFuelPair;
     }
 
-    private String sendPostRequest(Map<String, Object> requestData,String token) throws IOException {
+    /**
+     * 获取车辆位置 主要是获取车辆的 carId
+     * @param plates 中航北斗 车牌号 逗号分隔
+     * @return
+     * @throws Exception
+     */
+    public Map<String, Integer> carLocations(String plates) throws Exception {
+        String zhbdtoken = redisTemplate.opsForValue().get("zhbdtoken");
+        if (StrUtil.isBlank(zhbdtoken)) {
+            Map<String, String> map = new HashMap<>();
+            map.put("id", ID);
+            map.put("secret", SECRET);
+            JSONObject entries = restTemplate.postForObject(TOKEN_URL, map, JSONObject.class);
+            if (Objects.nonNull(entries)&&entries.get("code") != null&&Integer.parseInt(String.valueOf(entries.get("code")))==200) {
+                JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(entries.get("data")));
+                zhbdtoken = jsonObject.get("token").toString();
+                redisTemplate.opsForValue().set("zhbdtoken", zhbdtoken, 80000, TimeUnit.SECONDS);
+            }
+        }
+        // 构建请求JSON
+        Map<String, Object> requestMap = new HashMap<>();
+        // 车牌号码 用逗号分隔,例如(示例车牌1,示例车牌2)
+        requestMap.put("plates", plates);
+        requestMap.put("time", null);
+
+        // key车牌号   value车辆carId
+        Map<String, Integer> plateCarIdPair = new HashMap<>();
+        if (StrUtil.isNotBlank(zhbdtoken)) {
+            try {
+                String jsonResponse = sendPostRequest(requestMap, zhbdtoken, GET_LOCATION_USE_PLATES_URL);
+                ApiOilResponse response = parseResponse(jsonResponse);
+                if (response != null && response.code == 200) {
+                    List<Map<String, Object>> locations = response.data.data;
+                    // 所有车辆的最后位置信息
+                    for (Map<String, Object> detail : locations) {
+                        Integer carId = Integer.valueOf(detail.get("vehicle_id").toString());
+                        // 车牌号
+                        String vehicleName = String.valueOf(detail.get("vehicle_name"));
+                        if (StrUtil.isNotBlank(vehicleName) && ObjUtil.isNotEmpty(carId)) {
+                            plateCarIdPair.put(vehicleName, carId);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                log.info("获取油耗数据失败!");
+            }
+        }
+        return plateCarIdPair;
+    }
+
+    private String sendPostRequest(Map<String, Object> requestData, String token, String requestUrl) throws IOException {
 
         OkHttpClient client = new OkHttpClient();
         ObjectMapper objectMapper = new ObjectMapper();
@@ -109,7 +169,7 @@ public class ZHBDUtil {
         okhttp3.RequestBody body = okhttp3.RequestBody.create(json, okhttp3.MediaType.parse("application/json"));
 
         Request request = new Request.Builder()
-                .url(LOCATION_URL)
+                .url(requestUrl)
                 .post(body)
                 .addHeader("Content-Type", "application/json")
                 .addHeader("Authorization", "Bearer " + token)// 关键头信息

+ 14 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/resources/mapper/static/IotDeviceMapper.xml

@@ -509,4 +509,18 @@
         AND l.id = latest.max_id
         ) tmp ON tmp.device_id = t2.id
     </select>
+
+    <!-- 查询设备表中所有 设备名称中包含 车牌号的车辆 -->
+    <select id="allCarsContainsPlates"
+            resultType="cn.iocoder.yudao.module.pms.controller.admin.vo.IotCarDevicePlateVO">
+        SELECT
+            id deviceId,
+            device_code deviceCode,
+            device_name deviceName,
+            REGEXP_SUBSTR(device_name, '([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-Z0-9]{4,7}[A-Z0-9挂学警港澳使领])') as licensePlate
+        FROM rq_iot_device
+        WHERE
+            deleted = 0
+          AND device_name REGEXP '([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-Z0-9]{4,7}[A-Z0-9挂学警港澳使领])';
+    </select>
 </mapper>

+ 3 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java

@@ -23,5 +23,8 @@ public class DeptSimpleRespVO {
     @Schema(description = "类型")
     private String type;
 
+    @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "6")
+    private Integer sort;
+
     private Long originId;
 }