瀏覽代碼

获取素材消耗

PC-202304251453\Administrator 5 月之前
父節點
當前提交
d1acc2c173

+ 67 - 0
app/command/CostGdtVideo.php

@@ -0,0 +1,67 @@
+<?php
+
+namespace app\command;
+
+use app\v1\logic\tool\advertCost\GdtCostVideoLogic;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Attribute\AsCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+
+#[AsCommand('cost:gdt_video', 'cost gdt_video')]
+class CostGdtVideo extends Command
+{
+
+    /**
+     * 自定义脚本示例
+     * @return void
+     */
+    protected function configure()
+    {
+        // 用法示例 php webman cost:gdt_video date api_id advertiser_ids
+        // php webman cost:gdt_video 2025-07-30,2025-07-31 0 54970254,60853999
+        $this->addArgument('date', InputArgument::OPTIONAL, '日期范围');
+        $this->addArgument('api_id', InputArgument::OPTIONAL, '需要拉取的API配置ID');
+        $this->addArgument('advertiser_ids', InputArgument::OPTIONAL, '需要拉取的广告账户');
+    }
+
+    /**
+     * @param InputInterface $input
+     * @param OutputInterface $output
+     * @return int
+     */
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $ydate = date('Y-m-d', strtotime('-1 day'));
+        $date = $input->getArgument('date');
+        $date = $date ?: $ydate;
+
+        $apiId = (int)$input->getArgument('api_id');
+        $advertiserIds = $input->getArgument('advertiser_ids');
+
+        $params = [];
+
+        if($date){
+            $params['date'] = explode(',', $date);
+        }
+
+        if($apiId){
+            $params['api_id'] = $apiId;
+        }
+
+        if($advertiserIds){
+            $params['advertiser_ids'] = explode(',', $advertiserIds);
+        }
+
+        $output->writeln("\ngdt cost sta:" . json_encode($params, JSON_UNESCAPED_UNICODE));
+
+        $res = (new GdtCostVideoLogic)->run($params);
+
+        $output->writeln("\ngdt cost end:" . $res);
+
+        return self::SUCCESS;
+    }
+
+}

+ 66 - 0
app/command/CostTtVideo.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace app\command;
+
+use app\v1\logic\tool\advertCost\TtCostVideoLogic;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Attribute\AsCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+
+#[AsCommand('cost:tt_video', 'cost tt_video')]
+class CostTtVideo extends Command
+{
+    /**
+     * 自定义脚本示例
+     * @return void
+     */
+    protected function configure()
+    {
+        // 用法示例 php webman cost:tt_video date api_id advertiser_ids
+        // php webman cost:tt_video 2025-07-30,2025-07-31 0 54970254,60853999
+        $this->addArgument('date', InputArgument::OPTIONAL, '日期范围');
+        $this->addArgument('api_id', InputArgument::OPTIONAL, '需要拉取的API配置ID');
+        $this->addArgument('advertiser_ids', InputArgument::OPTIONAL, '需要拉取的广告账户');
+    }
+
+    /**
+     * @param InputInterface $input
+     * @param OutputInterface $output
+     * @return int
+     */
+    protected function execute(InputInterface $input, OutputInterface $output): int
+    {
+        $ydate = date('Y-m-d', strtotime('-1 day'));
+        $date = $input->getArgument('date');
+        $date = $date ?: $ydate;
+
+        $apiId = (int)$input->getArgument('api_id');
+        $advertiserIds = $input->getArgument('advertiser_ids');
+
+        $params = [];
+
+        if($date){
+            $params['date'] = explode(',', $date);
+        }
+
+        if($apiId){
+            $params['api_id'] = $apiId;
+        }
+
+        if($advertiserIds){
+            $params['advertiser_ids'] = explode(',', $advertiserIds);
+        }
+
+        $output->writeln("\ntt cost sta:" . json_encode($params, JSON_UNESCAPED_UNICODE));
+
+        $res = (new TtCostVideoLogic())->run($params);
+
+        $output->writeln("\ntt cost end:" . $res);
+
+        return self::SUCCESS;
+    }
+
+}

+ 1 - 1
app/process/CreateTables.php

