webman_ad/app/service/AdsInsightService.php
2025-01-10 18:23:32 +08:00

1141 lines
54 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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_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' => '-', // 没有计算逻辑,保持为 '-'
'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) . '%' : '-', // 格式化为百分比
];
// 获取分页数据
$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}")
->field('s.ad_set_id, s.status as status, s.name, s.account_id,s.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('s.ad_set_id, s.status, s.account_id, s.name,s.platform_type')
->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 为空则显示 '-'
'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}")
->field('a.ad_id, a.status as status, a.name, a.account_id,a.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('a.ad_id, a.status, a.account_id, a.name,a.platform_type')
->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 为空则显示 '-'
'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 as platform_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'],
'third_type' => $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 表
->where('i.platform', $platformType); // 根据 platform 筛选
// 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();
// 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_conversions'] += $creativeData->total_conversions;
$creativeSummaryData[$creativeData->creative_id]['total_impressions'] += $creativeData->total_impressions;
// 汇总总体统计数据
$statisticsData['spend'] += $creativeData->total_spend;
$statisticsData['conversions_value'] += $creativeData->total_conversions_value;
$statisticsData['total_conversions'] += $creativeData->total_conversions;
$statisticsData['total_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,
'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');
}
}