# ScheduleHistoryAndForecastServiceImpl.java

    package cloud.wconnect.workperformance.service.schedule.impl;

    import cloud.wconnect.commonlib.component.BeanMapper;
    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.workperformance.cons.schedule.ScheduleLimitDayCons;
    import cloud.wconnect.workperformance.entity.schedule.ScheduleHistoryAndForecast;
    import cloud.wconnect.workperformance.mapper.schedule.ScheduleHistoryAndForecastMapper;
    import cloud.wconnect.workperformance.pojo.bo.schedule.ScheduleHistoryAndForecastBO;
    import cloud.wconnect.workperformance.pojo.dto.schedule.ScheduleHistoryAndForecastDTO;
    import cloud.wconnect.workperformance.pojo.query.schedule.ScheduleQuery;
    import cloud.wconnect.workperformance.service.schedule.IScheduleHistoryAndForecastService;
    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.math.RoundingMode;
    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * <p>
     * history and forcast 服务实现类
     * </p>
     *
     * @author yyt
     * @since 2023-06-28
     */
    @Service
    @DS("work_performance")
    public class ScheduleHistoryAndForecastServiceImpl extends ServiceImpl<ScheduleHistoryAndForecastMapper, ScheduleHistoryAndForecast> implements IScheduleHistoryAndForecastService {

        @Autowired
        private PropertyI18nRedisService propertyI18nRedisService;
        @Autowired
        private BeanMapper beanMapper;

        /**
         * 处理插入 history and forecast 数据
         *
         * @param property               资产
         * @param historyAndForecastList history and forecast 数据
         * @author yyt
         * @date 2023/7/5
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void handleHistoryAndForecastData(PropertyDTO property, List<ScheduleHistoryAndForecastBO> historyAndForecastList) {
            long nowTime = DateTimeUtils.nowTimestamp();
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ENGLISH);

            InternationalizationNumberDTO number = propertyI18nRedisService.get(property.getId()).getInternationalization().getNumber();
            int dataPrecision = BaseConstant.DATA_PRECISION;
            RoundingMode roundingMode = InternationalizationUtils.getRoundingMode(number.getCalculationRule());

            Integer propertyId = property.getId();
            ZoneId zoneId = ZoneId.of(property.getTimezone());
            baseMapper.insertOrUpdateBatch(historyAndForecastList.stream().map(item ->
                            new ScheduleHistoryAndForecast().setPropertyId(propertyId)
                                    .setTheDate(LocalDate.parse(item.getTargetDate(), dateTimeFormatter).atStartOfDay(zoneId).toEpochSecond())
                                    .setSyncTime(nowTime)
                                    .setTotalOcc(item.getSoldRooms())
                                    .setArrRooms(item.getArrivalRooms())
                                    .setOccPercent(Objects.isNull(item.getOcc()) ? null
                                            : item.getOcc().setScale(dataPrecision, roundingMode))
                                    .setDepRooms(item.getDepartureRooms()))
                    .sorted(Comparator.comparing(ScheduleHistoryAndForecast::getTheDate))
                    .collect(Collectors.toList()));
        }

        /**
         * 批量新增、编辑
         *
         * @param historyAndForecasts history and forecast 数据
         * @author yyt
         * @date 2023/7/8
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void insertOrUpdateBatch(List<ScheduleHistoryAndForecast> historyAndForecasts) {
            if (CollectionUtils.isEmpty(historyAndForecasts)) return;

            historyAndForecasts.sort(Comparator.comparing(ScheduleHistoryAndForecast::getTheDate));

            baseMapper.insertOrUpdateBatch(historyAndForecasts);
        }

        /**
         * 查询 history and forecast 数据
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return history and forecast 数据
         * @author yyt
         * @date 2024/11/21
         **/
        @Override
        public Map<Long, ScheduleHistoryAndForecastDTO> list(Integer propertyId, ScheduleQuery query) {
            return list(new QueryWrapper<ScheduleHistoryAndForecast>().lambda()
                    .eq(ScheduleHistoryAndForecast::getPropertyId, propertyId)
                    .between(ScheduleHistoryAndForecast::getTheDate, query.getStartDate(), query.getEndDate()))
                    .stream().collect(Collectors.toMap(ScheduleHistoryAndForecast::getTheDate,
                            item -> beanMapper.map(item, ScheduleHistoryAndForecastDTO.class)));
        }

        /**
         * 查询 history and forecast 数据
         *
         * @param propertyId    资产 id
         * @param scheduleQuery 查询条件
         * @return history and forecast 数据
         * @author yyt
         * @date 2024/12/31
         **/
        @Override
        public List<ScheduleHistoryAndForecastDTO> listDTO(int propertyId, ScheduleQuery scheduleQuery) {
            Map<Long, ScheduleHistoryAndForecastDTO> historyAndForecastDTOByDate = list(propertyId, scheduleQuery);

            PropertyDTO propertyDTO = propertyI18nRedisService.get(propertyId);
            String timezone = propertyDTO.getTimezone();
            ZonedDateTime startDate = DateTimeUtils.toZonedDateTime(scheduleQuery.getStartDate(), timezone);
            ZonedDateTime endDate = DateTimeUtils.toZonedDateTime(scheduleQuery.getEndDate(), timezone);
            List<ScheduleHistoryAndForecastDTO> historyAndForecastDTOList = Lists.newArrayListWithExpectedSize(ScheduleLimitDayCons.PAGE_SEARCH);
            for (ZonedDateTime date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
                ScheduleHistoryAndForecastDTO historyAndForecastDTO =
                        historyAndForecastDTOByDate.getOrDefault(date.toEpochSecond(), new ScheduleHistoryAndForecastDTO());
                historyAndForecastDTO.setTheDate(date.toEpochSecond());
                historyAndForecastDTOList.add(historyAndForecastDTO);
            }
            return historyAndForecastDTOList;
        }

        /**
         * 更新 history and forecast 数据
         *
         * @param propertyId             资产 id
         * @param historyAndForecastDTOList history and forecast 数据
         * @author yyt
         * @date 2024/12/31
         **/
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void updateHistoryAndForecast(Integer propertyId, List<ScheduleHistoryAndForecastDTO> historyAndForecastDTOList) {
            long nowTime = DateTimeUtils.nowTimestamp();

            List<ScheduleHistoryAndForecast> historyAndForecasts = historyAndForecastDTOList.stream()
                    .map(item -> new ScheduleHistoryAndForecast().setPropertyId(propertyId)
                            .setTheDate(item.getTheDate()).setSyncTime(nowTime).setTotalOcc(item.getTotalOcc())
                            .setArrRooms(item.getArrRooms()).setOccPercent(item.getOccPercent())
                            .setDepRooms(item.getDepRooms())).collect(Collectors.toList());
            insertOrUpdateBatch(historyAndForecasts);
        }

    }