增加广告landing_page - 更新1
This commit is contained in:
parent
2094a49b67
commit
5d3c2e269e
@ -270,7 +270,7 @@ class GoogleAdsController
|
||||
public function getAds($options): Response
|
||||
{
|
||||
// $options['login_customer_id'] = 1401879025;
|
||||
$options['refresh_token'] = '1//0eeLHHxVH5EK1CgYIARAAGA4SNwF-L9Irjf3gV-k7bjIeT7WWpbNrvvJQU2cM9OTPUnG4aBYIJDntlUzQ8T1egKkQco5lNtg28FA';
|
||||
$options['refresh_token'] = '1//0eeaBHt4kvLb1CgYIARAAGA4SNwF-L9IrnDelBOMlPpU-5W7Zvs7lfLW2XC5U0szxRM1_BkoNNVcWsAYc07Tf63dj1R0Ea-AtwZc';
|
||||
|
||||
$resourceName = $this->googleAdsAdService->runListAds($options['customer_id'],$options);
|
||||
return $this->successResponse(['groups_list' => $resourceName]);
|
||||
@ -283,7 +283,7 @@ class GoogleAdsController
|
||||
public function getAssets($options): Response
|
||||
{
|
||||
$options['login_customer_id'] = 1401879025;
|
||||
$options['refresh_token'] = '1//0en4AWGamrTnOCgYIARAAGA4SNwF-L9IrMOw2DkHF2y3DL2kDiEmOUzk5KTsR1f1iU_axBNi-LgxfT_76JHK4AY1KChZWfyNc0Qs';
|
||||
$options['refresh_token'] = ',1//0eeaBHt4kvLb1CgYIARAAGA4SNwF-L9IrnDelBOMlPpU-5W7Zvs7lfLW2XC5U0szxRM1_BkoNNVcWsAYc07Tf63dj1R0Ea-AtwZc';
|
||||
$resourceName = $this->googleAdsAssetService->runListCreatives($options['customer_id'],$options);
|
||||
// return $this->successResponse(['assets_list' => $resourceName]);
|
||||
return $this->successResponse(['assets_list' => 'succeed added']);
|
||||
@ -320,10 +320,10 @@ class GoogleAdsController
|
||||
public function getDateDatas($options): Response
|
||||
{
|
||||
$todayStart = date('Y-m-d', strtotime('0 day'));
|
||||
$options['login_customer_id'] = 1401879025;
|
||||
$options['refresh_token'] = '1//0en4AWGamrTnOCgYIARAAGA4SNwF-L9IrMOw2DkHF2y3DL2kDiEmOUzk5KTsR1f1iU_axBNi-LgxfT_76JHK4AY1KChZWfyNc0Qs';
|
||||
$options['login_customer_id'] = 2202747396;
|
||||
$options['refresh_token'] = '1//0eeaBHt4kvLb1CgYIARAAGA4SNwF-L9IrnDelBOMlPpU-5W7Zvs7lfLW2XC5U0szxRM1_BkoNNVcWsAYc07Tf63dj1R0Ea-AtwZc';
|
||||
// dump($todayStart);
|
||||
$resourceName = $this->googleAdsCampaignService->runListDateDatas($options['customer_id'],$options, '2025-01-09');
|
||||
$resourceName = $this->googleAdsCampaignService->runListDateDatas($options['customer_id'],$options, '2024-12-21');
|
||||
return $this->successResponse(['date_datas_list' => $resourceName]);
|
||||
}
|
||||
|
||||
|
32
app/model/BpsAdLandingInsight.php
Normal file
32
app/model/BpsAdLandingInsight.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class BpsAdLandingInsight extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $table = 'bps.bps_ads_landing_url_insights';
|
||||
|
||||
// 设置复合主键
|
||||
protected $pk = ['id'];
|
||||
|
||||
// 设置自动时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 定义时间戳字段
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
// 设置字段类型和默认值
|
||||
protected $casts = [
|
||||
|
||||
];
|
||||
|
||||
// 默认值设置
|
||||
protected $defaults = [
|
||||
|
||||
];
|
||||
|
||||
}
|
39
app/model/BpsAdLandingUrl.php
Normal file
39
app/model/BpsAdLandingUrl.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class BpsAdLandingUrl extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $table = 'bps.bps_ads_landing_url';
|
||||
|
||||
// 设置主键
|
||||
// protected $pk = 'creative_id';
|
||||
|
||||
// 设置自动时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 定义时间戳字段
|
||||
protected $createTime = 'created_at';
|
||||
protected $updateTime = 'updated_at';
|
||||
|
||||
// 设置字段类型和默认值
|
||||
protected $casts = [
|
||||
'platform_type' => 'int',
|
||||
'account_id' => 'string',
|
||||
'ad_id' => 'string',
|
||||
'landing_url' => 'string',
|
||||
'landing_access' => 'string',
|
||||
'create_at' => 'timestamp',
|
||||
'update_at' => 'timestamp',
|
||||
];
|
||||
|
||||
// 默认值设置
|
||||
protected $defaults = [
|
||||
'landing_access' => '',
|
||||
'landing_url' => '',
|
||||
];
|
||||
|
||||
}
|
279
app/service/LandingUrlInsightService.php
Normal file
279
app/service/LandingUrlInsightService.php
Normal file
@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use app\model\Ad;
|
||||
use app\model\BpsAdCreativeInsight;
|
||||
use app\model\BpsAdAd;
|
||||
use app\model\AssetRelation;
|
||||
use app\model\BpsAdLandingUrl;
|
||||
use DateTime;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
use think\db\exception\DbException;
|
||||
use think\facade\Db as ThinkDb;
|
||||
use support\Response;
|
||||
|
||||
class LandingUrlInsightService
|
||||
{
|
||||
// 状态映射数组
|
||||
private static $statusMapping = [
|
||||
0 => 'UNSPECIFIED', // UNSPECIFIED
|
||||
1 => 'UNKNOWN', // UNKNOWN
|
||||
2 => 'ENABLED', // ENABLED
|
||||
3 => 'PAUSED', // PAUSED
|
||||
4 => 'REMOVED', // REMOVED
|
||||
5 => 'FROZEN', // FROZEN
|
||||
6 => 'ARCHIVED', // ARCHIVED
|
||||
];
|
||||
// 状态映射数组
|
||||
private static $platformMapping = [
|
||||
1 => 'meta',
|
||||
2 => 'google',
|
||||
3 => 'tiktok',
|
||||
];
|
||||
// 状态映射数组
|
||||
private static $creativeTypeMapping = [
|
||||
1 => 'image',
|
||||
2 => 'video',
|
||||
];
|
||||
|
||||
|
||||
public static function getLandingUrlInsightData($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $countOnly = false)
|
||||
{
|
||||
// 1. 创建查询对象,初始化 BpsAdCreativeInsight 查询
|
||||
$landingUrlDataQuery = BpsAdLandingUrl::alias('i')
|
||||
->join('bps.bps_ads_insights c', 'i.ad_id = c.ad_id', 'LEFT'); // 联接 bps_ads_creative 表
|
||||
if ($platformType != 0) {
|
||||
$landingUrlDataQuery->where('i.platform_type', $platformType); // 只有 platformType 不为 0 时才添加筛选
|
||||
}
|
||||
|
||||
// 2. 日期范围筛选
|
||||
if ($startDate && $endDate) {
|
||||
$landingUrlDataQuery->whereBetween('c.date', [$startDate, $endDate]);
|
||||
}
|
||||
|
||||
// 3. 客户 ID 过滤(如果提供了)
|
||||
if (!empty($customerIds)) {
|
||||
$landingUrlDataQuery->whereIn('i.account_id', $customerIds);
|
||||
} else {
|
||||
return $countOnly ? 0 : [
|
||||
'data' => [],
|
||||
'total' => 0,
|
||||
'statistics' => [],
|
||||
'pagination' => [
|
||||
'startIndex' => 0,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => 0,
|
||||
'pageNo' => 1,
|
||||
'pageSize' => $pageSize
|
||||
]
|
||||
];
|
||||
}
|
||||
// 仅返回记录数时的逻辑
|
||||
if ($countOnly) {
|
||||
return $landingUrlDataQuery->count('distinct(i.landing_url)') ?: 0;
|
||||
}
|
||||
|
||||
// 4. 关键词过滤
|
||||
if ($keyword) {
|
||||
// $creativeDataQuery->where('c.name', 'like', '%' . $keyword . '%'); // 在 bps_ads_creative 表中查找关键词
|
||||
$landingUrlDataQuery->whereRaw('LOWER(i.landing_url) LIKE ?', ['%' . strtolower($keyword) . '%']); // 在 bps_ads_creative 表中查找关键词
|
||||
}
|
||||
// 5. 数据聚合(按 landing_url 和其他字段)
|
||||
$landingUrlDataQuery->group('i.landing_url, i.platform_type, i.account_id') // 按需要的字段分组
|
||||
->field([
|
||||
'i.landing_url',
|
||||
'i.platform_type',
|
||||
'i.account_id',
|
||||
ThinkDb::raw('COALESCE(SUM(c.spend) / 1000000, 0) AS total_spend'),
|
||||
ThinkDb::raw('COALESCE(SUM(c.purchases_value) / 1000000, 0) AS total_purchases_value'),
|
||||
ThinkDb::raw('COALESCE(SUM(c.purchases), 0) AS total_purchases'),
|
||||
ThinkDb::raw('COALESCE(SUM(c.revenue) / 1000000, 0) AS total_revenue'),
|
||||
ThinkDb::raw('COALESCE(SUM(c.impressions), 0) AS total_impressions'),
|
||||
ThinkDb::raw('COALESCE(SUM(c.clicks), 0) AS total_clicks'),
|
||||
ThinkDb::raw('count(distinct(i.ad_id)) AS ad_count'),
|
||||
ThinkDb::raw('-1 AS click_to_atc_ratio'),
|
||||
ThinkDb::raw('-1 AS click_to_purchase'),
|
||||
ThinkDb::raw('-1 AS thumbstop'),
|
||||
ThinkDb::raw('-1 AS hold_rate'),
|
||||
ThinkDb::raw('-1 AS ctr_outbound'),//Hook score Watch score Click score Convert score
|
||||
ThinkDb::raw('-1 AS hook_score'),
|
||||
ThinkDb::raw('-1 AS watch_score'),
|
||||
ThinkDb::raw('-1 AS click_score'),
|
||||
ThinkDb::raw('-1 AS convert_score'),
|
||||
ThinkDb::raw('-1 AS ctr_link_click'),
|
||||
]);
|
||||
|
||||
// 6. 执行查询并获取聚合结果
|
||||
// $aggregatedData = $creativeDataQuery->select();
|
||||
// 6. 执行查询并获取聚合结果
|
||||
$aggregatedData = $landingUrlDataQuery->select()->toArray(); // 转换为数组处理
|
||||
// dump($landingUrlDataQuery->getLastSql());
|
||||
$totalRecords = count($aggregatedData); // 总记录数
|
||||
|
||||
// 计算分页起始索引
|
||||
$startIndex = ($page - 1) * $pageSize;
|
||||
|
||||
|
||||
// 打印调试 SQL 查询
|
||||
//$sql = $creativeDataQuery->getLastSql();
|
||||
// dump($sql);
|
||||
|
||||
// 7. 初始化广告创意的汇总数据和统计数据
|
||||
$landingUrlSummaryData = [];
|
||||
// $statisticsData = $this->initializeStatistics();
|
||||
$statisticsData = [];
|
||||
|
||||
// 8. 遍历查询结果并计算每个 creative 的相关统计数据
|
||||
foreach ($aggregatedData as $landingData) {
|
||||
// $landingUrl = self::normalizeUrl($landingData['landing_url']);
|
||||
$landingUrlHash = self::generateHash($landingData['landing_url']);
|
||||
if (!isset($creativeSummaryData[$landingUrlHash])) {
|
||||
$landingUrlSummaryData[$landingUrlHash] = [
|
||||
'landing_url' => $landingData['landing_url'],
|
||||
'platform_type' => $landingData['platform_type'],
|
||||
'account_id' => $landingData['account_id'],
|
||||
'total_conversions_value' => 0,
|
||||
'total_conversions' => 0,
|
||||
'impressions' => 0,
|
||||
'clicks' => 0,
|
||||
'revenue' => 0,
|
||||
'ad_count' => $landingData['ad_count'] ?: 0,
|
||||
'spend' => 0,
|
||||
'purchases' => 0,
|
||||
'purchases_value' => 0,
|
||||
'roas' => 0,
|
||||
'aov' => '-',
|
||||
'click_to_atc_ratio' => '-',
|
||||
'click_to_purchase' => '-',
|
||||
'thumbstop' => '-',
|
||||
'hold_rate' => '-',
|
||||
'ctr_outbound' => '-',
|
||||
'hook_score' => '-',
|
||||
'watch_score' => '-',
|
||||
'click_score' => '-',
|
||||
'convert_score' => '-',
|
||||
'ctr_link_click' => '-',
|
||||
];
|
||||
}
|
||||
|
||||
// 更新该 landing_url 的统计数据
|
||||
$landingUrlSummaryData[$landingUrlHash]['spend'] += $landingData['total_spend'];
|
||||
$landingUrlSummaryData[$landingUrlHash]['purchases_value'] += $landingData['total_purchases_value'];
|
||||
$landingUrlSummaryData[$landingUrlHash]['purchases'] += $landingData['total_purchases'];
|
||||
$landingUrlSummaryData[$landingUrlHash]['impressions'] += $landingData['total_impressions'];
|
||||
$landingUrlSummaryData[$landingUrlHash]['clicks'] += $landingData['total_clicks'];
|
||||
$landingUrlSummaryData[$landingUrlHash]['revenue'] += $landingData['total_revenue'];
|
||||
// 计算 ROAS
|
||||
$roas = $landingUrlSummaryData[$landingUrlHash]['spend'] > 0
|
||||
? $landingUrlSummaryData[$landingUrlHash]['revenue'] / $landingUrlSummaryData[$landingUrlHash]['spend']
|
||||
: 0;
|
||||
$landingUrlSummaryData[$landingUrlHash]['roas'] = $roas > 0 ? number_format($roas, 2) . 'X' : '-';
|
||||
|
||||
// 填充广告计数
|
||||
// $landingUrlSummaryData[$creativeData->creative_id]['ad_count'] = rand(10, 200); // 每个 creative_id 对应一个广告
|
||||
}
|
||||
// dump($landingUrlSummaryData);
|
||||
|
||||
// 汇总总体统计数据
|
||||
$statisticsData['spend'] = array_sum(array_column($landingUrlSummaryData, 'spend'));
|
||||
$statisticsData['purchases_value'] = array_sum(array_column($landingUrlSummaryData, 'purchases_value'));
|
||||
$statisticsData['purchases'] = array_sum(array_column($landingUrlSummaryData, 'purchases'));
|
||||
$statisticsData['impressions'] = array_sum(array_column($landingUrlSummaryData, 'impressions'));
|
||||
$statisticsData['clicks'] = array_sum(array_column($landingUrlSummaryData, 'clicks'));
|
||||
$statisticsData['revenue'] = array_sum(array_column($landingUrlSummaryData, 'revenue'));
|
||||
// 汇总统计数据
|
||||
$statistics = [
|
||||
'spend' => '$' . number_format($statisticsData['spend'], 2), // 格式化金额
|
||||
'purchases_value' => '$' . number_format($statisticsData['purchases_value'], 2), // 格式化金额
|
||||
'purchases' => $statisticsData['purchases'],
|
||||
'click_to_atc_ratio' => '-', // 格式化百分比
|
||||
'click_to_purchase' => '-',// 格式化百分比
|
||||
'roas' => $statisticsData['spend'] == 0 ? '-' : round($statisticsData['revenue'] / $statisticsData['spend'], 2) . 'X',
|
||||
'aov' => '-', // 格式化金额
|
||||
'thumbstop' => '-',
|
||||
'hold_rate' => '-',// 格式化百分比
|
||||
'ctr_outbound' => '-',
|
||||
'cpa' => '-', // 格式化金额
|
||||
'hook_score' => '-',
|
||||
'watch_score' => '-',
|
||||
'click_score' => '-',
|
||||
'convert_score' => '-',
|
||||
'ctr_link_click' => ($statisticsData['impressions'] > 0) ? number_format(($statisticsData['clicks'] / $statisticsData['impressions']) * 100, 2) . '%' : '-', // 格式化为百分比
|
||||
|
||||
];
|
||||
|
||||
|
||||
// 格式化返回的创意数据
|
||||
$formattedData = array_map(function ($item) {
|
||||
return [
|
||||
'landing_url' => $item['landing_url'],
|
||||
'platform_type' => $item['platform_type'],
|
||||
'account_id' => $item['account_id'],
|
||||
'ad_count' => $item['ad_count'],
|
||||
'spend' => '$' . number_format($item['spend'], 2),
|
||||
'purchases' => $item['purchases'],
|
||||
'purchases_value' => '$' . number_format($item['purchases_value'], 2),
|
||||
'click_to_atc_ratio' => '-',
|
||||
'click_to_purchase' => '-',// 格式化百分比
|
||||
'roas' => $item['roas'],
|
||||
'aov' => '-',
|
||||
'thumbstop' => '-',
|
||||
'hold_rate' => '-',// 格式化百分比
|
||||
'ctr_outbound' => '-',
|
||||
'cpa' => '-', // 格式化金额
|
||||
'hook_score' => '-',
|
||||
'watch_score' => '-',
|
||||
'click_score' => '-',
|
||||
'convert_score' => '-',
|
||||
'ctr_link_click' => ($item['impressions'] > 0) ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-',
|
||||
|
||||
// 添加更多的格式化字段
|
||||
];
|
||||
}, $landingUrlSummaryData);
|
||||
// $formattedData = array_values($landingUrlSummaryData); // 未分页处理
|
||||
$pagedData = array_slice(array_values($formattedData), $startIndex, $pageSize); // 分页处理
|
||||
// 9. 返回分页数据
|
||||
return [
|
||||
'data' => $pagedData,
|
||||
'total' => count($landingUrlSummaryData),
|
||||
'statistics' => $statistics, // 汇总的统计数据
|
||||
'pagination' => [
|
||||
'startIndex' => ($page - 1) * $pageSize,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => count($landingUrlSummaryData),
|
||||
'pageNo' => $page,
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => ceil(count($landingUrlSummaryData) / $pageSize)
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public static function normalizeUrl($url)
|
||||
{
|
||||
$url = strtolower($url); // 统一小写
|
||||
$url = preg_replace('#^https?://(www\.)?#', '', $url); // 移除协议和 www
|
||||
$url = rtrim($url, '/'); // 移除末尾的斜杠
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 生成哈希值
|
||||
public static function generateHash($url)
|
||||
{
|
||||
return md5($url); // 使用 MD5 生成哈希值
|
||||
}
|
||||
|
||||
|
||||
public static function getPlatformType($thirdType)
|
||||
{
|
||||
$platformMapping = [
|
||||
'facebook' => 1,
|
||||
'google' => 2,
|
||||
'tiktok' => 3,
|
||||
];
|
||||
|
||||
return $platformMapping[$thirdType] ?? null; // 如果不存在的第三方类型,返回 null
|
||||
}
|
||||
|
||||
}
|
@ -89,6 +89,14 @@ Route::group('/marketing', function () {
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthThirdCheck::class,
|
||||
]);
|
||||
Route::group('/landing', function () {
|
||||
Route::post('/list', [BpsAdController::class, 'listLandingUrls']);
|
||||
// Route::post('/export', [BpsAdController::class, 'exportLandingUrlsToExcel']);
|
||||
// Route::post('/chart', [BpsAdController::class, 'listCharts']);
|
||||
})->middleware([
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthThirdCheck::class,
|
||||
]);
|
||||
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user