Search by

多平台广告 API SDK,支持 Facebook、TikTok、Google Ads

Maintainers

Package info

github.com/goletter/hyperf-adv

pkg:composer/goletter/adv

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-09-06 14:54 UTC

This package is auto-updated.

Last update: 2026-09-06 15:07:34 UTC


README

多平台广告 API SDK,目前支持 Facebook Marketing API、TikTok Business API、Google Ads API。

安装

composer require goletter/adv

Hyperf 项目可发布配置:

php bin/hyperf.php vendor:publish goletter/adv

快速开始(推荐)

use Goletter\Adv\AdvFactory;

$adv = AdvFactory::make('facebook', $accessToken);
// 或 AdvFactory::make(2, $token, ['developer_token' => '...']); // 2 = google

foreach ($adv->account->iterateAccounts() as $account) {
    // ...
}

$google = AdvFactory::make('google', $oauthToken, [
    'developer_token' => env('GOOGLE_ADS_DEVELOPER_TOKEN'),
    'login_customer_id' => env('GOOGLE_ADS_LOGIN_CUSTOMER_ID'),
]);

跨平台捕获 Token 失效:

use Goletter\Adv\Exceptions\TokenExpiredExceptionInterface;

try {
    // ...
} catch (TokenExpiredExceptionInterface $e) {
    // facebook / tiktok / google token 失效
}

使用示例

Google Ads

需要 OAuth2 Access TokenDeveloper Token,操作 MCC 下子账户时还需 Login Customer ID(经理账户 ID,纯数字)。

use Goletter\Adv\Platforms\Google\GoogleClient;
use Goletter\Adv\Platforms\Google\GoogleAccount;
use Goletter\Adv\Platforms\Google\GoogleBusiness;
use Goletter\Adv\Platforms\Google\GoogleReport;
use Goletter\Adv\Platforms\Google\GoogleCampaign;

$client = new GoogleClient(
    accessToken: 'ya29....',
    developerToken: 'YOUR_DEVELOPER_TOKEN',
    loginCustomerId: '1234567890', // 可选,MCC ID
);

$account = new GoogleAccount($client);

// 可访问的客户账户
foreach ($account->iterateAccessibleCustomerIds() as $customerId) {
    echo $customerId . PHP_EOL;
}

// 客户详情
$customer = $account->getCustomer('1234567890');

// GAQL 查询
foreach ($client->iterateSearch('1234567890', 'SELECT campaign.id, campaign.name FROM campaign') as $row) {
    // ...
}

// MCC 下子账户
$business = new GoogleBusiness($client);
foreach ($business->iterateClientCustomers('1234567890') as $clientRow) {
    // ...
}

// 日报表
$report = new GoogleReport($client);
foreach ($report->iterateDailyReport('1234567890', '2026-01-01', '2026-01-31') as $day) {
    // metrics.cost_micros, segments.date ...
}

// 广告系列状态
$campaign = new GoogleCampaign($client);
$campaign->updateCampaignStatus(
    '1234567890',
    'customers/1234567890/campaigns/987654321',
    'PAUSED'
);

业务侧也可通过 AdvFactory::make(2, $oauthToken)AdvFactory::make('google', $oauthToken) 获取平台 Bundle(含 client / account / business / campaign / report)。

环境变量(可选):

  • GOOGLE_ADS_DEVELOPER_TOKEN
  • GOOGLE_ADS_LOGIN_CUSTOMER_ID

基础 Client

use Goletter\Adv\Platforms\Facebook\FacebookClient;

// 创建客户端
$client = new FacebookClient('YOUR_ACCESS_TOKEN');

// 设置自定义请求头(可选)
$client->setDefaultHeaders([
    'requestSource' => 4,
    'Content-Type' => 'application/json',
]);

// 基础 GET 请求
$result = $client->get('/me', ['fields' => 'id,name']);

// 分页获取数据(推荐)
foreach ($client->paginate('/me/adaccounts', ['fields' => 'id,name']) as $account) {
    // 处理每个账户
    echo $account['id'] . PHP_EOL;
}

应用授权 OAuth (FacebookAuth / dialog/oauth)

Facebook 应用授权分两步:

  1. dialog/oauth:生成授权页 URL,引导用户跳转 Facebook 登录并授权
  2. 回调:用返回的 code 换 Access Token(默认再换长期 Token,约 60 天)