@@ -12,7 +12,7 @@ class CreateTables
     public function onWorkerStart(): void
     {
         // 每天的0点10执行,注意这里省略了秒位
-        new Crontab('5 * * * *', function(){
+        new Crontab('10 0 * * *', function(){
             $this->initStart();
         });
     }

+ 20 - 0
app/process/advert/GdtCostVideo.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace app\process\advert;
+use Workerman\Crontab\Crontab;
+use app\v1\logic\tool\advertCost\GdtCostVideoLogic;
+
+class GdtCostVideo
+{
+    public function onWorkerStart(): void
+    {
+        // 每天的0点10执行,注意这里省略了秒位
+        new Crontab('10 0 * * *', function() {
+            // 获取前一天的素材消耗
+            $params = [
+                'date' => [date('Y-m-d', strtotime('-1 day'))]
+            ];
+            (new GdtCostVideoLogic())->run($params);
+        });
+    }
+}

+ 20 - 0
app/process/advert/TtCostVideo.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace app\process\advert;
+use Workerman\Crontab\Crontab;
+use app\v1\logic\tool\advertCost\TtCostVideoLogic;
+
+class TtCostVideo
+{
+    public function onWorkerStart(): void
+    {
+        // 每天的0点10执行,注意这里省略了秒位
+        new Crontab('10 0 * * *', function() {
+            // 获取前一天的素材消耗
+            $params = [
+                'date' => [date('Y-m-d', strtotime('-1 day'))]
+            ];
+            (new TtCostVideoLogic())->run($params);
+        });
+    }
+}

+ 106 - 0
app/v1/logic/tool/advertCost/BaseAdvertLogic.php

@@ -4,14 +4,20 @@ namespace app\v1\logic\tool\advertCost;
 
 use app\v1\logic\advert\AgentSiteLogic;
 use GuzzleHttp\Client;
+use plugin\saiadmin\app\model\system\SystemUser;
 use support\think\Db;
 
 abstract class BaseAdvertLogic
 {
+    // 媒体消耗(按天数据)
     protected string $dateTable = "media_cost";
 
+    // 媒体消耗(小时数据)
     protected string $hourTable = "media_cost_hour";
 
+    // 媒体消耗(素材数据)
+    protected string $materialTable = "media_cost_material";
+
     protected string $date;
 
     protected array $advertiserIds=[];
@@ -19,6 +25,7 @@ abstract class BaseAdvertLogic
     protected array $siteMap=[];
 
     protected int $apiId=0;
+    private array $adminUserMap=[];
 
     public function run($params=[])
     {
@@ -46,6 +53,7 @@ abstract class BaseAdvertLogic
         }
 
         $this->siteMap = (new AgentSiteLogic())->getSiteAuth();
+        $this->adminUserMap = (new SystemUser())->where("nickname","<>", "")->column("nickname", "id");
 
         for ($date = $sDate; $date <= $eDate; $date = date('Y-m-d', strtotime($date . '+1 day'))){
             $this->date = $date;
@@ -71,4 +79,102 @@ abstract class BaseAdvertLogic
     }
 
     abstract protected function initStart();
+
+    // Todo 从广告名称中拆分归因数据
+    protected function getSiteInfo($adName): array
+    {
+        $match = [];
+
+        preg_match("/([\d]*)_([\d]*)_([\d]*)/", $adName, $match);
+
+        $siteInfo = [
+            "game_id" => $match[1] ?? 0,
+            "agent_id" => $match[2] ?? 0,
+            "site_id" => $match[3] ?? 0,
+        ];
+
+        unset($match);
+
+        return $siteInfo;
+    }
+
+    // Todo 从素材名称中获取归属人
+    protected function getAuthorId($materialName): int|string
+    {
+        $auth_id = 0;
+        foreach ($this->adminUserMap as $id => $name) {
+            $name = mb_substr($name, -2); // 用姓名后两个字匹配,因为有的人素材填写喜欢不带姓
+            if ($name && strstr($materialName, $name)) {
+                $auth_id = $id;
+                break;
+            }
+        }
+
+        return $auth_id;
+    }
+
+    // 获取需要拉取消耗的媒体账户
+    protected function getTtAccountList(): array
+    {
+        $table = "ad_jrtt_account_list";
+        $where = [
+            "status" => 1
+        ];
+
+        if($this->advertiserIds){
+            $where['advertiser_id'] = $this->advertiserIds;
+        }
+
+        return Db::connect('db_advert')
+            ->table($table)
+            ->where($where)
+            ->column("pmid, advertiser_id, advertiser_name, son_fandian");
+    }
+
+    protected function getTtTokenMap(): array
+    {
+        $table = "ad_jrtt_account";
+        $where = [
+            "status" => 1
+        ];
+
+        if($this->apiId){
+            $where['id'] = $this->apiId;
+        }
+
+        return Db::connect('db_advert')->table($table)->where($where)->column("access_token", "id");
+    }
+
+
+    // 获取需要拉取消耗的媒体账户
+    protected function getGdtAccountList(): array
+    {
+        $table = "ad_gdt_account_list";
+        $where = [
+            "status" => 1
+        ];
+
+        if($this->advertiserIds){
+            $where['advertiser_id'] = $this->advertiserIds;
+        }
+
+        return Db::connect('db_advert')
+            ->table($table)
+            ->where($where)
+            ->column("pmid, advertiser_id, advertiser_name, son_fandian");
+    }
+
+    protected function getGdtTokenMap(): array
+    {
+        $table = "ad_gdt_account";
+        $where = [
+            "status" => 1
+        ];
+
+        if($this->apiId){
+            $where['id'] = $this->apiId;
+        }
+
+        return Db::connect('db_advert')->table($table)->where($where)->column("access_token", "id");
+    }
 }

+ 21 - 54
app/v1/logic/tool/advertCost/GdtCostHourLogic.php

@@ -29,7 +29,7 @@ class GdtCostHourLogic extends BaseAdvertLogic
     }
 
     // 获取媒体消耗
-    protected function getGdtCost($advertiser_id, $accessToken): array
+    protected function getGdtCost($advertiserId, $accessToken): array
     {
         $page = 1;
         $data = [];
@@ -37,7 +37,7 @@ class GdtCostHourLogic extends BaseAdvertLogic
             $nonce = md5(time().rand(00000, 99999));
             $url = 'https://api.e.qq.com/v3.0/hourly_reports/get?access_token='.$accessToken.'&timestamp='.time().'&nonce='.$nonce;
             $request_data = [
-                'account_id' => $advertiser_id,
+                'account_id' => $advertiserId,
                 'level'      => 'REPORT_LEVEL_ADGROUP',
                 'date_range' => json_encode(['start_date' => $this->date, 'end_date' => $this->date]),
                 'group_by'   => json_encode(['hour', 'adgroup_id']),
@@ -73,26 +73,25 @@ class GdtCostHourLogic extends BaseAdvertLogic
 
         $adData = [];
         foreach ($dataList as $val) {
-            $ad_id   = $val['adgroup_id'];
-            $ad_name = $val['adgroup_name'];
+            $adId   = $val['adgroup_id'];
+            $adName = $val['adgroup_name'];
             $cost    = $val['cost']/100;
+            if($cost==0) continue;
+
             $show    = $val['view_count'];
             $click   = $val['valid_click_count'];
             $convert = $val['activated_count'];
 
             // Todo 从广告名称中拆分归因数据
-            preg_match("/([\d]*)_([\d]*)_([\d]*)/", $ad_name, $matchs);
-            $game_id = $matchs[1] ?? 0;
-            $agent_id = $matchs[2] ?? 0;
-            $site_id = $matchs[3] ?? 0;
+            $siteInfo = $this->getSiteInfo($adName);
 
-            if(!$game_id || empty($this->siteMap[$site_id])) continue;
+            if(!$siteInfo['game_id'] || empty($this->siteMap[$siteInfo['site_id']])) continue;
 
             $where = [
-                'ad_id'   => $ad_id,
-                'game_id' => $game_id,
-                'agent_id'=> $agent_id,
-                'site_id' => $site_id,
+                'ad_id'   => $adId,
+                'game_id' => $siteInfo['game_id'],
+                'agent_id'=> $siteInfo['agent_id'],
+                'site_id' => $siteInfo['site_id'],
                 'tdate'   => $this->date,
                 'thour'   => (int)$val['hour'],
             ];
@@ -104,8 +103,8 @@ class GdtCostHourLogic extends BaseAdvertLogic
                 'ad_convert' => $convert,
                 'ori_money' => $cost,
                 'money' => $cost * $fandianRate,
-                'media_id' => $this->siteMap[$site_id]['media_id'],
-                'auth_id' => $this->siteMap[$site_id]['auth_id'],
+                'media_id' => $this->siteMap[$siteInfo['site_id']]['media_id'],
+                'auth_id' => $this->siteMap[$siteInfo['site_id']]['auth_id'],
             ];
 
             $hourId= $db->table($this->hourTable)->where($where)->value("id");
@@ -116,11 +115,11 @@ class GdtCostHourLogic extends BaseAdvertLogic
             $db->table($this->hourTable)->save(array_merge($hourData, $where));
 
             // 计算天的数据
-            $adKey = $game_id . "_" . $site_id . "_" . $ad_id;
-            $adData[$adKey]['game_id'] = $game_id;
-            $adData[$adKey]['agent_id'] = $agent_id;
-            $adData[$adKey]['site_id'] = $site_id;
-            $adData[$adKey]['ad_id'] = $ad_id;
+            $adKey = $siteInfo['game_id'] . "_" . $siteInfo['site_id'] . "_" . $adId;
+            $adData[$adKey]['game_id'] = $siteInfo['game_id'];
+            $adData[$adKey]['agent_id'] = $siteInfo['agent_id'];
+            $adData[$adKey]['site_id'] = $siteInfo['site_id'];
+            $adData[$adKey]['ad_id'] = $adId;
             $adData[$adKey]['ad_show'] = !empty($adData[$adKey]['ad_show']) ? $adData[$adKey]['ad_show']+$show : $show;
             $adData[$adKey]['ad_click'] = !empty($adData[$adKey]['ad_click']) ? $adData[$adKey]['ad_click']+$click : $click;
             $adData[$adKey]['ad_convert'] = !empty($adData[$adKey]['ad_convert']) ? $adData[$adKey]['ad_convert']+$convert : $convert;
@@ -143,8 +142,8 @@ class GdtCostHourLogic extends BaseAdvertLogic
                 'ad_convert'=> $value['ad_convert'],
                 'ori_money' => $value['cost'],
                 'money'     => $value['cost']*$fandianRate,
-                'media_id' => $this->siteMap[$site_id]['media_id'],
-                'auth_id' => $this->siteMap[$site_id]['auth_id'],
+                'media_id' => $this->siteMap[$siteInfo['site_id']]['media_id'] ?? 0,
+                'auth_id' => $this->siteMap[$siteInfo['site_id']]['auth_id'] ?? 0,
             ];
 
             $dateId= $db->table($this->dateTable)->where($where)->value("id");
@@ -155,36 +154,4 @@ class GdtCostHourLogic extends BaseAdvertLogic
             $db->table($this->dateTable)->save(array_merge($dateData, $where));
         }
     }
