作者 刘锟

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

正在显示 55 个修改的文件 包含 1568 行增加232 行删除
@@ -24,7 +24,7 @@ class RemainDay extends Command @@ -24,7 +24,7 @@ class RemainDay extends Command
24 protected $signature = 'remain_day'; 24 protected $signature = 'remain_day';
25 25
26 /** 26 /**
27 - * @var 按上线时间统计 27 + * @var 按优化时间统计
28 */ 28 */
29 protected $projectId = [ 29 protected $projectId = [
30 1434,1812,276,2414,2974 30 1434,1812,276,2414,2974
@@ -86,7 +86,7 @@ class RemainDay extends Command @@ -86,7 +86,7 @@ class RemainDay extends Command
86 * @time :2025/4/2 10:48 86 * @time :2025/4/2 10:48
87 */ 87 */
88 public function saveRemainDay(){ 88 public function saveRemainDay(){
89 - $list = $this->project->list(['extend_type'=>Project::TYPE_ZERO,'type'=>['in',[Project::TYPE_TWO,Project::TYPE_THREE,Project::TYPE_FOUR]]],'id',['id','type','uptime','remain_day','is_remain_today','pause_days','finish_remain_day']); 89 + $list = $this->project->list(['extend_type'=>Project::TYPE_ZERO,'type'=>['in',[Project::TYPE_TWO,Project::TYPE_THREE,Project::TYPE_FOUR,Project::TYPE_SIX]]],'id',['id','type','uptime','remain_day','is_remain_today','pause_days','finish_remain_day','bm_finish_remain_day']);
90 foreach ($list as $item){ 90 foreach ($list as $item){
91 $deploy_build = $this->deployBuild->read(['project_id'=>$item['id']],['service_duration','seo_service_duration','plan','seo_plan']); 91 $deploy_build = $this->deployBuild->read(['project_id'=>$item['id']],['service_duration','seo_service_duration','plan','seo_plan']);
92 echo 'start->项目id:' . $item['id'] . '执行时间:'. date('Y-m-d H:i:s') . PHP_EOL; 92 echo 'start->项目id:' . $item['id'] . '执行时间:'. date('Y-m-d H:i:s') . PHP_EOL;
@@ -123,19 +123,15 @@ class RemainDay extends Command @@ -123,19 +123,15 @@ class RemainDay extends Command
123 //白帽版本的系统 123 //白帽版本的系统
124 if($deploy_build['seo_plan'] == 1){ 124 if($deploy_build['seo_plan'] == 1){
125 if($deploy_build['seo_service_duration'] != 0){ 125 if($deploy_build['seo_service_duration'] != 0){
126 - if($item['uptime']){  
127 - $diff = time() - strtotime($item['uptime']);  
128 - $compliance_day = floor($diff / (60 * 60 * 24)); 126 + if($item['bm_finish_remain_day']){
  127 + $compliance_day = (int)$item['bm_finish_remain_day'];
129 $seo_remain_day = $deploy_build['seo_service_duration'] - $compliance_day; 128 $seo_remain_day = $deploy_build['seo_service_duration'] - $compliance_day;
130 }else{ 129 }else{
131 $seo_remain_day = $deploy_build['seo_service_duration']; 130 $seo_remain_day = $deploy_build['seo_service_duration'];
132 } 131 }
133 -// if($seo_remain_day < 0){  
134 -// $seo_remain_day = 0;  
135 -// }  
136 if($deploy_build['plan'] == 0 && $seo_remain_day < 0 && $deploy_build['seo_service_duration'] != 0){//只有白帽版本的项目且剩余服务时常为0,放入未续费中 132 if($deploy_build['plan'] == 0 && $seo_remain_day < 0 && $deploy_build['seo_service_duration'] != 0){//只有白帽版本的项目且剩余服务时常为0,放入未续费中
137 // $this->project->edit(['seo_remain_day'=>$seo_remain_day,'finish_remain_day'=>$compliance_day ?? 0,'extend_type'=>Project::TYPE_FIVE],['id'=>$item['id']]); 133 // $this->project->edit(['seo_remain_day'=>$seo_remain_day,'finish_remain_day'=>$compliance_day ?? 0,'extend_type'=>Project::TYPE_FIVE],['id'=>$item['id']]);
138 - $this->project->edit(['seo_remain_day'=>$seo_remain_day,'finish_remain_day'=>$compliance_day ?? 0],['id'=>$item['id']]); 134 + $this->project->edit(['seo_remain_day'=>$seo_remain_day,'bm_finish_remain_day'=>$compliance_day ?? 0],['id'=>$item['id']]);
139 }else{ 135 }else{
140 //同时包括白帽版本+默认版本的项目 136 //同时包括白帽版本+默认版本的项目
141 $this->project->edit(['seo_remain_day'=>$seo_remain_day],['id'=>$item['id']]); 137 $this->project->edit(['seo_remain_day'=>$seo_remain_day],['id'=>$item['id']]);
@@ -84,17 +84,27 @@ class GeoQuestionRes extends Command @@ -84,17 +84,27 @@ class GeoQuestionRes extends Command
84 GET_RESULT: 84 GET_RESULT:
85 $error_num++; 85 $error_num++;
86 try { 86 try {
87 - echo '执行次数:'.$error_num.PHP_EOL; 87 + $this->output('执行时间:'.date('Y-m-d H:i:s').'->执行次数:'.$error_num.',执行平台:'.$platform.'执行的项目id:'.$taskInfo['project_id'].PHP_EOL);
88 if ($error_num >= 3) { 88 if ($error_num >= 3) {
89 continue; 89 continue;
90 } 90 }
91 - echo '执行平台:'.$platform.PHP_EOL;  
92 - if ($platform == 'Google AI Overview') {  
93 - // overview 数据结构不确定, 需要单独处理数据  
94 - $data = $geo_service->getGooglePlatformResult($question);  
95 - $result = $this->dealGoogleData($data);  
96 - } else {  
97 - $result = $geo_service->getAiPlatformResult($question, $platform); 91 + switch ($platform){
  92 + case 'google_ai_overview':
  93 + // overview 数据结构不确定, 需要单独处理数据
  94 + $data = $geo_service->getGooglePlatformResult($question);
  95 + $result = $this->dealGoogleData($data);
  96 + break;
  97 + case 'deep_seek':
  98 + $data = $geo_service->getDeepSeekResult($question);
  99 + $result = $this->dealDeepSeek($data);
  100 + break;
  101 + case 'gpt-4o-mini':
  102 + $data = $geo_service->getDeepSeekResult($question,'gpt-4o-mini');
  103 + $result = $this->dealDeepSeek($data,'gpt-4o-mini');
  104 + break;
  105 + default:
  106 + $result = $geo_service->getAiPlatformResult($question, $platform);
  107 + break;
98 } 108 }
99 if (empty($result['text'])){ 109 if (empty($result['text'])){
100 goto GET_RESULT; 110 goto GET_RESULT;
@@ -110,24 +120,19 @@ class GeoQuestionRes extends Command @@ -110,24 +120,19 @@ class GeoQuestionRes extends Command
110 $title = array_column(array_column($result['annotations'], 'url_citation'), 'title'); 120 $title = array_column(array_column($result['annotations'], 'url_citation'), 'title');
111 $hit_data = array_merge($url, $title, $hit_data); 121 $hit_data = array_merge($url, $title, $hit_data);
112 } 122 }
113 - // 命中关键词和路由  
114 - $hit_keyword = $hit_url = [];  
115 $hit = 0; 123 $hit = 0;
116 - if (!empty($taskInfo['keywords'])) {  
117 - $hit_keyword = $this->getKeywords($taskInfo['keywords'],$hit_data);  
118 - if (!empty($hit_keyword)) {  
119 - $hit++;  
120 - } 124 + $hit_keyword = $this->getKeywords($taskInfo['keywords'],$hit_data);
  125 + if (!empty($hit_keyword['keywords'])) {
  126 + $hit++;
121 } 127 }
122 - if (!empty($taskInfo['url'])) {  
123 - $hit_url = $this->getUrl($taskInfo['url'],$hit_data);  
124 - if (!empty($hit_url)) {  
125 - $hit++;  
126 - } 128 + $keyword_num = json_encode($hit_keyword['keywords_num'] ?? [],true);
  129 + $hit_url = $this->getUrl($taskInfo['url'],$hit_data);
  130 + if (!empty($hit_url['url'])) {
  131 + $hit++;
127 } 132 }
128 - echo 'MZ-url'.json_encode($hit_url).PHP_EOL; 133 + $url_num = json_encode($hit_url['url_num'] ?? [],true);
129 // 保存数据结果 134 // 保存数据结果
130 - $geo_result = $geoResultModel->read(['project_id' => $taskInfo['project_id'], 'question_id' => $task_id, 'platform' => $platform, 'question' => $question],['id']); 135 + $geo_result = $geoResultModel->read(['project_id' => $taskInfo['project_id'],'type' => $taskInfo['type'], 'question_id' => $task_id, 'platform' => $platform, 'question' => $question],['id']);
131 $save_data = [ 136 $save_data = [
132 'project_id' => $taskInfo['project_id'], 137 'project_id' => $taskInfo['project_id'],
133 'question_id' => $task_id, 138 'question_id' => $task_id,
@@ -135,17 +140,18 @@ class GeoQuestionRes extends Command @@ -135,17 +140,18 @@ class GeoQuestionRes extends Command
135 'platform' => $platform, 140 'platform' => $platform,
136 'question' => $question, 141 'question' => $question,
137 'en_question'=> $en_question, 142 'en_question'=> $en_question,
138 - 'keywords' => json_encode($hit_keyword,true),//命中的关键词  
139 - 'url' => json_encode($hit_url,true),//命中的网址  
140 - 'text' => json_encode($result,true),  
141 - 'hit' => $hit, 143 + 'keywords' => json_encode($hit_keyword['keywords'] ?? [],true),//命中的关键词
  144 + 'url' => json_encode($hit_url['url'] ?? [],true),//命中的网址
  145 + 'text' => json_encode($result ?? [],true),
  146 + 'hit' => $hit ?? 0,
  147 + 'keywords_num'=>$keyword_num ?? [],
  148 + 'url_num'=>$url_num ?? [],
142 'created_at'=>date('Y-m-d H:i:s'), 149 'created_at'=>date('Y-m-d H:i:s'),
143 'updated_at'=>date('Y-m-d H:i:s'), 150 'updated_at'=>date('Y-m-d H:i:s'),
144 ]; 151 ];
145 // echo '当前数据INFO:'.json_encode($save_data,true).PHP_EOL; 152 // echo '当前数据INFO:'.json_encode($save_data,true).PHP_EOL;
146 if($geo_result === false){ 153 if($geo_result === false){
147 - $id= $geoResultModel->insertGetId($save_data);  
148 - echo '当前数据id:'.$id.PHP_EOL; 154 + $geoResultModel->insertGetId($save_data);
149 }else{ 155 }else{
150 $geoResultModel->edit($save_data, ['id' => $geo_result['id']]); 156 $geoResultModel->edit($save_data, ['id' => $geo_result['id']]);
151 } 157 }
@@ -169,15 +175,18 @@ class GeoQuestionRes extends Command @@ -169,15 +175,18 @@ class GeoQuestionRes extends Command
169 */ 175 */
170 public function getUrl($urlArr = [],$result_annotations = []){ 176 public function getUrl($urlArr = [],$result_annotations = []){
171 $url = []; 177 $url = [];
  178 + $url_num = [];
172 if(!empty($urlArr)){ 179 if(!empty($urlArr)){
173 $str = implode(',',$result_annotations); 180 $str = implode(',',$result_annotations);
174 foreach ($urlArr as $u_item){ 181 foreach ($urlArr as $u_item){
  182 + $count = substr_count($str, $u_item);
  183 + $url_num[$u_item] = $count;
175 if (str_contains($str, $u_item)) { 184 if (str_contains($str, $u_item)) {
176 $url[] = $u_item; 185 $url[] = $u_item;
177 } 186 }
178 } 187 }
179 } 188 }
180 - return array_values(array_unique($url)); 189 + return ['url'=>$url,'url_num'=>$url_num];
181 } 190 }
182 191
183 /** 192 /**
@@ -189,15 +198,43 @@ class GeoQuestionRes extends Command @@ -189,15 +198,43 @@ class GeoQuestionRes extends Command
189 */ 198 */
190 public function getKeywords($keywordArr = [],$result_text = []){ 199 public function getKeywords($keywordArr = [],$result_text = []){
191 $keywords = []; 200 $keywords = [];
  201 + $keywords_num = [];
192 if(!empty($keywordArr) && !empty($result_text)){ 202 if(!empty($keywordArr) && !empty($result_text)){
193 $str = implode(',',$result_text); 203 $str = implode(',',$result_text);
194 foreach ($keywordArr as $k_item){ 204 foreach ($keywordArr as $k_item){
  205 + $count = substr_count($str, $k_item);
  206 + $keywords_num[$k_item] = $count;
195 if (str_contains($str, $k_item)) { 207 if (str_contains($str, $k_item)) {
196 $keywords[] = $k_item; 208 $keywords[] = $k_item;
197 } 209 }
198 } 210 }
199 } 211 }
200 - return $keywords; 212 + return ['keywords'=>$keywords,'keywords_num'=>$keywords_num];
  213 + }
  214 +
  215 + /**
  216 + * @remark :整合deepSeek
  217 + * @name :requestDeepSeek
  218 + * @author :lyh
  219 + * @method :post
  220 + * @time :2025/7/15 10:58
  221 + */
  222 + public function dealDeepSeek($data,$model = 'DeepSeek'){
  223 + $result = [
  224 + 'code' => 200,
  225 + 'model' => $model,
  226 + 'text' => '',
  227 + ];
  228 + $texts = [];
  229 + if(!empty($data['text'])){
  230 + array_unshift($texts, $data['text']);
  231 + }
  232 + if(!empty($data['reasoning_content'])){
  233 + array_unshift($texts, $data['reasoning_content']);
  234 + }
  235 + $text = implode(PHP_EOL, $texts);
  236 + $result['text'] = $text;
  237 + return $result;
201 } 238 }
202 239
203 /** 240 /**
@@ -281,6 +318,7 @@ class GeoQuestionRes extends Command @@ -281,6 +318,7 @@ class GeoQuestionRes extends Command
281 return $task_id; 318 return $task_id;
282 } 319 }
283 320
  321 +
284 /** 322 /**
285 * 输出日志 323 * 输出日志
286 * @param $message 324 * @param $message
@@ -21,6 +21,7 @@ use App\Models\Inquiry\ReInquiryText; @@ -21,6 +21,7 @@ use App\Models\Inquiry\ReInquiryText;
21 use App\Models\Project\InquiryFilterConfig; 21 use App\Models\Project\InquiryFilterConfig;
22 use App\Models\Project\Project; 22 use App\Models\Project\Project;
23 use App\Models\WebSetting\WebLanguage; 23 use App\Models\WebSetting\WebLanguage;
  24 +use GuzzleHttp\Exception\ConnectException;
24 use Illuminate\Console\Command; 25 use Illuminate\Console\Command;
25 use Illuminate\Support\Arr; 26 use Illuminate\Support\Arr;
26 use Illuminate\Support\Facades\Cache; 27 use Illuminate\Support\Facades\Cache;
@@ -343,6 +344,12 @@ class RelayInquiry extends Command @@ -343,6 +344,12 @@ class RelayInquiry extends Command
343 } 344 }
344 $val->status = $res ? ReInquiryForm::STATUS_SUCCESS : ReInquiryForm::STATUS_FORGO; 345 $val->status = $res ? ReInquiryForm::STATUS_SUCCESS : ReInquiryForm::STATUS_FORGO;
345 $val->save(); 346 $val->save();
  347 + } catch (ConnectException $e) {
  348 + $val->status = ReInquiryForm::STATUS_FORGO;
  349 + $val->remark = mb_substr($e->getMessage(), 0, 200);
  350 + $val->save();
  351 + $this->logChannel()->info('执行询盘错误:',[$e->getMessage(), $e->getFile(), $e->getLine()]);
  352 + $this->output('执行询盘错误:' . $e->getMessage());
346 } catch (\Exception $e) { 353 } catch (\Exception $e) {
347 $this->logChannel()->info('执行询盘错误:',[$e->getMessage(), $e->getFile(), $e->getLine()]); 354 $this->logChannel()->info('执行询盘错误:',[$e->getMessage(), $e->getFile(), $e->getLine()]);
348 $this->output('执行询盘错误:' . $e->getMessage()); 355 $this->output('执行询盘错误:' . $e->getMessage());
@@ -469,12 +476,12 @@ class RelayInquiry extends Command @@ -469,12 +476,12 @@ class RelayInquiry extends Command
469 } 476 }
470 //手机号过滤 477 //手机号过滤
471 $phone = $form->phone; 478 $phone = $form->phone;
472 - $filter_phone = $this->get_rand($this->filter_phone);  
473 - if($filter_phone == 0){  
474 - $phone = trim(str_replace("+", '', $phone));  
475 - }elseif($filter_phone == 1){  
476 - $phone = '';  
477 - } 479 +// $filter_phone = $this->get_rand($this->filter_phone);
  480 +// if($filter_phone == 0){
  481 +// $phone = trim(str_replace("+", '', $phone));
  482 +// }elseif($filter_phone == 1){
  483 +// $phone = '';
  484 +// }
478 485
479 // 推送站点 486 // 推送站点
480 $domain = $item['url']; 487 $domain = $item['url'];
@@ -510,7 +517,7 @@ class RelayInquiry extends Command @@ -510,7 +517,7 @@ class RelayInquiry extends Command
510 517
511 $pre = 0; 518 $pre = 0;
512 $start_time = time(); 519 $start_time = time();
513 - $seconds = rand(300, 7200); // 开始时间 从5-2小时后开始 520 + $seconds = $this->delay_seconds($form->inquiry_date);
514 $email = ''; 521 $email = '';
515 if($is_inquiry) { 522 if($is_inquiry) {
516 $exists = ReInquiryDetail::where('re_website', $domain)->where('email', $form->email)->first(); 523 $exists = ReInquiryDetail::where('re_website', $domain)->where('email', $form->email)->first();
@@ -573,7 +580,7 @@ class RelayInquiry extends Command @@ -573,7 +580,7 @@ class RelayInquiry extends Command
573 $referrer = $this->getReferer($country_name, $lang); 580 $referrer = $this->getReferer($country_name, $lang);
574 581
575 $start_time = time(); 582 $start_time = time();
576 - $seconds = rand(300, 3000); // 开始时间 从5-50分钟后开始 583 + $seconds = $this->delay_seconds($form->inquiry_date);
577 $exists = ReInquiryDetail::where('re_website', $domain)->where('email', $form->email)->first(); 584 $exists = ReInquiryDetail::where('re_website', $domain)->where('email', $form->email)->first();
578 if($exists){ 585 if($exists){
579 $this->output('转发站点邮件已存在'); 586 $this->output('转发站点邮件已存在');
@@ -609,7 +616,7 @@ class RelayInquiry extends Command @@ -609,7 +616,7 @@ class RelayInquiry extends Command
609 $user_agent = $form->email ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua); 616 $user_agent = $form->email ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua);
610 617
611 $start_time = time(); 618 $start_time = time();
612 - $seconds = rand(300, 3000); // 开始时间 从5-50分钟后开始 619 + $seconds = $this->delay_seconds($form->inquiry_date);
613 $exists = ReInquiryDetail::where('re_website', $postid)->where('email', $form->email)->first(); 620 $exists = ReInquiryDetail::where('re_website', $postid)->where('email', $form->email)->first();
614 if($exists){ 621 if($exists){
615 $this->output('转发站点邮件已存在'); 622 $this->output('转发站点邮件已存在');
@@ -860,7 +867,7 @@ class RelayInquiry extends Command @@ -860,7 +867,7 @@ class RelayInquiry extends Command
860 function getLinksFromSitemap($sitemapUrl) { 867 function getLinksFromSitemap($sitemapUrl) {
861 try { 868 try {
862 //忽略cert证书 先下载到临时文件 869 //忽略cert证书 先下载到临时文件
863 - $result = Http::withoutVerifying()->get($sitemapUrl)->body(); 870 + $result = Http::withoutVerifying()->timeout(10)->get($sitemapUrl)->body();
864 $tempFilePath = tempnam(sys_get_temp_dir(), 'remote_file_'); 871 $tempFilePath = tempnam(sys_get_temp_dir(), 'remote_file_');
865 file_put_contents($tempFilePath, $result); 872 file_put_contents($tempFilePath, $result);
866 $xml = simplexml_load_file($tempFilePath); 873 $xml = simplexml_load_file($tempFilePath);
@@ -935,6 +942,23 @@ class RelayInquiry extends Command @@ -935,6 +942,23 @@ class RelayInquiry extends Command
935 } 942 }
936 943
937 /** 944 /**
  945 + * @author zbj
  946 + * @date 2025/7/16
  947 + */
  948 + public function delay_seconds($inquiry_date)
  949 + {
  950 + $seconds = rand(300, 2 * 3600); // 默认 从5分钟-2小时后开始
  951 + $time_diff = time() - strtotime($inquiry_date); //2小时前-24小时内的 当天发完
  952 + if ($time_diff > 2 * 3600 && $time_diff <= 24 * 3600) {
  953 + $seconds = rand(2 * 3600, 8 * 3600);
  954 + }
  955 + if ($time_diff > 24 * 3600) { //24小时前的 2天内发完
  956 + $seconds = rand(2 * 3600, 48 * 3600);
  957 + }
  958 + return $seconds;
  959 + }
  960 +
  961 + /**
938 * @return \Psr\Log\LoggerInterface 962 * @return \Psr\Log\LoggerInterface
939 */ 963 */
940 public function logChannel() 964 public function logChannel()
@@ -79,11 +79,11 @@ class RankDataLog extends BaseCommands @@ -79,11 +79,11 @@ class RankDataLog extends BaseCommands
79 $this->output('保存排名数据:ID'.$log->project_id . ',APINO' . $log->api_no); 79 $this->output('保存排名数据:ID'.$log->project_id . ',APINO' . $log->api_no);
80 if(Str::endsWith($log->api_no, '_bmseo')){ 80 if(Str::endsWith($log->api_no, '_bmseo')){
81 //白帽版 81 //白帽版
82 - (new RankDataLogic())->save_rank_bmseo($log->project_id, $log->api_no, $res); 82 + $is_compliance = (new RankDataLogic())->save_rank_bmseo($log->project_id, $log->api_no, $res);
83 }else{ 83 }else{
84 $is_compliance = (new RankDataLogic())->save_rank($log->project_id, $log->api_no, $res, null, $log->lang); 84 $is_compliance = (new RankDataLogic())->save_rank($log->project_id, $log->api_no, $res, null, $log->lang);
85 - $log->is_compliance = $is_compliance;  
86 } 85 }
  86 + $log->is_compliance = $is_compliance;
87 $log->status = 1; 87 $log->status = 1;
88 $log->save(); 88 $log->save();
89 89
@@ -49,12 +49,14 @@ class SyncMobile extends Command @@ -49,12 +49,14 @@ class SyncMobile extends Command
49 }catch (\Exception $e){ 49 }catch (\Exception $e){
50 echo date('Y-m-d H:i:s').':未拉起到数据'.PHP_EOL; 50 echo date('Y-m-d H:i:s').':未拉起到数据'.PHP_EOL;
51 } 51 }
  52 + $saveData = [];
52 if(!empty($data)){ 53 if(!empty($data)){
53 $mobileModel = new Mobile(); 54 $mobileModel = new Mobile();
54 $mobileModel->truncate(); 55 $mobileModel->truncate();
55 $data = json_decode($data, true); 56 $data = json_decode($data, true);
56 $userModel = new User(); 57 $userModel = new User();
57 foreach ($data as $mobile){ 58 foreach ($data as $mobile){
  59 + $saveData[] = $mobile;
58 $param = [ 60 $param = [
59 'mobile'=>$mobile, 61 'mobile'=>$mobile,
60 'created_at'=>date('Y-m-d H:i:s') 62 'created_at'=>date('Y-m-d H:i:s')
@@ -63,22 +65,21 @@ class SyncMobile extends Command @@ -63,22 +65,21 @@ class SyncMobile extends Command
63 //查看当前用户是否存在 65 //查看当前用户是否存在
64 $info = $userModel->read(['mobile'=>$mobile,'project_id'=>1],['id']); 66 $info = $userModel->read(['mobile'=>$mobile,'project_id'=>1],['id']);
65 if($info === false){ 67 if($info === false){
66 - $data = [ 68 + $userModel->add([
67 'mobile'=>$mobile, 69 'mobile'=>$mobile,
68 'password'=>base64_encode(md5('123456')), 70 'password'=>base64_encode(md5('123456')),
69 'project_id'=>1, 71 'project_id'=>1,
70 'name'=>$mobile, 72 'name'=>$mobile,
71 'type'=>$userModel::TYPE_ONE 73 'type'=>$userModel::TYPE_ONE
72 - ];  
73 - $userModel->add($data); 74 + ]);
74 } 75 }
75 } 76 }
76 - $data[] = '13083988828'; 77 + $saveData[] = '13083988828';
77 $managerModel = new Manage(); 78 $managerModel = new Manage();
78 $mobileArr = $managerModel->selectField(['status'=>1],'mobile'); 79 $mobileArr = $managerModel->selectField(['status'=>1],'mobile');
79 - $data = array_values(array_unique(array_merge($data,$mobileArr)));  
80 - $userModel->edit(['status'=>1],['project_id'=>1,'mobile'=>['not in',$data]]);  
81 - $userModel->edit(['status'=>0],['project_id'=>1,'mobile'=>['in',$data]]); 80 + $saveData = array_values(array_unique(array_merge($saveData,$mobileArr)));
  81 + $userModel->edit(['status'=>1],['project_id'=>1,'mobile'=>['not in',$saveData]]);
  82 + $userModel->edit(['status'=>0],['project_id'=>1,'mobile'=>['in',$saveData]]);
82 } 83 }
83 echo 'end.'.PHP_EOL; 84 echo 'end.'.PHP_EOL;
84 return true; 85 return true;
@@ -47,8 +47,14 @@ class WorkchatMessageSend extends Command @@ -47,8 +47,14 @@ class WorkchatMessageSend extends Command
47 $task->status = MessagePush::STATUS_SUCCESS; 47 $task->status = MessagePush::STATUS_SUCCESS;
48 }catch (ConnectionException $e){ 48 }catch (ConnectionException $e){
49 $this->output('推送消息' . $task->id . '超时'); 49 $this->output('推送消息' . $task->id . '超时');
50 - $task->status = MessagePush::STATUS_ERROR;  
51 - $task->remark = '请求超时'; 50 + if ($task->retry < 3) {
  51 + $task->status = MessagePush::STATUS_PENDING;
  52 + $task->retry = $task->retry + 1;
  53 + $task->send_time = date('Y-m-d H:i:s', strtotime('+ ' . $task->retry . ' minute'));
  54 + } else {
  55 + $task->status = MessagePush::STATUS_ERROR;
  56 + $task->remark = '请求超时';
  57 + }
52 }catch (\Exception $e){ 58 }catch (\Exception $e){
53 $this->output('推送消息' . $task->id . '失败:' . $e->getMessage()); 59 $this->output('推送消息' . $task->id . '失败:' . $e->getMessage());
54 $task->status = MessagePush::STATUS_ERROR; 60 $task->status = MessagePush::STATUS_ERROR;
@@ -4,12 +4,16 @@ namespace App\Console\Commands\Tdk; @@ -4,12 +4,16 @@ namespace App\Console\Commands\Tdk;
4 4
5 5
6 use App\Helper\Arr; 6 use App\Helper\Arr;
  7 +use App\Models\Blog\BlogCategory;
  8 +use App\Models\CustomModule\CustomModuleCategory;
7 use App\Models\News\News; 9 use App\Models\News\News;
8 use App\Models\News\NewsCategory; 10 use App\Models\News\NewsCategory;
  11 +use App\Models\Product\Category;
9 use App\Models\Product\Keyword; 12 use App\Models\Product\Keyword;
10 use App\Models\Project\KeywordPrefix; 13 use App\Models\Project\KeywordPrefix;
11 use App\Models\Project\Project; 14 use App\Models\Project\Project;
12 use App\Models\Project\ProjectUpdateTdk; 15 use App\Models\Project\ProjectUpdateTdk;
  16 +use App\Models\Template\BCustomTemplate;
13 use App\Services\ProjectServer; 17 use App\Services\ProjectServer;
14 use App\Utils\LogUtils; 18 use App\Utils\LogUtils;
15 use Illuminate\Console\Command; 19 use Illuminate\Console\Command;
@@ -55,11 +59,14 @@ class RerunSeoTdk extends Command @@ -55,11 +59,14 @@ class RerunSeoTdk extends Command
55 */ 59 */
56 public function handle() 60 public function handle()
57 { 61 {
58 - $project_ids = Project::where('type', Project::TYPE_TWO)->pluck('id')->toArray(); 62 + $where = [
  63 + 'id' => 624
  64 + ];
  65 + $project_ids = Project::where('type', Project::TYPE_TWO)->where($where)->pluck('id')->toArray();
59 foreach ($project_ids as $project_id){ 66 foreach ($project_ids as $project_id){
60 try { 67 try {
61 ProjectServer::useProject($project_id); 68 ProjectServer::useProject($project_id);
62 - $this->judgeAnomalies($project_id); 69 + $this->changeCompanyName($project_id);
63 DB::disconnect('custom_mysql'); 70 DB::disconnect('custom_mysql');
64 }catch (\Exception $e){ 71 }catch (\Exception $e){
65 dump($e->getMessage()); 72 dump($e->getMessage());
@@ -68,6 +75,25 @@ class RerunSeoTdk extends Command @@ -68,6 +75,25 @@ class RerunSeoTdk extends Command
68 } 75 }
69 76
70 /** 77 /**
  78 + * 换了公司英文名的
  79 + * @author zbj
  80 + * @date 2025/7/18
  81 + */
  82 + public function changeCompanyName($project_id){
  83 + $row1 = BCustomTemplate::where('description', 'like', '%BlueQ Biotechnology%')->update(['description' => '']);
  84 + $row2 = Category::where('seo_des', 'like', '%BlueQ Biotechnology%')->update(['seo_des' => '']);
  85 + $row3 = Keyword::where('seo_description', 'like', '%BlueQ Biotechnology%')->update(['seo_description' => '']);
  86 + $row4 = Keyword::where('keyword_content', 'like', '%BlueQ Biotechnology%')->update(['keyword_content' => '']);
  87 + $row5 = BlogCategory::where('seo_des', 'like', '%BlueQ Biotechnology%')->update(['seo_des' => '']);
  88 + $row6 = NewsCategory::where('seo_des', 'like', '%BlueQ Biotechnology%')->update(['seo_des' => '']);
  89 + $row7 = CustomModuleCategory::where('seo_description', 'like', '%BlueQ Biotechnology%')->update(['seo_description' => '']);
  90 +
  91 +
  92 + dump($row1,$row2,$row3,$row4,$row5,$row6,$row7);
  93 + }
  94 +
  95 +
  96 + /**
71 * 判断seo_title 前缀有wholesale或cheap或buy的词,后缀也有 manufacturer,factory,exporter,company 97 * 判断seo_title 前缀有wholesale或cheap或buy的词,后缀也有 manufacturer,factory,exporter,company
72 * 判断关键词最后一个词是前缀的词,前后缀都不拼 98 * 判断关键词最后一个词是前缀的词,前后缀都不拼
73 * @author zbj 99 * @author zbj
@@ -56,65 +56,99 @@ class FetchTicketProjects extends Command @@ -56,65 +56,99 @@ class FetchTicketProjects extends Command
56 */ 56 */
57 public function fetchV5() 57 public function fetchV5()
58 { 58 {
59 - # pm 项目经理 assm 售后服务经理  
60 - $response = Http::get('https://www.quanqiusou.cn/extend_api/webs/globalso_all.php');  
61 - if ($response->status() == 200) {  
62 - $items = $response->json();  
63 - foreach ($items as $item) {  
64 - # V5: 版本号+postid  
65 - $uuid = md5("V5{$item['postid']}");  
66 - $project = TicketProject::where('uuid', $uuid)->first();  
67 - $assm_id = Manage::where('name', $item['assm'])->value('id') ?? Manage::where('name', '张鸿飞')->value('id') ?? 0; //售后服务经理  
68 - $seom_id = Manage::where('name', $item['yhs'])->value('id') ?? Manage::where('name', '陶婵')->value('id') ?? 0; //优化师  
69 - // 如果 $item['cate'] 包含”推广“字符,则 $engineer_name = $item['assm']  
70 - /**  
71 - * 第一负责人逻即说明:  
72 - * 优化推广项目:找售后服务经理??鸿飞  
73 - * 建站类项目: 找杨长远  
74 - */  
75 - $engineer_id = (strpos($item['cate'], '推广') !== false) ? $assm_id : Manage::where('name', '杨长远')->value('id') ?? 0; 59 + $page = 1;
  60 + $postids = [];
76 61
77 - $fields = [  
78 - 'post_id' => $item['postid'],  
79 - 'company_name' => $item['company'],  
80 - 'title' => $item['title'],  
81 - 'engineer_id' => $engineer_id, // 第一负责人  
82 - 'assm_id' => $assm_id,  
83 - 'seom_id' => $seom_id,  
84 - 'website' => $item['main_url'] ?? '',  
85 - 'test_website' => $item['test_url'] ?? '',  
86 - 'is_del' => 0,  
87 - 'plan' => $item['plan'] ?? '',  
88 - 'project_cate' => 1,  
89 - ];  
90 - if (!$project) {  
91 - $new = new TicketProject();  
92 - $new->uuid = $uuid;  
93 - $new->version = 5;  
94 - $new->table_id = 0;  
95 - foreach ($fields as $k => $v) {  
96 - $new->$k = $v;  
97 - }  
98 - $new->save();  
99 - } else {  
100 - $changed = false;  
101 - foreach ($fields as $k => $v) {  
102 - if ($project->$k != $v) {  
103 - $project->$k = $v;  
104 - $changed = true; 62 + while (true) {
  63 + $response = Http::get('https://www.quanqiusou.cn/extend_api/webs/globalso_all.php?page=' . $page);
  64 + if ($response->status() == 200) {
  65 + $resp_json = $response->json();
  66 + $items = $resp_json['data'] ?? [];
  67 + if (empty($items))
  68 + {
  69 + echo now() . " | INFO | V5: not found items on page $page \n";
  70 + break;
  71 + }
  72 + foreach ($items as $item) {
  73 + # V5: 版本号+postid
  74 + $uuid = md5("V5{$item['postid']}");
  75 + $project = TicketProject::where('uuid', $uuid)->first();
  76 +
  77 + // 项目状态, 根据 $item['cate'] 判断,建站中,建站客户,推广
  78 + if (strpos($item['cate'], '推广') !== false)
  79 + $status=3; // 推广
  80 + elseif ($item['cate'] == "建站客户")
  81 + $status=2; // 建站客户
  82 + elseif ($item['cate'] == "建站中")
  83 + $status=1; // 建站中
  84 +
  85 + $assm_id = Manage::where('name', $item['assm'])->value('id') ?? Manage::where('name', '张鸿飞')->value('id') ?? 0; //售后服务经理
  86 + $seom_id = Manage::where('name', $item['yhs'])->value('id') ?? Manage::where('name', '陶婵')->value('id') ?? 0; //优化师
  87 + $pm_id = Manage::where('name', $item['pm'])->value('id') ?? Manage::where('name', '李洁玉')->value('id') ?? 0; // 项目经理
  88 +
  89 + /**
  90 + * 第一负责人逻即说明:
  91 + * 优化推广项目:找售后服务经理??鸿飞
  92 + * 建站中:项目经理
  93 + * 建站完成:杨长远
  94 + */
  95 +
  96 + if ($status == 3)
  97 + $engineer_id = $assm_id; // 推广类项目找售后服务经理
  98 + elseif ($status == 2)
  99 + $engineer_id = Manage::where('name', '杨长远')->value('id') ?? 0; //建站完成
  100 + elseif ($status == 1)
  101 + $engineer_id = $pm_id; // 建站中找项目经理
  102 +
  103 + $fields = [
  104 + 'post_id' => $item['postid'],
  105 + 'company_name' => $item['company'],
  106 + 'title' => $item['title'] . " - V5",
  107 + 'engineer_id' => $engineer_id, // 第一负责人
  108 + 'assm_id' => $assm_id,
  109 + 'seom_id' => $seom_id,
  110 + 'website' => $item['main_url'] ?? '',
  111 + 'test_website' => $item['test_url'] ?? '',
  112 + 'is_del' => 0,
  113 + 'plan' => $item['plan'] ?? '',
  114 + 'project_cate' => 1,
  115 + 'pm_id' => $pm_id,
  116 + 'status' => $status, // 项目状态
  117 + 'wechat_group_id' => $item['wx_id']
  118 + ];
  119 + if (!$project) {
  120 + $new = new TicketProject();
  121 + $new->uuid = $uuid;
  122 + $new->version = 5;
  123 + $new->table_id = 0;
  124 + foreach ($fields as $k => $v) {
  125 + $new->$k = $v;
  126 + }
  127 + $new->save();
  128 + } else {
  129 + $changed = false;
  130 + foreach ($fields as $k => $v) {
  131 + if ($project->$k != $v) {
  132 + $project->$k = $v;
  133 + $changed = true;
  134 + }
  135 + }
  136 + if ($changed) {
  137 + $project->save();
105 } 138 }
106 } 139 }
107 - if ($changed) {  
108 - $project->save();  
109 - } 140 + echo now() . " | INFO | V5: {$item['postid']} {$item['company']} fetch ok \n";
110 } 141 }
  142 + $page++;
  143 + $postids = array_merge($postids, collect($items)->pluck('postid')->toArray());
111 } 144 }
112 - $postids = collect($items)->pluck('postid')->toArray(); 145 + }
  146 + if ($postids)
  147 + {
113 // 软删除 gl_ticket_projects 中不存在的项目 148 // 软删除 gl_ticket_projects 中不存在的项目
114 TicketProject::where('version', 5) 149 TicketProject::where('version', 5)
115 ->whereNotIn('post_id', $postids) 150 ->whereNotIn('post_id', $postids)
116 ->update(['is_del' => 1]); 151 ->update(['is_del' => 1]);
117 - echo date("Y-m-d H:i:s") . " V5: fetch completed, total " . count($items) . " items\n";  
118 } 152 }
119 } 153 }
120 154
@@ -142,26 +176,35 @@ class FetchTicketProjects extends Command @@ -142,26 +176,35 @@ class FetchTicketProjects extends Command
142 foreach ($items as $item) { 176 foreach ($items as $item) {
143 $uuid = md5("V6{$item->id}"); 177 $uuid = md5("V6{$item->id}");
144 $project = TicketProject::where('uuid', $uuid)->first(); 178 $project = TicketProject::where('uuid', $uuid)->first();
  179 + // 项目状态
  180 + if ($item->type == Project::TYPE_ONE)
  181 + $status = 1; // 建站中
  182 + elseif ($item->type == Project::TYPE_THREE)
  183 + $status = 2; // 建站完成
  184 + else
  185 + $status = 3; // 推广找售后服务经理
  186 +
145 // 售后服务经理 187 // 售后服务经理
146 $assm_id = collect([ 188 $assm_id = collect([
147 ManageHr::find($item->deploy_optimize->manager_mid)->manage_id ?? 0, 189 ManageHr::find($item->deploy_optimize->manager_mid)->manage_id ?? 0,
148 ManageHr::find($item->deploy_optimize->tech_leader)->manage_id ?? 0, 190 ManageHr::find($item->deploy_optimize->tech_leader)->manage_id ?? 0,
149 8, //张鸿飞 191 8, //张鸿飞
150 ])->first(fn($v) => $v !== null && $v !== 0, 0); 192 ])->first(fn($v) => $v !== null && $v !== 0, 0);
  193 +
151 // 优化师 194 // 优化师
152 - $optimist_mid = ManageHr::find($item->deploy_optimize->optimist_mid) ? ManageHr::find($item->deploy_optimize->optimist_mid)->manage_id : 0;  
153 - $seom_id = $optimist_mid ? $optimist_mid : $assm_id;  
154 - /**  
155 - * 第一负责人逻辑  
156 - * 建站类项目:找杨长远  
157 - * 推广类:找售后  
158 - */  
159 - if ($item->type == Project::TYPE_THREE) {  
160 - $engineer_id = Manage::where('name', '杨长远')->value('id') ?? 0; // 建站类项目找杨长远  
161 - }else {  
162 - // 其他找售后服务经理  
163 - $engineer_id = $assm_id;  
164 - } 195 + $seom_id = ManageHr::find($item->deploy_optimize->optimist_mid) ? ManageHr::find($item->deploy_optimize->optimist_mid)->manage_id : 0;
  196 +
  197 + // 项目经理
  198 + $pm_id = ManageHr::find($item->deploy_build->manager_mid)->manage_id ?? ManageHr::where('name', '李洁玉')->value('manage_id') ?? 0;
  199 +
  200 + // 第一负责人
  201 + if ($status == 1)
  202 + $engineer_id = $pm_id; // 建站中找项目经理
  203 + elseif ($status == 2)
  204 + $engineer_id = Manage::where('name', '杨长远')->value('id') ?? 0; // 建站完成找杨长远
  205 + else
  206 + $engineer_id = $assm_id; // 推广找售后服务经理
  207 +
165 $is_del = ( 208 $is_del = (
166 $item->extend_type == 5 209 $item->extend_type == 5
167 || $item->type == 8 210 || $item->type == 8
@@ -171,7 +214,7 @@ class FetchTicketProjects extends Command @@ -171,7 +214,7 @@ class FetchTicketProjects extends Command
171 214
172 $fields = [ 215 $fields = [
173 'company_name' => $item->company, 216 'company_name' => $item->company,
174 - 'title' => $item->title, 217 + 'title' => $item->title . " - V6",
175 'assm_id' => $assm_id, 218 'assm_id' => $assm_id,
176 'seom_id' => $seom_id, 219 'seom_id' => $seom_id,
177 'engineer_id' => $engineer_id, 220 'engineer_id' => $engineer_id,
@@ -181,10 +224,12 @@ class FetchTicketProjects extends Command @@ -181,10 +224,12 @@ class FetchTicketProjects extends Command
181 'version' => empty($item->version) ? 7 : $item->version, // 版本号 224 'version' => empty($item->version) ? 7 : $item->version, // 版本号
182 'plan' => $item->planMap()[$item->deploy_build->plan] ?? '', 225 'plan' => $item->planMap()[$item->deploy_build->plan] ?? '',
183 'project_cate' => 2, 226 'project_cate' => 2,
184 - 'wechat_group_id' => ProjectAssociation::where('project_id', $project->table_id) 227 + 'wechat_group_id' => ProjectAssociation::where('project_id', $item->id)
185 ->where('status', ProjectAssociation::STATUS_NORMAL) 228 ->where('status', ProjectAssociation::STATUS_NORMAL)
186 ->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT) 229 ->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)
187 - ->value('friend_id') 230 + ->value('friend_id'),
  231 + 'pm_id' => $pm_id,
  232 + 'status' => $status, // 项目状态
188 ]; 233 ];
189 if (!$project) { 234 if (!$project) {
190 $project = new TicketProject(); 235 $project = new TicketProject();
@@ -260,7 +305,13 @@ class FetchTicketProjects extends Command @@ -260,7 +305,13 @@ class FetchTicketProjects extends Command
260 $seom_id = 0; 305 $seom_id = 0;
261 // 第一负责人 306 // 第一负责人
262 $engineer_id = $assm_id; 307 $engineer_id = $assm_id;
263 - $is_del = 0; 308 + if ($project_cate == 3)
  309 + $is_del = !empty($item["go_online"]);
  310 + else{
  311 + // 域途,以服务时间为准 $item['yutu_service_start_time'] 是开始时间 + 有效天数 yutu_planday
  312 + $is_del = !empty($item['yutu_service_start_time']) && !empty($item['yutu_planday'])
  313 + && (strtotime($item['yutu_service_start_time']) + $item['yutu_planday'] * 86400) < time();
  314 + }
264 315
265 $fields = [ 316 $fields = [
266 'company_name' => $item['company'], 317 'company_name' => $item['company'],
@@ -3,7 +3,9 @@ @@ -3,7 +3,9 @@
3 namespace App\Console\Commands\WorkOrder; 3 namespace App\Console\Commands\WorkOrder;
4 4
5 use App\Models\Manage\Manage; 5 use App\Models\Manage\Manage;
  6 +use App\Models\Manage\ManageHr;
6 use App\Models\WorkOrder\TicketChat; 7 use App\Models\WorkOrder\TicketChat;
  8 +use App\Models\WorkOrder\TicketDing;
7 use App\Models\WorkOrder\TicketLog; 9 use App\Models\WorkOrder\TicketLog;
8 use App\Services\DingTalkService; 10 use App\Services\DingTalkService;
9 use Illuminate\Console\Command; 11 use Illuminate\Console\Command;
@@ -56,7 +58,7 @@ class WorkOrderDing extends Command @@ -56,7 +58,7 @@ class WorkOrderDing extends Command
56 sleep(3); 58 sleep(3);
57 continue; 59 continue;
58 } 60 }
59 - $mobile = $log->engineer->mobile; 61 + $mobile = ManageHr::where('manage_id', $log->engineer_id)->value('mobile');
60 $response = Http::withBasicAuth( 62 $response = Http::withBasicAuth(
61 env('DINGDING_BASIC_USER'), 63 env('DINGDING_BASIC_USER'),
62 env('DINGDING_BASIC_PASS') 64 env('DINGDING_BASIC_PASS')
@@ -64,8 +66,10 @@ class WorkOrderDing extends Command @@ -64,8 +66,10 @@ class WorkOrderDing extends Command
64 if ($response->status() == 200) { 66 if ($response->status() == 200) {
65 $userid = $response->json()['data']['userid']; 67 $userid = $response->json()['data']['userid'];
66 $ding = new DingTalkService(); 68 $ding = new DingTalkService();
  69 + $created_at = date('m-d H', strtotime($log->ticket->created_at));
  70 + $plan_end_at = date('m-d H', strtotime($log->ticket->plan_end_at));
67 $resp = $ding->danliao(json_encode([ 71 $resp = $ding->danliao(json_encode([
68 - 'text' => "您有新的工单(ID: {$log->ticket_id}),请及时处理!", 72 + 'text' => "您有新工单{$log->ticket_id}{$created_at}{$plan_end_at} 截止!",
69 'title' => 'AI协同工单 - ' . $log->ticket->project->title, 73 'title' => 'AI协同工单 - ' . $log->ticket->project->title,
70 'picUrl' => 'https://hub.globalso.com/logocm.png', 74 'picUrl' => 'https://hub.globalso.com/logocm.png',
71 'messageUrl' => 'https://oa.quanqiusou.cn/afterorder?project_id=' . $log->ticket->project->uuid, 75 'messageUrl' => 'https://oa.quanqiusou.cn/afterorder?project_id=' . $log->ticket->project->uuid,
@@ -142,4 +146,54 @@ class WorkOrderDing extends Command @@ -142,4 +146,54 @@ class WorkOrderDing extends Command
142 } 146 }
143 } 147 }
144 } 148 }
  149 +
  150 + /**
  151 + * 消费 gl_ticket_dings 表的钉钉通知
  152 + */
  153 + public function dingSql()
  154 + {
  155 + while (true) {
  156 + $tickDing = TicketDing::where('status', 0)->first();
  157 + if (!$tickDing) {
  158 + echo now() . " | INFO | 没有通知任务\n";
  159 + sleep(3);
  160 + continue;
  161 + }
  162 + try {
  163 + $mobiles = ManageHr::whereIn('manage_id', json_decode($tickDing->userIds))
  164 + ->pluck('mobile')
  165 + ->toArray();
  166 +
  167 + $response = Http::withBasicAuth(
  168 + env('DINGDING_BASIC_USER'),
  169 + env('DINGDING_BASIC_PASS')
  170 + )->get('https://oa.cmer.com/api/dingding/usersByMobiles', [
  171 + 'mobiles' => $mobiles,
  172 + ]);
  173 + if ($response->status() == 200) {
  174 + $users = $response->json()['data'];
  175 + $userIds = [];
  176 + foreach ($users as $user) {
  177 + $userIds[] = $user['userid'];
  178 + }
  179 + $ding = new DingTalkService();
  180 + $resp = $ding->danliao($tickDing->msgParam, $userIds, $tickDing->msgKey);
  181 + $tickDing->status = 1;
  182 + $tickDing->save();
  183 + echo now() . " | INFO | gl_ticket_dings ID: {$tickDing->id} 通知成功\n";
  184 + }else
  185 + {
  186 + $tickDing->status = 2; // 标记为失败
  187 + $tickDing->errorMsg = '钉钉用户查询失败';
  188 + $tickDing->save();
  189 + echo now() . " | ERROR | gl_ticket_dings ID: {$tickDing->id} 通知失败\n";
  190 + }
  191 + }catch (\Exception $exception){
  192 + echo now() . " | ERROR | gl_ticket_dings ID {$tickDing->id} {$exception->getMessage()} {$exception->getTraceAsString()} \n";
  193 + $ding->status = 2; // 标记为失败
  194 + $ding->errorMsg = $exception->getMessage();
  195 + $ding->save();
  196 + }
  197 + }
  198 + }
145 } 199 }
@@ -24,6 +24,7 @@ class Kernel extends ConsoleKernel @@ -24,6 +24,7 @@ class Kernel extends ConsoleKernel
24 $schedule->command('sync_channel')->dailyAt('06:00')->withoutOverlapping(1); // 渠道信息,每天执行一次 24 $schedule->command('sync_channel')->dailyAt('06:00')->withoutOverlapping(1); // 渠道信息,每天执行一次
25 $schedule->command('inquiry_count')->dailyAt('01:00')->withoutOverlapping(1); // 询盘统计数据,每天凌晨执行一次 25 $schedule->command('inquiry_count')->dailyAt('01:00')->withoutOverlapping(1); // 询盘统计数据,每天凌晨执行一次
26 $schedule->command('share_user')->dailyAt('01:20')->withoutOverlapping(1);// 每天凌晨1点执行一次 26 $schedule->command('share_user')->dailyAt('01:20')->withoutOverlapping(1);// 每天凌晨1点执行一次
  27 + $schedule->command('ai_domain')->dailyAt('01:20')->withoutOverlapping(1);// 每天凌晨1点执行一次(同步ai域名)
27 $schedule->command('last_inquiry')->dailyAt('04:00')->withoutOverlapping(1);// 最近一次询盘信息 28 $schedule->command('last_inquiry')->dailyAt('04:00')->withoutOverlapping(1);// 最近一次询盘信息
28 $schedule->command('update_seo_tdk_crontab')->dailyAt('20:00')->withoutOverlapping(1); //更新上线项目TDK 29 $schedule->command('update_seo_tdk_crontab')->dailyAt('20:00')->withoutOverlapping(1); //更新上线项目TDK
29 $schedule->command('sync_manager')->dailyAt('01:00')->withoutOverlapping(1); //TODO::手机号码同步 每天执行一次 30 $schedule->command('sync_manager')->dailyAt('01:00')->withoutOverlapping(1); //TODO::手机号码同步 每天执行一次
@@ -757,6 +757,9 @@ class Translate @@ -757,6 +757,9 @@ class Translate
757 return ''; 757 return '';
758 } 758 }
759 $result = self::translate($texts, $tls); 759 $result = self::translate($texts, $tls);
  760 + if(!isset($result[0]['texts']) || empty($result[0]['texts'])){
  761 + $result = self::translate($texts, $tls);
  762 + }
760 return $result[0]['texts'] ?? ''; 763 return $result[0]['texts'] ?? '';
761 } 764 }
762 /** 765 /**
@@ -32,12 +32,6 @@ if (!function_exists('generateRoute')) { @@ -32,12 +32,6 @@ if (!function_exists('generateRoute')) {
32 $string = $string[0]; 32 $string = $string[0];
33 } 33 }
34 $sign = str_replace(".", "", trim(strtolower(preg_replace('/[^\w.]+/', '-', trim($string))), '-')); 34 $sign = str_replace(".", "", trim(strtolower(preg_replace('/[^\w.]+/', '-', trim($string))), '-'));
35 - if (strpos($sign, 'well-known-') === 0) {  
36 - $sign = substr($sign, strlen('well-known-'));  
37 - }  
38 - if(empty($sign)){  
39 - $sign = 'u';  
40 - }  
41 return $sign; 35 return $sign;
42 } 36 }
43 } 37 }
@@ -8,10 +8,8 @@ use App\Http\Requests\Api\WorkOrder\TicketStoreRequest; @@ -8,10 +8,8 @@ use App\Http\Requests\Api\WorkOrder\TicketStoreRequest;
8 use App\Models\WorkOrder\TicketLog; 8 use App\Models\WorkOrder\TicketLog;
9 use App\Models\WorkOrder\TicketProject; 9 use App\Models\WorkOrder\TicketProject;
10 use App\Models\WorkOrder\Tickets; 10 use App\Models\WorkOrder\Tickets;
11 -use Darabonba\GatewaySpi\Models\InterceptorContext\response;  
12 use Illuminate\Http\Request; 11 use Illuminate\Http\Request;
13 use Illuminate\Support\Facades\DB; 12 use Illuminate\Support\Facades\DB;
14 -use Illuminate\Support\Facades\Http;  
15 13
16 class TicketController extends BaseController 14 class TicketController extends BaseController
17 { 15 {
@@ -29,8 +27,11 @@ class TicketController extends BaseController @@ -29,8 +27,11 @@ class TicketController extends BaseController
29 $size = (int)$request->input('size', 10); 27 $size = (int)$request->input('size', 10);
30 28
31 $tickets = Tickets::with([ 29 $tickets = Tickets::with([
32 - 'project:*',  
33 - 'logs.engineer:id,name', 30 + 'project.pm',
  31 + 'project.assm',
  32 + 'project.seom',
  33 + 'project.first_engineer',
  34 + 'logs.engineer',
34 ]) 35 ])
35 ->where('project_id', $project->id) 36 ->where('project_id', $project->id)
36 // ->where('submit_side', 2) 37 // ->where('submit_side', 2)
@@ -85,9 +86,7 @@ class TicketController extends BaseController @@ -85,9 +86,7 @@ class TicketController extends BaseController
85 $ticket->submit_side = 2; // 2 for B-side submission 86 $ticket->submit_side = 2; // 2 for B-side submission
86 $ticket->submit_username = $request->input('submit_username'); 87 $ticket->submit_username = $request->input('submit_username');
87 $ticket->save(); 88 $ticket->save();
88 - $log = new TicketLog();  
89 - $log->engineer_id = $project->engineer_id; // 默认第一负责人  
90 - $ticket->logs()->save($log); 89 + $ticket->saveEngineers([$project->engineer_id]);
91 $project->pushWechatGroupMsg("客户新增了工单(ID:{$ticket->id}),请及时处理!"); 90 $project->pushWechatGroupMsg("客户新增了工单(ID:{$ticket->id}),请及时处理!");
92 return $ticket; 91 return $ticket;
93 }); 92 });
@@ -143,13 +142,13 @@ class TicketController extends BaseController @@ -143,13 +142,13 @@ class TicketController extends BaseController
143 public function projectInfo($project_id) 142 public function projectInfo($project_id)
144 { 143 {
145 $project = TicketProject::with([ 144 $project = TicketProject::with([
146 - 'projectV6:id,company',  
147 - 'assm:id,name',  
148 - 'seom:id,name',  
149 - 'first_engineer:id,name', 145 + 'pm',
  146 + 'assm',
  147 + 'seom',
  148 + 'first_engineer',
150 ]) 149 ])
151 ->where('uuid', $project_id)->first(); 150 ->where('uuid', $project_id)->first();
152 - if (!$project) return $this->response('未找到项目', 404); 151 + if (!$project) return response()->json(['message' => '未找到对应的工单项目'], 404);
153 return response()->json(['data' => $project]); 152 return response()->json(['data' => $project]);
154 } 153 }
155 154
@@ -6,6 +6,7 @@ use App\Enums\Common\Code; @@ -6,6 +6,7 @@ use App\Enums\Common\Code;
6 use App\Http\Controllers\Aside\BaseController; 6 use App\Http\Controllers\Aside\BaseController;
7 use App\Http\Logic\Aside\Domain\DomainInfoLogic; 7 use App\Http\Logic\Aside\Domain\DomainInfoLogic;
8 use App\Http\Requests\Aside\Domain\DomainInfoRequest; 8 use App\Http\Requests\Aside\Domain\DomainInfoRequest;
  9 +use App\Models\Devops\ServersIp;
9 use App\Models\Domain\CountryCode; 10 use App\Models\Domain\CountryCode;
10 use App\Models\Domain\DomainInfo; 11 use App\Models\Domain\DomainInfo;
11 use App\Models\Project\Project; 12 use App\Models\Project\Project;
@@ -34,17 +35,24 @@ class DomainInfoController extends BaseController @@ -34,17 +35,24 @@ class DomainInfoController extends BaseController
34 $lists = $domainModel->lists($this->map,$this->page,$this->row,$this->order); 35 $lists = $domainModel->lists($this->map,$this->page,$this->row,$this->order);
35 if(!empty($lists)){ 36 if(!empty($lists)){
36 $project_model = new Project(); 37 $project_model = new Project();
  38 + $serverIpModel = new ServersIp();
37 foreach ($lists['list'] as $k=>$v){ 39 foreach ($lists['list'] as $k=>$v){
38 - $company = '';  
39 - $pro_info = $project_model->read(['id'=>$v['project_id']],'company');  
40 - if($pro_info){  
41 - $company = $pro_info['company']; 40 + $v['company'] = '';
  41 + $v['ip_domain'] = [];
  42 + $pro_info = $project_model->read(['id'=>$v['project_id']],['company','serve_id']);
  43 + if($pro_info !== false){
  44 + $v['company'] = $pro_info['company'];
  45 + if(!empty($pro_info['serve_id'])){
  46 + $serveInfo = $serverIpModel->read(['id'=>$pro_info['serve_id']],['domain','ip']);
  47 + $v['ip_domain'] = $serveInfo;
  48 + }
42 } 49 }
43 - $lists['list'][$k]['company'] = $company; 50 + $lists['list'][$k] = $v;
44 } 51 }
45 } 52 }
46 - $lists['y_status'] = $domainModel->counts(['status'=>1]);  
47 - $lists['n_status'] = $domainModel->counts(['status'=>0]); 53 + $lists['total_count'] = $domainModel->counts();
  54 + $lists['y_count'] = $domainModel->counts(['status'=>1]);
  55 + $lists['n_count'] = $domainModel->counts(['status'=>0]);
48 return $this->response('success', Code::SUCCESS, $lists); 56 return $this->response('success', Code::SUCCESS, $lists);
49 } 57 }
50 58
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoArticleController.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/7/14 16:19
  8 + */
  9 +
  10 +namespace App\Http\Controllers\Aside\Geo;
  11 +
  12 +use App\Enums\Common\Code;
  13 +use App\Http\Controllers\Aside\BaseController;
  14 +use App\Http\Logic\Aside\Geo\GeoArticleLogic;
  15 +use Illuminate\Http\Request;
  16 +
  17 +/**
  18 + * @remark :GEO文章列表数据
  19 + * @name :GeoArticleController
  20 + * @author :lyh
  21 + * @method :post
  22 + * @time :2025/7/14 16:19
  23 + */
  24 +class GeoArticleController extends BaseController
  25 +{
  26 + public function __construct(Request $request)
  27 + {
  28 + parent::__construct($request);
  29 + $this->logic = new GeoArticleLogic();
  30 + }
  31 + /**
  32 + * @remark :文章列表数据
  33 + * @name :lists
  34 + * @author :lyh
  35 + * @method :post
  36 + * @time :2025/7/14 16:22
  37 + */
  38 + public function lists(){
  39 + $this->request->validate([
  40 + 'project_id'=>'required',
  41 + ],[
  42 + 'project_id.required' => '项目ID不能为空',
  43 + ]);
  44 + $lists = $this->logic->getArticleList($this->map,$this->page,$this->row,$this->order);
  45 + $this->response('success',Code::SUCCESS,$lists);
  46 + }
  47 +
  48 + /**
  49 + * @remark :获取数据详情
  50 + * @name :info
  51 + * @author :lyh
  52 + * @method :post
  53 + * @time :2025/7/14 17:05
  54 + */
  55 + public function info(){
  56 + $this->request->validate([
  57 + 'id'=>'required',
  58 + ],[
  59 + 'id.required' => 'ID不能为空',
  60 + ]);
  61 + $data = $this->logic->getArticleInfo();
  62 + $this->response('success',Code::SUCCESS,$data);
  63 + }
  64 +
  65 + /**
  66 + * @remark :更新单个数据
  67 + * @name :edit
  68 + * @author :lyh
  69 + * @method :post
  70 + * @time :2025/7/15 10:31
  71 + */
  72 + public function edit(){
  73 + $this->request->validate([
  74 + 'id'=>'required',
  75 + 'url'=>'required',
  76 + 'filename'=>'required',
  77 + ],[
  78 + 'id.required' => 'ID不能为空',
  79 + 'url.required' => '链接不能为空',
  80 + 'filename.required' => '文件名称不能为空',
  81 + ]);
  82 + $data = $this->logic->editArticle();
  83 + $this->response('success',Code::SUCCESS,$data);
  84 + }
  85 +
  86 + /**
  87 + * @remark :保存数据
  88 + * @name :save
  89 + * @author :lyh
  90 + * @method :post
  91 + * @time :2025/7/14 17:05
  92 + * @param :data->数组(filename:文件名称 url:链接)
  93 + * @json :测试数据 {"project_id": 1,"data": [{"url": "/upload/m/file/2023-09/6513d00870e0821127.pdf","filename": "测试文件1"}]}
  94 + */
  95 + public function save(){
  96 + $this->request->validate([
  97 + 'project_id'=>'required',
  98 + 'data'=>'required|array'
  99 + ],[
  100 + 'project_id.required' => '项目ID不能为空',
  101 + 'data.required' => '数据详情不能为空',
  102 + 'data.array' => '数据详情为数组',
  103 + ]);
  104 + $data = $this->logic->saveArticle();
  105 + $this->response('success',Code::SUCCESS,$data);
  106 + }
  107 +
  108 + /**
  109 + * @remark :删除数据
  110 + * @name :del
  111 + * @author :lyh
  112 + * @method :post
  113 + * @time :2025/7/14 17:05
  114 + */
  115 + public function del(){
  116 + $this->request->validate([
  117 + 'ids'=>'required|array',
  118 + ],[
  119 + 'ids.required' => 'IDs不能为空',
  120 + 'ids.array' => '数据详情为数组',
  121 + ]);
  122 + $data = $this->logic->delArticle();
  123 + $this->response('success',Code::SUCCESS,$data);
  124 + }
  125 +}
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoLinkController.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/7/14 16:17
  8 + */
  9 +
  10 +namespace App\Http\Controllers\Aside\Geo;
  11 +
  12 +use App\Enums\Common\Code;
  13 +use App\Http\Controllers\Aside\BaseController;
  14 +use App\Http\Logic\Aside\Geo\GeoLinkLogic;
  15 +use Illuminate\Http\Request;
  16 +/**
  17 + * @remark :geo权威新闻(链接数据)
  18 + * @name :GeoLinkController
  19 + * @author :lyh
  20 + * @method :post
  21 + * @time :2025/7/14 16:18
  22 + */
  23 +class GeoLinkController extends BaseController
  24 +{
  25 + public function __construct(Request $request)
  26 + {
  27 + parent::__construct($request);
  28 + $this->logic = new GeoLinkLogic();
  29 + }
  30 +
  31 + /**
  32 + * @remark :获取链接数据列表
  33 + * @name :lists
  34 + * @author :lyh
  35 + * @method :post
  36 + * @time :2025/7/15 9:14
  37 + */
  38 + public function lists(){
  39 + $this->request->validate([
  40 + 'project_id'=>'required',
  41 + ],[
  42 + 'project_id.required' => '项目ID不能为空',
  43 + ]);
  44 + $lists = $this->logic->getLinkList($this->map,$this->page,$this->row,$this->order);
  45 + $this->response('success',Code::SUCCESS,$lists);
  46 + }
  47 +
  48 + /**
  49 + * @remark :获取数据详情
  50 + * @name :info
  51 + * @author :lyh
  52 + * @method :post
  53 + * @time :2025/7/15 9:15
  54 + */
  55 + public function info(){
  56 + $this->request->validate([
  57 + 'id'=>'required',
  58 + ],[
  59 + 'id.required' => 'ID不能为空',
  60 + ]);
  61 + $data = $this->logic->getLinkInfo();
  62 + $this->response('success',Code::SUCCESS,$data);
  63 + }
  64 +
  65 + /**
  66 + * @remark :保存数据
  67 + * @name :save
  68 + * @author :lyh
  69 + * @method :post
  70 + * @time :2025/7/15 9:17
  71 + */
  72 + public function save(){
  73 + $this->request->validate([
  74 + 'project_id'=>'required',
  75 + 'data'=>'required|array'
  76 + ],[
  77 + 'project_id.required' => '项目ID不能为空',
  78 + 'data.required' => '数据详情不能为空',
  79 + 'data.array' => '数据详情为数组',
  80 + ]);
  81 + $data = $this->logic->saveLink();
  82 + $this->response('success',Code::SUCCESS,$data);
  83 + }
  84 +
  85 + /**
  86 + * @remark :删除数据
  87 + * @name :del
  88 + * @author :lyh
  89 + * @method :post
  90 + * @time :2025/7/15 9:17
  91 + */
  92 + public function del(){
  93 + $this->request->validate([
  94 + 'ids'=>'required|array',
  95 + ],[
  96 + 'ids.required' => 'IDs不能为空',
  97 + 'ids.array' => '数据详情为数组',
  98 + ]);
  99 + $data = $this->logic->delLink();
  100 + $this->response('success',Code::SUCCESS,$data);
  101 + }
  102 +}
@@ -33,9 +33,11 @@ use App\Models\Project\ProcessRecords; @@ -33,9 +33,11 @@ use App\Models\Project\ProcessRecords;
33 use App\Models\Project\Project; 33 use App\Models\Project\Project;
34 use App\Models\Project\ProjectUpdateTdk; 34 use App\Models\Project\ProjectUpdateTdk;
35 use App\Models\Project\RenewLog; 35 use App\Models\Project\RenewLog;
  36 +use App\Models\ProjectAssociation\ProjectAssociation;
36 use App\Models\RankData\RankData; 37 use App\Models\RankData\RankData;
37 use App\Models\Task\Task; 38 use App\Models\Task\Task;
38 use App\Models\WebSetting\WebLanguage; 39 use App\Models\WebSetting\WebLanguage;
  40 +use App\Models\WorkOrder\TicketProject;
39 use Illuminate\Http\Request; 41 use Illuminate\Http\Request;
40 use Illuminate\Support\Facades\DB; 42 use Illuminate\Support\Facades\DB;
41 43
@@ -70,23 +72,13 @@ class ProjectController extends BaseController @@ -70,23 +72,13 @@ class ProjectController extends BaseController
70 if(!empty($lists) && !empty($lists['list'])){ 72 if(!empty($lists) && !empty($lists['list'])){
71 foreach ($lists['list'] as $k => $v){ 73 foreach ($lists['list'] as $k => $v){
72 $v = $this->handleParam($v); 74 $v = $this->handleParam($v);
73 -  
74 - // 组装 工单UUID  
75 - $ticketProject = null;  
76 - $ticketProject = DB::table('gl_ticket_projects')  
77 - ->where('table_id', $v['id'])  
78 - ->where('project_cate', 2)  
79 - ->first();  
80 - $v['uuid'] = $ticketProject ? $ticketProject->uuid : null;  
81 // 组装 工单UUID END 75 // 组装 工单UUID END
82 -  
83 $lists['list'][$k] = $v; 76 $lists['list'][$k] = $v;
84 } 77 }
85 } 78 }
86 $this->response('success',Code::SUCCESS,$lists); 79 $this->response('success',Code::SUCCESS,$lists);
87 } 80 }
88 81
89 -  
90 /** 82 /**
91 * 需要查询的字段 83 * 需要查询的字段
92 * @return array 84 * @return array
@@ -458,6 +450,8 @@ class ProjectController extends BaseController @@ -458,6 +450,8 @@ class ProjectController extends BaseController
458 $item['task_finish_num'] = Task::getNumByProjectId($item['id'], Task::STATUS_DOWN); 450 $item['task_finish_num'] = Task::getNumByProjectId($item['id'], Task::STATUS_DOWN);
459 $item['task_pending_num'] = Task::getNumByProjectId($item['id'], [Task::STATUS_DONGING, Task::STATUS_WAIT]); 451 $item['task_pending_num'] = Task::getNumByProjectId($item['id'], [Task::STATUS_DONGING, Task::STATUS_WAIT]);
460 $item['collect_time'] = $item['is_upgrade'] ? UpdateLog::getProjectUpdate($item['id']) : ''; 452 $item['collect_time'] = $item['is_upgrade'] ? UpdateLog::getProjectUpdate($item['id']) : '';
  453 + $item['uuid'] = TicketProject::where('table_id', $item['id'])->where('project_cate', 2)->value('uuid') ?? null;
  454 + $item['friend_id'] = ProjectAssociation::where('project_id', $item['id'])->where('status', ProjectAssociation::STATUS_NORMAL)->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)->value('friend_id') ?? null;
461 return $item; 455 return $item;
462 } 456 }
463 457
@@ -86,9 +86,15 @@ class ProjectAssociationController extends BaseController @@ -86,9 +86,15 @@ class ProjectAssociationController extends BaseController
86 $this->response('success', Code::SERVER_ERROR); 86 $this->response('success', Code::SERVER_ERROR);
87 } 87 }
88 $cache = isset($cache); 88 $cache = isset($cache);
89 - $search = request()->input('search'); 89 +
  90 + try {
  91 + $search = request()->input('search');
90 // $result = ProjectAssociationServices::getInstance()->getAiccWechatLists($isRes, $app, $cache); 92 // $result = ProjectAssociationServices::getInstance()->getAiccWechatLists($isRes, $app, $cache);
91 - $result = ProjectAssociationServices::getInstance()->getWorkChatRoomList($search, $isRes->friend_id); 93 + $result = ProjectAssociationServices::getInstance()->getWorkChatRoomList($search, $isRes->friend_id);
  94 + }catch (\Exception $e){
  95 + $result = [];
  96 + }
  97 +
92 $result['info'] = [ 98 $result['info'] = [
93 'friend_id' => $isRes->friend_id ?? 0 99 'friend_id' => $isRes->friend_id ?? 0
94 ]; 100 ];
@@ -94,6 +94,13 @@ class AdsController extends BaseController @@ -94,6 +94,13 @@ class AdsController extends BaseController
94 foreach ($item['target'] as $k=>$target){ 94 foreach ($item['target'] as $k=>$target){
95 $repeat = ReInquiryTask::where('target', 'like', '%"'.$target['url'].'"%')->where('id', '<>', $item['id'])->first(); 95 $repeat = ReInquiryTask::where('target', 'like', '%"'.$target['url'].'"%')->where('id', '<>', $item['id'])->first();
96 $item['target'][$k]['is_repeat'] = $repeat ? 1 : 0; 96 $item['target'][$k]['is_repeat'] = $repeat ? 1 : 0;
  97 + $item['target'][$k]['is_ad_fee'] = 0;
  98 + if($item['target'][$k]['is_v6']){
  99 + $project = Project::getProjectByDomain($item['target'][$k]['url']);
  100 + if($project && $project->deploy_build){
  101 + $item['target'][$k]['is_ad_fee'] = $project->deploy_build->ads_price ? 1 : 0;
  102 + }
  103 + }
97 } 104 }
98 $item['cost'] = ReInquiryCost::getCostByAdIds($item['ad_id']); 105 $item['cost'] = ReInquiryCost::getCostByAdIds($item['ad_id']);
99 } 106 }
@@ -395,10 +402,14 @@ class AdsController extends BaseController @@ -395,10 +402,14 @@ class AdsController extends BaseController
395 $is_v6 = 1; 402 $is_v6 = 1;
396 } 403 }
397 } 404 }
  405 + $item['project_id'] = 0;
  406 + $item['is_ad_fee'] = 0;
398 if($is_v6){ 407 if($is_v6){
399 - $item['project_id'] = DomainInfo::where('domain', $item['domain'])->value('project_id') ?: 0;  
400 - }else{  
401 - $item['project_id'] = 0; 408 + $project = Project::getProjectByDomain($item['domain']);
  409 + if($project && $project->deploy_build){
  410 + $item['project_id'] = $project['id'];
  411 + $item['is_ad_fee'] = $project->deploy_build->ads_price ? 1 : 0;
  412 + }
402 } 413 }
403 } 414 }
404 415
@@ -8,7 +8,7 @@ use App\Http\Requests\Aside\WorkOrder\AsideTicketStoreRequest; @@ -8,7 +8,7 @@ use App\Http\Requests\Aside\WorkOrder\AsideTicketStoreRequest;
8 use App\Http\Requests\Aside\WorkOrder\AsideTicketListRequest; 8 use App\Http\Requests\Aside\WorkOrder\AsideTicketListRequest;
9 use App\Http\Requests\Aside\WorkOrder\AsideTicketUpdateRequest; 9 use App\Http\Requests\Aside\WorkOrder\AsideTicketUpdateRequest;
10 use App\Http\Requests\Aside\WorkOrder\TicketProjectListRequest; 10 use App\Http\Requests\Aside\WorkOrder\TicketProjectListRequest;
11 -use App\Models\Workchat\MessagePush; 11 +use App\Models\Manage\ManageHr;
12 use App\Models\WorkOrder\TicketLog; 12 use App\Models\WorkOrder\TicketLog;
13 use App\Models\WorkOrder\TicketProject; 13 use App\Models\WorkOrder\TicketProject;
14 use App\Models\WorkOrder\Tickets; 14 use App\Models\WorkOrder\Tickets;
@@ -26,8 +26,8 @@ class AsideTicketController extends BaseController @@ -26,8 +26,8 @@ class AsideTicketController extends BaseController
26 { 26 {
27 $validated = $request->validated(); 27 $validated = $request->validated();
28 $lists = Tickets::with([ 28 $lists = Tickets::with([
29 - 'logs.engineer:id,name',  
30 - 'project', 29 + 'logs.engineer',
  30 + 'project.pm',
31 ]) 31 ])
32 ->when(!empty($validated['engineer_id']), function ($query) use ($validated) { 32 ->when(!empty($validated['engineer_id']), function ($query) use ($validated) {
33 // 查 gl_tickets 表 submit_user_id 或 gl_ticket_logs 表 engineer_id 33 // 查 gl_tickets 表 submit_user_id 或 gl_ticket_logs 表 engineer_id
@@ -51,6 +51,10 @@ class AsideTicketController extends BaseController @@ -51,6 +51,10 @@ class AsideTicketController extends BaseController
51 $status = $request->input('status'); 51 $status = $request->input('status');
52 return $query->where('status', $status); 52 return $query->where('status', $status);
53 }) 53 })
  54 + ->when($request->input('star') !== null, function ($query) use ($request) {
  55 + $star = $request->input('star');
  56 + return $query->where('star', $star);
  57 + })
54 ->when($request->input('search'), function ($query) use ($request) { 58 ->when($request->input('search'), function ($query) use ($request) {
55 // search 查 gl_tickets.title 或 gl_ticket_projects.title 或 gl_ticket_projects.company_name 59 // search 查 gl_tickets.title 或 gl_ticket_projects.title 或 gl_ticket_projects.company_name
56 $search = $request->input('search'); 60 $search = $request->input('search');
@@ -92,7 +96,31 @@ class AsideTicketController extends BaseController @@ -92,7 +96,31 @@ class AsideTicketController extends BaseController
92 public function projectList(TicketProjectListRequest $request) 96 public function projectList(TicketProjectListRequest $request)
93 { 97 {
94 $validated = $request->validated(); 98 $validated = $request->validated();
  99 + $dept_id = ManageHr::where('manage_id', $this->manage['id'])
  100 + ->value('dept_id');
  101 +
95 $lists = TicketProject::where('is_del', 0) 102 $lists = TicketProject::where('is_del', 0)
  103 + ->when(($this->manage['role'] != 1 && $dept_id != 5), function ($query) use ($dept_id) {
  104 + /**
  105 + * 超管看所有项目 $this->manage['role']=1
  106 + * 全球搜: 技术部ID 1、售后部ID 2
  107 + * 超迹AI: AICC技术部 ID 4
  108 + * 域途:域途运营部 ID 17
  109 + */
  110 + if (in_array($dept_id, [1, 2])) {
  111 + // V5 V6
  112 + return $query->whereIn('project_cate', [1, 2]);
  113 + }elseif ($dept_id == 4) {
  114 + // 超迹AI
  115 + return $query->where('project_cate', 3);
  116 + }elseif ($dept_id == 17){
  117 + // 域途
  118 + return $query->where('project_cate', 4);
  119 + }else{
  120 + // 其他部门,不允许看数据
  121 + return $query->where('id', 0); // 返回空结果
  122 + }
  123 + })
96 ->when(!empty($validated['search']), function ($query) use ($validated) { 124 ->when(!empty($validated['search']), function ($query) use ($validated) {
97 // 查找项目名称或公司名称 125 // 查找项目名称或公司名称
98 $search = $validated['search']; 126 $search = $validated['search'];
@@ -141,14 +169,15 @@ class AsideTicketController extends BaseController @@ -141,14 +169,15 @@ class AsideTicketController extends BaseController
141 $ticket->submit_side = 1; // 1 for A-side submission 169 $ticket->submit_side = 1; // 1 for A-side submission
142 $ticket->submit_user_id = $this->manage['id']; 170 $ticket->submit_user_id = $this->manage['id'];
143 $ticket->submit_username = $this->manage['name']; 171 $ticket->submit_username = $this->manage['name'];
  172 + $ticket->star = $request->input('star', 3);
  173 + $ticket->plan_end_at = $request->input('plan_end_at', null);
144 $ticket->save(); 174 $ticket->save();
145 - // A 端提工单,都是针对客户提的需求等开发任务;比如翻译,修改页面等。。。  
146 - foreach ($request->input('engineer_ids', []) as $engineer_id) {  
147 - $log = new TicketLog();  
148 - $log->engineer_id = $engineer_id;  
149 - $ticket->logs()->save($log);  
150 - }  
151 - $project->pushWechatGroupMsg("创贸({$ticket->submit_username})新增了工单(ID:{$ticket->id}),请及时处理!"); 175 +
  176 + // 分配工单参与人
  177 + $ticket->saveEngineers($request->input('engineer_ids', []));
  178 + $nickname = ManageHr::where('manage_id', $this->manage['id'])->value('nickname') ?? mb_substr($ticket->submit_username, 0, 1) . '**';
  179 + if ($project->wechat_switch)
  180 + $project->pushWechatGroupMsg("创贸({$nickname})新增了工单(ID:{$ticket->id}),请及时处理!");
152 return $ticket; 181 return $ticket;
153 }); 182 });
154 $this->response('success', Code::SUCCESS, $result->toArray()); 183 $this->response('success', Code::SUCCESS, $result->toArray());
@@ -186,27 +215,32 @@ class AsideTicketController extends BaseController @@ -186,27 +215,32 @@ class AsideTicketController extends BaseController
186 // 开始修改 215 // 开始修改
187 $result = DB::transaction(function () use ($request, $ticket) { 216 $result = DB::transaction(function () use ($request, $ticket) {
188 if ($request->input('engineer_ids')) 217 if ($request->input('engineer_ids'))
189 - {  
190 - // 有邀请工程师协同处理  
191 - foreach ($request->input('engineer_ids') as $engineer_id)  
192 - {  
193 - try {  
194 - // 利用唯一索引去重  
195 - $new_log = new TicketLog();  
196 - $new_log->engineer_id = $engineer_id;  
197 - $ticket->logs()->save($new_log);  
198 - }catch (\Exception $exception){}  
199 - }  
200 - } 218 + $ticket->saveEngineers($request->input('engineer_ids'));
  219 +
  220 + // 其他字段有提交数据才修改,比如star plan_end_at
  221 + if ($request->input('title'))
  222 + $ticket->title = $request->input('title');
  223 + if ($request->input('content'))
  224 + $ticket->content = $request->input('content');
  225 + if ($request->input('star'))
  226 + $ticket->star = $request->input('star');
  227 + if ($request->input('plan_end_at'))
  228 + $ticket->plan_end_at = $request->input('plan_end_at');
  229 + if ($request->input('status'))
  230 + $ticket->status = $request->input('status');
201 231
202 - $ticket->reply = $request->input('reply', null);  
203 - $ticket->status = $request->input('status', $ticket->status);  
204 if ($ticket->status == Tickets::STATUS_COMPLETED) 232 if ($ticket->status == Tickets::STATUS_COMPLETED)
205 { 233 {
206 // 完成工单,把子任务里面未完成的工单改为完成 234 // 完成工单,把子任务里面未完成的工单改为完成
207 $ticket->end_at = now(); 235 $ticket->end_at = now();
208 - $ticket->logs()->where('status', '<', TicketLog::STATUS_COMPLETED) 236 + $ticket->logs()->where('status', '<', TicketLog::STATUS_COMPLETED)->where('is_engineer', 1)
209 ->update(['status' => TicketLog::STATUS_COMPLETED, 'end_at' => now()]); 237 ->update(['status' => TicketLog::STATUS_COMPLETED, 'end_at' => now()]);
  238 + // 推动微信通知
  239 + $project = $ticket->project;
  240 + if ($project->wechat_switch)
  241 + $project->pushWechatGroupMsg("工单(ID:{$ticket->id})已全部完成,请访问查看详情!");
  242 + $ticket->pushDing('finish');
  243 +
210 } 244 }
211 $ticket->save(); 245 $ticket->save();
212 return $ticket; 246 return $ticket;
@@ -241,7 +275,8 @@ class AsideTicketController extends BaseController @@ -241,7 +275,8 @@ class AsideTicketController extends BaseController
241 if (empty($project->wechat_group_id)) { 275 if (empty($project->wechat_group_id)) {
242 $this->response('该工单没有绑定的企微群', Code::USER_MODEL_NOTFOUND_ERROE); 276 $this->response('该工单没有绑定的企微群', Code::USER_MODEL_NOTFOUND_ERROE);
243 } 277 }
244 - $project->pushWechatGroupMsg(); 278 + if ($project->wechat_switch)
  279 + $project->pushWechatGroupMsg();
245 $this->response('success', Code::SUCCESS); 280 $this->response('success', Code::SUCCESS);
246 } 281 }
247 282
@@ -78,17 +78,24 @@ class AsideTicketLogController extends BaseController @@ -78,17 +78,24 @@ class AsideTicketLogController extends BaseController
78 // 是否有未完成的子任务 78 // 是否有未完成的子任务
79 $pending = $ticket->logs() 79 $pending = $ticket->logs()
80 ->where('status', '<', TicketLog::STATUS_COMPLETED) 80 ->where('status', '<', TicketLog::STATUS_COMPLETED)
  81 + ->where('is_engineer', 1)
81 ->count(); 82 ->count();
82 if ($pending) 83 if ($pending)
83 { 84 {
84 $ticket->status = Tickets::STATUS_PROCESSING; 85 $ticket->status = Tickets::STATUS_PROCESSING;
85 }else 86 }else
86 { 87 {
87 - $ticket->status = Tickets::STATUS_COMPLETED;  
88 // 如果所有子任务都完成了,则将工单状态改为已完成 88 // 如果所有子任务都完成了,则将工单状态改为已完成
89 - $ticket->end_at = now();  
90 - $project = $ticket->project;  
91 - $project->pushWechatGroupMsg("工单(ID:{$ticket->id})已全部完成,请访问查看详情!"); 89 + // todo 注意:建站期间的工单,所有人都完成后,不自动完成工单,需要项目经理验收修改工单状态
  90 + if (!($ticket->project->status == 1 && in_array($ticket->project->project_cate, [1, 2])))
  91 + {
  92 + $ticket->status = Tickets::STATUS_COMPLETED;
  93 + $ticket->end_at = now();
  94 + $project = $ticket->project;
  95 + if ($project->wechat_switch)
  96 + $project->pushWechatGroupMsg("工单(ID:{$ticket->id})已全部完成,请访问查看详情!");
  97 + $ticket->pushDing('finish');
  98 + }
92 } 99 }
93 $ticket->save(); 100 $ticket->save();
94 return $log; 101 return $log;
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Aside\WorkOrder; @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Aside\WorkOrder;
5 use App\Enums\Common\Code; 5 use App\Enums\Common\Code;
6 use App\Http\Controllers\Aside\BaseController; 6 use App\Http\Controllers\Aside\BaseController;
7 use App\Http\Requests\Api\WorkOrder\TicketChatStoreRequest; 7 use App\Http\Requests\Api\WorkOrder\TicketChatStoreRequest;
  8 +use App\Models\Manage\ManageHr;
8 use App\Models\WorkOrder\TicketChat; 9 use App\Models\WorkOrder\TicketChat;
9 use App\Models\WorkOrder\Tickets; 10 use App\Models\WorkOrder\Tickets;
10 use Illuminate\Http\Request; 11 use Illuminate\Http\Request;
@@ -64,7 +65,8 @@ class TicketChatController extends BaseController @@ -64,7 +65,8 @@ class TicketChatController extends BaseController
64 $chat->manage_id = $this->manage['id']; 65 $chat->manage_id = $this->manage['id'];
65 $chat->save(); 66 $chat->save();
66 $project = $ticket->project; 67 $project = $ticket->project;
67 - $project->pushWechatGroupMsg("{$chat->submit_username}对工单(ID:{$ticket->id})进行了补充,请及时查看处理!"); 68 + $nickname = ManageHr::where('manage_id', $this->manage['id'])->value('nickname') ?? mb_substr($ticket->submit_username, 0, 1) . '**';
  69 + $project->pushWechatGroupMsg("创贸({$nickname})对工单(ID:{$ticket->id})进行了补充,请及时查看处理!");
68 $this->response('success', Code::SUCCESS, $chat); 70 $this->response('success', Code::SUCCESS, $chat);
69 } 71 }
70 72
  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\Controllers\Controller;
  8 +use App\Http\Requests\Aside\WorkOrder\TicketProjectUpdateRequest;
  9 +use App\Models\WorkOrder\TicketProject;
  10 +use Illuminate\Http\Request;
  11 +
  12 +class TicketProjectController extends BaseController
  13 +{
  14 + /**
  15 + * Display a listing of the resource.
  16 + *
  17 + * @return \Illuminate\Http\Response
  18 + */
  19 + public function index()
  20 + {
  21 + //
  22 + }
  23 +
  24 + /**
  25 + * Show the form for creating a new resource.
  26 + *
  27 + * @return \Illuminate\Http\Response
  28 + */
  29 + public function create()
  30 + {
  31 + //
  32 + }
  33 +
  34 + /**
  35 + * Store a newly created resource in storage.
  36 + *
  37 + * @param \Illuminate\Http\Request $request
  38 + * @return \Illuminate\Http\Response
  39 + */
  40 + public function store(Request $request)
  41 + {
  42 + //
  43 + }
  44 +
  45 + /**
  46 + * Display the specified resource.
  47 + *
  48 + * @param int $id
  49 + * @return \Illuminate\Http\Response
  50 + */
  51 + public function show($id)
  52 + {
  53 + //
  54 + }
  55 +
  56 + /**
  57 + * Show the form for editing the specified resource.
  58 + *
  59 + * @param int $id
  60 + * @return \Illuminate\Http\Response
  61 + */
  62 + public function edit($id)
  63 + {
  64 + //
  65 + }
  66 +
  67 + /**
  68 + * Update the specified resource in storage.
  69 + *
  70 + * @param \Illuminate\Http\Request $request
  71 + * @param int $id
  72 + * @return \Illuminate\Http\Response
  73 + */
  74 + public function update(TicketProjectUpdateRequest $request, $id)
  75 + {
  76 + $request->validated();
  77 + $project = TicketProject::where('uuid', $id)->first();
  78 + if (!$project) $this->response('Project not found', Code::USER_MODEL_NOTFOUND_ERROE);
  79 +
  80 + if ($request->input('subtitle')) $project->subtitle = $request->input('subtitle');
  81 + $project->wechat_switch = $request->boolean('wechat_switch', true);
  82 + $project->save();
  83 + $this->response('Project updated successfully', Code::SUCCESS, $project);
  84 + }
  85 +
  86 + /**
  87 + * Remove the specified resource from storage.
  88 + *
  89 + * @param int $id
  90 + * @return \Illuminate\Http\Response
  91 + */
  92 + public function destroy($id)
  93 + {
  94 + //
  95 + }
  96 +}
@@ -92,4 +92,46 @@ class GeoQuestionResController extends BaseController @@ -92,4 +92,46 @@ class GeoQuestionResController extends BaseController
92 $data = $this->logic->getResultInfo(); 92 $data = $this->logic->getResultInfo();
93 $this->response('success',Code::SUCCESS,$data); 93 $this->response('success',Code::SUCCESS,$data);
94 } 94 }
  95 +
  96 + /**
  97 + * @remark :统计数量
  98 + * @name :countQuantity
  99 + * @author :lyh
  100 + * @method :post
  101 + * @time :2025/7/21 10:58
  102 + */
  103 + public function countQuantity(){
  104 + $data = $this->logic->countQuantity();
  105 + $data['platform'] = $this->logic->platformHitCount();
  106 + $this->response('success',Code::SUCCESS,$data);
  107 + }
  108 +
  109 + /**
  110 + * @remark :获取搜索时间
  111 + * @name :getSearchDate
  112 + * @author :lyh
  113 + * @method :post
  114 + * @time :2025/7/21 16:35
  115 + */
  116 + public function getSearchDate(){
  117 + $data = $this->logic->getSearchDate();
  118 + $this->response('success',Code::SUCCESS,$data);
  119 + }
  120 +
  121 + /**
  122 + * @remark :获取搜索列表
  123 + * @name :getSearchList
  124 + * @author :lyh
  125 + * @method :post
  126 + * @time :2025/7/21 16:47
  127 + */
  128 + public function getSearchList(){
  129 + $this->request->validate([
  130 + 'created_at'=>'required',
  131 + ],[
  132 + 'created_at.required' => 'created_at不能为空',
  133 + ]);
  134 + $data = $this->logic->getSearchList($this->map,$this->page,$this->row);
  135 + $this->response('success',Code::SUCCESS,$data);
  136 + }
95 } 137 }
@@ -320,7 +320,8 @@ class NewsController extends BaseController @@ -320,7 +320,8 @@ class NewsController extends BaseController
320 ],[ 320 ],[
321 'keyword.required' => 'keyword不能为空', 321 'keyword.required' => 'keyword不能为空',
322 ]); 322 ]);
323 - $data = curl_get('http://gnews.globalso.com/gnews_news.php?keyword='.$this->param['keyword'],true); 323 +
  324 + $data = curl_get('http://gnews.globalso.com/gnews_news.php?keyword='.str_replace(' ', '+', $this->param['keyword']),true);
324 if(isset($data['data']['p'][0]) && !empty($data['data']['p'][0])){ 325 if(isset($data['data']['p'][0]) && !empty($data['data']['p'][0])){
325 $data['data']['p'][0] = str_replace('&nbsp' ,' ',$data['data']['p'][0]); 326 $data['data']['p'][0] = str_replace('&nbsp' ,' ',$data['data']['p'][0]);
326 } 327 }
@@ -43,7 +43,8 @@ class TestController extends BaseController @@ -43,7 +43,8 @@ class TestController extends BaseController
43 * @time :2025/2/13 16:34 43 * @time :2025/2/13 16:34
44 */ 44 */
45 public function ceshi(){ 45 public function ceshi(){
46 - $data = Translate::tran('测试翻译', 'en');  
47 - $this->response('success',Code::SUCCESS,['data'=>$data]); 46 + $hrModel = new ManageHr();
  47 + $data = $hrModel->accordIdGetLeader($this->param['id']);
  48 + $this->response('success',Code::SUCCESS,$data);
48 } 49 }
49 } 50 }
@@ -230,6 +230,9 @@ class ImageController extends Controller @@ -230,6 +230,9 @@ class ImageController extends Controller
230 $suffix = array_pop($nameArr) ?? 'jpg'; 230 $suffix = array_pop($nameArr) ?? 'jpg';
231 $nameStr = implode('-', $nameArr); 231 $nameStr = implode('-', $nameArr);
232 $tran_name = Translate::tran($nameStr, 'en'); 232 $tran_name = Translate::tran($nameStr, 'en');
  233 + if(empty($tran_name)){
  234 + $tran_name = Translate::tran($nameStr, 'en');
  235 + }
233 if(is_array($tran_name)){ 236 if(is_array($tran_name)){
234 $tran_name = $tran_name[0]; 237 $tran_name = $tran_name[0];
235 } 238 }
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoArticleLogic.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/7/14 16:21
  8 + */
  9 +
  10 +namespace App\Http\Logic\Aside\Geo;
  11 +
  12 +use App\Http\Logic\Aside\BaseLogic;
  13 +use App\Models\Geo\GeoArticle;
  14 +
  15 +/**
  16 + * @remark :GEO文章列表
  17 + * @name :GeoArticleLogic
  18 + * @author :lyh
  19 + * @method :post
  20 + * @time :2025/7/14 16:22
  21 + */
  22 +class GeoArticleLogic extends BaseLogic
  23 +{
  24 + public function __construct()
  25 + {
  26 + parent::__construct();
  27 + $this->param = $this->requestAll;
  28 + $this->model = new GeoArticle();
  29 + }
  30 +
  31 + /**
  32 + * @remark :列表数据
  33 + * @name :getArticleList
  34 + * @author :lyh
  35 + * @method :post
  36 + * @time :2025/7/14 16:24
  37 + */
  38 + public function getArticleList($map = [],$page = 1,$row = 20,$order = 'id'){
  39 + if(isset($map['filename']) && !empty($map['filename'])){
  40 + $map['filename'] = ['like','%'.$map['filename'].'%'];
  41 + }
  42 + $filed = ['*'];
  43 + $lists = $this->model->lists($map,$page,$row,$order,$filed);
  44 + if(!empty($lists) && !empty($lists['list'])){
  45 + foreach ($lists['list'] as $key => $item){
  46 + $item['download_url'] = url('a/download_files?path='.$item['url']);
  47 + $item['url_link'] = getFileUrl($item['url']);
  48 + $lists['list'][$key] = $item;
  49 + }
  50 + }
  51 + return $this->success($lists);
  52 + }
  53 +
  54 + /**
  55 + * @remark :获取数据详情
  56 + * @name :getArticleInfo
  57 + * @author :lyh
  58 + * @method :post
  59 + * @time :2025/7/14 16:26
  60 + */
  61 + public function getArticleInfo(){
  62 + $info = $this->model->read($this->param);
  63 + if($info === false){
  64 + $this->fail('当前数据不存在或者已被删除');
  65 + }
  66 + $info['url'] = getFileUrl($info['url']);
  67 + return $this->success($info);
  68 + }
  69 +
  70 + /**
  71 + * @remark :更新单个数据
  72 + * @name :editArticle
  73 + * @author :lyh
  74 + * @method :post
  75 + * @time :2025/7/15 10:29
  76 + */
  77 + public function editArticle(){
  78 + $this->param['url'] = str_replace_url($this->param['url']);
  79 + $this->model->edit($this->param,['id'=>$this->param['id']]);
  80 + return $this->success(['id'=>$this->param['id']]);
  81 + }
  82 +
  83 + /**
  84 + * @remark :保存数据
  85 + * @name :saveArticle
  86 + * @author :lyh
  87 + * @method :post
  88 + * @time :2025/7/14 16:26
  89 + */
  90 + public function saveArticle(){
  91 + try {
  92 + if(!empty($this->param['data'])){
  93 + $data = [];
  94 + foreach ($this->param['data'] as $item){
  95 + $data[] = [
  96 + 'filename' => $item['filename'],
  97 + 'url'=> $item['url'],
  98 + 'project_id'=>$this->param['project_id']
  99 + ];
  100 + }
  101 + $this->model->insertAll($data);
  102 + }
  103 + }catch (\Exception $e){
  104 + $this->fail('保存失败,请联系管理员');
  105 + }
  106 + return $this->success();
  107 + }
  108 +
  109 + /**
  110 + * @remark :删除数据
  111 + * @name :delArticle
  112 + * @author :lyh
  113 + * @method :post
  114 + * @time :2025/7/14 16:28
  115 + */
  116 + public function delArticle(){
  117 + try {
  118 + $this->model->del(['id'=>['in',$this->param['ids']]]);
  119 + }catch (\Exception $e){
  120 + $this->fail('删除失败,请联系管理员.');
  121 + }
  122 + return $this->success();
  123 + }
  124 +}
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoLinkLogic.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/7/14 16:20
  8 + */
  9 +
  10 +namespace App\Http\Logic\Aside\Geo;
  11 +
  12 +use App\Http\Logic\Aside\BaseLogic;
  13 +use App\Models\Geo\GeoLink;
  14 +
  15 +/**
  16 + * @remark :geo权威新闻(链接数据)
  17 + * @name :GeoLinkLogic
  18 + * @author :lyh
  19 + * @method :post
  20 + * @time :2025/7/14 16:20
  21 + */
  22 +class GeoLinkLogic extends BaseLogic
  23 +{
  24 + public function __construct()
  25 + {
  26 + parent::__construct();
  27 + $this->param = $this->requestAll;
  28 + $this->model = new GeoLink();
  29 + }
  30 +
  31 + /**
  32 + * @remark :获取权威新闻链接数据
  33 + * @name :getLinkList
  34 + * @author :lyh
  35 + * @method :post
  36 + * @time :2025/7/14 16:47
  37 + */
  38 + public function getLinkList($map = [],$page = 1,$row = 20,$order = 'id'){
  39 + $filed = ['*'];
  40 + $lists = $this->model->lists($map,$page,$row,$order,$filed);
  41 + return $this->success($lists);
  42 + }
  43 +
  44 + /**
  45 + * @remark :获取链接数据详情
  46 + * @name :getLinkInfo
  47 + * @author :lyh
  48 + * @method :post
  49 + * @time :2025/7/14 16:51
  50 + */
  51 + public function getLinkInfo(){
  52 + $info = $this->model->read($this->param);
  53 + if($info === false){
  54 + $this->fail('当前数据不存在或者已被删除');
  55 + }
  56 + return $this->success($info);
  57 + }
  58 +
  59 + /**
  60 + * @remark :保存链接数据
  61 + * @name :saveLink
  62 + * @author :lyh
  63 + * @method :post
  64 + * @time :2025/7/14 16:50
  65 + */
  66 + public function saveLink(){
  67 + $data = [
  68 + 'project_id' => 1,
  69 + 'data' => [
  70 + ['da'=>'ce_shi1', 'url'=>'www.baidu.com', 'send_time'=>'2021-07-09 11:12:12'],
  71 + ['da'=>'ce_shi2', 'url'=>'www.baidu1.com', 'send_time'=>'2021-07-10 11:13:12'],
  72 + ],
  73 + ];
  74 + try {
  75 + if(!empty($this->param['data'])){
  76 + $data = [];
  77 + foreach ($this->param['data'] as $item){
  78 + $data[] = [
  79 + 'project_id'=>$this->param['project_id'],
  80 + 'da'=>$item['da'],
  81 + 'url'=>$item['url'],
  82 + 'send_time'=>$item['send_time']
  83 + ];
  84 + }
  85 + $this->model->insertAll($data);
  86 + }
  87 + }catch (\Exception $e){
  88 + $this->fail('保存失败,请联系管理员');
  89 + }
  90 + return $this->success();
  91 + }
  92 +
  93 + /**
  94 + * @remark :删除数据
  95 + * @name :delLink
  96 + * @author :lyh
  97 + * @method :post
  98 + * @time :2025/7/14 16:51
  99 + */
  100 + public function delLink(){
  101 + try {
  102 + $this->model->del(['id'=>['in',$this->param['ids']]]);
  103 + }catch (\Exception $e){
  104 + $this->fail('删除失败,请联系管理员.');
  105 + }
  106 + return $this->success();
  107 + }
  108 +}
@@ -427,6 +427,10 @@ class ProjectLogic extends BaseLogic @@ -427,6 +427,10 @@ class ProjectLogic extends BaseLogic
427 //上线时间大于当前时间的3天钱,默认不允许关闭推荐供应商 427 //上线时间大于当前时间的3天钱,默认不允许关闭推荐供应商
428 $this->param['deploy_build']['is_supplier'] = 1; 428 $this->param['deploy_build']['is_supplier'] = 1;
429 } 429 }
  430 + //之后上线的项目默认开启新版聚合页
  431 + if($param['uptime'] >= '2025-07-16 00:00:00'){
  432 + $param['tag_page_version'] = 2;
  433 + }
430 } 434 }
431 //聚合页评论为空时,生成评论 435 //聚合页评论为空时,生成评论
432 $keywordCommentModel = new AggregateKeywordComment(); 436 $keywordCommentModel = new AggregateKeywordComment();
@@ -174,7 +174,7 @@ class CustomTemplateLogic extends BaseLogic @@ -174,7 +174,7 @@ class CustomTemplateLogic extends BaseLogic
174 $six_read = $this->param['six_read'] ?? 0;//5.0数据时,是否按6.0显示 174 $six_read = $this->param['six_read'] ?? 0;//5.0数据时,是否按6.0显示
175 if($is_upgrade == 0 || $six_read == 0) { 175 if($is_upgrade == 0 || $six_read == 0) {
176 $this->param['url'] = RouteMap::setRoute($this->param['url'], RouteMap::SOURCE_PAGE, $id, $this->user['project_id']); 176 $this->param['url'] = RouteMap::setRoute($this->param['url'], RouteMap::SOURCE_PAGE, $id, $this->user['project_id']);
177 - if(($this->param['url'] == 'news') || ($this->param['url'] == 'product') || ($this->param['url'] == 'blog')){ 177 + if(($this->param['url'] == 'news') || ($this->param['url'] == 'products') || ($this->param['url'] == 'blog')){
178 $this->fail('不允许创建路由为:'.$this->param['url']); 178 $this->fail('不允许创建路由为:'.$this->param['url']);
179 } 179 }
180 } 180 }
@@ -159,6 +159,7 @@ class CustomModuleCategoryLogic extends BaseLogic @@ -159,6 +159,7 @@ class CustomModuleCategoryLogic extends BaseLogic
159 $route = RouteMap::setRoute($this->param['route'], RouteMap::SOURCE_MODULE_CATE, 159 $route = RouteMap::setRoute($this->param['route'], RouteMap::SOURCE_MODULE_CATE,
160 $this->param['id'], $this->user['project_id']); 160 $this->param['id'], $this->user['project_id']);
161 $this->editRoute($this->param['id'],$route); 161 $this->editRoute($this->param['id'],$route);
  162 + $this->param['route'] = $route;
162 $rs = $this->model->edit($this->param,['id'=>$this->param['id']]); 163 $rs = $this->model->edit($this->param,['id'=>$this->param['id']]);
163 if($rs === false){ 164 if($rs === false){
164 $this->fail('系统错误,请连续管理员'); 165 $this->fail('系统错误,请连续管理员');
@@ -10,6 +10,9 @@ @@ -10,6 +10,9 @@
10 namespace App\Http\Logic\Bside\Geo; 10 namespace App\Http\Logic\Bside\Geo;
11 11
12 use App\Http\Logic\Bside\BaseLogic; 12 use App\Http\Logic\Bside\BaseLogic;
  13 +use App\Models\Geo\GeoPlatform;
  14 +use App\Models\Geo\GeoQuestion;
  15 +use App\Models\Geo\GeoQuestionLog;
13 use App\Models\Geo\GeoQuestionResult; 16 use App\Models\Geo\GeoQuestionResult;
14 17
15 class GeoQuestionResLogic extends BaseLogic 18 class GeoQuestionResLogic extends BaseLogic
@@ -44,6 +47,11 @@ class GeoQuestionResLogic extends BaseLogic @@ -44,6 +47,11 @@ class GeoQuestionResLogic extends BaseLogic
44 */ 47 */
45 public function getResultList($map = [],$page = 1,$row = 20){ 48 public function getResultList($map = [],$page = 1,$row = 20){
46 $filed = ['id','project_id','question_id','platform','question','en_question','keywords','url','created_at','updated_at']; 49 $filed = ['id','project_id','question_id','platform','question','en_question','keywords','url','created_at','updated_at'];
  50 + if(!empty($map['created_at'])){
  51 + $map['project_id'] = $this->user['project_id'];
  52 + $map['created_at'] = ['between',[$map['created_at'].' 00:00:00',$map['created_at'].'23:59:59']];
  53 + $this->model = new GeoQuestionLog();
  54 + }
47 $query = $this->model->formatQuery($map); 55 $query = $this->model->formatQuery($map);
48 $query = $query->where(function ($q) { 56 $query = $query->where(function ($q) {
49 $q->whereRaw('JSON_LENGTH(keywords) > 0') 57 $q->whereRaw('JSON_LENGTH(keywords) > 0')
@@ -64,4 +72,73 @@ class GeoQuestionResLogic extends BaseLogic @@ -64,4 +72,73 @@ class GeoQuestionResLogic extends BaseLogic
64 $data = $this->model->read($this->param); 72 $data = $this->model->read($this->param);
65 return $this->success($data); 73 return $this->success($data);
66 } 74 }
  75 +
  76 + /**
  77 + * @remark :统计数量
  78 + * @name :countQuantity
  79 + * @author :lyh
  80 + * @method :post
  81 + * @time :2025/7/21 11:12
  82 + */
  83 + public function countQuantity(){
  84 + $questionModel = new GeoQuestion();
  85 + $list = $questionModel->list(['project_id',$this->user['project_id']],['question','keywords','url']);
  86 + $questionTotalCount = $urlTotalCount = $keywordsTotalCount = $keywordUrlCount = 0;
  87 + foreach ($list as $item){
  88 + $questionTotalCount += count($item['question'] ?? []);
  89 + $keywordsTotalCount += count($item['keywords'] ?? []);
  90 + $urlTotalCount += count($item['url'] ?? []);
  91 + }
  92 + $keywordArr = [];
  93 + $questionResModel = new GeoQuestionResult();
  94 + $resList = $questionResModel->list(['project_id',$this->user['project_id']],['keywords_num','url_num']);
  95 + foreach ($resList as $resItem){
  96 + $keywordsNumArr = json_decode($resItem['keywords_num'] ?? [], true); // 转为 PHP 关联数组
  97 + $keywordUrlCount += array_sum($keywordsNumArr); // 获取值的总和
  98 + $urlNumArr = json_decode($resItem['url'] ?? [],true);
  99 + $keywordUrlCount += array_sum($urlNumArr); // 获取值的总和
  100 + foreach ($resItem['keywords_num'] as $key => $value) {
  101 + $keywordArr[$key] = ($keywordArr[$key] ?? 0) + $value;
  102 + }
  103 + }
  104 + $data = [
  105 + 'keywords_count'=>$keywordsTotalCount,
  106 + 'url_count'=>$urlTotalCount,
  107 + 'question_count'=>$questionTotalCount,
  108 + 'keywords_url_count'=>$keywordUrlCount,
  109 + 'keywords_arr' => $keywordArr,
  110 + ];
  111 + return $this->success($data);
  112 + }
  113 +
  114 + /**
  115 + * @remark :按平台统计问题数量
  116 + * @name :platformHitCount
  117 + * @author :lyh
  118 + * @method :post
  119 + * @time :2025/7/21 15:00
  120 + */
  121 + public function platformHitCount(){
  122 + $data = [];
  123 + $platformModel = new GeoPlatform();
  124 + $list = $platformModel->list(['status'=>1],'id',['name','en_name']);
  125 + $questionResModel = new GeoQuestionResult();
  126 + foreach ($list as $item){
  127 + $data[$item['name']] = $questionResModel->counts(['project_id'=>$this->user['project_id'],'hit'=>['!=',0],'platform'=>$item['en_name']]);
  128 + }
  129 + return $this->success($data);
  130 + }
  131 +
  132 + /**
  133 + * @remark :获取搜索时间
  134 + * @name :getSearchDate
  135 + * @author :lyh
  136 + * @method :post
  137 + * @time :2025/7/21 16:36
  138 + */
  139 + public function getSearchDate(){
  140 + $dates = $this->model->select(DB::raw('DATE(created_at) as date_only'))->distinct()->pluck('date_only');
  141 + return $this->success($dates);
  142 + }
  143 +
67 } 144 }
@@ -22,6 +22,7 @@ use App\Models\RankData\IndexedPages; @@ -22,6 +22,7 @@ use App\Models\RankData\IndexedPages;
22 use App\Models\RankData\IndexedPages as IndexedPagesModel; 22 use App\Models\RankData\IndexedPages as IndexedPagesModel;
23 use App\Models\RankData\RankData; 23 use App\Models\RankData\RankData;
24 use App\Models\RankData\RankDataBmseo; 24 use App\Models\RankData\RankDataBmseo;
  25 +use App\Models\RankData\RankDataLog;
25 use App\Models\RankData\RankWeek; 26 use App\Models\RankData\RankWeek;
26 use App\Models\RankData\RankWeek as RankWeekModel; 27 use App\Models\RankData\RankWeek as RankWeekModel;
27 use App\Models\RankData\RecommDomain; 28 use App\Models\RankData\RecommDomain;
@@ -709,6 +710,28 @@ class RankDataLogic extends BaseLogic @@ -709,6 +710,28 @@ class RankDataLogic extends BaseLogic
709 $first_ten_pages_num ++; 710 $first_ten_pages_num ++;
710 } 711 }
711 } 712 }
  713 +
  714 + //保证关键词数
  715 + $keyword_num = DeployBuild::where('project_id', $project_id)->value('keyword_num');
  716 + if($keyword_num){
  717 + $is_compliance = $first_page_num >= $keyword_num;
  718 + }else{
  719 + $is_compliance = 0;
  720 + }
  721 +
  722 + if ($keyword_num && $is_compliance) {
  723 + Log::channel('rank_data')->info('项目' . $project_id . '白帽版:关键词达标'. $keyword_num .' - ' . $first_page_num);
  724 +
  725 + $compliance_log = RankDataLog::where('api_no', $api_no)->where('date', date('Y-m-d'))->where('is_compliance', 1)->first();
  726 + if (!$compliance_log) {
  727 + $compliance_day = Project::where(['id' => $project_id])->value('finish_remain_day') ?: 0;
  728 + Project::where('id', $project_id)->update(['bm_is_remain_today' => 1, 'bm_finish_remain_day' => $compliance_day + 1]);
  729 + Log::channel('rank_data')->info('项目' . $project_id . '白帽版:达标天数+1:' . ($compliance_day + 1));
  730 + }
  731 + }else {
  732 + Log::channel('rank_data')->info('项目' . $project_id . '白帽版:关键词未达标'. $keyword_num .' - ' . $first_page_num);
  733 + }
  734 +
712 $where = [ 735 $where = [
713 'project_id' => $project_id, 736 'project_id' => $project_id,
714 'api_no' => $api_no, 737 'api_no' => $api_no,
@@ -728,7 +751,7 @@ class RankDataLogic extends BaseLogic @@ -728,7 +751,7 @@ class RankDataLogic extends BaseLogic
728 $model->updated_date = date('Y-m-d'); 751 $model->updated_date = date('Y-m-d');
729 $model->save(); 752 $model->save();
730 753
731 - return true; 754 + return $is_compliance;
732 } 755 }
733 756
734 /** 757 /**
@@ -26,6 +26,7 @@ class AsideTicketListRequest extends FormRequest @@ -26,6 +26,7 @@ class AsideTicketListRequest extends FormRequest
26 return [ 26 return [
27 'project_id' => 'nullable|string', 27 'project_id' => 'nullable|string',
28 'status' => 'nullable|in:0,1,2,3|integer', 28 'status' => 'nullable|in:0,1,2,3|integer',
  29 + 'star' => 'nullable|in:1,2,3|integer',
29 'search' => 'nullable|string', // 搜索关键词 30 'search' => 'nullable|string', // 搜索关键词
30 'engineer_id' => 'nullable|integer', // 工程师ID 31 'engineer_id' => 'nullable|integer', // 工程师ID
31 'page' => 'nullable|integer', 32 'page' => 'nullable|integer',
@@ -29,6 +29,8 @@ class AsideTicketStoreRequest extends FormRequest @@ -29,6 +29,8 @@ class AsideTicketStoreRequest extends FormRequest
29 'content' => 'required|string', 29 'content' => 'required|string',
30 'files' => 'nullable|array', 30 'files' => 'nullable|array',
31 'engineer_ids' => 'array', 31 'engineer_ids' => 'array',
  32 + 'star' => 'nullable|in:1,2,3|integer',
  33 + 'plan_end_at' => 'nullable|date',
32 ]; 34 ];
33 } 35 }
34 } 36 }
@@ -24,9 +24,14 @@ class AsideTicketUpdateRequest extends FormRequest @@ -24,9 +24,14 @@ class AsideTicketUpdateRequest extends FormRequest
24 public function rules() 24 public function rules()
25 { 25 {
26 return [ 26 return [
  27 + 'title' => 'nullable|string',
  28 + 'content' => 'nullable|string',
  29 + 'files' => 'nullable|array',
27 'status' => 'nullable|in:0,1,2,3|integer', 30 'status' => 'nullable|in:0,1,2,3|integer',
28 - 'reply' => 'nullable|string',  
29 'engineer_ids' => 'nullable|array', 31 'engineer_ids' => 'nullable|array',
  32 + 'star' => 'nullable|in:1,2,3|integer',
  33 + 'plan_end_at' => 'nullable|date',
  34 + 'reply' => 'nullable|string', // 弃用
30 ]; 35 ];
31 } 36 }
32 } 37 }
  1 +<?php
  2 +
  3 +namespace App\Http\Requests\Aside\WorkOrder;
  4 +
  5 +use Illuminate\Foundation\Http\FormRequest;
  6 +
  7 +class TicketProjectUpdateRequest 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 + 'subtitle' => 'nullable|string',
  28 + 'wechat_switch' => 'nullable|boolean',
  29 + ];
  30 + }
  31 +}
@@ -162,7 +162,7 @@ class Base extends Model @@ -162,7 +162,7 @@ class Base extends Model
162 * @method :post 162 * @method :post
163 * @time :2024/9/26 10:52 163 * @time :2024/9/26 10:52
164 */ 164 */
165 - public function counts($condition){ 165 + public function counts($condition = []){
166 $condition = $this->filterRequestData($condition); 166 $condition = $this->filterRequestData($condition);
167 return $this->formatQuery($condition)->count(); 167 return $this->formatQuery($condition)->count();
168 } 168 }
@@ -362,4 +362,16 @@ class Base extends Model @@ -362,4 +362,16 @@ class Base extends Model
362 $lists = $lists->toArray(); 362 $lists = $lists->toArray();
363 return $lists; 363 return $lists;
364 } 364 }
  365 +
  366 + /**
  367 + * @remark :根据条件获取单个值
  368 + * @name :getValue
  369 + * @author :lyh
  370 + * @method :post
  371 + * @time :2025/7/22 10:00
  372 + */
  373 + public function getValue($data,$field = 'id'){
  374 + $data = $this->filterRequestData($data);
  375 + return $this->formatQuery($data)->value($field);
  376 + }
