Преглед изворни кода

Merge remote-tracking branch 'origin/master'

Zimo пре 2 недеља
родитељ
комит
2c678f0cb1

+ 627 - 2
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotoperationmeeting/IotOperationMeetingController.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeeting;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageParam;
@@ -33,6 +34,8 @@ import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -130,11 +133,93 @@ public class IotOperationMeetingController {
 
         IotOperationMeetingRespVO meetingResp = BeanUtils.toBean(operationMeeting, IotOperationMeetingRespVO.class);
         // 设置公司名称
-        Long deptId = operationMeeting.getDeptId();
-        DeptDO dept = deptService.getDept(deptId);
+        Long meetingDepartmentId = operationMeeting.getDeptId();
+        DeptDO dept = deptService.getDept(meetingDepartmentId);
         if (ObjUtil.isNotEmpty(dept)) {
             meetingResp.setCompanyName(dept.getName());
         }
+
+        // 查询上一期次的会议的财务数据及工作量数据 计算环比 完成率...
+        // key专业公司id   value专业公司计划注气量累加
+        Map<Long, BigDecimal> companyPlanGasInjectionPair = new HashMap<>();
+        // key专业公司id   value专业公司实际注气量累加
+        Map<Long, BigDecimal> companyActualGasInjectionPair = new HashMap<>();
+        // key专业公司id   value专业公司累计注气量累加
+        Map<Long, BigDecimal> companyCumulativeGasInjectionPair = new HashMap<>();
+        // 上期次会议 key专业公司id   value专业公司投运设备数量累加
+        Map<Long, BigDecimal> beforeCompanyInServiceDeviceNumPair = new HashMap<>();
+        // 上期次会议 key专业公司id   value专业公司施工设备数量累加
+        Map<Long, BigDecimal> beforeCompanyConstructionDeviceNumPair = new HashMap<>();
+        // key专业公司id   value专业公司投运设备数量累加
+        Map<Long, BigDecimal> companyInServiceDeviceNumPair = new HashMap<>();
+        // key专业公司id   value专业公司施工设备数量累加
+        Map<Long, BigDecimal> companyConstructionDeviceNumPair = new HashMap<>();
+        // key专业公司id   value专业公司各项目计划进尺累加
+        Map<Long, BigDecimal> companyPlanFootagePair = new HashMap<>();
+        // key专业公司id   value专业公司各项目实际进尺累加
+        Map<Long, BigDecimal> companyActualFootagePair = new HashMap<>();
+        // key专业公司id   value专业公司各项目累计进尺累加
+        Map<Long, BigDecimal> companyCumulativeFootagePair = new HashMap<>();
+        // key专业公司id   value专业公司各项目计划井次累加
+        Map<Long, BigDecimal> companyPlanWellTripsPair = new HashMap<>();
+        // key专业公司id   value专业公司各项目实际井次累加
+        Map<Long, BigDecimal> companyActualWellTripsPair = new HashMap<>();
+        // key专业公司id   value专业公司各项目累计井次累加
+        Map<Long, BigDecimal> companyCumulativeWellTripsPair = new HashMap<>();
+        // key专业公司id   value专业公司各项目计划层数累加
+        Map<Long, BigDecimal> companyPlanLayersPair = new HashMap<>();
+        // key专业公司id   value专业公司各项目实际层数累加
+        Map<Long, BigDecimal> companyActualLayersPair = new HashMap<>();
+        // key专业公司id   value专业公司各项目累计层数累加
+        Map<Long, BigDecimal> companyCumulativeLayersPair = new HashMap<>();
+
+        // key会议id   value当前会议/公司下的项目明细
+        Map<Long, List<IotOperationMeetingDetailDO>> companyMeetingIdDetailPair = new HashMap<>();
+
+        LocalDateTime meetingDate = operationMeeting.getMeetingDate();
+        // 查询当前年上一期次 的会议明细详情
+        IotOperationMeetingDetailPageReqVO beforeReqVO = new IotOperationMeetingDetailPageReqVO();
+        beforeReqVO.setYear(meetingDate.getYear());
+        beforeReqVO.setSort(operationMeeting.getSort() - 1);
+        List<IotOperationMeetingDetailDO> beforeSummarizedDetails = iotOperationMeetingDetailService.summarizedProjectDetails(beforeReqVO);
+        // 组装上一期次每个专业公司各项目的累加财务数据 本期 收入 挂账 回款 并计算 环比
+        if (CollUtil.isNotEmpty(beforeSummarizedDetails)) {
+            // 先以公司为单位 组装每个专业公司 的项目明细 然后再组装各专业公司的工作量细化数据
+            Map<Long, List<IotOperationMeetingDetailDO>> beforeCompanyDeptIdDetailPair = beforeSummarizedDetails.stream()
+                    .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId));
+            if (CollUtil.isNotEmpty(beforeCompanyDeptIdDetailPair)) {
+                beforeCompanyDeptIdDetailPair.forEach((departmentId, details) -> {
+                    // key专业公司id    details公司各项目明细
+                    if (CollUtil.isNotEmpty(details)) {
+                        details.forEach(detail -> {
+                            // 组装细分的工作量
+                            List<IotTaskAttrModelProperty> properties = detail.getExtProperty();
+                            if (CollUtil.isNotEmpty(properties)) {
+                                // 公共属性 投运设备 施工设备
+                                BigDecimal inServiceDeviceNum = getActualValue(properties, "inServiceDeviceNum");
+                                BigDecimal constructionDeviceNum = getActualValue(properties, "constructionDeviceNum");
+                                beforeCompanyInServiceDeviceNumPair.merge(departmentId, inServiceDeviceNum, BigDecimal::add);
+                                beforeCompanyConstructionDeviceNumPair.merge(departmentId   , constructionDeviceNum, BigDecimal::add);
+                            }
+                        });
+                    }
+                });
+            }
+            // key公司id   value上一期次公司本期收入
+            Map<Long, BigDecimal> beforeRevenuePair = beforeSummarizedDetails.stream()
+                    .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId,
+                            Collectors.reducing(BigDecimal.ZERO, IotOperationMeetingDetailDO::getCurrentRevenue, BigDecimal::add)));
+            // key公司id   value上一期次公司本期挂账
+            Map<Long, BigDecimal> beforeOnAccountPair = beforeSummarizedDetails.stream()
+                    .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId,
+                            Collectors.reducing(BigDecimal.ZERO, IotOperationMeetingDetailDO::getCurrentOnAccount, BigDecimal::add)));
+            // key公司id   value上一期次公司本期回款
+            Map<Long, BigDecimal> beforePaymentPair = beforeSummarizedDetails.stream()
+                    .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId,
+                            Collectors.reducing(BigDecimal.ZERO, IotOperationMeetingDetailDO::getCurrentPayment, BigDecimal::add)));
+
+        }
+
         // 查询运营会明细
         IotOperationMeetingDetailPageReqVO pageReqVO = new IotOperationMeetingDetailPageReqVO();
         pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
