package org.jeecg.modules.adweb.dmp.service;

import static org.jeecg.modules.adweb.dmp.vo.report.SiteOverviewStatsVO.DailyStatsVO;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Pair;
import org.jeecg.modules.adweb.common.enums.CountryCode;
import org.jeecg.modules.adweb.common.util.AdwebRedisUtil;
import org.jeecg.modules.adweb.common.util.DateUtil;
import org.jeecg.modules.adweb.common.util.NumberUtil;
import org.jeecg.modules.adweb.dmp.dto.google.analytics.GAPropertyDTO;
import org.jeecg.modules.adweb.dmp.dto.google.analytics.report.GAReportRequestDTO;
import org.jeecg.modules.adweb.dmp.dto.google.analytics.report.OrderByType;
import org.jeecg.modules.adweb.dmp.dto.google.analytics.report.ReportConstant;
import org.jeecg.modules.adweb.dmp.dto.google.analytics.report.ReportType;
import org.jeecg.modules.adweb.dmp.dto.google.analytics.report.data.CustomReportData;
import org.jeecg.modules.adweb.dmp.entity.GoogleGTM;
import org.jeecg.modules.adweb.dmp.service.google.GAReportService;
import org.jeecg.modules.adweb.dmp.vo.report.*;
import org.jeecg.modules.adweb.enquiry.mapper.AdwebEnquiryMapper;
import org.jeecg.modules.adweb.site.entity.AdwebSite;
import org.jeecg.modules.adweb.site.service.IAdwebSiteService;
import org.jeecg.modules.redis.CacheConfig;
import org.jeecg.modules.redis.TTLCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 从DataBridge实时拉取GA报表数据，替代DB离线查询 - http://data-bridge.v3.adwebcloud.com:9002/swagger-ui/index.html
 *
 * @author wfansh
 * @deprecated 相比XXL定期同步 + DB离线查询，响应速度较慢。后期DB数据量大产生性能瓶颈时，可重新评估并启用本方案
 */
@Deprecated
@Slf4j
@Service
public class RealtimeReportService {

    @Autowired private IAdwebSiteService adwebSiteService;
    @Autowired private IGoogleGTMService googleGTMService;
    @Autowired private AdwebEnquiryMapper adwebEnquiryMapper;
    @Autowired private GAReportService gaReportService;
    @Autowired private AdwebRedisUtil adwebRedisUtil;

    /**
     * 拉取Google Analytics - Period报表（today, yesterday, thisWeek, lastWeek, thisMonth, lastMonth,
     * allTime）
     *
     * <p>填充各时间段询盘数据
     */
    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "dmp:realtime:getPeriodicStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 10) // Redis TTL为10分钟
    public List<PeriodicStatsVO> getPeriodicStats(String siteCode) {
        GoogleGTM googleGTM = this.getGTMAccount(siteCode);
        if (StringUtils.isBlank(googleGTM.getGaPropertyId())) {
            log.info("Google Analytics帐号未配置, siteCode = {}", siteCode);
        }

        // 1. 查询GA统计数据 - UV，PV等
        Map<String, PeriodicStatsVO> periodicStatsVOs =
                List.of(
                                DateUtil.DATE_TYPE_TODAY,
                                DateUtil.DATE_TYPE_YESTERDAY,
                                DateUtil.DATE_TYPE_THIS_WEEK,
                                DateUtil.DATE_TYPE_LAST_WEEK,
                                DateUtil.DATE_TYPE_THIS_MONTH,
                                DateUtil.DATE_TYPE_LAST_MONTH,
                                DateUtil.DATE_TYPE_ALL_TIME)
                        .parallelStream()
                        .map(period -> this.getPeriodicStats(googleGTM.getGaPropertyId(), period))
                        .collect(
                                Collectors.toMap(
                                        PeriodicStatsVO::getPeriod,
                                        Function.identity(),
                                        (x, y) -> y,
                                        LinkedHashMap::new)); // 使用LinkedHashMap保持原查询顺序

        // 2. 询盘数据补充
        List<ImmutablePair<String, Long>> enquiryPeriodicCounts =
                adwebEnquiryMapper.getEnquiryPeriodicCounts(siteCode);
        for (ImmutablePair<String, Long> enquiryPeriodicCount : enquiryPeriodicCounts) {
            if (!periodicStatsVOs.containsKey(enquiryPeriodicCount.getKey())) {
                continue;
            }

            PeriodicStatsVO periodicStatsVO = periodicStatsVOs.get(enquiryPeriodicCount.getKey());
            periodicStatsVO.setEnquires(enquiryPeriodicCount.getValue().intValue());
            periodicStatsVO.setEnquiryConversionRate(
                    NumberUtil.formatPercentage(
                            NumberUtil.safeDivide(
                                    periodicStatsVO.getEnquires(), periodicStatsVO.getTotalUsers()),
                            2));
        }

        return periodicStatsVOs.values().stream()
                .collect(Collectors.toList()); // 使用toList()方法返回UnmodifiableList，导致Redis类型解析异常
    }

