作者 刘锟

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

正在显示 39 个修改的文件 包含 1057 行增加283 行删除
@@ -48,8 +48,9 @@ class AuthorityScore extends Command @@ -48,8 +48,9 @@ class AuthorityScore extends Command
48 continue; 48 continue;
49 } 49 }
50 echo '执行的项目id'.$item.PHP_EOL; 50 echo '执行的项目id'.$item.PHP_EOL;
51 - $url = $url.$domainInfo['domain'];  
52 - $data = http_get($url); 51 + $urls = $url.$domainInfo['domain'];
  52 + echo $urls.PHP_EOL;
  53 + $data = http_get($urls);
53 if(!empty($data) && !empty($data['data'])){ 54 if(!empty($data) && !empty($data['data'])){
54 echo json_encode($data).PHP_EOL; 55 echo json_encode($data).PHP_EOL;
55 $data = $data['data']; 56 $data = $data['data'];
@@ -34,87 +34,127 @@ class GeoQuestionRes extends Command @@ -34,87 +34,127 @@ class GeoQuestionRes extends Command
34 */ 34 */
35 protected $description = 'geo设置请求获取结果'; 35 protected $description = 'geo设置请求获取结果';
36 36
37 - public function handle(){  
38 - while (true){ 37 +
  38 + /**
  39 + * @return bool
  40 + */
  41 + public function handle()
  42 + {
  43 + while (true) {
39 $task_id = $this->getTaskId(); 44 $task_id = $this->getTaskId();
40 - if(empty($task_id)){  
41 - echo '无数据'.PHP_EOL;  
42 - sleep(30); 45 + if (empty($task_id)) {
  46 + sleep(300);
  47 + continue;
  48 + }
  49 + echo date('Y-m-d H:i:s').'执行的任务id:'.$task_id.PHP_EOL;
  50 + $geoQuestionModel = new GeoQuestion();
  51 + $taskInfo = $geoQuestionModel->read(['id'=>$task_id]);
  52 + if ($taskInfo === false) {
  53 + $this->output('当前任务详情为空!');
43 continue; 54 continue;
44 } 55 }
45 - $questionModel = new GeoQuestion();//问题  
46 - $info = $questionModel->read(['id'=>$task_id]);  
47 - //获取当前项目的执行频率  
48 $projectModel = new Project(); 56 $projectModel = new Project();
49 - $projectInfo = $projectModel->read(['id'=>$info['project_id']],['geo_status','geo_frequency']);  
50 - if($projectInfo['geo_status'] == 0){  
51 - $questionModel->edit(['status'=>0],['id'=>$task_id]); 57 + $projectInfo = $projectModel->read(['id' => $taskInfo['project_id']],['geo_status', 'geo_frequency']);
  58 + if ($projectInfo === false) {
  59 + $this->output('未获取到项目详情!');
  60 + $geoQuestionModel->edit(['status'=>$geoQuestionModel::STATUS_CLOSE],['id'=>$task_id]);
52 continue; 61 continue;
53 } 62 }
54 - $questionArr = $info['question'];  
55 - if(empty($questionArr)){  
56 - echo date('Y-m-d H:i:s').'当前任务不存在问题。'.PHP_EOL;  
57 - $questionModel->edit(['status'=>0],['id'=>$task_id]); 63 + if(empty($taskInfo['question']) || empty($taskInfo['keywords']) || empty($taskInfo['url'])){
  64 + $this->output('task id: ' . $task_id . ', error: 任务数据缺失, continue!');
  65 + $geoQuestionModel->edit(['status'=>$geoQuestionModel::STATUS_CLOSE],['id'=>$task_id]);
  66 + continue;
58 } 67 }
59 - //获取平台信息  
60 - $platformModel = new GeoPlatform();//平台  
61 - $platformArr = $platformModel->selectField(['status'=>$platformModel::STATUS_ON],'en_name');  
62 - if(empty($platformArr)){  
63 - echo date('Y-m-d H:i:s').'请求平台为空。'.PHP_EOL; 68 + $geoPlatformModel = new GeoPlatform();
  69 + $platformsArr = $geoPlatformModel->selectField(['status' => GeoPlatform::STATUS_ON],'en_name');
  70 + if (empty($platformsArr)) {
  71 + $this->output('未设置AI模型!');
64 continue; 72 continue;
65 } 73 }
66 - $geoService = new GeoService();  
67 - $keywordArr = $info['keywords'] ?? [];  
68 - $urlArr = $info['url'] ?? []; 74 + $geo_service = new GeoService();
69 $geoResultModel = new GeoQuestionResult(); 75 $geoResultModel = new GeoQuestionResult();
70 - foreach ($questionArr as $q_item){  
71 - foreach ($platformArr as $p_item){  
72 - $keywords = [];//命中的关键词  
73 - $urls = [];//命中的网址 76 + $geoLogModel = new GeoQuestionLog();
  77 + foreach ($taskInfo['question'] as $question) {
  78 + foreach ($platformsArr as $platform) {
  79 + $data = $hit_data = [];
  80 + $error_num = 0;
  81 + // 设置重试, 有的平台不一定能正常获取到数据
  82 + GET_RESULT:
  83 + $error_num++;
74 try { 84 try {
75 - $result_data = $geoService->setWebSearchChatAction($q_item,$p_item);  
76 - echo 'success:'.$result_data['code'].PHP_EOL;  
77 - if(isset($result_data) && $result_data['code'] == 200){  
78 - $keywords = $this->getKeywords($keywordArr,$result_data['text'] ?? []);  
79 - $urls = $this->getUrl($urlArr,$result_data['annotations'] ?? []);  
80 - }  
81 - }catch (\Exception $e){  
82 - echo $e->getMessage().PHP_EOL; 85 + echo '执行次数:'.$error_num.PHP_EOL;
  86 + if ($error_num >= 5) {
83 continue; 87 continue;
84 } 88 }
85 - //查询当前是否已有执行保存记录  
86 - $resultInfo = $geoResultModel->read(['project_id'=>$info['project_id'],'question_id'=>$info['id'],'platform'=>$p_item,'question'=>$q_item],['id']);  
87 - //保存一条结果记录  
88 - $data = [  
89 - 'project_id'=>$info['project_id'],  
90 - 'question_id'=>$info['id'],  
91 - 'platform'=>$p_item,  
92 - 'question'=>$q_item,  
93 - 'keywords'=>json_encode($keywords ?? [],true),//命中的关键词  
94 - 'text'=>json_encode($result_data ?? [],true),  
95 - 'url'=>json_encode($urls ?? [],true),//命中的网址  
96 - 'type'=>$info['type'] ?? 1 89 + echo '执行平台:'.$platform.PHP_EOL;
  90 + if ($platform == 'Google AI Overview') {
  91 + // overview 数据结构不确定, 需要单独处理数据
  92 + $data = $geo_service->getGooglePlatformResult($question);
  93 + $result = $this->dealGoogleData($data);
  94 + } else {
  95 + $result = $geo_service->getAiPlatformResult($question, $platform);
  96 + }
  97 + if (empty($result['text'])){
  98 + goto GET_RESULT;
  99 + }
  100 + } catch (\Exception $e) {
  101 + $this->output('task id:' . $task_id . ', question: ' . $question . ', platform: ' . $platform . ', error: ' . $e->getMessage());
  102 + goto GET_RESULT;
  103 + }
  104 + // 命中文案
  105 + $hit_data[] = $result['text'];
  106 + if(!empty($result['annotations'])){
  107 + $url = array_column(array_column($result['annotations'], 'url_citation'), 'url');
  108 + $title = array_column(array_column($result['annotations'], 'url_citation'), 'title');
  109 + $hit_data = array_merge($url, $title, $hit_data);
  110 + }
  111 + // 命中关键词和路由
  112 + $hit_keyword = $hit_url = [];
  113 + $hit = 0;
  114 + if (!empty($taskInfo['keywords'])) {
  115 + $hit_keyword = $this->getKeywords($taskInfo['keywords'],$hit_data);
  116 + if (!empty($hit_keyword)) {
  117 + $hit++;
  118 + }
  119 + }
  120 + if (!empty($taskInfo['url'])) {
  121 + $hit_url = $this->getUrl($taskInfo['url'],$hit_data);
  122 + if (!empty($hit_url)) {
  123 + $hit++;
  124 + }
  125 + }
  126 + echo 'MZ-url'.json_encode($hit_url).PHP_EOL;
  127 + // 保存数据结果
  128 + $geo_result = $geoResultModel->read(['project_id' => $taskInfo['project_id'], 'question_id' => $task_id, 'platform' => $platform, 'question' => $question],['id']);
  129 + $save_data = [
  130 + 'project_id' => $taskInfo['project_id'],
  131 + 'question_id' => $task_id,
  132 + 'type' => $taskInfo['type'] ?? $geoQuestionModel::TYPE_BRAND,
  133 + 'platform' => $platform,
  134 + 'question' => $question,
  135 + 'keywords' => json_encode($hit_keyword,true),//命中的关键词
  136 + 'url' => json_encode($hit_url,true),//命中的网址
  137 + 'text' => json_encode($result,true),
  138 + 'hit' => $hit,
  139 + 'created_at'=>date('Y-m-d H:i:s'),
  140 + 'updated_at'=>date('Y-m-d H:i:s'),
97 ]; 141 ];
98 - if($resultInfo === false){  
99 - $geoResultModel->addReturnId($data); 142 +// echo '当前数据INFO:'.json_encode($save_data,true).PHP_EOL;
  143 + if($geo_result === false){
  144 + $id= $geoResultModel->insertGetId($save_data);
  145 + echo '当前数据id:'.$id.PHP_EOL;
100 }else{ 146 }else{
101 - $geoResultModel->edit($data,['id'=>$resultInfo['id']]);  
102 - }  
103 - $data_log = [  
104 - 'project_id'=>$info['project_id'],  
105 - 'question_id'=>$info['id'],  
106 - 'platform'=>$p_item,  
107 - 'question'=>$q_item,  
108 - 'text'=>json_encode($result_data ?? [],true),  
109 - 'type'=>$info['type'] ?? 1  
110 - ];  
111 - $geoLogModel = new GeoQuestionLog();  
112 - $geoLogModel->addReturnId($data_log); 147 + $geoResultModel->edit($save_data, ['id' => $geo_result['id']]);
113 } 148 }
  149 + $save_data['text'] = json_encode(!empty($data) ? $data : $result,true);
  150 + $geoLogModel->addReturnId($save_data);
  151 + echo '执行结束:'.$platform.PHP_EOL;
114 } 152 }
115 - //更新下次执行时间  
116 - $questionModel->edit(['current_time'=>date('Y-m-d'),'next_time'=>date('Y-m-d', strtotime(date('Y-m-d') . ' +'.(int)$projectInfo['geo_frequency'].' days'))],['id'=>$info['id']]);  
117 } 153 }
  154 + $next_time = date('Y-m-d', strtotime('+' . ($projectInfo['geo_frequency'] ?? 3) . ' days'));
  155 + $geoQuestionModel->edit(['current_time'=>date('Y-m-d'),'next_time'=>$next_time],['id'=>$task_id]);
  156 + }
  157 + return true;
118 } 158 }
119 159
120 /** 160 /**
@@ -124,25 +164,16 @@ class GeoQuestionRes extends Command @@ -124,25 +164,16 @@ class GeoQuestionRes extends Command
124 * @method :post 164 * @method :post
125 * @time :2025/7/3 16:38 165 * @time :2025/7/3 16:38
126 */ 166 */
127 - public function getUrl($urlArr = [],$result_annotations = [],$result_text = []){ 167 + public function getUrl($urlArr = [],$result_annotations = []){
128 $url = []; 168 $url = [];
129 if(!empty($urlArr)){ 169 if(!empty($urlArr)){
  170 + $str = implode(',',$result_annotations);
130 foreach ($urlArr as $u_item){ 171 foreach ($urlArr as $u_item){
131 - if(!empty($result_text)){  
132 - if (str_contains($result_text, $u_item)) {  
133 - $url[] = $u_item;  
134 - }  
135 - }  
136 - if(!empty($result_annotations)){  
137 - foreach ($result_annotations as $a_item){  
138 - echo 'url'.$a_item['url_citation']['url'].PHP_EOL.'当前的url:'.$u_item;  
139 - if (str_contains($a_item['url_citation']['url'], $u_item)) { 172 + if (str_contains($str, $u_item)) {
140 $url[] = $u_item; 173 $url[] = $u_item;
141 } 174 }
142 } 175 }
143 } 176 }
144 - }  
145 - }  
146 return array_values(array_unique($url)); 177 return array_values(array_unique($url));
147 } 178 }
148 179
@@ -156,8 +187,9 @@ class GeoQuestionRes extends Command @@ -156,8 +187,9 @@ class GeoQuestionRes extends Command
156 public function getKeywords($keywordArr = [],$result_text = []){ 187 public function getKeywords($keywordArr = [],$result_text = []){
157 $keywords = []; 188 $keywords = [];
158 if(!empty($keywordArr) && !empty($result_text)){ 189 if(!empty($keywordArr) && !empty($result_text)){
  190 + $str = implode(',',$result_text);
159 foreach ($keywordArr as $k_item){ 191 foreach ($keywordArr as $k_item){
160 - if (str_contains($result_text, $k_item)) { 192 + if (str_contains($str, $k_item)) {
161 $keywords[] = $k_item; 193 $keywords[] = $k_item;
162 } 194 }
163 } 195 }
@@ -166,24 +198,94 @@ class GeoQuestionRes extends Command @@ -166,24 +198,94 @@ class GeoQuestionRes extends Command
166 } 198 }
167 199
168 /** 200 /**
169 - * @remark :拉取任务id  
170 - * @name :getTaskId  
171 - * @author :lyh  
172 - * @method :post  
173 - * @time :2025/7/3 15:15 201 + * 整合Google平台数据
  202 + * @param $data
  203 + * @return array
  204 + */
  205 + public function dealGoogleData($data)
  206 + {
  207 + $result = [
  208 + 'code' => 200,
  209 + 'model' => 'Google AI Overview',
  210 + 'text' => '',
  211 + ];
  212 + $texts = [];
  213 + if(!empty($data['data']['text_parts']) && is_array($data['data']['text_parts'])){
  214 + foreach ($data['data']['text_parts'] as $item){
  215 + switch ($item['type']){
  216 + case 'paragraph':
  217 + if(isset($item['text']) && !empty($item['text'])){
  218 + array_push($texts, $item['text']);
  219 + }
  220 + break;
  221 + case 'title':
  222 + if(isset($item['text']) && !empty($item['text'])) {
  223 + array_unshift($texts, $item['text']);
  224 + }
  225 + break;
  226 + case 'list':
  227 + if(!empty($item['list'])){
  228 + foreach ($item['list'] as $sonItem){
  229 + if(isset($sonItem['text']) && !empty($sonItem['text'])) {
  230 + array_push($texts, $sonItem['text']);
  231 + }
  232 + if(isset($item['title']) && !empty($item['title'])) {
  233 + array_push($texts, $sonItem['title']);
  234 + }
  235 + }
  236 + }
  237 + break;
  238 + default:
  239 + break;
  240 + }
  241 + }
  242 + }
  243 + if(!empty($data['data']['reference_links']) && is_array($data['data']['reference_links'])){
  244 + foreach ($data['data']['reference_links'] as $link) {
  245 + if (isset($link['title']) && !empty($link['title']) && isset($link['link']) && !empty($link['link'])) {
  246 + $result['annotations'][] = [
  247 + 'type' => 'url_citation',
  248 + 'url_citation' => [
  249 + 'url' => $link['link'],
  250 + 'title' => $link['title']
  251 + ],
  252 + ];
  253 + }
  254 + }
  255 + }
  256 + $text = implode(PHP_EOL, $texts);
  257 + $result['text'] = $text;
  258 + return $result;
  259 + }
  260 +
  261 + /**
  262 + * 获取待执行任务ID
  263 + * @return mixed
174 */ 264 */
175 public function getTaskId(){ 265 public function getTaskId(){
176 - $task_id = Redis::rpop('geo_question_result'); 266 + $key = 'geo_task_list';
  267 + $task_id = Redis::rpop($key);
177 if(empty($task_id)){ 268 if(empty($task_id)){
178 $questionModel = new GeoQuestion(); 269 $questionModel = new GeoQuestion();
179 - $ids = $questionModel->selectField(['status'=>1,'next_time'=>['<=',date('Y-m-d')]],'id'); 270 + $ids = $questionModel->selectField(['status'=>$questionModel::STATUS_OPEN,'next_time'=>['<=',date('Y-m-d')]],'id');
180 if(!empty($ids)){ 271 if(!empty($ids)){
181 foreach ($ids as $id) { 272 foreach ($ids as $id) {
182 - Redis::lpush('geo_question_result', $id); 273 + Redis::lpush($key, $id);
183 } 274 }
184 } 275 }
185 - $task_id = Redis::rpop('geo_question_result'); 276 + $task_id = Redis::rpop($key);
186 } 277 }
187 return $task_id; 278 return $task_id;
188 } 279 }
  280 +
  281 + /**
  282 + * 输出日志
  283 + * @param $message
  284 + * @return bool
  285 + */
  286 + public function output($message)
  287 + {
  288 + echo date('Y-m-d H:i:s') . ' ' . $message . PHP_EOL;
  289 + return true;
  290 + }
189 } 291 }
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 namespace App\Console\Commands\LyhTest; 10 namespace App\Console\Commands\LyhTest;
11 11
12 use App\Models\Ai\AiBlog; 12 use App\Models\Ai\AiBlog;
  13 +use App\Models\News\News;
13 use App\Models\Product\Category; 14 use App\Models\Product\Category;
14 use App\Models\Project\AggregateKeywordAffix; 15 use App\Models\Project\AggregateKeywordAffix;
15 use App\Models\Project\AiBlogTask; 16 use App\Models\Project\AiBlogTask;
@@ -41,6 +42,15 @@ class lyhDemo extends Command @@ -41,6 +42,15 @@ class lyhDemo extends Command
41 protected $description = '更新路由'; 42 protected $description = '更新路由';
42 43
43 public function handle(){ 44 public function handle(){
  45 + ProjectServer::useProject(3531);
  46 + $newsModel = new News();
  47 + $rows = $newsModel->select('id', 'text')->get();
  48 + foreach ($rows as $row) {
  49 + echo '执行数据id:'.$row->id.PHP_EOL;
  50 + $newText = preg_replace('/<h1 class="t">.*?<\/h1>/is', '', $row->text);
  51 + $newsModel->where('id', $row->id)->update(['text' => $newText]);
  52 + }
  53 + DB::disconnect('custom_mysql');
44 return true; 54 return true;
45 } 55 }
46 56
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :InitKeywordComment.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/6/3 15:38
  8 + */
  9 +
  10 +namespace App\Console\Commands\Project;
  11 +
  12 +use App\Helper\Common;
  13 +use App\Helper\Gpt;
  14 +use App\Models\Ai\AiCommand;
  15 +use App\Models\Com\NoticeLog;
  16 +use App\Models\Project\AggregateKeywordComment;
  17 +use App\Models\Project\Project;
  18 +use Illuminate\Console\Command;
  19 +
  20 +class ProjectComment extends Command
  21 +{
  22 + /**
  23 + * The name and signature of the console command.
  24 + *
  25 + * @var string
  26 + */
  27 + protected $signature = 'project_comment';
  28 +
  29 + /**
  30 + * The console command description.
  31 + *
  32 + * @var string
  33 + */
  34 + protected $description = '初始化关键字评论';
  35 +
  36 + public $number = 100;
  37 +
  38 + public function handle(){
  39 + $projectModel = new Project();
  40 + $ids = $projectModel->selectField(['delete_status' => 0,'project_type'=>0,'extend_type'=>0,'type'=>['in',[1,2,3,4,6]]], 'id');
  41 + foreach ($ids as $id){
  42 + echo date('Y-m-d H:i:s').'start:' . $id . PHP_EOL;
  43 + $project_id = $id;
  44 + echo date('Y-m-d H:i:s').'执行的项目id:' . $project_id . PHP_EOL;
  45 + try {
  46 + $this->_action($project_id);
  47 + }catch (\Exception $e){
  48 + echo date('Y-m-d H:i:s').'错误信息:'.$e->getMessage().PHP_EOL;
  49 + continue;
  50 + }
  51 + echo date('Y-m-d H:i:s').'end:' . $id . PHP_EOL;
  52 + }
  53 + return true;
  54 + }
  55 +
  56 + /**
  57 + * @remark :执行的方法
  58 + * @name :_action
  59 + * @author :lyh
  60 + * @method :post
  61 + * @time :2025/6/3 15:42
  62 + */
  63 + public function _action($project_id){
  64 + $aiCommonModel = new AiCommand();
  65 + $info = $aiCommonModel->read(['key'=>'tag_comment']);
  66 + $info['ai'] = str_replace('50', '100', $info['ai']);
  67 + $text = Gpt::instance()->openai_chat_qqs($info['ai']);
  68 + $text = Common::deal_keywords($text);
  69 + preg_match_all('/\{[^{}]*\}/', $text, $matches);
  70 + if(!empty($text)){
  71 + $data = [];
  72 + foreach ($matches[0] as $item){
  73 + $item = str_replace("'", '"', $item);
  74 + // 解码成 PHP 关联数组
  75 + $item = json_decode($item, true);
  76 + if(!isset($item['name']) || !isset($item['comment'])){
  77 + continue;
  78 + }
  79 + $twoMonthsAgo = strtotime('-2 months');
  80 + $randomTimestamp = rand($twoMonthsAgo, time());
  81 + $randomDateTime = date('Y-m-d H:i:s', $randomTimestamp);
  82 + $data[] = [
  83 + 'nickname'=>$item['name'],
  84 + 'text'=>$item['comment'],
  85 + 'project_id'=>$project_id,
  86 + 'type'=>1,
  87 + 'uid'=>0,
  88 + 'start_time'=>$randomDateTime,
  89 + 'created_at'=>date('Y-m-d H:i:s'),
  90 + 'updated_at'=>date('Y-m-d H:i:s')
  91 + ];
  92 + }
  93 + $keywordCommentModel = new AggregateKeywordComment();
  94 + $keywordCommentModel->insertAll($data);
  95 + }
  96 + return true;
  97 + }
  98 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2025/7/4
  6 + * Time: 11:14
  7 + */
  8 +namespace App\Console\Commands\Statistics;
  9 +
  10 +use App\Models\Project\ProjectFlow;
  11 +use App\Services\UpyunService;
  12 +use Illuminate\Console\Command;
  13 +use Illuminate\Support\Str;
  14 +
  15 +class Flow extends Command
  16 +{
  17 + /**
  18 + * The name and signature of the console command.
  19 + *
  20 + * @var string
  21 + */
  22 + protected $signature = 'project_flow_statistics';
  23 +
  24 + /**
  25 + * The console command description.
  26 + *
  27 + * @var string
  28 + */
  29 + protected $description = '项目流量统计';
  30 +
  31 + /**
  32 + * Create a new command instance.
  33 + *
  34 + * @return void
  35 + */
  36 + public function __construct()
  37 + {
  38 + parent::__construct(); // 确保调用父类构造函数
  39 + }
  40 +
  41 + public function handle()
  42 + {
  43 + $this->cdnStatistics();
  44 + return true;
  45 + }
  46 +
  47 + public function cdnStatistics()
  48 + {
  49 + $class = new UpyunService();
  50 +
  51 + list($start_at, $end_at) = $this->getTime();
  52 + $date = date('Y-m-d', strtotime($end_at));
  53 + echo 'start_at: ' . $start_at . PHP_EOL;
  54 + echo 'end_at: ' . $end_at . PHP_EOL;
  55 + $result = $class->realTimeStatistics($start_at, $end_at);
  56 +
  57 + file_put_contents(storage_path('logs/flow/' . date('YmdHis', strtotime($start_at)) . '.json'), $result);
  58 +// $result = file_get_contents(storage_path('logs/flow/' . date('YmdHis', strtotime($start_at)) . '.json'));
  59 + $result = json_decode($result, true);
  60 + $flow = [];
  61 + if (FALSE == empty($result['data']) && is_array($result['data'])) {
  62 + // 结算 所有项目 流量
  63 + foreach ($result['data'] as $item) {
  64 + if (Str::startsWith($item['key'], '/upload/p/')) {
  65 + $tmp = explode('/', $item['key']);
  66 + $flow[$tmp[3]] = FALSE == empty($flow[$tmp[3]]) ? $flow[$tmp[3]] + $item['val'] : $item['val'];
  67 + }
  68 + }
  69 + }
  70 +
  71 + ksort($flow);
  72 + $total_flow = 0;
  73 + foreach ($flow as $project_id=>$val) {
  74 + ProjectFlow::flowInsert($project_id, $date, $val, $end_at);
  75 + $total_flow += $val;
  76 + }
  77 + echo 'total project: ' . count($flow) . PHP_EOL;
  78 + echo 'total flow: ' . $total_flow . PHP_EOL;
  79 + return true;
  80 + }
  81 +
  82 + /**
  83 + * @return array
  84 + */
  85 + public function getTime()
  86 + {
  87 + $last_at_log = ProjectFlow::orderBy('last_at', 'desc')->first();
  88 + $today = date('Y-m-d 00:00:00');
  89 + $start_at = $last_at_log ? $last_at_log->last_at : $today;
  90 + if (strtotime($start_at) < strtotime($today))
  91 + $start_at = $today;
  92 + $end_at = date('Y-m-d H:i:s', time() - 1);
  93 + return [$start_at, $end_at];
  94 + }
  95 +}
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 9
10 namespace App\Console\Commands\Sync; 10 namespace App\Console\Commands\Sync;
11 11
  12 +use App\Models\Manage\Mobile;
12 use App\Models\User\User; 13 use App\Models\User\User;
13 use Illuminate\Console\Command; 14 use Illuminate\Console\Command;
14 use Illuminate\Support\Facades\DB; 15 use Illuminate\Support\Facades\DB;
@@ -40,30 +41,42 @@ class SyncMobile extends Command @@ -40,30 +41,42 @@ class SyncMobile extends Command
40 $url = 'https://www.quanqiusou.cn/extend_api/saas/get_phone.php'; 41 $url = 'https://www.quanqiusou.cn/extend_api/saas/get_phone.php';
41 // $data = curlGet($url);//TODO::获取号码库 42 // $data = curlGet($url);//TODO::获取号码库
42 $client = new \GuzzleHttp\Client(); 43 $client = new \GuzzleHttp\Client();
  44 + try {
43 $data = $client->request('GET', $url, [ 45 $data = $client->request('GET', $url, [
44 'proxy' => env('CURL_PROXY'), // 代理服务器地址和端口号 46 'proxy' => env('CURL_PROXY'), // 代理服务器地址和端口号
45 ])->getBody()->getContents(); 47 ])->getBody()->getContents();
  48 + }catch (\Exception $e){
  49 + echo date('Y-m-d H:i:s').':未拉起到数据'.PHP_EOL;
  50 + }
  51 + if(!empty($data)){
  52 + $mobileModel = new Mobile();
  53 + $mobileModel->truncate();
46 $data = json_decode($data, true); 54 $data = json_decode($data, true);
47 - DB::table('gl_mobile')->delete();  
48 - $create_time = date('Y-m-d H:i:s');  
49 - foreach ($data as $v){ 55 + $userModel = new User();
  56 + foreach ($data as $mobile){
50 $param = [ 57 $param = [
51 - 'mobile'=>$v,  
52 - 'created_at'=>$create_time 58 + 'mobile'=>$mobile,
  59 + 'created_at'=>date('Y-m-d H:i:s')
  60 + ];
  61 + $mobileModel->insert($param);
  62 + //查看当前用户是否存在
  63 + $info = $userModel->read(['mobile'=>$mobile,'project_id'=>1]);
  64 + if($info === false){
  65 + $data = [
  66 + 'mobile'=>$mobile,
  67 + 'password'=>base64_encode(md5('123456')),
  68 + 'project_id'=>1,
  69 + 'name'=>$mobile,
  70 + 'type'=>$userModel::TYPE_ONE
53 ]; 71 ];
54 - DB::table('gl_mobile')->insert($param); 72 + $userModel->add($data);
  73 + }
55 } 74 }
56 - if(!empty($data)){  
57 - $userModel = new User();  
58 - try {  
59 $data[] = '13083988828'; 75 $data[] = '13083988828';
60 - $data[] = '6591559603';  
61 $userModel->edit(['status'=>1],['project_id'=>1,'mobile'=>['not in',$data]]); 76 $userModel->edit(['status'=>1],['project_id'=>1,'mobile'=>['not in',$data]]);
62 $userModel->edit(['status'=>0],['project_id'=>1,'mobile'=>['in',$data]]); 77 $userModel->edit(['status'=>0],['project_id'=>1,'mobile'=>['in',$data]]);
63 - }catch (\Exception $e){  
64 - echo date('Y-m-d H:i:s') . 'error' . PHP_EOL;  
65 - }  
66 } 78 }
  79 + echo 'end.'.PHP_EOL;
  80 + return true;
67 } 81 }
68 -  
69 } 82 }
@@ -5,9 +5,11 @@ namespace App\Console\Commands\WorkOrder; @@ -5,9 +5,11 @@ namespace App\Console\Commands\WorkOrder;
5 use App\Models\Manage\Manage; 5 use App\Models\Manage\Manage;
6 use App\Models\Manage\ManageHr; 6 use App\Models\Manage\ManageHr;
7 use App\Models\Project\Project; 7 use App\Models\Project\Project;
  8 +use App\Models\ProjectAssociation\ProjectAssociation;
