# RevenueHourHotelPriceHandle.java

    package cloud.wconnect.rateshop.springlistener.revenue;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.UpdateLog;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.commonlib.util.UpdateLogUtil;
    import cloud.wconnect.rateshop.entity.revenue.HistoryBARRateNHours;
    import cloud.wconnect.rateshop.pojo.dto.revenue.HourHotelPriceDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.consumer.hotelprice.PlatformDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.consumer.hotelprice.PriceDTO;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.consumer.hotelprice.PriceMessage;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.consumer.hotelprice.RoomDTO;
    import cloud.wconnect.rateshop.service.revenue.IHistoryBARRateNHoursService;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.transaction.annotation.Transactional;

    import java.math.BigDecimal;
    import java.time.ZonedDateTime;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * 按小时存储房价的处理器
     *
     * @author lichen
     * @date 2024/3/25
     */
    @Component
    public class RevenueHourHotelPriceHandle {

        @Autowired
        private IHistoryBARRateNHoursService historyBARRateNHoursService;
        @Autowired
        private UpdateLogUtil updateLogUtil;
        @Autowired
        private ObjectMapper objectMapper;

        /**
         * 处理房价消息
         *
         * @param priceMessage 房价消息
         * @param propertyDTO  资产DTO（酒店被该资产设置为自身）
         * @return void
         * @author lichen
         * @date 2024/3/25
         */
        @Transactional(rollbackFor = Exception.class)
        public void handle(PriceMessage priceMessage, PropertyDTO propertyDTO) {
            Integer propertyId = propertyDTO.getId();
            /**
             * 正常情况：采集的时间 和 消费的时间是一天
             * 特殊情况：采集的时间 和 消费的时间不是一天
             */
            Long collectDate = Long.valueOf(DateTimeUtils.timestampToStr(priceMessage.getQueryDate(), BaseConstant.DATE_FORMAT_YYYYMMDD, propertyDTO.getTimezone()));

            //采集时间的房价
            Optional<PriceDTO> priceByCurrentDateOptional = priceMessage.getData().stream()
                    .filter(item -> item.getCheckInDate().equals(collectDate))
                    .findFirst();
            if (priceByCurrentDateOptional.isEmpty()) return;

            //抓取时刻的小时数
            ZonedDateTime zonedDateTime = DateTimeUtils.toZonedDateTime(priceMessage.getQueryDate(), propertyDTO.getTimezone());
            int hour = zonedDateTime.getHour();

            //各个平台的房价
            List<PlatformDTO> hotelPriceByplatform = priceByCurrentDateOptional.get().getPlatform();

            //每小时的酒店房价
            List<HourHotelPriceDTO> hourHotelPriceDTOS = Lists.newArrayListWithCapacity(hotelPriceByplatform.size());
            //获取各个平台每小时的最低房价
            for (PlatformDTO platformDTO : hotelPriceByplatform) {
                List<RoomDTO> hotelPriceByPlatform = platformDTO.getRoom();
                if (CollectionUtils.isNotEmpty(hotelPriceByPlatform)) {
                    //税前最低价
                    BigDecimal beforeTaxMinRate = hotelPriceByPlatform.stream().map(RoomDTO::getRate)
                            .filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(null);
                    //税后最低价
                    BigDecimal afterTaxMinRate = hotelPriceByPlatform.stream().map(RoomDTO::getAfterTaxRate)
                            .filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(null);
                    //添加到每小时的酒店房价集合中
                    hourHotelPriceDTOS.add(new HourHotelPriceDTO()
                            .setHour(hour)
                            .setDatasourceId(priceMessage.getDatasourceId())
                            .setPlatformId(platformDTO.getId())
                            .setPrice(beforeTaxMinRate).setAfterTaxPrice(afterTaxMinRate)
                            .setQueryDate(priceMessage.getQueryDate()));
                } else {
                    //Sold out
                    hourHotelPriceDTOS.add(new HourHotelPriceDTO()
                            .setHour(hour)
                            .setDatasourceId(priceMessage.getDatasourceId())
                            .setPlatformId(platformDTO.getId())
                            .setPrice(BigDecimal.ZERO).setAfterTaxPrice(BigDecimal.ZERO)
                            .setQueryDate(priceMessage.getQueryDate()));
                }
            }
            if (CollectionUtils.isEmpty(hourHotelPriceDTOS)) return;

            //查询酒店指定日期指定币种下的房价
            HistoryBARRateNHours oriHistoryBARRateNHours = historyBARRateNHoursService.get(Integer.valueOf(collectDate.toString()),
                    priceMessage.getHotelId(), priceMessage.getCurrencyId(), propertyId, priceMessage.getPos());
            if (Objects.isNull(oriHistoryBARRateNHours)) {
                //updateLog
                String updateLogStr = JsonUtils.convertObjListToString(objectMapper, Lists.newArrayList(new UpdateLog(0, DateTimeUtils.nowTimestamp())));
                HistoryBARRateNHours insertEntity = new HistoryBARRateNHours();
                insertEntity.setPropertyId(propertyId).setTenantId(propertyDTO.getTenantId())
                        .setHotelId(priceMessage.getHotelId())
                        .setCurrencyId(priceMessage.getCurrencyId())
                        .setTargetDate(Integer.valueOf(collectDate.toString()))
                        .setData(JsonUtils.convertObjListToString(objectMapper, hourHotelPriceDTOS))
                        .setPos(priceMessage.getPos())
                        .setUpdateLog(updateLogStr);
                historyBARRateNHoursService.save(insertEntity);
            } else {
                //原每小时房价
                List<HourHotelPriceDTO> oriHourHotelPriceDTO = JsonUtils.convertStringToList(objectMapper, oriHistoryBARRateNHours.getData(), HourHotelPriceDTO.class);
                Map<String, HourHotelPriceDTO> key2OriHourHotelPriceDTO = oriHourHotelPriceDTO.stream().collect(Collectors.toMap(
                        item -> HourHotelPriceDTO.generateKey(item.getHour(), item.getPlatformId(), item.getDatasourceId()),
                        Function.identity()));
                Set<String> oriKey = key2OriHourHotelPriceDTO.keySet();
                //最新的房价
                Map<String, HourHotelPriceDTO> key2LatestHourHotelPriceDTO = hourHotelPriceDTOS.stream().collect(Collectors.toMap(
                        item -> HourHotelPriceDTO.generateKey(item.getHour(), item.getPlatformId(), item.getDatasourceId()),
                        Function.identity()));
                Set<String> latestKey = key2LatestHourHotelPriceDTO.keySet();

                Set<String> allKey = new HashSet<>();
                allKey.addAll(oriKey);
                allKey.addAll(latestKey);

                //更新后的每小时房价
                List<HourHotelPriceDTO> hourHotelPriceDTOByUpdate = Lists.newArrayListWithCapacity(oriHourHotelPriceDTO.size());
                for (String key : allKey) {
                    HourHotelPriceDTO latest = key2LatestHourHotelPriceDTO.get(key);
                    if (Objects.nonNull(latest)) {
                        hourHotelPriceDTOByUpdate.add(latest);
                        continue;
                    }
                    hourHotelPriceDTOByUpdate.add(key2OriHourHotelPriceDTO.get(key));
                }

                //更新房价
                HistoryBARRateNHours updateEntity = new HistoryBARRateNHours();
                updateEntity.setId(oriHistoryBARRateNHours.getId());
                updateEntity.setData(JsonUtils.convertObjListToString(objectMapper, hourHotelPriceDTOByUpdate));
                updateEntity.setUpdateLog(updateLogUtil.append(oriHistoryBARRateNHours.getUpdateLog(), 0));
                historyBARRateNHoursService.updateById(updateEntity);
            }
        }
    }