# RevenueTransientGroupLyFutureServiceImpl.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.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.rateshop.entity.revenue.RevenueHistoryAndForecast;
    import cloud.wconnect.rateshop.entity.revenue.RevenueTransientGroupLyFuture;
    import cloud.wconnect.rateshop.mapper.revenue.RevenueTransientGroupLyFutureMapper;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.LatestHotelPriceDTO;
    import cloud.wconnect.rateshop.pojo.dto.revenue.TransientAndGroupLyDTO;
    import cloud.wconnect.rateshop.service.revenue.IRevenueHistoryAndForecastService;
    import cloud.wconnect.rateshop.service.revenue.IRevenueTransientGroupLyFutureService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    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.ZoneId;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * <p>
     *  服务实现类
     * </p>
     *
     * @author yyt
     * @since 2024-05-08
     */
    @Service
    @DS("rate_shop")
    public class RevenueTransientGroupLyFutureServiceImpl extends ServiceImpl<RevenueTransientGroupLyFutureMapper, RevenueTransientGroupLyFuture> implements IRevenueTransientGroupLyFutureService {

        @Autowired
        private BeanMapper beanMapper;
        @Autowired
        private IRevenueHistoryAndForecastService revenueHistoryAndForecastService;

        /**
         * 获取目标日期去年的 Transient And Group Last Year
         *
         * @param propertyDTO 资产
         * @param date        日期
         * @return Transient And Group Last Year
         * @author yyt
         * @date 2024/5/11
         */
        @Override
        public List<RevenueTransientGroupLyFuture> listLastYear(PropertyDTO propertyDTO, long date) {
            // 获取目标日期去年同期 ~ 未来 14 天数据
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);
            String timezone = propertyDTO.getTimezone();
            ZonedDateTime zonedDateTime = DateTimeUtils.toZonedDateTime(date, timezone);
            ZonedDateTime lastYearDate = DateTimeUtils.getSameDateLastYear(zonedDateTime.toLocalDate()).atStartOfDay(ZoneId.of(timezone));
            ZonedDateTime lastYearNextDate = lastYearDate.plusDays(13);
            return list(new QueryWrapper<RevenueTransientGroupLyFuture>().lambda()
                    .eq(RevenueTransientGroupLyFuture::getPropertyId, propertyDTO.getId())
                    .between(RevenueTransientGroupLyFuture::getTargetDate, Integer.parseInt(lastYearDate.format(dateTimeFormatter)),
                            Integer.parseInt(lastYearNextDate.format(dateTimeFormatter))));
        }

        /**
         * 获取 Transient And Group Last Year
         *
         * @param propertyDTO          资产
         * @param date                 日期
         * @param latestHotelPriceDTOS 采集房价
         * @return Transient And Group Last Year
         * @author yyt
         * @date 2024/5/11
         */
        @Override
        public List<TransientAndGroupLyDTO> listTransientAndGroupLyDTO(PropertyDTO propertyDTO, Long date, List<LatestHotelPriceDTO> latestHotelPriceDTOS) {
            //采集的房价数据
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYY_MM_DD);
            String timezone = propertyDTO.getTimezone();
            ZoneId zoneId = ZoneId.of(timezone);
            Map<Long, LatestHotelPriceDTO> checkInDate2LatestHotelPriceDTO = latestHotelPriceDTOS.stream().collect(Collectors.toMap(
                    item -> LocalDate.parse(item.getDate(), dateTimeFormatter).atStartOfDay(zoneId).toEpochSecond(),
                    Function.identity()));

            // 获取目标日期去年同期 ~ 未来 14 天数据
            List<RevenueTransientGroupLyFuture> transientGroupLies = listLastYear(propertyDTO, date);

            Map<Integer, RevenueTransientGroupLyFuture> transientGroupLyByTargetDate = transientGroupLies.stream()
                    .collect(Collectors.toMap(RevenueTransientGroupLyFuture::getTargetDate, v -> v));

            // 目标日期去年同期 ~ 未来 14 天
            DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);
            ZonedDateTime zonedDateTime = DateTimeUtils.toZonedDateTime(date, timezone);
            ZonedDateTime lastYearDate = DateTimeUtils.getSameDateLastYear(zonedDateTime.toLocalDate()).atStartOfDay(ZoneId.of(timezone));
            ZonedDateTime lastYearNextDate = lastYearDate.plusDays(13);

            // 获取目标日期去年同期 ~ 未来 14 天，自动导入的数据
            Map<Integer, RevenueHistoryAndForecast> historyAndForecastByTargetDate = revenueHistoryAndForecastService.listByDate(propertyDTO.getId(),
                            DateTimeUtils.parseZonedDateTimeToStr(lastYearDate, BaseConstant.DATE_FORMAT_YYYYMMDD),
                            DateTimeUtils.parseZonedDateTimeToStr(lastYearNextDate, BaseConstant.DATE_FORMAT_YYYYMMDD))
                    .stream().collect(Collectors.toMap(RevenueHistoryAndForecast::getTargetDate, Function.identity()));

            List<TransientAndGroupLyDTO> transientAndGroupLyDTOList = Lists.newArrayListWithExpectedSize(14);
            for (ZonedDateTime startDate = lastYearDate; !startDate.isAfter(lastYearNextDate); startDate = startDate.plusDays(1)) {
                int startDateFormatter = Integer.parseInt(startDate.format(dateTimeFormatter2));
                // 获取手动维护的数据
                RevenueTransientGroupLyFuture transientGroupLy = transientGroupLyByTargetDate.getOrDefault(startDateFormatter,
                        new RevenueTransientGroupLyFuture().setTargetDate(startDateFormatter));

                // 自动导入的数据
                RevenueHistoryAndForecast historyAndForecast = historyAndForecastByTargetDate.getOrDefault(startDateFormatter,
                        new RevenueHistoryAndForecast());

                //手动维护的值为空时，用自动导入的填充
                if(Objects.isNull(transientGroupLy.getOccPercent())){
                    transientGroupLy.setOccPercent(historyAndForecast.getOccPercent());
                }
                if(Objects.isNull(transientGroupLy.getTransientRoomNum())){
                    transientGroupLy.setTransientRoomNum(historyAndForecast.getTransientRoomNum());
                }
                if(Objects.isNull(transientGroupLy.getGroupRoomNum())){
                    transientGroupLy.setGroupRoomNum(historyAndForecast.getGroupRoomNum());
                }
                if(Objects.isNull(transientGroupLy.getAdr())){
                    transientGroupLy.setAdr(historyAndForecast.getAdr());
                }

                TransientAndGroupLyDTO transientAndGroupLyDTO = beanMapper.map(transientGroupLy, TransientAndGroupLyDTO.class);
                transientAndGroupLyDTO.setTargetDate(startDate.toEpochSecond());

                // total rooms sold = transient room num + group room num
                Integer totalRoomsSold = transientAndGroupLyDTO.getTransientRoomNum();
                Integer groupRoomNum = transientAndGroupLyDTO.getGroupRoomNum();
                if (Objects.nonNull(groupRoomNum)) {
                    totalRoomsSold = Objects.isNull(totalRoomsSold) ? groupRoomNum : totalRoomsSold + groupRoomNum;
                }

                transientAndGroupLyDTO.setTotalRoomSold(totalRoomsSold);

                //设置采集的房价
                LatestHotelPriceDTO latestHotelPriceDTO = checkInDate2LatestHotelPriceDTO.get(startDate.toEpochSecond());
                if(Objects.nonNull(latestHotelPriceDTO) && Objects.nonNull(latestHotelPriceDTO.getPrice())){
                    transientAndGroupLyDTO.setRate(latestHotelPriceDTO.getPrice());
                    transientAndGroupLyDTO.setPlatformId(latestHotelPriceDTO.getPlatformId());
                    transientAndGroupLyDTO.setDatasourceId(latestHotelPriceDTO.getDatasourceId());
                    transientAndGroupLyDTO.setOperatorIdByRate(0);
                    transientAndGroupLyDTO.setUpdateDateByRate(latestHotelPriceDTO.getUpdateTime());
                }

                transientAndGroupLyDTOList.add(transientAndGroupLyDTO);
            }
            return transientAndGroupLyDTOList;
        }

        /**
         * 保存 Transient And Group Last Year
         *
         * @param propertyDTO           资产
         * @param date                  日期
         * @param transientAndGroupLies 修改内容
         * @param userId                操作人
         * @author yyt
         * @date 2023/10/28
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void save(PropertyDTO propertyDTO, Long date, List<TransientAndGroupLyDTO> transientAndGroupLies, int userId) {
            Integer propertyId = propertyDTO.getId();
            long now = DateTimeUtils.nowTimestamp();
            Map<Integer, RevenueTransientGroupLyFuture> transientGroupLyByTargetDate = listLastYear(propertyDTO, date).stream()
                    .collect(Collectors.toMap(RevenueTransientGroupLyFuture::getTargetDate, v -> v));

            int size = transientAndGroupLies.size();
            List<RevenueTransientGroupLyFuture> inserts = Lists.newArrayListWithExpectedSize(size);
            List<RevenueTransientGroupLyFuture> updates = Lists.newArrayListWithExpectedSize(size);
            String timezone = propertyDTO.getTimezone();
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);

            transientAndGroupLies.forEach(item -> {
                Integer targetDate = Integer.parseInt(DateTimeUtils.toZonedDateTime(item.getTargetDate(), timezone).format(dateTimeFormatter));
                RevenueTransientGroupLyFuture existTransientGroupLy = transientGroupLyByTargetDate.get(targetDate);
                if (Objects.isNull(existTransientGroupLy)) {
                    RevenueTransientGroupLyFuture newTransientGroupLy = new RevenueTransientGroupLyFuture();
                    newTransientGroupLy.setPropertyId(propertyId).setTargetDate(targetDate)
                            .setOccPercent(item.getOccPercent()).setTransientRoomNum(item.getTransientRoomNum())
                            .setGroupRoomNum(item.getGroupRoomNum()).setAdr(item.getAdr()).setUpdatedBy(userId).setUpdatedTime(now);
                    inserts.add(newTransientGroupLy);
                } else {
                    existTransientGroupLy.setOccPercent(item.getOccPercent()).setTransientRoomNum(item.getTransientRoomNum())
                            .setGroupRoomNum(item.getGroupRoomNum()).setAdr(item.getAdr())
                            .setUpdatedBy(userId).setUpdatedTime(now);
                    updates.add(existTransientGroupLy);
                }
            });
            if (CollectionUtils.isNotEmpty(inserts)) {
                saveBatch(inserts);
            }
            if (CollectionUtils.isNotEmpty(updates)) {
                baseMapper.updateBatch(updates);
            }
        }

    }