# RevenueImportServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.SystemPlatformEnum;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.UpdateLogUtil;
    import cloud.wconnect.rateshop.cons.revenue.RevenueImportCons;
    import cloud.wconnect.rateshop.entity.revenue.RevenueMarketCodeRooms;
    import cloud.wconnect.rateshop.entity.revenue.RevenueSegment;
    import cloud.wconnect.rateshop.pojo.bo.revenue.*;
    import cloud.wconnect.rateshop.pojo.dto.revenue.CustomersDTO;
    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 org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.time.LocalDate;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.List;
    import java.util.Objects;

    /**
     * Service: Revenue Import
     *
     * @author yyt
     * 2023/10/30
     */
    @Service
    @DS("rate_shop")
    public class RevenueImportServiceImpl implements IRevenueImportService {

        @Autowired
        private IRevenueOtbRevenueService revenueOtbRevenueService;
        @Autowired
        private IRevenueTransientGroupService revenueTransientGroupService;
        @Autowired
        private IRevenueTransientGroupLyService revenueTransientGroupLyService;
        @Autowired
        private IRevenueSegmentService revenueSegmentService;
        @Autowired
        private IRevenueSegmentRevenueService revenueSegmentRevenueService;
        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private IRevenueMarketCodeRoomsService revenueMarketCodeRoomsService;
        @Autowired
        private UpdateLogUtil updateLogUtil;
        @Autowired
        private IRevenueCustomersService revenueCustomersService;
        @Autowired
        private IRevenueOtbRevenueFutureService revenueOtbRevenueFutureService;
        @Autowired
        private IRevenueTransientGroupFutureService revenueTransientGroupFutureService;
        @Autowired
        private IRevenueTransientGroupLyFutureService revenueTransientGroupLyFutureService;
        @Autowired
        private IRevenueSegmentRevenueFutureService revenueSegmentRevenueFutureService;
        @Autowired
        private IRevenueHistoryAndForecastService historyAndForecastService;

        /**
         * 保存 otb and TransientGroup
         *
         * @param property               资产
         * @param targetDate             指定日期
         * @param otbAndTransientGroupBO otb and TransientGroup
         * @param userId                 操作人
         * @author yyt
         * @date 2023/11/1
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveOtbAndTransientGroupData(PropertyDTO property, String targetDate,
                                                 OtbAndTransientGroupBO otbAndTransientGroupBO, int userId) {
            OtbRevenueBO otbRevenue = otbAndTransientGroupBO.getOtbRevenue();
            if (Objects.nonNull(otbRevenue)) {
                otbRevenue.setDate(targetDate);
                revenueOtbRevenueService.saveOtbRevenueBO(property, Lists.newArrayList(otbRevenue), userId);
            }

            List<TransientGroupBO> transientGroups = otbAndTransientGroupBO.getTransientGroups();
            if (CollectionUtils.isNotEmpty(transientGroups)) {
                revenueTransientGroupService.saveTransientGroupBO(property,
                        Lists.newArrayList(new TransientGroupsBO(targetDate, transientGroups)), userId);
            }
        }

        /**
         * 保存 otb and transientGroup future
         *
         * @param property           资产
         * @param otbRevenueFutureBO otb revenue future
         * @param transientGroups    transient Group future
         * @param userId             操作人
         * @author yyt
         * @date 2023/11/1
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveOtbAndTransientGroupFutureData(PropertyDTO property, OtbRevenueFutureBO otbRevenueFutureBO, List<TransientGroupBO> transientGroups, int userId) {
            if (Objects.nonNull(otbRevenueFutureBO)) {
                revenueOtbRevenueFutureService.saveOtbRevenueFutureBO(property, Lists.newArrayList(otbRevenueFutureBO), userId);
            }
            if (CollectionUtils.isNotEmpty(transientGroups)) {
                revenueTransientGroupFutureService.saveTransientGroupFutureBO(property, transientGroups, userId);
            }
        }

        /**
         * 保存 otb and transientGroup next month
         *
         * @param property               资产
         * @param targetDate             指定日期
         * @param otbAndTransientGroupBO otb and TransientGroup
         * @param userId                 操作人
         * @author yyt
         * @date 2023/11/1
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveOtbAndTransientGroupNextMonthData(PropertyDTO property, String targetDate,
                                                          OtbAndTransientGroupBO otbAndTransientGroupBO, int userId) {
            OtbRevenueBO otbRevenue = otbAndTransientGroupBO.getOtbRevenue();
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
            if (Objects.nonNull(otbRevenue) && DateTimeUtils.isLastDayOfThisMonth(LocalDate.parse(targetDate, dateTimeFormatter))) {
                otbRevenue.setDate(targetDate);
                revenueOtbRevenueService.saveOtbRevenueBONextMonth(property, otbRevenue, userId);
            }
            List<TransientGroupBO> transientGroups = otbAndTransientGroupBO.getTransientGroups();
            if (CollectionUtils.isNotEmpty(transientGroups)) {
                revenueTransientGroupService.saveTransientGroupBO(property,
                        Lists.newArrayList(new TransientGroupsBO(targetDate, transientGroups)), userId);
            }
        }

        /**
         * 保存 Transient Group Adr
         *
         * @param property           资产
         * @param targetDate         指定日期（yyyy-MM-dd）
         * @param transientGroupAdrs transientGroupAdr
         * @param userId             操作人
         * @author yyt
         * @date 2023/11/2
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveTransientGroupAdrData(PropertyDTO property, String targetDate,
                                              List<TransientGroupAdrBO> transientGroupAdrs, int userId) {
            if (CollectionUtils.isNotEmpty(transientGroupAdrs)) {
                revenueTransientGroupService.saveTransientGroupAdrBO(property,
                        Lists.newArrayList(new TransientGroupAdrsBO(targetDate, transientGroupAdrs)), userId);
            }
        }

        /**
         * 保存 Transient Group Adr Future
         *
         * @param property           资产
         * @param transientGroupAdrs TransientGroupAdr
         * @param userId             操作人
         * @author yyt
         * @date 2023/11/2
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveTransientGroupAdrFutureData(PropertyDTO property, List<TransientGroupAdrBO> transientGroupAdrs, int userId) {
            if (CollectionUtils.isNotEmpty(transientGroupAdrs)) {
                revenueTransientGroupFutureService.saveTransientGroupAdrBO(property, transientGroupAdrs, userId);
            }
        }

        /**
         * 保存 Forecast 报告 Segment
         *
         * @param property 资产
         * @param segment  Segment
         * @param userId   操作人
         * @author yyt
         * @date 2023/11/3
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveForecastSegmentData(PropertyDTO property, SegmentBO segment, int userId) {
            List<RevenueSegment> revenueSegments = revenueSegmentService.listActiveSegment(property.getId());
            if (revenueSegments.isEmpty()) {
                return;
            }

            revenueSegmentRevenueService.saveOrUpdateForecastSegment(property, revenueSegments, segment, userId);
        }

        /**
         * 保存 Forecast 报告 Next 90 Days Segment
         *
         * @param property                   资产
         * @param calculateCurrentSegments   计算 segment 当前数据（revenue_forecast、total_rooms_next_month、adr）
         * @param calculateNextMonthSegments 计算 segment 下个月的数据（revenue_next_month、total_rooms_next_month、adr_next_month）
         * @param userId                     操作人
         * @author yyt
         * @date 2024/5/22
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveForecastNext90DaysSegmentData(PropertyDTO property, List<SegmentBO> calculateCurrentSegments,
                                                      List<SegmentBO> calculateNextMonthSegments, int userId) {
            List<RevenueSegment> revenueSegments = revenueSegmentService.listActiveSegment(property.getId());
            if (revenueSegments.isEmpty()) {
                return;
            }

            if (CollectionUtils.isNotEmpty(calculateCurrentSegments)) {
                revenueSegmentRevenueFutureService.saveOrUpdateForecastSegment(property, revenueSegments, calculateCurrentSegments, userId);
            }
            if (CollectionUtils.isNotEmpty(calculateNextMonthSegments)) {
                revenueSegmentRevenueFutureService.saveOrUpdateForecastSegmentNextMonth(property, revenueSegments, calculateNextMonthSegments, userId);
            }
        }

        /**
         * 保存 Statistics 报告 Segment
         *
         * @param property              资产
         * @param segmentAndCustomersBO segmentAndCustomersBO
         * @param userId                操作人
         * @author yyt
         * @date 2024/2/28
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveStatisticsSegmentData(PropertyDTO property, SegmentAndCustomersBO segmentAndCustomersBO, int userId) {
            SegmentBO segmentBO = segmentAndCustomersBO.getSegmentBO();
            List<RevenueSegment> revenueSegments = revenueSegmentService.listActiveSegment(property.getId());
            if (CollectionUtils.isNotEmpty(revenueSegments)) {
                revenueSegmentRevenueService.saveOrUpdateStatisticsSegment(property, revenueSegments, segmentBO, userId);
            }

            String date = segmentAndCustomersBO.getDate();
            CustomersBO customersBO = segmentAndCustomersBO.getCustomersBO();
            if (Objects.nonNull(customersBO)) {
                revenueCustomersService.saveOrUpdate(property.getId(), Integer.parseInt(date.replaceAll("-", "")),
                        beanMapper.map(customersBO, CustomersDTO.class), userId);
            }
        }

        /**
         * 保存 Segment Next Month
         *
         * @param property 资产
         * @param segment  Segment
         * @param userId   操作人
         * @author yyt
         * @date 2023/11/4
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveSegmentNextMonthData(PropertyDTO property, SegmentBO segment, int userId) {
            revenueSegmentRevenueService.saveOrUpdateSegmentForNextMonth(property, segment, userId);
        }

        /**
         * 获取导入 history and forecast 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2023/11/1
         */
        @Override
        public List<Long> listImportHistoryAndForecastDateRange(String timeZone) {
            return calculateHistoryAndForecastDateRange(DateTimeUtils.dateNow(timeZone));
        }

        /**
         * 获取导入 history and forecast 报告时，选择指定日期需要的日期区间
         *
         * @param theDate  指定日期
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/25
         */
        @Override
        public List<Long> listImportHistoryAndForecastDateRange(Long theDate, String timeZone) {
            return calculateHistoryAndForecastDateRange(DateTimeUtils.toZonedDateTime(theDate, timeZone));
        }

        /**
         * 计算 history and forecast 报告，选择指定日期需要的日期区间
         *
         * @param targetDate 指定日期
         * @return 日期区间
         * @author yyt
         * @date 2024/5/29
         **/
        private List<Long> calculateHistoryAndForecastDateRange(ZonedDateTime targetDate) {
            ZonedDateTime startOfMonth = targetDate.withDayOfMonth(1);
            ZonedDateTime endOfMonth = startOfMonth.plusMonths(1).minusDays(1);
            return Lists.newArrayList(startOfMonth.toEpochSecond(), endOfMonth.toEpochSecond());
        }

        /**
         * 获取导入 history and forecast this month future 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/20
         */
        @Override
        public List<Long> listImportHistoryAndForecastThisMonthFutureDateRange(String timeZone) {
            ZonedDateTime startOfMonth = DateTimeUtils.dateNow(timeZone).withDayOfMonth(1);
            return Lists.newArrayList(startOfMonth.toEpochSecond(), startOfMonth.plusMonths(1).minusDays(1).toEpochSecond());
        }

        /**
         * 获取导入 history and forecast next month future 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/21
         */
        @Override
        public List<Long> listImportHistoryAndForecastNextMonthFutureDateRange(String timeZone) {
            ZonedDateTime startOfNextMonth = DateTimeUtils.dateNow(timeZone).withDayOfMonth(1).plusMonths(1);
            return Lists.newArrayList(startOfNextMonth.toEpochSecond(), startOfNextMonth.plusMonths(1).minusDays(1).toEpochSecond());
        }

        /**
         * 获取导入 history and forecast this month after next future 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/21
         */
        @Override
        public List<Long> listImportHistoryAndForecastThisMonthAfterNextFutureDateRange(String timeZone) {
            ZonedDateTime startOfThisMonthAfterNext = DateTimeUtils.dateNow(timeZone).withDayOfMonth(1).plusMonths(2);
            return Lists.newArrayList(startOfThisMonthAfterNext.toEpochSecond(), startOfThisMonthAfterNext.plusMonths(1).minusDays(1).toEpochSecond());
        }

        /**
         * 获取导入 history and forecast next month 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2023/11/1
         */
        @Override
        public List<Long> listImportHistoryAndForecastNextMonthDateRange(String timeZone) {
            return calculateHistoryAndForecastNextMonthDateRange(DateTimeUtils.dateNow(timeZone));
        }

        /**
         * 获取导入 history and forecast next month 报告时，选择指定日期需要的日期区间
         *
         * @param theDate  指定日期
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/25
         */
        @Override
        public List<Long> listImportHistoryAndForecastNextMonthDateRange(Long theDate, String timeZone) {
            return calculateHistoryAndForecastNextMonthDateRange(DateTimeUtils.toZonedDateTime(theDate, timeZone));
        }

        /**
         * 计算 history and forecast next month 报告，选择指定日期需要的日期区间
         *
         * @param targetDate 指定日期
         * @return 日期区间
         * @author yyt
         * @date 2024/5/29
         **/
        private List<Long> calculateHistoryAndForecastNextMonthDateRange(ZonedDateTime targetDate) {
            ZonedDateTime startOfNextMonth = targetDate.withDayOfMonth(1).plusMonths(1);
            return Lists.newArrayList(startOfNextMonth.toEpochSecond(), startOfNextMonth.plusMonths(1).minusDays(1).toEpochSecond());
        }

        /**
         * 获取导入 history and forecast business block 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2023/11/1
         */
        @Override
        public List<Long> listImportHistoryAndForecastBusinessBlockDateRange(String timeZone) {
            return calculateHistoryAndForecastBusinessBlockDateRange(DateTimeUtils.dateNow(timeZone));
        }

        /**
         * 获取导入 history and forecast business block 报告时，选择指定日期需要的日期区间
         *
         * @param theDate  指定日期
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/25
         */
        @Override
        public List<Long> listImportHistoryAndForecastBusinessBlockDateRange(Long theDate, String timeZone) {
            return calculateHistoryAndForecastBusinessBlockDateRange(DateTimeUtils.toZonedDateTime(theDate, timeZone));
        }

        /**
         * 计算 history and forecast business block 报告，选择指定日期需要的日期区间
         *
         * @param targetDate 指定日期
         * @return 日期区间
         * @author yyt
         * @date 2024/5/29
         **/
        private List<Long> calculateHistoryAndForecastBusinessBlockDateRange(ZonedDateTime targetDate) {
            return Lists.newArrayList(targetDate.toEpochSecond(), targetDate.plusDays(13).toEpochSecond());
        }

        /**
         * 获取导入 history and forecast business block next 90 days 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/21
         */
        @Override
        public List<Long> listImportHistoryAndForecastBusinessBlockNext90DaysDateRange(String timeZone) {
            ZonedDateTime dateNow = DateTimeUtils.dateNow(timeZone);
            return Lists.newArrayList(dateNow.toEpochSecond(), dateNow.plusDays(104).toEpochSecond());
        }

        /**
         * 获取导入 reservation statistics 报告日期区间
         *
         * @param propertyDTO 资产
         * @return 日期区间
         * @author yyt
         * @date 2023/11/3
         */
        @Override
        public List<Long> listImportReservationStatisticsDateRange(PropertyDTO propertyDTO) {
            return calculateReservationStatisticsDateRange(propertyDTO, DateTimeUtils.dateNow(propertyDTO.getTimezone()));
        }

        /**
         * 获取导入 reservation statistics 报告时，选择指定日期需要的日期区间
         *
         * @param propertyDTO 资产
         * @param theDate     指定日期
         * @param timeZone    时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/25
         */
        @Override
        public List<Long> listImportReservationStatisticsDateRange(PropertyDTO propertyDTO, Long theDate, String timeZone) {
            ZonedDateTime currentDate = DateTimeUtils.toZonedDateTime(theDate, timeZone);
            long startDate = currentDate.withDayOfMonth(1).toEpochSecond();
            long endDate = currentDate.minusDays(1).toEpochSecond();
            if (propertyDTO.getPmsType().equals(SystemPlatformEnum.OPERA.getDesc())) {
                return currentDate.getDayOfMonth() == 1 ? Lists.newArrayList(theDate, theDate) : Lists.newArrayList(startDate, endDate);
            } else {
                return Lists.newArrayList(endDate, endDate);
            }
        }

        /**
         * 计算 reservation statistics 报告，选择指定日期需要的日期区间
         *
         * @param targetDate 指定日期
         * @return 日期区间
         * @author yyt
         * @date 2024/5/29
         **/
        private List<Long> calculateReservationStatisticsDateRange(PropertyDTO propertyDTO, ZonedDateTime targetDate) {
            long startDate = targetDate.withDayOfMonth(1).toEpochSecond();
            long endDate = targetDate.minusDays(2).toEpochSecond();
            if (propertyDTO.getPmsType().equals(SystemPlatformEnum.OPERA.getDesc())) {
                return startDate > endDate ? Lists.newArrayList(endDate, startDate) : Lists.newArrayList(startDate, endDate);
            } else {
                return Lists.newArrayList(endDate, endDate);
            }
        }

        /**
         * 获取导入 reservation forecast next month 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2023/11/4
         */
        @Override
        public List<Long> listImportReservationForecastNextMonthDateRange(String timeZone) {
            return calculateReservationForecastNextMonthDateRange(DateTimeUtils.dateNow(timeZone));
        }

        /**
         * 获取导入 reservation forecast next month 报告时，选择指定日期需要的日期区间
         *
         * @param theDate  指定日期
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/25
         */
        @Override
        public List<Long> listImportReservationForecastNextMonthDateRange(Long theDate, String timeZone) {
            return calculateReservationForecastNextMonthDateRange(DateTimeUtils.toZonedDateTime(theDate, timeZone));
        }

        /**
         * 计算 reservation forecast next month 报告，选择指定日期需要的日期区间
         *
         * @param targetDate 指定日期
         * @return 日期区间
         * @author yyt
         * @date 2024/5/29
         **/
        private List<Long> calculateReservationForecastNextMonthDateRange(ZonedDateTime targetDate) {
            ZonedDateTime startOfNextMonth = targetDate.withDayOfMonth(1).plusMonths(1);
            return Lists.newArrayList(startOfNextMonth.toEpochSecond(), startOfNextMonth.plusMonths(1).minusDays(1).toEpochSecond());
        }

        /**
         * 获取导入 reservation forecast next 14 days 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/3/25
         */
        @Override
        public List<Long> listImportReservationForecastNext14DaysDateRange(String timeZone) {
            return calculateReservationForecastNext14DaysDateRange(DateTimeUtils.dateNow(timeZone));
        }

        /**
         * 获取导入 reservation forecast next 14 days 报告时，选择指定日期需要的日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/25
         */
        @Override
        public List<Long> listImportReservationForecastNext14DaysDateRange(Long theDate, String timeZone) {
            return calculateReservationForecastNext14DaysDateRange(DateTimeUtils.toZonedDateTime(theDate, timeZone));
        }

        /**
         * 计算 reservation forecast next 14 days 报告，选择指定日期需要的日期区间
         *
         * @param targetDate 指定日期
         * @return 日期区间
         * @author yyt
         * @date 2024/5/29
         **/
        private List<Long> calculateReservationForecastNext14DaysDateRange(ZonedDateTime targetDate) {
            return Lists.newArrayList(targetDate.toEpochSecond(), targetDate.plusDays(13).toEpochSecond());
        }

        /**
         * 获取导入 reservation forecast 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2023/11/3
         */
        @Override
        public List<Long> listImportReservationForecastDateRange(String timeZone) {
            return listImportReservationForecastDateRange(DateTimeUtils.dateNow(timeZone));
        }

        /**
         * 获取导入 reservation forecast 报告时，选择指定日期需要的日期区间
         *
         * @param theDate  指定日期
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/25
         */
        @Override
        public List<Long> listImportReservationForecastDateRange(Long theDate, String timeZone) {
            ZonedDateTime currentDate = DateTimeUtils.toZonedDateTime(theDate, timeZone);
            return Lists.newArrayList(currentDate.toEpochSecond(), currentDate.withDayOfMonth(1).plusMonths(1).minusDays(1).toEpochSecond());
        }

        /**
         * 导入 reservation forecast 报告时，获取指定日期所需要的日期区间
         *
         * @param targetDate 指定日期
         * @return 日期区间
         * @author yyt
         * @date 2024/5/21
         */
        @Override
        public List<Long> listImportReservationForecastDateRange(ZonedDateTime targetDate) {
            List<Long> dates = Lists.newArrayListWithExpectedSize(2);
            // 如果是月第一天，则插入月第一天
            if (targetDate.getDayOfMonth() == BaseConstant.ONE) {
                dates.add(targetDate.toEpochSecond());
            } else {
                // 否则，插入昨天
                dates.add(targetDate.minusDays(1).toEpochSecond());
            }
            // 插入月末
            dates.add(targetDate.withDayOfMonth(1).plusMonths(1).minusDays(1).toEpochSecond());
            return dates;
        }

        /**
         * 获取插入 reservation forecast next 90 days 报告日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2024/5/21
         */
        @Override
        public List<Long> listImportReservationForecastNext90DaysDateRange(String timeZone) {
            // 月初第一天，2月后的最后一天
            ZonedDateTime startOfMonth = DateTimeUtils.dateNow(timeZone).withDayOfMonth(1);
            return Lists.newArrayList(startOfMonth.toEpochSecond(), startOfMonth.plusMonths(3).minusDays(1).toEpochSecond());
        }

        /**
         * 获取导入 detailed availability next 14 days 报告时，选择指定日期需要的日期区间
         *
         * @param theDate  指定日期
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2025/2/11
         */
        @Override
        public List<Long> listImportDetailedAvailabilityNext14DaysDateRange(Long theDate, String timeZone) {
            return calculateDetailedAvailabilityNext14DaysDateRange(DateTimeUtils.toZonedDateTime(theDate, timeZone));
        }

        /**
         * 获取导入 detailed availability next 14 days 报告需要的日期区间
         *
         * @param timeZone 时区
         * @return 日期区间
         * @author yyt
         * @date 2025/2/11
         */
        @Override
        public List<Long> listImportDetailedAvailabilityNext14DaysDateRange(String timeZone) {
            return calculateDetailedAvailabilityNext14DaysDateRange(DateTimeUtils.dateNow(timeZone));
        }

        /**
         * 计算 detailed availability next 14 days 报告，选择指定日期需要的日期区间
         *
         * @param targetDate 指定日期
         * @return 日期区间
         * @author yyt
         * @date 2025/2/11
         **/
        private List<Long> calculateDetailedAvailabilityNext14DaysDateRange(ZonedDateTime targetDate) {
            return Lists.newArrayList(targetDate.toEpochSecond(), targetDate.plusDays(13).toEpochSecond());
        }

        /**
         * 检查是否跨月
         *
         * @param timezone 时区
         * @return true: 跨月，false: 为跨月
         * @author yyt
         * @date 2023/11/1
         */
        @Override
        public boolean checkCrossMonth(String timezone) {
            ZonedDateTime currentDate = DateTimeUtils.dateNow(timezone);
            ZonedDateTime theDateNext13DaysZoned = currentDate.plusDays(16);
            // 判断当前日期到未来第17天之间，是否出现跨月的情况
            return currentDate.getMonth().getValue() != theDateNext13DaysZoned.getMonth().getValue();
        }

        /**
         * 保存 market code rooms
         *
         * @param property    资产
         * @param targetDate  指定日期
         * @param marketCodes market code rooms
         * @param uid         操作人
         * @author yyt
         * @date 2024/3/25
         **/
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveMarketCodeRooms(PropertyDTO property, Long targetDate, List<MarketCodeBO> marketCodes, int uid) {
            Integer propertyId = property.getId();
            revenueMarketCodeRoomsService.remove(new QueryWrapper<RevenueMarketCodeRooms>().lambda()
                    .eq(RevenueMarketCodeRooms::getPropertyId, propertyId)
                    .eq(RevenueMarketCodeRooms::getTheDate, targetDate));

            if (CollectionUtils.isEmpty(marketCodes)) {
                return;
            }

            revenueMarketCodeRoomsService.saveBatch(marketCodes.stream().map(item -> {
                RevenueMarketCodeRooms marketCodeRooms = beanMapper.map(item, RevenueMarketCodeRooms.class);
                marketCodeRooms.setUpdateLog(updateLogUtil.get(uid));
                marketCodeRooms.setPropertyId(propertyId).setTheDate(targetDate);
                return marketCodeRooms;
            }).toList());
        }

        /**
         * 检查处理日期是否合法
         *
         * @param propertyDTO 资产
         * @param reportType  报告类型
         * @param importDate  导入日期
         * @return true:合法，false：不合法
         */
        @Override
        public boolean checkHandleDate(PropertyDTO propertyDTO, Integer reportType, Long importDate) {
            String timezone = propertyDTO.getTimezone();
            switch (reportType) {
                case RevenueImportCons.RESERVATION_STATISTIC: {
                    // Reservation Statistics 报告，月初前2天不处理报告（因为每天需要的数据是：月初 ~ 前2天）
                    return importDate > DateTimeUtils.toZonedDateTime(importDate, timezone).withDayOfMonth(2).toEpochSecond();
                }
            }

            return true;
        }

        /**
         * 导入操作检查处理日期是否合法
         *
         * @param propertyDTO 资产
         * @param reportType  报告类型
         * @param importDate  导入日期
         * @return true:合法，false：不合法
         */
        @Override
        public boolean checkHandleDateForImport(PropertyDTO propertyDTO, Integer reportType, Long importDate) {
            String timezone = propertyDTO.getTimezone();
            switch (reportType) {
                case RevenueImportCons.RESERVATION_STATISTIC: {
                    // Reservation Statistics 报告，月初前1天不处理报告（因为每天需要的数据是：月初 ~ 前1天）
                    return importDate > DateTimeUtils.toZonedDateTime(importDate, timezone).withDayOfMonth(1).toEpochSecond();
                }
            }

            return true;
        }

        /**
         * 保存History and Forecast from thirty days ago to yesterday数据
         *
         * @param propertyDTO           资产
         * @param historyAndForecastBOS 报告数据
         * @param userId                操作人
         * @return void
         * @author lichen
         * @date 2025/8/5
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void saveHistoryAndForecastData(PropertyDTO propertyDTO, List<HistoryAndForecastBO> historyAndForecastBOS, int userId) {
            if (CollectionUtils.isNotEmpty(historyAndForecastBOS)) {
                historyAndForecastService.saveHistoryAndForecastData(propertyDTO, historyAndForecastBOS, userId);
            }
        }

    }