作者 刘锟

Merge remote-tracking branch 'origin/master' into akun

@@ -73,6 +73,7 @@ class AfterDayCount extends Command @@ -73,6 +73,7 @@ class AfterDayCount extends Command
73 ->whereIn('gl_project.type',[2,4]) 73 ->whereIn('gl_project.type',[2,4])
74 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id') 74 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id')
75 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0") 75 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0")
  76 + ->whereRaw("FIND_IN_SET('7', gl_project_deploy_optimize.special) = 0 AND FIND_IN_SET('8', gl_project_deploy_optimize.special) = 0")
76 ->count(); 77 ->count();
77 $qualified_count = $projectModel->where('gl_project.extend_type',0) 78 $qualified_count = $projectModel->where('gl_project.extend_type',0)
78 ->where('gl_project.delete_status',0) 79 ->where('gl_project.delete_status',0)
@@ -82,6 +83,7 @@ class AfterDayCount extends Command @@ -82,6 +83,7 @@ class AfterDayCount extends Command
82 ->whereIn('gl_project.type',[2,4]) 83 ->whereIn('gl_project.type',[2,4])
83 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id') 84 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id')
84 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0") 85 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0")
  86 + ->whereRaw("FIND_IN_SET('7', gl_project_deploy_optimize.special) = 0 AND FIND_IN_SET('8', gl_project_deploy_optimize.special) = 0")
85 ->count(); 87 ->count();
86 $rate = number_format($qualified_count / $project_count, 2); 88 $rate = number_format($qualified_count / $project_count, 2);
87 $threeMonthsAgo = date('Y-m-d 00:00:00', strtotime('-3 months')); 89 $threeMonthsAgo = date('Y-m-d 00:00:00', strtotime('-3 months'));
@@ -92,6 +94,7 @@ class AfterDayCount extends Command @@ -92,6 +94,7 @@ class AfterDayCount extends Command
92 ->whereIn('gl_project.type',[2,4]) 94 ->whereIn('gl_project.type',[2,4])
93 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id') 95 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id')
94 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0") 96 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0")
  97 + ->whereRaw("FIND_IN_SET('7', gl_project_deploy_optimize.special) = 0 AND FIND_IN_SET('8', gl_project_deploy_optimize.special) = 0")
95 ->count(); 98 ->count();
96 $three_qualified_count = $projectModel->where('gl_project.extend_type',0) 99 $three_qualified_count = $projectModel->where('gl_project.extend_type',0)
97 ->whereIn('gl_project.id',$projectIdArr) 100 ->whereIn('gl_project.id',$projectIdArr)
@@ -101,6 +104,7 @@ class AfterDayCount extends Command @@ -101,6 +104,7 @@ class AfterDayCount extends Command
101 ->whereIn('gl_project.type',[2,4]) 104 ->whereIn('gl_project.type',[2,4])
102 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id') 105 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id')
103 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0") 106 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0")
  107 + ->whereRaw("FIND_IN_SET('7', gl_project_deploy_optimize.special) = 0 AND FIND_IN_SET('8', gl_project_deploy_optimize.special) = 0")
104 ->count(); 108 ->count();
105 $three_rate = number_format($three_qualified_count / $three_project_count, 2); 109 $three_rate = number_format($three_qualified_count / $three_project_count, 2);
106 $data = $projectModel->where('gl_project.extend_type',0) 110 $data = $projectModel->where('gl_project.extend_type',0)
@@ -111,6 +115,7 @@ class AfterDayCount extends Command @@ -111,6 +115,7 @@ class AfterDayCount extends Command
111 ->whereIn('gl_project.type',[2,4]) 115 ->whereIn('gl_project.type',[2,4])
112 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id') 116 ->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id')
113 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0") 117 ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0")
  118 + ->whereRaw("FIND_IN_SET('7', gl_project_deploy_optimize.special) = 0 AND FIND_IN_SET('8', gl_project_deploy_optimize.special) = 0")