use Goletter\Adv\AdvFactory;
use Goletter\Adv\Platforms\Facebook\FacebookAuth;

// 推荐:工厂创建(OAuth 阶段无需已有 user token)
$auth = AdvFactory::facebookAuth(['api_version' => 'v24.0']);
// 或:new FacebookAuth('v24.0');

// ---------- 1) 生成授权 URL 并跳转 ----------
$state = bin2hex(random_bytes(16)); // 建议落库,回调时校验
$url = $auth->getAuthUrl(
    clientId: 'YOUR_APP_ID',
    redirectUri: 'https://your.domain/api/facebook/callback', // 须与 Meta 后台配置一致
    state: $state,
    // scope 可省略,默认:
    // ads_management,ads_read,business_management,email,public_profile
);
// return $this->response->redirect($url);

// ---------- 2) 回调:code 换长期 Token ----------
// Facebook 回调参数:code、state(失败时可能有 error / error_description)
$result = $auth->handleCallback(
    clientId: 'YOUR_APP_ID',
    clientSecret: 'YOUR_APP_SECRET',
    redirectUri: 'https://your.domain/api/facebook/callback', // 须与第 1 步完全一致
    code: (string) $request->input('code'),
);
// $result['access_token']  长期 User Access Token
// $result['expires_in']    有效秒数(约 60 天)
// $result['short_lived'] / $result['long_lived']  原始分步响应

// 如只需短期 Token,不换长期:
// $auth->handleCallback(..., exchangeLongLived: false);

// 也可分步调用:
// $short = $auth->fetchToken($appId, $appSecret, $redirectUri, $code);
// $long  = $auth->exchangeLongLivedToken($appId, $appSecret, $short['access_token']);

也可通过 FacebookBusiness(内部委托 FacebookAuth):

use Goletter\Adv\Platforms\Facebook\FacebookBusiness;
use Goletter\Adv\Platforms\Facebook\FacebookClient;

$business = new FacebookBusiness(new FacebookClient('')); // OAuth 阶段可不传 user token
$url = $business->getDialogOauthUrl($appId, $redirectUri, $state);
$token = $business->handleOAuthCallback($appId, $appSecret, $redirectUri, $code);

配置项(config/autoload/adv.php,发布后可选填写):

'facebook' => [
    'api_version' => 'v24.0',
    // 'app_id' => env('FACEBOOK_APP_ID', ''),
    // 'app_secret' => env('FACEBOOK_APP_SECRET', ''),
    // 'redirect_uri' => env('FACEBOOK_REDIRECT_URI', ''),
    // 'scopes' => 'ads_management,ads_read,business_management,email,public_profile',
],

Business Manager 管理 (FacebookBusiness)

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookBusiness;

$client = new FacebookClient('YOUR_ACCESS_TOKEN');
$business = new FacebookBusiness($client);

// 获取当前用户的所有 Business Manager
$businesses = $business->listBusinesses();

// 流式处理 Business Manager(推荐)
foreach ($business->iterateBusinesses() as $bm) {
    // 处理每个 BM
    echo $bm['id'] . ': ' . $bm['name'] . PHP_EOL;
}

// 获取单个 Business Manager 详情
$businessDetail = $business->getBusiness('BUSINESS_ID');

// 获取 Business Manager 下的客户广告账户(client_ad_accounts)
$adAccounts = $business->listAdAccounts('BUSINESS_ID');

// 流式处理 BM 下的客户广告账户(推荐)
foreach ($business->iterateAdAccounts('BUSINESS_ID') as $account) {
    // 处理账户
}

// 获取 Business Manager 拥有的广告账户(owned_ad_accounts)
$ownedAccounts = $business->listOwnedAdAccounts('BUSINESS_ID');

// 流式处理 BM 拥有的广告账户(推荐)
foreach ($business->iterateOwnedAdAccounts('BUSINESS_ID') as $account) {
    // 处理账户
}

// 获取 Business Manager 下的业务用户
$users = $business->listBusinessUsers('BUSINESS_ID');

// 流式处理业务用户
foreach ($business->iterateBusinessUsers('BUSINESS_ID') as $user) {
    // 处理用户
}

// 获取 Business Manager 下的 Pages
$pages = $business->listPages('BUSINESS_ID');

// 从 Business Manager 移除广告账户
$result = $business->removeAdAccount('BUSINESS_ID', 'ACCOUNT_ID');

