Explorar el Código

Merge branch 'liuzj-wechat-check-dev' into stable

# Conflicts:
#	app/Console/Kernel.php
liuzejian hace 1 año
padre
commit
5f960d8d5f

+ 5 - 6
app/Console/Commands/Miniprogram/WechatAccessToken.php

@@ -46,13 +46,12 @@ class WechatAccessToken extends Command
             if($ttl > 600) {
                 continue;
             }
-            try {
-                $accessTokenInfo = AccessTokenService::getAccessToken($miniprogram->appid, $miniprogram->appsecret);
-            } catch (\Exception $exception) {
-                myLog('wechat-miniprogram')->info('刷新小程序accessToken失败', [
+
+            $accessTokenInfo = AccessTokenService::getAccessToken($miniprogram->appid, $miniprogram->appsecret);
+            if(false === $accessTokenInfo || (0 != ($accessTokenInfo['errcode'] ?? 0))) {
+                myLog('WechatAccessToken')->info('刷新小程序accessToken失败', [
                     'appid' => $miniprogram->appid,
-                    'exceptionMessage' => $exception->getMessage(),
-                    'exceptionCode' => $exception->getCode()
+                    'result' => $accessTokenInfo,
                 ]);
                 continue;
             }

+ 87 - 0
app/Console/Commands/Video/WechatCheck/GetTaskInfo.php

@@ -0,0 +1,87 @@
+<?php
+
+namespace App\Console\Commands\Video\WechatCheck;
+
+use App\Service\Miniprogram\Wechat\AccessTokenService;
+use App\Service\Util\Support\Http\HttpRequestService;
+use App\Service\Util\Support\Http\WechatURL;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Redis;
+use Modules\Common\Support\Http\HttpRequest;
+use Modules\Manage\Services\WechatMiniprogramService;
+use Modules\Video\Services\WechatCheckSyncService;
+
+class GetTaskInfo extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'WechatCheck:GetTaskInfo {--task_ids= : 英文逗号分割的任务id}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '查询短剧同步到微信的任务状态';
+
+    /**
+     * Execute the console command.
+     */
+    public function handle(): void
+    {
+        $task_ids = $this->option('task_ids');
+        $taskIdArr = null;
+        if($task_ids) {
+            $taskIdArr = explode(',', trim($task_ids, ','));
+        }
+        DB::table('video_series_wechat_check')
+            ->whereIn('sync_status', [1,2])
+            ->where('sync_task_id', '<>', '')
+            ->where('is_enabled', 1)
+            ->when($taskIdArr, function ($query, $taskIdArr) {
+                return $query->whereIn('sync_task_id', $taskIdArr);
+            })->orderBy('id')
+            ->chunk(100, function ($items) {
+                $now = date('Y-m-d H:i:s');
+                foreach ($items as $item) {
+                    $taskInfo = $this->getTask($item);
+                    if($taskInfo && 1 == $taskInfo['task_type']) {
+                        if(in_array($taskInfo['status'], [3,4])) {
+                            DB::table('video_series_wechat_check')
+                                ->where(['id' => $item->id])
+                                ->update([
+                                    'status' => $taskInfo['status'],
+                                    'remark' => $taskInfo['errmsg'] ?? '',
+                                    'media_id' => $taskInfo['media_id'] ?? '',
+                                    'updated_at' => $now,
+                                    'sync_task_info' => \json_encode($taskInfo),
+                                ]);
+                        }
+                    }
+                }
+            });
+    }
+
+    private function getTask($syncInfo) {
+        $appid = $syncInfo->appid ?: config('wechat.duanju.masterAppid');
+        $accessToken = Redis::get(AccessTokenService::getAccessTokenRedisKey($appid));
+        $parsedContent = HttpRequestService::simplePost(WechatURL::vod_gettask . $accessToken, [
+            'task_id' => $syncInfo->sync_task_id
+        ]);
+        if(false === $parsedContent || (0 != $parsedContent['errcode'] ?? 0)) {
+            return $parsedContent['task_info'];
+        } else {
+            myLog('GetTaskInfo')->error('拉取上传短剧任务查询失败', [
+                'task_id' => $syncInfo->sync_task_id,
+                'appid' => $appid,
+                'result' => $parsedContent,
+            ]);
+            return [];
+        }
+
+    }
+}

+ 87 - 0
app/Console/Commands/Video/WechatCheck/SyncDramaInfo.php

@@ -0,0 +1,87 @@
+<?php
+
+namespace App\Console\Commands\Video\WechatCheck;
+
+use App\Service\Miniprogram\Wechat\AccessTokenService;
+use App\Service\Util\Support\Http\WechatURL;
+use App\Service\Util\Support\Http\HttpRequestService;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+
+class SyncDramaInfo extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'WechatCheck:SyncDramaInfo {--drama_ids= : 剧目ids}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '微信剧目提审-获取剧目信息';
+
+    /**
+     * Execute the console command.
+     */
+    public function handle()
+    {
+        $drama_ids = $this->option('drama_ids');
+        if($drama_ids) {
+            $dramaIds = explode(',', $drama_ids);
+        }
+        DB::table('video_wechat_check')
+            ->when($dramaIds, function ($query, $dramaIds) {
+                return $query->whereIn('drama_id', $dramaIds);
+            })
+            ->where('drama_id', '<>', 0)
+            ->where([
+                'status' => 1,
+                'is_enabled' => 1
+            ])
+            ->select('drama_id', 'id', 'status', 'appid', 'video_id')
+            ->orderBy('id')
+            ->chunk(100, function ($items) {
+                foreach ($items as $item) {
+                    $accessToken = $this->getAccessToken($item->appid ?: config('wechat.duanju.masterAppid'));
+                    $result = HttpRequestService::simplePost(WechatURL::vod_getdrama. $accessToken, [
+                        'drama_id' => $item->drama_id
+                    ]);
+                    if(false === $result || (0 != ($result['errcode'] ?? 0))) {
+                        myLog('SyncDramaInfo')->error('获取剧目信息失败', [
+                            'id' => $item->id, 'drama_id' => $item->drama_id,
+                            'result' => $result,
+                        ]);
+                        continue;
+                    }
+                    $status = $result['drama_info']['audit_detail']['status'];
+                    $now = date('Y-m-d H:i:s');
+                    if($status != $item->status)
+                    {
+                        DB::table('video_wechat_check')
+                            ->where('id', $item->id)
+                            ->update([
+                                'check_at' => date('Y-m-d H:i:s', $result['drama_info']['audit_detail']['audit_time']),
+                                'status' => $status,
+                                'updated_at' => $now,
+                            ]);
+                    }
+                    if(3 == $status) {
+                        DB::table('videos')
+                            ->where('id', $item->video_id)
+                            ->update([
+                                'wechat_pass' => 1,
+                                'updated_at' => $now,
+                            ]);
+                    }
+                }
+            });
+    }
+
+    private function getAccessToken($appid) {
+        return Redis::get(AccessTokenService::getAccessTokenRedisKey($appid));
+    }
+}

+ 92 - 0
app/Console/Commands/Video/WechatCheck/SyncMediaInfo.php

@@ -0,0 +1,92 @@
+<?php
+
+namespace App\Console\Commands\Video\WechatCheck;
+
+use App\Service\Miniprogram\Wechat\AccessTokenService;
+use App\Service\Util\Support\Http\HttpRequestService;
+use App\Service\Util\Support\Http\WechatURL;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Redis;
+use Predis\Command\Traits\DB;
+
+class SyncMediaInfo extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'WechatCheck:SyncMediaInfo {--video_ids= : videos.id}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '获取短剧分集详细信息';
+
+    /**
+     * Execute the console command.
+     */
+    public function handle()
+    {
+        $video_ids = $this->option('video_ids');
+        $videoIds = [];
+        if($video_ids) {
+            $videoIds = explode(',', $video_ids);
+        }
+
+        DB::table('video_wechat_check')
+            ->where('status', '<>', 0)
+            ->where('is_enabled', 1)
+            ->where('drama_id', '<>', 0)
+            ->when($videoIds, function ($query, $videoIds) {
+                return $query->whereIn('video_id', $videoIds);
+            })->select('video_id', 'id', 'drama_id', 'appid')
+            ->orderBy('id')
+            ->chunk(10, function ($items) {
+                foreach ($items as $item) {
+                    $this->syncInfo($item);
+                }
+            });
+    }
+
+    public function syncInfo($item) {
+        $appid = $item->appid ?: config('wechat.duanju.masterAppid');
+        $accessToken = Redis::get(AccessTokenService::getAccessTokenRedisKey($appid));
+        $offset = 0;
+        while (true) {
+            $parsedContent = HttpRequestService::simplePost(WechatURL::vod_listmedia . $accessToken, [
+                'drama_id' => $item->drama_id,
+                'limit' => 100,
+                'offset' => $offset
+            ]);
+            $offset += 100;
+            if(false === $parsedContent || (0 != $parsedContent['errcode'] ?? 0)) {
+                myLog('SyncMediaInfo')->error('拉取短剧分集信息失败', [
+                    'appid' => $appid,
+                    'video_id' => $item->video_id, 'drama_id' => $item->drama_id,
+                ]);
+                break;
+            }
+
+            $media_info_list = $parsedContent['media_info_list'];
+            $now = date('Y-m-d H:i:s');
+            foreach ($media_info_list as $media_info) {
+                $audit_detail = $media_info['audit_detail'];
+                $media_id = $media_info['media_id'];
+                DB::table('video_series_wechat_check')
+                    ->where(['video_id' => $item->video_id, 'is_enabled' => 1, 'media_id' => $media_id])
+                    ->update([
+                        'check_status' => $audit_detail['status'],
+                        'check_at' => date('Y-m-d H:i:s', $audit_detail['audit_time']),
+                        'check_reason' => $audit_detail['check_reason'],
+                        'evidence_material_id_list' => \json_encode($audit_detail['evidence_material_id_list']),
+                        'updated_at' => $now,
+                    ]);
+            }
+
+        }
+
+    }
+}

