作者 刘锟

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

@@ -13,6 +13,7 @@ use App\Helper\Arr; @@ -13,6 +13,7 @@ use App\Helper\Arr;
13 use App\Models\Ai\AiBlog; 13 use App\Models\Ai\AiBlog;
14 use App\Models\Ai\AiBlogAuthor; 14 use App\Models\Ai\AiBlogAuthor;
15 use App\Models\Ai\AiBlogList; 15 use App\Models\Ai\AiBlogList;
  16 +use App\Models\Com\NoticeLog;
16 use App\Models\Com\Notify; 17 use App\Models\Com\Notify;
17 use App\Models\Devops\ServerConfig; 18 use App\Models\Devops\ServerConfig;
18 use App\Models\Devops\ServersIp; 19 use App\Models\Devops\ServersIp;
@@ -337,6 +338,9 @@ class AiBlogTask extends Command @@ -337,6 +338,9 @@ class AiBlogTask extends Command
337 'is_sitemap' => 0 338 'is_sitemap' => 0
338 ]; 339 ];
339 $res = http_post($c_url, json_encode($param,true)); 340 $res = http_post($c_url, json_encode($param,true));
  341 + if(empty($res)){
  342 + NoticeLog::createLog(NoticeLog::GENERATE_PAGE, json_encode(['c_url'=>$c_url,'c_params'=>$param]),date('Y-m-d H:i:s',time()+300));
  343 + }
340 $this->output('notify: project id: ' . $project_id . ', result: ' . json_encode($res,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); 344 $this->output('notify: project id: ' . $project_id . ', result: ' . json_encode($res,JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
341 } 345 }
342 } 346 }
@@ -10,12 +10,15 @@ @@ -10,12 +10,15 @@
10 namespace App\Console\Commands\LyhTest; 10 namespace App\Console\Commands\LyhTest;
11 11
12 use App\Helper\Common; 12 use App\Helper\Common;
  13 +use App\Models\Blog\Blog;
13 use App\Models\Com\V6WeeklyReport; 14 use App\Models\Com\V6WeeklyReport;
  15 +use App\Models\News\News;
14 use App\Models\Product\Category; 16 use App\Models\Product\Category;
15 use App\Models\Product\CategoryRelated; 17 use App\Models\Product\CategoryRelated;
16 use App\Models\Product\Product; 18 use App\Models\Product\Product;
17 use App\Models\ProjectAssociation\ProjectAssociation; 19 use App\Models\ProjectAssociation\ProjectAssociation;
18 use App\Models\RouteMap\RouteMap; 20 use App\Models\RouteMap\RouteMap;
  21 +use App\Models\Template\BTemplate;