// 批量移除广告账户
$results = $business->batchRemoveAdAccounts('BUSINESS_ID', ['ACCOUNT_ID_1', 'ACCOUNT_ID_2']);

账户管理 (FacebookAccount)

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookAccount;

$client = new FacebookClient('YOUR_ACCESS_TOKEN');
$account = new FacebookAccount($client);

// 获取当前用户的所有广告账户
$accounts = $account->listAccounts();

// 流式处理用户账户(推荐大数据量)
foreach ($account->iterateAccounts() as $acc) {
    // 处理账户
}

// 获取 Business Manager 下的所有广告账户(自动去重)
$businessAccounts = $account->listBusinessAccounts('BUSINESS_ID');

// 流式处理 BM 账户(推荐,自动去重)
foreach ($account->iterateBusinessAccounts('BUSINESS_ID') as $acc) {
    // 处理账户
}

// 获取单个账户详情
$accountDetail = $account->getAccount('ACCOUNT_ID');

// 更新账户名称
$updated = $account->updateAccountName('ACCOUNT_ID', '新的账户名称');

// 更新账户支出限额(以分为单位,100000 = 1000美元)
$updated = $account->updateAccountSpendCap('ACCOUNT_ID', 100000);

// 移除账户限额
$updated = $account->updateAccountSpendCap('ACCOUNT_ID', null);

// 同时更新名称和限额
$updated = $account->updateAccount('ACCOUNT_ID', '新名称', 200000);

// 只更新名称,不更新限额
$updated = $account->updateAccount('ACCOUNT_ID', '新名称');

// 只更新限额,不更新名称
$updated = $account->updateAccount('ACCOUNT_ID', null, 150000);

// 移除限额(传入 -1)
$updated = $account->updateAccount('ACCOUNT_ID', null, -1);

// 从 Business Manager 移除广告账户(注意:不会真正删除账户,只是移除访问权限)
$result = $account->removeAccountFromBusiness('BUSINESS_ID', 'ACCOUNT_ID');

// 获取账户的分配用户列表
$users = $account->listAssignedUsers('ACCOUNT_ID');

// 流式处理分配用户
foreach ($account->iterateAssignedUsers('ACCOUNT_ID') as $user) {
    // 处理每个用户
    echo $user['name'] . ' - ' . $user['role'] . PHP_EOL;
}

// 添加用户到账户(角色:ADMIN, ADVERTISER, ANALYST)
$result = $account->addAssignedUser('ACCOUNT_ID', 'USER_ID', 'ADVERTISER');

// 移除账户用户
$result = $account->removeAssignedUser('ACCOUNT_ID', 'USER_ID');

// 更新用户角色
$result = $account->updateAssignedUserRole('ACCOUNT_ID', 'USER_ID', 'ADMIN');

广告系列管理 (FacebookCampaign)

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookCampaign;

$client = new FacebookClient('YOUR_ACCESS_TOKEN');
$campaign = new FacebookCampaign($client);

// 获取账户下的所有广告系列
$campaigns = $campaign->listCampaigns('ACCOUNT_ID');

// 获取特定状态的广告系列
$activeCampaigns = $campaign->listCampaigns('ACCOUNT_ID', [
    'id',
    'name',
    'objective',
    'status',
    'effective_status',
    'daily_budget',
    'budget_remaining'
], [
    'status' => ['ACTIVE'],
    'effective_status' => ['ACTIVE']
]);

// 流式处理广告系列(推荐大数据量,自动去重)
foreach ($campaign->iterateCampaigns('ACCOUNT_ID') as $item) {
    // 处理每个广告系列
    echo $item['name'] . ' - ' . $item['status'] . PHP_EOL;
}

// 获取单个广告系列详情
$campaignDetail = $campaign->getCampaign('CAMPAIGN_ID');

// 创建广告系列
$newCampaign = $campaign->createCampaign(
    'ACCOUNT_ID',
    '我的广告系列',
    'OUTCOME_TRAFFIC', // 广告目标
    'PAUSED', // 初始状态
    [
        'daily_budget' => 10000, // 每日预算(分为单位)
        // 'lifetime_budget' => 100000, // 或设置总预算
        // 'start_time' => '2024-01-01T00:00:00+0000',
        // 'stop_time' => '2024-12-31T23:59:59+0000',
    ]
);

// 更新广告系列
$updated = $campaign->updateCampaign('CAMPAIGN_ID', [
    'name' => '新名称',
    'daily_budget' => 20000,
    'status' => 'ACTIVE',
]);

