webman_ad/app/service/AdsInsightService.php

996 lines
49 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\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)
{
// 检查 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,
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')
->where('c.account_id', 'in', $customerIds); // 添加 customerIds 条件
// 添加关键字过滤条件
$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'));
// 汇总统计数据
$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')),
'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'],
'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), // 格式化购买金额
'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)
{
// 检查 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,
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')
->where('s.account_id', 'in', $customerIds); // 添加 customerIds 条件
// 添加关键字过滤条件
$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'));
// 汇总统计数据
$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')),
'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'],
'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), // 格式化购买金额
'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)
{
// 检查 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,
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')
->where('a.account_id', 'in', $customerIds); // 添加 customerIds 条件
// 添加关键字过滤条件
$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'));
$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')),
'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'],
'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), // 格式化购买金额
'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);
// 计算 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,
'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,
'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]['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) . '%';
}
// 最终分页信息
$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' => $aggregatedUsers,
];
}
/**
* 获取广告资产报告
*
* @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 getAssetConversionData($customerIds, $page, $pageSize, $keyword, $dateRange, $startDate = null, $endDate = null)
{
$isSameMonth = true; // 判断是否跨月
$currentMonth = date('Y-m'); //一个月内的统计需要确定是什么月份。
if ($startDate && $endDate) {
// 比较日期的年月是否相同,若不同则为跨月
$isSameMonth = date('Y-m', strtotime($startDate)) === date('Y-m', strtotime($endDate));
if ($isSameMonth) {
$currentMonth = date('Y-m', strtotime($startDate));
}
}
// 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' => [],
'chat_1_data' => [], // 返回按月汇总的 chat_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]);
} else {
switch ($dateRange) {
case 'Today':
$adIdsQuery->where('r.date', '=', date('Y-m-d'));
$currentMonth = date('Y-m');
$isSameMonth = true;
break;
case 'Yesterday':
$adIdsQuery->where('r.date', '=', date('Y-m-d', strtotime('-1 day')));
$currentMonth = date('Y-m', strtotime('-1 day'));
$isSameMonth = true;
break;
case 'Last Week':
$adIdsQuery->where('r.date', '>=', date('Y-m-d', strtotime('-1 week')));
// 比较日期的年月是否相同,若不同则为跨月
$isSameMonth = date('Y-m', strtotime('-1 week')) === date('Y-m');
break;
case 'Last Month':
$adIdsQuery->where('r.date', '>=', date('Y-m-d', strtotime('-1 month')));
$isSameMonth = false;
break;
case 'Last Year':
$adIdsQuery->where('r.date', '>=', date('Y-m-d', strtotime('-1 year')));
$isSameMonth = false;
break;
default:
break;
}
}
// 获取唯一的 ad_id 列表
$adIds = $adIdsQuery->distinct(true)->column('r.ad_id');
// dump($adIds);return($adIds);
// 2. 根据 ad_id 和日期范围去查询 DayData 表,按 ad_id 和日期聚合统计
$dayDataQuery = DayData::alias('d')
// ->leftJoin('bps.bps_google_ads_campaign c', 'd.campaign_id = c.campaign_id') // 关联广告系列表(如果有)
// ->leftJoin('bps.bps_google_ads_ad_group ag', 'd.ad_group_id = ag.ad_group_id') // 关联广告组表(如果有)
->whereIn('d.ad_id', $adIds) // 使用 ad_id 过滤
->where(function ($query) use ($startDate, $endDate, $dateRange) {
// 日期范围的筛选
if ($startDate && $endDate) {
$query->whereBetween('d.date', [$startDate, $endDate]);
} else {
switch ($dateRange) {
case 'Today':
$query->where('d.date', '=', date('Y-m-d'));
break;
case 'Yesterday':
$query->where('d.date', '=', date('Y-m-d', strtotime('-1 day')));
break;
case 'Last Week':
$query->where('d.date', '>=', date('Y-m-d', strtotime('-1 week')));
break;
case 'Last Month':
$query->where('d.date', '>=', date('Y-m-d', strtotime('-1 month')));
break;
case 'Last Year':
$query->where('d.date', '>=', date('Y-m-d', strtotime('-1 year')));
break;
default:
break;
}
}
});
// 如果跨月,按月聚合;如果不跨月,按 ad_id 聚合
if (!$isSameMonth) {
$dayDataQuery->group("d.month,d.ad_id,d.ad_name, d.ad_resource_name, d.ad_group_id, d.campaign_id, d.customer_id"); // 按月聚合
$dayDataQuery->field([
'd.ad_id',
'd.month',
// ThinkDb::raw("TO_CHAR(d.date, 'YYYY-MM') AS month"), // 格式化为 YYYY-MM 格式
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')
]);
} else {
$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')
]);
}
// 获取聚合数据,不需要再传递字段给 select()
$aggregatedData = $dayDataQuery->select();
// dump($aggregatedData);return($aggregatedData);
// 3. 获取与 ad_id 关联的 asset_id 以及相关数据
$assetRelationsQuery = AssetRelation::whereIn('ad_id', $adIds)
->whereBetween('date', [$startDate, $endDate]) // 加上 AssetRelation 的 date 过滤
->field(['asset_id', 'ad_id'])->select();
// dump($assetRelationsQuery);return($assetRelationsQuery);
// 4. 汇总每个 asset_id 下的所有 ad_id 的聚合数据
$assetSummaryData = [];
foreach ($assetRelationsQuery as $assetRelation) {
// 从聚合数据中找到当前 ad_id 的相关数据
// $adStats = $aggregatedData->where('ad_id', $assetRelation->ad_id)->first();
// 获取该 ad_id 的所有聚合数据(可能有多条记录)
$adStatsCollection = $aggregatedData->where('ad_id', $assetRelation->ad_id);
// dump($adStatsCollection);return($adStatsCollection);
if (!$adStatsCollection->isEmpty()) {
// 如果该 ad_id 有对应的聚合数据,初始化资产汇总数据
if (!isset($assetSummaryData[$assetRelation->asset_id])) {
$assetSummaryData[$assetRelation->asset_id] = [
'asset_id' => $assetRelation->asset_id,
'asset_name' => '-', // 获取 asset_name
'asset_type' => 0, // 获取 asset_type
'asset_url' => '-', // 获取 asset_url
'total_spend' => 0,
'total_conversions_value' => 0,
'total_conversions' => 0,
'total_impressions' => 0,
'ad_count' => 0,
'monthly_data' => [], // 按月存储数据
];
}
// 遍历该 ad_id 所有的统计数据
foreach ($adStatsCollection as $adStats) {
// 获取当前月份
if ($isSameMonth) {
$month = $currentMonth; // 格式化为 YYYY-MM
} else {
$month = $adStats->month; // 格式化日期为 'YYYY-MM'
}
// 获取 asset 相关信息,通过模型查询 Asset
$asset = Asset::find($assetRelation->asset_id); // 根据 asset_id 查找对应的 Asset 数据
// 累加该 ad_id 的统计数据到对应的 asset_id 汇总
$assetSummaryData[$assetRelation->asset_id]['asset_name'] = $asset->asset_name;
$assetSummaryData[$assetRelation->asset_id]['asset_type'] = $asset->asset_type;
$assetSummaryData[$assetRelation->asset_id]['asset_url'] = $asset->asset_url;
$assetSummaryData[$assetRelation->asset_id]['total_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;
$assetSummaryData[$assetRelation->asset_id]['ad'][] = $adStats->ad_id; // 存储 ad_id下一步统计数量
// 按月分开存储每个月的 spend 和 ROAS
if (!isset($assetSummaryData[$assetRelation->asset_id]['monthly_data'][$month])) {
$assetSummaryData[$assetRelation->asset_id]['monthly_data'][$month] = [
'month' => $month,
'spend' => 0,
'conversions_value' => 0,
];
}
// 累加每个月的数据
$assetSummaryData[$assetRelation->asset_id]['monthly_data'][$month]['spend'] += $adStats->total_spend;
$assetSummaryData[$assetRelation->asset_id]['monthly_data'][$month]['conversions_value'] += $adStats->total_conversions_value; // 避免除以零
}
}
}
//return $assetSummaryData;
// 5. 格式化输出数据
// 生成最终输出的数据
$resultData = [];
$chat_data = [];
$statistics = $this->initializeStatistics(); // Initialize statistics before processing
foreach ($assetSummaryData as $assetId => $data) {
// 计算 ROAS
$roas = $data['total_spend'] ? ($data['total_conversions_value'] / $data['total_spend']) : 0;
// 合并月度数据到总数据
$resultData[] = [
'creative_id' => $assetId,
'creative' => $data['asset_name'],
'creative_type' => $data['asset_type'],
'creative_url' => $data['asset_url'],
'spend' => '$' . number_format($data['total_spend'], 2),
'purchase_value' => '-',
'roas' => number_format($roas, 2) . 'X',
'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' => '$' . number_format($data['total_conversions_value'], 2),
'total_conversions' => $data['total_conversions'],
'total_impressions' => $data['total_impressions'],
'ad_count' => count($data['ad']),
// 'monthly_data' => $data['monthly_data'], // 合并月度数据
];
// 计算每个月的汇总数据,用于 chat_data
foreach ($data['monthly_data'] as $month => $monthlyData) {
// 如果 chat_data 中已有该月的数据,则累加
if (!isset($chat_data[$month])) {
$chat_data[$month] = [
'month' => $month,
'total_spend' => 0,
'total_conversions_value' => 0,
'roas' => 0,
];
}
// 累加该月的 spend 和 conversions_value
$chat_data[$month]['total_spend'] += $monthlyData['spend'];
$chat_data[$month]['total_conversions_value'] += $monthlyData['conversions_value'];
// Aggregate statistics for overall summary
$statistics['spend'] += $monthlyData['spend'];
$statistics['conversions_value'] += $monthlyData['conversions_value'];
}
}
// 计算每个月的总 ROASROAS = 总的转换值 / 总的支出
foreach ($chat_data as $month => $data) {
// 计算 ROAS
$totalSpend = $data['total_spend'];
$totalConversionsValue = $data['total_conversions_value'];
// 如果有支出数据,计算 ROAS
$chat_data[$month]['roas'] = $totalSpend ? ($totalConversionsValue / $totalSpend) : 0;
// 格式化 ROAS 为倍数
$chat_data[$month]['roas'] = round($chat_data[$month]['roas'], 2);
}
// 计算整体的 ROAS
$statistics['spend'] = '$' . number_format($statistics['spend'], 2);
$statistics['roas'] = $statistics['spend'] > 0 ? ($statistics['conversions_value'] / $statistics['spend']) : 0;
// 返回分页数据
$totalItems = count($assetSummaryData);
$totalPages = ceil($totalItems / $pageSize);
$startIndex = ($page - 1) * $pageSize;
// 截取返回的数据
// $pagedData = array_slice($assetSummaryData, $startIndex, $pageSize);
$resultDataPaginated = array_slice($resultData, ($page - 1) * $pageSize, $pageSize);
return [
'pagination' => [
'startIndex' => $startIndex,
'maxResults' => $pageSize,
'count' => $totalItems,
'pageNo' => $page,
'pageSize' => $pageSize,
'pages' => $totalPages
],
'chat_1_data' => array_values($chat_data), // 返回按月汇总的 chat_data
'data' => $resultDataPaginated,
'statistics' => $statistics
];
}
/**
* 初始化统计数据
*/
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
];
}
}