'UNSPECIFIED', // UNSPECIFIED 1 => 'UNKNOWN', // UNKNOWN 2 => 'ENABLED', // ENABLED 3 => 'PAUSED', // PAUSED 4 => 'REMOVED', // REMOVED 5 => 'FROZEN', // FROZEN 6 => 'ARCHIVED', // ARCHIVED ]; // 状态映射数组 private static $platformMapping = [ 1 => 'meta', 2 => 'google', 3 => 'tiktok', ]; // 状态映射数组 private static $creativeTypeMapping = [ 1 => 'image', 2 => 'video', ]; public static function getLandingUrlInsightData($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $countOnly = false) { // 1. 创建查询对象,初始化 BpsAdCreativeInsight 查询 $landingUrlDataQuery = BpsAdLandingUrl::alias('i') ->join('bps.bps_ads_insights c', 'i.ad_id = c.ad_id', 'LEFT'); // 联接 bps_ads_creative 表 if ($platformType != 0) { $landingUrlDataQuery->where('i.platform_type', $platformType); // 只有 platformType 不为 0 时才添加筛选 } // 2. 日期范围筛选 if ($startDate && $endDate) { $landingUrlDataQuery->whereBetween('c.date', [$startDate, $endDate]); } // 3. 客户 ID 过滤(如果提供了) if (!empty($customerIds)) { $landingUrlDataQuery->whereIn('i.account_id', $customerIds); } else { return $countOnly ? 0 : [ 'data' => [], 'total' => 0, 'statistics' => [], 'pagination' => [ 'startIndex' => 0, 'maxResults' => $pageSize, 'count' => 0, 'pageNo' => 1, 'pageSize' => $pageSize ] ]; } // 仅返回记录数时的逻辑 if ($countOnly) { return $landingUrlDataQuery->count('distinct(i.landing_url)') ?: 0; } // 4. 关键词过滤 if ($keyword) { // $creativeDataQuery->where('c.name', 'like', '%' . $keyword . '%'); // 在 bps_ads_creative 表中查找关键词 $landingUrlDataQuery->whereRaw('LOWER(i.landing_url) LIKE ?', ['%' . strtolower($keyword) . '%']); // 在 bps_ads_creative 表中查找关键词 } // 5. 数据聚合(按 landing_url 和其他字段) $landingUrlDataQuery->group('i.landing_url, i.platform_type, i.account_id') // 按需要的字段分组 ->field([ 'i.landing_url', 'i.platform_type', 'i.account_id', ThinkDb::raw('COALESCE(SUM(c.spend) / 1000000, 0) AS total_spend'), ThinkDb::raw('COALESCE(SUM(c.purchases_value) / 1000000, 0) AS total_purchases_value'), ThinkDb::raw('COALESCE(SUM(c.purchases), 0) AS total_purchases'), ThinkDb::raw('COALESCE(SUM(c.revenue) / 1000000, 0) AS total_revenue'), ThinkDb::raw('COALESCE(SUM(c.impressions), 0) AS total_impressions'), ThinkDb::raw('COALESCE(SUM(c.clicks), 0) AS total_clicks'), ThinkDb::raw('count(distinct(i.ad_id)) AS ad_count'), ThinkDb::raw('-1 AS click_to_atc_ratio'), ThinkDb::raw('-1 AS click_to_purchase'), ThinkDb::raw('-1 AS thumbstop'), ThinkDb::raw('-1 AS hold_rate'), ThinkDb::raw('-1 AS ctr_outbound'),//Hook score Watch score Click score Convert score ThinkDb::raw('-1 AS hook_score'), ThinkDb::raw('-1 AS watch_score'), ThinkDb::raw('-1 AS click_score'), ThinkDb::raw('-1 AS convert_score'), ThinkDb::raw('-1 AS ctr_link_click'), ]); // 6. 执行查询并获取聚合结果 // $aggregatedData = $creativeDataQuery->select(); // 6. 执行查询并获取聚合结果 $aggregatedData = $landingUrlDataQuery->select()->toArray(); // 转换为数组处理 // dump($landingUrlDataQuery->getLastSql()); $totalRecords = count($aggregatedData); // 总记录数 // 计算分页起始索引 $startIndex = ($page - 1) * $pageSize; // 打印调试 SQL 查询 //$sql = $creativeDataQuery->getLastSql(); // dump($sql); // 7. 初始化广告创意的汇总数据和统计数据 $landingUrlSummaryData = []; // $statisticsData = $this->initializeStatistics(); $statisticsData = []; // 8. 遍历查询结果并计算每个 creative 的相关统计数据 foreach ($aggregatedData as $landingData) { // $landingUrl = self::normalizeUrl($landingData['landing_url']); $landingUrlHash = self::generateHash($landingData['landing_url']); if (!isset($creativeSummaryData[$landingUrlHash])) { $landingUrlSummaryData[$landingUrlHash] = [ 'landing_url' => $landingData['landing_url'], 'platform_type' => $landingData['platform_type'], 'account_id' => $landingData['account_id'], 'total_conversions_value' => 0, 'total_conversions' => 0, 'impressions' => 0, 'clicks' => 0, 'revenue' => 0, 'ad_count' => $landingData['ad_count'] ?: 0, 'spend' => 0, 'purchases' => 0, 'purchases_value' => 0, 'roas' => 0, 'aov' => '-', 'click_to_atc_ratio' => '-', 'click_to_purchase' => '-', 'thumbstop' => '-', 'hold_rate' => '-', 'ctr_outbound' => '-', 'hook_score' => '-', 'watch_score' => '-', 'click_score' => '-', 'convert_score' => '-', 'ctr_link_click' => '-', ]; } // 更新该 landing_url 的统计数据 $landingUrlSummaryData[$landingUrlHash]['spend'] += $landingData['total_spend']; $landingUrlSummaryData[$landingUrlHash]['purchases_value'] += $landingData['total_purchases_value']; $landingUrlSummaryData[$landingUrlHash]['purchases'] += $landingData['total_purchases']; $landingUrlSummaryData[$landingUrlHash]['impressions'] += $landingData['total_impressions']; $landingUrlSummaryData[$landingUrlHash]['clicks'] += $landingData['total_clicks']; $landingUrlSummaryData[$landingUrlHash]['revenue'] += $landingData['total_revenue']; // 计算 ROAS $roas = $landingUrlSummaryData[$landingUrlHash]['spend'] > 0 ? $landingUrlSummaryData[$landingUrlHash]['revenue'] / $landingUrlSummaryData[$landingUrlHash]['spend'] : 0; $landingUrlSummaryData[$landingUrlHash]['roas'] = $roas > 0 ? number_format($roas, 2) . 'X' : '-'; // 填充广告计数 // $landingUrlSummaryData[$creativeData->creative_id]['ad_count'] = rand(10, 200); // 每个 creative_id 对应一个广告 } // dump($landingUrlSummaryData); // 汇总总体统计数据 $statisticsData['spend'] = array_sum(array_column($landingUrlSummaryData, 'spend')); $statisticsData['purchases_value'] = array_sum(array_column($landingUrlSummaryData, 'purchases_value')); $statisticsData['purchases'] = array_sum(array_column($landingUrlSummaryData, 'purchases')); $statisticsData['impressions'] = array_sum(array_column($landingUrlSummaryData, 'impressions')); $statisticsData['clicks'] = array_sum(array_column($landingUrlSummaryData, 'clicks')); $statisticsData['revenue'] = array_sum(array_column($landingUrlSummaryData, 'revenue')); // 汇总统计数据 $statistics = [ 'spend' => '$' . number_format($statisticsData['spend'], 2), // 格式化金额 'purchases_value' => '$' . number_format($statisticsData['purchases_value'], 2), // 格式化金额 'purchases' => $statisticsData['purchases'], 'click_to_atc_ratio' => '-', // 格式化百分比 'click_to_purchase' => '-',// 格式化百分比 'roas' => $statisticsData['spend'] == 0 ? '-' : round($statisticsData['revenue'] / $statisticsData['spend'], 2) . 'X', 'aov' => '-', // 格式化金额 'thumbstop' => '-', 'hold_rate' => '-',// 格式化百分比 'ctr_outbound' => '-', 'cpa' => '-', // 格式化金额 'hook_score' => '-', 'watch_score' => '-', 'click_score' => '-', 'convert_score' => '-', 'ctr_link_click' => ($statisticsData['impressions'] > 0) ? number_format(($statisticsData['clicks'] / $statisticsData['impressions']) * 100, 2) . '%' : '-', // 格式化为百分比 ]; // 格式化返回的创意数据 $formattedData = array_map(function ($item) { return [ 'landing_url' => $item['landing_url'], 'platform_type' => $item['platform_type'], 'account_id' => $item['account_id'], 'ad_count' => $item['ad_count'], 'spend' => '$' . number_format($item['spend'], 2), 'purchases' => $item['purchases'], 'purchases_value' => '$' . number_format($item['purchases_value'], 2), 'click_to_atc_ratio' => '-', 'click_to_purchase' => '-',// 格式化百分比 'roas' => $item['roas'], 'aov' => '-', 'thumbstop' => '-', 'hold_rate' => '-',// 格式化百分比 'ctr_outbound' => '-', 'cpa' => '-', // 格式化金额 'hook_score' => '-', 'watch_score' => '-', 'click_score' => '-', 'convert_score' => '-', 'ctr_link_click' => ($item['impressions'] > 0) ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-', // 添加更多的格式化字段 ]; }, $landingUrlSummaryData); // $formattedData = array_values($landingUrlSummaryData); // 未分页处理 $pagedData = array_slice(array_values($formattedData), $startIndex, $pageSize); // 分页处理 // 9. 返回分页数据 return [ 'data' => $pagedData, 'total' => count($landingUrlSummaryData), 'statistics' => $statistics, // 汇总的统计数据 'pagination' => [ 'startIndex' => ($page - 1) * $pageSize, 'maxResults' => $pageSize, 'count' => count($landingUrlSummaryData), 'pageNo' => $page, 'pageSize' => $pageSize, 'pages' => ceil(count($landingUrlSummaryData) / $pageSize) ] ]; } public static function normalizeUrl($url) { $url = strtolower($url); // 统一小写 $url = preg_replace('#^https?://(www\.)?#', '', $url); // 移除协议和 www $url = rtrim($url, '/'); // 移除末尾的斜杠 return $url; } // 生成哈希值 public static function generateHash($url) { return md5($url); // 使用 MD5 生成哈希值 } public static function getPlatformType($thirdType) { $platformMapping = [ 'facebook' => 1, 'google' => 2, 'tiktok' => 3, ]; return $platformMapping[$thirdType] ?? null; // 如果不存在的第三方类型,返回 null } }