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;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeeting.vo.IotOperationMeetingPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeeting.vo.IotOperationMeetingRespVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeeting.vo.IotOperationMeetingSaveBatchVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeeting.vo.IotOperationMeetingSaveReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingdetail.vo.IotOperationMeetingDetailPageReqVO;
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingdetail.vo.IotOperationMeetingDetailRespVO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeeting.IotOperationMeetingDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeetingdetail.IotOperationMeetingDetailDO;
import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
import cn.iocoder.yudao.module.pms.service.iotoperationmeeting.IotOperationMeetingService;
import cn.iocoder.yudao.module.pms.service.iotoperationmeetingdetail.IotOperationMeetingDetailService;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

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.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;

@Tag(name = "管理后台 - 生产运营会")
@RestController
@RequestMapping("/pms/iot-operation-meeting")
@Validated
public class IotOperationMeetingController {

    @Resource
    private IotOperationMeetingService iotOperationMeetingService;
    @Resource
    private IotOperationMeetingDetailService iotOperationMeetingDetailService;
    @Resource
    private DeptService deptService;

    @PostMapping("/create")
    @Operation(summary = "创建生产运营会")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:create')")
    public CommonResult<Long> createIotOperationMeeting(@Valid @RequestBody IotOperationMeetingSaveReqVO createReqVO) {
        return success(iotOperationMeetingService.createIotOperationMeeting(createReqVO));
    }

    @PostMapping("/saveBatch")
    @Operation(summary = "创建生产运营会 主表+明细")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:create')")
    public CommonResult<Long> saveBatch(@Valid @RequestBody IotOperationMeetingSaveBatchVO saveBatchVO) {
        return success(iotOperationMeetingService.saveBatch(saveBatchVO));
    }

    @PutMapping("/update")
    @Operation(summary = "更新生产运营会")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:update')")
    public CommonResult<Boolean> updateIotOperationMeeting(@Valid @RequestBody IotOperationMeetingSaveReqVO updateReqVO) {
        iotOperationMeetingService.updateIotOperationMeeting(updateReqVO);
        return success(true);
    }

    @DeleteMapping("/delete")
    @Operation(summary = "删除生产运营会")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:delete')")
    public CommonResult<Boolean> deleteIotOperationMeeting(@RequestParam("id") Long id) {
        iotOperationMeetingService.deleteIotOperationMeeting(id);
        return success(true);
    }

    @GetMapping("/get")
    @Operation(summary = "获得生产运营会")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")
    public CommonResult<IotOperationMeetingRespVO> getIotOperationMeeting(@RequestParam("id") Long id) {
        IotOperationMeetingDO iotOperationMeeting = iotOperationMeetingService.getIotOperationMeeting(id);
        return success(buildMeetingResp(iotOperationMeeting));
    }

    @GetMapping("/mandatoryOrNot")
    @Operation(summary = "查询当前登录人所在公司是否需要设置 设备利用率 必填")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")
    public CommonResult<IotOperationMeetingRespVO> mandatoryOrNot() {
        IotOperationMeetingRespVO iotOperationMeeting = iotOperationMeetingService.mandatoryOrNot();
        return success(iotOperationMeeting);
    }

    @GetMapping("/currentCompanyExtProperties")
    @Operation(summary = "查询当前登录人所在公司相关的扩展属性")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")
    public CommonResult<List<IotTaskAttrModelProperty>> currentCompanyExtProperties() {
        List<IotTaskAttrModelProperty> workloadAttrs = iotOperationMeetingService.currentCompanyExtProperties();
        return success(workloadAttrs);
    }

    @GetMapping("/previousWorkPlan")
    @Operation(summary = "填报运营会议明细时查询上期填写的工作计划")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")
    public CommonResult<IotOperationMeetingDetailRespVO> previousWorkPlan(@Valid IotOperationMeetingDetailPageReqVO pageReqVO) {
        IotOperationMeetingDetailRespVO result = iotOperationMeetingService.previousWorkPlan(pageReqVO);
        return success(result);
    }