+ 12 - 1
app/Console/Kernel.php

@@ -16,12 +16,23 @@ class Kernel extends ConsoleKernel
          * 刷新微信小程序的accessToken
          */
         $schedule->command('Miniprogram:WechatAccessToken')->everyMinute();
-        // $schedule->command('inspire')->hourly();
         //每月24号执行创建表
         $schedule->command('create_track_table')->monthlyOn(24);
 
         //短剧统计
         $schedule->command('Stats:MiniprogramStats')->dailyAt('03:01');
+        /**
+         * 同步短剧剧目信息
+         */
+        $schedule->command('WechatCheck:SyncDramaInfo')->daily();
+        /**
+         * 同步短剧分集信息
+         */
+        $schedule->command('WechatCheck:SyncMediaInfo')->everySixHours();
+        /**
+         * 检查短剧剧目拉取任务
+         */
+        $schedule->command('WechatCheck:GetTaskInfo')->everyTenMinutes();
     }
 
     /**

+ 126 - 0
app/Jobs/Video/WechatCheck.php

@@ -0,0 +1,126 @@
+<?php
+
+namespace App\Jobs\Video;
+
+use App\Service\Miniprogram\Wechat\AccessTokenService;
+use App\Service\Util\Support\Http\WechatURL;
+use App\Service\Util\Support\Trace\TraceContext;
+use App\Service\Util\Support\Http\HttpRequestService;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\Redis;
+use Illuminate\Support\Facades\DB;
+
+class WechatCheck implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+    private $info;
+    /**
+     * Create a new job instance.
+     */
+    public function __construct($info)
+    {
+        $this->info = $info;
+    }
+
+    /**
+     * Execute the job.
+     */
+    public function handle(): void
+    {
+        myLog('WechatCheck')->info('开始处理微信提审', [
+            'info' => $this->info
+        ]);
+        $traceContext = TraceContext::newFromParent($this->info['traceInfo']);
+
+        $id = $this->info['id'];
+        $record = DB::table('video_wechat_check')
+            ->join('videos', 'videos.id', 'video_wechat_check.video_id')
+            ->where(['video_wechat_check.id' => $id,
+                'video_wechat_check.is_enabled' => 1, 'video_wechat_check.status' => 5])
+            ->select('video_wechat_check.*', 'videos.name as video_name', 'videos.cover_image as video_cover_image')
+            ->first();
+        if(!$record) {
+            myLog('WechatCheck')->info('当前状态不支持提审', [
+                'traceInfo' => $traceContext->getTraceInfo()
+            ]);
+            return;
+        }
+        $appid = $record->appid ?: config('wechat.duanju.masterAppid');
+        $accessToken = Redis::get(AccessTokenService::getAccessTokenRedisKey($appid));
+        $medias = DB::table('video_series_wechat_check')
+            ->where('video_id', $record->video_id)
+            ->where(['sync_status' => 4, 'is_enabled' => 1])
+            ->where('media_id', '<>', 0)
+            ->get();
+        $cover_material_id = $this->getMaterialId($record->video_cover_image, $accessToken);
+        $authorized_material_id = $this->getMaterialId($record->authorized_img, $accessToken);
+        if(!($cover_material_id && $authorized_material_id)) {
+            myLog('WechatCheck')->error('上传短剧封面和授权材料到临时素材失败, 请重新提审', [
+                'appid' => $appid,
+                'traceInfo' => $traceContext->getTraceInfo()
+            ]);
+            return ;
+        }
+        $postData = [
+            'name' => $record->video_name,
+            'media_count' => $medias->count(),
+            'media_id_list' => $medias->pluck('media_id')->toArray(),
+            'producer' => $record->producer,
+            'cover_material_id' =>$cover_material_id,
+            'authorized_material_id' => $authorized_material_id,
+            'registration_number' => $record->registration_number,
+        ];
+        if($record->drama_id) {
+            $postData['drama_id'] = $record->drama_id;
+        }
+        $result = HttpRequestService::simplePost(WechatURL::vod_auditdrama . $accessToken, $postData);
+        if(false === $result || (0 != ($result['errcode'] ?? 0)) || (! ($result['drama_id'] ?? ''))) {
+            myLog('WechatCheck')->error('提审请求失败', [
+                'appid' => $appid,
+                'post' => $postData,
+                'result' => $result,
+                'traceInfo' => $traceContext->getTraceInfo()
+            ]);
+            return;
+        }
+        $drama_id = $result['drama_id'];
+        $now = date('Y-m-d H:i:s');
+        DB::table('video_wechat_check')
+            ->where('id', $record->id)
+            ->update([
+                'status' =>1,
+                'apply_at' => $now,
+                'drama_id' => $drama_id,
+                'updated_at' => $now,
+            ]);
+    }
+
+    /**
+     * 上传临时图片素材
+     * @param string $url 图片http地址
+     * @param string $accessToken
+     * @return string media_id
+     */
+    public function getMaterialId($url, $accessToken){
+        $result = HttpRequestService::post(WechatURL::media_upload. $accessToken, [
+            'multipart' => [
+                [
+                    'name' => 'type',
+                    'contents' => 'image',
+                ],
+                [
+                    'name' => 'media',
+                    'contents' => file_get_contents($url),
+                    'filename' => Arr::last(explode('/', $url)),
+                ]
+            ]
+        ]);
+        return $result['media_id'] ?? '';
+    }
+}