365 } 377 }
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoArticle.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/7/14 16:06
  8 + */
  9 +
  10 +namespace App\Models\Geo;
  11 +
  12 +use App\Models\Base;
  13 +
  14 +/**
  15 + * @remark :geo文章列表
  16 + * @name :GeoArticle
  17 + * @author :lyh
  18 + * @method :post
  19 + * @time :2025/7/14 16:07
  20 + */
  21 +class GeoArticle extends Base
  22 +{
  23 + protected $table = 'gl_geo_article';
  24 +}
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoLink.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/7/14 16:05
  8 + */
  9 +
  10 +namespace App\Models\Geo;
  11 +
  12 +use App\Models\Base;
  13 +
  14 +/**
  15 + * @remark :geo权威新闻外链数据
  16 + * @name :GeoLink
  17 + * @author :lyh
  18 + * @method :post
  19 + * @time :2025/7/14 16:05
  20 + */
  21 +class GeoLink extends Base
  22 +{
  23 + protected $table = 'gl_geo_link';
  24 +}
@@ -69,4 +69,34 @@ class GeoQuestionResult extends Base @@ -69,4 +69,34 @@ class GeoQuestionResult extends Base
69 } 69 }
70 return $value; 70 return $value;
71 } 71 }
  72 +
  73 + /**
  74 + * @remark :命中的关键词数量
  75 + * @name :getKeywordsNumAttribute
  76 + * @author :lyh
  77 + * @method :post
  78 + * @time :2025/7/21 11:33
  79 + */
  80 + public function getKeywordsNumAttribute($value)
  81 + {
  82 + if($value){
  83 + $value = Arr::s2a($value);
  84 + }
  85 + return $value;
  86 + }
  87 +
  88 + /**
  89 + * @remark :命中的url数量
  90 + * @name :getUrlNumAttribute
  91 + * @author :lyh
  92 + * @method :post
  93 + * @time :2025/7/21 11:34
  94 + */
  95 + public function getUrlNumAttribute($value)
  96 + {
  97 + if($value){
  98 + $value = Arr::s2a($value);
  99 + }
  100 + return $value;
  101 + }