19 use App\Models\Visit\Visit; 22 use App\Models\Visit\Visit;
20 use App\Models\Visit\VisitItem; 23 use App\Models\Visit\VisitItem;
21 use App\Models\Workchat\MessagePush; 24 use App\Models\Workchat\MessagePush;
@@ -43,16 +46,51 @@ class DownloadProject extends Command @@ -43,16 +46,51 @@ class DownloadProject extends Command
43 46
44 public function handle(){ 47 public function handle(){
45 echo date('Y-m-d H:i:s') . 'start' . PHP_EOL; 48 echo date('Y-m-d H:i:s') . 'start' . PHP_EOL;
46 - ProjectServer::useProject(535);  
47 - $this->model = new Visit();  
48 - $data = $this->importVisit();  
49 - dd($data); 49 + ProjectServer::useProject(671);
  50 + $this->newsImportBlog();
50 DB::disconnect('custom_mysql'); 51 DB::disconnect('custom_mysql');
51 echo date('Y-m-d H:i:s') . 'end' . PHP_EOL; 52 echo date('Y-m-d H:i:s') . 'end' . PHP_EOL;
52 return true; 53 return true;
53 } 54 }
54 55
55 /** 56 /**
  57 + * @remark :新闻导入到博客
  58 + * @name :newsImportBlog
  59 + * @author :lyh
  60 + * @method :post
  61 + * @time :2025/6/7 11:12
  62 + */
  63 + public function newsImportBlog(){
  64 + $newsModel = new News();
  65 + $newsLists = $newsModel->list(['status'=>1]);
  66 + $blogModel = new Blog();
  67 + foreach ($newsLists as $item){
  68 + $data = [
  69 + 'name'=>$item['name'],
  70 + 'status'=>1,
  71 + 'remark'=>$item['remark'],
  72 + 'text'=>$item['text'],
  73 + 'image'=>$item['image'],
  74 + 'url'=>$item['url'],
  75 + 'seo_title'=>$item['seo_title'],
  76 + 'seo_description'=>$item['seo_title'],
  77 + 'seo_keywords'=>$item['seo_title'],
  78 + 'project_id'=>671,
  79 + 'release_at'=>$item['release_at'],
  80 + 'og_image'=>$item['og_image']
  81 + ];
  82 + $id = $blogModel->addReturnId($data);
  83 + RouteMap::delRoute(RouteMap::SOURCE_NEWS,$item['id'],671);
  84 + $route = RouteMap::setRoute($item['url'],RouteMap::SOURCE_BLOG,$id,671);
  85 + $blogModel->edit(['url'=>$route],['id'=>$id]);
  86 + //更新当前的可视化归宿
  87 + $templateModel = new BTemplate();
  88 + $templateModel->edit(['source'=>3,'source_id'=>$id],['source'=>4,'source_id'=>$item['id'],'is_custom'=>0,'is_list'=>0]);
  89 + }
  90 + return true;
  91 + }
  92 +
  93 + /**
56 * @remark :导出明细 94 * @remark :导出明细
57 * @name :importVisit 95 * @name :importVisit
58 * @author :lyh 96 * @author :lyh
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 namespace App\Console\Commands\LyhTest; 10 namespace App\Console\Commands\LyhTest;
11 11
12 use App\Console\Commands\Domain\DomainInfo; 12 use App\Console\Commands\Domain\DomainInfo;
  13 +use App\Helper\OaGlobalsoApi;
13 use App\Http\Logic\Aside\Project\ProjectLogic; 14 use App\Http\Logic\Aside\Project\ProjectLogic;
14 use App\Models\Ai\AiBlog; 15 use App\Models\Ai\AiBlog;
15 use App\Models\Ai\AiBlogAuthor; 16 use App\Models\Ai\AiBlogAuthor;
@@ -57,26 +58,28 @@ class lyhDemo extends Command @@ -57,26 +58,28 @@ class lyhDemo extends Command
57 protected $description = '更新路由'; 58 protected $description = '更新路由';
58 59
59 public function handle(){ 60 public function handle(){
60 - $str = '3671,955,752,1270,439,2674,3588,2388,1271,1543,738,624,552,1417,1237,651,1143,817,1556,1234,1350,650,538,491,631,2059,1845,866,1194,1699,546,684,905,1805,1728,2811,952,2972,2827,983,812,3081,554,741,1349,980';  
61 - $arr = explode(',',$str);  
62 - $model = new TranslateBigProject();  
63 - foreach ($arr as $val){  
64 - $model->addReturnId(['project_id'=>$val]); 61 + $projectModel = new Project();
  62 + $buildModel = new DeployBuild();
  63 + $lists = $projectModel->list(['delete_status' => 0,'project_type'=>0,'is_upgrade'=>0,'id'=>['<',1659],'extend_type'=>0,'type'=>['in',[1,2,3,4,6]]], 'id', ['id','notice_order_id']);
  64 + foreach ($lists as $item){
  65 + echo date('Y-m-d H:i:s') . '开始--项目的id:'. $item['id'] . PHP_EOL;
  66 + try {
  67 + if(!empty($item['notice_order_id'])){
  68 + $api = new OaGlobalsoApi();
  69 + $data = $api->order_info($item['notice_order_id']);
  70 + if(!empty($data)){
  71 + if(isset($data['data']['ads_price'])){
  72 + echo '奖励金额:'.$data['data']['ads_price'].PHP_EOL;
  73 + $buildModel->edit(['ads_price'=>$data['data']['ads_price'] ?? 0],['project_id'=>$item['id']]);
  74 + }
  75 + }
  76 + }
  77 + }catch (\Exception $e){
  78 + continue;
  79 + }
  80 + DB::disconnect('custom_mysql');
65 } 81 }
66 - return true;  
67 -// $projectModel = new Project();  
68 -// $lists = $projectModel->list(['delete_status' => 0,'project_type'=>0,'extend_type'=>0,'type'=>['in',[1,2,3,4,6]]], 'id', ['id']);  
69 -// foreach ($lists as $item){  
70 -//// echo date('Y-m-d H:i:s') . '开始--项目的id:'. $item['id'] . PHP_EOL;  
71 -// ProjectServer::useProject($item['id']);  
72 -// $webSettingModel = new WebSetting();  
73 -// $info = $webSettingModel->read(['project_id'=>$item['id']]);  
74 -// if($info === false){  
75 -// $webSettingModel->addReturnId(['project_id'=>$item['id']]);  
76 -// echo '当前数据为空:'.$item['id'].PHP_EOL;  
77 -// }  
78 -// DB::disconnect('custom_mysql');  
79 -// } 82 + echo date('Y-m-d H:i:s') . '结束。。。' . PHP_EOL;
80 } 83 }
81 84
82 public function _actionTemplateMain(){ 85 public function _actionTemplateMain(){
@@ -227,7 +227,8 @@ class SyncProject extends Command @@ -227,7 +227,8 @@ class SyncProject extends Command
227 'version'=>$version 227 'version'=>$version
228 ], 228 ],
229 'deploy_build' => [ 229 'deploy_build' => [
230 - 'login_mobile'=>$param['principal_mobile'] 230 + 'login_mobile'=>$param['principal_mobile'],
  231 + 'ads_price'=>$param['ads_price'] ?? 0
231 ], 232 ],
232 'deploy_optimize' => [ 233 'deploy_optimize' => [
233 // 'api_no' => 0 234 // 'api_no' => 0
  1 +<?php
  2 +
  3 +namespace App\Console\Commands\Tdk;
  4 +
  5 +use App\Exceptions\ValidateException;
  6 +use App\Helper\Arr;
  7 +use App\Helper\Gpt;
  8 +use App\Models\Ai\AiCommand;
  9 +use App\Models\Com\NoticeLog;
  10 +use App\Models\Domain\DomainInfo;
  11 +use App\Models\Product\Keyword;
  12 +use App\Models\Project\AggregateKeywordAffix;
  13 +use App\Models\Project\DeployBuild;
  14 +use App\Models\Project\DeployOptimize;
  15 +use App\Models\Project\ProjectKeywordAiTask;
  16 +use App\Services\ProjectServer;
  17 +use Illuminate\Console\Command;
  18 +use Illuminate\Support\Facades\DB;
  19 +use Illuminate\Support\Facades\Redis;
  20 +
  21 +/**
  22 + * 关键词聚合页AI生成内容
  23 + * Class InitProject
  24 + * @package App\Console\Commands
  25 + * @author zbj
  26 + * @date 2025/06/06
  27 + */
  28 +class KeywordPageAiContent extends Command
  29 +{
  30 + /**
  31 + * The name and signature of the console command.
  32 + *
  33 + * @var string
  34 + */
  35 + protected $signature = 'keyword_page_ai_content';
  36 +
  37 + /**
  38 + * The console command description.
  39 + *
  40 + * @var string
  41 + */
  42 + protected $description = '关键词聚合页AI生成内容';
  43 +
  44 + /**
  45 + * 统计图表类型 随机一个
  46 + * @var string[]
  47 + */
  48 + protected $chart_types = [
  49 + '柱状图',
  50 + '折线图',
  51 + ];
  52 +
  53 + /**
  54 + * @return bool
  55 + */
  56 + public function handle()
  57 + {
  58 + while (true) {
  59 + $task = ProjectKeywordAiTask::getPendingTask();
  60 + if (!$task) {
  61 + sleep(10);
  62 + continue;
  63 + }
  64 + $project_id = $task->project_id;
  65 +
  66 + echo getmypid() . ' ' . date('Y-m-d H:i:s') . ' start project_id: ' . $project_id . PHP_EOL;
  67 + try {
  68 + ProjectServer::useProject($project_id);
  69 +
  70 + $update_rows = $this->ai_content($task);
  71 +
  72 + DB::disconnect('custom_mysql');
  73 +
  74 + ProjectKeywordAiTask::finish($task->id, $update_rows);
  75 +
  76 + // $update_rows && $this->sendNotify($project_id);
  77 +
  78 + } catch (ValidateException $e) {
  79 + echo getmypid() . ' ' . date('Y-m-d H:i:s') . 'line: ' . $e->getLine() . ' error: ' . $project_id . '->' . $e->getMessage() . PHP_EOL;
  80 + $task->status = ProjectKeywordAiTask::STATUS_FAIL;
  81 + $task->remark = mb_substr($e->getMessage(), 0, 250);
  82 + $task->save();
  83 + } catch (\Exception $e) {
  84 + echo getmypid() . ' ' . date('Y-m-d H:i:s') . 'line: ' . $e->getLine() . ' error: ' . $project_id . '->' . $e->getMessage() . PHP_EOL;
  85 + ProjectKeywordAiTask::retry($task->id, $e->getMessage());
  86 + }
  87 + echo getmypid() . ' ' . date('Y-m-d H:i:s') . ' end project_id: ' . $project_id . PHP_EOL;
  88 + }
  89 + }
  90 +
  91 +
  92 + /**
  93 + * @param ProjectKeywordAiTask $task
  94 + * @author zbj
  95 + * @date 2025/6/6
  96 + */
  97 + public function ai_content(ProjectKeywordAiTask $task)
  98 + {
  99 + //前后缀
  100 + $affix = AggregateKeywordAffix::where('project_id', $task->project_id)->first();
  101 + $prefix = empty($affix['prefix']) ? [] : explode("\r\n", $affix['prefix']);
  102 + $suffix = empty($affix['suffix']) ? [] : explode("\r\n", $affix['suffix']);
  103 + if (!$prefix || !$suffix) {
  104 + throw new ValidateException('扩展标题前后缀不存在');
  105 + }
  106 + //公司英文描述
  107 + $company_en_description = DeployOptimize::where('project_id', $task->project_id)->value('company_en_description');
  108 + if (!$company_en_description) {
  109 + throw new ValidateException('公司英文描述不存在');
  110 + }
  111 + //指令
  112 + $ai_commands = AiCommand::whereIn('key', ['tag_sale_content', 'tag_count_content', 'tag_data_table'])->where('project_id', 0)->select('key', 'scene', 'ai')->get()->toArray();
  113 + $project_ai_commands = AiCommand::whereIn('key', ['tag_sale_content', 'tag_count_content', 'tag_data_table'])->where('project_id', $task->project_id)->select('key', 'scene', 'ai')->get()->toArray();
  114 + $ai_commands = Arr::setValueToKey($ai_commands, 'key');
  115 + $project_ai_commands = Arr::setValueToKey($project_ai_commands, 'key');
  116 + foreach ($ai_commands as $k => $ai_command) {
  117 + if (!empty($project_ai_commands[$k])) {
  118 + $ai_commands[$k] = $project_ai_commands[$k];
  119 + }
  120 + }
  121 +
  122 + //没有标题、文案、图表的关键词
  123 + $keyword_ids = Keyword::whereNull('sale_title')->orWhereNull('sale_content')->orWhereNull('table_html')
  124 + ->orWhereNull('count_title')->orWhereNull('count_html')
  125 + ->pluck('id')
  126 + ->toArray();
  127 + $update_rows = 0;
  128 + foreach ($keyword_ids as $id) {
  129 + //缓存 在处理的项目数据 id
  130 + $cache_key = "keyword_page_ai_content_task_lock_{$task->project_id}_{$id}";
  131 + if (!Redis::setnx($cache_key, 1)) {
  132 + continue;
  133 + }
  134 + Redis::expire($cache_key, 120);
  135 +
  136 + $keyword = Keyword::where('id', $id)->select(['id', 'title', 'sale_title', 'sale_content', 'table_html', 'count_title', 'count_html'])->first();
  137 +
  138 + echo getmypid() . ' ' . date('Y-m-d H:i:s') . ' id:' . $keyword['id'] . ' project_id:' . $task->project_id . PHP_EOL;
  139 +
  140 + $update = false;
  141 + if (empty($keyword['sale_title'])) {
  142 + $sale_title = $this->new_title($keyword['title'], $prefix, $suffix);
  143 + $keyword->sale_title = $sale_title;
  144 + $update = true;
  145 + }
  146 + if (empty($keyword['sale_content']) && $keyword->sale_title) {
  147 + $content = $this->ai_send($keyword->sale_title, $company_en_description, $ai_commands['tag_sale_content']['ai']);
  148 + if ($content) {
  149 + $keyword->sale_content = $content;
  150 + $update = true;
  151 + }
  152 + }
  153 + if (empty($keyword['table_html']) && $keyword->sale_title) {
  154 + $content = $this->ai_send($keyword->sale_title, $company_en_description, $ai_commands['tag_data_table']['ai']);
  155 + if ($content) {
  156 + $keyword->table_html = str_replace(['```html', '``html', '```'], '', $content);
  157 + $update = true;
  158 + }
  159 + }
  160 + if (empty($keyword['count_title'])) {
  161 + $count_title = $this->new_title($keyword['title'], $prefix, $suffix);
  162 + $count_title && $keyword->count_title = $count_title;
  163 + $update = true;
  164 + }
  165 + if (empty($keyword['count_html']) && $keyword->sale_title) {
  166 + $content = $this->ai_send($keyword->sale_title, $company_en_description, $ai_commands['tag_count_content']['ai']);
  167 + if ($content) {
  168 + $keyword->count_html = $this->fixChart(str_replace(['```html', '``html', '```'], '', $content));
  169 + $update = true;
  170 + }
  171 + }
  172 + if ($update) {
  173 + $keyword->save();
  174 + $update_rows++;
  175 + }
  176 + }
  177 + return $update_rows;
  178 + }
  179 +
  180 + public function new_title($title, $prefix, $suffix)
  181 + {
  182 + //打乱顺序
  183 + shuffle($prefix);
  184 + shuffle($suffix);
  185 + //标题(title):{聚合页扩展标题前缀} keywords {聚合页扩展标题后缀} {聚合页扩展标题后缀}
  186 +
  187 + return sprintf('%s %s %s %s', $prefix[0], $title, $suffix[0], $suffix[1]);
  188 + }
  189 +
  190 +
  191 + public function ai_send($title, $company_description, $prompt)
  192 + {
  193 + if (strpos($prompt, '{title}') !== false) {
  194 + $prompt = str_replace('{title}', $title, $prompt);
  195 + }
  196 + if (strpos($prompt, '{company introduction}') !== false) {
  197 + $prompt = str_replace('{company introduction}', $company_description, $prompt);
  198 + }
  199 + if (strpos($prompt, '{chart_type}') !== false) {
  200 + shuffle($this->chart_types);
  201 + $prompt = str_replace('{chart_type}', $this->chart_types[0], $prompt);
  202 + }
  203 +
  204 + $text = Gpt::instance()->openai_chat_qqs($prompt);
  205 + if (!$text) {
  206 + echo getmypid() . ' ' . '生成失败' . PHP_EOL;
  207 + }
  208 + return $text;
  209 + }
  210 +
  211 + function fixChart($html)
  212 + {
  213 + $html = '<body>' . $html . '</body>';
  214 + $dom = new \DOMDocument();
  215 + @$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
  216 + $canvas_count = $dom->getElementsByTagName('canvas')->count();
  217 + //没有canvas
  218 + if (!$canvas_count) {
  219 + $div = $dom->getElementsByTagName('div');
  220 + foreach ($div as $element) {
  221 + if ($element->hasAttribute('id')) {
  222 + $canvas = $dom->createElement('canvas');
  223 + $canvas->setAttribute('id', $element->getAttribute('id'));
  224 + $element->removeAttribute('id');
  225 + $element->appendChild($canvas);
  226 + break;
  227 + }
  228 + }
  229 + }
  230 + $body = $dom->getElementsByTagName('body')->item(0);
  231 + $modifiedHtml = '';
  232 + foreach ($body->childNodes as $child) {
  233 + $modifiedHtml .= $dom->saveHTML($child);
  234 + }
  235 + return $modifiedHtml;
  236 + }
  237 +
  238 + public function sendNotify($project_id)
  239 + {
  240 + //获取当前项目的域名
  241 + $domainModel = new DomainInfo();
  242 + $domainInfo = $domainModel->read(['project_id' => $project_id]);
  243 + if ($domainInfo === false) {
  244 + //获取测试域名
  245 + $deployBuildModel = new DeployBuild();
  246 + $buildInfo = $deployBuildModel->read(['project_id' => $project_id]);
  247 + $domain = $buildInfo['test_domain'];
  248 + } else {
  249 + $domain = 'https://' . $domainInfo['domain'] . '/';
  250 + }
  251 + $url = $domain . 'api/update_page/';
  252 + $param = [
  253 + 'project_id' => $project_id,
  254 + 'type' => 1,
  255 + 'route' => 2,
  256 + 'url' => [],
  257 + 'language' => [],
  258 + ];
  259 + NoticeLog::createLog(NoticeLog::GENERATE_PAGE, json_encode(['c_url' => $url, 'c_params' => $param]), date('Y-m-d H:i:s', time() + 300));
  260 + echo getmypid() . ' ' . '更新中请稍后, 更新完成将会发送站内信通知更新结果!' . PHP_EOL;
  261 + }
  262 +}
  1 +<?php
  2 +
  3 +namespace App\Exceptions;
  4 +
  5 +use Exception;
  6 +
  7 +/**
  8 + * @notes: 验证
  9 + * Class ValidateException
  10 + * @package App\Exceptions
  11 + */
  12 +class ValidateException extends Exception
  13 +{
  14 +
  15 +}
