# RevenueHandlerFutureDataServiceImpl.java

    package cloud.wconnect.rateshop.service.revenue.impl;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.internationalization.InternationalizationNumberDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.InternationalizationUtils;
    import cloud.wconnect.rateshop.entity.revenue.*;
    import cloud.wconnect.rateshop.pojo.bo.revenue.BanquetAndFBOTBRevenueBO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.LatestHotelPriceDTO;
    import cloud.wconnect.rateshop.pojo.dto.revenue.*;
    import cloud.wconnect.rateshop.pojo.query.revenue.RevenueDetailQuery;
    import cloud.wconnect.rateshop.service.revenue.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.google.common.collect.Lists;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.collections.CollectionUtils;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.stream.Collectors;

    /**
     * @author: yyt
     * 2024-05-08
     */
    @Service
    @DS("rate_shop")
    @Slf4j
    public class RevenueHandlerFutureDataServiceImpl implements IRevenueHandlerService {

        @Autowired
        private IRevenueOtbRevenueService revenueOtbRevenueService;
        @Autowired
        private IRevenueOtbRevenueFutureService revenueOtbRevenueFutureService;
        @Autowired
        private IRevenueBudgetOtbService revenueBudgetOtbService;
        @Autowired
        private IRevenueSegmentService revenueSegmentService;
        @Autowired
        private IRevenueBudgetSegmentService revenueBudgetSegmentService;
        @Autowired
        private IRevenueSegmentRevenueService revenueSegmentRevenueService;
        @Autowired
        private IRevenueSegmentRevenueFutureService revenueSegmentRevenueFutureService;
        @Autowired
        private IRevenueTransientGroupFutureService revenueTransientGroupFutureService;
        @Autowired
        private IRevenueTransientGroupLyFutureService revenueTransientGroupLyFutureService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private IRevenueOtbRevenueLastYearService revenueOtbRevenueLastYearService;
        @Autowired
        private IRevenueCustomersService revenueCustomersService;
        @Autowired
        private IRevenueHurdlePointFutureService revenueHurdlePointFutureService;
        @Autowired
        private IRevenueService revenueService;
        @Autowired
        private RedissonClient redissonClient;
        @Autowired
        private INext14DaysService next14DaysService;
        /**
         * redis key
         */
        public static final String UPDATE_RATE_REDIS_KEY = "wconnect:revenue:update-future-date-rate";

        /**
         * 检索数据
         *
         * @param propertyDTO 资产
         * @param date        指定日期
         * @param query       查询条件
         * @return revenue 数据
         * @author yyt
         * @date 2024/5/8
         **/
        @Override
        public RevenueDTO getDetail(PropertyDTO propertyDTO, Long date, RevenueDetailQuery query) {
            Integer propertyId = propertyDTO.getId();
            String timezone = propertyDTO.getTimezone();
            LocalDate targetDate = DateTimeUtils.longToLocalDate(date, timezone);
            OtbRevenueDTO otb = revenueOtbRevenueFutureService.getOtbRevenueDTO(propertyDTO, targetDate.getYear(), targetDate.getMonthValue());
            // OTB Revenue (Banquet Revenue)、F&B OTB Revenue 取当前数据的 db
            RevenueOtbRevenue otbRevenue = revenueOtbRevenueService.getOne(new QueryWrapper<RevenueOtbRevenue>().lambda()
                    .eq(RevenueOtbRevenue::getPropertyId, propertyId)
                    .eq(RevenueOtbRevenue::getTheDate, date));
            otb.setBanquetRevenue(Objects.isNull(otbRevenue) ? null : otbRevenue.getBanquetRevenue());
            otb.setFbOtbRevenue(Objects.isNull(otbRevenue) ? null : otbRevenue.getFbOtbRevenue());

            BudgetDTO budget = revenueBudgetOtbService.getBudgetDTO(propertyDTO, date);

            InternationalizationNumberDTO number = propertyDTO.getInternationalization().getNumber();
            int calculatePrecision = BaseConstant.DATA_PRECISION;
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(number.getCalculationRule());

            // achievement = otb revenue / budget otb revenue
            BigDecimal otbRevenueValue = otb.getOtbRevenue();
            BigDecimal budgetOtbRevenueValue = budget.getRoomRevenue();
            boolean validOtbRevenueAndBudget = (otbRevenueValue != null && otbRevenueValue.signum() >= 0)
                    && (budgetOtbRevenueValue != null && budgetOtbRevenueValue.signum() > 0);
            if (validOtbRevenueAndBudget) {
                otb.setOtbAchievement(otbRevenueValue.divide(budgetOtbRevenueValue, calculatePrecision, roundingMode)
                        .multiply(BigDecimal.valueOf(100)));
            }

            List<SegmentDTO> segments = listSegmentDTO(propertyDTO, date, DateTimeUtils.toZonedDateTime(date, timezone).minusDays(1).toEpochSecond());
            List<TransientAndGroupDTO> transientAndGroups = revenueTransientGroupFutureService.listTransientAndGroupDTO(propertyDTO, date);
            // 设置 hurdler point
            setHurdlePoint(propertyDTO, date, transientAndGroups);

            // 设置 MEPS Group
            revenueService.setMEPSGroup(propertyDTO, date, transientAndGroups);

            // 查询去年同期的 BAR 房价
            ZonedDateTime zonedDateTime = DateTimeUtils.toZonedDateTime(date, timezone);
            ZonedDateTime lastYearDate = DateTimeUtils.getSameDateLastYear(zonedDateTime.toLocalDate()).atStartOfDay(ZoneId.of(timezone));
            ZonedDateTime lastYearNextDate = lastYearDate.plusDays(13);
            Long startDate = Long.valueOf(lastYearDate.format(DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD)));
            Long endDate = Long.valueOf(lastYearNextDate.format(DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD)));
            List<LatestHotelPriceDTO> latestHotelPriceDTOS = revenueService.listBARRate(propertyId, startDate, endDate, query);
            // 获取 Transient And Group Last Year
            List<TransientAndGroupLyDTO> transientAndGroupLies = revenueTransientGroupLyFutureService.listTransientAndGroupLyDTO(propertyDTO, date, latestHotelPriceDTOS);

            LastYearOtbDTO lastYearOtbDTO =  revenueOtbRevenueLastYearService.getLastYearOtbDTO(propertyDTO, date);

            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);
            String dateStr = DateTimeUtils.toZonedDateTime(date, timezone).format(dateTimeFormatter);
            CustomersDTO customersDTO = revenueCustomersService.getCustomersDTO(propertyId, Integer.parseInt(dateStr));

            return new RevenueDTO(otb, budget, segments, transientAndGroups, transientAndGroupLies, lastYearOtbDTO, customersDTO);
        }

        /**
         * 获取 segment
         *
         * @param propertyDTO 资产
         * @param targetDate 目标日期
         * @param yesterday  昨天
         * @return segment
         * @author yyt
         * @date 2023/10/27
         */
        private List<SegmentDTO> listSegmentDTO(PropertyDTO propertyDTO, Long targetDate, Long yesterday) {
            Integer propertyId = propertyDTO.getId();
            List<RevenueSegment> segments = revenueSegmentService.listActiveSegment(propertyId);
            if (CollectionUtils.isEmpty(segments)) {
                return Collections.emptyList();
            }

            List<Integer> segmentIds = segments.stream().map(RevenueSegment::getId).collect(Collectors.toList());
            // 获取指定日期前一天 Segment Revenue 数据
            Map<Integer, RevenueSegmentRevenue> yesterdaySegmentRevenueBySegmentId =
                    revenueSegmentRevenueService.list(new QueryWrapper<RevenueSegmentRevenue>().lambda()
                            .eq(RevenueSegmentRevenue::getPropertyId, propertyId)
                            .eq(RevenueSegmentRevenue::getTheDate, yesterday)
                            .in(RevenueSegmentRevenue::getSegmentId, segmentIds))
                            .stream().collect(Collectors.toMap(RevenueSegmentRevenue::getSegmentId, v -> v));

            // 获取指定日期 + 昨天的 Segment Revenue Future 数据
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);
            String timezone = propertyDTO.getTimezone();
            Integer targetDateFormatter = Integer.parseInt(DateTimeUtils.longToLocalDate(targetDate, timezone).format(dateTimeFormatter));
            Integer yesterdayFormatter = Integer.parseInt(DateTimeUtils.longToLocalDate(yesterday, timezone).format(dateTimeFormatter));
            Map<Integer, Map<Integer, RevenueSegmentRevenueFuture>> segmentRevenueFutureBySegmentId =
                    revenueSegmentRevenueFutureService.list(new QueryWrapper<RevenueSegmentRevenueFuture>().lambda()
                                    .eq(RevenueSegmentRevenueFuture::getPropertyId, propertyId)
                                    .in(RevenueSegmentRevenueFuture::getTheDate, Lists.newArrayList(targetDateFormatter, yesterdayFormatter))
                                    .in(RevenueSegmentRevenueFuture::getSegmentId, segmentIds))
                            .stream().collect(Collectors.groupingBy(RevenueSegmentRevenueFuture::getSegmentId,
                                    Collectors.toMap(RevenueSegmentRevenueFuture::getTheDate, v -> v)));

            // 获取 segment budget
            ZonedDateTime targetDateZonedDateTime = DateTimeUtils.toZonedDateTime(targetDate, timezone);
            Map<Integer, BigDecimal> budgetRevenueBySegmentId = revenueBudgetSegmentService.list(new QueryWrapper<RevenueBudgetSegment>()
                            .lambda().eq(RevenueBudgetSegment::getPropertyId, propertyId)
                            .eq(RevenueBudgetSegment::getTheYear, targetDateZonedDateTime.getYear())
                            .eq(RevenueBudgetSegment::getTheMonth, targetDateZonedDateTime.getMonthValue())
                            .in(RevenueBudgetSegment::getSegmentId, segmentIds)).stream()
                    .filter(item -> Objects.nonNull(item.getBudgetRevenue()))
                    .collect(Collectors.toMap(RevenueBudgetSegment::getSegmentId, RevenueBudgetSegment::getBudgetRevenue));

            InternationalizationNumberDTO number = propertyDTO.getInternationalization().getNumber();
            int calculatePrecision = BaseConstant.DATA_PRECISION;
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(number.getCalculationRule());
            // 查看明天（当前日期后一天）的数据
            boolean viewAfterTheCurrentDate = DateTimeUtils.dateNow(timezone).plusDays(1).toEpochSecond() == targetDate;

            return segments.stream().map(item -> {
                int segmentId = item.getId();
                SegmentDTO segmentDTO = new SegmentDTO();
                segmentDTO.setSegmentId(segmentId);
                segmentDTO.setSegment(item.getSegment());

                // 获取 Segment对应的 Budget 值
                BigDecimal budget = budgetRevenueBySegmentId.get(segmentId);
                segmentDTO.setBudget(budget);

                Map<Integer, RevenueSegmentRevenueFuture> segmentRevenueFutureByDate = segmentRevenueFutureBySegmentId.getOrDefault(segmentId, Collections.emptyMap());
                RevenueSegmentRevenueFuture targetDateSegmentRevenue = segmentRevenueFutureByDate.getOrDefault(targetDateFormatter, new RevenueSegmentRevenueFuture());
                BigDecimal targetRevenue = targetDateSegmentRevenue.getRevenue();
                segmentDTO.setOtbRevenue(targetRevenue);
                segmentDTO.setAdr(targetDateSegmentRevenue.getAdr());
                if (Objects.nonNull(targetRevenue)) {
                    // 如果查看的是当前日期后一天的数据，则：pick up = 指定日期（未来数据） - 指定日期的前一天数据（不是未来数据）
                    // 否则：pick up = 指定日期（未来数据） - 指定日期的前一天数据（未来数据）
                    // 如果前一天是月底，则使用前一日中的 Revenue Next Month 计算 pick-up
                    if(viewAfterTheCurrentDate){
                        BigDecimal yesterdayRevenue;
                        boolean isLastDay = DateTimeUtils.isLastDayOfThisMonth(DateTimeUtils.toZonedDateTime(yesterday, timezone).toLocalDate());
                        RevenueSegmentRevenue yesterdaySegmentRevenue = yesterdaySegmentRevenueBySegmentId.getOrDefault(segmentId, new RevenueSegmentRevenue());
                        if (isLastDay) {
                            yesterdayRevenue = yesterdaySegmentRevenue.getRevenueNextMonth();
                        } else {
                            yesterdayRevenue = yesterdaySegmentRevenue.getRevenue();
                        }
                        if (Objects.nonNull(yesterdayRevenue)) {
                            segmentDTO.setPickup(targetRevenue.subtract(yesterdayRevenue));
                        }
                    } else {
                        BigDecimal yesterdayRevenue;
                        boolean isLastDay = DateTimeUtils.isLastDayOfThisMonth(DateTimeUtils.toZonedDateTime(yesterday, timezone).toLocalDate());
                        RevenueSegmentRevenueFuture yesterdaySegmentRevenueFuture = segmentRevenueFutureByDate.getOrDefault(yesterdayFormatter, new RevenueSegmentRevenueFuture());
                        if (isLastDay) {
                            yesterdayRevenue = yesterdaySegmentRevenueFuture.getRevenueNextMonth();
                        } else {
                            yesterdayRevenue = yesterdaySegmentRevenueFuture.getRevenue();
                        }
                        if (Objects.nonNull(yesterdayRevenue)) {
                            segmentDTO.setPickup(targetRevenue.subtract(yesterdayRevenue));
                        }
                    }

                    // 计算 achievement = revenue / budget
                    if (Objects.nonNull(budget) && budget.signum() != 0) {
                        segmentDTO.setAchievement(targetRevenue.divide(budget, calculatePrecision, roundingMode)
                                .multiply(BigDecimal.valueOf(100L)));
                    }
                }
                return segmentDTO;
            }).collect(Collectors.toList());
        }

        /**
         * 设置 Hurdle Point
         *
         * @param propertyDTO        资产
         * @param theDate            当前日期
         * @param transientAndGroups transientAndGroups
         * @author yyt
         * @date 2024/3/25
         **/
        private void setHurdlePoint(PropertyDTO propertyDTO, Long theDate, List<TransientAndGroupDTO> transientAndGroups) {
            // 查询当前日期对应的未来 14 天的 hurdle point
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);
            String timezone = propertyDTO.getTimezone();
            ZonedDateTime currentDay = DateTimeUtils.toZonedDateTime(theDate, timezone);
            ZonedDateTime nextDayDate = currentDay.plusDays(13);
            Map<Integer, BigDecimal> hurdlePointByDate = revenueHurdlePointFutureService.list(new QueryWrapper<RevenueHurdlePointFuture>()
                            .lambda()
                            .eq(RevenueHurdlePointFuture::getPropertyId, propertyDTO.getId())
                            .between(RevenueHurdlePointFuture::getTargetDate, Integer.parseInt(currentDay.format(dateTimeFormatter)),
                                    Integer.parseInt(nextDayDate.format(dateTimeFormatter)))).stream()
                    .filter(item -> Objects.nonNull(item.getHurdlePoint()))
                    .collect(Collectors.toMap(RevenueHurdlePointFuture::getTargetDate, RevenueHurdlePointFuture::getHurdlePoint));
            transientAndGroups.forEach(item -> {
                int targetDate = Integer.parseInt(DateTimeUtils.longToLocalDate(item.getTargetDate(), timezone).format(dateTimeFormatter));
                item.setHurdlePoint(hurdlePointByDate.get(targetDate));
            });
        }

        /**
         * 保存数据
         *
         * @param propertyDTO 资产
         * @param date       指定日期
         * @param revenueDTO revenue
         * @param userId     操作人
         * @author yyt
         * @date 2024/5/8
         **/
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void save(PropertyDTO propertyDTO, Long date, RevenueDTO revenueDTO, int userId) {
            // 保存 otb revenue
            OtbRevenueDTO otb = revenueDTO.getOtb();
            if (Objects.nonNull(otb)) {
                String timezone = propertyDTO.getTimezone();
                LocalDate targetDate = DateTimeUtils.longToLocalDate(date, timezone);
                revenueOtbRevenueFutureService.save(propertyDTO, targetDate.getYear(), targetDate.getMonthValue(), otb, userId);

                // 更新当前数据 db 中的 Banquet Revenue、F&B OTB Revenue
                BanquetAndFBOTBRevenueBO banquetAndFBOTBRevenueBO = new BanquetAndFBOTBRevenueBO();
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
                banquetAndFBOTBRevenueBO.setDate(DateTimeUtils.longToLocalDate(date, timezone).format(dateTimeFormatter));
                banquetAndFBOTBRevenueBO.setBanquetRevenue(otb.getBanquetRevenue());
                banquetAndFBOTBRevenueBO.setFbOtbRevenue(otb.getFbOtbRevenue());
                revenueOtbRevenueService.saveBanquetAndFBOTBRevenue(propertyDTO, Lists.newArrayList(banquetAndFBOTBRevenueBO), userId);
            }

            // 保存 budget
            BudgetDTO budget = revenueDTO.getBudget();
            if (Objects.nonNull(budget)) {
                revenueBudgetOtbService.save(propertyDTO, date, budget, userId);
            }

            // 保存 segment
            List<SegmentDTO> segments = revenueDTO.getSegments();
            if (CollectionUtils.isNotEmpty(segments)) {
                save(propertyDTO, date, segments, userId);
            }

            // 保存 transient and group
            List<TransientAndGroupDTO> transientAndGroups = revenueDTO.getTransientAndGroup();
            if (CollectionUtils.isNotEmpty(transientAndGroups)) {
                revenueTransientGroupFutureService.save(propertyDTO, date, transientAndGroups, userId);
            }

            // 保存 transient and group last year
            List<TransientAndGroupLyDTO> transientAndGroupLies = revenueDTO.getTransientAndGroupLy();
            if (CollectionUtils.isNotEmpty(transientAndGroupLies)) {
                revenueTransientGroupLyFutureService.save(propertyDTO, date, transientAndGroupLies, userId);
            }

            // 保存 last year otb revenue
            LastYearOtbDTO lastYearOtbDTO = revenueDTO.getLastYearOtb();
            if (Objects.nonNull(lastYearOtbDTO)) {
                ZonedDateTime zonedDateTime = DateTimeUtils.toZonedDateTime(date, propertyDTO.getTimezone());
                revenueOtbRevenueLastYearService.save(propertyDTO, zonedDateTime.getYear() - 1, zonedDateTime.getMonthValue(), lastYearOtbDTO, userId);
            }

            // 保存 customs
            CustomersDTO customers = revenueDTO.getCustomers();
            if (Objects.nonNull(customers)) {
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);
                String dateStr = DateTimeUtils.toZonedDateTime(date, propertyDTO.getTimezone()).format(dateTimeFormatter);
                revenueCustomersService.saveOrUpdate(propertyDTO.getId(), Integer.parseInt(dateStr), customers, userId);
            }
        }

        /**
         * 保存 segment
         *
         * @param propertyDTO 资产
         * @param targetDate 目标日期
         * @param segments   segment
         * @param userId     操作人
         * @author yyt
         * @date 2024/5/11
         */
        private void save(PropertyDTO propertyDTO, Long targetDate, List<SegmentDTO> segments, int userId) {
            Integer propertyId = propertyDTO.getId();
            String timezone = propertyDTO.getTimezone();
            int size = segments.size();
            List<RevenueBudgetSegment> budgetSegments = Lists.newArrayListWithExpectedSize(size);

            ZonedDateTime zonedDateTime = DateTimeUtils.toZonedDateTime(targetDate, timezone);
            int year = zonedDateTime.getYear();
            int monthValue = zonedDateTime.getMonthValue();

            InternationalizationNumberDTO number = propertyDTO.getInternationalization().getNumber();
            int calculatePrecision = BaseConstant.DATA_PRECISION;
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(number.getCalculationRule());

            // 获取指定日期 Segment Revenue 数据
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);
            Integer targetDateFormatter = Integer.parseInt(DateTimeUtils.longToLocalDate(targetDate, timezone).format(dateTimeFormatter));
            List<Integer> segmentIds = segments.stream().map(SegmentDTO::getSegmentId).collect(Collectors.toList());
            Map<Integer, Map<Integer, RevenueSegmentRevenueFuture>> segmentRevenueFutureBySegmentId =
                    revenueSegmentRevenueFutureService.list(new QueryWrapper<RevenueSegmentRevenueFuture>().lambda()
                                    .eq(RevenueSegmentRevenueFuture::getPropertyId, propertyId)
                                    .eq(RevenueSegmentRevenueFuture::getTheDate, targetDateFormatter)
                                    .in(RevenueSegmentRevenueFuture::getSegmentId, segmentIds))
                            .stream().collect(Collectors.groupingBy(RevenueSegmentRevenueFuture::getSegmentId,
                                    Collectors.toMap(RevenueSegmentRevenueFuture::getTheDate, v -> v)));
            long now = DateTimeUtils.nowTimestamp();
            List<RevenueSegmentRevenueFuture> insertSegmentRevenues = Lists.newArrayListWithExpectedSize(size);
            List<RevenueSegmentRevenueFuture> updateSegmentRevenues = Lists.newArrayListWithExpectedSize(size);
            for (SegmentDTO item : segments) {
                Integer segmentId = item.getSegmentId();
                BigDecimal revenue = item.getOtbRevenue();
                RevenueSegmentRevenueFuture existSegmentRevenue = segmentRevenueFutureBySegmentId.getOrDefault(segmentId,
                        Collections.emptyMap()).get(targetDateFormatter);
                if (Objects.isNull(existSegmentRevenue)) {
                    insertSegmentRevenues.add(new RevenueSegmentRevenueFuture().setPropertyId(propertyId)
                            .setTheDate(targetDateFormatter).setSegmentId(segmentId).setRevenue(revenue)
                            .setUpdatedBy(userId).setUpdatedTime(now));
                } else {
                    existSegmentRevenue.setRevenue(revenue);
                    if (Objects.isNull(revenue)) {
                        existSegmentRevenue.setAdr(null);
                    } else {
                        Integer totalRooms = existSegmentRevenue.getTotalRooms();
                        if (Objects.nonNull(totalRooms) && totalRooms != BaseConstant.ZERO) {
                            // 重新计算 adr = revenue / rooms
                            existSegmentRevenue.setAdr(revenue.divide(BigDecimal.valueOf(totalRooms), calculatePrecision, roundingMode));
                        } else {
                            existSegmentRevenue.setAdr(null);
                        }
                    }
                    updateSegmentRevenues.add(existSegmentRevenue);
                }

                // 更新 segment budget
                budgetSegments.add(new RevenueBudgetSegment().setPropertyId(propertyId).setTheYear(year)
                        .setTheMonth(monthValue).setSegmentId(segmentId).setBudgetRevenue(item.getBudget())
                        .setCreatedBy(userId).setCreatedTime(now).setUpdatedBy(userId).setUpdatedTime(now));
            }
            if (CollectionUtils.isNotEmpty(insertSegmentRevenues)) {
                revenueSegmentRevenueFutureService.saveBatch(insertSegmentRevenues);
            }
            if (CollectionUtils.isNotEmpty(updateSegmentRevenues)) {
                revenueSegmentRevenueFutureService.updateBatch(updateSegmentRevenues);
            }

            // 更新 segment budget
            revenueBudgetSegmentService.saveOrUpdateBatchRewrite(budgetSegments);
        }

        /**
         * 修改房价和 hurdle point 内容
         *
         * @param propertyDTO                  资产
         * @param date                         指定日期（YYYY-MM-DD)
         * @param updateHurdlerPointAndRateDTO 修改内容
         * @param userId                       操作人
         * @param properties                   当前操作人所属资产
         * @author yyt
         * @date 2024/5/23
         **/
        @Override
        public void updateNext14DaysData(PropertyDTO propertyDTO, String date, UpdateHurdlerPointAndRateDTO updateHurdlerPointAndRateDTO,
                                         int userId, List<Integer> properties) {
            RLock lock = redissonClient.getLock(UPDATE_RATE_REDIS_KEY);
            if (lock.tryLock()) {
                try {
                    //执行业务逻辑
                    next14DaysService.updateFutureDateNext14DaysData(propertyDTO, date, updateHurdlerPointAndRateDTO, userId, properties);
                } finally {
                    lock.unlock();
                }
            } else {
                //休眠300ms
                try {
                    Thread.sleep(300);
                } catch (InterruptedException exception) {
                    log.error(exception.getMessage(), exception);
                }
                //递归执行
                updateNext14DaysData(propertyDTO, date, updateHurdlerPointAndRateDTO, userId, properties);
            }
        }

    }