AiVideoTask.php 12.8 KB
<?php
/**
 * @remark :
 * @name   :AiVideoTask.php
 * @author :lyh
 * @method :post
 * @time   :2025/4/30 11:18
 */

namespace App\Console\Commands\Ai;

use App\Helper\Arr;
use App\Models\Ai\AiBlogAuthor;
use App\Models\Ai\AiVideo;
use App\Models\Ai\AiVideoList;
use App\Models\Com\Notify;
use App\Models\Devops\ServerConfig;
use App\Models\Devops\ServersIp;
use App\Models\Domain\DomainInfo;
use App\Models\Project\AiVideoTask as AiVideoTaskModel;
use App\Models\Project\Project;
use App\Models\RouteMap\RouteMap;
use App\Services\AiBlogService;
use App\Services\AiVideoService;
use App\Services\DingService;
use App\Services\ProjectServer;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

class AiVideoTask extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'save_ai_video';

    public $updateProject = [];//需更新的列表
    public $routes = [];//需要更新的路由
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '查询ai_video是否已经生成';
    /**
     * @return bool
     * @throws \Exception
     */
    public function handle(){
        while (true){
            //获取任务id
            $task_id = $this->getTaskId();
            if(empty($task_id)){
                sleep(300);
                continue;
            }
            $this->_action($task_id);
        }
        return true;
    }

    /**
     * 获取任务id
     * @param int $finish_at
     * @return mixed
     */
    public function getTaskId($finish_at = 2)
    {
        $task_id = Redis::rpop('ai_video_task');
        if (empty($task_id)) {
            if(!empty($this->updateProject)){
                $this->updateProject($this->updateProject);
                $this->updateProject = [];
            }
            if(!empty($this->routes)){
                $this->updateRoutes($this->routes);
                $this->routes = [];
            }
            $aiVideoTaskModel = new AiVideoTaskModel();
            $finish_at = date('Y-m-d H:i:s', strtotime('-' . $finish_at . ' hour'));
            $ids = $aiVideoTaskModel->formatQuery(['status'=>$aiVideoTaskModel::STATUS_RUNNING,'updated_at'=>['<=',$finish_at]])->pluck('id');
            if(!empty($ids)){
                foreach ($ids as $id) {
                    Redis::lpush('ai_video_task', $id);
                }
            }
            $task_id = Redis::rpop('ai_video_task');
        }
        return $task_id;
    }

    /**
     * @remark :请求
     * @name   :sendRequest
     * @author :lyh
     * @method :post
     * @time   :2025/4/30 11:31
     */
    public function _action($task_id){
        $aiVideoTaskModel = new AiVideoTaskModel();
        $item = $aiVideoTaskModel->read(['id'=>$task_id]);
        $this->output('ai_video->start:project ID: ' . $item['project_id'] . ',task ID: ' . $task_id);
        $aiVideoService = new AiVideoService($item['project_id']);
        $aiVideoService->task_id = $item['task_id'];
        //拉取文章数据
        $result = $aiVideoService->getVideoDetail();
        if(empty($result['status']) || ($result['status'] != 200)){
            if($item['number'] < 5){
                $aiVideoTaskModel->edit(['number'=>$item['number'] + 1],['id'=>$item['id']]);
            }else{
                $aiVideoTaskModel->edit(['status'=>9],['id'=>$item['id']]);
                // 钉钉通知
                $dingService = new DingService();
                $body = [
                    'keyword' => 'AI_VIDEO获取失败',
                    'msg' => '任务ID:' . $item['task_id'] . PHP_EOL . '返回信息:' . json_encode($result,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
                    'isAtAll' => false, // 是否@所有人
                ];
                $dingService->handle($body);
            }
            $this->output('error: 数据获取失败,status:' . $result['status'] . ',message: ' . ($result['message'] ?? 'null'));
            return false;
        }
        //保存当前项目ai_blog数据
        ProjectServer::useProject($item['project_id']);
        $aiVideoModel = new AiVideo();
        $aiVideoInfo = $aiVideoModel->read(['task_id'=>$item['task_id']],['id','route']);
        if($aiVideoInfo === false){
            // 钉钉通知
            $dingService = new DingService();
            $body = [
                'keyword' => 'AI_VIDEO生成错误',
                'msg' => '任务ID:' . $item['task_id'] . ', 子库获取数据失败, 检查子库数据是否被删除!',
                'isAtAll' => false, // 是否@所有人
            ];
            $dingService->handle($body);
            $this->output('error: 子库获取数据失败, task id: ' . $task_id);
            $aiVideoTaskModel->edit(['status'=>9],['id'=>$item['id']]);
            DB::disconnect('custom_mysql');
            return false;
        }
        //拿到返回的路由查看是否重复
        $route = RouteMap::setRoute($result['data']['url'], RouteMap::SOURCE_AI_VIDEO, $aiVideoInfo['id'], $item['project_id']);
        if($route != $result['data']['url']){
            $aiVideoService->updateDetail(['url'=>$route,'task_id'=>$item['task_id']]);
        }
        $saveData = [
            'title'=>$result['data']['title'],
            'image'=>$result['data']['thumb'],
            'video_url'=>$result['data']['video_url'],
            'route'=>$route,
            'author_id'=>$result['data']['author_id'],
            'keyword'=>json_encode($result['data']['keyword'],true),
            'content'=>$result['data']['content'],
            'text'=>$result['data']['section'],
            'status'=>$aiVideoTaskModel::STATUS_FINISH,
            'seo_title'=>$result['data']['title'],
            'seo_keyword'=>implode(',',$result['data']['keyword']),
            'seo_description'=>$result['data']['description'] ?? '',
        ];
        $aiVideoModel->edit($saveData,['task_id'=>$item['task_id']]);
        //需要更新的路由
        if (!in_array($result['data']['author_id'], $this->updateProject[$item['project_id']] ?? [])) {
            $this->updateProject[$item['project_id']][] = $result['data']['author_id'];
        }
        if (!in_array($route, $this->routes[$item['project_id']] ?? [])) {
            $this->routes[$item['project_id']][] = $route;
        }
        DB::disconnect('custom_mysql');
        $aiVideoTaskModel->edit(['status'=>$aiVideoTaskModel::STATUS_FINISH],['id'=>$item['id']]);
        $this->output('success: task id: ' . $task_id);
        return true;
    }

    /**
     * @remark :更新项目作者页面及列表页
     * @name   :updateProject
     * @author :lyh
     * @method :post
     * @time   :2025/4/30 15:43
     */
    public function updateProject($updateProject){
        if(empty($updateProject)){
            return true;
        }
        foreach ($updateProject as $project_id => $author){
            ProjectServer::useProject($project_id);
            $this->output('sync: list start, project_id: ' . $project_id);
            $this->updateBlogList($project_id);
            $this->output('sync: list end');
            //更新作者
            $this->output('sync: author start, project_id: ' . $project_id);
            foreach ($author as $val){
                $this->updateAiBlogAuthor($val,$project_id);
            }
            $this->output('sync: author end');
            DB::disconnect('custom_mysql');
        }

        return true;
    }

    /**
     * @remark :更新作者页面
     * @name   :updateAiBlogAuthor
     * @author :lyh
     * @method :post
     * @time   :2025/4/30 15:52
     */
    public function updateAiBlogAuthor($author_id,$project_id){
        if(empty($author_id)){
            return true;
        }
        $aiBlogService = new AiBlogService($project_id);
        $aiBlogService->author_id = $author_id;
        $result = $aiBlogService->getAuthorDetail();
        if(isset($result['status']) && $result['status'] == 200){
            //当前作者的页面
            $aiBlogAuthorModel = new AiBlogAuthor();
            $authorInfo = $aiBlogAuthorModel->read(['author_id'=>$author_id],['id','route']);
            if($authorInfo !== false && !empty($result['data']['section'])){
                //需要更新的路由
                if (!in_array($authorInfo['route'], $this->routes[$project_id] ?? [])) {
                    $this->routes[$project_id][] = $authorInfo['route'];
                }
                $aiBlogAuthorModel->edit(['text'=>$result['data']['section']],['author_id'=>$author_id]);
            }
        }
        return true;
    }

    /**
     * @remark :更新
     * @name   :updateBlogList
     * @author :lyh
     * @method :post
     * @time   :2025/4/30 15:45
     */
    public function updateBlogList($project_id){
        $aiVideoService = new AiVideoService($project_id);
        $page = 1;
        $saveData = [];
        $result = $aiVideoService->getAiVideoList($page,15);
        if(!isset($result['status']) && $result['status'] != 200){
            return true;
        }
        $total_page = $result['data']['total_page'];
        //组装数据保存
        $saveData[] = [
            'route'=>$page,
            'text'=>$result['data']['section'],
        ];
        while ($total_page > $page){
            $page++;
            $result = $aiVideoService->getAiVideoList($page,15);
            if(isset($result['status']) && $result['status'] == 200){
                $saveData[] = [
                    'route'=>$page,
                    'text'=>$result['data']['section'],
                ];
            }
        }
        $aiVideoListModel = new AiVideoList();
        if(!empty($saveData)){
            //写一条路由信息
            RouteMap::setRoute('top-video',RouteMap::SOURCE_AI_VIDEO_LIST,0,$project_id);//写一条列表页路由
            $aiVideoListModel->truncate();
            $aiVideoListModel->insertAll($saveData);
        }
        return true;
    }
    /**
     * 输入日志
     * @param $message
     * @return bool
     */
    public function output($message)
    {
        $message = date('Y-m-d H:i:s') . ' ' . $message . PHP_EOL;
        echo $message;
        return true;
    }

    /**
     * 通知C端生成界面
     * @param $project_id
     * @return bool
     */
    public function updateRoutes($routes){
        $domainModel = new DomainInfo();
        $project_model = new Project();
        foreach ($routes as $project_id => $route){
            $route[] = 'top-video';
            $domain = $domainModel->getProjectIdDomain($project_id);
            if (empty($domain)) {
                $this->output('send: 域名不存在, project id: ' . $project_id);
                continue;
            }
            //判断是否是自建站服务器,如果是,不请求C端接口,数据直接入库
            $project_info = $project_model->read(['id'=>$project_id],['serve_id']);
            if(!$project_info){
                $this->output('send: 项目不存在, project id: ' . $project_id);
                continue;
            }
            $serve_ip_model = new ServersIp();
            $serve_ip_info = $serve_ip_model->read(['id'=>$project_info['serve_id']],['servers_id']);
            $servers_id = $serve_ip_info ? $serve_ip_info['servers_id'] : 0;
            if($servers_id == ServerConfig::SELF_SITE_ID){
                //判断是否已有更新进行中
                $notify_model = new Notify();
                $data = [
                    'project_id' => $project_id,
                    'type' => Notify::TYPE_MASTER,
                    'route' => Notify::ROUTE_AI_BLOG,
                    'server_id' => ServerConfig::SELF_SITE_ID,
                    'status' => ['!=',Notify::STATUS_FINISH_SITEMAP]
                ];
                $notify = $notify_model->read($data,['id']);
                if(!$notify){
                    $domain_array = parse_url($domain);
                    $data['data'] = Arr::a2s(['domain'=>$domain_array['host'],'url'=>$route,'language'=>[]]);
                    $data['status'] = Notify::STATUS_INIT;
                    $data['sort'] = 2;
                    $notify_model->add($data);
                }
                $this->output('send: 自建站项目, project id: ' . $project_id);
            }else{
                $c_url = $domain.'api/update_page/';
                $param = [
                    'project_id' => $project_id,
                    'type' => 1,
                    'route' => 3,
                    'url' => $route,
                    'language'=> [],
                    'is_sitemap' => 0
                ];
                $res = http_post($c_url, json_encode($param,true));
                $this->output('notify: project id: ' . $project_id . ', result: ' . json_encode($res,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
            }
        }
        return true;
    }
}