# HotelKpiCrawlValueServiceImpl.java

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

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.InternationalizationUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.commonlib.util.YearMonthWeekUtils;
    import cloud.wconnect.workperformance.cons.hotelkpi.HotelKpiDateTypeCons;
    import cloud.wconnect.workperformance.cons.hotelkpi.HotelKpiMetricsEnum;
    import cloud.wconnect.workperformance.cons.hotelkpi.formula.HotelKpiFormulaParseCrawlCons;
    import cloud.wconnect.workperformance.entity.hotelkpi.*;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.factorconfig.HotelKpiCalculationFactorConfigDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.factorconfig.HotelKpiCalculationFactorConfigIndexDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.internal.crawl.value.v1.HotelKpiCrawlDailyDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.internal.crawl.value.v1.HotelKpiCrawlValueDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.internal.crawl.value.v1.HotelKpiCrawlValueIndexDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.internal.crawl.value.v2.HotelKpiV2CrawlValueDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.originkpi.HotelKpiOriginCalculationFactorDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.originkpi.HotelKpiOriginCrawlValueIndexDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.originkpi.HotelKpiOriginDetailDTO;
    import cloud.wconnect.workperformance.service.hotelkpi.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import jakarta.annotation.Resource;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.time.DayOfWeek;
    import java.time.LocalDate;
    import java.time.temporal.TemporalAdjusters;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     * Hotel KPI Crawl Value 服务实现类
     * </p>
     *
     * @author zc
     * @since 2024-07-11
     */
    @Service
    @DS("work_performance")
    public class HotelKpiCrawlValueServiceImpl implements IHotelKpiCrawlValueService {
        @Resource
        private IHotelKpiService hotelKpiService;
        @Resource
        private IHotelKpiDailyService dailyService;
        @Resource
        private IHotelKpiWeeklyService weeklyService;
        @Resource
        private IHotelKpiMonthlyService monthlyService;
        @Resource
        private IHotelKpiQuarterlyService quarterlyService;
        @Resource
        private IHotelKpiYearlyService yearlyService;
        @Resource
        private IHotelKpiQuoreService quoreService;
        @Resource
        private IHotelKpiMetricsConfigService metricsConfigService;
        @Resource
        private IHotelKpiWeekStartDayService weekStartDayService;
        @Resource
        private ObjectMapper objectMapper;

        /**
         * 处理采集数据
         *
         * @param propertyDTO   资产
         * @param crawlValueDTO 采集数据DTO
         * @author zc
         * @date 2024/6/30
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleCrawlValue(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO) {
            // 获取指标配置
            HotelKpiMetricsConfig metricsConfig = metricsConfigService.getMetricsConfigByTypeForUpdate(propertyDTO.getId(),
                    crawlValueDTO.getMetricsType());
            if (Objects.isNull(metricsConfig)) {
                return;
            }

            // 保存采集数据
            int dateType = crawlValueDTO.getDateType();
            long date = crawlValueDTO.getDate();
            if (dateType == HotelKpiDateTypeCons.WEEKLY) {
                saveWeeklyForCrawlValue(propertyDTO, crawlValueDTO, metricsConfig, date);
            } else if (dateType == HotelKpiDateTypeCons.MONTHLY) {
                saveMonthlyForCrawlValue(propertyDTO, crawlValueDTO, metricsConfig, date);
            } else if (dateType == HotelKpiDateTypeCons.QUARTERLY) {
                saveQuarterlyForCrawlValue(propertyDTO, crawlValueDTO, metricsConfig, date);
            } else {
                saveYearlyForCrawlValue(propertyDTO, crawlValueDTO, metricsConfig, date);
            }
        }

        /**
         * 保存周数据 For Crawl Value
         *
         * @param propertyDTO   资产
         * @param crawlValueDTO 采集数据
         * @param metricsConfig 指标配置
         * @param date          日期(yyyyMMdd)
         * @author zc
         * @date 2024/8/6
         */
        private void saveWeeklyForCrawlValue(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO,
                                             HotelKpiMetricsConfig metricsConfig, long date) {
            // 转换为所在周的周起始日
            DayOfWeek dayOfWeek = weekStartDayService.getWeekStartDay(propertyDTO.getId());
            LocalDate weekStartLocalDate = DateTimeUtils.strToLocalDate(String.valueOf(date), BaseConstant.DATE_FORMAT_YYYYMMDD)
                    .with(TemporalAdjusters.previousOrSame(dayOfWeek));
            long week = Long.parseLong(DateTimeUtils.localDateToStr(weekStartLocalDate, BaseConstant.DATE_FORMAT_YYYYMMDD));

            // 获取原始数据
            HotelKpiWeekly originKpiWeekly = weeklyService.getByDateAndMetricsType(propertyDTO.getId(), week, metricsConfig.getMetricsType());
            if (Objects.isNull(originKpiWeekly)) {
                originKpiWeekly = new HotelKpiWeekly();
            }

            // 组装原始指标数据DTO
            String originDetail = packageOriginDetail(propertyDTO, crawlValueDTO, metricsConfig, originKpiWeekly.getDetail());

            weeklyService.insertOrUpdate(HotelKpiWeekly.builder()
                    .propertyId(propertyDTO.getId())
                    .week(week)
                    .metricsType(metricsConfig.getMetricsType())
                    .detail(originDetail)
                    .build());
        }

        /**
         * 保存月数据 For Crawl Value
         *
         * @param propertyDTO   资产
         * @param crawlValueDTO 采集数据
         * @param metricsConfig 指标配置
         * @param date          日期(yyyyMM)
         * @author zc
         * @date 2024/8/6
         */
        private void saveMonthlyForCrawlValue(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO,
                                              HotelKpiMetricsConfig metricsConfig, long date) {
            // 获取原始数据
            HotelKpiMonthly originKpiMonthly = monthlyService.getByDateAndMetricsType(propertyDTO.getId(), date, metricsConfig.getMetricsType());
            if (Objects.isNull(originKpiMonthly)) {
                originKpiMonthly = new HotelKpiMonthly();
            }

            // 组装原始指标数据DTO
            String originDetail = packageOriginDetail(propertyDTO, crawlValueDTO, metricsConfig, originKpiMonthly.getDetail());

            monthlyService.insertOrUpdate(HotelKpiMonthly.builder()
                    .propertyId(propertyDTO.getId())
                    .month(date)
                    .metricsType(metricsConfig.getMetricsType())
                    .detail(originDetail)
                    .build());
        }

        /**
         * 保存季数据 For Crawl Value
         *
         * @param propertyDTO   资产
         * @param crawlValueDTO 采集数据
         * @param metricsConfig 指标配置
         * @param date          日期(yyyyMM)
         * @author zc
         * @date 2024/8/6
         */
        private void saveQuarterlyForCrawlValue(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO,
                                                HotelKpiMetricsConfig metricsConfig, long date) {
            // 转换为季度首月(如：202401, 202404, 202407, 202410)
            long quarter = YearMonthWeekUtils.getQuarterFirstDate(date);

            // 获取原始数据
            HotelKpiQuarterly originKpiQuarterly = quarterlyService.getByDateAndMetricsType(propertyDTO.getId(), quarter, metricsConfig.getMetricsType());
            if (Objects.isNull(originKpiQuarterly)) {
                originKpiQuarterly = new HotelKpiQuarterly();
            }

            // 组装原始指标数据DTO
            String originDetail = packageOriginDetail(propertyDTO, crawlValueDTO, metricsConfig, originKpiQuarterly.getDetail());

            quarterlyService.insertOrUpdate(HotelKpiQuarterly.builder()
                    .propertyId(propertyDTO.getId())
                    .month(quarter)
                    .metricsType(metricsConfig.getMetricsType())
                    .detail(originDetail)
                    .build());
        }

        /**
         * 保存年数据 For Crawl Value
         *
         * @param propertyDTO   资产
         * @param crawlValueDTO 采集数据
         * @param metricsConfig 指标配置
         * @param date          日期(yyyy)
         * @author zc
         * @date 2024/8/6
         */
        private void saveYearlyForCrawlValue(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO,
                                             HotelKpiMetricsConfig metricsConfig, long date) {
            // 获取原始数据
            HotelKpiYearly originKpiYearly = yearlyService.getByDateAndMetricsType(propertyDTO.getId(), date, metricsConfig.getMetricsType());
            if (Objects.isNull(originKpiYearly)) {
                originKpiYearly = new HotelKpiYearly();
            }

            // 组装原始指标数据DTO
            String originDetail = packageOriginDetail(propertyDTO, crawlValueDTO, metricsConfig, originKpiYearly.getDetail());

            yearlyService.insertOrUpdate(HotelKpiYearly.builder()
                    .propertyId(propertyDTO.getId())
                    .year(date)
                    .metricsType(metricsConfig.getMetricsType())
                    .detail(originDetail)
                    .build());
        }

        /**
         * 组装原始指标数据DTO
         *
         * @param propertyDTO   资产
         * @param crawlValueDTO 采集数据
         * @param metricsConfig 指标配置
         * @param originDetail  原始数据详情
         * @author zc
         * @date 2024/8/8
         */
        private String packageOriginDetail(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO,
                                           HotelKpiMetricsConfig metricsConfig, String originDetail) {
            HotelKpiOriginDetailDTO originDetailDTO = Objects.isNull(originDetail) ? new HotelKpiOriginDetailDTO() :
                    JsonUtils.convertStringToObject(objectMapper, originDetail, HotelKpiOriginDetailDTO.class);

            int metricsType = metricsConfig.getMetricsType();
            if (HotelKpiMetricsEnum.GSI.getMetricsType() == metricsType) {
                parseCrawlValueForGSI(propertyDTO, crawlValueDTO, originDetailDTO);
            } else if (HotelKpiMetricsEnum.QUORE_COMPLAINT.getMetricsType() == metricsType) {
                parseCrawlValueForQuoreComplaint(propertyDTO, crawlValueDTO, metricsConfig, originDetailDTO);
            } else if (HotelKpiMetricsEnum.KIPSU_CONVERSATION.getMetricsType() == metricsType) {
                parseCrawlValueForKipsuConversation(propertyDTO, crawlValueDTO, originDetailDTO);
            } else {
                parseCrawlValueForOther(propertyDTO, crawlValueDTO, originDetailDTO);
            }
            return JsonUtils.convertObjToString(objectMapper, originDetailDTO);
        }

        /**
         * 解析采集数据 for GSI
         *
         * @param propertyDTO     资产
         * @param crawlValueDTO   采集数据
         * @param originDetailDTO 原始数据详情DTO
         * @author zc
         * @date 2024/8/6
         */
        private void parseCrawlValueForGSI(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO,
                                           HotelKpiOriginDetailDTO originDetailDTO) {
            Map<Integer, HotelKpiCrawlValueIndexDTO> index2ValueDTO = crawlValueDTO.getActualList().stream()
                    .collect(Collectors.toMap(HotelKpiCrawlValueIndexDTO::getIndex, Function.identity()));
            HotelKpiCrawlValueIndexDTO valueIndex1DTO = index2ValueDTO.get(1);
            HotelKpiCrawlValueIndexDTO valueIndex2DTO = index2ValueDTO.get(2);
            if (Objects.isNull(valueIndex1DTO) || Objects.isNull(valueIndex2DTO)) {
                return;
            }

            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(propertyDTO
                    .getInternationalization().getNumber().getCalculationRule());

            BigDecimal value1 = valueIndex1DTO.getValue();
            BigDecimal value2 = valueIndex2DTO.getValue();
            BigDecimal value = value1.multiply(new BigDecimal("100")).divide(value2, BaseConstant.DATA_PRECISION, roundingMode);

            // 组装新的计算因子 1
            HotelKpiOriginCalculationFactorDTO newFactorDTO = new HotelKpiOriginCalculationFactorDTO().setIndex(1).setCrawl(value)
                    .setCrawlOrigin(List.of(HotelKpiOriginCrawlValueIndexDTO.builder().index(1).value(value1).build(),
                            HotelKpiOriginCrawlValueIndexDTO.builder().index(2).value(value2).build()));

            // 替换指标原始数值的计算因子 1
            hotelKpiService.replaceOriginFactorList(originDetailDTO.getActual().getCalculationFactor(), newFactorDTO);
        }

        /**
         * 解析采集数据 for Quore complaint
         *
         * @param propertyDTO     资产
         * @param crawlValueDTO   采集数据
         * @param metricsConfig   指标配置
         * @param originDetailDTO 原始数据详情DTO
         * @author zc
         * @date 2024/8/6
         */
        private void parseCrawlValueForQuoreComplaint(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO,
                                                      HotelKpiMetricsConfig metricsConfig, HotelKpiOriginDetailDTO originDetailDTO) {
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(propertyDTO
                    .getInternationalization().getNumber().getCalculationRule());

            Map<Integer, HotelKpiCrawlValueIndexDTO> index2ValueDTOMap = crawlValueDTO.getActualList().stream()
                    .collect(Collectors.toMap(HotelKpiCrawlValueIndexDTO::getIndex, Function.identity()));
            List<HotelKpiOriginCalculationFactorDTO> calculationFactorDTOList = originDetailDTO.getActual().getCalculationFactor();

            // index 1,2 分别对应计算因子 2 的被除数和除数
            HotelKpiCrawlValueIndexDTO valueIndex1DTO = index2ValueDTOMap.get(1);
            if (Objects.nonNull(valueIndex1DTO)) {

                HotelKpiCalculationFactorConfigDTO configDTO = JsonUtils.convertStringToObject(objectMapper,
                        metricsConfig.getCalculationFactorConfig(), HotelKpiCalculationFactorConfigDTO.class);

                // 提取计算因子的公式
                HotelKpiCalculationFactorConfigIndexDTO factorConfigIndex2DTOByActual = configDTO.getActual()
                        .getCalculationFactor().stream().filter(item -> item.getIndex() == 2).findFirst().orElse(null);
                String formula = "";
                if (Objects.nonNull(factorConfigIndex2DTOByActual)) {
                    formula = factorConfigIndex2DTOByActual.getFormula();
                }

                BigDecimal value = null;
                List<HotelKpiOriginCrawlValueIndexDTO> originValues;
                if (HotelKpiFormulaParseCrawlCons.VALUE1_MULTIPLY_VALUE2_PERCENTAGE.equals(formula)) {
                    HotelKpiCrawlValueIndexDTO valueIndex2DTO = index2ValueDTOMap.get(2);
                    if (Objects.isNull(valueIndex2DTO)) {
                        return;
                    }
                    BigDecimal value1 = valueIndex1DTO.getValue();
                    BigDecimal value2 = valueIndex2DTO.getValue();
                    if (Objects.nonNull(value1) && Objects.nonNull(value2)) {
                        value = value1.multiply(value2).divide(new BigDecimal("100"), BaseConstant.DATA_PRECISION, roundingMode);
                    }
                    originValues = List.of(HotelKpiOriginCrawlValueIndexDTO.builder().index(1).value(value1).build(),
                            HotelKpiOriginCrawlValueIndexDTO.builder().index(2).value(value2).build());
                } else {
                    value = valueIndex1DTO.getValue();
                    originValues = List.of(HotelKpiOriginCrawlValueIndexDTO.builder().index(1).value(value).build());
                }

                // 组装新的计算因子 2
                HotelKpiOriginCalculationFactorDTO newFactorDTO = new HotelKpiOriginCalculationFactorDTO().setIndex(2)
                        .setCrawl(value).setCrawlOrigin(originValues);

                // 替换指标原始数值的计算因子 2
                hotelKpiService.replaceOriginFactorList(calculationFactorDTOList, newFactorDTO);
            }
            // index 3 对应计算因子 1
            HotelKpiCrawlValueIndexDTO valueIndex3DTO = index2ValueDTOMap.get(3);
            if (Objects.nonNull(valueIndex3DTO)) {
                BigDecimal value = valueIndex3DTO.getValue();
                if (Objects.nonNull(value)) {
                    value = value.setScale(BaseConstant.DATA_PRECISION, roundingMode);
                }
                // 组装新的计算因子 1
                HotelKpiOriginCalculationFactorDTO newFactorDTO = new HotelKpiOriginCalculationFactorDTO().setIndex(1).setCrawl(value)
                        .setCrawlOrigin(List.of(HotelKpiOriginCrawlValueIndexDTO.builder().index(1).value(value).build()));

                // 替换指标原始数值的计算因子 1
                hotelKpiService.replaceOriginFactorList(calculationFactorDTOList, newFactorDTO);
            }
        }

        /**
         * 解析采集数据 for Kipsu conversation
         *
         * @param propertyDTO     资产
         * @param crawlValueDTO   采集数据
         * @param originDetailDTO 原始数据详情DTO
         * @author zc
         * @date 2024/8/6
         */
        private void parseCrawlValueForKipsuConversation(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO,
                                                         HotelKpiOriginDetailDTO originDetailDTO) {
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(propertyDTO
                    .getInternationalization().getNumber().getCalculationRule());

            Map<Integer, HotelKpiCrawlValueIndexDTO> index2ValueDTO = crawlValueDTO.getActualList().stream()
                    .collect(Collectors.toMap(HotelKpiCrawlValueIndexDTO::getIndex, Function.identity()));
            HotelKpiCrawlValueIndexDTO valueIndex1DTO = index2ValueDTO.get(1);
            if (Objects.isNull(valueIndex1DTO)) {
                return;
            }

            BigDecimal value = valueIndex1DTO.getValue();
            if (Objects.nonNull(value)) {
                value = value.setScale(BaseConstant.DATA_PRECISION, roundingMode);
            }

            // 组装新的计算因子 1
            HotelKpiOriginCalculationFactorDTO factorIndex1 = new HotelKpiOriginCalculationFactorDTO().setIndex(1).setCrawl(value)
                    .setCrawlOrigin(List.of(HotelKpiOriginCrawlValueIndexDTO.builder().index(1).value(value).build()));

            // 替换指标原始数值的计算因子 1
            hotelKpiService.replaceOriginFactorList(originDetailDTO.getActual().getCalculationFactor(), factorIndex1);
        }

        /**
         * 解析采集数据 for Other
         *
         * @param propertyDTO     资产
         * @param crawlValueDTO   采集数据
         * @param originDetailDTO 原始数据详情DTO
         * @author zc
         * @date 2025/1/3
         */
        private void parseCrawlValueForOther(PropertyDTO propertyDTO, HotelKpiCrawlValueDTO crawlValueDTO, HotelKpiOriginDetailDTO originDetailDTO) {
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(propertyDTO
                    .getInternationalization().getNumber().getCalculationRule());

            Map<Integer, HotelKpiCrawlValueIndexDTO> index2ValueDTO = crawlValueDTO.getActualList().stream()
                    .collect(Collectors.toMap(HotelKpiCrawlValueIndexDTO::getIndex, Function.identity()));

            List<HotelKpiOriginCalculationFactorDTO> calculationFactorDTOList = originDetailDTO.getActual().getCalculationFactor();
            HotelKpiCrawlValueIndexDTO valueIndex1DTO = index2ValueDTO.get(1);
            if (Objects.nonNull(valueIndex1DTO)) {
                BigDecimal value = valueIndex1DTO.getValue();
                if (Objects.nonNull(value)) {
                    value = value.setScale(BaseConstant.DATA_PRECISION, roundingMode);
                }
                // 组装新的计算因子 1
                HotelKpiOriginCalculationFactorDTO factorIndex1 = new HotelKpiOriginCalculationFactorDTO().setIndex(1).setCrawl(value)
                        .setCrawlOrigin(List.of(HotelKpiOriginCrawlValueIndexDTO.builder().index(1).value(value).build()));

                // 替换指标原始数值的计算因子 1
                hotelKpiService.replaceOriginFactorList(calculationFactorDTOList, factorIndex1);
            }
            HotelKpiCrawlValueIndexDTO valueIndex2DTO = index2ValueDTO.get(2);
            if (Objects.nonNull(valueIndex2DTO)) {
                BigDecimal value = valueIndex2DTO.getValue();
                if (Objects.nonNull(value)) {
                    value = value.setScale(BaseConstant.DATA_PRECISION, roundingMode);
                }
                // 组装新的计算因子 2
                HotelKpiOriginCalculationFactorDTO factorIndex2 = new HotelKpiOriginCalculationFactorDTO().setIndex(2).setCrawl(value)
                        .setCrawlOrigin(List.of(HotelKpiOriginCrawlValueIndexDTO.builder().index(1).value(value).build()));

                // 替换指标原始数值的计算因子 2
                hotelKpiService.replaceOriginFactorList(calculationFactorDTOList, factorIndex2);
            }
        }

        /**
         * 处理采集每日数据
         *
         * @param propertyDTO 资产
         * @param dailyDTO    采集数据DTO
         * @author zc
         * @date 2025/3/24
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleCrawlValueDaily(PropertyDTO propertyDTO, HotelKpiCrawlDailyDTO dailyDTO) {
            // 获取指标配置
            HotelKpiMetricsConfig metricsConfig = metricsConfigService.getMetricsConfigByTypeForUpdate(propertyDTO.getId(),
                    dailyDTO.getMetricsType());
            if (Objects.isNull(metricsConfig)) {
                return;
            }

            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(propertyDTO
                    .getInternationalization().getNumber().getCalculationRule());
            // 数字精度处理
            dailyDTO.getList().forEach(item ->
                    item.setValue(Objects.isNull(item.getValue()) ? null :
                            item.getValue().setScale(BaseConstant.DATA_PRECISION, roundingMode))
            );

            // 保存采集数据
            dailyService.saveCrawlValueDaily(propertyDTO, dailyDTO);
            // 处理采集数据
            if (Lists.newArrayList(HotelKpiMetricsEnum.QUORE_DEEP_CLEAN.getMetricsType(),
                    HotelKpiMetricsEnum.QUORE_GR_PM.getMetricsType()).contains(dailyDTO.getMetricsType())) {
                quoreService.handleCrawlValueDaily(propertyDTO, dailyDTO);
            }
        }

        /**
         * 处理采集数据 V2
         *
         * @param propertyDTO   资产
         * @param crawlValueDTO 采集数据DTO
         * @author zc
         * @date 2025/8/25
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleCrawlValueV2(PropertyDTO propertyDTO, HotelKpiV2CrawlValueDTO crawlValueDTO) {
            // 获取指标配置
            HotelKpiMetricsConfig metricsConfig = metricsConfigService.getMetricsConfigByTypeForUpdate(propertyDTO.getId(),
                    crawlValueDTO.getMetricsType());
            if (Objects.isNull(metricsConfig)) {
                return;
            }

            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(propertyDTO
                    .getInternationalization().getNumber().getCalculationRule());
            // 数字精度处理
            crawlValueDTO.getList().forEach(item -> {
                item.getActual().forEach(actual ->
                        actual.setValue(Objects.isNull(actual.getValue()) ? null :
                                actual.getValue().setScale(BaseConstant.DATA_PRECISION, roundingMode))
                );
                item.getBudget().forEach(budget ->
                        budget.setValue(Objects.isNull(budget.getValue()) ? null :
                                budget.getValue().setScale(BaseConstant.DATA_PRECISION, roundingMode))
                );
                item.getLy().forEach(ly ->
                        ly.setValue(Objects.isNull(ly.getValue()) ? null :
                                ly.getValue().setScale(BaseConstant.DATA_PRECISION, roundingMode))
                );
            });

            if (HotelKpiDateTypeCons.WEEKLY == crawlValueDTO.getDateType()) {
                weeklyService.saveCrawlValueV2(propertyDTO, crawlValueDTO, metricsConfig);
            } else if (HotelKpiDateTypeCons.MONTHLY == crawlValueDTO.getDateType()) {
                monthlyService.saveCrawlValueV2(propertyDTO, crawlValueDTO, metricsConfig);
            } else if (HotelKpiDateTypeCons.QUARTERLY == crawlValueDTO.getDateType()) {
                quarterlyService.saveCrawlValueV2(propertyDTO, crawlValueDTO, metricsConfig);
            } else {
                yearlyService.saveCrawlValueV2(propertyDTO, crawlValueDTO, metricsConfig);
            }
        }
    }