作者 刘锟

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

<?php
/**
* @remark :
* @name :AiVideoTask.php
* @author :lyh
* @method :post
* @time :2025/4/30 11:18
*/
namespace App\Console\Commands\Ai;
use App\Models\Ai\AiVideo;
use App\Models\Project\AiVideoTask as AiVideoTaskModel;
use App\Models\RouteMap\RouteMap;
use App\Services\AiVideoService;
use App\Services\DingService;
use App\Services\ProjectServer;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
class AiVideoTask extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'save_ai_video';
public $updateProject = [];//需更新的列表
public $routes = [];//需要更新的路由
/**
* The console command description.
*
* @var string
*/
protected $description = '查询ai_video是否已经生成';
/**
* @return bool
* @throws \Exception
*/
public function handle(){
while (true){
//获取任务id
$task_id = $this->getTaskId();
if(empty($task_id)){
sleep(300);
continue;
}
$this->_action($task_id);
}
return true;
}
/**
* 获取任务id
* @param int $finish_at
* @return mixed
*/
public function getTaskId($finish_at = 2)
{
$task_id = Redis::rpop('ai_video_task');
if (empty($task_id)) {
// if(!empty($this->updateProject)){
// $this->updateProject($this->updateProject);
// $this->updateProject = [];
// }
// if(!empty($this->routes)){
// $this->updateRoutes($this->routes);
// $this->routes = [];
// }
$aiVideoTaskModel = new AiVideoTaskModel();
$finish_at = date('Y-m-d H:i:s', strtotime('-' . $finish_at . ' hour'));
$ids = $aiVideoTaskModel->formatQuery(['status'=>$aiVideoTaskModel::STATUS_RUNNING,'updated_at'=>['<=',$finish_at]])->pluck('id');
if(!empty($ids)){
foreach ($ids as $id) {
Redis::lpush('ai_video_task', $id);
}
}
$task_id = Redis::rpop('ai_video_task');
}
return $task_id;
}
/**
* @remark :请求
* @name :sendRequest
* @author :lyh
* @method :post
* @time :2025/4/30 11:31
*/
public function _action($task_id){
$aiVideoTaskModel = new AiVideoTaskModel();
$item = $aiVideoTaskModel->read(['id'=>$task_id]);
$this->output('ai_video->start:project ID: ' . $item['project_id'] . ',task ID: ' . $task_id);
$aiVideoService = new AiVideoService($item['project_id']);
$aiVideoService->task_id = $item['task_id'];
//拉取文章数据
$result = $aiVideoService->getVideoDetail();
if(empty($result['status']) || ($result['status'] != 200)){
if($item['number'] < 5){
$aiVideoTaskModel->edit(['number'=>$item['number'] + 1],['id'=>$item['id']]);
}else{
$aiVideoTaskModel->edit(['status'=>9],['id'=>$item['id']]);
// 钉钉通知
$dingService = new DingService();
$body = [
'keyword' => 'AI_VIDEO获取失败',
'msg' => '任务ID:' . $item['task_id'] . PHP_EOL . '返回信息:' . json_encode($result,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
'isAtAll' => false, // 是否@所有人
];
$dingService->handle($body);
}
$this->output('error: 数据获取失败,status:' . $result['status'] . ',message: ' . ($result['message'] ?? 'null'));
return false;
}
//保存当前项目ai_blog数据
ProjectServer::useProject($item['project_id']);
$aiVideoModel = new AiVideo();
$aiVideoInfo = $aiVideoModel->read(['task_id'=>$item['task_id']],['id','route']);
if($aiVideoInfo === false){
// 钉钉通知
$dingService = new DingService();
$body = [
'keyword' => 'AI_VIDEO生成错误',
'msg' => '任务ID:' . $item['task_id'] . ', 子库获取数据失败, 检查子库数据是否被删除!',
'isAtAll' => false, // 是否@所有人
];
$dingService->handle($body);
$this->output('error: 子库获取数据失败, task id: ' . $task_id);
$aiVideoTaskModel->edit(['status'=>9],['id'=>$item['id']]);
DB::disconnect('custom_mysql');
return false;
}
//拿到返回的路由查看是否重复
$route = RouteMap::setRoute($result['data']['url'], RouteMap::SOURCE_AI_VIDEO, $aiVideoInfo['id'], $item['project_id']);
if($route != $result['data']['url']){
$aiVideoService->updateDetail(['route'=>$route,'task_id'=>$item['task_id']]);
}
$saveData = [
'title'=>$result['data']['title'],
'image'=>$result['data']['thumb'],
'video_url'=>$result['data']['video_url'],
'route'=>$route,
'author_id'=>$result['data']['author_id'],
'keyword'=>json_encode($result['data']['keyword'],true),
'content'=>$result['data']['content'],
'text'=>$result['data']['section'],
'status'=>$aiVideoTaskModel::STATUS_FINISH
];
$aiVideoModel->edit($saveData,['task_id'=>$item['task_id']]);
DB::disconnect('custom_mysql');
$aiVideoTaskModel->edit(['status'=>$aiVideoTaskModel::STATUS_FINISH],['id'=>$item['id']]);
$this->output('success: task id: ' . $task_id);
return true;
}
/**
* 输入日志
* @param $message
* @return bool
*/
public function output($message)
{
$message = date('Y-m-d H:i:s') . ' ' . $message . PHP_EOL;
echo $message;
return true;
}
}
... ...
... ... @@ -68,6 +68,7 @@ class AfterDayCount extends Command
$idArr = $this->managerHrModel->selectField(['name'=>['in',$valM]],'id');
$project_count = $projectModel->where('gl_project.extend_type',0)
->where('gl_project.delete_status',0)
->where('gl_project.old_project_id',0)
->where('gl_project.created_at','<=',$todayMidnight)
->whereIn('gl_project_deploy_optimize.optimist_mid',$idArr)
->whereIn('gl_project.type',[2,4])
... ... @@ -77,6 +78,7 @@ class AfterDayCount extends Command
->count();
$qualified_count = $projectModel->where('gl_project.extend_type',0)
->where('gl_project.delete_status',0)
->where('gl_project.old_project_id',0)
->where('gl_project.created_at','<=',$todayMidnight)
->where('gl_project.is_remain_today',1)
->where('gl_project_deploy_build.plan','!=',0)
... ... @@ -91,6 +93,7 @@ class AfterDayCount extends Command
$threeMonthsAgo = date('Y-m-d 00:00:00', strtotime('-3 months'));
$three_project_count = $projectModel->where('gl_project.extend_type',0)
->where('gl_project.delete_status',0)
->where('gl_project.old_project_id',0)
->where('gl_project.created_at','<=',$threeMonthsAgo)
->whereIn('gl_project_deploy_optimize.optimist_mid',$idArr)
->whereIn('gl_project.type',[2,4])
... ... @@ -101,6 +104,7 @@ class AfterDayCount extends Command
$three_qualified_count = $projectModel->where('gl_project.extend_type',0)
->whereIn('gl_project.id',$projectIdArr)
->where('gl_project.delete_status',0)
->where('gl_project.old_project_id',0)
->where('gl_project.created_at','<=',$threeMonthsAgo)
->whereIn('gl_project_deploy_optimize.optimist_mid',$idArr)
->whereIn('gl_project.type',[2,4])
... ...
... ... @@ -12,6 +12,7 @@ namespace App\Console\Commands\LyhTest;
use App\Helper\Arr;
use App\Helper\Translate;
use App\Models\Ai\AiBlog;
use App\Models\Ai\AiBlogAuthor;
use App\Models\Blog\Blog;
use App\Models\Com\WordCountry;
use App\Models\CustomModule\CustomModuleContent;
... ... @@ -67,7 +68,7 @@ class UpdateRoute extends Command
*/
public function handle()
{
return $this->getNullRoute();
return $this->getAiBlog();
}
/**
... ... @@ -77,16 +78,16 @@ class UpdateRoute extends Command
* @method :post
* @time :2025/4/21 11:52
*/
public function getNullRoute(){
public function getAiBlog(){
$projectModel = new Project();
$lists = $projectModel->list(['delete_status' => 0,'extend_type'=>0,'type'=>['in',[1,2,3,4]]], 'id', ['id']);
foreach ($lists as $val) {
echo date('Y-m-d H:i:s') . '开始--项目的id:'. $val['id'] . PHP_EOL;
ProjectServer::useProject($val['id']);
$keywordModel = new Keyword();
$info = $keywordModel->read(['route'=>'']);
if($info !== false){
echo '存在路由为空--项目id:'.$val['id'].PHP_EOL;
$aiBlogModel = new AiBlogAuthor();
$results = $aiBlogModel->whereColumn('title', '!=', 'seo_title')->get();
if(!$results){
echo '项目id:'.$val['id'].PHP_EOL;
}
DB::disconnect('custom_mysql');
}
... ... @@ -151,7 +152,7 @@ class UpdateRoute extends Command
* @method :post
* @time :2025/3/21 17:45
*/
public function getAiBlog($project_id){
public function getAiBlogs($project_id){
$aiBlogModel = new AiBlog();
$lists = $aiBlogModel->list(['updated_at'=>['<=','2025-03-21 00:00:00']]);
if(!empty($lists)){
... ...
... ... @@ -84,7 +84,11 @@ class CopyOldProject extends Command
'gl_customer_visit_item',
'gl_inquiry_other',
'gl_inquiry_form_data',
'gl_inquiry_form'
'gl_inquiry_form',
'gl_ai_blog',
'gl_ai_blog_author',
'gl_ai_blog_list',
'gl_ai_blog_log',
])) {
continue;
}
... ...
... ... @@ -245,7 +245,7 @@ class CopyProject extends Command
$result1 = DB::connection('custom_mysql')->statement(get_object_vars($sql[0])['Create Table']);
@file_put_contents(storage_path('logs/copy_mysql_error.log'), var_export('创建对应表数据:'.$result1, true) . PHP_EOL, FILE_APPEND);
// 3. 跳过指定的表
if (in_array($table, ['gl_customer_visit', 'gl_customer_visit_item', 'gl_inquiry_other', 'gl_inquiry_form_data', 'gl_inquiry_form'])) {
if (in_array($table, ['gl_customer_visit', 'gl_customer_visit_item', 'gl_inquiry_other', 'gl_inquiry_form_data', 'gl_inquiry_form','gl_ai_blog', 'gl_ai_blog_author', 'gl_ai_blog_list','gl_ai_blog_log'])) {
continue;
}
// 4. 原生 SQL 插入数据(完全复制)
... ...
... ... @@ -34,7 +34,7 @@ class UpdateSeoTdkCrontab extends Command
*/
public function handle()
{
$project_ids = Project::where('type', Project::TYPE_TWO)->pluck('id')->toArray();
$project_ids = Project::where('type', Project::TYPE_TWO)->where('uptime', '<=', date('Y-m-d H:i:s'))->pluck('id')->toArray();
foreach ($project_ids as $project_id){
try {
ProjectUpdateTdk::add_task($project_id);
... ...
... ... @@ -24,7 +24,7 @@ class AiVideoController extends BaseController
* @time :2025/3/5 14:12
*/
public function lists(AiVideo $aiVideo){
$lists = $aiVideo->lists($this->map,$this->page,$this->row,'id',['id','keyword','new_title','route','image','task_id','status','created_at','updated_at']);
$lists = $aiVideo->lists($this->map,$this->page,$this->row,'id',['id','title','route','video_url','image','task_id','status','created_at','updated_at']);
if(!empty($lists) && !empty($lists['list'])){
foreach ($lists['list'] as $k => $v){
$v['image'] = getImageUrl($v['image']);
... ... @@ -51,7 +51,6 @@ class AiVideoController extends BaseController
'id.required' => '主键不能为空',
]);
$info = $aiVideo->read(['id'=>$this->param['id']]);
$info['image'] = getImageUrl($info['image']);
$this->response('success',Code::SUCCESS,$info);
}
... ... @@ -64,11 +63,13 @@ class AiVideoController extends BaseController
*/
public function sendTask(AiVideoLogic $aiVideoLogic){
$this->request->validate([
'keyword'=>['required'],
'type'=>['required'],
'title'=>['required'],
'description'=>['required'],
'images'=>['required'],
],[
'keyword.required' => '关键字不能为空',
'type.required' => '场景不能为空',
'title.required' => '标题不能为空',
'description.required' => '短描述不能为空',
'images.required' => '图片集合不能为空',
]);
$result = $aiVideoLogic->sendTask();
$this->response('success',Code::SUCCESS,$result);
... ...
... ... @@ -28,6 +28,7 @@ use App\Models\Project\Project;
use App\Models\Project\ProjectAiSetting;
use App\Models\RouteMap\RouteMap;
use App\Services\AiBlogService;
use App\Services\AiVideoService;
use App\Services\RapIdApIService;
use App\Services\ProjectServer;
use Illuminate\Support\Facades\DB;
... ... @@ -42,8 +43,16 @@ class TestController extends BaseController
* @time :2025/2/13 16:34
*/
public function ceshi(){
//获取上一周询盘数量
$transData = Translate::tran(['heidenhain programming','heidenhain tnc 620'], 'zh');
$this->response('success',Code::SUCCESS,$transData);
$this->param = [
'title'=>'apple',
'description'=>'apples',
'images'=>[
['url'=>'https://ecdn6.globalso.com/upload/public/template/64e332671b32e25328.png','title'=>'apple'],
['url'=>'https://ecdn6.globalso.com/upload/public/template/64e32a24b314a39425.png','title'=>'apples'],
],
];
$aiVideoService = new AiVideoService(467);
$result = $aiVideoService->createTask($this->param['title'],$this->param['description'],$this->param['images'],$this->param['anchor'] ?? []);
$this->response('success',Code::SUCCESS,$result);
}
}
... ...
... ... @@ -268,12 +268,13 @@ class ProjectLogic extends BaseLogic
public function checkAiBlog($param){
$main_lang_id = $param['main_lang_id'] ?? 0;
$is_ai_blog = $param['is_ai_blog'] ?? 0;
$is_ai_video = $param['is_ai_video'] ?? 0;
$company = $param['company'] ?? '';
$company_en_name = $param['deploy_optimize']['company_en_name'] ?? '';
$company_en_description = $param['deploy_optimize']['company_en_description'] ?? '';
if($is_ai_blog == 1){
if($is_ai_blog == 1 || $is_ai_video){
if(empty($main_lang_id) || empty($company) || empty($company_en_name) || empty($company_en_description)){
$this->fail('开启ai_blog--请填写主语种+公司名称+公司英文名称+公司英文介绍');
$this->fail('开启ai博客/视频功能--请填写主语种+公司名称+公司英文名称+公司英文介绍');
}
}
return true;
... ...
... ... @@ -7,9 +7,11 @@ use App\Http\Logic\Bside\BaseLogic;
use App\Models\Ai\AiBlogAuthor;
use App\Models\Ai\AiVideo;
use App\Models\Project\AiBlogTask;
use App\Models\Project\AiVideoTask;
use App\Models\Project\ProjectAiSetting;
use App\Models\RouteMap\RouteMap;
use App\Services\AiBlogService;
use App\Services\AiVideoService;
/**
* @remark :视频模块
... ... @@ -52,10 +54,7 @@ class AiVideoLogic extends BaseLogic
*/
public function videoSave(){
try {
if(!empty($this->param['image'])){
$this->param['image'] = str_replace_url($this->param['image']);
}
$this->param['route'] = RouteMap::setRoute($this->param['route'], RouteMap::SOURCE_AI_VIDEO, $this->param['id'], $this->user['project_id']);
$this->param['route'] = RouteMap::setRoute($this->param['tit'], RouteMap::SOURCE_AI_VIDEO, $this->param['id'], $this->user['project_id']);
$this->model->edit($this->param,['id'=>$this->param['id']]);
$aiSettingInfo = $this->getProjectAiSetting();
$aiBlogService = new AiBlogService();
... ... @@ -74,20 +73,16 @@ class AiVideoLogic extends BaseLogic
* @author :lyh
* @method :post
* @time :2025/2/14 10:28
* @detail :createTask =>type=2/生成文章
* @detail :status=1/待执行
*/
public function sendTask(){
$aiSettingInfo = $this->getProjectAiSetting();
$aiBlogService = new AiBlogService();
$aiBlogService->mch_id = $aiSettingInfo['mch_id'];
$aiBlogService->key = $aiSettingInfo['key'];
$aiBlogService->route = generateRoute(Translate::tran($this->param['keyword'], 'en'));
$result = $aiBlogService->createTask($this->param['keyword'],2,'video',$this->param['anchor'] ?? []);
$aiVideoService = new AiVideoService($this->user['project_id']);
$result = $aiVideoService->createTask($this->param['title'],$this->param['description'],$this->param['images'],$this->param['anchor'] ?? []);
if($result['status'] == 200){
$aiBlogTaskModel = new AiBlogTask();
$aiBlogTaskModel->addReturnId(['project_id'=>$this->user['project_id'],'type'=>3,'task_id'=>$result['data']['task_id'],'status'=>1]);
$this->model->addReturnId(['keyword'=>$this->param['keyword'],'status'=>1,'task_id'=>$result['data']['task_id'],'project_id'=>$this->user['project_id'],'anchor'=>json_encode($this->param['anchor'] ?? [],true)]);
$aiVideoTaskModel = new AiVideoTask();
$aiVideoTaskModel->addReturnId(['task_id'=>$result['data']['task_id'],'project_id'=>$this->user['project_id']]);
$id = $this->model->addReturnId(['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)]);
return $this->success(['id'=>$id]);
}
return $this->success();
}
... ...
... ... @@ -21,4 +21,6 @@ use App\Models\Base;
class AiVideo extends Base
{
protected $table = 'gl_ai_video';
//连接数据库
protected $connection = 'custom_mysql';
}
... ...
<?php
/**
* @remark :
* @name :AiVideoTask.php
* @author :lyh
* @method :post
* @time :2025/4/30 9:41
*/
namespace App\Models\Project;
use App\Models\Base;
class AiVideoTask extends Base
{
protected $table = 'gl_ai_video_task';
/**
* 任务状态
*/
const STATUS_RUNNING = 1;
const STATUS_FINISH = 2;
}
... ...
<?php
/**
* @remark :
* @name :AiVideoService.php
* @author :lyh
* @method :post
* @time :2025/4/29 17:39
*/
namespace App\Services;
use App\Helper\Translate;
use App\Models\Project\ProjectAiSetting;
class AiVideoService
{
public $url = 'https://ai-extend.ai.cc/';
public $mch_id = 1;//默认配置
public $sign = '';//签名
public $key = 'b3e4c722b821';//默认key
public $route = '';//回调地址
public $task_id = '';//任务id
public $author_id = '';//作者id
public function __construct($project_id = 0)
{
if($project_id){
$projectAiSettingModel = new ProjectAiSetting();
$aiSettingInfo = $projectAiSettingModel->read(['project_id'=>$project_id]);
$this->mch_id = $aiSettingInfo['mch_id'];
$this->key = $aiSettingInfo['key'];
}
}
/**
* @remark :设置路由
* @name :setRoute
* @author :lyh
* @method :post
* @time :2025/3/25 9:45
*/
public function setRoute($keyword)
{
$this->route = generateRoute(Translate::tran($keyword, 'en'));
return $this;
}
/**
* @remark :创建任务
* @name :createTask
* @author :lyh
* @method :post
* @time :2025/4/29 17:59
*/
public function createTask($title,$description,$images = [],$anchor = []){
$request_url = $this->url.'api/video/create';
$param = ['title'=>$title, 'description'=>$description, 'images'=>$images,'anchor'=>$anchor];
$param['mch_id'] = $this->mch_id;
$this->sign = $this->generateSign($param,$this->key);
$param['sign'] = $this->sign;
return http_post($request_url,json_encode($param,true));
}
/**
* @remark :获取视频详情
* @name :getVideoDetail
* @author :lyh
* @method :post
* @time :2025/2/14 11:23
*/
public function getVideoDetail(){
$request_url = $this->url.'api/video/detail';
$param = [
'mch_id'=>$this->mch_id,
'task_id'=>$this->task_id,
];
$this->sign = $this->generateSign($param,$this->key);
$param['sign'] = $this->sign;
$result = http_post($request_url,json_encode($param,true));
return $result;
}
/**
* @remark :更新文章
* @name :updateDetail
* @author :lyh
* @method :post
* @time :2025/2/21 14:38
* @param :task_id , title , video_url ,thumb , content , author_id , url ,
*/
public function updateDetail($param){
$request_url = $this->url.'api/video/save';
$param['mch_id'] = $this->mch_id;
$this->sign = $this->generateSign($param,$this->key);
$param['sign'] = $this->sign;
$result = http_post($request_url,json_encode($param,true));
return $result;
}
/**
* @remark :计算签名
* @name :generateSign
* @author :lyh
* @method :post
* @time :2025/2/13 15:07
*/
public function generateSign($params, $key)
{
// 去除数组中所有值为空的项
array_filter($params);
// 按照key值的ASCII码从小到大排序
ksort($params);
// 生成URL的查询字符串
$string = http_build_query($params);
// 生成签名
$sign = md5($string . $key);
// 转换成大写
$sign = strtoupper($sign);
return $sign;
}
}
... ...