@@ -31,6 +31,19 @@ class HrController extends BaseController @@ -31,6 +31,19 @@ class HrController extends BaseController
31 } 31 }
32 32
33 /** 33 /**
  34 + * @remark :获取列表数据
  35 + * @name :getManagerList
  36 + * @author :lyh
  37 + * @method :post
  38 + * @time :2025/6/7 9:22
  39 + */
  40 + public function getManagerList(){
  41 + $manageHrModel = new ManageHr();
  42 + $lists = $manageHrModel->lists($this->map,$this->page,$this->row);
  43 + $this->response('success', Code::SUCCESS, $lists);
  44 + }
  45 +
  46 + /**
34 * @remark :获取详情 47 * @remark :获取详情
35 * @name :info 48 * @name :info
36 * @author :lyh 49 * @author :lyh
@@ -3,13 +3,17 @@ @@ -3,13 +3,17 @@
3 namespace App\Http\Controllers\Bside\Product; 3 namespace App\Http\Controllers\Bside\Product;
4 4
5 use App\Enums\Common\Code; 5 use App\Enums\Common\Code;
  6 +use App\Helper\Common;
  7 +use App\Helper\Gpt;
6 use App\Http\Controllers\Bside\BaseController; 8 use App\Http\Controllers\Bside\BaseController;
7 use App\Http\Logic\Bside\Product\KeywordLogic; 9 use App\Http\Logic\Bside\Product\KeywordLogic;
8 use App\Http\Requests\Bside\Product\KeywordRequest; 10 use App\Http\Requests\Bside\Product\KeywordRequest;
  11 +use App\Models\Ai\AiCommand;
9 use App\Models\Product\Keyword; 12 use App\Models\Product\Keyword;
10 use App\Models\Product\KeywordPage; 13 use App\Models\Product\KeywordPage;
11 use App\Models\Product\KeywordRelated; 14 use App\Models\Product\KeywordRelated;
12 use App\Models\Product\Product; 15 use App\Models\Product\Product;
  16 +use App\Models\Project\AggregateKeywordComment;
13 use App\Rules\Ids; 17 use App\Rules\Ids;
14 use Illuminate\Http\Request; 18 use Illuminate\Http\Request;
15 19
@@ -312,4 +316,70 @@ class KeywordController extends BaseController @@ -312,4 +316,70 @@ class KeywordController extends BaseController
312 $logic->delAllRelated($this->param['keyword_id']); 316 $logic->delAllRelated($this->param['keyword_id']);
313 $this->response('success'); 317 $this->response('success');
314 } 318 }
  319 +
  320 + /**
  321 + * @remark :添加评论
  322 + * @name :saveComment
  323 + * @author :lyh
  324 + * @method :post
  325 + * @time :2025/6/9 14:27
  326 + */
  327 + public function saveComment(KeywordLogic $logic){
  328 + $this->request->validate([
  329 + 'text'=>'required',
  330 + 'nickname'=>'required',
  331 + ],[
  332 + 'text.required' => '评论内容不能为空',
  333 + 'nickname.required'=>'昵称不能为空',
  334 + ]);
  335 + $data = $logic->saveComment();
  336 + $this->response('success',Code::SUCCESS,$data);
  337 + }
  338 +
  339 + /**
  340 + * @remark :生成评论
  341 + * @name :sendComment
  342 + * @author :lyh
  343 + * @method :post
  344 + * @time :2025/6/9 11:10
  345 + */
  346 + public function sendComment(KeywordLogic $logic){
  347 + $this->request->validate([
  348 + 'count'=>'required|max:10',
  349 + ],[
  350 + 'count.required' => '生成条数不能为空',
  351 + 'count.max'=>'count最大10',
  352 + ]);
  353 + $data = $logic->sendComment();
  354 + $this->response('success',Code::SUCCESS,$data);
  355 + }
  356 +
  357 + /**
  358 + * @remark :获取评论
  359 + * @name :getComment
  360 + * @author :lyh
  361 + * @method :post
  362 + * @time :2025/6/9 11:45
  363 + */
  364 + public function getComment(KeywordLogic $logic){
  365 + $data = $logic->getComment($this->map,$this->page,$this->row);
  366 + $this->response('success',Code::SUCCESS,$data);
  367 + }
  368 +
  369 + /**
  370 + * @remark :删除评论
  371 + * @name :getComment
  372 + * @author :lyh
  373 + * @method :post
  374 + * @time :2025/6/9 11:45
  375 + */
  376 + public function delComment(KeywordLogic $logic){
  377 + $this->request->validate([
  378 + 'id'=>'required',
  379 + ],[
  380 + 'id.required' => '主键不能为空',
  381 + ]);
  382 + $data = $logic->delComment();
  383 + $this->response('success',Code::SUCCESS,$data);
  384 + }