-
-    // 获取需要拉取消耗的媒体账户
-    protected function getGdtAccountList(): array
-    {
-        $table = "ad_gdt_account_list";
-        $where = [
-            "status" => 1
-        ];
-
-        if($this->advertiserIds){
-            $where['advertiser_id'] = $this->advertiserIds;
-        }
-
-        return Db::connect('db_advert')
-            ->table($table)
-            ->where($where)
-            ->column("pmid, advertiser_id, advertiser_name, son_fandian");
-    }
-
-    protected function getGdtTokenMap(): array
-    {
-        $table = "ad_gdt_account";
-        $where = [
-            "status" => 1
-        ];
-
-        if($this->apiId){
-            $where['id'] = $this->apiId;
-        }
-
-        return Db::connect('db_advert')->table($table)->where($where)->column("access_token", "id");
-    }
 }

+ 185 - 0
app/v1/logic/tool/advertCost/GdtCostVideoLogic.php

@@ -0,0 +1,185 @@
+<?php
+
+namespace app\v1\logic\tool\advertCost;
+
+use GuzzleHttp\Client;
+use support\think\Db;
+
+class GdtCostVideoLogic extends BaseAdvertLogic
+{
+    // 执行正文
+    protected function initStart(): void
+    {
+        $tokenMap = $this->getGdtTokenMap();
+
+        $accountList = $this->getGdtAccountList();
+
+        // 循环执行
+        foreach ($accountList as $account)
+        {
+            $accessToken = $tokenMap[$account['pmid']] ?? "";
+            if(!$accessToken) continue;
+
+            // 获取消耗
+            $dataList = $this->getGdtVideoCost($account['advertiser_id'], $accessToken);
+
+            // 数据加工,获取素材信息
+            if($dataList){
+                $videoMap = $this->getGdtVideoNameMap($account['advertiser_id'], $accessToken, $dataList);
+                // 整理数据入库
+                $this->organizeDataList($account, $dataList, $videoMap);
+            }
+        }
+    }
+
+    // 获取媒体消耗
+    protected function getGdtVideoCost($advertiserId, $accessToken): array
+    {
+        $page = 1;
+        $data = [];
+        do {
+            $nonce = md5(time().rand(00000, 99999));
+            $url = 'https://api.e.qq.com/v3.0/daily_reports/get?access_token='.$accessToken.'&timestamp='.time().'&nonce='.$nonce;
+
+            $request_data = [
+                'account_id' => $advertiserId,
+                'level'      => 'REPORT_LEVEL_MATERIAL_VIDEO',
+                'date_range' => json_encode(['start_date'=>$this->date,'end_date'=>$this->date]),
+                'group_by'   => json_encode(['date','video_id','adgroup_id']),
+                'fields'     => json_encode(['date','video_id','adgroup_id','adgroup_name', 'view_count','valid_click_count','cost', 'activated_count','download_count','first_pay_count','video_outer_play_count','video_outer_play100_count']),
+                'page'       => $page,
+                'page_size'  => 1000,
+            ];
+
+            $url = $url."&".http_build_query($request_data);
+            $httpClient = new Client(['timeout' => 10]);
+            $res = $httpClient->request('GET', $url);
+            $result = json_decode($res->getBody(), true);
+
+            if (empty($result['data']['list'])) {
+                break;
+            }
+
+            $data = array_merge($data, $result['data']['list']);
+            $totalPage = $result['data']['page_info']['total_page'] ?? 1;
+            $page++;
+        } while ($page <= $totalPage);
+
+        return $data;
+    }
+
+    // 获取素材id信息 $mediaIds 最多100个
+    protected function getGdtVideo($advertiserId, $accessToken, $videoIds): array
+    {
+        $nonce = md5(time().rand(00000, 99999));
+        $url = 'https://api.e.qq.com/v3.0/videos/get?access_token='.$accessToken.'&timestamp='.time().'&nonce='.$nonce;
+
+        $request_data = [
+            'account_id' => $advertiserId,
+            'filtering' => json_encode([
+                [
+                    'field' => 'media_id',
+                    'operator' => 'IN',
+                    'values' => $videoIds,
+                ]
+            ]),
+        ];
+
+        $url = $url."&".http_build_query($request_data);
+        $httpClient = new Client(['timeout' => 10]);
+        $res = $httpClient->request('GET', $url);
+        $result = json_decode($res->getBody(), true);
+
+        return $result['data']['list'] ?? [];
+    }
+
+    protected function getGdtVideoNameMap($advertiserId, $accessToken, $dataList)
+    {
+        if(!$dataList) return $dataList;
+
+        $videoMaterialIds = array_column($dataList, "video_id");
+
+        // 判断是否超过100条,切割处理
+        $videoIdList = array_chunk($videoMaterialIds, 100);
+        $videoList = [];
+        foreach ($videoIdList as $videoIds) {
+            $videos = $this->getGdtVideo($advertiserId, $accessToken, $videoIds);
+            $videoList = array_merge($videoList, $videos);
+        }
+
+        $videoData = [];
+        if($videoList) {
+            foreach ($videoList as $val){
+                $videoData[$val['video_id']] = $val;
+            }
+        }
+
+        return $videoData;
+    }
+
+    // 整理入库数据列表
+    protected function organizeDataList($account, $dataList, $videoMap): void
+    {
+        if(!$dataList) return;
+
+        $db = Db::connect('db_advert');
+        // 返点率
+        $fandianRate = ($account['son_fandian']>1) ? (1/$account['son_fandian']) : $account['son_fandian'];
+
+        foreach ($dataList as $val) {
+
+            $cost    = $val['cost']/100;
+            if($cost==0) continue;
+
+            $adId   = $val['adgroup_id'];
+            $adName = $val['adgroup_name'];
+            $materialId = $val['video_id'];
+            $materialName = $videoMap[$materialId]["description"] ?? "";
+            $materialUrl = $videoMap[$materialId]["preview_url"] ?? "";
+            $materialImg = $videoMap[$materialId]['key_frame_image_url'] ?? "";
+            // Todo 从素材名称中获取归属人
+            $authorId = $this->getAuthorId($materialName);
+
+            // Todo 从广告名称中拆分归因数据
+            $siteInfo = $this->getSiteInfo($adName);
+
+            if(!$siteInfo['game_id'] || empty($this->siteMap[$siteInfo['site_id']])) continue;
+
+            $materialData = [
+                'ad_id'   => $adId,
+                'game_id' => $siteInfo['game_id'],
+                'agent_id'=> $siteInfo['agent_id'],
+                'site_id' => $siteInfo['site_id'],
+                'media_id' => $this->siteMap[$siteInfo['site_id']]['media_id'] ?? 0,
+                'auth_id' => $this->siteMap[$siteInfo['site_id']]['auth_id'] ?? 0,
+                'material_name' => $materialName,
+                'material_url' => $materialUrl,
+                'material_img' => $materialImg,
+                'ori_money' => $cost,
+                'money' => $cost*$fandianRate,
+                'show' => $val['view_count'],
+                'click' => $val['valid_click_count'],
+                'total_play' => $val['video_outer_play_count'],
+                'valid_play' => $val['video_outer_play100_count'],
+                'download' => $val['download_count'],
+                'active' => $val['activated_count'],
+                'pay_count' => $val['first_pay_count'],
+                'author_id' => $authorId
+            ];
+
+            $where = [
+                'tdate' => $this->date,
+                'ad_id' => $adId,
+                'material_id'   => $materialId,
+                'advertiser_id' => $account['advertiser_id'],
+            ];
+
+            $findId= $db->table($this->materialTable)->where($where)->value("id");
+            if($findId) {
+                $materialData['id'] = $findId;
+            }
+            // 保存数据
+            $db->table($this->materialTable)->save(array_merge($materialData, $where));
+        }
+    }
+}

