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;
|
|
}
|
|
|
|
}
|