# HotelKpiMetricsSettingServiceImpl.java

    package cloud.wconnect.workperformance.service.hotelkpi.impl.old;

    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.workperformance.cons.hotelkpi.old.HotelKpiDateTypeCons;
    import cloud.wconnect.workperformance.cons.hotelkpi.old.HotelKpiSettingCons;
    import cloud.wconnect.workperformance.cons.hotelkpi.old.HotelKpiValueCategoryCons;
    import cloud.wconnect.workperformance.entity.hotelkpi.old.HotelKpiMetricsConfig;
    import cloud.wconnect.workperformance.entity.hotelkpi.old.HotelKpiMetricsSetting;
    import cloud.wconnect.workperformance.mapper.hotelkpi.old.HotelKpiMetricsSettingMapper;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.old.kpi.HotelKpiCalculationFactorDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.old.kpi.HotelKpiComparisonRuleDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.old.kpi.HotelKpiMetricsDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.old.kpi.HotelKpiValueDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.old.metricsconfig.HotelKpiCalculationFactorConfigDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.old.metricsconfig.HotelKpiCalculationFactorConfigItemDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.old.setting.*;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.old.setting.actual.HotelKpiMetricsSettingActualDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.old.setting.target.HotelKpiMetricsSettingTargetDTO;
    import cloud.wconnect.workperformance.service.hotelkpi.old.IHotelKpiMetricsConfigService;
    import cloud.wconnect.workperformance.service.hotelkpi.old.IHotelKpiMetricsSettingService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.lang.Nullable;
    import org.springframework.stereotype.Service;

    import jakarta.annotation.Resource;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     * Hotel KPI Metrics Setting 服务实现类
     * </p>
     *
     * @author zc
     * @since 2024-06-24
     */
    @Service("hotelKpiMetricsSettingServiceImpl_old")
    @DS("work_performance")
    public class HotelKpiMetricsSettingServiceImpl extends ServiceImpl<HotelKpiMetricsSettingMapper, HotelKpiMetricsSetting> implements IHotelKpiMetricsSettingService {
        @Resource
        private IHotelKpiMetricsConfigService metricsConfigService;
        @Resource
        private ObjectMapper objectMapper;

        /**
         * 获取指定年的设置数据
         *
         * @param propertyId 资产 id
         * @param year       年份(yyyy)
         * @return HotelKpiSettingDetailDTO 集合
         * @author zc
         * @date 2024/6/28
         */
        @Override
        public List<HotelKpiSettingDetailDTO> listSetting(int propertyId, long year) {
            // 获取有效的指标
            List<HotelKpiMetricsConfig> effectiveMetricsConfigs = metricsConfigService.listEffectiveMetricsOfYear(propertyId,
                    HotelKpiDateTypeCons.YEARLY, year, year, Boolean.FALSE);
            Map<Integer, List<HotelKpiMetricsConfig>> metricsParentId2Configs = effectiveMetricsConfigs.stream()
                    .collect(Collectors.groupingBy(HotelKpiMetricsConfig::getParentId));

            // 包含的指标类型
            List<Integer> metricsTypes = effectiveMetricsConfigs.stream().map(HotelKpiMetricsConfig::getMetricsType)
                    .filter(metricsType -> metricsType > 0).toList();

            // 获取指标设置数据
            Map<Integer, Map<Integer, List<HotelKpiMetricsSetting>>> metricsSettingMap = listByYearRangeAndMetricsTypes(propertyId,
                    metricsTypes, year, year, null).stream().collect(Collectors.groupingBy(HotelKpiMetricsSetting::getMetricsType,
                    Collectors.groupingBy(HotelKpiMetricsSetting::getValueCategory)));

            return effectiveMetricsConfigs.stream().filter(metrics -> metrics.getParentId() == 0)
                    .sorted(Comparator.comparing(HotelKpiMetricsConfig::getSort))
                    .map(parent0Metrics -> {
                        List<HotelKpiSettingItemDTO> items = metricsParentId2Configs.get(parent0Metrics.getId()).stream()
                                .sorted(Comparator.comparing(HotelKpiMetricsConfig::getSort))
                                .map(parent1Metrics -> {
                                    List<HotelKpiSettingMetricsDTO> metrics = metricsParentId2Configs.get(parent1Metrics.getId()).stream()
                                            .sorted(Comparator.comparing(HotelKpiMetricsConfig::getSort))
                                            .map(childMetrics -> {
                                                Map<Integer, List<HotelKpiMetricsSetting>> valueCategory2Settings =
                                                        metricsSettingMap.getOrDefault(childMetrics.getMetricsType(), Collections.emptyMap());
                                                Integer actualRuleCategory = getRuleCategory(valueCategory2Settings.getOrDefault(HotelKpiValueCategoryCons.ACTUAL,
                                                        Collections.emptyList()));
                                                Integer targetRuleCategory = getRuleCategory(valueCategory2Settings.getOrDefault(HotelKpiValueCategoryCons.TARGET,
                                                        Collections.emptyList()));

                                                // 对比规则 json 转换
                                                HotelKpiComparisonRuleDTO comparisonRuleDTO = JsonUtils.convertStringToObject(objectMapper,
                                                        childMetrics.getComparisonRule(), HotelKpiComparisonRuleDTO.class);

                                                return HotelKpiSettingMetricsDTO.builder()
                                                        .metricsId(childMetrics.getId())
                                                        .metricsType(childMetrics.getMetricsType())
                                                        .title(childMetrics.getTitle())
                                                        .unit(childMetrics.getUnit())
                                                        .description(childMetrics.getDescription())
                                                        .actualRuleCategory(actualRuleCategory)
                                                        .targetRuleCategory(targetRuleCategory)
                                                        .comparisonSetting(comparisonRuleDTO)
                                                        .dataSource(childMetrics.getDataSource())
                                                        .build();
                                            }).toList();
                                    return HotelKpiSettingItemDTO.builder()
                                            .title(parent1Metrics.getTitle())
                                            .metrics(metrics)
                                            .build();
                                }).toList();
                        return HotelKpiSettingDetailDTO.builder()
                                .title(parent0Metrics.getTitle())
                                .items(items).build();
                    }).toList();
        }

        /**
         * 获取 Setting 规则分类
         *
         * @param settingList 指标设置
         * @return java.lang.Integer
         * @author zc
         * @date 2025/1/14
         */
        private Integer getRuleCategory(List<HotelKpiMetricsSetting> settingList) {
            if (settingList.size() == 1) {
                return settingList.get(0).getSettingCategory();
            } else if (settingList.size() == 2) {
                return HotelKpiSettingCons.CALCULATION_FACTOR;
            }
            return null;
        }

        /**
         * 获取年指标设置数据
         *
         * @param propertyId    资产 id
         * @param metricsTypes  指标类型
         * @param startYear     开始年份
         * @param endYear       结束年份
         * @param valueCategory 1:Actual 2:Target
         * @return HotelKpiMetricsSetting 集合
         * @author zc
         * @date 2025/1/14
         */
        @Override
        public List<HotelKpiMetricsSetting> listByYearRangeAndMetricsTypes(int propertyId, List<Integer> metricsTypes,
                                                                           long startYear, long endYear, @Nullable Integer valueCategory) {
            if (CollectionUtils.isEmpty(metricsTypes)) {
                return Collections.emptyList();
            }
            return list(new LambdaQueryWrapper<HotelKpiMetricsSetting>()
                    .eq(HotelKpiMetricsSetting::getPropertyId, propertyId)
                    .between(HotelKpiMetricsSetting::getYear, startYear, endYear)
                    .in(HotelKpiMetricsSetting::getMetricsType, metricsTypes)
                    .eq(Objects.nonNull(valueCategory), HotelKpiMetricsSetting::getValueCategory, valueCategory));
        }

        /**
         * 获取 Setting Rule
         *
         * @param metricsConfig 指标配置
         * @param year          年份(yyyy)
         * @param valueCategory 1:Actual 2:Target
         * @return HotelKpiSettingCalculationDTO
         * @author zc
         * @date 2025/1/14
         */
        @Override
        public HotelKpiSettingCalculationDTO getSettingRule(HotelKpiMetricsConfig metricsConfig, long year, int valueCategory) {
            HotelKpiCalculationFactorConfigDTO configDTO = JsonUtils.convertStringToObject(objectMapper,
                    metricsConfig.getCalculationFactorConfig(), HotelKpiCalculationFactorConfigDTO.class);

            // 获取对应 valueCategory 的 calculationFactor
            HotelKpiCalculationFactorConfigItemDTO configItemDTO = (valueCategory == HotelKpiValueCategoryCons.ACTUAL) ?
                    configDTO.getActual() : configDTO.getTarget();

            // 获取指定年份的 Setting rule
            Map<Integer, HotelKpiMetricsSetting> index2Setting = listByYearRangeAndMetricsTypes(metricsConfig.getPropertyId(),
                    Lists.newArrayList(metricsConfig.getMetricsType()), year, year, valueCategory).stream()
                    .collect(Collectors.toMap(HotelKpiMetricsSetting::getIndex, Function.identity()));

            List<HotelKpiSettingCalculationFactorDTO> factorDTOList = configItemDTO.getCalculationFactor().stream().map(item -> {
                HotelKpiMetricsSetting itemSetting = index2Setting.getOrDefault(item.getIndex(), new HotelKpiMetricsSetting());
                return HotelKpiSettingCalculationFactorDTO.builder()
                        .index(item.getIndex())
                        .title(item.getTitle())
                        .valueSetting(HotelKpiSettingCalculationFactorSettingDTO.builder()
                                .category(itemSetting.getSettingCategory())
                                .settingValue(itemSetting.getDetail())
                                .build())
                        .build();
            }).toList();
            return HotelKpiSettingCalculationDTO.builder()
                    .metricsType(metricsConfig.getMetricsType())
                    .percent(configItemDTO.getPercent())
                    .calculationFactor(factorDTOList)
                    .dividerZeroContent(configItemDTO.getValueContent())
                    .build();
        }

        /**
         * 组装 Metrics Setting
         *
         * @param metricsDTOList              指标数据
         * @param metricsSettingActualDTOList Actual Setting
         * @param metricsSettingTargetDTOList Target/Cure Setting
         * @author zc
         * @date 2025/1/14
         */
        @Override
        public void packageMetricsSetting(List<HotelKpiMetricsDTO> metricsDTOList, List<HotelKpiMetricsSettingActualDTO> metricsSettingActualDTOList,
                                          List<HotelKpiMetricsSettingTargetDTO> metricsSettingTargetDTOList) {
            // Actual 的设置数据
            Map<Long, Map<Integer, List<HotelKpiMetricsSettingActualDTO>>> settingActualDTOMap =
                    metricsSettingActualDTOList.stream().collect(Collectors.groupingBy(HotelKpiMetricsSettingActualDTO::getDate,
                            Collectors.groupingBy(HotelKpiMetricsSettingActualDTO::getMetricsType)));

            // Target、Cure 设置数据
            Map<Long, Map<Integer, List<HotelKpiMetricsSettingTargetDTO>>> settingTargetDTOMap =
                    metricsSettingTargetDTOList.stream().collect(Collectors.groupingBy(HotelKpiMetricsSettingTargetDTO::getDate,
                            Collectors.groupingBy(HotelKpiMetricsSettingTargetDTO::getMetricsType)));

            for (HotelKpiMetricsDTO metricsDTO : metricsDTOList) {
                int metricsType = metricsDTO.getMetricsType();

                // 组装指标 Actual Setting
                List<HotelKpiMetricsSettingActualDTO> settingActualDTOS = settingActualDTOMap.getOrDefault(metricsDTO.getDate(),
                        Collections.emptyMap()).getOrDefault(metricsType, Collections.emptyList());
                packageActualMetricsSetting(metricsDTO.getActual(), settingActualDTOS);

                // 组装指标 Target Cure Setting
                List<HotelKpiMetricsSettingTargetDTO> settingTargetDTOS = settingTargetDTOMap.getOrDefault(metricsDTO.getDate(),
                        Collections.emptyMap()).getOrDefault(metricsType, Collections.emptyList());
                packageTargetCureMetricsSetting(metricsDTO, settingTargetDTOS);
            }
        }

        /**
         * 组装 setting 配置的计算因子数值
         *
         * @param kpiValueDTO       指标数值DTO
         * @param settingActualDTOS 设置数据
         * @author zc
         * @date 2025/1/15
         */
        private void packageActualMetricsSetting(HotelKpiValueDTO kpiValueDTO, List<HotelKpiMetricsSettingActualDTO> settingActualDTOS) {
            if (CollectionUtils.isEmpty(settingActualDTOS)) {
                return;
            }
            Map<Integer, HotelKpiCalculationFactorDTO> index2factorDTO = kpiValueDTO.getCalculationFactor().stream()
                    .collect(Collectors.toMap(HotelKpiCalculationFactorDTO::getIndex, Function.identity()));
            List<HotelKpiCalculationFactorDTO> calculationFactorDTOList = Lists.newArrayList();
            for (HotelKpiMetricsSettingActualDTO settingActualDTO : settingActualDTOS) {
                HotelKpiCalculationFactorDTO calculationFactorDTO = index2factorDTO.getOrDefault(settingActualDTO.getIndex(),
                        new HotelKpiCalculationFactorDTO());
                calculationFactorDTO.setIndex(settingActualDTO.getIndex());
                if (Objects.nonNull(settingActualDTO.getValue())) {
                    calculationFactorDTO.setCrawl(settingActualDTO.getValue());
                }
                calculationFactorDTO.setSettingCategory(settingActualDTO.getSettingCategory());

                calculationFactorDTOList.add(calculationFactorDTO);
            }
            kpiValueDTO.setCalculationFactor(calculationFactorDTOList);
        }

        /**
         * 组装指标 Target/Cure Setting
         *
         * @param metricsDTO        指标数值DTO
         * @param settingTargetDTOS 设置数据
         * @author zc
         * @date 2025/1/15
         */
        private void packageTargetCureMetricsSetting(HotelKpiMetricsDTO metricsDTO, List<HotelKpiMetricsSettingTargetDTO> settingTargetDTOS) {
            if (CollectionUtils.isEmpty(settingTargetDTOS)) {
                return;
            }
            // 组装 Target Setting
            Map<Integer, HotelKpiCalculationFactorDTO> index2factorDTOByTarget = metricsDTO.getTarget().getCalculationFactor().stream()
                    .collect(Collectors.toMap(HotelKpiCalculationFactorDTO::getIndex, Function.identity()));
            List<HotelKpiCalculationFactorDTO> calculationFactorDTOListForTarget = Lists.newArrayList();
            for (HotelKpiMetricsSettingTargetDTO settingTargetDTO : settingTargetDTOS) {
                HotelKpiCalculationFactorDTO calculationFactorDTO = index2factorDTOByTarget.getOrDefault(settingTargetDTO.getIndex(),
                        new HotelKpiCalculationFactorDTO());
                calculationFactorDTO.setIndex(settingTargetDTO.getIndex());
                if (Objects.nonNull(settingTargetDTO.getTarget())) {
                    calculationFactorDTO.setCrawl(settingTargetDTO.getTarget());
                }
                calculationFactorDTO.setSettingCategory(settingTargetDTO.getSettingCategory());

                calculationFactorDTOListForTarget.add(calculationFactorDTO);
            }
            metricsDTO.getTarget().setCalculationFactor(calculationFactorDTOListForTarget);

            // 组装 Cure Setting
            Map<Integer, HotelKpiCalculationFactorDTO> index2factorDTOByCure = metricsDTO.getCure().getCalculationFactor().stream()
                    .collect(Collectors.toMap(HotelKpiCalculationFactorDTO::getIndex, Function.identity()));
            List<HotelKpiCalculationFactorDTO> calculationFactorDTOListForCure = Lists.newArrayList();
            for (HotelKpiMetricsSettingTargetDTO settingTargetDTO : settingTargetDTOS) {
                HotelKpiCalculationFactorDTO calculationFactorDTO = index2factorDTOByCure.getOrDefault(settingTargetDTO.getIndex(),
                        new HotelKpiCalculationFactorDTO());
                calculationFactorDTO.setIndex(settingTargetDTO.getIndex());
                if (Objects.nonNull(settingTargetDTO.getCure())) {
                    calculationFactorDTO.setCrawl(settingTargetDTO.getCure());
                }
                calculationFactorDTO.setSettingCategory(settingTargetDTO.getSettingCategory());

                calculationFactorDTOListForCure.add(calculationFactorDTO);
            }
            metricsDTO.getCure().setCalculationFactor(calculationFactorDTOListForCure);
        }
    }