315 } 385 }
@@ -81,12 +81,14 @@ class RenewLogic extends BaseLogic @@ -81,12 +81,14 @@ class RenewLogic extends BaseLogic
81 } 81 }
82 DB::beginTransaction(); 82 DB::beginTransaction();
83 try { 83 try {
84 - $this->model->edit(['project_id'=>$this->param['id'],'operator_id'=>$this->manager['id']],['id'=>$this->param['renew_id']]); 84 + if($this->param['renew_id'] != 0){
  85 + $this->model->edit(['project_id'=>$this->param['id'],'operator_id'=>$this->manager['id']],['id'=>$this->param['renew_id']]);
  86 + }
85 $param = $this->param; 87 $param = $this->param;
86 $param['api_no'] = $info['api_no'] ?? 0; 88 $param['api_no'] = $info['api_no'] ?? 0;
87 $this->saveLog($param); 89 $this->saveLog($param);
88 $this->updateProject($this->param['id'],$this->param['type']); 90 $this->updateProject($this->param['id'],$this->param['type']);
89 - $this->updateProjectBuild($this->param['id'],$this->param['service_duration'],$this->param['plan']); 91 + $this->updateProjectBuild($this->param['id'],$this->param['service_duration'] ?? 0,$this->param['plan']);
90 DB::commit(); 92 DB::commit();
91 }catch (\Exception $e){ 93 }catch (\Exception $e){
92 DB::rollBack(); 94 DB::rollBack();
@@ -120,12 +122,12 @@ class RenewLogic extends BaseLogic @@ -120,12 +122,12 @@ class RenewLogic extends BaseLogic
120 public function saveLog($param){ 122 public function saveLog($param){
121 $data = [ 123 $data = [
122 'renew_id'=>$param['renew_id'], 124 'renew_id'=>$param['renew_id'],
123 - 'service_duration'=>$param['service_duration'], 125 + 'service_duration'=>$param['service_duration'] ?? 0,
124 'plan'=>$param['plan'], 126 'plan'=>$param['plan'],
125 'old_plan'=>$param['old_plan'], 127 'old_plan'=>$param['old_plan'],
126 'type'=>$param['type'], 128 'type'=>$param['type'],
127 'old_type'=>$param['old_type'], 129 'old_type'=>$param['old_type'],
128 - 'amount'=>$param['amount'], 130 + 'amount'=>$param['amount'] ?? 0,
129 'api_no'=>$param['api_no'], 131 'api_no'=>$param['api_no'],
130 'project_id'=>$param['id'], 132 'project_id'=>$param['id'],
131 'operator_id'=>$this->manager['id'], 133 'operator_id'=>$this->manager['id'],
@@ -68,6 +68,7 @@ class AiBlogLogic extends BaseLogic @@ -68,6 +68,7 @@ class AiBlogLogic extends BaseLogic
68 }catch (\Exception $e){ 68 }catch (\Exception $e){
69 $this->fail('保存失败,请联系管理员'); 69 $this->fail('保存失败,请联系管理员');
70 } 70 }
  71 + $this->sendHttpC([$this->param['route'],'top-blog']);
71 shell_exec("php artisan save_ai_blog_list {$this->user['project_id']} > /dev/null 2>&1 &"); 72 shell_exec("php artisan save_ai_blog_list {$this->user['project_id']} > /dev/null 2>&1 &");
72 return $this->success(); 73 return $this->success();
73 } 74 }
@@ -175,6 +175,9 @@ class NewsLogic extends BaseLogic @@ -175,6 +175,9 @@ class NewsLogic extends BaseLogic
175 RouteMap::delRoute(RouteMap::SOURCE_NEWS, $id, $this->user['project_id']); 175 RouteMap::delRoute(RouteMap::SOURCE_NEWS, $id, $this->user['project_id']);
176 $this->delRoute($id); 176 $this->delRoute($id);
177 $this->model->del(['id' => $id]); 177 $this->model->del(['id' => $id]);
  178 + //删除扩展字段
  179 + $extendInfoModel = new NewsExtendInfo();
  180 + $extendInfoModel->del(['news_id'=>$id]);
178 } 181 }
179 } 182 }
180 DB::commit(); 183 DB::commit();
@@ -5,12 +5,15 @@ namespace App\Http\Logic\Bside\Product; @@ -5,12 +5,15 @@ namespace App\Http\Logic\Bside\Product;
5 use App\Exceptions\BsideGlobalException; 5 use App\Exceptions\BsideGlobalException;
6 use App\Helper\Arr; 6 use App\Helper\Arr;
7 use App\Helper\Common; 7 use App\Helper\Common;
  8 +use App\Helper\Gpt;
