作者 刘锟

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

@@ -51,12 +51,13 @@ class GeoQuestionRes extends Command @@ -51,12 +51,13 @@ class GeoQuestionRes extends Command
51 sleep(300); 51 sleep(300);
52 continue; 52 continue;
53 } 53 }
54 - $lock_key = "geo_task_lock:$task_id"; 54 + $lock_key = "geo_task_lock_" . $task_id;
55 if (!Redis::setnx($lock_key, 1)) { 55 if (!Redis::setnx($lock_key, 1)) {
56 $this->output("任务 $task_id 已被其他进程锁定,跳过"); 56 $this->output("任务 $task_id 已被其他进程锁定,跳过");
  57 + sleep(30); // 程序挂起, 避免最后一个任务 扫数据表和Redis
57 continue; 58 continue;
58 } 59 }
59 - Redis::expire($lock_key, 1200); // 1小时自动解锁 60 + Redis::expire($lock_key, 600); // 10自动解锁
60 $this->output('执行的任务ID:' . $task_id); 61 $this->output('执行的任务ID:' . $task_id);
61 $geoQuestionModel = new GeoQuestion(); 62 $geoQuestionModel = new GeoQuestion();
62 $taskInfo = $geoQuestionModel->read(['id'=>$task_id]); 63 $taskInfo = $geoQuestionModel->read(['id'=>$task_id]);
@@ -86,6 +87,7 @@ class GeoQuestionRes extends Command @@ -86,6 +87,7 @@ class GeoQuestionRes extends Command
86 $geoResultModel = new GeoQuestionResult(); 87 $geoResultModel = new GeoQuestionResult();
87 $geoLogModel = new GeoQuestionLog(); 88 $geoLogModel = new GeoQuestionLog();
88 foreach ($taskInfo['question'] as $question) { 89 foreach ($taskInfo['question'] as $question) {
  90 + Redis::expire($lock_key, 1200); // 一个问题执行时间可能会达到15-18分钟
89 $en_question = Translate::tran($question, 'zh') ?? ''; 91 $en_question = Translate::tran($question, 'zh') ?? '';
90 $this->output('项目ID:' . $taskInfo['project_id'] . ', 问题 开始:' . $question); 92 $this->output('项目ID:' . $taskInfo['project_id'] . ', 问题 开始:' . $question);
91 foreach ($platformsArr as $platform) { 93 foreach ($platformsArr as $platform) {
@@ -345,21 +347,30 @@ class GeoQuestionRes extends Command @@ -345,21 +347,30 @@ class GeoQuestionRes extends Command
345 $key = 'geo_task_list'; 347 $key = 'geo_task_list';
346 $task_id = Redis::rpop($key); 348 $task_id = Redis::rpop($key);
347 if(empty($task_id)){ 349 if(empty($task_id)){
348 - $project_ids = GeoQuestion::where('status', GeoQuestion::STATUS_OPEN)->where('next_time', '<=', date('Y-m-d'))  
349 - ->orderBy('next_time', 'asc')->pluck('project_id')->unique()->values()->toArray();  
350 - if(!empty($project_ids)){  
351 - foreach ($project_ids as $project_id){  
352 - $ids = GeoQuestion::where(['project_id' => $project_id, 'status' => GeoQuestion::STATUS_OPEN])->where('next_time', '<=', date('Y-m-d'))->pluck('id'); 350 + $lock_key = 'geo_task_generation_lock';
  351 + $lock_ttl = 60; // 锁时间大于当前 锁功能执行时间
  352 + // 尝试获取锁,非阻塞方式
  353 + $lock = Redis::set($lock_key, 1, 'EX', $lock_ttl, 'NX');
  354 + if (empty($lock)){
  355 + return $task_id;
  356 + }
  357 + $project_ids = GeoQuestion::where('status', GeoQuestion::STATUS_OPEN)->where('next_time', '<=', date('Y-m-d'))->pluck('project_id')->unique()->values()->toArray();
  358 + if(FALSE == empty($project_ids)){
  359 + $ids = GeoQuestion::where('status', GeoQuestion::STATUS_OPEN)
  360 + ->whereIn('project_id', $project_ids)
  361 + ->where(function ($query){
  362 + $query->where('current_time', '!=', date('Y-m-d'))
  363 + ->orWhereNull('current_time');
  364 + })
  365 + ->orderBy('project_id', 'asc')
  366 + ->pluck('id');
  367 +
353 foreach ($ids as $id) { 368 foreach ($ids as $id) {
354 - //检查任务是否执行过  
355 - if (!Redis::exists("geo_task_lock:$id")) {  
356 Redis::lpush($key, $id); 369 Redis::lpush($key, $id);
357 } 370 }
358 - }  
359 $task_id = Redis::rpop($key); 371 $task_id = Redis::rpop($key);
360 } 372 }
361 } 373 }
362 - }  
363 return $task_id; 374 return $task_id;
364 } 375 }
365 376
@@ -39,6 +39,11 @@ class GeoWritingTaskController extends BaseController @@ -39,6 +39,11 @@ class GeoWritingTaskController extends BaseController
39 * @time :2025/10/25 15:12 39 * @time :2025/10/25 15:12
40 */ 40 */
41 public function lists(){ 41 public function lists(){
  42 + $this->request->validate([
  43 + 'project_id'=>'required',
  44 + ],[
  45 + 'project_id.required' => 'project_id不能为空',
  46 + ]);
42 $data = $this->logic->listWritingTask($this->map,$this->page,$this->row,$this->order); 47 $data = $this->logic->listWritingTask($this->map,$this->page,$this->row,$this->order);
43 $this->response('success',Code::SUCCESS,$data); 48 $this->response('success',Code::SUCCESS,$data);
44 } 49 }
@@ -39,6 +39,11 @@ class GeoWritingsController extends BaseController @@ -39,6 +39,11 @@ class GeoWritingsController extends BaseController
39 * @time :2025/10/25 15:53 39 * @time :2025/10/25 15:53
40 */ 40 */
41 public function lists(){ 41 public function lists(){
  42 + $this->request->validate([
  43 + 'project_id'=>'required',
  44 + ],[
  45 + 'project_id.required' => 'project_id不能为空',
  46 + ]);
42 $data = $this->logic->listWriting($this->map,$this->page,$this->row,$this->order); 47 $data = $this->logic->listWriting($this->map,$this->page,$this->row,$this->order);
43 $this->response('success',Code::SUCCESS,$data); 48 $this->response('success',Code::SUCCESS,$data);
44 } 49 }
@@ -251,7 +251,6 @@ class InquiryController extends BaseController @@ -251,7 +251,6 @@ class InquiryController extends BaseController
251 $data = $data['list'] ?? []; 251 $data = $data['list'] ?? [];
252 foreach ($data as &$item){ 252 foreach ($data as &$item){
253 //非正常登录的 253 //非正常登录的
254 -  
255 if(($this->user['login_source']??0) != 2 && ($this->user['login_source']??0) != 3){ 254 if(($this->user['login_source']??0) != 2 && ($this->user['login_source']??0) != 3){
256 if(!empty($item['email']) && (strpos($item['email'], '@') !== false)){ 255 if(!empty($item['email']) && (strpos($item['email'], '@') !== false)){
257 $item['email'] = email_desensitize($item['email']); 256 $item['email'] = email_desensitize($item['email']);
@@ -259,9 +258,7 @@ class InquiryController extends BaseController @@ -259,9 +258,7 @@ class InquiryController extends BaseController
259 //脱敏 258 //脱敏
260 !empty($item['phone']) && $item['phone'] = substr($item['phone'], 0, -4) . '****'; 259 !empty($item['phone']) && $item['phone'] = substr($item['phone'], 0, -4) . '****';
261 } 260 }
262 -  
263 $item['ip_address'] = "{$item['country']}({$item['ip']})"; 261 $item['ip_address'] = "{$item['country']}({$item['ip']})";
264 -  
265 if(!empty($this->param['form_id'])){ 262 if(!empty($this->param['form_id'])){
266 $item = array_merge($item, $item['data']); 263 $item = array_merge($item, $item['data']);
267 } 264 }
@@ -69,9 +69,11 @@ class GeoConfirmLogic extends BaseLogic @@ -69,9 +69,11 @@ class GeoConfirmLogic extends BaseLogic
69 { 69 {
70 $data = $this->model->read($this->param); 70 $data = $this->model->read($this->param);
71 if($data === false){ 71 if($data === false){
72 - $this->fail('当前数据不存在或者已被删除'); 72 + return $this->success();
73 } 73 }
  74 + if(empty($data['confirm'])){
74 $data['confirm'] = $data['content']; 75 $data['confirm'] = $data['content'];
  76 + }
75 return $this->success($data); 77 return $this->success($data);
76 } 78 }
77 79
@@ -112,6 +112,9 @@ class GeoLogic extends BaseLogic @@ -112,6 +112,9 @@ class GeoLogic extends BaseLogic
112 { 112 {
113 //获取问题数量 113 //获取问题数量
114 $geo_question_count = GeoQuestion::selectRaw('SUM(JSON_LENGTH(question)) as total_count')->where('project_id',$this->param['project_id'])->value('total_count'); 114 $geo_question_count = GeoQuestion::selectRaw('SUM(JSON_LENGTH(question)) as total_count')->where('project_id',$this->param['project_id'])->value('total_count');
  115 + if(empty($geo_question_count)){
  116 + $geo_question_count = 0;
  117 + }
115 $geo_pr_count = GeoLink::where('project_id',$this->param['project_id'])->count(); 118 $geo_pr_count = GeoLink::where('project_id',$this->param['project_id'])->count();
116 $geo_writings_count = GeoWritings::where('project_id',$this->param['project_id'])->count(); 119 $geo_writings_count = GeoWritings::where('project_id',$this->param['project_id'])->count();
117 return $this->success(['geo_writings_count'=>$geo_writings_count,'geo_pr_count'=>$geo_pr_count,'geo_question_count'=>$geo_question_count]); 120 return $this->success(['geo_writings_count'=>$geo_writings_count,'geo_pr_count'=>$geo_pr_count,'geo_question_count'=>$geo_question_count]);
@@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
8 namespace App\Models\Geo; 8 namespace App\Models\Geo;
9 9
10 use App\Models\Base; 10 use App\Models\Base;
  11 +use App\Models\Project\Project;
11 use App\Models\Workchat\MessagePush; 12 use App\Models\Workchat\MessagePush;
12 13
13 /** 14 /**
@@ -42,8 +43,23 @@ class GeoConfirm extends Base @@ -42,8 +43,23 @@ class GeoConfirm extends Base
42 public static function typeMapping() 43 public static function typeMapping()
43 { 44 {
44 return [ 45 return [
45 - self::TYPE_TITLE => '确认标题',  
46 - self::TYPE_KEYWORD => '确认关键词' 46 + self::TYPE_TITLE => '核心关键词问题已整理,请查看并确认',
  47 + self::TYPE_KEYWORD => '文章标题已整理,请查看并确认'
  48 + ];
  49 + }
  50 +
  51 + /**
  52 + * @remark :确认返回数据
  53 + * @name :typeDesc
  54 + * @author :lyh
  55 + * @method :post
  56 + * @time :2025/10/31 10:10
  57 + */
  58 + public static function typeDesc()
  59 + {
  60 + return [
  61 + self::TYPE_TITLE => '需选择确认10个文章标题,后续根据您确认的文章标题整理文章内容;如有补充展会、资质证书等资料,可一并提供。',
  62 + self::TYPE_KEYWORD => '需选择确认10个核心关键词问题,后续根据您确认的核心关键词问题整理文章标题;建议提供展会、资质证书等资料。'
47 ]; 63 ];
48 } 64 }
49 65
@@ -98,9 +114,12 @@ class GeoConfirm extends Base @@ -98,9 +114,12 @@ class GeoConfirm extends Base
98 $type = MessagePush::TYPE_GEO_CONFIRM; 114 $type = MessagePush::TYPE_GEO_CONFIRM;
99 $token = uniqid().$friend_id; 115 $token = uniqid().$friend_id;
100 $created_at = $updated_at = now(); 116 $created_at = $updated_at = now();
  117 + $projectModel = new Project();
  118 + $projectInfo = $projectModel->read(['id'=>$project_id],['company','seo_plan']);
  119 + $seo_plan = ($projectModel::seoMap()[$projectInfo['seo_plan']]) ?? '无选择';
101 $content_array = [ 120 $content_array = [
102 - 'title' => self::typeMapping()[$data->type],  
103 - 'desc' => self::typeMapping()[$data->type], 121 + 'title' => "【{$projectInfo['company']} {$seo_plan}】".self::typeMapping()[$data->type],
  122 + 'desc' => self::typeDesc()[$data->type],
104 'size' => 0, 123 'size' => 0,
105 'thumbSize' => 0, 124 'thumbSize' => 0,
106 'thumbUrl' => 'https://hub.globalso.com/logocm.png', 125 'thumbUrl' => 'https://hub.globalso.com/logocm.png',
@@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
8 namespace App\Models\Geo; 8 namespace App\Models\Geo;
9 9
10 use App\Models\Base; 10 use App\Models\Base;
  11 +use App\Models\Project\Project;
11 use App\Models\ProjectAssociation\ProjectAssociation; 12 use App\Models\ProjectAssociation\ProjectAssociation;
12 use App\Models\Workchat\MessagePush; 13 use App\Models\Workchat\MessagePush;
13 use Illuminate\Support\Facades\Crypt; 14 use Illuminate\Support\Facades\Crypt;
@@ -90,10 +91,13 @@ class GeoWritings extends Base @@ -90,10 +91,13 @@ class GeoWritings extends Base
90 'project_id' => $project_id, 91 'project_id' => $project_id,
91 'send_at' => time() 92 'send_at' => time()
92 ]; 93 ];
  94 + $projectModel = new Project();
  95 + $projectInfo = $projectModel->read(['id'=>$project_id],['company','seo_plan']);
  96 + $seo_plan = ($projectModel::seoMap()[$projectInfo['seo_plan']]) ?? '无选择';
93 $token = Crypt::encrypt($param); 97 $token = Crypt::encrypt($param);
94 $content_array = [ 98 $content_array = [
95 - 'title' => "确认核心文章",  
96 - 'desc' => '确认核心文章', 99 + 'title' => "【{$projectInfo['company']} {$seo_plan}】核心文章已整理,请查看并确认",
  100 + 'desc' => '需选择确认10篇文章,后续根据您确认的文章进行外链发布。',
97 'size' => 0, 101 'size' => 0,
98 'thumbSize' => 0, 102 'thumbSize' => 0,
99 'thumbUrl' => 'https://hub.globalso.com/logocm.png', 103 'thumbUrl' => 'https://hub.globalso.com/logocm.png',