72 } 102 }
@@ -80,17 +80,23 @@ class ReInquiryCount extends Base @@ -80,17 +80,23 @@ class ReInquiryCount extends Base
80 return Arr::setToArr($value); 80 return Arr::setToArr($value);
81 } 81 }
82 82
83 - public function getTasksAttribute(){  
84 - $tasks = ReInquiryTask::whereIn('id', $this->task_ids)->select(['title', 'industry','target', 'status'])->get()->toArray();  
85 - foreach ($tasks as &$task){  
86 - $target = collect($task['target'])->where('url', $this->domain)->first();  
87 - $task['is_del'] = $target ? 0 : 1;  
88 - $task['agent'] = $target['agent'] ?? '';  
89 - $task['is_v6'] = $target['is_v6'] ?? '';  
90 - $task['agent_group'] = $target['agent_group'] ?? '';  
91 - unset($task['target']);  
92 - }  
93 - return $tasks; 83 + public function getTasksAttribute()
  84 + {
  85 + $cache_key = 'ReInquiryCountTasks_' . Arr::arrToSet($this->task_ids);
  86 + $tasks = Cache::get($cache_key);
  87 + if (!$tasks) {
  88 + $tasks = ReInquiryTask::whereIn('id', $this->task_ids)->select(['title', 'industry', 'target', 'status'])->get()->toArray();
  89 + foreach ($tasks as &$task) {
  90 + $target = collect($task['target'])->where('url', $this->domain)->first();
  91 + $task['is_del'] = $target ? 0 : 1;
  92 + $task['agent'] = $target['agent'] ?? '';
  93 + $task['is_v6'] = $target['is_v6'] ?? '';
  94 + $task['agent_group'] = $target['agent_group'] ?? '';
  95 + unset($task['target']);
  96 + }
  97 + Cache::put($cache_key, $tasks, 7200);
  98 + }
  99 + return $tasks;
94 } 100 }
95 101
96 public static function getFobProjects(){ 102 public static function getFobProjects(){
@@ -11,6 +11,8 @@ class ManageHr extends Base @@ -11,6 +11,8 @@ class ManageHr extends Base
11 const GID_ZERO = 0;//超级管理员 11 const GID_ZERO = 0;//超级管理员
12 12
13 const STATUS_ONE = 1; 13 const STATUS_ONE = 1;
  14 +
  15 + const IS_LEADER = 1;//组长
14 /** 16 /**
15 * 特殊字段 17 * 特殊字段
16 * @return string[] 18 * @return string[]
@@ -202,4 +204,32 @@ class ManageHr extends Base @@ -202,4 +204,32 @@ class ManageHr extends Base
202 } 204 }
203 return $name; 205 return $name;
204 } 206 }
  207 +
  208 + /**
  209 + * @remark :根据当前用户登录的id获取当前用户的组长
  210 + * @name :accordIdGetLeader
  211 + * @author :lyh
  212 + * @method :post
  213 + * @time :2025/7/22 9:42
  214 + * @param :id->当前用户的人事id
  215 + */
  216 + public function accordIdGetLeader($id = 0){
  217 + if(empty($id)){
  218 + return 0;
  219 + }
  220 + //查看当前用户是否为组长
  221 + $info = $this->read(['id'=>$id],['belong_group','is_leader','dept_id']);
  222 + if($info === false){
  223 + return 0;
  224 + }
  225 + //不是组长:根据小组获取组长
  226 + if($info['is_leader'] != self::IS_LEADER){
  227 + $id = $this->getValue(['belong_group'=>$info['belong_group'],'dept_id'=>$info['dept_id'],'is_leader'=> self::IS_LEADER]);
  228 + if(empty($id)){
  229 + //未获取到时,根据当前大组去随机获取一个组长
  230 + $id = $this->getValue(['dept_id'=>$info['dept_id'],'is_leader'=> self::IS_LEADER]);
  231 + }
  232 + }
  233 + return $id;
  234 + }
205 } 235 }
@@ -69,6 +69,9 @@ class RouteMap extends Base @@ -69,6 +69,9 @@ class RouteMap extends Base
69 } 69 }
70 //过滤特殊字符 70 //过滤特殊字符
71 $sign = generateRoute($title); 71 $sign = generateRoute($title);
  72 + if (strpos($sign, 'well-known-') === 0) {
  73 + $sign = substr($sign, strlen('well-known-'));
  74 + }