+ 4 - 14
app/Service/Miniprogram/Wechat/AccessTokenService.php

@@ -2,24 +2,14 @@
 
 namespace App\Service\Miniprogram\Wechat;
 
-use GuzzleHttp\Client;
+use App\Service\Util\Support\Http\WechatURL;
+use App\Service\Util\Support\Http\HttpRequestService;
 
 class AccessTokenService
 {
     public static function getAccessToken($appid, $secret) {
-        $client = new Client(['timeout' => 3]);
-        $httpResult = $client->get('https://api.weixin.qq.com/cgi-bin/token', [
-            'query' => ['grant_type' => 'client_credential', 'appid' => $appid, 'secret' => $secret]
-        ]);
-
-        if(200 != $httpResult->getStatusCode()) {
-            throw new \RuntimeException('请求微信上游失败', '500001');
-        }
-        $parsedContent = \json_decode($httpResult->getBody()->getContents(), true);
-        if(0 != ($parsedContent['errcode'] ?? 0)) {
-            throw new \RuntimeException('请求微信上游失败:'. ($parsedContent['errmsg'] ?? ''), '500002');
-        }
-        return $parsedContent;
+        return HttpRequestService::simpleGet(WechatURL::access_token, ['grant_type' => 'client_credential',
+            'appid' => $appid, 'secret' => $secret]);
     }
 
     public static function getAccessTokenRedisKey($appid) {

+ 81 - 0
app/Service/Util/Support/Http/HttpRequestService.php

@@ -0,0 +1,81 @@
+<?php
+namespace App\Service\Util\Support\Http;
+
+use GuzzleHttp\Client;
+
+class HttpRequestService
+{
+
+    /**
+     * 发送post请求
+     * @param $url 请求地址
+     * @param $accessToken 请求access_token
+     * @param $postMessage 请求体
+     * @return array
+     */
+    public static function simplePost($url, $postMessage) {
+        $client = new Client(['timeout' => 10]);
+        try {
+            $res = $client->post(
+                $url,
+                [
+                    'json' => $postMessage
+                ]);
+            $httpStatusCode = $res->getStatusCode();
+            $httpContent = $res->getBody()->getContents();
+            $parsedContent = json_decode($httpContent, true);
+            if (200 == $httpStatusCode) {
+                return $parsedContent;
+            }
+        } catch (\Exception $exception) {
+            myLog('HttpRequest')->error('请求上游失败:', [
+                'url' => $url,
+                'postJson' => $postMessage,
+                'exceptionMessage' => $exception->getMessage(),
+            ]);
+        }
+        return false;
+    }
+
+    public static function post($url, $options) {
+        $client = new Client(['timeout' => 10]);
+        try {
+            $res = $client->request('POST', $url, $options);
+            $httpStatusCode = $res->getStatusCode();
+            $httpContent = $res->getBody()->getContents();
+            $parsedContent = json_decode($httpContent, true);
+            if (200 == $httpStatusCode) {
+                return $parsedContent;
+            }
+        } catch (\Exception $exception) {
+            myLog('HttpRequest')->error('请求上游失败:', [
+                'url' => $url,
+                'postOptions' => $options,
+                'exceptionMessage' => $exception->getMessage(),
+            ]);
+        }
+        return false;
+    }
+
+    public static function simpleGet($url, $query) {
+        $client = new Client(['timeout' => 10]);
+        try {
+            $response = $client->get($url, [
+                'query' => $query
+            ]);
+            $httpStatusCode = $response->getStatusCode();
+            $httpContent = $response->getBody()->getContents();
+            $parsedContent = json_decode($httpContent, true);
+            if (200 == $httpStatusCode)  {
+                return $parsedContent;
+            }
+        } catch (\Exception $exception) {
+            myLog('HttpRequest')->error('请求上游失败:', [
+                'url' => $url,
+                'query' => $query,
+                'exceptionMessage' => $exception->getMessage(),
+            ]);
+        }
+        return false;
+    }
+}

+ 19 - 0
app/Service/Util/Support/Http/WechatURL.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace App\Service\Util\Support\Http;
+
+class WechatURL
+{
+    // 获取剧目信息
+    public const vod_getdrama = 'https://api.weixin.qq.com/wxa/sec/vod/getdrama?access_token=';
+    // accessToken
+    public const access_token = 'https://api.weixin.qq.com/cgi-bin/token';
+    // 剧目提审
+    public const vod_auditdrama = 'https://api.weixin.qq.com/wxa/sec/vod/auditdrama?access_token=';
+    // 新增临时素材
+    public const media_upload = 'https://api.weixin.qq.com/cgi-bin/media/upload?access_token=';
+    // 媒资上传-查询任务
+    public const vod_gettask = 'https://api.weixin.qq.com/wxa/sec/vod/gettask?access_token=';
+    // 获取媒资列表
+    public const vod_listmedia = 'https://api.weixin.qq.com/wxa/sec/vod/listmedia?access_token=';
+}

+ 10 - 0
app/Service/Video/WechatCheck/DramaInfoService.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App\Service\Video\WechatCheck;
+
+class DramaInfoService
+{
+    public static function syncDramaInfo($dramaId, $accessToken) {
+
+    }
+}

+ 9 - 1
config/database.php

@@ -184,12 +184,20 @@ return [
             'port' => env('REDIS_PORT', '6379'),
             'database' => env('REDIS_CACHE_DB', '1'),
         ],
-        'report-redis' => [//专门处理回传队列的服务器
+        //专门处理回传队列的服务器
+        'report-redis' => [
             'host' => env('REPORT_QUEUE_REDIS_HOST', 'localhost'),
             'password' => env('REPORT_QUEUE_REDIS_PASSWORD', null),
             'port' => env('REPORT_QUEUE_REDIS_PORT', 6379),
             'database' => 2,
         ],
+        // 普通redis队列
+        'queue-redis' => [
+            'host' => env('COMMON_QUEUE_REDIS_HOST', 'localhost'),
+            'password' => env('COMMON_QUEUE_REDIS_PASSWORD', null),
+            'port' => env('COMMON_QUEUE_REDIS_PORT', 6379),
+            'database' => 2,
+        ],
     ],
 
 ];

+ 6 - 0
config/queue.php

@@ -75,6 +75,12 @@ return [
             'connection' => 'report-redis',
             'queue' => 'default',
             'expire' => 60,
+        ],
+        'queue-redis' => [
+            'driver' => 'redis',
+            'connection' => 'queue-redis',
+            'queue' => 'default',
+            'expire' => 60,
         ]
     ],
 