8 use App\Models\WorkOrder\TicketProject; 9 use App\Models\WorkOrder\TicketProject;
9 use Illuminate\Console\Command; 10 use Illuminate\Console\Command;
10 use Illuminate\Support\Facades\Http; 11 use Illuminate\Support\Facades\Http;
  12 +use Illuminate\Support\Str;
11 13
12 class FetchTicketProjects extends Command 14 class FetchTicketProjects extends Command
13 { 15 {
@@ -16,7 +18,7 @@ class FetchTicketProjects extends Command @@ -16,7 +18,7 @@ class FetchTicketProjects extends Command
16 * 18 *
17 * @var string 19 * @var string
18 */ 20 */
19 - protected $signature = 'workorder:fetch-ticket-projects {action}}'; 21 + protected $signature = 'workorder:fetch-ticket-projects {action}';
20 22
21 /** 23 /**
22 * The console command description. 24 * The console command description.
@@ -83,6 +85,7 @@ class FetchTicketProjects extends Command @@ -83,6 +85,7 @@ class FetchTicketProjects extends Command
83 'test_website' => $item['test_url'] ?? '', 85 'test_website' => $item['test_url'] ?? '',
84 'is_del' => 0, 86 'is_del' => 0,
85 'plan' => $item['plan'] ?? '', 87 'plan' => $item['plan'] ?? '',
  88 + 'project_cate' => 1,
86 ]; 89 ];
87 if (!$project) { 90 if (!$project) {
88 $new = new TicketProject(); 91 $new = new TicketProject();
@@ -177,6 +180,11 @@ class FetchTicketProjects extends Command @@ -177,6 +180,11 @@ class FetchTicketProjects extends Command
177 'test_website' => $item->deploy_build->test_domain ?? '', 180 'test_website' => $item->deploy_build->test_domain ?? '',
178 'version' => empty($item->version) ? 7 : $item->version, // 版本号 181 'version' => empty($item->version) ? 7 : $item->version, // 版本号
179 'plan' => $item->planMap()[$item->deploy_build->plan] ?? '', 182 'plan' => $item->planMap()[$item->deploy_build->plan] ?? '',
  183 + 'project_cate' => 2,
  184 + 'wechat_group_id' => ProjectAssociation::where('project_id', $project->table_id)
  185 + ->where('status', ProjectAssociation::STATUS_NORMAL)
  186 + ->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)
  187 + ->value('friend_id')
180 ]; 188 ];
181 if (!$project) { 189 if (!$project) {
182 $project = new TicketProject(); 190 $project = new TicketProject();
@@ -209,6 +217,98 @@ class FetchTicketProjects extends Command @@ -209,6 +217,98 @@ class FetchTicketProjects extends Command
209 } 217 }
210 } 218 }
211 219
  220 + public function fetchAICC()
  221 + {
  222 + $lastid = 0;
  223 + while (true) {
  224 + try {
  225 + $response = Http::withBasicAuth('bill', 'bill@ai.cc')
  226 + ->get('https://fob.ai.cc/api/tickets/projects', [
  227 + 'lastid' => $lastid,
  228 + ]);
  229 + $items = $response->json();
  230 + if (empty($items))
  231 + {
  232 + echo now() . " | INFO | not found items \n";
  233 + break;
  234 + }
  235 +
  236 + foreach ($items as $item) {
  237 + foreach ($item['plans'] as $plan)
  238 + {
  239 + // 判断套餐是超迹还是域途, 如果 $item['plans'][0]['name'] 包含 '超迹' 则为超迹,否则为域途
  240 + $project_cate = Str::contains($plan['name'], '超迹') ? 3 : 4;
  241 + $uuid = md5("AICC{$item['id']}{$project_cate}");
  242 + $project = TicketProject::where('uuid', $uuid)->first();
  243 + if ($project_cate == 3)
  244 + {
  245 + // 售后服务经理
  246 + $assm_id = collect([
  247 + ManageHr::where('name', $item['cj_assm']['real_name'] ?? '')->first()->manage_id ?? 0,
  248 + 20, //徐莹
  249 + ])->first(fn($v) => $v !== null && $v !== 0, 0);
  250 + }else
  251 + {
  252 + // 域途
  253 + $assm_id = collect([
  254 + ManageHr::where('name', $item['yutu_assm']['real_name'] ?? '')->first()->manage_id ?? 0,
  255 + 85, //黄小玉
  256 + ])->first(fn($v) => $v !== null && $v !== 0, 0);
  257 + }
  258 +
  259 + // 优化师
  260 + $seom_id = 0;
  261 + // 第一负责人
  262 + $engineer_id = $assm_id;
  263 + $is_del = 0;
  264 +
  265 + $fields = [
  266 + 'company_name' => $item['company'],
  267 + 'title' => $item['company'] . " - " . $plan['name'],
  268 + 'assm_id' => $assm_id,
  269 + 'seom_id' => $seom_id,
  270 + 'engineer_id' => $engineer_id,
  271 + 'is_del' => $is_del,
  272 + 'website' => '',
  273 + 'test_website' => '',
  274 + 'version' => 1, // 版本号
  275 + 'plan' => $plan['name'] ?? '',
  276 + 'project_cate' => $project_cate,
  277 + 'wechat_group_id' => $item['chatroom_id'],
  278 + ];
  279 +
  280 + if (!$project) {
  281 + $project = new TicketProject();
  282 + $project->uuid = $uuid;
  283 + $project->post_id = $item['postid'];
  284 + $project->table_id = $item['id'];
  285 + foreach ($fields as $k => $v) {
  286 + $project->$k = $v;
  287 + }
  288 + $project->save();
  289 + } else {
  290 + $changed = false;
  291 + foreach ($fields as $k => $v) {
  292 + if ($project->$k != $v) {
  293 + $project->$k = $v;
  294 + $changed = true;
  295 + }
  296 + }
  297 + if ($changed) {
  298 + $project->save();
  299 + }
  300 + }
  301 + $lastid = $item['id'];
  302 + echo now() . " | INFO | AICC: {$item['id']} {$item['company']} fetch ok \n";
  303 + }
  304 + }
  305 + }catch (\Exception $exception){
  306 + echo now() . " | ERROR | " . $exception->getMessage() . "\n" . $exception->getTraceAsString() . "\n";
  307 + break;
  308 + }
  309 + }
  310 + }
  311 +
212 public function fetch_uuid() 312 public function fetch_uuid()
213 { 313 {
214 $lastid = 0; 314 $lastid = 0;
1 -<?php  
2 -  
3 -namespace App\Console\Commands\WorkOrder;  
4 -  
5 -use App\Models\ProjectAssociation\ProjectAssociation;  
6 -use App\Models\Workchat\MessagePush;  
7 -use App\Models\WorkOrder\Tickets;  
8 -use Illuminate\Console\Command;  
9 -  
10 -class PushNotify extends Command  
11 -{  
12 - /**  
13 - * The name and signature of the console command.  
14 - *  
15 - * @var string  
16 - */  
17 - protected $signature = 'workorder:push-notify';  
18 -  
19 - /**  
20 - * The console command description.  
21 - *  
22 - * @var string  
23 - */  
24 - protected $description = 'tickets push notify';  
25 -  
26 - /**  
27 - * Create a new command instance.  
28 - *  
29 - * @return void  
30 - */  
31 - public function __construct()  
32 - {  
33 - parent::__construct();  
34 - }  
35 -  
36 - /**  
37 - * Execute the console command.  
38 - *  
39 - * @return int  
40 - */  
41 - public function handle()  
42 - {  
43 - while (true) {  
44 - try {  
45 - $tick = Tickets::where('ding', 0)  
46 - ->where('submit_side', 2)  
47 -// ->where('project_id', 1)  
48 - ->first();  
49 - if (!$tick) {  
50 - echo now() . " WARNING | 没有待推送的工单\n";  
51 - sleep(3);  
52 - continue;  
53 - }  
54 - $project = $tick->project;  
55 - if ($project->version != 6 || $project->is_del == 1) {  
56 - echo now() . " WARNING | 项目版本或状态异常 \n";  
57 - $tick->ding = 1;  
58 - $tick->save();  
59 - continue;  
60 - }  
61 - $message_push = new MessagePush();  
62 - $message_push->project_id = $project->table_id;  
63 - $message_push->friend_id = ProjectAssociation::where('project_id', $project->table_id)  
64 - ->where('status', ProjectAssociation::STATUS_NORMAL)  
65 - ->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)  
66 - ->value('friend_id');  
67 - if (empty($message_push->friend_id))  
68 - {  
69 - echo now() . " WARNING | 项目ID:{$project->table_id} 没有绑定企微群\n";  
70 - $tick->ding = 1;  
71 - $tick->save();  
72 - continue;  
73 - }  
74 - $message_push->content_type = 'Link';  
75 - $message_push->content = json_encode([  
76 - 'title' => '工单查看 - ' . $project->company_name,  
77 - 'desc' => $tick->title,  
78 - 'size' => 0,  
79 - 'thumbSize' => 0,  
80 - 'thumbUrl' => 'https://oa.quanqiusou.cn/logo.ico',  
81 - 'url' => 'https://oa.quanqiusou.cn/tickets?project_id='.$project->uuid  
82 - ], JSON_UNESCAPED_UNICODE);  
83 - $message_push->send_time = now();  
84 - $message_push->type = MessagePush::TYPE_TICKET;  
85 - $message_push->save();  
86 - $tick->ding = 1;  
87 - $tick->save();  
88 - echo now() . " INFO | 项目ID:{$project->table_id} 工单ID:{$tick->id} 推送成功\n";  
89 - }catch (\Exception $exception){  
90 - echo date('Y-m-d H:i:s')." ERROR | ".$exception->getMessage()."\n";  
91 - break;  
92 - }  
93 - }  
94 - }  
95 -}  
@@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
2 2
3 namespace App\Console\Commands\WorkOrder; 3 namespace App\Console\Commands\WorkOrder;
4 4
  5 +use App\Models\Manage\Manage;
  6 +use App\Models\WorkOrder\TicketChat;