8 use App\Http\Logic\Bside\BaseLogic; 9 use App\Http\Logic\Bside\BaseLogic;
  10 +use App\Models\Ai\AiCommand;
9 use App\Models\Com\NoticeLog; 11 use App\Models\Com\NoticeLog;
10 use App\Models\News\News; 12 use App\Models\News\News;
11 use App\Models\Product\Keyword; 13 use App\Models\Product\Keyword;
12 use App\Models\Product\KeywordRelated; 14 use App\Models\Product\KeywordRelated;
13 use App\Models\Product\Product; 15 use App\Models\Product\Product;
  16 +use App\Models\Project\AggregateKeywordComment;
14 use App\Models\RouteMap\RouteMap; 17 use App\Models\RouteMap\RouteMap;
15 use App\Models\User\User; 18 use App\Models\User\User;
16 use Illuminate\Support\Facades\DB; 19 use Illuminate\Support\Facades\DB;
@@ -345,4 +348,104 @@ class KeywordLogic extends BaseLogic @@ -345,4 +348,104 @@ class KeywordLogic extends BaseLogic
345 return $this->success(); 348 return $this->success();
346 } 349 }
347 350
  351 + /**
  352 + * @remark :手动添加评论
  353 + * @name :saveComment
  354 + * @author :lyh
  355 + * @method :post
  356 + * @time :2025/6/9 14:29
  357 + */
  358 + public function saveComment(){
  359 + $keywordCommonModel = new AggregateKeywordComment();
  360 + if(isset($this->param['id']) && !empty($this->param['id'])){
  361 + $id = $this->param['id'];
  362 + $keywordCommonModel->edit($this->param,['id'=>$this->param['id']]);
  363 + }else{
  364 + $param = [
  365 + 'nickname' => $this->param['nickname'],
  366 + 'text' => $this->param['text'],
  367 + 'project_id' => $this->user['project_id'],
  368 + 'type' => 1,
  369 + 'uid' => 0,
  370 + 'created_at' => date('Y-m-d H:i:s'),
  371 + 'updated_at' => date('Y-m-d H:i:s')
  372 + ];
  373 + $id = $keywordCommonModel->addReturnId($param);
  374 + }
  375 + return $this->success(['id'=>$id]);
  376 + }
  377 +
  378 + /**
  379 + * @remark :保存数据
  380 + * @name :sendComment
  381 + * @author :lyh
  382 + * @method :post
  383 + * @time :2025/6/9 11:19
  384 + */
  385 + public function sendComment()
  386 + {
  387 + $aiCommonModel = new AiCommand();
  388 + $info = $aiCommonModel->read(['key' => 'tag_comment','project_id'=>$this->user['project_id']]);
  389 + if($info === false){
  390 + $info = $aiCommonModel->read(['key' => 'tag_comment']);
  391 + $info['ai'] = str_replace('50', $this->param['count'], $info['ai']);
  392 + }
  393 + $text = Gpt::instance()->openai_chat_qqs($info['ai']);
  394 + $text = Common::deal_keywords($text);
  395 + preg_match_all('/\{[^{}]*\}/', $text, $matches);
  396 + $data = [];
  397 + if (!empty($text)) {
  398 + foreach ($matches[0] as $item) {
  399 + $item = str_replace("'", '"', $item);
  400 + // 解码成 PHP 关联数组
  401 + $item = json_decode($item, true);
  402 + if (!isset($item['name']) || !isset($item['comment'])) {
  403 + continue;
  404 + }
  405 + $data[] = [
  406 + 'nickname' => $item['name'],
  407 + 'text' => $item['comment'],
  408 + 'project_id' => $this->user['project_id'],
  409 + 'type' => 1,
  410 + 'uid' => 0,
  411 + 'created_at' => date('Y-m-d H:i:s'),
  412 + 'updated_at' => date('Y-m-d H:i:s')
  413 + ];
  414 + }
  415 + }
  416 + $keywordCommonModel = new AggregateKeywordComment();
  417 + $keywordCommonModel->insertAll($data);
  418 + return $this->success($data);
  419 + }
  420 +
  421 + /**
  422 + * @remark :获取评论
  423 + * @name :getComment
  424 + * @author :lyh
  425 + * @method :post
  426 + * @time :2025/6/9 11:46
  427 + */
  428 + public function getComment($map,$page,$row){
  429 + $keywordCommonModel = new AggregateKeywordComment();
  430 + $map['project_id'] = $this->user['project_id'];
  431 + $lists = $keywordCommonModel->lists($map,$page,$row);
  432 + return $this->success($lists);
  433 + }
  434 +
  435 + /**
  436 + * @remark :删除评论
  437 + * @name :delComment
  438 + * @author :lyh
  439 + * @method :post
  440 + * @time :2025/6/9 11:48
  441 + */
  442 + public function delComment(){
  443 + $keywordCommonModel = new AggregateKeywordComment();
  444 + if($this->param['id'] = 0){
  445 + $keywordCommonModel->del(['project_id'=>$this->param['project_id']]);
  446 + }else{
  447 + $rs = $keywordCommonModel->del(['id'=>$this->param['id']]);
  448 + }
  449 + return $this->success($rs);
  450 + }
