新原型的creative和图表接口

This commit is contained in:
huangguancheng 2025-01-09 23:16:06 +08:00
parent a3768b694e
commit 8e3dc9b325
5 changed files with 578 additions and 454 deletions

View File

@ -266,7 +266,7 @@ class BpsAdController
// 获取客户ID数组
$accountIds = array_column($accounts, 'account_id');
// dump($accountIds);
// 调用 Service 层查询广告组列表
$result = $this->adsInsightService::getAdsetList(
$platformType,
@ -282,241 +282,96 @@ class BpsAdController
return $this->successResponse($result, $request);
}
public function exportAdsToExcel(Request $request)
public function listCreatives(Request $request)
{
$options = $request->all();
$options = $request->all();
$options['jwtClaims'] = $request->jwtClaims;
// 获取请求参数
$keyword = $options['conditions']['keyword'] ?? ''; // 关键字搜索
$startDate = $options['conditions']['startDate'] ?? null; // 开始日期
$endDate = $options['conditions']['endDate'] ?? null; // 结束日期
$dateRange = 'Last Week'; // 默认日期范围
$page = $options['pageNo'] ?? 1; // 页码
$pageSize = $options['pageSize'] ?? 1000; // 每页数量
$keyword = $options['conditions']['keyword'] ?? ''; // 关键字搜索
$platformType = $options['conditions']['platformType'] ?? 0; // 平台类型
$startDate = $options['conditions']['startDate'] ?? null; // 开始日期
$endDate = $options['conditions']['endDate'] ?? null; // 结束日期
// $dateRange = 'Last Week'; // 默认日期范围
$customers = $this->googleOAuthService->getGoogleAdCustomers(['refresh_token' => $request->refresh_token]);
$customerIds = array_column($customers, 'customer_id');
// 调用 service 层导出数据
return $this->googleAdsReportService::exportAdListToExcel($customerIds, $keyword, $dateRange, $startDate, $endDate);
}
// 根据 platformType 获取广告账户
if ($platformType === 1) {
$accounts = $this->bpsAdAccountService->getMetaAdAccounts(['refresh_token' => $request->refresh_token]);
} elseif ($platformType === 2) {
$accounts = $this->bpsAdAccountService->getGoogleAdAccounts(['refresh_token' => $request->refresh_token]);
} elseif ($platformType === 3) {
$accounts = $this->bpsAdAccountService->getTiktokAdAccounts(['refresh_token' => $request->refresh_token]);
} else {
// TODO: 匹配jwt的商户id还是登录用户id
$accounts = $this->bpsAdAccountService->getAllAdAccounts(['uid' => $options['jwtClaims']['uid']]);
}
public function exportCampaignsToExcel(Request $request)
{
$options = $request->all();
// 获取请求参数
$keyword = $options['conditions']['keyword'] ?? ''; // 关键字搜索
$startDate = $options['conditions']['startDate'] ?? null; // 开始日期
$endDate = $options['conditions']['endDate'] ?? null; // 结束日期
$dateRange = 'Last Week'; // 默认日期范围
if (empty($accounts)) {
return $this->errorResponse(1, 'no data');
}
$customers = $this->googleOAuthService->getGoogleAdCustomers(['refresh_token' => $request->refresh_token]);
$customerIds = array_column($customers, 'customer_id');
// dump($customerIds);
// 调用 service 层导出数据
return $this->googleAdsReportService->exportCampaignsToExcel($customerIds, $keyword, $dateRange, $startDate, $endDate);
}
public function exportGroupsToExcel(Request $request)
{
$options = $request->all();
// 获取请求参数
$keyword = $options['conditions']['keyword'] ?? ''; // 关键字搜索
$startDate = $options['conditions']['startDate'] ?? null; // 开始日期
$endDate = $options['conditions']['endDate'] ?? null; // 结束日期
$dateRange = 'Last Week'; // 默认日期范围
$customers = $this->googleOAuthService->getGoogleAdCustomers(['refresh_token' => $request->refresh_token]);
$customerIds = array_column($customers, 'customer_id');
// 你可以进一步验证日期格式(可选)
// if ($startDate && !strtotime($startDate)) {
// return response()->json(['error' => 'Invalid start date format'], 400);
// }
// if ($endDate && !strtotime($endDate)) {
// return response()->json(['error' => 'Invalid end date format'], 400);
// }
// 调用 service 层导出数据
return $this->googleAdsReportService->exportAdGroupsToExcel($customerIds, $keyword, $dateRange, $startDate, $endDate);
}
public function listGroups(Request $request)
{
$options = $request->all();
// 获取请求参数
$page = $options['pageNo'] ?? 1; // 页码
$pageSize = $options['pageSize'] ?? 10; // 每页数量
$keyword = $options['conditions']['keyword'] ?? ''; // 关键字搜索
$startDate = $options['conditions']['startDate'] ?? null; // 开始日期
$endDate = $options['conditions']['endDate'] ?? null; // 结束日期
$dateRange = 'Last Week'; // 默认日期范围
$customers = $this->googleOAuthService->getGoogleAdCustomers(['refresh_token' => $request->refresh_token]);
$customerIds = array_column($customers, 'customer_id');
// 调用 Service 层查询
$result = $this->googleAdsReportService::getAdGroupList(
$customerIds, // 客户 ID 数组
// 获取客户ID数组
$accountIds = array_column($accounts, 'account_id');
// dump($accountIds);
// 调用 Service 层查询广告组列表
$result = $this->adsInsightService->getCreativeInsightData(
$platformType,
$accountIds, // 客户 ID 数组
$page, // 页码
$pageSize, // 每页数量
$keyword, // 关键字
$dateRange,
$startDate, // 开始日期
$endDate
$endDate // 结束日期
);
// 返回结果
return $this->successResponse($result, $request);
}
/**
* 获取广告系列的状态 备用
*/
// public function getCampaignStatus(int $campaignId): Response
// {
// try {
// $status = $this->googleAdsCampaignService->getCampaignStatus($campaignId);
// return $this->successResponse(['status' => $status]);
// } catch (ValidateException $e) {
// return $this->errorResponse(400, $e->getMessage());
// }
// }
/**
* 更新广告系列的状态
*/
public function updateCampaignStatus(Request $request): Response
public function listCharts(Request $request)
{
$campaignStatus = [
0, // UNSPECIFIED
1, // UNKNOWN
2, // ENABLED
3, // PAUSED
4, // REMOVED
];
$requestData = $request->all(); // 获取请求数据
$requestData['refresh_token'] = $request->refresh_token;
$requestData['login_customer_id'] = $request->login_customer_id;
// dump($requestData);
$options = $request->all();
$options['jwtClaims'] = $request->jwtClaims;
$status = $requestData['status'];
if (!in_array($status, $campaignStatus)) {
return $this->errorResponse(101, 'status参数错误');
// 获取请求参数
$cycle = $options['conditions']['cycle'] ?? 3; // 页码
$platformType = $options['conditions']['platformType'] ?? 0; // 平台类型
$startDate = $options['conditions']['startDate'] ?? null; // 开始日期
$endDate = $options['conditions']['endDate'] ?? null; // 结束日期
// $dateRange = 'Last Week'; // 默认日期范围
// 根据 platformType 获取广告账户
if ($platformType === 1) {
$accounts = $this->bpsAdAccountService->getMetaAdAccounts(['refresh_token' => $request->refresh_token]);
} elseif ($platformType === 2) {
$accounts = $this->bpsAdAccountService->getGoogleAdAccounts(['refresh_token' => $request->refresh_token]);
} elseif ($platformType === 3) {
$accounts = $this->bpsAdAccountService->getTiktokAdAccounts(['refresh_token' => $request->refresh_token]);
} else {
// TODO: 匹配jwt的商户id还是登录用户id
$accounts = $this->bpsAdAccountService->getAllAdAccounts(['uid' => $options['jwtClaims']['uid']]);
}
// try {
$reslut = $this->googleAdsCampaignService->updateCampaignStatus($requestData);
if (!$reslut) {
return $this->errorResponse(101, 'Status update failed');
if (empty($accounts)) {
return $this->errorResponse(1, 'no data');
}
return $this->successResponse(['message' => 'Status updated successfully'], $request);
// } catch (ValidateException $e) {
// return $this->errorResponse(400, $e->getMessage());
// }
}
/**
* 判断广告系列是否启用
*/
// public function isEnabled(int $campaignId): Json
// {
// try {
// $isEnabled = $this->campaignService->isCampaignEnabled($campaignId);
// return json(['enabled' => $isEnabled], 200);
// } catch (ValidateException $e) {
// return json(['error' => $e->getMessage()], 400);
// }
// }
// 获取客户ID数组
$accountIds = array_column($accounts, 'account_id');
// dump($accountIds);
// 调用 Service 层查询广告组列表
$result = $this->adsInsightService->getAdCycleInsight(
$platformType,
$accountIds, // 客户 ID 数组
$cycle, // 页码
$startDate, // 开始日期
$endDate // 结束日期
);
/**
* 判断广告系列是否暂停
*/
// public function isPaused(int $campaignId): Json
// {
// try {
// $isPaused = $this->campaignService->isCampaignPaused($campaignId);
// return json(['paused' => $isPaused], 200);
// } catch (ValidateException $e) {
// return json(['error' => $e->getMessage()], 400);
// }
// }
/**
* 判断广告系列是否停止
*/
// public function isStopped(int $campaignId): Json
// {
// try {
// $isStopped = $this->campaignService->isCampaignStopped($campaignId);
// return json(['stopped' => $isStopped], 200);
// } catch (ValidateException $e) {
// return json(['error' => $e->getMessage()], 400);
// }
// }
//
/**
* 更新广告组的状态
*/
public function updateGroupStatus(Request $request): Response
{
$adGroupStatus = [
0, // UNSPECIFIED
1, // UNKNOWN
2, // ENABLED
3, // PAUSED
4 // REMOVED
];
$requestData = $request->all(); // 获取请求数据
$requestData['refresh_token'] = $request->refresh_token;
$requestData['login_customer_id'] = $request->login_customer_id;
// dump($requestData);
$status = $requestData['status'];
// $options['bid_micro_amount'] = $options['amount'] * 1000000 < 0 ? 0 : $options['amount'] * 1000000;
if (!in_array($status, $adGroupStatus)) {
return $this->errorResponse(101, 'status参数错误');
}
// try {
$result = $this->googleAdsGroupService->updateGroupStatus($requestData);
if (!$result) {
return $this->errorResponse(101, 'Status update failed');
}
return $this->successResponse(['message' => 'Status updated successfully'], $request);
// } catch (ValidateException $e) {
// return $this->errorResponse(400, $e->getMessage());
// }
}
/**
* 更新广告的状态
*/
public function updateAdStatus(Request $request): Response
{
$adStatus = [
0, // UNSPECIFIED
1, // UNKNOWN
2, // ENABLED
3, // PAUSED
4 // REMOVED
];
$requestData = $request->all(); // 获取请求数据
$requestData['refresh_token'] = $request->refresh_token;
$requestData['login_customer_id'] = $request->login_customer_id;
// dump($requestData);
$status = $requestData['status'];
if (!in_array($status, $adStatus)) {
return $this->errorResponse(101, 'status参数错误');
}
// try {
$result = $this->googleAdsAdService->updateAdStatus($requestData);
if (!$result) {
return $this->errorResponse(101, 'Status update failed');
}
return $this->successResponse(['message' => 'Status updated successfully'], $request);
// } catch (ValidateException $e) {
// return $this->errorResponse(400, $e->getMessage());
// }
// 返回结果
return $this->successResponse($result, $request);
}

View File

@ -248,6 +248,8 @@ class GoogleAdsController
*/
public function getCampaigns($options): Response
{
$options['login_customer_id'] = 1401879025;
$options['refresh_token'] = '1//0en4AWGamrTnOCgYIARAAGA4SNwF-L9IrMOw2DkHF2y3DL2kDiEmOUzk5KTsR1f1iU_axBNi-LgxfT_76JHK4AY1KChZWfyNc0Qs';
$resourceName = $this->googleAdsCampaignService->runListCampaigns($options['customer_id'], $options);
return $this->successResponse(['campaigns_list' => $resourceName]);
}
@ -267,6 +269,9 @@ class GoogleAdsController
*/
public function getAds($options): Response
{
$options['login_customer_id'] = 1401879025;
$options['refresh_token'] = '1//0en4AWGamrTnOCgYIARAAGA4SNwF-L9IrMOw2DkHF2y3DL2kDiEmOUzk5KTsR1f1iU_axBNi-LgxfT_76JHK4AY1KChZWfyNc0Qs';
$resourceName = $this->googleAdsAdService->runListAds($options['customer_id'],$options);
return $this->successResponse(['groups_list' => $resourceName]);
}
@ -312,9 +317,11 @@ class GoogleAdsController
*/
public function getDateDatas($options): Response
{
$options['login_customer_id'] = 1509096882;
$options['refresh_token'] = '1//0eOTBBKbP-sPACgYIARAAGA4SNwF-L9Irg1hApAtVpZfvSCKX_pSSV2CJ7Fye3m1p3Nw12VbCjJ2Ey2D02dVy5iDhCET79TfWY0s';
$resourceName = $this->googleAdsCampaignService->runListDateDatas($options['customer_id'],$options, '2025-01-04');
$todayStart = date('Y-m-d', strtotime('0 day'));
$options['login_customer_id'] = 1401879025;
$options['refresh_token'] = '1//0en4AWGamrTnOCgYIARAAGA4SNwF-L9IrMOw2DkHF2y3DL2kDiEmOUzk5KTsR1f1iU_axBNi-LgxfT_76JHK4AY1KChZWfyNc0Qs';
// dump($todayStart);
$resourceName = $this->googleAdsCampaignService->runListDateDatas($options['customer_id'],$options, '2025-01-09');
return $this->successResponse(['date_datas_list' => $resourceName]);
}

View File

@ -0,0 +1,50 @@
<?php
namespace app\model;
use think\Model;
class BpsAdCreative extends Model
{
// 设置当前模型对应的完整数据表名称
protected $table = 'bps.bps_ads_creative';
// 设置主键
// protected $pk = 'creative_id';
// 设置自动时间戳
protected $autoWriteTimestamp = true;
// 定义时间戳字段
protected $createTime = 'created_at';
protected $updateTime = 'updated_at';
// 设置字段类型和默认值
protected $casts = [
'platform' => 'int',
'account_id' => 'string',
'customer_id' => 'string',
'creative_id' => 'string',
'name' => 'string',
'title' => 'string',
'thumbnail_url' => 'string',
'url' => 'string',
'type' => 'int',
'object_type' => 'string',
'status' => 'int',
'created_at' => 'timestamp',
'updated_at' => 'timestamp',
];
// 默认值设置
protected $defaults = [
'name' => '',
'title' => '',
'thumbnail_url' => '',
'url' => '',
'type' => 1, // 默认为图片
'object_type' => '',
'status' => 2, // 默认为启用
];
}

View File

@ -0,0 +1,94 @@
<?php
namespace app\model;
use think\Model;
class BpsAdCreativeInsight extends Model
{
// 设置当前模型对应的完整数据表名称
protected $table = 'bps.bps_ads_creative_insights';
// 设置复合主键
protected $pk = ['id'];
// 设置自动时间戳
protected $autoWriteTimestamp = true;
// 定义时间戳字段
protected $createTime = 'create_at';
protected $updateTime = 'update_at';
// 设置字段类型和默认值
protected $casts = [
'platform' => 'int',
'creative_id' => 'int',
'account_id' => 'int',
'clicks' => 'int',
'spend' => 'int',
'purchases_value' => 'int',
'revenue' => 'int',
'impressions' => 'int',
'date' => 'date',
];
// 默认值设置
protected $defaults = [
'clicks' => 0,
'spend' => 0,
'purchases_value' => 0,
'revenue ' => 0,
'impressions' => 0,
'ad_name' => '',
];
// 检查唯一键
// protected function checkUniqueKey()
// {
// $exists = $this->where('ad_id', $this->ad_id)
// ->where('date', $this->date)
// ->where('platform', $this->platform)
// ->find();
//
// if ($exists && $exists->id != $this->id) {
// throw new \Exception('数据已存在ad_id、date 和 platform 必须唯一');
// }
// }
// 在保存数据前调用
// protected static function onBeforeWrite($model)
// {
// $model->checkUniqueKey();
// }
// 关联 Campaign 模型(报告数据属于广告活动)
// public function campaign()
// {
// return $this->belongsTo(BpsAdCampaign::class, 'campaign_id', 'campaign_id');
// }
// 关联 AdGroup 模型(报告数据属于广告组)
// public function adSet()
// {
// return $this->belongsTo(BpsAdSet::class, 'ad_set_id', 'ad_set_id');
// }
// 关联 Ad 模型(报告数据属于广告)
// public function ad()
// {
// return $this->belongsTo(BpsAdAd::class, 'ad_id', 'ad_id');
// }
// 关联 Customer 模型(报告数据与客户相关)
// public function customer()
// {
// return $this->belongsTo(GoogleAdsCustomer::class, 'account_id', 'account_id');
// }
// 关联 Budget 模型(报告数据与预算相关)
// public function budget()
// {
// return $this->belongsTo(CampaignBudget::class, 'budget_id', 'budget_id');
// }
}

View File

@ -3,6 +3,8 @@
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;
@ -641,19 +643,8 @@ class AdsInsightService
* @param int $pageSize 每页数量
* @return array
*/
public function getAssetConversionData($customerIds, $page, $pageSize, $keyword, $dateRange, $startDate = null, $endDate = null)
public function getCreativeInsightData11111($platformType, $customerIds, $page, $pageSize, $keyword, $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') // 关联资产表
@ -669,7 +660,6 @@ class AdsInsightService
} else {
return [
'data' => [],
'chat_1_data' => [], // 返回按月汇总的 chat_data
'total' => 0,
'statistics' => [],
'pagination' => [
@ -686,281 +676,263 @@ class AdsInsightService
// 日期范围处理,筛选 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 和日期聚合统计
// 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) {
->where(function ($query) use ($startDate, $endDate) {
// 日期范围的筛选
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()
$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();
// 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 的聚合数据
// 4. 汇总每个 asset_id 下的所有 ad_id 的聚合数据
$assetSummaryData = [];
$statisticsData = $this->initializeStatistics(); // 初始化统计数据
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,
'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,
'monthly_data' => [], // 按月存储数据
'ad_count' => 0
];
}
// 遍历该 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]['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,
];
// 汇总 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;
}
// 累加该月的 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'];
// 计算 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
}
}
// 计算每个月的总 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 [
'data' => array_values($assetSummaryData),
'total' => count($assetSummaryData),
'statistics' => $statisticsData, // 汇总的统计数据
'pagination' => [
'startIndex' => $startIndex,
'startIndex' => ($page - 1) * $pageSize,
'maxResults' => $pageSize,
'count' => $totalItems,
'count' => count($assetSummaryData),
'pageNo' => $page,
'pageSize' => $pageSize,
'pages' => $totalPages
],
'chat_1_data' => array_values($chat_data), // 返回按月汇总的 chat_data
'data' => $resultDataPaginated,
'statistics' => $statistics
'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)
]
];
}
/**
* 初始化统计数据
*/
@ -992,4 +964,150 @@ class AdsInsightService
// 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');
}
}