Bladeren bron

运行记录1213-运行记录工单APP保存接口优化

yuanchao 6 dagen geleden
bovenliggende
commit
b9dd533a17

+ 244 - 6
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotopeationfill/IotOpeationFillController.java

@@ -364,7 +364,7 @@ public class IotOpeationFillController {
     /**
      * 处理累计逻辑
      */
-    private void processSummationLogic(List<IotOpeationFillSaveReqVO> allFillData) {
+    /*private void processSummationLogic(List<IotOpeationFillSaveReqVO> allFillData) {
         // 按modelId分组,便于查找
         Map<Long, List<IotOpeationFillSaveReqVO>> modelIdMap = allFillData.stream()
                 .filter(fill -> fill.getModelId() != null)
@@ -415,8 +415,80 @@ public class IotOpeationFillController {
                 //log.warn("DefaultValue转换失败: {}", fill.getDefaultValue());
             }
         }
+    }*/
+
+    /**
+     * 处理累计逻辑(优化后:批量查询历史数据)
+     */
+    private void processSummationLogic(List<IotOpeationFillSaveReqVO> allFillData) {
+        // 按modelId分组(不变)
+        Map<Long, List<IotOpeationFillSaveReqVO>> modelIdMap = allFillData.stream()
+                .filter(fill -> fill.getModelId() != null)
+                .collect(Collectors.groupingBy(IotOpeationFillSaveReqVO::getModelId));
+
+        // 筛选需要累计的数据(不变)
+        List<IotOpeationFillSaveReqVO> sumDataList = allFillData.stream()
+                .filter(fill -> 1 == fill.getIsSum()
+                        && StringUtils.isEmpty(fill.getDefaultValue())
+                        && StringUtils.isEmpty(fill.getDefaultValue().trim()))
+                .collect(Collectors.toList());
+
+        if (CollectionUtils.isEmpty(sumDataList)) {
+            return;
+        }
+
+        // 1. 批量构建查询条件(避免循环查询)
+        List<IotDeviceRunLogDO> queryLogs = sumDataList.stream()
+                .map(fill -> {
+                    IotDeviceRunLogDO queryLog = new IotDeviceRunLogDO();
+                    queryLog.setDeviceId(fill.getDeviceId());
+                    queryLog.setPointName(fill.getPointName());
+                    queryLog.setCreateTime(LocalDateTime.of(fill.getCreateTime(), LocalTime.MIDNIGHT));
+                    return queryLog;
+                })
+                .collect(Collectors.toList());
+
+        // 2. 批量查询历史最大数据(1次数据库交互)
+        List<IotDeviceRunLogDO> maxFillDataList = iotOpeationFillService.batchQueryMaxReportData(queryLogs);
+        Map<String, BigDecimal> maxDataMap = maxFillDataList.stream()
+                .filter(data -> data.getTotalRunTime() != null)
+                .collect(Collectors.toMap(
+                        data -> data.getDeviceId() + "_" + data.getPointName(), // 设备+测点唯一标识
+                        IotDeviceRunLogDO::getTotalRunTime
+                ));
+
+        // 3. 并行处理累计计算(CPU密集型,并行提速)
+        sumDataList.parallelStream().forEach(fill -> {
+            try {
+                Long refModelId = Long.parseLong(fill.getDefaultValue());
+                List<IotOpeationFillSaveReqVO> targetFills = modelIdMap.get(refModelId);
+                if (CollectionUtils.isEmpty(targetFills)) {
+                    return;
+                }
+
+                BigDecimal total = BigDecimal.ZERO;
+                for (IotOpeationFillSaveReqVO targetFill : targetFills) {
+                    BigDecimal currentValue = new BigDecimal(targetFill.getFillContent());
+                    if (fill.getSumId() == 1) {
+                        // 累加:从缓存获取历史最大值
+                        String key = fill.getDeviceId() + "_" + fill.getPointName();
+                        BigDecimal maxHistory = maxDataMap.getOrDefault(key, BigDecimal.ZERO);
+                        total = maxHistory.add(currentValue);
+                    } else if (fill.getSumId() == 0) {
+                        // 直接赋值
+                        total = currentValue;
+                    }
+                }
+                fill.setTotalRunTime(total);
+            } catch (NumberFormatException e) {
+                //log.warn("DefaultValue转换失败: {}", fill.getDefaultValue());
+            }
+        });
     }
 
+
+
+
     /**
      * 处理数据累加
      */
@@ -523,7 +595,7 @@ public class IotOpeationFillController {
     /**
      * 批量保存日志
      */
-    private void batchSaveLogs(List<IotDeviceRunLogDO> logDOList) {
+    /*private void batchSaveLogs(List<IotDeviceRunLogDO> logDOList) {
         for (IotDeviceRunLogDO log : logDOList) {
             IotDeviceRunLogDO existingData = iotOpeationFillService.reportData(log);
             if (existingData == null) {
@@ -536,8 +608,66 @@ public class IotOpeationFillController {
                 }
             }
         }
+    }*/
+
+    /**
+     * 批量保存日志(优化后:批量查询+批量插入+批量更新)
+     */
+    private void batchSaveLogs(List<IotDeviceRunLogDO> logDOList) {
+        if (CollectionUtils.isEmpty(logDOList)) {
+            return;
+        }
+
+        // 1. 批量查询已存在的数据(用复合索引:deviceId+pointName+createTime)
+        List<IotDeviceRunLogDO> existingLogs = iotOpeationFillService.batchQueryExistingLogs(logDOList);
+        Map<String, IotDeviceRunLogDO> existingLogMap = existingLogs.stream()
+                .collect(Collectors.toMap(
+                        log -> buildLogUniqueKey(log), // 构建唯一标识:deviceId_pointName_createTime
+                        log -> log,
+                        (v1, v2) -> v1 // 避免重复key(理论上不会有)
+                ));
+
+        // 2. 拆分:待插入列表 + 待更新列表
+        List<IotDeviceRunLogDO> insertList = new ArrayList<>();
+        List<IotDeviceRunLogDO> updateList = new ArrayList<>();
+        List<IotDeviceRunLogDO> updateSumList = new ArrayList<>();
+
+        for (IotDeviceRunLogDO log : logDOList) {
+            String key = buildLogUniqueKey(log);
+            if (existingLogMap.containsKey(key)) {
+                // 已存在:区分普通更新和累计更新
+                if (log.getIsSum() == 0) {
+                    updateList.add(log);
+                } else {
+                    updateSumList.add(log);
+                }
+            } else {
+                // 不存在:插入
+                insertList.add(log);
+            }
+        }
+
+        // 3. 批量执行(MyBatis批量操作)
+        if (!insertList.isEmpty()) {
+            iotOpeationFillService.batchInsertLogs(insertList); // 批量插入
+        }
+        if (!updateList.isEmpty()) {
+            iotOpeationFillService.batchUpdateLogs(updateList); // 批量更新普通日志
+        }
+        if (!updateSumList.isEmpty()) {
+            iotOpeationFillService.batchUpdateSumLogs(updateSumList); // 批量更新累计日志
+        }
+    }
+
+    /**
+     * 构建日志唯一标识(用于快速查找已存在数据)
+     */
+    private String buildLogUniqueKey(IotDeviceRunLogDO log) {
+        return log.getDeviceId() + "_" + log.getPointName() + "_" + log.getCreateTime().toLocalDate();
     }
 
+    //*******************批量保存到这********************
+
     /**
      * 获取部门ID集合
      */
@@ -558,7 +688,7 @@ public class IotOpeationFillController {
     /**
      * 生成日报
      */
-    private void generateDailyReports(List<IotDeviceRunLogDO> logDOList,
+    /*private void generateDailyReports(List<IotDeviceRunLogDO> logDOList,
                                       Map<String, Set<Long>> deptMap,
                                       LocalDate createDate,
                                       Integer userId) {
@@ -586,12 +716,55 @@ public class IotOpeationFillController {
                 processDepartmentReport(deviceLogs, fillStatus, deptMap, createDate);
             }
         }
+    }*/
+    private void generateDailyReports(List<IotDeviceRunLogDO> logDOList,
+                                      Map<String, Set<Long>> deptMap,
+                                      LocalDate createDate,
+                                      Integer userId) {
+        if (CollectionUtils.isEmpty(logDOList)) {
+            return;
+        }
+
+        // 1. 批量查询所有测点描述(1次数据库交互,避免循环查询)
+        Set<String> pointNameSet = logDOList.stream()
+                .map(IotDeviceRunLogDO::getPointName)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        List<IotDeviceRunLogDO> descList = iotOpeationFillService.batchGetDescByPointNames(pointNameSet);
+        Map<String, IotDeviceRunLogDO> descMap = descList.stream()
+                .collect(Collectors.toMap(
+                        IotDeviceRunLogDO::getPointName,
+                        desc -> desc,
+                        (v1, v2) -> v1
+                ));
+
+        // 2. 按设备分组(不变)
+        Map<Long, List<IotDeviceRunLogDO>> deviceLogsMap = logDOList.stream()
+                .collect(Collectors.groupingBy(IotDeviceRunLogDO::getDeviceId));
+
+        // 3. 并行处理每个设备的日报(IO密集型,并行提速)
+        deviceLogsMap.entrySet().parallelStream().forEach(entry -> {
+            List<IotDeviceRunLogDO> deviceLogs = entry.getValue();
+            IotDeviceRunLogDO sampleLog = deviceLogs.get(0);
+
+            // 查询上报状态(单条查询无法避免,但并行执行)
+            IotOpeationFillDO reportQuery = new IotOpeationFillDO();
+            reportQuery.setDeviceId(sampleLog.getDeviceId());
+            reportQuery.setCreateTime(sampleLog.getCreateTime());
+            IotOpeationFillDO fillStatus = iotOpeationFillService.isReport(reportQuery);
+
+            if (fillStatus != null && fillStatus.getIsReport() != null && fillStatus.getIsReport() == 1) {
+                fillStatus.setUserId(userId);
+                // 处理部门日报
+                processDepartmentReport(deviceLogs, fillStatus, deptMap, createDate, descMap);
+            }
+        });
     }
 
     /**
      * 处理部门日报生成
      */
-    private void processDepartmentReport(List<IotDeviceRunLogDO> deviceLogs,
+    /*private void processDepartmentReport(List<IotDeviceRunLogDO> deviceLogs,
                                          IotOpeationFillDO fillStatus,
                                          Map<String, Set<Long>> deptMap,
                                          LocalDate createDate) {
@@ -603,12 +776,30 @@ public class IotOpeationFillController {
         } else if (ryIdList.contains(fillStatus.getDeptId())) {
             generateRyDailyReport(deviceLogs, fillStatus, createDate);
         }
+    }*/
+
+    /**
+     * 处理部门日报生成(新增descMap缓存)
+     */
+    private void processDepartmentReport(List<IotDeviceRunLogDO> deviceLogs,
+                                         IotOpeationFillDO fillStatus,
+                                         Map<String, Set<Long>> deptMap,
+                                         LocalDate createDate,
+                                         Map<String, IotDeviceRunLogDO> descMap) {
+        Set<Long> rhIdList = deptMap.get("RH");
+        Set<Long> ryIdList = deptMap.get("RY");
+
+        if (rhIdList.contains(fillStatus.getDeptId())) {
+            generateRhDailyReport(deviceLogs, fillStatus, createDate, descMap);
+        } else if (ryIdList.contains(fillStatus.getDeptId())) {
+            generateRyDailyReport(deviceLogs, fillStatus, createDate, descMap);
+        }
     }
 
     /**
      * 生成RH日报
      */
-    private void generateRhDailyReport(List<IotDeviceRunLogDO> deviceLogs,
+    /*private void generateRhDailyReport(List<IotDeviceRunLogDO> deviceLogs,
                                        IotOpeationFillDO fillStatus,
                                        LocalDate createDate) {
         IotRhDailyReportSaveReqVO saveReqVO = new IotRhDailyReportSaveReqVO();
@@ -630,13 +821,38 @@ public class IotOpeationFillController {
         finalReport.setFillOrderCreateTime(createDate.atStartOfDay());
         finalReport.setCreator(String.valueOf(fillStatus.getUserId()));
 
+        iotRhDailyReportService.createIotRhDailyReport(finalReport);
+    }*/
+
+    /**
+     * 生成RH日报(使用缓存的descMap)
+     */
+    private void generateRhDailyReport(List<IotDeviceRunLogDO> deviceLogs,
+                                       IotOpeationFillDO fillStatus,
+                                       LocalDate createDate,
+                                       Map<String, IotDeviceRunLogDO> descMap) {
+        IotRhDailyReportSaveReqVO saveReqVO = new IotRhDailyReportSaveReqVO();
+        Map<String, Object> reportData = BeanUtil.beanToMap(saveReqVO);
+
+        for (IotDeviceRunLogDO log : deviceLogs) {
+            IotDeviceRunLogDO descDO = descMap.get(log.getPointName()); // 从缓存获取,无数据库交互
+            if (descDO != null && reportData.containsKey(descDO.getPointName())) {
+                reportData.put(descDO.getPointName(), log.getFillContent());
+            }
+        }
+
+        // 后续赋值和保存逻辑不变(建议日报也做批量保存,若存在多条设备)
+        IotRhDailyReportSaveReqVO finalReport = BeanUtil.mapToBean(reportData, IotRhDailyReportSaveReqVO.class, false);
+        finalReport.setDeptId(fillStatus.getDeptId());
+        finalReport.setFillOrderCreateTime(createDate.atStartOfDay());
+        finalReport.setCreator(String.valueOf(fillStatus.getUserId()));
         iotRhDailyReportService.createIotRhDailyReport(finalReport);
     }
 
     /**
      * 生成RY日报
      */
-    private void generateRyDailyReport(List<IotDeviceRunLogDO> deviceLogs,
+    /*private void generateRyDailyReport(List<IotDeviceRunLogDO> deviceLogs,
                                        IotOpeationFillDO fillStatus,
                                        LocalDate createDate) {
         IotRyDailyReportSaveReqVO saveReqVO = new IotRyDailyReportSaveReqVO();
@@ -663,8 +879,30 @@ public class IotOpeationFillController {
             finalReport.setProjectClassification("2");
         }
 
+        iotRyDailyReportService.createIotRyDailyReport(finalReport);
+    }*/
+    private void generateRyDailyReport(List<IotDeviceRunLogDO> deviceLogs,
+                                       IotOpeationFillDO fillStatus,
+                                       LocalDate createDate,
+                                       Map<String, IotDeviceRunLogDO> descMap) {
+        IotRyDailyReportSaveReqVO saveReqVO = new IotRyDailyReportSaveReqVO();
+        Map<String, Object> reportData = BeanUtil.beanToMap(saveReqVO);
+
+        for (IotDeviceRunLogDO log : deviceLogs) {
+            IotDeviceRunLogDO descDO = descMap.get(log.getPointName()); // 从缓存获取,无数据库交互
+            if (descDO != null && reportData.containsKey(descDO.getPointName())) {
+                reportData.put(descDO.getPointName(), log.getFillContent());
+            }
+        }
+
+        // 后续赋值和保存逻辑不变(建议日报也做批量保存,若存在多条设备)
+        IotRyDailyReportSaveReqVO finalReport = BeanUtil.mapToBean(reportData, IotRyDailyReportSaveReqVO.class, false);
+        finalReport.setDeptId(fillStatus.getDeptId());
+        finalReport.setFillOrderCreateTime(createDate.atStartOfDay());
+        finalReport.setCreator(String.valueOf(fillStatus.getUserId()));
         iotRyDailyReportService.createIotRyDailyReport(finalReport);
     }
+
     //************************************************8
 
     @PostMapping("/upOperationOrder")

+ 21 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/dal/mysql/iotopeationfill/IotOpeationFillMapper.java

@@ -27,6 +27,7 @@ import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 /**
  * 运行记录填报 Mapper
@@ -173,6 +174,20 @@ public interface IotOpeationFillMapper extends BaseMapperX<IotOpeationFillDO> {
     @TenantIgnore
     int updateFill(IotOpeationFillSaveReqVO vo);
 
+    // 批量查询已存在的日志
+    @TenantIgnore
+    List<IotDeviceRunLogDO> batchQueryExistingLogs(@Param("list") List<IotDeviceRunLogDO> logList);
+    // 批量插入
+    @TenantIgnore
+    int batchInsertLogs(@Param("list") List<IotDeviceRunLogDO> logList);
+    // 批量更新普通日志
+    @TenantIgnore
+    int batchUpdateLogs(@Param("list") List<IotDeviceRunLogDO> logList);
+    // 批量更新累计日志
+    @TenantIgnore
+    int batchUpdateSumLogs(@Param("list") List<IotDeviceRunLogDO> logList);
+
+
     @TenantIgnore
     int batchUpdateFill(@Param("list") List<IotOpeationFillSaveReqVO> list);
 
@@ -260,11 +275,17 @@ public interface IotOpeationFillMapper extends BaseMapperX<IotOpeationFillDO> {
     @TenantIgnore
     IotDeviceRunLogDO maxReportData(IotDeviceRunLogDO runLogDO);
 
+    @TenantIgnore
+    // 批量查询历史最大数据
+    List<IotDeviceRunLogDO> batchQueryMaxReportData(@Param("list") List<IotDeviceRunLogDO> queryLogs);
+
     @TenantIgnore
     IotDeviceRunLogDO reportData1(IotDeviceRunLogDO runLogDO);
 
     @TenantIgnore
     IotDeviceRunLogDO getDesc(IotDeviceRunLogDO runLogDO);
+    @TenantIgnore
+    List<IotDeviceRunLogDO> batchGetDescByPointNames(@Param("pointNameSet") Set<String> pointNameSet);
 
     @TenantIgnore
     IotDeviceRunLogDO getTeamType(IotDeviceRunLogDO runLogDO);

+ 21 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotopeationfill/IotOpeationFillService.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.pms.service.iotopeationfill;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.module.pms.controller.admin.iotmodeltemplateattrs.vo.IotModelTemplateAttrsRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeationFillPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeationFillPageVO;
@@ -18,6 +19,7 @@ import org.apache.ibatis.annotations.Param;
 import javax.validation.Valid;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 /**
  * 运行记录填报 Service 接口
@@ -186,10 +188,29 @@ public interface IotOpeationFillService {
 
     IotDeviceRunLogDO reportData(IotDeviceRunLogDO runLogDO);
 
+
+
+        // 批量查询已存在的日志
+    List<IotDeviceRunLogDO> batchQueryExistingLogs(List<IotDeviceRunLogDO> logList);
+
+        // 批量插入
+    int batchInsertLogs(List<IotDeviceRunLogDO> logList);
+
+        // 批量更新普通日志
+    int batchUpdateLogs(List<IotDeviceRunLogDO> logList);
+
+        // 批量更新累计日志
+    int batchUpdateSumLogs(List<IotDeviceRunLogDO> logList);
+
+
     IotDeviceRunLogDO maxReportData(IotDeviceRunLogDO runLogDO);
 
+    List<IotDeviceRunLogDO> batchQueryMaxReportData(List<IotDeviceRunLogDO> queryLogs);
+
     IotDeviceRunLogDO reportData1(IotDeviceRunLogDO runLogDO);
 
     IotDeviceRunLogDO getDesc(IotDeviceRunLogDO runLogDO);
 
+    List<IotDeviceRunLogDO> batchGetDescByPointNames(Set<String> pointNameSet);
+
 }

+ 30 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotopeationfill/IotOpeationFillServiceImpl.java

@@ -458,11 +458,36 @@ public class IotOpeationFillServiceImpl implements IotOpeationFillService {
         return iotOpeationFillMapper.reportData(runLogDO);
     }
 
+    @Override
+    public List<IotDeviceRunLogDO> batchQueryExistingLogs(List<IotDeviceRunLogDO> logList) {
+        return iotOpeationFillMapper.batchQueryExistingLogs(logList);
+    }
+
+    @Override
+    public int batchInsertLogs(List<IotDeviceRunLogDO> logList) {
+        return iotOpeationFillMapper.batchInsertLogs(logList);
+    }
+
+    @Override
+    public int batchUpdateLogs(List<IotDeviceRunLogDO> logList) {
+        return iotOpeationFillMapper.batchUpdateLogs(logList);
+    }
+
+    @Override
+    public int batchUpdateSumLogs(List<IotDeviceRunLogDO> logList) {
+        return iotOpeationFillMapper.batchUpdateSumLogs(logList);
+    }
+
     @Override
     public IotDeviceRunLogDO maxReportData(IotDeviceRunLogDO runLogDO) {
         return iotOpeationFillMapper.maxReportData(runLogDO);
     }
 
+    @Override
+    public List<IotDeviceRunLogDO> batchQueryMaxReportData(List<IotDeviceRunLogDO> queryLogs) {
+        return iotOpeationFillMapper.batchQueryMaxReportData(queryLogs);
+    }
+
     @Override
     public IotDeviceRunLogDO reportData1(IotDeviceRunLogDO runLogDO) {
         return iotOpeationFillMapper.reportData1(runLogDO);
@@ -473,4 +498,9 @@ public class IotOpeationFillServiceImpl implements IotOpeationFillService {
         return iotOpeationFillMapper.getDesc(runLogDO);
     }
 
+    @Override
+    public List<IotDeviceRunLogDO> batchGetDescByPointNames(Set<String> pointNameSet) {
+        return iotOpeationFillMapper.batchGetDescByPointNames(pointNameSet);
+    }
+
 }

+ 105 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/resources/mapper/static/IotOpeationFillMapper.xml

@@ -196,6 +196,16 @@
          #{pointCode}, #{fillContent}, #{createTime}, #{pointName}, #{totalRunTime},#{isSum})
     </insert>
 
+    <insert id="batchInsertLogs" parameterType="java.util.List">
+        insert into rq_iot_device_run_log
+        (dept_id, device_id, device_code, point_code, fill_content, create_time, point_name, total_run_time, is_sum)
+        values
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deptId}, #{item.deviceId}, #{item.deviceCode},
+            #{item.pointCode}, #{item.fillContent}, #{item.createTime}, #{item.pointName}, #{item.totalRunTime}, #{item.isSum})
+        </foreach>
+    </insert>
+
     <update id="updateLog" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
         update rq_iot_device_run_log
         set
@@ -210,6 +220,29 @@
             is_sum = 0
     </update>
 
+
+    <update id="batchUpdateLogs" parameterType="java.util.List">
+        update rq_iot_device_run_log
+        set
+        fill_content = CASE
+        <foreach collection="list" item="item" separator="">
+            WHEN device_id = #{item.deviceId}
+            AND point_name = #{item.pointName}
+            AND DATE(create_time) = #{item.createTime}
+            AND is_sum = 0
+            THEN #{item.fillContent}
+        </foreach>
+        ELSE fill_content  <!-- 不满足条件的记录不更新 -->
+        END
+        where
+        <!-- 只更新集合中存在的记录,避免全表扫描 -->
+        (device_id, point_name, DATE(create_time), is_sum) IN (
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deviceId}, #{item.pointName}, #{item.createTime}, 0)
+        </foreach>
+        )
+    </update>
+
     <update id="updateSumLog" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
         update rq_iot_device_run_log
         set
@@ -224,6 +257,30 @@
             is_sum = 1
     </update>
 
+
+    <update id="batchUpdateSumLogs" parameterType="java.util.List">
+        update rq_iot_device_run_log
+        set
+        total_run_time = CASE
+        <foreach collection="list" item="item" separator="">
+            <!-- 匹配每条记录的唯一条件:device_id + point_name + 日期 + is_sum=1 -->
+            WHEN device_id = #{item.deviceId}
+            AND point_name = #{item.pointName}
+            AND DATE(create_time) = #{item.createTime}
+            AND is_sum = 1
+            THEN #{item.totalRunTime}  <!-- 赋值对应的总运行时间 -->
+        </foreach>
+        ELSE total_run_time  <!-- 不匹配的记录保持原数值,不更新 -->
+        END
+        where
+        <!-- 限定更新范围,避免全表扫描(仅更新集合中存在的记录) -->
+        (device_id, point_name, DATE(create_time), is_sum) IN (
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deviceId}, #{item.pointName}, #{item.createTime}, 1)
+        </foreach>
+        )
+    </update>
+
     <!-- <select id="fillList" parameterType="cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeationFillRespVO"
      resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO">
          SELECT * FROM
@@ -1180,6 +1237,18 @@
           and DATE(create_time) = DATE(#{createTime})
     </select>
 
+    <select id="batchQueryExistingLogs" parameterType="java.util.List"
+            resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
+        select * from rq_iot_device_run_log
+        where
+        <!-- 多组条件组合:device_id + point_name + 日期 匹配 -->
+        (device_id, point_name, DATE(create_time)) IN (
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deviceId}, #{item.pointName}, DATE(#{item.createTime}))
+        </foreach>
+        )
+    </select>
+
     <select id="maxReportData" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO"
             resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
         SELECT
@@ -1192,6 +1261,20 @@
           AND create_time &lt; #{createTime}
     </select>
 
+
+    <select id="batchQueryMaxReportData" resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
+        SELECT t.device_id, t.point_name, MAX(t.total_run_time) as total_run_time
+        FROM iot_device_run_log t
+        WHERE
+        <foreach collection="list" item="item" separator="OR">
+            (t.device_id = #{item.deviceId}
+            AND t.point_name = #{item.pointName}
+            AND DATE(t.create_time) = DATE(#{item.createTime}))
+        </foreach>
+        GROUP BY t.device_id, t.point_name
+    </select>
+
+
     <select id="reportData1" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO"
             resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
         select * from
@@ -1217,6 +1300,28 @@
     </select>
 
 
+    <select id="batchGetDescByPointNames" resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
+        <!-- 注意:请根据你的实际表名和字段名修改! -->
+        <!-- 假设测点描述存储在 iot_device_run_log 表,核心字段:point_name(测点名)、point_desc(测点描述,可替换为你的实际字段) -->
+        select
+        description point_name
+        from
+        rq_iot_model_template_attrs
+        WHERE
+        1 = 1
+        <!-- 批量查询:用 IN 关键字,配合 foreach 遍历测点名集合 -->
+        <if test="pointNameSet != null and pointNameSet.size() > 0">
+            AND name IN
+            <foreach collection="pointNameSet" item="pointName" open="(" separator="," close=")">
+                #{pointName}  <!-- 遍历 Set 中的每个测点名 -->
+            </foreach>
+        </if>
+        <!-- 去重:避免同一测点名返回多条重复数据 -->
+        GROUP BY
+        point_name
+    </select>
+
+
     <select id="getTeamType" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO"
             resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
         select type as point_code from system_dept_type where dept_id = #{deptId}