348 } 451 }
  1 +<?php
  2 +
  3 +namespace App\Models\Project;
  4 +
  5 +use App\Helper\Arr;
  6 +use App\Models\Base;
  7 +use Illuminate\Support\Facades\DB;
  8 +use Illuminate\Support\Facades\Log;
  9 +use Illuminate\Support\Facades\Redis;
  10 +
  11 +class ProjectKeywordAiTask extends Base
  12 +{
  13 + //设置关联表名
  14 + protected $table = 'gl_project_keyword_ai_task';
  15 +
  16 + const STATUS_PENDING = 0;
  17 + const STATUS_SUCCESS = 1;
  18 + const STATUS_FAIL = 2;
  19 +
  20 + public static function add_task($project_id){
  21 + $task = self::where('project_id', $project_id)->where('status', self::STATUS_PENDING)->first();
  22 + if($task){
  23 + throw new \Exception('该项目有未执行的任务,请勿重复添加');
  24 + }
  25 + $model = new self();
  26 + $model->project_id = $project_id;
  27 + $model->save();
  28 +
  29 + Redis::lpush('projectKeywordAiTask', $project_id);
  30 + }
  31 +
  32 + public static function getPendingTask(){
  33 + //有其他任务 就取其他任务 没有其他任务运行未结束的任务
  34 + $project_id = Redis::rpop('projectKeywordAiTask');
  35 + $data = [];
  36 + if($project_id){
  37 + $data = self::where('status', self::STATUS_PENDING)->where('project_id', $project_id)->orderBy('id', 'asc')->first();
  38 + }
  39 + if($data){
  40 + return $data;
  41 + }
  42 + return self::where('status', self::STATUS_PENDING)->orderBy('id', 'asc')->first();
  43 + }
  44 +
  45 + /**
  46 + * 重试任务
  47 + * @param $id
  48 + * @param $remark
  49 + * @author zbj
  50 + * @date 2023/11/9
  51 + */
  52 + public static function retry($id, $remark)
  53 + {
  54 + DB::beginTransaction();
  55 + try {
  56 + //行锁 避免脏读写
  57 + $data = self::where('id', $id)->lockForUpdate()->first();
  58 + $data->retry = $data->retry + 1;
  59 + if ($data->retry > 3) {
  60 + $data->status = self::STATUS_FAIL;
  61 + }else{
  62 + $data->status = self::STATUS_PENDING;
  63 + }
  64 + $data->remark = mb_substr($remark, 0, 250);
  65 + $data->save();
  66 +
  67 + DB::commit();
  68 + } catch (\Exception $e) {
  69 + DB::rollback();
  70 + Log::error('project_keyword_ai_task retry error:' . $e->getMessage());
  71 + }
  72 + }
  73 +
  74 + /**
  75 + * 完成
  76 + * @param $id
  77 + * @param $update_rows
  78 + * @author zbj
  79 + * @date 2023/11/9
  80 + */
  81 + public static function finish($id, $update_rows){
  82 + DB::beginTransaction();
  83 + try {
  84 + //行锁 避免脏读写
  85 + $data = self::where('id', $id)->lockForUpdate()->first();
  86 + $data->status = self::STATUS_SUCCESS;
  87 + $data->update_rows = $update_rows;
  88 + $data->save();
  89 + DB::commit();
  90 + } catch (\Exception $e) {
  91 + DB::rollback();
  92 + Log::error('project_keyword_ai_task finish error:' . $e->getMessage());
  93 + }
  94 + }
  95 +}