// 更新广告系列状态
$campaign->pauseCampaign('CAMPAIGN_ID'); // 暂停
$campaign->activateCampaign('CAMPAIGN_ID'); // 启用
$campaign->deleteCampaign('CAMPAIGN_ID'); // 删除
$campaign->archiveCampaign('CAMPAIGN_ID'); // 归档

// 批量更新状态
$results = $campaign->batchUpdateStatus(['CAMPAIGN_ID_1', 'CAMPAIGN_ID_2'], 'PAUSED');

报告查询 (FacebookReport)

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookReport;

$client = new FacebookClient('YOUR_ACCESS_TOKEN');
$report = new FacebookReport($client);

// 获取账户级别的 insights
foreach ($report->iterateInsights(
    'ACCOUNT_ID',
    '2024-01-01',
    '2024-01-31',
    ['account_name', 'account_id', 'spend', 'date_start', 'date_stop'],
    'account',  // level: account, campaign, adset, ad
    1  // time_increment: 1 (每日)
) as $insight) {
    // 处理每个 insight
}

// 获取每日报告(按天拆分,推荐长时间范围)
foreach ($report->iterateDailyReport(
    'ACCOUNT_ID',
    '2024-01-01',
    '2024-01-31'
) as $dailyData) {
    // 处理每日数据
}

// 一次性获取所有数据(不推荐大数据量)
$allInsights = $report->getAllInsights(
    'ACCOUNT_ID',
    '2024-01-01',
    '2024-01-31'
);

// 分页获取消耗数据(推荐,使用 Cursor 分页)
$page1 = $report->paginateInsights(
    'ACCOUNT_ID',
    '2024-01-01',
    '2024-01-31',
    null, // after cursor(第一页传 null)
    null, // before cursor
    100   // 每页数量
);

// 获取下一页
if (isset($page1['paging']['cursors']['after'])) {
    $page2 = $report->paginateInsights(
        'ACCOUNT_ID',
        '2024-01-01',
        '2024-01-31',
        $page1['paging']['cursors']['after'] // 使用上一页返回的 after cursor
    );
}

// 处理分页数据
$data = $page1['data'];
$hasNext = isset($page1['paging']['next']);
$hasPrevious = isset($page1['paging']['previous']);

在项目中的实际使用示例

获取 Business Manager 列表

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookBusiness;

// 从平台模型获取 token
$platform = $platformModel;

// 创建客户端
$client = new FacebookClient($platform->token);
$client->setDefaultHeaders(['requestSource' => 4]);

// 获取所有 Business Manager
$businessService = new FacebookBusiness($client);
foreach ($businessService->iterateBusinesses() as $item) {
    Busines::query()->updateOrCreate(
        ['code' => $item['id']],
        [
            'platform_id' => $platform->id,
            'code' => $item['id'],
            'name' => $item['name'],
        ]
    );
}

获取 BM 下的广告账户

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookBusiness;
use Carbon\Carbon;

$client = new FacebookClient($platform->token);
$client->setDefaultHeaders(['requestSource' => 4]);

$businessService = new FacebookBusiness($client);
$busine = $busineModel;

// 获取 BM 下的客户账户(client_ad_accounts,自动去重)
foreach ($businessService->iterateAdAccounts($busine->code) as $item) {
    Account::query()->updateOrCreate(
        ['code' => $item['id']],
        [
            'busine_id' => $busine->id,
            'code' => $item['id'],
            'name' => $item['name'],
            'status' => $item['account_status'],
            'spend_cap' => $item['spend_cap'],
            'amount_spent' => $item['amount_spent'],
            'currency' => $item['currency'],
            'timezone' => $item['timezone_offset_hours_utc'],
            'created_at' => Carbon::parse($item['created_time'])->format('Y-m-d H:i:s'),
        ]
    );
}

// 获取 BM 拥有的账户(owned_ad_accounts,自动去重)
foreach ($businessService->iterateOwnedAdAccounts($busine->code) as $item) {
    Account::query()->updateOrCreate(
        ['code' => $item['id']],
        [
            'busine_id' => $busine->id,
            'code' => $item['id'],
            'name' => $item['name'],
            'status' => $item['account_status'],
            'spend_cap' => $item['spend_cap'],
            'amount_spent' => $item['amount_spent'],
            'currency' => $item['currency'],
            'timezone' => $item['timezone_offset_hours_utc'],
            'created_at' => Carbon::parse($item['created_time'])->format('Y-m-d H:i:s'),
        ]
    );
}

