Prechádzať zdrojové kódy

运行记录1216-运行记录工单APP保存接口,运行记录非正常工单插入日报优化

yuanchao 1 týždeň pred
rodič
commit
1a5096f739

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

@@ -1,33 +1,23 @@
 package cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill;
 
 import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.collection.CollUtil;
-import cn.iocoder.yudao.framework.common.exception.ServiceException;
-import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
-import cn.iocoder.yudao.module.pms.controller.admin.iotmodel.vo.IotModelPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotmodeltemplateattrs.vo.IotModelTemplateAttrsRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.*;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrhdailyreport.vo.IotRhDailyReportSaveReqVO;
-import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreport.vo.IotRyDailyReportPageReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotrydailyreport.vo.IotRyDailyReportSaveReqVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.DeviceVO;
-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;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotcountdata.IotCountDataDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotcountdata.IotCountListDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicecountdata.IotDeviceCountData;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO;
-import cn.iocoder.yudao.module.pms.dal.dataobject.iotmodel.IotModelDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotmodeltemplateattrs.IotModelTemplateAttrsDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotmodeltemplateattrs.IotModelTemplateAttrsDO1;
-import cn.iocoder.yudao.module.pms.dal.dataobject.iotmodeltemplateattrs.IotThingsModelDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillOrderDO;
 
 import cn.iocoder.yudao.module.pms.dal.dataobject.yanfan.YfDeviceDO;
 
-import cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant;
 import cn.iocoder.yudao.module.pms.service.IDeviceService;
 import cn.iocoder.yudao.module.pms.service.iotopeationfill.IotOpeationFillService;
 
@@ -37,7 +27,7 @@ import cn.iocoder.yudao.module.pms.service.yanfan.YfDeviceService;
 import cn.iocoder.yudao.module.system.service.dept.DeptService;
 import com.aliyun.tea.utils.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
-import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
@@ -49,7 +39,6 @@ import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Operation;
 
 import javax.annotation.security.PermitAll;
-import javax.validation.constraints.*;
 import javax.validation.*;
 import javax.servlet.http.*;
 import java.math.BigDecimal;
@@ -60,6 +49,7 @@ import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.util.*;
 import java.io.IOException;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
@@ -80,6 +70,7 @@ import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
 @RestController
 @RequestMapping("/rq/iot-opeation-fill")
 @Validated
+@Slf4j
 public class IotOpeationFillController {
 
     @Resource
@@ -361,61 +352,7 @@ public class IotOpeationFillController {
         }
     }
 
-    /**
-     * 处理累计逻辑
-     */
-    /*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 -> {
-                    if (fill.getIsSum() != 1) {
-                        return false;
-                    }
-
-                    String defaultValue = fill.getDefaultValue();
-                    // 同时检查null和空字符串
-                    if (defaultValue == null || defaultValue.isEmpty()) {
-                        return false;
-                    }
-
-                    // 如果还需要排除纯空格的情况
-                    if (defaultValue.trim().isEmpty()) {
-                        return false;
-                    }
-
-                    return true;
-                })
-                .collect(Collectors.toList());
-
-
-        for (IotOpeationFillSaveReqVO fill : sumDataList) {
-            try {
-                Long refModelId = Long.parseLong(fill.getDefaultValue());
-                List<IotOpeationFillSaveReqVO> targetFills = modelIdMap.get(refModelId);
 
-                if (CollectionUtils.isEmpty(targetFills)) {
-                    continue;
-                }
-
-                for (IotOpeationFillSaveReqVO targetFill : targetFills) {
-                    if (fill.getSumId() == 1) {
-                        // 需要累加的情况
-                        processAccumulation(fill, targetFill);
-                    } else if (fill.getSumId() == 0) {
-                        // 直接赋值的情况
-                        fill.setTotalRunTime(new BigDecimal(targetFill.getFillContent()));
-                    }
-                }
-            } catch (NumberFormatException e) {
-                //log.warn("DefaultValue转换失败: {}", fill.getDefaultValue());
-            }
-        }
-    }*/
 
     /**
      * 处理累计逻辑(优化后:批量查询历史数据)
@@ -428,9 +365,7 @@ public class IotOpeationFillController {
 
         // 筛选需要累计的数据(不变)
         List<IotOpeationFillSaveReqVO> sumDataList = allFillData.stream()
-                .filter(fill -> 1 == fill.getIsSum()
-                        && StringUtils.isEmpty(fill.getDefaultValue())
-                        && StringUtils.isEmpty(fill.getDefaultValue().trim()))
+                .filter(fill -> 1 == fill.getIsSum())
                 .collect(Collectors.toList());
 
         if (CollectionUtils.isEmpty(sumDataList)) {
@@ -592,23 +527,6 @@ public class IotOpeationFillController {
         }
     }
 
-    /**
-     * 批量保存日志
-     */
-    /*private void batchSaveLogs(List<IotDeviceRunLogDO> logDOList) {
-        for (IotDeviceRunLogDO log : logDOList) {
-            IotDeviceRunLogDO existingData = iotOpeationFillService.reportData(log);
-            if (existingData == null) {
-                iotOpeationFillService.insertLog1(log);
-            } else {
-                if (log.getIsSum() == 0) {
-                    iotOpeationFillService.updateLog(log);
-                } else {
-                    iotOpeationFillService.updateSumLog(log);
-                }
-            }
-        }
-    }*/
 
     /**
      * 批量保存日志(优化后:批量查询+批量插入+批量更新)
@@ -685,38 +603,7 @@ public class IotOpeationFillController {
         return deptMap;
     }
 
-    /**
-     * 生成日报
-     */
-    /*private void generateDailyReports(List<IotDeviceRunLogDO> logDOList,
-                                      Map<String, Set<Long>> deptMap,
-                                      LocalDate createDate,
-                                      Integer userId) {
-        // 按设备分组处理
-        Map<Long, List<IotDeviceRunLogDO>> deviceLogsMap = logDOList.stream()
-                .collect(Collectors.groupingBy(IotDeviceRunLogDO::getDeviceId));
-
-        for (Map.Entry<Long, List<IotDeviceRunLogDO>> entry : deviceLogsMap.entrySet()) {
-            List<IotDeviceRunLogDO> deviceLogs = entry.getValue();
-            if (CollectionUtils.isEmpty(deviceLogs)) {
-                continue;
-            }
-
-            IotDeviceRunLogDO sampleLog = deviceLogs.get(0);
-            IotOpeationFillDO reportQuery = new IotOpeationFillDO();
-            reportQuery.setDeviceId(sampleLog.getDeviceId());
-            reportQuery.setCreateTime(sampleLog.getCreateTime());
-
-            IotOpeationFillDO fillStatus = iotOpeationFillService.isReport(reportQuery);
-            fillStatus.setUserId(userId);
 
-            if (fillStatus != null && fillStatus.getIsReport() != null &&
-                    fillStatus.getIsReport() == 1) {
-
-                processDepartmentReport(deviceLogs, fillStatus, deptMap, createDate);
-            }
-        }
-    }*/
     private void generateDailyReports(List<IotDeviceRunLogDO> logDOList,
                                       Map<String, Set<Long>> deptMap,
                                       LocalDate createDate,
@@ -761,22 +648,7 @@ public class IotOpeationFillController {
         });
     }
 