@@ -144,6 +229,91 @@ public class IotOperationMeetingController {
             List<IotOperationMeetingDetailDO> details = pageDetails.getList();
             // 显示明细时 不显示 required = 0 的扩展属性 完成比 环比等
             if (CollUtil.isNotEmpty(details)) {
+                // 计算本期各公司财务数据集合
+                // key公司id   value本期次公司本期收入
+                Map<Long, BigDecimal> currentRevenuePair = details.stream()
+                        .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId,
+                                Collectors.reducing(BigDecimal.ZERO, IotOperationMeetingDetailDO::getCurrentRevenue, BigDecimal::add)));
+                // key公司id   value本期次公司累计收入
+                Map<Long, BigDecimal> currentTotalRevenuePair = details.stream()
+                        .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId,
+                                Collectors.reducing(BigDecimal.ZERO, IotOperationMeetingDetailDO::getCumulativeRevenue, BigDecimal::add)));
+                // key公司id   value本期次公司本期挂账
+                Map<Long, BigDecimal> currentOnAccountPair = details.stream()
+                        .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId,
+                                Collectors.reducing(BigDecimal.ZERO, IotOperationMeetingDetailDO::getCurrentOnAccount, BigDecimal::add)));
+                // key公司id   value本期次公司累计挂账
+                Map<Long, BigDecimal> currentTotalOnAccountPair = details.stream()
+                        .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId,
+                                Collectors.reducing(BigDecimal.ZERO, IotOperationMeetingDetailDO::getCumulativeOnAccount, BigDecimal::add)));
+                // key公司id   value本期次公司本期回款
+                Map<Long, BigDecimal> currentPaymentPair = details.stream()
+                        .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId,
+                                Collectors.reducing(BigDecimal.ZERO, IotOperationMeetingDetailDO::getCurrentPayment, BigDecimal::add)));
+                // key公司id   value本期次公司累计回款
+                Map<Long, BigDecimal> currentTotalPaymentPair = details.stream()
+                        .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId,
+                                Collectors.reducing(BigDecimal.ZERO, IotOperationMeetingDetailDO::getCumulativePayment, BigDecimal::add)));
+
+                // key部门id   value当前会议/公司下的项目明细
+                Map<Long, List<IotOperationMeetingDetailDO>> companyDeptIdDetailPair = details.stream()
+                        .collect(Collectors.groupingBy(IotOperationMeetingDetailDO::getDeptId));
+
+                companyDeptIdDetailPair.forEach((deptId, meetingDetails) -> {
+                    // key专业公司id    details公司各项目明细
+                    if (CollUtil.isNotEmpty(meetingDetails)) {
+                        meetingDetails.forEach(detail -> {
+                            // 组装细分的工作量
+                            List<IotTaskAttrModelProperty> properties = detail.getExtProperty();
+                            if (CollUtil.isNotEmpty(properties)) {
+                                // 公共属性 投运设备 施工设备
+                                BigDecimal inServiceDeviceNum = getActualValue(properties, "inServiceDeviceNum");
+                                BigDecimal constructionDeviceNum = getActualValue(properties, "constructionDeviceNum");
+                                companyInServiceDeviceNumPair.merge(deptId, inServiceDeviceNum, BigDecimal::add);
+                                companyConstructionDeviceNumPair.merge(deptId, constructionDeviceNum, BigDecimal::add);
+                                // 163 瑞都
+                                // 388 5#公司
+                                if (Long.valueOf(157).equals(deptId)) {
+                                    // 157 瑞恒 计划注气 实际注气 累计注气 总设备利用率及环比 总注气量完成比
+                                    BigDecimal planGasInjection = getActualValue(properties, "planGasInjection");
+                                    BigDecimal actualGasInjection = getActualValue(properties, "actualGasInjection");
+                                    BigDecimal cumulativeGasInjection = getActualValue(properties, "cumulativeGasInjection");
+                                    companyPlanGasInjectionPair.merge(deptId, planGasInjection, BigDecimal::add);
+                                    companyActualGasInjectionPair.merge(deptId, actualGasInjection, BigDecimal::add);
+                                    companyCumulativeGasInjectionPair.merge(deptId, cumulativeGasInjection, BigDecimal::add);
+                                }
+                                if (Long.valueOf(158).equals(deptId)) {
+                                    // 158 瑞鹰 计划进尺 实际进尺 完成比 累计进尺 总设备利用率及环比 计划井次 实际井次 完成比 累计井次
+                                    BigDecimal planFootage = getActualValue(properties, "planFootage");
+                                    BigDecimal actualFootage = getActualValue(properties, "actualFootage");
+                                    BigDecimal cumulativeFootage = getActualValue(properties, "cumulativeFootage");
+                                    companyPlanFootagePair.merge(deptId, planFootage, BigDecimal::add);
+                                    companyActualFootagePair.merge(deptId, actualFootage, BigDecimal::add);
+                                    companyCumulativeFootagePair.merge(deptId, cumulativeFootage, BigDecimal::add);
+                                }
+                                if (Long.valueOf(163).equals(deptId) || Long.valueOf(388).equals(deptId)) {
+                                    // 163 瑞都 计划层数 实际层数 完成比 累计层数 总设备利用率及环比 计划井次 实际井次 完成比 累计井次
+                                    BigDecimal planLayers = getActualValue(properties, "planLayers");
+                                    BigDecimal actualLayers = getActualValue(properties, "actualLayers");
+                                    BigDecimal cumulativeLayers = getActualValue(properties, "cumulativeLayers");
+                                    companyPlanLayersPair.merge(deptId, planLayers, BigDecimal::add);
+                                    companyActualLayersPair.merge(deptId, actualLayers, BigDecimal::add);
+                                    companyCumulativeLayersPair.merge(deptId, cumulativeLayers, BigDecimal::add);
+                                }
+                                if (Long.valueOf(163).equals(deptId) || Long.valueOf(158).equals(deptId)) {
+                                    // 瑞都 瑞鹰 同时具备 井次属性
+                                    BigDecimal planWellTrips = getActualValue(properties, "planWellTrips");
+                                    BigDecimal actualWellTrips = getActualValue(properties, "actualWellTrips");
+                                    BigDecimal cumulativeWellTrips = getActualValue(properties, "cumulativeWellTrips");
+                                    companyPlanWellTripsPair.merge(deptId, planWellTrips, BigDecimal::add);
+                                    companyActualWellTripsPair.merge(deptId, actualWellTrips, BigDecimal::add);
+                                    companyCumulativeWellTripsPair.merge(deptId, cumulativeWellTrips, BigDecimal::add);
+                                }
+                            }
+                        });
+                    }
+                });
+
                 details.forEach(detail -> {
                     // 修改后的扩展属性列表
                     List<IotTaskAttrModelProperty> modifiedExtProperties = new ArrayList<>();
@@ -157,6 +327,438 @@ public class IotOperationMeetingController {
                         });
                         detail.setExtProperty(modifiedExtProperties);
                     }