// 从 Business Manager 移除广告账户
try {
    $result = $businessService->removeAdAccount($busine->code, $account->code);
    // 移除成功,可以从数据库中删除相关记录
    // $account->delete();
} catch (\Exception $e) {
    // 处理错误
}

// 批量移除广告账户
try {
    $accountIds = ['ACCOUNT_ID_1', 'ACCOUNT_ID_2'];
    $results = $businessService->batchRemoveAdAccounts($busine->code, $accountIds);
    
    foreach ($results as $accountId => $result) {
        if ($result['success']) {
            // 移除成功
        } else {
            // 处理错误:$result['error']
        }
    }
} catch (\Exception $e) {
    // 处理错误
}

获取账户消耗数据

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookReport;
use Carbon\Carbon;

$client = new FacebookClient($platform->token);
$client->setDefaultHeaders(['requestSource' => 4]);

$account = $accountModel;
$startAt = Carbon::now()->subMonths(1)->toDateString();
$endAt = Carbon::now()->toDateString();

$reportService = new FacebookReport($client);

// 方式 1:流式处理所有数据(推荐大数据量)
foreach ($reportService->iterateInsights(
    $account->code,
    $startAt,
    $endAt,
    ['account_name', 'account_id', 'spend', 'date_start', 'date_stop'],
    'account',
    1
) as $insight) {
    AccountInsight::query()->updateOrCreate(
        [
            'account_id' => $account->id,
            'start_at' => $insight['date_start'],
            'end_at' => $insight['date_stop'],
        ],
        ['spend' => $insight['spend']]
    );
}

// 方式 2:分页获取消耗数据(推荐用于 API 接口返回)
try {
    // 获取第一页
    $page = $reportService->paginateInsights(
        $account->code,
        $startAt,
        $endAt,
        null, // after cursor(第一页传 null)
        null, // before cursor
        100   // 每页数量
    );
    
    // 处理当前页数据
    foreach ($page['data'] as $insight) {
        AccountInsight::query()->updateOrCreate(
            [
                'account_id' => $account->id,
                'start_at' => $insight['date_start'],
                'end_at' => $insight['date_stop'],
            ],
            ['spend' => $insight['spend']]
        );
    }
    
    // 判断是否有下一页
    $hasNext = isset($page['paging']['next']);
    $hasPrevious = isset($page['paging']['previous']);
    
    // 获取下一页(循环处理所有页)
    $after = $page['paging']['cursors']['after'] ?? null;
    while ($after) {
        $nextPage = $reportService->paginateInsights(
            $account->code,
            $startAt,
            $endAt,
            $after,
            null,
            100
        );
        
        foreach ($nextPage['data'] as $insight) {
            AccountInsight::query()->updateOrCreate(
                [
                    'account_id' => $account->id,
                    'start_at' => $insight['date_start'],
                    'end_at' => $insight['date_stop'],
                ],
                ['spend' => $insight['spend']]
            );
        }
        
        // 更新 cursor,继续下一页
        $after = $nextPage['paging']['cursors']['after'] ?? null;
    }
} catch (\Exception $e) {
    // 处理错误
}

更新账户名称和限额

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookAccount;

$client = new FacebookClient($platform->token);
$client->setDefaultHeaders(['requestSource' => 4]);

$accountService = new FacebookAccount($client);
$account = $accountModel;

// 更新账户名称
try {
    $result = $accountService->updateAccountName($account->code, '新账户名称');
    $account->update(['name' => '新账户名称']);
} catch (\Exception $e) {
    // 处理错误
}

// 更新账户限额(1000美元 = 100000分)
try {
    $spendCap = 100000; // 1000美元
    $result = $accountService->updateAccountSpendCap($account->code, $spendCap);
    $account->update(['spend_cap' => $spendCap]);
} catch (\Exception $e) {
    // 处理错误
}

// 同时更新名称和限额
try {
    $result = $accountService->updateAccount(
        $account->code,
        '新账户名称',
        200000 // 2000美元
    );
    $account->update([
        'name' => '新账户名称',
        'spend_cap' => 200000,
    ]);
} catch (\Exception $e) {
    // 处理错误
}

// 移除限额
try {
    $result = $accountService->updateAccountSpendCap($account->code, null);
    $account->update(['spend_cap' => null]);
} catch (\Exception $e) {
    // 处理错误
}

