From 5d9ca56b026c7f6d9171d582d534ebb627a8825b Mon Sep 17 00:00:00 2001 From: huangguancheng Date: Fri, 17 Jan 2025 17:31:20 +0800 Subject: [PATCH] =?UTF-8?q?Dashboard=20meta=E3=80=81google=E3=80=81tiktok?= =?UTF-8?q?=E7=9A=84=E7=9C=8B=E6=9D=BF=E6=95=B0=E6=8D=AE=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B01?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controller/BpsAdController.php | 62 ++++++- app/service/AdsDashboardService.php | 275 ++++++++++++++++++++++++++++ config/route.php | 6 + 3 files changed, 334 insertions(+), 9 deletions(-) create mode 100644 app/service/AdsDashboardService.php diff --git a/app/controller/BpsAdController.php b/app/controller/BpsAdController.php index adab638..88f1a44 100644 --- a/app/controller/BpsAdController.php +++ b/app/controller/BpsAdController.php @@ -8,6 +8,7 @@ use app\service\GoogleAdsGroupService; //use app\service\GoogleAdsReportService; use app\service\AdsInsightService; +use app\service\AdsDashboardService; use app\service\LandingUrlInsightService; use app\service\GoogleOAuthService; use app\service\BpsAdAccountService; @@ -25,6 +26,13 @@ class BpsAdController */ private $adsInsightService; + /** + * /** + * @Inject + * @var AdsDashboardService + */ + private $adsDashboardService; + /** * @Inject * @var LandingUrlInsightService @@ -85,7 +93,7 @@ class BpsAdController // } else { // $ad_data_count = []; // } - // 返回结果 + // 返回结果 return $this->successResponse($ad_data_count, $request); } @@ -100,8 +108,8 @@ class BpsAdController $keyword = $options['conditions']['keyword'] ?? ''; // 关键字搜索 $platformType = $options['conditions']['platformType'] ?? 0; // 平台类型 // $status = $options['conditions']['status'] ?? 0; // 平台类型 - $startDate = $options['conditions']['startDate'] ?? null; // 开始日期 - $endDate = $options['conditions']['endDate'] ?? null; // 结束日期 + $startDate = $options['conditions']['startDate'] ?? null; // 开始日期 + $endDate = $options['conditions']['endDate'] ?? null; // 结束日期 // $dateRange = 'Last Week'; // 默认日期范围 // 根据 platformType 获取广告账户 @@ -371,7 +379,7 @@ class BpsAdController ); } - public function exportAccountsToExcel(Request $request) + public function exportAccountsToExcel(Request $request) { $options = $request->all(); $options['jwtClaims'] = $request->jwtClaims; @@ -382,8 +390,8 @@ class BpsAdController $keyword = $options['conditions']['keyword'] ?? ''; // 关键字搜索 $platformType = $options['conditions']['platformType'] ?? 0; // 关键字搜索 // $status = $options['conditions']['status'] ?? 0; // 平台类型 - $startDate = $options['conditions']['startDate'] ?? null; // 开始日期 - $endDate = $options['conditions']['endDate'] ?? null; // 结束日期 + $startDate = $options['conditions']['startDate'] ?? null; // 开始日期 + $endDate = $options['conditions']['endDate'] ?? null; // 结束日期 // $dateRange = 'Last Week'; // 默认日期范围 // 根据 platformType 获取广告账户 if ($platformType === 1) { @@ -424,7 +432,7 @@ class BpsAdController ); } - public function exportCreativesToExcel(Request $request) + public function exportCreativesToExcel(Request $request) { $options = $request->all(); $options['jwtClaims'] = $request->jwtClaims; @@ -435,8 +443,8 @@ class BpsAdController $keyword = $options['conditions']['keyword'] ?? ''; // 关键字搜索 $platformType = $options['conditions']['platformType'] ?? 0; // 关键字搜索 // $status = $options['conditions']['status'] ?? 0; // 平台类型 - $startDate = $options['conditions']['startDate'] ?? null; // 开始日期 - $endDate = $options['conditions']['endDate'] ?? null; // 结束日期 + $startDate = $options['conditions']['startDate'] ?? null; // 开始日期 + $endDate = $options['conditions']['endDate'] ?? null; // 结束日期 // $dateRange = 'Last Week'; // 默认日期范围 // 根据 platformType 获取广告账户 if ($platformType === 1) { @@ -530,6 +538,7 @@ class BpsAdController $status ); } + public function exportAdsToExcel(Request $request) { $options = $request->all(); @@ -810,6 +819,41 @@ class BpsAdController return $this->successResponse($result, $request); } + public function listCards(Request $request) + { + $options = $request->all(); + $options['jwtClaims'] = $request->jwtClaims; + + // 获取请求参数 + $cycle = $options['conditions']['cycle'] ?? 'today'; //默认today Today 1 Yesterday 2 This Month 3 This Year 4 Custom 5 +// $platformType = $options['conditions']['platformType'] ?? 0; // 平台类型 + $startDate = $options['conditions']['startDate'] ?? null; // 开始日期 + $endDate = $options['conditions']['endDate'] ?? null; // 结束日期 +// $dateRange = 'Last Week'; // 默认日期范围 + + // TODO: 匹配jwt的商户id还是登录用户id + $accounts = $this->bpsAdAccountService->getAllAdAccounts(['uid' => $options['jwtClaims']['uid']]); + + + if (empty($accounts)) { + return $this->successResponse(['data' => []], $request); + } + + // 获取客户ID数组 + $accountIds = array_unique(array_column($accounts, 'account_id')); +// dump($accountIds); + // 调用 Service 层查询广告组列表 + $result = $this->adsDashboardService->getAdCycleCard( + $accountIds, // 客户 ID 数组 + $cycle, // 页码 + $startDate, // 开始日期 + $endDate // 结束日期 + ); + + // 返回结果 + return $this->successResponse($result, $request); + } + // 可以加入一些公共方法 protected function successResponse($data, Request $request): Response diff --git a/app/service/AdsDashboardService.php b/app/service/AdsDashboardService.php new file mode 100644 index 0000000..645339a --- /dev/null +++ b/app/service/AdsDashboardService.php @@ -0,0 +1,275 @@ +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'); + //dump($customerIds); + // 2. 客户 ID 过滤(如果提供了) + if (!empty($customerIds)) { + $adcycleDataQuery->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]); + } + + // 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.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.cost_per_atc), 0) as cost_per_atc, + COALESCE(SUM(d.purchases), 0) as purchases, + COALESCE(SUM(d.purchases_value) / 1000000, 0) as purchases_value, + COALESCE(SUM(d.revenue) / 1000000, 0) as revenue, + COALESCE(SUM(d.total_cost) / 1000000, 0) as total_cost' + ) + ->group('d.platform, d.date') // 按平台和日期进行分组 + ->select(); + + // 定义字段映射关系 + $fieldMapping = [ + 'meta' => [ + 'meta_ads' => 'spend', // 示例映射 + 'roas' => 'roas', + 'cpc' => 'cpc', + 'cpm' => 'cpm', + 'meta_purchases' => 'purchases', + 'web_purchases' => 'purchases', + 'purchases' => 'purchases', + 'meta_conversion_value' => 'purchases_value', + 'web_conversion_value' => 'purchases_value', + 'conversion_value' => 'purchases_value', + 'cpoc' => 'cost_per_atc', + 'ctr' => 'ctr', + 'revenue_per_link_click' => 'revenue_per_link_click', + 'cpa' => 'cpa', + ], + 'google' => [ + 'google_ads' => 'spend', // 示例映射 + 'conversion_value' => 'purchases_value', + 'roas' => 'roas', + 'conversions' => 'purchases', + 'ctr' => 'ctr', + 'cpa' => 'cpa', + 'cpm' => 'cpm', + 'clicks' => 'clicks', + 'impressions' => 'impressions', + 'all_conversions_value' => 'purchases_value', + 'all_roas' => 'roas', + 'all_conversions' => 'purchases', + 'all_cpa' => 'cpa', + ], + 'tiktok' => [ + 'tiktok_ads' => 'spend', // 示例映射 + 'roas' => 'roas', + 'impressions' => 'impressions', + 'cpm' => 'cpm', + 'cpc' => 'cpc', + 'ctr' => 'ctr', + 'cpa' => 'cpa', + 'tiktok_shop_conversions' => 'purchases', + 'tiktok_shop_conversion_value' => 'purchases_value', + 'conversion_value' => 'purchases_value', + 'web_conversion_value' => 'purchases_value', + 'purchases' => 'purchases', + 'web_purchases' => 'purchases', + 'tiktok_gmv_max_ads' => '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']), '-'), + ], + ]; + + // 在 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']; + $purchases = (int)$data['purchases']; + $purchasesValue = (float)$data['purchases_value']; + + // 计算指标 + $roas = ($spend == 0) ? 0 : $revenue / $spend; + $ctr = ($impressions == 0) ? 0 : $clicks / $impressions; + $cpa = ($purchases == 0) ? 0 : $spend / $purchases; + $cpm = ($impressions == 0) ? 0 : ($spend * 1000) / $impressions; + $cpc = ($clicks == 0) ? 0 : $spend / $clicks; + $conversionRate = ($clicks == 0) ? 0 : $purchases / $clicks; + $revenuePerLinkClick = ($clicks == 0) ? 0 : $revenue / $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($purchasesValue, 2); + break; + case 'roas': + $value = $roas . 'X'; + break; + case 'ctr': + $value = number_format($ctr * 100, 2) . '%'; + break; + case 'cpa': + $value = '$' . number_format($cpa, 2); + break; + case 'cpm': + $value = '$' . number_format($cpm, 2); + break; + case 'cpc': + $value = '$' . number_format($cpc, 2); + break; + case 'conversion_rate': + $value = $conversionRate; + break; + case 'revenue_per_link_click': + $value = $revenuePerLinkClick; + break; + case 'purchases': + $value = $purchases; + break; + case 'clicks': + $value = $clicks; + break; + case 'impressions': + $value = $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; + } + +} diff --git a/config/route.php b/config/route.php index eeee537..d9267c2 100644 --- a/config/route.php +++ b/config/route.php @@ -89,6 +89,12 @@ Route::group('/marketing', function () { app\middleware\JwtLocal::class, app\middleware\OauthThirdCheck::class, ]); + Route::group('/dashboard', function () { + Route::post('/ad_card', [BpsAdController::class, 'listCards']); + })->middleware([ + app\middleware\JwtLocal::class, + app\middleware\OauthThirdCheck::class, + ]); Route::group('/landing', function () { Route::post('/list', [BpsAdController::class, 'listLandingUrls']); // Route::post('/export', [BpsAdController::class, 'exportLandingUrlsToExcel']);