72 //查看当前数据路由是否存在 75 //查看当前数据路由是否存在
73 $routeInfo = self::where(['project_id' => $project_id, 'source' => $source, 'source_id'=>$source_id])->first(); 76 $routeInfo = self::where(['project_id' => $project_id, 'source' => $source, 'source_id'=>$source_id])->first();
74 $suffix = '';//设置路由后缀 77 $suffix = '';//设置路由后缀
  1 +<?php
  2 +
  3 +namespace App\Models\WorkOrder;
  4 +
  5 +use Illuminate\Database\Eloquent\Factories\HasFactory;
  6 +use Illuminate\Database\Eloquent\Model;
  7 +
  8 +class TicketDing extends Model
  9 +{
  10 + use HasFactory;
  11 +
  12 + protected $table = 'gl_ticket_dings';
  13 +}
@@ -4,6 +4,7 @@ namespace App\Models\WorkOrder; @@ -4,6 +4,7 @@ namespace App\Models\WorkOrder;
4 4
5 use App\Models\Base; 5 use App\Models\Base;
6 use App\Models\Manage\Manage; 6 use App\Models\Manage\Manage;
  7 +use App\Models\Manage\ManageHr;
7 use Illuminate\Database\Eloquent\Factories\HasFactory; 8 use Illuminate\Database\Eloquent\Factories\HasFactory;
8 9
9 class TicketLog extends Base 10 class TicketLog extends Base
@@ -19,8 +20,8 @@ class TicketLog extends Base @@ -19,8 +20,8 @@ class TicketLog extends Base
19 20
20 public function engineer() 21 public function engineer()
21 { 22 {
22 - return $this->belongsTo(Manage::class, 'engineer_id', 'id')  
23 - ->select(['id', 'name', 'mobile']); 23 + return $this->belongsTo(ManageHr::class, 'engineer_id', 'manage_id')
  24 + ->select(['manage_id', 'name', 'nickname']);
24 } 25 }
25 26
26 public function ticket() 27 public function ticket()
@@ -4,6 +4,7 @@ namespace App\Models\WorkOrder; @@ -4,6 +4,7 @@ namespace App\Models\WorkOrder;
4 4
5 use App\Models\Base; 5 use App\Models\Base;
6 use App\Models\Manage\Manage; 6 use App\Models\Manage\Manage;
  7 +use App\Models\Manage\ManageHr;