管理账户分配用户

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookAccount;

$client = new FacebookClient($platform->token);
$client->setDefaultHeaders(['requestSource' => 4]);

$accountService = new FacebookAccount($client);
$account = $accountModel;

// 获取账户的分配用户列表
try {
    $users = $accountService->listAssignedUsers($account->code);
    foreach ($users as $user) {
        // 处理用户信息:$user['id'], $user['name'], $user['email'], $user['role']
    }
} catch (\Exception $e) {
    // 处理错误
}

// 流式处理分配用户(推荐大数据量)
try {
    foreach ($accountService->iterateAssignedUsers($account->code) as $user) {
        // 处理每个用户
    }
} catch (\Exception $e) {
    // 处理错误
}

// 添加用户到账户(角色:ADMIN, ADVERTISER, ANALYST)
try {
    $facebookUserId = 'FACEBOOK_USER_ID';
    $result = $accountService->addAssignedUser(
        $account->code,
        $facebookUserId,
        'ADVERTISER' // 角色选项:ADMIN, ADVERTISER, ANALYST
    );
    // 添加成功
} catch (\Exception $e) {
    // 处理错误
}

// 移除用户
try {
    $result = $accountService->removeAssignedUser(
        $account->code,
        'FACEBOOK_USER_ID'
    );
    // 移除成功
} catch (\Exception $e) {
    // 处理错误
}

// 更新用户角色
try {
    $result = $accountService->updateAssignedUserRole(
        $account->code,
        'FACEBOOK_USER_ID',
        'ADMIN' // 新角色:ADMIN, ADVERTISER, ANALYST
    );
    // 更新成功
} catch (\Exception $e) {
    // 处理错误
}

管理广告系列

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookCampaign;

$client = new FacebookClient($platform->token);
$client->setDefaultHeaders(['requestSource' => 4]);

$campaignService = new FacebookCampaign($client);
$account = $accountModel;

// 获取账户下的所有活跃广告系列
try {
    $campaigns = $campaignService->listCampaigns(
        $account->code,
        ['id', 'name', 'objective', 'status', 'effective_status', 'daily_budget'],
        ['status' => ['ACTIVE']]
    );
    
    foreach ($campaigns as $campaign) {
        // 处理广告系列
    }
} catch (\Exception $e) {
    // 处理错误
}

// 流式处理所有广告系列
try {
    foreach ($campaignService->iterateCampaigns($account->code) as $campaign) {
        // 处理每个广告系列
    }
} catch (\Exception $e) {
    // 处理错误
}

// 创建广告系列
try {
    $newCampaign = $campaignService->createCampaign(
        $account->code,
        '新广告系列',
        'OUTCOME_TRAFFIC',
        'PAUSED',
        ['daily_budget' => 10000] // 100美元
    );
} catch (\Exception $e) {
    // 处理错误
}

// 批量暂停广告系列
try {
    $campaignIds = ['CAMPAIGN_ID_1', 'CAMPAIGN_ID_2'];
    $results = $campaignService->batchUpdateStatus($campaignIds, 'PAUSED');
    
    foreach ($results as $campaignId => $result) {
        if ($result['success']) {
            // 更新成功
        } else {
            // 处理错误:$result['error']
        }
    }
} catch (\Exception $e) {
    // 处理错误
}

异常处理

use Goletter\Adv\Platforms\Facebook\Exceptions\FacebookApiException;
use Goletter\Adv\Platforms\Facebook\Exceptions\FacebookTokenExpiredException;

try {
    $accounts = $account->listAccounts();
} catch (FacebookTokenExpiredException $e) {
    // Token 已过期,需要刷新
    // 错误码: 190
} catch (FacebookApiException $e) {
    // 其他 Facebook API 错误
    $errorData = $e->getResponse();
}

API 版本

默认使用 Facebook API v24.0,可在创建 Client 时自定义:

$client = new FacebookClient('TOKEN', 'v19.0');

在账户类中快捷访问广告系列

use Goletter\Adv\Platforms\Facebook\FacebookClient;
use Goletter\Adv\Platforms\Facebook\FacebookAccount;

$client = new FacebookClient('YOUR_ACCESS_TOKEN');
$account = new FacebookAccount($client);

// 直接通过账户类获取广告系列(快捷方法)
$campaigns = $account->listCampaigns('ACCOUNT_ID');