114 ->pluck('gl_project.title')->toArray(); 119 ->pluck('gl_project.title')->toArray();
115 $saveData[] = [ 120 $saveData[] = [
116 'date'=>date('Y-m-d', strtotime('yesterday')), 121 'date'=>date('Y-m-d', strtotime('yesterday')),
@@ -44,6 +44,7 @@ class CopyProject extends Command @@ -44,6 +44,7 @@ class CopyProject extends Command
44 public function handle() 44 public function handle()
45 { 45 {
46 while (true) { 46 while (true) {
  47 + $projectModel = new Project();
47 $list = NoticeLog::where('type', NoticeLog::TYPE_COPY_PROJECT)->where('status', NoticeLog::STATUS_PENDING)->get(); 48 $list = NoticeLog::where('type', NoticeLog::TYPE_COPY_PROJECT)->where('status', NoticeLog::STATUS_PENDING)->get();
48 if(empty($list)){ 49 if(empty($list)){
49 sleep(30); 50 sleep(30);
@@ -68,13 +69,15 @@ class CopyProject extends Command @@ -68,13 +69,15 @@ class CopyProject extends Command
68 $item->status = NoticeLog::STATUS_FAIL; 69 $item->status = NoticeLog::STATUS_FAIL;
69 $item->save(); 70 $item->save();
70 } 71 }
71 - sleep(60);  
72 try { 72 try {
73 $this->copyMysql($old_project_id,$project_id); 73 $this->copyMysql($old_project_id,$project_id);
  74 + $this->output('CopyProjectJob end, old project_id: ' . $old_project_id . ', new project_id: ' . $project_id);
74 }catch (\Exception $e){ 75 }catch (\Exception $e){
  76 + echo $e->getMessage().PHP_EOL;
75 echo '复制数据库失败:'.$old_project_id . '<->'.$project_id; 77 echo '复制数据库失败:'.$old_project_id . '<->'.$project_id;
76 } 78 }
77 - $this->output('CopyProjectJob end, old project_id: ' . $old_project_id . ', new project_id: ' . $project_id); 79 + //修改项目状态
  80 + $projectModel->edit(['delete_status'=>0],['id'=>$project_id]);
78 } 81 }
79 } 82 }
80 return true; 83 return true;
@@ -206,13 +209,54 @@ class CopyProject extends Command @@ -206,13 +209,54 @@ class CopyProject extends Command
206 } 209 }
207 //复制数据库 210 //复制数据库
208 public function copyMysql($project_id,$new_project_id){ 211 public function copyMysql($project_id,$new_project_id){
209 - Artisan::call("php artisan copy_project_s $project_id $new_project_id"); 212 + echo '进入复制数据:'.PHP_EOL;
  213 + //切换数据库配置
  214 + $project = ProjectServer::useProject($new_project_id);
  215 + //创建数据库
  216 + ProjectServer::createDatabase($project);
  217 + //创建表
  218 + $this->initTable($project_id,$new_project_id);
210 //修改项目状态 219 //修改项目状态
211 $projectModel = new Project(); 220 $projectModel = new Project();
212 $projectModel->edit(['delete_status'=>0],['id'=>$new_project_id]); 221 $projectModel->edit(['delete_status'=>0],['id'=>$new_project_id]);
213 } 222 }
214 223
215 /** 224 /**
  225 + * @remark :创建数据库
  226 + * @name :initTable
  227 + * @author :lyh
  228 + * @method :post
  229 + * @time :2023/12/11 10:09
  230 + */
  231 + public function initTable($project_id, $news_project_id)
  232 + {
  233 + // 设置源数据库
  234 + config(['database.connections.custom_tmp_mysql_copy.database' => 'gl_data_' . $project_id]);
  235 + $database_name = DB::connection('custom_tmp_mysql_copy')->getDatabaseName();
  236 + // 获取源数据库的所有表
  237 + $tables = Schema::connection('custom_tmp_mysql_copy')->getAllTables();
  238 + $tables = array_column($tables, 'Tables_in_' . $database_name);
  239 + foreach ($tables as $table) {
  240 + // 1. 删除目标数据库中的表
  241 + DB::connection('custom_mysql')->statement("DROP TABLE IF EXISTS {$table}");
  242 + // 2. 复制建表 SQL
  243 + $sql = DB::connection('custom_tmp_mysql_copy')->select("SHOW CREATE TABLE `{$table}`");
  244 + DB::connection('custom_mysql')->statement(get_object_vars($sql[0])['Create Table']);
  245 + // 3. 跳过指定的表
  246 + if (in_array($table, ['gl_customer_visit', 'gl_customer_visit_item', 'gl_inquiry_other', 'gl_inquiry_form_data', 'gl_inquiry_form'])) {
  247 + continue;
  248 + }
  249 + // 4. 原生 SQL 插入数据(完全复制)
  250 + $insert_sql = "INSERT INTO `{$table}` SELECT * FROM `gl_data_{$project_id}`.`{$table}`";
  251 + DB::connection('custom_mysql')->statement($insert_sql);
  252 + // 5. 更新 project_id(如果存在)
  253 + if (Schema::connection('custom_mysql')->hasColumn($table, 'project_id')) {
  254 + DB::connection('custom_mysql')->table($table)->update(['project_id' => $news_project_id]);
  255 + }
  256 + }
  257 + return true;
  258 + }
  259 + /**
216 * @param $message 260 * @param $message
217 * @return bool 261 * @return bool
218 */ 262 */
@@ -7,6 +7,7 @@ use App\Models\RankData\RankDataLog as RankDataLogModel; @@ -7,6 +7,7 @@ use App\Models\RankData\RankDataLog as RankDataLogModel;
7 use App\Models\Project\DeployOptimize; 7 use App\Models\Project\DeployOptimize;
8 use App\Models\Project\Project; 8 use App\Models\Project\Project;
9 use Illuminate\Support\Facades\Cache; 9 use Illuminate\Support\Facades\Cache;
  10 +use Illuminate\Support\Facades\DB;
