# RevenueHandlerCurrentDataServiceImpl.java

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

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.pojo.dto.internationalization.InternationalizationNumberDTO;
    import cloud.wconnect.commonlib.redis.PropertyI18nRedisService;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.InternationalizationUtils;
    import cloud.wconnect.rateshop.entity.revenue.RevenueHurdlePoint;
    import cloud.wconnect.rateshop.pojo.dto.rateshop.LatestHotelPriceDTO;
    import cloud.wconnect.rateshop.pojo.dto.revenue.*;
    import cloud.wconnect.rateshop.pojo.query.revenue.RevenueDetailQuery;
    import cloud.wconnect.rateshop.service.revenue.*;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.google.common.collect.Lists;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.collections.CollectionUtils;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.math.BigDecimal;
    import java.math.RoundingMode;
    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.stream.Collectors;

    /**
     * @author: yyt
     * 2024-05-08
     */
    @Service
    @DS("rate_shop")
    @Slf4j
    public class RevenueHandlerCurrentDataServiceImpl implements IRevenueHandlerService {

        @Autowired
        private IRevenueOtbRevenueService revenueOtbRevenueService;
        @Autowired
        private IRevenueBudgetOtbService revenueBudgetOtbService;
        @Autowired
        private IRevenueSegmentService revenueSegmentService;
        @Autowired
        private IRevenueTransientGroupService revenueTransientGroupService;
        @Autowired
        private IRevenueTransientGroupLyService revenueTransientGroupLyService;
        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private IRevenueOtbRevenueLastYearService revenueOtbRevenueLastYearService;
        @Autowired
        private IRevenueCustomersService revenueCustomersService;
        @Autowired
        private IRevenueHurdlePointService revenueHurdlePointService;
        @Autowired
        private IRevenueService revenueService;
        @Autowired
        private INext14DaysService next14DaysService;
        @Autowired
        private RedissonClient redissonClient;
        /**
         * redis key
         */
        public static final String UPDATE_RATE_REDIS_KEY = "wconnect:revenue:update-current-date-rate";

        /**
         * 检索数据
         *
         * @param propertyDTO 资产
         * @param date       指定日期
         * @param query      查询条件
         * @return revenue 数据
         * @author yyt
         * @date 2024/5/8
         **/
        @Override
        public RevenueDTO getDetail(PropertyDTO propertyDTO, Long date, RevenueDetailQuery query) {
            Integer propertyId = propertyDTO.getId();
            String timezone = propertyDTO.getTimezone();
            List<Long> dates = getCompareWithDates(timezone, date);
            Long targetDate = dates.get(0);
            Long yesterday = dates.get(1);
            OtbRevenueDTO otb = revenueOtbRevenueService.getOtbRevenueDTO(propertyDTO, targetDate, yesterday);
            BudgetDTO budget = revenueBudgetOtbService.getBudgetDTO(propertyDTO, date);

            InternationalizationNumberDTO number = propertyDTO.getInternationalization().getNumber();
            int dataPrecision = BaseConstant.DATA_PRECISION;
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(number.getCalculationRule());

            // achievement = otb revenue / budget otb revenue
            BigDecimal otbRevenueValue = otb.getOtbRevenue();
            BigDecimal budgetOtbRevenueValue = budget.getRoomRevenue();
            boolean validOtbRevenueAndBudget = (otbRevenueValue != null && otbRevenueValue.signum() >= 0)
                    && (budgetOtbRevenueValue != null && budgetOtbRevenueValue.signum() > 0);
            if (validOtbRevenueAndBudget) {
                otb.setOtbAchievement(otbRevenueValue.divide(budgetOtbRevenueValue, dataPrecision, roundingMode).multiply(BigDecimal.valueOf(100)));
            }

            List<SegmentDTO> segments = revenueSegmentService.listSegmentDTO(propertyDTO, targetDate, yesterday);
            List<TransientAndGroupDTO> transientAndGroups = revenueTransientGroupService.listTransientAndGroupDTO(propertyDTO, targetDate, yesterday);
            // 设置 hurdler point
            setHurdlePoint(propertyId, targetDate, transientAndGroups);

            // 设置 MEPS Group
            revenueService.setMEPSGroup(propertyDTO, targetDate, transientAndGroups);

            // 查询去年同期的 BAR 房价
            ZonedDateTime zonedDateTime = DateTimeUtils.toZonedDateTime(date, timezone);
            ZonedDateTime lastYearDate = DateTimeUtils.getSameDateLastYear(zonedDateTime.toLocalDate()).atStartOfDay(ZoneId.of(timezone));
            ZonedDateTime lastYearNextDate = lastYearDate.plusDays(13);
            Long startDate = Long.valueOf(lastYearDate.format(DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD)));
            Long endDate = Long.valueOf(lastYearNextDate.format(DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD)));
            List<LatestHotelPriceDTO> latestHotelPriceDTOS = revenueService.listBARRate(propertyId, startDate, endDate, query);
            // 获取 Transient And Group Last Year
            List<TransientAndGroupLyDTO> transientAndGroupLies = revenueTransientGroupLyService.listTransientAndGroupLyDTO(propertyDTO, date, latestHotelPriceDTOS);

            LastYearOtbDTO lastYearOtbDTO = revenueOtbRevenueLastYearService.getLastYearOtbDTO(propertyDTO, date);

            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);
            String dateStr = DateTimeUtils.toZonedDateTime(date, timezone).format(dateTimeFormatter);
            CustomersDTO customersDTO = revenueCustomersService.getCustomersDTO(propertyId, Integer.parseInt(dateStr));

            return new RevenueDTO(otb, budget, segments, transientAndGroups, transientAndGroupLies, lastYearOtbDTO, customersDTO);
        }

        /**
         * 获取比对日期
         *
         * @param timezone   时区
         * @param targetDate 目标日期
         * @return 比对日期
         * @author yyt
         * @date 2023/10/27
         */
        private List<Long> getCompareWithDates(String timezone, long targetDate) {
            return Lists.newArrayList(targetDate, DateTimeUtils.toZonedDateTime(targetDate, timezone).minusDays(1).toEpochSecond());
        }

        /**
         * 设置 Hurdle Point
         *
         * @param propertyId         资产 id
         * @param theDate            当前日期
         * @param transientAndGroups transientAndGroups
         * @author yyt
         * @date 2024/3/25
         **/
        private void setHurdlePoint(Integer propertyId, Long theDate, List<TransientAndGroupDTO> transientAndGroups) {
            Map<Long, BigDecimal> hurdlePointByDate = revenueHurdlePointService.list(new QueryWrapper<RevenueHurdlePoint>()
                            .lambda()
                            .eq(RevenueHurdlePoint::getPropertyId, propertyId)
                            .eq(RevenueHurdlePoint::getTheDate, theDate)).stream()
                    .filter(item -> Objects.nonNull(item.getHurdlePoint()))
                    .collect(Collectors.toMap(RevenueHurdlePoint::getTargetDate, RevenueHurdlePoint::getHurdlePoint));
            transientAndGroups.forEach(item -> item.setHurdlePoint(hurdlePointByDate.get(item.getTargetDate())));
        }

        /**
         * 保存数据
         *
         * @param propertyDTO 资产
         * @param date        指定日期
         * @param revenueDTO  revenue
         * @param userId      操作人
         * @author yyt
         * @date 2024/5/8
         **/
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void save(PropertyDTO propertyDTO, Long date, RevenueDTO revenueDTO, int userId) {
            // 保存 otb revenue
            OtbRevenueDTO otb = revenueDTO.getOtb();
            if (Objects.nonNull(otb)) {
                revenueOtbRevenueService.save(propertyDTO, date, otb, userId);
            }

            // 保存 budget
            BudgetDTO budget = revenueDTO.getBudget();
            if (Objects.nonNull(budget)) {
                revenueBudgetOtbService.save(propertyDTO, date, budget, userId);
            }

            // 保存 segment
            List<SegmentDTO> segments = revenueDTO.getSegments();
            if (CollectionUtils.isNotEmpty(segments)) {
                revenueSegmentService.save(propertyDTO, date, segments, userId);
            }

            // 保存 transient and group
            List<TransientAndGroupDTO> transientAndGroups = revenueDTO.getTransientAndGroup();
            if (CollectionUtils.isNotEmpty(transientAndGroups)) {
                revenueTransientGroupService.save(propertyDTO, date, transientAndGroups, userId);
            }

            // 保存 transient and group last year
            List<TransientAndGroupLyDTO> transientAndGroupLies = revenueDTO.getTransientAndGroupLy();
            if (CollectionUtils.isNotEmpty(transientAndGroupLies)) {
                revenueTransientGroupLyService.save(propertyDTO, date, transientAndGroupLies, userId);
            }

            // 保存 last year otb revenue
            LastYearOtbDTO lastYearOtbDTO = revenueDTO.getLastYearOtb();
            if (Objects.nonNull(lastYearOtbDTO)) {
                ZonedDateTime zonedDateTime = DateTimeUtils.toZonedDateTime(date, propertyDTO.getTimezone());
                revenueOtbRevenueLastYearService.save(propertyDTO, zonedDateTime.getYear() - 1, zonedDateTime.getMonthValue(), lastYearOtbDTO, userId);
            }

            // 保存 customs
            CustomersDTO customers = revenueDTO.getCustomers();
            if (Objects.nonNull(customers)) {
                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(BaseConstant.DATE_FORMAT_YYYYMMDD);
                String dateStr = DateTimeUtils.toZonedDateTime(date, propertyDTO.getTimezone()).format(dateTimeFormatter);
                revenueCustomersService.saveOrUpdate(propertyDTO.getId(), Integer.parseInt(dateStr), customers, userId);
            }
        }

        /**
         * 修改房价和 hurdle point 内容
         *
         * @param propertyDTO                  资产
         * @param date                         指定日期（YYYY-MM-DD)
         * @param updateHurdlerPointAndRateDTO 修改内容
         * @param userId                       操作人
         * @param properties                   当前操作人所属资产
         * @author yyt
         * @date 2024/5/23
         **/
        @Override
        public void updateNext14DaysData(PropertyDTO propertyDTO, String date, UpdateHurdlerPointAndRateDTO updateHurdlerPointAndRateDTO,
                                         int userId, List<Integer> properties) {
            RLock lock = redissonClient.getLock(UPDATE_RATE_REDIS_KEY);
            if (lock.tryLock()) {
                try {
                    //执行业务逻辑
                    next14DaysService.updateHistoryDateNext14DaysData(propertyDTO, date, updateHurdlerPointAndRateDTO, userId, properties);
                } finally {
                    lock.unlock();
                }
            } else {
                //休眠300ms
                try {
                    Thread.sleep(300);
                } catch (InterruptedException exception) {
                    log.error(exception.getMessage(), exception);
                }
                //递归执行
                updateNext14DaysData(propertyDTO, date, updateHurdlerPointAndRateDTO, userId, properties);
            }
        }

    }