Browse Source

Merge branch 'branch' of wangfan/adweb3-server into master

wangfan 3 months ago
parent
commit
4ef6d039d6

BIN
conf/adweb3_dev.sql.zip


+ 54 - 36
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/adweb/common/util/DateUtil.java

@@ -1,6 +1,7 @@
 package org.jeecg.modules.adweb.common.util;
 
 import lombok.extern.slf4j.Slf4j;
+
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 
@@ -25,47 +26,41 @@ import java.util.stream.IntStream;
 @Slf4j
 public class DateUtil {
 
-    /**
-     * 标准日期格式(yyyy-MM-dd)
-     */
+    /** 标准日期格式(yyyy-MM-dd) */
     public static final String DATE_FORMAT = "yyyy-MM-dd";
 
-    /**
-     * 标准时间格式(yyyy-MM-dd HH:mm:ss)
-     */
+    /** 标准时间格式(yyyy-MM-dd HH:mm:ss) */
     public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
 
-    /**
-     * 带时区的时间格式(yyyy-MM-dd HH:mm:ss +00:00)
-     */
+    /** 带时区的时间格式(yyyy-MM-dd HH:mm:ss +00:00) */
     public static final String ZONED_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss X";
 
-    /**
-     * 时间格式(yyyy/MM/dd)
-     */
+    /** 时间格式(yyyy/MM/dd) */
     public static final String SUBJECT_DATE_FORMAT = "yyyy/MM/dd";
 
-    /**
-     * 时间格式(yyyyMMdd)
-     */
+    /** 时间格式(yyyyMMdd) */
     public static final String COMPACT_DATE_FORMAT = "yyyyMMdd";
 
-    /**
-     * 时间格式(yyyyMMddHHmmss)
-     */
+    /** 时间格式(yyyyMMddHHmmss) */
     public static final String COMPACT_DATE_TIME_FORMAT = "yyyyMMddHHmmss";
 
-    /**
-     * 带分钟的事件格式
-     * yyyy-MM-dd HH:mm
-     */
+    /** 带分钟的事件格式 yyyy-MM-dd HH:mm */
     public static final String DATE_FORMAT_MINUTE = "yyyy-MM-dd HH:mm";
 
     public static final ZoneId DEFAULT_ZONE_ID = ZoneId.of("Asia/Shanghai");
 
-    /**
-     * 取得系统当前日期
-     */
+    /* 日期类型, {@link #getDateRangeByType(String)}方法参数 */
+    public static final String DATE_TYPE_TODAY = "today";
+    public static final String DATE_TYPE_YESTERDAY = "yesterday";
+    public static final String DATE_TYPE_SEVEN_DAYS = "sevenDay";
+    public static final String DATE_TYPE_THIRTY_DAYS = "thirtyDay";
+    public static final String DATE_TYPE_THIS_WEEK = "thisWeek";
+    public static final String DATE_TYPE_LAST_WEEK = "lastWeek";
+    public static final String DATE_TYPE_THIS_MONTH = "thisMonth";
+    public static final String DATE_TYPE_LAST_MONTH = "lastMonth";
+    public static final String DATE_TYPE_ALL_TIME = "allTime";
+
+    /** 取得系统当前日期 */
     public static Date getDate() {
         return new Date();
     }
@@ -79,26 +74,51 @@ public class DateUtil {
      */
     public static Pair<Date, Date> getDateRangeByType(String dateType) {
         Date now = new Date();
+        Calendar calendar = Calendar.getInstance();
+
         Date start = null;
         Date end = null;
 
         switch (dateType) {
-            case "yesterday":
-                start = getTodayZeroTime(DateUtil.addDays(now, -1));
+            case DATE_TYPE_TODAY:
+                start = getTodayZeroTime(now);
                 end = start;
                 break;
-            case "today":
-                start = getTodayZeroTime(now);
+            case DATE_TYPE_YESTERDAY:
+                start = getTodayZeroTime(DateUtil.addDays(now, -1));
                 end = start;
                 break;
-            case "sevenDay":
+            case DATE_TYPE_SEVEN_DAYS:
                 end = getTodayZeroTime(now);
                 start = addDays(end, -6);
                 break;
-            case "thirtyDay":
+            case DATE_TYPE_THIRTY_DAYS:
                 end = getTodayZeroTime(now);
                 start = addDays(end, -29);
                 break;
+            case DATE_TYPE_THIS_WEEK:
+                calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
+                start = getTodayZeroTime(calendar.getTime());
+                end = getTodayZeroTime(now);
+                break;
+            case DATE_TYPE_LAST_WEEK:
+                calendar.add(Calendar.DAY_OF_YEAR, -7);
+                calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());
+                start = getTodayZeroTime(calendar.getTime());
+                end = addDays(start, 6);
+                break;
+            case DATE_TYPE_THIS_MONTH:
+                calendar.set(Calendar.DAY_OF_MONTH, 1);
+                start = getTodayZeroTime(calendar.getTime());
+                end = getTodayZeroTime(now);
+                break;
+            case DATE_TYPE_LAST_MONTH:
+                calendar.add(Calendar.MONTH, -1);
+                calendar.set(Calendar.DAY_OF_MONTH, 1);
+                start = getTodayZeroTime(calendar.getTime());
+                calendar.set(
+                        Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+                end = getTodayZeroTime(calendar.getTime());
             default:
                 // e.g. dateType = 7
                 if (StringUtils.isNumeric(dateType)) {
@@ -146,7 +166,7 @@ public class DateUtil {
     /**
      * 在当前日期上追加N天
      *
-     * @param date      当前日期
+     * @param date 当前日期
      * @param daysToAdd 添加天数
      * @return 日期字符串形式
      */
@@ -161,7 +181,7 @@ public class DateUtil {
      * 在当前日期上追加N年
      *
      * @param date 当前日期
-     * @param num  添加年数
+     * @param num 添加年数
      * @return 日期字符串形式
      */
     public static Date addYear(Date date, int num) {
@@ -185,9 +205,7 @@ public class DateUtil {
                         LocalDate.ofInstant(end.toInstant(), DEFAULT_ZONE_ID));
     }
 
-    /**
-     * 获取start和end之间所有的日期,包含起止两端
-     */
+    /** 获取start和end之间所有的日期,包含起止两端 */
     public static List<Date> getAllDaysBetween(Date start, Date end) {
         return IntStream.rangeClosed(0, diffDays(start, end))
                 .mapToObj(diff -> addDays(start, diff))

+ 4 - 1
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/adweb/dmp/controller/DMPDataController.java

@@ -51,7 +51,10 @@ public class DMPDataController {
     @GetMapping("/site-periodic/stats")
     public Result<Map<String, PeriodicStatsVO>> getSitePeriodicStats(String siteCode) {
         // 1. 查询分时间段的GA Report和Enquiries
-        List<PeriodicStatsVO> periodicStatsVOs = gaDailyReportService.getPeriodicStats(siteCode);
+        List<PeriodicStatsVO> periodicStatsVOs =
+                realtimeReport
+                        ? realtimeReportService.getPeriodicStats(siteCode)
+                        : gaDailyReportService.getPeriodicStats(siteCode);
 
         // 2. 转换为Map并返回
         return Result.ok(

+ 133 - 29
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/adweb/dmp/service/RealtimeReportService.java

@@ -9,9 +9,12 @@ import com.google.common.collect.Sets;
 
 import lombok.extern.slf4j.Slf4j;
 
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.compress.utils.Lists;
 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;
@@ -35,6 +38,7 @@ import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 
 import java.util.*;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -55,13 +59,104 @@ public class RealtimeReportService {
     @Autowired private AdwebRedisUtil adwebRedisUtil;
 
     /**
-     * 拉取Google Analytics - 时间段报表 - 今天,昨天,本周,上周,本月,上月,全部
+     * 拉取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) {
-        // TODO
-        return Collections.EMPTY_LIST;
+        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;
     }
 
     /**
@@ -79,10 +174,6 @@ public class RealtimeReportService {
                             + 60 * 10) // Redis TTL为10分钟
     public List<DailyStatsVO> getDailyStats(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;
-        }
 
         // 0. 起止日期为空时,设置默认区间 - start = 网站发布日期 & end = 今天
         if (start == null || end == null) {
@@ -94,27 +185,31 @@ public class RealtimeReportService {
             end = DateUtil.getTodayZeroTime(new Date());
         }
 
-        // 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接口
-        List<CustomReportData> reportDataList =
-                gaReportService.runGAReport(gaReportRequest, CustomReportData.class);
+        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();
@@ -317,7 +412,12 @@ public class RealtimeReportService {
                             2));
         }
 
-        return sourceMediumStatsVOs;
+        return sourceMediumStatsVOs.stream()
+                .sorted(
+                        Comparator.comparingInt(SourceMediumStatsVO::getTotalUsers)
+                                .thenComparingDouble(SourceMediumStatsVO::getAvgSessionDuration)
+                                .reversed())
+                .collect(Collectors.toList()); // 使用toList()方法返回UnmodifiableList,导致Redis类型解析异常
     }
 
     /** 拉取Google Analytics - Page Path报表 */
@@ -388,6 +488,10 @@ public class RealtimeReportService {
         }
 
         return pagePathStatsVOs.stream()
+                .sorted(
+                        Comparator.comparingInt(PagePathStatsVO::getPageViews)
+                                .thenComparingDouble(PagePathStatsVO::getAvgTimeOnPage)
+                                .reversed())
                 .limit(limit)
                 .collect(Collectors.toList()); // 使用toList()方法返回UnmodifiableList,导致Redis类型解析异常
     }

+ 1 - 1
jeecg-module-system/jeecg-system-start/src/main/resources/application-test.yml

@@ -371,7 +371,7 @@ v3:
   projectPath: /opt/adweb3/pem
   dmp:
     # DMP模块实时报表开关,true-从DataBridge实时读取 false-从DB离线读取
-    realtimeReport: false
+    realtimeReport: true
 
 ### GA,GTM,Ads等数据交换中心
 data-bridge: