# HotelKpiMetricsConfigServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    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.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.commonlib.util.YearMonthUtils;
    import cloud.wconnect.workperformance.cons.hotelkpi.HotelKpiDateTypeCons;
    import cloud.wconnect.workperformance.entity.hotelkpi.HotelKpiMetricsConfig;
    import cloud.wconnect.workperformance.mapper.hotelkpi.HotelKpiMetricsConfigMapper;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.kpi.HotelKpiMetricsItemDTO;
    import cloud.wconnect.workperformance.pojo.dto.hotelkpi.kpi.HotelKpiShowConfigDTO;
    import cloud.wconnect.workperformance.pojo.query.hotelkpi.HotelKpiMetricsItemQuery;
    import cloud.wconnect.workperformance.service.hotelkpi.IHotelKpiMetricsConfigService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import jakarta.annotation.Nullable;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.stereotype.Service;

    import jakarta.annotation.Resource;

    import java.text.MessageFormat;
    import java.time.LocalDate;
    import java.time.YearMonth;
    import java.time.temporal.TemporalAdjusters;
    import java.util.Collections;
    import java.util.List;
    import java.util.Objects;

    /**
     * <p>
     * Hotel KPI Metrics Config 服务实现类
     * </p>
     *
     * @author zc
     * @since 2024-06-24
     */
    @Service
    @DS("work_performance")
    public class HotelKpiMetricsConfigServiceImpl extends ServiceImpl<HotelKpiMetricsConfigMapper, HotelKpiMetricsConfig> implements IHotelKpiMetricsConfigService {

        @Resource
        private BeanMapper beanMapper;
        @Resource
        private ObjectMapper objectMapper;

        /**
         * 获取年内有效的指标数据
         *
         * @param propertyId      资产 id
         * @param dateType        日期类型
         * @param startDate       开始日期
         * @param endDate         结束日期
         * @param judgeShowConfig 是否判断是否显示配置 true: 判断显示配置 false: 不判断显示配置
         * @param version         版本
         * @return HotelKpiMetricsConfig 集合
         * @author zc
         * @date 2024/6/24
         */
        @Override
        public List<HotelKpiMetricsConfig> listEffectiveMetricsOfYear(int propertyId, int dateType, long startDate, long endDate,
                                                                      boolean judgeShowConfig, int version) {
            // 转换日期为所在年的 yyyyMMdd 格式的开始、结束日期
            long startQueryDate = convertToStartDateOfYear(dateType, startDate);
            long endQueryDate = convertToEndDateOfYear(dateType, endDate);
            List<HotelKpiMetricsConfig> metricsConfigList = list(new LambdaQueryWrapper<HotelKpiMetricsConfig>()
                    .le(HotelKpiMetricsConfig::getEnabledDate, endQueryDate)
                    .ge(HotelKpiMetricsConfig::getDisabledDate, startQueryDate)
                    .eq(HotelKpiMetricsConfig::getPropertyId, propertyId)
                    .eq(HotelKpiMetricsConfig::getVersion, version));
            if (judgeShowConfig) {
                return metricsConfigList.stream().filter(metricsConfig ->
                        checkShowByShowConfig(metricsConfig, dateType)).toList();
            }
            return metricsConfigList;
        }

        /**
         * 获取年内有效的指标数据
         *
         * @param propertyIds 资产 id 集合
         * @param dateType    日期类型
         * @param date        日期
         * @return HotelKpiMetricsConfig 集合
         * @author zc
         * @date 2024/6/29
         */
        @Override
        public List<HotelKpiMetricsConfig> listEffectiveMetricsOfYearByPropertyIds(List<Integer> propertyIds, int dateType, long date) {
            if (CollectionUtils.isEmpty(propertyIds)) {
                return Collections.emptyList();
            }
            // 转换日期为所在年的 yyyyMMdd 格式的开始、结束日期
            long startQueryDate = convertToStartDateOfYear(dateType, date);
            long endQueryDate = convertToEndDateOfYear(dateType, date);
            return list(new LambdaQueryWrapper<HotelKpiMetricsConfig>()
                    .le(HotelKpiMetricsConfig::getEnabledDate, endQueryDate)
                    .ge(HotelKpiMetricsConfig::getDisabledDate, startQueryDate)
                    .in(HotelKpiMetricsConfig::getPropertyId, propertyIds));
        }

        /**
         * 转换日期为所在年的 yyyyMMdd 格式的开始日期
         *
         * @param dateType 日期类型
         * @param date     日期
         * @return 日期
         * @author zc
         * @date 2024/6/28
         */
        private long convertToStartDateOfYear(int dateType, long date) {
            LocalDate localDate;
            if (dateType == HotelKpiDateTypeCons.WEEKLY) {
                localDate = DateTimeUtils.strToLocalDate(String.valueOf(date),
                        BaseConstant.DATE_FORMAT_YYYYMMDD).withDayOfYear(1);
            } else if (dateType == HotelKpiDateTypeCons.MONTHLY) {
                localDate = YearMonthUtils.getYearMonth(String.valueOf(date)).atDay(1).withDayOfYear(1);
            } else if (dateType == HotelKpiDateTypeCons.QUARTERLY) {
                localDate = YearMonthUtils.getYearMonth(String.valueOf(date)).atDay(1).withDayOfYear(1);
            } else {
                localDate = YearMonth.of((int) date, 1).atDay(1);
            }
            return Long.parseLong(DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYYMMDD));
        }

        /**
         * 转换日期为所在年的 yyyyMMdd 格式的结束日期
         *
         * @param dateType 日期类型
         * @param date     日期
         * @return 日期
         * @author zc
         * @date 2024/6/28
         */
        private long convertToEndDateOfYear(int dateType, long date) {
            LocalDate localDate;
            if (dateType == HotelKpiDateTypeCons.WEEKLY) {
                localDate = DateTimeUtils.strToLocalDate(String.valueOf(date),
                        BaseConstant.DATE_FORMAT_YYYYMMDD).with(TemporalAdjusters.lastDayOfYear());
            } else if (dateType == HotelKpiDateTypeCons.MONTHLY) {
                localDate = YearMonthUtils.getYearMonth(String.valueOf(date)).atDay(1)
                        .with(TemporalAdjusters.lastDayOfYear());
            } else if (dateType == HotelKpiDateTypeCons.QUARTERLY) {
                localDate = YearMonthUtils.getYearMonth(String.valueOf(date)).atDay(1)
                        .with(TemporalAdjusters.lastDayOfYear());
            } else {
                localDate = YearMonth.of((int) date, 1).atDay(1).with(TemporalAdjusters.lastDayOfYear());
            }
            return Long.parseLong(DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYYMMDD));
        }

        /**
         * 获取所有指标数据
         *
         * @param propertyId      资产 id
         * @param dateType        日期类型
         * @param judgeShowConfig 是否判断是否显示配置 true: 判断显示配置 false: 不判断显示配置
         * @param version         版本
         * @return HotelKpiMetricsConfig 集合
         * @author zc
         * @date 2024/6/25
         */
        @Override
        public List<HotelKpiMetricsConfig> listAll(int propertyId, int dateType, boolean judgeShowConfig, @Nullable Integer version) {
            List<HotelKpiMetricsConfig> metricsConfigList = list(new LambdaQueryWrapper<HotelKpiMetricsConfig>()
                    .eq(HotelKpiMetricsConfig::getPropertyId, propertyId)
                    .eq(Objects.nonNull(version), HotelKpiMetricsConfig::getVersion, version));
            if (judgeShowConfig) {
                return metricsConfigList.stream()
                        .filter(metricsConfig -> checkShowByShowConfig(metricsConfig, dateType))
                        .toList();
            }
            return metricsConfigList;
        }

        /**
         * 根据指标日期和类型校验指标配置是否存在
         *
         * @param propertyId      资产 id
         * @param dateType        日期类型
         * @param date            类型
         * @param metricsType     指标类型
         * @param judgeShowConfig 是否判断是否显示配置 true: 判断显示配置 false: 不判断显示配置
         * @param version         版本
         * @return HotelKpiMetricsConfig
         * @author zc
         * @date 2024/6/28
         */
        @Override
        public HotelKpiMetricsConfig checkExistByDateAndMetricsType(int propertyId, int dateType, long date, int metricsType,
                                                                    boolean judgeShowConfig, Integer version) {
            // 转换日期为所在周期的 yyyyMMdd 格式的开始日期
            long startQueryDate = convertToStartDateOfYear(dateType, date);
            long endQueryDate = convertToEndDateOfYear(dateType, date);
            HotelKpiMetricsConfig metricsConfig = getOne(new LambdaQueryWrapper<HotelKpiMetricsConfig>()
                    .eq(HotelKpiMetricsConfig::getMetricsType, metricsType)
                    .le(HotelKpiMetricsConfig::getEnabledDate, endQueryDate)
                    .ge(HotelKpiMetricsConfig::getDisabledDate, startQueryDate)
                    .eq(HotelKpiMetricsConfig::getPropertyId, propertyId)
                    .eq(Objects.nonNull(version), HotelKpiMetricsConfig::getVersion, version));
            if (judgeShowConfig) {
                if (!checkShowByShowConfig(metricsConfig, dateType)) {
                    metricsConfig = null;
                }
            }
            if (Objects.isNull(metricsConfig)) {
                throw new BadRequestException(CommonErrorI18nConstant.DATA_NOT_FOUND,
                        BaseErrorCodeEnum.DATA_VALIDATION_ERROR.getCode(),
                        MessageFormat.format("The metricsConfig is not found; date: {0}, metricsType: {1}",
                                startQueryDate + " ~ " + endQueryDate, metricsType));
            }
            return metricsConfig;
        }

        /**
         * 获取指定日期范围有效的指标
         *
         * @param propertyId 资产 id
         * @param query      查询条件
         * @return HotelKpiMetricsItemDTO 集合
         * @author zc
         * @date 2024/7/17
         */
        @Override
        public List<HotelKpiMetricsItemDTO> listMetricsItem(int propertyId, HotelKpiMetricsItemQuery query) {
            int dateType = query.getDateType();
            long startQueryDate = convertToStartDateOfYear(dateType, query.getDate());
            long endQueryDate = convertToEndDateOfYear(dateType, query.getDate());

            List<HotelKpiMetricsConfig> metricsConfigList;
            if (Objects.nonNull(query.getParentId())) {
                query.setPropertyId(propertyId);
                query.setStartDate(startQueryDate);
                query.setEndDate(endQueryDate);
                metricsConfigList = baseMapper.listMetricsItemByRecursion(query);
            } else {
                metricsConfigList = list(new LambdaQueryWrapper<HotelKpiMetricsConfig>()
                        .le(HotelKpiMetricsConfig::getEnabledDate, endQueryDate)
                        .ge(HotelKpiMetricsConfig::getDisabledDate, startQueryDate)
                        .eq(HotelKpiMetricsConfig::getPropertyId, propertyId)
                        .in(CollectionUtils.isNotEmpty(query.getVersion()),
                                HotelKpiMetricsConfig::getVersion, query.getVersion()));
            }

            // 根据显示配置过滤指标数据
            List<HotelKpiMetricsConfig> effectiveMetricsConfigList = metricsConfigList.stream()
                    .filter(item -> checkShowByShowConfig(item, dateType)).toList();
            if (CollectionUtils.isEmpty(effectiveMetricsConfigList)) {
                return Collections.emptyList();
            }
            return beanMapper.mapList(effectiveMetricsConfigList, HotelKpiMetricsItemDTO.class);
        }

        /**
         * 根据显示配置过滤指标数据
         *
         * @param metricsConfig 指标配置
         * @param dateType      日期类型
         * @return boolean true: 显示，false: 不显示
         * @author zc
         * @date 2024/8/2
         */
        private boolean checkShowByShowConfig(HotelKpiMetricsConfig metricsConfig, int dateType) {
            HotelKpiShowConfigDTO showConfigDTO = JsonUtils.convertStringToObject(objectMapper,
                    metricsConfig.getShowConfig(), HotelKpiShowConfigDTO.class);
            if (dateType == HotelKpiDateTypeCons.WEEKLY) {
                return showConfigDTO.getWeekly();
            } else if (dateType == HotelKpiDateTypeCons.MONTHLY) {
                return showConfigDTO.getMonthly();
            } else if (dateType == HotelKpiDateTypeCons.QUARTERLY) {
                return showConfigDTO.getQuarterly();
            } else {
                return showConfigDTO.getYearly();
            }
        }

        /**
         * 根据资产id和指标类型获取指标配置
         *
         * @param propertyId  资产 id
         * @param metricsType 指标类型
         * @return HotelKpiMetricsConfig
         * @author zc
         * @date 2025/4/7
         */
        @Override
        public HotelKpiMetricsConfig getMetricsConfigByTypeForUpdate(int propertyId, int metricsType) {
            return getOne(new LambdaQueryWrapper<HotelKpiMetricsConfig>()
                    .eq(HotelKpiMetricsConfig::getPropertyId, propertyId)
                    .eq(HotelKpiMetricsConfig::getMetricsType, metricsType)
                    .last("for update"));
        }

        /**
         * 根据资产id和指标类型集合获取指标配置
         *
         * @param propertyId      资产 id
         * @param metricsTypeList 指标类型集合
         * @return HotelKpiMetricsConfig 集合
         * @author zc
         * @date 2025/4/7
         */
        @Override
        public List<HotelKpiMetricsConfig> listMetricsConfigByTypesForUpdate(int propertyId, List<Integer> metricsTypeList) {
            if (CollectionUtils.isEmpty(metricsTypeList)) {
                return Collections.emptyList();
            }
            return list(new LambdaQueryWrapper<HotelKpiMetricsConfig>()
                    .eq(HotelKpiMetricsConfig::getPropertyId, propertyId)
                    .in(HotelKpiMetricsConfig::getMetricsType, metricsTypeList)
                    .last("for update"));
        }

    }