作者 赵彬吉

update

<?php
namespace App\Console\Commands\Tdk;
use App\Helper\Arr;
use App\Helper\Common;
use App\Helper\Gpt;
use App\Models\Ai\AiCommand;
use App\Models\Domain\DomainInfo;
use App\Models\Mail\Mail;
use App\Models\Project\DeployBuild;
use App\Models\Project\DeployOptimize;
use App\Models\Project\ProjectUpdateTdk;
use App\Models\User\User;
use App\Models\WebSetting\WebLanguage;
use App\Services\ProjectServer;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
/**
* 指定跑某个项目
* Class InitProject
* @package App\Console\Commands
* @author zbj
* @date 2023/10/8
*/
class UpdateSeoTdkByTaskId extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'update_seo_tdk_by {task_id}';
/**
* The console command description.
*
* @var string
*/
protected $description = '一键生成tdk';
protected $project;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* '表' => [
* '指令key' => '表字段'
* ]
* @return array
* @author zbj
* @date 2023/11/3
*/
protected $maps = [
'gl_web_custom_template' => [
'page_title' => 'title',
'page_meta_keywords' => 'keywords',
'page_meta_description' => 'description',
],
'gl_product' => [
'product_title' => 'seo_mate.title',
'product_meta_keywords' => 'seo_mate.keyword',
'product_meta_description' => 'seo_mate.description',
],
'gl_product_category' => [
'product_cat_title' => 'seo_title',
'product_cat_meta_keywords' => 'seo_keywords',
'product_cat_meta_description' => 'seo_des',
],
'gl_blog' => [
'blog_title' => 'seo_title',
'blog_meta_keywords' => 'seo_keywords',
'blog_meta_description' => 'seo_description',
],
'gl_blog_category' => [
'blog_cat_title' => 'seo_title',
'blog_cat_meta_keywords' => 'seo_keywords',
'blog_cat_meta_description' => 'seo_des',
],
'gl_news' => [
'news_title' => 'seo_title',
'news_meta_keywords' => 'seo_keywords',
'news_meta_description' => 'seo_description',
],
'gl_news_category' => [
'news_cat_title' => 'seo_title',
'news_cat_meta_keywords' => 'seo_keywords',
'news_cat_meta_description' => 'seo_des',
],
'gl_product_keyword' => [
'tags_title' => 'seo_title',
'tags_meta_keywords' => 'seo_keywords',
'tags_meta_description' => 'seo_description',
'tags_content_title' => 'keyword_title',
'tags_content_description' => 'keyword_content',
]
];
/**
* topic-对相应字段
* @var array
*/
protected $topic_fields = [
'gl_web_custom_template' => 'name',
'gl_product' => 'title',
'gl_product_category' => 'title',
'gl_blog' => 'name',
'gl_blog_category' => 'name',
'gl_news' => 'name',
'gl_news_category' => 'name',
'gl_product_keyword' => 'title'
];
/**
* 使用核心关键词的key 数量
* @var array
*/
protected $core_keyword_keys = [
'page_meta_keywords' => 1,
'blog_cat_meta_keywords' => 8,
'news_cat_meta_keywords' => 8,
];
/**
* @return bool
*/
public function handle()
{
$task_id = $this->argument('task_id');
$task = ProjectUpdateTdk::where('status', ProjectUpdateTdk::STATUS_PENDING)->where('id', $task_id)->first();
if (!$task) {
echo date('Y-m-d H:i:s') . ' 任务不存在: ' . $task_id . PHP_EOL;
}
$project_id = $task->project_id;
echo date('Y-m-d H:i:s') . ' start project_id: ' . $project_id . PHP_EOL;
try {
$this->project = ProjectServer::useProject($project_id);
$this->seo_tdk($project_id, $task->id);
DB::disconnect('custom_mysql');
}catch (\Exception $e){
echo date('Y-m-d H:i:s') . 'line: '. $e->getLine() .' error: ' . $project_id . '->' . $e->getMessage() . PHP_EOL;
ProjectUpdateTdk::retry($task->id, $e->getMessage());
}
Cache::forget('project_deploy_optimize_info_' . $project_id);
echo date('Y-m-d H:i:s') . ' end project_id: ' . $project_id . PHP_EOL;
}
public function sendNotify($project_id, $route)
{
//获取当前项目的域名
$domainModel = new DomainInfo();
$domainInfo = $domainModel->read(['project_id'=>$project_id]);
if($domainInfo === false){
//获取测试域名
$deployBuildModel = new DeployBuild();
$buildInfo = $deployBuildModel->read(['project_id'=>$project_id]);
$this->param['domain'] = $buildInfo['test_domain'];
}else{
$this->param['domain'] = 'https://'.$domainInfo['domain'].'/';
}
$url = $this->param['domain'].'api/update_page/';
$param = [
'project_id' => $project_id,
'type' => 1,
'route' => $route,
'url' => [],
'language'=> [],
];
http_post($url, json_encode($param));
echo '更新中请稍后, 更新完成将会发送站内信通知更新结果!'. PHP_EOL;
}
public function seo_tdk($project_id, $task_id)
{
$notify_master = $notify_keyword = false;
//更新统计
$update = [];
$ai_commands = AiCommand::where('is_batch', 1)->select('key', 'scene', 'ai')->get()->toArray();
$ai_commands = Arr::setValueToKey($ai_commands, 'key');
foreach ($this->maps as $table => $map) {
$total_page = DB::connection('custom_mysql')->table($table)->count();
$update[$table] = ['total_page'=>$total_page, 'title'=>0, 'keyword'=>0, 'des'=>0,'keyword_title'=>0,'keyword_content'=>0];
echo date('Y-m-d H:i:s') . '更新--' . $table . ': 项目id' . $project_id . PHP_EOL;
$list = DB::connection('custom_mysql')->table($table)
->where(function ($query) use ($table, $map){
if($table == 'gl_product'){
foreach ($map as $field){
$field_arr = explode('.', $field);
$query->orWhereRaw('JSON_CONTAINS('.$field_arr[0].', "null", "$.'.$field_arr[1].'") OR JSON_EXTRACT('.$field_arr[0].', "$.'.$field_arr[1].'") = ""');
}
}else{
foreach ($map as $field){
$query->orWhereRaw($field . " IS NULL OR ".$field." = ''");
}
}
})->select('id')->get();
if (!empty($list)) {
$list = $list->toArray();
foreach ($list as $v) {
$v = (array)$v;
//缓存 在处理的项目数据 id
$cache_key = "seo_tdk_{$project_id}_{$table}_{$v['id']}";
if(!Redis::setnx($cache_key, 1)){
continue;
}
Redis::expire($cache_key, 120);
echo date('Y-m-d H:i:s') . '更新--' . $table . ': 项目id' . $project_id . ':id' . $v['id'] . PHP_EOL;
$v = DB::connection('custom_mysql')->table($table)->where('id', $v['id'])->first();
$v = (array)$v;
$data = [];
$json_field = '';
foreach ($map as $ai_key => $field) {
$field_arr = explode('.', $field);
if (count($field_arr) > 1) {
$json_field = $field_arr[0];
$value = json_decode($v[$field_arr[0]], true)[$field_arr[1]] ?? '';
} else {
$value = $v[$field] ?? '';
}
//已有值的 跳过
if ($value) {
echo $field.'已有值 跳过' . PHP_EOL;
continue;
}
//AI生成
if (!empty($ai_commands[$ai_key]['ai'])) {
$prompt = $this->getPrompt($project_id, $ai_commands[$ai_key]['ai'], $table, $v);
if(!$prompt){
continue;
}
if (count($field_arr) > 1) {
if($field_arr[1] == 'title'){
$update[$table]['title']++;
}elseif ($field_arr[1] == 'keyword'){
$update[$table]['keyword']++;
}elseif ($field_arr[1] == 'description'){
$update[$table]['des']++;
}
$data[$field_arr[0]][$field_arr[1]] = $this->ai_send($prompt);
}else{
if($field == 'title' || $field == 'seo_title'){
$update[$table]['title']++;
}
if($field == 'keywords' || $field == 'seo_keywords'){
$update[$table]['keyword']++;
}
if($field == 'seo_description' || $field == 'description' || $field == 'seo_des'){
$update[$table]['des']++;
}
if($field == 'keyword_title'){
$update[$table]['keyword_title']++;
}
if($field == 'keyword_content'){
$update[$table]['keyword_content']++;
}
$data[$field] = $this->ai_send($prompt);
}
} else {
//直接使用topic
if (count($field_arr) > 1) {
$data[$field_arr[0]][$field_arr[1]] = $v[$this->topic_fields[$table]] ?? '';
//使用核心关键词
if(in_array($ai_key, array_keys($this->core_keyword_keys))){
$data[$field_arr[0]][$field_arr[1]] = $this->mainKeywords($project_id, $this->core_keyword_keys[$ai_key]);
if(!empty($data[$field_arr[0]][$field_arr[1]])){
if($field_arr[1] == 'title'){
$data[$table]['title']++;
}elseif ($field_arr[1] == 'keyword'){
$data[$table]['keyword']++;
}elseif ($field_arr[1] == 'description'){
$data[$table]['des']++;
}
}
}
}else{
$data[$field] = $v[$this->topic_fields[$table]] ?? '';
//使用核心关键词
if(in_array($ai_key, array_keys($this->core_keyword_keys))){
$data[$field] = $this->mainKeywords($project_id, $this->core_keyword_keys[$ai_key]);
if(!empty($data[$field])){
if($field == 'title' || $field == 'seo_title'){
$update[$table]['title']++;
}
if($field == 'keywords' || $field == 'seo_keywords'){
$update[$table]['keyword']++;
}
if($field == 'seo_description' || $field == 'description' || $field == 'seo_des'){
$update[$table]['des']++;
}
if($field == 'keyword_title'){
$update[$table]['keyword_title']++;
}
if($field == 'keyword_content'){
$update[$table]['keyword_content']++;
}
}
}
}
}
}
if(!$data){
continue;
}
if($json_field){
$old_data = json_decode($v[$field_arr[0]], true);
foreach ($old_data ?: [] as $kk=>$vv){
empty($data[$json_field][$kk]) && $data[$json_field][$kk] = $vv;
}
$data[$json_field] = json_encode($data[$json_field]);
}
DB::connection('custom_mysql')->table($table)->where(['id' => $v['id']])->update($data);
if($table == 'gl_product_keyword'){
$notify_keyword = true;
}else{
$notify_master = true;
}
}
}
}
ProjectUpdateTdk::finish($task_id, $update);
//通知C端更新界面
$notify_master && $this->sendNotify($project_id, 1); //通知主站更新
$notify_keyword && $this->sendNotify($project_id, 4); //通知聚合页更新
}
public function getPrompt($project_id, $prompt, $table, $data){
if(strpos($prompt, '{topic}') !== false){
$topic = $data[$this->topic_fields[$table]] ?? '';
if(!$topic){
echo 'topic为空 跳过' . PHP_EOL;
return false;
}
$prompt = str_replace('{topic}', $topic, $prompt);
}
if(strpos($prompt, '{keyword}') !== false) {
$keyword = $this->mainKeywords($project_id, 1);
if(!$keyword){
echo '核心关键词为空 跳过' . PHP_EOL;
return false;
}
$prompt = str_replace('{keyword}', $keyword, $prompt);
}
if(strpos($prompt, '{company name}') !== false) {
$company_name = $this->companyName($project_id);
if(!$company_name){
echo '公司英文全称为空 跳过' . PHP_EOL;
return false;
}
$prompt = str_replace('{company name}', $company_name, $prompt);
}
$prompt .= '.Please answer in ' . $this->getLang();
return $prompt;
}
public function getDeployOptimize($project_id){
$cache_key = 'project_deploy_optimize_info_' . $project_id;
$info = Cache::get($cache_key);
if(!$info){
$projectOptimizeModel = new DeployOptimize();
$info = $projectOptimizeModel->read(['project_id' => $project_id], ['id', 'company_en_name', 'company_en_description', 'main_keywords']);
Cache::put($cache_key, $info, 600);
}
return $info;
}
/**
* @remark :获取公司英文名称
* @name :companyName
* @author :lyh
* @method :post
* @time :2023/10/30 11:22
*/
public function companyName($project_id, $key = '')
{
$data = [
'product_long_description',
];
$info = $this->getDeployOptimize($project_id);
if (in_array($key, $data)) {
return $info['company_en_description'];
} else {
return $info['company_en_name'];
}
}
/**
* @remark :获取公司核心关键词
* @name :mainKeywords
* @author :lyh
* @method :post
* @time :2023/10/30 11:22
*/
public function mainKeywords($project_id, $num)
{
$str = '';
$info = $this->getDeployOptimize($project_id);
if (!empty($info['main_keywords'])) {
$main_keywords = explode("\r\n", $info['main_keywords']);
//随机取
shuffle($main_keywords);
$main_keywords = array_slice($main_keywords, 0, $num);
$str = implode(", ", $main_keywords);
}
return $str;
}
public function getLang(){
$lang = WebLanguage::getLangById($this->project['main_lang_id']??1);
return $lang['english'] ?? 'English';
}
/**
* @remark :AI发送
* @name :ai_send
* @author :lyh
* @method :post
* @time :2023/8/19 10:40
*/
public function ai_send($prompt)
{
$text = Gpt::instance()->openai_chat_qqs($prompt);
$text = Common::deal_keywords($text);
$text = Common::deal_str($text);
//包含这写字 重新生成
if(Str::contains(Str::lower($text), ['[your brand]', '[brand name]'])){
return $this->ai_send($prompt);
}
return $text;
}
/**
* @remark :发送站内信
* @name :send_message
* @author :lyh
* @method :post
* @time :2023/11/4 10:22
*/
public function send_message($project_id){
$user = new User();
$userInfo = $user->read(['project_id'=>$project_id,'role_id'=>0]);
$data["title"] = "seo更新通知---完成";
$data["user_list"] = $userInfo['id'];
$data["content"] = "seo更新成功,更新完成时间".date('Y-m-d H:i:s');
$mail = new Mail();
return $mail->add($data);
}
}
... ...