# PriceController.java

    package cloud.wconnect.rateshop.controller.rateshop;

    import cloud.wconnect.commonlib.annotation.ApiId;
    import cloud.wconnect.commonlib.component.SessionContext;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.I18nMessageUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.rateshop.cons.rateshop.*;
    import cloud.wconnect.rateshop.entity.rateshop.Hotel;
    import cloud.wconnect.rateshop.entity.rateshop.RoomType;
    import cloud.wconnect.rateshop.entity.rateshop.RoomTypeInfoRel;
    import cloud.wconnect.rateshop.entity.rateshop.Setting;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.LatestHotelPriceDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.RateConfigDTO;
    import cloud.wconnect.rateshop.pojo.query.rateshop.LatestHotelPriceQuery;
    import cloud.wconnect.rateshop.pojo.query.rateshop.PriceQuery;
    import cloud.wconnect.rateshop.service.rateshop.*;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import jakarta.validation.Valid;

    import java.text.MessageFormat;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * 查询房价controller
     *
     * @author lichen
     * @date 2023/6/9
     */
    @RestController
    @RequestMapping("/rate-shop/price")
    public class PriceController {

        @Autowired
        private SessionContext sessionContext;
        @Autowired
        private IHotelService hotelService;
        @Autowired
        private IPropertyHotelService propertyHotelService;
        @Autowired
        private IRoomTypeService roomTypeService;
        @Autowired
        private ILatestRateService latestRateService;
        @Autowired
        private ISettingService settingService;
        @Autowired
        private ILatestBARRateService latestBARRateService;
        @Autowired
        private IRoomTypeInfoRelService roomTypeInfoRelService;
        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private I18nMessageUtils i18nMessageUtils;

        /**
         * 查询房价信息
         *
         * @param property   资产标识
         * @param priceQuery 查询条件
         * @return java.util.List<cloud.wconnect.rateshop.pojo.dto.rateshop.LatestHotelPriceDTO>
         * @author lichen
         * @date 2024/5/14
         */
        @ApiId(341)
        @GetMapping
        public List<LatestHotelPriceDTO> listHotelPrice(String property, @Valid PriceQuery priceQuery) {
            int propertyId = sessionContext.getPropertyId(property);
            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);

            //校验资产是否设置rate shop功能
            Setting setting = settingService.getByPropertyId(propertyId);
            if (Objects.isNull(setting)) {
                throw new BadRequestException(RateShopErrorI18nConstant.PROPERTY_NOT_CONFIG_RATE_SHOP_SETTING,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        "The property has no rateShop set, propertyId = " + propertyId);
            }

            //把yyyy-MM-dd的日期转化成时间戳
            long fromTimestamp = DateTimeUtils.dateStrToEpochSecond(priceQuery.getFromDate(), BaseConstant.DATE_FORMAT_YYYY_MM_DD, propertyDTO.getTimezone());
            long toTimestamp = DateTimeUtils.dateStrToEpochSecond(priceQuery.getToDate(), BaseConstant.DATE_FORMAT_YYYY_MM_DD, propertyDTO.getTimezone());
            //开始时间不能大于结束时间
            if (fromTimestamp > toTimestamp) {
                throw new BadRequestException(CommonErrorI18nConstant.START_DATE_CANNOT_GREATER_THAN_END_DATE,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The toDate must be greater than or equal to the fromDate");
            }
            //判断时间间隔必须小于70天
            long diffDay = DateTimeUtils.dateDiffOfDays(fromTimestamp, toTimestamp, propertyDTO.getTimezone());
            //日历面板最大区间70天，为避免由于时区原因导致数据查询不全，此处限制最大区间70天
            if (Math.abs(diffDay) > 70) {
                throw new BadRequestException(CommonErrorI18nConstant.SELECT_DATE_LIMIT,
                        i18nMessageUtils.getErrorParamMapNotNeedTranslate(70),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "The interval between fromDate and toDate must be less than or equal to 70 days");
            }

            //房价参数配置
            List<RateConfigDTO> rateConfigDTOS = JsonUtils.convertStringToList(objectMapper, setting.getRateConfig(), RateConfigDTO.class);

            Set<Integer> rateTypeSet = rateConfigDTOS.stream().map(RateConfigDTO::getRateType).collect(Collectors.toSet());
            //校验房价类型是否合法
            if (!rateTypeSet.contains(priceQuery.getRateType())) {
                List<String> rateTypeI18nCodeList = Arrays.stream(RateTypeEnum.values()).filter(rateTypeEnum -> rateTypeSet.contains(rateTypeEnum.getRateType()))
                        .map(RateTypeEnum::getI18nCode).toList();
                throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                        i18nMessageUtils.getErrorParamMapNeedTranslate(RateShopFieldI18nConstant.RATE_TYPE, rateTypeI18nCodeList),
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        MessageFormat.format("property not configured rateType, propertyId:{0}, rateType:{1}", propertyId, priceQuery.getRateType()));
            }

            //校验数据源是否合法
            if (Objects.nonNull(priceQuery.getDatasourceId())) {
                Map<Integer, List<Integer>> rateType2DatasourceIds = rateConfigDTOS.stream()
                        .collect(Collectors.groupingBy(RateConfigDTO::getRateType, Collectors.mapping(RateConfigDTO::getDatasourceId, Collectors.toList())));
                List<Integer> datasourceIds = rateType2DatasourceIds.get(priceQuery.getRateType());
                if (!datasourceIds.contains(priceQuery.getDatasourceId())) {
                    List<String> datasourceI18nCodeList = Arrays.stream(DatasourceEnum.values()).filter(item -> datasourceIds.contains(item.getDatasourceId()))
                            .map(DatasourceEnum::getI18nCode).toList();
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(RateShopFieldI18nConstant.DATASOURCE, datasourceI18nCodeList),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            MessageFormat.format("DatasourceId Invalid, propertyId:{0}, rateType:{1}, datasourceId:{2}",
                                    propertyId, priceQuery.getRateType(), priceQuery.getDatasourceId()));
                }
            }

            //校验平台是否合法
            if (Objects.nonNull(priceQuery.getPlatform())) {
                Map<String, List<Integer>> rateTypeAndDatasourceId2PlatformIds = rateConfigDTOS.stream()
                        .collect(Collectors.toMap(item -> RateConfigDTO.generateKey(item.getRateType(), item.getDatasourceId()),
                                RateConfigDTO::getPlatformIds));
                List<Integer> platformIdsByConfig = rateTypeAndDatasourceId2PlatformIds.get(RateConfigDTO.generateKey(priceQuery.getRateType(), priceQuery.getDatasourceId()));
                if (!platformIdsByConfig.contains(priceQuery.getPlatform())) {
                    List<String> platformI18nCodeList = Arrays.stream(PlatformEnum.values()).filter(item -> platformIdsByConfig.contains(item.getCode()))
                            .map(PlatformEnum::getI18nCode).toList();
                    throw new BadRequestException(CommonErrorI18nConstant.MUST_IN_LIST,
                            i18nMessageUtils.getErrorParamMapNeedTranslate(RateShopFieldI18nConstant.PLATFORM, platformI18nCodeList),
                            BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                            MessageFormat.format("platform Invalid, propertyId:{0}, rateType:{1}, datasourceId:{2}, platform:{3}",
                                    propertyId, priceQuery.getRateType(), priceQuery.getDatasourceId(), priceQuery.getPlatform()));
                }
            }

            //校验目标酒店是否存在 且 状态正常
            Hotel hotel = hotelService.checkHotel(priceQuery.getTarget());
            //校验酒店是否和资产有关系
            propertyHotelService.checkAffiliation(hotel.getId(), propertyId);
            if (priceQuery.getType() != BaseConstant.ZERO) {
                //校验类型是否存在
                RoomType roomType = roomTypeService.checkExist(priceQuery.getType());
                //校验类型是否归属于指定资产、指定平台、指定销售地区
                roomTypeService.checkBelong(roomType, propertyId, priceQuery.getPlatform(), setting.getPos());
            }

            List<Integer> roomInfoIds = new ArrayList<>();
            if (priceQuery.getType() != BaseConstant.ZERO) {
                //根据类型id查询roomInfo的id集合
                roomInfoIds = roomTypeInfoRelService.listByTypeId(propertyId, priceQuery.getType())
                        .stream().map(RoomTypeInfoRel::getRoomInfoId).toList();
                // 当类型未配置roomInfo时，返回空的数据
                if (CollectionUtils.isEmpty(roomInfoIds)) {
                    return Collections.emptyList();
                }
            }

            //构建查询条件
            LatestHotelPriceQuery latestHotelPriceQuery = new LatestHotelPriceQuery();
            latestHotelPriceQuery.setFromDate(Long.parseLong(priceQuery.getFromDate().replace("-", "")));
            latestHotelPriceQuery.setToDate(Long.parseLong(priceQuery.getToDate().replace("-", "")));
            latestHotelPriceQuery.setPlatformId(priceQuery.getPlatform());
            latestHotelPriceQuery.setDatasourceId(priceQuery.getDatasourceId());
            latestHotelPriceQuery.setRoomIds(roomInfoIds);

            if (priceQuery.getRateType() == RateTypeEnum.EARLY_BIRD_RATE.getRateType()) {
                //查询早鸟价
                return latestRateService.listLowestEBRHotelPrice(Collections.singletonList(hotel.getId()), setting.getCurrencyId(),
                        setting.getContainTaxFlag(), latestHotelPriceQuery, rateConfigDTOS, setting.getPos());
            } else {
                //查询BAR房价
                return latestBARRateService.listLowestBARHotelPrice(Collections.singletonList(hotel.getId()), setting.getCurrencyId(),
                        setting.getContainTaxFlag(), latestHotelPriceQuery, rateConfigDTOS, setting.getPos());
            }
        }

    }