AiBlogTask.php 14.5 KB
<?php
/**
 * @remark :
 * @name   :AiBlogTask.php
 * @author :lyh
 * @method :post
 * @time   :2025/2/14 11:14
 */

namespace App\Console\Commands\Ai;

use App\Helper\Arr;
use App\Models\Ai\AiBlog;
use App\Models\Ai\AiBlogAuthor;
use App\Models\Ai\AiBlogList;
use App\Models\Com\NoticeLog;
use App\Models\Com\Notify;
use App\Models\Devops\ServerConfig;
use App\Models\Devops\ServersIp;
use App\Models\Domain\DomainInfo;
use App\Models\Project\Project;
use App\Models\Project\ProjectAiSetting;
use App\Models\RouteMap\RouteMap;
use App\Services\AiBlogService;
use App\Services\DingService;
use App\Services\ProjectServer;
use Illuminate\Console\Command;
use App\Models\Project\AiBlogTask as AiBlogTaskModel;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

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

    public $updateProject = [];//需更新的列表
    public $projectSetting = [];
    public $routes = [];//需要更新的路由

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '查询ai_blog是否已经生成';

    /**
     * @return bool
     * @throws \Exception
     */
    public function handle(){
        while (true){
            //获取任务id
            $task_id = $this->getTaskId();
            if(empty($task_id)){
                sleep(300);
                continue;
            }
            $this->sendRequest($task_id);
        }
        return true;
    }

    /**
     * 请求方法
     * @param $task_id
     * @return bool
     * @throws \Exception
     */
    public function sendRequest($task_id){
        $aiBlogTaskModel = new AiBlogTaskModel();
        $item = $aiBlogTaskModel->read(['id'=>$task_id]);
        $this->output('start:project ID: ' . $item['project_id'] . ',task ID: ' . $task_id);
        //获取配置
        $aiSettingInfo = $this->getSetting($item['project_id']);
        $aiBlogService = new AiBlogService();
        $aiBlogService->mch_id = $aiSettingInfo['mch_id'];
        $aiBlogService->key = $aiSettingInfo['key'];
        $aiBlogService->task_id = $item['task_id'];
        //拉取文章数据
        $result = $aiBlogService->getDetail();
        if(empty($result['status']) || ($result['status'] != 200)){
            if($item['sort'] < 5){
                $aiBlogTaskModel->edit(['sort'=>$item['sort'] + 1],['id'=>$item['id']]);
            }else{
                $aiBlogTaskModel->edit(['status'=>9],['id'=>$item['id']]);
                // 钉钉通知
                $dingService = new DingService();
                $body = [
                    'keyword' => 'AI_BLOG获取失败',
                    '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']);
        $aiBlogModel = new AiBlog();
        $aiBlogInfo = $aiBlogModel->read(['task_id'=>$item['task_id']],['id','route']);
        if($aiBlogInfo === false){
            // 钉钉通知
            $dingService = new DingService();
            $body = [
                'keyword' => 'AI_BLOG生成错误',
                'msg' => '任务ID:' . $item['task_id'] . ', 子库获取数据失败, 检查子库数据是否被删除!',
                'isAtAll' => false, // 是否@所有人
            ];
            $dingService->handle($body);
            $this->output('error: 子库获取数据失败, task id: ' . $task_id);
            $aiBlogTaskModel->edit(['status'=>9],['id'=>$item['id']]);
            DB::disconnect('custom_mysql');
            return false;
        }
        //拿到返回的路由查看是否重复
        $route = RouteMap::setRoute($result['data']['url'], RouteMap::SOURCE_AI_BLOG, $aiBlogInfo['id'], $item['project_id']);
        if($route != $result['data']['url']){
            $aiBlogService->updateDetail(['route'=>$route,'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;
        }
        $aiBlogModel->edit(['new_title'=>$result['data']['title'], 'image'=>$result['data']['thumb'], 'text'=>$result['data']['section'], 'author_id'=>$result['data']['author_id'],'seo_title'=>$result['data']['title'],'seo_keyword'=>$result['data']['keyword'],'seo_description'=>$result['data']['description'], 'route'=>$route ,'status'=>$aiBlogModel::STATUS_FINISH], ['task_id'=>$item['task_id']]);
        DB::disconnect('custom_mysql');
        $aiBlogTaskModel->edit(['status'=>$aiBlogModel::STATUS_FINISH],['id'=>$item['id']]);
        $this->output('success: task id: ' . $task_id);
        return true;
    }

    /**
     * 获取任务id
     * @param int $finish_at
     * @return mixed
     */
    public function getTaskId($finish_at = 2)
    {
        $task_id = Redis::rpop('ai_blog_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 = [];
            }
            $aiBlogTaskModel = new AiBlogTaskModel();
            $finish_at = date('Y-m-d H:i:s', strtotime('-' . $finish_at . ' hour'));
            $ids = $aiBlogTaskModel->formatQuery(['status'=>$aiBlogTaskModel::STATUS_RUNNING, 'type'=>$aiBlogTaskModel::TYPE_BLOG, 'updated_at'=>['<=',$finish_at]])->pluck('id');
            if(!empty($ids)){
                foreach ($ids as $id) {
                    Redis::lpush('ai_blog_task', $id);
                }
            }
            $task_id = Redis::rpop('ai_blog_task');
        }
        return $task_id;
    }

    /**
     * 更新项目作者页面及列表页
     * @param $updateProject
     * @return bool
     */
    public function updateProject($updateProject){
        if(empty($updateProject)){
            return true;
        }
        foreach ($updateProject as $project_id => $author){
            ProjectServer::useProject($project_id);
            $aiSettingInfo = $this->getSetting($project_id);
            $this->output('sync: list start, project_id: ' . $project_id);
            $this->updateBlogList($aiSettingInfo,$project_id);
            $this->output('sync: list end');
            //更新作者
            $this->output('sync: author start, project_id: ' . $project_id);
            foreach ($author as $val){
                $this->updateAiBlogAuthor($aiSettingInfo,$val,$project_id);
            }
            $this->output('sync: author end');
            DB::disconnect('custom_mysql');
        }

        return true;
    }

    /**
     * 获取项目配置
     * @param $project_id
     * @return bool
     */
    public function getSetting($project_id)
    {
        $project_setting = $this->projectSetting;
        if (FALSE == empty($project_setting[$project_id])){
            return $project_setting[$project_id];
        }
        $projectAiSettingModel = new ProjectAiSetting();
        $aiSettingInfo = $projectAiSettingModel->read(['project_id'=>$project_id]);
        $this->projectSetting[$project_id] = $aiSettingInfo;
        return $aiSettingInfo;
    }

    /**
     * 更新作者的页面
     * @param $aiSettingInfo
     * @param $author_id
     * @return bool
     */
    public function updateAiBlogAuthor($aiSettingInfo,$author_id,$project_id){
        if(empty($author_id)){
            return true;
        }
        $aiBlogService = new AiBlogService();
        $aiBlogService->mch_id = $aiSettingInfo['mch_id'];
        $aiBlogService->key = $aiSettingInfo['key'];
        $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;
    }

    /**
     * 更新列表页
     * @param $aiSettingInfo
     * @return bool
     */
    public function updateBlogList($aiSettingInfo,$project_id = 0){
        $aiBlogService = new AiBlogService();
        $aiBlogService->mch_id = $aiSettingInfo['mch_id'];
        $aiBlogService->key = $aiSettingInfo['key'];
        $page = 1;
        $saveData = [];
        $result = $aiBlogService->getAiBlogList($page,15);
        if(!isset($result['status']) || $result['status'] != 200){
            try {
                // 钉钉通知
                $dingService = new DingService();
                $body = [
                    'keyword' => 'AI_BLOG列表页未生成拉取失败',
                    'msg' => '项目ID:' . $project_id . PHP_EOL . '返回信息:' . json_encode($result,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
                    'isAtAll' => false, // 是否@所有人
                ];
                $dingService->handle($body);
                //写一条更新记录
                $aiBlogTaskModel = new AiBlogTaskModel();
                $aiBlogTaskModel->addReturnId(['project_id'=>$project_id,'task_id'=>$project_id,'status'=>$aiBlogTaskModel::STATUS_RUNNING,'type'=>$aiBlogTaskModel::TYPE_LIST]);
            }catch (\Exception $e){
                $this->output('更新列表页失败同时通知失败--error:' . $e->getMessage());
            }
            return true;
        }
        $total_page = $result['data']['total_page'];
        //组装数据保存
        $saveData[] = [
            'route'=>$page,
            'text'=>$result['data']['section'],
        ];
        while ($total_page > $page){
            $page++;
            $result = $aiBlogService->getAiBlogList($page,15);
            if(isset($result['status']) && $result['status'] == 200){
                $saveData[] = [
                    'route'=>$page,
                    'text'=>$result['data']['section'],
                ];
            }
        }
        $aiBlogListModel = new AiBlogList();
        if(!empty($saveData)){
            //写一条路由信息
            $aiBlogListModel->truncate();
            $aiBlogListModel->insertAll($saveData);
        }
        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){
            if($project_id == 4339){
                continue;
            }
            $route[] = 'top-blog';
            $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));
                if(empty($res)){
                    NoticeLog::createLog(NoticeLog::GENERATE_PAGE, ['c_url'=>$c_url,'c_params'=>$param],date('Y-m-d H:i:s',time()+300));
                }
                $this->output('notify: project id: ' . $project_id . ', result: ' . json_encode($res,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
            }
        }
        return true;
    }

    /**
     * 输入日志
     * @param $message
     * @return bool
     */
    public function output($message)
    {
        $message = date('Y-m-d H:i:s') . ' ' . $message . PHP_EOL;
        echo $message;
        file_put_contents(storage_path('logs/AiBlog/') . date('Ymd') . '.log', $message, FILE_APPEND);
        return true;
    }
}