+ 8 - 0
config/wechat.php

@@ -0,0 +1,8 @@
+<?php
+
+return [
+    'duanju' => [
+        // 短剧主小程序
+        'masterAppid' => env('WECHAT_DUANJU_MASTER_APPID', 'wx86822355ccd03a78'),
+    ]
+];

+ 23 - 0
tests/Jobs/Video/WechatCheckTest.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Tests\Jobs\Video;
+
+use App\Jobs\Video\WechatCheck;
+use GuzzleHttp\Psr7\Utils;
+use PHPUnit\Framework\TestCase;
+
+class WechatCheckTest extends \Tests\TestCase
+{
+
+    public function testGetMaterialId()
+    {
+        $wechatCheck = new WechatCheck([]);
+        $url  = 'https://minifile-cdn.zvyhjkx.com/uploads/images/20230531/9NUcrj2Dfz1685513143.png';
+//        dump(fopen($url, 'r'));
+//        $url = '/Users/liuzejian/Pictures/images.png';
+//        dump(Utils::tryFopen($url, 'r'));
+        $accessToken = '70_8irXfqI0X-CVZh_2kd5UylnUIaAONdTv9AglcuYqNXwUdhiPylun0aNlOPTNl3nXamPo_WJEcu9XstHMSVtRZh4PQw0SAKxrKOKDL2VvxIvaCpnzSw54DWs95b8VUYcAIARQU';
+        $result = $wechatCheck->getMaterialId($url, $accessToken);
+        dump($result);
+    }
+}