# RevenueServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.*;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.internationalization.InternationalizationDTO;
    import cloud.wconnect.commonlib.pojo.dto.internationalization.InternationalizationDateTimeDTO;
    import cloud.wconnect.commonlib.pojo.dto.internationalization.InternationalizationNumberDTO;
    import cloud.wconnect.commonlib.util.*;
    import cloud.wconnect.rateshop.cons.rateshop.DatasourceEnum;
    import cloud.wconnect.rateshop.cons.rateshop.PlatformEnum;
    import cloud.wconnect.rateshop.cons.rateshop.RateTypeEnum;
    import cloud.wconnect.rateshop.cons.revenue.RevenueCons;
    import cloud.wconnect.rateshop.cons.revenue.RevenueImportCons;
    import cloud.wconnect.rateshop.entity.rateshop.Hotel;
    import cloud.wconnect.rateshop.entity.rateshop.Setting;
    import cloud.wconnect.rateshop.entity.revenue.RevenueRoomTypeRooms;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.LatestHotelPriceDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.RateConfigDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.hotel.PropertyHotelDTO;
    import cloud.wconnect.rateshop.pojo.dto.revenue.*;
    import cloud.wconnect.rateshop.pojo.query.rateshop.LatestHotelPriceQuery;
    import cloud.wconnect.rateshop.pojo.query.revenue.ExportRevenueQuery;
    import cloud.wconnect.rateshop.pojo.query.revenue.Next14DaysRateQuery;
    import cloud.wconnect.rateshop.pojo.query.revenue.RevenueDetailQuery;
    import cloud.wconnect.rateshop.pojo.vo.revenue.*;
    import cloud.wconnect.rateshop.service.rateshop.IHotelService;
    import cloud.wconnect.rateshop.service.rateshop.ILatestBARRateService;
    import cloud.wconnect.rateshop.service.rateshop.IPropertyHotelService;
    import cloud.wconnect.rateshop.service.rateshop.ISettingService;
    import cloud.wconnect.rateshop.service.revenue.INext14DaysService;
    import cloud.wconnect.rateshop.service.revenue.IRevenueMepsGroupConfigService;
    import cloud.wconnect.rateshop.service.revenue.IRevenueRoomTypeRoomsService;
    import cloud.wconnect.rateshop.service.revenue.IRevenueService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Maps;
    import org.apache.commons.collections.CollectionUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.i18n.LocaleContextHolder;
    import org.springframework.stereotype.Service;

    import java.math.BigDecimal;
    import java.time.LocalDate;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.time.format.TextStyle;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * @author yyt
     * 2023/10/27
     */
    @Service
    @DS("rate_shop")
    public class RevenueServiceImpl implements IRevenueService {

        @Autowired
        private IPropertyHotelService propertyHotelService;
        @Autowired
        private INext14DaysService next14DaysService;
        @Autowired
        private ISettingService settingService;
        @Autowired
        private IHotelService hotelService;
        @Autowired
        private ILatestBARRateService latestBARRateService;
        @Autowired
        private IRevenueRoomTypeRoomsService revenueRoomTypeRoomsService;
        @Autowired
        private IRevenueMepsGroupConfigService revenueMepsGroupConfigService;
        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;
        @Autowired
        private BeanMapper beanMapper;

        public static final String RATE = "rate";

        public static final String DIFF = "diff";

        /**
         * 设置文件显示内容
         *
         * @param propertyDTO        资产
         * @param theDate            指定日期
         * @param revenueDTO         revenue 详情
         * @param data               设置到渲染Excel作用域中的数据
         * @param exportRevenueQuery 导出房价时的查询条件，包括：数据源、平台
         * @author yyt
         * @date 2024/3/27
         **/
        @Override
        public void packageDataToFile(PropertyDTO propertyDTO, Long theDate, RevenueDTO revenueDTO, HashMap<String, Object> data, ExportRevenueQuery exportRevenueQuery) {
            String timezone = propertyDTO.getTimezone();
            InternationalizationDTO internationalization = propertyDTO.getInternationalization();
            Locale locale = LocaleContextHolder.getLocale();

            data.put("propertyName", propertyDTO.getName());
            // 设置日期、月
            String currentDate = InternationalizationUtils.formatDateTime(theDate, internationalization.getDateTime().getLongDate(), null,
                    timezone, locale);
            data.put("currentDate", currentDate);

            data.put("currentMonth", DateTimeUtils.toZonedDateTime(theDate, timezone).getMonth().getDisplayName(TextStyle.FULL, locale));

            // 设置 OTB Revenue 相关信息
            data.put("otb", packageOtbRevenue(revenueDTO.getOtb(), internationalization));

            // 设置 OTB Revenue Budget 相关信息
            data.put("budgetOtb", packageBudget(revenueDTO.getBudget(), internationalization));

            // 设置 Last Year OTB Revenue
            data.put("lastYearOtb", packageLastYearOtb(revenueDTO.getLastYearOtb(), internationalization));

            // 设置 Customers
            data.put("customer", packageCustomer(revenueDTO.getCustomers(), internationalization));

            // 设置 Segment 相关信息
            Map<String, Object> result = packageSegment(revenueDTO.getSegments(), internationalization);
            data.put("segments", result.get(RevenueCons.SEGMENT));
            data.put("totalRevenue", result.get(RevenueCons.TOTAL_REVENUE));
            data.put("totalBudget", result.get(RevenueCons.TOTAL_BUDGET));

            // 设置 Transient and Group 相关信息
            setTransientAndGroupInContext(propertyDTO, revenueDTO.getTransientAndGroup(), theDate, internationalization, data);

            data.put("transientAndGroupLy", packageSameTimeLastYear(propertyDTO, theDate, revenueDTO, internationalization, LocaleContextHolder.getLocale()));

            // 设置房价
            data.put("rates", getRate(propertyDTO, theDate, exportRevenueQuery, locale));

            //当前上下文语言
            Locale currentThreadLocale = LocaleContextHolder.getLocale();
            String i18nAllDatasource = i18nMessageUtils.message(RevenueCons.I18N_ALL_DATASOURCE, currentThreadLocale);
            String i18nAllPlatform = i18nMessageUtils.message(RevenueCons.I18N_ALL_PLATFORM, currentThreadLocale);

            //设置去年同期的查询条件-数据源
            Integer datasourceIdBySameTimeLastYear = exportRevenueQuery.getDatasourceIdBySameTimeLastYear();
            if (Objects.isNull(datasourceIdBySameTimeLastYear)) {
                data.put("datasourceBySameTimeLastYear", i18nAllDatasource);
            } else {
                data.put("datasourceBySameTimeLastYear",
                        i18nMessageUtils.message(DatasourceEnum.getI18nCode(datasourceIdBySameTimeLastYear), currentThreadLocale));
            }
            //设置去年同期的查询条件-平台
            Integer platformIdBySameTimeLastYear = exportRevenueQuery.getPlatformIdBySameTimeLastYear();
            if (Objects.isNull(platformIdBySameTimeLastYear)) {
                data.put("platformBySameTimeLastYear", i18nAllPlatform);
            } else {
                data.put("platformBySameTimeLastYear",
                        i18nMessageUtils.message(PlatformEnum.getI18nCode(platformIdBySameTimeLastYear), currentThreadLocale));
            }

            //设置未来14天BAR房价的查询条件-数据源
            Integer datasourceIdByNext14DaysBAR = exportRevenueQuery.getDatasourceIdByNext14DaysBAR();
            if (Objects.isNull(datasourceIdByNext14DaysBAR)) {
                data.put("datasourceByNext14DaysBAR", i18nAllDatasource);
            } else {
                data.put("datasourceByNext14DaysBAR",
                        i18nMessageUtils.message(DatasourceEnum.getI18nCode(datasourceIdByNext14DaysBAR), currentThreadLocale));
            }
            //设置未来14天BAR房价的查询条件-平台
            Integer platformIdByNext14DaysBAR = exportRevenueQuery.getPlatformIdByNext14DaysBAR();
            if (Objects.isNull(platformIdByNext14DaysBAR)) {
                data.put("platformByNext14DaysBAR", i18nAllPlatform);
            } else {
                data.put("platformByNext14DaysBAR",
                        i18nMessageUtils.message(PlatformEnum.getI18nCode(platformIdByNext14DaysBAR), currentThreadLocale));
            }

            //设置未来14天早鸟价的查询条件-数据源
            Integer datasourceIdByNext14DaysEBR = exportRevenueQuery.getDatasourceIdByNext14DaysEBR();
            if (Objects.isNull(datasourceIdByNext14DaysEBR)) {
                data.put("datasourceByNext14DaysEBR", i18nAllDatasource);
            } else {
                data.put("datasourceByNext14DaysEBR",
                        i18nMessageUtils.message(DatasourceEnum.getI18nCode(datasourceIdByNext14DaysEBR), currentThreadLocale));
            }
            //设置未来14天早鸟价的查询条件-平台
            Integer platformIdByNext14DaysEBR = exportRevenueQuery.getPlatformIdByNext14DaysEBR();
            if (Objects.isNull(platformIdByNext14DaysEBR)) {
                data.put("platformByNext14DaysEBR", i18nAllPlatform);
            } else {
                data.put("platformByNext14DaysEBR",
                        i18nMessageUtils.message(PlatformEnum.getI18nCode(platformIdByNext14DaysEBR), currentThreadLocale));
            }

            // 设置未来14天不同房型可用房间数
            data.put("roomTypeRooms", revenueRoomTypeRoomsService.listRoomTypeRooms(propertyDTO, theDate));
            data.put("next14Days", listNext14Days(propertyDTO, theDate));
            setNext14DaysRoomTypeRoomsTitle(propertyDTO, theDate, data);
        }

        /**
         * 设置未来14天不同房型可用房间数导出时的标题
         *
         * @param propertyDTO 资产
         * @param theDate     指定日期
         * @param data        文件显示内容
         * @author yyt
         * @date 2025/2/14
         **/
        private void setNext14DaysRoomTypeRoomsTitle(PropertyDTO propertyDTO, Long theDate, HashMap<String, Object> data) {
            Locale locale = LocaleContextHolder.getLocale();

            // 未来14天不同房型可用房间数，有数据时，返回 date
            List<RevenueRoomTypeRooms> roomTypeRooms = revenueRoomTypeRoomsService.list(propertyDTO.getId(), theDate);
            if (CollectionUtils.isNotEmpty(roomTypeRooms)) {
                data.put("next14DaysRoomTypeTitle", i18nMessageUtils.message(RevenueCons.DATE, locale));
                data.put("next14DaysRoomTypeWeekTitle", i18nMessageUtils.message(CommonFieldI18nConstant.WEEK, locale));
            } else {
                // 无数据时，返回 no data
                data.put("next14DaysRoomTypeTitle", i18nMessageUtils.message(RevenueCons.I18N_NO_DATA, locale));
                data.put("next14DaysRoomTypeWeekTitle", "");
            }
        }

        /**
         * 获取指定日期未来14天
         *
         * @param propertyDTO 资产
         * @param theDate     指定日期
         * @return 未来14天
         * @author yyt
         * @date 2025/2/11
         **/
        private List<DateVO> listNext14Days(PropertyDTO propertyDTO, Long theDate) {
            List<DateVO> dateVOs = Lists.newArrayListWithExpectedSize(14);
            String timezone = propertyDTO.getTimezone();
            LocalDate startDate = DateTimeUtils.longToLocalDate(theDate, timezone);
            LocalDate endDate = startDate.plusDays(13);
            InternationalizationDateTimeDTO dateTime = propertyDTO.getInternationalization().getDateTime();
            String longDate = dateTime.getLongDate();
            Locale locale = LocaleContextHolder.getLocale();
            for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
                DateVO dateVO = new DateVO();
                dateVO.setDate(InternationalizationUtils.formatLocalDate(date, longDate, locale.toString()));
                dateVO.setWeek(date.getDayOfWeek().getDisplayName(TextStyle.SHORT_STANDALONE, locale));
                dateVOs.add(dateVO);
            }
            return dateVOs;
        }

        /**
         * 封装 otb revenue
         *
         * @param otbRevenue           otbRevenue
         * @param internationalization 国际化
         * @return otb revenue
         * @author yyt
         * @date 2025/3/14
         **/
        @Override
        public OtbRevenueVO packageOtbRevenue(OtbRevenueDTO otbRevenue, InternationalizationDTO internationalization) {
            InternationalizationNumberDTO number = internationalization.getNumber();
            int precision = FunctionPrecisionEnum.DAILY_REVENUE.getPrecision();
            String calculationRule = number.getCalculationRule();
            String currency = number.getCurrency();

            OtbRevenueVO otbRevenueVo = new OtbRevenueVO();
            BigDecimal revenue = otbRevenue.getOtbRevenue();
            otbRevenueVo.setOtbRevenueStr(Objects.isNull(revenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(revenue, calculationRule, currency));

            BigDecimal otbOccPercent = otbRevenue.getOtbOccPercent();
            otbRevenueVo.setOtbOccPercentStr(Objects.isNull(otbOccPercent) ? null :
                    InternationalizationUtils.formatViewValue(otbOccPercent, precision, calculationRule, null) + "%");

            BigDecimal otbAdr = otbRevenue.getOtbAdr();
            otbRevenueVo.setOtbAdrStr(Objects.isNull(otbAdr) ? null :
                    InternationalizationUtils.formatAmountViewValue(otbAdr, calculationRule, currency));

            BigDecimal otbRevPar = otbRevenue.getOtbRevPar();
            otbRevenueVo.setOtbRevParStr(Objects.isNull(otbRevPar) ? null :
                    InternationalizationUtils.formatAmountViewValue(otbRevPar, calculationRule, currency));

            BigDecimal banquetRevenue = otbRevenue.getBanquetRevenue();
            otbRevenueVo.setBanquetRevenueStr(Objects.isNull(banquetRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(banquetRevenue, calculationRule, currency));

            BigDecimal otbAchievement = otbRevenue.getOtbAchievement();
            otbRevenueVo.setOtbAchievementStr(Objects.isNull(otbAchievement) ? null :
                    InternationalizationUtils.formatViewValue(otbAchievement, precision, calculationRule, null) + "%");

            BigDecimal otbPickup = otbRevenue.getOtbPickup();
            otbRevenueVo.setOtbPickupStr(Objects.isNull(otbPickup) ? null :
                    InternationalizationUtils.formatAmountViewValue(otbPickup, calculationRule, currency));

            BigDecimal fbOtbRevenue = otbRevenue.getFbOtbRevenue();
            otbRevenueVo.setFbOtbRevenueStr(Objects.isNull(fbOtbRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(fbOtbRevenue, calculationRule, currency));

            return otbRevenueVo;
        }

        /**
         * 封装 budget
         *
         * @param budgetOtb            budgetOtb
         * @param internationalization 国际化
         * @return budget
         * @author yyt
         * @date 2025/3/14
         **/
        @Override
        public BudgetVO packageBudget(BudgetDTO budgetOtb, InternationalizationDTO internationalization) {
            InternationalizationNumberDTO number = internationalization.getNumber();
            String calculationRule = number.getCalculationRule();
            String currency = number.getCurrency();
            int precision = FunctionPrecisionEnum.DAILY_REVENUE.getPrecision();

            BudgetVO budgetOtbVo = new BudgetVO();
            BigDecimal roomRevenue = budgetOtb.getRoomRevenue();
            budgetOtbVo.setRoomRevenueStr(Objects.isNull(roomRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(roomRevenue, calculationRule, currency));

            BigDecimal occPercent = budgetOtb.getOccPercent();
            budgetOtbVo.setOccPercentStr(Objects.isNull(occPercent) ? null :
                    InternationalizationUtils.formatViewValue(occPercent, precision, calculationRule, null) + "%");

            BigDecimal adr = budgetOtb.getAdr();
            budgetOtbVo.setAdrStr(Objects.isNull(adr) ? null :
                    InternationalizationUtils.formatAmountViewValue(adr, calculationRule, currency));

            BigDecimal revPar = budgetOtb.getRevPar();
            budgetOtbVo.setRevParStr(Objects.isNull(revPar) ? null :
                    InternationalizationUtils.formatAmountViewValue(revPar, calculationRule, currency));

            BigDecimal banquetRevenue = budgetOtb.getBanquetRevenue();
            budgetOtbVo.setBanquetRevenueStr(Objects.isNull(banquetRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(banquetRevenue, calculationRule, currency));

            BigDecimal fbOtbRevenue = budgetOtb.getFbOtbRevenue();
            budgetOtbVo.setFbOtbRevenueStr(Objects.isNull(fbOtbRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(fbOtbRevenue, calculationRule, currency));

            return budgetOtbVo;
        }

        /**
         * 封装 last year otb
         *
         * @param lastYearOtb          lastYearOtb
         * @param internationalization 国际化
         * @return last year otb
         * @author yyt
         * @date 2025/3/14
         **/
        @Override
        public LastYearOtbVO packageLastYearOtb(LastYearOtbDTO lastYearOtb, InternationalizationDTO internationalization) {
            InternationalizationNumberDTO number = internationalization.getNumber();
            String calculationRule = number.getCalculationRule();
            String currency = number.getCurrency();
            int precision = FunctionPrecisionEnum.DAILY_REVENUE.getPrecision();

            LastYearOtbVO lastYearOtbVO = new LastYearOtbVO();
            BigDecimal revenue = lastYearOtb.getOtbRevenue();
            lastYearOtbVO.setOtbRevenueStr(Objects.isNull(revenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(revenue, calculationRule, currency));

            BigDecimal otbOccPercent = lastYearOtb.getOtbOccPercent();
            lastYearOtbVO.setOtbOccPercentStr(Objects.isNull(otbOccPercent) ? null :
                    InternationalizationUtils.formatViewValue(otbOccPercent, precision, calculationRule, null) + "%");

            BigDecimal otbAdr = lastYearOtb.getOtbAdr();
            lastYearOtbVO.setOtbAdrStr(Objects.isNull(otbAdr) ? null :
                    InternationalizationUtils.formatAmountViewValue(otbAdr, calculationRule, currency));

            BigDecimal otbRevPar = lastYearOtb.getOtbRevPar();
            lastYearOtbVO.setOtbRevParStr(Objects.isNull(otbRevPar) ? null :
                    InternationalizationUtils.formatAmountViewValue(otbRevPar, calculationRule, currency));

            BigDecimal banquetRevenue = lastYearOtb.getBanquetRevenue();
            lastYearOtbVO.setBanquetRevenueStr(Objects.isNull(banquetRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(banquetRevenue, calculationRule, currency));

            BigDecimal fbOtbRevenue = lastYearOtb.getFbOtbRevenue();
            lastYearOtbVO.setFbOtbRevenueStr(Objects.isNull(fbOtbRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(fbOtbRevenue, calculationRule, currency));

            return lastYearOtbVO;
        }

        /**
         * 封装 customer
         *
         * @param customers            customers
         * @param internationalization 国际化
         * @return customer
         * @author yyt
         * @date 2025/3/14
         **/
        @Override
        public CustomersVO packageCustomer(CustomersDTO customers, InternationalizationDTO internationalization) {
            InternationalizationNumberDTO number = internationalization.getNumber();
            String calculationRule = number.getCalculationRule();
            String currency = number.getCurrency();

            CustomersVO customersVO = new CustomersVO();
            BigDecimal transientRevenue = customers.getTransientRevenue();
            customersVO.setTransientRevenueStr(Objects.isNull(transientRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(transientRevenue, calculationRule, currency));
            customersVO.setTransientRooms(customers.getTransientRooms());
            BigDecimal transientAdr = customers.getTransientAdr();
            customersVO.setTransientAdrStr(Objects.isNull(transientAdr) ? null :
                    InternationalizationUtils.formatAmountViewValue(transientAdr, calculationRule, currency));

            BigDecimal groupRevenue = customers.getGroupRevenue();
            customersVO.setGroupRevenueStr(Objects.isNull(groupRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(groupRevenue, calculationRule, currency));
            customersVO.setGroupRooms(customers.getGroupRooms());
            BigDecimal groupAdr = customers.getGroupAdr();
            customersVO.setGroupAdrStr(Objects.isNull(groupAdr) ? null :
                    InternationalizationUtils.formatAmountViewValue(groupAdr, calculationRule, currency));

            BigDecimal contractRevenue = customers.getContractRevenue();
            customersVO.setContractRevenueStr(Objects.isNull(contractRevenue) ? null :
                    InternationalizationUtils.formatAmountViewValue(contractRevenue, calculationRule, currency));
            customersVO.setContractRooms(customers.getContractRooms());
            BigDecimal contractAdr = customers.getContractAdr();
            customersVO.setContractAdrStr(Objects.isNull(contractAdr) ? null :
                    InternationalizationUtils.formatAmountViewValue(contractAdr, calculationRule, currency));

            return customersVO;
        }

        /**
         * 封装 segment 信息
         *
         * @param segments             segment
         * @param internationalization 国际化
         * @return segment 信息
         * @author yyt
         * @date 2025/3/15
         **/
        @Override
        public Map<String, Object> packageSegment(List<SegmentDTO> segments, InternationalizationDTO internationalization) {
            InternationalizationNumberDTO number = internationalization.getNumber();
            String calculationRule = number.getCalculationRule();
            String currency = number.getCurrency();
            int precision = FunctionPrecisionEnum.DAILY_REVENUE.getPrecision();
            List<SegmentVO> segmentVOs = Lists.newArrayListWithExpectedSize(segments.size());

            BigDecimal totalRevenue = null;
            BigDecimal totalBudget = null;
            for (SegmentDTO item : segments) {
                SegmentVO segmentBO = new SegmentVO();
                segmentBO.setSegment(item.getSegment());

                BigDecimal otbRevenue = item.getOtbRevenue();
                segmentBO.setOtbRevenueStr(Objects.isNull(otbRevenue) ? null :
                        InternationalizationUtils.formatAmountViewValue(otbRevenue, calculationRule, currency));

                if (Objects.nonNull(otbRevenue)) {
                    if (Objects.isNull(totalRevenue)) {
                        totalRevenue = BigDecimal.ZERO;
                    }
                    totalRevenue = totalRevenue.add(otbRevenue);
                }

                BigDecimal adr = item.getAdr();
                segmentBO.setAdrStr(Objects.isNull(adr) ? null :
                        InternationalizationUtils.formatAmountViewValue(adr, calculationRule, currency));

                BigDecimal budget = item.getBudget();
                segmentBO.setBudgetStr(Objects.isNull(budget) ? null :
                        InternationalizationUtils.formatAmountViewValue(budget, calculationRule, currency));

                if (Objects.nonNull(budget)) {
                    if (Objects.isNull(totalBudget)) {
                        totalBudget = BigDecimal.ZERO;
                    }
                    totalBudget = totalBudget.add(budget);
                }

                BigDecimal achievement = item.getAchievement();
                segmentBO.setAchievementStr(Objects.isNull(achievement) ? null :
                        InternationalizationUtils.formatViewValue(achievement, precision, calculationRule, null) + "%");

                BigDecimal pickup = item.getPickup();
                segmentBO.setPickupStr(Objects.isNull(pickup) ? null :
                        InternationalizationUtils.formatAmountViewValue(pickup, calculationRule, currency));

                // 小于0 是显示颜色（红色）
                if (Objects.nonNull(pickup)) {
                    segmentBO.setShowColor(pickup.compareTo(BigDecimal.ZERO) < 0);
                }

                segmentVOs.add(segmentBO);
            }

            Map<String, Object> result = Maps.newHashMap();
            result.put(RevenueCons.SEGMENT, segmentVOs);
            result.put(RevenueCons.TOTAL_REVENUE, totalRevenue == null ? null :
                    InternationalizationUtils.formatAmountViewValue(totalRevenue, calculationRule, currency));
            result.put(RevenueCons.TOTAL_BUDGET, totalBudget == null ? null :
                    InternationalizationUtils.formatAmountViewValue(totalBudget, calculationRule, currency));
            return result;
        }

        /**
         * 设置 Transient and Group 相关信息
         *
         * @param propertyDTO             资产
         * @param transientAndGroups      Transient and Group
         * @param theDate                 指定日期
         * @param internationalizationDTO 国际化
         * @param data                    设置到渲染Excel作用域中的数据
         * @author yyt
         * @date 2024/3/27
         **/
        private void setTransientAndGroupInContext(PropertyDTO propertyDTO, List<TransientAndGroupDTO> transientAndGroups, Long theDate,
                                                   InternationalizationDTO internationalizationDTO, HashMap<String, Object> data) {
            if (CollectionUtils.isEmpty(transientAndGroups)) {
                data.put("transientAndGroup", Collections.emptyList());
            } else {
                List<TransientAndGroupVO> transientAndGroupVOs = Lists.newArrayListWithExpectedSize(14);
                Map<Long, TransientAndGroupDTO> transientAndGroupByDate = transientAndGroups.stream()
                        .collect(Collectors.toMap(TransientAndGroupDTO::getTargetDate, v -> v));

                Map<Long, Next14DaysMEPSGroupDTO> mepsGroupDTOByDate = revenueMepsGroupConfigService.listNext14DaysMEPSGroup(propertyDTO, theDate)
                        .stream().collect(Collectors.toMap(Next14DaysMEPSGroupDTO::getDate, v -> v));

                Locale locale = LocaleContextHolder.getLocale();

                ZonedDateTime theDateZoned = DateTimeUtils.toZonedDateTime(theDate, propertyDTO.getTimezone());
                List<Long> next14Days = DateTimeUtils.listNextDays(theDateZoned, 14);
                for (Long day : next14Days) {
                    TransientAndGroupVO transientAndGroupVO = new TransientAndGroupVO();
                    setPropertyOperationInfo(propertyDTO, internationalizationDTO, day, transientAndGroupVO, transientAndGroupByDate,
                            mepsGroupDTOByDate.getOrDefault(day, new Next14DaysMEPSGroupDTO()), locale);
                    transientAndGroupVOs.add(transientAndGroupVO);
                }

                data.put("transientAndGroup", transientAndGroupVOs);
            }
        }

        /**
         * 设置资产运营信息
         *
         * @param propertyDTO             资产
         * @param internationalization    国际化
         * @param day                     当天
         * @param transientAndGroupVO     TransientAndGroupVO
         * @param transientAndGroupByDate transientAndGroupByDate
         * @param mepsGroupDTO            mepsGroupDTO
         * @param locale                  当前环境语言
         * @author yyt
         * @date 2025/3/14
         **/
        @Override
        public void setPropertyOperationInfo(PropertyDTO propertyDTO, InternationalizationDTO internationalization, Long day,
                                             TransientAndGroupVO transientAndGroupVO, Map<Long, TransientAndGroupDTO> transientAndGroupByDate,
                                             Next14DaysMEPSGroupDTO mepsGroupDTO, Locale locale) {
            InternationalizationNumberDTO number = internationalization.getNumber();
            String calculationRule = number.getCalculationRule();
            String currency = number.getCurrency();
            int precision = FunctionPrecisionEnum.DAILY_REVENUE.getPrecision();
            InternationalizationDateTimeDTO dateTime = internationalization.getDateTime();
            String longDate = dateTime.getLongDate();
            String timezone = propertyDTO.getTimezone();

            transientAndGroupVO.setTargetDate(day);
            transientAndGroupVO.setTargetDateStr(InternationalizationUtils.formatDateTime(day,
                    longDate, null, timezone, locale));
            transientAndGroupVO.setWeek(DateTimeUtils.toZonedDateTime(day, timezone).getDayOfWeek()
                    .getDisplayName(TextStyle.SHORT_STANDALONE, locale));

            TransientAndGroupDTO transientAndGroup = transientAndGroupByDate.getOrDefault(day, new TransientAndGroupDTO());

            BigDecimal occPercent = transientAndGroup.getOccPercent();
            transientAndGroupVO.setOccPercent(occPercent);
            transientAndGroupVO.setOccPercentStr(Objects.isNull(occPercent) ? null :
                    InternationalizationUtils.formatViewValue(occPercent, precision, calculationRule, null) + "%");

            BigDecimal adr = transientAndGroup.getAdr();
            transientAndGroupVO.setAdrStr(Objects.isNull(adr) ? null :
                    InternationalizationUtils.formatAmountViewValue(adr, calculationRule, currency));

            Integer transientRoomNum = transientAndGroup.getTransientRoomNum();
            transientAndGroupVO.setTransientRoomNum(transientRoomNum);

            BigDecimal transientAdr = transientAndGroup.getTransientAdr();
            transientAndGroupVO.setTransientAdrStr(Objects.isNull(transientAdr) ? null :
                    InternationalizationUtils.formatAmountViewValue(transientAdr, calculationRule, currency));

            Integer groupRoomNum = transientAndGroup.getGroupRoomNum();
            transientAndGroupVO.setGroupRoomNum(groupRoomNum);

            if (Objects.nonNull(transientRoomNum) || Objects.nonNull(groupRoomNum)) {
                Integer totalRoomSold = null;
                if (Objects.nonNull(transientRoomNum)) {
                    totalRoomSold = transientRoomNum;
                }
                if (Objects.nonNull(groupRoomNum)) {
                    totalRoomSold = totalRoomSold == null ? 0 : totalRoomSold;
                    totalRoomSold += groupRoomNum;
                }
                transientAndGroupVO.setTotalRoomSold(totalRoomSold);
            }

            BigDecimal groupAdr = transientAndGroup.getGroupAdr();
            transientAndGroupVO.setGroupAdrStr(Objects.isNull(groupAdr) ? null :
                    InternationalizationUtils.formatAmountViewValue(groupAdr, calculationRule, currency));

            BigDecimal hurdlePoint = transientAndGroup.getHurdlePoint();
            transientAndGroupVO.setHurdlePointStr(Objects.isNull(hurdlePoint) ? null :
                    InternationalizationUtils.formatViewValue(hurdlePoint, precision, calculationRule, StringUtils.EMPTY));

            transientAndGroupVO.setRoomsAvailable(transientAndGroup.getRoomsAvailable());
            transientAndGroupVO.setTransientPickup(transientAndGroup.getTransientPickup());
            transientAndGroupVO.setGroupPickup(transientAndGroup.getGroupPickup());

            transientAndGroupVO.setRoomBlocked(mepsGroupDTO.getRoomBlocked());
            transientAndGroupVO.setRoomPickup(mepsGroupDTO.getRoomPickup());
            String mepsGroup = "";
            if (Objects.nonNull(mepsGroupDTO.getRoomBlocked()) || Objects.nonNull(mepsGroupDTO.getRoomPickup())) {
                if (Objects.nonNull(mepsGroupDTO.getRoomBlocked()) && Objects.nonNull(mepsGroupDTO.getRoomPickup())) {
                    mepsGroup = mepsGroupDTO.getRoomBlocked() + " / " + mepsGroupDTO.getRoomPickup();
                } else {
                    if (Objects.nonNull(mepsGroupDTO.getRoomBlocked())) {
                        mepsGroup = mepsGroupDTO.getRoomBlocked() + " /   ";
                    } else {
                        mepsGroup = "   / " + mepsGroupDTO.getRoomPickup();
                    }
                }
            }
            transientAndGroupVO.setMepsGroup(mepsGroup);
        }

        /**
         * 封装 Same Time Last Year
         *
         * @param propertyDTO          资产
         * @param theDate              指定日期
         * @param detail               revenue 详情
         * @param internationalization 国际化
         * @param locale               locale
         * @return Same Time Last Year
         * @author yyt
         * @date 2025/3/14
         **/
        @Override
        public List<TransientAndGroupLyVO> packageSameTimeLastYear(PropertyDTO propertyDTO, Long theDate, RevenueDTO detail,
                                                                   InternationalizationDTO internationalization, Locale locale) {
            String noDataMsg = i18nMessageUtils.message(RevenueCons.I18N_NO_DATA, locale);
            String soldOutMsg = i18nMessageUtils.message(RevenueCons.I18N_SOLD_OUT, locale);

            InternationalizationNumberDTO number = internationalization.getNumber();
            String calculationRule = number.getCalculationRule();
            String currency = number.getCurrency();
            int precision = FunctionPrecisionEnum.DAILY_REVENUE.getPrecision();

            List<TransientAndGroupLyVO> transientAndGroupLyVOs = Lists.newArrayListWithExpectedSize(14);
            Map<Long, TransientAndGroupLyDTO> transientAndGroupLyByDate = detail.getTransientAndGroupLy().stream()
                    .collect(Collectors.toMap(TransientAndGroupLyDTO::getTargetDate, v -> v));

            // 计算得出指定日期对应去年日期未来14天的日期时间戳，保持日期顺序
            ZonedDateTime theDateZoned = DateTimeUtils.toZonedDateTime(theDate, propertyDTO.getTimezone());
            LocalDate theDateLyLocal = DateTimeUtils.getSameDateLastYear(theDateZoned.toLocalDate());
            List<Long> next14DaysLy = DateTimeUtils.listNextDays(theDateLyLocal.atStartOfDay(theDateZoned.getZone()), 14);
            for (Long day : next14DaysLy) {
                TransientAndGroupLyDTO transientAndGroupLy = transientAndGroupLyByDate.get(day);
                if (Objects.isNull(transientAndGroupLy)) {
                    TransientAndGroupLyVO transientAndGroupLyVO = new TransientAndGroupLyVO();
                    transientAndGroupLyVO.setTargetDate(day);
                    transientAndGroupLyVOs.add(transientAndGroupLyVO);
                } else {
                    TransientAndGroupLyVO transientAndGroupLyVO = new TransientAndGroupLyVO();
                    transientAndGroupLyVO.setTargetDate(day);

                    BigDecimal occPercent = transientAndGroupLy.getOccPercent();
                    transientAndGroupLyVO.setOccPercent(occPercent);
                    transientAndGroupLyVO.setOccPercentStr(Objects.isNull(occPercent) ? null :
                            InternationalizationUtils.formatViewValue(occPercent, precision, calculationRule, null) + "%");

                    transientAndGroupLyVO.setTotalRoomSold(transientAndGroupLy.getTotalRoomSold());

                    BigDecimal adr = transientAndGroupLy.getAdr();
                    transientAndGroupLyVO.setAdrStr(Objects.isNull(adr) ? null :
                            InternationalizationUtils.formatAmountViewValue(adr, calculationRule, currency));

                    Integer transientRoomNum = transientAndGroupLy.getTransientRoomNum();
                    transientAndGroupLyVO.setTransientRoomNum(transientRoomNum);

                    Integer groupRoomNum = transientAndGroupLy.getGroupRoomNum();
                    transientAndGroupLyVO.setGroupRoomNum(groupRoomNum);

                    BigDecimal rate = transientAndGroupLy.getRate();
                    if (Objects.isNull(rate)) {
                        transientAndGroupLyVO.setRateStr(noDataMsg);
                    } else {
                        if (rate.compareTo(BigDecimal.ZERO) == 0) {
                            transientAndGroupLyVO.setRateStr(soldOutMsg);
                        } else {
                            transientAndGroupLyVO.setRateStr(InternationalizationUtils.formatAmountViewValue(rate, calculationRule, currency));
                        }
                    }

                    transientAndGroupLyVOs.add(transientAndGroupLyVO);
                }
            }

            return transientAndGroupLyVOs;
        }

        /**
         * 获取房价
         *
         * @param propertyDTO        资产
         * @param theDate            指定日期
         * @param exportRevenueQuery 房价的查询条件，包括数据源、平台
         * @param locale             当前环境语言
         * @author yyt
         * @date 2024/3/27
         **/
        @Override
        public List<RateVO> getRate(PropertyDTO propertyDTO, Long theDate, ExportRevenueQuery exportRevenueQuery, Locale locale) {
            String timezone = propertyDTO.getTimezone();
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
            LocalDate theDateLocalDate = DateTimeUtils.longToLocalDate(theDate, timezone);
            String theDateStr = theDateLocalDate.format(dateTimeFormatter);
            String yesterdayStr = theDateLocalDate.minusDays(1).format(dateTimeFormatter);

            //查询rateShop配置
            Setting setting = settingService.getByPropertyId(propertyDTO.getId());

            // 查询酒店配置
            List<PropertyHotelDTO> propertyHotelDTOs = propertyHotelService.listSortDTOByPropertyId(propertyDTO.getId());
            List<Integer> hotelIds = propertyHotelDTOs.stream().map(PropertyHotelDTO::getHotelId).toList();
            Map<Integer, Hotel> hotelId2Hotel = hotelService.listByIds(hotelIds).stream().collect(Collectors.toMap(Hotel::getId, Function.identity()));

            int size = propertyHotelDTOs.size();
            // 价格
            List<RateVO> rates = Lists.newArrayListWithExpectedSize(size);
            // 国际化配置
            InternationalizationNumberDTO number = propertyDTO.getInternationalization().getNumber();
            //当前日期
            long currentDate = DateTimeUtils.dateNow(propertyDTO.getTimezone()).toEpochSecond();

            //构建房价的查询条件
            Next14DaysRateQuery next14DaysRateQuery = new Next14DaysRateQuery();
            next14DaysRateQuery.setDatasourceIdByBAR(exportRevenueQuery.getDatasourceIdByNext14DaysBAR());
            next14DaysRateQuery.setPlatformIdByBAR(exportRevenueQuery.getPlatformIdByNext14DaysBAR());
            next14DaysRateQuery.setDatasourceIdByEBR(exportRevenueQuery.getDatasourceIdByNext14DaysEBR());
            next14DaysRateQuery.setPlatformIdByEBR(exportRevenueQuery.getPlatformIdByNext14DaysEBR());

            for (PropertyHotelDTO propertyHotel : propertyHotelDTOs) {
                Integer hotelId = propertyHotel.getHotelId();
                Hotel hotel = hotelId2Hotel.get(hotelId);

                List<Next14DaysRateDTO> theDateNext14DaysRateDTOS;
                Map<String, List<Next14DaysRatesDTO>> checkInDay2Next14DaysRateDTOS = new HashMap<>();
                //判断查询的日期是否未来日期
                if (theDate > currentDate) {
                    //查询指定日期的房价（未来日期房价来源latest_rate_yyyy）
                    theDateNext14DaysRateDTOS = next14DaysService.listFutureDateNext14DaysRate(theDateStr, hotel, propertyDTO, setting, next14DaysRateQuery);
                } else {
                    //查询指定日期的房价（当前及历史日期房价来源history_rate_next_n_days）
                    theDateNext14DaysRateDTOS = next14DaysService.listHistoryDateNext14DaysRate(theDateStr, hotel, propertyDTO, setting, next14DaysRateQuery);
                    //查询昨天的房价，只有当前及历史日期才需要与昨天相比计算房价差值
                    List<Next14DaysRateDTO> yesterdayNext14DaysRateDTOS = next14DaysService.listHistoryDateNext14DaysRate(yesterdayStr, hotel, propertyDTO, setting, next14DaysRateQuery);
                    checkInDay2Next14DaysRateDTOS = yesterdayNext14DaysRateDTOS.stream()
                            .collect(Collectors.toMap(Next14DaysRateDTO::getCheckInDay, Next14DaysRateDTO::getRoomRate));
                }

                List<RatesVO> next14DayRates = Lists.newArrayListWithExpectedSize(14);

                for (Next14DaysRateDTO item : theDateNext14DaysRateDTOS) {
                    List<Next14DaysRatesDTO> roomRates = item.getRoomRate();

                    Map<Integer, Next14DaysRatesDTO> rateType2CurrentDateRoomRate = roomRates.stream().collect(Collectors.toMap(Next14DaysRatesDTO::getRateType, Function.identity()));
                    // 当天BAR房价
                    Next14DaysRatesDTO currentDateBARRoomRate = rateType2CurrentDateRoomRate.get(RateTypeEnum.BEST_AVAILABLE_RATE.getRateType());
                    // 当天早鸟价
                    Next14DaysRatesDTO currentDateEBRRoomRate = rateType2CurrentDateRoomRate.get(RateTypeEnum.EARLY_BIRD_RATE.getRateType());

                    // 昨天BAR房价
                    Next14DaysRatesDTO yesterdayBARRoomRate = null;
                    // 昨天早鸟价
                    Next14DaysRatesDTO yesterdayEBRRoomRate = null;
                    List<Next14DaysRatesDTO> yesterdayRoomRates = checkInDay2Next14DaysRateDTOS.getOrDefault(item.getCheckInDay(), Collections.emptyList());
                    if (CollectionUtils.isNotEmpty(yesterdayRoomRates)) {
                        Map<Integer, Next14DaysRatesDTO> rateType2yesterdayRoomRate = yesterdayRoomRates.stream()
                                .collect(Collectors.toMap(Next14DaysRatesDTO::getRateType, Function.identity()));
                        yesterdayBARRoomRate = rateType2yesterdayRoomRate.get(RateTypeEnum.BEST_AVAILABLE_RATE.getRateType());
                        yesterdayEBRRoomRate = rateType2yesterdayRoomRate.get(RateTypeEnum.EARLY_BIRD_RATE.getRateType());
                    }

                    // 封装BAR房价和早鸟价
                    RatesVO ratesVO = new RatesVO();
                    ratesVO.setHotelId(propertyHotel.getHotelId());
                    ratesVO.setDate(item.getCheckInDay());
                    Map<String, String> barRateAndDiff = packageLowestHotelRate(currentDateBARRoomRate, yesterdayBARRoomRate, number, locale);
                    ratesVO.setBarRate(barRateAndDiff.get(RATE));
                    ratesVO.setBarRateDiff(barRateAndDiff.get(DIFF));
                    Map<String, String> ebrRateAndDiff = packageLowestHotelRate(currentDateEBRRoomRate, yesterdayEBRRoomRate, number, locale);
                    ratesVO.setEbrHotelRate(ebrRateAndDiff.get(RATE));
                    ratesVO.setEbrHotelRateDiff(ebrRateAndDiff.get(DIFF));
                    next14DayRates.add(ratesVO);
                }

                RateVO rateVO = new RateVO();
                rateVO.setHotelId(propertyHotel.getHotelId());
                rateVO.setName(propertyHotel.getName());
                //价格
                rateVO.setRates(next14DayRates);
                rates.add(rateVO);
            }

            return rates;
        }

        /**
         * 最低酒店房价
         *
         * @param currentDateRoomRate 当天价格
         * @param yesterdayRoomRate   昨天价格
         * @param number              国际化配置
         * @param locale              当前环境语言
         * @return 最低酒店房价
         * @author yyt
         * @date 2024/3/28
         **/
        private Map<String, String> packageLowestHotelRate(Next14DaysRatesDTO currentDateRoomRate, Next14DaysRatesDTO yesterdayRoomRate,
                                                           InternationalizationNumberDTO number, Locale locale) {
            if (Objects.isNull(currentDateRoomRate)) {
                HashMap<String, String> resultMap = new HashMap<>();
                //Rate 设置为No Data
                resultMap.put(RATE, i18nMessageUtils.message(RevenueCons.I18N_NO_DATA, locale));
                return resultMap;
            }

            String calculationRule = number.getCalculationRule();
            String currency = number.getCurrency();

            Map<String, String> result = new HashMap<>();

            String price = currentDateRoomRate.getPrice();
            if (NumberUtil.isBigDecimal(price) && new BigDecimal(price).compareTo(BigDecimal.ZERO) == 0) {
                //Rate设置为Sold out
                result.put(RATE, i18nMessageUtils.message(RevenueCons.I18N_SOLD_OUT, locale));
            } else {
                result.put(RATE, (NumberUtil.isBigDecimal(price) ? InternationalizationUtils.formatAmountViewValue(new BigDecimal(price), calculationRule, currency) : price));
            }

            // 设置 差值
            if (Objects.nonNull(yesterdayRoomRate)) {
                //昨天的最低价
                String yesterdayRate = yesterdayRoomRate.getPrice();
                // 只有房价是数字类型 且 都不为0 才参与计算
                if (NumberUtil.isBigDecimal(price) && NumberUtil.isBigDecimal(yesterdayRate)
                        && new BigDecimal(price).compareTo(BigDecimal.ZERO) != 0
                        && new BigDecimal(yesterdayRate).compareTo(BigDecimal.ZERO) != 0) {
                    BigDecimal diff = new BigDecimal(price).subtract(new BigDecimal(yesterdayRate));
                    if (diff.compareTo(BigDecimal.ZERO) != 0) {
                        String diffStr = InternationalizationUtils.formatViewValue(diff, CurrencyEnum.getPrecisionBySymbol(currency), calculationRule, null);
                        // 差值（今天房价-昨天房价）
                        result.put(DIFF, diff.compareTo(BigDecimal.ZERO) < 0 ? "(" + diffStr + ")" : "(+" + diffStr + ")");
                    }
                }
            }

            return result;
        }

        /**
         * 查询 BAR 房价
         *
         * @param propertyId 资产 id
         * @param startDate  开始日期（yyyyMMdd）
         * @param endDate    结束日期（yyyyMMdd）
         * @param query      其他查询条件
         * @return BAR 房价
         * @author yyt
         * @date 2024/5/28
         **/
        @Override
        public List<LatestHotelPriceDTO> listBARRate(Integer propertyId, Long startDate, Long endDate, RevenueDetailQuery query) {
            Setting setting = settingService.getByPropertyId(propertyId);
            //房价配置参数
            List<RateConfigDTO> rateConfigDTOS = JsonUtils.convertStringToList(objectMapper, setting.getRateConfig(), RateConfigDTO.class);
            //房价类型集合
            Set<Integer> rateTypes = rateConfigDTOS.stream().map(RateConfigDTO::getRateType).collect(Collectors.toSet());

            List<LatestHotelPriceDTO> latestHotelPriceDTOS = new ArrayList<>();
            if (rateTypes.contains(RateTypeEnum.BEST_AVAILABLE_RATE.getRateType())) {
                //查询资产自身酒店
                Integer hotelId = propertyHotelService.getPropertySelfHotel(propertyId).getHotelId();
                //构造查询条件
                LatestHotelPriceQuery latestHotelBARPriceQuery = LatestHotelPriceQuery.builder()
                        .fromDate(startDate)
                        .toDate(endDate)
                        .datasourceId(query.getDatasourceId())
                        .platformId(query.getPlatformId())
                        .roomIds(Collections.emptyList())
                        .build();
                //获取指定数据源、指定平台的最低BAR房价
                latestHotelPriceDTOS = latestBARRateService.listLowestBARHotelPrice(Collections.singletonList(hotelId), setting.getCurrencyId(), setting.getContainTaxFlag(),
                        latestHotelBARPriceQuery, rateConfigDTOS, setting.getPos());
            }
            return latestHotelPriceDTOS;
        }

        /**
         * 设置 MEPS Group
         *
         * @param propertyDTO        资产
         * @param targetDate         指定日期
         * @param transientAndGroups 未来 14 天数据
         * @author yyt
         * @date 2025/3/14
         **/
        @Override
        public void setMEPSGroup(PropertyDTO propertyDTO, Long targetDate, List<TransientAndGroupDTO> transientAndGroups) {
            // 当前资产未配置，直接 return
            if (!revenueMepsGroupConfigService.hasConfig(propertyDTO.getId())) return;

            Map<Long, Next14DaysMEPSGroupDTO> mepsGroupDTOByDate = revenueMepsGroupConfigService.listNext14DaysMEPSGroup(propertyDTO, targetDate)
                    .stream().collect(Collectors.toMap(Next14DaysMEPSGroupDTO::getDate, v -> v));

            transientAndGroups.forEach(item -> {
                Next14DaysMEPSGroupDTO mepsGroupDTO = mepsGroupDTOByDate.getOrDefault(item.getTargetDate(), new Next14DaysMEPSGroupDTO());
                item.setRoomBlocked(mepsGroupDTO.getRoomBlocked());
                item.setRoomPickup(mepsGroupDTO.getRoomPickup());
            });
        }

        /**
         * 设置文件显示内容（未来14天的房价信息）
         *
         * @param propertyDTO        资产DTO
         * @param theDate            指定日期
         * @param data               文件中的数据
         * @param revenueDTO         revenue详情
         * @param exportRevenueQuery 导出房价时的查询条件，包括：数据源、平台
         * @author lichen
         * @date 2025/4/14
         */
        @Override
        public void packageNext14DaysRateToFile(PropertyDTO propertyDTO, long theDate, RevenueDTO revenueDTO, HashMap<String, Object> data, ExportRevenueQuery exportRevenueQuery) {
            String timezone = propertyDTO.getTimezone();
            InternationalizationDTO internationalization = propertyDTO.getInternationalization();
            Locale locale = LocaleContextHolder.getLocale();

            data.put("propertyName", propertyDTO.getName());
            // 设置日期、月
            String currentDate = InternationalizationUtils.formatDateTime(theDate, internationalization.getDateTime().getLongDate(), null,
                    timezone, locale);
            data.put("currentDate", currentDate);

            // 设置 Transient and Group 相关信息
            setTransientAndGroupInContext(propertyDTO, revenueDTO.getTransientAndGroup(), theDate, internationalization, data);

            // 设置房价
            data.put("rates", getRate(propertyDTO, theDate, exportRevenueQuery, locale));

            //当前上下文语言
            Locale currentThreadLocale = LocaleContextHolder.getLocale();
            String i18nAllDatasource = i18nMessageUtils.message(RevenueCons.I18N_ALL_DATASOURCE, currentThreadLocale);
            String i18nAllPlatform = i18nMessageUtils.message(RevenueCons.I18N_ALL_PLATFORM, currentThreadLocale);

            //设置未来14天BAR房价的查询条件-数据源
            Integer datasourceIdByNext14DaysBAR = exportRevenueQuery.getDatasourceIdByNext14DaysBAR();
            if (Objects.isNull(datasourceIdByNext14DaysBAR)) {
                data.put("datasourceByNext14DaysBAR", i18nAllDatasource);
            } else {
                data.put("datasourceByNext14DaysBAR",
                        i18nMessageUtils.message(DatasourceEnum.getI18nCode(datasourceIdByNext14DaysBAR), currentThreadLocale));
            }
            //设置未来14天BAR房价的查询条件-平台
            Integer platformIdByNext14DaysBAR = exportRevenueQuery.getPlatformIdByNext14DaysBAR();
            if (Objects.isNull(platformIdByNext14DaysBAR)) {
                data.put("platformByNext14DaysBAR", i18nAllPlatform);
            } else {
                data.put("platformByNext14DaysBAR",
                        i18nMessageUtils.message(PlatformEnum.getI18nCode(platformIdByNext14DaysBAR), currentThreadLocale));
            }

            //设置未来14天早鸟价的查询条件-数据源
            Integer datasourceIdByNext14DaysEBR = exportRevenueQuery.getDatasourceIdByNext14DaysEBR();
            if (Objects.isNull(datasourceIdByNext14DaysEBR)) {
                data.put("datasourceByNext14DaysEBR", i18nAllDatasource);
            } else {
                data.put("datasourceByNext14DaysEBR",
                        i18nMessageUtils.message(DatasourceEnum.getI18nCode(datasourceIdByNext14DaysEBR), currentThreadLocale));
            }
            //设置未来14天早鸟价的查询条件-平台
            Integer platformIdByNext14DaysEBR = exportRevenueQuery.getPlatformIdByNext14DaysEBR();
            if (Objects.isNull(platformIdByNext14DaysEBR)) {
                data.put("platformByNext14DaysEBR", i18nAllPlatform);
            } else {
                data.put("platformByNext14DaysEBR",
                        i18nMessageUtils.message(PlatformEnum.getI18nCode(platformIdByNext14DaysEBR), currentThreadLocale));
            }
        }

        /**
         * 检查当前资产设置 pms 是否支持此类型
         *
         * @param pmsType 资产
         * @param type    类型
         * @author yyt
         * @date 2025/7/7
         **/
        @Override
        public void checkType(String pmsType, int type) {
            if (pmsType.equals(SystemPlatformEnum.OPERA.getDesc())) {
                if (!RevenueImportCons.OPERA_NOTIFY_TYPES.contains(type)) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_REPORT_TYPE_NOT_SUPPORTED,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the type is not support");
                }
            } else if (pmsType.equals(SystemPlatformEnum.SHI_JI.getDesc())) {
                if (!RevenueImportCons.SHI_JI_NOTIFY_TYPES.contains(type)) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_REPORT_TYPE_NOT_SUPPORTED,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the type is not support");
                }
            } else {
                if (!RevenueImportCons.PEP_NOTIFY_TYPES.contains(type)) {
                    throw new BadRequestException(CommonErrorI18nConstant.DATA_INTEGRATION_REPORT_TYPE_NOT_SUPPORTED,
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the type is not support");
                }
            }
        }

    }