# HistoryRateNextNDaysServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    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.cons.rateshop.RateTypeEnum;
    import cloud.wconnect.rateshop.entity.rateshop.PropertyHotel;
    import cloud.wconnect.rateshop.entity.revenue.HistoryBARRateNextNDays;
    import cloud.wconnect.rateshop.entity.revenue.HistoryRateNextNDays;
    import cloud.wconnect.rateshop.entity.revenue.HistoryRatePropertyMapping;
    import cloud.wconnect.rateshop.mapper.revenue.HistoryRateNextNDaysMapper;
    import cloud.wconnect.rateshop.pojo.MergeDayHotelPriceDTO;
    import cloud.wconnect.rateshop.pojo.dto.revenue.DayHotelPriceDTO;
    import cloud.wconnect.rateshop.service.IHistoryRateNextNDaysByRateTypeService;
    import cloud.wconnect.rateshop.service.revenue.IHistoryRateNextNDaysService;
    import cloud.wconnect.rateshop.service.revenue.IHistoryRatePropertyMappingService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    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.Service;

    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     * 酒店当日的未来N天的历史房价 服务实现类
     * </p>
     *
     * @author lichen
     * @since 2024-03-25
     */
    @Service
    @DS("rate_shop")
    public class HistoryRateNextNDaysServiceImpl extends ServiceImpl<HistoryRateNextNDaysMapper, HistoryRateNextNDays> implements IHistoryRateNextNDaysService, IHistoryRateNextNDaysByRateTypeService {

        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private UpdateLogUtil updateLogUtil;
        @Autowired
        private IHistoryRatePropertyMappingService historyRatePropertyMappingService;
        @Autowired
        private BeanMapper beanMapper;

        //当前类对应的房价类型
        public static final int EARLY_BIRD_RATE = RateTypeEnum.EARLY_BIRD_RATE.getRateType();

        /**
         * 是否支持
         *
         * @param rateType 房价类型 1:早鸟价 2:BAR房价
         * @return java.lang.Boolean
         * @author lichen
         * @date 2024/5/15
         */
        @Override
        public Boolean supports(int rateType) {
            return rateType == EARLY_BIRD_RATE;
        }

        /**
         * 保存未来14天的早鸟价
         *
         * @param hotelId           酒店ID
         * @param currencyId        币种ID
         * @param queryDate         采集房价的时刻
         * @param dayHotelPriceDTOS 房价数据集合
         * @param propertyHotels    酒店关联的资产
         * @param pos               销售地区
         * @return java.lang.Long 未来14天房价表的数据ID
         * @author lichen
         * @date 2024/5/15
         */
        @Override
        public Long save(Integer hotelId, Integer currencyId, Long queryDate, List<DayHotelPriceDTO> dayHotelPriceDTOS, List<PropertyHotel> propertyHotels, String pos) {
            //版本号(系统默认时区下的yyyyMMdd)
            Integer version = Integer.valueOf(DateTimeUtils.timestampToStr(queryDate, BaseConstant.DATE_FORMAT_YYYYMMDD, null));
            HistoryRateNextNDays oriHistoryRateNextNDays = get(version, hotelId, currencyId, pos);
            if (Objects.isNull(oriHistoryRateNextNDays)) {
                //updateLog
                String updateLogStr = JsonUtils.convertObjListToString(objectMapper, Lists.newArrayList(new UpdateLog(0, DateTimeUtils.nowTimestamp())));
                HistoryRateNextNDays insertEntity = new HistoryRateNextNDays().setHotelId(hotelId)
                        .setCurrencyId(currencyId).setVersion(version)
                        .setData(JsonUtils.convertObjListToString(objectMapper, dayHotelPriceDTOS))
                        .setPos(pos)
                        .setUpdateLog(updateLogStr);
                //保存房价
                save(insertEntity);
                return insertEntity.getId();
            } else {
                //原房价
                List<DayHotelPriceDTO> oriHistoryRateNextNDaysDataList = JsonUtils.convertStringToList(objectMapper, oriHistoryRateNextNDays.getData(),
                        DayHotelPriceDTO.class);
                Map<String, DayHotelPriceDTO> key2OriDayHotelPriceDTO = oriHistoryRateNextNDaysDataList.stream()
                        .collect(Collectors.toMap(DayHotelPriceDTO::generateKey, Function.identity()));
                Set<String> oriKey = key2OriDayHotelPriceDTO.keySet();

                //最新的房价
                Map<String, DayHotelPriceDTO> key2LatestDayHotelPriceDTO = dayHotelPriceDTOS.stream()
                        .collect(Collectors.toMap(DayHotelPriceDTO::generateKey, Function.identity()));
                Set<String> latestKey = key2LatestDayHotelPriceDTO.keySet();

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

                List<DayHotelPriceDTO> historyRateNextNDaysDataListByUpdate = Lists.newArrayListWithCapacity(oriHistoryRateNextNDaysDataList.size());
                for (String key : allKey) {
                    DayHotelPriceDTO latest = key2LatestDayHotelPriceDTO.get(key);
                    if (Objects.nonNull(latest)) {
                        historyRateNextNDaysDataListByUpdate.add(latest);
                        continue;
                    }
                    historyRateNextNDaysDataListByUpdate.add(key2OriDayHotelPriceDTO.get(key));
                }
                //更新房价
                updateById(new HistoryRateNextNDays()
                        .setId(oriHistoryRateNextNDays.getId())
                        .setData(JsonUtils.convertObjListToString(objectMapper, historyRateNextNDaysDataListByUpdate))
                        .setUpdateLog(updateLogUtil.append(oriHistoryRateNextNDays.getUpdateLog(), 0)));
                return oriHistoryRateNextNDays.getId();
            }
        }

        /**
         * 查询指定版本号指定酒店指定币种的房价信息
         *
         * @param version    版本号
         * @param hotelId    酒店ID
         * @param currencyId 币种ID
         * @param pos        销售地区
         * @return cloud.wconnect.rateshop.entity.revenue.HistoryRateNextNDays
         * @author lichen
         * @date 2024/3/25
         */
        @Override
        public HistoryRateNextNDays get(Integer version, Integer hotelId, Integer currencyId, String pos) {
            return getOne(new LambdaQueryWrapper<HistoryRateNextNDays>().eq(HistoryRateNextNDays::getVersion, version)
                    .eq(HistoryRateNextNDays::getHotelId, hotelId)
                    .eq(HistoryRateNextNDays::getCurrencyId, currencyId)
                    .eq(HistoryRateNextNDays::getPos, pos));
        }

        /**
         * 查询酒店在 指定资产的指定原点日期，未来14天的早鸟价
         *
         * @param tenantId   租户 id
         * @param propertyId 资产 id
         * @param hotelId    酒店 id
         * @param targetDate 指定日期(yyyyMMdd)
         * @param currencyId 币种ID
         * @param pos        销售地区
         * @return cloud.wconnect.rateshop.entity.revenue.HistoryRateNextNDays
         * @author lichen
         * @date 2024/5/16
         **/
        @Override
        public HistoryRateNextNDays getNext14DaysRate(Integer tenantId, Integer propertyId, Integer hotelId, Integer targetDate, Integer currencyId, String pos) {
            List<HistoryRatePropertyMapping> propertyMappings = historyRatePropertyMappingService.list(new QueryWrapper<HistoryRatePropertyMapping>().lambda()
                    .eq(HistoryRatePropertyMapping::getTenantId, tenantId)
                    .eq(HistoryRatePropertyMapping::getPropertyId, propertyId)
                    .eq(HistoryRatePropertyMapping::getHotelId, hotelId)
                    .eq(HistoryRatePropertyMapping::getTargetDate, targetDate)
                    .eq(HistoryRatePropertyMapping::getRateType, EARLY_BIRD_RATE));
            if (CollectionUtils.isEmpty(propertyMappings)) return null;
            List<Long> rate14Ids = propertyMappings.stream().map(HistoryRatePropertyMapping::getRate14Id).toList();

            List<HistoryRateNextNDays> historyRateNextNDays = listByIdAndCurrencyIdAndPos(rate14Ids, currencyId, pos);
            if (CollectionUtils.isEmpty(historyRateNextNDays)) {
                return null;
            } else if (historyRateNextNDays.size() == 1) {
                return historyRateNextNDays.get(0);
            } else {
                Map<Integer, HistoryRateNextNDays> version2HistoryRateNextNDays = historyRateNextNDays.stream()
                        .collect(Collectors.toMap(HistoryRateNextNDays::getVersion, Function.identity()));
                Set<Integer> versions = version2HistoryRateNextNDays.keySet();
                //所有的房价数据
                List<MergeDayHotelPriceDTO> mergeDayHotelPriceDTOList = version2HistoryRateNextNDays.values().stream().map(item -> {
                    //把房价数据转化为集合
                    return JsonUtils.convertStringToList(objectMapper, item.getData(), MergeDayHotelPriceDTO.class)
                            .stream().peek(dayHotelPriceDTO -> dayHotelPriceDTO.setVersion(item.getVersion())).toList();
                }).flatMap(Collection::stream).toList();

                //version越大越优先，合并房价数据
                List<DayHotelPriceDTO> dayHotelPriceDTOList = mergeDayHotelPriceDTOList.stream()
                        .collect(Collectors.toMap(MergeDayHotelPriceDTO::generateKey, Function.identity(),
                                MergeDayHotelPriceDTO::merge, TreeMap::new)).values().stream()
                        .map(mergeDTO -> beanMapper.map(mergeDTO, DayHotelPriceDTO.class))
                        .toList();

                //获取最大的版本
                Integer maxVersion = versions.stream().max(Integer::compareTo).get();
                HistoryRateNextNDays historyRateNextNDaysByMaxVersion = version2HistoryRateNextNDays.get(maxVersion);
                historyRateNextNDaysByMaxVersion.setData(JsonUtils.convertObjListToString(objectMapper, dayHotelPriceDTOList));
                return historyRateNextNDaysByMaxVersion;
            }
        }

        /**
         * 根据ID、币种、销售地区查询酒店的房价
         *
         * @param rate14Ids  主键ID
         * @param currencyId 币种
         * @param pos        销售地区
         * @return java.util.List<cloud.wconnect.rateshop.entity.revenue.HistoryBARRateNextNDays>
         * @author lichen
         * @date 2024/7/11
         */
        private List<HistoryRateNextNDays> listByIdAndCurrencyIdAndPos(List<Long> rate14Ids, Integer currencyId, String pos) {
            return list(new LambdaQueryWrapper<HistoryRateNextNDays>().in(HistoryRateNextNDays::getId, rate14Ids)
                    .eq(HistoryRateNextNDays::getCurrencyId, currencyId)
                    .eq(HistoryRateNextNDays::getPos, pos));
        }

    }