作者 赵彬吉
正在显示 42 个修改的文件 包含 1044 行增加88 行删除
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :AiBlogTask.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/2/14 11:14
  8 + */
  9 +
  10 +namespace App\Console\Commands\Ai;
  11 +
  12 +use App\Models\Ai\AiBlog;
  13 +use App\Models\Ai\AiBlogAuthor;
  14 +use App\Models\Ai\AiBlogList;
  15 +use App\Models\Domain\DomainInfo;
  16 +use App\Models\Project\Project;
  17 +use App\Models\Project\ProjectAiSetting;
  18 +use App\Models\RouteMap\RouteMap;
  19 +use App\Services\AiBlogService;
  20 +use App\Services\ProjectServer;
  21 +use Illuminate\Console\Command;
  22 +use App\Models\Project\AiBlogTask as AiBlogTaskModel;
  23 +use Illuminate\Support\Facades\Cache;
  24 +use Illuminate\Support\Facades\DB;
  25 +use function Symfony\Component\String\s;
  26 +
  27 +/***
  28 + * @remark :根据项目更新blog列表
  29 + * @name :AiBlogListTask
  30 + * @author :lyh
  31 + * @method :post
  32 + * @time :2025/3/6 9:45
  33 + */
  34 +class AiBlogListAllTask extends Command
  35 +{
  36 + /**
  37 + * The name and signature of the console command.
  38 + *
  39 + * @var string
  40 + */
  41 + protected $signature = 'save_all_ai_blog_list';
  42 +
  43 + /**
  44 + * The console command description.
  45 + *
  46 + * @var string
  47 + */
  48 + protected $description = '生成blog列表';
  49 +
  50 + public function handle(){
  51 + $projectModel = new Project();
  52 + $lists = $projectModel->list(['delete_status' => 0,'project_type'=>0,'extend_type'=>0,'type'=>['in',[2,3,4,6]]], 'id', ['id']);
  53 + foreach ($lists as $item){
  54 + echo '执行的项目的id'.$item['id'].PHP_EOL;
  55 + $project_id = $item['id'];
  56 + ProjectServer::useProject($project_id);
  57 + $projectAiSettingModel = new ProjectAiSetting();
  58 + $aiSettingInfo = $projectAiSettingModel->read(['project_id'=>$project_id]);
  59 + if($aiSettingInfo === false){
  60 + echo '未加载到配置。'.PHP_EOL;
  61 + continue;
  62 + }
  63 + $this->updateBlogList($aiSettingInfo);
  64 + $this->curlDelRoute($project_id);
  65 + DB::disconnect('custom_mysql');
  66 + }
  67 + return true;
  68 + }
  69 +
  70 + /**
  71 + * @remark :更新列表页数据
  72 + * @name :updateBlogList
  73 + * @author :lyh
  74 + * @method :post
  75 + * @time :2025/3/5 11:07
  76 + */
  77 + public function updateBlogList($aiSettingInfo){
  78 + $aiBlogService = new AiBlogService();
  79 + $aiBlogService->mch_id = $aiSettingInfo['mch_id'];
  80 + $aiBlogService->key = $aiSettingInfo['key'];
  81 + $page = 1;
  82 + $saveData = [];
  83 + $result = $aiBlogService->getAiBlogList($page,15);
  84 + if(!isset($result['status']) || $result['status'] != 200){
  85 + return true;
  86 + }
  87 + $total_page = $result['data']['total_page'];
  88 + //组装数据保存
  89 + $saveData[] = [
  90 + 'route'=>$page,
  91 + 'text'=>$result['data']['section'],
  92 + ];
  93 + while ($total_page > $page){
  94 + $page++;
  95 + $result = $aiBlogService->getAiBlogList($page,15);
  96 + if(isset($result['status']) && $result['status'] == 200){
  97 + $saveData[] = [
  98 + 'route'=>$page,
  99 + 'text'=>$result['data']['section'],
  100 + ];
  101 + }
  102 + }
  103 + $aiBlogListModel = new AiBlogList();
  104 + if(!empty($saveData)){
  105 + //写一条路由信息
  106 + $aiBlogListModel->truncate();
  107 + $aiBlogListModel->insertAll($saveData);
  108 + }
  109 + return true;
  110 + }
  111 +
  112 + /**
  113 + * @remark :通知C端生成界面
  114 + * @name :sendNotice
  115 + * @author :lyh
  116 + * @method :post
  117 + * @time :2025/3/6 11:51
  118 + */
  119 + public function curlDelRoute($project_id){
  120 + $domainModel = new DomainInfo();
  121 + //获取项目域名
  122 + $domain = $domainModel->getProjectIdDomain($project_id);
  123 + if(!empty($domain)){
  124 + $c_url = $domain.'api/update_page/';
  125 + $param = [
  126 + 'project_id' => $project_id,
  127 + 'type' => 1,
  128 + 'route' => 3,
  129 + 'url' => ['top-blog'],
  130 + 'language'=> [],
  131 + 'is_sitemap' => 0
  132 + ];
  133 + http_post($c_url, json_encode($param));
  134 + }
  135 + return true;
  136 + }
  137 +}