+ 18 - 17
app/v1/logic/tool/advertCost/TtCostHourLogic.php

@@ -29,14 +29,14 @@ class TtCostHourLogic extends BaseAdvertLogic
     }
 
     // 获取媒体消耗
-    protected function getTtCost($advertiser_id, $accessToken): array
+    protected function getTtCost($advertiserId, $accessToken): array
     {
         $page = 1;
         $data = [];
         do {
             $url = 'https://ad.oceanengine.com/open_api/v3.0/report/custom/get/';
             $request_data = [
-                'advertiser_id'=>$advertiser_id,
+                'advertiser_id'=>$advertiserId,
                 'dimensions'=>json_encode(["cdp_project_id","cdp_project_name","ad_platform_cdp_project_action_track_url","ad_platform_cdp_project_download_url","stat_time_hour"]),
                 'metrics'=>json_encode(["stat_cost","show_cnt","click_cnt","convert_cnt"]),
                 'filters'=>'[{"operator":4,"values":["0"],"field":"stat_cost","type":3}]',
@@ -84,18 +84,18 @@ class TtCostHourLogic extends BaseAdvertLogic
             $cost = $val['metrics']['stat_cost'];
             if($cost=='0.00') continue;
 
-            $ad_id   = $val['dimensions']['cdp_project_id'];
-            $ad_name = $val['dimensions']['cdp_project_name'];
+            $adId   = $val['dimensions']['cdp_project_id'];
+            $adName = $val['dimensions']['cdp_project_name'];
             $show    = $val['metrics']['show_cnt'];
             $click   = $val['metrics']['click_cnt'];
             $convert = $val['metrics']['convert_cnt'];
             $hour = (int)explode(' ',$val['dimensions']['stat_time_hour'])[1];
 
             // Todo 从广告名称中拆分归因数据
-            preg_match("/([\d]*)_([\d]*)_([\d]*)/", $ad_name, $match);
-            $game_id = $match[1] ?? 0;
-            $agent_id = $match[2] ?? 0;
-            $site_id = $match[3] ?? 0;
+            $siteInfo = $this->getSiteInfo($adName);
+            $game_id = $siteInfo['game_id'] ?? 0;
+            $agent_id = $siteInfo['agent_id'] ?? 0;
+            $site_id = $siteInfo['site_id'] ?? 0;
 
             // 广告名获取不到则获取监测链接或者下载链接
             if(!$game_id || empty($this->siteMap[$site_id])){
@@ -103,16 +103,17 @@ class TtCostHourLogic extends BaseAdvertLogic
                 if(!$tjurl){
                     $tjurl = $val['dimensions']['ad_platform_cdp_project_download_url'];
                 }
-                preg_match("/([\d]*)_([\d]*)_([\d]*)/", $tjurl, $urlMatch);
-                $game_id = $urlMatch[1] ?? 0;
-                $agent_id = $urlMatch[2] ?? 0;
-                $site_id = $urlMatch[3] ?? 0;
+
+                $siteInfo = $this->getSiteInfo($tjurl);
+                $game_id = $siteInfo['game_id'] ?? 0;
+                $agent_id = $siteInfo['agent_id'] ?? 0;
+                $site_id = $siteInfo['site_id'] ?? 0;
             }
 
             if(!$game_id || empty($this->siteMap[$site_id])) continue;
 
             $where = [
-                'ad_id'   => $ad_id,
+                'ad_id'   => $adId,
                 'game_id' => $game_id,
                 'agent_id'=> $agent_id,
                 'site_id' => $site_id,
@@ -127,8 +128,8 @@ class TtCostHourLogic extends BaseAdvertLogic
                 'ad_convert' => $convert,
                 'ori_money' => $cost,
                 'money' => $cost * $fandianRate,
-                'media_id' => $this->siteMap[$site_id]['media_id'],
-                'auth_id' => $this->siteMap[$site_id]['auth_id'],
+                'media_id' => $this->siteMap[$site_id]['media_id'] ?? 0,
+                'auth_id' => $this->siteMap[$site_id]['auth_id'] ?? 0,
             ];
 
             $hourId= $db->table($this->hourTable)->where($where)->value("id");
@@ -139,11 +140,11 @@ class TtCostHourLogic extends BaseAdvertLogic
             $db->table($this->hourTable)->save(array_merge($hourData, $where));
 
             // 计算天的数据
-            $adKey = $game_id . "_" . $site_id . "_" . $ad_id;
+            $adKey = $game_id . "_" . $site_id . "_" . $adId;
             $adData[$adKey]['game_id'] = $game_id;
             $adData[$adKey]['agent_id'] = $agent_id;
             $adData[$adKey]['site_id'] = $site_id;
-            $adData[$adKey]['ad_id'] = $ad_id;
+            $adData[$adKey]['ad_id'] = $adId;
             $adData[$adKey]['ad_show'] = !empty($adData[$adKey]['ad_show']) ? $adData[$adKey]['ad_show']+$show : $show;
             $adData[$adKey]['ad_click'] = !empty($adData[$adKey]['ad_click']) ? $adData[$adKey]['ad_click']+$click : $click;
             $adData[$adKey]['ad_convert'] = !empty($adData[$adKey]['ad_convert']) ? $adData[$adKey]['ad_convert']+$convert : $convert;

+ 205 - 0
app/v1/logic/tool/advertCost/TtCostVideoLogic.php

@@ -0,0 +1,205 @@
+<?php
+
+namespace app\v1\logic\tool\advertCost;
+
+use GuzzleHttp\Client;
+use support\think\Db;
+
+class TtCostVideoLogic extends BaseAdvertLogic
+{
+    // 执行正文
+    protected function initStart(): void
+    {
+        $tokenMap = $this->getTtTokenMap();
+
+        $accountList = $this->getTtAccountList();
+
+        // 循环执行
+        foreach ($accountList as $account)
+        {
+            $accessToken = $tokenMap[$account['pmid']] ?? "";
+            if(!$accessToken) continue;
+
+            // 获取消耗
+            $dataList = $this->getTtVideoCost($account['advertiser_id'], $accessToken);
+
+            // 数据加工,获取素材信息
+            if($dataList){
+                $videoMap = $this->getTtVideoNameMap($account['advertiser_id'], $accessToken, $dataList);
+
+                // 整理数据入库
+                $this->organizeDataList($account, $dataList, $videoMap);
+            }
+        }
+    }
+
+    // 获取媒体消耗
+    protected function getTtVideoCost($advertiserId, $accessToken): array
+    {
+        $page = 1;
+        $data = [];
+        do {
+            $url = 'https://ad.oceanengine.com/open_api/v3.0/report/custom/get/';
+
+            $request_data = [
+                'advertiser_id'=>$advertiserId,
+                'dimensions'=>json_encode(["ad_platform_material_name","material_id","cdp_project_id","cdp_project_name","stat_time_day", "ad_platform_cdp_project_action_track_url"]),
+                'metrics'=>json_encode(["stat_cost","show_cnt","click_cnt","active","active_register","active_pay","valid_play","download_finish_cnt","total_play", "stat_pay_amount"]),
+                'filters'=>'[{"field":"image_mode","type":1,"operator":0,"values":["5","15"]},{"operator":4,"values":["0"],"field":"stat_cost","type":3}]',
+                'start_time'=>$this->date,
+                'end_time'=>$this->date,
+                'order_by'=>json_encode([]),
+                'page'=>1,
+                'page_size'=>100,
+            ];
+
+            $options = [
+                "headers" => [
+                    'Access-Token' => $accessToken,
+                    'Content-Type' => 'application/json',
+                ]
+            ];
+            $url = $url."?".http_build_query($request_data);
+
+            $httpClient = new Client(['timeout' => 10]);
+            $res = $httpClient->request('GET', $url, $options);
+            $result = json_decode($res->getBody(), true);
+
+            if (empty($result['data']['rows'])) {
+                break;
+            }
+
+            $data = array_merge($data, $result['data']['rows']);
+            $totalPage = $result['data']['page_info']['total_page'] ?? 1;
+            $page++;
+        } while ($page <= $totalPage);
+
+        return $data;
+    }
+
+    protected function getTtVideo($advertiserId, $accessToken, $materialIds): array
+    {
+
+        $url = 'https://api.oceanengine.com/open_api/2/file/video/get/';
+        $request_data = [
+            'advertiser_id'=>$advertiserId,
+            'filtering' => json_encode([
+                "material_ids" => $materialIds,
+            ]),
+        ];
+
+        $options = [
+            "headers" => [
+                'Access-Token' => $accessToken,
+                'Content-Type' => 'application/json',
+            ]
+        ];
+        $url = $url."?".http_build_query($request_data);
+
+        $httpClient = new Client(['timeout' => 10]);
+        $res = $httpClient->request('GET', $url, $options);
+        $result = json_decode($res->getBody(), true);
+
+        return $result['data']['list'] ?? [];
+    }
+
+    protected function getTtVideoNameMap($advertiserId, $accessToken, $dataList): array
+    {
+        if(!$dataList) return $dataList;
+
+        $videoMaterialIds = [];
+        foreach ($dataList as $vv){
+            $videoMaterialIds[] = $vv['dimensions']['material_id'];
+        }
+
+        // 判断是否超过100条,切割处理
+        $videoIdList = array_chunk($videoMaterialIds, 100);
+        $videoList = [];
+        foreach ($videoIdList as $videoIds) {
+            $videos = $this->getTtVideo($advertiserId, $accessToken, $videoIds);
+            $videoList = array_merge($videoList, $videos);
+        }
+
+        $videoData = [];
+        if($videoList) {
+            foreach ($videoList as $val){
+                $videoData[$val['material_id']] = $val;
+            }
+        }
+
+        return $videoData;
+    }
+
+    // 整理入库数据列表
+    protected function organizeDataList($account, $dataList, $videoMap): void
+    {
+        if(!$dataList) return;
+
+        $db = Db::connect('db_advert');
+        // 返点率
+        $fandianRate = ($account['son_fandian']>1) ? (1/$account['son_fandian']) : $account['son_fandian'];
+
+        foreach ($dataList as $val) {
+            $cost = $val['metrics']['stat_cost'];
+            if($cost=='0.00') continue;
+            $materialId = $val['dimensions']['material_id'];
+            $materialName = $val['dimensions']['ad_platform_material_name'];
+            $adId   = $val['dimensions']['cdp_project_id'];
+            $adName = $val['dimensions']['cdp_project_name'];
+            $materialUrl = $videoMap[$materialId]['url'] ?? "";
+            $materialImg = $videoMap[$materialId]['poster_url'] ?? "";
+
+            // Todo 从素材名称中获取归属人
+            $authorId = $this->getAuthorId($materialName);
+
+            // Todo 从广告名称中拆分归因数据
+            $siteInfo = $this->getSiteInfo($adName);
+
+            if(!$siteInfo['game_id'] || empty($this->siteMap[$siteInfo['site_id']])){
+                $trackUrl = $val['dimensions']['ad_platform_cdp_project_action_track_url'] ?? "";
+                $siteInfo = $this->getSiteInfo($trackUrl);
+            }
+
+            if(!$siteInfo['game_id'] || empty($this->siteMap[$siteInfo['site_id']])) continue;
+
+            $materialData = [
+                'ad_id'   => $adId,
+                'game_id' => $siteInfo['game_id'],
+                'agent_id'=> $siteInfo['agent_id'],
+                'site_id' => $siteInfo['site_id'],
+                'media_id' => $this->siteMap[$siteInfo['site_id']]['media_id'] ?? 0,
+                'auth_id' => $this->siteMap[$siteInfo['site_id']]['auth_id'] ?? 0,
+                'material_name' => $materialName,
+                'material_url' => $materialUrl,
+                'material_img' => $materialImg,
+                'ori_money' => $cost,
+                'money' => $cost*$fandianRate,
+                'show' => $val['metrics']['show_cnt'],
+                'click' => $val['metrics']['click_cnt'],
+                'total_play' => $val['metrics']['total_play'],
+                'valid_play' => $val['metrics']['valid_play'],
+                'download' => $val['metrics']['download_finish_cnt'],
+                'active' => $val['metrics']['active'],
+                'register' => $val['metrics']['active_register'],
+                'pay_count' => $val['metrics']['active_pay'],
+                'pay_amount' => $val['metrics']['stat_pay_amount'],
+                'author_id' => $authorId
+            ];
+
+            $where = [
+                'tdate' => $this->date,
+                'ad_id' => $adId,
+                'material_id'   => $materialId,
+                'advertiser_id' => $account['advertiser_id'],
+            ];
+
+            $findId= $db->table($this->materialTable)->where($where)->value("id");
+            if($findId) {
+                $materialData['id'] = $findId;
+            }
+            // 保存数据
+            $db->table($this->materialTable)->save(array_merge($materialData, $where));
+        }
+    }
+
+}

+ 0 - 1
plugin/saiadmin/app/logic/system/SystemUserLogic.php

@@ -258,5 +258,4 @@ class SystemUserLogic extends BaseLogic
         }
         parent::edit($id, $data);
     }
-
 }