-    /**
-     * 处理部门日报生成
-     */
-    /*private void processDepartmentReport(List<IotDeviceRunLogDO> deviceLogs,
-                                         IotOpeationFillDO fillStatus,
-                                         Map<String, Set<Long>> deptMap,
-                                         LocalDate createDate) {
-        Set<Long> rhIdList = deptMap.get("RH");
-        Set<Long> ryIdList = deptMap.get("RY");
 
-        if (rhIdList.contains(fillStatus.getDeptId())) {
-            generateRhDailyReport(deviceLogs, fillStatus, createDate);
-        } else if (ryIdList.contains(fillStatus.getDeptId())) {
-            generateRyDailyReport(deviceLogs, fillStatus, createDate);
-        }
-    }*/
 
     /**
      * 处理部门日报生成(新增descMap缓存)
@@ -796,33 +668,7 @@ public class IotOpeationFillController {
         }
     }
 
-    /**
-     * 生成RH日报
-     */
-    /*private void generateRhDailyReport(List<IotDeviceRunLogDO> deviceLogs,
-                                       IotOpeationFillDO fillStatus,
-                                       LocalDate createDate) {
-        IotRhDailyReportSaveReqVO saveReqVO = new IotRhDailyReportSaveReqVO();
-        Map<String, Object> reportData = BeanUtil.beanToMap(saveReqVO);
-
-        for (IotDeviceRunLogDO log : deviceLogs) {
-            IotDeviceRunLogDO descDO = iotOpeationFillService.getDesc(log);
-            if (descDO != null && descDO.getPointName() != null) {
-                String pointName = descDO.getPointName();
-                if (reportData.containsKey(pointName)) {
-                    reportData.put(pointName, 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);
-    }*/
 
     /**
      * 生成RH日报(使用缓存的descMap)
@@ -849,38 +695,7 @@ public class IotOpeationFillController {
         iotRhDailyReportService.createIotRhDailyReport(finalReport);
     }
 
-    /**
-     * 生成RY日报
-     */
-    /*private void generateRyDailyReport(List<IotDeviceRunLogDO> deviceLogs,
-                                       IotOpeationFillDO fillStatus,
-                                       LocalDate createDate) {
-        IotRyDailyReportSaveReqVO saveReqVO = new IotRyDailyReportSaveReqVO();
-        Map<String, Object> reportData = BeanUtil.beanToMap(saveReqVO);
-
-        for (IotDeviceRunLogDO log : deviceLogs) {
-            IotDeviceRunLogDO descDO = iotOpeationFillService.getDesc(log);
-            if (descDO != null && descDO.getPointName() != null) {
-                String pointName = descDO.getPointName();
-                if (reportData.containsKey(pointName)) {
-                    reportData.put(pointName, log.getFillContent());
-                }
-            }
-        }
-
-        IotRyDailyReportSaveReqVO finalReport = BeanUtil.mapToBean(reportData,
-                IotRyDailyReportSaveReqVO.class, false);
-        finalReport.setDeptId(fillStatus.getDeptId());
-        finalReport.setFillOrderCreateTime(createDate.atStartOfDay());
-        finalReport.setCreator(String.valueOf(fillStatus.getUserId()));
-
-        if (fillStatus.getDeviceCategoryId() != null &&
-                fillStatus.getDeviceCategoryId() == 228) {
-            finalReport.setProjectClassification("2");
-        }
 
-        iotRyDailyReportService.createIotRyDailyReport(finalReport);
-    }*/
     private void generateRyDailyReport(List<IotDeviceRunLogDO> deviceLogs,
                                        IotOpeationFillDO fillStatus,
                                        LocalDate createDate,
@@ -1087,258 +902,299 @@ public class IotOpeationFillController {
 
     //**************
 
-    // 新VO类,用于返回包含属性详情的分页结果
-    @Data
-    public class IotOpeationFillWithAttrsVO extends IotOpeationFillDO {
-        // 保留IotOpeationFillDO的所有属性
-        // 新增属性详情字段
-        private List<IotModelTemplateAttrsDO1> attrsDetail;
-    }
-
     @GetMapping("/orderFillpage1")
     @Operation(summary = "获得运行记录填报分页")
     @PreAuthorize("@ss.hasPermission('rq:iot-opeation-fill:query')")
-    public CommonResult<PageResult<IotOpeationFillWithAttrsVO>> getIotOpeationFillWithAttrsPage1(@Valid IotOpeationFillPageVO pageReqVO) throws SQLException {
-        // 1. 获取分页数据
-        PageResult<IotOpeationFillDO> fillList = iotOpeationFillService.fillListPage(pageReqVO);
-
-        // 2. 转换为新的VO类型(包含属性详情)
-        List<IotOpeationFillWithAttrsVO> resultList = new ArrayList<>();
-
-        for (IotOpeationFillDO fillDO : fillList.getList()) {
-            IotOpeationFillWithAttrsVO vo = BeanUtils.toBean(fillDO, IotOpeationFillWithAttrsVO.class);
+    public CommonResult<PageResult<IotOpeationFillWithAttrsVO>> getIotOpeationFillWithAttrsPage1(
+            @Valid IotOpeationFillPageVO pageReqVO) throws Exception {
 
-            // 3. 为每个分页项获取属性详情
-            IotModelTemplateAttrsRespVO attrsReqVO = new IotModelTemplateAttrsRespVO();
-            attrsReqVO.setDeviceId(fillDO.getDeviceId());
-            attrsReqVO.setDeviceCode(fillDO.getDeviceCode()); // 假设分页接口返回了deviceCode
-            attrsReqVO.setCreateTime(fillDO.getCreateTime().toLocalDate());
-            attrsReqVO.setDeviceCategoryId(fillDO.getDeviceCategoryId());
+        // 1. 分页查询主数据(不变)
+        PageResult<IotOpeationFillDO> fillPage = iotOpeationFillService.fillListPage(pageReqVO);
+        List<IotOpeationFillDO> fillList = fillPage.getList();
+        /*if (CollectionUtils.isEmpty(fillList)) {
+            return success(BeanUtils.toBean(fillList, IotOpeationFillWithAttrsVO.class));
 
+        }*/
 
-            // 4. 调用原属性详情逻辑
-            List<IotModelTemplateAttrsDO1> attrsDetail = getAttrsDetail(attrsReqVO);
-            vo.setAttrsDetail(attrsDetail);
-
-            resultList.add(vo);
-        }
+        // 2. 批量预处理参数,避免循环中重复操作
+        //List<Long> deviceIds = fillList.stream().map(IotOpeationFillDO::getDeviceId).distinct().collect(Collectors.toList());
+        List<String> deviceCodes = fillList.stream().map(IotOpeationFillDO::getDeviceCode).distinct().collect(Collectors.toList());
+        //Set<LocalDate> createDates = fillList.stream().map(doObj -> doObj.getCreateTime().toLocalDate()).collect(Collectors.toSet());
 
-        // 5. 创建新的分页结果
-        PageResult<IotOpeationFillWithAttrsVO> pageResult = new PageResult<>(
-                resultList,
-                fillList.getTotal()
+        // 3. 批量查询公共数据(一次查询,全局复用)
+        // 3.1 批量查询设备状态(判断是否为虚拟设备)
+        List<YfDeviceDO> allDevice = yfDeviceService.getAllDevice(); // 仅查1次,而非循环中查N次
+        Map<String, YfDeviceDO> deviceStatusMap = allDevice.stream()
+                .collect(Collectors.toMap(
+                        YfDeviceDO::getSerialNumber,
+                        Function.identity(),
+                        (oldVal, newVal) -> newVal
+                ));
+        // 3.2 批量查询isReport状态(避免循环查询)
+        Map<Long, Integer> deviceReportMap = iotOpeationFillService.batchGetIsReport(
+                fillList.stream().map(doObj -> {
+                    IotOpeationFillDO query = new IotOpeationFillDO();
+                    query.setDeviceId(doObj.getDeviceId());
+                    query.setCreateTime(doObj.getCreateTime().toLocalDate().atStartOfDay());
+                    return query;
+                }).collect(Collectors.toList())
         );
+        // 3.3 批量查询orderDO数据
+        Map<Long, IotOpeationFillDO> deviceOrderMap = iotOpeationFillService.batchGetOrderDO(
+                fillList.stream().map(doObj -> {
+                    IotOpeationFillDO query = new IotOpeationFillDO();
+                    query.setDeviceId(doObj.getDeviceId());
+                    query.setCreateTime(doObj.getCreateTime().toLocalDate().atStartOfDay());
+                    return query;
+                }).collect(Collectors.toList())
+        );
+
+        // 4. 并行处理分页数据(利用多核CPU,非IO密集型操作适合并行)
+        List<IotOpeationFillWithAttrsVO> resultList = fillList.parallelStream().map(fillDO -> {
+            try {
+                IotOpeationFillWithAttrsVO vo = BeanUtils.toBean(fillDO, IotOpeationFillWithAttrsVO.class);
+                LocalDate createDate = fillDO.getCreateTime().toLocalDate();
+
+                // 构建参数VO
+                IotModelTemplateAttrsRespVO attrsReqVO = new IotModelTemplateAttrsRespVO();
+                attrsReqVO.setDeviceId(fillDO.getDeviceId());
+                attrsReqVO.setDeviceCode(fillDO.getDeviceCode());
+                attrsReqVO.setCreateTime(createDate);
+                attrsReqVO.setDeviceCategoryId(fillDO.getDeviceCategoryId());
+                // 调用优化后的属性详情查询(带缓存和批量查询)
+                List<IotModelTemplateAttrsDO1> attrsDetail = getAttrsDetailOptimized(
+                        attrsReqVO, deviceStatusMap, deviceReportMap, deviceOrderMap);
+
+                vo.setAttrsDetail(attrsDetail);
+                return vo;
+            } catch (Exception e) {
+                log.error("处理设备[{}]属性详情失败", fillDO.getDeviceId(), e);
+                // 降级处理:返回空属性详情,避免整个分页失败
+                IotOpeationFillWithAttrsVO vo = BeanUtils.toBean(fillDO, IotOpeationFillWithAttrsVO.class);
+                vo.setAttrsDetail(Collections.emptyList());
+                return vo;
+            }
+        }).collect(Collectors.toList());
 
+        // 5. 构建返回结果
+        PageResult<IotOpeationFillWithAttrsVO> pageResult = new PageResult<>(resultList, fillPage.getTotal());
         return success(pageResult);
     }
 
-    // 提取属性详情逻辑为私有方法
-    private List<IotModelTemplateAttrsDO1> getAttrsDetail(IotModelTemplateAttrsRespVO vo) throws SQLException {
-        // 以下是您原getAttrs接口的核心逻辑,保持不变
-        //判断是否为虚拟设备
-        //如果是走原来逻辑
-        //不是走虚拟设备逻辑
-        IotOpeationFillDO fillDO = new IotOpeationFillDO();
-        fillDO.setDeviceId(vo.getDeviceId());
-        fillDO.setCreateTime(vo.getCreateTime().atStartOfDay());
-        IotOpeationFillDO fillDO1 = iotOpeationFillService.isReport(fillDO);
-
-        List<IotModelTemplateAttrsDO1> result = new ArrayList<>();
-        List<IotModelTemplateAttrsDO> list = iotOpeationFillService.getAttrsById(vo);
-
-        if (fillDO1.getIsReport() != null && fillDO1.getIsReport() == 1) {
-            IotOpeationFillDO fillDO2 = iotOpeationFillService.orderDO(fillDO);
+    /**
+     * 优化后的属性详情查询:批量查询+缓存复用+减少冗余计算
+     */
+    private List<IotModelTemplateAttrsDO1> getAttrsDetailOptimized(
+            IotModelTemplateAttrsRespVO vo,
+            Map<String, YfDeviceDO> deviceStatusMap,
+            Map<Long, Integer> deviceReportMap,
+            Map<Long, IotOpeationFillDO> deviceOrderMap) throws SQLException {
+
+        Long deviceId = vo.getDeviceId();
+        String deviceCode = vo.getDeviceCode();
+        LocalDate createDate = vo.getCreateTime();
+
+        // 1. 复用批量查询结果(避免重复数据库查询)
+        Integer isReport = deviceReportMap.getOrDefault(deviceId, 0);
+        IotOpeationFillDO fillDO2 = deviceOrderMap.get(deviceId);
+        YfDeviceDO yfDevice = deviceStatusMap.get(deviceCode);
+        boolean isVirtualDevice = yfDevice != null && yfDevice.getStatus() == 3;
+
+        // 2. 批量查询属性模板(一次查询)
+        List<IotModelTemplateAttrsDO> attrsList = iotOpeationFillService.getAttrsByIdBatch(
+                Collections.singletonList(vo)); // 可扩展为多设备批量查询
+
+        if (CollectionUtils.isEmpty(attrsList)) {
+            return Collections.emptyList();
+        }
 
-            List<IotOpeationFillDO> reportList = new ArrayList<>();
-            List<IotOpeationFillDO> reportList1 = new ArrayList<>();
+        // 3. 按isReport分支处理(优化数据查询逻辑)
+        if (isReport != null && isReport == 1) {
+            handleReportDeviceAttrs(attrsList, fillDO2);
+        } else {
+            handleNormalDeviceAttrs(attrsList, vo, isVirtualDevice, createDate);
+        }
 
-            if (fillDO2 != null) {
-                reportList = iotOpeationFillService.reportList(fillDO2);
-                reportList1 = iotOpeationFillService.reportList1(fillDO2);
+        // 4. 分组处理(提前分组,避免重复流处理)
+        List<IotModelTemplateAttrsDO> sumList = new ArrayList<>();
+        List<IotModelTemplateAttrsDO> nonSumList = new ArrayList<>();
+        for (IotModelTemplateAttrsDO attrsDO : attrsList) {
+            if (attrsDO.getIsSum() == 1) {
+                sumList.add(attrsDO);
+            } else {
+                nonSumList.add(attrsDO);
             }
+        }
 
-            for (IotModelTemplateAttrsDO attrsDO : list) {
-                List<IotOpeationFillDO> cxLixt = reportList.stream().filter(e -> e.getOrgName().equals(attrsDO.getName())).collect(Collectors.toList());
-                //虚拟设备取值
-                List<IotOpeationFillDO> cxLixt1 = reportList1.stream().filter(e -> e.getOrgName().equals(attrsDO.getName())).collect(Collectors.toList());
-
-                if (cxLixt.size() > 0) {
-                    // 使用Map按orgName分组存储累加结果
-                    Map<String, String> orgNameToFillContent = new HashMap<>();
-
-                    for (IotOpeationFillDO reportData : cxLixt) {
-                        IotDeviceRunLogDO report = new IotDeviceRunLogDO();
-                        report.setDeviceId(reportData.getDeviceId());
-                        report.setPointName(reportData.getOrgName());
-                        report.setCreateTime(reportData.getCreateTime());
-
-                        IotDeviceRunLogDO reportCx = iotOpeationFillService.reportData(report);
-
-                        String currentContent = "";
-                        if (reportCx == null) {
-                            attrsDO.setFillContent("");
-                        } else {
-                            currentContent = reportCx.getFillContent() != null ? reportCx.getFillContent() : "";
-                        }
-
-                        // 获取当前orgName
-                        String orgName = reportData.getOrgName();
-
-                        // 累加相同orgName的内容
-                        if (orgNameToFillContent.containsKey(orgName)) {
-                            String existingContent = orgNameToFillContent.get(orgName);
-
-                            double existingValue = safeParseInt(existingContent);
-                            double currentValue = safeParseInt(currentContent);
-
-                            if ((existingValue + currentValue) == 0) {
-                                orgNameToFillContent.put(orgName, "");
-                            } else {
-                                orgNameToFillContent.put(orgName, String.valueOf(existingValue + currentValue));
-                            }
-
-                        } else {
-                            orgNameToFillContent.put(orgName, currentContent);
-                        }
-                        attrsDO.setFillContent(orgNameToFillContent.get(reportData.getOrgName()));
-                    }
-                }
-
-                if (cxLixt1.size() > 0) {
-                    for (IotOpeationFillDO reportData : cxLixt1) {
-                        IotDeviceRunLogDO report = new IotDeviceRunLogDO();
-                        report.setDeviceId(reportData.getDeviceId());
-                        report.setPointName(reportData.getOrgName());
-                        report.setCreateTime(reportData.getCreateTime());
-                        IotDeviceRunLogDO reportCx = iotOpeationFillService.reportData(report);
-
-                        String currentContent = "";
-                        if (reportCx != null) {
-                            currentContent = reportCx.getFillContent() != null ? reportCx.getFillContent() : "";
-                            attrsDO.setFillContent(currentContent);
-                        }
-                    }
-                }
+        // 5. 构建结果(减少对象创建)
+        IotModelTemplateAttrsDO1 resultDO = new IotModelTemplateAttrsDO1();
+        resultDO.setSumList(sumList);
+        resultDO.setNonSumList(nonSumList);
+        return Collections.singletonList(resultDO);
+    }
 
+    /**
+     * 处理虚拟设备(isReport=1)的属性详情
+     */
+    private void handleReportDeviceAttrs(List<IotModelTemplateAttrsDO> attrsList, IotOpeationFillDO fillDO2) throws SQLException {
+        if (fillDO2 == null) {
+            attrsList.forEach(attrsDO -> {
+                attrsDO.setFillContent("");
                 attrsDO.setIsCollection(0);
                 attrsDO.setIsSum(0);
-            }
-
-        } else {
-            List<YfDeviceDO> allDevice = yfDeviceService.getAllDevice();
-
-            LocalTime localTime = LocalTime.of(0, 0, 0);
-            LocalTime localTime1 = LocalTime.of(23, 59, 59);
+            });
+            return;
+        }
 
-            LocalDateTime start = LocalDateTime.of(vo.getCreateTime(), localTime);
-            LocalDateTime end = LocalDateTime.of(vo.getCreateTime(), localTime1);
+        // 批量查询reportList和reportList1(一次查询,避免循环)
+        List<IotOpeationFillDO> reportList = iotOpeationFillService.reportList(fillDO2);
+        List<IotOpeationFillDO> reportList1 = iotOpeationFillService.reportList1(fillDO2);
+
+        // 构建属性名到数据的映射(提前分组,避免重复stream过滤)
+        Map<String, List<IotOpeationFillDO>> reportDataMap = reportList.stream()
+                .collect(Collectors.groupingBy(IotOpeationFillDO::getOrgName));
+        Map<String, List<IotOpeationFillDO>> reportDataMap1 = reportList1.stream()
+                .collect(Collectors.groupingBy(IotOpeationFillDO::getOrgName));
+
+        // 批量收集需要查询的runLog参数
+        List<IotDeviceRunLogDO> runLogQueries = new ArrayList<>();
+        reportList.forEach(reportData -> {
+            IotDeviceRunLogDO query = new IotDeviceRunLogDO();
+            query.setDeviceId(reportData.getDeviceId());
+            query.setPointName(reportData.getOrgName());
+            query.setCreateTime(reportData.getCreateTime());
+            //query.setExt1(reportData.getOrgName()); // 存储orgName用于后续映射
+            runLogQueries.add(query);
+        });
 
-            Timestamp startTime = Timestamp.valueOf(start);
-            Timestamp endTime = Timestamp.valueOf(end);
+        // 批量查询runLog数据(一次查询,替代循环查询)
+        Map<Long, String> runLogFillContentMap = new HashMap<>();
+        if (!runLogQueries.isEmpty()) {
+            List<IotDeviceRunLogDO> runLogList = iotOpeationFillService.batchReportData(runLogQueries);
+            runLogFillContentMap = runLogList.stream()
+                    .collect(Collectors.toMap(
+                            logDO -> logDO.getDeviceId(), // orgName
+                            logDO -> logDO.getFillContent() != null ? logDO.getFillContent() : "",
+                            (v1, v2) -> v1 // 冲突时取第一个
+                    ));
+        }
 
-            List<YfDeviceDO> existList = allDevice.stream().filter(e -> e.getStatus() == 3).collect(Collectors.toList());
-            boolean exists1 = existList.stream().anyMatch(yfDeviceDO -> yfDeviceDO.getSerialNumber().equals(vo.getDeviceCode()));
+        // 处理属性数据
+        for (IotModelTemplateAttrsDO attrsDO : attrsList) {
+            String attrName = attrsDO.getName();
+            List<IotOpeationFillDO> cxList = reportDataMap.getOrDefault(attrName, Collections.emptyList());
+            List<IotOpeationFillDO> cxList1 = reportDataMap1.getOrDefault(attrName, Collections.emptyList());
+
+            // 处理reportList数据(累加逻辑)
+            if (!cxList.isEmpty()) {
+                double total = 0;
+                for (IotOpeationFillDO reportData : cxList) {
+                    String content = runLogFillContentMap.getOrDefault(reportData.getOrgName(), "");
+                    total += safeParseInt(content);
+                }
+                attrsDO.setFillContent(total == 0 ? "" : String.valueOf(total));
+            }
 
-            IotDeviceRunLogDO logDO1 = new IotDeviceRunLogDO();
-            logDO1.setDeviceId(vo.getDeviceId());
-            LocalTime local = LocalTime.of(12, 0);
-            logDO1.setCreateTime(LocalDateTime.of(vo.getCreateTime(), local));
+            // 处理reportList1数据(覆盖逻辑)
+            if (!cxList1.isEmpty()) {
+                IotOpeationFillDO reportData = cxList1.get(0); // 取第一条(原逻辑未处理多条,这里保持一致)
+                String content = runLogFillContentMap.getOrDefault(reportData.getOrgName(), "");
+                attrsDO.setFillContent(content);
+            }
 
-            if (exists1) {
-                for (IotModelTemplateAttrsDO attrsDO : list) {
-                    DeviceVO dv = new DeviceVO();
-                    dv.setDeviceName(vo.getDeviceCode().toLowerCase());
-                    dv.setColName(attrsDO.getModelAttr());
-                    dv.setTs(startTime);
-                    dv.setTs1(endTime);
+            attrsDO.setIsCollection(0);
+            attrsDO.setIsSum(0);
+        }
+    }
 
-                    DeviceVO deviceVO = iDeviceService.getYesInfo(dv);
+    /**
+     * 处理普通设备(isReport!=1)的属性详情
+     */
+    private void handleNormalDeviceAttrs(List<IotModelTemplateAttrsDO> attrsList,
+                                         IotModelTemplateAttrsRespVO vo,
+                                         boolean isVirtualDevice,
+                                         LocalDate createDate) throws SQLException {
+
+        Long deviceId = vo.getDeviceId();
+        String deviceCode = vo.getDeviceCode();
+
+        // 构建时间参数(一次计算,全局复用)
+        LocalDateTime start = LocalDateTime.of(createDate, LocalTime.MIN);
+        LocalDateTime end = LocalDateTime.of(createDate, LocalTime.MAX);
+        Timestamp startTime = Timestamp.valueOf(start);
+        Timestamp endTime = Timestamp.valueOf(end);
+        LocalDateTime midDay = LocalDateTime.of(createDate, LocalTime.of(12, 0));
+
+        // 批量收集DeviceVO查询参数
+        List<DeviceVO> deviceVOQueries = new ArrayList<>();
+        List<IotDeviceRunLogDO> logDOQueries = new ArrayList<>();
+        for (IotModelTemplateAttrsDO attrsDO : attrsList) {
+            if (isVirtualDevice) {
+                DeviceVO dv = new DeviceVO();
+                dv.setDeviceName(deviceCode.toLowerCase());
+                dv.setColName(attrsDO.getModelAttr());
+                dv.setTs(startTime);
+                dv.setTs1(endTime);
+                //dv.setExt1(attrsDO.getId().toString()); // 存储属性ID用于映射
+                deviceVOQueries.add(dv);
+            }
 
-                    if (!StringUtils.isEmpty(deviceVO) && !deviceVO.getEarliestData().equals("0.0")) {
-                        attrsDO.setFillContent(
-                                String.valueOf(Double.parseDouble(deviceVO.getLatestData()) - Double.parseDouble(deviceVO.getEarliestData())));
-                        attrsDO.setTotalRunTime(BigDecimal.valueOf(Double.parseDouble(deviceVO.getLatestData())));
-                        /**
-                         * 设置为数采
-                         */
-                        attrsDO.setIsCollection(1);
-                    } else {
-                        logDO1.setPointName(attrsDO.getName());
-                        IotDeviceRunLogDO logInfo = iotOpeationFillService.getLogInfo(logDO1);
-                        IotDeviceRunLogDO maxLog = iotOpeationFillService.getMaxFillInfo(logDO1);
-                        if (!StringUtils.isEmpty(logInfo)) {
+            IotDeviceRunLogDO logDO = new IotDeviceRunLogDO();
+            logDO.setDeviceId(deviceId);
+            logDO.setPointName(attrsDO.getName());
+            logDO.setCreateTime(midDay);
+            //logDO.setExt1(attrsDO.getId().toString()); // 存储属性ID用于映射
+            logDOQueries.add(logDO);
+        }
 
-                            attrsDO.setFillContent(logInfo.getFillContent());
+        // 批量查询(替代循环查询)
+        Map<String, DeviceVO> deviceVOMap = new HashMap<>();
+        if (isVirtualDevice && !deviceVOQueries.isEmpty()) {
+            List<DeviceVO> deviceVOList = iDeviceService.batchGetYesInfo(deviceVOQueries);
+            deviceVOMap = deviceVOList.stream()
+                    .filter(voObj -> !StringUtils.isEmpty(voObj.getEarliestData()) && !"0.0".equals(voObj.getEarliestData()))
+                    .collect(Collectors.toMap(DeviceVO::getDeviceName, Function.identity()));
+        }
 
-                            if (StringUtils.isEmpty(maxLog)) {
-                                attrsDO.setTotalRunTime(BigDecimal.valueOf(0));
-                            } else {
-                                attrsDO.setTotalRunTime(maxLog.getTotalRunTime());
-                            }
-                            /**
-                             * 设置为非数采
-                             */
-                            attrsDO.setIsCollection(0);
+        // 批量查询logInfo和maxLog
+        Map<String, IotDeviceRunLogDO> logInfoMap = new HashMap<>();
+        Map<String, IotDeviceRunLogDO> maxLogMap = new HashMap<>();
+        if (!logDOQueries.isEmpty()) {
+            logInfoMap = iotOpeationFillService.batchGetLogInfo(logDOQueries).stream()
+                    .collect(Collectors.toMap(IotDeviceRunLogDO::getPointName, Function.identity()));
+            maxLogMap = iotOpeationFillService.batchGetMaxFillInfo(logDOQueries).stream()
+                    .collect(Collectors.toMap(IotDeviceRunLogDO::getPointName, Function.identity()));
+        }
 
-                        } else {
-                            attrsDO.setFillContent("");
-                            if (StringUtils.isEmpty(maxLog)) {
-                                attrsDO.setTotalRunTime(BigDecimal.valueOf(0));
-                            } else {
-                                attrsDO.setTotalRunTime(maxLog.getTotalRunTime());
-                            }
-                            /**
-                             * 设置为非数采
-                             */
-                            attrsDO.setIsCollection(0);
-                        }
-                    }
+        // 处理属性数据
+        for (IotModelTemplateAttrsDO attrsDO : attrsList) {
+            String attrId = attrsDO.getId().toString();
+            String attrName = attrsDO.getName();
+            if (isVirtualDevice) {
+                DeviceVO deviceVO = deviceVOMap.get(attrId);
+                if (deviceVO != null) {
+                    // 数采设备逻辑
+                    double diff = Double.parseDouble(deviceVO.getLatestData()) - Double.parseDouble(deviceVO.getEarliestData());
+                    attrsDO.setFillContent(String.valueOf(diff));
+                    attrsDO.setTotalRunTime(BigDecimal.valueOf(Double.parseDouble(deviceVO.getLatestData())));
+                    attrsDO.setIsCollection(1);
+                    continue;
                 }
-            } else {
-                for (IotModelTemplateAttrsDO attrsDO : list) {
-                    logDO1.setPointName(attrsDO.getName());
-                    IotDeviceRunLogDO logInfo = iotOpeationFillService.getLogInfo(logDO1);
-                    IotDeviceRunLogDO maxLog = iotOpeationFillService.getMaxFillInfo(logDO1);
-                    if (!StringUtils.isEmpty(logInfo)) {
-
-                        attrsDO.setFillContent(logInfo.getFillContent());
-                        attrsDO.setTotalRunTime(logInfo.getTotalRunTime());
-
-                        /**
-                         * 设置为非数采
-                         */
-                        attrsDO.setIsCollection(0);
-
-                    } else {
-                        attrsDO.setFillContent("");
-                        if (StringUtils.isEmpty(maxLog)) {
-                            attrsDO.setTotalRunTime(BigDecimal.valueOf(0));
-                        } else {
-                            attrsDO.setTotalRunTime(maxLog.getTotalRunTime());
-                        }
-
-                        /**
-                         * 设置为非数采
-                         */
-                        attrsDO.setIsCollection(0);
-
-                    }
+            }
 
-                }
+            // 非数采设备逻辑
+            IotDeviceRunLogDO logInfo = logInfoMap.get(attrName);
+            IotDeviceRunLogDO maxLog = maxLogMap.get(attrName);
 
+            if (logInfo != null) {
+                attrsDO.setFillContent(logInfo.getFillContent());
+                attrsDO.setTotalRunTime(logInfo.getTotalRunTime() != null ? logInfo.getTotalRunTime() : BigDecimal.ZERO);
+            } else {
+                attrsDO.setFillContent("");
+                attrsDO.setTotalRunTime(maxLog != null ? maxLog.getTotalRunTime() : BigDecimal.ZERO);
             }
-
+            attrsDO.setIsCollection(0);
         }
-        List<IotModelTemplateAttrsDO> sumList = list.stream().filter(e -> e.getIsSum() == 1).collect(Collectors.toList());
-        List<IotModelTemplateAttrsDO> nonSumList = list.stream().filter(e -> e.getIsSum() == 0).collect(Collectors.toList());
-
-        IotModelTemplateAttrsDO1 sum = new IotModelTemplateAttrsDO1();
-        sum.setSumList(sumList);
-        sum.setNonSumList(nonSumList);
-
-        result.add(sum);
-
-        return result;
     }
 
 

+ 62 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotopeationfill/vo/IotOpeationFillWithAttrsVO.java

@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo;
+
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotmodeltemplateattrs.IotModelTemplateAttrsDO1;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * @author yc
+ * @version 1.0
+ * @className IotOpeationFillWithAttrsVO
+ * @date 2025/12/15 9:31
+ * @description
+ */
+@Schema(description = "新增实体类")
+@Data
+@ToString(callSuper = true)
+public class IotOpeationFillWithAttrsVO {
+    private Long id;
+    private String deviceId;
+    private String deviceCode;
+    private LocalDate createTime;
+    private Long deviceCategoryId;
+    private String deviceName;
+    private String orgName;
+    // 新增的属性
+    private List<IotModelTemplateAttrsDO1> attrsDetail;
+
+    // 3. 必须有无参构造器(Lombok的@NoArgsConstructor可自动生成,@Data包含@NoArgsConstructor)
+    public IotOpeationFillWithAttrsVO() {}
+
+    // 4. 所有属性必须有getter和setter(Lombok的@Data可自动生成)
+    // 手动生成示例(如果不用Lombok):
+    public Long getId() { return id; }
+    public void setId(Long id) { this.id = id; }
+    public String getDeviceId() { return deviceId; }
+    public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
+
+    public String getDeviceName() {
+        return deviceName;
+    }
+
+    public void setDeviceName(String deviceName) {
+        this.deviceName = deviceName;
+    }
+
+    public String getOrgName() {
+        return orgName;
+    }
+
+    public void setOrgName(String orgName) {
+        this.orgName = orgName;
+    }
+
+    // ... 其他属性的getter/setter
+    public List<IotModelTemplateAttrsDO1> getAttrsDetail() { return attrsDetail; }
+    public void setAttrsDetail(List<IotModelTemplateAttrsDO1> attrsDetail) { this.attrsDetail = attrsDetail; }
+}

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

@@ -81,6 +81,26 @@ public interface TDDeviceMapper extends BaseMapperX<TDDeviceDO> {
     @TenantIgnore
     DeviceVO getYesInfo(@Param("deviceName") String tableName, @Param("colName") String colName, @Param("ts") Timestamp ts,@Param("ts1")Timestamp ts1);
 
+    // 7. 批量查询DeviceVO
+    @Select("<script>" +
+            "<foreach collection='queries' item='item' index='index' separator='UNION ALL'>" +
+            "SELECT" +
+            "  #{item.tableName} AS table_name," + // 关联入参表名
+            "  #{item.colName} AS col_name," +     // 关联入参列名
+            "  FIRST(log_value) AS earliest_data," +
+            "  LAST(log_value) AS latest_data" +
+            " FROM iot_log.device_${item.tableName}" + // 表名动态拼接(注意SQL注入风险)
+            " WHERE" +
+            "  ts BETWEEN #{item.startTime} AND #{item.endTime}" +
+            "  AND identity = #{item.colName}" +
+            "  AND log_value != 0.0" +
+            "  AND _c0 IS NOT NULL" +
+            "</foreach>" +
+            "</script>")
+    @DS("tdengine")
+    @TenantIgnore
+    List<DeviceVO> batchGetYesInfo(@Param("queries") List<DeviceVO> queries);
+
 
     @Select("SELECT   _WSTART AS ts, AVG(CAST(log_value AS FLOAT)) as log_value   FROM iot_log.device_${deviceName} " +
             "WHERE ts between #{start} and #{end} and identity = #{identifier}  INTERVAL(1m) " )

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

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeati
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeationFillPageVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeationFillRespVO;
 import cn.iocoder.yudao.module.pms.controller.admin.iotopeationfill.vo.IotOpeationFillSaveReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.vo.DeviceVO;
 import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDeviceRespVO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotZHBD.DeviceZHBDDO;
@@ -27,6 +28,7 @@ import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -108,7 +110,7 @@ public interface IotOpeationFillMapper extends BaseMapperX<IotOpeationFillDO> {
     @TenantIgnore
     List<IotOpeationFillDO> getFillDevices(@Param("deviceIds")List<Long> deviceIds);
 
-    List<IotModelTemplateAttrsDO> getAttrsById(IotModelTemplateAttrsRespVO vo);
+
 
     @TenantIgnore
     int insertFill(List<IotOpeationFillDO> vo);
@@ -199,10 +201,8 @@ public interface IotOpeationFillMapper extends BaseMapperX<IotOpeationFillDO> {
     List<IotOpeationFillRespVO> getFillList(IotOpeationFillRespVO vo);
     @TenantIgnore
     List<IotDeviceRunLogDO> getDeivceFillInfo(IotDeviceRunLogDO vo);
-    @TenantIgnore
-    IotDeviceRunLogDO getLogInfo(IotDeviceRunLogDO vo);
-    @TenantIgnore
-    IotDeviceRunLogDO getMaxFillInfo(IotDeviceRunLogDO vo);
+
+
 
 
     @TenantIgnore
@@ -260,9 +260,15 @@ public interface IotOpeationFillMapper extends BaseMapperX<IotOpeationFillDO> {
 
     @TenantIgnore
     IotOpeationFillDO isReport(IotOpeationFillDO fillDO);
+    // 1. 批量查询isReport状态
+    @TenantIgnore
+    List<IotOpeationFillDO> batchGetIsReport(List<IotOpeationFillDO> queries);
 
     @TenantIgnore
     IotOpeationFillDO orderDO(IotOpeationFillDO fillDO);
+    // 2. 批量查询orderDO
+    @TenantIgnore
+    List<IotOpeationFillDO> batchGetOrderDO(List<IotOpeationFillDO> queries);
 
     @TenantIgnore
     IotOpeationFillDO orderDO1(IotOpeationFillDO fillDO);
@@ -270,11 +276,36 @@ public interface IotOpeationFillMapper extends BaseMapperX<IotOpeationFillDO> {
     @TenantIgnore
     List<IotOpeationFillDO> reportList(IotOpeationFillDO fillDO);
 
+
+    @TenantIgnore
+    List<IotModelTemplateAttrsDO> getAttrsById(IotModelTemplateAttrsRespVO vo);
+
+    // 3. 批量查询属性模板
+    @TenantIgnore
+    List<IotModelTemplateAttrsDO> getAttrsByIdBatch(@Param("vos") List<IotModelTemplateAttrsRespVO> vos);
+
+
+    @TenantIgnore
+    IotDeviceRunLogDO getLogInfo(IotDeviceRunLogDO vo);
+    // 5. 批量查询logInfo
+    @TenantIgnore
+    List<IotDeviceRunLogDO> batchGetLogInfo(List<IotDeviceRunLogDO> queries);
+
+    @TenantIgnore
+    IotDeviceRunLogDO getMaxFillInfo(IotDeviceRunLogDO vo);
+    // 6. 批量查询maxFillInfo
+    @TenantIgnore
+    List<IotDeviceRunLogDO> batchGetMaxFillInfo(List<IotDeviceRunLogDO> queries);
+
+
     @TenantIgnore
     List<IotOpeationFillDO> reportList1(IotOpeationFillDO fillDO);
 
     @TenantIgnore
     IotDeviceRunLogDO reportData(IotDeviceRunLogDO runLogDO);
+    // 4. 批量查询reportData
+    @TenantIgnore
+    List<IotDeviceRunLogDO> batchReportData(List<IotDeviceRunLogDO> queries);
 
     @TenantIgnore
     IotDeviceRunLogDO maxReportData(IotDeviceRunLogDO runLogDO);
@@ -297,4 +328,8 @@ public interface IotOpeationFillMapper extends BaseMapperX<IotOpeationFillDO> {
     @TenantIgnore
     AdminUserDO getUserInfo(IotDeviceRunLogDO runLogDO);
 
+
+    @TenantIgnore
+    IotDeviceRunLogDO getUserId(IotDeviceRunLogDO runLogDO);
+
 }

+ 25 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/job/IotOperationPlanJob.java

@@ -27,6 +27,9 @@ import cn.iocoder.yudao.module.system.service.dept.DeptService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Component;
 
@@ -35,8 +38,12 @@ import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;
@@ -72,6 +79,8 @@ public class IotOperationPlanJob implements JobHandler {
     private IotOpeationFillService opeationFillService;
     @Resource
     private IotRhDailyReportService iotRhDailyReportService;
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;;
     /**
      * 1、查询开启状态运行计划
      * 2、根据计划获取设备
@@ -304,7 +313,14 @@ public class IotOperationPlanJob implements JobHandler {
         });
     }
 
+
+    private static final AtomicBoolean hasExecuted = new AtomicBoolean(false);
     private void unNormalTeamInPlan(IotOperationPlanDO planDO) {
+
+        if (!hasExecuted.compareAndSet(false, true)) {
+            return; // 已经执行过
+        }
+
         Set<Long> rhIdList = new HashSet<>();
         rhIdList =  deptService.getChildDeptIdListFromCache(157L);
         rhIdList.add(157L);
@@ -319,6 +335,9 @@ public class IotOperationPlanJob implements JobHandler {
                     IotDeviceDO virDev = new IotDeviceDO();
                     virDev.setDeptId(vir);
                     IotDeviceDO devStatus = iotOpeationFillMapper.devStatus(virDev);
+                    IotDeviceRunLogDO cx = new IotDeviceRunLogDO();
+                    cx.setDeptId(vir);
+                    IotDeviceRunLogDO result = iotOpeationFillMapper.getUserId(cx);
                     //不为空则为日报赋值
                     if(devStatus!=null){
                         saveReqVO.setConstructionStatus(devStatus.getDeviceStatus());
@@ -337,6 +356,9 @@ public class IotOperationPlanJob implements JobHandler {
                     saveReqVO.setNonProductionTime(BigDecimal.valueOf(0.00));
                     saveReqVO.setDeptId(vir);
                     saveReqVO.setFillOrderCreateTime(LocalDateTime.now());
+                    if(result!=null){
+                        saveReqVO.setCreator(String.valueOf(result.getDeviceId()));
+                    }
                     iotRhDailyReportService.createIotRhDailyReport(saveReqVO);
                 }
             }
@@ -344,6 +366,8 @@ public class IotOperationPlanJob implements JobHandler {
     }
 
 
+
+
     private void ryReportInsert(IotOperationPlanDO plan, List<IotOpeationFillOrderDO> orderList1, boolean ryContain) {
         if(ryContain){
             //创建日报设备
@@ -411,6 +435,7 @@ public class IotOperationPlanJob implements JobHandler {
                     saveReqVO.setNonProductionTime(BigDecimal.valueOf(0.00));
                     saveReqVO.setDeptId(vir.getDeptId());
                     saveReqVO.setFillOrderCreateTime(LocalDateTime.now());
+                    saveReqVO.setCreator(String.valueOf(vir.getUserId()));
                     iotRhDailyReportService.createIotRhDailyReport(saveReqVO);
                 }
             }

+ 4 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/DeviceServiceImpl.java

@@ -51,6 +51,10 @@ public class DeviceServiceImpl implements IDeviceService {
         return deviceMapper.getYesInfo(deviceVO.getDeviceName(), deviceVO.getColName(),deviceVO.getTs(),deviceVO.getTs1());
     }
 
+    @Override
+    public List<DeviceVO> batchGetYesInfo(List<DeviceVO> queries) {
+        return deviceMapper.batchGetYesInfo(queries);
+    }
 
 
 }

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

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.pms.service;
 
 import cn.iocoder.yudao.module.pms.controller.admin.vo.DeviceVO;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -33,4 +34,6 @@ public interface IDeviceService {
 
     DeviceVO getYesInfo(DeviceVO deviceVO);
 
+    List<DeviceVO> batchGetYesInfo(List<DeviceVO> queries);
+
 }

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

@@ -19,6 +19,7 @@ import org.apache.ibatis.annotations.Param;
 import javax.validation.Valid;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -213,4 +214,16 @@ public interface IotOpeationFillService {
 
     List<IotDeviceRunLogDO> batchGetDescByPointNames(Set<String> pointNameSet);
 
+    Map<Long, Integer> batchGetIsReport(List<IotOpeationFillDO> queries);
+
+    List<IotModelTemplateAttrsDO> getAttrsByIdBatch(List<IotModelTemplateAttrsRespVO> vos);
+
+    List<IotDeviceRunLogDO> batchGetLogInfo(List<IotDeviceRunLogDO> queries);
+
+    List<IotDeviceRunLogDO> batchGetMaxFillInfo(List<IotDeviceRunLogDO> queries);
+
+    List<IotDeviceRunLogDO> batchReportData(List<IotDeviceRunLogDO> queries);
+
+    Map<Long, IotOpeationFillDO> batchGetOrderDO(List<IotOpeationFillDO> queries);
+
 }

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

@@ -35,6 +35,7 @@ import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -503,4 +504,58 @@ public class IotOpeationFillServiceImpl implements IotOpeationFillService {
         return iotOpeationFillMapper.batchGetDescByPointNames(pointNameSet);
     }
 
+    @Override
+    public Map<Long, Integer> batchGetIsReport(List<IotOpeationFillDO> queries) {
+        List<IotOpeationFillDO> result = iotOpeationFillMapper.batchGetIsReport(queries);
+        Map<Long, Integer> map = result.stream()
+                // 过滤 null 元素(避免空指针,可选但推荐)
+                .filter(doObj -> doObj != null)
+                // 生成 Map:Key 为 DO 的 Long 字段,Value 为 DO 的 Integer 字段
+                .collect(Collectors.toMap(
+                        // Key 映射:替换为你的实际字段 getter(如 getId()、getDeviceId())
+                        IotOpeationFillDO::getDeviceId,
+                        // Value 映射:替换为你的实际字段 getter(如 getIsReport()、getStatus())
+                        IotOpeationFillDO::getIsReport,
+                        // 冲突解决策略(可选):若 Key 重复,取第一个值(或自定义逻辑)
+                        (v1, v2) -> v1
+                ));
+        return map;
+    }
+
+    @Override
+    public List<IotModelTemplateAttrsDO> getAttrsByIdBatch(List<IotModelTemplateAttrsRespVO> vos) {
+        return iotOpeationFillMapper.getAttrsByIdBatch(vos);
+    }
+
+    @Override
+    public List<IotDeviceRunLogDO> batchGetLogInfo(List<IotDeviceRunLogDO> queries) {
+        return iotOpeationFillMapper.batchGetLogInfo(queries);
+    }
+
+    @Override
+    public List<IotDeviceRunLogDO> batchGetMaxFillInfo(List<IotDeviceRunLogDO> queries) {
+        return iotOpeationFillMapper.batchGetMaxFillInfo(queries);
+    }
+
+    @Override
+    public List<IotDeviceRunLogDO> batchReportData(List<IotDeviceRunLogDO> queries) {
+        return iotOpeationFillMapper.batchReportData(queries);
+    }
+
+    @Override
+    public Map<Long, IotOpeationFillDO> batchGetOrderDO(List<IotOpeationFillDO> queries) {
+        List<IotOpeationFillDO> result = iotOpeationFillMapper.batchGetOrderDO(queries);
+        // 核心:替换 -> 后的 getXXX() 为实际的 Long 类型字段 getter 方法
+        Map<Long, IotOpeationFillDO> fillDOMap = result.stream()
+                // 第一个参数:Map 的 Key(取自 DO 的 Long 字段)
+                // 第二个参数:Map 的 Value(DO 本身)
+                .collect(Collectors.toMap(
+                        IotOpeationFillDO::getId,  // 示例:用 id 作为 Key,替换为实际字段(如 getOrderId())
+                        Function.identity(),       // Value:DO本身(简化写法)
+                        (oldValue, newValue) -> oldValue  // 重复Key时保留旧值(核心修复点)
+                        // 可选:如需保留新值,改为 (oldValue, newValue) -> newValue           // Value 直接是 DO 实例(可简化为 Function.identity())
+                ));
+        return fillDOMap;
+    }
+
 }

+ 211 - 16
yudao-module-pms/yudao-module-pms-biz/src/main/resources/mapper/static/IotOpeationFillMapper.xml

@@ -758,6 +758,40 @@
             device_category_id = #{deviceCategoryId} and deleted = 0
     </select>
 
+
+    <!--
+批量查询属性模板:入参为List<IotDeviceRespVO>(含多个deviceCategoryId)
+出参:List<IotModelTemplateAttrsDO>(所有分类ID对应的属性模板,包含deviceCategoryId便于分组)
+-->
+    <select id="getAttrsByIdBatch" parameterType="java.util.List"
+            resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotmodeltemplateattrs.IotModelTemplateAttrsDO">
+        SELECT
+        id,
+        name,
+        type,
+        model_attr,
+        is_sum,
+        threshold,
+        default_value,
+        sum_id,
+        description,
+        device_category_id
+        FROM
+        rq_iot_model_template_attrs
+        WHERE
+        -- 批量匹配多个deviceCategoryId,deleted=0条件不变
+        device_category_id IN (
+        <foreach collection="vos" item="item" separator="," open="" close="">
+            #{item.deviceCategoryId}
+        </foreach>
+        )
+        AND deleted = 0
+        -- 按分类ID和属性名称排序,保持结果一致性
+        ORDER BY
+        device_category_id ASC, name ASC
+    </select>
+
+
     <update id="updateFill"  parameterType="cn.iocoder.yudao.module.pms.controller.admin.vo.IotOpeationModelPageReqVO">
         update  rq_iot_opeation_fill
         set is_fill = '1'
@@ -819,12 +853,6 @@
 
     <select id="getLogInfo" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO"
             resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
-        <!--SELECT * FROM rq_iot_device_run_log
-        WHERE
-            device_id = #{deviceId}
-        and
-            create_time  = (SELECT MAX(create_time) FROM rq_iot_device_run_log WHERE
-                device_id = #{deviceId})-->
         SELECT
         *
         FROM rq_iot_device_run_log
@@ -839,15 +867,32 @@
         limit 1
     </select>
 
-    <select id="getMaxFillInfo" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO"
+    <select id="batchGetLogInfo" parameterType="java.util.List"
             resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
-        <!--SELECT * FROM rq_iot_device_run_log
-        WHERE
-            device_id = #{deviceId}
-        and
-            create_time  = (SELECT MAX(create_time) FROM rq_iot_device_run_log WHERE
-                device_id = #{deviceId})-->
+        SELECT
+            *
+        FROM rq_iot_device_run_log r
+        WHERE-- 批量匹配多个 (device_id, point_name, DATE(create_time)) 三元组
+        (r.device_id, r.point_name, DATE(r.create_time)) IN (
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deviceId}, #{item.pointName}, DATE(#{item.createTime}))
+        </foreach>
+        )
+        -- 确保每个条件只返回最新一条记录(按id降序,取最大id)
+        AND r.id IN (
+        SELECT MAX(id)
+        FROM rq_iot_device_run_log
+        WHERE (device_id, point_name, DATE(create_time)) IN (
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deviceId}, #{item.pointName}, DATE(#{item.createTime}))
+        </foreach>
+        )
+        GROUP BY device_id, point_name, DATE(create_time)
+        )
+    </select>
 
+    <select id="getMaxFillInfo" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO"
+            resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
         select max(total_run_time)total_run_time
         from rq_iot_device_run_log
         where
@@ -856,6 +901,57 @@
         point_name=#{pointName}
     </select>
 
+    <!--<select id="batchGetMaxFillInfo" parameterType="java.util.List"
+            resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
+        select
+        device_id,
+        point_name,
+        max(total_run_time)total_run_time
+        from rq_iot_device_run_log
+        where
+        &#45;&#45; 批量匹配多个 (device_id, point_name) 组合
+        (device_id, point_name) IN (
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deviceId}, #{item.pointName})
+        </foreach>
+        )
+        group by
+        device_id
+    </select>-->
+
+    <select id="batchGetMaxFillInfo" parameterType="java.util.List"
+            resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
+        SELECT
+        device_id,
+        point_name,
+        total_run_time  -- 直接取最新记录的 total_run_time,无需 max()(最新数据已通过排序筛选)
+        FROM (
+        SELECT
+        device_id,
+        point_name,
+        total_run_time,
+        create_time,  -- 表中的时间字段,替换为实际字段名(如 log_time/update_time)
+        -- 按 (device_id, point_name) 分组,每组内按时间倒序排名
+        ROW_NUMBER() OVER (
+        PARTITION BY device_id, point_name
+        ORDER BY create_time DESC  -- 时间倒序:最新的排第1
+        -- 可选:若同一时间有重复记录,加次要排序(如 id DESC)确保唯一
+        -- ORDER BY create_time DESC, id DESC
+        ) AS rn
+        FROM rq_iot_device_run_log
+        WHERE
+        -- 批量匹配多个 (device_id, point_name) 组合(保留原 foreach 逻辑)
+        (device_id, point_name) IN (
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deviceId}, #{item.pointName})
+        </foreach>
+        )
+        ) AS temp
+        WHERE rn = 1;  -- 只取每个组合的最新记录(排名第1)
+    </select>
+
+
+
 
     <select id="devList" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO"
             resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO">
@@ -1155,6 +1251,37 @@
             LIMIT 1
     </select>
 
+
+    <!--
+批量查询isReport状态:入参为List<IotOpeationFillDO>(包含deviceId和createTime)
+出参:List<IotOpeationFillDO>(每条记录对应一个deviceId+createTime组合的查询结果)
+-->
+    <select id="batchGetIsReport" parameterType="java.util.List"
+            resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO">
+        SELECT
+        t.device_id,
+        COALESCE(t.is_report, 0) AS is_report
+        FROM (
+        -- 子查询:先过滤批量匹配的记录,再按设备+日期分组取最新1条
+        SELECT
+        *,
+        -- 分组内排序:按create_time降序(最新的排第1)
+        ROW_NUMBER() OVER (
+        PARTITION BY device_id, DATE(create_time)  -- 分组键:设备+日期
+        ORDER BY create_time DESC  -- 取数规则:最新的1条(可修改)
+        ) AS rn
+        FROM rq_iot_opeation_fill
+        WHERE
+        -- 批量匹配:(设备ID, 日期) 组合(与之前批量查询逻辑兼容)
+        (device_id, DATE(create_time)) IN
+        <foreach collection="list" item="item" separator="," open="(" close=")">
+            (#{item.deviceId}, DATE(#{item.createTime}))
+        </foreach>
+        ) t
+        WHERE t.rn = 1;  -- 只保留每组第1条(即每个设备每天1条)
+    </select>
+
+
     <select id="orderDO" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO"
             resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO">
         select
@@ -1167,6 +1294,37 @@
             DATE(create_time) = #{createTime}
     </select>
 
+    <select id="batchGetOrderDO" parameterType="java.util.List"
+            resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO">
+        select
+            order_id
+        from(
+        -- 内层查询:批量匹配条件 + 去重(同一 deviceId+日期 取最新记录)
+        SELECT
+        order_id
+        FROM
+        rq_iot_opeation_fill
+        WHERE
+        (device_id, DATE(create_time)) IN (
+        <foreach collection="list" item="item" separator=",">
+            -- 匹配入参中的 deviceId 和 日期(DATE(createTime) 与原逻辑一致)
+            (#{item.deviceId}, DATE(#{item.createTime}))
+        </foreach>
+        )
+        -- 去重逻辑:按 id 降序,确保每个分组取最新记录
+        AND id IN (
+        SELECT MAX(id) -- 取最新记录的 id
+        FROM rq_iot_opeation_fill
+        WHERE (device_id, DATE(create_time)) IN (
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deviceId}, DATE(#{item.createTime}))
+        </foreach>
+        )
+        GROUP BY device_id, DATE(create_time) -- 按 deviceId+日期 分组
+        )
+        ) AS iof
+    </select>
+
 
     <select id="orderDO1" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO"
             resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO">
@@ -1253,6 +1411,29 @@
           and DATE(create_time) = DATE(#{createTime})
     </select>
 
+    <!--
+批量查询设备运行日志:入参为List<IotDeviceRunLogDO>(含deviceId、pointName、createTime)
+出参:List<IotDeviceRunLogDO>(所有匹配条件的日志记录,结构与原单查一致)
+-->
+    <select id="batchReportData" parameterType="java.util.List"
+            resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
+        SELECT
+        *
+        -- 如需其他字段,按原单查SQL补充(原SQL用select *,此处显式列出避免冗余)
+        FROM
+        rq_iot_device_run_log
+        WHERE
+        -- 批量匹配多个 (device_id, point_name, DATE(create_time)) 三元组条件
+        (device_id, point_name, DATE(create_time)) IN (
+        <foreach collection="list" item="item" separator=",">
+            (#{item.deviceId}, #{item.pointName}, DATE(#{item.createTime}))
+        </foreach>
+        )
+        -- 按创建时间降序,保持结果一致性(可选,与原单查行为对齐)
+        ORDER BY
+        create_time DESC
+    </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
@@ -1280,12 +1461,11 @@
 
     <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
+        FROM rq_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}))
+            AND t.point_name = #{item.pointName})
         </foreach>
         GROUP BY t.device_id, t.point_name
     </select>
@@ -1349,6 +1529,21 @@
         select * from system_users where dept_id = #{deptId} and nickname = '技术员'
     </select>
 
+    <select id="getUserId" parameterType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO"
+            resultType="cn.iocoder.yudao.module.pms.dal.dataobject.iotdevicerunlog.IotDeviceRunLogDO">
+        select distinct a.person_id device_id from
+            rq_iot_device_person a
+                left join
+            rq_iot_device b
+            on a.device_id = b.id
+                left join
+            system_dept c
+            on b.dept_id = c.id
+        where c.id = #{deptId}
+            limit 1
+    </select>
+
+    
 
 
 </mapper>