@@ -149,6 +149,7 @@ Route::middleware(['aloginauth'])->group(function () { @@ -149,6 +149,7 @@ Route::middleware(['aloginauth'])->group(function () {
149 //人事管理 149 //人事管理
150 Route::prefix('hr')->group(function () { 150 Route::prefix('hr')->group(function () {
151 Route::any('/', [Aside\Manage\HrController::class, 'list'])->name('admin.hr'); 151 Route::any('/', [Aside\Manage\HrController::class, 'list'])->name('admin.hr');
  152 + Route::any('/getManagerList', [Aside\Manage\HrController::class, 'getManagerList'])->name('admin.hr_getManagerList');
152 Route::any('/info', [Aside\Manage\HrController::class, 'info'])->name('admin.hr_info'); 153 Route::any('/info', [Aside\Manage\HrController::class, 'info'])->name('admin.hr_info');
153 Route::post('/save', [Aside\Manage\HrController::class, 'save'])->name('admin.hr_save'); 154 Route::post('/save', [Aside\Manage\HrController::class, 'save'])->name('admin.hr_save');
154 Route::post('/sort', [Aside\Manage\HrController::class, 'sort'])->name('admin.hr_sort'); 155 Route::post('/sort', [Aside\Manage\HrController::class, 'sort'])->name('admin.hr_sort');
@@ -327,6 +327,10 @@ Route::middleware(['bloginauth'])->group(function () { @@ -327,6 +327,10 @@ Route::middleware(['bloginauth'])->group(function () {
327 Route::any('keyword/batchKeywordIsVideo', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'batchKeywordIsVideo'])->name('product_keyword_batchKeywordIsVideo'); 327 Route::any('keyword/batchKeywordIsVideo', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'batchKeywordIsVideo'])->name('product_keyword_batchKeywordIsVideo');
328 Route::any('keyword/batchKeywordFiled', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'batchKeywordFiled'])->name('product_keyword_batchKeywordFiled'); 328 Route::any('keyword/batchKeywordFiled', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'batchKeywordFiled'])->name('product_keyword_batchKeywordFiled');
329 Route::any('keyword/delRelatedProductId', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'delRelatedProductId'])->name('product_keyword_delRelatedProductId'); 329 Route::any('keyword/delRelatedProductId', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'delRelatedProductId'])->name('product_keyword_delRelatedProductId');
  330 + Route::any('keyword/sendComment', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'sendComment'])->name('product_keyword_sendComment');
  331 + Route::any('keyword/getComment', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'getComment'])->name('product_keyword_getComment');
  332 + Route::any('keyword/delComment', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'delComment'])->name('product_keyword_delComment');
  333 + Route::any('keyword/saveComment', [\App\Http\Controllers\Bside\Product\KeywordController::class, 'saveComment'])->name('product_keyword_saveComment');
330 //产品参数 334 //产品参数
331 Route::get('attr', [\App\Http\Controllers\Bside\Product\AttrController::class, 'index'])->name('product_attr'); 335 Route::get('attr', [\App\Http\Controllers\Bside\Product\AttrController::class, 'index'])->name('product_attr');
332 Route::get('attr/info', [\App\Http\Controllers\Bside\Product\AttrController::class, 'info'])->name('product_attr_info'); 336 Route::get('attr/info', [\App\Http\Controllers\Bside\Product\AttrController::class, 'info'])->name('product_attr_info');