creative图表的日期排序、格式化

This commit is contained in:
huangguancheng 2025-01-10 21:50:48 +08:00
parent 6a9d7337c5
commit 368c45f050

View File

@ -13,6 +13,7 @@ use app\model\BpsAdAd;
use app\model\ThirdUser;
use app\model\Asset;
use app\model\AssetRelation;
use DateTime;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use think\db\exception\DbException;
@ -103,7 +104,7 @@ class AdsInsightService
$statistics = [
'assisted_purchases' => array_sum(array_column($allCampaigns, 'assisted_purchases')),
'last_clicked_purchases' => array_sum(array_column($allCampaigns, 'last_clicked_purchases')),
'roas' => $total_spend == 0 ? '-' : round($total_purchases_value / $total_spend, 2),
'roas' => $total_spend == 0 ? '-' : round($total_revenue / $total_spend, 2) . 'X',
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
'clicks' => $total_clicks,
'impressions' => $total_impressions,
@ -127,7 +128,16 @@ class AdsInsightService
// 确保数据格式统一
$result = array_map(function ($item) {
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-'; // 格式化为百分比
// CTR 的计算:点击率 = 点击数 / 展示数
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-';
// Conversion Rate 的计算:转换率 = 购买数 / 点击数
$conversion_rate = $item['clicks'] > 0 ? round(($item['purchases'] / $item['clicks']) * 100, 2) . '%' : '-';
// Net Profit 的计算:净利润 = 收入 - 总成本
$net_profit = ($item['revenue'] - $item['total_cost']) >= 0 ? '+$' . number_format($item['revenue'] - $item['total_cost'], 2) : '-$' . number_format($item['revenue'] - $item['total_cost'], 2);
// Net Profit Margin 的计算:净利润率 = 净利润 / 收入
$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) : '-';
return [
'id' => $item['campaign_id'],
'platform_type' => $item['platform_type'],
@ -136,7 +146,7 @@ class AdsInsightService
'status' => $item['status'],
'assisted_purchases' => $item['assisted_purchases'],
'last_clicked_purchases' => $item['last_clicked_purchases'],
'roas' => $item['spend'] == 0 ? '-' : round($item['purchases_value'] / $item['spend'], 2),
'roas' => $item['spend'] == 0 ? '-' : round($item['revenue'] / $item['spend'], 2) . 'X',
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
'impressions' => $item['impressions'],
'clicks' => $item['clicks'],
@ -148,10 +158,10 @@ class AdsInsightService
'cost_per_purchase' => $item['purchases'] > 0 ? '$' . number_format(($item['spend'] / $item['purchases']), 2) : '$0.00',
'revenue' => '$' . number_format($item['revenue'], 2), // 格式化收入
'total_cost' => '$' . number_format($item['total_cost'], 2), // 格式化总成本
'conversion_rate' => '-', // 没有提供有效的计算,保持为 '-'
'net_profit' => '-', // 没有提供 net_profit 计算,保持为 '-'
'net_profit_margin' => '-', // 没有提供 net_profit_margin 计算,保持为 '-'
'net_profit_on_ad_spend' => '-', // 没有提供 net_profit_on_ad_spend 计算,保持为 '-'
'conversion_rate' => $conversion_rate,
'net_profit' => $net_profit,
'net_profit_margin' => $net_profit_margin,
'net_profit_on_ad_spend' => $net_profit_on_ad_spend,
];
}, $campaigns->items());
@ -243,6 +253,7 @@ class AdsInsightService
$total_impressions = array_sum(array_column($allAdsets, 'impressions'));
$total_clicks = array_sum(array_column($allAdsets, 'clicks'));
$total_purchases_value = array_sum(array_column($allAdsets, 'purchases_value'));
$total_revenue = array_sum(array_column($allAdsets, 'revenue'));
$total_purchases = array_sum(array_column($allAdsets, 'purchases'));
$cost_per_purchase = $total_purchases == 0 ? 0 : round($total_spend / $total_purchases, 2);
@ -250,7 +261,7 @@ class AdsInsightService
$statistics = [
'assisted_purchases' => array_sum(array_column($allAdsets, 'assisted_purchases')),
'last_clicked_purchases' => array_sum(array_column($allAdsets, 'last_clicked_purchases')),
'roas' => $total_spend == 0 ? '-' : round($total_purchases_value / $total_spend, 2),
'roas' => $total_spend == 0 ? '-' : round($total_revenue / $total_spend, 2) . 'X',
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
'clicks' => $total_clicks,
'impressions' => array_sum(array_column($allAdsets, 'impressions')),
@ -261,10 +272,10 @@ class AdsInsightService
'cost_per_purchase' => '$' . number_format($cost_per_purchase, 2) ?: '$0.00',
'revenue' => '$' . number_format(array_sum(array_column($allAdsets, 'revenue')), 2) ?: '$0.00', // 格式化收入
'total_cost' => '$' . number_format($total_cost, 2) ?: '$0.00', // 格式化总成本
'conversion_rate' => '-', // 没有计算逻辑,保持为 '-'
'net_profit' => '-', // 没有计算 net_profit保持为 '-'
'net_profit_margin' => '-', // 没有计算 net_profit_margin保持为 '-'
'net_profit_on_ad_spend' => '-', // 没有计算 net_profit_on_ad_spend保持为 '-'
'conversion_rate' => $total_clicks == 0 ? '-' : round(($total_purchases / $total_clicks) * 100, 2) . '%', // 转换率
'net_profit' => ($total_revenue - $total_cost) >= 0 ? '+$' . number_format($total_revenue - $total_cost, 2) : '-$' . number_format($total_revenue - $total_cost, 2), // 净利润
'net_profit_margin' => $total_revenue == 0 ? '-' : round(($total_revenue - $total_cost) / $total_revenue, 2) * 100 . '%', // 净利润率
'net_profit_on_ad_spend' => $total_spend == 0 ? '-' : round(($total_revenue - $total_cost) / $total_spend, 2), // 广告支出净利润
// 计算总的 CTR
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
];
@ -274,7 +285,16 @@ class AdsInsightService
// 确保数据格式统一
$result = array_map(function ($item) {
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-'; // 格式化为百分比
// CTR 的计算:点击率 = 点击数 / 展示数
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-';
// Conversion Rate 的计算:转换率 = 购买数 / 点击数
$conversion_rate = $item['clicks'] > 0 ? round(($item['purchases'] / $item['clicks']) * 100, 2) . '%' : '-';
// Net Profit 的计算:净利润 = 收入 - 总成本
$net_profit = ($item['revenue'] - $item['total_cost']) >= 0 ? '+$' . number_format($item['revenue'] - $item['total_cost'], 2) : '-$' . number_format($item['revenue'] - $item['total_cost'], 2);
// Net Profit Margin 的计算:净利润率 = 净利润 / 收入
$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) : '-';
return [
'id' => $item['ad_set_id'],
'platform_type' => $item['platform_type'],
@ -284,7 +304,7 @@ class AdsInsightService
'status' => $item['status'],
'assisted_purchases' => $item['assisted_purchases'],
'last_clicked_purchases' => $item['last_clicked_purchases'],
'roas' => $item['spend'] == 0 ? '-' : round($item['purchases_value'] / $item['spend'], 2),
'roas' => $item['spend'] == 0 ? '-' : round($item['purchases_value'] / $item['spend'], 2) . 'X',
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
'impressions' => $item['impressions'],
'clicks' => $item['clicks'],
@ -296,10 +316,10 @@ class AdsInsightService
'cost_per_purchase' => $item['purchases'] > 0 ? '$' . number_format(($item['spend'] / $item['purchases']), 2) : '$0.00',
'revenue' => '$' . number_format($item['revenue'], 2), // 格式化收入
'total_cost' => '$' . number_format($item['total_cost'], 2), // 格式化总成本
'conversion_rate' => '-', // 没有提供有效的计算,保持为 '-'
'net_profit' => '-', // 没有提供 net_profit 计算,保持为 '-'
'net_profit_margin' => '-', // 没有提供 net_profit_margin 计算,保持为 '-'
'net_profit_on_ad_spend' => '-', // 没有提供 net_profit_on_ad_spend 计算,保持为 '-'
'conversion_rate' => $conversion_rate, // 没有提供有效的计算,保持为 '-'
'net_profit' => $net_profit, // 没有提供 net_profit 计算,保持为 '-'
'net_profit_margin' => $net_profit_margin, // 没有提供 net_profit_margin 计算,保持为 '-'
'net_profit_on_ad_spend' => $net_profit_on_ad_spend, // 没有提供 net_profit_on_ad_spend 计算,保持为 '-'
];
}, $adsets->items());
@ -388,6 +408,7 @@ class AdsInsightService
$total_impressions = array_sum(array_column($allAds, 'impressions'));
$total_clicks = array_sum(array_column($allAds, 'clicks'));
$total_purchases_value = array_sum(array_column($allAds, 'purchases_value'));
$total_revenue = array_sum(array_column($allAds, 'revenue'));
$total_purchases = array_sum(array_column($allAds, 'purchases'));
$cost_per_purchase = $total_purchases == 0 ? 0 : round($total_spend / $total_purchases, 2);
@ -405,10 +426,10 @@ class AdsInsightService
'cost_per_purchase' => '$' . number_format($cost_per_purchase, 2) ?: '$0.00',
'revenue' => '$' . number_format(array_sum(array_column($allAds, 'revenue')), 2) ?: '$0.00', // 格式化收入
'total_cost' => '$' . number_format($total_cost, 2) ?: '$0.00', // 格式化总成本
'conversion_rate' => '-', // 没有计算逻辑,保持为 '-'
'net_profit' => '-', // 没有计算 net_profit保持为 '-'
'net_profit_margin' => '-', // 没有计算 net_profit_margin保持为 '-'
'net_profit_on_ad_spend' => '-', // 没有计算 net_profit_on_ad_spend保持为 '-'
'conversion_rate' => $total_clicks == 0 ? '-' : round(($total_purchases / $total_clicks) * 100, 2) . '%', // 转换率
'net_profit' => ($total_revenue - $total_cost) >= 0 ? '+$' . number_format($total_revenue - $total_cost, 2) : '-$' . number_format($total_revenue - $total_cost, 2), // 净利润
'net_profit_margin' => $total_revenue == 0 ? '-' : round(($total_revenue - $total_cost) / $total_revenue, 2) * 100 . '%', // 净利润率
'net_profit_on_ad_spend' => $total_spend == 0 ? '-' : round(($total_revenue - $total_cost) / $total_spend, 2),
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
];
@ -669,9 +690,9 @@ class AdsInsightService
// 1. 创建查询对象,初始化 BpsAdCreativeInsight 查询
$creativeDataQuery = BpsAdCreativeInsight::alias('i')
->join('bps.bps_ads_creative c', 'i.creative_id = c.creative_id', 'LEFT'); // 联接 bps_ads_creative 表
if ($platformType != 0) {
$creativeDataQuery->where('i.platform', $platformType); // 只有 platformType 不为 0 时才添加筛选
}
if ($platformType != 0) {
$creativeDataQuery->where('i.platform', $platformType); // 只有 platformType 不为 0 时才添加筛选
}
// 2. 日期范围筛选
if ($startDate && $endDate) {
@ -702,11 +723,12 @@ class AdsInsightService
}
// 5. 数据聚合(按 creative_id 和其他字段)
$creativeDataQuery->group('i.creative_id, i.platform, i.account_id, c.name, c.url, c.thumbnail_url') // 按需要的字段分组
$creativeDataQuery->group('i.creative_id, i.platform, i.account_id, c.name, c.type,c.url, c.thumbnail_url') // 按需要的字段分组
->field([
'i.creative_id',
'c.name AS creative_name', // 从 bps_ads_creative 表中选择 name
'c.url AS creative_url', // 从 bps_ads_creative 表中选择 url
'c.type AS creative_type', // 从 bps_ads_creative 表中选择 url
'c.thumbnail_url', // 从 bps_ads_creative 表中选择 thumbnail_url
ThinkDb::raw('SUM(i.spend) AS total_spend'),
ThinkDb::raw('SUM(i.purchases_value) AS total_conversions_value'),
@ -737,9 +759,10 @@ class AdsInsightService
$creativeSummaryData[$creativeData->creative_id] = [
'creative_id' => $creativeData->creative_id,
'creative' => $creativeData->creative_name, // 使用联接查询中的 creative_name
'creative_url' => $creativeData->creative_url?: '', // 使用联接查询中的 creative_url
'thumbnail_url' => $creativeData->thumbnail_url?: '', // 使用联接查询中的 thumbnail_url
'title' => $creativeData->title?: '', // 使用联接查询中的 title
'creative_type' => $creativeData->creative_type, // 使用联接查询中的 creative_name
'creative_url' => $creativeData->creative_url ?: '', // 使用联接查询中的 creative_url
'thumbnail_url' => $creativeData->thumbnail_url ?: '', // 使用联接查询中的 thumbnail_url
'title' => $creativeData->title ?: '', // 使用联接查询中的 title
'spend' => 0,
'purchase_value' => '-',
'roas' => 0,
@ -841,8 +864,10 @@ class AdsInsightService
public function getAdcycleInsight($platformType, $customerIds, $cycle, $startDate = null, $endDate = null)
{
// 1. 查询全部数据集
$adcycleDataQuery = BpsAdInsight::alias('i')
->where('i.platform', $platformType); // 根据 platform 筛选
$adcycleDataQuery = BpsAdInsight::alias('i');
if ($platformType != 0) {
$adcycleDataQuery->where('i.platform', $platformType); // 只有 platformType 不为 0 时才添加筛选
}
//dump($customerIds);
// 2. 客户 ID 过滤(如果提供了)
if (!empty($customerIds)) {
@ -871,7 +896,7 @@ class AdsInsightService
// dump(ThinkDb::getLastSql());
// 5. 处理数据,按照周期进行分组
$processedData = $this->processDataByCycle($adcycleData, $cycle);
$processedData = $this->processDataByCycle($adcycleData->toArray(), $cycle);
// 6. 返回处理后的数据
return $processedData;
@ -886,21 +911,27 @@ class AdsInsightService
*/
private function processDataByCycle($adcycleData, $cycle)
{
// 排序 adcycleData 数组,确保按照日期升序排列
usort($adcycleData, function ($a, $b) {
return $a['date'] <=> $b['date']; // 按照 'YYYYMMDD' 字符顺序排序
});
$groupedData = [];
foreach ($adcycleData as $data) {
// 根据周期类型,调整日期分组
switch ($cycle) {
case 1:
$key = $data->date; // 按日期直接分组
// 将 'YYYYMMDD' 格式的日期转换为 'YYYY-MM-DD'
$date = DateTime::createFromFormat('Ymd', $data['date']); // 转换为 DateTime 对象
$key = $date ? $date->format('Y-m-d') : ''; // 格式化为 'YYYY-MM-DD'
break;
case 2:
// 使用 ISO 周格式来分组
$key = $this->getWeekFromDate($data->date);
$key = $this->getWeekFromDate($data['date']);
break;
case 3:
// 按年-月格式分组
$key = $this->getMonthFromDate($data->date);
$key = $this->getMonthFromDate($data['date']);
break;
default:
throw new \InvalidArgumentException("Invalid cycle value. Use 'daily', 'weekly' or 'monthly'.");
@ -919,8 +950,8 @@ class AdsInsightService
}
// 累加数据
$groupedData[$key]['spend'] += $data->spend;
$groupedData[$key]['revenue'] += $data->revenue;
$groupedData[$key]['spend'] += $data['spend'];
$groupedData[$key]['revenue'] += $data['revenue'];
// $groupedData[$key]['conversions_value'] += $data->purchases_value;
// $groupedData[$key]['conversions'] += $data->purchases;
// $groupedData[$key]['impressions'] += $data->impressions;
@ -965,7 +996,7 @@ class AdsInsightService
$dateObj = \DateTime::createFromFormat('Ymd', $dateStr);
// 获取 ISO 周格式
return $dateObj->format('o-W');
return $dateObj->format('o-W'). 'W';
}
/**