@@ -72,7 +72,7 @@ class AiBlogListTask extends Command @@ -72,7 +72,7 @@ class AiBlogListTask extends Command
72 $page = 1; 72 $page = 1;
73 $saveData = []; 73 $saveData = [];
74 $result = $aiBlogService->getAiBlogList($page,15); 74 $result = $aiBlogService->getAiBlogList($page,15);
75 - if(!isset($result['status']) && $result['status'] != 200){ 75 + if(!isset($result['status']) || $result['status'] != 200){
76 return true; 76 return true;
77 } 77 }
78 $total_page = $result['data']['total_page']; 78 $total_page = $result['data']['total_page'];
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :AiVideoAutoPublish.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/8/1 15:19
  8 + */
  9 +
  10 +namespace App\Console\Commands\Ai;
  11 +
  12 +use App\Models\Ai\AiVideo;
  13 +use App\Models\Product\Keyword;
  14 +use App\Models\Product\Product;
  15 +use App\Models\Project\AiBlogTask as AiBlogTaskModel;
  16 +use App\Models\Project\AiVideoAutoLog;
  17 +use App\Models\Project\DeployOptimize;
  18 +use App\Models\Project\Project;
  19 +use App\Services\AiVideoService;
  20 +use App\Services\MidJourneyService;
  21 +use App\Services\ProjectServer;
  22 +use Illuminate\Console\Command;
  23 +use Illuminate\Support\Facades\DB;
  24 +use Illuminate\Support\Facades\Log;
  25 +use App\Models\Project\AiVideoTask;
  26 +use Illuminate\Support\Facades\Redis;
  27 +
  28 +/**
  29 + * @remark :ai视频自动发布
  30 + * @name :AiVideoAutoPublish
  31 + * @author :lyh
  32 + * @method :post
  33 + * @time :2025/8/1 15:19
  34 + */
  35 +class AiVideoAutoPublish extends Command
  36 +{
  37 + /**
  38 + * The name and signature of the console command.
  39 + *
  40 + * @var string
  41 + */
  42 + protected $signature = 'ai_video_auto_publish {action}';
  43 +
  44 + /**
  45 + * The console command description.
  46 + *
  47 + * @var string
  48 + */
  49 + protected $description = '自动发布AI Video';
  50 +
  51 + public function handle(){
  52 + $action = $this->argument('action');
  53 + if($action == 'auto_publish'){
  54 + $this->auto_publish();
  55 + }
  56 + }
  57 +
  58 + /**
  59 + * @remark :普通项目--自动发布
  60 + * @name :auto_six_publish
  61 + * @author :lyh
  62 + * @method :post
  63 + * @time :2025/8/1 15:22
  64 + */
  65 + public function auto_publish(){
  66 + $this->output('开始自动发布Video文章');
  67 + $projectModel = new Project();
  68 + $optimizeModel = new DeployOptimize();
  69 + $projectList = $projectModel->list(['is_ai_video'=>1,'id'=>1,'project_type'=>0,'delete_status'=>0,'site_status'=>0,'extend_type'=>0],'id',['id']);
  70 + foreach ($projectList as $item){
  71 + $this->output("项目{$item['id']}开始自动发布");
  72 + //获取当前是否开启自动发布aiVideo
  73 + $opInfo = $optimizeModel->read(['project_id'=>$item['id']],['is_ai_video_send','send_ai_video_frequency','start_date']);
  74 + if($opInfo['is_ai_video_send'] != 1){
  75 + $this->output("项目{$item['id']}未开启自动发布" . $opInfo['start_date']);
  76 + continue;
  77 + }
  78 + if(($opInfo['start_date'] > date('Y-m-d')) || empty($opInfo['start_date'])){
  79 + $this->output("项目{$item['id']}未到推广时间" . $opInfo['start_date']);
  80 + continue;
  81 + }
  82 + $aiVideoTaskModel = new AiVideoTask();
  83 + $next_auto_date = $aiVideoTaskModel->formatQuery(['project_id'=>$item['id'],'next_auto_date'=>['!=',null]])->orderBy('id', 'desc')->value('next_auto_date');
  84 + if($next_auto_date && ($next_auto_date > date('Y-m-d'))){
  85 + $this->output("项目{$item['id']}未到执行时间" . $next_auto_date);
  86 + continue;
  87 + }
  88 + //获取当前网站的标题
  89 + ProjectServer::useProject($item['id']);
  90 + $data = $this->getVideoInfo();
  91 + if(!empty($data)){
  92 + //写入一条零时生成视频记录
  93 + $aiVideoAutoLogModel = new AiVideoAutoLog();
  94 + $aiVideoAutoLogModel->addReturnId(
  95 + ['project_id'=>$item['id'],'title'=>$data['title'],'remark'=>$data['remark'],'images'=>json_encode($data['images'],true),'date'=>date('Y-m-d')]
  96 + );
  97 + }
  98 + DB::disconnect('custom_mysql');
  99 + }
  100 + }
  101 +
  102 + /**
  103 + * @remark :获取产品标题+产品描述
  104 + * @name :getProduct
  105 + * @author :lyh
  106 + * @method :post
  107 + * @time :2025/8/1 16:09
  108 + */
  109 + public function getVideoInfo(){
  110 + $data = [];
  111 + $random = 2;
  112 + if($random == 1){//取产品
  113 + $productModel = new Product();
  114 + $info = $productModel->formatQuery(['status'=>1,'title'=>['!=',null],'intro'=>['!=',null]])->select(['title','gallery','intro'])->inRandomOrder()->first();
  115 + if(empty($info)){
  116 + return $data;
  117 + }
  118 + $data['title'] = $info['title'];
  119 + $data['remark'] = strip_tags($info['intro']);
  120 + if(empty($data['remark'])){
  121 + $data['remark'] = $data['title'];
  122 + }
  123 + $data['images'] = array_filter(array_map(function ($item) use ($data) {
  124 + if (!empty($item['url'])) {
  125 + return [
  126 + 'alt' => $item['title'] ?? $data['title'],
  127 + 'url' => getImageUrl($item['url']),
  128 + ];
  129 + }
  130 + return null;
  131 + }, $info['gallery']));
  132 + return $data;
  133 + }else{
  134 + //聚合页获取当前关联产品的图片
  135 + $keywordModel = new Keyword();
  136 + $keywordInfo = $keywordModel->formatQuery(['keyword_title'=>['!=',null],'keyword_content'=>['!=',null]])->select(['keyword_title','keyword_content'])->inRandomOrder()->first();
  137 + if(empty($keywordInfo)){
  138 + return $data;
  139 + }
  140 + $data['title'] = $keywordInfo['keyword_title'];
  141 + $data['remark'] = $keywordInfo['keyword_content'];
  142 + $data['remark'] = strip_tags($keywordInfo['intro']);
  143 + if(empty($data['remark'])){
  144 + $data['remark'] = $data['title'];
  145 + }
  146 + $data['images'] = [];
  147 + $productModel = new Product();
  148 + $productList = $productModel->list(['keyword_id'=>['like','%,'.$keywordInfo['id'].',%']],'id',['gallery'],'desc',10);
  149 + foreach ($productList as $info){
  150 + $images = array_filter(array_map(function ($item) use ($data) {
  151 + if (!empty($item['url'])) {
  152 + return [
  153 + 'alt' => $item['title'] ?? $data['title'],
  154 + 'url' => getImageUrl($item['url']),
  155 + ];
  156 + }
  157 + return null; // 返回 null 让 array_filter 去除
  158 + }, $info['gallery']));
  159 + $data['images'] = array_merge($data['images'],$images);
  160 + }
  161 + return $data;
  162 + }
  163 + }
  164 +
  165 + /**
  166 + * @remark :组装缺少图片数据-推送至发送平台
  167 + * @name :send_video
  168 + * @author :lyh
  169 + * @method :post
  170 + * @time :2025/8/2 10:37
  171 + */
  172 + public function auto_send_video(){
  173 + $number = Redis::get('ai_video_image') ?? 0;
  174 + $aiVideoAutoLogModel = new AiVideoAutoLog();
  175 + while (true){
  176 + if($number > 5){
  177 + echo date('Y-m-d H:i:s').':当前生成图片数量已达到最大限度。'.$number.PHP_EOL;
  178 + sleep(300);
  179 + continue;
  180 + }
  181 + $item = $aiVideoAutoLogModel->read(['status'=>0]);
  182 + if($item === false){
  183 + echo date('Y-m-d H:i:s').':无需生成图片的数据。'.PHP_EOL;
  184 + sleep(60);
  185 + continue;
  186 + }
  187 + if(count($item['images']) < 6){
  188 + echo date('Y-m-d H:i:s').':提交生成图片。'.$item['project_id'].PHP_EOL;
  189 + //需要生成图片
  190 + $content = "{$item['remark']}{$item['title']}4K,高清 --no logo --ar 16:9";
  191 + $midJourneyService = new MidJourneyService();
  192 + $result = $midJourneyService->imagine($content);
  193 + if($result && !empty($result['trigger_id'])){
  194 + echo '提交的数据详情。'.json_encode($result,true).$item['project_id'].PHP_EOL;
  195 + Redis::incr('ai_video_image');
  196 + $aiVideoAutoLogModel->edit(['trigger_id'=>$result['trigger_id']],['id'=>$item['id']]);
  197 + }
  198 + }else{
  199 + //提交到待执行
  200 + $aiVideoAutoLogModel->edit(['status'=>1],['id'=>$item['id']]);
  201 + }
  202 + }
  203 + }
  204 +
  205 + /**
  206 + * @remark :状态为1的数据推送至生成视频任务表
  207 + * @name :auto_save_video_task
  208 + * @author :lyh
  209 + * @method :post
  210 + * @time :2025/8/4 9:39
  211 + */
  212 + public function auto_save_video_task(){
  213 + $aiVideoAutoLogModel = new AiVideoAutoLog();
  214 + while (true){
  215 + //获取任务id
  216 + $task_id = $this->getAutoTaskId();
  217 + if(empty($task_id)){
  218 + sleep(300);
  219 + continue;
  220 + }
  221 + $info = $aiVideoAutoLogModel->read(['id'=>$task_id]);
  222 + if($info === false){
  223 + echo date('Y-m-d H:i:s').':当前数据不存在或已被删除'.$task_id.PHP_EOL;
  224 + continue;
  225 + }
  226 + try {
  227 + $aiVideoTaskModel = new AiVideoTask();
  228 + $aiVideoService = new AiVideoService($info['project_id']);
  229 + $projectModel = new DeployOptimize();
  230 + $video_setting = $projectModel->getValue(['project_id'=>$info['project_id']],'video_setting');
  231 + $storage = $aiVideoTaskModel->videoSetting()[$video_setting ?? 1];
  232 + $result = $aiVideoService->createTask($info['title'],$info['remark'],$info['images'],[],$storage);
  233 + if($result['status'] == 200){
  234 + $aiVideoTaskModel->addReturnId(['task_id'=>$result['data']['task_id'],'project_id'=>$info['project_id'],'storage'=>$storage]);
  235 + ProjectServer::useProject($info['project_id']);
  236 + $aiVideoModel = new AiVideo();
  237 + $aiVideoModel->addReturnId(['title'=>$info['title'],'task_id'=>$result['data']['task_id'],'description'=>$info['remark'],'project_id'=>$info['project_id'],'images'=>json_encode($info['images'],true),'anchor'=>json_encode([],true)]);
  238 + DB::disconnect('custom_mysql');
  239 + }
  240 + }catch (\Exception $e){
  241 + echo '错误信息:'.$e->getMessage().PHP_EOL;
  242 + continue;
  243 + }
  244 + }
  245 +
  246 +
  247 + }
  248 +
  249 + /**
  250 + * @remark :火锅自动发布任务id
  251 + * @name :getAutoTaskId
  252 + * @author :lyh
  253 + * @method :post
  254 + * @time :2025/8/4 9:44
  255 + */
  256 + public function getAutoTaskId()
  257 + {
  258 + $task_id = Redis::rpop('auto_ai_video_task');
  259 + if (empty($task_id)) {
  260 + $aiVideoAutoLogModel = new AiVideoAutoLog();
  261 + $ids = $aiVideoAutoLogModel->formatQuery(['status'=>1])->pluck('id');
  262 + if(!empty($ids)){
  263 + foreach ($ids as $id) {
  264 + Redis::lpush('auto_ai_video_task', $id);
  265 + }
  266 + }
  267 + $task_id = Redis::rpop('auto_ai_video_task');
  268 + }
  269 + return $task_id;
  270 + }
  271 +
  272 + /**
  273 + * @remark :
  274 + * @name :getAiVideoParam
  275 + * @author :lyh
  276 + * @method :post
  277 + * @time :2025/8/1 16:25
  278 + */
  279 + public function getAiVideoParam()
  280 + {
  281 + $project_id = '1';
  282 + $domain = 'www.cs-conveyor.com';
  283 + try {
  284 + $sitemap_url = 'https://' . $domain . '/sitemap_post_tag.xml';
  285 + $sitemap_string = file_get_contents($sitemap_url);
  286 + $xml = new \SimpleXMLElement($sitemap_string);
  287 + $json = json_encode($xml);
  288 + $array = json_decode($json, true);
  289 + $urls = array_column($array['url'], 'loc');
  290 + $num = 0;
  291 + if ($num >= 10) {
  292 + return false;
  293 + }
  294 + AGAIN:
  295 + $url = $urls[array_rand($urls)];
  296 + $dom = file_get_html($url);
  297 + $h1 = $dom->find('.layout .global_section h1', 0);
  298 + $title = $h1 ? trim($h1->plaintext) : '';
  299 + $p = $dom->find('.layout .global_section p', 0);
  300 + $content = $p ? trim($p->plaintext) : '';
  301 + $img = $dom->find('.layout .global_section img');
  302 + $images = [];
  303 + foreach ($img as $item) {
  304 + if (empty($item->src) || empty($item->alt))
  305 + continue;
  306 + array_push($images, ['src' => $item->src, 'alt' => $item->alt]);
  307 + }
  308 + if (empty($title) || empty($content) || empty($images)) {
  309 + $num++;
  310 + goto AGAIN;
  311 + }
  312 + } catch (\Exception $e) {
  313 + echo 'project_id: ' . $project_id . ', domain: ' . $domain . ', error: ' . $e->getMessage() . PHP_EOL;
  314 + }
  315 + }
  316 +
  317 + /**
  318 + * @remark :日志
  319 + * @name :output
  320 + * @author :lyh
  321 + * @method :post
  322 + * @time :2025/8/1 15:28
  323 + */
  324 + public function output($message)
  325 + {
  326 + Log::channel('ai_video')->info($message);
  327 + echo date('Y-m-d H:i:s') . ' ' . $message . PHP_EOL;
  328 + }
  329 +}
