作者 zhl

Merge branch 'master' of 47.244.231.31:zhl/globalso-v6 into zhl

正在显示 53 个修改的文件 包含 2095 行增加119 行删除
@@ -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 }
@@ -43,7 +43,9 @@ class RemainDay extends Command @@ -43,7 +43,9 @@ class RemainDay extends Command
43 1893, 43 1893,
44 2066, 44 2066,
45 2250, 45 2250,
46 - 2193 46 + 2193,
  47 + 2399,
  48 + 1685
47 ];//需要单独处理的项目 49 ];//需要单独处理的项目
48 /** 50 /**
49 * The console command description. 51 * The console command description.
@@ -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
@@ -68,7 +68,9 @@ class UpdateRoute extends Command @@ -68,7 +68,9 @@ class UpdateRoute extends Command
68 */ 68 */
69 public function handle() 69 public function handle()
70 { 70 {
71 - return $this->getAiBlog(); 71 + ProjectServer::useProject(1181);
  72 + return $this->setProductKeyword();
  73 + DB::disconnect('custom_mysql');
72 } 74 }
73 75
74 /** 76 /**
@@ -299,18 +301,18 @@ class UpdateRoute extends Command @@ -299,18 +301,18 @@ class UpdateRoute extends Command
299 if(!empty($lists)){ 301 if(!empty($lists)){
300 foreach ($lists as $v){ 302 foreach ($lists as $v){
301 if(!empty($v['route'])){ 303 if(!empty($v['route'])){
302 -// echo date('Y-m-d H:i:s') . 'route :'.$v['id'] . PHP_EOL;  
303 -// $route = $this->setRoute($v['route'], RouteMap::SOURCE_PRODUCT_KEYWORD, $v['id'], $v['project_id']);  
304 -// $keywordModel->edit(['route'=>$route],['id'=>$v['id']]);  
305 - continue;  
306 - }else{  
307 echo date('Y-m-d H:i:s') . 'route :'.$v['id'] . PHP_EOL; 304 echo date('Y-m-d H:i:s') . 'route :'.$v['id'] . PHP_EOL;
308 - $route = Translate::tran($v['title'], 'en');  
309 - if(!empty($route)){  
310 - echo date('Y-m-d H:i:s') . $route . PHP_EOL;  
311 - $route = $this->setRoute($route, RouteMap::SOURCE_PRODUCT_KEYWORD, $v['id'], $v['project_id']); 305 + $route = $this->setRoute($v['title'], RouteMap::SOURCE_PRODUCT_KEYWORD, $v['id'], $v['project_id']);
312 $keywordModel->edit(['route'=>$route],['id'=>$v['id']]); 306 $keywordModel->edit(['route'=>$route],['id'=>$v['id']]);
313 - } 307 + continue;
  308 + }else{
  309 +// echo date('Y-m-d H:i:s') . 'route :'.$v['id'] . PHP_EOL;
  310 +// $route = Translate::tran($v['title'], 'en');
  311 +// if(!empty($route)){
  312 +// echo date('Y-m-d H:i:s') . $route . PHP_EOL;
  313 +// $route = $this->setRoute($route, RouteMap::SOURCE_PRODUCT_KEYWORD, $v['id'], $v['project_id']);
  314 +// $keywordModel->edit(['route'=>$route],['id'=>$v['id']]);
  315 +// }
314 } 316 }
315 } 317 }
316 } 318 }
@@ -372,7 +374,7 @@ class UpdateRoute extends Command @@ -372,7 +374,7 @@ class UpdateRoute extends Command
372 } 374 }
373 } 375 }
374 $route = $sign.$suffix; 376 $route = $sign.$suffix;
375 - while(RouteMap::isExist($route, $source_id, $project_id)){ 377 + while(RouteMap::isExist($route, $source_id, $project_id,$source)){
376 $route = $sign .'-'.$i.$suffix; 378 $route = $sign .'-'.$i.$suffix;
377 $i++; 379 $i++;
378 } 380 }
@@ -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;
@@ -28,6 +29,7 @@ use App\Models\RouteMap\RouteMap; @@ -28,6 +29,7 @@ use App\Models\RouteMap\RouteMap;
28 use App\Models\Template\BTemplateMain; 29 use App\Models\Template\BTemplateMain;
29 use App\Models\Template\TemplateTypeMain; 30 use App\Models\Template\TemplateTypeMain;
30 use App\Models\Visit\Visit; 31 use App\Models\Visit\Visit;
  32 +use App\Models\WebSetting\TranslateBigProject;
31 use App\Models\WebSetting\WebLanguage; 33 use App\Models\WebSetting\WebLanguage;
32 use App\Models\WebSetting\WebSetting; 34 use App\Models\WebSetting\WebSetting;
33 use App\Models\Workchat\MessagePush; 35 use App\Models\Workchat\MessagePush;
@@ -57,18 +59,27 @@ class lyhDemo extends Command @@ -57,18 +59,27 @@ class lyhDemo extends Command
57 59
58 public function handle(){ 60 public function handle(){
59 $projectModel = new Project(); 61 $projectModel = new Project();
60 - $lists = $projectModel->list(['delete_status' => 0,'project_type'=>0,'extend_type'=>0,'type'=>['in',[1,2,3,4,6]]], 'id', ['id']); 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']);
61 foreach ($lists as $item){ 64 foreach ($lists as $item){
62 -// echo date('Y-m-d H:i:s') . '开始--项目的id:'. $item['id'] . PHP_EOL;  
63 - ProjectServer::useProject($item['id']);  
64 - $webSettingModel = new WebSetting();  
65 - $info = $webSettingModel->read(['project_id'=>$item['id']]);  
66 - if($info === false){  
67 - $webSettingModel->addReturnId(['project_id'=>$item['id']]);  
68 - echo '当前数据为空:'.$item['id'].PHP_EOL; 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;
69 } 79 }
70 DB::disconnect('custom_mysql'); 80 DB::disconnect('custom_mysql');
71 } 81 }
  82 + echo date('Y-m-d H:i:s') . '结束。。。' . PHP_EOL;
72 } 83 }
73 84
74 public function _actionTemplateMain(){ 85 public function _actionTemplateMain(){
@@ -188,6 +188,7 @@ class Supervisory extends Command @@ -188,6 +188,7 @@ class Supervisory extends Command
188 ->where('gl_project.type', Project::TYPE_TWO) 188 ->where('gl_project.type', Project::TYPE_TWO)
189 ->where('gl_project.extend_type', 0) // 是否续费是由extend_type字段控制 189 ->where('gl_project.extend_type', 0) // 是否续费是由extend_type字段控制
190 ->where('gl_project.delete_status', Project::IS_DEL_FALSE) 190 ->where('gl_project.delete_status', Project::IS_DEL_FALSE)
  191 + ->where('gl_project.project_type', '!=', Project::PROJECT_TYPE_SEO)//排除白帽项目
191 ->where(function ($subQuery) { 192 ->where(function ($subQuery) {
192 $subQuery->orwhere('c.qa_status', OnlineCheck::STATUS_ONLINE_TRUE)->orwhere('gl_project.is_upgrade', Project::IS_UPGRADE_TRUE); 193 $subQuery->orwhere('c.qa_status', OnlineCheck::STATUS_ONLINE_TRUE)->orwhere('gl_project.is_upgrade', Project::IS_UPGRADE_TRUE);
193 }) 194 })
@@ -75,6 +75,7 @@ class CopyProject extends Command @@ -75,6 +75,7 @@ class CopyProject extends Command
75 }catch (\Exception $e){ 75 }catch (\Exception $e){
76 echo $e->getMessage().PHP_EOL; 76 echo $e->getMessage().PHP_EOL;
77 echo '复制数据库失败:'.$old_project_id . '<->'.$project_id; 77 echo '复制数据库失败:'.$old_project_id . '<->'.$project_id;
  78 + continue;
78 } 79 }
79 //修改项目状态 80 //修改项目状态
80 $projectModel->edit(['delete_status'=>0],['id'=>$project_id]); 81 $projectModel->edit(['delete_status'=>0],['id'=>$project_id]);
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :InitKeywordComment.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/6/3 15:38
  8 + */
  9 +
  10 +namespace App\Console\Commands\Project;
  11 +
  12 +use App\Helper\Common;
  13 +use App\Helper\Gpt;
  14 +use App\Models\Ai\AiCommand;
  15 +use App\Models\Com\NoticeLog;
  16 +use App\Models\Project\AggregateKeywordComment;
  17 +use App\Models\Project\Project;
  18 +use Illuminate\Console\Command;
  19 +
  20 +class InitKeywordComment extends Command
  21 +{
  22 + /**
  23 + * The name and signature of the console command.
  24 + *
  25 + * @var string
  26 + */
  27 + protected $signature = 'init_keyword_comment';
  28 +
  29 + /**
  30 + * The console command description.
  31 + *
  32 + * @var string
  33 + */
  34 + protected $description = '初始化关键字评论';
  35 +
  36 + public $number = 100;
  37 +
  38 + public function handle(){
  39 + return $this->_action(467);
  40 + while (true){
  41 + $list = NoticeLog::where('type', NoticeLog::TYPE_INIT_KEYWORD_COMMON)->where('status', NoticeLog::STATUS_PENDING)->get();
  42 + if(empty($list)){
  43 + sleep(200);
  44 + continue;
  45 + }
  46 + foreach ($list as $item){
  47 + echo date('Y-m-d H:i:s').'start:' . $item['id'] . PHP_EOL;
  48 + $project_id = $item['data']['project_id'];
  49 + echo date('Y-m-d H:i:s').'执行的项目id:' . $project_id . PHP_EOL;
  50 + try {
  51 + $this->_action($project_id);
  52 + $count = $keywordCommonModel->counts(['project_id'=>$project_id]);
  53 + if($count > 100){
  54 + $item->status = NoticeLog::STATUS_SUCCESS;
  55 + $item->save();
  56 + }
  57 + }catch (\Exception $e){
  58 + echo date('Y-m-d H:i:s').'错误信息:'.$e->getMessage().PHP_EOL;
  59 + continue;
  60 + }
  61 + echo date('Y-m-d H:i:s').'end:' . $item['id'] . PHP_EOL;
  62 + }
  63 + }
  64 + return true;
  65 + }
  66 +
  67 + /**
  68 + * @remark :执行的方法
  69 + * @name :_action
  70 + * @author :lyh
  71 + * @method :post
  72 + * @time :2025/6/3 15:42
  73 + */
  74 + public function _action($project_id){
  75 + $aiCommonModel = new AiCommand();
  76 + $info = $aiCommonModel->read(['key'=>'tag_comment']);
  77 + $text = Gpt::instance()->openai_chat_qqs($info['ai']);
  78 + $text = Common::deal_keywords($text);
  79 + preg_match_all('/\{[^{}]*\}/', $text, $matches);
  80 + if(!empty($text)){
  81 + $data = [];
  82 + foreach ($matches[0] as $item){
  83 + $item = str_replace("'", '"', $item);
  84 + // 解码成 PHP 关联数组
  85 + $item = json_decode($item, true);
  86 + if(!isset($item['name']) || !isset($item['comment'])){
  87 + continue;
  88 + }
  89 + $data[] = [
  90 + 'nickname'=>$item['name'],
  91 + 'text'=>$item['comment'],
  92 + 'project_id'=>$project_id,
  93 + 'type'=>1,
  94 + 'uid'=>0,
  95 + 'created_at'=>date('Y-m-d H:i:s'),
  96 + 'updated_at'=>date('Y-m-d H:i:s')
  97 + ];
  98 + }
  99 + $keywordCommonModel = new AggregateKeywordComment();
  100 + $keywordCommonModel->insertAll($data);
  101 + }
  102 + return true;
  103 + }
  104 +}
@@ -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 +}
@@ -5,6 +5,7 @@ namespace App\Console\Commands\Test; @@ -5,6 +5,7 @@ namespace App\Console\Commands\Test;
5 use App\Helper\Arr; 5 use App\Helper\Arr;
6 use App\Http\Logic\Bside\Product\CategoryLogic; 6 use App\Http\Logic\Bside\Product\CategoryLogic;
7 use App\Models\Com\Notify; 7 use App\Models\Com\Notify;
  8 +use App\Models\CustomModule\CustomModuleContent;
8 use App\Models\Devops\ServerConfig; 9 use App\Models\Devops\ServerConfig;
9 use App\Models\Devops\ServersIp; 10 use App\Models\Devops\ServersIp;
10 use App\Models\Domain\DomainCreateTask; 11 use App\Models\Domain\DomainCreateTask;
@@ -16,6 +17,7 @@ use App\Models\Product\Product; @@ -16,6 +17,7 @@ use App\Models\Product\Product;
16 use App\Models\Project\DeployBuild; 17 use App\Models\Project\DeployBuild;
17 use App\Models\Project\DeployOptimize; 18 use App\Models\Project\DeployOptimize;
18 use App\Models\Project\Project; 19 use App\Models\Project\Project;
  20 +use App\Models\RouteMap\RouteMap;