    /**
     * 查询 运营会 对应的明细
     * @param operationMeeting
     * @return
     */
    private IotOperationMeetingRespVO buildMeetingResp(IotOperationMeetingDO operationMeeting) {
        if (ObjUtil.isEmpty(operationMeeting)) {
            return new IotOperationMeetingRespVO();
        }

        IotOperationMeetingRespVO meetingResp = BeanUtils.toBean(operationMeeting, IotOperationMeetingRespVO.class);
        // 设置公司名称
        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);
        pageReqVO.setMeetingId(meetingResp.getId());
        PageResult<IotOperationMeetingDetailDO> pageDetails = iotOperationMeetingDetailService.getIotOperationMeetingDetailPage(pageReqVO);
        if (ObjUtil.isNotEmpty(pageDetails)) {
            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<>();
                    // 真实的扩展属性列表
                    List<IotTaskAttrModelProperty> extProperties = detail.getExtProperty();
                    if (CollUtil.isNotEmpty(extProperties)) {
                        extProperties.forEach(property -> {
                            if (ObjUtil.isNotEmpty(property) && ObjUtil.isNotEmpty(property.getRequired()) && 1 == property.getRequired()) {
                                modifiedExtProperties.add(property);
                            }
                        });
                        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);
            meetingResp.setDetails(meetingDetails);
        }
        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')")
    public CommonResult<PageResult<IotOperationMeetingRespVO>> getIotOperationMeetingPage(@Valid IotOperationMeetingPageReqVO pageReqVO) {
        PageResult<IotOperationMeetingDO> pageResult = iotOperationMeetingService.getIotOperationMeetingPage(pageReqVO);
        return success(new PageResult<>(buildMeetingDetails(pageResult.getList()), pageResult.getTotal()));
    }

    @GetMapping("/integratedMeeting")
    @Operation(summary = "石油技术查询所有公司整合的生产会议项目信息 按照期次维度整合")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")
    public CommonResult<List<IotOperationMeetingRespVO>> integratedMeeting(@Valid IotOperationMeetingPageReqVO pageReqVO) {
        List<IotOperationMeetingRespVO> combineMeetings = new ArrayList<>();
        Map<String, List<IotOperationMeetingDO>> companyMeetingPair = iotOperationMeetingService.integratedMeeting(pageReqVO);
        // key格式2026-第8期   value当期公司会议集合
        if (CollUtil.isNotEmpty(companyMeetingPair)) {
            Map<String, List<IotOperationMeetingDO>> sortedMeetings = companyMeetingPair.entrySet().stream()
                    .sorted(Comparator.<Map.Entry<String, List<IotOperationMeetingDO>>>comparingInt(
                                    e -> Integer.parseInt(e.getKey().substring(0, 4)))   // 提取年份
                            .thenComparingInt(e -> Integer.parseInt(e.getKey().split("第|期")[1])) // 提取期次数字
                            .reversed())  // 年份倒序，相同时期次倒序
                    .collect(Collectors.toMap(
                            Map.Entry::getKey,
                            Map.Entry::getValue,
                            (oldVal, newVal) -> oldVal,
                            LinkedHashMap::new));

            sortedMeetings.forEach((key, meetings) -> {
                IotOperationMeetingRespVO tempMeeting = new IotOperationMeetingRespVO();
                String[] keyArr = key.split("-");
                String series = keyArr[1];
                tempMeeting.setMeetingSeries(series);
                tempMeeting.setCompanyName("科瑞石油技术");
                meetings.stream()
                        .findAny() // 随机取任意一个（等价于随机）
                        .map(IotOperationMeetingDO::getMeetingDate)
                        .ifPresent(tempMeeting::setMeetingDate);
                combineMeetings.add(tempMeeting);
            });
        }
        return success(combineMeetings);
    }

    @GetMapping("/summarizedProjectDetails")
    @Operation(summary = "查询汇总的石油技术下的公司所有项目明细")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")
    public CommonResult<List<IotOperationMeetingRespVO>> summarizedProjectDetails(@Valid IotOperationMeetingPageReqVO pageReqVO) {
        List<IotOperationMeetingRespVO> iotOperationMeeting = iotOperationMeetingService.summarizedProjectDetails(pageReqVO);
        return success(iotOperationMeeting);
    }

    @GetMapping("/companyWorkloadDetails")
    @Operation(summary = "汇总页面 查询指定公司 的拆分工作量明细")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")
    public CommonResult<List<IotOperationMeetingRespVO>> companyWorkloadDetails(@Valid IotOperationMeetingPageReqVO pageReqVO) {
        List<IotOperationMeetingRespVO> iotOperationMeeting = iotOperationMeetingService.companyWorkloadDetails(pageReqVO);
        return success(iotOperationMeeting);
    }

    @GetMapping("/availableQueryPeriods")
    @Operation(summary = "石油技术查询所有公司的会议记录列表 查询可供查询的期次 下拉列表")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")
    public CommonResult<Set<String>> availableQueryPeriods() {
        // 查询所有公司的线上运营会议期次 下拉列表 数据格式 2026-第8期
        Set<String> availablePeriods = iotOperationMeetingService.availableQueryPeriods();
        return success(availablePeriods);
    }

    @GetMapping("/cachedProjects")
    @Operation(summary = "查询当前公司填写过的项目名称 缓存项目名称")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:query')")
    public CommonResult<Set<String>> cachedProjects() {
        // 将当前公司填写过的项目名称作为下拉列表返回 提高填写效率
        Set<String> cachedProjects = iotOperationMeetingService.cachedProjects();
        return success(cachedProjects);
    }