@@ -127,7 +127,7 @@ class RemainDay extends Command @@ -127,7 +127,7 @@ class RemainDay extends Command
127 //按自然日统计 127 //按自然日统计
128 $diff = time() - strtotime($item['uptime']); 128 $diff = time() - strtotime($item['uptime']);
129 $compliance_day = floor($diff / (60 * 60 * 24)); 129 $compliance_day = floor($diff / (60 * 60 * 24));
130 - $seo_remain_day = $deploy_build['service_duration'] - $compliance_day; 130 + $seo_remain_day = $deploy_build['seo_service_duration'] - $compliance_day;
131 } 131 }
132 if($deploy_build['plan'] == 0 && $seo_remain_day < 0 && $deploy_build['seo_service_duration'] != 0){//只有白帽版本的项目且剩余服务时常为0,放入未续费中 132 if($deploy_build['plan'] == 0 && $seo_remain_day < 0 && $deploy_build['seo_service_duration'] != 0){//只有白帽版本的项目且剩余服务时常为0,放入未续费中
133 // $this->project->edit(['seo_remain_day'=>$seo_remain_day,'finish_remain_day'=>$compliance_day ?? 0,'extend_type'=>Project::TYPE_FIVE],['id'=>$item['id']]); 133 // $this->project->edit(['seo_remain_day'=>$seo_remain_day,'finish_remain_day'=>$compliance_day ?? 0,'extend_type'=>Project::TYPE_FIVE],['id'=>$item['id']]);
@@ -47,7 +47,7 @@ class GeoQuestionRes extends Command @@ -47,7 +47,7 @@ class GeoQuestionRes extends Command
47 sleep(300); 47 sleep(300);
48 continue; 48 continue;
49 } 49 }
50 - echo date('Y-m-d H:i:s').'执行的任务id:'.$task_id.PHP_EOL; 50 + $this->output('执行的任务ID:' . $task_id);
51 $geoQuestionModel = new GeoQuestion(); 51 $geoQuestionModel = new GeoQuestion();
52 $taskInfo = $geoQuestionModel->read(['id'=>$task_id]); 52 $taskInfo = $geoQuestionModel->read(['id'=>$task_id]);
53 if ($taskInfo === false) { 53 if ($taskInfo === false) {
@@ -85,23 +85,20 @@ class GeoQuestionRes extends Command @@ -85,23 +85,20 @@ class GeoQuestionRes extends Command
85 GET_RESULT: 85 GET_RESULT:
86 $error_num++; 86 $error_num++;
87 try { 87 try {
88 - if ($error_num >= 3) { 88 + if ($error_num > 3) {
  89 + $this->output('任务ID:' . $task_id . ', 项目ID:' . $taskInfo['project_id'] . ', 平台:' . $platform . ', 问题:' . $question . ', 获取失败.');
89 continue; 90 continue;
90 } 91 }
91 - $this->output('执行平台:' . $platform . '执行次数:' . $error_num); 92 + $this->output('平台:' . $platform . ', 执行次数:' . $error_num);
92 switch ($platform){ 93 switch ($platform){
93 case 'google_ai_overview': 94 case 'google_ai_overview':
94 // overview 数据结构不确定, 需要单独处理数据 95 // overview 数据结构不确定, 需要单独处理数据
95 $data = $geo_service->getGooglePlatformResult($question); 96 $data = $geo_service->getGooglePlatformResult($question);
96 $result = $this->dealGoogleData($data); 97 $result = $this->dealGoogleData($data);
97 break; 98 break;
98 - case 'deep_seek':  
99 - $data = $geo_service->getDeepSeekResult($question);  
100 - $result = $this->dealDeepSeek($data);  
101 - break;  
102 case 'openai-not-network': 99 case 'openai-not-network':
103 - $data = $geo_service->getDeepSeekResult($question,'gpt-4o-mini');  
104 - $result = $this->dealDeepSeek($data,'gpt-4o-mini'); 100 + $data = $geo_service->getChatResult($question, 'gpt-4o-mini');
  101 + $result = $this->dealChatData($data, 'gpt-4o-mini');
105 break; 102 break;
106 default: 103 default:
107 $result = $geo_service->getAiPlatformResult($question, $platform); 104 $result = $geo_service->getAiPlatformResult($question, $platform);
@@ -158,7 +155,7 @@ class GeoQuestionRes extends Command @@ -158,7 +155,7 @@ class GeoQuestionRes extends Command
158 } 155 }
159 $save_data['text'] = json_encode(!empty($data) ? $data : $result,true); 156 $save_data['text'] = json_encode(!empty($data) ? $data : $result,true);
160 $geoLogModel->addReturnId($save_data); 157 $geoLogModel->addReturnId($save_data);
161 - echo '执行结束:'.$platform.PHP_EOL; 158 + $this->output('平台:' . $platform . ' 完成');
162 } 159 }
163 } 160 }
164 $next_time = date('Y-m-d', strtotime('+' . ($projectInfo['geo_frequency'] ?? 3) . ' days')); 161 $next_time = date('Y-m-d', strtotime('+' . ($projectInfo['geo_frequency'] ?? 3) . ' days'));
@@ -168,13 +165,12 @@ class GeoQuestionRes extends Command @@ -168,13 +165,12 @@ class GeoQuestionRes extends Command
168 } 165 }
169 166
170 /** 167 /**
171 - * @remark :获取命中的url  
172 - * @name :getUrl  
173 - * @author :lyh  
174 - * @method :post  
175 - * @time :2025/7/3 16:38 168 + * 获取命中的url
  169 + * @param array $urlArr
  170 + * @param array $result_annotations
  171 + * @return array
176 */ 172 */
177 - public function getUrl($urlArr = [],$result_annotations = []){ 173 + public function getUrl($urlArr = [], $result_annotations = []){
178 $url = []; 174 $url = [];
179 $url_num = []; 175 $url_num = [];
180 if(!empty($urlArr)){ 176 if(!empty($urlArr)){
@@ -187,17 +183,16 @@ class GeoQuestionRes extends Command @@ -187,17 +183,16 @@ class GeoQuestionRes extends Command
187 } 183 }
188 } 184 }
189 } 185 }
190 - return ['url'=>$url,'url_num'=>$url_num]; 186 + return ['url' => $url, 'url_num' => $url_num];
191 } 187 }
192 188
193 /** 189 /**
194 - * @remark :获取命中的关键词  
195 - * @name :getKeywords  
196 - * @author :lyh  
197 - * @method :post  
198 - * @time :2025/7/3 16:26 190 + * 获取命中的关键词
  191 + * @param array $keywordArr
  192 + * @param array $result_text
  193 + * @return array
199 */ 194 */
200 - public function getKeywords($keywordArr = [],$result_text = []){ 195 + public function getKeywords($keywordArr = [], $result_text = []){
201 $keywords = []; 196 $keywords = [];
202 $keywords_num = []; 197 $keywords_num = [];
203 if(!empty($keywordArr) && !empty($result_text)){ 198 if(!empty($keywordArr) && !empty($result_text)){
@@ -210,17 +205,16 @@ class GeoQuestionRes extends Command @@ -210,17 +205,16 @@ class GeoQuestionRes extends Command
210 } 205 }
211 } 206 }
212 } 207 }
213 - return ['keywords'=>$keywords,'keywords_num'=>$keywords_num]; 208 + return ['keywords' => $keywords, 'keywords_num' => $keywords_num];
214 } 209 }
215 210
216 /** 211 /**
217 - * @remark :整合deepSeek  
218 - * @name :requestDeepSeek  
219 - * @author :lyh  
220 - * @method :post  
221 - * @time :2025/7/15 10:58 212 + * 处理 会话 返回数据
  213 + * @param $data
  214 + * @param string $model
  215 + * @return array
222 */ 216 */
223 - public function dealDeepSeek($data,$model = 'DeepSeek'){ 217 + public function dealChatData($data, $model){
224 $result = [ 218 $result = [
225 'code' => 200, 219 'code' => 200,
226 'model' => $model, 220 'model' => $model,
@@ -9,7 +9,10 @@ use App\Models\Ai\AiCommand; @@ -9,7 +9,10 @@ use App\Models\Ai\AiCommand;
9 use App\Models\Domain\DomainInfo; 9 use App\Models\Domain\DomainInfo;
10 use App\Models\Industry\ProjectIndustry; 10 use App\Models\Industry\ProjectIndustry;
11 use App\Models\Industry\ProjectIndustryRelated; 11 use App\Models\Industry\ProjectIndustryRelated;
  12 +use App\Models\WebSetting\WebSetting;
  13 +use App\Services\ProjectServer;
12 use Illuminate\Console\Command; 14 use Illuminate\Console\Command;
  15 +use Illuminate\Support\Facades\DB;
13 16
14 class CheckProjectIndustry extends Command 17 class CheckProjectIndustry extends Command
15 { 18 {
@@ -31,7 +34,13 @@ class CheckProjectIndustry extends Command @@ -31,7 +34,13 @@ class CheckProjectIndustry extends Command
31 { 34 {
32 $ai_command = AiCommand::where('key', 'project_industry')->value('ai'); 35 $ai_command = AiCommand::where('key', 'project_industry')->value('ai');
33 if (!$ai_command) { 36 if (!$ai_command) {
34 - $this->output('AI指令未配置'); 37 + $this->output('project_industry指令未配置');
  38 + return;
  39 + }
  40 +
  41 + $ai_command_new = AiCommand::where('key', 'project_industry2')->value('ai');
  42 + if (!$ai_command_new) {
  43 + $this->output('project_industry2指令未配置');
35 return; 44 return;
36 } 45 }
37 46
@@ -55,6 +64,32 @@ class CheckProjectIndustry extends Command @@ -55,6 +64,32 @@ class CheckProjectIndustry extends Command
55 continue; 64 continue;
56 } 65 }
57 66
  67 + $is_industry = false;
  68 + $project = ProjectServer::useProject($project_id);
  69 + if ($project) {
  70 + $webSettingModel = new WebSetting();
  71 + $web_setting = $webSettingModel->read(['project_id' => $project_id], ['title', 'keyword']);
  72 + if (isset($web_setting['title']) && $web_setting['title'] && isset($web_setting['keyword']) && $web_setting['keyword']) {
  73 + $project_industry = $this->getIndustryByAINew($ai_command_new, $industry_names, $web_setting['title'], $web_setting['keyword']);
  74 + if ($project_industry) {
  75 + $project_industry_name = Arr::splitFilterToArray($project_industry);
  76 +
  77 + $project_industry_id = ProjectIndustry::where('status', 1)->whereIn('industry_name', $project_industry_name)->pluck('id')->toArray();
  78 +
  79 + if ($project_industry_id) {
  80 + ProjectIndustryRelated::saveRelated($project_id, $project_industry_id);
  81 +
  82 + $is_industry = true;
  83 +
  84 + $this->output('project_id:' . $project_id . ' , domain:' . $domain . ' | success');
  85 + }
  86 + }
  87 + }
  88 +
  89 + DB::disconnect('custom_mysql');
  90 + }
  91 +
  92 + if (!$is_industry) {
58 $project_industry = $this->getIndustryByAI($ai_command, $industry_names, $domain); 93 $project_industry = $this->getIndustryByAI($ai_command, $industry_names, $domain);
59 94
60 if ($project_industry) { 95 if ($project_industry) {
@@ -62,12 +97,17 @@ class CheckProjectIndustry extends Command @@ -62,12 +97,17 @@ class CheckProjectIndustry extends Command
62 97
63 $project_industry_id = ProjectIndustry::where('status', 1)->whereIn('industry_name', $project_industry_name)->pluck('id')->toArray(); 98 $project_industry_id = ProjectIndustry::where('status', 1)->whereIn('industry_name', $project_industry_name)->pluck('id')->toArray();
64 99
  100 + if ($project_industry_id) {
65 ProjectIndustryRelated::saveRelated($project_id, $project_industry_id); 101 ProjectIndustryRelated::saveRelated($project_id, $project_industry_id);
66 102
67 $this->output('project_id:' . $project_id . ' , domain:' . $domain . ' | success'); 103 $this->output('project_id:' . $project_id . ' , domain:' . $domain . ' | success');
68 } else { 104 } else {
69 $this->output('project_id:' . $project_id . ' , domain:' . $domain . ' | AI分析行业失败'); 105 $this->output('project_id:' . $project_id . ' , domain:' . $domain . ' | AI分析行业失败');
70 } 106 }
  107 + } else {
  108 + $this->output('project_id:' . $project_id . ' , domain:' . $domain . ' | AI分析行业失败');
  109 + }
  110 + }
71 } 111 }
72 } 112 }
73 113
@@ -90,6 +130,27 @@ class CheckProjectIndustry extends Command @@ -90,6 +130,27 @@ class CheckProjectIndustry extends Command
90 return Common::deal_str($text); 130 return Common::deal_str($text);
91 } 131 }
92 132
  133 + /**
  134 + * AI分析行业2.0
  135 + * @param $ai_command
  136 + * @param $industry_names
  137 + * @param $title
  138 + * @param $keyword
  139 + * @return string|string[]
  140 + * @author Akun
  141 + * @date 2025/08/01 10:10
  142 + */
  143 + public function getIndustryByAINew($ai_command, $industry_names, $title, $keyword)
  144 + {
  145 + $ai_command = str_replace('{industry}', $industry_names, $ai_command);
  146 + $ai_command = str_replace('{title}', $title, $ai_command);
  147 + $ai_command = str_replace('{keyword}', $keyword, $ai_command);
  148 +
  149 + $text = Gpt::instance()->openai_chat_qqs($ai_command);
  150 +
  151 + return Common::deal_str($text);
  152 + }
  153 +