19 use App\Models\Template\BCustomTemplate; 21 use App\Models\Template\BCustomTemplate;
20 use App\Models\WebSetting\WebLanguage; 22 use App\Models\WebSetting\WebLanguage;
21 use App\Services\BatchExportService; 23 use App\Services\BatchExportService;
@@ -41,7 +43,197 @@ class Temp extends Command @@ -41,7 +43,197 @@ class Temp extends Command
41 43
42 public function handle() 44 public function handle()
43 { 45 {
  46 + $this->specialImport();
  47 + }
  48 +
  49 + /**
  50 + * 3531项目导入扩展模块内容
  51 + * @return bool
  52 + * @author Akun
  53 + * @date 2025/06/05 10:47
  54 + */
  55 + public function specialImport()
  56 + {
  57 + $file_url = 'https://ecdn6.globalso.com/upload/p/3531/file/2025-06/news.csv';
  58 + $domain = 'www.hybio.com.cn';
  59 + $project_id = 3531;
  60 + $is_gbk = 0;
  61 + $file_code_type = $this->get_code_type($file_url);
  62 + if ($file_code_type === false) {
  63 + echo 'date:' . date('Y-m-d H:i:s') . ', import fail, error: 文件编码格式错误' . PHP_EOL;
  64 + return true;
  65 + } elseif ($file_code_type === 'GBK') {
  66 + $is_gbk = 1;
  67 + setlocale(LC_ALL, 'zh_CN');
  68 + }
  69 +
  70 + //读取csv文件
  71 + $line_of_text = [];
  72 + try {
  73 + $opts = [
  74 + 'http' => [
  75 + 'method' => 'GET',
  76 + 'header' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246'
  77 + ],
  78 + 'ssl' => [
  79 + 'verify_peer' => false,
  80 + 'verify_peer_name' => false
  81 + ]
  82 + ];
  83 + $file_handle = fopen($file_url, 'r', null, stream_context_create($opts));
  84 + while (!feof($file_handle)) {
  85 + $line_of_text[] = fgetcsv($file_handle, 0, ',');
  86 + }
  87 + fclose($file_handle);
  88 + } catch (\Exception $e) {
  89 + $this->output('import fail, error: ' . $e->getMessage());
  90 + return true;
  91 + }
  92 +
  93 + $total_count = 0; //总条数
  94 + $success_count = 0; //成功导入条数
  95 + $repeat_count = 0; //过滤已存在条数
  96 + $fail_count = 0;//导入失败条数
  97 + $fail_line = [];//失败行数
  98 + if (count($line_of_text) > 1) {
  99 +
  100 + //设置数据库
  101 + $project = ProjectServer::useProject($project_id);
  102 + if ($project) {
  103 + foreach ($line_of_text as $k => $v) {
  104 + if ($k > 0 && isset($v[0]) && $v[0]) {
  105 +
  106 + if ($is_gbk) {
  107 + foreach ($v as $kk => $vv) {
  108 + $v[$kk] = mb_convert_encoding($vv, 'utf-8', 'gbk');
  109 + }
  110 + }
  111 +
  112 + $v[0] = $this->special2str($v[0]);
  113 +
  114 + $total_count += 1;
  115 + try {
  116 + if ($this->importModule($project_id, $domain, $v)) {
  117 + $success_count += 1;
  118 + } else {
  119 + $repeat_count += 1;
  120 + }
  121 + } catch (\Exception $e) {
  122 + $fail_count += 1;
  123 + $fail_line[] = $k + 1;
  124 + $this->output('title: ' . $v[0] . ', import fail, error: ' . $e->getMessage());
  125 + }
  126 + }
  127 + }
  128 + }
  129 + //关闭数据库
  130 + DB::disconnect('custom_mysql');
  131 + }
  132 +
  133 + $this->output('import end, total count: ' . $total_count . ', success count: ' . $success_count . ', repeat_count count: ' . $repeat_count . ', fail_count count: ' . $fail_count);
  134 +
  135 + return true;
  136 + }
  137 +
  138 + protected function importModule($project_id, $domain, $data)
  139 + {
  140 + $model = new CustomModuleContent();
  141 + $module = $model->read(['name' => $data[0]]);
  142 + if (!$module) {
  143 +
  144 + $content = '';
  145 + if ($data[4] ?? '') {
  146 + //处理内容中的图片
  147 + preg_match_all('/<img\s+[^>]*?src\s*=\s*(\'|\")(.*?)\\1[^>]*?\/?\s*>/i', $data[4], $result);
  148 + if ($result[2] ?? []) {
  149 + foreach ($result[2] as $img) {
  150 + $new_img = check_remote_url_down($img, $project_id, $domain, 1);
  151 + $new_img && $data[4] = str_replace($img, $new_img, $data[4]);
  152 + }
  153 + }
  154 +
  155 + //处理内容中的视频
  156 + preg_match_all('/<source\s+[^>]*?src\s*=\s*(\'|\")(.*?)\\1[^>]*?\/?\s*>/i', $data[4], $result_video);
  157 + if ($result_video[2] ?? []) {
  158 + foreach ($result_video[2] as $video) {
  159 + $new_video = check_remote_url_down($video, $project_id, $domain, 1);
  160 + $new_video && $data[4] = str_replace($video, $new_video, $data[4]);
  161 + }
  162 + }
  163 +
  164 + $content = $data[4];
  165 + }
  166 +
  167 + $seo_title = '';
  168 + if ($data[6] ?? '') {
  169 + $seo_title = substr(strip_tags($data[6]), 0, 70);
  170 + }
  171 +
  172 + $seo_keywords = '';
  173 + if ($data[7] ?? '') {
  174 + $seo_keywords = substr(strip_tags(str_replace('^v6sp$', ',', $data[7])), 0, 255);
  175 + }
  176 +
  177 + $seo_description = '';
  178 + if ($data[8] ?? '') {
  179 + $seo_description = substr(strip_tags($data[8]), 0, 200);
  180 + }
  181 +
  182 + $release_at = date('Y-m-d H:i:s');
  183 + if ($data[9] ?? '') {
  184 + $release_at = date('Y-m-d H:i:s', strtotime($data[9]));
  185 + }
  186 +
  187 +
  188 + $id = $model->addReturnId(
  189 + [
  190 + 'name' => $data[0],
  191 + 'category_id' => ',1,',
  192 + 'module_id' => 2,
  193 + 'content' => $content,
  194 + 'seo_title' => $seo_title,
  195 + 'seo_keywords' => $seo_keywords,
  196 + 'seo_description' => $seo_description,
  197 + 'project_id' => $project_id,
  198 + 'operator_id' => 8143,
  199 + 'status' => 0,
  200 + 'route' => '',
  201 + 'release_at' => $release_at
  202 + ]
  203 + );
  204 +
  205 + $route = RouteMap::setRoute($data[0], RouteMap::SOURCE_MODULE, $id, $project_id);
  206 +
  207 + $model->edit(['route' => $route], ['id' => $id]);
  208 +
  209 + return true;
  210 + }
  211 +
  212 + return false;
  213 + }
  214 +
  215 + //特殊字符转换
  216 + protected function special2str($str)
  217 + {
  218 + if (strpos($str, ';') === false) {
  219 + return $str;
  220 + }
  221 +
  222 + $list = [
  223 + '&lt;' => '<',
  224 + '&gt;' => '>',
  225 + '&amp;' => '&',
  226 + '&acute;' => "'",
  227 + '&quot;' => '"',
  228 + '&nbsp;' => ' ',
  229 + '&#x27;' => "'"
  230 + ];
  231 +
  232 + foreach ($list as $k => $v) {
  233 + $str = str_replace($k, $v, $str);
  234 + }
44 235
  236 + return $str;
45 } 237 }
46 238
47 /** 239 /**
  1 +<?php
  2 +
  3 +namespace App\Console\Commands;
  4 +
  5 +use App\Models\WorkOrder\WorkOrderLog;
  6 +use App\Services\DingTalkService;
  7 +use Illuminate\Console\Command;
  8 +use Illuminate\Support\Facades\Http;
  9 +
  10 +class WorkOrderDing extends Command
  11 +{
  12 + /**
  13 + * The name and signature of the console command.
  14 + *
  15 + * @var string
  16 + */
  17 + protected $signature = 'workorder:ding';
  18 +
  19 + /**
  20 + * The console command description.
  21 + *
  22 + * @var string
  23 + */
  24 + protected $description = '售后工单钉钉通知';
  25 +
  26 + /**
  27 + * Create a new command instance.
  28 + *
  29 + * @return void
  30 + */
  31 + public function __construct()
  32 + {
  33 + parent::__construct();
  34 + }
  35 +
  36 + /**
  37 + * Execute the console command.
  38 + *
  39 + * @return int
  40 + */
  41 + public function handle()
  42 + {
  43 + while (true) {
  44 + try {
  45 + $log = WorkOrderLog::where('ding', 0)->first();
  46 + if (!$log) {
  47 + sleep(3);
  48 + continue;
  49 + }
  50 + $mobile = $log->manager->mobile;
  51 + $response = Http::withBasicAuth(
  52 + env('DINGDING_BASIC_USER'),
  53 + env('DINGDING_BASIC_PASS')
  54 + )->get('https://oa.cmer.com/api/dingding/user/' . $mobile);
  55 + if ($response->status() == 200) {
  56 + $userid = $response->json()['data']['userid'];
  57 + $text = "**您有新的售后工单**<br>";
  58 + $text .= "工单ID:{$log->work_order_id}<br>";
  59 + $text .= "工单类型:<font color='red'>{$log->workOrder->product}</font><br>";
  60 + $text .= "项目:{$log->workOrder->project->title}<br>";
  61 + $text .= "时间:{$log->created_at}<br>";
  62 + $ding = new DingTalkService();
  63 + $resp = $ding->danliao(json_encode([
  64 + 'text' => $text,
  65 + 'title' => '售后工单通知',
  66 + ]), [$userid]);
  67 + $log->ding = 1;
  68 + }else
  69 + $log->ding = 2;
  70 + $log->save();
  71 + }catch (\Exception $exception){
  72 + echo date('Y-m-d H:i:s')." ".$exception->getMessage()."\n";
  73 + break;
  74 + }
  75 + }
  76 + }
  77 +}
  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 +}
@@ -52,6 +52,7 @@ class PrivateController extends BaseController @@ -52,6 +52,7 @@ class PrivateController extends BaseController
52 ->leftJoin('gl_project_online_check as c', 'gl_project.id', '=', 'c.project_id') 52 ->leftJoin('gl_project_online_check as c', 'gl_project.id', '=', 'c.project_id')
53 ->leftJoin('gl_domain_info as d', 'gl_project.id', '=', 'd.project_id') 53 ->leftJoin('gl_domain_info as d', 'gl_project.id', '=', 'd.project_id')
54 ->where('gl_project.type', Project::TYPE_TWO) 54 ->where('gl_project.type', Project::TYPE_TWO)
  55 + ->where('gl_project.project_type',Project::TYPE_ZERO)
