# HotelKpiManagerFlashServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.workperformance.entity.hotelkpi.HotelKpiManagerFlash;
    import cloud.wconnect.workperformance.mapper.hotelkpi.HotelKpiManagerFlashMapper;
    import cloud.wconnect.workperformance.pojo.bo.hotelkpi.HotelKpiManagerFlashBO;
    import cloud.wconnect.workperformance.service.hotelkpi.IHotelKpiManagerFlashService;
    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.google.common.collect.Lists;
    import jakarta.annotation.Resource;
    import org.apache.commons.collections.CollectionUtils;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.time.DayOfWeek;
    import java.time.LocalDate;
    import java.time.YearMonth;
    import java.time.temporal.TemporalAdjusters;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;

    /**
     * <p>
     * Hotel KPI Manager Flash 服务类
     * </p>
     *
     * @author zc
     * @since 2024-07-13
     */
    @Service
    @DS("work_performance")
    public class HotelKpiManagerFlashServiceImpl extends ServiceImpl<HotelKpiManagerFlashMapper, HotelKpiManagerFlash> implements IHotelKpiManagerFlashService {

        @Resource
        private BeanMapper beanMapper;

        /**
         * 保存数据
         *
         * @param propertyId 资产 id
         * @param date       日期（yyyyMMdd）
         * @param boList     数据集合
         * @author zc
         * @date 2025/7/24
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void insertOrUpdateBatch(int propertyId, long date, List<HotelKpiManagerFlashBO> boList) {
            if (CollectionUtils.isEmpty(boList)) {
                return;
            }
            List<HotelKpiManagerFlash> managerFlashList = beanMapper.mapList(boList, HotelKpiManagerFlash.class,
                    (bo, entity) -> {
                        entity.setPropertyId(propertyId);
                        entity.setDate(date);
                    });

            // 按唯一索引字段排序，避免死锁
            managerFlashList.sort(Comparator.comparing(HotelKpiManagerFlash::getPropertyId)
                    .thenComparing(HotelKpiManagerFlash::getDate)
                    .thenComparing(HotelKpiManagerFlash::getCategory));

            baseMapper.insertOrUpdateBatchByUnique(managerFlashList);
        }

        /**
         * 根据日期和分类查询数据
         *
         * @param propertyId    资产 id
         * @param queryDateList 查询日期列表(yyyyMMdd)
         * @param categoryList  分类集合
         * @return 数据集合
         * @author zc
         * @date 2025/8/29
         */
        @Override
        public List<HotelKpiManagerFlash> listByDatesAndCategories(int propertyId, List<Long> queryDateList, List<String> categoryList) {
            if (CollectionUtils.isEmpty(queryDateList) || CollectionUtils.isEmpty(categoryList)) {
                return Collections.emptyList();
            }
            return list(new LambdaQueryWrapper<HotelKpiManagerFlash>()
                    .eq(HotelKpiManagerFlash::getPropertyId, propertyId)
                    .in(HotelKpiManagerFlash::getDate, queryDateList)
                    .in(HotelKpiManagerFlash::getCategory, categoryList));
        }

        /**
         * 根据日期范围和分类获取月末的 Manager Flash 数据
         *
         * @param propertyId     资产 id
         * @param startYearMonth 开始日期
         * @param endYearMonth   结束日期
         * @param categoryList   分类集合
         * @return HotelKpiManagerFlash 集合
         * @author zc
         * @date 2025/9/12
         */
        @Override
        public List<HotelKpiManagerFlash> listMonthEnd(int propertyId, YearMonth startYearMonth, YearMonth endYearMonth,
                                                       List<String> categoryList) {
            if (CollectionUtils.isEmpty(categoryList)) {
                return Collections.emptyList();
            }
            List<Long> queryDateList = Lists.newArrayList();
            for (YearMonth indexYearMonth = startYearMonth; !indexYearMonth.isAfter(endYearMonth);
                 indexYearMonth = indexYearMonth.plusMonths(1)) {
                long lastDateOfMonth = Long.parseLong(DateTimeUtils.localDateToStr(indexYearMonth.atEndOfMonth(),
                        BaseConstant.DATE_FORMAT_YYYYMMDD));
                queryDateList.add(lastDateOfMonth);
            }
            return listByDatesAndCategories(propertyId, queryDateList, categoryList);
        }

        /**
         * 根据日期范围和分类获取周末的 Manager Flash 数据
         *
         * @param propertyId     资产 id
         * @param startLocalDate 开始日期
         * @param endLocalDate   结束日期
         * @param dayOfWeek      周几
         * @param categoryList   分类集合
         * @return HotelKpiManagerFlash 集合
         * @author zc
         * @date 2025/9/12
         */
        @Override
        public List<HotelKpiManagerFlash> listWeekEnd(int propertyId, LocalDate startLocalDate, LocalDate endLocalDate,
                                                      DayOfWeek dayOfWeek, List<String> categoryList) {
            if (CollectionUtils.isEmpty(categoryList)) {
                return Collections.emptyList();
            }
            List<Long> queryDateList = Lists.newArrayList();
            for (LocalDate indexLocalDate = startLocalDate; !indexLocalDate.isAfter(endLocalDate);
                 indexLocalDate = indexLocalDate.plusDays(7)) {
                long lastDateOfWeek = Long.parseLong(DateTimeUtils.localDateToStr(indexLocalDate.with(TemporalAdjusters
                        .previousOrSame(dayOfWeek)).plusDays(6), BaseConstant.DATE_FORMAT_YYYYMMDD));
                queryDateList.add(lastDateOfWeek);
            }
            return listByDatesAndCategories(propertyId, queryDateList, categoryList);
        }
    }