93 public function output($msg) 154 public function output($msg)
94 { 155 {
95 echo date('Y-m-d H:i:s') . ' | ' . $msg . PHP_EOL; 156 echo date('Y-m-d H:i:s') . ' | ' . $msg . PHP_EOL;
@@ -9,19 +9,24 @@ @@ -9,19 +9,24 @@
9 9
10 namespace App\Console\Commands\LyhTest; 10 namespace App\Console\Commands\LyhTest;
11 11
  12 +use App\Helper\OaGlobalsoApi;
12 use App\Models\Ai\AiBlog; 13 use App\Models\Ai\AiBlog;
13 use App\Models\News\News; 14 use App\Models\News\News;
14 use App\Models\Product\Category; 15 use App\Models\Product\Category;
15 use App\Models\Project\AggregateKeywordAffix; 16 use App\Models\Project\AggregateKeywordAffix;
16 use App\Models\Project\AiBlogTask; 17 use App\Models\Project\AiBlogTask;
  18 +use App\Models\Project\DeployBuild;
17 use App\Models\Project\KeywordPrefix; 19 use App\Models\Project\KeywordPrefix;
18 use App\Models\Project\Project; 20 use App\Models\Project\Project;
19 use App\Models\Project\ProjectAiSetting; 21 use App\Models\Project\ProjectAiSetting;
20 use App\Models\Project\ProjectWhiteHatAffix; 22 use App\Models\Project\ProjectWhiteHatAffix;
21 use App\Models\Template\BTemplateMain; 23 use App\Models\Template\BTemplateMain;
22 use App\Models\Template\TemplateTypeMain; 24 use App\Models\Template\TemplateTypeMain;
  25 +use App\Models\WebSetting\WebSetting;
23 use App\Services\AiBlogService; 26 use App\Services\AiBlogService;
  27 +use App\Services\CosService;
24 use App\Services\Geo\GeoService; 28 use App\Services\Geo\GeoService;
  29 +use App\Services\MidJourneyService;
25 use App\Services\ProjectServer; 30 use App\Services\ProjectServer;
26 use Illuminate\Console\Command; 31 use Illuminate\Console\Command;
27 use Illuminate\Support\Facades\DB; 32 use Illuminate\Support\Facades\DB;
@@ -43,7 +48,13 @@ class lyhDemo extends Command @@ -43,7 +48,13 @@ class lyhDemo extends Command
43 protected $description = '更新路由'; 48 protected $description = '更新路由';
44 49
45 public function handle(){ 50 public function handle(){
46 - return $this->_actionRoute(); 51 +// $content = "{Introducing the cutting-edge product from Zhejiang Yuexiang Gas Technology Co., Ltd., the Bracket Gas Flowmeter. This innovative gas flowmeter is designed to provide accurate and reliable measurements for a wide range of industrial applications,Equipped with advanced technology and precision components, the Bracket Gas Flowmeter ensures exceptional performance and durability. It offers highly accurate gas flow readings, allowing users to closely monitor and control gas consumption in various processes. The meter's user-friendly interface displays real-time flow rates and totalized flow data, facilitating efficient and convenient data management,With its robust construction and corrosion-resistant materials, the Bracket Gas Flowmeter guarantees long-term reliability, even in harsh operating conditions. It is compatible with various gases such as natural gas, propane, and hydrogen, making it a versatile choice for multiple industries including oil and gas, chemical, and manufacturing,Furthermore, the Bracket Gas Flowmeter is engineered with ease of installation and maintenance in mind. Its compact and space-saving design allows for flexible mounting options, while its modular construction enables quick and hassle-free maintenance. Additionally, it meets international standards for accuracy and safety, ensuring compliance with industry regulations,Choose the Bracket Gas Flowmeter for precise and efficient gas flow measurement, backed by the expertise and commitment of Zhejiang Yuexiang Gas Technology Co., Ltd},{Shop Quality Brackets: Top China Products & Services for Your Needs},4K,高清 --no logo --ar 16:9";
  52 +// $midJourneyService = new MidJourneyService();
  53 +// $result = $midJourneyService->imagine($content);
  54 + $url = 'https://ecdn6-nc.globalso.com/upload/p/1/png/2025-08/688dcebc26a7a59911.png';
  55 + $cosService = new CosService();
  56 + $data = $cosService->cropAndUploadToCOS($url);
  57 + dd($data);
47 } 58 }
48 59
49 /** 60 /**
@@ -202,7 +202,9 @@ class SyncProject extends Command @@ -202,7 +202,9 @@ class SyncProject extends Command
202 * @param :version:7->v7版本(升级项目默认为v7) 202 * @param :version:7->v7版本(升级项目默认为v7)
203 */ 203 */
204 public function sync($param,$is_update = 0){ 204 public function sync($param,$is_update = 0){
205 - $version = (($is_update == 1) ? Project::VERSION_SEVEN : ($param['version'] ?? Project::VERSION_ZERO)); 205 + //TODO::4月12日 之前的项目都是v6
  206 +
  207 + $version = (($is_update == 1) ? Project::VERSION_SIX : (empty($param['version']) ? Project::VERSION_SEVEN : $param['version']));
206 $title = date('Ymd') . '-' . $param['company_name']; 208 $title = date('Ymd') . '-' . $param['company_name'];
207 $data = [ 209 $data = [
208 'project'=>[ 210 'project'=>[
@@ -250,6 +252,9 @@ class SyncProject extends Command @@ -250,6 +252,9 @@ class SyncProject extends Command
250 } 252 }
251 DB::beginTransaction(); 253 DB::beginTransaction();
252 try { 254 try {
  255 + if(isset($data['deploy_build']['plan']) && in_array($data['deploy_build']['plan'],[4,5,15,16,17])){
  256 + $data['project']['version'] = 0;
  257 + }
253 if(isset($data['deploy_build']['seo_plan']) && ($data['deploy_build']['seo_plan'] == 1)){ 258 if(isset($data['deploy_build']['seo_plan']) && ($data['deploy_build']['seo_plan'] == 1)){
254 $data['project']['project_type'] = 1;//白帽版本 259 $data['project']['project_type'] = 1;//白帽版本
255 } 260 }
@@ -54,7 +54,7 @@ class Temp extends Command @@ -54,7 +54,7 @@ class Temp extends Command
54 */ 54 */
55 public function specialImport() 55 public function specialImport()
56 { 56 {
57 - $file_url = 'https://ecdn6.globalso.com/upload/p/3531/file/2025-07/www-cninfo-com-cn_news_v1-1.csv'; 57 + $file_url = 'https://ecdn6.globalso.com/upload/p/3531/file/2025-07/www-cninfo-com-cn_news_v1-2.csv';
58 $domain = 'static.cninfo.com.cn'; 58 $domain = 'static.cninfo.com.cn';
59 $project_id = 3531; 59 $project_id = 3531;
60 $is_gbk = 0; 60 $is_gbk = 0;
@@ -138,11 +138,19 @@ class Temp extends Command @@ -138,11 +138,19 @@ class Temp extends Command
138 protected function importModule($project_id, $domain, $data) 138 protected function importModule($project_id, $domain, $data)
139 { 139 {
140 $module_id = 4; 140 $module_id = 4;
141 - $category_id = 3;  
142 $model = new CustomModuleContent(); 141 $model = new CustomModuleContent();
143 $module = $model->read(['module_id' => $module_id, 'name' => $data[0]]); 142 $module = $model->read(['module_id' => $module_id, 'name' => $data[0]]);
144 if (!$module) { 143 if (!$module) {
145 144
  145 + $category_id = '';
  146 + if ($data[2] ?? '') {
  147 + if ($data['2'] == '最新公告') {
  148 + $category_id = ',3,4,';
  149 + } elseif ($data['2'] == '定期报告') {
  150 + $category_id = ',3,5,';
  151 + }
  152 + }
  153 +
146 $content = ''; 154 $content = '';
147 if ($data[4] ?? '') { 155 if ($data[4] ?? '') {
148 //处理内容中的pdf文件 156 //处理内容中的pdf文件
@@ -199,7 +207,7 @@ class Temp extends Command @@ -199,7 +207,7 @@ class Temp extends Command
199 $id = $model->addReturnId( 207 $id = $model->addReturnId(
200 [ 208 [
201 'name' => $data[0], 209 'name' => $data[0],
202 - 'category_id' => ',' . $category_id . ',', 210 + 'category_id' => $category_id,
203 'module_id' => $module_id, 211 'module_id' => $module_id,
204 'content' => $content, 212 'content' => $content,
205 'seo_title' => $seo_title, 213 'seo_title' => $seo_title,
@@ -218,10 +226,21 @@ class Temp extends Command @@ -218,10 +226,21 @@ class Temp extends Command
218 $model->edit(['route' => $route], ['id' => $id]); 226 $model->edit(['route' => $route], ['id' => $id]);
219 227
220 return true; 228 return true;
  229 + } else {
  230 + $category_id = '';
  231 + if ($data[2] ?? '') {
  232 + if ($data['2'] == '最新公告') {
  233 + $category_id = ',3,4,';
  234 + } elseif ($data['2'] == '定期报告') {
  235 + $category_id = ',3,5,';
  236 + }
221 } 237 }
222 238
  239 + $model->edit(['category_id' => $category_id], ['id' => $module['id']]);
  240 +
223 return false; 241 return false;
224 } 242 }
  243 + }
225 244
226 //特殊字符转换 245 //特殊字符转换
227 protected function special2str($str) 246 protected function special2str($str)
@@ -215,7 +215,13 @@ class FetchTicketProjects extends Command @@ -215,7 +215,13 @@ class FetchTicketProjects extends Command
215 $pm_id = ManageHr::where('status', 1)->find($item->deploy_build->manager_mid)->manage_id ?? ManageHr::where('status', 1)->where('name', '李洁玉')->value('manage_id') ?? 0; 215 $pm_id = ManageHr::where('status', 1)->find($item->deploy_build->manager_mid)->manage_id ?? ManageHr::where('status', 1)->where('name', '李洁玉')->value('manage_id') ?? 0;
216 216
217 // 第一负责人 217 // 第一负责人
218 - if ($status == 1) 218 + /**
  219 + * 5.0升级6.0的项目,白帽SEO的项目 都划给售后
  220 + * 其他:建站中找项目经理,建站完成找杨长远,推广找售后服务经理
  221 + */
  222 + if ($item->is_upgrade || $item->project_type == 1)
  223 + $engineer_id = $assm_id; // V5升V6,白帽SEO,找售后服务经理
  224 + elseif ($status == 1)
219 $engineer_id = $pm_id; // 建站中找项目经理 225 $engineer_id = $pm_id; // 建站中找项目经理
220 elseif ($status == 2) 226 elseif ($status == 2)
221 $engineer_id = Manage::where('status', 1)->where('name', '杨长远')->value('id') ?? 0; // 建站完成找杨长远 227 $engineer_id = Manage::where('status', 1)->where('name', '杨长远')->value('id') ?? 0; // 建站完成找杨长远
@@ -247,7 +253,7 @@ class FetchTicketProjects extends Command @@ -247,7 +253,7 @@ class FetchTicketProjects extends Command
247 // $team_ids 去掉下标 253 // $team_ids 去掉下标
248 $team_ids_in = array_values($team_ids_in); 254 $team_ids_in = array_values($team_ids_in);
249 255
250 - $team_ids = ManageHr::whereIn('manage_id', $team_ids_in)->where('status', 1)->pluck('manage_id') 256 + $team_ids = ManageHr::whereIn('id', $team_ids_in)->where('status', 1)->pluck('manage_id')
251 ->unique() 257 ->unique()
252 ->filter(fn($v) => !is_null($v) && $v !== 0) 258 ->filter(fn($v) => !is_null($v) && $v !== 0)
253 ->values() 259 ->values()
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :AiVideoController.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/8/2 11:18
  8 + */
  9 +
  10 +namespace App\Http\Controllers\Api;
  11 +
  12 +use App\Enums\Common\Code;
  13 +use App\Models\Project\AiVideoAutoLog;
  14 +use App\Services\CosService;
  15 +use Illuminate\Support\Facades\Log;
  16 +use Illuminate\Support\Facades\Redis;
  17 +
  18 +class AiVideoController extends BaseController
  19 +{
  20 + /**
  21 + * @remark :回调方法
  22 + * @name :ImageCallBack
  23 + * @author :lyh
  24 + * @method :post
  25 + * @time :2025/8/2 11:19
  26 + */
  27 + public function ImageCallBack(){
  28 + @file_put_contents(storage_path('logs/lyh_error.log'), var_export($this->param, true) . PHP_EOL, FILE_APPEND);
  29 + Log::channel('ai_video')->info('数据详情:'.json_encode($this->param,true));
  30 + $count = Redis::decr('ai_video_image');
  31 + if ($count < 0) {
  32 + Redis::set('ai_video_image', 0);
  33 + }
  34 + $data = $this->param['attachments'] ?? [];
  35 + $aiVideoAutoLogModel = new AiVideoAutoLog();
  36 + if(empty($data) || empty($data['url'])){
  37 + $aiVideoAutoLogModel->edit(['status'=>9,'result'=>json_encode($this->param,true)],['trigger_id'=>$this->param['id']]);
  38 + }
  39 + //获取当前数据详情
  40 + $info = $aiVideoAutoLogModel->read(['trigger_id'=>$this->param['id']]);
  41 + if($info === false){
  42 + Log::channel('ai_video')->info('当前数据不存在或已被删除'.$this->param['id']);
  43 + }
  44 + //上传图片 返回cdn链接
  45 + $cosService = new CosService();
  46 + $imagePath = $cosService->uploadRemote($info['project_id'],'video',$data['url']);
  47 + try {
  48 + if($imagePath){
  49 + $cos = config('filesystems.disks.cos');
  50 + $url = $cos['cdn1'].'/'.$imagePath;
  51 + //裁剪图片为4张
  52 + $images = [];
  53 + $cosService = new CosService();
  54 + $data = $cosService->cropAndUploadToCOS($url);
  55 + if(!empty($data)){
  56 + foreach ($data as $item){
  57 + $images[] = ['url'=>$item,'alt'=>''];
  58 + }
  59 + }
  60 + $images = array_merge($images,$info['images']);
  61 + Log::channel('ai_video')->info('图片:'.json_encode($images));
  62 + $aiVideoAutoLogModel->edit(['images'=>$images,'result'=>json_encode($this->param,true),'status'=>1],['id'=>$info['id']]);
  63 + }
  64 + }catch (\Exception $e){
  65 + Log::channel('ai_video')->info('上传图片失败'.$e->getMessage());
  66 + }
  67 + $this->response('success');
  68 + }
  69 +}
@@ -845,6 +845,7 @@ class ProjectController extends BaseController @@ -845,6 +845,7 @@ class ProjectController extends BaseController
845 "project_manager_name" => $manageHr->getName($item['deploy_build']['manager_mid']), //项目经理 845 "project_manager_name" => $manageHr->getName($item['deploy_build']['manager_mid']), //项目经理
846 "after_sales_manager_name" => $manageHr->getName($item['deploy_optimize']['manager_mid']), //售后服务经理 846 "after_sales_manager_name" => $manageHr->getName($item['deploy_optimize']['manager_mid']), //售后服务经理
847 "leader_name" => $manageHr->getName($item['deploy_build']['leader_mid']), //组长 847 "leader_name" => $manageHr->getName($item['deploy_build']['leader_mid']), //组长
  848 + 'version' => $item['version']
848 ]; 849 ];
849 if ($item['type'] == Project::TYPE_TWO) { 850 if ($item['type'] == Project::TYPE_TWO) {
850 $param['is_compliance'] = RankData::where('project_id', $item['id'])->where('lang', '')->value('is_compliance') ?: 0; 851 $param['is_compliance'] = RankData::where('project_id', $item['id'])->where('lang', '')->value('is_compliance') ?: 0;
@@ -1283,4 +1284,5 @@ class ProjectController extends BaseController @@ -1283,4 +1284,5 @@ class ProjectController extends BaseController
1283 NoticeLog::createLog(NoticeLog::TYPE_UPDATE_PROJECT_TDK, ['project_id' => $this->param['project_id'],'url'=>$this->param['url']]); 1284 NoticeLog::createLog(NoticeLog::TYPE_UPDATE_PROJECT_TDK, ['project_id' => $this->param['project_id'],'url'=>$this->param['url']]);
1284 $this->response('success',Code::SUCCESS,['url'=>$this->param['url']]); 1285 $this->response('success',Code::SUCCESS,['url'=>$this->param['url']]);
1285 } 1286 }
  1287 +
1286 } 1288 }
@@ -114,17 +114,24 @@ class RenewProjectController extends BaseController @@ -114,17 +114,24 @@ class RenewProjectController extends BaseController
114 * @time :2023/8/18 14:33 114 * @time :2023/8/18 14:33
115 */ 115 */
116 public function notHaveRenewItems(Project $project){ 116 public function notHaveRenewItems(Project $project){
117 - $this->map['extend_type'] = $project::TYPE_FIVE;//未续费网站 if(!empty($param['search']) && !empty($param['search_type'])){ 117 + $this->map['extend_type'] = $project::TYPE_FIVE;//未续费网站
118 if(!empty($this->map['title'])){ 118 if(!empty($this->map['title'])){
119 $this->map['title'] = ['like', '%'.$this->map['title'].'%']; 119 $this->map['title'] = ['like', '%'.$this->map['title'].'%'];
120 } 120 }
  121 + $domainModel = new DomainInfo();
  122 + if(!empty($this->map['domain'])){
  123 + $parsedUrl = parse_url($this->map['domain']);
  124 + $search_domain = $parsedUrl['host'] ?? $this->map['domain'];
  125 + $projectIds = $domainModel->selectField(['domain'=>['like','%'.$search_domain.'%']],'project_id');
  126 + $this->map['id'] = ['in',$projectIds];
  127 + unset($this->map['domain']);
  128 + }
121 $lists = $project->formatQuery($this->map)->with('payment')->with('deploy_build') 129 $lists = $project->formatQuery($this->map)->with('payment')->with('deploy_build')
122 ->with('deploy_optimize')->with('online_check') 130 ->with('deploy_optimize')->with('online_check')
123 ->with('project_after')->paginate($this->row, ['*'], 'page', $this->page); 131 ->with('project_after')->paginate($this->row, ['*'], 'page', $this->page);
124 if(!empty($lists)){ 132 if(!empty($lists)){
125 $lists = $lists->toArray(); 133 $lists = $lists->toArray();
126 $manageModel = new ManageHr(); 134 $manageModel = new ManageHr();
127 - $domainModel = new DomainInfo();  
128 foreach ($lists['list'] as $k=>$item){ 135 foreach ($lists['list'] as $k=>$item){
129 $item = $this->handleParam($item,$manageModel,$domainModel); 136 $item = $this->handleParam($item,$manageModel,$domainModel);
130 $lists['list'][$k] = $item; 137 $lists['list'][$k] = $item;
@@ -28,6 +28,7 @@ class AsideTicketController extends BaseController @@ -28,6 +28,7 @@ class AsideTicketController extends BaseController
28 $lists = Tickets::with([ 28 $lists = Tickets::with([
29 'logs.engineer', 29 'logs.engineer',
30 'project.pm', 30 'project.pm',
  31 + 'project.projectV6',
31 ]) 32 ])
32 ->when(!empty($validated['engineer_id']), function ($query) use ($validated) { 33 ->when(!empty($validated['engineer_id']), function ($query) use ($validated) {
33 // 查 gl_tickets 表 submit_user_id 或 gl_ticket_logs 表 engineer_id 34 // 查 gl_tickets 表 submit_user_id 或 gl_ticket_logs 表 engineer_id
@@ -30,7 +30,7 @@ class AuthorityScoreController extends BaseController @@ -30,7 +30,7 @@ class AuthorityScoreController extends BaseController
30 } 30 }
31 31
32 /** 32 /**
33 - * @remark :豹猫数据统计表详情 33 + * @remark :白帽数据统计表详情
34 * @name :whiteHatReportInfo 34 * @name :whiteHatReportInfo
35 * @author :lyh 35 * @author :lyh
36 * @method :post 36 * @method :post
@@ -61,11 +61,11 @@ class ComController extends BaseController @@ -61,11 +61,11 @@ class ComController extends BaseController
61 $data = []; 61 $data = [];
62 $is_ai_blog = $this->getIsAiBlog(); 62 $is_ai_blog = $this->getIsAiBlog();
63 if($is_ai_blog != 1){ 63 if($is_ai_blog != 1){
64 - $data[] = 57; 64 + $data[] = 6;
65 } 65 }
66 $is_ai_video = $this->getIsAiVideo(); 66 $is_ai_video = $this->getIsAiVideo();
67 if($is_ai_video != 1){ 67 if($is_ai_video != 1){
68 - $data[] = 74; 68 + $data[] = 27;
69 } 69 }
70 if($this->user['login_source'] == User::LOGIN_PASSWORD_SOURCE){ 70 if($this->user['login_source'] == User::LOGIN_PASSWORD_SOURCE){
71 $data[] = 19; 71 $data[] = 19;
@@ -37,7 +37,7 @@ class GoogleSearchController extends BaseController @@ -37,7 +37,7 @@ class GoogleSearchController extends BaseController
37 //查询详情数据 37 //查询详情数据
38 $searchDetailModel = new GoogleSearchDetail(); 38 $searchDetailModel = new GoogleSearchDetail();
39 $this->map['project_id'] = $this->user['project_id']; 39 $this->map['project_id'] = $this->user['project_id'];
40 - $data = $searchDetailModel->lists($this->map,$this->page,$this->row,'impressions',['keys','click_rate','position','impressions_rate']); 40 + $data = $searchDetailModel->lists($this->map,$this->page,$this->row,'impressions');
41 if(!empty($data)){ 41 if(!empty($data)){
42 if($this->param['type'] == 'country'){ 42 if($this->param['type'] == 'country'){
43 $codeCountryModel = new GoogleCodeCountry(); 43 $codeCountryModel = new GoogleCodeCountry();
@@ -287,7 +287,6 @@ class InquiryController extends BaseController @@ -287,7 +287,6 @@ class InquiryController extends BaseController
287 throw new \Exception('文件生成失败,请重试'); 287 throw new \Exception('文件生成失败,请重试');
288 } 288 }
289 $fileurl = Storage::disk('runtime')->url($file); 289 $fileurl = Storage::disk('runtime')->url($file);
290 -// return Storage::disk('runtime')->download($file); //直接下载  
291 $this->response('success',Code::SUCCESS,['url' => $fileurl]); 290 $this->response('success',Code::SUCCESS,['url' => $fileurl]);
292 } 291 }
293 292
@@ -40,7 +40,7 @@ class CategoryController extends BaseController @@ -40,7 +40,7 @@ class CategoryController extends BaseController
40 public function index() 40 public function index()
41 { 41 {
42 $this->map = $this->searchParam(); 42 $this->map = $this->searchParam();
43 - $filed = ['id', 'project_id', 'pid', 'title', 'image', 'route', 'status','created_at','sort']; 43 + $filed = ['id', 'project_id', 'pid', 'title', 'image', 'route', 'status','created_at','sort','is_type'];
44 $this->map['deleted_at'] = null; 44 $this->map['deleted_at'] = null;
45 if($this->user['project_id'] == 3283){//分类太多加载失败 45 if($this->user['project_id'] == 3283){//分类太多加载失败
46 $list = $this->get3283Lists($filed); 46 $list = $this->get3283Lists($filed);
@@ -260,4 +260,23 @@ class CategoryController extends BaseController @@ -260,4 +260,23 @@ class CategoryController extends BaseController
260 $this->logic->copyCategoryInfo(); 260 $this->logic->copyCategoryInfo();
261 $this->response('success'); 261 $this->response('success');
262 } 262 }
  263 +
  264 + /**
  265 + * @remark :更改状态
  266 + * @name :editIsType
  267 + * @author :lyh
  268 + * @method :post
  269 + * @time :2025/7/30 14:23
  270 + */
  271 + public function editIsType(){
  272 + $this->request->validate([
  273 + 'id'=>['required'],
  274 + 'is_type'=>['required'],
  275 + ],[
  276 + 'id.required' => 'ID不能为空',
  277 + 'is_type.required' => 'is_type不能为空',
  278 + ]);
  279 + $data = $this->logic->editIsType($this->param['is_type'],$this->param['id']);
  280 + $this->response('success',Code::SUCCESS,$data);
  281 + }
263 } 282 }
@@ -80,6 +80,8 @@ class KeywordController extends BaseController @@ -80,6 +80,8 @@ class KeywordController extends BaseController
80 public function searchParam($map){ 80 public function searchParam($map){
81 if(!empty($map['title'])){ 81 if(!empty($map['title'])){
82 $map['title'] = ['like','%'.$map['title'].'%']; 82 $map['title'] = ['like','%'.$map['title'].'%'];
  83 + }else{
  84 + unset($map['title']);
83 } 85 }
84 if(!empty($map['keyword_title'])){ 86 if(!empty($map['keyword_title'])){
85 $map['keyword_title'] = ['like','%'.$map['keyword_title'].'%']; 87 $map['keyword_title'] = ['like','%'.$map['keyword_title'].'%'];
@@ -330,7 +330,7 @@ class TranslateController extends BaseController @@ -330,7 +330,7 @@ class TranslateController extends BaseController
330 $this->pageSixList($data,$count,$v,1,15); 330 $this->pageSixList($data,$count,$v,1,15);
331 } 331 }
332 $blogInfo = $routeMapModel->read(['route'=>'blog']); 332 $blogInfo = $routeMapModel->read(['route'=>'blog']);
333 - if($blogInfo === false){ 333 + if($blogInfo === false && ($this->user['is_show_blog'] == 1)){
334 $blogModel = new Blog(); 334 $blogModel = new Blog();
335 $count = $blogModel->formatQuery(['status'=>1])->count(); 335 $count = $blogModel->formatQuery(['status'=>1])->count();
336 $v['route'] = 'blog'; 336 $v['route'] = 'blog';
@@ -162,9 +162,6 @@ class DomainInfoLogic extends BaseLogic @@ -162,9 +162,6 @@ class DomainInfoLogic extends BaseLogic
162 if($project_info['type'] == Project::TYPE_CLOSE){ 162 if($project_info['type'] == Project::TYPE_CLOSE){
163 $this->fail('项目已关闭,无法执行此操作'); 163 $this->fail('项目已关闭,无法执行此操作');
164 } 164 }
165 - if($project_info['project_type'] == Project::PROJECT_TYPE_SEO){  
166 - $this->fail('白帽SEO项目,无法执行此操作');  
167 - }  
168 $serverIpModel = new ServersIp(); 165 $serverIpModel = new ServersIp();
169 $serversIpInfo = $serverIpModel->read(['id'=>$project_info['serve_id']],['servers_id','ip','domain']); 166 $serversIpInfo = $serverIpModel->read(['id'=>$project_info['serve_id']],['servers_id','ip','domain']);
170 if($serversIpInfo === false){ 167 if($serversIpInfo === false){
@@ -230,7 +227,13 @@ class DomainInfoLogic extends BaseLogic @@ -230,7 +227,13 @@ class DomainInfoLogic extends BaseLogic
230 } 227 }
231 } 228 }
232 229
233 - if(isset($this->param['amp_status']) && $this->param['amp_status'] == 1){ 230 + //是否开通m站
  231 + $amp_status = $this->param['amp_status'] ?? 0;
  232 + if($project_info['project_type'] == Project::PROJECT_TYPE_SEO){
  233 + $amp_status = 0;
  234 + }
  235 +
  236 + if($amp_status){
234 //需要开通amp站点,判断m域名是否已经解析 237 //需要开通amp站点,判断m域名是否已经解析
235 $host_array_amp = $host_array; 238 $host_array_amp = $host_array;
236 if (count($host_array_amp) <= 2) { 239 if (count($host_array_amp) <= 2) {
@@ -264,9 +267,21 @@ class DomainInfoLogic extends BaseLogic @@ -264,9 +267,21 @@ class DomainInfoLogic extends BaseLogic
264 $unique_extend = []; 267 $unique_extend = [];
265 if(isset($this->param['extend_config']) && $this->param['extend_config']){ 268 if(isset($this->param['extend_config']) && $this->param['extend_config']){
266 foreach ($this->param['extend_config'] as $k=>$v){ 269 foreach ($this->param['extend_config'] as $k=>$v){
267 - if($v['origin'] && (!in_array($v['origin'],$unique_extend))){  
268 - $unique_extend[] = $v['origin'];  
269 - $extend_config[] = $v; 270 + if(!$v['origin']){
  271 + continue;
  272 + }
  273 + if(!$v['target']){
  274 + continue;
  275 + }
  276 +
  277 + $origin_array = parse_url($v['origin']);
  278 + $origin_url = isset($origin_array['path']) && $origin_array['path'] ? $origin_array['path'] : $origin_array['host'];
  279 +
  280 + $target_array = parse_url($v['target']);
  281 + $target_url = isset($target_array['path']) && $target_array['path'] ? $target_array['path'] : $target_array['host'];
  282 + if((!in_array($origin_url,$unique_extend))){
  283 + $unique_extend[] = $origin_url;
  284 + $extend_config[] = ['origin'=>$origin_url,'target'=>$target_url];
270 } 285 }
271 } 286 }
272 } 287 }
@@ -275,9 +290,21 @@ class DomainInfoLogic extends BaseLogic @@ -275,9 +290,21 @@ class DomainInfoLogic extends BaseLogic
275 $amp_unique_extend = []; 290 $amp_unique_extend = [];
276 if(isset($this->param['amp_extend_config']) && $this->param['amp_extend_config']){ 291 if(isset($this->param['amp_extend_config']) && $this->param['amp_extend_config']){
277 foreach ($this->param['amp_extend_config'] as $ka=>$va){ 292 foreach ($this->param['amp_extend_config'] as $ka=>$va){
278 - if($va['origin'] && (!in_array($va['origin'],$amp_unique_extend))){  
279 - $amp_unique_extend[] = $va['origin'];  
280 - $amp_extend_config[] = $va; 293 + if(!$va['origin']){
  294 + continue;
  295 + }
  296 + if(!$va['target']){
  297 + continue;
  298 + }
  299 +
  300 + $amp_origin_array = parse_url($va['origin']);
  301 + $amp_origin_url = isset($amp_origin_array['path']) && $amp_origin_array['path'] ? $amp_origin_array['path'] : $amp_origin_array['host'];
  302 +
  303 + $amp_target_array = parse_url($va['target']);
  304 + $amp_target_url = isset($amp_target_array['path']) && $amp_target_array['path'] ? $amp_target_array['path'] : $amp_target_array['host'];
  305 + if((!in_array($amp_origin_url,$amp_unique_extend))){
  306 + $amp_unique_extend[] = $amp_origin_url;
  307 + $amp_extend_config[] = ['origin'=>$amp_origin_url,'target'=>$amp_target_url];
281 } 308 }
282 } 309 }
283 } 310 }
@@ -298,7 +325,7 @@ class DomainInfoLogic extends BaseLogic @@ -298,7 +325,7 @@ class DomainInfoLogic extends BaseLogic
298 'private_key' => $this->param['key'] ?? '', 325 'private_key' => $this->param['key'] ?? '',
299 'private_cert' => $this->param['cert'] ?? '', 326 'private_cert' => $this->param['cert'] ?? '',
300 'is_https' => $is_https, 327 'is_https' => $is_https,
301 - 'amp_status' => $this->param['amp_status'] ?? 0, 328 + 'amp_status' => $amp_status,
302 'amp_type' => $this->param['amp_type'] ?? 0, 329 'amp_type' => $this->param['amp_type'] ?? 0,
303 'amp_extend_config'=>json_encode($amp_extend_config), 330 'amp_extend_config'=>json_encode($amp_extend_config),
304 'amp_private_key' => $this->param['amp_key'] ?? '', 331 'amp_private_key' => $this->param['amp_key'] ?? '',
@@ -310,21 +337,27 @@ class DomainInfoLogic extends BaseLogic @@ -310,21 +337,27 @@ class DomainInfoLogic extends BaseLogic
310 $this->model->edit($data,['id'=>$this->param['id']]); 337 $this->model->edit($data,['id'=>$this->param['id']]);
311 338
312 //新增建站任务 339 //新增建站任务
  340 + if ($project_info['project_type'] == Project::PROJECT_TYPE_SEO) {
  341 + $type = DomainCreateTask::TYPE_BLOG;
  342 + } else {
  343 + $type = DomainCreateTask::TYPE_MAIN;
  344 + }
313 $task_model = new DomainCreateTask(); 345 $task_model = new DomainCreateTask();
314 - $task_info = $task_model->read(['type'=>DomainCreateTask::TYPE_MAIN,'domain_id'=>$this->param['id'],'status'=>['<',DomainCreateTask::STATUS_SUC]]); 346 + $task_info = $task_model->read(['type'=>$type,'domain_id'=>$this->param['id'],'status'=>['<',DomainCreateTask::STATUS_SUC]]);
315 if(!$task_info){ 347 if(!$task_info){
316 $task_model->add([ 348 $task_model->add([
317 'server_id' => $serversIpInfo['servers_id'], 349 'server_id' => $serversIpInfo['servers_id'],
318 'project_id' => $info['project_id'], 350 'project_id' => $info['project_id'],
319 'domain_id' => $this->param['id'], 351 'domain_id' => $this->param['id'],
320 - 'type' => DomainCreateTask::TYPE_MAIN 352 + 'type' => $type
321 ]); 353 ]);
322 }else{ 354 }else{
323 if($task_info['status'] == DomainCreateTask::STATUS_UN){ 355 if($task_info['status'] == DomainCreateTask::STATUS_UN){
324 - $task_model->edit(['sort'=>0],['id'=>$task_info['id']]); 356 + //重复提交,提高优先级
  357 + $task_model->edit(['sort'=>$task_info['sort'] + 1],['id'=>$task_info['id']]);
325 } 358 }
326 } 359 }
327 - if($data['amp_status']){ 360 + if($amp_status){
328 $task_amp_info = $task_model->read(['type'=>DomainCreateTask::TYPE_AMP,'domain_id'=>$this->param['id'],'status'=>['<',DomainCreateTask::STATUS_SUC]]); 361 $task_amp_info = $task_model->read(['type'=>DomainCreateTask::TYPE_AMP,'domain_id'=>$this->param['id'],'status'=>['<',DomainCreateTask::STATUS_SUC]]);
329 if(!$task_amp_info){ 362 if(!$task_amp_info){
330 $task_model->add([ 363 $task_model->add([
@@ -738,6 +738,24 @@ class ProjectLogic extends BaseLogic @@ -738,6 +738,24 @@ class ProjectLogic extends BaseLogic
738 //改为异步 738 //改为异步
739 NoticeLog::createLog(NoticeLog::TYPE_INIT_PROJECT, ['project_id' => $param['id']]); 739 NoticeLog::createLog(NoticeLog::TYPE_INIT_PROJECT, ['project_id' => $param['id']]);
740 } 740 }
  741 + //推广续费网站单独处理
  742 + if($param['type'] == Project::TYPE_FOUR){
  743 + // 提取非 null 的 expire_at 字段
  744 + $validDates = array_filter(
  745 + array_column($param['payment']['renewal_record'] ?? [], 'expire_at'),
  746 + fn($date) => !is_null($date)
  747 + );
  748 + // 获取最大日期(如果有)
  749 + $maxExpireAt = $validDates ? max($validDates) : null;
  750 + if(!empty($maxExpireAt)){
  751 + $diff = (strtotime($maxExpireAt) - strtotime($param['uptime'] ?? date('Y-m-d'))) / (60 * 60 * 24);
  752 + if($param['project_type'] == Project::PROJECT_TYPE_SEO){
  753 + $param['deploy_build']['seo_service_duration'] = $diff;
  754 + }else{
  755 + $param['deploy_build']['service_duration'] = $diff;
  756 + }
  757 + }
  758 + }
741 return $this->success($param); 759 return $this->success($param);
742 } 760 }
743 761
@@ -944,7 +962,13 @@ class ProjectLogic extends BaseLogic @@ -944,7 +962,13 @@ class ProjectLogic extends BaseLogic
944 if($param['zone_id'] != 0){ 962 if($param['zone_id'] != 0){
945 $map['zone_id'] = $param['zone_id']; 963 $map['zone_id'] = $param['zone_id'];
946 } 964 }
947 - return Channel::where($map)->pluck('alias', 'id')->toArray(); 965 + $channelData = [];
  966 + $chanelModel = new Channel();
  967 + $channelList = $chanelModel->list($map,'id',['id','title','alias']);
  968 + foreach ($channelList as $item){
  969 + $channelData[$item['id']] = $item['title'].'-'.$item['alias'];
  970 + }
  971 + return $channelData;
948 case Project::TYPE_THREE: 972 case Project::TYPE_THREE:
949 return User::where('channel_id', $param['channel_id']??0)->pluck('name', 'id')->toArray(); 973 return User::where('channel_id', $param['channel_id']??0)->pluck('name', 'id')->toArray();
950 } 974 }
@@ -63,11 +63,12 @@ class AiVideoLogic extends BaseLogic @@ -63,11 +63,12 @@ class AiVideoLogic extends BaseLogic
63 * @detail :status=1/待执行 63 * @detail :status=1/待执行
64 */ 64 */
65 public function sendTask(){ 65 public function sendTask(){
  66 + $aiVideoTaskModel = new AiVideoTask();
66 $aiVideoService = new AiVideoService($this->user['project_id']); 67 $aiVideoService = new AiVideoService($this->user['project_id']);
67 - $result = $aiVideoService->createTask($this->param['title'],$this->param['description'],$this->param['images'],$this->param['anchor'] ?? []); 68 + $storage = $aiVideoTaskModel->videoSetting()[$this->user['video_setting'] ?? 1];
  69 + $result = $aiVideoService->createTask($this->param['title'],$this->param['description'],$this->param['images'],$this->param['anchor'] ?? [],$storage);
68 if($result['status'] == 200){ 70 if($result['status'] == 200){
69 - $aiVideoTaskModel = new AiVideoTask();  
70 - $aiVideoTaskModel->addReturnId(['task_id'=>$result['data']['task_id'],'project_id'=>$this->user['project_id']]); 71 + $aiVideoTaskModel->addReturnId(['task_id'=>$result['data']['task_id'],'project_id'=>$this->user['project_id'],'storage'=>$storage]);
71 $id = $this->model->addReturnId(['title'=>$this->param['title'],'task_id'=>$result['data']['task_id'],'description'=>$this->param['description'],'project_id'=>$this->user['project_id'],'images'=>json_encode($this->param['images'],true),'anchor'=>json_encode($this->param['anchor'] ?? [],true)]); 72 $id = $this->model->addReturnId(['title'=>$this->param['title'],'task_id'=>$result['data']['task_id'],'description'=>$this->param['description'],'project_id'=>$this->user['project_id'],'images'=>json_encode($this->param['images'],true),'anchor'=>json_encode($this->param['anchor'] ?? [],true)]);
72 return $this->success(['id'=>$id]); 73 return $this->success(['id'=>$id]);
73 } 74 }
@@ -50,8 +50,8 @@ class AuthorityScoreLogic extends BaseLogic @@ -50,8 +50,8 @@ class AuthorityScoreLogic extends BaseLogic
50 $endTime = date('Y-m-t 23:59:59', strtotime($this->param['date'])); 50 $endTime = date('Y-m-t 23:59:59', strtotime($this->param['date']));
51 //当月ai_blog发布数量及链接 51 //当月ai_blog发布数量及链接
52 $aiBlogModel = new AiBlog(); 52 $aiBlogModel = new AiBlog();
53 - $data['ai_blog']['count'] = $aiBlogModel->counts(['created_at'=>['between',[$startTime,$endTime]]]);  
54 - $aiRouteArr = $aiBlogModel->selectField(['created_at'=>['between',[$startTime,$endTime]]],'route'); 53 + $data['ai_blog']['count'] = $aiBlogModel->counts(['created_at'=>['between',[$startTime,$endTime]],'route'=>['!=',null]]);
  54 + $aiRouteArr = $aiBlogModel->selectField(['created_at'=>['between',[$startTime,$endTime]],'route'=>['!=',null]],'route');
55 if(!empty($aiRouteArr)){ 55 if(!empty($aiRouteArr)){
56 foreach ($aiRouteArr as $k => $item){ 56 foreach ($aiRouteArr as $k => $item){
57 $aiRouteArr[$k] = $this->user['domain'] . 'blog/' . $item; 57 $aiRouteArr[$k] = $this->user['domain'] . 'blog/' . $item;
@@ -60,8 +60,8 @@ class AuthorityScoreLogic extends BaseLogic @@ -60,8 +60,8 @@ class AuthorityScoreLogic extends BaseLogic
60 $data['ai_blog']['link'] = $aiRouteArr; 60 $data['ai_blog']['link'] = $aiRouteArr;
61 //当月ai_video发布数量及链接 61 //当月ai_video发布数量及链接
62 $aiVideoModel = new AiVideo(); 62 $aiVideoModel = new AiVideo();
63 - $data['ai_video']['count'] =$aiVideoModel->counts(['created_at'=>['between',[$startTime,$endTime]]]);  
64 - $aiVideoRouteArr = $aiVideoModel->selectField(['created_at'=>['between',[$startTime,$endTime]]],'route'); 63 + $data['ai_video']['count'] =$aiVideoModel->counts(['created_at'=>['between',[$startTime,$endTime]],'route'=>['!=',null]]);
  64 + $aiVideoRouteArr = $aiVideoModel->selectField(['created_at'=>['between',[$startTime,$endTime]],'route'=>['!=',null]],'route');
65 if(!empty($aiVideoRouteArr)){ 65 if(!empty($aiVideoRouteArr)){
66 foreach ($aiVideoRouteArr as $kVideo => $itemVideo){ 66 foreach ($aiVideoRouteArr as $kVideo => $itemVideo){
67 $aiVideoRouteArr[$kVideo] = $this->user['domain'] . 'video/' . $itemVideo; 67 $aiVideoRouteArr[$kVideo] = $this->user['domain'] . 'video/' . $itemVideo;
@@ -116,7 +116,7 @@ class BTemplateLogic extends BaseLogic @@ -116,7 +116,7 @@ class BTemplateLogic extends BaseLogic
116 $bTemplateComModel = new BTemplateCom(); 116 $bTemplateComModel = new BTemplateCom();
117 $otherInfo = $bTemplateComModel->read(['source'=>$headComInfo['source'],'common_type'=>BTemplate::COMMON_OTHER,'is_list'=>$headComInfo['is_list'],'is_custom'=>$headComInfo['is_custom'],'template_id'=>$template_id]); 117 $otherInfo = $bTemplateComModel->read(['source'=>$headComInfo['source'],'common_type'=>BTemplate::COMMON_OTHER,'is_list'=>$headComInfo['is_list'],'is_custom'=>$headComInfo['is_custom'],'template_id'=>$template_id]);
118 if($otherInfo === false){ 118 if($otherInfo === false){
119 - $this->fail('获取失败,请联系管理员222'); 119 + $otherInfo = $bTemplateComModel->read(['source'=>99,'common_type'=>BTemplate::COMMON_OTHER,'is_list'=>$headComInfo['is_list'],'is_custom'=>$headComInfo['is_custom'],'template_id'=>$template_id]);
120 } 120 }
121 $footerComInfo = $this->getFooterComHtml($condition,$source,$is_list,$is_custom,$template_id); 121 $footerComInfo = $this->getFooterComHtml($condition,$source,$is_list,$is_custom,$template_id);
122 $data = ['head_html'=>$headComInfo['html'] ?? '', 'head_style'=>$headComInfo['html_style'] ?? '', 'other'=>$otherInfo['html'] ?? '', 122 $data = ['head_html'=>$headComInfo['html'] ?? '', 'head_style'=>$headComInfo['html_style'] ?? '', 'other'=>$otherInfo['html'] ?? '',
@@ -151,6 +151,7 @@ class CategoryLogic extends BaseLogic @@ -151,6 +151,7 @@ class CategoryLogic extends BaseLogic
151 }else{ 151 }else{
152 $param['describe_image'] = json_encode([]); 152 $param['describe_image'] = json_encode([]);
153 } 153 }
  154 + $this->param['image'] = $this->param['image'] ?? '';
154 if(isset($param['cate_tak']) && !empty($param['cate_tak'])){ 155 if(isset($param['cate_tak']) && !empty($param['cate_tak'])){
155 $param['cate_tak'] = json_encode($param['cate_tak']); 156 $param['cate_tak'] = json_encode($param['cate_tak']);
156 }else{ 157 }else{
@@ -361,4 +362,16 @@ class CategoryLogic extends BaseLogic @@ -361,4 +362,16 @@ class CategoryLogic extends BaseLogic
361 ]; 362 ];
362 return $this->success($param); 363 return $this->success($param);
363 } 364 }
  365 +
  366 + /**
  367 + * @remark :更改分类为聚合页模式
  368 + * @name :editIsType
  369 + * @author :lyh
  370 + * @method :post
  371 + * @time :2025/7/30 14:21
  372 + */
  373 + public function editIsType($is_type = 0,$id){
  374 + $data = $this->model->edit(['is_type'=>$is_type],['id'=>$id]);
  375 + return $this->success($data);
  376 + }
364 } 377 }
@@ -61,7 +61,11 @@ class TranslateLogic extends BaseLogic @@ -61,7 +61,11 @@ class TranslateLogic extends BaseLogic
61 $param['url'] = $this->param['url']; 61 $param['url'] = $this->param['url'];
62 $info = $this->model->with('translate_data')->where($param)->first(); 62 $info = $this->model->with('translate_data')->where($param)->first();
63 //获取当前URl的所有文本内容 63 //获取当前URl的所有文本内容
  64 + try {
64 $text_array = $this->getUrlRead($url); 65 $text_array = $this->getUrlRead($url);
  66 + }catch (\Exception $e){
  67 + $text_array = [];
  68 + }
65 // 原始校对程序 69 // 原始校对程序
66 $old_key = [];//key值组成数据 70 $old_key = [];//key值组成数据
67 $data_read = json_decode($info && $info['translate_data'] ? $info['translate_data']['data'] : '',JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); 71 $data_read = json_decode($info && $info['translate_data'] ? $info['translate_data']['data'] : '',JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
@@ -68,7 +68,7 @@ class WebSettingTextLogic extends BaseLogic @@ -68,7 +68,7 @@ class WebSettingTextLogic extends BaseLogic
68 DB::beginTransaction(); 68 DB::beginTransaction();
69 try { 69 try {
70 $data = [ 70 $data = [
71 - 'anchor_setting'=>$this->param['anchor_setting'] ?? [], 71 + 'anchor_setting'=>$this->param['anchor_setting'] ?? ["5","3","4"],
72 'anchor_is_enable'=>$this->param['anchor_is_enable'], 72 'anchor_is_enable'=>$this->param['anchor_is_enable'],
73 'anchor_num'=>$this->param['anchor_num'] ?? 0, 73 'anchor_num'=>$this->param['anchor_num'] ?? 0,
74 'anchor_page_num'=>$this->param['anchor_page_num'] ?? 0, 74 'anchor_page_num'=>$this->param['anchor_page_num'] ?? 0,
@@ -277,6 +277,7 @@ class UserLoginLogic @@ -277,6 +277,7 @@ class UserLoginLogic
277 $info['main_lang_id'] = $project['main_lang_id']; 277 $info['main_lang_id'] = $project['main_lang_id'];
278 $info['is_ai_blog'] = $project['is_ai_blog'] ?? 0; 278 $info['is_ai_blog'] = $project['is_ai_blog'] ?? 0;
279 $info['is_ai_video'] = $project['is_ai_video'] ?? 0; 279 $info['is_ai_video'] = $project['is_ai_video'] ?? 0;
  280 + $info['video_setting'] = $project['deploy_optimize']['video_setting'] ?? 1;
280 $info['image_max'] = $project['image_max']; 281 $info['image_max'] = $project['image_max'];
281 $info['is_del_inquiry'] = $project['is_del_inquiry'] ?? 0; 282 $info['is_del_inquiry'] = $project['is_del_inquiry'] ?? 0;
282 $info['uptime_type'] = $this->getHistory($project); 283 $info['uptime_type'] = $this->getHistory($project);
@@ -16,6 +16,16 @@ class AsideTicketStoreRequest extends FormRequest @@ -16,6 +16,16 @@ class AsideTicketStoreRequest extends FormRequest
16 return true; 16 return true;
17 } 17 }
18 18
  19 + protected function prepareForValidation()
  20 + {
  21 + if ($this->has('close_wechat')) {
  22 + $this->merge([
  23 + // 将 "true", "1", "on", "yes" 等转换为 true,其他转换为 false
  24 + 'close_wechat' => filter_var($this->close_wechat, FILTER_VALIDATE_BOOLEAN),
  25 + ]);
  26 + }
  27 + }
  28 +
19 /** 29 /**
20 * Get the validation rules that apply to the request. 30 * Get the validation rules that apply to the request.
21 * 31 *
@@ -24,7 +24,6 @@ class ProjectIndustryRelated extends Base @@ -24,7 +24,6 @@ class ProjectIndustryRelated extends Base
24 } 24 }
25 //先删除 25 //先删除
26 self::where('project_id', $project_id)->delete(); 26 self::where('project_id', $project_id)->delete();
27 -  
28 //批量保存 27 //批量保存
29 if (!empty($industry_ids)) { 28 if (!empty($industry_ids)) {
30 $data = []; 29 $data = [];
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :AiVideoAutoLog.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/8/2 9:59
  8 + */
  9 +
  10 +namespace App\Models\Project;
  11 +
  12 +use App\Helper\Arr;
  13 +use App\Models\Base;
  14 +
  15 +class AiVideoAutoLog extends Base
  16 +{
  17 + protected $table = 'gl_ai_video_auto_log';
  18 +
  19 + /**
  20 + * @remark :获取器图片集合
  21 + * @name :getImagesAttribute
  22 + * @author :lyh
  23 + * @method :post
  24 + * @time :2025/8/2 10:45
  25 + */
  26 + public function getImagesAttribute($value){
  27 + if(!empty($value)){
  28 + $value = Arr::s2a($value);
  29 + }
  30 + return $value;
  31 + }
  32 +}
@@ -20,4 +20,18 @@ class AiVideoTask extends Base @@ -20,4 +20,18 @@ class AiVideoTask extends Base
20 */ 20 */
21 const STATUS_RUNNING = 1; 21 const STATUS_RUNNING = 1;
22 const STATUS_FINISH = 2; 22 const STATUS_FINISH = 2;
  23 +
  24 + /**
  25 + * @remark :视频设置
  26 + * @name :videoSetting
  27 + * @author :lyh
  28 + * @method :post
  29 + * @time :2025/8/1 17:17
  30 + */
  31 + public function videoSetting(){
  32 + return [
  33 + 1 => 'CDN',
  34 + 2 => 'YOUTUBE'
  35 + ];
  36 + }
23 } 37 }
@@ -80,7 +80,7 @@ class RouteMap extends Base @@ -80,7 +80,7 @@ class RouteMap extends Base
80 if($source == self::SOURCE_PRODUCT){ 80 if($source == self::SOURCE_PRODUCT){
81 $suffix = '-product'; 81 $suffix = '-product';
82 } 82 }
83 - $route_len = 65; 83 +// $route_len = 65;
84 } 84 }
85 $length = strlen($sign); 85 $length = strlen($sign);
86 if($length > $route_len){ 86 if($length > $route_len){
@@ -19,7 +19,7 @@ class TicketProject extends Base @@ -19,7 +19,7 @@ class TicketProject extends Base
19 public function projectV6() 19 public function projectV6()
20 { 20 {
21 return $this->hasOne(Project::class, 'id', 'table_id') 21 return $this->hasOne(Project::class, 'id', 'table_id')
22 - ->where('version', 6); 22 + ->select(['id', 'title', 'company', 'is_upgrade', 'project_type']);
23 } 23 }
24 24
25 // 项目经理 25 // 项目经理
@@ -55,9 +55,9 @@ class AiVideoService @@ -55,9 +55,9 @@ class AiVideoService
55 * @method :post 55 * @method :post
56 * @time :2025/4/29 17:59 56 * @time :2025/4/29 17:59
57 */ 57 */
58 - public function createTask($title,$description,$images = [],$anchor = []){ 58 + public function createTask($title,$description,$images = [],$anchor = [],$storage = 'CDN'){
59 $request_url = $this->url.'api/video/create'; 59 $request_url = $this->url.'api/video/create';
60 - $param = ['title'=>$title, 'description'=>$description, 'images'=>$images,'anchor'=>$anchor]; 60 + $param = ['title'=>$title, 'description'=>$description, 'images'=>$images,'anchor'=>$anchor,'storage'=>$storage];
61 $param['mch_id'] = $this->mch_id; 61 $param['mch_id'] = $this->mch_id;
62 $this->sign = $this->generateSign($param,$this->key); 62 $this->sign = $this->generateSign($param,$this->key);
63 $param['sign'] = $this->sign; 63 $param['sign'] = $this->sign;
@@ -108,7 +108,6 @@ class CosService @@ -108,7 +108,6 @@ class CosService
108 { 108 {
109 if(!$key){ 109 if(!$key){
110 $url_arr = parse_url($file_url); 110 $url_arr = parse_url($file_url);
111 -  
112 if($same_name){ 111 if($same_name){
113 $path_arr = explode('/',$url_arr['path']); 112 $path_arr = explode('/',$url_arr['path']);
114 $filename = end($path_arr); 113 $filename = end($path_arr);
@@ -131,7 +130,6 @@ class CosService @@ -131,7 +130,6 @@ class CosService
131 'secretKey' => $cos['credentials']['secretKey'], 130 'secretKey' => $cos['credentials']['secretKey'],
132 ], 131 ],
133 ]); 132 ]);
134 -  
135 if(empty($body_str)){ 133 if(empty($body_str)){
136 try { 134 try {
137 $body_str = curl_c($file_url,false); 135 $body_str = curl_c($file_url,false);
@@ -146,7 +144,6 @@ class CosService @@ -146,7 +144,6 @@ class CosService
146 if(!$body_str){ 144 if(!$body_str){
147 return ''; 145 return '';
148 } 146 }
149 -  
150 try { 147 try {
151 $cosClient->putObject([ 148 $cosClient->putObject([
152 'Bucket' => $cos['bucket'], 149 'Bucket' => $cos['bucket'],
@@ -378,4 +375,77 @@ class CosService @@ -378,4 +375,77 @@ class CosService
378 } 375 }
379 return []; 376 return [];
380 } 377 }
  378 +
  379 + /**
  380 + * @remark :ai_video裁剪图片为4张
  381 + * @name :cropAndUploadToCOS
  382 + * @author :lyh
  383 + * @method :post
  384 + * @time :2025/8/2 16:52
  385 + */
  386 + public function cropAndUploadToCOS($imageUrl)
  387 + {
  388 + // 1. 下载远程图片内容
  389 + $imageData = file_get_contents($imageUrl);
  390 + if (!$imageData) {
  391 + return false;
  392 + }
  393 + // 2. 保存原图到临时文件
  394 + $tempOriginal = tempnam(sys_get_temp_dir(), 'original_') . '.png';
  395 + file_put_contents($tempOriginal, $imageData);
  396 + // 3. 使用 GD 加载图像
  397 + $src = imagecreatefrompng($tempOriginal);
  398 + if (!$src) {
  399 + return false;
  400 + }
  401 + $width = imagesx($src);
  402 + $height = imagesy($src);
  403 + $halfWidth = intval($width / 2);
  404 + $halfHeight = intval($height / 2);
  405 + // 4. 从原图 URL 提取路径信息
  406 + $parsed = parse_url($imageUrl);
  407 + $pathInfo = pathinfo($parsed['path']); // upload/p/1/png/2025-08/688dcebc26a7a59911.png
  408 + $cosPath = ltrim($pathInfo['dirname'], '/'); // 相对路径:upload/p/1/png/2025-08
  409 + $baseName = $pathInfo['filename']; // 文件名:688dcebc26a7a59911
  410 + $ext = $pathInfo['extension'] ?? 'png'; // 扩展名
  411 + // 5. 初始化 COS 客户端
  412 + $cos = config('filesystems.disks.cos');
  413 + $cosClient = new Client([
  414 + 'region' => $cos['region'],
  415 + 'credentials' => [
  416 + 'secretId' => $cos['credentials']['secretId'],
  417 + 'secretKey' => $cos['credentials']['secretKey'],
  418 + ],
  419 + ]);
  420 + // 6. 循环裁剪并上传
  421 + $resultPaths = [];
  422 + $index = 0;
  423 + $cos = config('filesystems.disks.cos');
  424 +
  425 + for ($y = 0; $y < 2; $y++) {
  426 + for ($x = 0; $x < 2; $x++) {
  427 + $crop = imagecreatetruecolor($halfWidth, $halfHeight);
  428 + imagecopy($crop, $src, 0, 0, $x * $halfWidth, $y * $halfHeight, $halfWidth, $halfHeight);
  429 + $tempCropped = tempnam(sys_get_temp_dir(), 'crop_') . '.png';
  430 + imagepng($crop, $tempCropped);
  431 + imagedestroy($crop);
  432 + // 新文件名,保持路径不变
  433 + $filename = $baseName . '_part' . $index++ . '.' . $ext;
  434 + $objectKey = $cosPath . '/' . $filename;
  435 + // 上传到 COS
  436 + $cosClient->putObject([
  437 + 'Bucket' => $cos['bucket'],
  438 + 'Key' => $objectKey,
  439 + 'Body' => fopen($tempCropped, 'rb'),
  440 + ]);
  441 + // 返回相对路径
  442 + $resultPaths[] = $cos['cdn1'].'/'.$objectKey;
  443 + unlink($tempCropped);
  444 + }
  445 + }
  446 + // 清理资源
  447 + imagedestroy($src);
  448 + unlink($tempOriginal);
  449 + return $resultPaths; // 相对路径数组
  450 + }
381 } 451 }
@@ -70,13 +70,12 @@ class GeoService @@ -70,13 +70,12 @@ class GeoService
70 } 70 }
71 71
72 /** 72 /**
73 - * @remark :请求deepSeek数据  
74 - * @name :getDeepSeek  
75 - * @author :lyh  
76 - * @method :post  
77 - * @time :2025/7/15 10:59 73 + * 请求chat会话, 自定义模型
  74 + * @param $content
  75 + * @param string $model
  76 + * @return mixed|string
78 */ 77 */
79 - public function getDeepSeekResult($content,$model = 'deepseek-r1'){ 78 + public function getChatResult($content, $model = 'deepseek-r1'){
80 $url = $this->api_url . 'v1/chat'; 79 $url = $this->api_url . 'v1/chat';
81 switch ($model){ 80 switch ($model){
82 case 'deepseek-r1': 81 case 'deepseek-r1':
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :MidJourneyService.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/8/2 10:48
  8 + */
  9 +
  10 +namespace App\Services;
  11 +
  12 +use Illuminate\Support\Facades\Http;
  13 +
  14 +class MidJourneyService
  15 +{
  16 + /**
  17 + * 请求域名
  18 + * @var string
  19 + */
  20 + public $url = 'https://api.cmer.com';
  21 +
  22 + public $header = [
  23 + 'apikey' => 'UkzZljFv83Z2qBi5YR1o3f2otAVWtug6',
  24 + 'X-CmerApi-Host' => 'aiccmj.p.cmer.com',
  25 + ];
  26 +
  27 + /**
  28 + * @remark :提交生成图片
  29 + * @name :imagine
  30 + * @author :lyh
  31 + * @method :post
  32 + * @time :2025/8/2 10:50
  33 + */
  34 + public function imagine($content, string $refer_img = ''){
  35 + try {
  36 + $data = ['prompt' => $content];
  37 + //回调地址
  38 + $data['callback_url'] = route('api.ImageCallBack');
  39 + $result = Http::withHeaders($this->header)->withoutVerifying()->post($this->url.'/v1/api/trigger/imagine', $data);
  40 + $json = $result->json();
  41 + }catch (\Throwable $e){
  42 + dump($e->getMessage());
  43 + $json = [];
  44 + }
  45 + return $json ?: [];
  46 + }
  47 +
  48 + /**
  49 + * 扩展图片
  50 + * @param $index
  51 + * @param $msg_id
  52 + * @param $msg_hash
  53 + * @param $trigger_id
  54 + * @return array|mixed
  55 + */
  56 + public function upscale($index, $msg_id, $msg_hash, $trigger_id){
  57 + try {
  58 + $result = Http::withHeaders($this->header)->withoutVerifying()->post($this->url.'/v1/api/trigger/upscale', [
  59 + "index" => $index,
  60 + "msg_id" => $msg_id,
  61 + "msg_hash" => $msg_hash,
  62 + "trigger_id" => $trigger_id
  63 + ]);
  64 + $json = $result->json();
  65 + }catch (\Throwable $e){
  66 + $json = [];
  67 + }
  68 + return $json;
  69 + }
  70 +
  71 + /**
  72 + * 优化图片
  73 + * @param $index
  74 + * @param $msg_id
  75 + * @param $msg_hash
  76 + * @param $trigger_id
  77 + * @return array|mixed
  78 + */
  79 + public function variation($index, $msg_id, $msg_hash, $trigger_id){
  80 + try {
  81 + $result = Http::withHeaders($this->header)->withoutVerifying()->post($this->url.'/v1/api/trigger/variation', [
  82 + "index" => $index,
  83 + "msg_id" => $msg_id,
  84 + "msg_hash" => $msg_hash,
  85 + "trigger_id" => $trigger_id
  86 + ]);
  87 + $json = $result->json();
  88 + }catch (\Throwable $e){
  89 + $json = [];
  90 + }
  91 + return $json;
  92 + }
  93 +}
@@ -17,6 +17,7 @@ use Illuminate\Support\Facades\Route; @@ -17,6 +17,7 @@ use Illuminate\Support\Facades\Route;
17 Route::middleware('auth:sanctum')->get('/user', function (Request $request) { 17 Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
18 return $request->user(); 18 return $request->user();
19 }); 19 });
  20 +Route::any('ImageCallBack', [\App\Http\Controllers\Api\AiVideoController::class, 'ImageCallBack'])->name('api.ImageCallBack');
20 Route::any('stripeWebhook', [\App\Http\Controllers\Api\PayStripeController::class, 'handleWebhook'])->name('api.handleWebhook'); 21 Route::any('stripeWebhook', [\App\Http\Controllers\Api\PayStripeController::class, 'handleWebhook'])->name('api.handleWebhook');
21 Route::any('traffic_visit', [\App\Http\Controllers\Api\NoticeController::class, 'trafficVisit'])->name('api.traffic_visit'); 22 Route::any('traffic_visit', [\App\Http\Controllers\Api\NoticeController::class, 'trafficVisit'])->name('api.traffic_visit');
22 Route::get('optimize_project_list', [\App\Http\Controllers\Api\PrivateController::class, 'optimizeProjectList'])->name('api.optimize_project_list'); 23 Route::get('optimize_project_list', [\App\Http\Controllers\Api\PrivateController::class, 'optimizeProjectList'])->name('api.optimize_project_list');
@@ -304,6 +304,7 @@ Route::middleware(['bloginauth'])->group(function () { @@ -304,6 +304,7 @@ Route::middleware(['bloginauth'])->group(function () {
304 Route::post('category/sort', [\App\Http\Controllers\Bside\Product\CategoryController::class, 'sort'])->name('product_category_sort'); 304 Route::post('category/sort', [\App\Http\Controllers\Bside\Product\CategoryController::class, 'sort'])->name('product_category_sort');
305 Route::any('category/delete', [\App\Http\Controllers\Bside\Product\CategoryController::class, 'delete'])->name('product_category_delete'); 305 Route::any('category/delete', [\App\Http\Controllers\Bside\Product\CategoryController::class, 'delete'])->name('product_category_delete');
306 Route::any('category/allSort', [\App\Http\Controllers\Bside\Product\CategoryController::class, 'allSort'])->name('product_category_allSort'); 306 Route::any('category/allSort', [\App\Http\Controllers\Bside\Product\CategoryController::class, 'allSort'])->name('product_category_allSort');
  307 + Route::any('category/editIsType', [\App\Http\Controllers\Bside\Product\CategoryController::class, 'editIsType'])->name('product_category_editIsType');//列表页聚合页设置
307 Route::any('category/copyCategory', [\App\Http\Controllers\Bside\Product\CategoryController::class, 'copyCategory'])->name('product_category_copyCategory'); 308 Route::any('category/copyCategory', [\App\Http\Controllers\Bside\Product\CategoryController::class, 'copyCategory'])->name('product_category_copyCategory');
308 //产品关键词 309 //产品关键词
309 Route::get('keyword', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'index'])->name('product_keyword'); 310 Route::get('keyword', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'index'])->name('product_keyword');