55 ->where('gl_project.extend_type', 0) // 是否续费是由extend_type字段控制 56 ->where('gl_project.extend_type', 0) // 是否续费是由extend_type字段控制
56 ->where('gl_project.delete_status', Project::IS_DEL_FALSE) 57 ->where('gl_project.delete_status', Project::IS_DEL_FALSE)
57 ->where(function ($subQuery) { 58 ->where(function ($subQuery) {
@@ -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
@@ -15,6 +15,7 @@ use App\Models\Channel\Channel; @@ -15,6 +15,7 @@ use App\Models\Channel\Channel;
15 use App\Models\Channel\User; 15 use App\Models\Channel\User;
16 use App\Models\Channel\Zone; 16 use App\Models\Channel\Zone;
17 use App\Models\Com\City; 17 use App\Models\Com\City;
  18 +use App\Models\Com\NoticeLog;
18 use App\Models\Com\UpdateLog; 19 use App\Models\Com\UpdateLog;
19 use App\Models\Devops\ServerConfig; 20 use App\Models\Devops\ServerConfig;
20 use App\Models\Devops\ServersIp; 21 use App\Models\Devops\ServersIp;
@@ -960,14 +961,10 @@ class ProjectController extends BaseController @@ -960,14 +961,10 @@ class ProjectController extends BaseController
960 'id'=>'required', 961 'id'=>'required',
961 'aicc'=>'required', 962 'aicc'=>'required',
962 'hagro'=>'required', 963 'hagro'=>'required',
963 -// 'exclusive_aicc_day'=>'required',  
964 -// 'exclusive_hagro_day'=>'required',  
965 ],[ 964 ],[
966 'id.required' => 'id不能为空', 965 'id.required' => 'id不能为空',
967 'aicc.required' => 'aicc是否开启不能为空', 966 'aicc.required' => 'aicc是否开启不能为空',
968 'hagro.required' => 'hagro是否开启不能为空', 967 'hagro.required' => 'hagro是否开启不能为空',
969 -// 'exclusive_aicc_day.required' => '服务天数不能为空',  
970 -// 'exclusive_hagro_day.required' => '服务天数不能为空',  
971 ]); 968 ]);
972 $logic->saveOtherProject(); 969 $logic->saveOtherProject();
973 $this->response('success'); 970 $this->response('success');
@@ -1136,7 +1133,7 @@ class ProjectController extends BaseController @@ -1136,7 +1133,7 @@ class ProjectController extends BaseController
1136 1133
1137 //获取项目数据 1134 //获取项目数据
1138 $projectModel = new Project(); 1135 $projectModel = new Project();
1139 - $projectInfo = $projectModel->read(['id'=>$this->param['id']],['project_type','serve_id','site_status']); 1136 + $projectInfo = $projectModel->read(['id'=>$this->param['id']],['project_type','serve_id','site_status','site_token']);
1140 if(!$projectInfo){ 1137 if(!$projectInfo){
1141 $this->fail('获取项目数据失败'); 1138 $this->fail('获取项目数据失败');
1142 } 1139 }
@@ -1144,6 +1141,19 @@ class ProjectController extends BaseController @@ -1144,6 +1141,19 @@ class ProjectController extends BaseController
1144 $this->response('success'); 1141 $this->response('success');
1145 } 1142 }
1146 1143
  1144 + if($projectInfo['serve_id'] == 8){
  1145 + //自建站项目
  1146 + if($this->param['site_status'] == 1){
  1147 + //关闭站点
  1148 + $site_token = $projectInfo['site_token'] ? $projectInfo['site_token'].'_expired' : '';
  1149 + }else{
  1150 + //开启站点
  1151 + $site_token = str_replace('_expired','',$projectInfo['site_token']);
  1152 + }
  1153 +
  1154 + $projectModel->edit(['site_status'=>$this->param['site_status'],'site_token'=>$site_token],['id'=>$this->param['id']]);
  1155 + }else{
  1156 + //普通项目
1147 //获取域名数据 1157 //获取域名数据
1148 $domainModel = new DomainInfoModel(); 1158 $domainModel = new DomainInfoModel();
1149 $domainInfo = $domainModel->read(['project_id'=>$this->param['id']],['id','domain','amp_status']); 1159 $domainInfo = $domainModel->read(['project_id'=>$this->param['id']],['id','domain','amp_status']);
@@ -1199,6 +1209,7 @@ class ProjectController extends BaseController @@ -1199,6 +1209,7 @@ class ProjectController extends BaseController
1199 } 1209 }
1200 1210
1201 $projectModel->edit(['site_status'=>$this->param['site_status']],['id'=>$this->param['id']]); 1211 $projectModel->edit(['site_status'=>$this->param['site_status']],['id'=>$this->param['id']]);
  1212 + }
1202 1213
1203 $this->response('success'); 1214 $this->response('success');
1204 } 1215 }
@@ -1215,4 +1226,25 @@ class ProjectController extends BaseController @@ -1215,4 +1226,25 @@ class ProjectController extends BaseController
1215 $this->response('success', Code::SUCCESS, $lists); 1226 $this->response('success', Code::SUCCESS, $lists);
1216 } 1227 }
1217 1228
  1229 + /**
  1230 + * @remark :生成关键词图表数据
  1231 + * @name :generateCountCharts
  1232 + * @author :lyh
  1233 + * @method :post
  1234 + * @time :2025/6/10 10:51
  1235 + */
  1236 + public function generateCountCharts(){
  1237 + $this->request->validate([
  1238 + 'project_id'=>'required',
  1239 + ],[
  1240 + 'project_id.required' => '项目id不能为空',
  1241 + ]);
  1242 + $noticeModel = new NoticeLog();
  1243 + $info = $noticeModel->read(['type'=>NoticeLog::TYPE_GENERATE_COUNT_CHARTS,'status'=>0,'data'=>['like','%"'.$this->param['project_id'].'"%']]);
  1244 + if($info !== false){
  1245 + $this->fail('当前数据在生成中');
  1246 + }
  1247 + NoticeLog::createLog(NoticeLog::TYPE_GENERATE_COUNT_CHARTS, ['project_id' => $this->param['project_id']]);
  1248 + $this->response('success');
  1249 + }
1218 } 1250 }
  1 +<?php
  2 +
  3 +namespace App\Http\Controllers\Aside\WorkOrder;
  4 +
  5 +use App\Enums\Common\Code;
  6 +use App\Http\Controllers\Aside\BaseController;
  7 +use App\Http\Requests\Aside\WorkOrder\WorkOrderListRequest;
  8 +use App\Http\Requests\Aside\WorkOrder\WorkOrderUpdateRequest;
  9 +use App\Models\WorkOrder\WorkOrder;
  10 +use App\Models\WorkOrder\WorkOrderLog;
  11 +use Illuminate\Support\Facades\DB;
  12 +
  13 +class WorkOrderController extends BaseController
  14 +{
  15 + /**
  16 + * A端工单列表
  17 + * 显示有我参与的工单列表
  18 + */
  19 + public function index(WorkOrderListRequest $request)
  20 + {
  21 + /*
  22 + * A端工程师工单列表, 查询我的工单
  23 + */
  24 + $request->validated();
  25 + # manage_id 或 engineer_id 是我
  26 + $lists = WorkOrderLog::with([
  27 + 'workOrder.logs.manager:id,name',
  28 + 'workOrder.project:id,company,title',
  29 + ])
  30 + ->where('manage_id', $this->manage['id'])
  31 + ->when($request->input('project_id') !== null, function ($query) use ($request) {
  32 + // project_id 查 workOrder
  33 + return $query->whereHas('workOrder', function ($q) use ($request) {
  34 + $q->where('project_id', $request->input('project_id'));
  35 + });
  36 + })
  37 + ->when($request->input('status') !== null, function ($query) use ($request) {
  38 + // status 查 workOrder
  39 + return $query->whereHas('workOrder', function ($q) use ($request) {
  40 + $q->where('status', $request->input('status'));
  41 + });
  42 + })
  43 + ->when($request->input('search'), function ($query) use ($request) {
  44 + // search 查 workOrder
  45 + return $query->whereHas('workOrder', function ($q) use ($request) {
  46 + $q->where('product', 'like', '%' . $request->input('search') . '%')
  47 + ->orWhere('content', 'like', '%' . $request->input('search') . '%');
  48 + });
  49 + })
  50 + ->orderBy('id', 'desc')
  51 + ->paginate($this->row, ['*'], 'page', $this->page);
  52 + $this->response('success', Code::SUCCESS, $lists);
  53 + }
  54 +
  55 +
  56 + /**
  57 + * @param WorkOrderListRequest $request
  58 + * @return void
  59 + * A端管理员的工单列表
  60 + */
  61 + public function manager(WorkOrderListRequest $request)
  62 + {
  63 + $request->validated();
  64 + // 管理员查看所有工单
  65 + $lists = WorkOrder::with([
  66 + 'user:id,name',
  67 + 'manager:id,name',
  68 + 'engineer:id,name',
  69 + 'logs.manager:id,name',
  70 + 'project:id,company:title',
  71 + ])
  72 + ->when($request->input('project_id') !== null, function ($query) use ($request) {
  73 + return $query->where('project_id', $request->input('project_id'));
  74 + })
  75 + ->when($request->input('status') !== null, function ($query) use ($request) {
  76 + return $query->where('status', $request->input('status'));
  77 + })
  78 + ->when($request->input('search'), function ($query) use ($request) {
  79 + return $query->where('product', 'like', '%' . $request->input('search') . '%')
  80 + ->orWhere('content', 'like', '%' . $request->input('search') . '%');
  81 + })
  82 + ->orderBy('id', 'desc')
  83 + ->paginate($this->row, ['*'], 'page', $this->page);
  84 + $this->response('success', Code::SUCCESS, $lists);
  85 + }
  86 +
  87 + /**
  88 + * A端工单详情
  89 + *
  90 + * @param int $id
  91 + * @return \Illuminate\Http\Response
  92 + */
  93 + public function show($id)
  94 + {
  95 + $workOrder = WorkOrder::with([
  96 + 'logs.manager:id,name',
  97 + 'user:id,name',
  98 + 'manager:id,name',
  99 + 'engineer:id,name',
  100 + 'project:id,company:title',
  101 + ])->find($id);
  102 +
  103 + if (!$workOrder)
  104 + $this->response('工单不存在', Code::USER_MODEL_NOTFOUND_ERROE);
  105 +
  106 + // TODO 判断是否有查看工单详情权限,待添加
  107 + $this->response('success', Code::SUCCESS, $workOrder->toArray());
  108 + }
  109 +
  110 + /**
  111 + * A端操作工单,工程师操作的是工单日志
  112 + * - 工程师:回复工单,自动��拆分给自己的子任务改为完成
  113 + * - 工单第一对接人:
  114 + * - 邀请工程师处理工单
  115 + * - 修改工单状态
  116 + * 若全部子任务完成,则将工单状态改为完成
  117 + */
  118 + public function update(WorkOrderUpdateRequest $request, $id)
  119 + {
  120 + $request->validated();
  121 + $log = WorkOrderLog::find($id); // 拆分的子工单
  122 + if (!$log) {
  123 + $this->response('工单不存在', Code::USER_MODEL_NOTFOUND_ERROE);
  124 + }
  125 + if ($log->manage_id != $this->manage['id']) {
  126 + // 只能操作自己的工单
  127 + $this->response('没有权限操作该工单', Code::USER_PERMISSION_ERROE);
  128 + }
  129 + $workOrder = $log->workOrder;
  130 + $result = DB::transaction(function () use ($request, $workOrder, $log) {
  131 + if ($request->input('engineer_ids'))
  132 + {
  133 + // 有邀请工程师协同处理
  134 + foreach ($request->input('engineer_ids') as $engineer_id)
  135 + {
  136 + try {
  137 + // 利用唯一索引去重
  138 + $new_log = new WorkOrderLog();
  139 + $new_log->manage_id = $engineer_id;
  140 + $workOrder->logs()->save($new_log);
  141 + $workOrder->engineer_id = $engineer_id;
  142 + $workOrder->save();
  143 + }catch (\Exception $exception){}
  144 + }
  145 + }
  146 + if ($request->input('content'))
  147 + $log->content = $request->input('content');
  148 + if ($request->input('files'))
  149 + $log->files = $request->input('files');
  150 + if ($request->input('status') !== null)
  151 + {
  152 + $log->status = $request->input('status');
  153 + if ($log->status >= WorkOrder::STATUS_COMPLETED)
  154 + {
  155 + // 我的工单标记为已完成
  156 + $log->status = $request->input('status');
  157 + $log->end_at = now();
  158 + }
  159 + }
  160 + $log->save();
  161 + // 是否有未完成的子任务
  162 + $pending = $workOrder->logs()
  163 + ->where('status', '<', WorkOrderLog::STATUS_COMPLETED)
  164 + ->count();
  165 + if ($pending)
  166 + {
  167 + $workOrder->status = WorkOrder::STATUS_PROCESSING;
  168 + }else
  169 + {
  170 + $workOrder->status = WorkOrder::STATUS_COMPLETED;
  171 + // 如果所有子任务都完成了,则将工单状态改为已完成
  172 + $workOrder->end_at = now();
  173 + }
  174 + $workOrder->save();
  175 + return $log;
  176 + });
  177 + $this->response('success', Code::SUCCESS, $result->toArray());
  178 + }
  179 +
  180 +}