10 use Illuminate\Support\Facades\Log; 11 use Illuminate\Support\Facades\Log;
11 12
12 /** 13 /**
@@ -47,8 +48,19 @@ class RankData extends BaseCommands @@ -47,8 +48,19 @@ class RankData extends BaseCommands
47 Project::where('is_remain_today', 1)->update(['is_remain_today' => 0]); 48 Project::where('is_remain_today', 1)->update(['is_remain_today' => 0]);
48 Cache::set('clear_remain_today_' . date('Y-m-d'), 1, 24 * 3600); 49 Cache::set('clear_remain_today_' . date('Y-m-d'), 1, 24 * 3600);
49 } 50 }
  51 +
  52 + $projectModel = new Project();
  53 + $list = $projectModel->leftJoin('gl_project_deploy_optimize', 'gl_project.id', '=', 'gl_project_deploy_optimize.project_id')
  54 + ->where('gl_project.extend_type',0)
  55 + ->where('gl_project.delete_status',0)
  56 + ->where('gl_project_deploy_optimize.api_no', '>', 0)
  57 + ->whereIn('gl_project.type',[2,4])
  58 + ->whereRaw("FIND_IN_SET('2', gl_project.level) = 0 AND FIND_IN_SET('3', gl_project.level) = 0")
  59 + ->select(['gl_project_deploy_optimize.api_no as api_no', 'gl_project_deploy_optimize.project_id as project_id'])
  60 + ->orderBy('gl_project.id', 'asc')
  61 + ->get()->toArray();
50 //有排名api编号的项目 62 //有排名api编号的项目
51 - $list = DeployOptimize::where('api_no', '>', 0)->select('api_no', 'project_id')->orderBy('project_id', 'asc')->get()->toArray(); 63 +// $list = DeployOptimize::where('api_no', '>', 0)->select('api_no', 'project_id')->orderBy('project_id', 'asc')->get()->toArray();
52 //特殊项目 一个项目多个apino 64 //特殊项目 一个项目多个apino
53 $list[] = ['api_no' => 11201, 'project_id' => 2104]; 65 $list[] = ['api_no' => 11201, 'project_id' => 2104];
54 $list[] = ['api_no' => 10690, 'project_id' => 2104]; 66 $list[] = ['api_no' => 10690, 'project_id' => 2104];
@@ -55,16 +55,27 @@ class RecommendedSuppliers extends Command @@ -55,16 +55,27 @@ class RecommendedSuppliers extends Command
55 */ 55 */
56 public function handle() 56 public function handle()
57 { 57 {
58 - $project_list = $this->deployBuildModel->list(['is_supplier'=>1]);//TODO::已开启推荐供应商 58 + $projectModel = new Project();
  59 + $projectArr = $projectModel->selectField(['delete_status'=>0,'type'=>['in',[2,3,4]]],'id');
  60 + $project_list = $this->deployBuildModel->list(['is_supplier'=>1,'project_id'=>['in',$projectArr]]);//TODO::已开启推荐供应商
59 foreach ($project_list as $v){ 61 foreach ($project_list as $v){
  62 + try {
60 echo date('Y-m-d H:i:s') . '推荐供应商执行的project_id:'.$v['project_id'] . PHP_EOL; 63 echo date('Y-m-d H:i:s') . '推荐供应商执行的project_id:'.$v['project_id'] . PHP_EOL;
61 $result = $this->countPurchaser($v); 64 $result = $this->countPurchaser($v);
62 if($result !== false){ 65 if($result !== false){
63 ProjectServer::useProject($v['project_id']); 66 ProjectServer::useProject($v['project_id']);
64 $title = $this->getKeywords($v['project_id']); 67 $title = $this->getKeywords($v['project_id']);
  68 + if(!empty($title)){
65 $this->savePurchaser($v['project_id'],$title); 69 $this->savePurchaser($v['project_id'],$title);
  70 + }else{
  71 + echo '关键词已取完'.PHP_EOL;
  72 + }
66 DB::disconnect('custom_mysql'); 73 DB::disconnect('custom_mysql');
67 } 74 }
  75 + }catch (\Exception $e){
  76 + echo date('Y-m-d hH:i:s').'当前项目执行错误:'.$e->getMessage().PHP_EOL;
  77 + continue;
  78 + }
68 } 79 }
69 return true; 80 return true;
70 } 81 }
@@ -84,7 +95,9 @@ class RecommendedSuppliers extends Command @@ -84,7 +95,9 @@ class RecommendedSuppliers extends Command
84 $count = $purchaserInfoModel->counts(['project_id'=>$v['project_id']]); 95 $count = $purchaserInfoModel->counts(['project_id'=>$v['project_id']]);
85 //获取项目版本 96 //获取项目版本
86 $plan = ['专业版'=>300, '标准版'=>500, '商务版'=>800, '旗舰版'=>1200]; 97 $plan = ['专业版'=>300, '标准版'=>500, '商务版'=>800, '旗舰版'=>1200];
87 - $total_number = $plan[Project::planMap()[$v['plan']]] ?? 300; 98 + $typePlan = Project::planMap();
  99 + $version = $typePlan[$v['plan']] ?? '专业版';
  100 + $total_number = $plan[$version] ?? 300;
88 if($count > $total_number){ 101 if($count > $total_number){
89 echo date('Y-m-d H:i:s') . '达到数量上线关闭的项目:'.$v['project_id'] . PHP_EOL; 102 echo date('Y-m-d H:i:s') . '达到数量上线关闭的项目:'.$v['project_id'] . PHP_EOL;
90 //更新数量上限字段,并关闭推荐供应商 103 //更新数量上限字段,并关闭推荐供应商
@@ -95,12 +108,31 @@ class RecommendedSuppliers extends Command @@ -95,12 +108,31 @@ class RecommendedSuppliers extends Command
95 return true; 108 return true;
96 } 109 }
97 110
  111 + /**
  112 + * @remark :获取关键词数据
  113 + * @name :getPurchaser
  114 + * @author :lyh
  115 + * @method :post
  116 + * @time :2025/4/15 17:55
  117 + */
