# RevenueMepsGroupConfigServiceImpl.java

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

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.cons.BaseErrorCodeEnum;
    import cloud.wconnect.commonlib.cons.CommonErrorI18nConstant;
    import cloud.wconnect.commonlib.exception.BadRequestException;
    import cloud.wconnect.commonlib.exception.ForbiddenException;
    import cloud.wconnect.commonlib.pojo.dto.ListPagedDTO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.rateshop.entity.rateshop.GroupPickup;
    import cloud.wconnect.rateshop.entity.revenue.RevenueMepsGroupConfig;
    import cloud.wconnect.rateshop.mapper.revenue.RevenueMepsGroupConfigMapper;
    import cloud.wconnect.rateshop.pojo.dto.revenue.MepsGroupConfigDTO;
    import cloud.wconnect.rateshop.pojo.dto.revenue.Next14DaysMEPSGroupDTO;
    import cloud.wconnect.rateshop.pojo.dto.revenue.SaveMepsGroupConfigDTO;
    import cloud.wconnect.rateshop.pojo.query.revenue.MepsGroupConfigQuery;
    import cloud.wconnect.rateshop.service.rateshop.IGroupPickupService;
    import cloud.wconnect.rateshop.service.revenue.IRevenueMepsGroupConfigService;
    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.plugins.pagination.Page;
    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.time.ZonedDateTime;
    import java.util.*;
    import java.util.stream.Collectors;

    /**
     * <p>
     * MEPS Group 显示规则 服务实现类
     * </p>
     *
     * @author yyt
     * @since 2025-06-10
     */
    @Service
    @DS("rate_shop")
    public class RevenueMepsGroupConfigServiceImpl extends ServiceImpl<RevenueMepsGroupConfigMapper, RevenueMepsGroupConfig> implements IRevenueMepsGroupConfigService {

        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private IGroupPickupService groupPickupService;

        /**
         * 查询配置列表
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return 配置列表
         * @author yyt
         * @date 2025/6/10
         **/
        @Override
        public ListPagedDTO<MepsGroupConfigDTO> listPage(Integer propertyId, MepsGroupConfigQuery query) {
            long total;
            List<RevenueMepsGroupConfig> list;

            // 拿下上一页最后一条版本数据，用于渲染当前页第一条版本数据的结束日期（结束日期为上个版本的开始日期减一天）
            RevenueMepsGroupConfig previousPageLastData = null;

            Integer page = query.getPage();
            Integer size = query.getSize();
            if (Objects.nonNull(page) && Objects.nonNull(size)) {
                Page<RevenueMepsGroupConfig> pageData = page(page, size, propertyId);
                list = pageData.getRecords();
                total = pageData.getTotal();

                if (page > 1) {
                    List<RevenueMepsGroupConfig> lastPage = page(page - 1, size, propertyId).getRecords();
                    if (CollectionUtils.isNotEmpty(lastPage)) {
                        previousPageLastData = lastPage.get(lastPage.size() - 1);
                    }
                }
            } else {
                LambdaQueryWrapper<RevenueMepsGroupConfig> sql = new QueryWrapper<RevenueMepsGroupConfig>().lambda()
                        .eq(RevenueMepsGroupConfig::getPropertyId, propertyId);
                String date = query.getDate();
                if (Objects.nonNull(date)) {
                    sql.eq(RevenueMepsGroupConfig::getDate, Integer.parseInt(date));
                }
                Integer id = query.getId();
                if (Objects.nonNull(id)) {
                    sql.ne(RevenueMepsGroupConfig::getId, id);
                }

                list = list(sql);
                total = list.size();
            }

            List<MepsGroupConfigDTO> dtos = new ArrayList<>();
            for (int i = 0; i < list.size(); i++) {
                RevenueMepsGroupConfig item = list.get(i);
                MepsGroupConfigDTO mepsGroupConfigDTO = new MepsGroupConfigDTO();
                mepsGroupConfigDTO.setId(item.getId());
                mepsGroupConfigDTO.setStartDate(item.getDate());

                if (i == BaseConstant.ZERO) {
                    if (Objects.nonNull(previousPageLastData)) {
                        mepsGroupConfigDTO.setEndDate(formatEndDate(previousPageLastData.getDate()));
                    }
                } else {
                    mepsGroupConfigDTO.setEndDate(formatEndDate(list.get(i - 1).getDate()));
                }

                mepsGroupConfigDTO.setKeyWords(JsonUtils.convertStringToList(objectMapper, item.getKeyWords(), String.class));
                dtos.add(mepsGroupConfigDTO);
            }

            return new ListPagedDTO<>(total, dtos);
        }

        /**
         * 分页
         *
         * @param page       页码
         * @param size       每页显示条数
         * @param propertyId 资产 id
         * @return 分页后的配置列表
         * @author yyt
         * @date 2025/6/11
         **/
        private Page<RevenueMepsGroupConfig> page(Integer page, Integer size, Integer propertyId) {
          return page(new Page<>(page, size), new QueryWrapper<RevenueMepsGroupConfig>().lambda()
                    .eq(RevenueMepsGroupConfig::getPropertyId, propertyId)
                    .orderByDesc(RevenueMepsGroupConfig::getDate));
        }

        /**
         * 格式化结束日期
         *
         * @param endDate 结束日期
         * @return 结束日期
         * @author yyt
         * @date 2025/6/11
         **/
        private Integer formatEndDate(Integer endDate) {
            return Integer.parseInt(DateTimeUtils.localDateToStr(DateTimeUtils.strToLocalDate(endDate.toString(), BaseConstant.DATE_FORMAT_YYYYMMDD)
                    .minusDays(1), BaseConstant.DATE_FORMAT_YYYYMMDD));
        }

        /**
         * 检查重复
         *
         * @param propertyId 资产 idd
         * @param date       指定日期
         * @param id         排除自身
         * @author yyt
         * @date 2025/6/10
         **/
        @Override
        public void checkRepeat(Integer propertyId, int date, Integer id) {
            RevenueMepsGroupConfig existConfig = getOne(new QueryWrapper<RevenueMepsGroupConfig>().lambda()
                    .eq(RevenueMepsGroupConfig::getPropertyId, propertyId)
                    .eq(RevenueMepsGroupConfig::getDate, date)
                    .ne(Objects.nonNull(id), RevenueMepsGroupConfig::getId, id));
            if (Objects.nonNull(existConfig)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_EXISTED,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the config already exists");
            }
        }

        /**
         * 检查存在
         *
         * @param id 配置 id
         * @return 配置
         * @author yyt
         * @date 2025/6/10
         **/
        @Override
        public RevenueMepsGroupConfig checkExist(Integer id) {
            RevenueMepsGroupConfig config = getById(id);
            if (Objects.isNull(config)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(), "the config not found");
            }
            return config;
        }

        /**
         * 校验资产归属
         *
         * @param config     配置
         * @param propertyId 资产 id
         * @author yyt
         * @date 2025/6/11
         **/
        @Override
        public void checkBelong(RevenueMepsGroupConfig config, Integer propertyId) {
            if (!config.getPropertyId().equals(propertyId)) {
                throw new ForbiddenException(BaseErrorCodeEnum.DATA_FORBIDDEN.getI18nCode(),
                        BaseErrorCodeEnum.DATA_FORBIDDEN.getCode(), "the property id parameter is different from " +
                        "the property id of the revenueMepsGroupConfig;propertyId:" + propertyId + ",revenueMepsGroupConfigPropertyId:" +
                        config.getPropertyId() + ",revenueMepsGroupConfigId:" + config.getId());
            }
        }

        /**
         * 保存配置
         *
         * @param propertyId         资产 id
         * @param mepsGroupConfigDTO 配置信息
         * @param userId             操作人
         * @author yyt
         * @date 2025/6/10
         **/
        @Override
        public void save(Integer propertyId, SaveMepsGroupConfigDTO mepsGroupConfigDTO, int userId) {
            save(new RevenueMepsGroupConfig().setPropertyId(propertyId)
                    .setDate(Integer.parseInt(mepsGroupConfigDTO.getDate()))
                    .setKeyWords(JsonUtils.convertObjListToString(objectMapper, mepsGroupConfigDTO.getKeyWords().stream().toList()))
                    .setUpdatedBy(userId)
                    .setUpdatedTime(DateTimeUtils.nowTimestamp()));
        }

        /**
         * 更新配置
         *
         * @param originalConfig     原配置
         * @param mepsGroupConfigDTO 配置信息
         * @param userId             操作人
         * @author yyt
         * @date 2025/6/11
         **/
        @Override
        public void update(RevenueMepsGroupConfig originalConfig, SaveMepsGroupConfigDTO mepsGroupConfigDTO, int userId) {
            updateById(new RevenueMepsGroupConfig()
                    .setId(originalConfig.getId())
                    .setDate(Integer.parseInt(mepsGroupConfigDTO.getDate()))
                    .setKeyWords(JsonUtils.convertObjListToString(objectMapper, mepsGroupConfigDTO.getKeyWords().stream().toList()))
                    .setUpdatedBy(userId)
                    .setUpdatedTime(DateTimeUtils.nowTimestamp()));
        }

        /**
         * 当前资产是否有配置
         *
         * @param propertyId 资产 id
         * @return true；有，false：无
         * @author yyt
         * @date 2025/6/11
         **/
        @Override
        public boolean hasConfig(Integer propertyId) {
            return getOne(new QueryWrapper<RevenueMepsGroupConfig>().lambda()
                    .eq(RevenueMepsGroupConfig::getPropertyId, propertyId)
                    .select(RevenueMepsGroupConfig::getId)
                    .last("limit 1")) != null;
        }

        /**
         * 查询指定日期区间的配置信息
         *
         * @param propertyId 资产 id
         * @param startDate  开始日期（yyyyMMdd）
         * @param endDate    结束日期（yyyyMMdd）
         * @return 配置信息
         * @author yyt
         * @date 2025/6/25
         **/
        @Override
        public List<RevenueMepsGroupConfig> listByDateRange(Integer propertyId, Integer startDate, Integer endDate) {
            return baseMapper.listByDateRange(propertyId, startDate, endDate);
        }

        /**
         * 查询指定日期的配置信息
         *
         * @param propertyId 资产 id
         * @param targetDate 指定日期（yyyyMMdd）
         * @return 配置信息
         * @author yyt
         * @date 2025/6/25
         **/
        @Override
        public RevenueMepsGroupConfig getByDate(Integer propertyId, Integer targetDate) {
            return baseMapper.getByDate(propertyId, targetDate);
        }

        /**
         * 查询指定日期未来 14 天 MEPS Group
         *
         * @param propertyDTO 资产
         * @param targetDate  指定日期
         * @return MEPS Group
         * @author yyt
         * @date 2025/6/11
         **/
        @Override
        public List<Next14DaysMEPSGroupDTO> listNext14DaysMEPSGroup(PropertyDTO propertyDTO, Long targetDate) {
            // 查询指定日期区间+选择日期之前的最新的一条数据
            Integer propertyId = propertyDTO.getId();
            String timezone = propertyDTO.getTimezone();
            ZonedDateTime startDateZoneDateTime = DateTimeUtils.toZonedDateTime(targetDate, timezone);
            ZonedDateTime endDateZoneDateTime = startDateZoneDateTime.plusDays(13);
            List<RevenueMepsGroupConfig> configs = listByDateRange(propertyId,
                    Integer.parseInt(DateTimeUtils.localDateToStr(startDateZoneDateTime.toLocalDate(), BaseConstant.DATE_FORMAT_YYYYMMDD)),
                    Integer.parseInt(DateTimeUtils.localDateToStr(endDateZoneDateTime.toLocalDate(), BaseConstant.DATE_FORMAT_YYYYMMDD)));
            if (CollectionUtils.isEmpty(configs)) return Collections.emptyList();

            // 查询 rate shop 功能指定日期区间的 group pick 数据
            List<GroupPickup> groupPickups = groupPickupService.list(propertyId, startDateZoneDateTime.toEpochSecond(), endDateZoneDateTime.toEpochSecond());
            if (CollectionUtils.isEmpty(groupPickups)) return Collections.emptyList();

            List<Next14DaysMEPSGroupDTO> mepsGroupDTOS = Lists.newArrayListWithExpectedSize(14);
            groupPickups.stream().collect(Collectors.groupingBy(GroupPickup::getTargetDate))
                    .forEach((date, currentDateGroupPickups) -> {
                        // 过滤出适配当前日期的 meps group 配置
                        configs.stream()
                                .filter(config -> DateTimeUtils.dateStrToEpochSecond(config.getDate().toString(), BaseConstant.DATE_FORMAT_YYYYMMDD, timezone) <= date)
                                .max(Comparator.comparing(RevenueMepsGroupConfig::getDate))
                                .ifPresent(config -> {

                                    // 过滤包含关键字的 group pick 数据
                                    List<String> keyWords = JsonUtils.convertStringToList(objectMapper, config.getKeyWords(), String.class)
                                            .stream().map(keyword -> keyword.trim().toLowerCase()).toList();
                                    List<GroupPickup> filteredGroupPickups = currentDateGroupPickups.stream()
                                            .filter(groupPickup -> keyWords.stream().anyMatch(groupPickup.getGroupName().toLowerCase()::contains))
                                            .collect(Collectors.toList());
                                    if (CollectionUtils.isNotEmpty(filteredGroupPickups)) {
                                        Next14DaysMEPSGroupDTO mepsGroupDTO = new Next14DaysMEPSGroupDTO();
                                        mepsGroupDTO.setDate(date);
                                        mepsGroupDTO.setRoomBlocked(filteredGroupPickups.stream().map(GroupPickup::getRoomBlocked).filter(Objects::nonNull).reduce(Integer::sum).orElse(null));
                                        mepsGroupDTO.setRoomPickup(filteredGroupPickups.stream().map(GroupPickup::getRoomPickup).filter(Objects::nonNull).reduce(Integer::sum).orElse(null));
                                        mepsGroupDTOS.add(mepsGroupDTO);
                                    }
                                });
                    });
            return mepsGroupDTOS;
        }

        /**
         * 删除配置
         *
         * @param id 配置 id
         * @author yyt
         * @date 2025/6/11
         **/
        @Override
        public void delete(Integer id) {
            removeById(id);
        }

    }