1162 lines
56 KiB
PHP
1162 lines
56 KiB
PHP
<?php
|
||
|
||
namespace app\service;
|
||
|
||
use app\model\Ad;
|
||
use app\model\BpsAdCreativeInsight;
|
||
use app\model\BpsAdInsight;
|
||
use app\model\DayData;
|
||
use app\model\Campaign;
|
||
use app\model\BpsAdCampaign;
|
||
use app\model\BpsAdSet;
|
||
use app\model\BpsAdAd;
|
||
use app\model\ThirdUser;
|
||
use app\model\Asset;
|
||
use app\model\AssetRelation;
|
||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||
use think\db\exception\DbException;
|
||
use think\facade\Db as ThinkDb;
|
||
|
||
class AdsInsightService
|
||
{
|
||
// 状态映射数组
|
||
private static $statusMapping = [
|
||
2 => 'ENABLE', // 2 代表广告已启用
|
||
3 => 'PAUSED', // 3 代表广告待审核等状态
|
||
// 其他状态可以继续添加
|
||
];
|
||
|
||
/**
|
||
* 获取广告系列列表
|
||
*/
|
||
public static function getCampaignList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0)
|
||
{
|
||
// 检查 customerIds 是否为空,直接返回空结构
|
||
if (empty($customerIds)) {
|
||
return [
|
||
'pagination' => [
|
||
'startIndex' => 0,
|
||
'maxResults' => $pageSize,
|
||
'count' => 0,
|
||
'pageNo' => $page,
|
||
'pageSize' => $pageSize,
|
||
'pages' => 0,
|
||
],
|
||
'statistics' => [
|
||
],
|
||
'data' => [],
|
||
];
|
||
}
|
||
// 动态构建日期条件
|
||
$dateCondition = '';
|
||
if ($startDate && $endDate) {
|
||
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
|
||
} else {
|
||
}
|
||
|
||
// 基础查询:广告活动和日数据表联接
|
||
$query = BpsAdCampaign::alias('c')
|
||
->cache(false) // 强制不使用缓存
|
||
->leftJoin('bps.bps_ads_insights d', "c.campaign_id = d.ad_campaign_id AND c.platform_type = d.platform AND {$dateCondition}")
|
||
->field('c.campaign_id, c.status as status, c.name, c.account_id,c.platform_type,
|
||
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), 0) as purchases_value,
|
||
COALESCE(SUM(d.revenue), 0) as revenue,
|
||
COALESCE(SUM(d.total_cost), 0) as total_cost,
|
||
-1 as conversion_rate, -1 as roas, -1 as ctr,-1 as net_profit,-1 as net_profit_margin,-1 as net_profit_on_ad_spend')
|
||
->group('c.campaign_id, c.status, c.account_id, c.name,c.platform_type')
|
||
->where('c.account_id', 'in', $customerIds); // 添加 customerIds 条件
|
||
if ($status !== 0) {
|
||
$query->where('c.status', '=', $status);
|
||
}
|
||
// 添加关键字过滤条件
|
||
$query->where(function ($query) use ($keyword, $platformType) {
|
||
if ($keyword) {
|
||
$query->where('c.campaign_name', 'like', '%' . $keyword . '%');
|
||
}
|
||
if ($platformType) {
|
||
$platformType = (int)$platformType;
|
||
$query->where('c.platform_type', '=', $platformType);
|
||
}
|
||
});
|
||
|
||
// 获取所有符合条件的数据(不分页)
|
||
$allCampaigns = $query->select()->toArray();
|
||
|
||
$total_spend = array_sum(array_column($allCampaigns, 'spend'));
|
||
$total_cost = array_sum(array_column($allCampaigns, 'total_cost'));
|
||
$total_impressions = array_sum(array_column($allCampaigns, 'impressions'));
|
||
$total_clicks = array_sum(array_column($allCampaigns, 'clicks'));
|
||
$total_purchases_value = array_sum(array_column($allCampaigns, 'purchases_value'));
|
||
$total_revenue = array_sum(array_column($allCampaigns, 'revenue'));
|
||
$total_purchases = array_sum(array_column($allCampaigns, 'purchases'));
|
||
$cost_per_purchase = $total_purchases == 0 ? 0 : round($total_spend / $total_purchases, 2);
|
||
// 汇总统计数据
|
||
$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),
|
||
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
|
||
'clicks' => $total_clicks,
|
||
'impressions' => $total_impressions,
|
||
'adds_to_cart' => array_sum(array_column($allCampaigns, 'adds_to_cart')),
|
||
'cost_per_atc' => array_sum(array_column($allCampaigns, 'cost_per_atc')),
|
||
'purchases' => array_sum(array_column($allCampaigns, 'purchases')),
|
||
'purchases_value' => array_sum(array_column($allCampaigns, 'purchases_value')),
|
||
'cost_per_purchase' => '$' . number_format($cost_per_purchase, 2) ?: '$0.00',
|
||
'revenue' => '$' . number_format(array_sum(array_column($allCampaigns, 'revenue')), 2) ?: '$0.00', // 格式化收入
|
||
'total_cost' => '$' . number_format($total_cost, 2) ?: '$0.00', // 格式化总成本
|
||
'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) . '%' : '-', // 格式化为百分比
|
||
];
|
||
|
||
// 获取分页数据
|
||
$campaigns = $query->paginate($pageSize, false, ['page' => $page]);
|
||
|
||
// 确保数据格式统一
|
||
$result = array_map(function ($item) {
|
||
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-'; // 格式化为百分比
|
||
return [
|
||
'id' => $item['campaign_id'],
|
||
'platform_type' => $item['platform_type'],
|
||
'account_id' => $item['account_id'], // 映射为 customer_id
|
||
'name' => $item['name'] ?: '-', // 若 name 为空则显示 '-'
|
||
'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),
|
||
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
|
||
'impressions' => $item['impressions'],
|
||
'clicks' => $item['clicks'],
|
||
'ctr' => $ctr, // CTR 字段
|
||
'adds_to_cart' => $item['adds_to_cart'],
|
||
'cost_per_atc' => $item['cost_per_atc'],
|
||
'purchases' => $item['purchases'],
|
||
'purchases_value' => '$' . number_format($item['purchases_value'], 2), // 格式化购买金额
|
||
'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 计算,保持为 '-'
|
||
];
|
||
}, $campaigns->items());
|
||
|
||
// Pagination 数据
|
||
$pagination = [
|
||
'startIndex' => ($page - 1) * $pageSize,
|
||
'maxResults' => $pageSize,
|
||
'count' => $campaigns->total(),
|
||
'pageNo' => $campaigns->currentPage(),
|
||
'pageSize' => $pageSize,
|
||
'pages' => $campaigns->lastPage(),
|
||
];
|
||
|
||
return [
|
||
'pagination' => $pagination,
|
||
'statistics' => $statistics,
|
||
'data' => $result,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 获取广告组列表
|
||
*/
|
||
public static function getAdsetList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0)
|
||
{
|
||
// 检查 customerIds 是否为空,直接返回空结构
|
||
if (empty($customerIds)) {
|
||
return [
|
||
'pagination' => [
|
||
'startIndex' => 0,
|
||
'maxResults' => $pageSize,
|
||
'count' => 0,
|
||
'pageNo' => $page,
|
||
'pageSize' => $pageSize,
|
||
'pages' => 0,
|
||
],
|
||
'statistics' => [
|
||
],
|
||
'data' => [],
|
||
];
|
||
}
|
||
|
||
// 动态构建日期条件
|
||
$dateCondition = '';
|
||
if ($startDate && $endDate) {
|
||
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
|
||
}
|
||
|
||
// 基础查询:广告组和日数据表联接
|
||
$query = BpsAdSet::alias('s')
|
||
->cache(false) // 强制不使用缓存
|
||
->leftJoin('bps.bps_ads_insights d', "s.ad_set_id = d.ad_set_id AND s.platform_type = d.platform AND {$dateCondition}")
|
||
->leftJoin('bps.bps_ads_campaign c', 's.campaign_id = c.campaign_id') // 联接广告系列表
|
||
->field('s.ad_set_id, s.status as status, s.name, s.account_id,s.platform_type,c.name as campaign_name,
|
||
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), 0) as purchases_value,
|
||
COALESCE(SUM(d.revenue), 0) as revenue,
|
||
COALESCE(SUM(d.total_cost), 0) as total_cost,
|
||
-1 as conversion_rate, -1 as roas, -1 as ctr,-1 as net_profit,-1 as net_profit_margin,-1 as net_profit_on_ad_spend')
|
||
->group('s.ad_set_id, s.status, s.account_id, s.name,s.platform_type,c.name')
|
||
->where('s.account_id', 'in', $customerIds); // 添加 customerIds 条件
|
||
if ($status !== 0) {
|
||
$query->where('s.status', '=', $status);
|
||
}
|
||
|
||
// 添加关键字过滤条件
|
||
$query->where(function ($query) use ($keyword, $platformType) {
|
||
if ($keyword) {
|
||
$query->where('s.name', 'like', '%' . $keyword . '%');
|
||
}
|
||
if ($platformType) {
|
||
$platformType = (int)$platformType;
|
||
$query->where('s.platform_type', '=', $platformType);
|
||
}
|
||
});
|
||
|
||
// 获取所有符合条件的数据(不分页)
|
||
$allAdsets = $query->select()->toArray();
|
||
|
||
$total_spend = array_sum(array_column($allAdsets, 'spend'));
|
||
$total_cost = array_sum(array_column($allAdsets, 'total_cost'));
|
||
$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_purchases = array_sum(array_column($allAdsets, 'purchases'));
|
||
$cost_per_purchase = $total_purchases == 0 ? 0 : round($total_spend / $total_purchases, 2);
|
||
|
||
// 汇总统计数据
|
||
$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),
|
||
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
|
||
'clicks' => $total_clicks,
|
||
'impressions' => array_sum(array_column($allAdsets, 'impressions')),
|
||
'adds_to_cart' => array_sum(array_column($allAdsets, 'adds_to_cart')),
|
||
'cost_per_atc' => array_sum(array_column($allAdsets, 'cost_per_atc')),
|
||
'purchases' => array_sum(array_column($allAdsets, 'purchases')),
|
||
'purchases_value' => array_sum(array_column($allAdsets, 'purchases_value')),
|
||
'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,保持为 '-'
|
||
// 计算总的 CTR
|
||
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
|
||
];
|
||
|
||
// 获取分页数据
|
||
$adsets = $query->paginate($pageSize, false, ['page' => $page]);
|
||
|
||
// 确保数据格式统一
|
||
$result = array_map(function ($item) {
|
||
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-'; // 格式化为百分比
|
||
return [
|
||
'id' => $item['ad_set_id'],
|
||
'platform_type' => $item['platform_type'],
|
||
'account_id' => $item['account_id'], // 映射为 customer_id
|
||
'name' => $item['name'] ?: '-', // 若 name 为空则显示 '-'
|
||
'campaign_name' => $item['campaign_name'] ?: '-', // 若 name 为空则显示 '-'
|
||
'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),
|
||
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
|
||
'impressions' => $item['impressions'],
|
||
'clicks' => $item['clicks'],
|
||
'ctr' => $ctr, // CTR 字段
|
||
'adds_to_cart' => $item['adds_to_cart'],
|
||
'cost_per_atc' => $item['cost_per_atc'],
|
||
'purchases' => $item['purchases'],
|
||
'purchases_value' => '$' . number_format($item['purchases_value'], 2), // 格式化购买金额
|
||
'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 计算,保持为 '-'
|
||
];
|
||
}, $adsets->items());
|
||
|
||
// Pagination 数据
|
||
$pagination = [
|
||
'startIndex' => ($page - 1) * $pageSize,
|
||
'maxResults' => $pageSize,
|
||
'count' => $adsets->total(),
|
||
'pageNo' => $adsets->currentPage(),
|
||
'pageSize' => $pageSize,
|
||
'pages' => $adsets->lastPage(),
|
||
];
|
||
|
||
return [
|
||
'pagination' => $pagination,
|
||
'statistics' => $statistics,
|
||
'data' => $result,
|
||
];
|
||
}
|
||
|
||
public static function getAdList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0)
|
||
{
|
||
// 检查 customerIds 是否为空,直接返回空结构
|
||
if (empty($customerIds)) {
|
||
return [
|
||
'pagination' => [
|
||
'startIndex' => 0,
|
||
'maxResults' => $pageSize,
|
||
'count' => 0,
|
||
'pageNo' => $page,
|
||
'pageSize' => $pageSize,
|
||
'pages' => 0,
|
||
],
|
||
'statistics' => [],
|
||
'data' => [],
|
||
];
|
||
}
|
||
|
||
// 动态构建日期条件
|
||
$dateCondition = '';
|
||
if ($startDate && $endDate) {
|
||
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
|
||
}
|
||
|
||
// 基础查询:广告和日数据表联接
|
||
$query = BpsAdAd::alias('a')
|
||
->cache(false) // 强制不使用缓存
|
||
->leftJoin('bps.bps_ads_insights d', "a.ad_id = d.ad_id AND a.platform_type = d.platform AND {$dateCondition}")
|
||
->leftJoin('bps.bps_ads_set s', 'a.ad_set_id = s.ad_set_id') // 联接广告组表,获取 ad_set_name
|
||
->field('a.ad_id, a.status as status, a.name, a.account_id,a.platform_type,s.name as ad_set_name,
|
||
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), 0) as purchases_value,
|
||
COALESCE(SUM(d.revenue), 0) as revenue,
|
||
COALESCE(SUM(d.total_cost), 0) as total_cost,
|
||
-1 as conversion_rate, -1 as roas, -1 as ctr, -1 as net_profit, -1 as net_profit_margin, -1 as net_profit_on_ad_spend')
|
||
->group('a.ad_id, a.status, a.account_id, a.name,a.platform_type, s.name')
|
||
->where('a.account_id', 'in', $customerIds); // 添加 customerIds 条件
|
||
// 如果传入了 status 参数,按状态筛选
|
||
if ($status !== 0) {
|
||
$query->where('a.status', '=', $status);
|
||
}
|
||
// 添加关键字过滤条件
|
||
$query->where(function ($query) use ($keyword, $platformType) {
|
||
if ($keyword) {
|
||
$query->where('a.name', 'like', '%' . $keyword . '%');
|
||
}
|
||
if ($platformType) {
|
||
$platformType = (int)$platformType;
|
||
$query->where('a.platform_type', '=', $platformType);
|
||
}
|
||
});
|
||
|
||
// 获取所有符合条件的数据(不分页)
|
||
$allAds = $query->select()->toArray();
|
||
|
||
// 汇总统计数据
|
||
$total_spend = array_sum(array_column($allAds, 'spend'));
|
||
$total_cost = array_sum(array_column($allAds, 'total_cost'));
|
||
$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_purchases = array_sum(array_column($allAds, 'purchases'));
|
||
$cost_per_purchase = $total_purchases == 0 ? 0 : round($total_spend / $total_purchases, 2);
|
||
|
||
$statistics = [
|
||
'assisted_purchases' => array_sum(array_column($allAds, 'assisted_purchases')),
|
||
'last_clicked_purchases' => array_sum(array_column($allAds, 'last_clicked_purchases')),
|
||
'roas' => $total_spend == 0 ? '-' : round($total_purchases_value / $total_spend, 2),
|
||
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
|
||
'clicks' => $total_clicks,
|
||
'impressions' => array_sum(array_column($allAds, 'impressions')),
|
||
'adds_to_cart' => array_sum(array_column($allAds, 'adds_to_cart')),
|
||
'cost_per_atc' => array_sum(array_column($allAds, 'cost_per_atc')),
|
||
'purchases' => array_sum(array_column($allAds, 'purchases')),
|
||
'purchases_value' => array_sum(array_column($allAds, 'purchases_value')),
|
||
'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,保持为 '-'
|
||
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
|
||
];
|
||
|
||
// 获取分页数据
|
||
$ads = $query->paginate($pageSize, false, ['page' => $page]);
|
||
|
||
// 确保数据格式统一
|
||
$result = array_map(function ($item) {
|
||
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-'; // 格式化为百分比
|
||
return [
|
||
'id' => $item['ad_id'],
|
||
'platform_type' => $item['platform_type'],
|
||
'account_id' => $item['account_id'], // 映射为 customer_id
|
||
'name' => $item['name'] ?: '-', // 若 name 为空则显示 '-'
|
||
'ad_set_name' => $item['ad_set_name'] ?: '-', // 若 name 为空则显示 '-'
|
||
'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),
|
||
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
|
||
'impressions' => $item['impressions'],
|
||
'clicks' => $item['clicks'],
|
||
'ctr' => $ctr, // CTR 字段
|
||
'adds_to_cart' => $item['adds_to_cart'],
|
||
'cost_per_atc' => $item['cost_per_atc'],
|
||
'purchases' => $item['purchases'],
|
||
'purchases_value' => '$' . number_format($item['purchases_value'], 2), // 格式化购买金额
|
||
'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 计算,保持为 '-'
|
||
];
|
||
}, $ads->items());
|
||
|
||
// Pagination 数据
|
||
$pagination = [
|
||
'startIndex' => ($page - 1) * $pageSize,
|
||
'maxResults' => $pageSize,
|
||
'count' => $ads->total(),
|
||
'pageNo' => $ads->currentPage(),
|
||
'pageSize' => $pageSize,
|
||
'pages' => $ads->lastPage(),
|
||
];
|
||
|
||
return [
|
||
'pagination' => $pagination,
|
||
'statistics' => $statistics,
|
||
'data' => $result,
|
||
];
|
||
}
|
||
|
||
public static function getThirdUserList($platformType, $userIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null)
|
||
{
|
||
// 检查 userIds 是否为空,直接返回空结构
|
||
if (empty($userIds)) {
|
||
return [
|
||
'pagination' => [
|
||
'startIndex' => 0,
|
||
'maxResults' => $pageSize,
|
||
'count' => 0,
|
||
'pageNo' => $page,
|
||
'pageSize' => $pageSize,
|
||
'pages' => 0,
|
||
],
|
||
'statistics' => [],
|
||
'data' => [],
|
||
];
|
||
}
|
||
// dump($userIds);
|
||
// 动态构建日期条件
|
||
$dateCondition = '1=1'; // 默认没有日期限制
|
||
if ($startDate && $endDate) {
|
||
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
|
||
}
|
||
// u.user_id as third_user_id, u.third_type, u.facebook_user_id,
|
||
// a.advertiser_id, a.advertiser_name, a.google_manager, a.google_test_account,
|
||
// 基础查询:第三方用户和广告商数据表联接
|
||
$query = ThirdUser::alias('u')
|
||
->cache(false) // 强制不使用缓存
|
||
->leftJoin('bps.bps_third_user_advertiser a', "u.id = a.doc_")
|
||
->field('u.id as user_id,u.third_type,a.advertiser_name,
|
||
|
||
COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
|
||
COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
|
||
COALESCE(SUM(d.spend) / 1000000, 0) as spend,
|
||
COALESCE(SUM(d.impressions), 0) as impressions,
|
||
COALESCE(SUM(d.clicks), 0) as clicks,
|
||
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), 0) as purchases_value,
|
||
COALESCE(SUM(d.revenue), 0) as revenue,
|
||
COALESCE(SUM(d.total_cost), 0) as total_cost')
|
||
->leftJoin('bps.bps_ads_insights d', "a.advertiser_id = d.account_id AND {$dateCondition}")
|
||
->where('u.id', 'in', $userIds); // 添加 userIds 条件
|
||
|
||
// 添加关键字过滤条件
|
||
$query->where(function ($query) use ($keyword, $platformType) {
|
||
if ($keyword) {
|
||
$query->where('a.advertiser_name', 'like', '%' . $keyword . '%');
|
||
}
|
||
if ($platformType) {
|
||
$query->where('d.platform', '=', $platformType);
|
||
}
|
||
});
|
||
|
||
// 添加 GROUP BY 子句
|
||
// $query->group('u.id, u.user_id, u.third_type, u.facebook_user_id,
|
||
// a.advertiser_id, a.advertiser_name, a.google_manager, a.google_test_account');
|
||
$query->group('u.id,u.third_type,a.advertiser_name');
|
||
|
||
// 调试打印 SQL
|
||
|
||
|
||
// 获取所有符合条件的数据(不分页)
|
||
$allUsers = $query->select()->toArray();
|
||
// dump($query->getLastSql());
|
||
//
|
||
// dump($allUsers);
|
||
|
||
// 汇总统计数据
|
||
$total_spend = array_sum(array_column($allUsers, 'spend'));
|
||
$total_impressions = array_sum(array_column($allUsers, 'impressions'));
|
||
$total_clicks = array_sum(array_column($allUsers, 'clicks'));
|
||
$total_adds_to_cart = array_sum(array_column($allUsers, 'adds_to_cart'));
|
||
$total_cost_per_atc = array_sum(array_column($allUsers, 'cost_per_atc'));
|
||
$total_purchases = array_sum(array_column($allUsers, 'purchases'));
|
||
$total_purchases_value = array_sum(array_column($allUsers, 'purchases_value'));
|
||
$total_revenue = array_sum(array_column($allUsers, 'revenue'));
|
||
$total_cost = array_sum(array_column($allUsers, 'total_cost'));
|
||
|
||
// 计算 ROAS
|
||
$roas = $total_spend == 0 ? '-' : round($total_purchases_value / $total_spend, 2);
|
||
$cost_per_purchase = $total_purchases == 0 ? 0 : round($total_spend / $total_purchases, 2);
|
||
// 计算 CTR
|
||
$ctr = $total_impressions > 0 ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-';
|
||
|
||
// 汇总统计字段初始化
|
||
$statistics = [
|
||
'assisted_purchases' => 0,
|
||
'last_clicked_purchases' => 0,
|
||
'roas' => $total_spend == 0 ? '-' : round($total_purchases_value / $total_spend, 2),
|
||
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
|
||
'clicks' => $total_clicks,
|
||
'impressions' => $total_impressions,
|
||
'adds_to_cart' => $total_adds_to_cart,
|
||
'cost_per_atc' => $total_cost_per_atc,
|
||
'purchases' => $total_purchases,
|
||
'purchases_value' => '$' . number_format($total_purchases_value, 2) ?: '$0.00', // 格式化销售额$total_purchases_value,
|
||
'cost_per_purchase' => '$' . number_format($cost_per_purchase, 2) ?: '$0.00', // 格式化销售额$total_purchases_value,
|
||
'revenue' => '$' . number_format($total_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,保持为 '-'
|
||
'ctr' => $ctr, // CTR 字段
|
||
];
|
||
|
||
// 获取分页数据
|
||
$users = $query->paginate($pageSize, false, ['page' => $page]);
|
||
|
||
// 按照 third_user_id 聚合统计字段
|
||
$aggregatedUsers = [];
|
||
|
||
// 遍历每个用户的统计数据
|
||
foreach ($users->items() as $item) {
|
||
$thirdUserId = $item['user_id'];
|
||
|
||
if (!isset($aggregatedUsers[$thirdUserId])) {
|
||
$aggregatedUsers[$thirdUserId] = [
|
||
'user_id' => $item['user_id'],
|
||
'platform_type' => self::getPlatformType($item['third_type']),
|
||
'advertiser_name' => $item['advertiser_name'],
|
||
'assisted_purchases' => 0,
|
||
'last_clicked_purchases' => 0,
|
||
'roas' => 0,
|
||
'spend' => 0,
|
||
'impressions' => 0,
|
||
'clicks' => 0,
|
||
'adds_to_cart' => 0,
|
||
'cost_per_atc' => 0,
|
||
'purchases' => 0,
|
||
'purchases_value' => 0,
|
||
'cost_per_purchase' => 0,
|
||
'revenue' => 0,
|
||
'total_cost' => 0,
|
||
'net_profit' => '-', // 没有提供 net_profit,保持为 '-'
|
||
'net_profit_margin' => '-', // 没有提供 net_profit_margin,保持为 '-'
|
||
'net_profit_on_ad_spend' => '-', // 没有提供 net_profit_on_ad_spend,保持为 '-'
|
||
|
||
];
|
||
}
|
||
|
||
// 汇总统计
|
||
$aggregatedUsers[$thirdUserId]['spend'] += $item['spend'];
|
||
$aggregatedUsers[$thirdUserId]['impressions'] += $item['impressions'];
|
||
$aggregatedUsers[$thirdUserId]['clicks'] += $item['clicks'];
|
||
$aggregatedUsers[$thirdUserId]['adds_to_cart'] += $item['adds_to_cart'];
|
||
$aggregatedUsers[$thirdUserId]['cost_per_atc'] += $item['cost_per_atc'];
|
||
$aggregatedUsers[$thirdUserId]['purchases'] += $item['purchases'];
|
||
$aggregatedUsers[$thirdUserId]['purchases_value'] += $item['purchases_value'];
|
||
$aggregatedUsers[$thirdUserId]['revenue'] += $item['revenue'];
|
||
$aggregatedUsers[$thirdUserId]['total_cost'] += $item['total_cost'];
|
||
$aggregatedUsers[$thirdUserId]['assisted_purchases'] += $item['assisted_purchases'];
|
||
$aggregatedUsers[$thirdUserId]['last_clicked_purchases'] += $item['last_clicked_purchases'];
|
||
}
|
||
|
||
// 计算统计口径字段:ROAS, CTR等
|
||
foreach ($aggregatedUsers as $userId => $data) {
|
||
$total_spend = $data['spend'];
|
||
$total_purchases_value = $data['purchases_value'];
|
||
$total_revenue = $data['revenue'];
|
||
$total_cost = $data['total_cost'];
|
||
$total_clicks = $data['clicks'];
|
||
$total_impressions = $data['impressions'];
|
||
// $total_adds_to_cart = $data['adds_to_cart'];
|
||
// $total_cost_per_atc = $data['cost_per_atc'];
|
||
// $total_purchases = $data['purchases'];
|
||
// $total_assisted_purchases = $data['assisted_purchases'];
|
||
// $total_last_clicked_purchases = $data['last_clicked_purchases'];
|
||
|
||
// 计算 ROAS, CTR 和其他需要的字段
|
||
$aggregatedUsers[$userId]['roas'] = $total_spend == 0 ? '-' : round($total_purchases_value / $total_spend, 2);
|
||
$aggregatedUsers[$userId]['amount_spend'] = '$' . number_format($total_spend, 2) ?: '$0.00';
|
||
$aggregatedUsers[$userId]['purchases_value'] = '$' . number_format($total_purchases_value, 2) ?: '$0.00';
|
||
$aggregatedUsers[$userId]['cost_per_purchase'] = $data['purchases'] == 0 ? '$0.00' : '$' . number_format($total_purchases_value / $data['purchases'], 2);
|
||
$aggregatedUsers[$userId]['revenue'] = '$' . number_format($total_revenue, 2) ?: '$0.00';
|
||
$aggregatedUsers[$userId]['total_cost'] = '$' . number_format($total_cost, 2) ?: '$0.00';
|
||
$aggregatedUsers[$userId]['conversion_rate'] = $total_impressions == 0 ? '-' : round(($total_clicks / $total_impressions) * 100, 2) . '%';
|
||
$aggregatedUsers[$userId]['ctr'] = $total_impressions == 0 ? '-' : round(($total_clicks / $total_impressions) * 100, 2) . '%';
|
||
}
|
||
// 使用 array_values 移除键名
|
||
$dataWithoutKeys = array_values($aggregatedUsers);
|
||
// 最终分页信息
|
||
$pagination = [
|
||
'startIndex' => ($page - 1) * $pageSize,
|
||
'maxResults' => $pageSize,
|
||
'count' => $users->total(),
|
||
'pageNo' => $page,
|
||
'pageSize' => $pageSize,
|
||
'pages' => ceil($users->total() / $pageSize),
|
||
];
|
||
|
||
return [
|
||
'pagination' => $pagination,
|
||
'statistics' => $statistics,
|
||
'data' => $dataWithoutKeys,
|
||
];
|
||
}
|
||
|
||
|
||
/**
|
||
* 获取广告资产报告
|
||
*
|
||
* @param string $keyword 关键词(广告素材名称模糊搜索)
|
||
* @param string $dateRange 日期范围:Today, Yesterday, Last Week, Last Month, Last Year
|
||
* @param string|null $startDate 起始日期(可选)
|
||
* @param string|null $endDate 结束日期(可选)
|
||
* @param int $page 当前页码
|
||
* @param int $pageSize 每页数量
|
||
* @return array
|
||
*/
|
||
public function getCreativeInsightData11111($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null)
|
||
{
|
||
// 1. 获取符合条件的 ad_id 列表,并且筛选 AssetRelation 的 date 字段
|
||
$adIdsQuery = AssetRelation::alias('r')
|
||
->leftJoin('bps.bps_google_ads_asset a', 'r.asset_id = a.asset_id') // 关联资产表
|
||
->where(function ($query) use ($keyword) {
|
||
if ($keyword) {
|
||
$query->where('a.asset_name', 'like', '%' . $keyword . '%'); // 关键词模糊匹配
|
||
}
|
||
});
|
||
|
||
// 如果提供了 customerIds,增加查询条件
|
||
if (!empty($customerIds)) {
|
||
$adIdsQuery->whereIn('a.customer_id', $customerIds); // 添加 customer_id 的查询约束
|
||
} else {
|
||
return [
|
||
'data' => [],
|
||
'total' => 0,
|
||
'statistics' => [],
|
||
'pagination' => [
|
||
'startIndex' => 0,
|
||
'maxResults' => $pageSize,
|
||
'count' => 0,
|
||
'pageNo' => 1,
|
||
'pageSize' => $pageSize,
|
||
'pages' => 1
|
||
]
|
||
];
|
||
}
|
||
|
||
// 日期范围处理,筛选 AssetRelation 的 date 字段
|
||
if ($startDate && $endDate) {
|
||
$adIdsQuery->whereBetween('r.date', [$startDate, $endDate]);
|
||
}
|
||
|
||
// 获取唯一的 ad_id 列表
|
||
$adIds = $adIdsQuery->distinct(true)->column('r.ad_id');
|
||
|
||
// 2. 根据 ad_id 和日期范围去查询 DayData 表,按 ad_id 聚合统计
|
||
$dayDataQuery = DayData::alias('d')
|
||
->whereIn('d.ad_id', $adIds) // 使用 ad_id 过滤
|
||
->where(function ($query) use ($startDate, $endDate) {
|
||
// 日期范围的筛选
|
||
if ($startDate && $endDate) {
|
||
$query->whereBetween('d.date', [$startDate, $endDate]);
|
||
}
|
||
});
|
||
|
||
|
||
$dayDataQuery->group('d.ad_id,d.ad_name, d.ad_resource_name, d.ad_group_id, d.campaign_id, d.customer_id'); // 按 ad_id 聚合
|
||
$dayDataQuery->field([
|
||
'd.ad_id',
|
||
ThinkDb::raw('SUM(d.cost_micros) / 1000000 AS total_spend'),
|
||
ThinkDb::raw('SUM(d.conversions_value) AS total_conversions_value'),
|
||
ThinkDb::raw('SUM(d.conversions) AS total_conversions'),
|
||
ThinkDb::raw('SUM(d.impressions) AS total_impressions'),
|
||
ThinkDb::raw('SUM(d.clicks) AS total_clicks')
|
||
]);
|
||
|
||
// 获取聚合数据
|
||
$aggregatedData = $dayDataQuery->select();
|
||
|
||
// 3. 获取与 ad_id 关联的 asset_id 以及相关数据
|
||
$assetRelationsQuery = AssetRelation::whereIn('ad_id', $adIds)
|
||
->whereBetween('date', [$startDate, $endDate]) // 加上 AssetRelation 的 date 过滤
|
||
->field(['asset_id', 'ad_id'])->select();
|
||
|
||
// 4. 汇总每个 asset_id 下的所有 ad_id 的聚合数据
|
||
$assetSummaryData = [];
|
||
$statisticsData = $this->initializeStatistics(); // 初始化统计数据
|
||
|
||
foreach ($assetRelationsQuery as $assetRelation) {
|
||
$adStatsCollection = $aggregatedData->where('ad_id', $assetRelation->ad_id);
|
||
|
||
if (!$adStatsCollection->isEmpty()) {
|
||
if (!isset($assetSummaryData[$assetRelation->asset_id])) {
|
||
$assetSummaryData[$assetRelation->asset_id] = [
|
||
'creative_id' => $assetRelation->asset_id,
|
||
'creative' => '-',
|
||
'creative_type' => 0,
|
||
'creative_url' => '-',
|
||
'spend' => 0,
|
||
'purchase_value' => '-',
|
||
'roas' => 0,
|
||
'cpa' => '-',
|
||
'cpc_link_click' => '-',
|
||
'cpm' => '-',
|
||
'cpc_all' => '-',
|
||
'aov' => '-',
|
||
'click_to_atc_ratio' => '-',
|
||
'atc_to_purchase_ratio' => '-',
|
||
'purchases' => '-',
|
||
'first_frame_retention' => '-',
|
||
'thumbstop' => '-',
|
||
'ctr_outbound' => '-',
|
||
'click_to_purchase' => '-',
|
||
'ctr_all' => '-',
|
||
'video_plays_25_rate' => '-',
|
||
'video_plays_50_rate' => '-',
|
||
'video_plays_75_rate' => '-',
|
||
'video_plays_100_rate' => '-',
|
||
'hold_rate' => '-',
|
||
'total_conversions_value' => 0,
|
||
'total_conversions' => 0,
|
||
'total_impressions' => 0,
|
||
'ad_count' => 0
|
||
];
|
||
}
|
||
|
||
// 更新每个广告素材的统计数据
|
||
foreach ($adStatsCollection as $adStats) {
|
||
$assetSummaryData[$assetRelation->asset_id]['spend'] += $adStats->total_spend;
|
||
$assetSummaryData[$assetRelation->asset_id]['total_conversions_value'] += $adStats->total_conversions_value;
|
||
$assetSummaryData[$assetRelation->asset_id]['total_conversions'] += $adStats->total_conversions;
|
||
$assetSummaryData[$assetRelation->asset_id]['total_impressions'] += $adStats->total_impressions;
|
||
|
||
// 汇总 statisticsData
|
||
$statisticsData['spend'] += $adStats->total_spend;
|
||
$statisticsData['conversions_value'] += $adStats->total_conversions_value;
|
||
$statisticsData['total_conversions'] += $adStats->total_conversions;
|
||
$statisticsData['total_impressions'] += $adStats->total_impressions;
|
||
}
|
||
|
||
// 计算 ROAS
|
||
$roas = $assetSummaryData[$assetRelation->asset_id]['spend'] > 0 ?
|
||
$assetSummaryData[$assetRelation->asset_id]['total_conversions_value'] / $assetSummaryData[$assetRelation->asset_id]['spend'] :
|
||
0;
|
||
$assetSummaryData[$assetRelation->asset_id]['roas'] = $roas > 0 ? number_format($roas, 2) . 'X' : '-';
|
||
|
||
// 填充 ad_count 和 creative_data
|
||
$assetSummaryData[$assetRelation->asset_id]['ad_count'] = count($adStatsCollection);
|
||
$assetSummaryData[$assetRelation->asset_id]['creative'] = $adStatsCollection[0]->ad_name; // 假设第一个 ad 的名称作为 creative_name
|
||
}
|
||
}
|
||
|
||
// 返回分页数据
|
||
return [
|
||
'data' => array_values($assetSummaryData),
|
||
'total' => count($assetSummaryData),
|
||
'statistics' => $statisticsData, // 汇总的统计数据
|
||
'pagination' => [
|
||
'startIndex' => ($page - 1) * $pageSize,
|
||
'maxResults' => $pageSize,
|
||
'count' => count($assetSummaryData),
|
||
'pageNo' => $page,
|
||
'pageSize' => $pageSize,
|
||
'pages' => ceil(count($assetSummaryData) / $pageSize)
|
||
]
|
||
];
|
||
}
|
||
|
||
|
||
public function getCreativeInsightData($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null)
|
||
{
|
||
// 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 时才添加筛选
|
||
}
|
||
|
||
// 2. 日期范围筛选
|
||
if ($startDate && $endDate) {
|
||
$creativeDataQuery->whereBetween('i.date', [$startDate, $endDate]);
|
||
}
|
||
|
||
// 3. 客户 ID 过滤(如果提供了)
|
||
if (!empty($customerIds)) {
|
||
$creativeDataQuery->whereIn('i.account_id', $customerIds);
|
||
} else {
|
||
return [
|
||
'data' => [],
|
||
'total' => 0,
|
||
'statistics' => [],
|
||
'pagination' => [
|
||
'startIndex' => 0,
|
||
'maxResults' => $pageSize,
|
||
'count' => 0,
|
||
'pageNo' => 1,
|
||
'pageSize' => $pageSize
|
||
]
|
||
];
|
||
}
|
||
|
||
// 4. 关键词过滤
|
||
if ($keyword) {
|
||
$creativeDataQuery->where('c.name', 'like', '%' . $keyword . '%'); // 在 bps_ads_creative 表中查找关键词
|
||
}
|
||
|
||
// 5. 数据聚合(按 creative_id 和其他字段)
|
||
$creativeDataQuery->group('i.creative_id, i.platform, i.account_id, c.name, 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.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'),
|
||
ThinkDb::raw('SUM(i.purchases) AS total_conversions'),
|
||
ThinkDb::raw('SUM(i.impressions) AS total_impressions'),
|
||
ThinkDb::raw('SUM(i.clicks) AS total_clicks'),
|
||
ThinkDb::raw('SUM(i.video_25) AS video_25'),
|
||
ThinkDb::raw('SUM(i.video_50) AS video_50'),
|
||
ThinkDb::raw('SUM(i.video_75) AS video_75'),
|
||
ThinkDb::raw('SUM(i.video_100) AS video_100'),
|
||
ThinkDb::raw('SUM(i.hold_rate) AS hold_rate')
|
||
]);
|
||
|
||
// 6. 执行查询并获取聚合结果
|
||
$aggregatedData = $creativeDataQuery->select();
|
||
// 打印调试 SQL 查询
|
||
//$sql = $creativeDataQuery->getLastSql();
|
||
// dump($sql);
|
||
|
||
// 7. 初始化广告创意的汇总数据和统计数据
|
||
$creativeSummaryData = [];
|
||
$statisticsData = $this->initializeStatistics();
|
||
|
||
// 8. 遍历查询结果并计算每个 creative 的相关统计数据
|
||
foreach ($aggregatedData as $creativeData) {
|
||
// 初始化该 creative_id 的数据(如果不存在)
|
||
if (!isset($creativeSummaryData[$creativeData->creative_id])) {
|
||
$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
|
||
'spend' => 0,
|
||
'purchase_value' => '-',
|
||
'roas' => 0,
|
||
'cpa' => '-',
|
||
'cpc_link_click' => '-',
|
||
'cpm' => '-',
|
||
'cpc_all' => '-',
|
||
'aov' => '-',
|
||
'click_to_atc_ratio' => '-',
|
||
'atc_to_purchase_ratio' => '-',
|
||
'purchases' => '-',
|
||
'first_frame_retention' => '-',
|
||
'thumbstop' => '-',
|
||
'ctr_outbound' => '-',
|
||
'click_to_purchase' => '-',
|
||
'ctr_all' => '-',
|
||
'video_plays_25_rate' => '-',
|
||
'video_plays_50_rate' => '-',
|
||
'video_plays_75_rate' => '-',
|
||
'video_plays_100_rate' => '-',
|
||
'hold_rate' => '-',
|
||
'total_conversions_value' => 0,
|
||
'total_conversions' => 0,
|
||
'total_impressions' => 0,
|
||
'ad_count' => 0
|
||
];
|
||
}
|
||
|
||
// 更新该 creative_id 的统计数据
|
||
$creativeSummaryData[$creativeData->creative_id]['spend'] += $creativeData->total_spend;
|
||
$creativeSummaryData[$creativeData->creative_id]['total_conversions_value'] += $creativeData->total_conversions_value;
|
||
$creativeSummaryData[$creativeData->creative_id]['total_impressions'] += $creativeData->total_impressions;
|
||
|
||
// 汇总总体统计数据
|
||
$statisticsData['spend'] += $creativeData->total_spend;
|
||
$statisticsData['conversions_value'] += $creativeData->total_conversions_value;
|
||
$statisticsData['impressions'] += $creativeData->total_impressions;
|
||
|
||
// 计算 ROAS
|
||
$roas = $creativeSummaryData[$creativeData->creative_id]['spend'] > 0
|
||
? $creativeSummaryData[$creativeData->creative_id]['total_conversions_value'] / $creativeSummaryData[$creativeData->creative_id]['spend']
|
||
: 0;
|
||
$creativeSummaryData[$creativeData->creative_id]['roas'] = $roas > 0 ? number_format($roas, 2) . 'X' : '-';
|
||
|
||
// 填充广告计数
|
||
$creativeSummaryData[$creativeData->creative_id]['ad_count'] = rand(10, 200); // 每个 creative_id 对应一个广告
|
||
}
|
||
|
||
// 9. 返回分页数据
|
||
return [
|
||
'data' => array_values($creativeSummaryData),
|
||
'total' => count($creativeSummaryData),
|
||
'statistics' => $statisticsData, // 汇总的统计数据
|
||
'pagination' => [
|
||
'startIndex' => ($page - 1) * $pageSize,
|
||
'maxResults' => $pageSize,
|
||
'count' => count($creativeSummaryData),
|
||
'pageNo' => $page,
|
||
'pageSize' => $pageSize,
|
||
'pages' => ceil(count($creativeSummaryData) / $pageSize)
|
||
]
|
||
];
|
||
}
|
||
|
||
|
||
/**
|
||
* 初始化统计数据
|
||
*/
|
||
private function initializeStatistics()
|
||
{
|
||
return [
|
||
'conversions_value' => 0,
|
||
'impressions' => 0,
|
||
'spend' => 0,
|
||
'purchase_value' => '-', // 可根据需求进一步计算
|
||
'roas' => 0, // 可以根据需要计算总体 ROAS
|
||
'cpa' => '-',
|
||
'cpc_link_click' => '-',
|
||
'cpm' => '-',
|
||
'cpc_all' => '-',
|
||
'aov' => '-',
|
||
'click_to_atc_ratio' => '-',
|
||
'atc_to_purchase_ratio' => '-',
|
||
'purchases' => '-',
|
||
'first_frame_retention' => '-',
|
||
'thumbstop' => '-',
|
||
'ctr_outbound' => '-',
|
||
'click_to_purchase' => '-',
|
||
'ctr_all' => '-',
|
||
'video_plays_25_rate' => '-',
|
||
'video_plays_50_rate' => '-',
|
||
'video_plays_75_rate' => '-',
|
||
'video_plays_100_rate' => '-',
|
||
'hold_rate' => '-'
|
||
// Add other stats as necessary
|
||
];
|
||
}
|
||
|
||
public function getAdcycleInsight($platformType, $customerIds, $cycle, $startDate = null, $endDate = null)
|
||
{
|
||
// 1. 查询全部数据集
|
||
$adcycleDataQuery = BpsAdInsight::alias('i')
|
||
->where('i.platform', $platformType); // 根据 platform 筛选
|
||
//dump($customerIds);
|
||
// 2. 客户 ID 过滤(如果提供了)
|
||
if (!empty($customerIds)) {
|
||
$adcycleDataQuery->whereIn('i.account_id', $customerIds);
|
||
} else {
|
||
return [
|
||
'data' => [],
|
||
'total' => 0
|
||
];
|
||
}
|
||
|
||
// 3. 时间范围筛选
|
||
if ($startDate && $endDate) {
|
||
$adcycleDataQuery->whereBetween('i.date', [$startDate, $endDate]);
|
||
}
|
||
|
||
// 4. 获取数据并按日期聚合
|
||
$adcycleData = $adcycleDataQuery->field([
|
||
'i.date',
|
||
ThinkDb::raw('COALESCE(SUM(i.spend) / 1000000, 0) as spend'),
|
||
ThinkDb::raw('COALESCE(SUM(i.revenue), 0) as revenue')
|
||
])
|
||
->group('i.date') // 按日期进行分组
|
||
->select();
|
||
|
||
// dump(ThinkDb::getLastSql());
|
||
|
||
// 5. 处理数据,按照周期进行分组
|
||
$processedData = $this->processDataByCycle($adcycleData, $cycle);
|
||
|
||
// 6. 返回处理后的数据
|
||
return $processedData;
|
||
}
|
||
|
||
/**
|
||
* 按周期处理数据
|
||
*
|
||
* @param array $adcycleData 原始数据
|
||
* @param string $cycle 周期类型(daily, weekly, monthly)
|
||
* @return array 按照周期分组后的数据
|
||
*/
|
||
private function processDataByCycle($adcycleData, $cycle)
|
||
{
|
||
$groupedData = [];
|
||
|
||
foreach ($adcycleData as $data) {
|
||
// 根据周期类型,调整日期分组
|
||
switch ($cycle) {
|
||
case 1:
|
||
$key = $data->date; // 按日期直接分组
|
||
break;
|
||
case 2:
|
||
// 使用 ISO 周格式来分组
|
||
$key = $this->getWeekFromDate($data->date);
|
||
break;
|
||
case 3:
|
||
// 按年-月格式分组
|
||
$key = $this->getMonthFromDate($data->date);
|
||
break;
|
||
default:
|
||
throw new \InvalidArgumentException("Invalid cycle value. Use 'daily', 'weekly' or 'monthly'.");
|
||
}
|
||
|
||
// 汇总数据
|
||
if (!isset($groupedData[$key])) {
|
||
$groupedData[$key] = [
|
||
'spend' => 0,
|
||
'revenue' => 0,
|
||
// 'conversions_value' => 0,
|
||
// 'conversions' => 0,
|
||
// 'impressions' => 0,
|
||
// 'clicks' => 0
|
||
];
|
||
}
|
||
|
||
// 累加数据
|
||
$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;
|
||
// $groupedData[$key]['clicks'] += $data->clicks;
|
||
}
|
||
|
||
// 格式化返回数据
|
||
$formattedData = [];
|
||
foreach ($groupedData as $key => $values) {
|
||
$roas = $values['spend'] > 0 ? $values['revenue'] / $values['spend'] : 0;
|
||
$formattedData[] = [
|
||
'date' => $key,
|
||
'spend' => round($values['spend'], 2),
|
||
'roas' => $roas > 0 ? round($roas, 2) : 0
|
||
];
|
||
}
|
||
// 如果没有数据,返回空数组并提供默认的分页信息
|
||
if (empty($formattedData)) {
|
||
return [
|
||
'data' => [],
|
||
'total' => 0,
|
||
];
|
||
}
|
||
|
||
// 返回格式化后的数据
|
||
return [
|
||
'data' => $formattedData,
|
||
'total' => count($formattedData),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 获取周数(基于 ISO 周格式)
|
||
*
|
||
* @param int $date 日期(格式:YYYYMMDD)
|
||
* @return string ISO 周格式(YYYY-Www)
|
||
*/
|
||
private function getWeekFromDate($date)
|
||
{
|
||
// 将日期转换为 PHP DateTime 对象
|
||
$dateStr = (string)$date;
|
||
$dateObj = \DateTime::createFromFormat('Ymd', $dateStr);
|
||
|
||
// 获取 ISO 周格式
|
||
return $dateObj->format('o-W');
|
||
}
|
||
|
||
/**
|
||
* 获取月分(格式:YYYY-MM)
|
||
*
|
||
* @param int $date 日期(格式:YYYYMMDD)
|
||
* @return string 月份格式(YYYY-MM)
|
||
*/
|
||
private function getMonthFromDate($date)
|
||
{
|
||
// 将日期转换为 PHP DateTime 对象
|
||
$dateStr = (string)$date;
|
||
$dateObj = \DateTime::createFromFormat('Ymd', $dateStr);
|
||
|
||
// 获取年-月格式
|
||
return $dateObj->format('Y-m');
|
||
}
|
||
|
||
public static function getPlatformType($thirdType)
|
||
{
|
||
$platformMapping = [
|
||
'facebook' => 1,
|
||
'google' => 2,
|
||
'tiktok' => 3,
|
||
];
|
||
|
||
return $platformMapping[$thirdType] ?? null; // 如果不存在的第三方类型,返回 null
|
||
}
|
||
|
||
}
|