新原型的accounts接口、campaign接口、adset接口、ad接口
This commit is contained in:
parent
83dcc0b7b5
commit
a3768b694e
553
app/controller/BpsAdController.php
Normal file
553
app/controller/BpsAdController.php
Normal file
@ -0,0 +1,553 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\service\GoogleAdsAdService;
|
||||
use app\service\GoogleAdsCampaignService;
|
||||
use app\service\GoogleAdsGroupService;
|
||||
|
||||
//use app\service\GoogleAdsReportService;
|
||||
use app\service\AdsInsightService;
|
||||
use app\service\GoogleOAuthService;
|
||||
use app\service\BpsAdAccountService;
|
||||
use DI\Annotation\Inject;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
|
||||
class BpsAdController
|
||||
{
|
||||
|
||||
/**
|
||||
* @Inject
|
||||
* @var AdsInsightService
|
||||
*/
|
||||
private $adsInsightService;
|
||||
|
||||
/**
|
||||
* @Inject
|
||||
* @var GoogleAdsGroupService
|
||||
*/
|
||||
private $googleAdsGroupService;
|
||||
|
||||
/**
|
||||
* @Inject
|
||||
* @var GoogleAdsAdService
|
||||
*/
|
||||
private $googleAdsAdService;
|
||||
|
||||
/**
|
||||
* @Inject
|
||||
* @var GoogleAdsCampaignService
|
||||
*/
|
||||
private $googleAdsCampaignService;
|
||||
|
||||
/**
|
||||
* @Inject
|
||||
* @var GoogleOAuthService
|
||||
*/
|
||||
private $googleOAuthService;
|
||||
/**
|
||||
* @Inject
|
||||
* @var BpsAdAccountService
|
||||
*/
|
||||
private $bpsAdAccountService;
|
||||
|
||||
|
||||
public function listThirdUsers(Request $request)
|
||||
{
|
||||
$options = $request->all();
|
||||
$options['jwtClaims'] = $request->jwtClaims;
|
||||
|
||||
// 获取请求参数
|
||||
$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; // 结束日期
|
||||
// dump($options);
|
||||
// 根据 platformType 获取第三方用户数据
|
||||
if ($platformType === 1) {
|
||||
// 获取 Facebook 第三方用户数据
|
||||
$users = $this->bpsAdAccountService->getMetaThirdUsers(['uid' => $options['jwtClaims']['uid']]);
|
||||
} elseif ($platformType === 2) {
|
||||
// 获取 Google 第三方用户数据
|
||||
$users = $this->bpsAdAccountService->getGoogleThirdUsers(['uid' => $options['jwtClaims']['uid']]);
|
||||
} elseif ($platformType === 3) {
|
||||
// 获取 TikTok 第三方用户数据
|
||||
$users = $this->bpsAdAccountService->getTiktokThirdUsers(['uid' => $options['jwtClaims']['uid']]);
|
||||
} else {
|
||||
// 根据 JWT claims 获取所有平台的第三方用户
|
||||
$users = $this->bpsAdAccountService->getAllThirdUsers(['uid' => $options['jwtClaims']['uid']]);
|
||||
}
|
||||
|
||||
if (empty($users)) {
|
||||
return $this->errorResponse(1, 'no data');
|
||||
}
|
||||
|
||||
// 获取客户 ID 数组,并去重
|
||||
$userIds = array_unique(array_column($users, 'id'));
|
||||
|
||||
|
||||
// 调用 Service 层查询第三方用户数据列表
|
||||
$result = $this->adsInsightService::getThirdUserList(
|
||||
$platformType, // 平台类型
|
||||
$userIds, // 第三方用户 ID 数组
|
||||
$page, // 页码
|
||||
$pageSize, // 每页数量
|
||||
$keyword, // 关键字
|
||||
$startDate, // 开始日期
|
||||
$endDate // 结束日期
|
||||
);
|
||||
|
||||
// 返回结果
|
||||
return $this->successResponse($result, $request);
|
||||
}
|
||||
|
||||
|
||||
public function listAds(Request $request)
|
||||
{
|
||||
$options = $request->all();
|
||||
$options['jwtClaims'] = $request->jwtClaims;
|
||||
|
||||
// 获取请求参数
|
||||
$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'; // 默认日期范围
|
||||
|
||||
// 根据 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']]);
|
||||
}
|
||||
|
||||
if (empty($accounts)) {
|
||||
return $this->errorResponse(1, 'no data');
|
||||
}
|
||||
|
||||
// 获取客户ID数组
|
||||
$accountIds = array_column($accounts, 'account_id');
|
||||
|
||||
// 调用 Service 层查询广告列表
|
||||
$result = $this->adsInsightService::getAdList(
|
||||
$platformType, // 平台类型
|
||||
$accountIds, // 客户 ID 数组
|
||||
$page, // 页码
|
||||
$pageSize, // 每页数量
|
||||
$keyword, // 关键字
|
||||
$startDate, // 开始日期
|
||||
$endDate // 结束日期
|
||||
);
|
||||
|
||||
// 返回结果
|
||||
return $this->successResponse($result, $request);
|
||||
}
|
||||
|
||||
|
||||
public function listAssets(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'; // 默认日期范围
|
||||
|
||||
// $customerId = 4060397299;
|
||||
$customers = $this->googleOAuthService->getGoogleAdCustomers(['refresh_token' => $request->refresh_token]);
|
||||
$customerIds = array_column($customers, 'customer_id');
|
||||
//dump( $customerIds, // 客户 ID 数组
|
||||
// $page, // 页码
|
||||
// $pageSize, // 每页数量
|
||||
// $keyword, // 关键字
|
||||
// $dateRange,
|
||||
// $startDate, // 开始日期
|
||||
// $endDate);
|
||||
// 调用 Service 层查询
|
||||
$result = $this->googleAdsReportService->getAssetConversionData(
|
||||
$customerIds, // 客户 ID 数组
|
||||
$page, // 页码
|
||||
$pageSize, // 每页数量
|
||||
$keyword, // 关键字
|
||||
$dateRange,
|
||||
$startDate, // 开始日期
|
||||
$endDate);
|
||||
return $this->successResponse($result, $request);
|
||||
}
|
||||
|
||||
public function listCampaigns(Request $request)
|
||||
{
|
||||
$options = $request->all();
|
||||
$options['jwtClaims'] = $request->jwtClaims;
|
||||
|
||||
// 获取请求参数
|
||||
$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'; // 默认日期范围
|
||||
// 根据 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']]);
|
||||
}
|
||||
|
||||
if (empty($accounts)) {
|
||||
return $this->errorResponse(1, 'no data');
|
||||
}
|
||||
|
||||
// 获取客户ID数组
|
||||
$accountIds = array_column($accounts, 'account_id');
|
||||
// 调用 Service 层查询
|
||||
$result = $this->adsInsightService::getCampaignList(
|
||||
$platformType,
|
||||
$accountIds, // 客户 ID 数组
|
||||
$page, // 页码
|
||||
$pageSize, // 每页数量
|
||||
$keyword, // 关键字
|
||||
$startDate, // 开始日期
|
||||
$endDate // 结束日期
|
||||
);
|
||||
return $this->successResponse($result, $request);
|
||||
// return $this->errorResponse(300,'授权失败');
|
||||
}
|
||||
|
||||
public function listAdsets(Request $request)
|
||||
{
|
||||
$options = $request->all();
|
||||
$options['jwtClaims'] = $request->jwtClaims;
|
||||
|
||||
// 获取请求参数
|
||||
$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'; // 默认日期范围
|
||||
|
||||
// 根据 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']]);
|
||||
}
|
||||
|
||||
if (empty($accounts)) {
|
||||
return $this->errorResponse(1, 'no data');
|
||||
}
|
||||
|
||||
// 获取客户ID数组
|
||||
$accountIds = array_column($accounts, 'account_id');
|
||||
|
||||
// 调用 Service 层查询广告组列表
|
||||
$result = $this->adsInsightService::getAdsetList(
|
||||
$platformType,
|
||||
$accountIds, // 客户 ID 数组
|
||||
$page, // 页码
|
||||
$pageSize, // 每页数量
|
||||
$keyword, // 关键字
|
||||
$startDate, // 开始日期
|
||||
$endDate // 结束日期
|
||||
);
|
||||
|
||||
// 返回结果
|
||||
return $this->successResponse($result, $request);
|
||||
}
|
||||
|
||||
|
||||
public function exportAdsToExcel(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');
|
||||
// 调用 service 层导出数据
|
||||
return $this->googleAdsReportService::exportAdListToExcel($customerIds, $keyword, $dateRange, $startDate, $endDate);
|
||||
}
|
||||
|
||||
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'; // 默认日期范围
|
||||
|
||||
$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 数组
|
||||
$page, // 页码
|
||||
$pageSize, // 每页数量
|
||||
$keyword, // 关键字
|
||||
$dateRange,
|
||||
$startDate, // 开始日期
|
||||
$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
|
||||
{
|
||||
$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);
|
||||
|
||||
$status = $requestData['status'];
|
||||
if (!in_array($status, $campaignStatus)) {
|
||||
return $this->errorResponse(101, 'status参数错误');
|
||||
}
|
||||
// try {
|
||||
$reslut = $this->googleAdsCampaignService->updateCampaignStatus($requestData);
|
||||
if (!$reslut) {
|
||||
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 isEnabled(int $campaignId): Json
|
||||
// {
|
||||
// try {
|
||||
// $isEnabled = $this->campaignService->isCampaignEnabled($campaignId);
|
||||
// return json(['enabled' => $isEnabled], 200);
|
||||
// } catch (ValidateException $e) {
|
||||
// return json(['error' => $e->getMessage()], 400);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 判断广告系列是否暂停
|
||||
*/
|
||||
// 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());
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
// 可以加入一些公共方法
|
||||
protected function successResponse($data, Request $request): Response
|
||||
{
|
||||
if ($request->jwtNewToken) {
|
||||
return new Response(200,
|
||||
[
|
||||
'Content-Type' => 'application/json',
|
||||
'X-New-Token' => $request->jwtNewToken
|
||||
],
|
||||
json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
} else {
|
||||
return Json([
|
||||
'code' => 0,
|
||||
'msg' => 'ok',
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected function errorResponse($code, $message, $data = []): Response
|
||||
{
|
||||
return Json([
|
||||
'code' => $code,
|
||||
'msg' => $message ?: 'error',
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
195
app/model/BpsAdAd.php
Normal file
195
app/model/BpsAdAd.php
Normal file
@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class BpsAdAd extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $table = 'bps.bps_ads_ad';
|
||||
|
||||
// 设置主键
|
||||
protected $pk = 'id';
|
||||
|
||||
// 设置自动时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 定义时间戳字段
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
// 设置字段类型和默认值
|
||||
protected $casts = [
|
||||
'ad_id' => 'int',
|
||||
'ad_set_id' => 'int',
|
||||
'account_id' => 'int',
|
||||
];
|
||||
|
||||
// 设置json类型字段
|
||||
protected $json = ['metadata'];
|
||||
|
||||
|
||||
// 默认值设置
|
||||
protected $defaults = [
|
||||
'status' => 2, // 广告状态默认值为 'ENABLED'
|
||||
'platform_type' => 2,
|
||||
];
|
||||
|
||||
// 状态判断常量
|
||||
const STATUS_UNSPECIFIED = 0;
|
||||
const STATUS_UNKNOWN = 1;
|
||||
const STATUS_ENABLED = 2;
|
||||
const STATUS_PAUSED = 3;
|
||||
const STATUS_REMOVED = 4;
|
||||
|
||||
// 获取广告状态
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$statusMap = [
|
||||
self::STATUS_UNSPECIFIED => 'UNSPECIFIED',
|
||||
self::STATUS_UNKNOWN => 'UNKNOWN',
|
||||
self::STATUS_ENABLED => 'ENABLED',
|
||||
self::STATUS_PAUSED => 'PAUSED',
|
||||
self::STATUS_REMOVED => 'REMOVED',
|
||||
];
|
||||
return $statusMap[$data['status']] ?? 'UNKNOWN';
|
||||
}
|
||||
|
||||
// 定义广告类型枚举常量
|
||||
const UNSPECIFIED = 0;
|
||||
const UNKNOWN = 1;
|
||||
const TEXT_AD = 2;
|
||||
const EXPANDED_TEXT_AD = 3;
|
||||
const EXPANDED_DYNAMIC_SEARCH_AD = 7;
|
||||
const HOTEL_AD = 8;
|
||||
const SHOPPING_SMART_AD = 9;
|
||||
const SHOPPING_PRODUCT_AD = 10;
|
||||
const VIDEO_AD = 12;
|
||||
const IMAGE_AD = 14;
|
||||
const RESPONSIVE_SEARCH_AD = 15;
|
||||
const LEGACY_RESPONSIVE_DISPLAY_AD = 16;
|
||||
const APP_AD = 17;
|
||||
const LEGACY_APP_INSTALL_AD = 18;
|
||||
const RESPONSIVE_DISPLAY_AD = 19;
|
||||
const LOCAL_AD = 20;
|
||||
const HTML5_UPLOAD_AD = 21;
|
||||
const DYNAMIC_HTML5_AD = 22;
|
||||
const APP_ENGAGEMENT_AD = 23;
|
||||
const SHOPPING_COMPARISON_LISTING_AD = 24;
|
||||
const VIDEO_BUMPER_AD = 25;
|
||||
const VIDEO_NON_SKIPPABLE_IN_STREAM_AD = 26;
|
||||
const VIDEO_OUTSTREAM_AD = 27;
|
||||
const VIDEO_TRUEVIEW_IN_STREAM_AD = 29;
|
||||
const VIDEO_RESPONSIVE_AD = 30;
|
||||
const SMART_CAMPAIGN_AD = 31;
|
||||
const CALL_AD = 32;
|
||||
const APP_PRE_REGISTRATION_AD = 33;
|
||||
const IN_FEED_VIDEO_AD = 34;
|
||||
const DEMAND_GEN_MULTI_ASSET_AD = 40;
|
||||
const DEMAND_GEN_CAROUSEL_AD = 41;
|
||||
const TRAVEL_AD = 37;
|
||||
const DEMAND_GEN_VIDEO_RESPONSIVE_AD = 42;
|
||||
const DEMAND_GEN_PRODUCT_AD = 39;
|
||||
|
||||
private static $valueToName = [
|
||||
self::UNSPECIFIED => 'UNSPECIFIED',
|
||||
self::UNKNOWN => 'UNKNOWN',
|
||||
self::TEXT_AD => 'TEXT_AD',
|
||||
self::EXPANDED_TEXT_AD => 'EXPANDED_TEXT_AD',
|
||||
self::EXPANDED_DYNAMIC_SEARCH_AD => 'EXPANDED_DYNAMIC_SEARCH_AD',
|
||||
self::HOTEL_AD => 'HOTEL_AD',
|
||||
self::SHOPPING_SMART_AD => 'SHOPPING_SMART_AD',
|
||||
self::SHOPPING_PRODUCT_AD => 'SHOPPING_PRODUCT_AD',
|
||||
self::VIDEO_AD => 'VIDEO_AD',
|
||||
self::IMAGE_AD => 'IMAGE_AD',
|
||||
self::RESPONSIVE_SEARCH_AD => 'RESPONSIVE_SEARCH_AD',
|
||||
self::LEGACY_RESPONSIVE_DISPLAY_AD => 'LEGACY_RESPONSIVE_DISPLAY_AD',
|
||||
self::APP_AD => 'APP_AD',
|
||||
self::LEGACY_APP_INSTALL_AD => 'LEGACY_APP_INSTALL_AD',
|
||||
self::RESPONSIVE_DISPLAY_AD => 'RESPONSIVE_DISPLAY_AD',
|
||||
self::LOCAL_AD => 'LOCAL_AD',
|
||||
self::HTML5_UPLOAD_AD => 'HTML5_UPLOAD_AD',
|
||||
self::DYNAMIC_HTML5_AD => 'DYNAMIC_HTML5_AD',
|
||||
self::APP_ENGAGEMENT_AD => 'APP_ENGAGEMENT_AD',
|
||||
self::SHOPPING_COMPARISON_LISTING_AD => 'SHOPPING_COMPARISON_LISTING_AD',
|
||||
self::VIDEO_BUMPER_AD => 'VIDEO_BUMPER_AD',
|
||||
self::VIDEO_NON_SKIPPABLE_IN_STREAM_AD => 'VIDEO_NON_SKIPPABLE_IN_STREAM_AD',
|
||||
self::VIDEO_OUTSTREAM_AD => 'VIDEO_OUTSTREAM_AD',
|
||||
self::VIDEO_TRUEVIEW_IN_STREAM_AD => 'VIDEO_TRUEVIEW_IN_STREAM_AD',
|
||||
self::VIDEO_RESPONSIVE_AD => 'VIDEO_RESPONSIVE_AD',
|
||||
self::SMART_CAMPAIGN_AD => 'SMART_CAMPAIGN_AD',
|
||||
self::CALL_AD => 'CALL_AD',
|
||||
self::APP_PRE_REGISTRATION_AD => 'APP_PRE_REGISTRATION_AD',
|
||||
self::IN_FEED_VIDEO_AD => 'IN_FEED_VIDEO_AD',
|
||||
self::DEMAND_GEN_MULTI_ASSET_AD => 'DEMAND_GEN_MULTI_ASSET_AD',
|
||||
self::DEMAND_GEN_CAROUSEL_AD => 'DEMAND_GEN_CAROUSEL_AD',
|
||||
self::TRAVEL_AD => 'TRAVEL_AD',
|
||||
self::DEMAND_GEN_VIDEO_RESPONSIVE_AD => 'DEMAND_GEN_VIDEO_RESPONSIVE_AD',
|
||||
self::DEMAND_GEN_PRODUCT_AD => 'DEMAND_GEN_PRODUCT_AD',
|
||||
];
|
||||
|
||||
// 检查广告类型是否有效
|
||||
public static function isValidAdType($adType) {
|
||||
return in_array($adType, array_keys(self::$valueToName));
|
||||
}
|
||||
|
||||
// 获取广告类型名称
|
||||
public static function getAdTypeName($adType) {
|
||||
return self::$valueToName[$adType] ?? 'UNKNOWN';
|
||||
}
|
||||
|
||||
// 在模型中使用这些常量
|
||||
public function setAdType($adType) {
|
||||
if (!self::isValidAdType($adType)) {
|
||||
throw new Exception("Invalid ad type");
|
||||
}
|
||||
$this->ad_type = $adType;
|
||||
}
|
||||
|
||||
// 更新广告状态
|
||||
// public function updateStatus($status)
|
||||
// {
|
||||
// if (!in_array($status, [self::STATUS_ENABLED, self::STATUS_PAUSED, self::STATUS_REMOVED])) {
|
||||
//// throw new \think\exception\ValidateException('Invalid status');
|
||||
// }
|
||||
//
|
||||
// $this->status = $status;
|
||||
// return $this->save();
|
||||
// }
|
||||
|
||||
// 判断广告状态
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->status == self::STATUS_ENABLED;
|
||||
}
|
||||
|
||||
public function isPaused()
|
||||
{
|
||||
return $this->status == self::STATUS_PAUSED;
|
||||
}
|
||||
|
||||
public function isRemoved()
|
||||
{
|
||||
return $this->status == self::STATUS_REMOVED;
|
||||
}
|
||||
|
||||
// 关联 AdGroup 模型(广告属于广告组)
|
||||
// 即使没有外键约束,依然可以使用 belongsTo 访问 AdGroup 数据
|
||||
public function adSet()
|
||||
{
|
||||
return $this->belongsTo(BpsAdSet::class, 'ad_set_id', 'ad_set_id');
|
||||
}
|
||||
|
||||
// 关联到素材关系
|
||||
// public function assetRelations()
|
||||
// {
|
||||
// return $this->hasMany(AssetRelation::class, 'ad_id', 'ad_id');
|
||||
// }
|
||||
|
||||
// 关联 Customer 模型(广告属于客户)
|
||||
// public function customer()
|
||||
// {
|
||||
// return $this->belongsTo(GoogleAdsCustomer::class, 'customer_id', 'customer_id');
|
||||
// }
|
||||
}
|
118
app/model/BpsAdCampaign.php
Normal file
118
app/model/BpsAdCampaign.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class BpsAdCampaign extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $table = 'bps.bps_ads_campaign';
|
||||
|
||||
// 设置主键
|
||||
protected $pk = 'id';
|
||||
|
||||
// 设置自动时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 定义时间戳字段
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
// 设置字段类型和默认值
|
||||
// protected $casts = [
|
||||
// 'campaign_id' => 'int',
|
||||
// 'account_id' => 'int',
|
||||
// 'budget_amount_micros' => 'int',
|
||||
// ];
|
||||
|
||||
// 默认值设置
|
||||
protected $defaults = [
|
||||
'status' => 'ENABLED', // 活动状态默认值为 'ENABLED'
|
||||
// 'advertising_channel_type' => 'SEARCH', // 广告渠道类型默认值为 'SEARCH'
|
||||
'platform_type' => 2, // 广告渠道类型默认值为 'SEARCH'
|
||||
];
|
||||
|
||||
|
||||
// 状态判断常量
|
||||
const STATUS_UNSPECIFIED = 0;
|
||||
const STATUS_UNKNOWN = 1;
|
||||
const STATUS_ENABLED = 2;
|
||||
const STATUS_PAUSED = 3;
|
||||
const STATUS_REMOVED = 4;
|
||||
|
||||
// 获取活动状态
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$statusMap = [
|
||||
self::STATUS_UNSPECIFIED => 'UNSPECIFIED',
|
||||
self::STATUS_UNKNOWN => 'UNKNOWN',
|
||||
self::STATUS_ENABLED => 'ENABLED',
|
||||
self::STATUS_PAUSED => 'PAUSED',
|
||||
self::STATUS_REMOVED => 'REMOVED',
|
||||
];
|
||||
return $statusMap[$data['status']] ?? 'UNKNOWN';
|
||||
}
|
||||
|
||||
// 更新状态方法
|
||||
// public function updateStatus($status)
|
||||
// {
|
||||
// if (!in_array($status, [self::STATUS_ENABLED, self::STATUS_PAUSED, self::STATUS_REMOVED])) {
|
||||
//// throw new \think\exception\ValidateException('Invalid status');
|
||||
// }
|
||||
// $this->status = $status;
|
||||
// return $this->save();
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// 判断当前活动状态
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->status == self::STATUS_ENABLED;
|
||||
}
|
||||
|
||||
public function isPaused()
|
||||
{
|
||||
return $this->status == self::STATUS_PAUSED;
|
||||
}
|
||||
|
||||
public function isRemoved()
|
||||
{
|
||||
return $this->status == self::STATUS_REMOVED;
|
||||
}
|
||||
|
||||
|
||||
// 关联 Customer 模型(广告活动属于客户)
|
||||
// public function customer()
|
||||
// {
|
||||
// return $this->belongsTo(GoogleAdsCustomer::class, 'customer_id', 'customer_id');
|
||||
// }
|
||||
|
||||
// 关联 AdGroup 模型(一个广告活动下有多个广告组)
|
||||
public function adGroups()
|
||||
{
|
||||
return $this->hasMany(AdGroup::class, 'campaign_id', 'campaign_id');
|
||||
}
|
||||
|
||||
// 关联 Ad 模型(一个广告活动下有多个广告)
|
||||
public function ads()
|
||||
{
|
||||
return $this->hasManyThrough(BpsAdAd::class, BpsAdSet::class, 'campaign_id', 'ad_set_id', 'campaign_id', 'ad_set_id');
|
||||
}
|
||||
|
||||
// 关联到广告数据表
|
||||
// public function adDayData()
|
||||
// {
|
||||
// return $this->hasMany(DayData::class, 'campaign_id', 'campaign_id');
|
||||
// }
|
||||
public function adDayData()
|
||||
{
|
||||
return $this->hasMany(DayData::class, 'campaign_id', 'campaign_id');
|
||||
}
|
||||
// 关联到素材关系
|
||||
public function assetRelations()
|
||||
{
|
||||
return $this->hasMany(AssetRelation::class, 'campaign_id', 'campaign_id');
|
||||
}
|
||||
}
|
93
app/model/BpsAdInsight.php
Normal file
93
app/model/BpsAdInsight.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class BpsAdInsight extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $table = 'bps.bps_ads_insights';
|
||||
|
||||
// 设置复合主键
|
||||
protected $pk = ['id'];
|
||||
|
||||
// 设置自动时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 定义时间戳字段
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
// 设置字段类型和默认值
|
||||
protected $casts = [
|
||||
'ad_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');
|
||||
// }
|
||||
}
|
106
app/model/BpsAdSet.php
Normal file
106
app/model/BpsAdSet.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
class BpsAdSet extends Model
|
||||
{
|
||||
// 设置当前模型对应的完整数据表名称
|
||||
protected $table = 'bps.bps_ads_set';
|
||||
|
||||
// 设置主键
|
||||
protected $pk = 'id';
|
||||
|
||||
// 设置自动时间戳
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
// 定义时间戳字段
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
// 设置字段类型和默认值
|
||||
// protected $casts = [
|
||||
// 'ad_set_id' => 'int',
|
||||
// 'campaign_id' => 'int',
|
||||
// 'account_id' => 'int',
|
||||
// ];
|
||||
|
||||
// 默认值设置
|
||||
protected $defaults = [
|
||||
'platform_type' => 2,
|
||||
];
|
||||
|
||||
// 状态判断常量
|
||||
const STATUS_UNSPECIFIED = 0;
|
||||
const STATUS_UNKNOWN = 1;
|
||||
const STATUS_ENABLED = 2;
|
||||
const STATUS_PAUSED = 3;
|
||||
const STATUS_REMOVED = 4;
|
||||
|
||||
// 获取广告组状态
|
||||
public function getStatusTextAttr($value, $data)
|
||||
{
|
||||
$statusMap = [
|
||||
self::STATUS_UNSPECIFIED => 'UNSPECIFIED',
|
||||
self::STATUS_UNKNOWN => 'UNKNOWN',
|
||||
self::STATUS_ENABLED => 'ENABLED',
|
||||
self::STATUS_PAUSED => 'PAUSED',
|
||||
self::STATUS_REMOVED => 'REMOVED',
|
||||
];
|
||||
return $statusMap[$data['status']] ?? 'UNKNOWN';
|
||||
}
|
||||
|
||||
// 更新广告组状态
|
||||
// public function updateStatus($status)
|
||||
// {
|
||||
// if (!in_array($status, [self::STATUS_ENABLED, self::STATUS_PAUSED, self::STATUS_REMOVED])) {
|
||||
//// throw new \think\exception\ValidateException('Invalid status');
|
||||
// }
|
||||
//
|
||||
// $this->status = $status;
|
||||
// return $this->save();
|
||||
// }
|
||||
|
||||
// 判断广告组状态
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->status == self::STATUS_ENABLED;
|
||||
}
|
||||
|
||||
public function isPaused()
|
||||
{
|
||||
return $this->status == self::STATUS_PAUSED;
|
||||
}
|
||||
|
||||
public function isRemoved()
|
||||
{
|
||||
return $this->status == self::STATUS_REMOVED;
|
||||
}
|
||||
|
||||
|
||||
// 关联 Campaign 模型(广告组属于广告活动)
|
||||
public function campaign()
|
||||
{
|
||||
return $this->belongsTo(BpsAdCampaign::class, 'campaign_id', 'campaign_id');
|
||||
}
|
||||
|
||||
// 关联 Customer 模型(广告组属于客户)
|
||||
// public function customer()
|
||||
// {
|
||||
// return $this->belongsTo(GoogleAdsCustomer::class, 'customer_id', 'customer_id');
|
||||
// }
|
||||
|
||||
// 关联 Ad 模型(广告组包含多个广告)
|
||||
public function ads()
|
||||
{
|
||||
return $this->hasMany(BpsAdAd::class, 'ad_set_id', 'ad_set_id');
|
||||
}
|
||||
|
||||
// 关联到素材关系
|
||||
// public function assetRelations()
|
||||
// {
|
||||
// return $this->hasMany(AssetRelation::class, 'ad_group_id', 'ad_group_id');
|
||||
// }
|
||||
}
|
||||
|
995
app/service/AdsInsightService.php
Normal file
995
app/service/AdsInsightService.php
Normal file
@ -0,0 +1,995 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use app\model\Ad;
|
||||
use app\model\DayData;
|
||||
use app\model\Campaign;
|
||||
use app\model\BpsAdCampaign;
|
||||
use app\model\BpsAdSet;
|
||||
use app\model\BpsAdAd;
|
||||
use app\model\ThirdUser;
|
||||
use app\model\Asset;
|
||||
use app\model\AssetRelation;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||
use think\db\exception\DbException;
|
||||
use think\facade\Db as ThinkDb;
|
||||
|
||||
class AdsInsightService
|
||||
{
|
||||
// 状态映射数组
|
||||
private static $statusMapping = [
|
||||
2 => 'ENABLE', // 2 代表广告已启用
|
||||
3 => 'PAUSED', // 3 代表广告待审核等状态
|
||||
// 其他状态可以继续添加
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取广告系列列表
|
||||
*/
|
||||
public static function getCampaignList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null)
|
||||
{
|
||||
// 检查 customerIds 是否为空,直接返回空结构
|
||||
if (empty($customerIds)) {
|
||||
return [
|
||||
'pagination' => [
|
||||
'startIndex' => 0,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => 0,
|
||||
'pageNo' => $page,
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => 0,
|
||||
],
|
||||
'statistics' => [
|
||||
],
|
||||
'data' => [],
|
||||
];
|
||||
}
|
||||
// 动态构建日期条件
|
||||
$dateCondition = '';
|
||||
if ($startDate && $endDate) {
|
||||
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
|
||||
} else {
|
||||
}
|
||||
|
||||
// 基础查询:广告活动和日数据表联接
|
||||
$query = BpsAdCampaign::alias('c')
|
||||
->cache(false) // 强制不使用缓存
|
||||
->leftJoin('bps.bps_ads_insights d', "c.campaign_id = d.ad_campaign_id AND c.platform_type = d.platform AND {$dateCondition}")
|
||||
->field('c.campaign_id, c.status as status, c.name, c.account_id,
|
||||
COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
|
||||
COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
|
||||
COALESCE(SUM(d.clicks), 0) as clicks,
|
||||
COALESCE(SUM(d.spend) / 1000000, 0) as spend,
|
||||
COALESCE(SUM(d.impressions), 0) as impressions,
|
||||
COALESCE(SUM(d.adds_to_cart), 0) as adds_to_cart,
|
||||
COALESCE(SUM(d.cost_per_atc), 0) as cost_per_atc,
|
||||
COALESCE(SUM(d.purchases), 0) as purchases,
|
||||
COALESCE(SUM(d.purchases_value), 0) as purchases_value,
|
||||
COALESCE(SUM(d.revenue), 0) as revenue,
|
||||
COALESCE(SUM(d.total_cost), 0) as total_cost,
|
||||
-1 as conversion_rate, -1 as roas, -1 as ctr,-1 as net_profit,-1 as net_profit_margin,-1 as net_profit_on_ad_spend')
|
||||
->group('c.campaign_id, c.status, c.account_id, c.name')
|
||||
->where('c.account_id', 'in', $customerIds); // 添加 customerIds 条件
|
||||
|
||||
// 添加关键字过滤条件
|
||||
$query->where(function ($query) use ($keyword, $platformType) {
|
||||
if ($keyword) {
|
||||
$query->where('c.campaign_name', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
if ($platformType) {
|
||||
$platformType = (int)$platformType;
|
||||
$query->where('c.platform_type', '=', $platformType);
|
||||
}
|
||||
});
|
||||
|
||||
// 获取所有符合条件的数据(不分页)
|
||||
$allCampaigns = $query->select()->toArray();
|
||||
|
||||
$total_spend = array_sum(array_column($allCampaigns, 'spend'));
|
||||
$total_cost = array_sum(array_column($allCampaigns, 'total_cost'));
|
||||
$total_impressions = array_sum(array_column($allCampaigns, 'impressions'));
|
||||
$total_clicks = array_sum(array_column($allCampaigns, 'clicks'));
|
||||
$total_purchases_value = array_sum(array_column($allCampaigns, 'purchases_value'));
|
||||
// 汇总统计数据
|
||||
$statistics = [
|
||||
'assisted_purchases' => array_sum(array_column($allCampaigns, 'assisted_purchases')),
|
||||
'last_clicked_purchases' => array_sum(array_column($allCampaigns, 'last_clicked_purchases')),
|
||||
'roas' => $total_spend === 0 ? '-' : round($total_purchases_value / $total_spend, 2),
|
||||
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
|
||||
'clicks' => $total_clicks,
|
||||
'impressions' => $total_impressions,
|
||||
'adds_to_cart' => array_sum(array_column($allCampaigns, 'adds_to_cart')),
|
||||
'cost_per_atc' => array_sum(array_column($allCampaigns, 'cost_per_atc')),
|
||||
'purchases' => array_sum(array_column($allCampaigns, 'purchases')),
|
||||
'purchases_value' => array_sum(array_column($allCampaigns, 'purchases_value')),
|
||||
'revenue' => '$' . number_format(array_sum(array_column($allCampaigns, 'revenue')), 2) ?: '$0.00', // 格式化收入
|
||||
'total_cost' => '$' . number_format($total_cost, 2) ?: '$0.00', // 格式化总成本
|
||||
'conversion_rate' => '-', // 没有计算逻辑,保持为 '-'
|
||||
'net_profit' => '-', // 没有计算 net_profit,保持为 '-'
|
||||
'net_profit_margin' => '-', // 没有计算 net_profit_margin,保持为 '-'
|
||||
'net_profit_on_ad_spend' => '-', // 没有计算 net_profit_on_ad_spend,保持为 '-'
|
||||
// 计算总的 CTR
|
||||
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
|
||||
];
|
||||
|
||||
// 获取分页数据
|
||||
$campaigns = $query->paginate($pageSize, false, ['page' => $page]);
|
||||
|
||||
// 确保数据格式统一
|
||||
$result = array_map(function ($item) {
|
||||
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-'; // 格式化为百分比
|
||||
return [
|
||||
'id' => $item['campaign_id'],
|
||||
'account_id' => $item['account_id'], // 映射为 customer_id
|
||||
'name' => $item['name'] ?: '-', // 若 name 为空则显示 '-'
|
||||
'status' => $item['status'],
|
||||
'assisted_purchases' => $item['assisted_purchases'],
|
||||
'last_clicked_purchases' => $item['last_clicked_purchases'],
|
||||
'roas' => $item['spend'] == 0 ? '-' : round($item['purchases_value'] / $item['spend'], 2),
|
||||
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
|
||||
'impressions' => $item['impressions'],
|
||||
'clicks' => $item['clicks'],
|
||||
'ctr' => $ctr, // CTR 字段
|
||||
'adds_to_cart' => $item['adds_to_cart'],
|
||||
'cost_per_atc' => $item['cost_per_atc'],
|
||||
'purchases' => $item['purchases'],
|
||||
'purchases_value' => '$' . number_format($item['purchases_value'], 2), // 格式化购买金额
|
||||
'revenue' => '$' . number_format($item['revenue'], 2), // 格式化收入
|
||||
'total_cost' => '$' . number_format($item['total_cost'], 2), // 格式化总成本
|
||||
'conversion_rate' => '-', // 没有提供有效的计算,保持为 '-'
|
||||
'net_profit' => '-', // 没有提供 net_profit 计算,保持为 '-'
|
||||
'net_profit_margin' => '-', // 没有提供 net_profit_margin 计算,保持为 '-'
|
||||
'net_profit_on_ad_spend' => '-', // 没有提供 net_profit_on_ad_spend 计算,保持为 '-'
|
||||
];
|
||||
}, $campaigns->items());
|
||||
|
||||
// Pagination 数据
|
||||
$pagination = [
|
||||
'startIndex' => ($page - 1) * $pageSize,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => $campaigns->total(),
|
||||
'pageNo' => $campaigns->currentPage(),
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => $campaigns->lastPage(),
|
||||
];
|
||||
|
||||
return [
|
||||
'pagination' => $pagination,
|
||||
'statistics' => $statistics,
|
||||
'data' => $result,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取广告组列表
|
||||
*/
|
||||
public static function getAdsetList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null)
|
||||
{
|
||||
// 检查 customerIds 是否为空,直接返回空结构
|
||||
if (empty($customerIds)) {
|
||||
return [
|
||||
'pagination' => [
|
||||
'startIndex' => 0,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => 0,
|
||||
'pageNo' => $page,
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => 0,
|
||||
],
|
||||
'statistics' => [
|
||||
],
|
||||
'data' => [],
|
||||
];
|
||||
}
|
||||
|
||||
// 动态构建日期条件
|
||||
$dateCondition = '';
|
||||
if ($startDate && $endDate) {
|
||||
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
|
||||
}
|
||||
|
||||
// 基础查询:广告组和日数据表联接
|
||||
$query = BpsAdSet::alias('s')
|
||||
->cache(false) // 强制不使用缓存
|
||||
->leftJoin('bps.bps_ads_insights d', "s.ad_set_id = d.ad_set_id AND s.platform_type = d.platform AND {$dateCondition}")
|
||||
->field('s.ad_set_id, s.status as status, s.name, s.account_id,
|
||||
COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
|
||||
COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
|
||||
COALESCE(SUM(d.clicks), 0) as clicks,
|
||||
COALESCE(SUM(d.spend) / 1000000, 0) as spend,
|
||||
COALESCE(SUM(d.impressions), 0) as impressions,
|
||||
COALESCE(SUM(d.adds_to_cart), 0) as adds_to_cart,
|
||||
COALESCE(SUM(d.cost_per_atc), 0) as cost_per_atc,
|
||||
COALESCE(SUM(d.purchases), 0) as purchases,
|
||||
COALESCE(SUM(d.purchases_value), 0) as purchases_value,
|
||||
COALESCE(SUM(d.revenue), 0) as revenue,
|
||||
COALESCE(SUM(d.total_cost), 0) as total_cost,
|
||||
-1 as conversion_rate, -1 as roas, -1 as ctr,-1 as net_profit,-1 as net_profit_margin,-1 as net_profit_on_ad_spend')
|
||||
->group('s.ad_set_id, s.status, s.account_id, s.name')
|
||||
->where('s.account_id', 'in', $customerIds); // 添加 customerIds 条件
|
||||
|
||||
// 添加关键字过滤条件
|
||||
$query->where(function ($query) use ($keyword, $platformType) {
|
||||
if ($keyword) {
|
||||
$query->where('s.name', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
if ($platformType) {
|
||||
$platformType = (int)$platformType;
|
||||
$query->where('s.platform_type', '=', $platformType);
|
||||
}
|
||||
});
|
||||
|
||||
// 获取所有符合条件的数据(不分页)
|
||||
$allAdsets = $query->select()->toArray();
|
||||
|
||||
$total_spend = array_sum(array_column($allAdsets, 'spend'));
|
||||
$total_cost = array_sum(array_column($allAdsets, 'total_cost'));
|
||||
$total_impressions = array_sum(array_column($allAdsets, 'impressions'));
|
||||
$total_clicks = array_sum(array_column($allAdsets, 'clicks'));
|
||||
$total_purchases_value = array_sum(array_column($allAdsets, 'purchases_value'));
|
||||
|
||||
// 汇总统计数据
|
||||
$statistics = [
|
||||
'assisted_purchases' => array_sum(array_column($allAdsets, 'assisted_purchases')),
|
||||
'last_clicked_purchases' => array_sum(array_column($allAdsets, 'last_clicked_purchases')),
|
||||
'roas' => $total_spend === 0 ? '-' : round($total_purchases_value / $total_spend, 2),
|
||||
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
|
||||
'clicks' => $total_clicks,
|
||||
'impressions' => array_sum(array_column($allAdsets, 'impressions')),
|
||||
'adds_to_cart' => array_sum(array_column($allAdsets, 'adds_to_cart')),
|
||||
'cost_per_atc' => array_sum(array_column($allAdsets, 'cost_per_atc')),
|
||||
'purchases' => array_sum(array_column($allAdsets, 'purchases')),
|
||||
'purchases_value' => array_sum(array_column($allAdsets, 'purchases_value')),
|
||||
'revenue' => '$' . number_format(array_sum(array_column($allAdsets, 'revenue')), 2) ?: '$0.00', // 格式化收入
|
||||
'total_cost' => '$' . number_format($total_cost, 2) ?: '$0.00', // 格式化总成本
|
||||
'conversion_rate' => '-', // 没有计算逻辑,保持为 '-'
|
||||
'net_profit' => '-', // 没有计算 net_profit,保持为 '-'
|
||||
'net_profit_margin' => '-', // 没有计算 net_profit_margin,保持为 '-'
|
||||
'net_profit_on_ad_spend' => '-', // 没有计算 net_profit_on_ad_spend,保持为 '-'
|
||||
// 计算总的 CTR
|
||||
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
|
||||
];
|
||||
|
||||
// 获取分页数据
|
||||
$adsets = $query->paginate($pageSize, false, ['page' => $page]);
|
||||
|
||||
// 确保数据格式统一
|
||||
$result = array_map(function ($item) {
|
||||
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-'; // 格式化为百分比
|
||||
return [
|
||||
'id' => $item['ad_set_id'],
|
||||
'account_id' => $item['account_id'], // 映射为 customer_id
|
||||
'name' => $item['name'] ?: '-', // 若 name 为空则显示 '-'
|
||||
'status' => $item['status'],
|
||||
'assisted_purchases' => $item['assisted_purchases'],
|
||||
'last_clicked_purchases' => $item['last_clicked_purchases'],
|
||||
'roas' => $item['spend'] == 0 ? '-' : round($item['purchases_value'] / $item['spend'], 2),
|
||||
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
|
||||
'impressions' => $item['impressions'],
|
||||
'clicks' => $item['clicks'],
|
||||
'ctr' => $ctr, // CTR 字段
|
||||
'adds_to_cart' => $item['adds_to_cart'],
|
||||
'cost_per_atc' => $item['cost_per_atc'],
|
||||
'purchases' => $item['purchases'],
|
||||
'purchases_value' => '$' . number_format($item['purchases_value'], 2), // 格式化购买金额
|
||||
'revenue' => '$' . number_format($item['revenue'], 2), // 格式化收入
|
||||
'total_cost' => '$' . number_format($item['total_cost'], 2), // 格式化总成本
|
||||
'conversion_rate' => '-', // 没有提供有效的计算,保持为 '-'
|
||||
'net_profit' => '-', // 没有提供 net_profit 计算,保持为 '-'
|
||||
'net_profit_margin' => '-', // 没有提供 net_profit_margin 计算,保持为 '-'
|
||||
'net_profit_on_ad_spend' => '-', // 没有提供 net_profit_on_ad_spend 计算,保持为 '-'
|
||||
];
|
||||
}, $adsets->items());
|
||||
|
||||
// Pagination 数据
|
||||
$pagination = [
|
||||
'startIndex' => ($page - 1) * $pageSize,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => $adsets->total(),
|
||||
'pageNo' => $adsets->currentPage(),
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => $adsets->lastPage(),
|
||||
];
|
||||
|
||||
return [
|
||||
'pagination' => $pagination,
|
||||
'statistics' => $statistics,
|
||||
'data' => $result,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getAdList($platformType, $customerIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null)
|
||||
{
|
||||
// 检查 customerIds 是否为空,直接返回空结构
|
||||
if (empty($customerIds)) {
|
||||
return [
|
||||
'pagination' => [
|
||||
'startIndex' => 0,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => 0,
|
||||
'pageNo' => $page,
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => 0,
|
||||
],
|
||||
'statistics' => [],
|
||||
'data' => [],
|
||||
];
|
||||
}
|
||||
|
||||
// 动态构建日期条件
|
||||
$dateCondition = '';
|
||||
if ($startDate && $endDate) {
|
||||
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
|
||||
}
|
||||
|
||||
// 基础查询:广告和日数据表联接
|
||||
$query = BpsAdAd::alias('a')
|
||||
->cache(false) // 强制不使用缓存
|
||||
->leftJoin('bps.bps_ads_insights d', "a.ad_id = d.ad_id AND a.platform_type = d.platform AND {$dateCondition}")
|
||||
->field('a.ad_id, a.status as status, a.name, a.account_id,
|
||||
COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
|
||||
COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
|
||||
COALESCE(SUM(d.clicks), 0) as clicks,
|
||||
COALESCE(SUM(d.spend) / 1000000, 0) as spend,
|
||||
COALESCE(SUM(d.impressions), 0) as impressions,
|
||||
COALESCE(SUM(d.adds_to_cart), 0) as adds_to_cart,
|
||||
COALESCE(SUM(d.cost_per_atc), 0) as cost_per_atc,
|
||||
COALESCE(SUM(d.purchases), 0) as purchases,
|
||||
COALESCE(SUM(d.purchases_value), 0) as purchases_value,
|
||||
COALESCE(SUM(d.revenue), 0) as revenue,
|
||||
COALESCE(SUM(d.total_cost), 0) as total_cost,
|
||||
-1 as conversion_rate, -1 as roas, -1 as ctr, -1 as net_profit, -1 as net_profit_margin, -1 as net_profit_on_ad_spend')
|
||||
->group('a.ad_id, a.status, a.account_id, a.name')
|
||||
->where('a.account_id', 'in', $customerIds); // 添加 customerIds 条件
|
||||
|
||||
// 添加关键字过滤条件
|
||||
$query->where(function ($query) use ($keyword, $platformType) {
|
||||
if ($keyword) {
|
||||
$query->where('a.name', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
if ($platformType) {
|
||||
$platformType = (int)$platformType;
|
||||
$query->where('a.platform_type', '=', $platformType);
|
||||
}
|
||||
});
|
||||
|
||||
// 获取所有符合条件的数据(不分页)
|
||||
$allAds = $query->select()->toArray();
|
||||
|
||||
// 汇总统计数据
|
||||
$total_spend = array_sum(array_column($allAds, 'spend'));
|
||||
$total_cost = array_sum(array_column($allAds, 'total_cost'));
|
||||
$total_impressions = array_sum(array_column($allAds, 'impressions'));
|
||||
$total_clicks = array_sum(array_column($allAds, 'clicks'));
|
||||
$total_purchases_value = array_sum(array_column($allAds, 'purchases_value'));
|
||||
|
||||
$statistics = [
|
||||
'assisted_purchases' => array_sum(array_column($allAds, 'assisted_purchases')),
|
||||
'last_clicked_purchases' => array_sum(array_column($allAds, 'last_clicked_purchases')),
|
||||
'roas' => $total_spend === 0 ? '-' : round($total_purchases_value / $total_spend, 2),
|
||||
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
|
||||
'clicks' => $total_clicks,
|
||||
'impressions' => array_sum(array_column($allAds, 'impressions')),
|
||||
'adds_to_cart' => array_sum(array_column($allAds, 'adds_to_cart')),
|
||||
'cost_per_atc' => array_sum(array_column($allAds, 'cost_per_atc')),
|
||||
'purchases' => array_sum(array_column($allAds, 'purchases')),
|
||||
'purchases_value' => array_sum(array_column($allAds, 'purchases_value')),
|
||||
'revenue' => '$' . number_format(array_sum(array_column($allAds, 'revenue')), 2) ?: '$0.00', // 格式化收入
|
||||
'total_cost' => '$' . number_format($total_cost, 2) ?: '$0.00', // 格式化总成本
|
||||
'conversion_rate' => '-', // 没有计算逻辑,保持为 '-'
|
||||
'net_profit' => '-', // 没有计算 net_profit,保持为 '-'
|
||||
'net_profit_margin' => '-', // 没有计算 net_profit_margin,保持为 '-'
|
||||
'net_profit_on_ad_spend' => '-', // 没有计算 net_profit_on_ad_spend,保持为 '-'
|
||||
'ctr' => ($total_impressions > 0) ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-', // 格式化为百分比
|
||||
];
|
||||
|
||||
// 获取分页数据
|
||||
$ads = $query->paginate($pageSize, false, ['page' => $page]);
|
||||
|
||||
// 确保数据格式统一
|
||||
$result = array_map(function ($item) {
|
||||
$ctr = $item['impressions'] > 0 ? number_format(($item['clicks'] / $item['impressions']) * 100, 2) . '%' : '-'; // 格式化为百分比
|
||||
return [
|
||||
'id' => $item['ad_id'],
|
||||
'account_id' => $item['account_id'], // 映射为 customer_id
|
||||
'name' => $item['name'] ?: '-', // 若 name 为空则显示 '-'
|
||||
'status' => $item['status'],
|
||||
'assisted_purchases' => $item['assisted_purchases'],
|
||||
'last_clicked_purchases' => $item['last_clicked_purchases'],
|
||||
'roas' => $item['spend'] == 0 ? '-' : round($item['purchases_value'] / $item['spend'], 2),
|
||||
'spend' => '$' . number_format($item['spend'], 2), // 格式化支出
|
||||
'impressions' => $item['impressions'],
|
||||
'clicks' => $item['clicks'],
|
||||
'ctr' => $ctr, // CTR 字段
|
||||
'adds_to_cart' => $item['adds_to_cart'],
|
||||
'cost_per_atc' => $item['cost_per_atc'],
|
||||
'purchases' => $item['purchases'],
|
||||
'purchases_value' => '$' . number_format($item['purchases_value'], 2), // 格式化购买金额
|
||||
'revenue' => '$' . number_format($item['revenue'], 2), // 格式化收入
|
||||
'total_cost' => '$' . number_format($item['total_cost'], 2), // 格式化总成本
|
||||
'conversion_rate' => '-', // 没有提供有效的计算,保持为 '-'
|
||||
'net_profit' => '-', // 没有提供 net_profit 计算,保持为 '-'
|
||||
'net_profit_margin' => '-', // 没有提供 net_profit_margin 计算,保持为 '-'
|
||||
'net_profit_on_ad_spend' => '-', // 没有提供 net_profit_on_ad_spend 计算,保持为 '-'
|
||||
];
|
||||
}, $ads->items());
|
||||
|
||||
// Pagination 数据
|
||||
$pagination = [
|
||||
'startIndex' => ($page - 1) * $pageSize,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => $ads->total(),
|
||||
'pageNo' => $ads->currentPage(),
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => $ads->lastPage(),
|
||||
];
|
||||
|
||||
return [
|
||||
'pagination' => $pagination,
|
||||
'statistics' => $statistics,
|
||||
'data' => $result,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getThirdUserList($platformType, $userIds, $page, $pageSize, $keyword, $startDate = null, $endDate = null)
|
||||
{
|
||||
// 检查 userIds 是否为空,直接返回空结构
|
||||
if (empty($userIds)) {
|
||||
return [
|
||||
'pagination' => [
|
||||
'startIndex' => 0,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => 0,
|
||||
'pageNo' => $page,
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => 0,
|
||||
],
|
||||
'statistics' => [],
|
||||
'data' => [],
|
||||
];
|
||||
}
|
||||
// dump($userIds);
|
||||
// 动态构建日期条件
|
||||
$dateCondition = '1=1'; // 默认没有日期限制
|
||||
if ($startDate && $endDate) {
|
||||
$dateCondition = "d.date BETWEEN '{$startDate}' AND '{$endDate}'";
|
||||
}
|
||||
// u.user_id as third_user_id, u.third_type, u.facebook_user_id,
|
||||
// a.advertiser_id, a.advertiser_name, a.google_manager, a.google_test_account,
|
||||
// 基础查询:第三方用户和广告商数据表联接
|
||||
$query = ThirdUser::alias('u')
|
||||
->cache(false) // 强制不使用缓存
|
||||
->leftJoin('bps.bps_third_user_advertiser a', "u.id = a.doc_")
|
||||
->field('u.id as user_id,u.third_type,a.advertiser_name,
|
||||
|
||||
COALESCE(SUM(d.assisted_purchases), 0) as assisted_purchases,
|
||||
COALESCE(SUM(d.last_clicked_purchases), 0) as last_clicked_purchases,
|
||||
COALESCE(SUM(d.spend) / 1000000, 0) as spend,
|
||||
COALESCE(SUM(d.impressions), 0) as impressions,
|
||||
COALESCE(SUM(d.clicks), 0) as clicks,
|
||||
COALESCE(SUM(d.adds_to_cart), 0) as adds_to_cart,
|
||||
COALESCE(SUM(d.cost_per_atc), 0) as cost_per_atc,
|
||||
COALESCE(SUM(d.purchases), 0) as purchases,
|
||||
COALESCE(SUM(d.purchases_value), 0) as purchases_value,
|
||||
COALESCE(SUM(d.revenue), 0) as revenue,
|
||||
COALESCE(SUM(d.total_cost), 0) as total_cost')
|
||||
->leftJoin('bps.bps_ads_insights d', "a.advertiser_id = d.account_id AND {$dateCondition}")
|
||||
->where('u.id', 'in', $userIds); // 添加 userIds 条件
|
||||
|
||||
// 添加关键字过滤条件
|
||||
$query->where(function ($query) use ($keyword, $platformType) {
|
||||
if ($keyword) {
|
||||
$query->where('a.advertiser_name', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
if ($platformType) {
|
||||
$query->where('d.platform', '=', $platformType);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加 GROUP BY 子句
|
||||
// $query->group('u.id, u.user_id, u.third_type, u.facebook_user_id,
|
||||
// a.advertiser_id, a.advertiser_name, a.google_manager, a.google_test_account');
|
||||
$query->group('u.id,u.third_type,a.advertiser_name');
|
||||
|
||||
// 调试打印 SQL
|
||||
|
||||
|
||||
// 获取所有符合条件的数据(不分页)
|
||||
$allUsers = $query->select()->toArray();
|
||||
// dump($query->getLastSql());
|
||||
//
|
||||
// dump($allUsers);
|
||||
|
||||
// 汇总统计数据
|
||||
$total_spend = array_sum(array_column($allUsers, 'spend'));
|
||||
$total_impressions = array_sum(array_column($allUsers, 'impressions'));
|
||||
$total_clicks = array_sum(array_column($allUsers, 'clicks'));
|
||||
$total_adds_to_cart = array_sum(array_column($allUsers, 'adds_to_cart'));
|
||||
$total_cost_per_atc = array_sum(array_column($allUsers, 'cost_per_atc'));
|
||||
$total_purchases = array_sum(array_column($allUsers, 'purchases'));
|
||||
$total_purchases_value = array_sum(array_column($allUsers, 'purchases_value'));
|
||||
$total_revenue = array_sum(array_column($allUsers, 'revenue'));
|
||||
$total_cost = array_sum(array_column($allUsers, 'total_cost'));
|
||||
|
||||
// 计算 ROAS
|
||||
$roas = $total_spend === 0 ? '-' : round($total_purchases_value / $total_spend, 2);
|
||||
|
||||
// 计算 CTR
|
||||
$ctr = $total_impressions > 0 ? number_format(($total_clicks / $total_impressions) * 100, 2) . '%' : '-';
|
||||
|
||||
// 汇总统计字段初始化
|
||||
$statistics = [
|
||||
'assisted_purchases' => 0,
|
||||
'last_clicked_purchases' => 0,
|
||||
'roas' => $total_spend === 0 ? '-' : round($total_purchases_value / $total_spend, 2),
|
||||
'amount_spend' => '$' . number_format($total_spend, 2) ?: '$0.00', // 格式化支出
|
||||
'clicks' => $total_clicks,
|
||||
'impressions' => $total_impressions,
|
||||
'adds_to_cart' => $total_adds_to_cart,
|
||||
'cost_per_atc' => $total_cost_per_atc,
|
||||
'purchases' => $total_purchases,
|
||||
'purchases_value' => '$' . number_format($total_purchases_value, 2) ?: '$0.00', // 格式化销售额$total_purchases_value,
|
||||
'revenue' => '$' . number_format($total_revenue, 2) ?: '$0.00', // 格式化收入
|
||||
'total_cost' => '$' . number_format($total_cost, 2) ?: '$0.00', // 格式化总成本
|
||||
'conversion_rate' => '-', // 没有计算逻辑,保持为 '-'
|
||||
'net_profit' => '-', // 没有提供 net_profit,保持为 '-'
|
||||
'net_profit_margin' => '-', // 没有提供 net_profit_margin,保持为 '-'
|
||||
'net_profit_on_ad_spend' => '-', // 没有提供 net_profit_on_ad_spend,保持为 '-'
|
||||
'ctr' => $ctr, // CTR 字段
|
||||
];
|
||||
|
||||
// 获取分页数据
|
||||
$users = $query->paginate($pageSize, false, ['page' => $page]);
|
||||
|
||||
// 按照 third_user_id 聚合统计字段
|
||||
$aggregatedUsers = [];
|
||||
|
||||
// 遍历每个用户的统计数据
|
||||
foreach ($users->items() as $item) {
|
||||
$thirdUserId = $item['user_id'];
|
||||
|
||||
if (!isset($aggregatedUsers[$thirdUserId])) {
|
||||
$aggregatedUsers[$thirdUserId] = [
|
||||
'user_id' => $item['user_id'],
|
||||
'third_type' => $item['third_type'],
|
||||
'advertiser_name' => $item['advertiser_name'],
|
||||
'assisted_purchases' => 0,
|
||||
'last_clicked_purchases' => 0,
|
||||
'roas' => 0,
|
||||
'spend' => 0,
|
||||
'impressions' => 0,
|
||||
'clicks' => 0,
|
||||
'adds_to_cart' => 0,
|
||||
'cost_per_atc' => 0,
|
||||
'purchases' => 0,
|
||||
'purchases_value' => 0,
|
||||
'revenue' => 0,
|
||||
'total_cost' => 0,
|
||||
'net_profit' => '-', // 没有提供 net_profit,保持为 '-'
|
||||
'net_profit_margin' => '-', // 没有提供 net_profit_margin,保持为 '-'
|
||||
'net_profit_on_ad_spend' => '-', // 没有提供 net_profit_on_ad_spend,保持为 '-'
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
// 汇总统计
|
||||
$aggregatedUsers[$thirdUserId]['spend'] += $item['spend'];
|
||||
$aggregatedUsers[$thirdUserId]['impressions'] += $item['impressions'];
|
||||
$aggregatedUsers[$thirdUserId]['clicks'] += $item['clicks'];
|
||||
$aggregatedUsers[$thirdUserId]['adds_to_cart'] += $item['adds_to_cart'];
|
||||
$aggregatedUsers[$thirdUserId]['cost_per_atc'] += $item['cost_per_atc'];
|
||||
$aggregatedUsers[$thirdUserId]['purchases'] += $item['purchases'];
|
||||
$aggregatedUsers[$thirdUserId]['purchases_value'] += $item['purchases_value'];
|
||||
$aggregatedUsers[$thirdUserId]['revenue'] += $item['revenue'];
|
||||
$aggregatedUsers[$thirdUserId]['total_cost'] += $item['total_cost'];
|
||||
$aggregatedUsers[$thirdUserId]['assisted_purchases'] += $item['assisted_purchases'];
|
||||
$aggregatedUsers[$thirdUserId]['last_clicked_purchases'] += $item['last_clicked_purchases'];
|
||||
}
|
||||
|
||||
// 计算统计口径字段:ROAS, CTR等
|
||||
foreach ($aggregatedUsers as $userId => $data) {
|
||||
$total_spend = $data['spend'];
|
||||
$total_purchases_value = $data['purchases_value'];
|
||||
$total_revenue = $data['revenue'];
|
||||
$total_cost = $data['total_cost'];
|
||||
$total_clicks = $data['clicks'];
|
||||
$total_impressions = $data['impressions'];
|
||||
// $total_adds_to_cart = $data['adds_to_cart'];
|
||||
// $total_cost_per_atc = $data['cost_per_atc'];
|
||||
// $total_purchases = $data['purchases'];
|
||||
// $total_assisted_purchases = $data['assisted_purchases'];
|
||||
// $total_last_clicked_purchases = $data['last_clicked_purchases'];
|
||||
|
||||
// 计算 ROAS, CTR 和其他需要的字段
|
||||
$aggregatedUsers[$userId]['roas'] = $total_spend === 0 ? '-' : round($total_purchases_value / $total_spend, 2);
|
||||
$aggregatedUsers[$userId]['amount_spend'] = '$' . number_format($total_spend, 2) ?: '$0.00';
|
||||
$aggregatedUsers[$userId]['purchases_value'] = '$' . number_format($total_purchases_value, 2) ?: '$0.00';
|
||||
$aggregatedUsers[$userId]['revenue'] = '$' . number_format($total_revenue, 2) ?: '$0.00';
|
||||
$aggregatedUsers[$userId]['total_cost'] = '$' . number_format($total_cost, 2) ?: '$0.00';
|
||||
$aggregatedUsers[$userId]['conversion_rate'] = $total_impressions === 0 ? '-' : round(($total_clicks / $total_impressions) * 100, 2) . '%';
|
||||
$aggregatedUsers[$userId]['ctr'] = $total_impressions === 0 ? '-' : round(($total_clicks / $total_impressions) * 100, 2) . '%';
|
||||
}
|
||||
|
||||
// 最终分页信息
|
||||
$pagination = [
|
||||
'startIndex' => ($page - 1) * $pageSize,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => $users->total(),
|
||||
'pageNo' => $page,
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => ceil($users->total() / $pageSize),
|
||||
];
|
||||
|
||||
return [
|
||||
'pagination' => $pagination,
|
||||
'statistics' => $statistics,
|
||||
'data' => $aggregatedUsers,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取广告资产报告
|
||||
*
|
||||
* @param string $keyword 关键词(广告素材名称模糊搜索)
|
||||
* @param string $dateRange 日期范围:Today, Yesterday, Last Week, Last Month, Last Year
|
||||
* @param string|null $startDate 起始日期(可选)
|
||||
* @param string|null $endDate 结束日期(可选)
|
||||
* @param int $page 当前页码
|
||||
* @param int $pageSize 每页数量
|
||||
* @return array
|
||||
*/
|
||||
public function getAssetConversionData($customerIds, $page, $pageSize, $keyword, $dateRange, $startDate = null, $endDate = null)
|
||||
{
|
||||
|
||||
$isSameMonth = true; // 判断是否跨月
|
||||
$currentMonth = date('Y-m'); //一个月内的统计需要确定是什么月份。
|
||||
if ($startDate && $endDate) {
|
||||
// 比较日期的年月是否相同,若不同则为跨月
|
||||
$isSameMonth = date('Y-m', strtotime($startDate)) === date('Y-m', strtotime($endDate));
|
||||
if ($isSameMonth) {
|
||||
$currentMonth = date('Y-m', strtotime($startDate));
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 获取符合条件的 ad_id 列表,并且筛选 AssetRelation 的 date 字段
|
||||
$adIdsQuery = AssetRelation::alias('r')
|
||||
->leftJoin('bps.bps_google_ads_asset a', 'r.asset_id = a.asset_id') // 关联资产表
|
||||
->where(function ($query) use ($keyword) {
|
||||
if ($keyword) {
|
||||
$query->where('a.asset_name', 'like', '%' . $keyword . '%'); // 关键词模糊匹配
|
||||
}
|
||||
});
|
||||
|
||||
// 如果提供了 customerIds,增加查询条件
|
||||
if (!empty($customerIds)) {
|
||||
$adIdsQuery->whereIn('a.customer_id', $customerIds); // 添加 customer_id 的查询约束
|
||||
} else {
|
||||
return [
|
||||
'data' => [],
|
||||
'chat_1_data' => [], // 返回按月汇总的 chat_data
|
||||
'total' => 0,
|
||||
'statistics' => [],
|
||||
'pagination' => [
|
||||
'startIndex' => 0,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => 0,
|
||||
'pageNo' => 1,
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => 1
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// 日期范围处理,筛选 AssetRelation 的 date 字段
|
||||
if ($startDate && $endDate) {
|
||||
$adIdsQuery->whereBetween('r.date', [$startDate, $endDate]);
|
||||
} else {
|
||||
switch ($dateRange) {
|
||||
case 'Today':
|
||||
$adIdsQuery->where('r.date', '=', date('Y-m-d'));
|
||||
$currentMonth = date('Y-m');
|
||||
$isSameMonth = true;
|
||||
break;
|
||||
case 'Yesterday':
|
||||
$adIdsQuery->where('r.date', '=', date('Y-m-d', strtotime('-1 day')));
|
||||
$currentMonth = date('Y-m', strtotime('-1 day'));
|
||||
$isSameMonth = true;
|
||||
break;
|
||||
case 'Last Week':
|
||||
$adIdsQuery->where('r.date', '>=', date('Y-m-d', strtotime('-1 week')));
|
||||
// 比较日期的年月是否相同,若不同则为跨月
|
||||
$isSameMonth = date('Y-m', strtotime('-1 week')) === date('Y-m');
|
||||
break;
|
||||
case 'Last Month':
|
||||
$adIdsQuery->where('r.date', '>=', date('Y-m-d', strtotime('-1 month')));
|
||||
$isSameMonth = false;
|
||||
break;
|
||||
case 'Last Year':
|
||||
$adIdsQuery->where('r.date', '>=', date('Y-m-d', strtotime('-1 year')));
|
||||
$isSameMonth = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 获取唯一的 ad_id 列表
|
||||
$adIds = $adIdsQuery->distinct(true)->column('r.ad_id');
|
||||
// dump($adIds);return($adIds);
|
||||
|
||||
// 2. 根据 ad_id 和日期范围去查询 DayData 表,按 ad_id 和日期聚合统计
|
||||
$dayDataQuery = DayData::alias('d')
|
||||
// ->leftJoin('bps.bps_google_ads_campaign c', 'd.campaign_id = c.campaign_id') // 关联广告系列表(如果有)
|
||||
// ->leftJoin('bps.bps_google_ads_ad_group ag', 'd.ad_group_id = ag.ad_group_id') // 关联广告组表(如果有)
|
||||
->whereIn('d.ad_id', $adIds) // 使用 ad_id 过滤
|
||||
->where(function ($query) use ($startDate, $endDate, $dateRange) {
|
||||
// 日期范围的筛选
|
||||
if ($startDate && $endDate) {
|
||||
$query->whereBetween('d.date', [$startDate, $endDate]);
|
||||
} else {
|
||||
switch ($dateRange) {
|
||||
case 'Today':
|
||||
$query->where('d.date', '=', date('Y-m-d'));
|
||||
break;
|
||||
case 'Yesterday':
|
||||
$query->where('d.date', '=', date('Y-m-d', strtotime('-1 day')));
|
||||
break;
|
||||
case 'Last Week':
|
||||
$query->where('d.date', '>=', date('Y-m-d', strtotime('-1 week')));
|
||||
break;
|
||||
case 'Last Month':
|
||||
$query->where('d.date', '>=', date('Y-m-d', strtotime('-1 month')));
|
||||
break;
|
||||
case 'Last Year':
|
||||
$query->where('d.date', '>=', date('Y-m-d', strtotime('-1 year')));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 如果跨月,按月聚合;如果不跨月,按 ad_id 聚合
|
||||
if (!$isSameMonth) {
|
||||
$dayDataQuery->group("d.month,d.ad_id,d.ad_name, d.ad_resource_name, d.ad_group_id, d.campaign_id, d.customer_id"); // 按月聚合
|
||||
$dayDataQuery->field([
|
||||
'd.ad_id',
|
||||
'd.month',
|
||||
// ThinkDb::raw("TO_CHAR(d.date, 'YYYY-MM') AS month"), // 格式化为 YYYY-MM 格式
|
||||
ThinkDb::raw('SUM(d.cost_micros) / 1000000 AS total_spend'),
|
||||
ThinkDb::raw('SUM(d.conversions_value) AS total_conversions_value'),
|
||||
ThinkDb::raw('SUM(d.conversions) AS total_conversions'),
|
||||
ThinkDb::raw('SUM(d.impressions) AS total_impressions'),
|
||||
ThinkDb::raw('SUM(d.clicks) AS total_clicks')
|
||||
]);
|
||||
} else {
|
||||
$dayDataQuery->group('d.ad_id,d.ad_name, d.ad_resource_name, d.ad_group_id, d.campaign_id, d.customer_id'); // 按 ad_id 聚合
|
||||
$dayDataQuery->field([
|
||||
'd.ad_id',
|
||||
ThinkDb::raw('SUM(d.cost_micros) / 1000000 AS total_spend'),
|
||||
ThinkDb::raw('SUM(d.conversions_value) AS total_conversions_value'),
|
||||
ThinkDb::raw('SUM(d.conversions) AS total_conversions'),
|
||||
ThinkDb::raw('SUM(d.impressions) AS total_impressions'),
|
||||
ThinkDb::raw('SUM(d.clicks) AS total_clicks')
|
||||
]);
|
||||
}
|
||||
|
||||
// 获取聚合数据,不需要再传递字段给 select()
|
||||
$aggregatedData = $dayDataQuery->select();
|
||||
// dump($aggregatedData);return($aggregatedData);
|
||||
|
||||
// 3. 获取与 ad_id 关联的 asset_id 以及相关数据
|
||||
$assetRelationsQuery = AssetRelation::whereIn('ad_id', $adIds)
|
||||
->whereBetween('date', [$startDate, $endDate]) // 加上 AssetRelation 的 date 过滤
|
||||
->field(['asset_id', 'ad_id'])->select();
|
||||
// dump($assetRelationsQuery);return($assetRelationsQuery);
|
||||
// 4. 汇总每个 asset_id 下的所有 ad_id 的聚合数据
|
||||
$assetSummaryData = [];
|
||||
|
||||
foreach ($assetRelationsQuery as $assetRelation) {
|
||||
// 从聚合数据中找到当前 ad_id 的相关数据
|
||||
// $adStats = $aggregatedData->where('ad_id', $assetRelation->ad_id)->first();
|
||||
// 获取该 ad_id 的所有聚合数据(可能有多条记录)
|
||||
$adStatsCollection = $aggregatedData->where('ad_id', $assetRelation->ad_id);
|
||||
// dump($adStatsCollection);return($adStatsCollection);
|
||||
if (!$adStatsCollection->isEmpty()) {
|
||||
// 如果该 ad_id 有对应的聚合数据,初始化资产汇总数据
|
||||
if (!isset($assetSummaryData[$assetRelation->asset_id])) {
|
||||
|
||||
|
||||
$assetSummaryData[$assetRelation->asset_id] = [
|
||||
'asset_id' => $assetRelation->asset_id,
|
||||
'asset_name' => '-', // 获取 asset_name
|
||||
'asset_type' => 0, // 获取 asset_type
|
||||
'asset_url' => '-', // 获取 asset_url
|
||||
'total_spend' => 0,
|
||||
'total_conversions_value' => 0,
|
||||
'total_conversions' => 0,
|
||||
'total_impressions' => 0,
|
||||
'ad_count' => 0,
|
||||
'monthly_data' => [], // 按月存储数据
|
||||
];
|
||||
}
|
||||
|
||||
// 遍历该 ad_id 所有的统计数据
|
||||
foreach ($adStatsCollection as $adStats) {
|
||||
// 获取当前月份
|
||||
if ($isSameMonth) {
|
||||
$month = $currentMonth; // 格式化为 YYYY-MM
|
||||
} else {
|
||||
$month = $adStats->month; // 格式化日期为 'YYYY-MM'
|
||||
}
|
||||
// 获取 asset 相关信息,通过模型查询 Asset
|
||||
$asset = Asset::find($assetRelation->asset_id); // 根据 asset_id 查找对应的 Asset 数据
|
||||
// 累加该 ad_id 的统计数据到对应的 asset_id 汇总
|
||||
$assetSummaryData[$assetRelation->asset_id]['asset_name'] = $asset->asset_name;
|
||||
$assetSummaryData[$assetRelation->asset_id]['asset_type'] = $asset->asset_type;
|
||||
$assetSummaryData[$assetRelation->asset_id]['asset_url'] = $asset->asset_url;
|
||||
$assetSummaryData[$assetRelation->asset_id]['total_spend'] += $adStats->total_spend;
|
||||
$assetSummaryData[$assetRelation->asset_id]['total_conversions_value'] += $adStats->total_conversions_value;
|
||||
$assetSummaryData[$assetRelation->asset_id]['total_conversions'] += $adStats->total_conversions;
|
||||
$assetSummaryData[$assetRelation->asset_id]['total_impressions'] += $adStats->total_impressions;
|
||||
$assetSummaryData[$assetRelation->asset_id]['ad'][] = $adStats->ad_id; // 存储 ad_id下一步统计数量
|
||||
|
||||
// 按月分开存储每个月的 spend 和 ROAS
|
||||
if (!isset($assetSummaryData[$assetRelation->asset_id]['monthly_data'][$month])) {
|
||||
$assetSummaryData[$assetRelation->asset_id]['monthly_data'][$month] = [
|
||||
'month' => $month,
|
||||
'spend' => 0,
|
||||
'conversions_value' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
// 累加每个月的数据
|
||||
$assetSummaryData[$assetRelation->asset_id]['monthly_data'][$month]['spend'] += $adStats->total_spend;
|
||||
$assetSummaryData[$assetRelation->asset_id]['monthly_data'][$month]['conversions_value'] += $adStats->total_conversions_value; // 避免除以零
|
||||
}
|
||||
}
|
||||
}
|
||||
//return $assetSummaryData;
|
||||
// 5. 格式化输出数据
|
||||
// 生成最终输出的数据
|
||||
$resultData = [];
|
||||
$chat_data = [];
|
||||
$statistics = $this->initializeStatistics(); // Initialize statistics before processing
|
||||
|
||||
foreach ($assetSummaryData as $assetId => $data) {
|
||||
// 计算 ROAS
|
||||
$roas = $data['total_spend'] ? ($data['total_conversions_value'] / $data['total_spend']) : 0;
|
||||
|
||||
// 合并月度数据到总数据
|
||||
$resultData[] = [
|
||||
'creative_id' => $assetId,
|
||||
'creative' => $data['asset_name'],
|
||||
'creative_type' => $data['asset_type'],
|
||||
'creative_url' => $data['asset_url'],
|
||||
'spend' => '$' . number_format($data['total_spend'], 2),
|
||||
'purchase_value' => '-',
|
||||
'roas' => number_format($roas, 2) . 'X',
|
||||
'cpa' => '-',
|
||||
'cpc_link_click' => '-',
|
||||
'cpm' => '-',
|
||||
'cpc_all' => '-',
|
||||
'aov' => '-',
|
||||
'click_to_atc_ratio' => '-',
|
||||
'atc_to_purchase_ratio' => '-',
|
||||
'purchases' => '-',
|
||||
'first_frame_retention' => '-',
|
||||
'thumbstop' => '-',
|
||||
'ctr_outbound' => '-',
|
||||
'click_to_purchase' => '-',
|
||||
'ctr_all' => '-',
|
||||
'video_plays_25_rate' => '-',
|
||||
'video_plays_50_rate' => '-',
|
||||
'video_plays_75_rate' => '-',
|
||||
'video_plays_100_rate' => '-',
|
||||
'hold_rate' => '-',
|
||||
'total_conversions_value' => '$' . number_format($data['total_conversions_value'], 2),
|
||||
'total_conversions' => $data['total_conversions'],
|
||||
'total_impressions' => $data['total_impressions'],
|
||||
'ad_count' => count($data['ad']),
|
||||
|
||||
// 'monthly_data' => $data['monthly_data'], // 合并月度数据
|
||||
];
|
||||
|
||||
// 计算每个月的汇总数据,用于 chat_data
|
||||
foreach ($data['monthly_data'] as $month => $monthlyData) {
|
||||
// 如果 chat_data 中已有该月的数据,则累加
|
||||
if (!isset($chat_data[$month])) {
|
||||
$chat_data[$month] = [
|
||||
'month' => $month,
|
||||
'total_spend' => 0,
|
||||
'total_conversions_value' => 0,
|
||||
'roas' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
// 累加该月的 spend 和 conversions_value
|
||||
$chat_data[$month]['total_spend'] += $monthlyData['spend'];
|
||||
$chat_data[$month]['total_conversions_value'] += $monthlyData['conversions_value'];
|
||||
|
||||
// Aggregate statistics for overall summary
|
||||
$statistics['spend'] += $monthlyData['spend'];
|
||||
$statistics['conversions_value'] += $monthlyData['conversions_value'];
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 计算每个月的总 ROAS,ROAS = 总的转换值 / 总的支出
|
||||
foreach ($chat_data as $month => $data) {
|
||||
// 计算 ROAS
|
||||
$totalSpend = $data['total_spend'];
|
||||
$totalConversionsValue = $data['total_conversions_value'];
|
||||
|
||||
// 如果有支出数据,计算 ROAS
|
||||
$chat_data[$month]['roas'] = $totalSpend ? ($totalConversionsValue / $totalSpend) : 0;
|
||||
// 格式化 ROAS 为倍数
|
||||
$chat_data[$month]['roas'] = round($chat_data[$month]['roas'], 2);
|
||||
}
|
||||
|
||||
// 计算整体的 ROAS
|
||||
$statistics['spend'] = '$' . number_format($statistics['spend'], 2);
|
||||
$statistics['roas'] = $statistics['spend'] > 0 ? ($statistics['conversions_value'] / $statistics['spend']) : 0;
|
||||
|
||||
|
||||
// 返回分页数据
|
||||
$totalItems = count($assetSummaryData);
|
||||
$totalPages = ceil($totalItems / $pageSize);
|
||||
$startIndex = ($page - 1) * $pageSize;
|
||||
// 截取返回的数据
|
||||
// $pagedData = array_slice($assetSummaryData, $startIndex, $pageSize);
|
||||
$resultDataPaginated = array_slice($resultData, ($page - 1) * $pageSize, $pageSize);
|
||||
|
||||
return [
|
||||
'pagination' => [
|
||||
'startIndex' => $startIndex,
|
||||
'maxResults' => $pageSize,
|
||||
'count' => $totalItems,
|
||||
'pageNo' => $page,
|
||||
'pageSize' => $pageSize,
|
||||
'pages' => $totalPages
|
||||
],
|
||||
'chat_1_data' => array_values($chat_data), // 返回按月汇总的 chat_data
|
||||
'data' => $resultDataPaginated,
|
||||
'statistics' => $statistics
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化统计数据
|
||||
*/
|
||||
private function initializeStatistics()
|
||||
{
|
||||
return [
|
||||
'conversions_value' => 0,
|
||||
'spend' => 0,
|
||||
'purchase_value' => '-', // 可根据需求进一步计算
|
||||
'roas' => 0, // 可以根据需要计算总体 ROAS
|
||||
'cpa' => '-',
|
||||
'cpc_link_click' => '-',
|
||||
'cpm' => '-',
|
||||
'cpc_all' => '-',
|
||||
'aov' => '-',
|
||||
'click_to_atc_ratio' => '-',
|
||||
'atc_to_purchase_ratio' => '-',
|
||||
'purchases' => '-',
|
||||
'first_frame_retention' => '-',
|
||||
'thumbstop' => '-',
|
||||
'ctr_outbound' => '-',
|
||||
'click_to_purchase' => '-',
|
||||
'ctr_all' => '-',
|
||||
'video_plays_25_rate' => '-',
|
||||
'video_plays_50_rate' => '-',
|
||||
'video_plays_75_rate' => '-',
|
||||
'video_plays_100_rate' => '-',
|
||||
'hold_rate' => '-'
|
||||
// Add other stats as necessary
|
||||
];
|
||||
}
|
||||
}
|
271
app/service/BpsAdAccountService.php
Normal file
271
app/service/BpsAdAccountService.php
Normal file
@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use think\facade\Db as ThinkDb;
|
||||
use app\model\ThirdUserAdvertiser;
|
||||
use app\model\ThirdUser;
|
||||
|
||||
//use Webman\RedisQueue\Redis;
|
||||
use Webman\RedisQueue\Client as QueueClient;
|
||||
use app\event\GoogleAdsCustomers;
|
||||
|
||||
class BpsAdAccountService
|
||||
{
|
||||
|
||||
public function getCustomerList($third_user_id = 0)
|
||||
{
|
||||
$tableName = 'bps_third_user_advertiser';
|
||||
$tableName = getenv('DB_PG_SCHEMA') ? getenv('DB_PG_SCHEMA') . '.' . $tableName : 'bps' . $tableName;
|
||||
|
||||
$sql = "SELECT * FROM {$tableName} WHERE doc_ = :third_user_id";
|
||||
$data = ['third_user_id' => $third_user_id];
|
||||
|
||||
return ThinkDb::query($sql, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取全部Google广告账号数据
|
||||
*
|
||||
*/
|
||||
public function getGoogleAdAccounts($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$refreshToken = $options['refresh_token'];
|
||||
// 获取符合条件的客户ID数组
|
||||
$customers = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'google') // 筛选 third_type 为 google 的记录
|
||||
->where('tu.access_token', $refreshToken) // 筛选 third_type 为 google 的记录
|
||||
->field('CAST(tua.advertiser_id AS BIGINT) as account_id,tua.google_login_customer_id as login_customer_id,tua.google_test_account as test_account,tua.google_manager as manager, tu.access_token as refresh_token') // 获取 advertiser_id 字段
|
||||
->select(); // 执行查询
|
||||
} else {
|
||||
// 获取符合条件的客户ID数组
|
||||
$customers = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'google') // 筛选 third_type 为 google 的记录
|
||||
->field('CAST(tua.advertiser_id AS BIGINT) as account_id,tua.google_login_customer_id as login_customer_id,tua.google_test_account as test_account,tua.google_manager as manager, tu.access_token as refresh_token') // 获取 advertiser_id 字段
|
||||
->select(); // 执行查询
|
||||
}
|
||||
|
||||
|
||||
// 如果没有找到符合条件的广告主,抛出异常
|
||||
if ($customers->isEmpty()) {
|
||||
return [];
|
||||
// throw new ApiException('No customers found for google third type');
|
||||
}
|
||||
|
||||
// 转换为简单的数组(提取 advertiser_id)
|
||||
return $customers->toArray();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取全部meta广告账号数据
|
||||
*
|
||||
*/
|
||||
public function getMetaAdAccounts($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$refreshToken = $options['refresh_token'];
|
||||
// 获取符合条件的客户ID数组
|
||||
$customers = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'facebook') // 筛选 third_type 为 google 的记录
|
||||
->where('tu.access_token', $refreshToken) // 筛选 third_type 为 google 的记录
|
||||
->field('CAST(tua.advertiser_id AS BIGINT) as account_id, tu.access_token as refresh_token') // 获取 advertiser_id 字段
|
||||
->select(); // 执行查询
|
||||
} else {
|
||||
// 获取符合条件的客户ID数组
|
||||
$customers = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'facebook') // 筛选 third_type 为 google 的记录
|
||||
->field('CAST(tua.advertiser_id AS BIGINT) as account_id, tu.access_token as refresh_token') // 获取 advertiser_id 字段
|
||||
->select(); // 执行查询
|
||||
}
|
||||
|
||||
|
||||
// 如果没有找到符合条件的广告主,抛出异常
|
||||
if ($customers->isEmpty()) {
|
||||
return [];
|
||||
// throw new ApiException('No customers found for google third type');
|
||||
}
|
||||
|
||||
// 转换为简单的数组(提取 advertiser_id)
|
||||
return $customers->toArray();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取全部meta广告账号数据
|
||||
*
|
||||
*/
|
||||
public function getTiktokAdAccounts($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$refreshToken = $options['refresh_token'];
|
||||
// 获取符合条件的客户ID数组
|
||||
$customers = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'tiktok') // 筛选 third_type 为 google 的记录
|
||||
->where('tu.access_token', $refreshToken) // 筛选 third_type 为 google 的记录
|
||||
->field('CAST(tua.advertiser_id AS BIGINT) as account_id, tu.access_token as refresh_token') // 获取 advertiser_id 字段
|
||||
->select(); // 执行查询
|
||||
} else {
|
||||
// 获取符合条件的客户ID数组
|
||||
$customers = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'tiktok') // 筛选 third_type 为 google 的记录
|
||||
->field('CAST(tua.advertiser_id AS BIGINT) as account_id, tu.access_token as refresh_token') // 获取 advertiser_id 字段
|
||||
->select(); // 执行查询
|
||||
}
|
||||
|
||||
|
||||
// 如果没有找到符合条件的广告主,抛出异常
|
||||
if ($customers->isEmpty()) {
|
||||
return [];
|
||||
// throw new ApiException('No customers found for google third type');
|
||||
}
|
||||
|
||||
// 转换为简单的数组(提取 advertiser_id)
|
||||
return $customers->toArray();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取全部meta广告账号数据
|
||||
*
|
||||
*/
|
||||
public function getAllAdAccounts($options = [])
|
||||
{
|
||||
// 获取符合条件的客户ID数组
|
||||
$customers = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.user_id', $options['uid']) // 筛选 third_type 为 google 的记录
|
||||
->field('CAST(tua.advertiser_id AS BIGINT) as account_id, tu.access_token as refresh_token') // 获取 advertiser_id 字段
|
||||
->select(); // 执行查询
|
||||
|
||||
|
||||
// 如果没有找到符合条件的广告主,抛出异常
|
||||
if ($customers->isEmpty()) {
|
||||
return [];
|
||||
// throw new ApiException('No customers found for google third type');
|
||||
}
|
||||
|
||||
// 转换为简单的数组(提取 advertiser_id)
|
||||
return $customers->toArray();
|
||||
|
||||
}
|
||||
|
||||
// 获取 Facebook 第三方用户数据
|
||||
public function getMetaThirdUsers($options = [])
|
||||
{
|
||||
$uid = $options['uid'] ?? null;
|
||||
|
||||
if ($uid) {
|
||||
// 根据 UID 获取符合条件的用户数据
|
||||
$users = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'facebook') // 筛选 third_type 为 facebook 的记录
|
||||
->where('tu.user_id', $uid) // 使用 UID 筛选用户
|
||||
->field('tu.id, tu.access_token as refresh_token') // 获取相关字段
|
||||
->select();
|
||||
} else {
|
||||
// 获取符合条件的用户数据(如果没有 UID)
|
||||
$users = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps.bps_third_user 表
|
||||
->where('tu.third_type', 'facebook') // 筛选 third_type 为 facebook 的记录
|
||||
->field('tu.id, tu.access_token as refresh_token') // 获取相关字段
|
||||
->select();
|
||||
}
|
||||
|
||||
// 如果没有找到符合条件的用户,返回空数组
|
||||
if ($users->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $users->toArray();
|
||||
}
|
||||
|
||||
|
||||
// 获取 Google 第三方用户数据
|
||||
public function getGoogleThirdUsers($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$refreshToken = $options['refresh_token'];
|
||||
// 获取符合条件的用户数据
|
||||
$users = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'google') // 筛选 third_type 为 google 的记录
|
||||
->where('tu.access_token', $refreshToken) // 筛选 third_type 为 google 的记录
|
||||
->field('tu.id, tu.access_token as refresh_token') // 获取相关字段
|
||||
->select();
|
||||
} else {
|
||||
// 获取符合条件的用户数据
|
||||
$users = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'google') // 筛选 third_type 为 google 的记录
|
||||
->field('tu.id, tu.access_token as refresh_token') // 获取相关字段
|
||||
->select();
|
||||
}
|
||||
|
||||
// 如果没有找到符合条件的用户,返回空数组
|
||||
if ($users->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $users->toArray();
|
||||
}
|
||||
|
||||
// 获取 TikTok 第三方用户数据
|
||||
public function getTiktokThirdUsers($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$refreshToken = $options['refresh_token'];
|
||||
// 获取符合条件的用户数据
|
||||
$users = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'tiktok') // 筛选 third_type 为 tiktok 的记录
|
||||
->where('tu.access_token', $refreshToken) // 筛选 third_type 为 tiktok 的记录
|
||||
->field('tu.id, tu.access_token as refresh_token') // 获取相关字段
|
||||
->select();
|
||||
} else {
|
||||
// 获取符合条件的用户数据
|
||||
$users = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.third_type', 'tiktok') // 筛选 third_type 为 tiktok 的记录
|
||||
->field('tu.id, tu.access_token as refresh_token') // 获取相关字段
|
||||
->select();
|
||||
}
|
||||
|
||||
// 如果没有找到符合条件的用户,返回空数组
|
||||
if ($users->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $users->toArray();
|
||||
}
|
||||
|
||||
// 获取所有平台的第三方用户数据
|
||||
public function getAllThirdUsers($options = [])
|
||||
{
|
||||
$userId = $options['uid']; // 获取用户ID
|
||||
|
||||
// 获取符合条件的用户数据
|
||||
$users = ThirdUserAdvertiser::alias('tua')
|
||||
->join('bps.bps_third_user tu', 'tua.doc_ = tu.id') // 连接 bps_third_user 表
|
||||
->where('tu.user_id', $userId) // 筛选 user_id 的记录
|
||||
->field('tu.id, tu.access_token as refresh_token') // 获取相关字段
|
||||
->select();
|
||||
|
||||
// 如果没有找到符合条件的用户,返回空数组
|
||||
if ($users->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
//dump($users->toArray());
|
||||
return $users->toArray();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -278,7 +278,7 @@ class GoogleOAuthService
|
||||
*/
|
||||
public function getGoogleAdCustomers($options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
if (!empty($options['refresh_token'])) {
|
||||
$refreshToken = $options['refresh_token'];
|
||||
// 获取符合条件的客户ID数组
|
||||
$customers = ThirdUserAdvertiser::alias('tua')
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
//use app\controller\IndexController;
|
||||
use app\controller\AdController;
|
||||
use app\controller\BpsAdController;
|
||||
use app\controller\OAuthController;
|
||||
use app\controller\CustomerController;
|
||||
use app\controller\GoogleAdsController;
|
||||
@ -30,6 +31,60 @@ use Webman\Route;
|
||||
// });
|
||||
//});
|
||||
|
||||
//三平台合并后的路由2025-1-9
|
||||
Route::group('/marketing', function () {
|
||||
Route::group('/v18', function () {
|
||||
Route::group('/ad', function () {
|
||||
Route::post('/list', [BpsAdController::class, 'listAds']);
|
||||
// Route::post('/export', [BpsAdController::class, 'exportAdsToExcel']);
|
||||
// Route::group('/status', function () {
|
||||
// Route::post('/update', [BpsAdController::class, 'updateAdStatus']);
|
||||
//// Route::post('/get', [AdController::class, 'getAdStatus']);
|
||||
// });
|
||||
})->middleware([
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
|
||||
Route::group('/campaign', function () {
|
||||
Route::post('/list', [BpsAdController::class, 'listCampaigns']);
|
||||
// Route::post('/export', [BpsAdController::class, 'exportCampaignsToExcel']);
|
||||
// Route::group('/status', function () {
|
||||
// Route::post('/update', [BpsAdController::class, 'updateCampaignStatus']);
|
||||
// Route::post('/get', [BpsAdController::class, 'getCampaignStatus']);
|
||||
// });
|
||||
})->middleware([
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
Route::group('/adset', function () {
|
||||
Route::post('/list', [BpsAdController::class, 'listAdsets']);
|
||||
// Route::post('/export', [BpsAdController::class, 'exportGroupsToExcel']);
|
||||
// Route::group('/status', function () {
|
||||
// Route::post('/update', [BpsAdController::class, 'updateGroupStatus']);
|
||||
//// Route::post('/get', [BpsAdController::class, 'getGroupStatus']);
|
||||
// });
|
||||
})->middleware([
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
Route::group('/third_user', function () {
|
||||
Route::post('/list', [BpsAdController::class, 'listThirdUsers']);
|
||||
})->middleware([
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
Route::group('/asset', function () {
|
||||
Route::post('/list', [BpsAdController::class, 'listAssets']);
|
||||
})->middleware([
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
//未合并前使用的google广告路由
|
||||
Route::group('/googleads', function () {
|
||||
|
||||
Route::group('/v18', function () {
|
||||
@ -41,7 +96,7 @@ Route::group('/googleads', function () {
|
||||
// Route::post('/get', [AdController::class, 'getAdStatus']);
|
||||
});
|
||||
})->middleware([
|
||||
app\middleware\Jwt::class,
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
|
||||
@ -53,7 +108,7 @@ Route::group('/googleads', function () {
|
||||
// Route::post('/get', [AdController::class, 'getCampaignStatus']);
|
||||
});
|
||||
})->middleware([
|
||||
app\middleware\Jwt::class,
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
Route::group('/adgroup', function () {
|
||||
@ -64,13 +119,13 @@ Route::group('/googleads', function () {
|
||||
// Route::post('/get', [AdController::class, 'getGroupStatus']);
|
||||
});
|
||||
})->middleware([
|
||||
app\middleware\Jwt::class,
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
Route::group('/asset', function () {
|
||||
Route::post('/list', [AdController::class, 'listAssets']);
|
||||
})->middleware([
|
||||
app\middleware\Jwt::class,
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
Route::group('/customer', function () {
|
||||
@ -78,8 +133,9 @@ Route::group('/googleads', function () {
|
||||
Route::post('/bind', [CustomerController::class, 'bindAdvertiser']);
|
||||
Route::post('/list_resource', [CustomerController::class, 'accessibleCustomers']);
|
||||
Route::post('/list_tree', [CustomerController::class, 'accountHierarchy']);
|
||||
Route::post('/access_role', [CustomerController::class, 'accountAccess']);
|
||||
})->middleware([
|
||||
app\middleware\Jwt::class,
|
||||
app\middleware\JwtLocal::class,
|
||||
app\middleware\OauthCheck::class,
|
||||
]);
|
||||
|
||||
@ -91,7 +147,7 @@ Route::group('/googleads', function () {
|
||||
Route::post('/refresh_token_test', [OAuthController::class, 'testRefreshToken']);
|
||||
Route::post('/refresh_token_revoke', [OAuthController::class, 'revokeRefreshToken']);
|
||||
})->middleware([
|
||||
app\middleware\Jwt::class,
|
||||
// app\middleware\JwtLocal::class,
|
||||
]);
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user