# WeeklyMonthlyRevenueOtherServiceImpl.java

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

    import cloud.wconnect.commonlib.component.BeanMapper;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.JsonUtils;
    import cloud.wconnect.commonlib.util.YearMonthUtils;
    import cloud.wconnect.rateshop.entity.weeklymonthlyrevenue.WeeklyMonthlyRevenueOther;
    import cloud.wconnect.rateshop.mapper.weeklymonthlyrevenue.WeeklyMonthlyRevenueOtherMapper;
    import cloud.wconnect.rateshop.pojo.dto.weeklymonthlyrevenue.WeeklyMonthlyRevenueOtherCopyToDTO;
    import cloud.wconnect.rateshop.pojo.dto.weeklymonthlyrevenue.WeeklyMonthlyRevenueOtherDTO;
    import cloud.wconnect.rateshop.pojo.dto.weeklymonthlyrevenue.WeeklyMonthlyRevenueOtherRevenueDTO;
    import cloud.wconnect.rateshop.service.weeklymonthlyrevenue.IWeeklyMonthlyRevenueOtherService;
    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 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.YearMonth;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Objects;
    import java.util.stream.Collectors;

    /**
     * <p>
     * Weekly Monthly Revenue other 服务实现类
     * </p>
     *
     * @author zc
     * @since 2024-05-13
     */
    @Service
    @DS("rate_shop")
    public class WeeklyMonthlyRevenueOtherServiceImpl extends ServiceImpl<WeeklyMonthlyRevenueOtherMapper, WeeklyMonthlyRevenueOther> implements IWeeklyMonthlyRevenueOtherService {

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

        /**
         * 获取指定年月的 Other Revenue
         *
         * @param propertyId 资产 id
         * @param theMonth   年月（yyyyMM）
         * @param category   分类 （1:"F&B"; 2:"MISC"）
         * @return WeeklyMonthlyRevenueOtherRevenueDTO 集合
         * @author zc
         * @date 2024/5/15
         */
        @Override
        public List<WeeklyMonthlyRevenueOtherRevenueDTO> getOtherRevenue(int propertyId, long theMonth, int category) {
            WeeklyMonthlyRevenueOther revenueOther = getOne(new LambdaQueryWrapper<WeeklyMonthlyRevenueOther>()
                    .eq(WeeklyMonthlyRevenueOther::getTheMonth, theMonth)
                    .eq(WeeklyMonthlyRevenueOther::getPropertyId, propertyId)
                    .eq(WeeklyMonthlyRevenueOther::getCategory, category));
            if (Objects.isNull(revenueOther)) {
                return Collections.emptyList();
            }

            return JsonUtils.convertStringToList(objectMapper, revenueOther.getValues(), WeeklyMonthlyRevenueOtherRevenueDTO.class);
        }

        /**
         * 保存指定年月的 Other Revenue
         *
         * @param propertyId      资产 id
         * @param revenueOtherDTO Other Revenue DTO
         * @param operatorId      操作人 id
         * @author zc
         * @date 2024/5/15
         */
        @Override
        public void saveOtherRevenue(int propertyId, WeeklyMonthlyRevenueOtherDTO revenueOtherDTO, int operatorId) {
            WeeklyMonthlyRevenueOther revenueOther = beanMapper.map(revenueOtherDTO, WeeklyMonthlyRevenueOther.class, entity -> {
                entity.setPropertyId(propertyId);
                entity.setValues(JsonUtils.convertObjListToString(objectMapper, revenueOtherDTO.getOtherRevenues()));
                entity.setUpdatedBy(operatorId);
                entity.setUpdatedTime(DateTimeUtils.nowTimestamp());
            });

            baseMapper.insertOrUpdateByUnique(revenueOther);
        }

        /**
         * 获取指定年份中没有指定分类 Other revenue 数据的月份
         *
         * @param propertyId 资产 id
         * @param theYear    年份（yyyy）
         * @param category   分类 （1:"F&B"; 2:"MISC"）
         * @return 年月（yyyyMM）集合
         * @author zc
         */
        @Override
        public List<Long> listNoDataMonth(int propertyId, long theYear, int category) {
            YearMonth startYearMonth = YearMonth.of((int) theYear, 1);
            YearMonth endYearMonth = startYearMonth.withMonth(12);

            List<Long> existMonthList = list(new LambdaQueryWrapper<WeeklyMonthlyRevenueOther>()
                    .between(WeeklyMonthlyRevenueOther::getTheMonth, YearMonthUtils.parseYearMonthToLong(startYearMonth),
                            YearMonthUtils.parseYearMonthToLong(endYearMonth))
                    .eq((category != WeeklyMonthlyRevenueOther.CATEGORY_ALL), WeeklyMonthlyRevenueOther::getCategory, category)
                    .eq(WeeklyMonthlyRevenueOther::getPropertyId, propertyId)).stream()
                    // 过滤出有 Other Revenue 标题和数值的数据
                    .filter(item -> {
                        List<WeeklyMonthlyRevenueOtherRevenueDTO> otherRevenueDTOList = JsonUtils.convertStringToList(objectMapper,
                                item.getValues(), WeeklyMonthlyRevenueOtherRevenueDTO.class);
                        boolean exist = false;
                        for (WeeklyMonthlyRevenueOtherRevenueDTO otherRevenueDTO : otherRevenueDTOList) {
                            if (CollectionUtils.isNotEmpty(otherRevenueDTO.getValues())) {
                                exist = true;
                                break;
                            }
                        }
                        return exist;
                    }).map(WeeklyMonthlyRevenueOther::getTheMonth).distinct().toList();

            List<Long> noDataMonthList = Lists.newArrayList();
            for (YearMonth currentYearMonth = startYearMonth; !currentYearMonth.isAfter(endYearMonth); currentYearMonth = currentYearMonth.plusMonths(1)) {
                long currentMonth = YearMonthUtils.parseYearMonthToLong(currentYearMonth);
                if (!existMonthList.contains(currentMonth)) {
                    noDataMonthList.add(currentMonth);
                }
            }
            return noDataMonthList;
        }

        /**
         * 校验指定年月的 Other Revenue 是否存在
         *
         * @param propertyId 资产 id
         * @param theMonth   年月（yyyyMM）
         * @param category   分类 （0: "ALL"; 1:"F&B"; 2:"MISC"）
         * @return boolean true：存在 false：不存在
         * @author zc
         * @date 2024/5/15
         */
        @Override
        public boolean checkAlreadyExist(int propertyId, long theMonth, int category) {
            List<WeeklyMonthlyRevenueOther> existList = listByMonthAndCategory(propertyId, theMonth, category).stream()
                    // 过滤出有 Other Revenue 标题和数值的数据
                    .filter(item -> {
                        List<WeeklyMonthlyRevenueOtherRevenueDTO> otherRevenueDTOList = JsonUtils.convertStringToList(objectMapper,
                                item.getValues(), WeeklyMonthlyRevenueOtherRevenueDTO.class);
                        boolean exist = false;
                        for (WeeklyMonthlyRevenueOtherRevenueDTO otherRevenueDTO : otherRevenueDTOList) {
                            if (CollectionUtils.isNotEmpty(otherRevenueDTO.getValues())) {
                                exist = true;
                                break;
                            }
                        }
                        return exist;
                    }).toList();
            return CollectionUtils.isNotEmpty(existList);
        }

        /**
         * 拷贝指定年月 Other Revenue 到指定年月
         *
         * @param propertyId 资产 id
         * @param copyToDTO  Copy To DTO
         * @param operatorId 操作人 id
         * @author zc
         * @date 2024/5/15
         */
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void copyTo(int propertyId, WeeklyMonthlyRevenueOtherCopyToDTO copyToDTO, int operatorId) {
            // 获取克隆源的 Other Revenue 数据
            List<WeeklyMonthlyRevenueOther> originOtherRevenueList = listByMonthAndCategory(propertyId,
                    copyToDTO.getFromMonth(), copyToDTO.getCategory());
            List<WeeklyMonthlyRevenueOther> newOtherRevenueList = originOtherRevenueList.stream().map(item -> {
                List<WeeklyMonthlyRevenueOtherRevenueDTO> otherRevenueDTOList = JsonUtils.convertStringToList(objectMapper,
                        item.getValues(), WeeklyMonthlyRevenueOtherRevenueDTO.class);
                otherRevenueDTOList.forEach(otherRevenueDTO ->
                        otherRevenueDTO.getValues().forEach(otherRevenueValueDTO ->
                                otherRevenueValueDTO.setValue(null)));
                return new WeeklyMonthlyRevenueOther()
                        .setPropertyId(propertyId)
                        .setTheMonth(copyToDTO.getToMonth())
                        .setCategory(item.getCategory())
                        .setValues(JsonUtils.convertObjListToString(objectMapper, otherRevenueDTOList))
                        .setUpdatedBy(operatorId)
                        .setUpdatedTime(DateTimeUtils.nowTimestamp());
            }).collect(Collectors.toList());

            // 批量保存数据
            insertOrUpdateBatchByUnique(newOtherRevenueList);
        }

        /**
         * 批量保存数据
         *
         * @param list 数据
         * @author zc
         * @date 2026/1/27
         */
        private void insertOrUpdateBatchByUnique(List<WeeklyMonthlyRevenueOther> list) {
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            // 按唯一索引字段排序，避免死锁
            list.sort(Comparator.comparing(WeeklyMonthlyRevenueOther::getPropertyId)
                    .thenComparing(WeeklyMonthlyRevenueOther::getTheMonth)
                    .thenComparing(WeeklyMonthlyRevenueOther::getCategory));

            baseMapper.insertOrUpdateBatchByUnique(list);
        }

        /**
         * 根据年月和分类查询指定年月的 Other Revenue
         *
         * @param propertyId 资产 id
         * @param theMonth   年月（yyyyMM）
         * @param category   分类 （0: "ALL"; 1:"F&B"; 2:"MISC"）
         * @return WeeklyMonthlyRevenueOther 集合
         * @author zc
         * @date 2024/5/15
         */
        private List<WeeklyMonthlyRevenueOther> listByMonthAndCategory(int propertyId, long theMonth, int category) {
            return list(new LambdaQueryWrapper<WeeklyMonthlyRevenueOther>()
                    .eq(WeeklyMonthlyRevenueOther::getTheMonth, theMonth)
                    .eq((category != WeeklyMonthlyRevenueOther.CATEGORY_ALL), WeeklyMonthlyRevenueOther::getCategory, category)
                    .eq(WeeklyMonthlyRevenueOther::getPropertyId, propertyId));
        }
    }