    /**
     * 设置运营会明细相关的金额 收入 挂账 回款
     * @param meetings 运营会列表
     * @return
     */
    private List<IotOperationMeetingRespVO> buildMeetingDetails(List<IotOperationMeetingDO> meetings) {
        if (CollUtil.isEmpty(meetings)) {
            return Collections.emptyList();
        }
        // 查询每个运营会关联的公司
        Map<Long, DeptDO> deptPair = deptService.getDeptMap(convertList(meetings, IotOperationMeetingDO::getDeptId));
        List<Long> meetingIds = convertList(meetings, IotOperationMeetingDO::getId);
        IotOperationMeetingDetailPageReqVO pageReqVO = new IotOperationMeetingDetailPageReqVO();
        pageReqVO.setMeetingIds(meetingIds);
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        PageResult<IotOperationMeetingDetailDO> pageMeetingDetails = iotOperationMeetingDetailService.getIotOperationMeetingDetailPage(pageReqVO);
        // 当期收入集合 key运营会id  value明细本期收入累加和
        Map<Long, BigDecimal> currentRevenuePair = new HashMap<>();
        // 当期挂账集合 key运营会id  value明细本期挂账累加和
        Map<Long, BigDecimal> currentOnAccountPair = new HashMap<>();
        // 当期回款集合 key运营会id  value明细本期回款累加和
        Map<Long, BigDecimal> currentPaymentPair = new HashMap<>();
        if (ObjUtil.isNotEmpty(pageMeetingDetails)) {
            List<IotOperationMeetingDetailDO> details = pageMeetingDetails.getList();
            // 计算每个运营会明细的本期 收入 挂账 回款 累加和
            if (CollUtil.isNotEmpty(details)) {
                details.forEach(detail -> {
                    // 本期收入
                    if (ObjUtil.isNotEmpty(detail.getCurrentRevenue())) {
                        if (currentRevenuePair.containsKey(detail.getMeetingId())) {
                            BigDecimal existRevenue = currentRevenuePair.get(detail.getMeetingId());
                            BigDecimal tempRevenue = existRevenue.add(detail.getCurrentRevenue());
                            currentRevenuePair.put(detail.getMeetingId(), tempRevenue);
                        } else {
                            currentRevenuePair.put(detail.getMeetingId(), detail.getCurrentRevenue());
                        }
                    }
                    // 本期挂账
                    if (ObjUtil.isNotEmpty(detail.getCurrentOnAccount())) {
                        if (currentOnAccountPair.containsKey(detail.getMeetingId())) {
                            BigDecimal existOnAccount = currentOnAccountPair.get(detail.getMeetingId());
                            BigDecimal tempOnAccount = existOnAccount.add(detail.getCurrentOnAccount());
                            currentOnAccountPair.put(detail.getMeetingId(), tempOnAccount);
                        } else {
                            currentOnAccountPair.put(detail.getMeetingId(), detail.getCurrentOnAccount());
                        }
                    }
                    // 本期回款
                    if (ObjUtil.isNotEmpty(detail.getCurrentPayment())) {
                        if (currentPaymentPair.containsKey(detail.getMeetingId())) {
                            BigDecimal existPayment = currentPaymentPair.get(detail.getMeetingId());
                            BigDecimal tempPayment = existPayment.add(detail.getCurrentPayment());
                            currentPaymentPair.put(detail.getMeetingId(), tempPayment);
                        } else {
                            currentPaymentPair.put(detail.getMeetingId(), detail.getCurrentPayment());
                        }
                    }
                });
            }
        }
        // 查询每个运营会对应的明细中的 本期 收入 挂账 回款 金额 总和
        return BeanUtils.toBean(meetings, IotOperationMeetingRespVO.class, (meetingVO) -> {
            findAndThen(deptPair, meetingVO.getDeptId(), dept -> meetingVO.setCompanyName(dept.getName()));
            findAndThen(currentRevenuePair, meetingVO.getId(), currentRevenue -> meetingVO.setCurrentRevenue(currentRevenue));
            findAndThen(currentOnAccountPair, meetingVO.getId(), currentOnAccount -> meetingVO.setCurrentOnAccount(currentOnAccount));
            findAndThen(currentPaymentPair, meetingVO.getId(), currentPayment -> meetingVO.setCurrentPayment(currentPayment));
        });
    }

    @GetMapping("/export-excel")
    @Operation(summary = "导出生产运营会 Excel")
    @PreAuthorize("@ss.hasPermission('pms:iot-operation-meeting:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportIotOperationMeetingExcel(@Valid IotOperationMeetingPageReqVO pageReqVO,
              HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<IotOperationMeetingDO> list = iotOperationMeetingService.getIotOperationMeetingPage(pageReqVO).getList();
        // 导出 Excel
        ExcelUtils.write(response, "生产运营会.xls", "数据", IotOperationMeetingRespVO.class,
                        BeanUtils.toBean(list, IotOperationMeetingRespVO.class));
    }

}