7 use App\Models\Project\Project; 8 use App\Models\Project\Project;
8 use App\Models\ProjectAssociation\ProjectAssociation; 9 use App\Models\ProjectAssociation\ProjectAssociation;
9 use App\Models\Workchat\MessagePush; 10 use App\Models\Workchat\MessagePush;
@@ -21,18 +22,28 @@ class TicketProject extends Base @@ -21,18 +22,28 @@ class TicketProject extends Base
21 ->where('version', 6); 22 ->where('version', 6);
22 } 23 }
23 24
  25 + // 项目经理
  26 + public function pm()
  27 + {
  28 + return $this->hasOne(ManageHr::class, 'manage_id', 'pm_id')
  29 + ->where('manage_id', '>', 0)
  30 + ->select(['manage_id', 'name', 'nickname']);
  31 + }
  32 +
24 //售后服务经理 33 //售后服务经理
25 public function assm() 34 public function assm()
26 { 35 {
27 - return $this->hasOne(Manage::class, 'id', 'assm_id')  
28 - ->select(['id', 'name']); 36 + return $this->hasOne(ManageHr::class, 'manage_id', 'assm_id')
  37 + ->where('manage_id', '>', 0)
  38 + ->select(['manage_id', 'name', 'nickname']);
29 } 39 }
30 40
31 // 优化师 41 // 优化师
32 public function seom() 42 public function seom()
33 { 43 {
34 - return $this->hasOne(Manage::class, 'id', 'seom_id')  
35 - ->select(['id', 'name']); 44 + return $this->hasOne(ManageHr::class, 'manage_id', 'seom_id')
  45 + ->where('manage_id', '>', 0)
  46 + ->select(['manage_id', 'name', 'nickname']);
36 } 47 }
37 48
38 /** 49 /**
@@ -40,8 +51,9 @@ class TicketProject extends Base @@ -40,8 +51,9 @@ class TicketProject extends Base
40 */ 51 */
41 public function first_engineer() 52 public function first_engineer()
42 { 53 {
43 - return $this->hasOne(Manage::class, 'id', 'engineer_id')  
44 - ->select(['id', 'name']); 54 + return $this->hasOne(ManageHr::class, 'manage_id', 'engineer_id')
  55 + ->where('manage_id', '>', 0)
  56 + ->select(['manage_id', 'name', 'nickname']);
45 } 57 }
46 58
47 /** 59 /**
@@ -3,7 +3,9 @@ @@ -3,7 +3,9 @@
3 namespace App\Models\WorkOrder; 3 namespace App\Models\WorkOrder;
4 4
5 use App\Models\Base; 5 use App\Models\Base;
  6 +use App\Models\Manage\ManageHr;
6 use Illuminate\Database\Eloquent\Factories\HasFactory; 7 use Illuminate\Database\Eloquent\Factories\HasFactory;
  8 +use Illuminate\Support\Facades\Log;
7 9
8 class Tickets extends Base 10 class Tickets extends Base
9 { 11 {
@@ -14,7 +16,7 @@ class Tickets extends Base @@ -14,7 +16,7 @@ class Tickets extends Base
14 const STATUS_PEDDING = 0; // 待处理 16 const STATUS_PEDDING = 0; // 待处理
15 const STATUS_PROCESSING = 1; // 处理中 17 const STATUS_PROCESSING = 1; // 处理中
16 const STATUS_COMPLETED = 2; // 已完成 18 const STATUS_COMPLETED = 2; // 已完成
17 - const STATUS_CLOSED = 3; // 已关闭 19 + const STATUS_CLOSED = 3; // 已关闭,已失效
18 20
19 /** 21 /**
20 * @return void 22 * @return void
@@ -32,4 +34,95 @@ class Tickets extends Base @@ -32,4 +34,95 @@ class Tickets extends Base
32 { 34 {
33 return $this->belongsTo(TicketProject::class, 'project_id', 'id'); 35 return $this->belongsTo(TicketProject::class, 'project_id', 'id');
34 } 36 }
  37 +
  38 + /**
  39 + * 当前工单,保存参与的人员到 gl_ticket_logs 表
  40 + * 逻辑说明:
  41 + * 1. 如果当前项目是超迹,要把徐莹和第一负责人加进去,为参与人
  42 + * 2. 若是域途项目,把黄小玉和第一负责人加进去,为参与人
  43 + * 3. 若是V5V6的项目,则把组长和第一负责人加进去,为参与人
  44 + */
  45 + public function saveEngineers($engineer_ids = [])
  46 + {
  47 + $canyu = [
  48 + $this->project->engineer_id, // 第一负责人
  49 + ];
  50 +
  51 + if ($this->project->project_cate == 3)
  52 + $canyu[] = 20; // 徐莹
  53 + elseif ($this->project->project_cate == 4)
  54 + $canyu[] = 85; // 黄小玉
  55 + else{
  56 + // todo 待完善
  57 + }
  58 +
  59 + $all_engineer_ids = array_unique(array_merge($canyu, $engineer_ids));
  60 +
  61 + foreach ($all_engineer_ids as $engineer_id)
  62 + {
  63 + try {
  64 + $log = $this->logs()->where('engineer_id', $engineer_id)->first();
  65 + if ($log && $log->is_engineer != in_array($engineer_id, $engineer_ids))
  66 + {
  67 + $log->is_engineer = in_array($engineer_id, $engineer_ids);
  68 + $log->save();
  69 + }else
  70 + {
  71 + // 利用唯一索引去重
  72 + $log = new TicketLog();
  73 + $log->engineer_id = $engineer_id;
  74 + $log->is_engineer = in_array($engineer_id, $engineer_ids);
  75 + $this->logs()->save($log);
  76 + }
  77 + }catch (\Exception $exception){
  78 + Log::error(" | ERRPR | Ticket saveEngineers {$exception->getMessage()} \n {$exception->getTraceAsString()}");
  79 + }
  80 + }
  81 +
  82 + // 删除没有参与当前工单的人员(若之前已有)
  83 + $this->logs()->whereNotIn('engineer_id', $all_engineer_ids)->delete();
  84 + }
  85 +
  86 + /**
  87 + * TODO 这个是一个补充功能
  88 + * 那些情况需要推送钉钉内部通知?
  89 + * 1. 客户提交了工单
  90 + * - 通知第一负责人 (gl_ticket_logs 表做了标记)
  91 + * 2. 客户补充了工单
  92 + * - 通知最近的聊天技术(gl_ticket_chats 表做了标记)
  93 + * 3. 技术完成了工单
  94 + * - 通知第一负责人 (暂无,所以这里做补充,如果以后要把上面的逻辑也加进来也可以,只是我为了偷懒,暂时只考虑完成工单请款)
  95 + */
  96 + public function pushDing($type = 'finish')
  97 + {
  98 + try {
  99 + $ding = new TicketDing();
  100 + $ding->msgKey = 'sampleLink';
  101 + $ding->table_name = 'gl_tickets';
  102 + $ding->table_id = $this->id;
  103 +
  104 + if ($type == 'finish')
  105 + {
  106 + // 所有技术完成了工单,通知第一负责人 和 工单的提交人
  107 + $userIds = [$this->project->engineer_id];
  108 + if ($this->submit_side == 1) {
  109 + // A 端提的,还要通知提交人
  110 + $userIds[] = $this->submit_user_id;
  111 + // 去重
  112 + $userIds = array_unique($userIds);
  113 + }
  114 + $ding->userIds = json_encode($userIds);
  115 + $ding->msgParam = json_encode([
  116 + 'text' => "工单(ID: {$this->id}),已经全部完成,请访问查看详情!",
  117 + 'title' => 'AI协同工单 - ' . $this->project->title,
  118 + 'picUrl' => 'https://hub.globalso.com/logocm.png',
  119 + 'messageUrl' => 'https://oa.quanqiusou.cn/afterorder?project_id=' . $this->project->uuid,
  120 + ], JSON_UNESCAPED_UNICODE);
  121 + $ding->save();
  122 + }
  123 + }catch (\Exception $exception){
  124 + Log::error(" | ERRPR | Ticket {$exception->getMessage()} \n {$exception->getTraceAsString()}");
  125 + }
  126 + }
  127 +
