From da191acf7102288de24db5885bedba52b5cfc51b Mon Sep 17 00:00:00 2001
From: huangguancheng <huangguancheng@bestfulfill>
Date: Tue, 14 Jan 2025 21:29:50 +0800
Subject: [PATCH] =?UTF-8?q?=E6=A0=87=E7=AD=BE=E9=A1=B5=E8=A7=92=E6=A0=87?=
 =?UTF-8?q?=E6=95=B0=E5=AD=97=EF=BC=88Accounts=E3=80=81Campaigns=E3=80=81A?=
 =?UTF-8?q?d=20Sets=20=E3=80=81Ads=E3=80=81Creatives=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/controller/BpsAdController.php |  13 +-
 app/service/AdsInsightService.php  | 229 +++++++++++++++++++++++------
 2 files changed, 198 insertions(+), 44 deletions(-)

diff --git a/app/controller/BpsAdController.php b/app/controller/BpsAdController.php
index c532025..00f0ea4 100644
--- a/app/controller/BpsAdController.php
+++ b/app/controller/BpsAdController.php
@@ -97,6 +97,15 @@ class BpsAdController
         // 获取客户ID数组
         $accountIds = array_unique(array_column($accounts, 'account_id'));
 
+        $endDateLastWeek   = (int)date('Ymd');
+        $startDateLastWeek = (int)date('Ymd', strtotime('-6 days'));
+//        dump($startDateLastWeek, $endDateLastWeek);
+        if ($startDateLastWeek === $startDate && $endDateLastWeek === $endDate) {
+            $ad_data_count = $this->adsInsightService::getAdCountData($accountIds,$startDate, $endDate);
+        } else {
+            $ad_data_count = [];
+        }
+
         // 调用 Service 层查询广告列表
         $result = $this->adsInsightService::getAccountList(
             $platformType,    // 平台类型
@@ -108,6 +117,8 @@ class BpsAdController
             $endDate,          // 结束日期
         );
 
+        $result['ad_data_count'] = $ad_data_count;
+
         // 返回结果
         return $this->successResponse($result, $request);
     }
@@ -382,7 +393,7 @@ class BpsAdController
         $accountIds = array_unique(array_column($accounts, 'account_id'));
 //        dump($accountIds);
         // 调用 Service 层查询广告组列表
