367 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			367 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace app\service;
 | |
| 
 | |
| 
 | |
| use app\model\BpsAdInsight;
 | |
| //use think\facade\Db as ThinkDb;
 | |
| 
 | |
| class AdsDashboardService
 | |
| {
 | |
|     public function getAdCycleCard($customerIds, $cycle, $startDate = null, $endDate = null)
 | |
|     {
 | |
| 
 | |
|         // 将 Y-m-d 格式的日期转换为纯数字格式
 | |
|         $startDateNumeric = $startDate ? str_replace('-', '', $startDate) : null;
 | |
|         $endDateNumeric   = $endDate ? str_replace('-', '', $endDate) : null;
 | |
| 
 | |
|         // 获取当前周期的数据
 | |
|         $currentData = $this->getAdcycleInsight($customerIds, $cycle, $startDateNumeric, $endDateNumeric);
 | |
|         return $currentData;
 | |
| 
 | |
|         // 计算前一天的日期
 | |
| //        $previousStartDate = $startDate ?
 | |
| //            (new DateTime($startDate))->modify('-1 day')->format('Ymd') :
 | |
| //            null;
 | |
| //
 | |
| //        $previousEndDate = $endDate ?
 | |
| //            (new DateTime($endDate))->modify('-1 day')->format('Ymd') :
 | |
| //            null;
 | |
| 
 | |
|         // 获取前一天周期的数据
 | |
| //        $lastData = $this->getAdcycleInsight($customerIds, $cycle, $previousStartDate, $previousEndDate);
 | |
| 
 | |
|     }
 | |
| 
 | |
|     public function getAdcycleInsight($customerIds, $cycle, $startDate = null, $endDate = null)
 | |
|     {
 | |
|         // 1. 查询全部数据集
 | |
|         $adcycleDataQuery = BpsAdInsight::alias('d');
 | |
| 
 | |
|         $adRevenueQuery = BpsAdInsight::alias('d')
 | |
|             ->where('d.platform', 3); // 过滤 platform=3
 | |
|         //dump($customerIds);
 | |
|         // 2. 客户 ID 过滤(如果提供了)
 | |
|         if (!empty($customerIds)) {
 | |
|             $adcycleDataQuery->whereIn('d.account_id', $customerIds);
 | |
|             $adRevenueQuery->whereIn('d.account_id', $customerIds);
 | |
|         } else {
 | |
|             return [
 | |
|                 'data' => [
 | |
|                     'meta' => [
 | |
|                         'meta_ads' => '-',
 | |
|                         'roas' => '-',
 | |
|                         'cpc' => '-',
 | |
|                         'cpm' => '-',
 | |
|                         'meta_purchases' => '-',
 | |
|                         'web_purchases' => '-',
 | |
|                         'purchases' => '-',
 | |
|                         'meta_conversion_value' => '-',
 | |
|                         'web_conversion_value' => '-',
 | |
|                         'conversion_value' => '-',
 | |
|                         'cpoc' => '-',
 | |
|                         'ctr' => '-',
 | |
|                         'revenue_per_link_click' => '-',
 | |
|                         'cpa' => '-',
 | |
|                     ],
 | |
|                     'google' => [
 | |
|                         'google_ads' => '-',
 | |
|                         'conversion_value' => '-',
 | |
|                         'roas' => '-',
 | |
|                         'conversions' => '-',
 | |
|                         'ctr' => '-',
 | |
|                         'cpa' => '-',
 | |
|                         'cpm' => '-',
 | |
|                         'clicks' => '-',
 | |
|                         'impressions' => '-',
 | |
|                         'all_conversions_value' => '-',
 | |
|                         'all_roas' => '-',
 | |
|                         'all_conversions' => '-',
 | |
|                         'all_cpa' => '-',
 | |
|                     ],
 | |
|                     'tiktok' => [
 | |
|                         'tiktok_ads' => '-',
 | |
|                         'roas' => '-',
 | |
|                         'impressions' => '-',
 | |
|                         'cpm' => '-',
 | |
|                         'cpc' => '-',
 | |
|                         'ctr' => '-',
 | |
|                         'cpa' => '-',
 | |
|                         'tiktok_shop_conversions' => '-',
 | |
|                         'tiktok_shop_conversion_value' => '-',
 | |
|                         'conversion_value' => '-',
 | |
|                         'web_conversion_value' => '-',
 | |
|                         'purchases' => '-',
 | |
|                         'web_purchases' => '-',
 | |
|                         'tiktok_gmv_max_ads' => '-',
 | |
|                     ]
 | |
|                 ]
 | |
| 
 | |
|             ];
 | |
|         }
 | |
|         // 3. 时间范围筛选
 | |
|         if ($startDate && $endDate) {
 | |
|             $adcycleDataQuery->whereBetween('d.date', [$startDate, $endDate]);
 | |
|             $adRevenueQuery->whereBetween('d.date', [$startDate, $endDate]);
 | |
|         }
 | |
| 
 | |
|         // 4. 获取数据并按平台和日期聚合
 | |
|         $adcycleData = $adcycleDataQuery->field(
 | |
|             'd.platform,
 | |
|         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,
 | |
|         COALESCE(SUM(d.outbound_clicks), 0) as outbound_clicks,
 | |
|         COALESCE(SUM(d.spend) / 1000000, 0) as spend,
 | |
|         COALESCE(SUM(d.impressions), 0) as impressions,
 | |
|         COALESCE(SUM(d.adds_to_cart), 0) as adds_to_cart,
 | |
|         COALESCE(SUM(d.purchases), 0) as purchases,
 | |
|         COALESCE(SUM(d.onsite_purchase), 0) as onsite_purchase,
 | |
|         COALESCE(SUM(d.offsite_purchase), 0) as offsite_purchase,
 | |
|         COALESCE(SUM(d.purchases_all), 0) as purchases_all,
 | |
|         COALESCE(SUM(d.tiktok_shop_conversion), 0) as tiktok_shop_conversion,
 | |
|         COALESCE(SUM(d.platform_purchase), 0) as platform_purchase,
 | |
|         COALESCE(SUM(d.purchases_value) / 1000000, 0) as purchases_value,
 | |
|         COALESCE(SUM(d.onsite_purchases_value) / 1000000, 0) as onsite_purchases_value,
 | |
|         COALESCE(SUM(d.offsite_purchases_value) / 1000000, 0) as offsite_purchases_value,
 | |
|         COALESCE(SUM(d.platform_purchase_value) / 1000000, 0) as platform_purchase_value,
 | |
|         COALESCE(SUM(d.tiktok_shop_conversion_value) / 1000000, 0) as tiktok_shop_conversion_value,
 | |
|         COALESCE(SUM(d.revenue) / 1000000, 0) as revenue,
 | |
|         COALESCE(SUM(d.total_cost) / 1000000, 0) as total_cost'
 | |
|         )
 | |
|             ->group('d.platform') // 按平台和日期进行分组
 | |
|             ->select();
 | |
| //         dump(ThinkDb::getLastSql());
 | |
|         // 4. 获取tiktok数据并按 ad_id 分组,计算 revenue 的总和
 | |
|         $adRevenueData = $adRevenueQuery->field(
 | |
|             'd.ad_id, COALESCE(SUM(d.revenue) / 1000000, 0) as total_revenue' // 计算 revenue 的总和
 | |
|         )
 | |
|             ->group('d.ad_id') // 按 ad_id 分组
 | |
|             ->select();
 | |
| //         dump(ThinkDb::getLastSql());
 | |
|         // 定义字段映射关系
 | |
|         $fieldMapping = [
 | |
|             'meta' => [
 | |
|                 'meta_ads' => 'spend', // 示例映射
 | |
|                 'roas' => 'meta_roas',
 | |
|                 'cpc' => 'cpc',
 | |
|                 'cpm' => 'cpm',
 | |
|                 'meta_purchases' => 'onsite_purchase',
 | |
|                 'web_purchases' => 'offsite_purchase',
 | |
|                 'purchases' => 'platform_purchase',
 | |
|                 'meta_conversion_value' => 'meta_conversion_value',
 | |
|                 'web_conversion_value' => 'web_conversion_value',
 | |
|                 'conversion_value' => 'platform_purchase_value',
 | |
|                 'cpoc' => 'cpoc',
 | |
|                 'ctr' => 'ctr',
 | |
|                 'revenue_per_link_click' => 'revenue_per_link_click',
 | |
|                 'cpa' => 'cpa',
 | |
|             ],
 | |
|             'google' => [
 | |
|                 'google_ads' => 'spend', // 示例映射
 | |
|                 'conversion_value' => 'platform_purchase_value',
 | |
|                 'roas' => 'google_roas',
 | |
|                 'conversions' => 'platform_purchase',
 | |
|                 'ctr' => 'ctr',
 | |
|                 'cpa' => 'google_cpa',
 | |
|                 'cpm' => 'cpm',
 | |
|                 'clicks' => 'clicks',
 | |
|                 'impressions' => 'impressions',
 | |
|                 'all_conversions_value' => 'revenue',
 | |
|                 'all_roas' => 'roas',
 | |
| //                'all_conversions' => 'purchases',
 | |
|                 'all_conversions' => 'platform_purchase', //2025-02-24临时改跟conversions一样
 | |
|                 'all_cpa' => 'cpa',
 | |
|             ],
 | |
|             'tiktok' => [
 | |
|                 'tiktok_ads' => 'spend', // 示例映射
 | |
|                 'roas' => 'roas',
 | |
|                 'impressions' => 'impressions',
 | |
|                 'cpm' => 'cpm',
 | |
|                 'cpc' => 'cpc',
 | |
|                 'ctr' => 'ctr',
 | |
|                 'cpa' => 'cpa',
 | |
|                 'tiktok_shop_conversions' => 'tiktok_shop_conversion',
 | |
|                 'tiktok_shop_conversion_value' => 'tiktok_shop_conversion_value',
 | |
|                 'conversion_value' => 'platform_purchase_value',
 | |
|                 'web_conversion_value' => 'revenue',
 | |
|                 'purchases' => 'platform_purchase',
 | |
|                 'web_purchases' => 'purchases',
 | |
|                 'tiktok_gmv_max_ads' => 'max_ad_revenue',
 | |
|             ],
 | |
|         ];
 | |
| 
 | |
|         // 初始化结果数组
 | |
|         $result = [
 | |
|             'data' => [
 | |
|                 'meta' => array_fill_keys(array_keys($fieldMapping['meta']), '-'),
 | |
|                 'google' => array_fill_keys(array_keys($fieldMapping['google']), '-'),
 | |
|                 'tiktok' => array_fill_keys(array_keys($fieldMapping['tiktok']), '-'),
 | |
|             ],
 | |
|         ];
 | |
| 
 | |
|         // 5. 计算最大的 total_revenue
 | |
|         $maxAdRevenue = 0;
 | |
|         foreach ($adRevenueData as $data) {
 | |
|             if ($data['total_revenue'] > $maxAdRevenue) {
 | |
|                 $maxAdRevenue = $data['total_revenue'];
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         // 在 PHP 中计算指标
 | |
|         foreach ($adcycleData as $data) {
 | |
|             $platform                     = $data['platform']; // 获取平台类型
 | |
|             $spend                        = (float)$data['spend'];
 | |
|             $revenue                      = (float)$data['revenue'];
 | |
|             $totalCost                    = (float)$data['total_cost'];
 | |
|             $impressions                  = (int)$data['impressions'];
 | |
|             $clicks                       = (int)$data['clicks'];
 | |
|             $outbound_clicks              = (int)$data['outbound_clicks'];
 | |
|             $purchases                    = (int)$data['purchases'];
 | |
|             $purchases_all                = (int)$data['purchases_all'];
 | |
|             $tiktok_shop_conversion       = (int)$data['tiktok_shop_conversion'];
 | |
|             $platform_purchase            = (int)$data['platform_purchase'];
 | |
|             $onsite_purchase              = (int)$data['onsite_purchase'];
 | |
|             $offsite_purchase             = (int)$data['offsite_purchase'];
 | |
|             $purchases_value              = (float)$data['purchases_value'];
 | |
|             $platform_purchase_value      = (float)$data['platform_purchase_value'];
 | |
|             $conversion_value             = $platform_purchase_value; //meta的$conversion_value=$platform_purchase_value
 | |
|             $tiktok_shop_conversion_value = (float)$data['tiktok_shop_conversion_value'];
 | |
|             $web_conversion_value         = (float)$data['offsite_purchases_value'];
 | |
|             $meta_conversion_value        = (float)$data['onsite_purchases_value'];
 | |
| 
 | |
|             // 计算指标
 | |
|             $roas                = ($spend == 0) ? 0 : $revenue / $spend;
 | |
|             $meta_roas           = ($spend == 0) ? 0 : $conversion_value / $spend;
 | |
|             $google_roas         = ($spend == 0) ? 0 : $purchases_value / $spend;
 | |
|             $ctr                 = ($impressions == 0) ? 0 : $clicks / $impressions;
 | |
|             $cpoc                = ($outbound_clicks == 0) ? 0 : $spend / $outbound_clicks;
 | |
|             $cpa                 = ($platform_purchase == 0) ? 0 : $spend / $platform_purchase;
 | |
|             $google_cpa          = ($platform_purchase == 0) ? 0 : $spend / $platform_purchase;
 | |
|             $cpm                 = ($impressions == 0) ? 0 : ($spend * 1000) / $impressions;
 | |
|             $cpc                 = ($clicks == 0) ? 0 : $spend / $clicks;
 | |
|             $conversionRate      = ($clicks == 0) ? 0 : $purchases / $clicks;
 | |
|             $revenuePerLinkClick = ($outbound_clicks == 0) ? 0 : $conversion_value / $outbound_clicks;
 | |
| 
 | |
|             // 根据平台类型映射数据
 | |
|             $platformKey = match ($platform) {
 | |
|                 1 => 'meta',
 | |
|                 2 => 'google',
 | |
|                 3 => 'tiktok',
 | |
|                 default => null,
 | |
|             };
 | |
| 
 | |
|             if ($platformKey) {
 | |
|                 foreach ($fieldMapping[$platformKey] as $targetField => $sourceField) {
 | |
|                     $value = '-';
 | |
|                     switch ($sourceField) {
 | |
|                         case 'spend':
 | |
|                             $value = '$' . number_format($spend, 2);
 | |
|                             break;
 | |
|                         case 'revenue':
 | |
|                             $value = '$' . number_format($revenue, 2);
 | |
|                             break;
 | |
|                         case 'total_cost':
 | |
|                             $value = '$' . number_format($totalCost, 2);
 | |
|                             break;
 | |
|                         case 'purchases_value':
 | |
|                             $value = '$' . number_format($purchases_value, 2);
 | |
|                             break;
 | |
|                         case 'platform_purchase_value':
 | |
|                             $value = '$' . number_format($platform_purchase_value, 2);
 | |
|                             break;
 | |
|                         case 'roas':
 | |
|                             $value = round($roas, 2) . 'x';
 | |
|                             break;
 | |
|                         case 'meta_roas':
 | |
|                             $value = round($meta_roas, 2) . 'x';
 | |
|                             break;
 | |
|                         case 'google_roas':
 | |
|                             $value = round($google_roas, 2) . 'x';
 | |
|                             break;
 | |
|                         case 'cpoc':
 | |
|                             $value = '$' . number_format($cpoc, 2);
 | |
|                             break;
 | |
|                         case 'ctr':
 | |
|                             $value = number_format($ctr * 100, 2) . '%';
 | |
|                             break;
 | |
|                         case 'cpa':
 | |
|                             $value = '$' . number_format($cpa, 2);
 | |
|                             break;
 | |
|                         case 'google_cpa':
 | |
|                             $value = '$' . number_format($google_cpa, 2);
 | |
|                             break;
 | |
|                         case 'cpm':
 | |
|                             $value = '$' . number_format($cpm, 2);
 | |
|                             break;
 | |
|                         case 'cpc':
 | |
|                             $value = '$' . number_format($cpc, 2);
 | |
|                             break;
 | |
|                         case 'meta_conversion_value':
 | |
|                             $value = '$' . number_format($meta_conversion_value, 2);
 | |
|                             break;
 | |
|                         case 'web_conversion_value':
 | |
|                             $value = '$' . number_format($web_conversion_value, 2);
 | |
|                             break;
 | |
|                         case 'conversion_value':
 | |
|                             $value = '$' . number_format($conversion_value, 2);
 | |
|                             break;
 | |
|                         case 'conversion_rate':
 | |
|                             $value = $conversionRate;
 | |
|                             break;
 | |
|                         case 'tiktok_shop_conversion':
 | |
|                             $value = number_format($tiktok_shop_conversion);
 | |
|                             break;
 | |
|                         case 'tiktok_shop_conversion_value':
 | |
|                             $value = '$' . number_format($tiktok_shop_conversion_value, 2);
 | |
|                             break;
 | |
|                         case 'max_ad_revenue':
 | |
|                             $value = '$' . number_format($maxAdRevenue, 2);
 | |
|                             break;
 | |
|                         case 'revenue_per_link_click':
 | |
|                             $value = '$' . number_format($revenuePerLinkClick, 2);
 | |
|                             break;
 | |
|                         case 'purchases':
 | |
|                             $value = number_format($purchases);
 | |
|                             break;
 | |
|                         case 'purchases_all':
 | |
|                             $value = number_format($purchases_all);
 | |
|                             break;
 | |
|                         case 'platform_purchase':
 | |
|                             $value = number_format($platform_purchase);
 | |
|                             break;
 | |
|                         case 'onsite_purchase':
 | |
|                             $value = number_format($onsite_purchase);
 | |
|                             break;
 | |
|                         case 'offsite_purchase':
 | |
|                             $value = number_format($offsite_purchase);
 | |
|                             break;
 | |
|                         case 'clicks':
 | |
|                             $value = number_format($clicks);
 | |
|                             break;
 | |
|                         case 'impressions':
 | |
|                             $value = number_format($impressions);
 | |
|                             break;
 | |
|                         default:
 | |
|                             $value = '-';
 | |
|                             break;
 | |
|                     }
 | |
|                     $result['data'][$platformKey][$targetField] = $value;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $result;
 | |
| 
 | |
| //        dump(ThinkDb::getLastSql());
 | |
| 
 | |
|         // 5. 处理数据,按照周期进行分组
 | |
| //        $processedData = $this->processDataByCycle($adcycleData->toArray(), $cycle);
 | |
| 
 | |
|         // 6. 返回处理后的数据
 | |
| //        return $adcycleData;
 | |
|     }
 | |
| 
 | |
| }
 | 
