# RevenueTransientGroupLyServiceImpl.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.RevenueTransientGroupLy;
    import cloud.wconnect.rateshop.mapper.revenue.RevenueTransientGroupLyMapper;
    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.IRevenueTransientGroupLyService;
    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 2023-10-26
     */
    @Service
    @DS("rate_shop")
    public class RevenueTransientGroupLyServiceImpl extends ServiceImpl<RevenueTransientGroupLyMapper, RevenueTransientGroupLy> implements IRevenueTransientGroupLyService {

        @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 2023/10/28
         */
        @Override
        public List<RevenueTransientGroupLy> listLastYear(PropertyDTO propertyDTO, long date) {
            // 获取目标日期去年同期 ~ 未来 14 天数据
            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);
            Integer propertyId = propertyDTO.getId();
            return list(new QueryWrapper<RevenueTransientGroupLy>().lambda()
                    .eq(RevenueTransientGroupLy::getPropertyId, propertyId)
                    .eq(RevenueTransientGroupLy::getTheDate, date)
                    .between(RevenueTransientGroupLy::getTargetDate, lastYearDate.toEpochSecond(), lastYearNextDate.toEpochSecond()));
        }

        /**
         * 获取 Transient And Group Last Year
         *
         * @param propertyDTO          资产
         * @param date                 日期
         * @param latestHotelPriceDTOS 最低房价集合
         * @return Transient And Group Last Year
         * @author yyt
         * @date 2023/10/27
         */
        @Override
        public List<TransientAndGroupLyDTO> listTransientAndGroupLyDTO(PropertyDTO propertyDTO, long date, List<LatestHotelPriceDTO> latestHotelPriceDTOS) {
            //采集的房价数据
            Integer propertyId = propertyDTO.getId();
            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<RevenueTransientGroupLy> transientGroupLies = listLastYear(propertyDTO, date);
            Map<Long, RevenueTransientGroupLy> transientGroupLyByTargetDate = transientGroupLies.stream()
                    .collect(Collectors.toMap(RevenueTransientGroupLy::getTargetDate, v -> v));

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

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

            // 目标日期去年同期 ~ 未来 14 天
            List<TransientAndGroupLyDTO> transientAndGroupLyDTOList = Lists.newArrayListWithExpectedSize(14);
            for (ZonedDateTime startDate = lastYearDate; !startDate.isAfter(lastYearNextDate); startDate = startDate.plusDays(1)) {
                //手动维护的数据
                RevenueTransientGroupLy transientGroupLy = transientGroupLyByTargetDate.getOrDefault(startDate.toEpochSecond(),
                        new RevenueTransientGroupLy().setTheDate(date).setTargetDate(startDate.toEpochSecond()));

                //自动导入的数据
                RevenueHistoryAndForecast historyAndForecast = historyAndForecastByTargetDate.getOrDefault(startDate.toEpochSecond(),
                        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);
                // 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<Long, RevenueTransientGroupLy> transientGroupLyByTargetDate = listLastYear(propertyDTO, date).stream()
                    .collect(Collectors.toMap(RevenueTransientGroupLy::getTargetDate, v -> v));

            int size = transientAndGroupLies.size();
            List<RevenueTransientGroupLy> inserts = Lists.newArrayListWithExpectedSize(size);
            List<RevenueTransientGroupLy> updates = Lists.newArrayListWithExpectedSize(size);
            transientAndGroupLies.forEach(item->{
                Long targetDate = item.getTargetDate();
                RevenueTransientGroupLy existTransientGroupLy = transientGroupLyByTargetDate.get(targetDate);
                if (Objects.isNull(existTransientGroupLy)) {
                    RevenueTransientGroupLy newTransientGroupLy = new RevenueTransientGroupLy();
                    newTransientGroupLy.setPropertyId(propertyId).setTheDate(date).setTargetDate(targetDate)
                            .setOccPercent(item.getOccPercent()).setTransientRoomNum(item.getTransientRoomNum())
                            .setGroupRoomNum(item.getGroupRoomNum()).setAdr(item.getAdr())
                            .setCreatedBy(userId).setCreatedTime(now).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);
            }
        }

    }