@@ -143,9 +143,15 @@ class AyrShareController extends BaseController @@ -143,9 +143,15 @@ class AyrShareController extends BaseController
143 $data = [ 143 $data = [
144 'profileKey'=>$info['profile_key'] 144 'profileKey'=>$info['profile_key']
145 ]; 145 ];
  146 + try {
146 $res = $ayrShareHelper->post_generate_jwt($data); 147 $res = $ayrShareHelper->post_generate_jwt($data);
147 if($res['status'] == 'fail'){ 148 if($res['status'] == 'fail'){
148 - $this->response($res['message'],Code::USER_ERROR); 149 + $ayrShareLogic->ayr_share_edit(['profile_key'=>null,'bind_platforms'=>null,'ref_id'=>null],$info['id']);
  150 + $this->response('当前key值已过期,请刷新重新绑定',Code::USER_ERROR);
  151 + }
  152 + }catch (\Exception $e){
  153 + $ayrShareLogic->ayr_share_edit(['profile_key'=>null,'bind_platforms'=>null,'ref_id'=>null],$info['id']);
  154 + $this->response('当前key值已过期,请刷新重新绑定',Code::USER_ERROR);
149 } 155 }
150 $this->response('success',Code::SUCCESS,$res); 156 $this->response('success',Code::SUCCESS,$res);
151 } 157 }
@@ -34,6 +34,7 @@ use App\Models\Project\Country as CountryModel; @@ -34,6 +34,7 @@ use App\Models\Project\Country as CountryModel;
34 use App\Models\Project\Project; 34 use App\Models\Project\Project;
35 use App\Models\RouteMap\RouteMap; 35 use App\Models\RouteMap\RouteMap;
36 use App\Models\WebSetting\SettingNum; 36 use App\Models\WebSetting\SettingNum;
  37 +use App\Models\WebSetting\TranslateBigProject;
37 use App\Models\WebSetting\WebLanguage; 38 use App\Models\WebSetting\WebLanguage;
38 use Illuminate\Http\Request; 39 use Illuminate\Http\Request;
39 use Illuminate\Support\Facades\DB; 40 use Illuminate\Support\Facades\DB;
@@ -61,7 +62,8 @@ class CNoticeController extends BaseController @@ -61,7 +62,8 @@ class CNoticeController extends BaseController
61 ],[ 62 ],[
62 'language.required' => 'language不能为空', 63 'language.required' => 'language不能为空',
63 ]); 64 ]);
64 - $project_id_arr = explode(',',env('PROJECT_ID')) ?? []; 65 + $bigProjectModel = new TranslateBigProject();
  66 + $project_id_arr = $bigProjectModel->selectField(['status'=>1],'project_id') ?? [];
65 if(in_array($this->user['project_id'],$project_id_arr)){ 67 if(in_array($this->user['project_id'],$project_id_arr)){
66 $this->response('success'); 68 $this->response('success');
67 } 69 }
@@ -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,73 @@ class KeywordController extends BaseController @@ -312,4 +316,73 @@ 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 + 'start_time'=>'required',
  332 + ],[
  333 + 'text.required' => '评论内容不能为空',
  334 + 'nickname.required'=>'昵称不能为空',
  335 + 'start_time.required'=>'发布时间不能为空',
  336 + ]);
  337 + $data = $logic->saveComment();
  338 + $this->response('success',Code::SUCCESS,$data);
  339 + }
  340 +
  341 + /**
  342 + * @remark :生成评论
  343 + * @name :sendComment
  344 + * @author :lyh
  345 + * @method :post
  346 + * @time :2025/6/9 11:10
  347 + */
  348 + public function sendComment(KeywordLogic $logic){
  349 + $this->request->validate([
  350 + 'count' => 'required|integer|max:10',
  351 + ], [
  352 + 'count.required' => '生成条数不能为空',
  353 + 'count.integer' => '生成条数必须是整数',
  354 + 'count.max' => '生成条数最大不能超过10',
  355 + ]);
  356 + $data = $logic->sendComment();
  357 + $this->response('success',Code::SUCCESS,$data);
  358 + }
  359 +
  360 + /**
  361 + * @remark :获取评论
  362 + * @name :getComment
  363 + * @author :lyh
  364 + * @method :post
  365 + * @time :2025/6/9 11:45
  366 + */
  367 + public function getComment(KeywordLogic $logic){
  368 + $data = $logic->getComment($this->map,$this->page,$this->row);
  369 + $this->response('success',Code::SUCCESS,$data);
  370 + }
  371 +
  372 + /**
  373 + * @remark :删除评论
  374 + * @name :getComment
  375 + * @author :lyh
  376 + * @method :post
  377 + * @time :2025/6/9 11:45
  378 + */
  379 + public function delComment(KeywordLogic $logic){
  380 + $this->request->validate([
  381 + 'id'=>'required',
  382 + ],[
  383 + 'id.required' => '主键不能为空',
  384 + ]);
  385 + $data = $logic->delComment();
  386 + $this->response('success',Code::SUCCESS,$data);
  387 + }
315 } 388 }
@@ -23,7 +23,7 @@ class WebSettingImageController extends BaseController @@ -23,7 +23,7 @@ class WebSettingImageController extends BaseController
23 * @time :2023/9/21 15:12 23 * @time :2023/9/21 15:12
24 */ 24 */
25 public function lists(WebSettingImage $webSettingImage){ 25 public function lists(WebSettingImage $webSettingImage){
26 - $list = $webSettingImage->list($this->map,'id',['id','image','type']); 26 + $list = $webSettingImage->list($this->map,'id',['id','image','type','is_call']);
27 foreach ($list as $k=>$v){ 27 foreach ($list as $k=>$v){
28 $v['image'] = getImageUrl($v['image'],$this->user['storage_type'],$this->user['project_location']); 28 $v['image'] = getImageUrl($v['image'],$this->user['storage_type'],$this->user['project_location']);
29 $list[$k] = $v; 29 $list[$k] = $v;
@@ -40,12 +40,16 @@ class WebSettingImageController extends BaseController @@ -40,12 +40,16 @@ class WebSettingImageController extends BaseController
40 */ 40 */
41 public function save(WebSettingImage $webSettingImage){ 41 public function save(WebSettingImage $webSettingImage){
42 try { 42 try {
43 - $webSettingImage->del(['project_id'=>$this->user['project_id']]);  
44 foreach ($this->param['data'] as $v){ 43 foreach ($this->param['data'] as $v){
  44 + if(isset($v['id']) && !empty($v['id'])){
  45 + $v['image'] = str_replace_url($v['image']);
  46 + $webSettingImage->edit($v,['id'=>$v['id']]);
  47 + }else{
45 $v['project_id'] = $this->user['project_id']; 48 $v['project_id'] = $this->user['project_id'];
46 $v['image'] = str_replace_url($v['image']); 49 $v['image'] = str_replace_url($v['image']);
47 $webSettingImage->add($v); 50 $webSettingImage->add($v);
48 } 51 }
  52 + }
49 }catch (\Exception $e){ 53 }catch (\Exception $e){
50 $this->response('系统错误请联系管理员'); 54 $this->response('系统错误请联系管理员');
51 } 55 }
  1 +<?php
  2 +
  3 +namespace App\Http\Controllers\Bside\WorkOrder;
  4 +
  5 +use App\Enums\Common\Code;
  6 +use App\Http\Controllers\Bside\BaseController;
  7 +use App\Http\Requests\Aside\WorkOrder\WorkOrderListRequest;
  8 +use App\Http\Requests\Bside\WorkOrder\WorkOrderCreateRequest;
  9 +use App\Http\Requests\Bside\WorkOrder\WorkOrderUpdateRequest;
  10 +use App\Models\Project\Project;
  11 +use App\Models\WorkOrder\WorkOrder;
  12 +use App\Models\WorkOrder\WorkOrderLog;
  13 +use Illuminate\Support\Facades\DB;
  14 +
  15 +class WorkOrderController extends BaseController
  16 +{
  17 + /**
  18 + * B端售后工单列表
  19 + *
  20 + * @return \Illuminate\Http\Response
  21 + */
  22 + public function index(WorkOrderListRequest $request)
  23 + {
  24 + $request->validated();
  25 + $where = [
  26 + 'project_id' => $this->user['project_id'],
  27 + ];
  28 + // 分页查询
  29 + $lists = WorkOrder::with([
  30 + 'manager:id,name',
  31 + ])
  32 + ->where($where)
  33 + ->when($request->input('status'), function ($query) use ($request) {
  34 + return $query->where('status', $request->input('status'));
  35 + })
  36 + ->when($request->input('search'), function ($query) use ($request) {
  37 + return $query->where('product', 'like', '%' . $request->input('search') . '%')
  38 + ->orWhere('content', 'like', '%' . $request->input('search') . '%');
  39 + })
  40 + ->orderBy('id', 'desc')->paginate($this->row, ['*'], 'page', $this->page);
  41 + $this->response('success', Code::SUCCESS, $lists);
  42 + }
  43 +
  44 + /**
  45 + * B端用户提交工单
  46 + *
  47 + * @return \Illuminate\Http\Response
  48 + */
  49 + public function store(WorkOrderCreateRequest $request)
  50 + {
  51 + $request->validated();
  52 + $result = DB::transaction(function () use ($request) {
  53 + $workOrder = new WorkOrder;
  54 + $workOrder->product = $request->input('product');
  55 + $workOrder->content = $request->input('content');
  56 + $workOrder->files = json_encode($request->input('files'));
  57 + $workOrder->project_user_id = $this->user['id'];
  58 + $workOrder->project_id = $this->user['project_id'];
  59 + $workOrder->engineer_id = 0;
  60 + if ($this->project['type'] == 3){
  61 + // 项目类型是优化推广,项目负责人找优化
  62 + $seo = Project::find($this->user['project_id'])->deploy_optimize;
  63 + $workOrder->manage_id = $seo->manager_mid ?? $seo->optimist_mid ?? 0;
  64 + }else{
  65 + // 非优化推广项目,项目负责人找技术组长
  66 + $build = Project::find($this->user['project_id'])->deploy_build;
  67 + $workOrder->manage_id = $build->leader_mid ?? 0;
  68 + }
  69 + $workOrder->save();
  70 + // 添加工单日志
  71 + $log = new WorkOrderLog();
  72 + $log->manage_id = $workOrder->manage_id;
  73 + $workOrder->logs()->save($log);
  74 + return $workOrder;
  75 + });
  76 + $this->response('success', Code::SUCCESS, $result->toArray());
  77 + }
  78 +
  79 + /**
  80 + * Display the specified resource.
  81 + *
  82 + * @param int $id
  83 + * @return \Illuminate\Http\Response
  84 + */
  85 + public function show($id)
  86 + {
  87 + $workOrder = WorkOrder::with([
  88 + 'logs.manager:id,name',
  89 + 'user:id,name',
  90 + 'manager:id,name',
  91 + 'engineer:id,name',
  92 + ])->find($id);
  93 + if (!$workOrder) {
  94 + $this->response('工单未找到', 404);
  95 + }
  96 + if ($workOrder->project_id != $this->user['project_id'] && $workOrder->engineer_id != $this->user['id']) {
  97 + $this->response('无权限查看该工单', 403);
  98 + }
  99 + $this->response('success', Code::SUCCESS, $workOrder->toArray());
  100 + }
  101 +
  102 + /**
  103 + * B端用户修改工单,只能是修改工单状态
  104 + *
  105 + * @param \Illuminate\Http\Request $request
  106 + * @param int $id
  107 + * @return \Illuminate\Http\Response
  108 + */
  109 + public function update(WorkOrderUpdateRequest $request, $id)
  110 + {
  111 + $request->validated();
  112 + $workOrder = WorkOrder::find($id);
  113 + if (!$workOrder) {
  114 + $this->response('工单未找到', 404);
  115 + }
  116 + // b端只有自己项目下的账号可以更新工单
  117 + if ($workOrder->project_id != $this->user['project_id']) {
  118 + $this->response('无权限更新该工单', 403);
  119 + }
  120 + // 更新工单状态
  121 + $workOrder->status = WorkOrder::STATUS_COMPLETED;
  122 + $workOrder->end_at = now();
  123 + $workOrder->save();
  124 + // B端完成工单,将所有未完成的子工单状态改为已完成。并记录完成时间
  125 + $workOrder->logs()->where('status', '<', WorkOrderLog::STATUS_COMPLETED)
  126 + ->update(['status' => WorkOrderLog::STATUS_COMPLETED, 'end_at' => now()]);
  127 + $this->response('success', Code::SUCCESS);
  128 + }
  129 +
  130 + public function destroy($id)
  131 + {
  132 + $workOrder = WorkOrder::find($id);
  133 + if (!$workOrder) {
  134 + $this->response('工单未找到', 404);
  135 + }
  136 + if ($workOrder->status >= WorkOrder::STATUS_COMPLETED)
  137 + $this->response('已完结的工单不能删除', 403);
  138 +
  139 + if ($this->user['type'] != 1 && $workOrder->project_user_id != $this->user['id']) {
  140 + // 只有项目负责人可以删除工单
  141 + $this->response('无权限删除该工单', 403);
  142 + }
  143 + # 删除工单
  144 + $workOrder->delete();
  145 + $this->response('工单已删除', Code::SUCCESS);
  146 +
  147 + }
  148 +
  149 +}
@@ -514,22 +514,22 @@ class ImageController extends Controller @@ -514,22 +514,22 @@ class ImageController extends Controller
514 foreach ($files as $file){ 514 foreach ($files as $file){
515 if(isset($this->cache['image_max']) && ($this->cache['image_max'] != 0)){ 515 if(isset($this->cache['image_max']) && ($this->cache['image_max'] != 0)){
516 if ($file->getSize() > $this->cache['image_max'] * 1024) { 516 if ($file->getSize() > $this->cache['image_max'] * 1024) {
517 - $this->response('图片最大为'.$this->cache['image_max'],Code::SYSTEM_ERROR); 517 + $this->response('图片最大为'.$this->cache['image_max'].'k',Code::SYSTEM_ERROR);
518 } 518 }
519 }else{ 519 }else{
520 if ($file->getSize() > $max) { 520 if ($file->getSize() > $max) {
521 - $this->response('图片最大为1024K',Code::SYSTEM_ERROR); 521 + $this->response('图片最大为2m',Code::SYSTEM_ERROR);
522 } 522 }
523 } 523 }
524 } 524 }
525 }else{ 525 }else{
526 if(isset($this->cache['image_max']) && ($this->cache['image_max'] != 0)){ 526 if(isset($this->cache['image_max']) && ($this->cache['image_max'] != 0)){
527 if ($files->getSize() > $this->cache['image_max'] * 1024) { 527 if ($files->getSize() > $this->cache['image_max'] * 1024) {
528 - $this->response('图片最大为'.$this->cache['image_max'],Code::SYSTEM_ERROR); 528 + $this->response('图片最大为'.$this->cache['image_max'].'k',Code::SYSTEM_ERROR);
529 } 529 }
530 }else{ 530 }else{
531 if ($files->getSize() > $max) { 531 if ($files->getSize() > $max) {
532 - $this->response('图片最大为1024K',Code::SYSTEM_ERROR); 532 + $this->response('图片最大为2m',Code::SYSTEM_ERROR);
533 } 533 }
534 } 534 }
535 } 535 }
@@ -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 + if($this->param['renew_id'] != 0){
84 $this->model->edit(['project_id'=>$this->param['id'],'operator_id'=>$this->manager['id']],['id'=>$this->param['renew_id']]); 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 }
@@ -9,7 +9,6 @@ @@ -9,7 +9,6 @@
9 9
10 namespace App\Http\Logic\Bside\Gpt; 10 namespace App\Http\Logic\Bside\Gpt;
11 11
12 -use App\Helper\Stream;  
13 use App\Http\Logic\Bside\BaseLogic; 12 use App\Http\Logic\Bside\BaseLogic;
14 use App\Models\Gpt\Chat; 13 use App\Models\Gpt\Chat;
15 use App\Models\Gpt\ChatItem; 14 use App\Models\Gpt\ChatItem;
@@ -81,7 +81,7 @@ class NewsLogic extends BaseLogic @@ -81,7 +81,7 @@ class NewsLogic extends BaseLogic
81 $this->edit(['url' => $route], ['id' => $id]); 81 $this->edit(['url' => $route], ['id' => $id]);
82 $this->curlDelRoute(['new_route'=>$route]); 82 $this->curlDelRoute(['new_route'=>$route]);
83 } 83 }
84 - $this->model->saveExtendInfo($id,$this->param['extend'] ?? []); 84 + $this->model->saveExtendInfo($id,$this->param['extend'] ?? [],$this->user['project_id']);
85 $this->addUpdateNotify(RouteMap::SOURCE_NEWS,$route); 85 $this->addUpdateNotify(RouteMap::SOURCE_NEWS,$route);
86 return $this->success(['id'=>$id]); 86 return $this->success(['id'=>$id]);
87 } 87 }
@@ -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,110 @@ class KeywordLogic extends BaseLogic @@ -345,4 +348,110 @@ 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 + 'start_time'=>$this->param['start_time'],
  371 + 'created_at' => date('Y-m-d H:i:s'),
  372 + 'updated_at' => date('Y-m-d H:i:s')
  373 + ];
  374 + $id = $keywordCommonModel->addReturnId($param);
  375 + }
  376 + return $this->success(['id'=>$id]);
  377 + }
  378 +
  379 + /**
  380 + * @remark :保存数据
  381 + * @name :sendComment
  382 + * @author :lyh
  383 + * @method :post
  384 + * @time :2025/6/9 11:19
  385 + */
  386 + public function sendComment()
  387 + {
  388 + $aiCommonModel = new AiCommand();
  389 + $info = $aiCommonModel->read(['key' => 'tag_comment','project_id'=>$this->user['project_id']]);
  390 + if($info === false){
  391 + $info = $aiCommonModel->read(['key' => 'tag_comment']);
  392 + $info['ai'] = str_replace('50', $this->param['count'], $info['ai']);
  393 + }
  394 + $text = Gpt::instance()->openai_chat_qqs($info['ai']);
  395 + $text = Common::deal_keywords($text);
  396 + preg_match_all('/\{[^{}]*\}/', $text, $matches);
  397 + $data = [];
  398 + $twoMonthsAgo = strtotime('-2 months');
  399 +
  400 + if (!empty($text)) {
  401 + foreach ($matches[0] as $item) {
  402 + $item = str_replace("'", '"', $item);
  403 + // 解码成 PHP 关联数组
  404 + $item = json_decode($item, true);
  405 + if (!isset($item['name']) || !isset($item['comment'])) {
  406 + continue;
  407 + }
  408 + $randomTimestamp = rand($twoMonthsAgo, time());
  409 + $randomDateTime = date('Y-m-d H:i:s', $randomTimestamp);
  410 + $data[] = [
  411 + 'nickname' => $item['name'],
  412 + 'text' => $item['comment'],
  413 + 'project_id' => $this->user['project_id'],
  414 + 'type' => 1,
  415 + 'uid' => 0,
  416 + 'start_time'=>$randomDateTime,
  417 + 'created_at' => date('Y-m-d H:i:s'),
  418 + 'updated_at' => date('Y-m-d H:i:s')
  419 + ];
  420 + }
  421 + }
  422 + $keywordCommonModel = new AggregateKeywordComment();
  423 + $keywordCommonModel->insertAll($data);
  424 + return $this->success($data);
  425 + }
  426 +
  427 + /**
  428 + * @remark :获取评论
  429 + * @name :getComment
  430 + * @author :lyh
  431 + * @method :post
  432 + * @time :2025/6/9 11:46
  433 + */
  434 + public function getComment($map,$page,$row){
  435 + $keywordCommonModel = new AggregateKeywordComment();
  436 + $map['project_id'] = $this->user['project_id'];
  437 + $lists = $keywordCommonModel->lists($map,$page,$row);
  438 + return $this->success($lists);
  439 + }
  440 +
  441 + /**
  442 + * @remark :删除评论
  443 + * @name :delComment
  444 + * @author :lyh
  445 + * @method :post
  446 + * @time :2025/6/9 11:48
  447 + */
  448 + public function delComment(){
  449 + $keywordCommonModel = new AggregateKeywordComment();
  450 + if($this->param['id'] == 0){
  451 + $keywordCommonModel->del(['project_id'=>$this->user['project_id']]);
  452 + }else{
  453 + $keywordCommonModel->del(['id'=>$this->param['id']]);
  454 + }
  455 + return $this->success();
  456 + }