35 } 128 }
@@ -168,7 +168,7 @@ class ProjectAssociationServices extends BaseService @@ -168,7 +168,7 @@ class ProjectAssociationServices extends BaseService
168 } 168 }
169 $param['sign'] = $this->getSign($param); 169 $param['sign'] = $this->getSign($param);
170 $url = 'https://hub.ai.cc/api/globalso_ai_customer_service/chatroom_list'; 170 $url = 'https://hub.ai.cc/api/globalso_ai_customer_service/chatroom_list';
171 - $result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->post($url, $param)->json(); 171 + $result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->timeout(20)->post($url, $param)->json();
172 if(empty($result) || $result['status'] != 200){ 172 if(empty($result) || $result['status'] != 200){
173 return []; 173 return [];
174 } 174 }
@@ -11,7 +11,7 @@ namespace App\Services\Geo; @@ -11,7 +11,7 @@ namespace App\Services\Geo;
11 11
12 class GeoService 12 class GeoService
13 { 13 {
14 - public $api_key = '7yn!We6$&NnVA38bpGy*A@4TQ5iYLJcW'; 14 + public $api_key = 'UkzZljFv83Z2qBi5YR1o3f2otAVWtug6';
15 15
16 public $api_url = 'https://api.cmer.com/'; 16 public $api_url = 'https://api.cmer.com/';
17 17
@@ -68,4 +68,34 @@ class GeoService @@ -68,4 +68,34 @@ class GeoService
68 $url = $url . '?' . http_build_query($param); 68 $url = $url . '?' . http_build_query($param);
69 return http_get($url, $header); 69 return http_get($url, $header);
70 } 70 }
  71 +
  72 + /**
  73 + * @remark :请求deepSeek数据
  74 + * @name :getDeepSeek
  75 + * @author :lyh
  76 + * @method :post
  77 + * @time :2025/7/15 10:59
  78 + */
  79 + public function getDeepSeekResult($content,$model = 'deepseek-r1'){
  80 + $url = $this->api_url . 'v1/chat';
  81 + $header = [
  82 + 'accept: application/json',
  83 + 'X-CmerApi-Host: llm-chat.p.cmer.com',
  84 + 'apikey: '.$this->api_key,
  85 + 'Content-Type: application/json'
  86 + ];
  87 + $message = [
  88 + 'messages'=>[
  89 + [
  90 + 'content'=>$content,
  91 + 'role'=>'user'
  92 + ],
  93 + ],
  94 + 'model' => $model,
  95 + "supplier"=> "bailian",
  96 + 'security_check' => true
  97 + ];
  98 + $data = http_post($url,json_encode($message,true),$header);
  99 + return $data;
  100 + }