5 use App\Models\WorkOrder\TicketLog; 7 use App\Models\WorkOrder\TicketLog;
6 use App\Services\DingTalkService; 8 use App\Services\DingTalkService;
7 use Illuminate\Console\Command; 9 use Illuminate\Console\Command;
@@ -14,7 +16,7 @@ class WorkOrderDing extends Command @@ -14,7 +16,7 @@ class WorkOrderDing extends Command
14 * 16 *
15 * @var string 17 * @var string
16 */ 18 */
17 - protected $signature = 'workorder:ding'; 19 + protected $signature = 'workorder:ding {action}';
18 20
19 /** 21 /**
20 * The console command description. 22 * The console command description.
@@ -40,11 +42,17 @@ class WorkOrderDing extends Command @@ -40,11 +42,17 @@ class WorkOrderDing extends Command
40 */ 42 */
41 public function handle() 43 public function handle()
42 { 44 {
  45 + $action = $this->argument('action');
  46 + $this->$action();
  47 + }
  48 +
  49 + public function dingLog()
  50 + {
43 while (true) { 51 while (true) {
44 try { 52 try {
45 $log = TicketLog::where('ding', 0)->first(); 53 $log = TicketLog::where('ding', 0)->first();
46 if (!$log) { 54 if (!$log) {
47 - echo now() . " INFO | 没有通知任务\n"; 55 + echo now() . " | INFO | 没有通知任务\n";
48 sleep(3); 56 sleep(3);
49 continue; 57 continue;
50 } 58 }
@@ -55,23 +63,82 @@ class WorkOrderDing extends Command @@ -55,23 +63,82 @@ class WorkOrderDing extends Command
55 )->get('https://oa.cmer.com/api/dingding/user/' . $mobile); 63 )->get('https://oa.cmer.com/api/dingding/user/' . $mobile);
56 if ($response->status() == 200) { 64 if ($response->status() == 200) {
57 $userid = $response->json()['data']['userid']; 65 $userid = $response->json()['data']['userid'];
58 - $text = "**您有新的售后工单**<br>";  
59 - $text .= "工单ID:{$log->work_order_id}<br>";  
60 - $text .= "工单类型:<font color='red'>{$log->workOrder->product}</font><br>";  
61 - $text .= "项目:{$log->workOrder->project->title}<br>";  
62 - $text .= "时间:{$log->created_at}<br>";  
63 $ding = new DingTalkService(); 66 $ding = new DingTalkService();
64 $resp = $ding->danliao(json_encode([ 67 $resp = $ding->danliao(json_encode([
65 - 'text' => $text,  
66 - 'title' => '售后工单通知',  
67 - ]), [$userid]); 68 + 'text' => "您有新的工单(ID: {$log->ticket_id}),请及时处理!",
  69 + 'title' => 'AI协同工单 - ' . $log->ticket->project->title,
  70 + 'picUrl' => 'https://hub.globalso.com/logocm.png',
  71 + 'messageUrl' => 'https://oa.quanqiusou.cn/afterorder?project_id=' . $log->ticket->project->uuid,
  72 + ]), [$userid], 'sampleLink');
68 $log->ding = 1; 73 $log->ding = 1;
  74 + echo now() . " | INFO | 工单ID: {$log->ticket_id} 通知成功\n";
69 }else 75 }else
  76 + {
70 $log->ding = 2; 77 $log->ding = 2;
  78 + echo now() . " | ERROR | 工单ID: {$log->ticket_id} 通知失败\n";
  79 + }
71 $log->save(); 80 $log->save();
72 }catch (\Exception $exception){ 81 }catch (\Exception $exception){
73 - echo now() . " ERROR | ".$exception->getMessage()."\n";  
74 - break; 82 + echo now() . " | ERROR | log ID {$log->id} {$exception->getMessage()} {$exception->getTraceAsString()} \n";
  83 + $log->ding = 2;
  84 + $log->save();
  85 + }
  86 + }
  87 + }
  88 +
  89 + public function dingChat()
  90 + {
  91 + while (true) {
  92 + $chat = TicketChat::where([
  93 + 'ding' => 0,
  94 + 'submit_side' => 2
  95 + ])->first();
  96 +
  97 + if (!$chat) {
  98 + echo now() . " | INFO | 没有通知任务\n";
  99 + sleep(3);
  100 + continue;
  101 + }
  102 +
  103 + try {
  104 + $project = $chat->ticket->project;
  105 +
  106 + // 通知谁?暂时通知A端最近一次提交的chat记录的人,如果没有,则通第一负责人
  107 + $lastChat = TicketChat::where('ticket_id', $chat->ticket_id)
  108 + ->where('submit_side', 1)
  109 + ->orderBy('id', 'desc')
  110 + ->first();
  111 + if ($lastChat) {
  112 + $mobile = Manage::where('id', $lastChat->manage_id)->first()->mobile;
  113 + }else
  114 + {
  115 + $mobile = Manage::where('id', $project->first_engineer)->first()->mobile;
  116 + }
  117 + $response = Http::withBasicAuth(
  118 + env('DINGDING_BASIC_USER'),
  119 + env('DINGDING_BASIC_PASS')
  120 + )->get('https://oa.cmer.com/api/dingding/user/' . $mobile);
  121 + if ($response->status() == 200) {
  122 + $userid = $response->json()['data']['userid'];
  123 + $ding = new DingTalkService();
  124 + $resp = $ding->danliao(json_encode([
  125 + 'text' => "客户对工单(ID: {$chat->ticket_id})进行了补充,请及时查看处理!",
  126 + 'title' => 'AI协同工单 - ' . $project->title,
  127 + 'picUrl' => 'https://hub.globalso.com/logocm.png',
  128 + 'messageUrl' => 'https://oa.quanqiusou.cn/afterorder?project_id=' . $project->uuid,
  129 + ]), [$userid], 'sampleLink');
  130 + $chat->ding = 1;
  131 + echo now() . " | INFO | 工单ID: {$chat->ticket_id} 通知成功\n";
  132 + }else
  133 + {
  134 + $chat->ding = 2;
  135 + echo now() . " | ERROR | 工单ID: {$chat->ticket_id} 通知失败\n";
  136 + }
  137 + $chat->save();
  138 + }catch (\Exception $exception) {
  139 + echo now() . " | ERROR | chat ID {$chat->id} {$exception->getMessage()} {$exception->getTraceAsString()} \n";
  140 + $chat->ding = 2;
  141 + $chat->save();
75 } 142 }
76 } 143 }
77 } 144 }
@@ -32,6 +32,12 @@ if (!function_exists('generateRoute')) { @@ -32,6 +32,12 @@ 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 + }
35 return $sign; 41 return $sign;
36 } 42 }
37 } 43 }
@@ -659,7 +665,7 @@ if (!function_exists('getImageUrl')) { @@ -659,7 +665,7 @@ if (!function_exists('getImageUrl')) {
659 * @method :post 665 * @method :post
660 * @time :2023/7/20 16:46 666 * @time :2023/7/20 16:46
661 */ 667 */
662 - function getImageUrl($path,$storage_type = 0,$location = 0){ 668 + function getImageUrl($path,$storage_type = 0,$location = 0,$image_cdn = 1){
663 if(is_array($path)){ 669 if(is_array($path)){
664 $url =[]; 670 $url =[];
665 foreach ($path as $v){ 671 foreach ($path as $v){
@@ -677,8 +683,11 @@ if (!function_exists('getImageUrl')) { @@ -677,8 +683,11 @@ if (!function_exists('getImageUrl')) {
677 } 683 }
678 if($location == 0){ 684 if($location == 0){
679 $cos = config('filesystems.disks.cos'); 685 $cos = config('filesystems.disks.cos');
  686 + if($image_cdn == 0){//v6链接
  687 + $cosCdn = $cos['cdn2'];
  688 + }else{
680 $cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1']; 689 $cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1'];
681 -// $cosCdn = 'https://file.globalso.com';//TODO::暂时使用 690 + }
682 $url = $cosCdn.$path; 691 $url = $cosCdn.$path;
683 }else{ 692 }else{
684 $s3 = config('filesystems.disks.s3'); 693 $s3 = config('filesystems.disks.s3');
@@ -17,10 +17,10 @@ class TicketChatController extends Controller @@ -17,10 +17,10 @@ class TicketChatController extends Controller
17 */ 17 */
18 public function index($project_id, $ticket_id) 18 public function index($project_id, $ticket_id)
19 { 19 {
20 - $ticket = Tickets::find($ticket_id);;  
21 - if (!$ticket) return response('工单未找到', 404);  
22 - if ($ticket->project->uuid !== $project_id) return response('无权限查看该工单', 403);  
23 - if ($ticket->project->is_del) return response('项目状态异常', 400); 20 + $ticket = Tickets::find($ticket_id);
  21 + if (!$ticket) return response()->json(['message' => '工单未找到'], 404);
  22 + if ($ticket->project->uuid !== $project_id) return response()->json(['message' => '无权限查看该工单'], 403);
  23 + if ($ticket->project->is_del) return response()->json(['message' => '项目状态异常'], 400);
24 24
25 $chats = TicketChat::where('ticket_id', $ticket->id) 25 $chats = TicketChat::where('ticket_id', $ticket->id)
26 ->get(); 26 ->get();
@@ -47,9 +47,11 @@ class TicketChatController extends Controller @@ -47,9 +47,11 @@ class TicketChatController extends Controller
47 { 47 {
48 $request->validated(); 48 $request->validated();
49 $ticket = Tickets::with(['project'])->find($ticket_id); 49 $ticket = Tickets::with(['project'])->find($ticket_id);
50 - if (!$ticket) return response('工单未找到', 404);  
51 - if ($ticket->project->uuid !== $project_id) return response('无权限查看该工单', 403);  
52 - if ($ticket->project->is_del) return response('项目状态异常', 400); 50 + if (!$ticket) return response()->json(['message' => '工单未找到'], 404);
  51 + if ($ticket->status >= Tickets::STATUS_COMPLETED) return response()->json(['message' => '工单已完成或已关闭'], 400);
  52 +
  53 + if ($ticket->project->uuid !== $project_id) return response()->json(['message' => '无权限查看该工单'], 403);
  54 + if ($ticket->project->is_del) return response()->json(['message' => '项目状态异常'], 400);
53 55
54 $chat = new TicketChat(); 56 $chat = new TicketChat();
55 $chat->ticket_id = $ticket->id; 57 $chat->ticket_id = $ticket->id;
@@ -8,8 +8,10 @@ use App\Http\Requests\Api\WorkOrder\TicketStoreRequest; @@ -8,8 +8,10 @@ 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;
11 use Illuminate\Http\Request; 12 use Illuminate\Http\Request;
12 use Illuminate\Support\Facades\DB; 13 use Illuminate\Support\Facades\DB;
  14 +use Illuminate\Support\Facades\Http;
13 15
14 class TicketController extends BaseController 16 class TicketController extends BaseController
15 { 17 {
@@ -86,6 +88,7 @@ class TicketController extends BaseController @@ -86,6 +88,7 @@ class TicketController extends BaseController
86 $log = new TicketLog(); 88 $log = new TicketLog();
87 $log->engineer_id = $project->engineer_id; // 默认第一负责人 89 $log->engineer_id = $project->engineer_id; // 默认第一负责人
88 $ticket->logs()->save($log); 90 $ticket->logs()->save($log);
  91 + $project->pushWechatGroupMsg("客户新增了工单(ID:{$ticket->id}),请及时处理!");
89 return $ticket; 92 return $ticket;
90 }); 93 });
91 return response()->json(['data' => $result]); 94 return response()->json(['data' => $result]);
@@ -149,4 +152,36 @@ class TicketController extends BaseController @@ -149,4 +152,36 @@ class TicketController extends BaseController
149 if (!$project) return $this->response('未找到项目', 404); 152 if (!$project) return $this->response('未找到项目', 404);
150 return response()->json(['data' => $project]); 153 return response()->json(['data' => $project]);
151 } 154 }
  155 +
  156 + /**
  157 + * @param $friend_id
  158 + * @return void
  159 + * 企微群里@小超或艾丝,触发推送工单
  160 + * 接收群ID
  161 + */
  162 + public function pushTicketByBot($friend_id)
  163 + {
  164 + $project = TicketProject::where('wechat_group_id', $friend_id)->where('is_del', 0)->first();
  165 + if (!$project)
  166 + return response()->json(['message' => '未找到对应的工单项目'], 404);
  167 +
  168 +// $url = in_array($project->project_cate, [3,4]) ? 'https://hub.ai.cc/api/fob_ai_customer_service/push_message' : 'https://hub.ai.cc/api/globalso_ai_customer_service/send_msg';
  169 +// $response = Http::post($url, [
  170 +// 'type' => 'Link',
  171 +// 'friend_id' => $friend_id,
  172 +// 'content' => json_encode([
  173 +// 'title' => 'AI协同工单 - ' . $project->company_name,
  174 +// 'desc' => "您好,我们同事没有及时回复,你可以查看工单进度!",
  175 +// 'size' => 0,
  176 +// 'thumbSize' => 0,
  177 +// 'thumbUrl' => 'https://hub.globalso.com/logocm.png',
  178 +// 'url' => 'https://oa.quanqiusou.cn/afterorder?project_id='.$project->uuid
  179 +// ], JSON_UNESCAPED_UNICODE)
  180 +// ]);
  181 +// // 返回 $response 的相应内容以及网络状态码
  182 +// return response($response->body(), $response->status());
  183 +
  184 + $project->pushWechatGroupMsg("您好,我们同事没有及时回复,你可以查看工单进度!");
  185 + return response()->json(['message' => '工单推送成功']);
  186 + }
152 } 187 }
@@ -90,7 +90,7 @@ class GeoQuestionController extends BaseController @@ -90,7 +90,7 @@ class GeoQuestionController extends BaseController
90 * @param : keywords->提交的关键字 90 * @param : keywords->提交的关键字
91 * @param : status->状态(0:可执行 1:禁止执行) 91 * @param : status->状态(0:可执行 1:禁止执行)
92 * @param : project_id->项目id 92 * @param : project_id->项目id
93 - * @param : type->类型(1:品牌 2:营销 93 + * @param : type->类型()
94 */ 94 */
95 public function saveGeoQuestion(){ 95 public function saveGeoQuestion(){
96 $this->request->validate([ 96 $this->request->validate([
@@ -13,6 +13,7 @@ use App\Enums\Common\Code; @@ -13,6 +13,7 @@ use App\Enums\Common\Code;
13 use App\Http\Controllers\Aside\BaseController; 13 use App\Http\Controllers\Aside\BaseController;
14 use App\Models\Domain\DomainInfo; 14 use App\Models\Domain\DomainInfo;
15 use App\Models\Project\CountAllProject; 15 use App\Models\Project\CountAllProject;
  16 +use Illuminate\Support\Facades\DB;
16 17
17 /** 18 /**
18 * @remark :统计所有项目(4.0,5.0,6.0) 19 * @remark :统计所有项目(4.0,5.0,6.0)
@@ -46,6 +47,27 @@ class AllProjectController extends BaseController @@ -46,6 +47,27 @@ class AllProjectController extends BaseController
46 $this->map['company'] = ['like','%'.$this->map['company'].'%']; 47 $this->map['company'] = ['like','%'.$this->map['company'].'%'];
47 } 48 }
48 $data = $allProject->lists($this->map,$this->page,$this->row); 49 $data = $allProject->lists($this->map,$this->page,$this->row);
  50 +
  51 + if (!empty($data['list'])) {
  52 + foreach ($data['list'] as $key => $value) {
  53 + $ticketProject = null;
  54 + if ($value['version'] == 1) {
  55 + // version 为 1:6.0
  56 + $ticketProject = DB::table('gl_ticket_projects')
  57 + ->where('table_id', $value['project_id'])
  58 + ->where('project_cate', 2)
  59 + ->first();
  60 + } else {
  61 + // version 不为 1
  62 + $ticketProject = DB::table('gl_ticket_projects')
  63 + ->where('post_id', $value['project_id'])
  64 + ->where('project_cate', 1)
  65 + ->first();
  66 + }
  67 + $data['list'][$key]['uuid'] = $ticketProject ? $ticketProject->uuid : null;
  68 + }
  69 + }
  70 +
49 $this->response('success',Code::SUCCESS,$data); 71 $this->response('success',Code::SUCCESS,$data);
50 } 72 }
51 } 73 }
@@ -37,6 +37,7 @@ use App\Models\RankData\RankData; @@ -37,6 +37,7 @@ use App\Models\RankData\RankData;
37 use App\Models\Task\Task; 37 use App\Models\Task\Task;
38 use App\Models\WebSetting\WebLanguage; 38 use App\Models\WebSetting\WebLanguage;
39 use Illuminate\Http\Request; 39 use Illuminate\Http\Request;
  40 +use Illuminate\Support\Facades\DB;
40 41
41 /** 42 /**
42 * 项目管理 43 * 项目管理
@@ -69,6 +70,16 @@ class ProjectController extends BaseController @@ -69,6 +70,16 @@ class ProjectController extends BaseController
69 if(!empty($lists) && !empty($lists['list'])){ 70 if(!empty($lists) && !empty($lists['list'])){
70 foreach ($lists['list'] as $k => $v){ 71 foreach ($lists['list'] as $k => $v){
71 $v = $this->handleParam($v); 72 $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
  82 +
72 $lists['list'][$k] = $v; 83 $lists['list'][$k] = $v;
73 } 84 }
74 } 85 }
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2025/7/8
  6 + * Time: 14:11
  7 + */
  8 +namespace App\Http\Controllers\Aside\Project;
  9 +
  10 +use App\Enums\Common\Code;
  11 +use App\Http\Controllers\Aside\BaseController;
  12 +use App\Models\Project\Project;
  13 +use App\Models\Project\ProjectFlow;
  14 +use Illuminate\Http\Request;
  15 +
  16 +/**
  17 + * 数据统计结果
  18 + * Class StatisticsController
  19 + * @package App\Http\Controllers\Aside\Project
  20 + */
  21 +class StatisticsController extends BaseController
  22 +{
  23 +
  24 + /**
  25 + * 流量统计
  26 + * @param Request $request
  27 + * @return \Illuminate\Http\JsonResponse
  28 + */
  29 + public function flow(Request $request)
  30 + {
  31 + $project_id = intval($request->input('project_id', '0'));
  32 + $start_at = $request->input('start_at', '');
  33 + $end_at = $request->input('end_at', '');
  34 +
  35 + $result = ProjectFlow::when($project_id, function ($query) use ($project_id) {
  36 + return $query->where(['project_id' => $project_id]);
  37 + })
  38 + ->when($start_at, function ($query) use ($start_at) {
  39 + return $query->where('date', '>=', $start_at);
  40 + })
  41 + ->when($end_at, function ($query) use ($end_at) {
  42 + return $query->where('date', '<=', $end_at);
  43 + })
  44 + ->orderBy('date', 'desc')
  45 + ->orderBy('id', 'desc')
  46 + ->paginate($this->row);
  47 +
  48 + if ($result->isEmpty())
  49 + return $this->response('success',Code::SUCCESS, []);
  50 +
  51 + $result = $result->toArray();
  52 + $project_ids = array_column($result['list'], 'project_id');
  53 + $projects = Project::whereIn('id', $project_ids)->pluck('company', 'id')->toArray();
  54 + $total = 0;
  55 + foreach ($result['list'] as &$item) {
  56 + $item['company'] = $projects[$item['project_id']];
  57 + $item['use_flow'] = $this->formatBytes($item['cdn_flow']);
  58 + $item['use_flow_mb'] = round($item['cdn_flow'] / 1048576, 2);
  59 + $total += $item['cdn_flow'];
  60 + unset($item['id']);
  61 + unset($item['last_at']);
  62 + unset($item['updated_at']);
  63 + unset($item['created_at']);
  64 + }
  65 + $result['use_flow'] = $this->formatBytes($total);
  66 + return $this->response('success',Code::SUCCESS, $result);
  67 + }
  68 +
  69 + /**
  70 + * 字节单位转换
  71 + * @param $bytes
  72 + * @param int $precision
  73 + * @return string
  74 + */
  75 + public function formatBytes($bytes, $precision = 2) {
  76 + $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
  77 +
  78 + $bytes = max($bytes, 0);
  79 + $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
  80 + $pow = min($pow, count($units) - 1);
  81 +
  82 + $bytes /= pow(1024, $pow);
  83 +
  84 + return round($bytes, $precision) . ' ' . $units[$pow];
  85 + }
  86 +}
@@ -8,13 +8,13 @@ use App\Http\Requests\Aside\WorkOrder\AsideTicketStoreRequest; @@ -8,13 +8,13 @@ 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\ProjectAssociation\ProjectAssociation;  
12 use App\Models\Workchat\MessagePush; 11 use App\Models\Workchat\MessagePush;
13 use App\Models\WorkOrder\TicketLog; 12 use App\Models\WorkOrder\TicketLog;
14 use App\Models\WorkOrder\TicketProject; 13 use App\Models\WorkOrder\TicketProject;
15 use App\Models\WorkOrder\Tickets; 14 use App\Models\WorkOrder\Tickets;
16 use Illuminate\Support\Facades\DB; 15 use Illuminate\Support\Facades\DB;
17 16
  17 +
18 class AsideTicketController extends BaseController 18 class AsideTicketController extends BaseController
19 { 19 {
20 /** 20 /**
@@ -106,6 +106,11 @@ class AsideTicketController extends BaseController @@ -106,6 +106,11 @@ class AsideTicketController extends BaseController
106 $version = $validated['version']; 106 $version = $validated['version'];
107 return $query->where('version', $version); 107 return $query->where('version', $version);
108 }) 108 })
  109 + ->when(!empty($validated['project_cate']), function ($query) use ($validated) {
  110 + // 版本号筛选
  111 + $project_cate = $validated['project_cate'];
  112 + return $query->where('project_cate', $project_cate);
  113 + })
109 ->paginate($this->row, ['*'], 'page', $this->page); 114 ->paginate($this->row, ['*'], 'page', $this->page);
110 $this->response('success', Code::SUCCESS, $lists); 115 $this->response('success', Code::SUCCESS, $lists);
111 } 116 }
@@ -143,6 +148,7 @@ class AsideTicketController extends BaseController @@ -143,6 +148,7 @@ class AsideTicketController extends BaseController
143 $log->engineer_id = $engineer_id; 148 $log->engineer_id = $engineer_id;
144 $ticket->logs()->save($log); 149 $ticket->logs()->save($log);
145 } 150 }
  151 + $project->pushWechatGroupMsg("创贸({$ticket->submit_username})新增了工单(ID:{$ticket->id}),请及时处理!");
146 return $ticket; 152 return $ticket;
147 }); 153 });
148 $this->response('success', Code::SUCCESS, $result->toArray()); 154 $this->response('success', Code::SUCCESS, $result->toArray());
@@ -232,34 +238,11 @@ class AsideTicketController extends BaseController @@ -232,34 +238,11 @@ class AsideTicketController extends BaseController
232 if (empty($project)) 238 if (empty($project))
233 $this->response('工单项目不存在', Code::USER_MODEL_NOTFOUND_ERROE); 239 $this->response('工单项目不存在', Code::USER_MODEL_NOTFOUND_ERROE);
234 240
235 - if (empty($project->association)) { 241 + if (empty($project->wechat_group_id)) {
236 $this->response('该工单没有绑定的企微群', Code::USER_MODEL_NOTFOUND_ERROE); 242 $this->response('该工单没有绑定的企微群', Code::USER_MODEL_NOTFOUND_ERROE);
237 } 243 }
238 -  
239 - $ticket = Tickets::where('project_id', $project->id)  
240 - ->orderBy('id', 'desc')  
241 - ->first();  
242 -  
243 - $message_push = new MessagePush();  
244 - $message_push->project_id = $project->table_id;  
245 - $message_push->friend_id = $project->association->friend_id;  
246 - $message_push->content_type = 'Link';  
247 - $message_push->content = json_encode([  
248 - 'title' => '工单查看 - ' . $project->company_name,  
249 - 'desc' => $ticket ? $ticket->title : "工单列表",  
250 - 'size' => 0,  
251 - 'thumbSize' => 0,  
252 - 'thumbUrl' => 'https://oa.quanqiusou.cn/logo.ico',  
253 - 'url' => 'https://oa.quanqiusou.cn/afterorder?project_id='.$project->uuid  
254 - ], JSON_UNESCAPED_UNICODE);  
255 - $message_push->send_time = now();  
256 - $message_push->type = MessagePush::TYPE_TICKET;  
257 - $message_push->save();  
258 -  
259 - if (!empty($ticket)) {  
260 - $ticket->ding = 1; // 标记为已推送  
261 - $ticket->save();  
262 - } 244 + $project->pushWechatGroupMsg();
263 $this->response('success', Code::SUCCESS); 245 $this->response('success', Code::SUCCESS);
264 } 246 }
  247 +
265 } 248 }
@@ -87,6 +87,8 @@ class AsideTicketLogController extends BaseController @@ -87,6 +87,8 @@ class AsideTicketLogController extends BaseController
87 $ticket->status = Tickets::STATUS_COMPLETED; 87 $ticket->status = Tickets::STATUS_COMPLETED;
88 // 如果所有子任务都完成了,则将工单状态改为已完成 88 // 如果所有子任务都完成了,则将工单状态改为已完成
89 $ticket->end_at = now(); 89 $ticket->end_at = now();
  90 + $project = $ticket->project;
  91 + $project->pushWechatGroupMsg("工单(ID:{$ticket->id})已全部完成,请访问查看详情!");
90 } 92 }
91 $ticket->save(); 93 $ticket->save();
92 return $log; 94 return $log;
@@ -45,6 +45,7 @@ class TicketChatController extends BaseController @@ -45,6 +45,7 @@ class TicketChatController extends BaseController
45 $validated = $request->validated(); 45 $validated = $request->validated();
46 $ticket = Tickets::find($ticket_id); 46 $ticket = Tickets::find($ticket_id);
47 if (!$ticket) return response('工单未找到', 404); 47 if (!$ticket) return response('工单未找到', 404);
  48 + if ($ticket->status >= Tickets::STATUS_COMPLETED) return response('工单已完成或已关闭', 400);
48 if ($ticket->project->is_del) return response('项目状态异常', 400); 49 if ($ticket->project->is_del) return response('项目状态异常', 400);
49 50
50 $chat = new TicketChat(); 51 $chat = new TicketChat();
@@ -62,6 +63,8 @@ class TicketChatController extends BaseController @@ -62,6 +63,8 @@ class TicketChatController extends BaseController
62 $chat->submit_side = 1; 63 $chat->submit_side = 1;
63 $chat->manage_id = $this->manage['id']; 64 $chat->manage_id = $this->manage['id'];
64 $chat->save(); 65 $chat->save();
  66 + $project = $ticket->project;
  67 + $project->pushWechatGroupMsg("{$chat->submit_username}对工单(ID:{$ticket->id})进行了补充,请及时查看处理!");
65 $this->response('success', Code::SUCCESS, $chat); 68 $this->response('success', Code::SUCCESS, $chat);
66 } 69 }
67 70
@@ -70,6 +70,9 @@ class ComController extends BaseController @@ -70,6 +70,9 @@ class ComController extends BaseController
70 if($this->user['login_source'] == User::LOGIN_PASSWORD_SOURCE){ 70 if($this->user['login_source'] == User::LOGIN_PASSWORD_SOURCE){
71 $data[] = 19; 71 $data[] = 19;
72 } 72 }
  73 + if($this->user['login_source'] != User::LOGIN_AUTO_SOURCE){
  74 + $data[] = 76;
  75 + }
73 if(!empty($data)){ 76 if(!empty($data)){
74 $this->map['id'] = ['not in',$data]; 77 $this->map['id'] = ['not in',$data];
75 } 78 }
@@ -95,7 +95,7 @@ class OperationHeartbeatController extends BaseController @@ -95,7 +95,7 @@ class OperationHeartbeatController extends BaseController
95 }else{ 95 }else{
96 $date_time = strtotime($info['updated_at']) + 60; 96 $date_time = strtotime($info['updated_at']) + 60;
97 if($date_time < time()){ 97 if($date_time < time()){
98 - $operationHeartbeatModel->edit(['status'=>0,'ip'=>'127.0.0.1'],$condition); 98 + $operationHeartbeatModel->edit(['status'=>0,'ip'=>'127.0.0.1'],['id'=>$info['id']]);
99 $info['status'] = 0; 99 $info['status'] = 0;
100 } 100 }
101 if($info['status'] == 1){ 101 if($info['status'] == 1){
@@ -12,6 +12,8 @@ namespace App\Http\Controllers\Bside\Geo; @@ -12,6 +12,8 @@ namespace App\Http\Controllers\Bside\Geo;
12 use App\Enums\Common\Code; 12 use App\Enums\Common\Code;
13 use App\Http\Controllers\Bside\BaseController; 13 use App\Http\Controllers\Bside\BaseController;
14 use App\Http\Logic\Bside\Geo\GeoQuestionResLogic; 14 use App\Http\Logic\Bside\Geo\GeoQuestionResLogic;
  15 +use App\Models\Geo\GeoPlatform;
  16 +use App\Models\Geo\GeoQuestion;
15 use Illuminate\Http\Request; 17 use Illuminate\Http\Request;
16 18
17 /** 19 /**
@@ -28,6 +30,32 @@ class GeoQuestionResController extends BaseController @@ -28,6 +30,32 @@ class GeoQuestionResController extends BaseController
28 parent::__construct($request); 30 parent::__construct($request);
29 $this->logic = new GeoQuestionResLogic(); 31 $this->logic = new GeoQuestionResLogic();
30 } 32 }
  33 + /**
  34 + * @remark :获取类型
  35 + * @name :getType
  36 + * @author :lyh
  37 + * @method :post
  38 + * @time :2025/7/3 10:47
  39 + */
  40 + public function getType(){
  41 + $questionModel = new GeoQuestion();
  42 + $data['type'] = $questionModel->brandType();
  43 + $geoPlatformModel = new GeoPlatform();
  44 + $data['platform'] = $geoPlatformModel->getList();
  45 + return $this->response('success',Code::SUCCESS,$data);
  46 + }
  47 +
  48 + /**
  49 + * @remark :获取统计数据
  50 + * @name :getCount
  51 + * @author :lyh
  52 + * @method :post
  53 + * @time :2025/7/8 17:18
  54 + */
  55 + public function getCount(){
  56 + $data = $this->logic->getCount();
  57 + $this->response('success',Code::SUCCESS,$data);
  58 + }
31 59
32 /** 60 /**
33 * @remark :获取列表数据 61 * @remark :获取列表数据
@@ -44,7 +72,7 @@ class GeoQuestionResController extends BaseController @@ -44,7 +72,7 @@ class GeoQuestionResController extends BaseController
44 'project_id.required' => 'project_id不能为空', 72 'project_id.required' => 'project_id不能为空',
45 'type.required' => '品牌类型不能为空' 73 'type.required' => '品牌类型不能为空'
46 ]); 74 ]);
47 - $data = $this->logic->getResultList($this->map,$this->page,$this->row,$this->order); 75 + $data = $this->logic->getResultList($this->map,$this->page,$this->row);
48 $this->response('success',Code::SUCCESS,$data); 76 $this->response('success',Code::SUCCESS,$data);
49 } 77 }
50 78
@@ -27,7 +27,7 @@ class AiCommandLogic extends BaseLogic @@ -27,7 +27,7 @@ class AiCommandLogic extends BaseLogic
27 */ 27 */
28 public function getPrompt($is_batch = 0){ 28 public function getPrompt($is_batch = 0){
29 //是否有项目指令 29 //是否有项目指令
30 - $ai_command = $this->model->where('key', $this->param['key'])->where('project_id', $this->project['id'])->first(); 30 + $ai_command = $this->model->where('key', $this->param['key'])->where('project_id', $this->user['project_id'] ?? $this->project['id'])->first();
31 if(!$ai_command){ 31 if(!$ai_command){
32 $ai_command = $this->model->where('key', $this->param['key'])->where('project_id', 0)->first(); 32 $ai_command = $this->model->where('key', $this->param['key'])->where('project_id', 0)->first();
33 } 33 }
@@ -154,7 +154,7 @@ class CustomModuleContentLogic extends BaseLogic @@ -154,7 +154,7 @@ class CustomModuleContentLogic extends BaseLogic
154 if($info !== false){ 154 if($info !== false){
155 $this->fail('当前数据名称已存在'); 155 $this->fail('当前数据名称已存在');
156 } 156 }
157 - try { 157 +// try {
158 $this->param['sort'] = $this->setNewsSort(); 158 $this->param['sort'] = $this->setNewsSort();
159 $id = $this->model->addReturnId($this->param); 159 $id = $this->model->addReturnId($this->param);
160 $route = RouteMap::setRoute($this->param['route'], RouteMap::SOURCE_MODULE, 160 $route = RouteMap::setRoute($this->param['route'], RouteMap::SOURCE_MODULE,
@@ -162,9 +162,9 @@ class CustomModuleContentLogic extends BaseLogic @@ -162,9 +162,9 @@ class CustomModuleContentLogic extends BaseLogic
162 $this->addUpdateNotify(RouteMap::SOURCE_MODULE,$route); 162 $this->addUpdateNotify(RouteMap::SOURCE_MODULE,$route);
163 $this->curlDelRoute(['new_route'=>$route]); 163 $this->curlDelRoute(['new_route'=>$route]);
164 $this->edit(['route' => $route], ['id' => $id]); 164 $this->edit(['route' => $route], ['id' => $id]);
165 - }catch (\Exception $e){  
166 - $this->fail('系统错误,请联系管理员');  
167 - } 165 +// }catch (\Exception $e){
  166 +// $this->fail('系统错误,请联系管理员');
  167 +// }
168 return $id; 168 return $id;
169 } 169 }
170 170
@@ -248,8 +248,10 @@ class CustomModuleContentLogic extends BaseLogic @@ -248,8 +248,10 @@ class CustomModuleContentLogic extends BaseLogic
248 { 248 {
249 //生成一条删除路由记录 249 //生成一条删除路由记录
250 $info = $this->model->read(['id' => $id], ['id', 'route']); 250 $info = $this->model->read(['id' => $id], ['id', 'route']);
  251 + if($info !== false){
251 $this->addUpdateNotify(RouteMap::SOURCE_MODULE,$route); 252 $this->addUpdateNotify(RouteMap::SOURCE_MODULE,$route);
252 $this->curlDelRoute(['old_route'=>$info['route'],'new_route'=>$route]); 253 $this->curlDelRoute(['old_route'=>$info['route'],'new_route'=>$route]);
  254 + }
253 return true; 255 return true;
254 } 256 }
255 257
@@ -22,15 +22,34 @@ class GeoQuestionResLogic extends BaseLogic @@ -22,15 +22,34 @@ class GeoQuestionResLogic extends BaseLogic
22 } 22 }
23 23
24 /** 24 /**
  25 + * @remark :获取类型统计数据
  26 + * @name :getCount
  27 + * @author :lyh
  28 + * @method :post
  29 + * @time :2025/7/8 17:16
  30 + */
  31 + public function getCount(){
  32 + $total = $this->model->counts(['project_id'=>$this->user['project_id']]);
  33 + $type_1 = $this->model->counts(['type'=>$this->model::BRAND_TYPE,'project_id'=>$this->user['project_id']]);
  34 + $type_2 = $this->model->counts(['type'=>$this->model::MARKETING_TYPE,'project_id'=>$this->user['project_id']]);
  35 + return $this->success(['total'=>$total,'type_1'=>$type_1,'type_2'=>$type_2]);
  36 + }
  37 +
  38 + /**
25 * @remark :获取列表页数据 39 * @remark :获取列表页数据
26 * @name :getResultList 40 * @name :getResultList
27 * @author :lyh 41 * @author :lyh
28 * @method :post 42 * @method :post
29 * @time :2025/7/4 9:48 43 * @time :2025/7/4 9:48
30 */ 44 */
31 - public function getResultList($map = [],$page = 1,$row = 20,$order = 'id'){  
32 - $filed = ['id','project_id','question_id','platform','question','keywords','url'];  
33 - $data = $this->model->lists($map,$page,$row,$order,$filed); 45 + public function getResultList($map = [],$page = 1,$row = 20){
  46 + $filed = ['id','project_id','question_id','platform','question','keywords','url','created_at','updated_at'];
  47 + $query = $this->model->formatQuery($map);
  48 + $query = $query->where(function ($q) {
  49 + $q->whereRaw('JSON_LENGTH(keywords) > 0')
  50 + ->orWhereRaw('JSON_LENGTH(url) > 0');
  51 + });
  52 + $data = $query->orderByRaw('CHAR_LENGTH(question) ASC')->paginate($row, $filed, 'page', $page);;
34 return $this->success($data); 53 return $this->success($data);
35 } 54 }
36 55
@@ -25,7 +25,8 @@ class TicketProjectListRequest extends FormRequest @@ -25,7 +25,8 @@ class TicketProjectListRequest extends FormRequest
25 { 25 {
26 return [ 26 return [
27 'search' => 'nullable|string', // 搜索关键词 27 'search' => 'nullable|string', // 搜索关键词
28 - 'version' => 'nullable|in:5,6,7', // 版本号 28 + 'project_cate' => 'nullable|in:1,2,3,4', // 项目分类:1V5,2V6,3超迹,4域途
  29 + 'version' => 'nullable|integer', // 版本号
29 ]; 30 ];
30 } 31 }
31 } 32 }
@@ -25,6 +25,12 @@ class GeoQuestion extends Base @@ -25,6 +25,12 @@ class GeoQuestion extends Base
25 25
26 public $frequency = [1,2,3,4,5,6,7,8,9,10];//类型 26 public $frequency = [1,2,3,4,5,6,7,8,9,10];//类型
27 27
  28 + const TYPE_BRAND = 1;
  29 + const TYPE_MARKET = 2;
  30 +
  31 + const STATUS_CLOSE = 0;
  32 + const STATUS_OPEN = 1;
  33 +
28 /** 34 /**
29 * @remark :geo提交网址获取器 35 * @remark :geo提交网址获取器
30 * @name :getUrlAttribute 36 * @name :getUrlAttribute
@@ -71,16 +77,13 @@ class GeoQuestion extends Base @@ -71,16 +77,13 @@ class GeoQuestion extends Base
71 } 77 }
72 78
73 /** 79 /**
74 - * @remark :品牌类型  
75 - * @name :brandType  
76 - * @author :lyh  
77 - * @method :post  
78 - * @time :2025/7/3 9:43 80 + * 品牌类型
  81 + * @return array
79 */ 82 */
80 public function brandType(){ 83 public function brandType(){
81 return [ 84 return [
82 - 1=>'品牌数据',  
83 - 2=>'营销数据' 85 + self::TYPE_BRAND => '品牌数据',
  86 + self::TYPE_MARKET => '营销数据'
84 ]; 87 ];
85 } 88 }
86 } 89 }
@@ -22,6 +22,9 @@ use App\Models\Base; @@ -22,6 +22,9 @@ use App\Models\Base;
22 class GeoQuestionResult extends Base 22 class GeoQuestionResult extends Base
23 { 23 {
24 protected $table = 'gl_geo_question_result'; 24 protected $table = 'gl_geo_question_result';
  25 +
  26 + const BRAND_TYPE = 1;//品牌类型
  27 + const MARKETING_TYPE = 2;//营销类型
25 /** 28 /**
26 * @remark :geo提交关键字获取器 29 * @remark :geo提交关键字获取器
27 * @name :getUrlAttribute 30 * @name :getUrlAttribute
@@ -52,4 +55,18 @@ class GeoQuestionResult extends Base @@ -52,4 +55,18 @@ class GeoQuestionResult extends Base
52 return $value; 55 return $value;
53 } 56 }
54 57
  58 + /**
  59 + * @remark :geo提交结果获取器
  60 + * @name :getUrlAttribute
  61 + * @author :lyh
  62 + * @method :post
  63 + * @time :2025/7/3 9:52
  64 + */
  65 + public function getTextAttribute($value)
  66 + {
  67 + if($value){
  68 + $value = Arr::s2a($value);
  69 + }
  70 + return $value;
  71 + }
55 } 72 }
@@ -14,4 +14,5 @@ use App\Models\Base; @@ -14,4 +14,5 @@ use App\Models\Base;
14 class CountAllProject extends Base 14 class CountAllProject extends Base
15 { 15 {
16 protected $table = 'gl_all_project'; 16 protected $table = 'gl_all_project';
  17 +
17 } 18 }
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2025/7/4
  6 + * Time: 15:05
  7 + */
  8 +namespace App\Models\Project;
  9 +
  10 +use App\Models\Base;
  11 +
  12 +/**
  13 + * 流量统计
  14 + * Class ProjectFlow
  15 + * @package App\Models\Project
  16 + */
  17 +class ProjectFlow extends Base
  18 +{
  19 + /**
  20 + * 表名
  21 + * @var string
  22 + */
  23 + protected $table = 'gl_project_flow';
  24 +
  25 + /**
  26 + * 流量日志记录
  27 + * @param $project_id
  28 + * @param $date
  29 + * @param $cdn_flow
  30 + * @param $last_at
  31 + * @return ProjectFlow
  32 + */
  33 + public static function flowInsert($project_id, $date, $cdn_flow, $last_at)
  34 + {
  35 + $flow = self::where(['project_id' => $project_id, 'date' => $date])->first();
  36 + if (empty($flow)) {
  37 + $flow = new self();
  38 + $flow->project_id = $project_id;
  39 + $flow->date = $date;
  40 + $flow->cdn_flow = $cdn_flow;
  41 + } else {
  42 + $flow->cdn_flow = $flow->cdn_flow + $cdn_flow;
  43 + }
  44 + $flow->last_at = $last_at;
  45 + $flow->save();
  46 + return $flow;
  47 + }
  48 +}
@@ -6,6 +6,7 @@ use App\Models\Base; @@ -6,6 +6,7 @@ use App\Models\Base;
6 use App\Models\Manage\Manage; 6 use App\Models\Manage\Manage;
7 use App\Models\Project\Project; 7 use App\Models\Project\Project;
8 use App\Models\ProjectAssociation\ProjectAssociation; 8 use App\Models\ProjectAssociation\ProjectAssociation;
  9 +use App\Models\Workchat\MessagePush;
9 use Illuminate\Database\Eloquent\Factories\HasFactory; 10 use Illuminate\Database\Eloquent\Factories\HasFactory;
10 11
11 class TicketProject extends Base 12 class TicketProject extends Base
@@ -53,4 +54,31 @@ class TicketProject extends Base @@ -53,4 +54,31 @@ class TicketProject extends Base
53 ->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT) 54 ->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)
54 ->select(['id', 'project_id', 'friend_id', 'binding_app']); 55 ->select(['id', 'project_id', 'friend_id', 'binding_app']);
55 } 56 }
  57 +
  58 +
  59 + /**
  60 + * @return void
  61 + * 企微群推送工单消息
  62 + */
  63 + public function pushWechatGroupMsg($desc="可提交新的工单、查询工单进度、AI会同步通知售后人员!")
  64 + {
  65 + if (!empty($this->wechat_group_id))
  66 + {
  67 + $message_push = new MessagePush();
  68 + $message_push->project_id = $this->table_id;
  69 + $message_push->friend_id = $this->wechat_group_id;
  70 + $message_push->content_type = 'Link';
  71 + $message_push->content = json_encode([
  72 + 'title' => "AI协同工单 - " . $this->company_name,
  73 + 'desc' => $desc,
  74 + 'size' => 0,
  75 + 'thumbSize' => 0,
  76 + 'thumbUrl' => 'https://hub.globalso.com/logocm.png',
  77 + 'url' => 'https://oa.quanqiusou.cn/afterorder?project_id='.$this->uuid
  78 + ], JSON_UNESCAPED_UNICODE);
  79 + $message_push->send_time = now();
  80 + $message_push->type = MessagePush::TYPE_TICKET;
  81 + $message_push->save();
  82 + }
  83 + }
56 } 84 }
@@ -66,7 +66,7 @@ class MessagePush extends Base @@ -66,7 +66,7 @@ class MessagePush extends Base
66 //9-21 点,每条消息及时通知 66 //9-21 点,每条消息及时通知
67 //21-第二天 9 点,整合一起通知 67 //21-第二天 9 点,整合一起通知
68 $hour = date('H', strtotime($submit_at)); 68 $hour = date('H', strtotime($submit_at));
69 - if(($hour >= 9 && $hour < 21) || in_array($project_id, $special_project_ids)) { 69 + if(($hour >= 8 && $hour < 21) || in_array($project_id, $special_project_ids)) {
70 $model = new self(); 70 $model = new self();
71 $model->project_id = $project_id; 71 $model->project_id = $project_id;
72 $model->friend_id = $friend_id; 72 $model->friend_id = $friend_id;
@@ -76,7 +76,7 @@ class MessagePush extends Base @@ -76,7 +76,7 @@ class MessagePush extends Base
76 $model->send_time = $submit_at; 76 $model->send_time = $submit_at;
77 }else{ 77 }else{
78 //定时发送时间 78 //定时发送时间
79 - $send_time = $hour >= 9 ? date('Y-m-d 09:00:00', strtotime($submit_at . '+1 day')) : date('Y-m-d 09:00:00', strtotime($submit_at)); 79 + $send_time = $hour >= 8 ? date('Y-m-d 08:00:00', strtotime($submit_at . '+1 day')) : date('Y-m-d 08:00:00', strtotime($submit_at));
80 $model = self::where('project_id', $project_id)->where('type', self::TYPE_INQUIRY)->where('send_time', $send_time)->first(); 80 $model = self::where('project_id', $project_id)->where('type', self::TYPE_INQUIRY)->where('send_time', $send_time)->first();
81 if(!$model){ 81 if(!$model){
82 $model = new self(); 82 $model = new self();
@@ -86,7 +86,7 @@ class MessagePush extends Base @@ -86,7 +86,7 @@ class MessagePush extends Base
86 $model->ref_ids = $id; 86 $model->ref_ids = $id;
87 $model->countries = $country; 87 $model->countries = $country;
88 $model->send_time = $send_time; 88 $model->send_time = $send_time;
89 - $model->content = '[09:00] 您的全球搜网站收到来自【' . $country . $name . '】的询盘信息,请登录后台或APP进行查看!'; 89 + $model->content = '[08:00] 您的全球搜网站收到来自【' . $country . $name . '】的询盘信息,请登录后台或APP进行查看!';
90 }else{ 90 }else{
91 $ref_ids = explode(',', $model->ref_ids); 91 $ref_ids = explode(',', $model->ref_ids);
92 $ref_ids[] = $id; 92 $ref_ids[] = $id;
@@ -105,7 +105,7 @@ class MessagePush extends Base @@ -105,7 +105,7 @@ class MessagePush extends Base
105 $country = implode(',', $countries); 105 $country = implode(',', $countries);
106 } 106 }
107 } 107 }
108 - $model->content = '[09:00] 您的全球搜网站收到来自【' . $country . '】'.$count.'条询盘信息,请登录后台或APP进行查看!'; 108 + $model->content = '[08:00] 您的全球搜网站收到来自【' . $country . '】'.$count.'条询盘信息,请登录后台或APP进行查看!';
109 } 109 }
110 } 110 }
111 $model->save(); 111 $model->save();
@@ -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()->post($url, $param)->json(); 171 + $result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->post($url, $param)->json();
172 if(empty($result) || $result['status'] != 200){ 172 if(empty($result) || $result['status'] != 200){
173 return []; 173 return [];
174 } 174 }
@@ -189,7 +189,7 @@ class ProjectAssociationServices extends BaseService @@ -189,7 +189,7 @@ class ProjectAssociationServices extends BaseService
189 ]; 189 ];
190 $param['sign'] = $this->getSign($param); 190 $param['sign'] = $this->getSign($param);
191 $url = 'https://hub.ai.cc/api/globalso_ai_customer_service/send_msg'; 191 $url = 'https://hub.ai.cc/api/globalso_ai_customer_service/send_msg';
192 - $result = Http::withoutVerifying()->timeout(30)->post($url, $param)->json(); 192 + $result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->timeout(30)->post($url, $param)->json();
193 if(empty($result) || $result['status'] != 200){ 193 if(empty($result) || $result['status'] != 200){
194 throw new \Exception($result['message'] ?? ''); 194 throw new \Exception($result['message'] ?? '');
195 } 195 }
@@ -45,7 +45,7 @@ class DingTalkService @@ -45,7 +45,7 @@ class DingTalkService
45 } 45 }
46 46
47 /** 批量发送私聊消息 */ 47 /** 批量发送私聊消息 */
48 - public function danliao(string $text, array $user_ids) 48 + public function danliao(string $text, array $user_ids, string $msg_key='sampleMarkdown')
49 { 49 {
50 $endpoint = '/v1/danliao'; 50 $endpoint = '/v1/danliao';
51 $payload = [ 51 $payload = [
@@ -53,7 +53,8 @@ class DingTalkService @@ -53,7 +53,8 @@ class DingTalkService
53 "appSecret" => $this->appSecret, 53 "appSecret" => $this->appSecret,
54 "robotCode" => $this->robotCode, 54 "robotCode" => $this->robotCode,
55 "msg_param" => $text, 55 "msg_param" => $text,
56 - "user_ids" => $user_ids 56 + "user_ids" => $user_ids,
  57 + "msg_key" => $msg_key
57 ]; 58 ];
58 return $this->send_request('POST', $this->bashUrl . $endpoint, $payload); 59 return $this->send_request('POST', $this->bashUrl . $endpoint, $payload);
59 } 60 }
@@ -14,16 +14,16 @@ class GeoService @@ -14,16 +14,16 @@ class GeoService
14 public $api_key = '7yn!We6$&NnVA38bpGy*A@4TQ5iYLJcW'; 14 public $api_key = '7yn!We6$&NnVA38bpGy*A@4TQ5iYLJcW';
15 15
16 public $api_url = 'https://api.cmer.com/'; 16 public $api_url = 'https://api.cmer.com/';
  17 +