348 } 457 }
@@ -101,6 +101,7 @@ class RankDataLogic extends BaseLogic @@ -101,6 +101,7 @@ class RankDataLogic extends BaseLogic
101 'keyword_num' => $project['deploy_build']['keyword_num'], 101 'keyword_num' => $project['deploy_build']['keyword_num'],
102 'compliance_day' => $project['finish_remain_day'] ?? 0, 102 'compliance_day' => $project['finish_remain_day'] ?? 0,
103 'remain_day' => $project['remain_day'], 103 'remain_day' => $project['remain_day'],
  104 + 'seo_remain_day' => $project['seo_remain_day'],
104 'g_top_plan' => $g_top_plan ?? [], 105 'g_top_plan' => $g_top_plan ?? [],
105 ]; 106 ];
106 //小语种列表 107 //小语种列表
@@ -553,7 +554,7 @@ class RankDataLogic extends BaseLogic @@ -553,7 +554,7 @@ class RankDataLogic extends BaseLogic
553 $without_extension_project_ids = [658]; //是否达标只统计主词的 554 $without_extension_project_ids = [658]; //是否达标只统计主词的
554 $extension_project_ids = [354]; //扩展词也到达标的 555 $extension_project_ids = [354]; //扩展词也到达标的
555 $compliance_project_ids = [2163,257,823,1750,497]; //直接达标处理的 556 $compliance_project_ids = [2163,257,823,1750,497]; //直接达标处理的
556 - $ceaseProjectId = [354, 378, 649, 1226, 1283, 1703, 1893, 2066, 2250,2193];//暂停的项目 557 + $ceaseProjectId = [354, 378, 649, 1226, 1283, 1703, 1893, 2066, 2250,2193,2399,1685];//暂停的项目
557 $uptimeProjectId = [1434,1812,276,2414,2974];//按上线时间统计的项目 558 $uptimeProjectId = [1434,1812,276,2414,2974];//按上线时间统计的项目
558 //一个项目多个api_no 559 //一个项目多个api_no
559 $multiple_api_no_project_ids = [ 560 $multiple_api_no_project_ids = [
@@ -74,6 +74,7 @@ class TranslateLogic extends BaseLogic @@ -74,6 +74,7 @@ class TranslateLogic extends BaseLogic
74 } 74 }
75 $arr2 = []; 75 $arr2 = [];
76 foreach ($text_array as $val) { 76 foreach ($text_array as $val) {
  77 + $val = str_replace(['%22', '%', '+'], ['"', '%', '+'], $val);
77 $val = rawurldecode($val); 78 $val = rawurldecode($val);
78 $val = str_replace(' ','',$val);//处理特殊字符 79 $val = str_replace(' ','',$val);//处理特殊字符
79 $val = trim(str_replace(' ','',$val)); 80 $val = trim(str_replace(' ','',$val));
@@ -272,15 +273,11 @@ class TranslateLogic extends BaseLogic @@ -272,15 +273,11 @@ class TranslateLogic extends BaseLogic
272 $data = []; 273 $data = [];
273 if(!empty($this->param['data'])){ 274 if(!empty($this->param['data'])){
274 //处理传递的data 275 //处理传递的data
275 - foreach ($this->param['data'] as $k => $v){  
276 - if(!empty($v) && is_array($v)){  
277 - foreach ($v as $text => $translate){  
278 - $text = str_replace(['%22', '%', '+'], ['"', '%', '+'], $text);  
279 - $translate = str_replace(['%22', '%', '+'], ['"', '%', '+'], $translate); 276 + foreach ($this->param['data'] as $k => $translate){
  277 + $text = str_replace(['%22', '%', '+'], ['"', '%', '+'], $translate[0]);
  278 + $translate = str_replace(['%22', '%', '+'], ['"', '%', '+'], $translate[1]);
280 $data[$text] = $translate; 279 $data[$text] = $translate;
281 } 280 }
282 - }  
283 - }  
284 $this->param['data'] = $data; 281 $this->param['data'] = $data;
285 } 282 }
286 //解析路由 283 //解析路由
@@ -7,6 +7,7 @@ use App\Exceptions\AsideGlobalException; @@ -7,6 +7,7 @@ use App\Exceptions\AsideGlobalException;
7 use App\Exceptions\BsideGlobalException; 7 use App\Exceptions\BsideGlobalException;
8 use App\Helper\Common; 8 use App\Helper\Common;
9 use App\Models\Domain\DomainInfo; 9 use App\Models\Domain\DomainInfo;
  10 +use App\Models\Manage\ManageHr;
10 use App\Models\Project\Project; 11 use App\Models\Project\Project;
11 use App\Models\Scoring\ScoringSystem; 12 use App\Models\Scoring\ScoringSystem;
12 use App\Models\Sms\SmsLog; 13 use App\Models\Sms\SmsLog;
@@ -292,6 +293,9 @@ class UserLoginLogic @@ -292,6 +293,9 @@ class UserLoginLogic
292 $info['service_duration'] = $project['deploy_build']['service_duration'] ?? 0; 293 $info['service_duration'] = $project['deploy_build']['service_duration'] ?? 0;
293 $info['is_comment'] = $project['deploy_build']['is_comment'] ?? 0; 294 $info['is_comment'] = $project['deploy_build']['is_comment'] ?? 0;
294 $info['is_ai_blog_send'] = $project['deploy_optimize']['is_ai_blog_send'] ?? 0; 295 $info['is_ai_blog_send'] = $project['deploy_optimize']['is_ai_blog_send'] ?? 0;
  296 + $info['tech_leader'] = $project['deploy_optimize']['tech_leader'] ?? 0;
  297 + $manageModel = new ManageHr();
  298 + $info['tech_leader_name'] = $manageModel->getName($project['deploy_optimize']['tech_leader'] ?? 0);
295 $info['remain_day'] = $project['remain_day'] ?? 0; 299 $info['remain_day'] = $project['remain_day'] ?? 0;
296 $info['project_created_at'] = $project['created_at']; 300 $info['project_created_at'] = $project['created_at'];
297 $info['type'] = $project['type'] ?? 1; 301 $info['type'] = $project['type'] ?? 1;
  1 +<?php
  2 +
  3 +namespace App\Http\Requests\Aside\WorkOrder;
  4 +
  5 +use Illuminate\Foundation\Http\FormRequest;
  6 +
  7 +class WorkOrderListRequest extends FormRequest
  8 +{
  9 + /**
  10 + * Determine if the user is authorized to make this request.
  11 + *
  12 + * @return bool
  13 + */
  14 + public function authorize()
  15 + {
  16 + return true;
  17 + }
  18 +
  19 + /**
  20 + * Get the validation rules that apply to the request.
  21 + *
  22 + * @return array
  23 + */
  24 + public function rules()
  25 + {
  26 + return [
  27 + 'page' => 'nullable|integer',
  28 + 'size' => 'nullable|integer',
  29 + 'project_id' => 'nullable|integer', // 产品ID
  30 + 'status' => 'nullable|in:0,1,2,3|integer',
  31 + 'search' => 'nullable|string', // 搜索关键词
  32 + ];
  33 + }
  34 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Requests\Aside\WorkOrder;
  4 +
  5 +use Illuminate\Foundation\Http\FormRequest;
  6 +
  7 +class WorkOrderUpdateRequest extends FormRequest
  8 +{
  9 + /**
  10 + * Determine if the user is authorized to make this request.
  11 + *
  12 + * @return bool
  13 + */
  14 + public function authorize()
  15 + {
  16 + return true;
  17 + }
  18 +
  19 + /**
  20 + * Get the validation rules that apply to the request.
  21 + *
  22 + * @return array
  23 + * A端更新售后工单
  24 + */
  25 + public function rules()
  26 + {
  27 + return [
  28 + 'status' => 'nullable|in:0,1,2,3|integer',
  29 + 'engineer_ids' => 'nullable|array',
  30 + 'content' => 'nullable|string',
  31 + 'files' => 'nullable|array',
  32 + ];
  33 + }
  34 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Requests\Bside\WorkOrder;
  4 +
  5 +use Illuminate\Foundation\Http\FormRequest;
  6 +
  7 +class WorkOrderCreateRequest extends FormRequest
  8 +{
  9 + /**
  10 + * Determine if the user is authorized to make this request.
  11 + *
  12 + * @return bool
  13 + */
  14 + public function authorize()
  15 + {
  16 + return true;
  17 + }
  18 +
  19 + /**
  20 + * Get the validation rules that apply to the request.
  21 + *
  22 + * @return array
  23 + */
  24 + public function rules()
  25 + {
  26 + return [
  27 + 'product' => 'required|string',
  28 + 'content' => 'required|string',
  29 + 'files' => 'nullable|array',
  30 + ];
  31 + }
  32 +}
  1 +<?php
  2 +
  3 +namespace App\Http\Requests\Bside\WorkOrder;
  4 +
  5 +use Illuminate\Foundation\Http\FormRequest;
  6 +
  7 +class WorkOrderUpdateRequest extends FormRequest
  8 +{
  9 + /**
  10 + * Determine if the user is authorized to make this request.
  11 + *
  12 + * @return bool
  13 + */
  14 + public function authorize()
  15 + {
  16 + return true;
  17 + }
  18 +
  19 + /**
  20 + * Get the validation rules that apply to the request.
  21 + *
  22 + * @return array
  23 + */
  24 + public function rules()
  25 + {
  26 + return [
  27 + 'status' => 'nullable|in:0,1,2,3|integer',
  28 + 'engineer_ids' => 'nullable|array',
  29 + 'content' => 'nullable|string',
  30 + 'files' => 'nullable|array',
  31 + ];
  32 + }
  33 +}
@@ -13,6 +13,8 @@ class NoticeLog extends Base @@ -13,6 +13,8 @@ class NoticeLog extends Base
13 const TYPE_PROJECT = 'project'; 13 const TYPE_PROJECT = 'project';
14 const TYPE_RANK_DATA = 'rank_data'; 14 const TYPE_RANK_DATA = 'rank_data';
15 const TYPE_INIT_PROJECT = 'init_project'; 15 const TYPE_INIT_PROJECT = 'init_project';
  16 + const TYPE_INIT_KEYWORD_COMMON = 'init_keyword_common';//聚合页关键词评论
  17 + const TYPE_GENERATE_COUNT_CHARTS = 'generate_count_charts';//聚合页关键字图表生成
16 const TYPE_COPY_PROJECT = 'copy_project'; 18 const TYPE_COPY_PROJECT = 'copy_project';
17 const TYPE_INIT_KEYWORD = 'init_keyword'; 19 const TYPE_INIT_KEYWORD = 'init_keyword';
18 const DELETE_PRODUCT_CATEGORY = 'delete_product_category'; 20 const DELETE_PRODUCT_CATEGORY = 'delete_product_category';
@@ -80,7 +80,7 @@ class News extends Base @@ -80,7 +80,7 @@ class News extends Base
80 $arr = json_decode($info['values']); 80 $arr = json_decode($info['values']);
81 foreach ($arr as $k1=>$v1){ 81 foreach ($arr as $k1=>$v1){
82 $v1 = (array)$v1; 82 $v1 = (array)$v1;
83 - $v1['url'] = getImageUrl($v1['url'],$this->user['storage_type'],$this->user['project_location']); 83 + $v1['url'] = getImageUrl($v1['url']);
84 $arr[$k1] = $v1; 84 $arr[$k1] = $v1;
85 } 85 }
86 $v['values'] = $arr; 86 $v['values'] = $arr;
@@ -89,9 +89,9 @@ class News extends Base @@ -89,9 +89,9 @@ class News extends Base
89 foreach ($arr1 as $k1=>$v1){ 89 foreach ($arr1 as $k1=>$v1){
90 $v1 = (array)$v1; 90 $v1 = (array)$v1;
91 if(isset($v1['url'])){ 91 if(isset($v1['url'])){
92 - $v1['url'] = getFileUrl($v1['url'],$this->user['storage_type'],$this->user['project_location'],$this->user['file_cdn'] ?? 0); 92 + $v1['url'] = getFileUrl($v1['url']);
93 }else{ 93 }else{
94 - $v1 = getFileUrl($v1,$this->user['storage_type'],$this->user['project_location'],$this->user['file_cdn'] ?? 0); 94 + $v1 = getFileUrl($v1);
95 } 95 }
96 $arr1[$k1] = $v1; 96 $arr1[$k1] = $v1;
97 } 97 }
@@ -109,7 +109,7 @@ class News extends Base @@ -109,7 +109,7 @@ class News extends Base
109 * @method :post 109 * @method :post
110 * @time :2023/11/9 15:02 110 * @time :2023/11/9 15:02
111 */ 111 */
112 - public function saveExtendInfo($news_id,$extend){ 112 + public function saveExtendInfo($news_id,$extend,$project_id){
113 //先删除以前的数据 113 //先删除以前的数据
114 $extendInfoModel = new NewsExtendInfo(); 114 $extendInfoModel = new NewsExtendInfo();
115 $extendInfoModel->del(['news_id'=>$news_id]); 115 $extendInfoModel->del(['news_id'=>$news_id]);
@@ -120,7 +120,7 @@ class News extends Base @@ -120,7 +120,7 @@ class News extends Base
120 if(empty($v['values'])){ 120 if(empty($v['values'])){
121 continue; 121 continue;
122 } 122 }
123 - $v = $this->saveHandleExtend($v,$news_id); 123 + $v = $this->saveHandleExtend($v,$news_id,$project_id);
124 $extendInfoModel->add($v); 124 $extendInfoModel->add($v);
125 } 125 }
126 return true; 126 return true;
@@ -133,7 +133,7 @@ class News extends Base @@ -133,7 +133,7 @@ class News extends Base
133 * @method :post 133 * @method :post
134 * @time :2023/12/6 15:11 134 * @time :2023/12/6 15:11
135 */ 135 */
136 - public function saveHandleExtend(&$v,$news_id){ 136 + public function saveHandleExtend(&$v,$news_id,$project_id){
137 unset($v['title']); 137 unset($v['title']);
138 if($v['type'] == 3){ 138 if($v['type'] == 3){
139 foreach ($v['values'] as $k1=>$v1){ 139 foreach ($v['values'] as $k1=>$v1){
@@ -148,7 +148,7 @@ class News extends Base @@ -148,7 +148,7 @@ class News extends Base
148 } 148 }
149 $v['values'] = json_encode($v['values']); 149 $v['values'] = json_encode($v['values']);
150 } 150 }
151 - $v['project_id'] = $this->user['project_id']; 151 + $v['project_id'] = $project_id;
152 $v['news_id'] = $news_id; 152 $v['news_id'] = $news_id;
153 return $v; 153 return $v;
154 } 154 }
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :AggregateKeywordComment.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/6/3 17:42
  8 + */
  9 +
  10 +namespace App\Models\Project;
  11 +
  12 +use App\Models\Base;
  13 +
  14 +class AggregateKeywordComment extends Base
  15 +{
  16 + protected $table = 'gl_aggregate_keyword_comment';
  17 +}
  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 +}
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :TranslateBigProject.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/6/4 14:40
  8 + */
  9 +
  10 +namespace App\Models\WebSetting;
  11 +
  12 +use App\Models\Base;
  13 +
  14 +/**
  15 + * @remark :需要翻译的大网站
  16 + * @name :TranslateBigProject
  17 + * @author :lyh
  18 + * @method :post
  19 + * @time :2025/6/4 14:41
  20 + */
  21 +class TranslateBigProject extends Base
  22 +{
  23 + protected $table = 'gl_translate_big_project';
  24 +}
  1 +<?php
  2 +
  3 +namespace App\Models\WorkOrder;
  4 +
  5 +use App\Models\Base;
  6 +use App\Models\Manage\Manage;
  7 +use App\Models\Project\Project;
  8 +use App\Models\User\User;
  9 +
  10 +class WorkOrder extends Base
  11 +{
  12 + protected $table = 'gl_work_orders';
  13 +
  14 + const STATUS_PEDDING = 0; // 待处理
  15 + const STATUS_PROCESSING = 1; // 处理中
  16 + const STATUS_COMPLETED = 2; // 已完成
  17 + const STATUS_CLOSED = 3; // 已关闭
  18 +
  19 +
  20 + /**
  21 + * 工单操作日志
  22 + */
  23 + public function logs()
  24 + {
  25 + return $this->hasMany(WorkOrderLog::class, 'work_order_id', 'id');
  26 + }
  27 +
  28 + /**
  29 + * 提交工单的用户
  30 + */
  31 + public function user()
  32 + {
  33 + return $this->belongsTo(User::class, 'project_user_id', 'id');
  34 + }
  35 +
  36 + /**
  37 + * 工单分配的管理员
  38 + */
  39 + public function manager()
  40 + {
  41 + return $this->belongsTo(Manage::class, 'manage_id', 'id');
  42 + }
  43 +
  44 + /**
  45 + * 工单分配的工程师
  46 + */
  47 + public function engineer()
  48 + {
  49 + return $this->belongsTo(Manage::class, 'engineer_id', 'id');
  50 + }
  51 +
  52 + /** 工单所属项目 */
  53 + public function project()
  54 + {
  55 + return $this->belongsTo(Project::class, 'project_id', 'id');
  56 + }
  57 +}
  1 +<?php
  2 +
  3 +namespace App\Models\WorkOrder;
  4 +
  5 +use App\Models\Base;
  6 +use App\Models\Manage\Manage;
  7 +
  8 +class WorkOrderLog extends Base
  9 +{
  10 + protected $table = 'gl_work_order_logs';
  11 +
  12 + const STATUS_PEDDING = 0; // 待处理
  13 + const STATUS_PROCESSING = 1; // 处理中
  14 + const STATUS_COMPLETED = 2; // 已完成
  15 + const STATUS_CLOSED = 3; // 已关闭
  16 +
  17 + /**
  18 + * 所属工单
  19 + */
  20 + public function workOrder()
  21 + {
  22 + return $this->belongsTo(WorkOrder::class, 'work_order_id');
  23 + }
  24 +
  25 + /**
  26 + * 分配的工单负责人,工程师等
  27 + */
  28 + public function manager()
  29 + {
  30 + return $this->belongsTo(Manage::class, 'manage_id', 'id');
  31 + }
  32 +}
  1 +<?php
  2 +
  3 +namespace App\Services;
  4 +
  5 +use App\DingDepartment;
  6 +use App\DingUser;
  7 +
  8 +/**
  9 + * 文档 https://apihub.cmer.com/docs/cmerdingtalk.html
  10 + */
  11 +class DingTalkService
  12 +{
  13 + protected $appKey;
  14 + protected $appSecret;
  15 + protected $robotCode;
  16 +
  17 + protected $headers = [];
  18 + protected $bashUrl = 'https://api.cmer.com';
  19 +
  20 + public function __construct()
  21 + {
  22 + $this->appKey = env('DING_TALK_APP_KEY');
  23 + $this->appSecret = env('DING_TALK_APP_SECRET');
  24 + $this->robotCode = env('DING_TALK_APP_KEY');
  25 + $this->headers = [
  26 + "Content-Type" => "application/json",
  27 + "apikey" => "UkzZljFv83Z2qBi5YR1o3f2otAVWtug6",
  28 + "X-CmerApi-Host" => "cmerdingtalk.p.cmer.com",
  29 + ];
  30 + }
  31 +
  32 + /** 发起请求 */
  33 + public function send_request(string $method, string $url, array $payload = [], array $params = [])
  34 + {
  35 + $client = new \GuzzleHttp\Client();
  36 + $options = [
  37 + 'headers' => $this->headers,
  38 + 'json' => $payload
  39 + ];
  40 + if (!empty($params))
  41 + $options['query'] = $params;
  42 +
  43 + $response = $client->request($method, $url, $options);
  44 + return $response;
  45 + }
  46 +
  47 + /** 批量发送私聊消息 */
  48 + public function danliao(string $text, array $user_ids)
  49 + {
  50 + $endpoint = '/v1/danliao';
  51 + $payload = [
  52 + "appKey" => $this->appKey,
  53 + "appSecret" => $this->appSecret,
  54 + "robotCode" => $this->robotCode,
  55 + "msg_param" => $text,
  56 + "user_ids" => $user_ids
  57 + ];
  58 + return $this->send_request('POST', $this->bashUrl . $endpoint, $payload);
  59 + }
  60 +
  61 + /** 批量发送Ding */
  62 + public function danliao_ding(string $text, array $user_ids)
  63 + {
  64 + $endpoint = '/v1/danliao_ding';
  65 + $payload = [
  66 + "appKey" => $this->appKey,
  67 + "appSecret" => $this->appSecret,
  68 + "robotCode" => $this->robotCode,
  69 + "content" => $text,
  70 + "receiver_user_id_list" => $user_ids
  71 + ];
  72 + return $this->send_request('POST', $this->bashUrl . $endpoint, $payload);
  73 + }
  74 +
  75 +}
@@ -25,7 +25,7 @@ class ProjectServer @@ -25,7 +25,7 @@ class ProjectServer
25 <section data-section="section" data-screen="screen-large" class="section-404-wrap-block section-block-error404" 25 <section data-section="section" data-screen="screen-large" class="section-404-wrap-block section-block-error404"
26 id="sectionIdyxqu938"> 26 id="sectionIdyxqu938">
27 <div class="layout" data-unable="demo01-error404"> 27 <div class="layout" data-unable="demo01-error404">
28 - <img src="https://ecdn6.globalso.com/upload/m/image_other/2023-10/6528a87e594db30162.png" /> 28 + <img src="https://ecdn6.globalso.com/upload/m/image_other/2023-10/6528a87e594db30162.png" alt=""/>
29 </div> 29 </div>
30 <p style="text-align: center">SORRY. THE PAGE HAS EITHER MOVED OR CANNOT BE FOUND.</p> 30 <p style="text-align: center">SORRY. THE PAGE HAS EITHER MOVED OR CANNOT BE FOUND.</p>
31 <style> 31 <style>
@@ -38,7 +38,7 @@ class ProjectServer @@ -38,7 +38,7 @@ class ProjectServer
38 .section-block-error404 img { 38 .section-block-error404 img {
39 width: 400px; 39 width: 400px;
40 } 40 }
41 - @media only screen and (max-width:500) { 41 + @media only screen and (max-width:500px) {
42 .section-block-error404 img { 42 .section-block-error404 img {
43 max-width: 100%; 43 max-width: 100%;
44 } 44 }
@@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
12 "beyondcode/laravel-websockets": "^1.14", 12 "beyondcode/laravel-websockets": "^1.14",
13 "doctrine/dbal": "^3.6", 13 "doctrine/dbal": "^3.6",
14 "fruitcake/laravel-cors": "^2.0", 14 "fruitcake/laravel-cors": "^2.0",
  15 + "g4t/swagger": "^4.0",
15 "guzzlehttp/guzzle": "^7.0.1", 16 "guzzlehttp/guzzle": "^7.0.1",
16 "hashids/hashids": "^4.1", 17 "hashids/hashids": "^4.1",
17 "intervention/image": "^2.7", 18 "intervention/image": "^2.7",
  1 +<?php
  2 +
  3 +return [
  4 + /*
  5 + |--------------------------------------------------------------------------
  6 + | API Title
  7 + |--------------------------------------------------------------------------
  8 + |
  9 + | The title of your API documentation.
  10 + |
  11 + */
  12 + "title" => env("SWAGGER_TITLE", "Globalso V6 Api Documentation"),
  13 +
  14 + /*
  15 + |--------------------------------------------------------------------------
  16 + | API Description
  17 + |--------------------------------------------------------------------------
  18 + |
  19 + | The description of your API.
  20 + |
  21 + */
  22 + "description" => env("SWAGGER_DESCRIPTION", "Laravel autogenerate swagger"),
  23 +
  24 + /*
  25 + |--------------------------------------------------------------------------
  26 + | API Email
  27 + |--------------------------------------------------------------------------
  28 + |
  29 + | The email associated with your API documentation.
  30 + |
  31 + */
  32 + "email" => env("SWAGGER_EMAIL", "bill@ai.cc"),
  33 +
  34 + /*
  35 + |--------------------------------------------------------------------------
  36 + | API Version
  37 + |--------------------------------------------------------------------------
  38 + |
  39 + | The version of your API.
  40 + |
  41 + */
  42 + "version" => env("SWAGGER_VERSION", "3.0.7"),
  43 +
  44 + /*
  45 + |--------------------------------------------------------------------------
  46 + | Documentation Auth
  47 + |--------------------------------------------------------------------------
  48 + |
  49 + | This options to enable documentation auth
  50 + |
  51 + */
  52 + "enable_auth" => false,
  53 + "username" => "admin",
  54 + "password" => "pass",
  55 + "sesson_ttl" => 100000,
  56 +
  57 + /*
  58 + |--------------------------------------------------------------------------
  59 + | Enable Response Schema
  60 + |--------------------------------------------------------------------------
  61 + |
  62 + | Whether to enable response schema or not.
  63 + |
  64 + */
  65 + "enable_response_schema" => true,
  66 +
  67 + "suggestions_select_input" => false,
  68 +
  69 + "load_from_json" => false,
  70 +
  71 + /*
  72 + |--------------------------------------------------------------------------
  73 + | Authentication Middlewares
  74 + |--------------------------------------------------------------------------
  75 + |
  76 + | List of middleware names used for authentication.
  77 + |
  78 + */
  79 + "auth_middlewares" => [
  80 + "auth",
  81 + "auth:api",
  82 + "aloginauth",
  83 + "bloginauth",
  84 + ],
  85 +
  86 + /*
  87 + |--------------------------------------------------------------------------
  88 + | API URL
  89 + |--------------------------------------------------------------------------
  90 + |
  91 + | The URL path for accessing your API documentation.
  92 + |
  93 + */
  94 + "url" => env("SWAGGER_URL", "swagger/documentation"),
  95 +
  96 + /*
  97 + |--------------------------------------------------------------------------
  98 + | Issues URL
  99 + |--------------------------------------------------------------------------
  100 + |
  101 + | The URL path for accessing issues related to your API documentation.
  102 + |
  103 + */
  104 + "issues_url" => env("SWAGGER_ISSUE_URL", "swagger/issues"),
  105 +
  106 + /*
  107 + |--------------------------------------------------------------------------
  108 + | Enable Swagger
  109 + |--------------------------------------------------------------------------
  110 + |
  111 + | Whether Swagger is enabled or not.
  112 + |
  113 + */
  114 + "enable" => env('SWAGGER_ENABLED', true),
  115 +
  116 + /*
  117 + |--------------------------------------------------------------------------
  118 + | Show Prefix
  119 + |--------------------------------------------------------------------------
  120 + |
  121 + | List of prefixes to show in Swagger.
  122 + |
  123 + */
  124 + "show_prefix" => [],
  125 +
  126 + /*
  127 + |--------------------------------------------------------------------------
  128 + | Include Web Routes
  129 + |--------------------------------------------------------------------------
  130 + |
  131 + | If you want to includes web.php routes, then enable this
  132 + |
  133 + */
  134 + "include_web_routes" => env('SWAGGER_INCLUDE_WEB_ROUTES', true),
  135 +
  136 +
  137 + /*
  138 + |--------------------------------------------------------------------------
  139 + | API Versions
  140 + |--------------------------------------------------------------------------
  141 + |
  142 + | List of versions to show in Swagger.
  143 + |
  144 + */
  145 + "versions" => [
  146 + "all",
  147 + // "v1"
  148 + ],
  149 +
  150 + "default" => "all",
  151 +
  152 +
  153 + /*
  154 + |--------------------------------------------------------------------------
  155 + | Servers
  156 + |--------------------------------------------------------------------------
  157 + |
  158 + | List of servers associated with your API.
  159 + |
  160 + */
  161 + "servers" => [
  162 + [
  163 + "url" => env("APP_URL"),
  164 + "description" => "localhost"
  165 + ]
  166 + ],
  167 +
  168 +
  169 + /*
  170 + |--------------------------------------------------------------------------
  171 + | Security Schemes
  172 + |--------------------------------------------------------------------------
  173 + |
  174 + | Security schemes used in your API.
  175 + |
  176 + */
  177 + "security_schemes" => [
  178 + "APIKeyHeader" => [
  179 + "type" => "apiKey",
  180 + "name" => "token",
  181 + "in" => "header"
  182 + ],
  183 + ],
  184 +
  185 + /*
  186 + |--------------------------------------------------------------------------
  187 + | Spatie Query Builder
  188 + |--------------------------------------------------------------------------
  189 + |
  190 + | Enable it if you using Spatie query builder package to add spatie filters in all GET routes.
  191 + |
  192 + */
  193 + "spatie_query_builder" => false,
  194 +
  195 +
  196 + /*
  197 + |--------------------------------------------------------------------------
  198 + | Status
  199 + |--------------------------------------------------------------------------
  200 + |
  201 + | HTTP response statuses for various methods.
  202 + |
  203 + */
  204 + "status" => [
  205 + "GET" => [
  206 + "200" => [
  207 + "description" => "Successful Operation",
  208 + ],
  209 + "404" => [
  210 + "description" => "Not Found"
  211 + ]
  212 + ],
  213 + "POST" => [
  214 + "200" => [
  215 + "description" => "Successful Operation",
  216 + ],
  217 + "422" => [
  218 + "description" => "Validation Issues"
  219 + ]
  220 + ],
  221 + "PUT" => [
  222 + "200" => [
  223 + "description" => "Successful Operation",
  224 + ],
  225 + "404" => [
  226 + "description" => "Not Found"
  227 + ],
  228 + "405" => [
  229 + "description" => "Validation exception"
  230 + ]
  231 + ],
  232 + "PATCH" => [
  233 + "200" => [
  234 + "description" => "Successful Operation",
  235 + ],
  236 + "404" => [
  237 + "description" => "Not Found"
  238 + ],
  239 + "405" => [
  240 + "description" => "Validation exception"
  241 + ]
  242 + ],
  243 + "DELETE" => [
  244 + "200" => [
  245 + "description" => "successful Operation",
  246 + ],
  247 + "404" => [
  248 + "description" => "page Not Found"
  249 + ]
  250 + ],
  251 + ],
  252 +
  253 +];
1 -<?php  
2 -  
3 -use Illuminate\Database\Migrations\Migration;  
4 -use Illuminate\Database\Schema\Blueprint;  
5 -use Illuminate\Support\Facades\Schema;  
6 -  
7 -class CreateFailedJobsTable extends Migration  
8 -{  
9 - /**  
10 - * Run the migrations.  
11 - *  
12 - * @return void  
13 - */  
14 - public function up()  
15 - {  
16 - Schema::create('failed_jobs', function (Blueprint $table) {  
17 - $table->id();  
18 - $table->string('uuid')->unique();  
19 - $table->text('connection');  
20 - $table->text('queue');  
21 - $table->longText('payload');  
22 - $table->longText('exception');  
23 - $table->timestamp('failed_at')->useCurrent();  
24 - });  
25 - }  
26 -  
27 - /**  
28 - * Reverse the migrations.  
29 - *  
30 - * @return void  
31 - */  
32 - public function down()  
33 - {  
34 - Schema::dropIfExists('failed_jobs');  
35 - }  
36 -}  
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4 use Illuminate\Database\Schema\Blueprint; 4 use Illuminate\Database\Schema\Blueprint;
5 use Illuminate\Support\Facades\Schema; 5 use Illuminate\Support\Facades\Schema;
6 6
7 -class CreatePersonalAccessTokensTable extends Migration 7 +class CreateWorkOrdersTable extends Migration
8 { 8 {
9 /** 9 /**
10 * Run the migrations. 10 * Run the migrations.
@@ -13,15 +13,21 @@ class CreatePersonalAccessTokensTable extends Migration @@ -13,15 +13,21 @@ class CreatePersonalAccessTokensTable extends Migration
13 */ 13 */
14 public function up() 14 public function up()
15 { 15 {
16 - Schema::create('personal_access_tokens', function (Blueprint $table) { 16 + Schema::create('gl_work_orders', function (Blueprint $table) {
17 $table->id(); 17 $table->id();
18 - $table->morphs('tokenable');  
19 - $table->string('name');  
20 - $table->string('token', 64)->unique();  
21 - $table->text('abilities')->nullable();  
22 - $table->timestamp('last_used_at')->nullable();  
23 $table->timestamps(); 18 $table->timestamps();
  19 + $table->string('product')->nullable()->comment('相关产品,工单类型');
  20 + $table->longText('content')->comment('工单图文描述');
  21 + $table->json('files')->nullable()->comment('附件');
  22 + $table->integer('status')->index()->default(0)->comment('工单状态,0:待处理, 1:处理中,2:已完成, 3:已关闭');
  23 + $table->integer('project_user_id')->index()->comment('客户ID');
  24 + $table->integer('project_id')->index()->comment('项目ID');
  25 + $table->integer('manage_id')->index()->comment('A端项目负责人ID, gl_manage 表ID');
  26 + $table->integer('engineer_id')->index()->comment('处理技术,工程师id, gl_manage 表ID');
  27 + $table->timestamp('end_at')->nullable()->comment('完成时间');
24 }); 28 });
  29 + # 添加表注释
  30 + \Illuminate\Support\Facades\DB::statement("ALTER TABLE gl_work_orders comment '工单表'");
25 } 31 }
26 32
27 /** 33 /**
@@ -31,6 +37,6 @@ class CreatePersonalAccessTokensTable extends Migration @@ -31,6 +37,6 @@ class CreatePersonalAccessTokensTable extends Migration
31 */ 37 */
32 public function down() 38 public function down()
33 { 39 {
34 - Schema::dropIfExists('personal_access_tokens'); 40 + Schema::dropIfExists('gl_work_orders');
35 } 41 }
36 } 42 }
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4 use Illuminate\Database\Schema\Blueprint; 4 use Illuminate\Database\Schema\Blueprint;
5 use Illuminate\Support\Facades\Schema; 5 use Illuminate\Support\Facades\Schema;
6 6
7 -class CreateUsersTable extends Migration 7 +class CreateWorkOrderLogsTable extends Migration
8 { 8 {
9 /** 9 /**
10 * Run the migrations. 10 * Run the migrations.
@@ -13,15 +13,19 @@ class CreateUsersTable extends Migration @@ -13,15 +13,19 @@ class CreateUsersTable extends Migration
13 */ 13 */
14 public function up() 14 public function up()
15 { 15 {
16 - Schema::create('users', function (Blueprint $table) { 16 + Schema::create('gl_work_order_logs', function (Blueprint $table) {
17 $table->id(); 17 $table->id();
18 - $table->string('name');  
19 - $table->string('email')->unique();  
20 - $table->timestamp('email_verified_at')->nullable();  
21 - $table->string('password');  
22 - $table->rememberToken();  
23 $table->timestamps(); 18 $table->timestamps();
  19 + $table->integer('work_order_id')->index();
  20 + $table->integer('manage_id')->comment('gl_manage 表ID,操作人ID');
  21 + $table->boolean('ding')->default(false)->comment('是否钉钉通知');
  22 + $table->longText('content')->nullable()->comment('处理结果,图文描述');
  23 + $table->json('files')->nullable()->comment('附件');
  24 + $table->integer('status')->index()->default(0)->comment('工单状态,0:待处理, 1:处理中, 2:已完成');
  25 + $table->timestamp('end_at')->nullable()->comment('完成时间');
  26 + $table->unique(['work_order_id', 'manage_id'], 'work_order_manage_unique'); # 唯一索引,防止同一工单被同一人多次操作,同一工单,可以多人协作
24 }); 27 });
  28 + \Illuminate\Support\Facades\DB::statement('ALTER TABLE gl_work_order_logs comment "工单操作日志表,分配工程师时创建,工程师完成时修改状态"');
25 } 29 }
26 30
27 /** 31 /**
@@ -31,6 +35,6 @@ class CreateUsersTable extends Migration @@ -31,6 +35,6 @@ class CreateUsersTable extends Migration
31 */ 35 */
32 public function down() 36 public function down()
33 { 37 {
34 - Schema::dropIfExists('users'); 38 + Schema::dropIfExists('gl_work_order_logs');
35 } 39 }
36 } 40 }
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
4 use Illuminate\Database\Schema\Blueprint; 4 use Illuminate\Database\Schema\Blueprint;
5 use Illuminate\Support\Facades\Schema; 5 use Illuminate\Support\Facades\Schema;
6 6
7 -class CreatePasswordResetsTable extends Migration 7 +class WorkOrderLogsAddForeignId extends Migration
8 { 8 {
9 /** 9 /**
10 * Run the migrations. 10 * Run the migrations.
@@ -13,10 +13,11 @@ class CreatePasswordResetsTable extends Migration @@ -13,10 +13,11 @@ class CreatePasswordResetsTable extends Migration
13 */ 13 */
14 public function up() 14 public function up()
15 { 15 {
16 - Schema::create('password_resets', function (Blueprint $table) {  
17 - $table->string('email')->index();  
18 - $table->string('token');  
19 - $table->timestamp('created_at')->nullable(); 16 + Schema::table('gl_work_order_logs', function (Blueprint $table) {
  17 + // 字段已存在,给字段添加外键约束
  18 + $table->foreign('work_order_id')
  19 + ->references('id')->on('gl_work_orders')
  20 + ->onDelete('cascade'); // 设置级联删除
20 }); 21 });
21 } 22 }
22 23
@@ -27,6 +28,9 @@ class CreatePasswordResetsTable extends Migration @@ -27,6 +28,9 @@ class CreatePasswordResetsTable extends Migration
27 */ 28 */
28 public function down() 29 public function down()
29 { 30 {
30 - Schema::dropIfExists('password_resets'); 31 + Schema::table('gl_work_order_logs', function (Blueprint $table) {
  32 + // 删除外键约束
  33 + $table->dropForeign(['work_order_id']);
  34 + });
31 } 35 }
32 } 36 }
不能预览此文件类型
@@ -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');
@@ -208,6 +209,7 @@ Route::middleware(['aloginauth'])->group(function () { @@ -208,6 +209,7 @@ Route::middleware(['aloginauth'])->group(function () {
208 Route::any('/getKeywordPrefix', [Aside\Project\KeywordPrefixController::class, 'getKeywordPrefix'])->name('admin.keyword_getKeywordPrefix'); 209 Route::any('/getKeywordPrefix', [Aside\Project\KeywordPrefixController::class, 'getKeywordPrefix'])->name('admin.keyword_getKeywordPrefix');
209 Route::any('/save', [Aside\Project\KeywordPrefixController::class, 'save'])->name('admin.keyword_save'); 210 Route::any('/save', [Aside\Project\KeywordPrefixController::class, 'save'])->name('admin.keyword_save');
210 Route::any('/del', [Aside\Project\KeywordPrefixController::class, 'del'])->name('admin.keyword_del'); 211 Route::any('/del', [Aside\Project\KeywordPrefixController::class, 'del'])->name('admin.keyword_del');
  212 + Route::any('/generateCountCharts', [Aside\Project\ProjectController::class, 'generateCountCharts'])->name('admin.keyword_generateCountCharts');
211 }); 213 });
212 //企业资料库 214 //企业资料库
213 Route::prefix('enterprise_product')->group(function () { 215 Route::prefix('enterprise_product')->group(function () {
@@ -246,6 +248,13 @@ Route::middleware(['aloginauth'])->group(function () { @@ -246,6 +248,13 @@ Route::middleware(['aloginauth'])->group(function () {
246 Route::post('/save_follow', [Aside\Task\TaskController::class, 'save_follow'])->name('admin.task_save_follow'); 248 Route::post('/save_follow', [Aside\Task\TaskController::class, 'save_follow'])->name('admin.task_save_follow');
247 Route::any('/getUserTaskList', [Aside\Task\TaskController::class, 'getUserTaskList'])->name('admin.task_getUserTaskList'); 249 Route::any('/getUserTaskList', [Aside\Task\TaskController::class, 'getUserTaskList'])->name('admin.task_getUserTaskList');
248 }); 250 });
  251 + // 售后工单
  252 + Route::prefix('workorder')->group(function () {
  253 + Route::get('/', [Aside\WorkOrder\WorkOrderController::class, 'index'])->name('admin.workorder.index')->summary('A端工程师的工单列表');
  254 + Route::get('/manager', [Aside\WorkOrder\WorkOrderController::class, 'manager'])->name('admin.workorder.manager')->summary('A端管理员的工单列表');
  255 + Route::get('/{id}', [Aside\WorkOrder\WorkOrderController::class, 'show'])->name('admin.workorder.show')->summary('A端工单详情');
  256 + Route::post('/{id}', [Aside\WorkOrder\WorkOrderController::class, 'update'])->name('admin.workorder.update')->summary('A端更新工单');
  257 + });
249 //服务器配置 258 //服务器配置
250 Route::prefix('devops')->group(function () { 259 Route::prefix('devops')->group(function () {
251 Route::any('/', [Aside\Devops\ServerConfigController::class, 'lists'])->name('admin.devops.lists'); 260 Route::any('/', [Aside\Devops\ServerConfigController::class, 'lists'])->name('admin.devops.lists');
@@ -269,6 +269,16 @@ Route::middleware(['bloginauth'])->group(function () { @@ -269,6 +269,16 @@ Route::middleware(['bloginauth'])->group(function () {
269 Route::any('/save',[\App\Http\Controllers\Bside\Setting\WebSettingAmpController::class, 'save'])->name('amp_save'); 269 Route::any('/save',[\App\Http\Controllers\Bside\Setting\WebSettingAmpController::class, 'save'])->name('amp_save');
270 }); 270 });
271 }); 271 });
  272 +
  273 + // 售后工单
  274 + Route::prefix('workorder')->group(function () {
  275 + Route::get('/', [\App\Http\Controllers\Bside\WorkOrder\WorkOrderController::class, 'index'])->name('workorder.index')->summary('B端售后工单列表');
  276 + Route::post('/', [\App\Http\Controllers\Bside\WorkOrder\WorkOrderController::class, 'store'])->name('workorder.store')->summary('B端创建工单');
  277 + Route::get('/{id}', [\App\Http\Controllers\Bside\WorkOrder\WorkOrderController::class, 'show'])->name('workorder.show')->summary('B端查看工单');
  278 + Route::post('/{id}', [\App\Http\Controllers\Bside\WorkOrder\WorkOrderController::class, 'update'])->name('workorder.update')->summary('B端完结工单');
  279 + Route::delete('/{id}', [\App\Http\Controllers\Bside\WorkOrder\WorkOrderController::class, 'destroy'])->name('workorder.destroy')->summary('B端删除工单,已完结的工单无法删除');
  280 + });
  281 +
272 //产品 282 //产品
273 Route::prefix('product')->group(function () { 283 Route::prefix('product')->group(function () {
274 //产品 284 //产品
@@ -317,6 +327,10 @@ Route::middleware(['bloginauth'])->group(function () { @@ -317,6 +327,10 @@ Route::middleware(['bloginauth'])->group(function () {
317 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');
318 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');
319 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');
320 //产品参数 334 //产品参数
321 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');
322 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');