-        $result = $this->adsInsightService->getCreativeInsightData(
+        $result = $this->adsInsightService::getCreativeInsightData(
             $platformType,
             $accountIds,   // 客户 ID 数组
             $page,          // 页码
diff --git a/app/service/AdsInsightService.php b/app/service/AdsInsightService.php
index ffe26ee..767e5d4 100644
--- a/app/service/AdsInsightService.php
+++ b/app/service/AdsInsightService.php
@@ -19,6 +19,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
 use think\db\exception\DbException;
 use think\facade\Db as ThinkDb;
+use support\Redis;
 
 class AdsInsightService
 {
@@ -29,38 +30,155 @@ class AdsInsightService
         // 其他状态可以继续添加
     ];
 
+    // Redis 键名前缀
+    const REDIS_KEY_PREFIX = 'ad_data_count:';
+
+    /**
+     * 获取广告数据的各项总数(统一接口)
+     *
+     * @param array $customerIds 客户 ID 数组
+     * @return array 各项 count 统计数据
+     */
+    public static function getAdCountData($customerIds, $startDate, $endDate)
+    {
+        if (empty($customerIds)) {
+            return [];
+        }
+        // 生成唯一的哈希键
+        $hashKey  = self::generateHashKey($customerIds);
+        $redisKey = self::REDIS_KEY_PREFIX . $hashKey;
+
+        // 尝试从 Redis 中获取缓存值
+        $cachedData = Redis::get($redisKey);
+        if (!empty($cachedData)) {
+            return json_decode($cachedData, true);
+        }
+
+        // 没有缓存时重新计算
+        $countData = self::calculateCountData($customerIds, $startDate, $endDate);
+
+        // 缓存到 Redis,有效期 10 分钟
+        Redis::setex($redisKey, 600, json_encode($countData));
+
+        return $countData;
+    }
+
+
+    /**
+     * 计算广告数据的 count
+     */
+    protected static function calculateCountData(array $customerIds, $startDate, $endDate)
+    {
+        if (!$startDate || !$endDate) {
+            [$startDate, $endDate] = self::getLastWeekDateRange();
+        }
+        return [
+            'account_list_count' => self::getAccountListCount($customerIds, $startDate, $endDate),
+            'campaign_list_count' => self::getCampaignListCount($customerIds, $startDate, $endDate),
+            'adset_list_count' => self::getAdsetListCount($customerIds, $startDate, $endDate),
+            'ad_list_count' => self::getAdListCount($customerIds, $startDate, $endDate),
+            'creative_list_count' => self::getCreativeListCount($customerIds, $startDate, $endDate),
+        ];
+    }
+
+    /**
+     * 生成 Redis 键的唯一哈希值
+     */
+    protected static function generateHashKey(array $customerIds)
+    {
+        sort($customerIds);
+        return md5(json_encode($customerIds));
+    }
+
+    /**
+     * 获取广告列表的 count
+     */
+    protected static function getAdListCount(array $customerIds, $startDate, $endDate)
+    {
+        return self::getAdList(0, $customerIds, 1, 1, '', $startDate, $endDate, 0, true);
+    }
+
+    /**
+     * 获取广告系列列表的 count
+     */
+    protected static function getCampaignListCount(array $customerIds, $startDate, $endDate)
+    {
+        return self::getCampaignList(0, $customerIds, 1, 1, '', $startDate, $endDate, 0, true);
+    }
+
+    /**
+     * 获取广告组列表的 count
+     */
+    protected static function getAdsetListCount(array $customerIds, $startDate, $endDate)
+    {
+        return self::getAdsetList(0, $customerIds, 1, 1, '', $startDate, $endDate, 0, true);
+    }
+
+    /**
+     * 获取账户列表的 count
+     */
+    protected static function getAccountListCount(array $customerIds, $startDate, $endDate)
+    {
+        return self::getAccountList(0, $customerIds, 1, 1, '', $startDate, $endDate, true);
+    }
+
+    /**
+     * 获取账户列表的 count
+     */
+    protected static function getCreativeListCount(array $customerIds, $startDate, $endDate)
+    {
+        return self::getCreativeInsightData(0, $customerIds, 1, 1, '', $startDate, $endDate, true);
+    }
+
+    /**
+     * 获取最近 7 天的起始和结束日期
+     */
+    protected static function getLastWeekDateRange()
+    {
+        $endDate   = (int)date('Ymd');
+        $startDate = (int)date('Ymd', strtotime('-6 days'));
+        return [$startDate, $endDate];
+    }
+
+
     /**
      * 获取广告系列列表
      */
-    public static function getCampaignList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0)
+    public static function getCampaignList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0, $countOnly = false)
     {
         // 检查 customerIds 是否为空,直接返回空结构
         if (empty($customerIds)) {
-            return [
+            return $countOnly ? 0 : [
                 'pagination' => [
                     'startIndex' => 0,
-                    'maxResults' => $pageSize,
+                    'maxResults' => 0,
                     'count' => 0,
                     'pageNo' => $page,
                     'pageSize' => $pageSize,
                     'pages' => 0,
                 ],
-                'statistics' => [
-                ],
+                'statistics' => [],
                 'data' => [],
             ];
         }
-        // 动态构建日期条件
-        $dateCondition = '';
-        if ($startDate && $endDate) {
-            $dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
-        } else {
-        }
 
         // 基础查询:广告活动和日数据表联接
         $query = BpsAdCampaign::alias('c')
             ->cache(false) // 强制不使用缓存
-            ->leftJoin('bps.bps_ads_insights d', "c.campaign_id = d.ad_campaign_id AND c.platform_type = d.platform AND {$dateCondition}")
+            ->where('c.account_id', 'in', $customerIds);
+        // 如果只需要记录条数,执行计数查询
+        if ($countOnly) {
+            return $query->count();
+        }
+
+        // 动态构建日期条件
+        $dateCondition = '';
+        if ($startDate && $endDate) {
+            $query->leftJoin('bps.bps_ads_insights d', "c.campaign_id = d.ad_campaign_id AND c.platform_type = d.platform AND {$dateCondition}");
+            $dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
+        } else {
+        }
+        $query->leftJoin('bps.bps_ads_insights d', "c.campaign_id = d.ad_campaign_id AND c.platform_type = d.platform AND {$dateCondition}")
             ->field('c.campaign_id, c.status as status, c.name, c.account_id,c.platform_type,
              COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
              COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
@@ -190,37 +308,45 @@ class AdsInsightService
     /**
      * 获取广告组列表
      */
-    public static function getAdsetList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0)
+    public static function getAdsetList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0, $countOnly = false)
     {
         // 检查 customerIds 是否为空,直接返回空结构
         if (empty($customerIds)) {
-            return [
+            return $countOnly ? 0 : [
                 'pagination' => [
                     'startIndex' => 0,
-                    'maxResults' => $pageSize,
+                    'maxResults' => 0,
                     'count' => 0,
                     'pageNo' => $page,
                     'pageSize' => $pageSize,
                     'pages' => 0,
                 ],
-                'statistics' => [
-                ],
+                'statistics' => [],
                 'data' => [],
             ];
         }
 
+        // 基础查询:广告活动和日数据表联接
+        $query = BpsAdSet::alias('s')
+            ->cache(false) // 强制不使用缓存
+            ->where('s.account_id', 'in', $customerIds);
+        // 如果只需要记录条数,执行计数查询
+        if ($countOnly) {
+            return $query->count();
+        }
+
+
         // 动态构建日期条件
         $dateCondition = '';
         if ($startDate && $endDate) {
             $dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
+            $query->leftJoin('bps.bps_ads_insights d', "s.ad_set_id = d.ad_set_id AND s.platform_type = d.platform AND {$dateCondition}");
         }
 
         // 基础查询:广告组和日数据表联接
-        $query = BpsAdSet::alias('s')
-            ->cache(false) // 强制不使用缓存
-            ->leftJoin('bps.bps_ads_insights d', "s.ad_set_id = d.ad_set_id AND s.platform_type = d.platform AND {$dateCondition}")
-            ->leftJoin('bps.bps_ads_campaign c', 's.campaign_id = c.campaign_id') // 联接广告系列表
-            ->field('s.ad_set_id, s.status as status, s.name, s.account_id,s.platform_type,c.name as campaign_name,
+
+        $query->leftJoin('bps.bps_ads_campaign c', 's.campaign_id = c.campaign_id') // 联接广告系列表
+        ->field('s.ad_set_id, s.status as status, s.name, s.account_id,s.platform_type,c.name as campaign_name,
             COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
             COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
             COALESCE(SUM(d.clicks), 0) as clicks,
@@ -243,7 +369,7 @@ class AdsInsightService
         $query->where(function ($query) use ($keyword, $platformType) {
             if ($keyword) {
 //                $query->where('s.name', 'like', '%' . $keyword . '%');
-                 $query->whereRaw('LOWER(s.name) LIKE ?', ['%' . strtolower($keyword) . '%']);
+                $query->whereRaw('LOWER(s.name) LIKE ?', ['%' . strtolower($keyword) . '%']);
             }
             if ($platformType) {
                 $platformType = (int)$platformType;
@@ -348,12 +474,11 @@ class AdsInsightService
         ];
     }
 
-    public static function getAccountList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null)
+    public static function getAccountList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $countOnly = false)
     {
-
-        // 检查 customerIds 是否为空,直接返回空结构
+        // 检查 customerIds 是否为空,直接返回计数为 0
         if (empty($customerIds)) {
-            return [
+            return $countOnly ? 0 : [
                 'pagination' => [
                     'startIndex' => 0,
                     'maxResults' => $pageSize,
@@ -366,18 +491,25 @@ class AdsInsightService
                 'data' => [],
             ];
         }
+        // 构建查询条件
+        $query = ThirdUserAdvertiser::alias('a')
+            ->cache(false)
+            ->where('a.advertiser_id', 'in', $customerIds);
 
+        // 仅计数时优化查询
+        if ($countOnly) {
+            return $query->count(); // 只查询总记录数
+        }
         // 动态构建日期条件
         $dateCondition = '';
         if ($startDate && $endDate) {
             $dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
+            $query->leftJoin('bps.bps_ads_insights d', "a.advertiser_id = d.account_id AND {$dateCondition}");
         }
 
         // 基础查询:广告和日数据表联接
-        $query = ThirdUserAdvertiser::alias('a')
-            ->cache(false) // 强制不使用缓存
-            ->leftJoin('bps.bps_ads_insights d', "a.advertiser_id = d.account_id AND {$dateCondition}")
-            ->leftJoin('bps.bps_third_user u', 'a.doc_ = u.id')  // 联接广告组表,获取 ad_set_name
+        // 其他联表及字段计算
+        $query->leftJoin('bps.bps_third_user u', 'a.doc_ = u.id')
             ->field('a.advertiser_id, a.advertiser_name,u.third_type,
         COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
         COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
@@ -403,7 +535,7 @@ class AdsInsightService
                 $query->whereRaw('LOWER(a.advertiser_name) LIKE ?', ['%' . strtolower($keyword) . '%']);
             }
             if ($platformType) {
-                $a = (int)$platformType;
+                $a                 = (int)$platformType;
                 $platformTypeNames = [1 => 'facebook', 2 => 'google', 3 => 'tiktok'];
                 $query->where('u.third_type', '=', $platformTypeNames[$a]);
             }
@@ -463,7 +595,7 @@ class AdsInsightService
             $net_profit_margin = $item['revenue'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['revenue'], 2) * 100 . '%' : '-';
             // Net Profit on Ad Spend 的计算:广告支出净利润 = (收入 - 总成本) / 广告支出
             $net_profit_on_ad_spend = $item['spend'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['spend'], 2) * 100 . '%' : '-';
-            $platformTypeIds = [ 'facebook' => 1, 'google' => 2, 'tiktok' => 3];
+            $platformTypeIds        = ['facebook' => 1, 'google' => 2, 'tiktok' => 3];
             return [
                 'user_id' => $item['advertiser_id'],
                 'platform_type' => $platformTypeIds[$item['third_type']],
@@ -510,14 +642,14 @@ class AdsInsightService
     }
 
 
-    public static function getAdList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0)
+    public static function getAdList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0, $countOnly = false)
     {
         // 检查 customerIds 是否为空,直接返回空结构
         if (empty($customerIds)) {
-            return [
+            return $countOnly ? 0 : [
                 'pagination' => [
                     'startIndex' => 0,
-                    'maxResults' => $pageSize,
+                    'maxResults' => 0,
                     'count' => 0,
                     'pageNo' => $page,
                     'pageSize' => $pageSize,
@@ -528,18 +660,25 @@ class AdsInsightService
             ];
         }
 
+        // 基础查询:广告活动和日数据表联接
+        $query = BpsAdAd::alias('a')
+            ->cache(false) // 强制不使用缓存
+            ->where('a.account_id', 'in', $customerIds);
+        // 如果只需要记录条数,执行计数查询
+        if ($countOnly) {
+            return $query->count();
+        }
+
         // 动态构建日期条件
         $dateCondition = '';
         if ($startDate && $endDate) {
             $dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
+            $query->leftJoin('bps.bps_ads_insights d', "a.ad_id = d.ad_id AND a.platform_type = d.platform AND {$dateCondition}");
         }
 
         // 基础查询:广告和日数据表联接
-        $query = BpsAdAd::alias('a')
-            ->cache(false) // 强制不使用缓存
-            ->leftJoin('bps.bps_ads_insights d', "a.ad_id = d.ad_id AND a.platform_type = d.platform AND {$dateCondition}")
-            ->leftJoin('bps.bps_ads_set s', 'a.ad_set_id = s.ad_set_id')  // 联接广告组表,获取 ad_set_name
-            ->field('a.ad_id, a.status as status, a.name, a.account_id,a.platform_type,s.name as ad_set_name,
+        $query->leftJoin('bps.bps_ads_set s', 'a.ad_set_id = s.ad_set_id')  // 联接广告组表,获取 ad_set_name
+        ->field('a.ad_id, a.status as status, a.name, a.account_id,a.platform_type,s.name as ad_set_name,
         COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
         COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
         COALESCE(SUM(d.clicks), 0) as clicks,
@@ -951,7 +1090,7 @@ class AdsInsightService
     }
 
 
-    public function getCreativeInsightData($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null)
+    public static function getCreativeInsightData($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $countOnly = false)
     {
         // 1. 创建查询对象,初始化 BpsAdCreativeInsight 查询
         $creativeDataQuery = BpsAdCreativeInsight::alias('i')
@@ -969,7 +1108,7 @@ class AdsInsightService
         if (!empty($customerIds)) {
             $creativeDataQuery->whereIn('i.account_id', $customerIds);
         } else {
-            return [
+            return $countOnly ? 0 :[
                 'data' => [],
                 'total' => 0,
                 'statistics' => [],
@@ -982,6 +1121,10 @@ class AdsInsightService
                 ]
             ];
         }
+        // 仅返回记录数时的逻辑
+        if ($countOnly) {
+            return $creativeDataQuery->count() ?: 0;
+        }
 
         // 4. 关键词过滤
         if ($keyword) {