    /**
     * 拉取Google Analytics - 指定Period报表
     *
     * <p>see {@link #getPeriodicStats(String)}
     */
    private PeriodicStatsVO getPeriodicStats(String propertyId, String period) {
        // 0. 计算时间区间
        Pair<Date, Date> dateRange = DateUtil.getDateRangeByType(period);

        List<CustomReportData> reportDataList = Lists.newArrayList();
        if (StringUtils.isNotBlank(propertyId)) {
            // 1. 构建GA报表请求参数
            GAReportRequestDTO gaReportRequest =
                    this.buildGAReportRequest(
                            propertyId,
                            dateRange.getLeft(),
                            dateRange.getRight(),
                            List.of(
                                    ReportConstant.METRIC_TOTAL_USERS,
                                    ReportConstant.METRIC_SCREEN_PAGE_VIEWS),
                            List.of(),
                            OrderByType.METRICS,
                            ReportConstant.METRIC_TOTAL_USERS,
                            true);

            // 2. 请求API接口
            reportDataList = gaReportService.runGAReport(gaReportRequest, CustomReportData.class);
        }

        // 3. 转化为VO
        PeriodicStatsVO periodicStatsVO = new PeriodicStatsVO();
        periodicStatsVO.setPeriod(period);
        if (CollectionUtils.isNotEmpty(reportDataList)) {
            periodicStatsVO.setTotalUsers(
                    Integer.parseInt(reportDataList.get(0).get(ReportConstant.METRIC_TOTAL_USERS)));
            periodicStatsVO.setPageViews(
                    Integer.parseInt(
                            reportDataList.get(0).get(ReportConstant.METRIC_SCREEN_PAGE_VIEWS)));
        }

        return periodicStatsVO;
    }

    /**
     * 拉取Google Analytics - Daily报表
     *
     * <p>如数据库中某天数据缺失，需填充空数据
     *
     * <p>填充每日询盘数据
     */
    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "dmp:realtime:getDailyStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 10) // Redis TTL为10分钟
    public List<DailyStatsVO> getDailyStats(String siteCode, Date start, Date end) {
        GoogleGTM googleGTM = this.getGTMAccount(siteCode);

        // 0. 起止日期为空时，设置默认区间 - start = 网站发布日期 & end = 今天
        if (start == null || end == null) {
            AdwebSite adwebSite = adwebSiteService.getSiteByCode(siteCode);
            start =
                    DateUtil.getTodayZeroTime(
                            Optional.ofNullable(adwebSite.getIssueTime())
                                    .orElse(adwebSite.getCtime()));
            end = DateUtil.getTodayZeroTime(new Date());
        }

        List<CustomReportData> reportDataList = Lists.newArrayList();
        if (StringUtils.isBlank(googleGTM.getGaPropertyId())) {
            log.info("Google Analytics帐号未配置, siteCode = {}", siteCode);
        } else {
            // 1. 构建GA报表请求参数
            GAReportRequestDTO gaReportRequest =
                    this.buildGAReportRequest(
                            googleGTM.getGaPropertyId(),
                            start,
                            end,
                            List.of(
                                    ReportConstant.METRIC_TOTAL_USERS,
                                    ReportConstant.METRIC_SESSIONS,
                                    ReportConstant.METRIC_BOUNCE_RATE,
                                    ReportConstant.METRIC_AVG_SESSION_DURATION,
                                    ReportConstant.METRIC_SCREEN_PAGE_VIEWS,
                                    ReportConstant.METRIC_SCREEN_PAGE_VIEWS_PER_SESSION),
                            List.of(ReportConstant.DIMENSION_DATE),
                            OrderByType.DIMENSIONS,
                            ReportConstant.DIMENSION_DATE,
                            false);

            // 2. 请求API接口
            reportDataList = gaReportService.runGAReport(gaReportRequest, CustomReportData.class);
        }

        // 3. 转化为VOs
        Map<String, DailyStatsVO> dailyStatsVOs = Maps.newHashMap();
        for (CustomReportData reportData : reportDataList) {
            DailyStatsVO dailyStatsVO = new DailyStatsVO();
            dailyStatsVO.setDate(reportData.get(ReportConstant.DIMENSION_DATE));
            dailyStatsVO.setTotalUsers(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_TOTAL_USERS)));
            dailyStatsVO.setSessions(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_SESSIONS)));
            dailyStatsVO.setBounceRate(
                    Double.parseDouble(reportData.get(ReportConstant.METRIC_BOUNCE_RATE)));
            dailyStatsVO.setAvgSessionDuration(
                    Double.parseDouble(reportData.get(ReportConstant.METRIC_AVG_SESSION_DURATION)));
            dailyStatsVO.setPageViews(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_SCREEN_PAGE_VIEWS)));
            dailyStatsVO.setPageViewsPerSession(
                    Double.parseDouble(
                            reportData.get(ReportConstant.METRIC_SCREEN_PAGE_VIEWS_PER_SESSION)));

            dailyStatsVOs.put(dailyStatsVO.getDate(), dailyStatsVO);
        }

        // 4. 缺失日期补充
        Set<String> absentDays =
                Sets.difference(
                        DateUtil.getAllDaysBetween(start, end).stream()
                                .map(date -> DateUtil.formatDate(date, DateUtil.DATE_FORMAT))
                                .collect(Collectors.toSet()),
                        dailyStatsVOs.keySet());
        absentDays.forEach(
                date -> {
                    DailyStatsVO dailyStatsVO = new DailyStatsVO();
                    dailyStatsVO.setDate(date); // 其它数值字段设置为默认值
                    dailyStatsVOs.put(date, dailyStatsVO);
                });

        // 5. 询盘数据补充
        List<ImmutableTriple<String, Long, Long>> enquiryDailyCounts =
                adwebEnquiryMapper.getEnquiryDailyCounts(siteCode, start, end);
        for (ImmutableTriple<String, Long, Long> enquiryDailyCount : enquiryDailyCounts) {
            if (!dailyStatsVOs.containsKey(enquiryDailyCount.getLeft())) {
                continue;
            }

            DailyStatsVO dailyStatsVO = dailyStatsVOs.get(enquiryDailyCount.getLeft());
            dailyStatsVO.setEnquires(enquiryDailyCount.getMiddle().intValue());
            dailyStatsVO.setUnreadEnquires(enquiryDailyCount.getRight().intValue());
        }

        // 6. 根据日期排序并返回
        return dailyStatsVOs.values().stream()
                .sorted(Comparator.comparing(DailyStatsVO::getDate)) // 按日期升序排序
                .collect(Collectors.toList()); // 使用toList()方法返回UnmodifiableList，导致Redis类型解析异常
    }

    /** 拉取Google Analytics - Country报表 */
    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "dmp:realtime:getCountryStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 10) // Redis TTL为10分钟
    public List<CountryStatsVO> getCountryStats(String siteCode, Date start, Date end) {
        GoogleGTM googleGTM = this.getGTMAccount(siteCode);
        if (StringUtils.isBlank(googleGTM.getGaPropertyId())) {
            log.info("Google Analytics帐号未配置, siteCode = {}", siteCode);
            return Collections.EMPTY_LIST;
        }

        // 1. 构建GA报表请求参数
        GAReportRequestDTO gaReportRequest =
                this.buildGAReportRequest(
                        googleGTM.getGaPropertyId(),
                        start,
                        end,
                        List.of(ReportConstant.METRIC_TOTAL_USERS),
                        List.of(ReportConstant.DIMENSION_COUNTRY),
                        OrderByType.METRICS,
                        ReportConstant.METRIC_TOTAL_USERS,
                        true);

        // 2. 请求API接口
        List<CustomReportData> reportDataList =
                gaReportService.runGAReport(gaReportRequest, CustomReportData.class);

        // 3.1 转化为VOs
        List<CountryStatsVO> countryStatsVOs = Lists.newArrayList();
        for (CustomReportData reportData : reportDataList) {
            CountryStatsVO countryStatsVO = new CountryStatsVO();
            countryStatsVO.setCountry(reportData.get(ReportConstant.DIMENSION_COUNTRY));

            CountryCode countryCode = CountryCode.valueOfName(countryStatsVO.getCountry());
            countryStatsVO.setCountryName(countryCode.getCnName());
            countryStatsVO.setCountryCode(countryCode.getCode());

            countryStatsVO.setTotalUsers(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_TOTAL_USERS)));

            countryStatsVOs.add(countryStatsVO);
        }

        // 3.2 时间区间内所有国家totalUsers总数
        int totalUsersSum = countryStatsVOs.stream().mapToInt(CountryStatsVO::getTotalUsers).sum();

        // 3.3 VO数据计算填充
        for (CountryStatsVO countryStatsVO : countryStatsVOs) {
            countryStatsVO.setTotalUsersProportion(
                    NumberUtil.formatPercentage(
                            NumberUtil.safeDivide(countryStatsVO.getTotalUsers(), totalUsersSum),
                            2));
        }

        return countryStatsVOs;
    }

    /** 拉取Google Analytics - Session Source Medium报表 */
    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "dmp:realtime:getSourceMediumStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 10) // Redis TTL为10分钟
    public List<SourceMediumStatsVO> getSourceMediumStats(String siteCode, Date start, Date end) {
        GoogleGTM googleGTM = this.getGTMAccount(siteCode);
        if (StringUtils.isBlank(googleGTM.getGaPropertyId())) {
            log.info("Google Analytics帐号未配置, siteCode = {}", siteCode);
            return Collections.EMPTY_LIST;
        }

        // 1. 构建GA报表请求参数
        GAReportRequestDTO gaReportRequest =
                this.buildGAReportRequest(
                        googleGTM.getGaPropertyId(),
                        start,
                        end,
                        List.of(
                                ReportConstant.METRIC_TOTAL_USERS,
                                ReportConstant.METRIC_NEW_USERS,
                                ReportConstant.METRIC_SESSIONS,
                                ReportConstant.METRIC_SCREEN_PAGE_VIEWS,
                                ReportConstant.METRIC_AVG_SESSION_DURATION,
                                ReportConstant.METRIC_SCREEN_PAGE_VIEWS_PER_SESSION),
                        List.of(ReportConstant.DIMENSION_SESSION_SOURCE_MEDIUM),
                        OrderByType.METRICS,
                        ReportConstant.METRIC_TOTAL_USERS,
                        true);

        // 2. 请求API接口
        List<CustomReportData> reportDataList =
                gaReportService.runGAReport(gaReportRequest, CustomReportData.class);

        // 3.1 转化为VOs
        List<SourceMediumStatsVO> sourceMediumStatsVOs = Lists.newArrayList();
        for (CustomReportData reportData : reportDataList) {
            SourceMediumStatsVO sourceMediumStatsVO = new SourceMediumStatsVO();
            sourceMediumStatsVO.setType(
                    reportData.get(ReportConstant.DIMENSION_SESSION_SOURCE_MEDIUM));
            sourceMediumStatsVO.setTotalUsers(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_TOTAL_USERS)));
            sourceMediumStatsVO.setNewUsers(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_NEW_USERS)));
            sourceMediumStatsVO.setSessions(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_SESSIONS)));
            sourceMediumStatsVO.setPageViews(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_SCREEN_PAGE_VIEWS)));
            sourceMediumStatsVO.setAvgSessionDuration(
                    NumberUtil.formatDecimal(
                                    Double.parseDouble(
                                            reportData.get(
                                                    ReportConstant.METRIC_AVG_SESSION_DURATION)),
                                    2)
                            .doubleValue());
            sourceMediumStatsVO.setPageViewsPerSession(
                    NumberUtil.formatDecimal(
                                    Double.parseDouble(
                                            reportData.get(
                                                    ReportConstant
                                                            .METRIC_SCREEN_PAGE_VIEWS_PER_SESSION)),
                                    2)
                            .doubleValue());

            sourceMediumStatsVOs.add(sourceMediumStatsVO);
        }

        // 3.2 时间区间内所有媒介totalUsers总数
        int totalUsersSum =
                sourceMediumStatsVOs.stream().mapToInt(SourceMediumStatsVO::getTotalUsers).sum();

        // 3.3 VO数据计算填充
        for (SourceMediumStatsVO sourceMediumStatsVO : sourceMediumStatsVOs) {
            int totalUsers = sourceMediumStatsVO.getTotalUsers();
            sourceMediumStatsVO.setTotalUsersProportion(
                    NumberUtil.formatPercentage(
                            NumberUtil.safeDivide(totalUsers, totalUsersSum), 2));

            sourceMediumStatsVO.setNewUsersRatio(
                    NumberUtil.formatPercentage(
                            NumberUtil.safeDivide(sourceMediumStatsVO.getNewUsers(), totalUsers),
                            2));
        }

        return sourceMediumStatsVOs.stream()
                .sorted(
                        Comparator.comparingInt(SourceMediumStatsVO::getTotalUsers)
                                .thenComparingDouble(SourceMediumStatsVO::getAvgSessionDuration)
                                .reversed()) // 按totalUsers和avgSessionDuration降序排序
                .collect(Collectors.toList()); // 使用toList()方法返回UnmodifiableList，导致Redis类型解析异常
    }

    /** 拉取Google Analytics - Page Path报表 */
    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "dmp:realtime:getPagePathStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 10) // Redis TTL为10分钟
    public List<PagePathStatsVO> getPagePathStats(
            String siteCode, Date start, Date end, int limit) {
        GoogleGTM googleGTM = this.getGTMAccount(siteCode);
        if (StringUtils.isBlank(googleGTM.getGaPropertyId())) {
            log.info("Google Analytics帐号未配置, siteCode = {}", siteCode);
            return Collections.EMPTY_LIST;
        }

        // 1. 构建GA报表请求参数
        GAReportRequestDTO gaReportRequest =
                this.buildGAReportRequest(
                        googleGTM.getGaPropertyId(),
                        start,
                        end,
                        List.of(
                                ReportConstant.METRIC_SCREEN_PAGE_VIEWS,
                                ReportConstant.METRIC_USER_ENGAGEMENT_DURATION),
                        List.of(ReportConstant.DIMENSION_PAGE_PATH),
                        OrderByType.METRICS,
                        ReportConstant.METRIC_SCREEN_PAGE_VIEWS,
                        true);

        // 2. 请求API接口
        List<CustomReportData> reportDataList =
                gaReportService.runGAReport(gaReportRequest, CustomReportData.class);

        // 3.1 转化为VOs
        List<PagePathStatsVO> pagePathStatsVOs = Lists.newArrayList();
        for (CustomReportData reportData : reportDataList) {
            PagePathStatsVO pagePathStatsVO = new PagePathStatsVO();
            pagePathStatsVO.setPagePath(reportData.get(ReportConstant.DIMENSION_PAGE_PATH));
            pagePathStatsVO.setPageViews(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_SCREEN_PAGE_VIEWS)));
            pagePathStatsVO.setAvgTimeOnPage(
                    NumberUtil.formatDecimal(
                                    Double.parseDouble(
                                            reportData.get(
                                                    ReportConstant
                                                            .METRIC_USER_ENGAGEMENT_DURATION)),
                                    2)
                            .doubleValue());

            pagePathStatsVOs.add(pagePathStatsVO);
        }

        // 3.2. 网站URL及格式化
        AdwebSite adwebSite = adwebSiteService.getSiteByCode(siteCode);
        String siteUrl = StringUtils.removeEnd(Strings.nullToEmpty(adwebSite.getDomain()), "/");

        // 3.3. 时间区间内PV总数
        int totalPVs = pagePathStatsVOs.stream().mapToInt(PagePathStatsVO::getPageViews).sum();

        // 3.4 VO数据计算填充
        for (PagePathStatsVO pagePathStatsVO : pagePathStatsVOs) {
            pagePathStatsVO.setPagePath(siteUrl + pagePathStatsVO.getPagePath());
            pagePathStatsVO.setPvProportion(
                    NumberUtil.formatPercentage(
                            NumberUtil.safeDivide(pagePathStatsVO.getPageViews(), totalPVs), 2));
        }

        return pagePathStatsVOs.stream()
                .sorted(
                        Comparator.comparingInt(PagePathStatsVO::getPageViews)
                                .thenComparingDouble(PagePathStatsVO::getAvgTimeOnPage)
                                .reversed()) // 按pageViews和avgTimeOnPage降序排序
                .limit(limit)
                .collect(Collectors.toList()); // 使用toList()方法返回UnmodifiableList，导致Redis类型解析异常
    }

    /** 拉取Google Analytics - Device报表 */
    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "dmp:realtime:getDeviceStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 10) // Redis TTL为10分钟
    public List<DeviceStatsVO> getDeviceStats(String siteCode, Date start, Date end) {
        GoogleGTM googleGTM = this.getGTMAccount(siteCode);
        if (StringUtils.isBlank(googleGTM.getGaPropertyId())) {
            log.info("Google Analytics帐号未配置, siteCode = {}", siteCode);
            return Collections.EMPTY_LIST;
        }

        // 1. 构建GA报表请求参数
        GAReportRequestDTO gaReportRequest =
                this.buildGAReportRequest(
                        googleGTM.getGaPropertyId(),
                        start,
                        end,
                        List.of(
                                ReportConstant.METRIC_TOTAL_USERS,
                                ReportConstant.METRIC_SESSIONS,
                                ReportConstant.METRIC_SCREEN_PAGE_VIEWS),
                        List.of(ReportConstant.DIMENSION_PLATFORM_DEVICE_CATEGORY),
                        OrderByType.METRICS,
                        ReportConstant.METRIC_TOTAL_USERS,
                        true);

        // 2. 请求API接口
        List<CustomReportData> reportDataList =
                gaReportService.runGAReport(gaReportRequest, CustomReportData.class);

        // 3. 转化为VOs
        List<DeviceStatsVO> deviceStatsVOs = Lists.newArrayList();
        for (CustomReportData reportData : reportDataList) {
            DeviceStatsVO deviceStatsVO = new DeviceStatsVO();
            deviceStatsVO.setDevice(
                    reportData.get(ReportConstant.DIMENSION_PLATFORM_DEVICE_CATEGORY));
            deviceStatsVO.setTotalUsers(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_TOTAL_USERS)));
            deviceStatsVO.setSessions(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_SESSIONS)));
            deviceStatsVO.setPageViews(
                    Integer.parseInt(reportData.get(ReportConstant.METRIC_SCREEN_PAGE_VIEWS)));

            deviceStatsVOs.add(deviceStatsVO);
        }

        return deviceStatsVOs;
    }

    /**
     * 获取{@link GoogleGTM}帐号信息 - 放入Redis缓存，避免同一页面多个HTTP请求频繁DB查询
     *
     * <p>使用AdwebRedisUtil操作 - @Cacheable注解无法嵌套使用
     */
    private synchronized GoogleGTM getGTMAccount(String siteCode) {
        final String redisKey = String.format("%s:account:%s", "googlegtm", siteCode);

        GoogleGTM googleGTM = (GoogleGTM) adwebRedisUtil.get(redisKey);
        if (Objects.nonNull(googleGTM)) {
            return googleGTM;
        }

        // DB查询
        googleGTM =
                googleGTMService.getOne(
                        new LambdaQueryWrapper<GoogleGTM>().eq(GoogleGTM::getSiteCode, siteCode),
                        false);
        googleGTM = Objects.nonNull(googleGTM) ? googleGTM : new GoogleGTM(); // Redis存储对象不允许为空
        adwebRedisUtil.set(redisKey, googleGTM, 30); // Redis TTL为30秒

        return googleGTM;
    }

    private GAReportRequestDTO buildGAReportRequest(
            String propertyId,
            Date start,
            Date end,
            List<String> metrics,
            List<String> dimensions,
            OrderByType orderByType,
            String orderBy,
            boolean orderByDesc) {
        GAReportRequestDTO gaReportRequest = new GAReportRequestDTO();
        gaReportRequest.setPropertyResourceName(GAPropertyDTO.toResourceName(propertyId));
        gaReportRequest.setReportType(ReportType.ADWEB_CUSTOM_REPORT);
        gaReportRequest.setStartDate(
                DateUtil.formatDate(
                        Optional.ofNullable(start)
                                // 如果start为空，设置为无限早时间
                                .orElse(DateUtil.parseDate("2016-01-01", DateUtil.DATE_FORMAT)),
                        DateUtil.DATE_FORMAT));
        gaReportRequest.setEndDate(
                DateUtil.formatDate(
                        Optional.ofNullable(end).orElse(new Date()), DateUtil.DATE_FORMAT));
        gaReportRequest.setMetrics(metrics);
        gaReportRequest.setDimensions(dimensions);
        gaReportRequest.setOrderByType(orderByType);
        gaReportRequest.setOrderBy(orderBy);
        gaReportRequest.setOrderByDesc(orderByDesc);
        return gaReportRequest;
    }
}