// 流式处理
foreach ($account->iterateCampaigns('ACCOUNT_ID') as $campaign) {
    // 处理广告系列
}

TikTok 使用示例

基础 Client

use Goletter\Adv\Platforms\TikTok\TikTokClient;

// 创建 TikTok 客户端
$client = new TikTokClient('YOUR_ACCESS_TOKEN');

// 设置自定义请求头(可选)
$client->setDefaultHeaders([
    'requestSource' => 4,
]);

// 示例:调用 TikTok 接口
$result = $client->get('/open_api/v1.3/advertiser/info/', [
    'advertiser_id' => 'YOUR_ADVERTISER_ID',
]);

报表查询 (TikTokReport)

use Goletter\Adv\Platforms\TikTok\TikTokClient;
use Goletter\Adv\Platforms\TikTok\TikTokReport;

$client = new TikTokClient('YOUR_ACCESS_TOKEN');
$client->setDefaultHeaders(['requestSource' => 4]);

$report = new TikTokReport($client);

// 获取广告主的基础消耗报表(按天)
foreach ($report->iterateReport(
    'YOUR_ADVERTISER_ID',
    '2024-01-01',
    '2024-01-31',
    ['stat_time_day'],                       // 维度
    ['spend', 'impressions', 'clicks']      // 指标
) as $row) {
    // 处理每一行报表数据
    // $row['stat_time_day'], $row['spend'], $row['impressions'], $row['clicks'] ...
}

广告主信息 (TikTokAccount)

use Goletter\Adv\Platforms\TikTok\TikTokClient;
use Goletter\Adv\Platforms\TikTok\TikTokAccount;

$client = new TikTokClient('YOUR_ACCESS_TOKEN');
$client->setDefaultHeaders(['requestSource' => 4]);

$accountService = new TikTokAccount($client);

// 获取单个广告主信息
$advertiser = $accountService->getAdvertiser('YOUR_ADVERTISER_ID');

// 批量获取广告主信息
$advertisers = $accountService->listAdvertisers([
    'ADVERTISER_ID_1',
    'ADVERTISER_ID_2',
]);

在项目中的实际使用示例(同步 TikTok 消耗到数据库)

use Goletter\Adv\Platforms\TikTok\TikTokClient;
use Goletter\Adv\Platforms\TikTok\TikTokReport;
use Carbon\Carbon;

// 从平台模型获取 TikTok token 和 advertiser_id
$platform = $platformModel;

$client = new TikTokClient($platform->token);
$client->setDefaultHeaders(['requestSource' => 4]);

$reportService = new TikTokReport($client);

$startAt = Carbon::now()->subMonth()->toDateString();
$endAt = Carbon::now()->toDateString();

foreach ($reportService->iterateReport(
    $platform->advertiser_id,
    $startAt,
    $endAt,
    ['stat_time_day'],
    ['spend', 'impressions', 'clicks']
) as $row) {
    AccountInsight::query()->updateOrCreate(
        [
            'account_id' => $account->id,
            'start_at' => $row['stat_time_day'],
            'end_at' => $row['stat_time_day'],
        ],
        [
            'spend' => $row['spend'],
            // 如有需要可额外存 impressions/clicks 等字段
        ]
    );
}

TikTok 异常处理

use Goletter\Adv\Platforms\TikTok\Exceptions\TikTokApiException;
use Goletter\Adv\Platforms\TikTok\Exceptions\TikTokTokenExpiredException;

try {
    $result = $client->get('/open_api/v1.3/advertiser/info/', [
        'advertiser_id' => 'YOUR_ADVERTISER_ID',
    ]);
} catch (TikTokTokenExpiredException $e) {
    // Access-Token 失效,需要刷新
} catch (TikTokApiException $e) {
    // 其他 TikTok API 错误
    $errorData = $e->getResponse();
}

特性

  • ✅ 流式分页处理(Generator),内存友好
  • ✅ 自动处理 Facebook 分页
  • ✅ 自动去重(基于 ID),避免重复数据
  • ✅ 完整的错误处理和异常类型
  • ✅ 支持自定义请求头
  • ✅ 支持 BM 账户查询
  • ✅ 支持多级别 insights 报告
  • ✅ 灵活的时间范围查询
  • ✅ 完整的广告系列(Campaigns)管理
  • ✅ 支持过滤条件和批量操作
  • ✅ Facebook 应用授权(dialog/oauth + 回调换长期 Token)