17 /** 18 /**
18 - * @remark :请求的方法  
19 - * @name :requestAction  
20 - * @author :lyh  
21 - * @method :post  
22 - * @time :2025/7/3 14:26 19 + * 获取AI平台数据
  20 + * @param $content
  21 + * @param $platform
  22 + * @return mixed|string
23 */ 23 */
24 - public function setWebSearchChatAction($content,$platform){  
25 - $route = 'v1/websearch_chat';  
26 - $url = $this->api_url.$route; 24 + public function getAiPlatformResult($content,$platform)
  25 + {
  26 + $url = $this->api_url . 'v1/websearch_chat';
27 $header = [ 27 $header = [
28 'accept: application/json', 28 'accept: application/json',
29 'X-CmerApi-Host: llm-chat.p.cmer.com', 29 'X-CmerApi-Host: llm-chat.p.cmer.com',
@@ -37,10 +37,35 @@ class GeoService @@ -37,10 +37,35 @@ class GeoService
37 'role'=>'user' 37 'role'=>'user'
38 ], 38 ],
39 ], 39 ],
40 - 'platform'=>$platform,  
41 - 'security_check'=>true 40 + 'platform' => $platform,
  41 + 'security_check' => true
42 ]; 42 ];
43 $data = http_post($url,json_encode($message,true),$header); 43 $data = http_post($url,json_encode($message,true),$header);
44 return $data; 44 return $data;
45 } 45 }
  46 +
  47 + /**
  48 + * 获取Google数据
  49 + * @param $search
  50 + * @param int $lum_json 默认1 不只是什么参数
  51 + * @return mixed|string
  52 + */
  53 + public function getGooglePlatformResult($search)
  54 + {
  55 + $url = 'https://api.cmer.com/ai-overviews';
  56 + $header = [
  57 + 'accept: application/json',
  58 + 'apikey: UkzZljFv83Z2qBi5YR1o3f2otAVWtug6',
  59 + 'Content-Type: application/json',
  60 + 'X-CmerApi-Host:ai-overviews.p.cmer.com'
  61 + ];
  62 + $param = [
  63 + 'q' => $search,
  64 + 'location' => 'New York, United States',
  65 + 'gl' => 'us',
  66 + 'hl'=>'en'
  67 + ];
  68 + $url = $url . '?' . http_build_query($param);
  69 + return http_get($url, $header);
  70 + }
