谷歌创意素材2
This commit is contained in:
parent
f145391b52
commit
f385f6f2ef
@ -10,6 +10,7 @@ use app\service\GoogleAdsCampaignService;
|
|||||||
use app\service\GoogleAdsGroupService;
|
use app\service\GoogleAdsGroupService;
|
||||||
use app\service\GoogleAdsAdService;
|
use app\service\GoogleAdsAdService;
|
||||||
use app\service\GoogleAdsAssetService;
|
use app\service\GoogleAdsAssetService;
|
||||||
|
use app\service\GoogleAdsAssetRelationService;
|
||||||
use app\service\GoogleAdsAccountService;
|
use app\service\GoogleAdsAccountService;
|
||||||
use support\Response;
|
use support\Response;
|
||||||
use DI\Annotation\Inject;
|
use DI\Annotation\Inject;
|
||||||
@ -42,6 +43,12 @@ class GoogleAdsController
|
|||||||
*/
|
*/
|
||||||
private $googleAdsAssetService;
|
private $googleAdsAssetService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Inject
|
||||||
|
* @var GoogleAdsAssetRelationService
|
||||||
|
*/
|
||||||
|
private $googleAdsAssetRelationService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Inject
|
* @Inject
|
||||||
* @var GoogleAdsAccountService
|
* @var GoogleAdsAccountService
|
||||||
@ -93,6 +100,17 @@ class GoogleAdsController
|
|||||||
// 继续处理 Google Ads API 操作
|
// 继续处理 Google Ads API 操作
|
||||||
return $this->getAssets($options);
|
return $this->getAssets($options);
|
||||||
}
|
}
|
||||||
|
public function listAssetRelations(Request $request)
|
||||||
|
{
|
||||||
|
$options = $request->all();
|
||||||
|
|
||||||
|
// 继续处理 Google Ads API 操作
|
||||||
|
if($options['asset_type'] == 2){
|
||||||
|
return $this->getVideoAssetRelations($options);
|
||||||
|
}elseif($options['asset_type'] == 4){
|
||||||
|
$this->getAssetRelations($options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function listDateDatas(Request $request)
|
public function listDateDatas(Request $request)
|
||||||
{
|
{
|
||||||
@ -279,6 +297,27 @@ class GoogleAdsController
|
|||||||
// return $this->successResponse(['assets_list' => $resourceName]);
|
// return $this->successResponse(['assets_list' => $resourceName]);
|
||||||
return $this->successResponse(['assets_list' => 'succeed added']);
|
return $this->successResponse(['assets_list' => 'succeed added']);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* get assets
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function getAssetRelations($options): Response
|
||||||
|
{
|
||||||
|
$resourceName = $this->googleAdsAssetRelationService->runListAssetRelations($options['customer_id']);
|
||||||
|
return $this->successResponse(['assets_relation_list' => $resourceName]);
|
||||||
|
// return $this->successResponse(['assets_relation_list' => 'succeed added']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get assets
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function getVideoAssetRelations($options): Response
|
||||||
|
{
|
||||||
|
$resourceName = $this->googleAdsAssetRelationService->runListVideoAssetRelations($options['customer_id']);
|
||||||
|
return $this->successResponse(['assets_relation_list' => $resourceName]);
|
||||||
|
// return $this->successResponse(['assets_relation_list' => 'succeed added']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get date datas
|
* get date datas
|
||||||
|
297
app/event/GoogleAdsAssetRelations.php
Normal file
297
app/event/GoogleAdsAssetRelations.php
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\event;
|
||||||
|
|
||||||
|
use app\service\GoogleAdsAssetRelationService;
|
||||||
|
use app\service\GoogleOAuthService;
|
||||||
|
use Google\ApiCore\ApiException;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
use support\Db;
|
||||||
|
use support\Request;
|
||||||
|
use support\Response;
|
||||||
|
use DI\Annotation\Inject;
|
||||||
|
|
||||||
|
//use QL\QueryList;
|
||||||
|
use support\Redis;
|
||||||
|
|
||||||
|
|
||||||
|
class GoogleAdsAssetRelations
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Inject
|
||||||
|
* @var GoogleOAuthService
|
||||||
|
*/
|
||||||
|
|
||||||
|
private $googleOAuthService;
|
||||||
|
|
||||||
|
|
||||||
|
//微博热榜地址
|
||||||
|
// const url = 'https://library.tiktok.com/api/v1/search?region=GB&type=1&start_time=1666540800&end_time=1666627200';
|
||||||
|
|
||||||
|
const userAgent = 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
|
||||||
|
|
||||||
|
const IMAGEASSET = 'googleadsassetrelations_image';
|
||||||
|
const VIDEOASSET = 'googleadsassetrelations_video';
|
||||||
|
const limit = 12;
|
||||||
|
const sort_order = 'impression,desc';
|
||||||
|
|
||||||
|
|
||||||
|
// const countries = ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IS", "IE","IT", "LV", "LI", "LT", "LU", "MT", "NL", "NO", "PL", "PT", "RO", "SK", "SI", "ES", "SE", "CH", "TR", "GB"] ;
|
||||||
|
const countries = ["GB", "BE"];
|
||||||
|
|
||||||
|
|
||||||
|
public function listAssetRelations(Request $request)
|
||||||
|
{
|
||||||
|
$options = $request->all();
|
||||||
|
|
||||||
|
// 继续处理 Google Ads API 操作
|
||||||
|
return $this->getAssetRelations($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get asset relations
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function getAssetRelations($options)
|
||||||
|
{
|
||||||
|
$customers = $this->googleOAuthService->getGoogleAdCustomers([]);
|
||||||
|
foreach ($customers as $customerId) {
|
||||||
|
$googleAdsAssetRelationService = new GoogleAdsAssetRelationService($customerId);
|
||||||
|
$resourceName = $googleAdsAssetRelationService->runListAssetRelations($customerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return $this->successResponse(['ads_list' => $resourceName]);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* get asset relations
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function getVideoAssetRelations($options)
|
||||||
|
{
|
||||||
|
$customers = $this->googleOAuthService->getGoogleAdCustomers([]);
|
||||||
|
foreach ($customers as $customerId) {
|
||||||
|
$googleAdsAssetRelationService = new GoogleAdsAssetRelationService($customerId);
|
||||||
|
$resourceName = $googleAdsAssetRelationService->runListVideoAssetRelations($customerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return $this->successResponse(['ads_list' => $resourceName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每天爬取tiktok广告
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function update()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
$client = new Client([
|
||||||
|
//允许重定向
|
||||||
|
// 'allow_redirects' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
// 获取前两天 0 点的时间戳
|
||||||
|
$dayBeforeYesterdayStart = strtotime('-2 days 00:00:00');
|
||||||
|
|
||||||
|
// 获取前一天 0 点的时间戳
|
||||||
|
$yesterdayStart = strtotime('-1 day 00:00:00');
|
||||||
|
$countryCache = Redis::get(self::type . 'lastCountry');
|
||||||
|
//全部跑完跳出
|
||||||
|
if ($countryCache == 'All') {
|
||||||
|
dump($countryCache . '国家更新tiktok Ads 完成');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($countryCache)) {
|
||||||
|
$countryCache = 'GB';
|
||||||
|
}
|
||||||
|
|
||||||
|
$searchIdCache = Redis::get(self::type . 'lastSearchId');
|
||||||
|
$offsetCache = Redis::get(self::type . 'nextOffset');
|
||||||
|
$totalCache = Redis::get(self::type . 'totalCache');
|
||||||
|
if (!isset($searchIdCache) || empty($searchIdCache)) {
|
||||||
|
$searchIdCache = '';
|
||||||
|
}
|
||||||
|
if (!isset($offsetCache) || empty($searchIdCache)) {
|
||||||
|
$offsetCache = 0;
|
||||||
|
}
|
||||||
|
if (!isset($totalCache) || empty($searchIdCache)) {
|
||||||
|
$totalCache = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断国家是否跑完。
|
||||||
|
if ($totalCache > 0 && $offsetCache > ceil($totalCache / self::limit)) {
|
||||||
|
$key = array_search($countryCache, self::countries, true);
|
||||||
|
if (in_array($countryCache, self::countries) && isset(self::countries[$key + 1])) {
|
||||||
|
$countryCache = self::countries[$key + 1];
|
||||||
|
dump('更新' . $countryCache . '国家的tiktok Ads 完成');
|
||||||
|
} else {
|
||||||
|
$countryCache = 'All'; //赋值非正常国家的code中断定时任务
|
||||||
|
dump($countryCache . '国家更新tiktok Ads 完成');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$start_time = $dayBeforeYesterdayStart;
|
||||||
|
$end_time = $yesterdayStart;
|
||||||
|
//当前国家爬取
|
||||||
|
$currentParams = null;
|
||||||
|
$currentParams = ['country' => $countryCache, 'search_id' => $searchIdCache, 'type' => 1, 'start_time' => $start_time, 'end_time' => $end_time];
|
||||||
|
|
||||||
|
|
||||||
|
$url = 'https://library.tiktok.com/api/v1/search?region=' . $currentParams['country'] . '&type=' . $currentParams['type'] . '&start_time=' . $currentParams['start_time'] . '&end_time=' . $currentParams['end_time'];
|
||||||
|
|
||||||
|
$res = json_decode($client->post($url, [
|
||||||
|
'headers' => [
|
||||||
|
'accept' => 'application/json, text/plain, */*',
|
||||||
|
'accept-language' => 'zh-CN,zh;q=0.9',
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'cookie' => 'cookie: _ttp=2ov8Fc4C2CaNscHJd90O9fMhlpE; _ga=GA1.1.1025820618.1731926196; FPID=FPID2.2.Bcgkp%2Fk%2Bbn5w5YeSMR9wd9VpNHJwTUpkkaEqSdCEa0w%3D.1731926196; FPAU=1.2.944915349.1731926193; FPLC=mbVyryI5aG6IVpAvhs1JsgWjA7FVA6QsCJ7VbXhM7zWoXNp4rcD0IK7FNTTf%2FuOrqeOgqEhTd4NB3hY7q3aDVTGQa3WGHqxkGte4%2BBZxsrpaHFas9kb7DPRXM12T5Q%3D%3D; _ga_TEQXTT9FE4=GS1.1.1732097542.7.0.1732097542.0.0.857840528',
|
||||||
|
'origin' => 'https://library.tiktok.com',
|
||||||
|
'priority' => 'u=1, i',
|
||||||
|
'referer' => 'https://library.tiktok.com/ads?region=AT&start_time=1731945600000&end_time=1732032000000&adv_name=&adv_biz_ids=&query_type=&sort_type=last_shown_date,desc',
|
||||||
|
'sec-ch-ua' => '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
||||||
|
'sec-ch-ua-mobile' => '?0',
|
||||||
|
'sec-ch-ua-platform' => '"Windows"',
|
||||||
|
'sec-fetch-dest' => 'empty',
|
||||||
|
'sec-fetch-mode' => 'cors',
|
||||||
|
'sec-fetch-site' => 'same-origin',
|
||||||
|
'user-agent' => self::userAgent,
|
||||||
|
// 'user-agent' => 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||||
|
],
|
||||||
|
'json' => [
|
||||||
|
'query' => '',
|
||||||
|
'query_type' => '',
|
||||||
|
'adv_biz_ids' => '',
|
||||||
|
'order' => self::sort_order,
|
||||||
|
'offset' => (int)$offsetCache,
|
||||||
|
'search_id' => $currentParams['search_id'],
|
||||||
|
'limit' => self::limit,
|
||||||
|
],
|
||||||
|
|
||||||
|
])->getBody()->getContents(), true);
|
||||||
|
// dump($res);return; //调试点
|
||||||
|
|
||||||
|
if ($res['search_id'] != $searchIdCache) {
|
||||||
|
$searchIdCache = $res['search_id'];
|
||||||
|
dump('search_id更新 ' . $searchIdCache . ' 成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($res['total'] == 0 || $res['code'] != 0) {
|
||||||
|
dump('更新tiktok Ads接口响应异常:' . json_encode($res, JSON_UNESCAPED_UNICODE));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$listAdsIds = [];
|
||||||
|
foreach ($res['data'] as $ad) {
|
||||||
|
if ($ad['audit_status'] == 2 || empty($ad['videos'])) {
|
||||||
|
continue; //审核不过或者没视频不采集
|
||||||
|
}
|
||||||
|
|
||||||
|
$imagesJson = is_array($ad['image_urls']) ? json_encode($ad['image_urls']) : json_encode([]);
|
||||||
|
$rejection_info = is_array($ad['rejection_info']) ? json_encode($ad['rejection_info']) : null;
|
||||||
|
// dump($rejection_info);
|
||||||
|
$insertData[$ad['id']] = [
|
||||||
|
'ad_id' => $ad['id'],
|
||||||
|
'name' => $ad['name'],
|
||||||
|
'audit_status' => $ad['audit_status'],
|
||||||
|
'type' => $ad['type'],
|
||||||
|
'first_shown_date' => $ad['first_shown_date'],
|
||||||
|
'last_shown_date' => $ad['last_shown_date'],
|
||||||
|
'image_urls' => $imagesJson,
|
||||||
|
'estimated_audience' => $ad['estimated_audience'],
|
||||||
|
'spent' => $ad['spent'],
|
||||||
|
'impression' => $ad['impression'],
|
||||||
|
'show_mode' => $ad['show_mode'],
|
||||||
|
'rejection_info' => $rejection_info,
|
||||||
|
'sor_audit_status' => $ad['sor_audit_status'],
|
||||||
|
'country_code' => $countryCache,
|
||||||
|
];
|
||||||
|
if (isset($ad['videos']) && !empty($ad['videos'])) {
|
||||||
|
// 遍历 "videos" 数组
|
||||||
|
foreach ($ad['videos'] as $video) {
|
||||||
|
$insertData[$ad['id']]['video_url'] = $video['video_url'];
|
||||||
|
$insertData[$ad['id']]['cover_img'] = $video['cover_img'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$listAdsIds = array_column($insertData, 'ad_id');
|
||||||
|
|
||||||
|
}
|
||||||
|
// dump($insertData);return;
|
||||||
|
|
||||||
|
|
||||||
|
if (empty($insertData)) return;
|
||||||
|
|
||||||
|
//开启事务
|
||||||
|
Db::beginTransaction();
|
||||||
|
|
||||||
|
|
||||||
|
//删除原来的旧数据
|
||||||
|
TiktokAd::query()->whereIn('ad_id', array_keys($insertData))->delete();
|
||||||
|
//添加新的数据
|
||||||
|
TiktokAd::query()->insert($insertData);
|
||||||
|
|
||||||
|
//redis缓存
|
||||||
|
Redis::set(self::type, json_encode($insertData, JSON_UNESCAPED_UNICODE));
|
||||||
|
|
||||||
|
//redis缓存 记录更新时间
|
||||||
|
$time = date('Y-m-d H:i:s');
|
||||||
|
Redis::set(self::type . 'time', $time);
|
||||||
|
Redis::set(self::type . 'lastCountry', $countryCache);
|
||||||
|
Redis::set(self::type . 'nextOffset', ++$offsetCache); //记录下一次的offset
|
||||||
|
Redis::set(self::type . 'totalCache', $res['total']);
|
||||||
|
Redis::set(self::type . 'lastSearchId', $searchIdCache);
|
||||||
|
if (!empty($listAdsIds)) {
|
||||||
|
Redis::rPush(self::type . 'AdsIds', ...$listAdsIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
//提交事务
|
||||||
|
Db::commit();
|
||||||
|
//销毁$res
|
||||||
|
unset($res);
|
||||||
|
|
||||||
|
dump(date('Y-m-d H:i:s') . '更新' . self::type . '成功');
|
||||||
|
} catch (GuzzleException|\Exception $exception) {
|
||||||
|
//回滚事务
|
||||||
|
Db::rollBack();
|
||||||
|
dump('更' . self::type . '异常:' . $exception->getMessage());
|
||||||
|
dump($exception);
|
||||||
|
}
|
||||||
|
// } catch (ClientExceptionInterface $e) {
|
||||||
|
// // 捕获 4xx 错误
|
||||||
|
// dump( 'Client error: ' . $e->getMessage() . "\n");
|
||||||
|
// } catch (ServerExceptionInterface $e) {
|
||||||
|
// // 捕获 5xx 错误
|
||||||
|
// dump('Server error: ' . $e->getMessage() . "\n");
|
||||||
|
// } catch (TransportExceptionInterface $e) {
|
||||||
|
// // 捕获网络传输错误
|
||||||
|
// dump('Transport error: ' . $e->getMessage() . "\n") ;
|
||||||
|
// } catch (\Exception $e) {
|
||||||
|
// // 捕获所有其他错误
|
||||||
|
// dump('General error: ' . $e->getMessage() . "\n") ;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 可以加入一些公共方法
|
||||||
|
protected function successResponse($data): Response
|
||||||
|
{
|
||||||
|
return Json([
|
||||||
|
'code' => 0,
|
||||||
|
'msg' => 'ok',
|
||||||
|
'data' => $data,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function errorResponse($code, $message, $data = []): Response
|
||||||
|
{
|
||||||
|
return Json([
|
||||||
|
'code' => $code,
|
||||||
|
'msg' => $message ?: 'error',
|
||||||
|
'data' => $data
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,11 @@ class Ad extends Model
|
|||||||
'ad_group_id' => 'int',
|
'ad_group_id' => 'int',
|
||||||
'customer_id' => 'int',
|
'customer_id' => 'int',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 设置json类型字段
|
||||||
|
protected $json = ['metadata'];
|
||||||
|
|
||||||
|
|
||||||
// 默认值设置
|
// 默认值设置
|
||||||
protected $defaults = [
|
protected $defaults = [
|
||||||
'status' => 1, // 广告状态默认值为 'ENABLED'
|
'status' => 1, // 广告状态默认值为 'ENABLED'
|
||||||
|
@ -7,6 +7,7 @@ use app\event\GoogleAdsCampaigns;
|
|||||||
use app\event\GoogleAdsGroups;
|
use app\event\GoogleAdsGroups;
|
||||||
use app\event\GoogleAdsAds;
|
use app\event\GoogleAdsAds;
|
||||||
use app\event\GoogleAdsAssets;
|
use app\event\GoogleAdsAssets;
|
||||||
|
use app\event\GoogleAdsAssetRelations;
|
||||||
use app\event\GoogleAdsDateDatas;
|
use app\event\GoogleAdsDateDatas;
|
||||||
use Webman\Event\Event;
|
use Webman\Event\Event;
|
||||||
use Workerman\Crontab\Crontab;
|
use Workerman\Crontab\Crontab;
|
||||||
@ -24,33 +25,33 @@ class UpdateGoogleAdsTask
|
|||||||
// 每15分钟执行一次
|
// 每15分钟执行一次
|
||||||
new Crontab('10 */15 * * * *', function () {
|
new Crontab('10 */15 * * * *', function () {
|
||||||
|
|
||||||
$dayBeforeYesterdayStart = date('Y-m-d', strtotime('-2 day'));
|
// $dayBeforeYesterdayStart = date('Y-m-d', strtotime('-2 day'));
|
||||||
dump($dayBeforeYesterdayStart . '更新' . GoogleAdsDateDatas::type . '开始');
|
// dump($dayBeforeYesterdayStart . '更新' . GoogleAdsDateDatas::type . '开始');
|
||||||
Event::emit(GoogleAdsDateDatas::type, ['date' => $dayBeforeYesterdayStart]);
|
// Event::emit(GoogleAdsDateDatas::type, ['date' => $dayBeforeYesterdayStart]);
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 每15分钟执行一次
|
// 每15分钟执行一次
|
||||||
new Crontab('20 */15 * * * *', function () {
|
new Crontab('20 */15 * * * *', function () {
|
||||||
$yesterdayStart = date('Y-m-d', strtotime('-1 day'));
|
// $yesterdayStart = date('Y-m-d', strtotime('-1 day'));
|
||||||
dump($yesterdayStart . '更新' . GoogleAdsDateDatas::type . '开始');
|
// dump($yesterdayStart . '更新' . GoogleAdsDateDatas::type . '开始');
|
||||||
Event::emit(GoogleAdsDateDatas::type, ['date' => $yesterdayStart]);
|
// Event::emit(GoogleAdsDateDatas::type, ['date' => $yesterdayStart]);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 每15分钟执行一次
|
// 每15分钟执行一次
|
||||||
new Crontab('30 */15 * * * *', function () {
|
new Crontab('30 */15 * * * *', function () {
|
||||||
//获取今天的 0 点的YYYY-MM-DD格式
|
//获取今天的 0 点的YYYY-MM-DD格式
|
||||||
$todayStart = date('Y-m-d', strtotime('0 day'));
|
// $todayStart = date('Y-m-d', strtotime('0 day'));
|
||||||
dump($todayStart . '更新' . GoogleAdsDateDatas::type . '开始');
|
// dump($todayStart . '更新' . GoogleAdsDateDatas::type . '开始');
|
||||||
Event::emit(GoogleAdsDateDatas::type, ['date' => $todayStart]);
|
// Event::emit(GoogleAdsDateDatas::type, ['date' => $todayStart]);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// 每15分钟执行一次
|
// 每15分钟执行一次
|
||||||
new Crontab('40 */15 * * * *', function () {
|
new Crontab('40 */15 * * * *', function () {
|
||||||
dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsCampaigns::type . '开始');
|
// dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsCampaigns::type . '开始');
|
||||||
Event::emit(GoogleAdsCampaigns::type, []);
|
// Event::emit(GoogleAdsCampaigns::type, []);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -63,15 +64,26 @@ class UpdateGoogleAdsTask
|
|||||||
|
|
||||||
// 每15分钟执行一次
|
// 每15分钟执行一次
|
||||||
new Crontab('55 */15 * * * *', function () {
|
new Crontab('55 */15 * * * *', function () {
|
||||||
dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAds::type . '开始');
|
// dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAds::type . '开始');
|
||||||
Event::emit(GoogleAdsAds::type, []);
|
// Event::emit(GoogleAdsAds::type, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
);
|
);
|
||||||
// 每15分钟执行一次
|
// 每15分钟执行一次
|
||||||
new Crontab('58 */15 * * * *', function () {
|
new Crontab('25 */15 * * * *', function () {
|
||||||
dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAssets::type . '开始');
|
// dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAssets::type . '开始');
|
||||||
Event::emit(GoogleAdsAssets::type, []);
|
// Event::emit(GoogleAdsAssets::type, []);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
new Crontab('55 */50 * * * *', function () {
|
||||||
|
dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAssetRelations::IMAGEASSET . '开始');
|
||||||
|
Event::emit(GoogleAdsAssetRelations::IMAGEASSET, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
new Crontab('55 */51 * * * *', function () {
|
||||||
|
dump(date('Y-m-d H:i:s') . '更新' . GoogleAdsAssetRelations::VIDEOASSET . '开始');
|
||||||
|
Event::emit(GoogleAdsAssetRelations::VIDEOASSET, []);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
679
app/service/GoogleAdsAssetRelationService.php
Normal file
679
app/service/GoogleAdsAssetRelationService.php
Normal file
@ -0,0 +1,679 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\service;
|
||||||
|
|
||||||
|
use app\service\GoogleAdsClientService;
|
||||||
|
use app\model\ThirdUserAdvertiser;
|
||||||
|
use app\util\Helper;
|
||||||
|
use app\util\ArgumentNames;
|
||||||
|
use app\util\ArgumentParser;
|
||||||
|
|
||||||
|
//use Google\Ads\GoogleAds\Lib\V18\GoogleAdsClient;
|
||||||
|
//use Google\Ads\GoogleAds\Util\FieldMasks;
|
||||||
|
//use Google\Ads\GoogleAds\Util\V18\ResourceNames;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Common\ResponsiveSearchAdInfo;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Enums\AdGroupAdStatusEnum\AdGroupAdStatus;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Enums\GoogleAdsFieldCategoryEnum\GoogleAdsFieldCategory;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Enums\GoogleAdsFieldDataTypeEnum\GoogleAdsFieldDataType;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Errors\GoogleAdsError;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Resources\Ad;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Resources\AdGroupAd;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Resources\GoogleAdsField;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Services\AdGroupAdOperation;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Services\AdOperation;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Services\MutateAdGroupAdsRequest;
|
||||||
|
//
|
||||||
|
//use Google\Ads\GoogleAds\V18\Common\AdTextAsset;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Enums\ServedAssetFieldTypeEnum\ServedAssetFieldType;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Services\GoogleAdsRow;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Services\MutateAdsRequest;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Services\SearchGoogleAdsFieldsRequest;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Services\SearchGoogleAdsRequest;
|
||||||
|
//use Google\Ads\GoogleAds\V18\Services\SearchGoogleAdsStreamRequest;
|
||||||
|
//use Google\Protobuf\Internal\RepeatedField;
|
||||||
|
|
||||||
|
|
||||||
|
use think\facade\Db as ThinkDb;
|
||||||
|
use app\model\Ad as AdModel;
|
||||||
|
use app\model\Asset as AssetModel;
|
||||||
|
use app\model\AssetRelation as AssetRelationModel;
|
||||||
|
|
||||||
|
|
||||||
|
use Google\ApiCore\ApiException;
|
||||||
|
|
||||||
|
class GoogleAdsAssetRelationService extends BaseService
|
||||||
|
{
|
||||||
|
// private $googleAdsClient;
|
||||||
|
private $customerId;
|
||||||
|
|
||||||
|
public function __construct($customerId = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从数据库动态获取 google RefreshToken
|
||||||
|
// private function getRefreshTokenFromDatabase($advertiserId)
|
||||||
|
// {
|
||||||
|
// // 通过 advertiser_id 查询 ThirdUserAdvertiser,联表查询 ThirdUser 数据
|
||||||
|
// $userAdvertiser = ThirdUserAdvertiser::with('googleUser') // 联表查询 user 关联
|
||||||
|
// ->where('advertiser_id', $advertiserId) // 根据 advertiser_id 查询
|
||||||
|
// ->find(); // 获取第一个结果
|
||||||
|
//
|
||||||
|
//// 如果找到广告主数据
|
||||||
|
// if ($userAdvertiser && $userAdvertiser->googleUser) {
|
||||||
|
// // 获取关联用户的 access_token
|
||||||
|
// return $userAdvertiser->googleUser ? $userAdvertiser->googleUser->access_token : null;
|
||||||
|
// } else {
|
||||||
|
//// return $this->errorResponse('101', '未找到该广告主或关联的用户');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
/* @param int $customerId the customer ID
|
||||||
|
* @param $options
|
||||||
|
* @return mixed
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function runListAssetRelations(int $customerId): mixed
|
||||||
|
{
|
||||||
|
// Creates a single shared budget to be used by the campaigns added below.
|
||||||
|
$assetsResourceName = self::getAssetRelations($customerId);
|
||||||
|
// dump(json_encode($assetsResourceName));
|
||||||
|
if (is_array($assetsResourceName)) {
|
||||||
|
self::saveAssetRelations($assetsResourceName);
|
||||||
|
}
|
||||||
|
// return $assetsResourceName;
|
||||||
|
return 'insert success';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* @param int $customerId the customer ID
|
||||||
|
* @param $options
|
||||||
|
* @return mixed
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function runListVideoAssetRelations(int $customerId): mixed
|
||||||
|
{
|
||||||
|
// Creates a single shared budget to be used by the campaigns added below.
|
||||||
|
$assetsResourceName = self::getVideoAssetRelations($customerId);
|
||||||
|
// dump(json_encode($assetsResourceName));
|
||||||
|
if (is_array($assetsResourceName)) {
|
||||||
|
self::saveAssetRelations($assetsResourceName);
|
||||||
|
}
|
||||||
|
// return $assetsResourceName;
|
||||||
|
return 'insert success';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在数据库中保存广告素材信息
|
||||||
|
* @param $assetsResourceName
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function saveAssetRelations($assetsResourceName)
|
||||||
|
{
|
||||||
|
$tableName = 'bps_google_ads_asset_relations';
|
||||||
|
$tableName = getenv('DB_PG_SCHEMA') ? getenv('DB_PG_SCHEMA') . '.' . $tableName : 'public' . $tableName;
|
||||||
|
foreach ($assetsResourceName as $data) {
|
||||||
|
// 修改后的插入 SQL 语句
|
||||||
|
$sql = "INSERT INTO {$tableName}
|
||||||
|
(asset_id, ad_id, ad_group_id, campaign_id, date, create_at, update_at)
|
||||||
|
VALUES (:asset_id, :ad_id, :ad_group_id, :campaign_id, :date, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT (asset_id, ad_id, date)
|
||||||
|
DO NOTHING"; // 如果 (asset_id, ad_id, date) 存在,忽略插入操作
|
||||||
|
|
||||||
|
ThinkDb::execute($sql, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param int $customerId the customer ID
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function getAssetRelations(int $customerId)
|
||||||
|
{
|
||||||
|
// 获取所有素材
|
||||||
|
// $assets = AssetModel::where('asset_type', 4)->select(); //图片素材
|
||||||
|
|
||||||
|
$resourceNames = AssetModel::where('asset_type', 4)
|
||||||
|
->column('asset_id, resource_name');
|
||||||
|
|
||||||
|
// dump($resourceNames);return($resourceNames);
|
||||||
|
$result = [];
|
||||||
|
foreach ($resourceNames as $resourceName) {
|
||||||
|
// 获取广告表中的所有广告
|
||||||
|
$ads = ThinkDb::table('bps.bps_google_ads_ad')
|
||||||
|
// ->whereRaw("metadata->'marketing_images' @> ?", ['["customers/4060397299/assets/191677352383"]'])
|
||||||
|
// ->whereOrRaw("metadata->'square_marketing_images' @> ?", ['["customers/4060397299/assets/191677352383"]'])
|
||||||
|
->whereRaw("metadata->'marketing_images' @> ?", ['["' . $resourceName['resource_name'] . '"]'])
|
||||||
|
->whereOrRaw("metadata->'square_marketing_images' @> ?", ['["' . $resourceName['resource_name'] . '"]'])
|
||||||
|
->select();
|
||||||
|
|
||||||
|
// $ads = AdModel::where(function ($query) use ($resourceName) {
|
||||||
|
// $query->whereJsonContains('metadata->marketing_images', $resourceName)
|
||||||
|
// ->WhereOrJsonContains('metadata->square_marketing_images', $resourceName);
|
||||||
|
// })->select();
|
||||||
|
// $result[$resourceName['asset_id']] = $ads;
|
||||||
|
|
||||||
|
foreach ($ads as $ad) {
|
||||||
|
$result[$resourceName['asset_id']]['ad_id'] = $ad['ad_id'];
|
||||||
|
$result[$resourceName['asset_id']]['ad_group_id'] = $ad['ad_group_id'];
|
||||||
|
$result[$resourceName['asset_id']]['campaign_id'] = $ad['campaign_id'];
|
||||||
|
$result[$resourceName['asset_id']]['asset_id'] = $resourceName['asset_id'];
|
||||||
|
$result[$resourceName['asset_id']]['date'] = date('Y-m-d');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
// dump('Google Ads Asset synchronization completed.');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param int $customerId the customer ID
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function getVideoAssetRelations(int $customerId)
|
||||||
|
{
|
||||||
|
// 获取所有素材
|
||||||
|
// $assets = AssetModel::where('asset_type', 2)->select(); //视频素材
|
||||||
|
|
||||||
|
$resourceNames = AssetModel::where('asset_type', 2)
|
||||||
|
->column('asset_id, resource_name');
|
||||||
|
|
||||||
|
// dump($resourceNames);return($resourceNames);
|
||||||
|
$result = [];
|
||||||
|
foreach ($resourceNames as $resourceName) {
|
||||||
|
// 获取广告表中的所有匹配的广告
|
||||||
|
$ads = ThinkDb::table('bps.bps_google_ads_ad')
|
||||||
|
->whereRaw("metadata->'youtube_videos' @> ?", ['["' . $resourceName['resource_name'] . '"]'])
|
||||||
|
->select();
|
||||||
|
foreach ($ads as $ad) {
|
||||||
|
$result[$resourceName['asset_id']]['ad_id'] = $ad['ad_id'];
|
||||||
|
$result[$resourceName['asset_id']]['ad_group_id'] = $ad['ad_group_id'];
|
||||||
|
$result[$resourceName['asset_id']]['campaign_id'] = $ad['campaign_id'];
|
||||||
|
$result[$resourceName['asset_id']]['asset_id'] = $resourceName['asset_id'];
|
||||||
|
$result[$resourceName['asset_id']]['date'] = date('Y-m-d');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
// dump('Google Ads Asset synchronization completed.');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This example updates the CPC bid and status for a given ad group. To get ad groups, run
|
||||||
|
* GetAdAds.php.
|
||||||
|
*/
|
||||||
|
/* @param int $customerId the customer ID
|
||||||
|
* @param $options
|
||||||
|
* @return mixed
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function runUpdateAd($options): mixed
|
||||||
|
{
|
||||||
|
// $googleAdsClient = $this->googleAdsClient;
|
||||||
|
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||||
|
// Creates a single shared budget to be used by the campaigns added below.
|
||||||
|
|
||||||
|
$resourceNames = self::updateAd($googleAdsClient->getGoogleAdsClient(), $options['customer_id'], $options['group_id'], $options['ad_id'], $options['status']);
|
||||||
|
|
||||||
|
return $resourceNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the updateAd example.
|
||||||
|
*
|
||||||
|
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||||
|
* @param int $customerId the customer ID
|
||||||
|
* @param int $adGroupId the ad group ID that the ad group ad belongs to
|
||||||
|
* @param int $adId the ID of the ad to pause
|
||||||
|
*/
|
||||||
|
public static function updateAd(
|
||||||
|
GoogleAdsClient $googleAdsClient,
|
||||||
|
int $customerId,
|
||||||
|
int $adGroupId,
|
||||||
|
int $adId,
|
||||||
|
int $status
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Creates ad group ad resource name.
|
||||||
|
$adGroupAdResourceName = ResourceNames::forAdGroupAd($customerId, $adGroupId, $adId);
|
||||||
|
|
||||||
|
// Creates an ad and sets its status to PAUSED.
|
||||||
|
$adGroupAd = new AdGroupAd();
|
||||||
|
$adGroupAd->setResourceName($adGroupAdResourceName);
|
||||||
|
// $adGroupAd->setStatus(AdGroupAdStatus::PAUSED);
|
||||||
|
$adGroupAd->setStatus($status);
|
||||||
|
|
||||||
|
// Constructs an operation that will pause the ad with the specified resource name,
|
||||||
|
// using the FieldMasks utility to derive the update mask. This mask tells the Google Ads
|
||||||
|
// API which attributes of the ad group you want to change.
|
||||||
|
$adGroupAdOperation = new AdGroupAdOperation();
|
||||||
|
$adGroupAdOperation->setUpdate($adGroupAd);
|
||||||
|
$adGroupAdOperation->setUpdateMask(FieldMasks::allSetFieldsOf($adGroupAd));
|
||||||
|
|
||||||
|
// Issues a mutate request to pause the ad group ad.
|
||||||
|
$adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();
|
||||||
|
$response = $adGroupAdServiceClient->mutateAdGroupAds(MutateAdGroupAdsRequest::build(
|
||||||
|
$customerId,
|
||||||
|
[$adGroupAdOperation]
|
||||||
|
));
|
||||||
|
|
||||||
|
// Prints the resource name of the paused ad group ad.
|
||||||
|
/** @var AdGroupAd $pausedAdGroupAd */
|
||||||
|
$pausedAdGroupAd = $response->getResults()[0];
|
||||||
|
// printf(
|
||||||
|
// "Ad group ad with resource name: '%s' is paused.%s",
|
||||||
|
// $pausedAdGroupAd->getResourceName(),
|
||||||
|
// PHP_EOL
|
||||||
|
// );
|
||||||
|
return $pausedAdGroupAd->getResourceName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新广告状态
|
||||||
|
*/
|
||||||
|
public function updateAdStatus(int $customerId, int $adGroupId, int $adId, int $status)
|
||||||
|
{
|
||||||
|
// 从数据库获取 Ad
|
||||||
|
$ad = AdModel::find($adId);
|
||||||
|
if (!$ad) {
|
||||||
|
// throw new ValidateException('Ad not found');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 更新数据库中的状态
|
||||||
|
// $ad->updateStatus($status);
|
||||||
|
if ($this->modifyDbAdStatus($adId, $status)) {
|
||||||
|
// 更新 Google Ads 上的状态
|
||||||
|
// $googleAdsClient = $this->googleAdsClient;
|
||||||
|
$googleAdsClient = new GoogleAdsClientService($customerId);
|
||||||
|
$resourceName = self::updateAd($googleAdsClient->getGoogleAdsClient(), $customerId, $adGroupId, $adId, $status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取广告状态
|
||||||
|
*/
|
||||||
|
public function getAdStatus(int $adId)
|
||||||
|
{
|
||||||
|
// 从数据库获取 Ad
|
||||||
|
$ad = AdModel::find($adId);
|
||||||
|
if (!$ad) {
|
||||||
|
// throw new ValidateException('Ad not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回广告状态
|
||||||
|
return $ad->getStatusTextAttr(null, $ad->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断广告是否启用
|
||||||
|
*/
|
||||||
|
// public function isAdEnabled(int $adId)
|
||||||
|
// {
|
||||||
|
// $ad = Ad::find($adId);
|
||||||
|
// if (!$ad) {
|
||||||
|
// throw new ValidateException('Ad not found');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return $ad->isEnabled();
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断广告是否暂停
|
||||||
|
*/
|
||||||
|
// public function isAdPaused(int $adId)
|
||||||
|
// {
|
||||||
|
// $ad = Ad::find($adId);
|
||||||
|
// if (!$ad) {
|
||||||
|
// throw new ValidateException('Ad not found');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return $ad->isPaused();
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断广告是否停止
|
||||||
|
*/
|
||||||
|
// public function isAdStopped(int $adId)
|
||||||
|
// {
|
||||||
|
// $ad = Ad::find($adId);
|
||||||
|
// if (!$ad) {
|
||||||
|
// throw new ValidateException('Ad not found');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return $ad->isStopped();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This example updates the CPC bid and status for a given ad group. To get ad groups, run
|
||||||
|
* GetAdAds.php.
|
||||||
|
*/
|
||||||
|
/* @param int $customerId the customer ID
|
||||||
|
* @param $options
|
||||||
|
* @return mixed
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function runGetResponsiveSearchAds($options): mixed
|
||||||
|
{
|
||||||
|
// $googleAdsClient = $this->googleAdsClient;
|
||||||
|
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||||
|
// Creates a single shared budget to be used by the campaigns added below.
|
||||||
|
if (!isset($options['group_id'])) {
|
||||||
|
$options['group_id'] = null;
|
||||||
|
}
|
||||||
|
$resourceNames = self::getResponsiveSearchAds($googleAdsClient->getGoogleAdsClient(), $options['customer_id'], $options['group_id']);
|
||||||
|
|
||||||
|
return $resourceNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定广告组中未移除的自适应搜索广告。
|
||||||
|
*
|
||||||
|
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||||
|
* @param int $customerId the customer ID
|
||||||
|
* @param int|null $adGroupId the ad group ID for which responsive search ads will be retrieved.
|
||||||
|
* If `null`, returns from all ad groups
|
||||||
|
*/
|
||||||
|
public static function getResponsiveSearchAds(
|
||||||
|
GoogleAdsClient $googleAdsClient,
|
||||||
|
int $customerId,
|
||||||
|
?int $adGroupId
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
|
||||||
|
|
||||||
|
// Creates a query that retrieves responsive search ads.
|
||||||
|
$query =
|
||||||
|
'SELECT ad_group.id, '
|
||||||
|
. 'ad_group_ad.ad.id, '
|
||||||
|
. 'ad_group_ad.ad.responsive_search_ad.headlines, '
|
||||||
|
. 'ad_group_ad.ad.responsive_search_ad.descriptions, '
|
||||||
|
. 'ad_group_ad.status '
|
||||||
|
. 'FROM ad_group_ad '
|
||||||
|
. 'WHERE ad_group_ad.ad.type = RESPONSIVE_SEARCH_AD '
|
||||||
|
. 'AND ad_group_ad.status != "REMOVED"';
|
||||||
|
if (!is_null($adGroupId)) {
|
||||||
|
$query .= " AND ad_group.id = $adGroupId";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issues a search request.
|
||||||
|
$response =
|
||||||
|
$googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));
|
||||||
|
|
||||||
|
// Iterates over all rows in all pages and prints the requested field values for
|
||||||
|
// the responsive search ad in each row.
|
||||||
|
$isEmptyResult = true;
|
||||||
|
$resources = [];
|
||||||
|
foreach ($response->iterateAllElements() as $googleAdsRow) {
|
||||||
|
/** @var GoogleAdsRow $googleAdsRow */
|
||||||
|
$isEmptyResult = false;
|
||||||
|
$ad = $googleAdsRow->getAdGroupAd()->getAd();
|
||||||
|
$resource = [];
|
||||||
|
// printf(
|
||||||
|
// "Responsive search ad with resource name '%s' and status '%s' was found.%s",
|
||||||
|
// $ad->getResourceName(),
|
||||||
|
// AdGroupAdStatus::name($googleAdsRow->getAdGroupAd()->getStatus()),
|
||||||
|
// PHP_EOL
|
||||||
|
// );
|
||||||
|
$resource['resource_name'] = $ad->getResourceName();
|
||||||
|
$resource['status'] = AdGroupAdStatus::name($googleAdsRow->getAdGroupAd()->getStatus());
|
||||||
|
$responsiveSearchAdInfo = $ad->getResponsiveSearchAd();
|
||||||
|
// printf(
|
||||||
|
// 'Headlines:%1$s%2$sDescriptions:%1$s%3$s%1$s',
|
||||||
|
// PHP_EOL,
|
||||||
|
// self::convertAdTextAssetsToString($responsiveSearchAdInfo->getHeadlines()),
|
||||||
|
// self::convertAdTextAssetsToString($responsiveSearchAdInfo->getDescriptions())
|
||||||
|
// )
|
||||||
|
$resource['content'] = sprintf(
|
||||||
|
'Headlines:%1$s%2$sDescriptions:%1$s%3$s%1$s',
|
||||||
|
PHP_EOL,
|
||||||
|
self::convertAdTextAssetsToString($responsiveSearchAdInfo->getHeadlines()),
|
||||||
|
self::convertAdTextAssetsToString($responsiveSearchAdInfo->getDescriptions())
|
||||||
|
);
|
||||||
|
$resources[] = $resource;
|
||||||
|
}
|
||||||
|
return $resources;
|
||||||
|
// if ($isEmptyResult) {
|
||||||
|
// print 'No responsive search ads were found.' . PHP_EOL;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the list of AdTextAsset objects into a string representation.
|
||||||
|
*
|
||||||
|
* @param RepeatedField $assets the list of AdTextAsset objects
|
||||||
|
* @return string the string representation of the provided list of AdTextAsset objects
|
||||||
|
*/
|
||||||
|
private static function convertAdTextAssetsToString(RepeatedField $assets): string
|
||||||
|
{
|
||||||
|
$result = '';
|
||||||
|
foreach ($assets as $asset) {
|
||||||
|
/** @var AdTextAsset $asset */
|
||||||
|
$result .= sprintf(
|
||||||
|
"\t%s pinned to %s.%s",
|
||||||
|
$asset->getText(),
|
||||||
|
ServedAssetFieldType::name($asset->getPinnedField()),
|
||||||
|
PHP_EOL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This example updates the CPC bid and status for a given ad group. To get ad groups, run
|
||||||
|
* GetAdAds.php.
|
||||||
|
*/
|
||||||
|
/* @param int $customerId the customer ID
|
||||||
|
* @param $options
|
||||||
|
* @return mixed
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function runUpdateResponsiveSearchAd($options): mixed
|
||||||
|
{
|
||||||
|
// $googleAdsClient = $this->googleAdsClient;
|
||||||
|
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||||
|
// Creates a single shared budget to be used by the campaigns added below.
|
||||||
|
$resourceName = self::updateResponsiveSearchAd($googleAdsClient->getGoogleAdsClient(), $options['customer_id'], $options['ad_id']);
|
||||||
|
|
||||||
|
return $resourceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* updateResponsiveSearchAd.
|
||||||
|
*
|
||||||
|
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||||
|
* @param int $customerId the customer ID
|
||||||
|
* @param int $adId the ad ID to update
|
||||||
|
*/
|
||||||
|
// [START update_responsive_search_ad]
|
||||||
|
public static function updateResponsiveSearchAd(
|
||||||
|
GoogleAdsClient $googleAdsClient,
|
||||||
|
int $customerId,
|
||||||
|
int $adId
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Creates an ad with the specified resource name and other changes.
|
||||||
|
$ad = new Ad([
|
||||||
|
'resource_name' => ResourceNames::forAd($customerId, $adId),
|
||||||
|
'responsive_search_ad' => new ResponsiveSearchAdInfo([
|
||||||
|
// Update some properties of the responsive search ad.
|
||||||
|
'headlines' => [
|
||||||
|
new AdTextAsset([
|
||||||
|
'text' => 'Cruise to Pluto #' . Helper::getShortPrintableDatetime(),
|
||||||
|
'pinned_field' => ServedAssetFieldType::HEADLINE_1
|
||||||
|
]),
|
||||||
|
new AdTextAsset(['text' => 'Tickets on sale now', 'pinned_field' => ServedAssetFieldType::HEADLINE_2]),
|
||||||
|
new AdTextAsset(['text' => 'Buy your ticket now'])
|
||||||
|
],
|
||||||
|
'descriptions' => [
|
||||||
|
new AdTextAsset(['text' => 'Best space cruise ever.']),
|
||||||
|
new AdTextAsset([
|
||||||
|
'text' => 'The most wonderful space experience you will ever have.'])
|
||||||
|
]
|
||||||
|
]),
|
||||||
|
'final_urls' => ['http://www.baidu.com'],
|
||||||
|
'final_mobile_urls' => ['http://www.baidu.com/mobile']
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Constructs an operation that will update the ad, using the FieldMasks to derive the
|
||||||
|
// update mask. This mask tells the Google Ads API which attributes of the ad you want to
|
||||||
|
// change.
|
||||||
|
$adOperation = new AdOperation();
|
||||||
|
$adOperation->setUpdate($ad);
|
||||||
|
$adOperation->setUpdateMask(FieldMasks::allSetFieldsOf($ad));
|
||||||
|
|
||||||
|
// Issues a mutate request to update the ad.
|
||||||
|
$adServiceClient = $googleAdsClient->getAdServiceClient();
|
||||||
|
$response =
|
||||||
|
$adServiceClient->mutateAds(MutateAdsRequest::build($customerId, [$adOperation]));
|
||||||
|
|
||||||
|
// Prints the resource name of the updated ad.
|
||||||
|
/** @var Ad $updatedAd */
|
||||||
|
$updatedAd = $response->getResults()[0];
|
||||||
|
printf(
|
||||||
|
"Updated ad with resource name: '%s'.%s",
|
||||||
|
$updatedAd->getResourceName(),
|
||||||
|
PHP_EOL
|
||||||
|
);
|
||||||
|
return $updatedAd->getResourceName();
|
||||||
|
}
|
||||||
|
// [END update_responsive_search_ad]
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据给定的前缀搜索 Google Ads 字段,检索到关于这些字段的元数据(metadata)
|
||||||
|
*/
|
||||||
|
/* @param int $customerId the customer ID
|
||||||
|
* @param $options
|
||||||
|
* @return mixed
|
||||||
|
* @throws ApiException
|
||||||
|
*/
|
||||||
|
public function runSearchForGoogleAdsFields($options): mixed
|
||||||
|
{
|
||||||
|
// $googleAdsClient = $this->googleAdsClient;
|
||||||
|
$googleAdsClient = new GoogleAdsClientService($options['customer_id']);
|
||||||
|
// Creates a single shared budget to be used by the campaigns added below.
|
||||||
|
$googleAdsFieldData = self::searchForGoogleAdsFields($googleAdsClient->getGoogleAdsClient(), $options['name_prefix']);
|
||||||
|
|
||||||
|
return $googleAdsFieldData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the example SearchForGoogleAdsFields.
|
||||||
|
*
|
||||||
|
* @param GoogleAdsClient $googleAdsClient the Google Ads API client
|
||||||
|
* @param string $namePrefix the name prefix to use in the query
|
||||||
|
*/
|
||||||
|
public static function searchForGoogleAdsFields(GoogleAdsClient $googleAdsClient, string $namePrefix)
|
||||||
|
{
|
||||||
|
$googleAdsFieldServiceClient = $googleAdsClient->getGoogleAdsFieldServiceClient();
|
||||||
|
// Searches for all fields whose name begins with the specified namePrefix.
|
||||||
|
// A single "%" is the wildcard token in the Google Ads Query language.
|
||||||
|
$query = "SELECT name, category, selectable, filterable, sortable, selectable_with, "
|
||||||
|
. "data_type, is_repeated WHERE name LIKE '$namePrefix%'";
|
||||||
|
$response = $googleAdsFieldServiceClient->searchGoogleAdsFields(
|
||||||
|
SearchGoogleAdsFieldsRequest::build($query)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (iterator_count($response->getIterator()) === 0) {
|
||||||
|
printf(
|
||||||
|
"No GoogleAdsFields found with a name that begins with %s.%s",
|
||||||
|
$namePrefix,
|
||||||
|
PHP_EOL
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Iterates over all rows and prints our the metadata of each matching GoogleAdsField.
|
||||||
|
foreach ($response->iterateAllElements() as $googleAdsField) {
|
||||||
|
/** @var GoogleAdsField $googleAdsField */
|
||||||
|
$fieldInfo = [
|
||||||
|
'name' => $googleAdsField->getName(),
|
||||||
|
'category' => GoogleAdsFieldCategory::name($googleAdsField->getCategory()),
|
||||||
|
'data_type' => GoogleAdsFieldDataType::name($googleAdsField->getDataType()),
|
||||||
|
'selectable' => $googleAdsField->getSelectable() ? 'true' : 'false',
|
||||||
|
'filterable' => $googleAdsField->getFilterable() ? 'true' : 'false',
|
||||||
|
'sortable' => $googleAdsField->getSortable() ? 'true' : 'false',
|
||||||
|
'repeated' => $googleAdsField->getIsRepeated() ? 'true' : 'false',
|
||||||
|
'selectable_with' => []
|
||||||
|
];
|
||||||
|
// Check if there are fields that are selectable with the current field
|
||||||
|
if ($googleAdsField->getSelectableWith()->count() > 0) {
|
||||||
|
$selectableWithFields = iterator_to_array($googleAdsField->getSelectableWith()->getIterator());
|
||||||
|
sort($selectableWithFields); // Sort the fields alphabetically
|
||||||
|
$fieldInfo['selectable_with'] = $selectableWithFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the field info to the result array
|
||||||
|
$googleAdsFieldData[] = $fieldInfo;
|
||||||
|
//
|
||||||
|
// printf("%s:%s", $googleAdsField->getName(), PHP_EOL);
|
||||||
|
// printf(
|
||||||
|
// " %-16s: %s%s",
|
||||||
|
// "category:",
|
||||||
|
// GoogleAdsFieldCategory::name($googleAdsField->getCategory()),
|
||||||
|
// PHP_EOL
|
||||||
|
// );
|
||||||
|
// printf(
|
||||||
|
// " %-16s: %s%s",
|
||||||
|
// "data type:",
|
||||||
|
// GoogleAdsFieldDataType::name($googleAdsField->getDataType()),
|
||||||
|
// PHP_EOL
|
||||||
|
// );
|
||||||
|
// printf(
|
||||||
|
// " %-16s: %s%s",
|
||||||
|
// "selectable:",
|
||||||
|
// $googleAdsField->getSelectable() ? 'true' : 'false',
|
||||||
|
// PHP_EOL
|
||||||
|
// );
|
||||||
|
// printf(
|
||||||
|
// " %-16s: %s%s",
|
||||||
|
// "filterable:",
|
||||||
|
// $googleAdsField->getFilterable() ? 'true' : 'false',
|
||||||
|
// PHP_EOL
|
||||||
|
// );
|
||||||
|
// printf(
|
||||||
|
// " %-16s: %s%s",
|
||||||
|
// "sortable:",
|
||||||
|
// $googleAdsField->getSortable() ? 'true' : 'false',
|
||||||
|
// PHP_EOL
|
||||||
|
// );
|
||||||
|
// printf(
|
||||||
|
// " %-16s: %s%s",
|
||||||
|
// "repeated:",
|
||||||
|
// $googleAdsField->getIsRepeated() ? 'true' : 'false',
|
||||||
|
// PHP_EOL
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if ($googleAdsField->getSelectableWith()->count() > 0) {
|
||||||
|
// // Prints the list of fields that are selectable with the field.
|
||||||
|
// $selectableWithFields =
|
||||||
|
// iterator_to_array($googleAdsField->getSelectableWith()->getIterator());
|
||||||
|
// // Sorts and then prints the list.
|
||||||
|
// sort($selectableWithFields);
|
||||||
|
// print ' selectable with:' . PHP_EOL;
|
||||||
|
// foreach ($selectableWithFields as $selectableWithField) {
|
||||||
|
// /** @var string $selectableWithField */
|
||||||
|
// printf(" $selectableWithField%s", PHP_EOL);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
return $googleAdsFieldData; // Return the result array
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -119,8 +119,8 @@ class GoogleAdsAssetService extends BaseService
|
|||||||
foreach ($assetsResourceName as $data) {
|
foreach ($assetsResourceName as $data) {
|
||||||
// 修改后的插入 SQL 语句
|
// 修改后的插入 SQL 语句
|
||||||
$sql = "INSERT INTO {$tableName}
|
$sql = "INSERT INTO {$tableName}
|
||||||
(asset_id, customer_id, asset_type, asset_name, resource_name, asset_url, status, metadata, create_at, update_at)
|
(asset_id, customer_id, asset_type, asset_name, resource_name, asset_url, status, asset_source,metadata, create_at, update_at)
|
||||||
VALUES (:asset_id, :customer_id, :asset_type, :asset_name, :resource_name, :asset_url, :status, :metadata, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
VALUES (:asset_id, :customer_id, :asset_type, :asset_name, :resource_name, :asset_url, :status,:source, :metadata, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||||
ON CONFLICT (asset_id)
|
ON CONFLICT (asset_id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
customer_id = EXCLUDED.customer_id,
|
customer_id = EXCLUDED.customer_id,
|
||||||
@ -129,6 +129,7 @@ class GoogleAdsAssetService extends BaseService
|
|||||||
resource_name = EXCLUDED.resource_name,
|
resource_name = EXCLUDED.resource_name,
|
||||||
asset_url = EXCLUDED.asset_url,
|
asset_url = EXCLUDED.asset_url,
|
||||||
status = EXCLUDED.status,
|
status = EXCLUDED.status,
|
||||||
|
asset_source = EXCLUDED.asset_source,
|
||||||
metadata = EXCLUDED.metadata,
|
metadata = EXCLUDED.metadata,
|
||||||
update_at = CURRENT_TIMESTAMP"; // update_at 使用 CURRENT_TIMESTAMP 自动更新
|
update_at = CURRENT_TIMESTAMP"; // update_at 使用 CURRENT_TIMESTAMP 自动更新
|
||||||
|
|
||||||
@ -204,6 +205,7 @@ class GoogleAdsAssetService extends BaseService
|
|||||||
|
|
||||||
$resourceName['status'] = 2; //未定义,先占坑
|
$resourceName['status'] = 2; //未定义,先占坑
|
||||||
$resourceName['resource_name'] = $googleAdsRow->getAsset()->getResourceName();
|
$resourceName['resource_name'] = $googleAdsRow->getAsset()->getResourceName();
|
||||||
|
$resourceName['source'] = $googleAdsRow->getAsset()->getSource();
|
||||||
$resourceName['customer_id'] = $googleAdsRow->getCustomer()->getId();
|
$resourceName['customer_id'] = $googleAdsRow->getCustomer()->getId();
|
||||||
|
|
||||||
$resourceNames[] = $resourceName;
|
$resourceNames[] = $resourceName;
|
||||||
|
@ -7,6 +7,7 @@ use app\event\GoogleAdsCampaigns;
|
|||||||
use app\event\GoogleAdsGroups;
|
use app\event\GoogleAdsGroups;
|
||||||
use app\event\GoogleAdsAds;
|
use app\event\GoogleAdsAds;
|
||||||
use app\event\GoogleAdsAssets;
|
use app\event\GoogleAdsAssets;
|
||||||
|
use app\event\GoogleAdsAssetRelations;
|
||||||
use app\event\GoogleAdsDateDatas;
|
use app\event\GoogleAdsDateDatas;
|
||||||
|
|
||||||
|
|
||||||
@ -33,6 +34,12 @@ return [
|
|||||||
GoogleAdsAssets::type => [
|
GoogleAdsAssets::type => [
|
||||||
[GoogleAdsAssets::class, 'getAssets'],
|
[GoogleAdsAssets::class, 'getAssets'],
|
||||||
],
|
],
|
||||||
|
GoogleAdsAssetRelations::IMAGEASSET => [
|
||||||
|
[GoogleAdsAssetRelations::class, 'getAssetRelations'],
|
||||||
|
],
|
||||||
|
GoogleAdsAssetRelations::VIDEOASSET => [
|
||||||
|
[GoogleAdsAssetRelations::class, 'getVideoAssetRelations'],
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
@ -103,6 +103,9 @@ Route::group('/googleads', function () {
|
|||||||
Route::group('/asset', function () {
|
Route::group('/asset', function () {
|
||||||
Route::post('/list', [GoogleAdsController::class, 'listAssets']);
|
Route::post('/list', [GoogleAdsController::class, 'listAssets']);
|
||||||
});
|
});
|
||||||
|
Route::group('/asset_relation', function () {
|
||||||
|
Route::post('/list', [GoogleAdsController::class, 'listAssetRelations']);
|
||||||
|
});
|
||||||
Route::group('/ad', function () {
|
Route::group('/ad', function () {
|
||||||
Route::post('/update', [GoogleAdsController::class, 'updateAd']);
|
Route::post('/update', [GoogleAdsController::class, 'updateAd']);
|
||||||
Route::post('/list', [GoogleAdsController::class, 'listAds']);
|
Route::post('/list', [GoogleAdsController::class, 'listAds']);
|
||||||
|
Loading…
Reference in New Issue
Block a user