+
+                    companyMeetingIdDetailPair.computeIfAbsent(detail.getMeetingId(), k -> new ArrayList<>()).add(detail);
+                });
+
+                companyMeetingIdDetailPair.forEach((meetingId, meetingDetails) -> {
+                    // 各专业公司会议 工作量扩展属性集合
+                    List<IotTaskAttrModelProperty> companyExtProperties = new ArrayList<>();
+                    // meetingId会议id   details各专业公司的会议明细
+                    // IotOperationMeetingRespVO tempMeeting = new IotOperationMeetingRespVO();
+                        Long deptId = operationMeeting.getDeptId();
+                        // 上期收入
+                        BigDecimal beforeRevenue = CollUtil.isEmpty(beforeSummarizedDetails) ? BigDecimal.ZERO :
+                                beforeSummarizedDetails.stream()
+                                        .filter(d -> deptId.equals(d.getDeptId()))
+                                        .map(IotOperationMeetingDetailDO::getCurrentRevenue)
+                                        .reduce(BigDecimal.ZERO, BigDecimal::add);
+                        // 设置 各专业公司 财务数据汇总
+                        if (currentRevenuePair.containsKey(deptId)) {
+                            BigDecimal currentRevenue = currentRevenuePair.get(deptId);
+                            // 本期收入
+                            meetingResp.setCurrentRevenue(currentRevenue);
+                            // 计算本期收入 环比 (本期收入-上期收入)/上期收入
+                            if (beforeRevenue.compareTo(BigDecimal.ZERO) > 0) {
+                                BigDecimal revenuePeriodOnPeriod = currentRevenue.subtract(beforeRevenue).divide(beforeRevenue, 4, RoundingMode.HALF_UP);
+                                meetingResp.setRevenuePeriodOnPeriod(revenuePeriodOnPeriod);
+                            }
+                        }
+                        meetingResp.setBeforeRevenue(beforeRevenue);
+                        if (currentTotalRevenuePair.containsKey(deptId)) {
+                            // 本期收入 累计
+                            meetingResp.setCumulativeRevenue(currentTotalRevenuePair.get(deptId));
+                        }
+                        // 上期挂账
+                        BigDecimal beforeOnAccount = CollUtil.isEmpty(beforeSummarizedDetails) ? BigDecimal.ZERO :
+                                beforeSummarizedDetails.stream()
+                                        .filter(d -> deptId.equals(d.getDeptId()))
+                                        .map(IotOperationMeetingDetailDO::getCurrentOnAccount)
+                                        .reduce(BigDecimal.ZERO, BigDecimal::add);
+                        if (currentOnAccountPair.containsKey(deptId)) {
+                            BigDecimal currentOnAccount = currentOnAccountPair.get(deptId);
+                            // 本期挂账
+                            meetingResp.setCurrentOnAccount(currentOnAccount);
+                            // 计算本期挂账 环比 (本期挂账-上期挂账)/上期挂账
+                            if (beforeOnAccount.compareTo(BigDecimal.ZERO) > 0) {
+                                BigDecimal onAccountPeriodOnPeriod = currentOnAccount.subtract(beforeOnAccount).divide(beforeOnAccount, 4, RoundingMode.HALF_UP);
+                                meetingResp.setOnAccountPeriodOnPeriod(onAccountPeriodOnPeriod);
+                            }
+                        }
+                        meetingResp.setBeforeOnAccount(beforeOnAccount);
+                        if (currentTotalOnAccountPair.containsKey(deptId)) {
+                            // 本期挂账 累计
+                            meetingResp.setCumulativeOnAccount(currentTotalOnAccountPair.get(deptId));
+                        }
+                        // 上期回款
+                        BigDecimal beforePayment = CollUtil.isEmpty(beforeSummarizedDetails) ? BigDecimal.ZERO :
+                                beforeSummarizedDetails.stream()
+                                        .filter(d -> deptId.equals(d.getDeptId()))
+                                        .map(IotOperationMeetingDetailDO::getCurrentPayment)
+                                        .reduce(BigDecimal.ZERO, BigDecimal::add);
+                        if (currentPaymentPair.containsKey(deptId)) {
+                            // 本期回款
+                            BigDecimal currentPayment = currentPaymentPair.get(deptId);
+                            meetingResp.setCurrentPayment(currentPayment);
+                            // 计算回款 环比 (本期回款-上期回款)/上期回款
+                            if (beforePayment.compareTo(BigDecimal.ZERO) > 0) {
+                                BigDecimal paymentPeriodOnPeriod = currentPayment.subtract(beforePayment).divide(beforePayment, 4, RoundingMode.HALF_UP);
+                                meetingResp.setPaymentPeriodOnPeriod(paymentPeriodOnPeriod);
+                            }
+                        }
+                        meetingResp.setBeforePayment(beforePayment);
+                        if (currentTotalPaymentPair.containsKey(deptId)) {
+                            // 本期回款 累计
+                            meetingResp.setCumulativePayment(currentTotalPaymentPair.get(deptId));
+                        }
+                        // 设置各专业公司的工作量细化数据
+                        // 瑞恒 注气量 细化数据
+                        if (companyPlanGasInjectionPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty planGasProperty = new IotTaskAttrModelProperty();
+                            planGasProperty.setName("计划注气量");
+                            planGasProperty.setAccessMode("r");
+                            planGasProperty.setRequired(1);
+                            planGasProperty.setSort(1);
+                            planGasProperty.setUnit("万方");
+                            planGasProperty.setDataType("double");
+                            planGasProperty.setIdentifier("planGasInjection");
+                            planGasProperty.setActualValue(companyPlanGasInjectionPair.get(deptId).toString());
+                            planGasProperty.setMaxValue(StrUtil.EMPTY);
+                            planGasProperty.setMinValue(StrUtil.EMPTY);
+                            planGasProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(planGasProperty);
+                        }
+                        if (companyActualGasInjectionPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty actualGasProperty = new IotTaskAttrModelProperty();
+                            actualGasProperty.setName("实际注气量");
+                            actualGasProperty.setAccessMode("r");
+                            actualGasProperty.setRequired(1);
+                            actualGasProperty.setSort(2);
+                            actualGasProperty.setUnit("万方");
+                            actualGasProperty.setDataType("double");
+                            actualGasProperty.setIdentifier("actualGasInjection");
+                            actualGasProperty.setActualValue(companyActualGasInjectionPair.get(deptId).toString());
+                            actualGasProperty.setMaxValue(StrUtil.EMPTY);
+                            actualGasProperty.setMinValue(StrUtil.EMPTY);
+                            actualGasProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(actualGasProperty);
+                        }
+                        if (companyCumulativeGasInjectionPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty cumulativeGasProperty = new IotTaskAttrModelProperty();
+                            cumulativeGasProperty.setName("累计注气量");
+                            cumulativeGasProperty.setAccessMode("r");
+                            cumulativeGasProperty.setRequired(1);
+                            cumulativeGasProperty.setSort(4);
+                            cumulativeGasProperty.setUnit("万方");
+                            cumulativeGasProperty.setDataType("double");
+                            cumulativeGasProperty.setIdentifier("cumulativeGasInjection");
+                            cumulativeGasProperty.setActualValue(companyCumulativeGasInjectionPair.get(deptId).toString());
+                            cumulativeGasProperty.setMaxValue(StrUtil.EMPTY);
+                            cumulativeGasProperty.setMinValue(StrUtil.EMPTY);
+                            cumulativeGasProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(cumulativeGasProperty);
+                        }
+                        // 注气量完成比
+                        if (companyActualGasInjectionPair.containsKey(deptId) && companyPlanGasInjectionPair.containsKey(deptId)) {
+                            BigDecimal actualGasInjection = companyActualGasInjectionPair.get(deptId);
+                            BigDecimal planGasInjection = companyPlanGasInjectionPair.get(deptId);
+                            if (planGasInjection.compareTo(BigDecimal.ZERO) > 0) {
+                                BigDecimal gasCompletionRatio = actualGasInjection.divide(planGasInjection, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
+                                IotTaskAttrModelProperty gasCompletionRatioProperty = new IotTaskAttrModelProperty();
+                                gasCompletionRatioProperty.setName("注气完成比");
+                                gasCompletionRatioProperty.setAccessMode("r");
+                                gasCompletionRatioProperty.setRequired(1);
+                                gasCompletionRatioProperty.setSort(3);
+                                gasCompletionRatioProperty.setUnit("%");
+                                gasCompletionRatioProperty.setDataType("double");
+                                gasCompletionRatioProperty.setIdentifier("gasCompletionRatio");
+                                gasCompletionRatioProperty.setActualValue(gasCompletionRatio.toString());
+                                gasCompletionRatioProperty.setMaxValue(StrUtil.EMPTY);
+                                gasCompletionRatioProperty.setMinValue(StrUtil.EMPTY);
+                                gasCompletionRatioProperty.setDefaultValue(StrUtil.EMPTY);
+                                companyExtProperties.add(gasCompletionRatioProperty);
+                            }
+                        }
+
+                        // 瑞鹰 进尺 井次 细化数据
+                        if (companyPlanFootagePair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty planFootageProperty = new IotTaskAttrModelProperty();
+                            planFootageProperty.setName("计划进尺");
+                            planFootageProperty.setAccessMode("r");
+                            planFootageProperty.setRequired(1);
+                            planFootageProperty.setSort(1);
+                            planFootageProperty.setUnit("m");
+                            planFootageProperty.setDataType("double");
+                            planFootageProperty.setIdentifier("planFootage");
+                            planFootageProperty.setActualValue(companyPlanFootagePair.get(deptId).toString());
+                            planFootageProperty.setMaxValue(StrUtil.EMPTY);
+                            planFootageProperty.setMinValue(StrUtil.EMPTY);
+                            planFootageProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(planFootageProperty);
+                        }
+                        if (companyActualFootagePair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty actualFootageProperty = new IotTaskAttrModelProperty();
+                            actualFootageProperty.setName("实际进尺");
+                            actualFootageProperty.setAccessMode("r");
+                            actualFootageProperty.setRequired(1);
+                            actualFootageProperty.setSort(2);
+                            actualFootageProperty.setUnit("m");
+                            actualFootageProperty.setDataType("double");
+                            actualFootageProperty.setIdentifier("actualFootage");
+                            actualFootageProperty.setActualValue(companyActualFootagePair.get(deptId).toString());
+                            actualFootageProperty.setMaxValue(StrUtil.EMPTY);
+                            actualFootageProperty.setMinValue(StrUtil.EMPTY);
+                            actualFootageProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(actualFootageProperty);
+                        }
+                        if (companyCumulativeFootagePair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty cumulativeFootageProperty = new IotTaskAttrModelProperty();
+                            cumulativeFootageProperty.setName("累计进尺");
+                            cumulativeFootageProperty.setAccessMode("r");
+                            cumulativeFootageProperty.setRequired(1);
+                            cumulativeFootageProperty.setSort(4);
+                            cumulativeFootageProperty.setUnit("m");
+                            cumulativeFootageProperty.setDataType("double");
+                            cumulativeFootageProperty.setIdentifier("cumulativeFootage");
+                            cumulativeFootageProperty.setActualValue(companyCumulativeFootagePair.get(deptId).toString());
+                            cumulativeFootageProperty.setMaxValue(StrUtil.EMPTY);
+                            cumulativeFootageProperty.setMinValue(StrUtil.EMPTY);
+                            cumulativeFootageProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(cumulativeFootageProperty);
+                        }
+                        // 进尺完成比
+                        if (companyActualFootagePair.containsKey(deptId) && companyPlanFootagePair.containsKey(deptId)) {
+                            BigDecimal actualFootage = companyActualFootagePair.get(deptId);
+                            BigDecimal planFootage = companyPlanFootagePair.get(deptId);
+                            if (planFootage.compareTo(BigDecimal.ZERO) > 0) {
+                                BigDecimal footageCompletionRatio = actualFootage.divide(planFootage, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
+                                IotTaskAttrModelProperty footageCompletionRatioProperty = new IotTaskAttrModelProperty();
+                                footageCompletionRatioProperty.setName("进尺完成比");
+                                footageCompletionRatioProperty.setAccessMode("r");
+                                footageCompletionRatioProperty.setRequired(1);
+                                footageCompletionRatioProperty.setSort(3);
+                                footageCompletionRatioProperty.setUnit("%");
+                                footageCompletionRatioProperty.setDataType("double");
+                                footageCompletionRatioProperty.setIdentifier("footageCompletionRatio");
+                                footageCompletionRatioProperty.setActualValue(footageCompletionRatio.toString());
+                                footageCompletionRatioProperty.setMaxValue(StrUtil.EMPTY);
+                                footageCompletionRatioProperty.setMinValue(StrUtil.EMPTY);
+                                footageCompletionRatioProperty.setDefaultValue(StrUtil.EMPTY);
+                                companyExtProperties.add(footageCompletionRatioProperty);
+                            }
+                        }
+                        // 瑞鹰 瑞都 井次
+                        if (companyPlanWellTripsPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty planWellTripsProperty = new IotTaskAttrModelProperty();
+                            planWellTripsProperty.setName("计划井次");
+                            planWellTripsProperty.setAccessMode("r");
+                            planWellTripsProperty.setRequired(1);
+                            planWellTripsProperty.setSort(5);
+                            planWellTripsProperty.setUnit("井");
+                            planWellTripsProperty.setDataType("double");
+                            planWellTripsProperty.setIdentifier("planWellTrips");
+                            planWellTripsProperty.setActualValue(companyPlanWellTripsPair.get(deptId).toString());
+                            planWellTripsProperty.setMaxValue(StrUtil.EMPTY);
+                            planWellTripsProperty.setMinValue(StrUtil.EMPTY);
+                            planWellTripsProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(planWellTripsProperty);
+                        }
+                        if (companyActualWellTripsPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty actualWellTripsProperty = new IotTaskAttrModelProperty();
+                            actualWellTripsProperty.setName("实际井次");
+                            actualWellTripsProperty.setAccessMode("r");
+                            actualWellTripsProperty.setRequired(1);
+                            actualWellTripsProperty.setSort(6);
+                            actualWellTripsProperty.setUnit("井");
+                            actualWellTripsProperty.setDataType("double");
+                            actualWellTripsProperty.setIdentifier("actualWellTrips");
+                            actualWellTripsProperty.setActualValue(companyActualWellTripsPair.get(deptId).toString());
+                            actualWellTripsProperty.setMaxValue(StrUtil.EMPTY);
+                            actualWellTripsProperty.setMinValue(StrUtil.EMPTY);
+                            actualWellTripsProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(actualWellTripsProperty);
+                        }
+                        if (companyCumulativeWellTripsPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty cumulativeWellTripsProperty = new IotTaskAttrModelProperty();
+                            cumulativeWellTripsProperty.setName("累计井次");
+                            cumulativeWellTripsProperty.setAccessMode("r");
+                            cumulativeWellTripsProperty.setRequired(1);
+                            cumulativeWellTripsProperty.setSort(8);
+                            cumulativeWellTripsProperty.setUnit("井");
+                            cumulativeWellTripsProperty.setDataType("double");
+                            cumulativeWellTripsProperty.setIdentifier("cumulativeWellTrips");
+                            cumulativeWellTripsProperty.setActualValue(companyCumulativeWellTripsPair.get(deptId).toString());
+                            cumulativeWellTripsProperty.setMaxValue(StrUtil.EMPTY);
+                            cumulativeWellTripsProperty.setMinValue(StrUtil.EMPTY);
+                            cumulativeWellTripsProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(cumulativeWellTripsProperty);
+                        }
+                        // 井次完成比
+                        if (companyActualWellTripsPair.containsKey(deptId) && companyPlanWellTripsPair.containsKey(deptId)) {
+                            BigDecimal actualWellTrips = companyActualWellTripsPair.get(deptId);
+                            BigDecimal planWellTrips = companyPlanWellTripsPair.get(deptId);
+                            if (planWellTrips.compareTo(BigDecimal.ZERO) > 0) {
+                                BigDecimal wellTripsCompletionRatio = actualWellTrips.divide(planWellTrips, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
+                                IotTaskAttrModelProperty wellTripsCompletionRatioProperty = new IotTaskAttrModelProperty();
+                                wellTripsCompletionRatioProperty.setName("井次完成比");
+                                wellTripsCompletionRatioProperty.setAccessMode("r");
+                                wellTripsCompletionRatioProperty.setRequired(1);
+                                wellTripsCompletionRatioProperty.setSort(7);
+                                wellTripsCompletionRatioProperty.setUnit("%");
+                                wellTripsCompletionRatioProperty.setDataType("double");
+                                wellTripsCompletionRatioProperty.setIdentifier("wellTripsCompletionRatio");
+                                wellTripsCompletionRatioProperty.setActualValue(wellTripsCompletionRatio.toString());
+                                wellTripsCompletionRatioProperty.setMaxValue(StrUtil.EMPTY);
+                                wellTripsCompletionRatioProperty.setMinValue(StrUtil.EMPTY);
+                                wellTripsCompletionRatioProperty.setDefaultValue(StrUtil.EMPTY);
+                                companyExtProperties.add(wellTripsCompletionRatioProperty);
+                            }
+                        }
+
+                        // 5#公司 瑞都 层数
+                        if (companyPlanLayersPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty planLayersProperty = new IotTaskAttrModelProperty();
+                            planLayersProperty.setName("计划层数");
+                            planLayersProperty.setAccessMode("r");
+                            planLayersProperty.setRequired(1);
+                            planLayersProperty.setSort(1);
+                            planLayersProperty.setUnit("层");
+                            planLayersProperty.setDataType("double");
+                            planLayersProperty.setIdentifier("planLayers");
+                            planLayersProperty.setActualValue(companyPlanLayersPair.get(deptId).toString());
+                            planLayersProperty.setMaxValue(StrUtil.EMPTY);
+                            planLayersProperty.setMinValue(StrUtil.EMPTY);
+                            planLayersProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(planLayersProperty);
+                        }
+                        if (companyActualLayersPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty actualLayersProperty = new IotTaskAttrModelProperty();
+                            actualLayersProperty.setName("实际层数");
+                            actualLayersProperty.setAccessMode("r");
+                            actualLayersProperty.setRequired(1);
+                            actualLayersProperty.setSort(2);
+                            actualLayersProperty.setUnit("层");
+                            actualLayersProperty.setDataType("double");
+                            actualLayersProperty.setIdentifier("actualLayers");
+                            actualLayersProperty.setActualValue(companyActualLayersPair.get(deptId).toString());
+                            actualLayersProperty.setMaxValue(StrUtil.EMPTY);
+                            actualLayersProperty.setMinValue(StrUtil.EMPTY);
+                            actualLayersProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(actualLayersProperty);
+                        }
+                        if (companyCumulativeLayersPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty cumulativeLayersProperty = new IotTaskAttrModelProperty();
+                            cumulativeLayersProperty.setName("累计层数");
+                            cumulativeLayersProperty.setAccessMode("r");
+                            cumulativeLayersProperty.setRequired(1);
+                            cumulativeLayersProperty.setSort(4);
+                            cumulativeLayersProperty.setUnit("层");
+                            cumulativeLayersProperty.setDataType("double");
+                            cumulativeLayersProperty.setIdentifier("cumulativeLayers");
+                            cumulativeLayersProperty.setActualValue(companyCumulativeLayersPair.get(deptId).toString());
+                            cumulativeLayersProperty.setMaxValue(StrUtil.EMPTY);
+                            cumulativeLayersProperty.setMinValue(StrUtil.EMPTY);
+                            cumulativeLayersProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(cumulativeLayersProperty);
+                        }
+                        // 层数完成比
+                        if (companyActualLayersPair.containsKey(deptId) && companyPlanLayersPair.containsKey(deptId)) {
+                            BigDecimal actualLayers = companyActualLayersPair.get(deptId);
+                            BigDecimal planLayers = companyPlanLayersPair.get(deptId);
+                            if (planLayers.compareTo(BigDecimal.ZERO) > 0) {
+                                BigDecimal layersCompletionRatio = actualLayers.divide(planLayers, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
+                                IotTaskAttrModelProperty layersCompletionRatioProperty = new IotTaskAttrModelProperty();
+                                layersCompletionRatioProperty.setName("层数完成比");
+                                layersCompletionRatioProperty.setAccessMode("r");
+                                layersCompletionRatioProperty.setRequired(1);
+                                layersCompletionRatioProperty.setSort(3);
+                                layersCompletionRatioProperty.setUnit("%");
+                                layersCompletionRatioProperty.setDataType("double");
+                                layersCompletionRatioProperty.setIdentifier("layersCompletionRatio");
+                                layersCompletionRatioProperty.setActualValue(layersCompletionRatio.toString());
+                                layersCompletionRatioProperty.setMaxValue(StrUtil.EMPTY);
+                                layersCompletionRatioProperty.setMinValue(StrUtil.EMPTY);
+                                layersCompletionRatioProperty.setDefaultValue(StrUtil.EMPTY);
+                                companyExtProperties.add(layersCompletionRatioProperty);
+                            }
+                        }
+                        // 各专业公司公共属性 施工设备 投运设备
+                        if (companyInServiceDeviceNumPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty inServiceDeviceProperty = new IotTaskAttrModelProperty();
+                            inServiceDeviceProperty.setName("投运设备数量");
+                            inServiceDeviceProperty.setAccessMode("r");
+                            inServiceDeviceProperty.setRequired(0);
+                            inServiceDeviceProperty.setSort(9);
+                            inServiceDeviceProperty.setUnit("台");
+                            inServiceDeviceProperty.setDataType("double");
+                            inServiceDeviceProperty.setIdentifier("inServiceDeviceNum");
+                            inServiceDeviceProperty.setActualValue(companyInServiceDeviceNumPair.get(deptId).toString());
+                            inServiceDeviceProperty.setMaxValue(StrUtil.EMPTY);
+                            inServiceDeviceProperty.setMinValue(StrUtil.EMPTY);
+                            inServiceDeviceProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(inServiceDeviceProperty);
+                        }
+                        if (companyConstructionDeviceNumPair.containsKey(deptId)) {
+                            IotTaskAttrModelProperty constructionDeviceProperty = new IotTaskAttrModelProperty();
+                            constructionDeviceProperty.setName("施工设备数量");
+                            constructionDeviceProperty.setAccessMode("r");
+                            constructionDeviceProperty.setRequired(0);
+                            constructionDeviceProperty.setSort(10);
+                            constructionDeviceProperty.setUnit("台");
+                            constructionDeviceProperty.setDataType("double");
+                            constructionDeviceProperty.setIdentifier("constructionDeviceNum");
+                            constructionDeviceProperty.setActualValue(companyConstructionDeviceNumPair.get(deptId).toString());
+                            constructionDeviceProperty.setMaxValue(StrUtil.EMPTY);
+                            constructionDeviceProperty.setMinValue(StrUtil.EMPTY);
+                            constructionDeviceProperty.setDefaultValue(StrUtil.EMPTY);
+                            companyExtProperties.add(constructionDeviceProperty);
+                        }
+
+                        if (companyInServiceDeviceNumPair.containsKey(deptId) && companyConstructionDeviceNumPair.containsKey(deptId)) {
+                            // 各专业公司共用属性 设备利用率 施工设备/投运设备
+                            BigDecimal inServiceDeviceNum = companyInServiceDeviceNumPair.get(deptId);
+                            BigDecimal constructionDeviceNum = companyConstructionDeviceNumPair.get(deptId);
+                            if (inServiceDeviceNum.compareTo(BigDecimal.ZERO) > 0) {
+                                BigDecimal utilizationRate = constructionDeviceNum.divide(inServiceDeviceNum, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
+                                IotTaskAttrModelProperty utilizationRateProperty = new IotTaskAttrModelProperty();
+                                utilizationRateProperty.setName("设备利用率");
+                                utilizationRateProperty.setAccessMode("r");
+                                utilizationRateProperty.setRequired(1);
+                                utilizationRateProperty.setSort(11);
+                                utilizationRateProperty.setUnit("%");
+                                utilizationRateProperty.setDataType("double");
+                                utilizationRateProperty.setIdentifier("utilizationRate");
+                                utilizationRateProperty.setActualValue(utilizationRate.toString());
+                                utilizationRateProperty.setMaxValue(StrUtil.EMPTY);
+                                utilizationRateProperty.setMinValue(StrUtil.EMPTY);
+                                utilizationRateProperty.setDefaultValue(StrUtil.EMPTY);
+                                companyExtProperties.add(utilizationRateProperty);
+                                // 设备利用率 环比 (本期-上期)/上期
+                                if (beforeCompanyInServiceDeviceNumPair.containsKey(deptId) && beforeCompanyConstructionDeviceNumPair.containsKey(deptId)) {
+                                    // 计算上期会议 对应专业公司的设备利用率
+                                    BigDecimal beforeInServiceDeviceNum = beforeCompanyInServiceDeviceNumPair.get(deptId);
+                                    BigDecimal beforeConstructionDeviceNum = beforeCompanyConstructionDeviceNumPair.get(deptId);
+                                    if (beforeInServiceDeviceNum.compareTo(BigDecimal.ZERO) > 0) {
+                                        BigDecimal beforeUtilizationRate = beforeConstructionDeviceNum.divide(beforeInServiceDeviceNum, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
+                                        if (beforeUtilizationRate.compareTo(BigDecimal.ZERO) > 0) {
+                                            BigDecimal periodOnPeriod = utilizationRate.subtract(beforeUtilizationRate).
+                                                    divide(beforeUtilizationRate, 4, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
+                                            IotTaskAttrModelProperty periodOnPeriodProperty = new IotTaskAttrModelProperty();
+                                            periodOnPeriodProperty.setName("环比");
+                                            periodOnPeriodProperty.setAccessMode("r");
+                                            periodOnPeriodProperty.setRequired(1);
+                                            periodOnPeriodProperty.setSort(12);
+                                            periodOnPeriodProperty.setUnit("%");
+                                            periodOnPeriodProperty.setDataType("double");
+                                            periodOnPeriodProperty.setIdentifier("periodOnPeriod");
+                                            periodOnPeriodProperty.setActualValue(periodOnPeriod.toString());
+                                            periodOnPeriodProperty.setMaxValue(StrUtil.EMPTY);
+                                            periodOnPeriodProperty.setMinValue(StrUtil.EMPTY);
+                                            periodOnPeriodProperty.setDefaultValue(StrUtil.EMPTY);
+                                            companyExtProperties.add(periodOnPeriodProperty);
+                                        }
+                                    }
+                                }
+                            }
+
+                        }
+                        // 设置各专业公司的工作量汇总数据
+                        // 将拆分工作量明细 按照 sort 升序排列
+                        companyExtProperties.sort(Comparator.comparing(
+                                IotTaskAttrModelProperty::getSort,
+                                Comparator.nullsLast(Integer::compareTo)
+                        ));
+                        meetingResp.setExtProperty(companyExtProperties);
                 });
             }
             List<IotOperationMeetingDetailRespVO> meetingDetails = BeanUtils.toBean(details, IotOperationMeetingDetailRespVO.class);
@@ -165,6 +767,29 @@ public class IotOperationMeetingController {
         return meetingResp;
     }
 
+    /**
+     * 从工作量扩展属性中获取 指定名称的值
+     */
+    private BigDecimal getActualValue(List<IotTaskAttrModelProperty> properties, String identifier) {
+        // 空值直接返回 0
+        if (CollUtil.isEmpty(properties)) {
+            return BigDecimal.ZERO;
+        }
+
+        // Java 8 流式优雅查找目标属性
+        return properties.stream()
+                // 过滤标识符 = planGasInjection
+                .filter(property -> identifier.equals(property.getIdentifier()))
+                // 提取值
+                .map(IotTaskAttrModelProperty::getActualValue)
+                .filter(StrUtil::isNotBlank)
+                // 只取第一个匹配的
+                .findFirst()
+                // 转成 BigDecimal,无值则返回 0
+                .map(BigDecimal::new)
+                .orElse(BigDecimal.ZERO);
+    }
+
     @GetMapping("/page")
     @Operation(summary = "获得生产运营会分页")
     @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")

+ 51 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotwebtopoproject/IotWebtopoProjectController.java

@@ -36,6 +36,8 @@ import java.util.*;
 
 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
 
 @Tag(name = "管理后台 - 组态项目列")
 @RestController
@@ -158,7 +160,55 @@ public class IotWebtopoProjectController {
     @PreAuthorize("@ss.hasPermission('pms:iot-webtopo-project:query')")
     public CommonResult<PageResult<IotWebtopoProjectRespVO>> getIotWebtopoProjectPage(@Valid IotWebtopoProjectPageReqVO pageReqVO) {
         PageResult<IotWebtopoProjectDO> pageResult = iotWebtopoProjectService.getIotWebtopoProjectPage(pageReqVO);
-        return success(BeanUtils.toBean(pageResult, IotWebtopoProjectRespVO.class));
+        return success(new PageResult<>(buildWebtopProjects(pageResult.getList(), pageReqVO), pageResult.getTotal()));
+        // return success(BeanUtils.toBean(pageResult, IotWebtopoProjectRespVO.class));
+    }
+
+    /**
+     * 查询对应的 组态项目信息 列表 绑定的设备列表
+     * @param projects
+     * @return
+     */
+    private List<IotWebtopoProjectRespVO> buildWebtopProjects(List<IotWebtopoProjectDO> projects, IotWebtopoProjectPageReqVO reqVO) {
+        if (CollUtil.isEmpty(projects)) {
+            return Collections.emptyList();
+        }
+        Set<Long> projectIds = convertSet(projects, IotWebtopoProjectDO::getId);
+        if (CollUtil.isEmpty(projectIds)) {
+            return Collections.emptyList();
+        }
+        Map<Long, List<IotWebtopoProjectDeviceDO>> projectDevicesPair = new HashMap<>();
+        // key组态项目id   value项目项目关联的设备id集合
+        Map<Long, List<Long>> projectDeviceIdPair = new HashMap<>();
+        // 查询组态项目对应的设备列表
+        IotWebtopoProjectDevicePageReqVO pageReqVO = new IotWebtopoProjectDevicePageReqVO();
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        pageReqVO.setProjectIds(projectIds);
+        PageResult<IotWebtopoProjectDeviceDO> pageDevices = iotWebtopoProjectDeviceService.getIotWebtopoProjectDevicePage(pageReqVO);
+        if (ObjUtil.isNotEmpty(pageDevices)) {
+            List<IotWebtopoProjectDeviceDO> projectDevices = pageDevices.getList();
+            List<IotWebtopoDeviceRespVO> devices = new ArrayList<>();
+            Set<Long> deviceIds = new HashSet<>();
+            if (CollUtil.isNotEmpty(projectDevices)) {
+                projectDevices.forEach(pd -> {
+                    deviceIds.add(pd.getDeviceId());
+                    if (projectDeviceIdPair.containsKey(pd.getProjectId())) {
+                        List<Long> tempDeviceIds = projectDeviceIdPair.get(pd.getProjectId());
+                        tempDeviceIds.add(pd.getDeviceId());
+                        projectDeviceIdPair.put(pd.getProjectId(), tempDeviceIds);
+                    } else {
+                        List<Long> tempDeviceIds = new ArrayList<>();
+                        tempDeviceIds.add(pd.getDeviceId());
+                        projectDeviceIdPair.put(pd.getProjectId(), tempDeviceIds);
+                    }
+                });
+
+            }
+        }
+        return BeanUtils.toBean(projects, IotWebtopoProjectRespVO.class, (projectVO) -> {
+            // 组态项目绑定的设备id列表
+            findAndThen(projectDeviceIdPair, projectVO.getId(), deviceIds -> projectVO.setLinkedDeviceIds(deviceIds));
+        });
     }
 
     @GetMapping("/export-excel")

+ 4 - 1
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/controller/admin/iotwebtopoproject/vo/IotWebtopoProjectRespVO.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.module.pms.controller.admin.iotwebtopoproject.vo;
 
-import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDeviceRespVO;
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -57,4 +56,8 @@ public class IotWebtopoProjectRespVO {
     @Schema(description = "设备列表", example = "2")
     @ExcelProperty("设备列表")
     private List<IotWebtopoDeviceRespVO> linkedDevices;
+
+    @Schema(description = "组态项目关联的设备id列表", example = "2")
+    @ExcelProperty("组态项目关联的设备id列表")
+    private List<Long> linkedDeviceIds;
 }

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

@@ -8,6 +8,7 @@ import lombok.ToString;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.time.LocalDateTime;
+import java.util.Collection;
 
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 
@@ -36,4 +37,9 @@ public class IotWebtopoProjectDevicePageReqVO extends PageParam {
     @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     private LocalDateTime[] createTime;
 
+    /**
+     * 扩展属性
+     */
+    @Schema(description = "组态项目id集合", example = "42,99")
+    private Collection<Long> projectIds;
 }

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

@@ -19,6 +19,7 @@ public interface IotWebtopoProjectDeviceMapper extends BaseMapperX<IotWebtopoPro
         return selectPage(reqVO, new LambdaQueryWrapperX<IotWebtopoProjectDeviceDO>()
                 .eqIfPresent(IotWebtopoProjectDeviceDO::getDeptId, reqVO.getDeptId())
                 .eqIfPresent(IotWebtopoProjectDeviceDO::getProjectId, reqVO.getProjectId())
+                .inIfPresent(IotWebtopoProjectDeviceDO::getProjectId, reqVO.getProjectIds())
                 .eqIfPresent(IotWebtopoProjectDeviceDO::getDeviceId, reqVO.getDeviceId())
                 .eqIfPresent(IotWebtopoProjectDeviceDO::getRemark, reqVO.getRemark())
                 .eqIfPresent(IotWebtopoProjectDeviceDO::getStatus, reqVO.getStatus())

+ 10 - 0
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotoperationmeetingdetail/IotOperationMeetingDetailService.java

@@ -5,6 +5,8 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingdetail.vo
 import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeetingdetail.IotOperationMeetingDetailDO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 
+import java.util.List;
+
 /**
  * 生产运营会明细 Service 接口
  *
@@ -50,4 +52,12 @@ public interface IotOperationMeetingDetailService {
      */
     PageResult<IotOperationMeetingDetailDO> getIotOperationMeetingDetailPage(IotOperationMeetingDetailPageReqVO pageReqVO);
 
+    /**
+     * 查询生产运营会明细列表
+     *
+     * @param reqVO 列表查询
+     * @return 生产运营会明细列表
+     */
+    List<IotOperationMeetingDetailDO> summarizedProjectDetails(IotOperationMeetingDetailPageReqVO reqVO);
+
 }

+ 13 - 7
yudao-module-pms/yudao-module-pms-biz/src/main/java/cn/iocoder/yudao/module/pms/service/iotoperationmeetingdetail/IotOperationMeetingDetailServiceImpl.java

@@ -1,15 +1,16 @@
 package cn.iocoder.yudao.module.pms.service.iotoperationmeetingdetail;
 
-import org.springframework.stereotype.Service;
-import javax.annotation.Resource;
-import org.springframework.validation.annotation.Validated;
-
-import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingdetail.vo.*;
-import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeetingdetail.IotOperationMeetingDetailDO;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-
+import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingdetail.vo.IotOperationMeetingDetailPageReqVO;
+import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingdetail.vo.IotOperationMeetingDetailSaveReqVO;
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeetingdetail.IotOperationMeetingDetailDO;
 import cn.iocoder.yudao.module.pms.dal.mysql.iotoperationmeetingdetail.IotOperationMeetingDetailMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_OPERATION_MEETING_DETAIL_NOT_EXISTS;
@@ -68,4 +69,9 @@ public class IotOperationMeetingDetailServiceImpl implements IotOperationMeeting
         return iotOperationMeetingDetailMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    public List<IotOperationMeetingDetailDO> summarizedProjectDetails(IotOperationMeetingDetailPageReqVO reqVO) {
+        return iotOperationMeetingDetailMapper.summarizedProjectDetails(reqVO);
+    }
+
 }