71 } 101 }
@@ -28,7 +28,7 @@ class ProjectServer @@ -28,7 +28,7 @@ class ProjectServer
28 <div class="layout" data-unable="demo01-error404"> 28 <div class="layout" data-unable="demo01-error404">
29 <img src="https://ecdn6.globalso.com/upload/m/image_other/2023-10/6528a87e594db30162.png" alt=""/> 29 <img src="https://ecdn6.globalso.com/upload/m/image_other/2023-10/6528a87e594db30162.png" alt=""/>
30 </div> 30 </div>
31 - <p style="text-align: center">SORRY. THE PAGE HAS EITHER MOVED OR CANNOT BE FOUND.</p> 31 + <h1 style="text-align: center">SORRY. THE PAGE HAS EITHER MOVED OR CANNOT BE FOUND.</h1>
32 <style> 32 <style>
33 .section-block-error404 .layout { 33 .section-block-error404 .layout {
34 height: 700px; 34 height: 700px;
  1 +<?php
  2 +
  3 +use Illuminate\Database\Migrations\Migration;
  4 +use Illuminate\Database\Schema\Blueprint;
  5 +use Illuminate\Support\Facades\Schema;
  6 +
  7 +class CreateTicketDingsTable extends Migration
  8 +{
  9 + /**
  10 + * Run the migrations.
  11 + *
  12 + * @return void
  13 + */
  14 + public function up()
  15 + {
  16 + Schema::create('gl_ticket_dings', function (Blueprint $table) {
  17 + $table->id();
  18 + $table->json('userIds')->comment('接收人ID列表');
  19 + $table->string('msgKey')->default('sampleMarkdown')->comment('消息模板标识');
  20 + $table->json('msgParam')->comment('消息内容参数,JSON格式');
  21 + $table->smallInteger('status')->default(0)->index()->comment('发送状态:0-未发送,1-已发送,2-发送失败');
  22 + $table->string('errorMsg')->nullable()->comment('错误信息,发送失败时记录');
  23 + $table->string('table_name', 50)->nullable()->comment('关联表名称');
  24 + $table->string('table_id', 50)->nullable()->comment('关联表ID');
  25 + $table->timestamps();
  26 + });
  27 + }
  28 +
  29 + /**
  30 + * Reverse the migrations.
  31 + *
  32 + * @return void
  33 + */
  34 + public function down()
  35 + {
  36 + Schema::dropIfExists('gl_ticket_dings');
  37 + }
  38 +}
@@ -257,6 +257,7 @@ Route::middleware(['aloginauth'])->group(function () { @@ -257,6 +257,7 @@ Route::middleware(['aloginauth'])->group(function () {
257 Route::post('/{id}', [Aside\WorkOrder\AsideTicketController::class, 'update'])->name('admin.tickets.update')->summary('A端更新工单,审核,邀请同事'); 257 Route::post('/{id}', [Aside\WorkOrder\AsideTicketController::class, 'update'])->name('admin.tickets.update')->summary('A端更新工单,审核,邀请同事');
258 Route::get('/pushNotify/{id}', [Aside\WorkOrder\AsideTicketController::class, 'pushNotify'])->name('admin.tickets.pushNotify')->summary('A端工单推送企微群'); 258 Route::get('/pushNotify/{id}', [Aside\WorkOrder\AsideTicketController::class, 'pushNotify'])->name('admin.tickets.pushNotify')->summary('A端工单推送企微群');
259 Route::get('/projects/{search}', [Aside\WorkOrder\AsideTicketController::class, 'getProjects'])->name('admin.tickets.projects')->summary('A端V5V6项目列表'); 259 Route::get('/projects/{search}', [Aside\WorkOrder\AsideTicketController::class, 'getProjects'])->name('admin.tickets.projects')->summary('A端V5V6项目列表');
  260 + Route::patch('/projects/{id}', [Aside\WorkOrder\TicketProjectController::class, 'update'])->name('admin.tickets.projects.update')->summary('A端修改工单项目');
260 Route::get('/v56_projects/list', [Aside\WorkOrder\AsideTicketController::class, 'projectList'])->name('admin.tickets.projectList')->summary('A端V5V6项目列表') 261 Route::get('/v56_projects/list', [Aside\WorkOrder\AsideTicketController::class, 'projectList'])->name('admin.tickets.projectList')->summary('A端V5V6项目列表')
261 ->description("project_cate[项目分类1]: 1 V5, 2 V6, 3 超迹, 4 域途"); 262 ->description("project_cate[项目分类1]: 1 V5, 2 V6, 3 超迹, 4 域途");
262 Route::post('/log/{id}', [Aside\WorkOrder\AsideTicketLogController::class, 'update'])->name('admin.tickets.log.update')->summary('A端工单操作日志更新,完成工单'); 263 Route::post('/log/{id}', [Aside\WorkOrder\AsideTicketLogController::class, 'update'])->name('admin.tickets.log.update')->summary('A端工单操作日志更新,完成工单');
@@ -563,6 +564,21 @@ Route::middleware(['aloginauth'])->group(function () { @@ -563,6 +564,21 @@ Route::middleware(['aloginauth'])->group(function () {
563 Route::any('/saveGeoQuestion', [Aside\Geo\GeoQuestionController::class, 'saveGeoQuestion'])->name('admin.geo_question_saveGeoQuestion'); 564 Route::any('/saveGeoQuestion', [Aside\Geo\GeoQuestionController::class, 'saveGeoQuestion'])->name('admin.geo_question_saveGeoQuestion');
564 Route::any('/delGeoQuestion', [Aside\Geo\GeoQuestionController::class, 'delGeoQuestion'])->name('admin.geo_question_delGeoQuestion'); 565 Route::any('/delGeoQuestion', [Aside\Geo\GeoQuestionController::class, 'delGeoQuestion'])->name('admin.geo_question_delGeoQuestion');
565 }); 566 });
  567 + //文章列表
  568 + Route::prefix('article')->group(function () {
  569 + Route::any('/lists', [Aside\Geo\GeoArticleController::class, 'lists'])->name('admin.geo_article_lists');
  570 + Route::any('/save', [Aside\Geo\GeoArticleController::class, 'save'])->name('admin.geo_article_save');
  571 + Route::any('/edit', [Aside\Geo\GeoArticleController::class, 'edit'])->name('admin.geo_article_edit');
  572 + Route::any('/info', [Aside\Geo\GeoArticleController::class, 'info'])->name('admin.geo_article_info');
  573 + Route::any('/del', [Aside\Geo\GeoArticleController::class, 'del'])->name('admin.geo_article_del');
  574 + });
  575 + //权威文章链接
  576 + Route::prefix('link')->group(function () {
  577 + Route::any('/lists', [Aside\Geo\GeoLinkController::class, 'lists'])->name('admin.geo_link_lists');
  578 + Route::any('/save', [Aside\Geo\GeoLinkController::class, 'save'])->name('admin.geo_link_save');
  579 + Route::any('/info', [Aside\Geo\GeoLinkController::class, 'info'])->name('admin.geo_link_info');
  580 + Route::any('/del', [Aside\Geo\GeoLinkController::class, 'del'])->name('admin.geo_link_del');
  581 + });
566 }); 582 });
567 // 任务相关 583 // 任务相关
568 Route::prefix('task')->group(function () { 584 Route::prefix('task')->group(function () {
@@ -755,6 +755,8 @@ Route::middleware(['bloginauth'])->group(function () { @@ -755,6 +755,8 @@ Route::middleware(['bloginauth'])->group(function () {
755 Route::any('/getInfo', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class,'getInfo'])->name('geo_result_getInfo'); 755 Route::any('/getInfo', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class,'getInfo'])->name('geo_result_getInfo');
756 Route::any('/getType', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'getType'])->name('geo_result_getType');//geo设置类型 756 Route::any('/getType', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'getType'])->name('geo_result_getType');//geo设置类型
757 Route::any('/getCount', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'getCount'])->name('geo_result_getCount');//geo设置类型统计数量 757 Route::any('/getCount', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'getCount'])->name('geo_result_getCount');//geo设置类型统计数量
  758 + Route::any('/countQuantity', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'countQuantity'])->name('geo_result_countQuantity');//geo统计
  759 + Route::any('/getSearchDate', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'getSearchDate'])->name('geo_result_getSearchDate');//搜索记录时间
758 }); 760 });
759 }); 761 });
760 //无需登录验证的路由组 762 //无需登录验证的路由组