作者 赵彬吉
@@ -340,12 +340,11 @@ class GeoQuestionRes extends Command @@ -340,12 +340,11 @@ class GeoQuestionRes extends Command
340 $task_id = Redis::rpop($key); 340 $task_id = Redis::rpop($key);
341 if(empty($task_id)){ 341 if(empty($task_id)){
342 //todo::这里需要执行统计一次,统计当前项目当前日期的统计 342 //todo::这里需要执行统计一次,统计当前项目当前日期的统计
343 -  
344 # TODO 按照项目进行获取, 一个项目当天需要将所有跑完 343 # TODO 按照项目进行获取, 一个项目当天需要将所有跑完
345 $project_id = GeoQuestion::where('status', GeoQuestion::STATUS_OPEN)->where('next_time', '<=', date('Y-m-d'))->value('project_id'); 344 $project_id = GeoQuestion::where('status', GeoQuestion::STATUS_OPEN)->where('next_time', '<=', date('Y-m-d'))->value('project_id');
346 if (!empty($project_id)){ 345 if (!empty($project_id)){
347 $this->project_id = $project_id; 346 $this->project_id = $project_id;
348 - $ids = GeoQuestion::where(['project_id' => $project_id, 'status' => GeoQuestion::STATUS_OPEN])->where('current_time', '<>', date('Y-m-d'))->pluck('id'); 347 + $ids = GeoQuestion::where(['project_id' => $project_id, 'status' => GeoQuestion::STATUS_OPEN])->where('next_time', '<=', date('Y-m-d'))->pluck('id');
349 foreach ($ids as $id) { 348 foreach ($ids as $id) {
350 Redis::lpush($key, $id); 349 Redis::lpush($key, $id);
351 } 350 }
@@ -9,10 +9,7 @@ @@ -9,10 +9,7 @@
9 9
10 namespace App\Console\Commands\LyhTest; 10 namespace App\Console\Commands\LyhTest;
11 11
12 -use App\Models\CustomModule\CustomModuleCategory;  
13 -use App\Models\CustomModule\CustomModuleContent;  
14 -use App\Models\CustomModule\CustomModuleExtentContent;  
15 -use App\Models\News\News; 12 +
16 use App\Models\Product\Category; 13 use App\Models\Product\Category;
17 use App\Models\Product\CategoryRelated; 14 use App\Models\Product\CategoryRelated;
18 use App\Models\Product\Column; 15 use App\Models\Product\Column;
@@ -57,7 +54,7 @@ class LyhImportTest extends Command @@ -57,7 +54,7 @@ class LyhImportTest extends Command
57 echo date('Y-m-d H:i:s') . 'start->1517' . PHP_EOL; 54 echo date('Y-m-d H:i:s') . 'start->1517' . PHP_EOL;
58 $url1 = 'https://ecdn6.globalso.com/upload/p/1517/file/2025-10/horse-racing-tkd-modification-1.xlsx';//改tdk 55 $url1 = 'https://ecdn6.globalso.com/upload/p/1517/file/2025-10/horse-racing-tkd-modification-1.xlsx';//改tdk
59 $url2 = 'https://v6-file.globalso.com/upload/p/1517/file/2025-10/horse-racing-url-modification.xlsx';//改url路由+tdk 56 $url2 = 'https://v6-file.globalso.com/upload/p/1517/file/2025-10/horse-racing-url-modification.xlsx';//改url路由+tdk
60 - $this->download_1517_action($url1); 57 + $this->download_1517_action($url2);
61 DB::disconnect('custom_mysql'); 58 DB::disconnect('custom_mysql');
62 echo date('Y-m-d H:i:s') . 'end' . PHP_EOL; 59 echo date('Y-m-d H:i:s') . 'end' . PHP_EOL;
63 } 60 }
@@ -71,22 +68,73 @@ class LyhImportTest extends Command @@ -71,22 +68,73 @@ class LyhImportTest extends Command
71 */ 68 */
72 public function download_1517_action($url) 69 public function download_1517_action($url)
73 { 70 {
74 - // 下载到 Laravel storage 的临时路径  
75 - $tempPath = storage_path('app/temp_url.xlsx');  
76 - file_put_contents($tempPath, file_get_contents($url));  
77 - // 载入 Excel 71 + // 临时文件路径(确保目录存在)
  72 + $tempDir = storage_path('app');
  73 + if (!is_dir($tempDir)) {
  74 + mkdir($tempDir, 0755, true);
  75 + }
  76 + $tempPath = $tempDir . '/temp_url.xlsx';
78 try { 77 try {
  78 + // 使用 cURL 下载文件,防止403问题
  79 + $ch = curl_init($url);
  80 + curl_setopt_array($ch, [
  81 + CURLOPT_RETURNTRANSFER => true,
  82 + CURLOPT_FOLLOWLOCATION => true, // 跟随重定向
  83 + CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; LaravelApp/1.0)', // 模拟浏览器
  84 + CURLOPT_TIMEOUT => 30, // 超时防止卡死
  85 + ]);
  86 + $data = curl_exec($ch);
  87 + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  88 + curl_close($ch);
  89 + // 检查下载是否成功
  90 + if ($httpCode !== 200 || !$data) {
  91 + echo "文件下载失败,HTTP状态码:{$httpCode}";
  92 + }
  93 + // 保存到临时文件
  94 + file_put_contents($tempPath, $data);
  95 + // 加载 Excel 文件
79 $spreadsheet = IOFactory::load($tempPath); 96 $spreadsheet = IOFactory::load($tempPath);
80 $sheet = $spreadsheet->getActiveSheet(); 97 $sheet = $spreadsheet->getActiveSheet();
81 $rows = $sheet->toArray(); 98 $rows = $sheet->toArray();
  99 + // 删除临时文件
  100 + if (file_exists($tempPath)) {
82 unlink($tempPath); 101 unlink($tempPath);
83 - dd($rows);  
84 - }catch (\Exception $e){  
85 - echo '文件打不开'.PHP_EOL; 102 + }
  103 + $d = [];
  104 + //todo::处理数据
  105 + $productModel = new Product();
  106 + foreach ($rows as $key => $row) {
  107 + if($key == 0){
  108 + continue;
  109 + }
  110 + $path = parse_url($row[1], PHP_URL_PATH);
  111 + $path = ltrim(parse_url($path, PHP_URL_PATH), '/');
  112 + $info = $productModel->read(['route'=>$path],['id','title','route','seo_mate']);
  113 + if($info === false){
  114 + echo '未找到数据->'.$row[0].PHP_EOL;
  115 + $d[] = $row[0];
  116 + continue;
  117 + }
  118 + $seo_mate = $info['seo_mate'];
  119 + $seo_mate['title'] = $row[2] ?? '';
  120 + $seo_mate['keyword'] = $row[3] ?? '';
  121 + //todo::重新生成路由
  122 + $route = RouteMap::setRoute($row[2],RouteMap::SOURCE_PRODUCT,$info['id'],$info['project_id']);
  123 + $productModel->edit(['route'=>$route,'old_route'=>$path,'seo_mate'=>json_encode($seo_mate,true),'title'=>$row[2]],['id'=>$info['id']]);
  124 + echo '执行的ID:'.$info['id'].PHP_EOL;
  125 + }
  126 + } catch (\Exception $e) {
  127 + // 输出更清晰的错误信息
  128 + echo '文件下载或解析失败:' . $e->getMessage() . PHP_EOL;
  129 + // 清理资源
  130 + if (file_exists($tempPath)) {
  131 + unlink($tempPath);
  132 + }
86 DB::disconnect('custom_mysql'); 133 DB::disconnect('custom_mysql');
87 } 134 }
88 } 135 }
89 136
  137 +
90 /** 138 /**
91 * @remark :3951项目导入产品 139 * @remark :3951项目导入产品
92 * @name :import3951Product 140 * @name :import3951Product
@@ -86,8 +86,8 @@ class SendProduct extends Command @@ -86,8 +86,8 @@ class SendProduct extends Command
86 $start_date = date('Y-m-d 00:00:00'); 86 $start_date = date('Y-m-d 00:00:00');
87 $end_date = date('Y-m-d 23:59:59'); 87 $end_date = date('Y-m-d 23:59:59');
88 $productModel = new Product(); 88 $productModel = new Product();
89 - $arr = $productModel->formatQuery(['send_time'=>['between',[$start_date,$end_date]],'status'=>3])->pluck('route')->toArray();  
90 - $productModel->edit(['status'=>1],['send_time'=>['between',[$start_date,$end_date]],'status'=>3]); 89 + $arr = $productModel->formatQuery(['send_time'=>['<=',$end_date],'status'=>3])->pluck('route')->toArray();
  90 + $productModel->edit(['status'=>1],['send_time'=>['<=',$end_date],'status'=>3]);
91 return $arr; 91 return $arr;
92 } 92 }
93 93
@@ -102,8 +102,8 @@ class SendProduct extends Command @@ -102,8 +102,8 @@ class SendProduct extends Command
102 $start_date = date('Y-m-d 00:00:00'); 102 $start_date = date('Y-m-d 00:00:00');
103 $end_date = date('Y-m-d 23:59:59'); 103 $end_date = date('Y-m-d 23:59:59');
104 $blogModel = new Blog(); 104 $blogModel = new Blog();
105 - $arr = $blogModel->formatQuery(['release_at'=>['between',[$start_date,$end_date]],'status'=>3])->pluck('url')->toArray();  
106 - $blogModel->edit(['status'=>1],['release_at'=>['between',[$start_date,$end_date]],'status'=>3]); 105 + $arr = $blogModel->formatQuery(['release_at'=>['<=',$end_date],'status'=>3])->pluck('url')->toArray();
  106 + $blogModel->edit(['status'=>1],['release_at'=>['<=',$end_date],'status'=>3]);
107 return $arr; 107 return $arr;
108 } 108 }
109 109
@@ -118,8 +118,8 @@ class SendProduct extends Command @@ -118,8 +118,8 @@ class SendProduct extends Command
118 $start_date = date('Y-m-d 00:00:00'); 118 $start_date = date('Y-m-d 00:00:00');
119 $end_date = date('Y-m-d 23:59:59'); 119 $end_date = date('Y-m-d 23:59:59');
120 $newsModel = new News(); 120 $newsModel = new News();
121 - $arr = $newsModel->formatQuery(['release_at'=>['between',[$start_date,$end_date]],'status'=>3])->pluck('url')->toArray();  
122 - $newsModel->edit(['status'=>1],['release_at'=>['between',[$start_date,$end_date]],'status'=>3]); 121 + $arr = $newsModel->formatQuery(['release_at'=>['<=',$end_date],'status'=>3])->pluck('url')->toArray();
  122 + $newsModel->edit(['status'=>1],['release_at'=>['<=',$end_date],'status'=>3]);
123 return $arr; 123 return $arr;
124 } 124 }
125 } 125 }
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2025/10/23
  6 + * Time: 17:29
  7 + */
  8 +namespace App\Http\Controllers\Api;
  9 +
  10 +use App\Models\Geo\GeoConfirm;
  11 +use App\Models\Geo\GeoWritings;
  12 +use App\Models\Project\Project;
  13 +use Illuminate\Http\Request;
  14 +use Illuminate\Support\Facades\Crypt;
  15 +
  16 +/**
  17 + * GEO相关
  18 + * Class GeoController
  19 + * @package App\Http\Controllers\Api
  20 + */
  21 +class GeoController extends BaseController
  22 +{
  23 + /**
  24 + * 获取确认文章列表
  25 + * @param Request $request
  26 + * @return false|string
  27 + */
  28 + public function getWritingsList()
  29 + {
  30 + try {
  31 + $token = trim($this->param['token']);
  32 + $param = Crypt::decrypt($token);
  33 + if ($param['send_at'] + 86400 < time()) {}
  34 + $project_id = $param['project_id'];
  35 + } catch (\Exception $e) {
  36 + return $this->error('非法请求');
  37 + }
  38 + $project = Project::select('title', 'version')->where(['project_id' => $this->param['project_id']])->first();
  39 + $list = GeoWritings::select(['title', 'status', 'uniqid', 'confirm_at'])->where(['project_id' => $project_id, 'is_del' => GeoWritings::IS_DEL_FALSE])->get();
  40 + $result = [
  41 + 'project' => $project,
  42 + 'list' => $list
  43 + ];
  44 + return $this->success($result);
  45 + }
  46 +
  47 + /**
  48 + * 获取详情
  49 + * @param Request $request
  50 + * @return false|string
  51 + */
  52 + public function getWritingsDetail(Request $request)
  53 + {
  54 + $token = trim($request->input('token'));
  55 + $detail = GeoWritings::select(['title', 'content', 'status'])->where(['uniqid' => $token])->first();
  56 + return $this->success($detail);
  57 + }
  58 +
  59 +
  60 + /**
  61 + * 确认核心文章数据
  62 + * @param Request $request
  63 + * @return false|string
  64 + */
  65 + public function confirmWritings(Request $request)
  66 + {
  67 + $request->validate([
  68 + 'token' => 'required',
  69 + 'title' => 'required|max:120',
  70 + 'content' => 'required|max:5000'
  71 + ], [
  72 + 'token.required' => '非法请求',
  73 + 'title.required' => '标题不能为空',
  74 + 'title.max' => '最大长度不能超过120字符',
  75 + 'content.required' => '内容不能为空',
  76 + 'content.max' => '内容过长保存失败',
  77 + ]);
  78 + $token = trim($request->input('token'));
  79 + $data = GeoWritings::where(['uniqid' => $token])->first();
  80 + if (empty($data)){
  81 + return $this->error('非法请求');
  82 + }
  83 +
  84 + if ($data->status != GeoWritings::STATUS_RUNNING){
  85 + return $this->error('当前文章已确认,不可再次确认');
  86 + }
  87 +
  88 +
  89 + // FIXME 验证完成,保存数据,计算内容长度,处理内容中的资源, IP 确认时间 状态
  90 + return $data;
  91 + }
  92 +
  93 + /**
  94 + * 获取确认数据
  95 + * @param Request $request
  96 + * @return false|string
  97 + */
  98 + public function getConfirm(Request $request)
  99 + {
  100 + $token = trim($request->input('token'));
  101 + $data = GeoConfirm::where(['uniqid' => $token])->first();
  102 + if (empty($data)){
  103 + return $this->error('当前授权已失效');
  104 + }
  105 + $content = explode("\n", $data->content);
  106 + $confirm = explode("\n", $data->confirm);
  107 + $type = $data->type;
  108 + $status = $data->status;
  109 + $result = compact('content', 'confirm', 'type', 'status');
  110 + return $this->success($result);
  111 + }
  112 +
  113 + /**
  114 + * 保存确认数据
  115 + * 验证当前确认数据状态, 不可重复确认
  116 + * @param Request $request
  117 + */
  118 + public function saveConfirm(Request $request)
  119 + {}
  120 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2025/10/23
  6 + * Time: 10:23
  7 + */
  8 +namespace App\Http\Controllers\Aside\Geo;
  9 +
  10 +use App\Enums\Common\Code;
  11 +use App\Http\Controllers\Aside\BaseController;
  12 +use App\Models\Geo\GeoConf;
  13 +use App\Models\Geo\GeoConfirm;
  14 +use App\Models\Manage\ManageHr;
  15 +use App\Models\Project\KeywordPrefix;
  16 +use App\Models\Project\Project;
  17 +use App\Models\ProjectAssociation\ProjectAssociation;
  18 +use Illuminate\Http\Request;
  19 +
  20 +/**
  21 + * Class GeoController
  22 + * @package App\Http\Controllers\Aside\Geo
  23 + */
  24 +class GeoController extends BaseController
  25 +{
  26 + /**
  27 + * 获取GEO相关配置
  28 + * @param Request $request
  29 + */
  30 + public function getConfig()
  31 + {
  32 + $this->request->validate([
  33 + 'project_id' => 'required',
  34 + ], [
  35 + 'project_id.required' => '项目ID不能为空',
  36 + ]);
  37 + $projectModel = new Project();
  38 + $project_geo_conf = $projectModel->read(['id' => $this->param['project_id']],['title', 'version', 'geo_status', 'geo_qualify_num']);
  39 + $geoConfModel = new GeoConf();
  40 + $geo_conf = $geoConfModel->read(['project_id' => $this->param['project_id']]);
  41 + if($geo_conf === false){//数据未初始化
  42 + $geo_conf = [
  43 + 'project_id' => $this->param['project_id'],
  44 + 'manager_id'=>0,
  45 + 'company'=>$project_geo_conf['title'],
  46 + 'brand'=>'',
  47 + 'description'=>''
  48 + ];
  49 + }
  50 + //负责人集合
  51 + $geo_manage_list = $geoConfModel->geoManage();
  52 + // geo配置管理员,已经移除管理员列表,补充管理员信息
  53 + if ($geo_conf && $geo_conf['manager_id'] && empty($geo_manage_list[$geo_conf['manager_id']])) {
  54 + $manage = ManageHr::where(['id' => $geo_conf['manager_id']])->pluck('name', 'id')->toArray();
  55 + $geo_manage_list = array_merge($geo_manage_list, $manage);
  56 + }
  57 + $result = [
  58 + 'project_geo_conf' => $project_geo_conf,
  59 + 'geo_conf' => $geo_conf,
  60 + 'geo_manage_list' => $geo_manage_list,
  61 + 'geo_keyword' => [
  62 + 'prefix' => KeywordPrefix::getKeyword($this->param['project_id'], KeywordPrefix::TYPE_GEO_PREFIX),
  63 + 'suffix' => KeywordPrefix::getKeyword($this->param['project_id'], KeywordPrefix::TYPE_GEO_SUFFIX),
  64 + ],
  65 + ];
  66 + $this->response('success', Code::SUCCESS, $result);
  67 + }
  68 +
  69 + /**
  70 + * 保存GEO配置
  71 + * TODO 单独保存GEO开启状态, 达标数量
  72 + * @param Request $request
  73 + * @throws \App\Exceptions\AsideGlobalException
  74 + */
  75 + public function saveConfig()
  76 + {
  77 + $this->request->validate([
  78 + 'project_id' => 'required',
  79 + 'manager_id' => 'nullable|integer',
  80 + 'company' => 'nullable|max:200',
  81 + 'brand' => 'nullable|max:200',
  82 + 'description' => 'nullable|max:500',
  83 + ], [
  84 + 'project_id.required' => '项目ID不能为空',
  85 + 'manager_id.integer' => '管理员参数非法',
  86 + 'company.max' => '公司名称不能超过200个字符',
  87 + 'brand.max' => '品牌名不能超过200个字符',
  88 + 'description.max' => '描述不能超过500个字符',
  89 + ]);
  90 + try {
  91 + $data = GeoConf::saveConf($this->param['project_id'], $this->param['manager_id'], $this->param['company'], $this->param['brand'], $this->param['description'], $this->param['prefix'], $this->param['suffix']);
  92 + # FIXME 保存GEO状态 达标数量
  93 + $this->response('success', Code::SUCCESS, $data);
  94 + } catch (\Exception $e) {
  95 + $this->fail('配置保存失败, error:' . $e->getMessage());
  96 + }
  97 + }
  98 +
  99 +
  100 + /**
  101 + * 保存确认数据, 并推送微信群
  102 + * @param Request $request
  103 + * @throws \App\Exceptions\AsideGlobalException
  104 + */
  105 + public function saveConfirmContent()
  106 + {
  107 + $this->request->validate([
  108 + 'project_id' => 'required',
  109 + 'type' => 'required|integer',
  110 + 'content' => 'required',
  111 + 'max_num' => 'required',
  112 + ], [
  113 + 'project_id.required' => '项目ID不能为空',
  114 + 'type.required' => '确定数据类型不能为空',
  115 + 'type.integer' => '确定数据类型不正确',
  116 + 'content.required' => '确定数据不能为空',
  117 + 'max_num.required' => '最大确认数量不能为空',
  118 + ]);
  119 + try {
  120 + $data = GeoConfirm::saveContent($this->param['project_id'], $this->param['type'], $this->param['content'], $this->param['max_num']);
  121 + $friend = ProjectAssociation::where(['project_id' => $this->param['project_id']])->first();
  122 + if (empty($friend)){
  123 + $this->fail('项目未绑定微信群, 推送消息失败!');
  124 + }
  125 + $data = GeoConfirm::sendConfirmMessage($data->id, $friend->friend_id);
  126 + $this->response('success', Code::SUCCESS, $data);
  127 + } catch (\Exception $e) {
  128 + $this->fail('操作失败, error:' . $e->getMessage());
  129 + }
  130 + }
  131 +
  132 + /**
  133 + * OA后台管理员,保存确认数据
  134 + * 客户可以进行确认, OA后台也可以进行确认,以及修改
  135 + * @param Request $request
  136 + */
  137 + public function saveConfirmData(Request $request)
  138 + {
  139 +
  140 + }
  141 +}
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoWritingTaskController.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/10/25 10:40
  8 + */
  9 +
  10 +namespace App\Http\Controllers\Aside\Geo;
  11 +
  12 +use App\Http\Controllers\Aside\BaseController;
  13 +
  14 +/**
  15 + * @remark :文章任务(收集数据)
  16 + * @name :GeoWritingTaskController
  17 + * @author :lyh
  18 + * @method :post
  19 + * @time :2025/10/25 10:40
  20 + */
  21 +class GeoWritingTaskController extends BaseController
  22 +{
  23 + /**
  24 + * @remark :文章任务列表
  25 + * @name :lists
  26 + * @author :lyh
  27 + * @method :post
  28 + * @time :2025/10/25 10:41
  29 + */
  30 + public function lists(){
  31 +
  32 + }
  33 +}
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoWritingsController.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/10/25 10:41
  8 + */
  9 +
  10 +namespace App\Http\Controllers\Aside\Geo;
  11 +
  12 +use App\Http\Controllers\Aside\BaseController;
  13 +
  14 +/**
  15 + * @remark :geo文章
  16 + * @name :GeoWritingsController
  17 + * @author :lyh
  18 + * @method :post
  19 + * @time :2025/10/25 10:41
  20 + */
  21 +class GeoWritingsController extends BaseController
  22 +{
  23 +
  24 +}
@@ -10,7 +10,6 @@ use App\Http\Logic\Aside\Project\OnlineCheckLogic; @@ -10,7 +10,6 @@ use App\Http\Logic\Aside\Project\OnlineCheckLogic;
10 use App\Http\Logic\Aside\Project\ProcessRecordsLogic; 10 use App\Http\Logic\Aside\Project\ProcessRecordsLogic;
11 use App\Http\Logic\Aside\Project\ProjectLogic; 11 use App\Http\Logic\Aside\Project\ProjectLogic;
12 use App\Http\Requests\Aside\Project\ProcessRecordsRequest; 12 use App\Http\Requests\Aside\Project\ProcessRecordsRequest;
13 -use App\Models\Ai\AiVideo;  
14 use App\Models\ASide\APublicModel; 13 use App\Models\ASide\APublicModel;
15 use App\Models\Channel\Channel; 14 use App\Models\Channel\Channel;
16 use App\Models\Channel\User; 15 use App\Models\Channel\User;
@@ -23,7 +22,10 @@ use App\Models\Devops\ServersIp; @@ -23,7 +22,10 @@ use App\Models\Devops\ServersIp;
23 use App\Models\Domain\DomainCreateTask; 22 use App\Models\Domain\DomainCreateTask;
24 use App\Models\Domain\DomainInfo; 23 use App\Models\Domain\DomainInfo;
25 use App\Models\Domain\DomainInfo as DomainInfoModel; 24 use App\Models\Domain\DomainInfo as DomainInfoModel;
  25 +use App\Models\Geo\GeoArticle;
  26 +use App\Models\Geo\GeoConf;
26 use App\Models\Geo\GeoLink; 27 use App\Models\Geo\GeoLink;
  28 +use App\Models\Geo\GeoQuestionResult;
27 use App\Models\HomeCount\Count; 29 use App\Models\HomeCount\Count;
28 use App\Models\Industry\ProjectIndustry; 30 use App\Models\Industry\ProjectIndustry;
29 use App\Models\Inquiry\InquirySet; 31 use App\Models\Inquiry\InquirySet;
@@ -31,7 +33,6 @@ use App\Models\Manage\BelongingGroup; @@ -31,7 +33,6 @@ use App\Models\Manage\BelongingGroup;
31 use App\Models\Manage\ManageHr; 33 use App\Models\Manage\ManageHr;
32 use App\Models\Project\AiVideoTask; 34 use App\Models\Project\AiVideoTask;
33 use App\Models\Project\DeployBuild; 35 use App\Models\Project\DeployBuild;
34 -use App\Models\Project\DeployOptimize;  
35 use App\Models\Project\Payment; 36 use App\Models\Project\Payment;
36 use App\Models\Project\ProcessRecords; 37 use App\Models\Project\ProcessRecords;
37 use App\Models\Project\Project; 38 use App\Models\Project\Project;
@@ -43,7 +44,6 @@ use App\Models\Task\Task; @@ -43,7 +44,6 @@ use App\Models\Task\Task;
43 use App\Models\WebSetting\WebLanguage; 44 use App\Models\WebSetting\WebLanguage;
44 use App\Models\WorkOrder\TicketProject; 45 use App\Models\WorkOrder\TicketProject;
45 use Illuminate\Http\Request; 46 use Illuminate\Http\Request;
46 -use Illuminate\Support\Facades\DB;  
47 use Illuminate\Support\Facades\Http; 47 use Illuminate\Support\Facades\Http;
48 48
49 /** 49 /**
@@ -226,7 +226,7 @@ class ProjectController extends BaseController @@ -226,7 +226,7 @@ class ProjectController extends BaseController
226 if($this->map['domain_type'] == 'domain'){ 226 if($this->map['domain_type'] == 'domain'){
227 $parsedUrl = parse_url($this->map['domain_search']); 227 $parsedUrl = parse_url($this->map['domain_search']);
228 $this->map['domain_search'] = $parsedUrl['host'] ?? $this->map['domain_search']; 228 $this->map['domain_search'] = $parsedUrl['host'] ?? $this->map['domain_search'];
229 - $ids = DomainInfo::where('domain', 'like', '%'.$this->map['domain_search'].'%')->pluck('id')->toArray(); 229 + $ids = DomainInfoModel::where('domain', 'like', '%'.$this->map['domain_search'].'%')->pluck('id')->toArray();
230 $query->whereIn('gl_project_deploy_optimize.domain', $ids); 230 $query->whereIn('gl_project_deploy_optimize.domain', $ids);
231 }else{ 231 }else{
232 $query->where('gl_project_deploy_build.test_domain','like','%'.$this->map['domain_search'].'%'); 232 $query->where('gl_project_deploy_build.test_domain','like','%'.$this->map['domain_search'].'%');
@@ -320,8 +320,12 @@ class ProjectController extends BaseController @@ -320,8 +320,12 @@ class ProjectController extends BaseController
320 }); 320 });
321 } 321 }
322 } 322 }
323 - if(isset($this->map['seo_plan'])){  
324 - $query = $query->whereNotIn('gl_project_deploy_build.seo_plan',[0,9])->where('gl_project_deploy_build.plan',0); 323 + if (isset($this->map['seo_plan'])) {
  324 + $query = $query->where('gl_project_deploy_build.seo_plan', '!=', 0)
  325 + ->where(function ($subQuery) {
  326 + $subQuery->where('gl_project_deploy_build.plan', '=', 0)
  327 + ->orWhere('gl_project_deploy_build.seo_plan', '!=', 9);
  328 + });
325 } 329 }
326 if(isset($this->map['site_status'])){ 330 if(isset($this->map['site_status'])){
327 $query = $query->where('gl_project.site_status',$this->map['site_status']); 331 $query = $query->where('gl_project.site_status',$this->map['site_status']);
@@ -445,7 +449,19 @@ class ProjectController extends BaseController @@ -445,7 +449,19 @@ class ProjectController extends BaseController
445 $item['type'] = $item['extend_type']; 449 $item['type'] = $item['extend_type'];
446 } 450 }
447 $manageModel = new ManageHr(); 451 $manageModel = new ManageHr();
448 - $item['channel'] = Channel::getChannelText($item['channel']['user_id'] ?? 0); 452 + //geo项目
  453 + if(($item['plan'] == 0) && ($item['seo_plan'] != 0)){
  454 + //geo项目负责人
  455 + $geoConfModel = new GeoConf();
  456 + $manage_id = $geoConfModel->getValue(['project_id'=>$item['id']],'manager_id');
  457 + $item['geo_manage_name'] = $manageModel->getName($manage_id);
  458 + $geoArticleModel = new GeoArticle();
  459 + $item['geo_article_num'] = $geoArticleModel->counts(['project_id'=>$item['id']]);//文章数量
  460 + $geoLinkModel = new GeoLink();
  461 + $item['geo_link_num'] = $geoLinkModel->counts(['project_id'=>$item['id']]);//权威新闻数量
  462 + $questionResModel = new GeoQuestionResult();
  463 + $item['geo_qualify_num'] = $questionResModel->counts(['project_id'=>$item['id'],'hit'=>['!=',0],'platform'=>['in',['openai', 'gemini','google_ai_overview']]]);//排名
  464 + }
449 $item['build_leader'] = $manageModel->getName($item['leader_mid']); 465 $item['build_leader'] = $manageModel->getName($item['leader_mid']);
450 $item['build_manager'] = $manageModel->getName($item['manager_mid']); 466 $item['build_manager'] = $manageModel->getName($item['manager_mid']);
451 $item['build_designer'] = $manageModel->getName($item['designer_mid']); 467 $item['build_designer'] = $manageModel->getName($item['designer_mid']);
@@ -459,18 +475,19 @@ class ProjectController extends BaseController @@ -459,18 +475,19 @@ class ProjectController extends BaseController
459 $seoPlanMap = Project::seoMap(); 475 $seoPlanMap = Project::seoMap();
460 $item['plan'] = $planMap[$item['plan']] ?? ''; 476 $item['plan'] = $planMap[$item['plan']] ?? '';
461 $item['seo_plan'] = $seoPlanMap[$item['seo_plan']] ?? ''; 477 $item['seo_plan'] = $seoPlanMap[$item['seo_plan']] ?? '';
462 - $item['created_at'] = date('Y年m月d日', strtotime($item['cooperate_date']));  
463 - $item['autologin_code'] = getAutoLoginCode($item['id']);  
464 - $domainModel = new DomainInfo(); 478 + $domainModel = new DomainInfoModel();
465 $item['domain'] = !empty($item['domain']) ? $domainModel->getDomain($item['domain']) : ''; 479 $item['domain'] = !empty($item['domain']) ? $domainModel->getDomain($item['domain']) : '';
  480 + $item['uuid'] = TicketProject::where('table_id', $item['id'])->where('project_cate', 2)->value('uuid') ?? null;
  481 + $item['friend_id'] = ProjectAssociation::where('project_id', $item['id'])->where('status', ProjectAssociation::STATUS_NORMAL)->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)->value('friend_id') ?? null;
  482 + $item['autologin_code'] = getAutoLoginCode($item['id']);
  483 + $item['created_at'] = date('Y年m月d日', strtotime($item['cooperate_date']));
466 $item['product_num'] = $data['product'] ?? 0; 484 $item['product_num'] = $data['product'] ?? 0;
467 $item['keyword_num'] = $data['key'] ?? 0; 485 $item['keyword_num'] = $data['key'] ?? 0;
468 $item['article_num'] = ($data['blog'] ?? 0) + ($data['news'] ?? 0); 486 $item['article_num'] = ($data['blog'] ?? 0) + ($data['news'] ?? 0);
469 $item['task_finish_num'] = Task::getNumByProjectId($item['id'], Task::STATUS_DOWN); 487 $item['task_finish_num'] = Task::getNumByProjectId($item['id'], Task::STATUS_DOWN);
470 $item['task_pending_num'] = Task::getNumByProjectId($item['id'], [Task::STATUS_DONGING, Task::STATUS_WAIT]); 488 $item['task_pending_num'] = Task::getNumByProjectId($item['id'], [Task::STATUS_DONGING, Task::STATUS_WAIT]);
471 $item['collect_time'] = $item['is_upgrade'] ? UpdateLog::getProjectUpdate($item['id']) : ''; 489 $item['collect_time'] = $item['is_upgrade'] ? UpdateLog::getProjectUpdate($item['id']) : '';
472 - $item['uuid'] = TicketProject::where('table_id', $item['id'])->where('project_cate', 2)->value('uuid') ?? null;  
473 - $item['friend_id'] = ProjectAssociation::where('project_id', $item['id'])->where('status', ProjectAssociation::STATUS_NORMAL)->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)->value('friend_id') ?? null; 490 + $item['channel'] = Channel::getChannelText($item['channel']['user_id'] ?? 0);
474 return $item; 491 return $item;
475 } 492 }
476 493
@@ -793,7 +810,6 @@ class ProjectController extends BaseController @@ -793,7 +810,6 @@ class ProjectController extends BaseController
793 $order_by_sort = $request->input('order_by_sort', 'desc'); 810 $order_by_sort = $request->input('order_by_sort', 'desc');
794 $start_time = $this->param['start_time'] ?? ''; 811 $start_time = $this->param['start_time'] ?? '';
795 $end_time = $this->param['end_time'] ?? ''; 812 $end_time = $this->param['end_time'] ?? '';
796 -  
797 if(!$source_id && !$id){ 813 if(!$source_id && !$id){
798 $this->response('参数异常',Code::SYSTEM_ERROR); 814 $this->response('参数异常',Code::SYSTEM_ERROR);
799 } 815 }
@@ -887,7 +903,6 @@ class ProjectController extends BaseController @@ -887,7 +903,6 @@ class ProjectController extends BaseController
887 $param['yesterday_ip_count'] = $yesterday_count['ip_num'] ?? 0; 903 $param['yesterday_ip_count'] = $yesterday_count['ip_num'] ?? 0;
888 $param['today_ip_count'] = $today_count['ip_num'] ?? 0; 904 $param['today_ip_count'] = $today_count['ip_num'] ?? 0;
889 $param['inquiry_num'] = $today_count['inquiry_num'] ?? 0; 905 $param['inquiry_num'] = $today_count['inquiry_num'] ?? 0;
890 -  
891 $list[] = $param; 906 $list[] = $param;
892 } 907 }
893 $data['list'] = $list; 908 $data['list'] = $list;
@@ -1178,7 +1193,6 @@ class ProjectController extends BaseController @@ -1178,7 +1193,6 @@ class ProjectController extends BaseController
1178 'id.required' => '项目id不能为空', 1193 'id.required' => '项目id不能为空',
1179 'site_status.required' => '状态不能为空', 1194 'site_status.required' => '状态不能为空',
1180 ]); 1195 ]);
1181 -  
1182 //获取项目数据 1196 //获取项目数据
1183 $projectModel = new Project(); 1197 $projectModel = new Project();
1184 $projectInfo = $projectModel->read(['id'=>$this->param['id']],['project_type','serve_id','site_status','site_token']); 1198 $projectInfo = $projectModel->read(['id'=>$this->param['id']],['project_type','serve_id','site_status','site_token']);
@@ -1188,14 +1202,12 @@ class ProjectController extends BaseController @@ -1188,14 +1202,12 @@ class ProjectController extends BaseController
1188 if($projectInfo['site_status'] == $this->param['site_status']){ 1202 if($projectInfo['site_status'] == $this->param['site_status']){
1189 $this->response('success'); 1203 $this->response('success');
1190 } 1204 }
1191 -  
1192 //获取服务器数据 1205 //获取服务器数据
1193 $serverIpModel = new ServersIp(); 1206 $serverIpModel = new ServersIp();
1194 $serversIpInfo = $serverIpModel->read(['id' => $projectInfo['serve_id']], ['servers_id']); 1207 $serversIpInfo = $serverIpModel->read(['id' => $projectInfo['serve_id']], ['servers_id']);
1195 if(!$serversIpInfo){ 1208 if(!$serversIpInfo){
1196 $this->fail('获取项目所属服务器失败'); 1209 $this->fail('获取项目所属服务器失败');
1197 } 1210 }
1198 -  
1199 if($serversIpInfo['servers_id'] == ServerConfig::SELF_SITE_ID){ 1211 if($serversIpInfo['servers_id'] == ServerConfig::SELF_SITE_ID){
1200 //自建站项目 1212 //自建站项目
1201 if($this->param['site_status'] == 1){ 1213 if($this->param['site_status'] == 1){
@@ -1205,7 +1217,6 @@ class ProjectController extends BaseController @@ -1205,7 +1217,6 @@ class ProjectController extends BaseController
1205 //开启站点 1217 //开启站点
1206 $site_token = str_replace('_expired','',$projectInfo['site_token']); 1218 $site_token = str_replace('_expired','',$projectInfo['site_token']);
1207 } 1219 }
1208 -  
1209 $projectModel->edit(['site_status'=>$this->param['site_status'],'site_token'=>$site_token],['id'=>$this->param['id']]); 1220 $projectModel->edit(['site_status'=>$this->param['site_status'],'site_token'=>$site_token],['id'=>$this->param['id']]);
1210 }else{ 1221 }else{
1211 //普通项目 1222 //普通项目
@@ -1215,7 +1226,6 @@ class ProjectController extends BaseController @@ -1215,7 +1226,6 @@ class ProjectController extends BaseController
1215 if(!$domainInfo){ 1226 if(!$domainInfo){
1216 $this->fail('获取域名数据失败'); 1227 $this->fail('获取域名数据失败');
1217 } 1228 }
1218 -  
1219 if($this->param['site_status'] == 1){ 1229 if($this->param['site_status'] == 1){
1220 //关闭站点:通知C端 1230 //关闭站点:通知C端
1221 $re = curl_get('https://'.$domainInfo['domain'].'/api/stop_or_start_website'); 1231 $re = curl_get('https://'.$domainInfo['domain'].'/api/stop_or_start_website');
@@ -1256,10 +1266,8 @@ class ProjectController extends BaseController @@ -1256,10 +1266,8 @@ class ProjectController extends BaseController
1256 } 1266 }
1257 } 1267 }
1258 } 1268 }
1259 -  
1260 $projectModel->edit(['site_status'=>$this->param['site_status']],['id'=>$this->param['id']]); 1269 $projectModel->edit(['site_status'=>$this->param['site_status']],['id'=>$this->param['id']]);
1261 } 1270 }
1262 -  
1263 $this->response('success'); 1271 $this->response('success');
1264 } 1272 }
1265 1273
@@ -1339,6 +1347,7 @@ class ProjectController extends BaseController @@ -1339,6 +1347,7 @@ class ProjectController extends BaseController
1339 */ 1347 */
1340 public function getSpAdsLists() 1348 public function getSpAdsLists()
1341 { 1349 {
  1350 + $params = $this->param;
1342 $url = 'https://oa.cmer.com/api/sp_ads_lists'; 1351 $url = 'https://oa.cmer.com/api/sp_ads_lists';
1343 // 发送 GET 请求(附带 token) 1352 // 发送 GET 请求(附带 token)
1344 $params['token'] = md5('qqs' . date('Y-m-d')); 1353 $params['token'] = md5('qqs' . date('Y-m-d'));
@@ -21,6 +21,9 @@ class AiBlogController extends BaseController @@ -21,6 +21,9 @@ class AiBlogController extends BaseController
21 * @time :2025/2/14 13:59 21 * @time :2025/2/14 13:59
22 */ 22 */
23 public function getAiBlog(AiBlog $aiBlog){ 23 public function getAiBlog(AiBlog $aiBlog){
  24 + if(isset($this->map['new_title']) && !empty($this->map['new_title'])){
  25 + $this->map['new_title'] = ['like', '%'.$this->map['new_title'].'%'];
  26 + }
24 $lists = $aiBlog->lists($this->map,$this->page,$this->row,'id',['id','keyword','new_title','route','image','task_id','status','created_at','updated_at']); 27 $lists = $aiBlog->lists($this->map,$this->page,$this->row,'id',['id','keyword','new_title','route','image','task_id','status','created_at','updated_at']);
25 if(!empty($lists) && !empty($lists['list'])){ 28 if(!empty($lists) && !empty($lists['list'])){
26 foreach ($lists['list'] as $k => $v){ 29 foreach ($lists['list'] as $k => $v){
@@ -389,14 +389,17 @@ class FileController @@ -389,14 +389,17 @@ class FileController
389 */ 389 */
390 public function getFileList(){ 390 public function getFileList(){
391 if(isset($this->cache['project_id']) && !empty($this->cache['project_id'])){ 391 if(isset($this->cache['project_id']) && !empty($this->cache['project_id'])){
392 - $this->map['project_id'] = $this->cache['project_id']; 392 + $this->param['project_id'] = $this->cache['project_id'];
393 } 393 }
394 $fileModel = new File(); 394 $fileModel = new File();
395 - $this->map['type'] = 'mp4';  
396 - $lists = $fileModel->list($this->map,'id',['id','hash','type','path','created_at','name']);  
397 - foreach ($lists as $k => $v){ 395 + $this->param['type'] = 'mp4';
  396 + $page = $this->param['page'] ?? 1;
  397 + $row = $this->param['row'] ?? 20;
  398 + unset($this->param['page'],$this->param['row']);
  399 + $lists = $fileModel->lists($this->param,$page,$row,'id',['id','hash','type','path','created_at','name']);
  400 + foreach ($lists['list'] as $k => $v){
398 $v['file_link'] = getFileUrl($v['path'],$this->cache['storage_type'] ?? 0,$this->cache['project_location'] ?? 0,$this->cache['file_cdn'] ?? 0); 401 $v['file_link'] = getFileUrl($v['path'],$this->cache['storage_type'] ?? 0,$this->cache['project_location'] ?? 0,$this->cache['file_cdn'] ?? 0);
399 - $lists[$k] = $v; 402 + $lists['list'][$k] = $v;
400 } 403 }
401 $this->response('success',Code::SUCCESS,$lists); 404 $this->response('success',Code::SUCCESS,$lists);
402 } 405 }
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoWritingsLogic.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/10/25 10:43
  8 + */
  9 +
  10 +namespace App\Http\Logic\Aside\Geo;
  11 +
  12 +use App\Http\Logic\Aside\BaseLogic;
  13 +use App\Models\Geo\GeoWritings;
  14 +
  15 +/**
  16 + * @remark :文章任务
  17 + * @name :GeoWritingsLogic
  18 + * @author :lyh
  19 + * @method :post
  20 + * @time :2025/10/25 10:44
  21 + */
  22 +class GeoWritingsLogic extends BaseLogic
  23 +{
  24 + public function __construct()
  25 + {
  26 + parent::__construct();
  27 + $this->param = $this->requestAll;
  28 + $this->model = new GeoWritings();
  29 + }
  30 +}
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoWritingsTaskLogic.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/10/25 10:45
  8 + */
  9 +
  10 +namespace App\Http\Logic\Aside\Geo;
  11 +
  12 +use App\Http\Logic\Aside\BaseLogic;
  13 +use App\Models\Geo\GeoWritings;
  14 +use App\Models\Geo\GeoWritingsTask;
  15 +
  16 +class GeoWritingsTaskLogic extends BaseLogic
  17 +{
  18 + public function __construct()
  19 + {
  20 + parent::__construct();
  21 + $this->param = $this->requestAll;
  22 + $this->model = new GeoWritingsTask();
  23 + }
  24 +}
@@ -34,9 +34,20 @@ class GeoQuestionResLogic extends BaseLogic @@ -34,9 +34,20 @@ class GeoQuestionResLogic extends BaseLogic
34 * @time :2025/7/8 17:16 34 * @time :2025/7/8 17:16
35 */ 35 */
36 public function getCount(){ 36 public function getCount(){
37 - $total = $this->model->counts(['project_id'=>$this->user['project_id']]);  
38 - $type_1 = $this->model->counts(['type'=>$this->model::BRAND_TYPE,'project_id'=>$this->user['project_id']]);  
39 - $type_2 = $this->model->counts(['type'=>$this->model::MARKETING_TYPE,'project_id'=>$this->user['project_id']]); 37 + $total = $this->model
  38 + ->where('project_id', $this->user['project_id'])
  39 + ->distinct('question')
  40 + ->count('question');
  41 + $type_1 = $this->model
  42 + ->where('type', $this->model::BRAND_TYPE)
  43 + ->where('project_id', $this->user['project_id'])
  44 + ->distinct('question')
  45 + ->count('question');
  46 + $type_2 = $this->model
  47 + ->where('type', $this->model::MARKETING_TYPE)
  48 + ->where('project_id', $this->user['project_id'])
  49 + ->distinct('question')
  50 + ->count('question');
40 return $this->success(['total'=>$total,'type_1'=>$type_1,'type_2'=>$type_2]); 51 return $this->success(['total'=>$total,'type_1'=>$type_1,'type_2'=>$type_2]);
41 } 52 }
42 53
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2025/10/22
  6 + * Time: 17:01
  7 + */
  8 +namespace App\Models\Geo;
  9 +
  10 +use App\Models\Base;
  11 +use App\Models\Manage\ManageHr;
  12 +use Illuminate\Support\Facades\Cache;
  13 +
  14 +/**
  15 + * GEO 相关配置
  16 + * Class GeoConf
  17 + * @package App\Models\Geo
  18 + */
  19 +class GeoConf extends Base
  20 +{
  21 + /**
  22 + * @var string table
  23 + */
  24 + protected $table = 'gl_project_geo_conf';
  25 +
  26 + /**
  27 + * 保存GEO相关配置
  28 + * @param $project_id
  29 + * @param $manager_id
  30 + * @param $company
  31 + * @param $brand
  32 + * @param $description
  33 + * @param $prefix
  34 + * @param $suffix
  35 + * @return GeoConf
  36 + */
  37 + public static function saveConf($project_id, $manager_id, $company, $brand, $description, $prefix, $suffix)
  38 + {
  39 + $data = self::where(compact('project_id'))->first();
  40 + if (empty($data)) {
  41 + $data = new self();
  42 + $data->project_id = $project_id;
  43 + }
  44 + $data->manager_id = $manager_id;
  45 + $data->company = $company;
  46 + $data->brand = $brand;
  47 + $data->description = $description;
  48 + $data->prefix = $prefix;
  49 + $data->suffix = $suffix;
  50 + $data->save();
  51 + return $data;
  52 + }
  53 +
  54 + /**
  55 + * GEO 负责人集合
  56 + * TODO 负责人:优化师 + 陶婵 + 艾媛媛
  57 + * @return array
  58 + */
  59 + public function geoManage()
  60 + {
  61 + $key = 'geo_manage_list_' . date('Ymd');
  62 + $optimize = Cache::get($key);
  63 + if (empty($optimize)) {
  64 + $optimize = ManageHr::where(['status' => ManageHr::STATUS_ONE, 'entry_position' => 46])->pluck('name', 'id')->toArray();
  65 + $optimize[1] = '陶婵';
  66 + $optimize[875] = '艾媛媛';
  67 + ksort($optimize);
  68 + Cache::put($key, $optimize, 3600);
  69 + }
  70 + return $optimize;
  71 + }
  72 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2025/10/22
  6 + * Time: 17:03
  7 + */
  8 +namespace App\Models\Geo;
  9 +
  10 +use App\Models\Base;
  11 +use App\Models\Workchat\MessagePush;
  12 +
  13 +/**
  14 + * GEO 客户确认相关数据
  15 + * Class GeoConfirm
  16 + * @package App\Models\Geo
  17 + */
  18 +class GeoConfirm extends Base
  19 +{
  20 + /**
  21 + * @var string table
  22 + */
  23 + protected $table = 'gl_project_geo_confirm';
  24 +
  25 + /**
  26 + * 客户确认类型
  27 + */
  28 + const TYPE_TITLE = 1;
  29 + const TYPE_KEYWORD = 2;
  30 +
  31 + /**
  32 + * 数据状态
  33 + */
  34 + const STATUS_INIT = 1; # 初始化数据,仅保存完成
  35 + const STATUS_RUNNING = 2; # 已推送客户,等待客户确认
  36 + const STATUS_FINISH = 3; # 客户已确认完成
  37 +
  38 + /**
  39 + * 客户确认数据类型
  40 + * @return array
  41 + */
  42 + public static function typeMapping()
  43 + {
  44 + return [
  45 + self::TYPE_TITLE => '确认标题',
  46 + self::TYPE_KEYWORD => '确认关键词'
  47 + ];
  48 + }
  49 +
  50 + /**
  51 + * 客户确认数据状态
  52 + * @return array
  53 + */
  54 + public static function statusMapping()
  55 + {
  56 + return [
  57 + self::STATUS_INIT => '初始数据',
  58 + self::STATUS_RUNNING => '数据确认中',
  59 + self::STATUS_FINISH => '客户已确认'
  60 + ];
  61 + }
  62 +
  63 + /**
  64 + * @param $project_id
  65 + * @param $type
  66 + * @param $content
  67 + * @param $max_num
  68 + * @return GeoConfirm
  69 + */
  70 + public static function saveContent($project_id, $type, $content, $max_num)
  71 + {
  72 + $data = self::where(compact('project_id', 'type'))->first();
  73 + if (empty($data)) {
  74 + $data = new self();
  75 + $data->project_id = $project_id;
  76 + $data->type = $type;
  77 + }
  78 + $data->content = $content;
  79 + $data->max_num = $max_num;
  80 + $data->save();
  81 + return $data;
  82 + }
  83 +
  84 + /**
  85 + * 保存确认数据
  86 + * @param $project_id
  87 + * @param $type
  88 + * @param $confirm
  89 + * @param $confirm_num
  90 + * @param $confirm_ip
  91 + * @return bool
  92 + */
  93 + public static function saveConfirm($project_id, $type, $confirm, $confirm_num, $confirm_ip)
  94 + {
  95 + $data = self::where(compact('project_id', 'type'))->first();
  96 + if (empty($data))
  97 + return false;
  98 + $data->confirm = $confirm;
  99 + $data->confirm_ip = $confirm_ip;
  100 + $data->confirm_num = $confirm_num;
  101 + $data->confirm_at = now();
  102 + $data->status = self::STATUS_FINISH;
  103 + $data->save();
  104 + return $data;
  105 + }
  106 +
  107 + /**
  108 + * 推送确认消息
  109 + * @param $id
  110 + * @return bool
  111 + */
  112 + public static function sendConfirmMessage($id, $friend_id)
  113 + {
  114 + $data = self::where(compact('id'))->first();
  115 + $project_id = $data->project_id;
  116 + $content_type = 'Link';
  117 + $send_time = now();
  118 + $type = MessagePush::TYPE_GEO_CONFIRM;
  119 + $token = uniqid().$friend_id;
  120 + $created_at = $updated_at = now();
  121 + $content_array = [
  122 + 'title' => self::typeMapping()[$data->type],
  123 + 'desc' => self::typeMapping()[$data->type],
  124 + 'size' => 0,
  125 + 'thumbSize' => 0,
  126 + 'thumbUrl' => 'https://hub.globalso.com/logocm.png',
  127 + 'url' => 'https://oa.quanqiusou.cn/public-geo-confirm?token=' . $token
  128 + ];
  129 + $content = json_encode($content_array, JSON_UNESCAPED_UNICODE);
  130 + MessagePush::insert(compact('project_id', 'friend_id', 'type', 'content_type', 'content', 'send_time', 'updated_at', 'created_at'));
  131 + // 消息推送, 更新数据
  132 + $data->confirm = '';
  133 + $data->send_at = now();
  134 + $data->uniqid = $token;
  135 + $data->status = self::STATUS_RUNNING;
  136 + $data->save();
  137 + return $data;
  138 + }
  139 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2025/10/22
  6 + * Time: 17:25
  7 + */
  8 +namespace App\Models\Geo;
  9 +
  10 +use App\Models\Base;
  11 +use App\Models\ProjectAssociation\ProjectAssociation;
  12 +use App\Models\Workchat\MessagePush;
  13 +use Illuminate\Support\Facades\Crypt;
  14 +
  15 +/**
  16 + * GEO 文章相关
  17 + * Class GeoWritings
  18 + * @package App\Models\Geo
  19 + */
  20 +class GeoWritings extends Base
  21 +{
  22 + /**
  23 + * @var string $table
  24 + */
  25 + protected $table = 'gl_project_geo_writings';
  26 +
  27 + /**
  28 + * 文章来源类型
  29 + */
  30 + const TYPE_AI_CREATE = 1;
  31 + const TYPE_SUBMIT = 2;
  32 +
  33 + /**
  34 + * 文章状态类型
  35 + */
  36 + const STATUS_INIT = 1; # 文章仅保存完成,未推送给客户
  37 + const STATUS_RUNNING = 2; # 已推送客户,等待客户确认
  38 + const STATUS_FINISH = 3; # 客户已确认完成
  39 + const STATUS_AI_WAIT = 7; # 任务提交成功,等待AI生成
  40 + const STATUS_AI_RUNNING = 8; # AI生成中
  41 +
  42 + const IS_DEL_FALSE = 0;
  43 + const IS_DEL_TRUE = 1;
  44 +
  45 + /**
  46 + * @return array
  47 + */
  48 + public static function typeMapping()
  49 + {
  50 + return [
  51 + self::TYPE_AI_CREATE => 'AI生成文章',
  52 + self::TYPE_SUBMIT => '上传已有文章'
  53 + ];
  54 + }
  55 +
  56 + /**
  57 + * 状态隐私
  58 + * @return array
  59 + */
  60 + public static function statusMapping()
  61 + {
  62 + return [
  63 + self::STATUS_INIT => '已就绪,待推送',
  64 + self::STATUS_RUNNING => '已推送,待确认',
  65 + self::STATUS_FINISH => '已确认',
  66 + self::STATUS_AI_WAIT => '等待生成',
  67 + self::STATUS_AI_RUNNING => '生成中',
  68 + ];
  69 + }
  70 +
  71 + /**
  72 + * 推送确认消息
  73 + * TODO 通过项目ID,时间生成推送token,页面打开后, 回传token解码确定展示项目数据
  74 + * @param $project_id
  75 + * @return bool
  76 + * @throws \Exception
  77 + */
  78 + public static function sendConfirmMessage($project_id)
  79 + {
  80 + $friend = ProjectAssociation::where(['project_id' => $project_id])->first();
  81 + if (empty($friend)) {
  82 + throw new \Exception('项目未绑定微信群');
  83 + }
  84 + $content_type = 'Link';
  85 + $send_time = now();
  86 + $type = MessagePush::TYPE_GEO_CONFIRM;
  87 + $friend_id = $friend->friend_id;
  88 + $created_at = $updated_at = now();
  89 + $param = [
  90 + 'project_id' => $project_id,
  91 + 'send_at' => time()
  92 + ];
  93 + $token = Crypt::encrypt($param);
  94 + $content_array = [
  95 + 'title' => "确认核心文章",
  96 + 'desc' => '确认核心文章',
  97 + 'size' => 0,
  98 + 'thumbSize' => 0,
  99 + 'thumbUrl' => 'https://hub.globalso.com/logocm.png',
  100 + 'url' => 'https://oa.quanqiusou.cn/public-geo-article-list?token=' . $token
  101 + ];
  102 + $content = json_encode($content_array, JSON_UNESCAPED_UNICODE);
  103 + MessagePush::insert(compact('project_id', 'friend_id', 'type', 'content_type', 'content', 'send_time', 'updated_at', 'created_at'));
  104 + return true;
  105 + }
  106 +
  107 +}
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GeoWritingsTask.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/10/25 10:47
  8 + */
  9 +
  10 +namespace App\Models\Geo;
  11 +
  12 +use App\Models\Base;
  13 +
  14 +/**
  15 + * @remark :文章生成任务
  16 + * @name :GeoWritingsTask
  17 + * @author :lyh
  18 + * @method :post
  19 + * @time :2025/10/25 10:48
  20 + */
  21 +class GeoWritingsTask extends Base
  22 +{
  23 + protected $table = 'gl_project_geo_writings_task';
  24 +}
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 namespace App\Models\Project; 10 namespace App\Models\Project;
11 11
12 use App\Models\Base; 12 use App\Models\Base;
  13 +use Illuminate\Support\Facades\Cache;
13 14
14 /** 15 /**
15 * @remark :关键字前缀/后缀 16 * @remark :关键字前缀/后缀
@@ -21,4 +22,68 @@ use App\Models\Base; @@ -21,4 +22,68 @@ use App\Models\Base;
21 class KeywordPrefix extends Base 22 class KeywordPrefix extends Base
22 { 23 {
23 protected $table = 'gl_project_keyword_prefix'; 24 protected $table = 'gl_project_keyword_prefix';
  25 +
  26 + /**
  27 + * 前后缀类型
  28 + */
  29 + const TYPE_OPTIMIZE_PREFIX = 1;
  30 + const TYPE_OPTIMIZE_SUFFIX = 2;
  31 + const TYPE_GEO_PREFIX = 3;
  32 + const TYPE_GEO_SUFFIX = 4;
  33 +
  34 + /**
  35 + * 前后缀类型映射
  36 + * @return array
  37 + */
  38 + public static function typeMapping()
  39 + {
  40 + return [
  41 + self::TYPE_OPTIMIZE_PREFIX => '优化关键词前缀',
  42 + self::TYPE_OPTIMIZE_SUFFIX => '优化关键词后缀',
  43 + self::TYPE_GEO_PREFIX => 'GEO前缀',
  44 + self::TYPE_GEO_SUFFIX => 'GEO后缀',
  45 + ];
  46 + }
  47 +
  48 + /**
  49 + * 保存关键词前后缀
  50 + * @param $project_id
  51 + * @param $type
  52 + * @param $keyword
  53 + * @param $remark
  54 + * @return KeywordPrefix
  55 + */
  56 + public static function saveKeyword($project_id, $type, $keyword, $remark)
  57 + {
  58 + $data = self::where(['project_id' => $project_id, 'keyword' => $keyword])->first();
  59 + if (empty($data)) {
  60 + $data = new self();
  61 + $data->project_id = $project_id;
  62 + $data->type = $type;
  63 + $data->keyword = $keyword;
  64 + }
  65 + $data->remark = $remark;
  66 + $data->save();
  67 + Cache::forget('project_keyword_ps_' . $project_id . '_' . $type);
  68 + return $data;
  69 + }
  70 +
  71 + /**
  72 + * 获取关键词前后缀
  73 + * @param $project_id
  74 + * @param $type
  75 + * @return array|mixed
  76 + */
  77 + public static function getKeyword($project_id, $type)
  78 + {
  79 + $key = 'project_keyword_ps_' . $project_id . '_' . $type;
  80 + $list = Cache::get($key);
  81 + if (empty($list)) {
  82 + $list = self::select(['id', 'type', 'keyword', 'remark'])->where(['type' => $type])->whereIn('project_id', [0, $project_id])->orderBy('project_id', 'asc')->get();
  83 + $list = $list->isEmpty() ? [] : $list->toArray();
  84 + if ($list)
  85 + Cache::put($key, $list, 1800);
  86 + }
  87 + return $list;
  88 + }
24 } 89 }
@@ -30,6 +30,7 @@ class MessagePush extends Base @@ -30,6 +30,7 @@ class MessagePush extends Base
30 const TYPE_TICKET = 'Ticket'; 30 const TYPE_TICKET = 'Ticket';
31 const TYPE_DOMAIN = 'domain'; 31 const TYPE_DOMAIN = 'domain';
32 const TYPE_DOMAIN_V5 = 'domain_v5'; 32 const TYPE_DOMAIN_V5 = 'domain_v5';
  33 + const TYPE_GEO_CONFIRM = 'geo_confirm';
33 //设置关联表名 34 //设置关联表名
34 /** 35 /**
35 * @var mixed 36 * @var mixed
@@ -107,4 +107,8 @@ Route::prefix('ticket_upload')->group(function () { @@ -107,4 +107,8 @@ Route::prefix('ticket_upload')->group(function () {
107 Route::any('/saveLayoutDesign', [\App\Http\Controllers\Api\WorkOrder\TicketUploadDataController::class, 'saveLayoutDesign'])->name('ticket_upload.saveLayoutDesign'); 107 Route::any('/saveLayoutDesign', [\App\Http\Controllers\Api\WorkOrder\TicketUploadDataController::class, 'saveLayoutDesign'])->name('ticket_upload.saveLayoutDesign');
108 Route::any('/getLayoutDesignInfo', [\App\Http\Controllers\Api\WorkOrder\TicketUploadDataController::class, 'getLayoutDesignInfo'])->name('ticket_upload.getLayoutDesignInfo'); 108 Route::any('/getLayoutDesignInfo', [\App\Http\Controllers\Api\WorkOrder\TicketUploadDataController::class, 'getLayoutDesignInfo'])->name('ticket_upload.getLayoutDesignInfo');
109 }); 109 });
  110 +//geo设置
  111 +Route::prefix('geo')->group(function () {
  112 + Route::any('/getConfirm', [\App\Http\Controllers\Api\GeoController::class, 'getConfirm'])->name('geo.getConfirm');
  113 +});
110 114
@@ -586,6 +586,12 @@ Route::middleware(['aloginauth'])->group(function () { @@ -586,6 +586,12 @@ Route::middleware(['aloginauth'])->group(function () {
586 Route::any('/downloadGeoLink', [Aside\Geo\GeoLinkController::class, 'downloadGeoLink'])->name('admin.geo_link_downloadGeoLink'); 586 Route::any('/downloadGeoLink', [Aside\Geo\GeoLinkController::class, 'downloadGeoLink'])->name('admin.geo_link_downloadGeoLink');
587 Route::any('/daResultData', [Aside\Geo\GeoLinkController::class, 'daResultData'])->name('admin.geo_link_daResultData'); 587 Route::any('/daResultData', [Aside\Geo\GeoLinkController::class, 'daResultData'])->name('admin.geo_link_daResultData');
588 }); 588 });
  589 + //geo设置
  590 + Route::prefix('conf')->group(function () {
  591 + Route::any('/getConfig', [Aside\Geo\GeoController::class, 'getConfig'])->name('admin.geo_conf_getConfig');
  592 + Route::any('/saveConfig', [Aside\Geo\GeoController::class, 'saveConfig'])->name('admin.geo_conf_saveConfig');
  593 + Route::any('/saveConfirmContent', [Aside\Geo\GeoController::class, 'saveConfirmContent'])->name('admin.geo_conf_saveConfirmContent');
  594 + });
589 }); 595 });
590 // 任务相关 596 // 任务相关
591 Route::prefix('task')->group(function () { 597 Route::prefix('task')->group(function () {
@@ -652,6 +658,8 @@ Route::middleware(['aloginauth'])->group(function () { @@ -652,6 +658,8 @@ Route::middleware(['aloginauth'])->group(function () {
652 Route::any('/save', [Aside\Ticket\TicketUploadDataController::class,'save'])->name('ticket_upload_save'); 658 Route::any('/save', [Aside\Ticket\TicketUploadDataController::class,'save'])->name('ticket_upload_save');
653 Route::any('/detail', [Aside\Ticket\TicketUploadDataController::class,'detail'])->name('ticket_upload_detail'); 659 Route::any('/detail', [Aside\Ticket\TicketUploadDataController::class,'detail'])->name('ticket_upload_detail');
654 }); 660 });
  661 +
  662 +
655 }); 663 });
656 664
657 //无需登录验证的路由组 665 //无需登录验证的路由组