98 public function getPurchaser($keyword,$project_id){ 118 public function getPurchaser($keyword,$project_id){
99 $purchaserModel = new Purchaser(); 119 $purchaserModel = new Purchaser();
100 return $purchaserModel->read(['keyword'=>$keyword,'project_id'=>$project_id]); 120 return $purchaserModel->read(['keyword'=>$keyword,'project_id'=>$project_id]);
101 } 121 }
102 122
103 /** 123 /**
  124 + * @remark :获取已经处理过的关键词
  125 + * @name :getPurchaserList
  126 + * @author :lyh
  127 + * @method :post
  128 + * @time :2025/4/15 17:55
  129 + */
  130 + public function getPurchaserList($project_id){
  131 + $purchaserModel = new Purchaser();
  132 + return $purchaserModel->selectField(['project_id'=>$project_id],'keyword');
  133 + }
  134 +
  135 + /**
104 * @remark :保存供应商 136 * @remark :保存供应商
105 * @name :getPurchaser 137 * @name :getPurchaser
106 * @author :lyh 138 * @author :lyh
@@ -110,6 +142,7 @@ class RecommendedSuppliers extends Command @@ -110,6 +142,7 @@ class RecommendedSuppliers extends Command
110 public function savePurchaser($project_id,$keyword,$row = 10){ 142 public function savePurchaser($project_id,$keyword,$row = 10){
111 //项目还没有关键词 143 //项目还没有关键词
112 if(!$keyword){ 144 if(!$keyword){
  145 + echo '项目还没有关键词'.PHP_EOL;
113 return true; 146 return true;
114 } 147 }
115 $url = 'https://fob.ai.cc/api/company_list'; 148 $url = 'https://fob.ai.cc/api/company_list';
@@ -126,6 +159,7 @@ class RecommendedSuppliers extends Command @@ -126,6 +159,7 @@ class RecommendedSuppliers extends Command
126 'total'=>$this->param['row'] ?? 10, 159 'total'=>$this->param['row'] ?? 10,
127 ]; 160 ];
128 $res = http_post($url,json_encode($param)); 161 $res = http_post($url,json_encode($param));
  162 + echo '请求返回状态'. ($res['code']?? '').PHP_EOL;
129 // echo date('Y-m-d H:i:s') . json_encode($res) . PHP_EOL; 163 // echo date('Y-m-d H:i:s') . json_encode($res) . PHP_EOL;
130 if(!empty($res) && isset($res['code']) && $res['code'] == 200 && !empty($res['data'])){ 164 if(!empty($res) && isset($res['code']) && $res['code'] == 200 && !empty($res['data'])){
131 //保存多条数据 165 //保存多条数据
@@ -152,15 +186,13 @@ class RecommendedSuppliers extends Command @@ -152,15 +186,13 @@ class RecommendedSuppliers extends Command
152 * @time :2024/7/1 18:07 186 * @time :2024/7/1 18:07
153 */ 187 */
154 public function getKeywords($project_id){ 188 public function getKeywords($project_id){
155 - $info = Keyword::inRandomOrder()->first();  
156 - if(!$info){ 189 + $keywordModel = new Keyword();
  190 + $keyword_array = $this->getPurchaserList($project_id);
  191 + $info = $keywordModel->read(['title'=>['not in',$keyword_array]],'title');
  192 + if($info === false){
157 return ''; 193 return '';
158 } 194 }
159 - $keywordInfo = $this->getPurchaser($info->title,$project_id);  
160 - if($keywordInfo !== false){  
161 - $this->getKeywords($project_id);  
162 - }  
163 - return $info->title; 195 + return $info['title'] ?? '';
164 } 196 }
165 197
166 /** 198 /**
@@ -13,6 +13,7 @@ use App\Models\Domain\DomainInfo; @@ -13,6 +13,7 @@ use App\Models\Domain\DomainInfo;
13 use App\Models\Mail\Mail; 13 use App\Models\Mail\Mail;
14 use App\Models\Project\DeployBuild; 14 use App\Models\Project\DeployBuild;
15 use App\Models\Project\DeployOptimize; 15 use App\Models\Project\DeployOptimize;
  16 +use App\Models\Project\KeywordPrefix;
16 use App\Models\Project\Project; 17 use App\Models\Project\Project;
17 use App\Models\Project\ProjectKeyword; 18 use App\Models\Project\ProjectKeyword;
18 use App\Models\Project\ProjectUpdateTdk; 19 use App\Models\Project\ProjectUpdateTdk;
@@ -373,39 +374,13 @@ class UpdateSeoTdk extends Command @@ -373,39 +374,13 @@ class UpdateSeoTdk extends Command
373 $seo_title = $v[$this->topic_fields[$table]];; 374 $seo_title = $v[$this->topic_fields[$table]];;
374 //只有推广项目 且未标记特殊前后缀 才加 前后缀 375 //只有推广项目 且未标记特殊前后缀 才加 前后缀
375 if($project->type == Project::TYPE_TWO && !in_array(8, explode(',', $project->deploy_optimize->special))) { 376 if($project->type == Project::TYPE_TWO && !in_array(8, explode(',', $project->deploy_optimize->special))) {
376 - $prefix = $this->getPrefixKeyword($project_id, 'prefix', 1);  
377 - $suffix = $this->getPrefixKeyword($project_id, 'suffix', 2);  
378 - if (empty($prefix) || empty($suffix)) {  
379 - continue;  
380 - }  
381 -  
382 - $title = $seo_title;  
383 -  
384 - $prefix = $this->getPrefixKeyword($project_id, 'prefix', 1, $title);  
385 - //in,for,with,to,near,from 这些介词 只拼前缀,不拼后缀  
386 - $is_contains_jieci = false;  
387 - $words = explode(' ', $title);  
388 - foreach ($words as $word){  
389 - $word = Str::replace([',', '!', '?'], '', $word);  
390 - if(in_array(strtolower($word), ['in', 'for', 'with', 'to', 'near','from'])){  
391 - $is_contains_jieci = true;  
392 - }  
393 - }  
394 - $suffix = '';  
395 - if(!$is_contains_jieci){  
396 - // 某些后缀不能并存的情况  
397 - $ban_suffix = [];  
398 - //services/service 结尾的词,后缀不拼manufacturer,factory  
399 - if (Str::endsWith($title, ['services', 'service', 'Services', 'Service'])) {  
400 - $ban_suffix = ['manufacturer', 'factory', 'Manufacturer', 'Factory', 'Factories', 'Manufacturers'];  
401 - }  
402 - //前缀有wholesale或cheap的词,后缀不拼 manufacturer,factory,exporter,company  
403 - if (Str::startsWith($title, ['wholesale', 'cheap', 'Wholesale', 'Cheap'])) {  
404 - $ban_suffix = array_merge($ban_suffix, ['manufacturer', 'factory', 'exporter', 'company', 'Manufacturer', 'Factory', 'Exporter', 'Company', 'Factories', 'Manufacturers', 'Exporters', 'Companies']);  
405 - }  
406 - $suffix = $this->getPrefixKeyword($project_id, 'suffix', 2, $title, $ban_suffix); 377 + $prefix = $this->getPrefixKeyword($project_id, 'prefix', 1, $seo_title);
  378 + $suffix = $this->getPrefixKeyword($project_id, 'suffix', 2, $seo_title);
  379 + if(Str::startsWith($suffix, ', ')){
  380 + $seo_title = $prefix . ' ' . $seo_title . $suffix;
  381 + }else{
  382 + $seo_title = $prefix . ' ' . $seo_title . ' ' . $suffix;
407 } 383 }
408 - $seo_title = $prefix . ' ' . $title . ' ' . $suffix;  
409 } 384 }
410 385
411 $data[$field] = trim($seo_title); 386 $data[$field] = trim($seo_title);
@@ -577,61 +552,118 @@ class UpdateSeoTdk extends Command @@ -577,61 +552,118 @@ class UpdateSeoTdk extends Command
577 * @param $type 552 * @param $type
578 * @param $num 553 * @param $num
579 * @param string $topic 554 * @param string $topic
580 - * @param array $ban 被禁用的前后缀  
581 * @return string 555 * @return string
582 */ 556 */
583 - public function getPrefixKeyword($project_id, $type, $num, $topic='', $ban = []) 557 + public function getPrefixKeyword($project_id, $type, $num, $topic='')
584 { 558 {
585 $str = ''; 559 $str = '';
  560 + $ban = []; //被禁用的前后缀
  561 +
586 $info = $this->getDeployOptimize($project_id); 562 $info = $this->getDeployOptimize($project_id);
587 - if (!empty($info['keyword_' . $type])) {  
588 - $fix_keyword = explode(",", $info['keyword_' . $type]);  
589 - $fix_keyword = array_filter($fix_keyword);  
590 - //去掉标题存在的词  
591 - if ($topic) {  
592 - foreach ($fix_keyword as $k=>$keyword) {  
593 - // 被禁用的关键词  
594 - if (in_array($keyword, $ban)) {  
595 - unset($fix_keyword[$k]); 563 +
  564 + //没有勾选前后缀
  565 + if (empty($info['keyword_' . $type])) {
  566 + return $str;
  567 + }
  568 +
  569 + //前后缀(包括自定义前后缀)如果已经存在,就不在拼接当前类型 后缀只包含了一个,要再拼一个(需去重)
  570 + $all_prefixes = $this->getAllPrefix($type == 'prefix' ? 1 : 2, $project_id);
  571 + $all_prefixes = array_map('strtolower', $all_prefixes);
  572 +
  573 + //in,for,with,to,near,from 这些介词 只拼前缀,不拼后缀
  574 + $preposition = ['in', 'for', 'with', 'to', 'near','from'];
  575 +
  576 + //标题拆成词
  577 + $topic_words = explode(' ', strtolower($topic));
  578 + $i= 0;
  579 + foreach ($topic_words as $topic_word){
  580 + //包含了前后缀
  581 + if(in_array($topic_word, $all_prefixes)){
  582 + //前缀包含一个就不拼了 后缀包含两个才不再拼
  583 + if($i == $num - 1){
  584 + return $str;
  585 + }
  586 + $ban[] = $topic_word;
  587 + $i++;
  588 + $num--;
596 } 589 }
597 - // 前后缀如果已经存在, 就不在拼接当前类型  
598 - if (FALSE !== strpos($topic, $keyword)) 590 + //in,for,with,to,near,from 这些介词 只拼前缀,不拼后缀
  591 + if(in_array($topic_word, $preposition) && $type == 'suffix'){
599 return $str; 592 return $str;
600 - //复数转单数  
601 - $keyword = Str::singular($keyword); 593 + }
602 594
603 - $topic_words = explode(" ", $topic);  
604 - if($type == 'prefix' && Str::startsWith($topic_words[0], $keyword)){  
605 - unset($fix_keyword[$k]);  
606 } 595 }
607 - if($type == 'suffix' && Str::startsWith($topic_words[count($topic_words)-1], $keyword)){ 596 +
  597 + //services/service 结尾的词,后缀不拼manufacturer,factory
  598 + if (Str::endsWith(strtolower($topic), ['services', 'service']) && $type == 'suffix') {
  599 + $ban = array_merge($ban, ['manufacturer', 'manufacturers', 'factory', 'factories']);
  600 + }
  601 + //manufacturer,factory 结尾的词,后缀不拼 services/service
  602 + if (Str::endsWith(strtolower($topic), ['manufacturer', 'manufacturers', 'factory', 'factories']) && $type == 'suffix') {
  603 + $ban = array_merge($ban, ['services', 'service']);
  604 + }
  605 +
  606 + //前缀有wholesale或cheap的词,后缀不拼 manufacturer,factory,exporter,company
  607 + if (Str::startsWith(strtolower($topic), ['wholesale', 'cheap']) && $type == 'prefix') {
  608 + $ban = array_merge($ban, ['manufacturer', 'manufacturers', 'factory', 'factories', 'exporter', 'exporters', 'company', 'companies', 'supplier', 'suppliers']);
  609 + }
  610 + //关键词以manufacturer,factory,exporter,company结尾, 前缀不拼wholesale或cheap的词
  611 + if (Str::endsWith(strtolower($topic), ['manufacturer', 'manufacturers', 'factory', 'factories', 'exporter', 'exporters', 'company', 'companies', 'supplier', 'suppliers']) && $type == 'prefix') {
  612 + $ban = array_merge($ban, ['wholesale', 'cheap']);
  613 + }
  614 +
  615 + //关键词是否包含 品牌词
  616 + $brand_keywords = explode("\r\n", $info['brand_keyword']);
  617 + $is_contains_brand = false;
  618 + foreach ($brand_keywords as $brand_keyword) {
  619 + if (Str::contains(strtolower($topic), strtolower(trim($brand_keyword)))) {
  620 + $is_contains_brand = true;
  621 + break;
  622 + }
  623 + }
  624 + //包含品牌词 排除自定义前后缀
  625 + if ($is_contains_brand) {
  626 + $customer_keywords = KeywordPrefix::where('project_id', $project_id)->pluck('keyword')->toArray();
  627 + $ban = array_merge($ban, $customer_keywords);
  628 + }
  629 +
  630 + //勾选的前后缀
  631 + $fix_keyword = explode(",", $info['keyword_' . $type]);
  632 + $fix_keyword = array_filter($fix_keyword);
  633 + foreach ($fix_keyword as $k => $keyword) {
  634 + // 被禁用的关键词
  635 + if (in_array(strtolower(Str::plural($keyword)), $ban)) {
608 unset($fix_keyword[$k]); 636 unset($fix_keyword[$k]);
609 } 637 }
  638 + if (in_array(strtolower(Str::singular($keyword)), $ban)) {
  639 + unset($fix_keyword[$k]);
610 } 640 }
611 } 641 }
  642 +
612 //随机取 并单复数去重 643 //随机取 并单复数去重
613 shuffle($fix_keyword); 644 shuffle($fix_keyword);
614 $keywords = []; 645 $keywords = [];
615 - foreach ($fix_keyword as $v){  
616 - if($num == 0){ 646 + foreach ($fix_keyword as $v) {
  647 + if ($num == 0) {
617 break; 648 break;
618 } 649 }
619 $is_repeat = false; 650 $is_repeat = false;
620 - foreach ($keywords as $keyword){  
621 - if(Str::singular($keyword) == Str::singular($v)){ 651 + foreach ($keywords as $keyword) {
  652 + if (Str::singular($keyword) == Str::singular($v)) {
622 $is_repeat = true; 653 $is_repeat = true;
623 break; 654 break;
624 } 655 }
625 } 656 }
626 - if($is_repeat){ 657 + if ($is_repeat) {
627 continue; 658 continue;
628 } 659 }
629 $keywords[] = $v; 660 $keywords[] = $v;
630 $num--; 661 $num--;
631 } 662 }
632 - $str = implode(', ', $keywords); 663 + if ($type == 'suffix' && count($keywords) == 1 && in_array(Arr::last($topic_words), $all_prefixes)) {
  664 + return ', ' . $keywords[0];
633 } 665 }
634 - return $str; 666 + return implode(', ', $keywords);
635 } 667 }
636 668
637 669
@@ -653,6 +685,24 @@ class UpdateSeoTdk extends Command @@ -653,6 +685,24 @@ class UpdateSeoTdk extends Command
653 } 685 }
654 686
655 /** 687 /**
  688 + * 所有前后缀 和 当期项目的自定义前后缀
  689 + * @param $type
  690 + * @param int $project_id
  691 + * @return mixed
  692 + * @author zbj
  693 + * @date 2025/4/15
  694 + */
  695 + public function getAllPrefix($type, int $project_id = 0){
  696 + $cache_key = 'AllPrefix_' . $type . '_' . $project_id;
  697 + $data = Cache::get($cache_key);
  698 + if(!$data){
  699 + $data = KeywordPrefix::whereIn('project_id', [0, $project_id])->where('type', $type)->pluck('keyword')->toArray();
  700 + Cache::put($cache_key, $data, 600);
  701 + }
  702 + return $data;
  703 + }
  704 +
  705 + /**
656 * @remark :获取公司英文名称 706 * @remark :获取公司英文名称
657 * @name :companyName 707 * @name :companyName
658 * @author :lyh 708 * @author :lyh
@@ -185,6 +185,9 @@ class KeywordController extends BaseController @@ -185,6 +185,9 @@ class KeywordController extends BaseController
185 */ 185 */
186 public function batchKeywordFiled(){ 186 public function batchKeywordFiled(){
187 $param = []; 187 $param = [];
  188 + if(isset($this->param['seo_title'])){
  189 + $param['seo_title'] = null;
  190 + }
188 if(isset($this->param['keyword'])){ 191 if(isset($this->param['keyword'])){
189 $param['seo_keywords'] = null; 192 $param['seo_keywords'] = null;
190 } 193 }
@@ -53,9 +53,11 @@ use App\Services\DingService; @@ -53,9 +53,11 @@ use App\Services\DingService;
53 use App\Services\ProjectServer; 53 use App\Services\ProjectServer;
54 use App\Services\SyncService; 54 use App\Services\SyncService;
55 use App\Utils\LogUtils; 55 use App\Utils\LogUtils;
  56 +use AWS\CRT\Log;
56 use Illuminate\Support\Facades\Cache; 57 use Illuminate\Support\Facades\Cache;
57 use Illuminate\Support\Facades\DB; 58 use Illuminate\Support\Facades\DB;
58 use Illuminate\Support\Facades\Http; 59 use Illuminate\Support\Facades\Http;
  60 +use Illuminate\Support\Facades\Log as LogInfo;
59 61
60 /** 62 /**
61 * Class ProjectLogic 63 * Class ProjectLogic
@@ -159,13 +161,14 @@ class ProjectLogic extends BaseLogic @@ -159,13 +161,14 @@ class ProjectLogic extends BaseLogic
159 * @param :1->建站中 2->优化中 3->建站完成 6-》错误单 161 * @param :1->建站中 2->优化中 3->建站完成 6-》错误单
160 */ 162 */
161 public function projectSave(){ 163 public function projectSave(){
162 - $this->saveSeoPlan($this->param['id'],$this->param['type'],$this->param['deploy_build']['plan'],$this->param['deploy_build']['seo_plan'],$this->param['deploy_optimize']['optimist_mid'] ?? 0,$this->param['deploy_optimize']['quality_mid'] ?? 0);  
163 - $this->checkAiBlog($this->param['main_lang_id'],$this->param['is_ai_blog'],$this->param['company'],$this->param['deploy_optimize']['company_en_name'] ?? '',$this->param['deploy_optimize']['company_en_description'] ?? '');  
164 - DB::beginTransaction();  
165 - try {  
166 if($this->param['type'] == Project::TYPE_SEVEN){ 164 if($this->param['type'] == Project::TYPE_SEVEN){
167 $this->setTypeSevenEdit($this->param); 165 $this->setTypeSevenEdit($this->param);
168 }else{ 166 }else{
  167 + $this->param = $this->handleLevelStr($this->param);//处理星级客户暂停优化默认参数
  168 + $this->saveSeoPlan($this->param);//保存seo白帽类型,上线保存一条审核记录
  169 + $this->checkAiBlog($this->param);//开启白帽验证参数
  170 + DB::beginTransaction();
  171 + try {
169 //初始化项目 172 //初始化项目
170 $this->param = $this->createProjectData($this->param); 173 $this->param = $this->createProjectData($this->param);
171 //双向绑定服务器,需放到保存项目的上方 174 //双向绑定服务器,需放到保存项目的上方
@@ -191,16 +194,37 @@ class ProjectLogic extends BaseLogic @@ -191,16 +194,37 @@ class ProjectLogic extends BaseLogic
191 $this->syncImageFile($this->param['project_location'],$this->param['id']); 194 $this->syncImageFile($this->param['project_location'],$this->param['id']);
192 //同步信息表 195 //同步信息表
193 (new SyncService())->projectAcceptAddress($this->param['id']); 196 (new SyncService())->projectAcceptAddress($this->param['id']);
194 - }  
195 DB::commit(); 197 DB::commit();
196 }catch (\Exception $e){ 198 }catch (\Exception $e){
  199 + LogInfo::info('项目保存失败:project_id'.$this->param['id'].',错误详情:'.$e->getMessage(). PHP_EOL);
197 DB::rollBack(); 200 DB::rollBack();
198 $this->fail('保存失败,请联系管理员'); 201 $this->fail('保存失败,请联系管理员');
199 } 202 }
  203 + }
200 return $this->success(); 204 return $this->success();
201 } 205 }
202 206
203 /** 207 /**
  208 + * @remark :星际客户为(2,3)暂停优化
  209 + * @name :handleLevelStr
  210 + * @author :lyh
  211 + * @method :post
  212 + * @time :2025/4/16 11:14
  213 + * @remark :http://zentao.globalso.com/index.php?m=task&f=view&taskID=195163
  214 + */
  215 + public function handleLevelStr($param){
  216 + if(!empty($param['level'])){
  217 + if (in_array('2', $param['level']) || in_array('3', $param['level'])) {
  218 + //优化设置默认关闭
  219 + $param['is_ai_blog'] = 0;
  220 + $param['deploy_optimize']['is_ai_blog_send'] = 0;
  221 + $param['deploy_optimize']['is_auto_keywords'] = 0;
  222 + }
  223 + }
  224 + return $param;
  225 + }
  226 +
  227 + /**
204 * @remark :保存上线审核问题 228 * @remark :保存上线审核问题
205 * @name :saveOnlineCheck 229 * @name :saveOnlineCheck
206 * @author :lyh 230 * @author :lyh
@@ -235,13 +259,18 @@ class ProjectLogic extends BaseLogic @@ -235,13 +259,18 @@ class ProjectLogic extends BaseLogic
235 } 259 }
236 260
237 /** 261 /**
238 - * @remark :开启白帽验证 262 + * @remark :开启白帽验证
239 * @name :checkAiBlog 263 * @name :checkAiBlog
240 * @author :lyh 264 * @author :lyh
241 * @method :post 265 * @method :post
242 * @time :2025/3/21 17:32 266 * @time :2025/3/21 17:32
243 */ 267 */
244 - public function checkAiBlog($main_lang_id,$is_ai_blog,$company,$company_en_name,$company_en_description){ 268 + public function checkAiBlog($param){
  269 + $main_lang_id = $param['main_lang_id'] ?? 0;
  270 + $is_ai_blog = $param['is_ai_blog'] ?? 0;
  271 + $company = $param['company'] ?? '';
  272 + $company_en_name = $param['deploy_optimize']['company_en_name'] ?? '';
  273 + $company_en_description = $param['deploy_optimize']['company_en_description'] ?? '';
245 if($is_ai_blog == 1){ 274 if($is_ai_blog == 1){
246 if(empty($main_lang_id) || empty($company) || empty($company_en_name) || empty($company_en_description)){ 275 if(empty($main_lang_id) || empty($company) || empty($company_en_name) || empty($company_en_description)){
247 $this->fail('开启ai_blog--请填写主语种+公司名称+公司英文名称+公司英文介绍'); 276 $this->fail('开启ai_blog--请填写主语种+公司名称+公司英文名称+公司英文介绍');
@@ -488,7 +517,13 @@ class ProjectLogic extends BaseLogic @@ -488,7 +517,13 @@ class ProjectLogic extends BaseLogic
488 * @method :post 517 * @method :post
489 * @time :2025/4/1 15:33 518 * @time :2025/4/1 15:33
490 */ 519 */
491 - protected function saveSeoPlan($project_id,$type,$plan,$seo_plan,$optimist_mid,$quality_mid){ 520 + protected function saveSeoPlan($param){
  521 + $project_id = $param['id'];
  522 + $type = $param['type'];
  523 + $plan = $param['deploy_build']['plan'];
  524 + $seo_plan = $param['deploy_build']['seo_plan'];
  525 + $optimist_mid = $param['deploy_optimize']['optimist_mid'] ?? 0;
  526 + $quality_mid = $param['deploy_optimize']['quality_mid'] ?? 0;
492 $onlineCheckModel = new OnlineCheck(); 527 $onlineCheckModel = new OnlineCheck();
493 if(($plan == Project::TYPE_ZERO) && ($seo_plan == Project::TYPE_ONE) && ($type == Project::TYPE_TWO || $type == Project::TYPE_THREE)){ 528 if(($plan == Project::TYPE_ZERO) && ($seo_plan == Project::TYPE_ONE) && ($type == Project::TYPE_TWO || $type == Project::TYPE_THREE)){
494 $onlineInfo = $onlineCheckModel->read(['project_id'=>$project_id]); 529 $onlineInfo = $onlineCheckModel->read(['project_id'=>$project_id]);
@@ -62,7 +62,12 @@ class RankDataLogic extends BaseLogic @@ -62,7 +62,12 @@ class RankDataLogic extends BaseLogic
62 $external_links = ExternalLinks::where('project_id', $project_id)->where('api_no', $api_no)->first(); 62 $external_links = ExternalLinks::where('project_id', $project_id)->where('api_no', $api_no)->first();
63 $indexed_pages = IndexedPages::where('project_id', $project_id)->where('api_no', $api_no)->first(); 63 $indexed_pages = IndexedPages::where('project_id', $project_id)->where('api_no', $api_no)->first();
64 $speed = Speed::where('project_id', $project_id)->first(); 64 $speed = Speed::where('project_id', $project_id)->first();
65 - 65 + //暂停优化的项目(排名数据显示横杠 - ,关键词排名第一页 、 关键词排名前十页 、 Google收录页面数 、 可查询外链数)
  66 + if(!empty($project['level'])){
  67 + if (in_array('2', $project['level']) || in_array('3', $project['level'])) {
  68 + $rank['first_page_num'] = $rank['first_ten_pages_num'] = $rank['indexed_pages_num'] = $external_links['total'] = '-';
  69 + }
  70 + }
66 //排名数据 71 //排名数据
67 $data = [ 72 $data = [
68 'first_num' => $rank['first_num'] ?? 0, 73 'first_num' => $rank['first_num'] ?? 0,
@@ -195,6 +195,7 @@ class TranslateLogic extends BaseLogic @@ -195,6 +195,7 @@ class TranslateLogic extends BaseLogic
195 $texts = $dom->find("text"); 195 $texts = $dom->find("text");
196 $description = $dom->find("meta[name=description]",0); 196 $description = $dom->find("meta[name=description]",0);
197 $keywords = $dom->find("meta[name=keywords]",0); 197 $keywords = $dom->find("meta[name=keywords]",0);
  198 + $placeholders = $dom->find("[placeholder]");
198 // 组装需要翻译的内容 HTML内文案、meta description、meta keywords 199 // 组装需要翻译的内容 HTML内文案、meta description、meta keywords
199 $need_tran = []; 200 $need_tran = [];
200 foreach ($texts as $k=>$text) { 201 foreach ($texts as $k=>$text) {
@@ -217,6 +218,12 @@ class TranslateLogic extends BaseLogic @@ -217,6 +218,12 @@ class TranslateLogic extends BaseLogic
217 $need_tran[] = htmlspecialchars_decode(html_entity_decode($string)); 218 $need_tran[] = htmlspecialchars_decode(html_entity_decode($string));
218 } 219 }
219 } 220 }
  221 + foreach ($placeholders as $placeholder) {
  222 + $placeholderText = trim($placeholder->placeholder);
  223 + if ($placeholderText) {
  224 + $need_tran[] = $placeholderText;
  225 + }
  226 + }
220 $need_tran[] = $description ? $description->attr['content'] : ''; 227 $need_tran[] = $description ? $description->attr['content'] : '';
221 $need_tran[] = $keywords ? $keywords->attr['content'] : ''; 228 $need_tran[] = $keywords ? $keywords->attr['content'] : '';
222 $need_tran = array_values(array_unique($need_tran)); 229 $need_tran = array_values(array_unique($need_tran));
@@ -283,9 +290,9 @@ class TranslateLogic extends BaseLogic @@ -283,9 +290,9 @@ class TranslateLogic extends BaseLogic
283 } 290 }
284 DB::beginTransaction(); 291 DB::beginTransaction();
285 try { 292 try {
286 - $info = $this->model->read(['language_id'=>$this->param['language_id'],'url'=>$this->param['url'],'project_id'=>$this->user['project_id'],'type'=>$this->param['type']]);  
287 - if($info === false){  
288 $sourceInfo = $this->getRouteSource($sendData['new_route']); 293 $sourceInfo = $this->getRouteSource($sendData['new_route']);
  294 + $info = $this->model->read(['language_id'=>$this->param['language_id'],'source_id'=>$sourceInfo['source_id'],'url'=>$this->param['url'],'project_id'=>$this->user['project_id'],'type'=>$this->param['type']]);
  295 + if($info === false){
289 $param = [ 296 $param = [
290 'type'=>$this->param['type'] ?? 1, 297 'type'=>$this->param['type'] ?? 1,
291 'project_id'=>$this->user['project_id'], 298 'project_id'=>$this->user['project_id'],