Merge remote-tracking branch 'origin/master' into akun
正在显示
10 个修改的文件
包含
300 行增加
和
35 行删除
| @@ -133,10 +133,8 @@ class AiBlogAutoPublish extends Command | @@ -133,10 +133,8 @@ class AiBlogAutoPublish extends Command | ||
| 133 | $aiBlogTaskModel = new AiBlogTaskModel(); | 133 | $aiBlogTaskModel = new AiBlogTaskModel(); |
| 134 | $next_auto_date = date('Y-m-d', strtotime('+' . mt_rand($frequency[0],$frequency[1]) . 'days')); //每3-6天自动发布 | 134 | $next_auto_date = date('Y-m-d', strtotime('+' . mt_rand($frequency[0],$frequency[1]) . 'days')); //每3-6天自动发布 |
| 135 | $aiBlogTaskModel->addReturnId(['project_id' => $project_id, 'type' => 2, 'task_id' => $result['data']['task_id'], 'status' => 1, 'next_auto_date' => $next_auto_date]); | 135 | $aiBlogTaskModel->addReturnId(['project_id' => $project_id, 'type' => 2, 'task_id' => $result['data']['task_id'], 'status' => 1, 'next_auto_date' => $next_auto_date]); |
| 136 | - | ||
| 137 | ProjectServer::useProject($project_id); | 136 | ProjectServer::useProject($project_id); |
| 138 | $aiBlogModel = new AiBlog(); | 137 | $aiBlogModel = new AiBlog(); |
| 139 | - | ||
| 140 | $start = strtotime('10:00:00'); | 138 | $start = strtotime('10:00:00'); |
| 141 | $end = strtotime('16:00:00'); | 139 | $end = strtotime('16:00:00'); |
| 142 | $randomTimestamp = mt_rand($start, $end); | 140 | $randomTimestamp = mt_rand($start, $end); |
| @@ -9,11 +9,15 @@ | @@ -9,11 +9,15 @@ | ||
| 9 | 9 | ||
| 10 | namespace App\Console\Commands\Ai; | 10 | namespace App\Console\Commands\Ai; |
| 11 | 11 | ||
| 12 | +use App\Models\Ai\AiVideo; | ||
| 13 | +use App\Models\Domain\DomainInfo; | ||
| 12 | use App\Models\Product\Keyword; | 14 | use App\Models\Product\Keyword; |
| 13 | use App\Models\Product\Product; | 15 | use App\Models\Product\Product; |
| 16 | +use App\Models\Project\AiBlogTask as AiBlogTaskModel; | ||
| 14 | use App\Models\Project\AiVideoAutoLog; | 17 | use App\Models\Project\AiVideoAutoLog; |
| 15 | use App\Models\Project\DeployOptimize; | 18 | use App\Models\Project\DeployOptimize; |
| 16 | use App\Models\Project\Project; | 19 | use App\Models\Project\Project; |
| 20 | +use App\Services\AiVideoService; | ||
| 17 | use App\Services\MidJourneyService; | 21 | use App\Services\MidJourneyService; |
| 18 | use App\Services\ProjectServer; | 22 | use App\Services\ProjectServer; |
| 19 | use Illuminate\Console\Command; | 23 | use Illuminate\Console\Command; |
| @@ -50,6 +54,12 @@ class AiVideoAutoPublish extends Command | @@ -50,6 +54,12 @@ class AiVideoAutoPublish extends Command | ||
| 50 | if($action == 'auto_publish'){ | 54 | if($action == 'auto_publish'){ |
| 51 | $this->auto_publish(); | 55 | $this->auto_publish(); |
| 52 | } | 56 | } |
| 57 | + if($action == 'auto_send_video'){ | ||
| 58 | + $this->auto_send_video(); | ||
| 59 | + } | ||
| 60 | + if($action == 'auto_save_video_task'){ | ||
| 61 | + $this->auto_save_video_task(); | ||
| 62 | + } | ||
| 53 | } | 63 | } |
| 54 | 64 | ||
| 55 | /** | 65 | /** |
| @@ -63,7 +73,7 @@ class AiVideoAutoPublish extends Command | @@ -63,7 +73,7 @@ class AiVideoAutoPublish extends Command | ||
| 63 | $this->output('开始自动发布Video文章'); | 73 | $this->output('开始自动发布Video文章'); |
| 64 | $projectModel = new Project(); | 74 | $projectModel = new Project(); |
| 65 | $optimizeModel = new DeployOptimize(); | 75 | $optimizeModel = new DeployOptimize(); |
| 66 | - $projectList = $projectModel->list(['is_ai_video'=>1,'project_type'=>0,'delete_status'=>0,'site_status'=>0,'extend_type'=>0],'id',['id']); | 76 | + $projectList = $projectModel->list(['is_ai_video'=>1,'id'=>1,'delete_status'=>0,'site_status'=>0,'extend_type'=>0],'id',['id','project_type']); |
| 67 | foreach ($projectList as $item){ | 77 | foreach ($projectList as $item){ |
| 68 | $this->output("项目{$item['id']}开始自动发布"); | 78 | $this->output("项目{$item['id']}开始自动发布"); |
| 69 | //获取当前是否开启自动发布aiVideo | 79 | //获取当前是否开启自动发布aiVideo |
| @@ -82,9 +92,15 @@ class AiVideoAutoPublish extends Command | @@ -82,9 +92,15 @@ class AiVideoAutoPublish extends Command | ||
| 82 | $this->output("项目{$item['id']}未到执行时间" . $next_auto_date); | 92 | $this->output("项目{$item['id']}未到执行时间" . $next_auto_date); |
| 83 | continue; | 93 | continue; |
| 84 | } | 94 | } |
| 85 | - //获取当前网站的标题 | ||
| 86 | - ProjectServer::useProject($item['id']); | ||
| 87 | - $data = $this->getVideoInfo(); | 95 | + if($item['project_type'] == 1){ |
| 96 | + //todo::页面上获取数据 | ||
| 97 | + $data = $this->getAiVideoParam($item['id']); | ||
| 98 | + }else{ | ||
| 99 | + //获取当前网站的标题 | ||
| 100 | + ProjectServer::useProject($item['id']); | ||
| 101 | + $data = $this->getVideoInfo(); | ||
| 102 | + DB::disconnect('custom_mysql'); | ||
| 103 | + } | ||
| 88 | if(!empty($data)){ | 104 | if(!empty($data)){ |
| 89 | //写入一条零时生成视频记录 | 105 | //写入一条零时生成视频记录 |
| 90 | $aiVideoAutoLogModel = new AiVideoAutoLog(); | 106 | $aiVideoAutoLogModel = new AiVideoAutoLog(); |
| @@ -92,8 +108,8 @@ class AiVideoAutoPublish extends Command | @@ -92,8 +108,8 @@ class AiVideoAutoPublish extends Command | ||
| 92 | ['project_id'=>$item['id'],'title'=>$data['title'],'remark'=>$data['remark'],'images'=>json_encode($data['images'],true),'date'=>date('Y-m-d')] | 108 | ['project_id'=>$item['id'],'title'=>$data['title'],'remark'=>$data['remark'],'images'=>json_encode($data['images'],true),'date'=>date('Y-m-d')] |
| 93 | ); | 109 | ); |
| 94 | } | 110 | } |
| 95 | - DB::disconnect('custom_mysql'); | ||
| 96 | } | 111 | } |
| 112 | + return true; | ||
| 97 | } | 113 | } |
| 98 | 114 | ||
| 99 | /** | 115 | /** |
| @@ -113,8 +129,19 @@ class AiVideoAutoPublish extends Command | @@ -113,8 +129,19 @@ class AiVideoAutoPublish extends Command | ||
| 113 | return $data; | 129 | return $data; |
| 114 | } | 130 | } |
| 115 | $data['title'] = $info['title']; | 131 | $data['title'] = $info['title']; |
| 116 | - $data['remark'] = $info['intro']; | ||
| 117 | - $data['images'] = array_walk($info['gallery'], function (&$item) {$item['alt'] = $item['title'];unset($item['title']);}); | 132 | + $data['remark'] = strip_tags($info['intro']); |
| 133 | + if(empty($data['remark'])){ | ||
| 134 | + $data['remark'] = $data['title']; | ||
| 135 | + } | ||
| 136 | + $data['images'] = array_filter(array_map(function ($item) use ($data) { | ||
| 137 | + if (!empty($item['url'])) { | ||
| 138 | + return [ | ||
| 139 | + 'alt' => $item['title'] ?? $data['title'], | ||
| 140 | + 'url' => getImageUrl($item['url']), | ||
| 141 | + ]; | ||
| 142 | + } | ||
| 143 | + return null; | ||
| 144 | + }, $info['gallery'])); | ||
| 118 | return $data; | 145 | return $data; |
| 119 | }else{ | 146 | }else{ |
| 120 | //聚合页获取当前关联产品的图片 | 147 | //聚合页获取当前关联产品的图片 |
| @@ -123,13 +150,26 @@ class AiVideoAutoPublish extends Command | @@ -123,13 +150,26 @@ class AiVideoAutoPublish extends Command | ||
| 123 | if(empty($keywordInfo)){ | 150 | if(empty($keywordInfo)){ |
| 124 | return $data; | 151 | return $data; |
| 125 | } | 152 | } |
| 126 | - $data['title'] = $keywordInfo['title']; | ||
| 127 | - $data['remark'] = $keywordInfo['intro']; | 153 | + $data['title'] = $keywordInfo['keyword_title']; |
| 154 | + $data['remark'] = $keywordInfo['keyword_content']; | ||
| 155 | + $data['remark'] = strip_tags($keywordInfo['intro']); | ||
| 156 | + if(empty($data['remark'])){ | ||
| 157 | + $data['remark'] = $data['title']; | ||
| 158 | + } | ||
| 128 | $data['images'] = []; | 159 | $data['images'] = []; |
| 129 | $productModel = new Product(); | 160 | $productModel = new Product(); |
| 130 | $productList = $productModel->list(['keyword_id'=>['like','%,'.$keywordInfo['id'].',%']],'id',['gallery'],'desc',10); | 161 | $productList = $productModel->list(['keyword_id'=>['like','%,'.$keywordInfo['id'].',%']],'id',['gallery'],'desc',10); |
| 131 | foreach ($productList as $info){ | 162 | foreach ($productList as $info){ |
| 132 | - $data['images'] = array_merge($data['images'],array_walk($info['gallery'], function (&$item) {$item['alt'] = $item['title'];unset($item['title']);})); | 163 | + $images = array_filter(array_map(function ($item) use ($data) { |
| 164 | + if (!empty($item['url'])) { | ||
| 165 | + return [ | ||
| 166 | + 'alt' => $item['title'] ?? $data['title'], | ||
| 167 | + 'url' => getImageUrl($item['url']), | ||
| 168 | + ]; | ||
| 169 | + } | ||
| 170 | + return null; // 返回 null 让 array_filter 去除 | ||
| 171 | + }, $info['gallery'])); | ||
| 172 | + $data['images'] = array_merge($data['images'],$images); | ||
| 133 | } | 173 | } |
| 134 | return $data; | 174 | return $data; |
| 135 | } | 175 | } |
| @@ -147,20 +187,24 @@ class AiVideoAutoPublish extends Command | @@ -147,20 +187,24 @@ class AiVideoAutoPublish extends Command | ||
| 147 | $aiVideoAutoLogModel = new AiVideoAutoLog(); | 187 | $aiVideoAutoLogModel = new AiVideoAutoLog(); |
| 148 | while (true){ | 188 | while (true){ |
| 149 | if($number > 5){ | 189 | if($number > 5){ |
| 190 | + echo date('Y-m-d H:i:s').':当前生成图片数量已达到最大限度。'.$number.PHP_EOL; | ||
| 150 | sleep(300); | 191 | sleep(300); |
| 151 | continue; | 192 | continue; |
| 152 | } | 193 | } |
| 153 | - $item = $aiVideoAutoLogModel->read(['status'=>0]); | ||
| 154 | - if(empty($info)){ | 194 | + $item = $aiVideoAutoLogModel->read(['status'=>0,'trigger_id'=>null]); |
| 195 | + if($item === false){ | ||
| 196 | + echo date('Y-m-d H:i:s').':无生成图片的数据。'.PHP_EOL; | ||
| 155 | sleep(60); | 197 | sleep(60); |
| 156 | continue; | 198 | continue; |
| 157 | } | 199 | } |
| 158 | if(count($item['images']) < 6){ | 200 | if(count($item['images']) < 6){ |
| 201 | + echo date('Y-m-d H:i:s').':提交生成图片。'.$item['project_id'].PHP_EOL; | ||
| 159 | //需要生成图片 | 202 | //需要生成图片 |
| 160 | $content = "{$item['remark']},{$item['title']},4K,高清 --no logo --ar 16:9"; | 203 | $content = "{$item['remark']},{$item['title']},4K,高清 --no logo --ar 16:9"; |
| 161 | $midJourneyService = new MidJourneyService(); | 204 | $midJourneyService = new MidJourneyService(); |
| 162 | $result = $midJourneyService->imagine($content); | 205 | $result = $midJourneyService->imagine($content); |
| 163 | if($result && !empty($result['trigger_id'])){ | 206 | if($result && !empty($result['trigger_id'])){ |
| 207 | + echo '提交的数据详情。'.json_encode($result,true).$item['project_id'].PHP_EOL; | ||
| 164 | Redis::incr('ai_video_image'); | 208 | Redis::incr('ai_video_image'); |
| 165 | $aiVideoAutoLogModel->edit(['trigger_id'=>$result['trigger_id']],['id'=>$item['id']]); | 209 | $aiVideoAutoLogModel->edit(['trigger_id'=>$result['trigger_id']],['id'=>$item['id']]); |
| 166 | } | 210 | } |
| @@ -172,16 +216,89 @@ class AiVideoAutoPublish extends Command | @@ -172,16 +216,89 @@ class AiVideoAutoPublish extends Command | ||
| 172 | } | 216 | } |
| 173 | 217 | ||
| 174 | /** | 218 | /** |
| 175 | - * @remark : | 219 | + * @remark :状态为1的数据推送至生成视频任务表 |
| 220 | + * @name :auto_save_video_task | ||
| 221 | + * @author :lyh | ||
| 222 | + * @method :post | ||
| 223 | + * @time :2025/8/4 9:39 | ||
| 224 | + */ | ||
| 225 | + public function auto_save_video_task(){ | ||
| 226 | + $aiVideoAutoLogModel = new AiVideoAutoLog(); | ||
| 227 | + while (true){ | ||
| 228 | + //获取任务id | ||
| 229 | + $task_id = $this->getAutoTaskId(); | ||
| 230 | + if(empty($task_id)){ | ||
| 231 | + sleep(300); | ||
| 232 | + continue; | ||
| 233 | + } | ||
| 234 | + $info = $aiVideoAutoLogModel->read(['id'=>$task_id]); | ||
| 235 | + if($info === false){ | ||
| 236 | + $this->output(date('Y-m-d H:i:s').':当前数据不存在或已被删除'.$task_id); | ||
| 237 | + continue; | ||
| 238 | + } | ||
| 239 | + try { | ||
| 240 | + $aiVideoTaskModel = new AiVideoTask(); | ||
| 241 | + $aiVideoService = new AiVideoService($info['project_id']); | ||
| 242 | + $projectModel = new DeployOptimize(); | ||
| 243 | + $video_setting = $projectModel->getValue(['project_id'=>$info['project_id']],'video_setting'); | ||
| 244 | + $storage = $aiVideoTaskModel->videoSetting()[$video_setting ?? 1]; | ||
| 245 | + $result = $aiVideoService->createTask($info['title'],$info['remark'],$info['images'],[],$storage); | ||
| 246 | + if($result['status'] == 200){ | ||
| 247 | + $aiVideoTaskModel->addReturnId(['task_id'=>$result['data']['task_id'],'project_id'=>$info['project_id'],'storage'=>$storage]); | ||
| 248 | + ProjectServer::useProject($info['project_id']); | ||
| 249 | + $aiVideoModel = new AiVideo(); | ||
| 250 | + $next_auto_date = date('Y-m-d', strtotime('+' . mt_rand(5,7) . 'days')); //每5-7天自动发布 | ||
| 251 | + $aiVideoModel->addReturnId(['next_auto_date'=>$next_auto_date,'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)]); | ||
| 252 | + DB::disconnect('custom_mysql'); | ||
| 253 | + $aiVideoAutoLogModel->edit(['status'=>2],['id'=>$info['id']]); | ||
| 254 | + } | ||
| 255 | + }catch (\Exception $e){ | ||
| 256 | + $this->output( date('Y-m-d H:i:s').':当前数据不存在或已被删除'.$task_id); | ||
| 257 | + continue; | ||
| 258 | + } | ||
| 259 | + } | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + /** | ||
| 263 | + * @remark :火锅自动发布任务id | ||
| 264 | + * @name :getAutoTaskId | ||
| 265 | + * @author :lyh | ||
| 266 | + * @method :post | ||
| 267 | + * @time :2025/8/4 9:44 | ||
| 268 | + */ | ||
| 269 | + public function getAutoTaskId() | ||
| 270 | + { | ||
| 271 | + $task_id = Redis::rpop('auto_ai_video_task'); | ||
| 272 | + if (empty($task_id)) { | ||
| 273 | + $aiVideoAutoLogModel = new AiVideoAutoLog(); | ||
| 274 | + $ids = $aiVideoAutoLogModel->formatQuery(['status'=>1])->pluck('id'); | ||
| 275 | + if(!empty($ids)){ | ||
| 276 | + foreach ($ids as $id) { | ||
| 277 | + Redis::lpush('auto_ai_video_task', $id); | ||
| 278 | + } | ||
| 279 | + } | ||
| 280 | + $task_id = Redis::rpop('auto_ai_video_task'); | ||
| 281 | + } | ||
| 282 | + return $task_id; | ||
| 283 | + } | ||
| 284 | + | ||
| 285 | + /** | ||
| 286 | + * @remark :页面获取 | ||
| 176 | * @name :getAiVideoParam | 287 | * @name :getAiVideoParam |
| 177 | * @author :lyh | 288 | * @author :lyh |
| 178 | * @method :post | 289 | * @method :post |
| 179 | * @time :2025/8/1 16:25 | 290 | * @time :2025/8/1 16:25 |
| 180 | */ | 291 | */ |
| 181 | - public function getAiVideoParam() | 292 | + public function getAiVideoParam($project_id = 3751) |
| 182 | { | 293 | { |
| 183 | - $project_id = '1'; | ||
| 184 | - $domain = 'www.cs-conveyor.com'; | 294 | + //获取当前网站域名 |
| 295 | + $domainModel = new DomainInfo(); | ||
| 296 | + $domain = $domainModel->getValue(['project_id'=>$project_id],'domain'); | ||
| 297 | + if(empty($domain)){ | ||
| 298 | + return true; | ||
| 299 | + } | ||
| 300 | + $domain = str_replace('blog.', 'www.', $domain); | ||
| 301 | + //todo::看是否获取建站的产品数据 | ||
| 185 | try { | 302 | try { |
| 186 | $sitemap_url = 'https://' . $domain . '/sitemap_post_tag.xml'; | 303 | $sitemap_url = 'https://' . $domain . '/sitemap_post_tag.xml'; |
| 187 | $sitemap_string = file_get_contents($sitemap_url); | 304 | $sitemap_string = file_get_contents($sitemap_url); |
| @@ -203,16 +320,20 @@ class AiVideoAutoPublish extends Command | @@ -203,16 +320,20 @@ class AiVideoAutoPublish extends Command | ||
| 203 | $img = $dom->find('.layout .global_section img'); | 320 | $img = $dom->find('.layout .global_section img'); |
| 204 | $images = []; | 321 | $images = []; |
| 205 | foreach ($img as $item) { | 322 | foreach ($img as $item) { |
| 206 | - if (empty($item->src) || empty($item->alt)) | 323 | + if (empty($item->src) || empty($item->alt)){ |
| 207 | continue; | 324 | continue; |
| 208 | - array_push($images, ['src' => $item->src, 'alt' => $item->alt]); | 325 | + } |
| 326 | + array_push($images, ['url' => $item->src, 'alt' => $item->alt]); | ||
| 209 | } | 327 | } |
| 210 | if (empty($title) || empty($content) || empty($images)) { | 328 | if (empty($title) || empty($content) || empty($images)) { |
| 211 | $num++; | 329 | $num++; |
| 212 | goto AGAIN; | 330 | goto AGAIN; |
| 213 | } | 331 | } |
| 332 | + return ['title'=>$title,'remark'=>$content,'images'=>$images]; | ||
| 214 | } catch (\Exception $e) { | 333 | } catch (\Exception $e) { |
| 334 | + $this->output('project_id: ' . $project_id . ', domain: ' . $domain . ', error: ' . $e->getMessage()); | ||
| 215 | echo 'project_id: ' . $project_id . ', domain: ' . $domain . ', error: ' . $e->getMessage() . PHP_EOL; | 335 | echo 'project_id: ' . $project_id . ', domain: ' . $domain . ', error: ' . $e->getMessage() . PHP_EOL; |
| 336 | + return []; | ||
| 216 | } | 337 | } |
| 217 | } | 338 | } |
| 218 | 339 |
| @@ -24,6 +24,7 @@ use App\Models\Template\BTemplateMain; | @@ -24,6 +24,7 @@ use App\Models\Template\BTemplateMain; | ||
| 24 | use App\Models\Template\TemplateTypeMain; | 24 | use App\Models\Template\TemplateTypeMain; |
| 25 | use App\Models\WebSetting\WebSetting; | 25 | use App\Models\WebSetting\WebSetting; |
| 26 | use App\Services\AiBlogService; | 26 | use App\Services\AiBlogService; |
| 27 | +use App\Services\CosService; | ||
| 27 | use App\Services\Geo\GeoService; | 28 | use App\Services\Geo\GeoService; |
| 28 | use App\Services\MidJourneyService; | 29 | use App\Services\MidJourneyService; |
| 29 | use App\Services\ProjectServer; | 30 | use App\Services\ProjectServer; |
| @@ -47,10 +48,13 @@ class lyhDemo extends Command | @@ -47,10 +48,13 @@ class lyhDemo extends Command | ||
| 47 | protected $description = '更新路由'; | 48 | protected $description = '更新路由'; |
| 48 | 49 | ||
| 49 | public function handle(){ | 50 | public function handle(){ |
| 50 | - $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"; | ||
| 51 | - $midJourneyService = new MidJourneyService(); | ||
| 52 | - $result = $midJourneyService->imagine($content); | ||
| 53 | - dd($result); | 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); | ||
| 54 | } | 58 | } |
| 55 | 59 | ||
| 56 | /** | 60 | /** |
| @@ -55,10 +55,10 @@ class WorkchatMessageSend extends Command | @@ -55,10 +55,10 @@ class WorkchatMessageSend extends Command | ||
| 55 | $task->status = MessagePush::STATUS_ERROR; | 55 | $task->status = MessagePush::STATUS_ERROR; |
| 56 | $task->remark = '请求超时'; | 56 | $task->remark = '请求超时'; |
| 57 | } | 57 | } |
| 58 | - }catch (\Exception $e){ | ||
| 59 | - $this->output('推送消息' . $task->id . '失败:' . $e->getMessage()); | 58 | + } catch (\Exception $e) { |
| 59 | + $this->output('推送消息' . $task->id . '失败:' . $e->getLine() . ' - ' . $e->getMessage()); | ||
| 60 | $task->status = MessagePush::STATUS_ERROR; | 60 | $task->status = MessagePush::STATUS_ERROR; |
| 61 | - $task->remark = mb_substr($e->getMessage(), 0 , 200); | 61 | + $task->remark = mb_substr($e->getMessage(), 0, 200); |
| 62 | } | 62 | } |
| 63 | $task->save(); | 63 | $task->save(); |
| 64 | } | 64 | } |
| @@ -11,6 +11,8 @@ namespace App\Http\Controllers\Api; | @@ -11,6 +11,8 @@ namespace App\Http\Controllers\Api; | ||
| 11 | 11 | ||
| 12 | use App\Enums\Common\Code; | 12 | use App\Enums\Common\Code; |
| 13 | use App\Models\Project\AiVideoAutoLog; | 13 | use App\Models\Project\AiVideoAutoLog; |
| 14 | +use App\Services\CosService; | ||
| 15 | +use Illuminate\Support\Facades\Redis; | ||
| 14 | 16 | ||
| 15 | class AiVideoController extends BaseController | 17 | class AiVideoController extends BaseController |
| 16 | { | 18 | { |
| @@ -22,10 +24,59 @@ class AiVideoController extends BaseController | @@ -22,10 +24,59 @@ class AiVideoController extends BaseController | ||
| 22 | * @time :2025/8/2 11:19 | 24 | * @time :2025/8/2 11:19 |
| 23 | */ | 25 | */ |
| 24 | public function ImageCallBack(){ | 26 | public function ImageCallBack(){ |
| 25 | - $data = $this->param; | ||
| 26 | - @file_put_contents(storage_path('logs/lyh_error.log'), var_export($data, true) . PHP_EOL, FILE_APPEND); | ||
| 27 | -// $aiVideoAutoLogModel = new AiVideoAutoLog(); | ||
| 28 | -// $aiVideoAutoLogModel->edit(['result'=>$data],['trigger_id'=>$data['trigger_id']]); | 27 | + $this->saveLog('数据详情:'.json_encode($this->param,true)); |
| 28 | + $count = Redis::decr('ai_video_image'); | ||
| 29 | + if ($count < 0) { | ||
| 30 | + Redis::set('ai_video_image', 0); | ||
| 31 | + } | ||
| 32 | + $data = $this->param['attachments'][0] ?? []; | ||
| 33 | + $aiVideoAutoLogModel = new AiVideoAutoLog(); | ||
| 34 | + if(empty($data) || empty($data['url'])){ | ||
| 35 | + $this->saveLog('未获取到回调数据详情'); | ||
| 36 | + $aiVideoAutoLogModel->edit(['status'=>9,'result'=>json_encode($this->param,true)],['trigger_id'=>$this->param['id']]); | ||
| 37 | + } | ||
| 38 | + //获取当前数据详情 | ||
| 39 | + $info = $aiVideoAutoLogModel->read(['trigger_id'=>$this->param['trigger_id']]); | ||
| 40 | + if($info === false){ | ||
| 41 | + $this->saveLog('未获取到当前数据详情。'.json_encode($info,true)); | ||
| 42 | + $this->response('success'); | ||
| 43 | + } | ||
| 44 | + //上传图片 返回cdn链接 | ||
| 45 | + $cosService = new CosService(); | ||
| 46 | + $imagePath = $cosService->uploadRemote($info['project_id'],'video',$data['url']); | ||
| 47 | + $this->saveLog($imagePath); | ||
| 48 | + try { | ||
| 49 | + if($imagePath){ | ||
| 50 | + $cos = config('filesystems.disks.cos'); | ||
| 51 | + $url = $cos['cdn1'].'/'.$imagePath; | ||
| 52 | + //裁剪图片为4张 | ||
| 53 | + $images = []; | ||
| 54 | + $cosService = new CosService(); | ||
| 55 | + $data = $cosService->cropAndUploadToCOS($url); | ||
| 56 | + if(!empty($data)){ | ||
| 57 | + foreach ($data as $item){ | ||
| 58 | + $images[] = ['url'=>$item,'alt'=>$info['title']]; | ||
| 59 | + } | ||
| 60 | + } | ||
| 61 | + $images = array_merge($images,$info['images']); | ||
| 62 | + $this->saveLog('success->ok'); | ||
| 63 | + $aiVideoAutoLogModel->edit(['images'=>$images,'result'=>json_encode($this->param,true),'status'=>1],['id'=>$info['id']]); | ||
| 64 | + } | ||
| 65 | + }catch (\Exception $e){ | ||
| 66 | + $this->saveLog('上传图片失败,获取到数据详情。'.$e->getMessage()); | ||
| 67 | + } | ||
| 29 | $this->response('success'); | 68 | $this->response('success'); |
| 30 | } | 69 | } |
| 70 | + | ||
| 71 | + /** | ||
| 72 | + * @remark :保存日志 | ||
| 73 | + * @name :saveLog | ||
| 74 | + * @author :lyh | ||
| 75 | + * @method :post | ||
| 76 | + * @time :2025/8/4 11:54 | ||
| 77 | + */ | ||
| 78 | + public function saveLog($message){ | ||
| 79 | + @file_put_contents(storage_path('logs/ai_video.log'), var_export($message, true) . PHP_EOL, FILE_APPEND); | ||
| 80 | + return true; | ||
| 81 | + } | ||
| 31 | } | 82 | } |
| @@ -32,7 +32,11 @@ class DomainInfoController extends BaseController | @@ -32,7 +32,11 @@ class DomainInfoController extends BaseController | ||
| 32 | if(isset($this->map['domain']) && !empty($this->map['domain'])){ | 32 | if(isset($this->map['domain']) && !empty($this->map['domain'])){ |
| 33 | $this->map['domain'] = ['like','%'.$this->map['domain'].'%']; | 33 | $this->map['domain'] = ['like','%'.$this->map['domain'].'%']; |
| 34 | } | 34 | } |
| 35 | - $lists = $domainModel->lists($this->map,$this->page,$this->row,$this->order); | 35 | + if(isset($this->map['sort']) && !empty($this->map['sort'])){ |
| 36 | + $sort = $this->map['sort']; | ||
| 37 | + unset($this->map['sort']); | ||
| 38 | + } | ||
| 39 | + $lists = $domainModel->lists($this->map,$this->page,$this->row,$this->order,['*'],$sort ?? 'desc'); | ||
| 36 | if(!empty($lists)){ | 40 | if(!empty($lists)){ |
| 37 | $project_model = new Project(); | 41 | $project_model = new Project(); |
| 38 | $serverIpModel = new ServersIp(); | 42 | $serverIpModel = new ServersIp(); |
| @@ -10,6 +10,7 @@ use App\Http\Logic\Aside\Project\OnlineCheckLogic; | @@ -10,6 +10,7 @@ use App\Http\Logic\Aside\Project\OnlineCheckLogic; | ||
| 10 | use App\Http\Logic\Aside\Project\ProcessRecordsLogic; | 10 | use App\Http\Logic\Aside\Project\ProcessRecordsLogic; |
| 11 | use App\Http\Logic\Aside\Project\ProjectLogic; | 11 | use App\Http\Logic\Aside\Project\ProjectLogic; |
| 12 | use App\Http\Requests\Aside\Project\ProcessRecordsRequest; | 12 | use App\Http\Requests\Aside\Project\ProcessRecordsRequest; |
| 13 | +use App\Models\Ai\AiVideo; | ||
| 13 | use App\Models\ASide\APublicModel; | 14 | use App\Models\ASide\APublicModel; |
| 14 | use App\Models\Channel\Channel; | 15 | use App\Models\Channel\Channel; |
| 15 | use App\Models\Channel\User; | 16 | use App\Models\Channel\User; |
| @@ -27,7 +28,9 @@ use App\Models\Industry\ProjectIndustry; | @@ -27,7 +28,9 @@ use App\Models\Industry\ProjectIndustry; | ||
| 27 | use App\Models\Inquiry\InquirySet; | 28 | use App\Models\Inquiry\InquirySet; |
| 28 | use App\Models\Manage\BelongingGroup; | 29 | use App\Models\Manage\BelongingGroup; |
| 29 | use App\Models\Manage\ManageHr; | 30 | use App\Models\Manage\ManageHr; |
| 31 | +use App\Models\Project\AiVideoTask; | ||
| 30 | use App\Models\Project\DeployBuild; | 32 | use App\Models\Project\DeployBuild; |
| 33 | +use App\Models\Project\DeployOptimize; | ||
| 31 | use App\Models\Project\Payment; | 34 | use App\Models\Project\Payment; |
| 32 | use App\Models\Project\ProcessRecords; | 35 | use App\Models\Project\ProcessRecords; |
| 33 | use App\Models\Project\Project; | 36 | use App\Models\Project\Project; |
| @@ -1285,4 +1288,16 @@ class ProjectController extends BaseController | @@ -1285,4 +1288,16 @@ class ProjectController extends BaseController | ||
| 1285 | $this->response('success',Code::SUCCESS,['url'=>$this->param['url']]); | 1288 | $this->response('success',Code::SUCCESS,['url'=>$this->param['url']]); |
| 1286 | } | 1289 | } |
| 1287 | 1290 | ||
| 1291 | + /** | ||
| 1292 | + * @remark :获取视频配置类型 | ||
| 1293 | + * @name :videoSetting | ||
| 1294 | + * @author :lyh | ||
| 1295 | + * @method :post | ||
| 1296 | + * @time :2025/8/5 9:50 | ||
| 1297 | + */ | ||
| 1298 | + public function videoSetting(){ | ||
| 1299 | + $videoModel = new AiVideoTask(); | ||
| 1300 | + $data = $videoModel->videoSetting(); | ||
| 1301 | + $this->response('success',Code::SUCCESS,$data); | ||
| 1302 | + } | ||
| 1288 | } | 1303 | } |
| @@ -191,7 +191,7 @@ class ProjectAssociationServices extends BaseService | @@ -191,7 +191,7 @@ class ProjectAssociationServices extends BaseService | ||
| 191 | $url = 'https://hub.ai.cc/api/globalso_ai_customer_service/send_msg'; | 191 | $url = 'https://hub.ai.cc/api/globalso_ai_customer_service/send_msg'; |
| 192 | $result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->timeout(30)->post($url, $param)->json(); | 192 | $result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->timeout(30)->post($url, $param)->json(); |
| 193 | if(empty($result) || $result['status'] != 200){ | 193 | if(empty($result) || $result['status'] != 200){ |
| 194 | - throw new \Exception($result['message'] ?? ''); | 194 | + throw new \Exception(json_encode($result)); |
| 195 | } | 195 | } |
| 196 | return true; | 196 | return true; |
| 197 | } | 197 | } |
| @@ -130,7 +130,6 @@ class CosService | @@ -130,7 +130,6 @@ class CosService | ||
| 130 | 'secretKey' => $cos['credentials']['secretKey'], | 130 | 'secretKey' => $cos['credentials']['secretKey'], |
| 131 | ], | 131 | ], |
| 132 | ]); | 132 | ]); |
| 133 | - | ||
| 134 | if(empty($body_str)){ | 133 | if(empty($body_str)){ |
| 135 | try { | 134 | try { |
| 136 | $body_str = curl_c($file_url,false); | 135 | $body_str = curl_c($file_url,false); |
| @@ -145,7 +144,6 @@ class CosService | @@ -145,7 +144,6 @@ class CosService | ||
| 145 | if(!$body_str){ | 144 | if(!$body_str){ |
| 146 | return ''; | 145 | return ''; |
| 147 | } | 146 | } |
| 148 | - | ||
| 149 | try { | 147 | try { |
| 150 | $cosClient->putObject([ | 148 | $cosClient->putObject([ |
| 151 | 'Bucket' => $cos['bucket'], | 149 | 'Bucket' => $cos['bucket'], |
| @@ -377,4 +375,77 @@ class CosService | @@ -377,4 +375,77 @@ class CosService | ||
| 377 | } | 375 | } |
| 378 | return []; | 376 | return []; |
| 379 | } | 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 | + } | ||
| 380 | } | 451 | } |
| @@ -205,6 +205,7 @@ Route::middleware(['aloginauth'])->group(function () { | @@ -205,6 +205,7 @@ Route::middleware(['aloginauth'])->group(function () { | ||
| 205 | Route::any('/setIsParticiple', [Aside\Project\ProjectController::class, 'setIsParticiple'])->name('admin.project_setIsParticiple');//开启/关闭分词 | 205 | Route::any('/setIsParticiple', [Aside\Project\ProjectController::class, 'setIsParticiple'])->name('admin.project_setIsParticiple');//开启/关闭分词 |
| 206 | Route::any('/saveSiteStatus', [Aside\Project\ProjectController::class, 'saveSiteStatus'])->name('admin.project_saveSiteStatus'); | 206 | Route::any('/saveSiteStatus', [Aside\Project\ProjectController::class, 'saveSiteStatus'])->name('admin.project_saveSiteStatus'); |
| 207 | Route::any('/updateTdk', [Aside\Project\ProjectController::class, 'updateTdk'])->name('admin.project_updateTdk');//更新项目tdk | 207 | Route::any('/updateTdk', [Aside\Project\ProjectController::class, 'updateTdk'])->name('admin.project_updateTdk');//更新项目tdk |
| 208 | + Route::any('/videoSetting', [Aside\Project\ProjectController::class, 'videoSetting'])->name('admin.project_videoSetting');//项目管理AI.video设置 | ||
| 208 | //获取关键词前缀和后缀 | 209 | //获取关键词前缀和后缀 |
| 209 | Route::prefix('keyword')->group(function () { | 210 | Route::prefix('keyword')->group(function () { |
| 210 | Route::any('/getKeywordPrefix', [Aside\Project\KeywordPrefixController::class, 'getKeywordPrefix'])->name('admin.keyword_getKeywordPrefix'); | 211 | Route::any('/getKeywordPrefix', [Aside\Project\KeywordPrefixController::class, 'getKeywordPrefix'])->name('admin.keyword_getKeywordPrefix'); |
-
请 注册 或 登录 后发表评论