webman_ad/app/service/AdsInsightService.php

1566 lines
80 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 app\model\ThirdUserAdvertiser;
use DateTime;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use think\db\exception\DbException;
use think\facade\Db as ThinkDb;
use support\Redis;
class AdsInsightService
{
// 状态映射数组
private static $statusMapping = [
2 => 'ENABLE', // 2 代表广告已启用
3 => 'PAUSED', // 3 代表广告待审核等状态
// 其他状态可以继续添加
];
// Redis 键名前缀
const REDIS_KEY_PREFIX = 'ad_data_count:';
/**
* 获取广告数据的各项总数(统一接口)
*
* @param array $customerIds 客户 ID 数组
* @return array 各项 count 统计数据
*/
public static function getAdCountData($customerIds, $startDate, $endDate)
{
if (empty($customerIds)) {
return [];
}
// 生成唯一的哈希键
$hashKey = self::generateHashKey($customerIds);
$redisKey = self::REDIS_KEY_PREFIX . $hashKey;
// 尝试从 Redis 中获取缓存值
$cachedData = Redis::get($redisKey);
if (!empty($cachedData)) {
return json_decode($cachedData, true);
}
// 没有缓存时重新计算
$countData = self::calculateCountData($customerIds, $startDate, $endDate);
// 缓存到 Redis有效期 10 分钟
Redis::setex($redisKey, 600, json_encode($countData));
return $countData;
}
/**
* 计算广告数据的 count
*/
protected static function calculateCountData(array $customerIds, $startDate, $endDate)
{
if (!$startDate || !$endDate) {
[$startDate, $endDate] = self::getLastWeekDateRange();
}
return [
'account_list_count' => self::getAccountListCount($customerIds, $startDate, $endDate),
'campaign_list_count' => self::getCampaignListCount($customerIds, $startDate, $endDate),
'adset_list_count' => self::getAdsetListCount($customerIds, $startDate, $endDate),
'ad_list_count' => self::getAdListCount($customerIds, $startDate, $endDate),
'creative_list_count' => self::getCreativeListCount($customerIds, $startDate, $endDate),
];
}
/**
* 生成 Redis 键的唯一哈希值
*/
protected static function generateHashKey(array $customerIds)
{
sort($customerIds);
return md5(json_encode($customerIds));
}
/**
* 获取广告列表的 count
*/
protected static function getAdListCount(array $customerIds, $startDate, $endDate)
{
return self::getAdList(0, $customerIds, 1, 1, '', $startDate, $endDate, 0, true);
}
/**
* 获取广告系列列表的 count
*/
protected static function getCampaignListCount(array $customerIds, $startDate, $endDate)
{
return self::getCampaignList(0, $customerIds, 1, 1, '', $startDate, $endDate, 0, true);
}
/**
* 获取广告组列表的 count
*/
protected static function getAdsetListCount(array $customerIds, $startDate, $endDate)
{
return self::getAdsetList(0, $customerIds, 1, 1, '', $startDate, $endDate, 0, true);
}
/**
* 获取账户列表的 count
*/
protected static function getAccountListCount(array $customerIds, $startDate, $endDate)
{
return self::getAccountList(0, $customerIds, 1, 1, '', $startDate, $endDate, true);
}
/**
* 获取账户列表的 count
*/
protected static function getCreativeListCount(array $customerIds, $startDate, $endDate)
{
return self::getCreativeInsightData(0, $customerIds, 1, 1, '', $startDate, $endDate, true);
}
/**
* 获取最近 7 天的起始和结束日期
*/
protected static function getLastWeekDateRange()
{
$endDate = (int)date('Ymd');
$startDate = (int)date('Ymd', strtotime('-6 days'));
return [$startDate, $endDate];
}
/**
* 获取广告系列列表
*/
public static function getCampaignList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0, $countOnly = false)
{
// 检查 customerIds 是否为空,直接返回空结构
if (empty($customerIds)) {
return $countOnly ? 0 : [
'pagination' => [
'startIndex' => 0,
'maxResults' => 0,
'count' => 0,
'pageNo' => $page,
'pageSize' => $pageSize,
'pages' => 0,
],
'statistics' => [],
'data' => [],
];
}
// 基础查询:广告活动和日数据表联接
$query = BpsAdCampaign::alias('c')
->cache(false) // 强制不使用缓存
->where('c.account_id', 'in', $customerIds);
// 如果只需要记录条数,执行计数查询
if ($countOnly) {
return $query->count();
}
// 动态构建日期条件
$dateCondition = '';
if ($startDate && $endDate) {
$query->leftJoin('bps.bps_ads_insights d', "c.campaign_id = d.ad_campaign_id AND c.platform_type = d.platform AND {$dateCondition}");
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
} else {
}
$query->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 / 1000000), 0) as purchases_value,
COALESCE(SUM(d.revenue / 1000000), 0) as revenue,
COALESCE(SUM(d.total_cost / 1000000), 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->whereRaw('LOWER(c.name) LIKE ?', ['%' . strtolower($keyword) . '%']);
}
if ($platformType) {
$platformType = (int)$platformType;
$query->where('c.platform_type', '=', $platformType);
}
});
// 获取所有符合条件的数据(不分页)
$allCampaigns = $query->select()->toArray();
$total_spend = array_sum(array_column($allCampaigns, 'spend'));
$total_cost = array_sum(array_column($allCampaigns, 'total_cost'));
$total_impressions = array_sum(array_column($allCampaigns, 'impressions'));
$total_clicks = array_sum(array_column($allCampaigns, 'clicks'));
$total_purchases_value = array_sum(array_column($allCampaigns, 'purchases_value'));
$total_revenue = array_sum(array_column($allCampaigns, 'revenue'));
$total_purchases = array_sum(array_column($allCampaigns, 'purchases'));
$cost_per_purchase = $total_purchases == 0 ? 0 : round($total_spend / $total_purchases, 2);
// 汇总统计数据
$statistics = [
'assisted_purchases' => array_sum(array_column($allCampaigns, 'assisted_purchases')),
'last_clicked_purchases' => array_sum(array_column($allCampaigns, 'last_clicked_purchases')),
'roas' => $total_spend == 0 ? '-' : round($total_revenue / $total_spend, 2) . 'X',
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
'clicks' => $total_clicks,
'impressions' => $total_impressions,
'adds_to_cart' => array_sum(array_column($allCampaigns, 'adds_to_cart')),
'cost_per_atc' => array_sum(array_column($allCampaigns, 'cost_per_atc')),
'purchases' => array_sum(array_column($allCampaigns, 'purchases')),
'purchases_value' => array_sum(array_column($allCampaigns, 'purchases_value')),
'cost_per_purchase' => '$' . number_format($cost_per_purchase, 2) ?: '$0.00',
'revenue' => '$' . number_format(array_sum(array_column($allCampaigns, 'revenue')), 2) ?: '$0.00', // 格式化收入
'total_cost' => '$' . number_format($total_cost, 2) ?: '$0.00', // 格式化总成本
'conversion_rate' => $total_clicks == 0 ? '-' : round(($total_purchases / $total_clicks) * 100, 2) . '%', // 转换率
'net_profit' => ($total_revenue - $total_cost) >= 0 ? '+$' . number_format($total_revenue - $total_cost, 2) : '-$' . number_format(abs($total_revenue - $total_cost), 2), // 净利润
'net_profit_margin' => $total_revenue == 0 ? '-' : round(($total_revenue - $total_cost) / $total_revenue, 2) * 100 . '%', // 净利润率
'net_profit_on_ad_spend' => $total_spend == 0 ? '-' : round(($total_revenue - $total_cost) / $total_spend, 2) * 100 . '%', // 广告支出净利润
// 计算总的 CTR
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
];
// 获取分页数据
// $campaigns = $query->paginate($pageSize, false, ['page' => $page]);
// 获取分页数据
$page = max(1, (int)$page); // 确保页码不小于 1
// $ads = $query->page($page, $pageSize)->select();
$campaigns = $query->limit(($page - 1) * $pageSize, $pageSize)->select();
// 确保数据格式统一
$result = array_map(function ($item) {
// CTR 的计算:点击率 = 点击数 / 展示数
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-';
// Conversion Rate 的计算:转换率 = 购买数 / 点击数
$conversion_rate = $item['clicks'] > 0 ? round(($item['purchases'] / $item['clicks']) * 100, 2) . '%' : '-';
// Net Profit 的计算:净利润 = 收入 - 总成本
$net_profit = ($item['revenue'] - $item['total_cost']) >= 0 ? '+$' . number_format($item['revenue'] - $item['total_cost'], 2) : '-$' . number_format(abs($item['revenue'] - $item['total_cost']), 2);
// Net Profit Margin 的计算:净利润率 = 净利润 / 收入
$net_profit_margin = $item['revenue'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['revenue'], 2) * 100 . '%' : '-';
// Net Profit on Ad Spend 的计算:广告支出净利润 = (收入 - 总成本) / 广告支出
$net_profit_on_ad_spend = $item['spend'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['spend'], 2) * 100 . '%' : '-';
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['revenue'] / $item['spend'], 2) . 'X',
'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' => $conversion_rate,
'net_profit' => $net_profit,
'net_profit_margin' => $net_profit_margin,
'net_profit_on_ad_spend' => $net_profit_on_ad_spend,
];
}, $campaigns->toArray());
// Pagination 数据
$pagination = [
'startIndex' => ($page - 1) * $pageSize + 1, // 确保索引从 1 开始
'maxResults' => count($campaigns), // 当前页实际返回数据数量
'count' => count($allCampaigns), // 符合条件的总记录数
'pageNo' => $page, // 当前页码
'pageSize' => $pageSize, // 每页条数
'pages' => (int)ceil(count($allCampaigns) / $pageSize), // 总页数
];
return [
'pagination' => $pagination,
'statistics' => $statistics,
'data' => $result,
];
}
/**
* 获取广告组列表
*/
public static function getAdsetList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0, $countOnly = false)
{
// 检查 customerIds 是否为空,直接返回空结构
if (empty($customerIds)) {
return $countOnly ? 0 : [
'pagination' => [
'startIndex' => 0,
'maxResults' => 0,
'count' => 0,
'pageNo' => $page,
'pageSize' => $pageSize,
'pages' => 0,
],
'statistics' => [],
'data' => [],
];
}
// 基础查询:广告活动和日数据表联接
$query = BpsAdSet::alias('s')
->cache(false) // 强制不使用缓存
->where('s.account_id', 'in', $customerIds);
// 如果只需要记录条数,执行计数查询
if ($countOnly) {
return $query->count();
}
// 动态构建日期条件
$dateCondition = '';
if ($startDate && $endDate) {
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
$query->leftJoin('bps.bps_ads_insights d', "s.ad_set_id = d.ad_set_id AND s.platform_type = d.platform AND {$dateCondition}");
}
// 基础查询:广告组和日数据表联接
$query->leftJoin('bps.bps_ads_campaign c', 's.campaign_id = c.campaign_id') // 联接广告系列表
->field('s.ad_set_id, s.status as status, s.name, s.account_id,s.platform_type,c.name as campaign_name,
COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
COALESCE(SUM(d.clicks), 0) as clicks,
COALESCE(SUM(d.spend) / 1000000, 0) as spend,
COALESCE(SUM(d.impressions), 0) as impressions,
COALESCE(SUM(d.adds_to_cart), 0) as adds_to_cart,
COALESCE(SUM(d.cost_per_atc), 0) as cost_per_atc,
COALESCE(SUM(d.purchases), 0) as purchases,
COALESCE(SUM(d.purchases_value / 1000000), 0) as purchases_value,
COALESCE(SUM(d.revenue / 1000000), 0) as revenue,
COALESCE(SUM(d.total_cost / 1000000), 0) as total_cost,
-1 as conversion_rate, -1 as roas, -1 as ctr,-1 as net_profit,-1 as net_profit_margin,-1 as net_profit_on_ad_spend')
->group('s.ad_set_id, s.status, s.account_id, s.name,s.platform_type,c.name')
->where('s.account_id', 'in', $customerIds); // 添加 customerIds 条件
if ($status !== 0) {
$query->where('s.status', '=', $status);
}
// 添加关键字过滤条件
$query->where(function ($query) use ($keyword, $platformType) {
if ($keyword) {
// $query->where('s.name', 'like', '%' . $keyword . '%');
$query->whereRaw('LOWER(s.name) LIKE ?', ['%' . strtolower($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_revenue = array_sum(array_column($allAdsets, 'revenue'));
$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_revenue / $total_spend, 2) . 'X',
'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' => $total_clicks == 0 ? '-' : round(($total_purchases / $total_clicks) * 100, 2) . '%', // 转换率
'net_profit' => ($total_revenue - $total_cost) >= 0 ? '+$' . number_format($total_revenue - $total_cost, 2) : '-$' . number_format(abs($total_revenue - $total_cost), 2), // 净利润
'net_profit_margin' => $total_revenue == 0 ? '-' : round(($total_revenue - $total_cost) / $total_revenue, 2) * 100 . '%', // 净利润率
'net_profit_on_ad_spend' => $total_spend == 0 ? '-' : round(($total_revenue - $total_cost) / $total_spend, 2) * 100 . '%', // 广告支出净利润
// 计算总的 CTR
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
];
// 获取分页数据
// $adsets = $query->paginate($pageSize, false, ['page' => $page]);
$page = max(1, (int)$page); // 确保页码不小于 1
$adsets = $query->limit(($page - 1) * $pageSize, $pageSize)->select();
// 确保数据格式统一
$result = array_map(function ($item) {
// CTR 的计算:点击率 = 点击数 / 展示数
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-';
// Conversion Rate 的计算:转换率 = 购买数 / 点击数
$conversion_rate = $item['clicks'] > 0 ? round(($item['purchases'] / $item['clicks']) * 100, 2) . '%' : '-';
// Net Profit 的计算:净利润 = 收入 - 总成本
$net_profit = ($item['revenue'] - $item['total_cost']) >= 0 ? '+$' . number_format($item['revenue'] - $item['total_cost'], 2) : '-$' . number_format(abs($item['revenue'] - $item['total_cost']), 2);
// Net Profit Margin 的计算:净利润率 = 净利润 / 收入
$net_profit_margin = $item['revenue'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['revenue'], 2) * 100 . '%' : '-';
// Net Profit on Ad Spend 的计算:广告支出净利润 = (收入 - 总成本) / 广告支出
$net_profit_on_ad_spend = $item['spend'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['spend'], 2) * 100 . '%' : '-';
return [
'id' => $item['ad_set_id'],
'platform_type' => $item['platform_type'],
'account_id' => $item['account_id'], // 映射为 customer_id
'name' => $item['name'] ?: '-', // 若 name 为空则显示 '-'
'campaign_name' => $item['campaign_name'] ?: '-', // 若 name 为空则显示 '-'
'status' => $item['status'],
'assisted_purchases' => $item['assisted_purchases'],
'last_clicked_purchases' => $item['last_clicked_purchases'],
'roas' => $item['spend'] == 0 ? '-' : round($item['purchases_value'] / $item['spend'], 2) . 'X',
'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' => $conversion_rate, // 没有提供有效的计算,保持为 '-'
'net_profit' => $net_profit, // 没有提供 net_profit 计算,保持为 '-'
'net_profit_margin' => $net_profit_margin, // 没有提供 net_profit_margin 计算,保持为 '-'
'net_profit_on_ad_spend' => $net_profit_on_ad_spend, // 没有提供 net_profit_on_ad_spend 计算,保持为 '-'
];
}, $adsets->toArray());
// Pagination 数据
$pagination = [
'startIndex' => ($page - 1) * $pageSize + 1, // 确保索引从 1 开始
'maxResults' => count($adsets), // 当前页实际返回数据数量
'count' => count($allAdsets), // 符合条件的总记录数
'pageNo' => $page, // 当前页码
'pageSize' => $pageSize, // 每页条数
'pages' => (int)ceil(count($allAdsets) / $pageSize), // 总页数
];
return [
'pagination' => $pagination,
'statistics' => $statistics,
'data' => $result,
];
}
public static function getAccountList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $countOnly = false)
{
// 检查 customerIds 是否为空,直接返回计数为 0
if (empty($customerIds)) {
return $countOnly ? 0 : [
'pagination' => [
'startIndex' => 0,
'maxResults' => $pageSize,
'count' => 0,
'pageNo' => $page,
'pageSize' => $pageSize,
'pages' => 0,
],
'statistics' => [],
'data' => [],
];
}
// 构建查询条件
$query = ThirdUserAdvertiser::alias('a')
->cache(false)
->where('a.advertiser_id', 'in', $customerIds);
// 仅计数时优化查询
if ($countOnly) {
return $query->count(); // 只查询总记录数
}
// 动态构建日期条件
$dateCondition = '';
if ($startDate && $endDate) {
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
$query->leftJoin('bps.bps_ads_insights d', "a.advertiser_id = d.account_id AND {$dateCondition}");
}
// 基础查询:广告和日数据表联接
// 其他联表及字段计算
$query->leftJoin('bps.bps_third_user u', 'a.doc_ = u.id')
->field('a.advertiser_id, a.advertiser_name,u.third_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) / 1000000, 0) as purchases_value,
COALESCE(SUM(d.revenue) / 1000000, 0) as revenue,
COALESCE(SUM(d.total_cost) / 1000000, 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.advertiser_id, a.advertiser_name,u.third_type')
->where('a.advertiser_id', 'in', $customerIds); // 添加 customerIds 条件
// 如果传入了 status 参数,按状态筛选
// if ($status !== 0) {
// $query->where('a.status', '=', $status);
// }
// 添加关键字过滤条件
$query->where(function ($query) use ($keyword, $platformType) {
if ($keyword) {
$query->whereRaw('LOWER(a.advertiser_name) LIKE ?', ['%' . strtolower($keyword) . '%']);
}
if ($platformType) {
$a = (int)$platformType;
$platformTypeNames = [1 => 'facebook', 2 => 'google', 3 => 'tiktok'];
$query->where('u.third_type', '=', $platformTypeNames[$a]);
}
});
$query->order('u.third_type', 'asc');
// 获取所有符合条件的数据(不分页)
$allAccounts = $query->select()->toArray();
// 汇总统计数据
$total_spend = array_sum(array_column($allAccounts, 'spend'));
$total_cost = array_sum(array_column($allAccounts, 'total_cost'));
$total_impressions = array_sum(array_column($allAccounts, 'impressions'));
$total_clicks = array_sum(array_column($allAccounts, 'clicks'));
$total_purchases_value = array_sum(array_column($allAccounts, 'purchases_value'));
$total_revenue = array_sum(array_column($allAccounts, 'revenue'));
$total_purchases = array_sum(array_column($allAccounts, 'purchases'));
$cost_per_purchase = $total_purchases == 0 ? 0 : round($total_spend / $total_purchases, 2);
$statistics = [
'assisted_purchases' => array_sum(array_column($allAccounts, 'assisted_purchases')),
'last_clicked_purchases' => array_sum(array_column($allAccounts, '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($allAccounts, 'impressions')),
'adds_to_cart' => array_sum(array_column($allAccounts, 'adds_to_cart')),
'cost_per_atc' => array_sum(array_column($allAccounts, 'cost_per_atc')),
'purchases' => array_sum(array_column($allAccounts, 'purchases')),
'purchases_value' => array_sum(array_column($allAccounts, 'purchases_value')),
'cost_per_purchase' => '$' . number_format($cost_per_purchase, 2) ?: '$0.00',
'revenue' => '$' . number_format(array_sum(array_column($allAccounts, 'revenue')), 2) ?: '$0.00', // 格式化收入
'total_cost' => '$' . number_format($total_cost, 2) ?: '$0.00', // 格式化总成本
'conversion_rate' => $total_clicks == 0 ? '-' : round(($total_purchases / $total_clicks) * 100, 2) . '%', // 转换率
'net_profit' => ($total_revenue - $total_cost) >= 0 ? '+$' . number_format($total_revenue - $total_cost, 2) : '-$' . number_format(abs($total_revenue - $total_cost), 2), // 净利润
'net_profit_margin' => $total_revenue == 0 ? '-' : round(($total_revenue - $total_cost) / $total_revenue, 2) * 100 . '%', // 净利润率
'net_profit_on_ad_spend' => $total_spend == 0 ? '-' : round(($total_revenue - $total_cost) / $total_spend, 2) * 100 . '%', // 平均广告花费的净利润
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
];
// 获取分页数据
$page = max(1, (int)$page); // 确保页码不小于 1
// $ads = $query->page($page, $pageSize)->select();
$accounts = $query->limit(($page - 1) * $pageSize, $pageSize)->select();
// dump($ads);
// 确保数据格式统一
$result = array_map(function ($item) {
// CTR 的计算:点击率 = 点击数 / 展示数
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-';
// Conversion Rate 的计算:转换率 = 购买数 / 点击数
$conversion_rate = $item['clicks'] > 0 ? round(($item['purchases'] / $item['clicks']) * 100, 2) . '%' : '-';
// Net Profit 的计算:净利润 = 收入 - 总成本
$net_profit = ($item['revenue'] - $item['total_cost']) >= 0 ? '+$' . number_format($item['revenue'] - $item['total_cost'], 2) : '-$' . number_format(abs($item['revenue'] - $item['total_cost']), 2);
// Net Profit Margin 的计算:净利润率 = 净利润 / 收入
$net_profit_margin = $item['revenue'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['revenue'], 2) * 100 . '%' : '-';
// Net Profit on Ad Spend 的计算:广告支出净利润 = (收入 - 总成本) / 广告支出
$net_profit_on_ad_spend = $item['spend'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['spend'], 2) * 100 . '%' : '-';
$platformTypeIds = ['facebook' => 1, 'google' => 2, 'tiktok' => 3];
return [
'user_id' => $item['advertiser_id'],
'platform_type' => $platformTypeIds[$item['third_type']],
'advertiser_name' => $item['advertiser_name'] ?: '-', // 若 name 为空则显示 '-'
// 'ad_set_name' => $item['ad_set_name'] ?: '-', // 若 name 为空则显示 '-'
// 'status' => $item['status'],
'assisted_purchases' => $item['assisted_purchases'],
'last_clicked_purchases' => $item['last_clicked_purchases'],
'roas' => $item['spend'] == 0 ? '-' : round($item['purchases_value'] / $item['spend'], 2),
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
'amount_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' => $conversion_rate,
'net_profit' => $net_profit,
'net_profit_margin' => $net_profit_margin,
'net_profit_on_ad_spend' => $net_profit_on_ad_spend,
];
}, $accounts->toArray());
// Pagination 数据
$pagination = [
'startIndex' => ($page - 1) * $pageSize + 1, // 确保索引从 1 开始
'maxResults' => count($accounts), // 当前页实际返回数据数量
'count' => count($allAccounts), // 符合条件的总记录数
'pageNo' => $page, // 当前页码
'pageSize' => $pageSize, // 每页条数
'pages' => (int)ceil(count($allAccounts) / $pageSize), // 总页数
];
return [
'pagination' => $pagination,
'statistics' => $statistics,
'data' => $result,
];
}
public static function getAdList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $status = 0, $countOnly = false)
{
// 检查 customerIds 是否为空,直接返回空结构
if (empty($customerIds)) {
return $countOnly ? 0 : [
'pagination' => [
'startIndex' => 0,
'maxResults' => 0,
'count' => 0,
'pageNo' => $page,
'pageSize' => $pageSize,
'pages' => 0,
],
'statistics' => [],
'data' => [],
];
}
// 基础查询:广告活动和日数据表联接
$query = BpsAdAd::alias('a')
->cache(false) // 强制不使用缓存
->where('a.account_id', 'in', $customerIds);
// 如果只需要记录条数,执行计数查询
if ($countOnly) {
return $query->count();
}
// 动态构建日期条件
$dateCondition = '';
if ($startDate && $endDate) {
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
$query->leftJoin('bps.bps_ads_insights d', "a.ad_id = d.ad_id AND a.platform_type = d.platform AND {$dateCondition}");
}
// 基础查询:广告和日数据表联接
$query->leftJoin('bps.bps_ads_set s', 'a.ad_set_id = s.ad_set_id') // 联接广告组表,获取 ad_set_name
->field('a.ad_id, a.status as status, a.name, a.account_id,a.platform_type,s.name as ad_set_name,
COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
COALESCE(SUM(d.clicks), 0) as clicks,
COALESCE(SUM(d.spend) / 1000000, 0) as spend,
COALESCE(SUM(d.impressions), 0) as impressions,
COALESCE(SUM(d.adds_to_cart), 0) as adds_to_cart,
COALESCE(SUM(d.cost_per_atc), 0) as cost_per_atc,
COALESCE(SUM(d.purchases), 0) as purchases,
COALESCE(SUM(d.purchases_value) / 1000000, 0) as purchases_value,
COALESCE(SUM(d.revenue) / 1000000, 0) as revenue,
COALESCE(SUM(d.total_cost) / 1000000, 0) as total_cost,
-1 as conversion_rate, -1 as roas, -1 as ctr, -1 as net_profit, -1 as net_profit_margin, -1 as net_profit_on_ad_spend')
->group('a.ad_id, a.status, a.account_id, a.name,a.platform_type, s.name')
->where('a.account_id', 'in', $customerIds); // 添加 customerIds 条件
// 如果传入了 status 参数,按状态筛选
if ($status !== 0) {
$query->where('a.status', '=', $status);
}
// 添加关键字过滤条件
$query->where(function ($query) use ($keyword, $platformType) {
if ($keyword) {
// $query->where('a.name', 'like', '%' . $keyword . '%');
$query->whereRaw('LOWER(a.name) LIKE ?', ['%' . strtolower($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_revenue = array_sum(array_column($allAds, 'revenue'));
$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' => $total_clicks == 0 ? '-' : round(($total_purchases / $total_clicks) * 100, 2) . '%', // 转换率
'net_profit' => ($total_revenue - $total_cost) >= 0 ? '+$' . number_format($total_revenue - $total_cost, 2) : '-$' . number_format(abs($total_revenue - $total_cost), 2), // 净利润
'net_profit_margin' => $total_revenue == 0 ? '-' : round(($total_revenue - $total_cost) / $total_revenue, 2) * 100 . '%', // 净利润率
'net_profit_on_ad_spend' => $total_spend == 0 ? '-' : round(($total_revenue - $total_cost) / $total_spend, 2) * 100 . '%', // 平均广告花费的净利润
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
];
// 获取分页数据
$page = max(1, (int)$page); // 确保页码不小于 1
// $ads = $query->page($page, $pageSize)->select();
$ads = $query->limit(($page - 1) * $pageSize, $pageSize)->select();
// dump($ads);
// 确保数据格式统一
$result = array_map(function ($item) {
// CTR 的计算:点击率 = 点击数 / 展示数
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-';
// Conversion Rate 的计算:转换率 = 购买数 / 点击数
$conversion_rate = $item['clicks'] > 0 ? round(($item['purchases'] / $item['clicks']) * 100, 2) . '%' : '-';
// Net Profit 的计算:净利润 = 收入 - 总成本
$net_profit = ($item['revenue'] - $item['total_cost']) >= 0 ? '+$' . number_format($item['revenue'] - $item['total_cost'], 2) : '-$' . number_format(abs($item['revenue'] - $item['total_cost']), 2);
// Net Profit Margin 的计算:净利润率 = 净利润 / 收入
$net_profit_margin = $item['revenue'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['revenue'], 2) * 100 . '%' : '-';
// Net Profit on Ad Spend 的计算:广告支出净利润 = (收入 - 总成本) / 广告支出
$net_profit_on_ad_spend = $item['spend'] > 0 ? round(($item['revenue'] - $item['total_cost']) / $item['spend'], 2) * 100 . '%' : '-';
return [
'id' => $item['ad_id'],
'platform_type' => $item['platform_type'],
'account_id' => $item['account_id'], // 映射为 customer_id
'name' => $item['name'] ?: '-', // 若 name 为空则显示 '-'
'ad_set_name' => $item['ad_set_name'] ?: '-', // 若 name 为空则显示 '-'
'status' => $item['status'],
'assisted_purchases' => $item['assisted_purchases'],
'last_clicked_purchases' => $item['last_clicked_purchases'],
'roas' => $item['spend'] == 0 ? '-' : round($item['purchases_value'] / $item['spend'], 2),
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
'impressions' => $item['impressions'],
'clicks' => $item['clicks'],
'ctr' => $ctr, // CTR 字段
'adds_to_cart' => $item['adds_to_cart'],
'cost_per_atc' => $item['cost_per_atc'],
'purchases' => $item['purchases'],
'purchases_value' => '$' . number_format($item['purchases_value'], 2), // 格式化购买金额
'cost_per_purchase' => $item['purchases'] > 0 ? '$' . number_format(($item['spend'] / $item['purchases']), 2) : '$0.00',
'revenue' => '$' . number_format($item['revenue'], 2), // 格式化收入
'total_cost' => '$' . number_format($item['total_cost'], 2), // 格式化总成本
'conversion_rate' => $conversion_rate,
'net_profit' => $net_profit,
'net_profit_margin' => $net_profit_margin,
'net_profit_on_ad_spend' => $net_profit_on_ad_spend,
];
}, $ads->toArray());
// Pagination 数据
$pagination = [
'startIndex' => ($page - 1) * $pageSize + 1, // 确保索引从 1 开始
'maxResults' => count($ads), // 当前页实际返回数据数量
'count' => count($allAds), // 符合条件的总记录数
'pageNo' => $page, // 当前页码
'pageSize' => $pageSize, // 每页条数
'pages' => (int)ceil(count($allAds) / $pageSize), // 总页数
];
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' => [],
];
}
$userCount = count($userIds);
// 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/1000000), 0) as purchases_value,
COALESCE(SUM(d.revenue/1000000), 0) as revenue,
COALESCE(SUM(d.total_cost)/1000000, 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' => $total_clicks == 0 ? '-' : round(($total_purchases / $total_clicks) * 100, 2) . '%', // 转换率
'net_profit' => ($total_revenue - $total_cost) >= 0 ? '+$' . number_format($total_revenue - $total_cost, 2) : '-$' . number_format(abs($total_revenue - $total_cost), 2), // 净利润
'net_profit_margin' => $total_revenue == 0 ? '-' : round(($total_revenue - $total_cost) / $total_revenue, 2) * 100 . '%', // 净利润率
'net_profit_on_ad_spend' => $total_spend == 0 ? '-' : round(($total_revenue - $total_cost) / $total_spend, 2) * 100 . '%', // 广告支出净利润
'ctr' => $ctr, // CTR 字段
];
// 获取分页数据
$users = $query->paginate($pageSize, false, ['page' => $page]);
// 按照 third_user_id 聚合统计字段
$aggregatedUsers = [];
// 遍历每个用户的统计数据
foreach ($users->items() as $item) {
$thirdUserId = $item['user_id'];
if (!isset($aggregatedUsers[$thirdUserId])) {
$aggregatedUsers[$thirdUserId] = [
'user_id' => $item['user_id'],
'platform_type' => self::getPlatformType($item['third_type']),
'advertiser_name' => $item['advertiser_name'],
'assisted_purchases' => 0,
'last_clicked_purchases' => 0,
'roas' => 0,
'spend' => 0,
'impressions' => 0,
'clicks' => 0,
'adds_to_cart' => 0,
'cost_per_atc' => 0,
'purchases' => 0,
'purchases_value' => 0,
'cost_per_purchase' => 0,
'revenue' => 0,
'total_cost' => 0,
'net_profit' => '-', // 没有提供 net_profit保持为 '-'
'net_profit_margin' => '-', // 没有提供 net_profit_margin保持为 '-'
'net_profit_on_ad_spend' => '-', // 没有提供 net_profit_on_ad_spend保持为 '-'
];
}
// 汇总统计
$aggregatedUsers[$thirdUserId]['spend'] += $item['spend'];
$aggregatedUsers[$thirdUserId]['impressions'] += $item['impressions'];
$aggregatedUsers[$thirdUserId]['clicks'] += $item['clicks'];
$aggregatedUsers[$thirdUserId]['adds_to_cart'] += $item['adds_to_cart'];
$aggregatedUsers[$thirdUserId]['cost_per_atc'] += $item['cost_per_atc'];
$aggregatedUsers[$thirdUserId]['purchases'] += $item['purchases'];
$aggregatedUsers[$thirdUserId]['purchases_value'] += $item['purchases_value'];
$aggregatedUsers[$thirdUserId]['revenue'] += $item['revenue'];
$aggregatedUsers[$thirdUserId]['total_cost'] += $item['total_cost'];
$aggregatedUsers[$thirdUserId]['assisted_purchases'] += $item['assisted_purchases'];
$aggregatedUsers[$thirdUserId]['last_clicked_purchases'] += $item['last_clicked_purchases'];
}
// 计算统计口径字段ROAS, CTR等
foreach ($aggregatedUsers as $userId => $data) {
$total_spend = $data['spend'];
$total_purchases_value = $data['purchases_value'];
$total_revenue = $data['revenue'];
$total_cost = $data['total_cost'];
$total_clicks = $data['clicks'];
$total_impressions = $data['impressions'];
// CTR 的计算:点击率 = 点击数 / 展示数
$ctr = $data['impressions'] > 0 ? number_format(($data['clicks'] / $data['impressions']) * 100, 2) . '%' : '-';
// Conversion Rate 的计算:转换率 = 购买数 / 点击数
$conversion_rate = $data['clicks'] > 0 ? round(($data['purchases'] / $data['clicks']) * 100, 2) . '%' : '-';
// Net Profit 的计算:净利润 = 收入 - 总成本
$net_profit = ($data['revenue'] - $data['total_cost']) >= 0 ? '+$' . number_format($data['revenue'] - $data['total_cost'], 2) : '-$' . number_format(abs($data['revenue'] - $data['total_cost']), 2);
// Net Profit Margin 的计算:净利润率 = 净利润 / 收入
$net_profit_margin = $data['revenue'] > 0 ? round(($data['revenue'] - $data['total_cost']) / $data['revenue'], 2) * 100 . '%' : '-';
// Net Profit on Ad Spend 的计算:广告支出净利润 = (收入 - 总成本) / 广告支出
$net_profit_on_ad_spend = $data['spend'] > 0 ? round(($data['revenue'] - $data['total_cost']) / $data['spend'], 2) * 100 . '%' : '-';
// 计算 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'] = $conversion_rate;
$aggregatedUsers[$userId]['ctr'] = $ctr;
$aggregatedUsers[$userId]['net_profit'] = $net_profit;
$aggregatedUsers[$userId]['net_profit_margin'] = $net_profit_margin;
$aggregatedUsers[$userId]['net_profit_on_ad_spend'] = $net_profit_on_ad_spend;
}
// 如果 aggregatedUsers 为空,返回默认的数据结构
if (empty($aggregatedUsers) && !$keyword) {
$emptyUser = [];
$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');
// 添加关键字过滤条件
$query->where(function ($query) use ($platformType) {
// if ($keyword) {
// $query->where('a.advertiser_name', 'like', '%' . $keyword . '%');
// }
if ($platformType) {
$a = (int)$platformType;
$platformTypeNames = [1 => 'facebook', 2 => 'google', 3 => 'tiktok'];
$query->where('u.third_type', '=', $platformTypeNames[$a]);
}
});
// 获取所有符合条件的数据(不分页)
$allUsers = $query->select()->toArray();
foreach ($allUsers as $user) {
if (in_array($user['user_id'], $userIds)) {
$emptyUser[] = [
'user_id' => $user['user_id'],
'platform_type' => (int)$platformType,
'advertiser_name' => $user['advertiser_name'],
'assisted_purchases' => 0,
'last_clicked_purchases' => 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,
'conversion_rate' => '-',
'net_profit' => '-',
'net_profit_margin' => '-',
'net_profit_on_ad_spend' => '-',
'ctr' => '-',
];
}
}
// dump($allUsers,'222');
return [
'pagination' => [
'startIndex' => ($page - 1) * $pageSize,
'maxResults' => $pageSize,
'count' => count($emptyUser),
'pageNo' => $page,
'pageSize' => $pageSize,
'pages' => 0,
],
'statistics' => $statistics,
'data' => $emptyUser
];
}
// 使用 array_values 移除键名
$dataWithoutKeys = array_values($aggregatedUsers);
// 最终分页信息
$pagination = [
// 'startIndex' => ($page - 1) * $pageSize,
// 'maxResults' => $pageSize,
//// 'count' => $users->total(),
// 'count' => $userCount,
// 'pageNo' => $page,
// 'pageSize' => $pageSize,
// 'pages' => ceil($users->total() / $pageSize),
'startIndex' => ($page - 1) * $pageSize + 1, // 确保索引从 1 开始
'maxResults' => count($dataWithoutKeys), // 当前页实际返回数据数量
'count' => count($dataWithoutKeys), // 符合条件的总记录数
'pageNo' => $page, // 当前页码
'pageSize' => $pageSize, // 每页条数
'pages' => (int)ceil(count($dataWithoutKeys) / $pageSize), // 总页数
];
return [
'pagination' => $pagination,
'statistics' => $statistics,
'data' => $dataWithoutKeys,
];
}
public static function getCreativeInsightData($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null, $countOnly = false)
{
// 1. 创建查询对象,初始化 BpsAdCreativeInsight 查询
$creativeDataQuery = BpsAdCreativeInsight::alias('i')
->join('bps.bps_ads_creative c', 'i.creative_id = c.creative_id', 'LEFT'); // 联接 bps_ads_creative 表
if ($platformType != 0) {
$creativeDataQuery->where('i.platform', $platformType); // 只有 platformType 不为 0 时才添加筛选
}
// 2. 日期范围筛选
if ($startDate && $endDate) {
$creativeDataQuery->whereBetween('i.date', [$startDate, $endDate]);
}
// 3. 客户 ID 过滤(如果提供了)
if (!empty($customerIds)) {
$creativeDataQuery->whereIn('i.account_id', $customerIds);
} else {
return $countOnly ? 0 :[
'data' => [],
'total' => 0,
'statistics' => [],
'pagination' => [
'startIndex' => 0,
'maxResults' => $pageSize,
'count' => 0,
'pageNo' => 1,
'pageSize' => $pageSize
]
];
}
// 仅返回记录数时的逻辑
if ($countOnly) {
return $creativeDataQuery->count() ?: 0;
}
// 4. 关键词过滤
if ($keyword) {
// $creativeDataQuery->where('c.name', 'like', '%' . $keyword . '%'); // 在 bps_ads_creative 表中查找关键词
$creativeDataQuery->whereRaw('LOWER(c.name) LIKE ?', ['%' . strtolower($keyword) . '%']); // 在 bps_ads_creative 表中查找关键词
}
// 5. 数据聚合(按 creative_id 和其他字段)
$creativeDataQuery->group('i.creative_id, i.platform, i.account_id, c.name, c.type,c.url, c.thumbnail_url') // 按需要的字段分组
->field([
'i.creative_id',
'i.platform',
'c.name AS creative_name', // 从 bps_ads_creative 表中选择 name
'c.url AS creative_url', // 从 bps_ads_creative 表中选择 url
'c.type AS creative_type', // 从 bps_ads_creative 表中选择 url
'c.thumbnail_url', // 从 bps_ads_creative 表中选择 thumbnail_url
ThinkDb::raw('COALESCE(SUM(i.spend) / 1000000, 0) AS total_spend'),
ThinkDb::raw('COALESCE(SUM(i.purchases_value) / 1000000, 0) AS total_purchases_value'),
ThinkDb::raw('COALESCE(SUM(i.purchases), 0) AS total_purchases'),
ThinkDb::raw('COALESCE(SUM(i.revenue) / 1000000, 0) AS total_revenue'),
ThinkDb::raw('COALESCE(SUM(i.impressions), 0) AS total_impressions'),
ThinkDb::raw('COALESCE(SUM(i.clicks), 0) AS total_clicks'),
ThinkDb::raw('COALESCE(SUM(i.video_25),0) AS video_plays_25_rate'),
ThinkDb::raw('COALESCE(SUM(i.video_50),0) AS video_plays_50_rate'),
ThinkDb::raw('COALESCE(SUM(i.video_75),0) AS video_plays_75_rate'),
ThinkDb::raw('COALESCE(SUM(i.video_100),0) AS video_plays_100_rate'),
ThinkDb::raw('-1 AS hold_rate')
]);
// 6. 执行查询并获取聚合结果
// $aggregatedData = $creativeDataQuery->select();
// 6. 执行查询并获取聚合结果
$aggregatedData = $creativeDataQuery->select()->toArray(); // 转换为数组处理
$totalRecords = count($aggregatedData); // 总记录数
// 计算分页起始索引
$startIndex = ($page - 1) * $pageSize;
// 打印调试 SQL 查询
//$sql = $creativeDataQuery->getLastSql();
// dump($sql);
// 7. 初始化广告创意的汇总数据和统计数据
$creativeSummaryData = [];
// $statisticsData = $this->initializeStatistics();
$statisticsData = [];
// 8. 遍历查询结果并计算每个 creative 的相关统计数据
foreach ($aggregatedData as $creativeData) {
// 初始化该 creative_id 的数据(如果不存在)
$creativeId = $creativeData['creative_id'];
if (!isset($creativeSummaryData[$creativeId])) {
$creativeSummaryData[$creativeId] = [
'creative_id' => $creativeData['creative_id'],
'platform' => $creativeData['platform'],
'creative' => $creativeData['creative_name'],
'creative_type' => $creativeData['creative_type'],
'creative_url' => $creativeData['creative_url'] ?? '',
'thumbnail_url' => $creativeData['thumbnail_url'] ?? '',
'title' => $creativeData['title'] ?? '',
'spend' => 0,
'purchases_value' => 0,
'roas' => 0,
'cpa' => '-',
'cpc_link_click' => '-',
'cpm' => '-',
'cpc_all' => '-',
'aov' => '-',
'click_to_atc_ratio' => '-',
'atc_to_purchase_ratio' => '-',
'purchases' => 0,
'first_frame_retention' => '-',
'thumbstop' => '-',
'ctr_outbound' => '-',
'click_to_purchase' => '-',
'ctr_all' => '-',
'video_plays_25_rate' => 0,
'video_plays_50_rate' => 0,
'video_plays_75_rate' => 0,
'video_plays_100_rate' => 0,
'hold_rate' => '-',
'total_conversions_value' => 0,
'total_conversions' => 0,
'impressions' => 0,
'clicks' => 0,
'revenue' => 0,
'ad_count' => 0
];
}
// 更新该 creative_id 的统计数据
$creativeSummaryData[$creativeId]['spend'] += $creativeData['total_spend'];
$creativeSummaryData[$creativeId]['purchases_value'] += $creativeData['total_purchases_value'];
$creativeSummaryData[$creativeId]['purchases'] += $creativeData['total_purchases'];
$creativeSummaryData[$creativeId]['impressions'] += $creativeData['total_impressions'];
$creativeSummaryData[$creativeId]['clicks'] += $creativeData['total_clicks'];
$creativeSummaryData[$creativeId]['revenue'] += $creativeData['total_revenue'];
$creativeSummaryData[$creativeId]['video_plays_25_rate'] += $creativeData['video_plays_25_rate'];
$creativeSummaryData[$creativeId]['video_plays_50_rate'] += $creativeData['video_plays_50_rate'];
$creativeSummaryData[$creativeId]['video_plays_75_rate'] += $creativeData['video_plays_75_rate'];
$creativeSummaryData[$creativeId]['video_plays_100_rate'] += $creativeData['video_plays_100_rate'];
// 计算 ROAS
$roas = $creativeSummaryData[$creativeId]['spend'] > 0
? $creativeSummaryData[$creativeId]['revenue'] / $creativeSummaryData[$creativeId]['spend']
: 0;
$creativeSummaryData[$creativeId]['roas'] = $roas > 0 ? number_format($roas, 2) . 'X' : '-';
// 填充广告计数
// $creativeSummaryData[$creativeData->creative_id]['ad_count'] = rand(10, 200); // 每个 creative_id 对应一个广告
}
// dump($creativeSummaryData);
// 汇总总体统计数据
$statisticsData['spend'] = array_sum(array_column($creativeSummaryData, 'spend'));
$statisticsData['purchases_value'] = array_sum(array_column($creativeSummaryData, 'purchases_value'));
$statisticsData['purchases'] = array_sum(array_column($creativeSummaryData, 'purchases'));
$statisticsData['impressions'] = array_sum(array_column($creativeSummaryData, 'impressions'));
$statisticsData['clicks'] = array_sum(array_column($creativeSummaryData, 'clicks'));
$statisticsData['revenue'] = array_sum(array_column($creativeSummaryData, 'revenue'));
// 汇总统计数据
$statistics = [
'spend' => '$' . number_format($statisticsData['spend'], 2), // 格式化金额
'purchases_value' => '$' . number_format($statisticsData['purchases_value'], 2), // 格式化金额
'roas' => $statisticsData['spend'] == 0 ? '-' : round($statisticsData['revenue'] / $statisticsData['spend'], 2) . 'X',
'cpa' => '-', // 格式化金额
'cpc_link_click' => '-', // 格式化金额
'cpm' => '-', // 格式化金额
'cpc_all' => '-', //格式化金额
'aov' => '-', // 格式化金额
'click_to_atc_ratio' => '-', // 格式化百分比
'atc_to_purchase_ratio' => '-',// 格式化百分比
'purchases' => $statisticsData['purchases'],
'first_frame_retention' => '-',
'thumbstop' => '-',
'ctr_outbound' => '-',
'click_to_purchase' => '-',
'ctr_all' => ($statisticsData['impressions'] > 0) ? number_format(($statisticsData['clicks'] / $statisticsData['impressions']) * 100, 2) . '%' : '-', // 格式化为百分比
'video_plays_25_rate' => array_sum(array_column($creativeSummaryData, 'video_plays_25_rate')),
'video_plays_50_rate' => array_sum(array_column($creativeSummaryData, 'video_plays_50_rate')),
'video_plays_75_rate' => array_sum(array_column($creativeSummaryData, 'video_plays_75_rate')),
'video_plays_100_rate' => array_sum(array_column($creativeSummaryData, 'video_plays_100_rate')),
'hold_rate' => '-',// 格式化百分比
];
// 获取每个 creative_id 对应的广告数量
$creativeIds_from_google = array_keys(array_filter($creativeSummaryData, function ($item) {
return $item['platform'] == 2;
}));
if (!empty($creativeIds_from_google)) {
// 将整数日期转为 Y-m-d 格式
$formattedStartDate = DateTime::createFromFormat('Ymd', $startDate)->format('Y-m-d');
$formattedEndDate = DateTime::createFromFormat('Ymd', $endDate)->format('Y-m-d');
// 查询满足条件的 asset_id 和唯一 ad_id 计数
$ad_count_google = AssetRelation::whereIn('asset_id', $creativeIds_from_google)
->whereBetween('date', [$formattedStartDate, $formattedEndDate])
->group('asset_id')
->column('COUNT(DISTINCT ad_id) AS ad_count', 'asset_id');
}
$creativeIds_from_fb_tk = array_keys(array_filter($creativeSummaryData, function ($item) {
return ($item['platform'] == 1 || $item['platform'] == 3);
}));
if (!empty($creativeIds_from_fb_tk)) {
$ad_count_fb_tk = BpsAdAd::whereIn('creative_id', $creativeIds_from_fb_tk)
->group('creative_id')
->column('COUNT(DISTINCT ad_id) as ad_count', 'creative_id');
}
// 确保变量存在,避免未定义错误
$ad_count_fb_tk = $ad_count_fb_tk ?? [];
$ad_count_google = $ad_count_google ?? [];
// 合并数组
$ad_count_combined = $ad_count_fb_tk + $ad_count_google;
//dump($ad_count_combined);
// dump($creativeIds_from_fb_tk);
// 将广告数量填充到对应的 creativeSummaryData 中
foreach ($creativeSummaryData as $creativeId => &$creativeData) {
$creativeData['ad_count'] = $ad_count_combined[$creativeId] ?? 0; // 如果没有匹配记录,默认填充 0
// dump($creativeData);
}
unset($creativeData); // 解除引用
// dump($creativeSummaryData);
// 格式化返回的创意数据
$formattedData = array_map(function ($item) {
return [
'creative_id' => $item['creative_id'],
'creative' => $item['creative'] ?: '-',
'creative_type' => $item['creative_type'],
'creative_url' => $item['creative_url'],
'thumbnail_url' => $item['thumbnail_url'],
'spend' => '$' . number_format($item['spend'], 2),
'purchases_value' => '$' . number_format($item['purchases_value'], 2),
'roas' => $item['roas'],
'cpa' => '-',
'cpc_link_click' => '-',
'cpm' => '-',
'cpc_all' => '-',
'aov' => '-',
'click_to_atc_ratio' => '-',
'atc_to_purchase_ratio' => '-',
'purchases' => $item['purchases'],
'first_frame_retention' => '-',
'thumbstop' => '-',
'ctr_outbound' => '-',
'click_to_purchase' => '-',
'ctr_all' => ($item['impressions'] > 0) ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-',
'total_conversions_value' => '$' . number_format($item['purchases_value'], 2), //准备删除
'impressions' => $item['impressions'],
'video_plays_25_rate' => $item['video_plays_25_rate'],
'video_plays_50_rate' => $item['video_plays_50_rate'],
'video_plays_75_rate' => $item['video_plays_75_rate'],
'video_plays_100_rate' => $item['video_plays_100_rate'],
'hold_rate' => '-',
'ad_count' => $item['ad_count'],
// 添加更多的格式化字段
];
}, $creativeSummaryData);
// $formattedData = array_values($creativeSummaryData); // 未分页处理
$pagedData = array_slice(array_values($formattedData), $startIndex, $pageSize); // 分页处理
// 9. 返回分页数据
return [
'data' => $pagedData,
'total' => count($creativeSummaryData),
'statistics' => $statistics, // 汇总的统计数据
'pagination' => [
'startIndex' => ($page - 1) * $pageSize,
'maxResults' => $pageSize,
'count' => count($creativeSummaryData),
'pageNo' => $page,
'pageSize' => $pageSize,
'pages' => ceil(count($creativeSummaryData) / $pageSize)
]
];
}
/**
* 初始化统计数据
*/
private function initializeStatistics()
{
return [
'impressions' => 0,
'clicks' => 0,
'revenue' => 0,
'spend' => 0,
'purchases_value' => 0,
'roas' => 0,
'cpa' => '-',
'cpc_link_click' => '-',
'cpm' => '-',
'cpc_all' => '-',
'aov' => '-',
'click_to_atc_ratio' => '-',
'atc_to_purchase_ratio' => '-',
'purchases' => 0,
'first_frame_retention' => '-',
'thumbstop' => '-',
'ctr_outbound' => '-',
'click_to_purchase' => '-',
'ctr_all' => '-',
'video_plays_25_rate' => 0,
'video_plays_50_rate' => 0,
'video_plays_75_rate' => 0,
'video_plays_100_rate' => 0,
'hold_rate' => '-',
// Add other stats as necessary
];
}
public function getAdcycleInsight($platformType, $customerIds, $cycle, $startDate = null, $endDate = null)
{
// 1. 查询全部数据集
$adcycleDataQuery = BpsAdInsight::alias('i');
if ($platformType != 0) {
$adcycleDataQuery->where('i.platform', $platformType); // 只有 platformType 不为 0 时才添加筛选
}
//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->toArray(), $cycle);
// 6. 返回处理后的数据
return $processedData;
}
/**
* 按周期处理数据
*
* @param array $adcycleData 原始数据
* @param string $cycle 周期类型daily, weekly, monthly
* @return array 按照周期分组后的数据
*/
private function processDataByCycle($adcycleData, $cycle)
{
// 排序 adcycleData 数组,确保按照日期升序排列
usort($adcycleData, function ($a, $b) {
return $a['date'] <=> $b['date']; // 按照 'YYYYMMDD' 字符顺序排序
});
$groupedData = [];
foreach ($adcycleData as $data) {
// 根据周期类型,调整日期分组
switch ($cycle) {
case 1:
// 将 'YYYYMMDD' 格式的日期转换为 'YYYY-MM-DD'
$date = DateTime::createFromFormat('Ymd', $data['date']); // 转换为 DateTime 对象
$key = $date ? $date->format('Y-m-d') : ''; // 格式化为 'YYYY-MM-DD'
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') . 'W';
}
/**
* 获取月分格式YYYY-MM
*
* @param int $date 日期格式YYYYMMDD
* @return string 月份格式YYYY-MM
*/
private function getMonthFromDate($date)
{
// 将日期转换为 PHP DateTime 对象
$dateStr = (string)$date;
$dateObj = \DateTime::createFromFormat('Ymd', $dateStr);
// 获取年-月格式
return $dateObj->format('Y-m');
}
public static function getPlatformType($thirdType)
{
$platformMapping = [
'facebook' => 1,
'google' => 2,
'tiktok' => 3,
];
return $platformMapping[$thirdType] ?? null; // 如果不存在的第三方类型,返回 null
}
}