# OperatingStatisticsReportEnhancerServiceImpl.java

    package cloud.wconnect.workperformance.service.sva.operatingstatistics.impl;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import cloud.wconnect.commonlib.pojo.bo.m3.DailyOperatingStatisticsIndexBO;
    import cloud.wconnect.commonlib.pojo.bo.m3.DailyOperatingStatisticsIndexValueBO;
    import cloud.wconnect.commonlib.pojo.dto.PropertyDTO;
    import cloud.wconnect.commonlib.util.DateTimeUtils;
    import cloud.wconnect.commonlib.util.RedissonLockUtil;
    import cloud.wconnect.workperformance.cons.operatingstatisticsdaily.OperatingStatisticsDatasourceEnum;
    import cloud.wconnect.workperformance.cons.sva.SvaConstant;
    import cloud.wconnect.workperformance.cons.sva.SvaDailyOperatingStatisticsIndexEnum;
    import cloud.wconnect.workperformance.entity.sva.operatingstatistics.SvaOperatingStatisticsOriginal;
    import cloud.wconnect.workperformance.pojo.bo.sva.operatingstatistics.OperatingStatisticsListBO;
    import cloud.wconnect.workperformance.service.sva.operatingstatistics.IOperatingStatisticsReportEnhancerService;
    import cloud.wconnect.workperformance.service.sva.operatingstatistics.IOperatingStatisticsService;
    import cloud.wconnect.workperformance.service.sva.operatingstatistics.ISvaOperatingStatisticsOriginalService;
    import com.baomidou.dynamic.datasource.annotation.DS;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Maps;
    import jakarta.annotation.Resource;
    import org.apache.commons.collections4.CollectionUtils;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.springframework.stereotype.Service;

    import java.text.MessageFormat;
    import java.time.LocalDate;
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;

    /**
     * Operating Statistics报告增强处理类
     *
     * @author lichen
     * @date 2026/4/14
     */
    @Service
    @DS("work_performance")
    public class OperatingStatisticsReportEnhancerServiceImpl implements IOperatingStatisticsReportEnhancerService {

        @Resource
        private RedissonClient redissonClient;
        @Resource
        private ISvaOperatingStatisticsOriginalService svaOperatingStatisticsOriginalService;
        @Resource
        private IOperatingStatisticsService operatingStatisticsService;

        /**
         * 处理Operating Statistics报告数据
         *
         * @param propertyDTO                     资产DTO
         * @param dailyOperatingStatisticsIndexBO Operating Statistics报告数据
         * @author lichen
         * @date 2026/4/14
         */
        @Override
        public void handleOperatingStatisticsOriginByM3Api(PropertyDTO propertyDTO, DailyOperatingStatisticsIndexBO dailyOperatingStatisticsIndexBO) {
            List<DailyOperatingStatisticsIndexValueBO> indexValues = dailyOperatingStatisticsIndexBO.getIndexValues();
            if (CollectionUtils.isEmpty(indexValues)) return;

            // 日期集合
            List<Integer> dates = dailyOperatingStatisticsIndexBO.getIndexValues().stream().map(DailyOperatingStatisticsIndexValueBO::getDate).collect(Collectors.toList());

            // 处理原始数据锁key
            List<String> originKeys = dates.stream().map(date -> MessageFormat.format(SvaConstant.HANDLE_DAILY_OPERATING_STATISTICS_ORIGINAL_KEY,
                    propertyDTO.getId(), date.toString())).toList();

            // 处理Operating statistics原始数据
            RedissonLockUtil.executeMultipleLock(redissonClient, originKeys, 3, 5, () -> {
                // 按日期集合、指定数据源查询
                List<SvaOperatingStatisticsOriginal> svaOperatingStatisticsOriginals = svaOperatingStatisticsOriginalService.listByDatesAndDataType(propertyDTO.getId(),
                        dates, OperatingStatisticsDatasourceEnum.M3_API.getValue());
                Map<Integer, SvaOperatingStatisticsOriginal> date2Original = svaOperatingStatisticsOriginals.stream()
                        .collect(Collectors.toMap(SvaOperatingStatisticsOriginal::getDate, Function.identity()));

                // 指标对应的字段setter方法
                SvaDailyOperatingStatisticsIndexEnum.OperatingStatisticsOriginalSetterBiConsumer setterFunction =
                        SvaDailyOperatingStatisticsIndexEnum.getOperatingStatisticsOriginSetterFunction(dailyOperatingStatisticsIndexBO.getIndexName());

                ArrayList<SvaOperatingStatisticsOriginal> batchList = Lists.newArrayListWithCapacity(indexValues.size());
                long nowTimestamp = DateTimeUtils.nowTimestamp();
                for (DailyOperatingStatisticsIndexValueBO indexValue : indexValues) {
                    SvaOperatingStatisticsOriginal svaOperatingStatisticsOriginal = date2Original.get(indexValue.getDate());

                    if (Objects.isNull(svaOperatingStatisticsOriginal)) {
                        svaOperatingStatisticsOriginal = new SvaOperatingStatisticsOriginal();
                        svaOperatingStatisticsOriginal.setTenantId(propertyDTO.getTenantId());
                        svaOperatingStatisticsOriginal.setPropertyId(propertyDTO.getId());
                        svaOperatingStatisticsOriginal.setDataType(OperatingStatisticsDatasourceEnum.M3_API.getValue());
                        svaOperatingStatisticsOriginal.setDate(indexValue.getDate());
                        svaOperatingStatisticsOriginal.setCreateBy(0);
                        svaOperatingStatisticsOriginal.setCreatedTime(nowTimestamp);
                        svaOperatingStatisticsOriginal.setLastUpdatedBy(0);
                        svaOperatingStatisticsOriginal.setLastUpdatedTime(nowTimestamp);
                    } else {
                        svaOperatingStatisticsOriginal.setLastUpdatedBy(0);
                        svaOperatingStatisticsOriginal.setLastUpdatedTime(nowTimestamp);
                    }

                    // 给指标对应的字段赋值
                    setterFunction.accept(svaOperatingStatisticsOriginal, indexValue.getIndexValue());

                    batchList.add(svaOperatingStatisticsOriginal);
                }

                // 按照唯一索引更新Operating Statistics 原始数据
                svaOperatingStatisticsOriginalService.saveOrUpdateByUnique(batchList);
            });
        }

        /**
         * 处理报告数据
         *
         * @param propertyList         资产
         * @param boList               报告数据 BO
         * @param operatorId           操作人 id
         * @param propertyId2DateRange 待处理数据的日期集合
         * @param dataType             数据源 1:PDF 2:CSV 4:Other
         * @author lichen
         * @date 2025/11/21
         */
        @Override
        public void handleOperatingStatisticsOriginByNonM3Api(List<PropertyDTO> propertyList, List<OperatingStatisticsListBO> boList,
                                                              int operatorId, Map<Integer, List<Long>> propertyId2DateRange, Integer dataType) {
            Map<String, List<OperatingStatisticsListBO>> property2BOS = boList.stream().collect(Collectors.groupingBy(OperatingStatisticsListBO::getProperty));
            for (PropertyDTO propertyDTO : propertyList) {
                List<Long> dateRange = propertyId2DateRange.get(propertyDTO.getId());
                LocalDate startLocalDate = DateTimeUtils.strToLocalDate(dateRange.get(0).toString(), BaseConstant.DATE_FORMAT_YYYYMMDD);
                LocalDate endLocalDate = DateTimeUtils.strToLocalDate(dateRange.get(1).toString(), BaseConstant.DATE_FORMAT_YYYYMMDD);

                // 获取开始日期到结束日期的每一天
                List<Integer> dates = startLocalDate.datesUntil(endLocalDate.plusDays(1)).map(localDate -> {
                    String date = DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYYMMDD);
                    return Integer.valueOf(date);
                }).collect(Collectors.toList());

                // 生成锁key
                List<String> keys = dates.stream().map(date ->
                        MessageFormat.format(SvaConstant.HANDLE_DAILY_OPERATING_STATISTICS_ORIGINAL_KEY, propertyDTO.getId(), date.toString())).toList();

                RedissonLockUtil.executeMultipleLock(redissonClient, keys, 3, 5, () -> {
                    // 处理数据
                    svaOperatingStatisticsOriginalService.handleOperatingStatisticsOriginByNonM3Api(propertyDTO,
                            property2BOS.getOrDefault(propertyDTO.getAbbr(), Collections.emptyList()),
                            operatorId, dates, dataType);
                });
            }
        }

        /**
         * 按优先级更新Operating statistics数据
         *
         * @param propertyList         资产集合
         * @param propertyId2DateRange 待处理数据的日期区间(日期格式yyyyMMdd)
         * @author lichen
         * @date 2025/11/21
         */
        @Override
        public void updateOperatingStatisticsByPriority(List<PropertyDTO> propertyList, Map<Integer, List<Long>> propertyId2DateRange) {
            for (PropertyDTO propertyDTO : propertyList) {
                List<Long> dateRange = propertyId2DateRange.get(propertyDTO.getId());
                if (CollectionUtils.isEmpty(dateRange)) continue;
                LocalDate startLocalDate = DateTimeUtils.strToLocalDate(dateRange.get(0).toString(), BaseConstant.DATE_FORMAT_YYYYMMDD);
                LocalDate endLocalDate = DateTimeUtils.strToLocalDate(dateRange.get(1).toString(), BaseConstant.DATE_FORMAT_YYYYMMDD);

                // 获取开始日期到结束日期的每一天
                List<Integer> dates = startLocalDate.datesUntil(endLocalDate.plusDays(1)).map(localDate -> {
                    String date = DateTimeUtils.localDateToStr(localDate, BaseConstant.DATE_FORMAT_YYYYMMDD);
                    return Integer.valueOf(date);
                }).collect(Collectors.toList());

                // 生成锁key
                List<String> keys = dates.stream().map(date ->
                        MessageFormat.format(SvaConstant.HANDLE_DAILY_OPERATING_STATISTICS_KEY, propertyDTO.getId(), date.toString())).toList();

                RedissonLockUtil.executeMultipleLock(redissonClient, keys, 3, 5, () -> {
                    // 处理数据
                    operatingStatisticsService.updateOperatingStatisticsByPriority(propertyDTO, dates);
                });
            }
        }

    }