46 } 71 }
@@ -67,6 +67,47 @@ class UpyunService @@ -67,6 +67,47 @@ class UpyunService
67 } 67 }
68 68
69 /** 69 /**
  70 + * 统计流量
  71 + * @param $start_time
  72 + * @param $end_time
  73 + * @param string $domain
  74 + * @return mixed
  75 + */
  76 + public function commonData($start_time, $end_time, $domain = 'ecdn6.globalso.com')
  77 + {
  78 + $action = '/flow/common_data';
  79 + $param = [
  80 + 'start_time' => $start_time,
  81 + 'end_time' => $end_time,
  82 + 'domain' => $domain,
  83 + ];
  84 +// dd($param);
  85 + list($status, $result) = $this->curlRequest($action, $param, 'GET', $this->getHeader());
  86 + return $result;
  87 + }
  88 +
  89 + /**
  90 + * 统计流量
  91 + * @param $start_time
  92 + * @param $end_time
  93 + * @param string $domain
  94 + * @return mixed
  95 + */
  96 + public function realTimeStatistics($start_time, $end_time, $domain = 'ecdn6.globalso.com')
  97 + {
  98 + $action = '/flow/real_time_statistics';
  99 + $param = [
  100 + 'start_time' => $start_time,
  101 + 'end_time' => $end_time,
  102 + 'query_domain' => $domain,
  103 + 'query_type' => 'uri_flux'
  104 + ];
  105 +// dd($param);
  106 + list($status, $result) = $this->curlRequest($action, $param, 'GET', $this->getHeader());
  107 + return $result;
  108 + }
  109 +
  110 + /**
70 * 头信息需要携带授权token 111 * 头信息需要携带授权token
71 * @return array 112 * @return array
72 */ 113 */
@@ -88,6 +129,9 @@ class UpyunService @@ -88,6 +129,9 @@ class UpyunService
88 public function curlRequest($url, $data, $method = 'POST', $header = [], $time_out = 60) 129 public function curlRequest($url, $data, $method = 'POST', $header = [], $time_out = 60)
89 { 130 {
90 $url = config('custom.upyun.api_url') . $url; 131 $url = config('custom.upyun.api_url') . $url;
  132 + if ($method == 'GET') {
  133 + $url .= '?' . http_build_query($data);
  134 + }
91 $ch = curl_init(); 135 $ch = curl_init();
92 curl_setopt($ch, CURLOPT_TIMEOUT, $time_out); 136 curl_setopt($ch, CURLOPT_TIMEOUT, $time_out);
93 curl_setopt($ch, CURLOPT_URL, $url); 137 curl_setopt($ch, CURLOPT_URL, $url);
@@ -86,3 +86,4 @@ Route::prefix('tickets')->group(function () { @@ -86,3 +86,4 @@ Route::prefix('tickets')->group(function () {
86 Route::get('/chat/{project_id}/{ticket_id}', [\App\Http\Controllers\Api\WorkOrder\TicketChatController::class, 'index'])->summary('B端,渠道-工单聊天记录')->name('tickets.chat.index'); 86 Route::get('/chat/{project_id}/{ticket_id}', [\App\Http\Controllers\Api\WorkOrder\TicketChatController::class, 'index'])->summary('B端,渠道-工单聊天记录')->name('tickets.chat.index');
87 Route::post('/chat/{project_id}/{ticket_id}', [\App\Http\Controllers\Api\WorkOrder\TicketChatController::class, 'store'])->summary('B端,渠道-工单聊天记录提交')->name('tickets.chat.store'); 87 Route::post('/chat/{project_id}/{ticket_id}', [\App\Http\Controllers\Api\WorkOrder\TicketChatController::class, 'store'])->summary('B端,渠道-工单聊天记录提交')->name('tickets.chat.store');
88 }); 88 });
  89 +Route::get('/pushTicketByBot/{friend_id}', [\App\Http\Controllers\Api\WorkOrder\TicketController::class, 'pushTicketByBot'])->summary('企微群@机器人触发工单推送')->name('tickets.pushTicketByBot');
@@ -257,7 +257,8 @@ Route::middleware(['aloginauth'])->group(function () { @@ -257,7 +257,8 @@ 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::get('/v56_projects/list', [Aside\WorkOrder\AsideTicketController::class, 'projectList'])->name('admin.tickets.projectList')->summary('A端V5V6项目列表'); 260 + 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 域途");
261 Route::post('/log/{id}', [Aside\WorkOrder\AsideTicketLogController::class, 'update'])->name('admin.tickets.log.update')->summary('A端工单操作日志更新,完成工单'); 262 Route::post('/log/{id}', [Aside\WorkOrder\AsideTicketLogController::class, 'update'])->name('admin.tickets.log.update')->summary('A端工单操作日志更新,完成工单');
262 Route::get('/chat/{ticket_id}', [Aside\WorkOrder\TicketChatController::class, 'index'])->name('admin.tickets.chat.index')->summary('A端工单聊天记录'); 263 Route::get('/chat/{ticket_id}', [Aside\WorkOrder\TicketChatController::class, 'index'])->name('admin.tickets.chat.index')->summary('A端工单聊天记录');
263 Route::post('/chat/{ticket_id}', [Aside\WorkOrder\TicketChatController::class, 'store'])->name('admin.tickets.chat.store')->summary('A端工单聊天记录创建'); 264 Route::post('/chat/{ticket_id}', [Aside\WorkOrder\TicketChatController::class, 'store'])->name('admin.tickets.chat.store')->summary('A端工单聊天记录创建');
@@ -608,6 +609,12 @@ Route::middleware(['aloginauth'])->group(function () { @@ -608,6 +609,12 @@ Route::middleware(['aloginauth'])->group(function () {
608 Route::any('/save', [Aside\Project\ProjectWhiteHatAffixController::class, 'save'])->name('admin.white_hat_save'); 609 Route::any('/save', [Aside\Project\ProjectWhiteHatAffixController::class, 'save'])->name('admin.white_hat_save');
609 Route::any('/del', [Aside\Project\ProjectWhiteHatAffixController::class, 'del'])->name('admin.white_hat_del'); 610 Route::any('/del', [Aside\Project\ProjectWhiteHatAffixController::class, 'del'])->name('admin.white_hat_del');
610 }); 611 });
  612 + // 统计数据
  613 + Route::prefix('statistics')->group(function () {
  614 + // 流量统计
  615 + Route::any('/flow', [Aside\Project\StatisticsController::class, 'flow'])->name('admin.statistics_flow');
  616 + });
  617 +
611 }); 618 });
612 619
613 //无需登录验证的路由组 620 //无需登录验证的路由组
@@ -753,6 +753,8 @@ Route::middleware(['bloginauth'])->group(function () { @@ -753,6 +753,8 @@ Route::middleware(['bloginauth'])->group(function () {
753 Route::prefix('geo_result')->group(function () { 753 Route::prefix('geo_result')->group(function () {
754 Route::any('/getList', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class,'getList'])->name('geo_result_getList'); 754 Route::any('/getList', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class,'getList'])->name('geo_result_getList');
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设置类型
  757 + Route::any('/getCount', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'getCount'])->name('geo_result_getCount');//geo设置类型统计数量
756 }); 758 });
757 }); 759 });
758 //无需登录验证的路由组 760 //无需登录验证的路由组