作者 刘锟

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

正在显示 45 个修改的文件 包含 1559 行增加153 行删除
... ... @@ -113,7 +113,7 @@ class AiBlogAutoPublish extends Command
}
}
} else {
if($frequency[0] == '1/2'){//一天2篇
if(strpos($frequency[0],'/')){//一天2篇
$aiBlogTaskModel = new AiBlogTaskModel();
$frequency = explode('/', $frequency[0]);
//查询当前已发布几篇
... ...
... ... @@ -11,6 +11,7 @@ namespace App\Console\Commands\Geo;
use App\Models\Geo\GeoQuestionLog;
use App\Models\Geo\GeoQuestionResult;
use App\Models\Project\Project;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use App\Models\Geo\GeoCount as GeoCountModel;
... ... @@ -64,15 +65,23 @@ class GeoCount extends Command
$geoQuestionResModel = new GeoQuestionResult();
$platforms = ['gemini','openai','deepseek','poe','perplexity','google_ai_overview','openai-not-network','claude'];
foreach ($project_id as $item){
$projectModel = new Project();
$geo_qualify_num = $projectModel->getValue(['id'=>$item],'geo_qualify_num');
$this->output('执行的项目ID----'.$item);
//收录总数
$total = $geoQuestionResModel->counts(['project_id' => $item,'hit'=>['!=',0],'created_at' => ['between',[$start,$end]]]);
$qualify_total = $geoQuestionResModel->counts(['platform'=>['in',['openai', 'gemini','google_ai_overview']],'project_id' => $item,'hit'=>['!=',0],'created_at' => ['between',[$start,$end]]]);
if($qualify_total > $geo_qualify_num){
$is_qualify = 1;
}
$data = [
'project_id' => $item,
'date' => $date,
'created_at' => Carbon::now()->format('Y-m-d H:i:s'),
'updated_at' => Carbon::now()->format('Y-m-d H:i:s'),
'total'=>$total,//收录总数
'is_qualify'=>$is_qualify,
'qualify_total'=>$qualify_total
];
foreach ($platforms as $platform){
if($platform == 'openai-not-network'){
... ...
... ... @@ -11,6 +11,7 @@ namespace App\Console\Commands\Geo;
use App\Models\Geo\GeoQuestionLog;
use App\Models\Geo\GeoQuestionResult;
use App\Models\Project\Project;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use App\Models\Geo\GeoCount as GeoCountModel;
... ... @@ -68,15 +69,23 @@ class GeoCountAll extends Command
$geoQuestionResModel = new GeoQuestionLog();
$platforms = ['gemini','openai','deepseek','poe','perplexity','google_ai_overview','openai-not-network','claude'];
foreach ($project_id as $item){
$projectModel = new Project();
$geo_qualify_num = $projectModel->getValue(['id'=>$item],'geo_qualify_num');
$this->output('执行的项目ID----'.$item);
//收录总数
$total = $geoQuestionResModel->counts(['project_id' => $item,'hit'=>['!=',0],'created_at' => ['between',[$start,$end]]]);
$qualify_total = $geoQuestionResModel->counts(['platform'=>['in',['openai', 'gemini','google_ai_overview']],'project_id' => $item,'hit'=>['!=',0],'created_at' => ['between',[$start,$end]]]);
if($total > $geo_qualify_num){
$is_qualify = 1;
}
$data = [
'project_id' => $item,
'date' => $date,
'created_at' => Carbon::now()->format('Y-m-d H:i:s'),
'updated_at' => Carbon::now()->format('Y-m-d H:i:s'),
'total'=>$total,//收录总数
'is_qualify'=>$is_qualify,
'qualify_total'=>$qualify_total
];
foreach ($platforms as $platform){
if($platform == 'openai-not-network'){
... ...
... ... @@ -340,12 +340,11 @@ class GeoQuestionRes extends Command
$task_id = Redis::rpop($key);
if(empty($task_id)){
//todo::这里需要执行统计一次,统计当前项目当前日期的统计
# TODO 按照项目进行获取, 一个项目当天需要将所有跑完
$project_id = GeoQuestion::where('status', GeoQuestion::STATUS_OPEN)->where('next_time', '<=', date('Y-m-d'))->value('project_id');
if (!empty($project_id)){
$this->project_id = $project_id;
$ids = GeoQuestion::where(['project_id' => $project_id, 'status' => GeoQuestion::STATUS_OPEN])->where('current_time', '<>', date('Y-m-d'))->pluck('id');
$ids = GeoQuestion::where(['project_id' => $project_id, 'status' => GeoQuestion::STATUS_OPEN])->where('next_time', '<=', date('Y-m-d'))->pluck('id');
foreach ($ids as $id) {
Redis::lpush($key, $id);
}
... ...
... ... @@ -9,10 +9,7 @@
namespace App\Console\Commands\LyhTest;
use App\Models\CustomModule\CustomModuleCategory;
use App\Models\CustomModule\CustomModuleContent;
use App\Models\CustomModule\CustomModuleExtentContent;
use App\Models\News\News;
use App\Models\Product\Category;
use App\Models\Product\CategoryRelated;
use App\Models\Product\Column;
... ... @@ -25,6 +22,7 @@ use App\Models\Template\Template;
use App\Services\ProjectServer;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use PhpOffice\PhpSpreadsheet\IOFactory;
class LyhImportTest extends Command
{
... ... @@ -52,15 +50,92 @@ class LyhImportTest extends Command
*/
public function handle()
{
ProjectServer::useProject(3531);
echo date('Y-m-d H:i:s') . 'start->3531' . PHP_EOL;
// $this->importProductCategory('https://ecdn6-nc.globalso.com/upload/p/3654/file/2025-06/products-1.csv',3654);
$this->import3531CustomModule(3531);
ProjectServer::useProject(1517);
echo date('Y-m-d H:i:s') . 'start->1517' . PHP_EOL;
$url1 = 'https://ecdn6.globalso.com/upload/p/1517/file/2025-10/horse-racing-tkd-modification-1.xlsx';//改tdk
$url2 = 'https://v6-file.globalso.com/upload/p/1517/file/2025-10/horse-racing-url-modification.xlsx';//改url路由+tdk
$this->download_1517_action($url2);
DB::disconnect('custom_mysql');
echo date('Y-m-d H:i:s') . 'end' . PHP_EOL;
}
/**
* @remark :导入
* @name :download_1517_action
* @author :lyh
* @method :post
* @time :2025/10/22 16:47
*/
public function download_1517_action($url)
{
// 临时文件路径(确保目录存在)
$tempDir = storage_path('app');
if (!is_dir($tempDir)) {
mkdir($tempDir, 0755, true);
}
$tempPath = $tempDir . '/temp_url.xlsx';
try {
// 使用 cURL 下载文件,防止403问题
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true, // 跟随重定向
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; LaravelApp/1.0)', // 模拟浏览器
CURLOPT_TIMEOUT => 30, // 超时防止卡死
]);
$data = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// 检查下载是否成功
if ($httpCode !== 200 || !$data) {
echo "文件下载失败,HTTP状态码:{$httpCode}";
}
// 保存到临时文件
file_put_contents($tempPath, $data);
// 加载 Excel 文件
$spreadsheet = IOFactory::load($tempPath);
$sheet = $spreadsheet->getActiveSheet();
$rows = $sheet->toArray();
// 删除临时文件
if (file_exists($tempPath)) {
unlink($tempPath);
}
$d = [];
//todo::处理数据
$productModel = new Product();
foreach ($rows as $key => $row) {
if($key == 0){
continue;
}
$path = parse_url($row[1], PHP_URL_PATH);
$path = ltrim(parse_url($path, PHP_URL_PATH), '/');
$info = $productModel->read(['route'=>$path],['id','title','route','seo_mate']);
if($info === false){
echo '未找到数据->'.$row[0].PHP_EOL;
$d[] = $row[0];
continue;
}
$seo_mate = $info['seo_mate'];
$seo_mate['title'] = $row[2] ?? '';
$seo_mate['keyword'] = $row[3] ?? '';
//todo::重新生成路由
$route = RouteMap::setRoute($row[2],RouteMap::SOURCE_PRODUCT,$info['id'],$info['project_id']);
$productModel->edit(['route'=>$route,'old_route'=>$path,'seo_mate'=>json_encode($seo_mate,true),'title'=>$row[2]],['id'=>$info['id']]);
echo '执行的ID:'.$info['id'].PHP_EOL;
}
} catch (\Exception $e) {
// 输出更清晰的错误信息
echo '文件下载或解析失败:' . $e->getMessage() . PHP_EOL;
// 清理资源
if (file_exists($tempPath)) {
unlink($tempPath);
}
DB::disconnect('custom_mysql');
}
}
/**
* @remark :3951项目导入产品
* @name :import3951Product
* @author :lyh
... ...
... ... @@ -86,8 +86,8 @@ class SendProduct extends Command
$start_date = date('Y-m-d 00:00:00');
$end_date = date('Y-m-d 23:59:59');
$productModel = new Product();
$arr = $productModel->formatQuery(['send_time'=>['between',[$start_date,$end_date]],'status'=>3])->pluck('route')->toArray();
$productModel->edit(['status'=>1],['send_time'=>['between',[$start_date,$end_date]],'status'=>3]);
$arr = $productModel->formatQuery(['send_time'=>['<=',$end_date],'status'=>3])->pluck('route')->toArray();
$productModel->edit(['status'=>1],['send_time'=>['<=',$end_date],'status'=>3]);
return $arr;
}
... ... @@ -102,8 +102,8 @@ class SendProduct extends Command
$start_date = date('Y-m-d 00:00:00');
$end_date = date('Y-m-d 23:59:59');
$blogModel = new Blog();
$arr = $blogModel->formatQuery(['release_at'=>['between',[$start_date,$end_date]],'status'=>3])->pluck('url')->toArray();
$blogModel->edit(['status'=>1],['release_at'=>['between',[$start_date,$end_date]],'status'=>3]);
$arr = $blogModel->formatQuery(['release_at'=>['<=',$end_date],'status'=>3])->pluck('url')->toArray();
$blogModel->edit(['status'=>1],['release_at'=>['<=',$end_date],'status'=>3]);
return $arr;
}
... ... @@ -118,8 +118,8 @@ class SendProduct extends Command
$start_date = date('Y-m-d 00:00:00');
$end_date = date('Y-m-d 23:59:59');
$newsModel = new News();
$arr = $newsModel->formatQuery(['release_at'=>['between',[$start_date,$end_date]],'status'=>3])->pluck('url')->toArray();
$newsModel->edit(['status'=>1],['release_at'=>['between',[$start_date,$end_date]],'status'=>3]);
$arr = $newsModel->formatQuery(['release_at'=>['<=',$end_date],'status'=>3])->pluck('url')->toArray();
$newsModel->edit(['status'=>1],['release_at'=>['<=',$end_date],'status'=>3]);
return $arr;
}
}
... ...
... ... @@ -33,11 +33,11 @@ class SyncImage extends Command
public function handle(){
$str = $this->getProjectConfig(4636);
$str = $this->getProjectConfig(2123);
$imageModel = new Image();
// $strImage = '/upload/p/4240/image_other/2025-09/picture-18-copy-3.jpg,/upload/p/4240/image_other/2025-09/picture-17-copy-2.jpg,/upload/p/4240/image_other/2025-09/picture-16-copy-2.jpg,/upload/p/4240/image_other/2025-09/picture-12-copy-3.jpg,/upload/p/4240/image_other/2025-09/picture-5-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-6-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-10-copy-2.jpg,/upload/p/4240/image_other/2025-09/picture-11-copy-2.jpg,/upload/p/4240/image_other/2025-09/4-1.jpg,/upload/p/4240/image_other/2025-09/picture-18-copy-2.jpg,/upload/p/4240/image_other/2025-09/picture-16-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-17-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-15-copy-2.jpg,/upload/p/4240/image_other/2025-09/picture-13-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-14-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-12-copy-2.jpg,/upload/p/4240/image_other/2025-09/picture-10-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-11-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-20-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-18-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-19-copy-1.jpg,/upload/p/4240/image_other/2025-09/image-1-copy.jpg,/upload/p/4240/image_other/2025-09/image-2-copy.jpg,/upload/p/4240/image_other/2025-09/picture-3-copy.jpg,/upload/p/4240/image_other/2025-09/picture-6-copy.jpg,/upload/p/4240/image_other/2025-09/picture-4-copy.jpg,/upload/p/4240/image_other/2025-09/picture-5-copy.jpg,/upload/p/4240/image_other/2025-09/picture-9-copy.jpg,/upload/p/4240/image_other/2025-09/picture-7-copy.jpg,/upload/p/4240/image_other/2025-09/picture-8-copy.jpg,/upload/p/4240/image_other/2025-09/picture-15-copy-1.jpg,/upload/p/4240/image_other/2025-09/picture-14-copy.jpg,/upload/p/4240/image_other/2025-09/picture-13-copy.jpg,/upload/p/4240/image_other/2025-09/picture-12-copy-1.jpg,/upload/p/4240/image_other/2025-08/picture-44-copy.jpg,/upload/p/4240/image_other/2025-08/45.jpg,/upload/p/4240/image_other/2025-09/picture-10-copy.jpg,/upload/p/4240/image_other/2025-09/picture-11-copy.jpg,/upload/p/4240/image_other/2025-08/43.jpg,/upload/p/4240/image_other/2025-08/42.jpg,/upload/p/4240/image_other/2025-08/picture-38-copy.jpg,/upload/p/4240/image_other/2025-08/picture-37-copy.jpg,/upload/p/4240/image_other/2025-08/picture-36-copy.jpg,/upload/p/4240/image_other/2025-08/picture-35-copy-1.jpg,/upload/p/4240/image_other/2025-08/picture-30-copy.jpg,/upload/p/4240/image_other/2025-08/29.jpg,/upload/p/4240/image_other/2025-08/28-1.jpg,/upload/p/4240/image_other/2025-08/27-1.jpg,/upload/p/4240/image_other/2025-08/picture-23-copy.jpg,/upload/p/4240/image_other/2025-08/22-1.jpg,/upload/p/4240/image_other/2025-08/21.jpg,/upload/p/4240/image_other/2025-08/17.jpg,/upload/p/4240/image_other/2025-08/16.jpg,/upload/p/4240/image_other/2025-08/picture-15-copy.jpg,/upload/p/4240/image_other/2025-08/11-2.jpg,/upload/p/4240/image_other/2025-08/10-1.jpg,/upload/p/4240/image_other/2025-08/picture-6.jpg,/upload/p/4240/image_other/2025-08/picture-5.jpg,/upload/p/4240/image_other/2025-08/picture-4.jpg,/upload/p/4240/image_other/2025-08/api-standard-welded-pipe.jpg,/upload/p/4240/image_other/2025-08/gb-standard-welded-pipe.jpg,/upload/p/4240/image_other/2025-08/gost-standard-welded-pipe.jpg,/upload/p/4240/image_other/2025-08/jis-standard-welded-pipe.jpg,/upload/p/4240/image_other/2025-08/asmeastm-welded-pipe.jpg,/upload/p/4240/image_other/2025-08/en-standard-welded-pipe.jpg,/upload/p/4240/image_other/2025-08/api-corrosion-resistant-alloy-cra-pipe.jpg,/upload/p/4240/image_other/2025-08/nickel-alloy-seamless-pipe.jpg,/upload/p/4240/image_other/2025-08/duplexsuper-duplex-seamless-pipe.jpg,/upload/p/4240/image_other/2025-08/super-austenitic-seamless-pipe.jpg,/upload/p/4240/image_other/2025-08/austenitic-stainless-steel-seamless-pipe.jpg,/upload/p/4240/image_other/2025-08/thick-zinc-coated-pipe.jpg,/upload/p/4240/image_other/2025-08/galvanized-fbe-pipe.jpg,/upload/p/4240/image_other/2025-08/electro-galvanized-pipe.jpg,/upload/p/4240/image_other/2025-08/hot-dip-galvanized-pipe.jpg,/upload/p/4240/image_other/2025-08/q235-galvanized-pipe.jpg,/upload/p/4240/image_other/2025-08/q195-galvanized-pipe.jpg,/upload/p/4240/image_other/2025-08/bare-ssaw-steel-pipe.jpg,/upload/p/4240/image_other/2025-08/fbe-coated-ssaw-steel-pipe.jpg,/upload/p/4240/image_other/2025-08/x52-ssaw-steel-pipe.jpg,/upload/p/4240/image_other/2025-08/q235b-ssaw-steel-pipe.jpg,/upload/p/4240/image_other/2025-08/low-alloy-steel-erw-pipe.jpg,/upload/p/4240/image_other/2025-08/carbon-steel-erw-pipe.jpg,/upload/p/4240/image_other/2025-08/black-carbon-steel-pipe.jpg,/upload/p/4240/image_other/2025-08/picture-19-copy.jpg,/upload/p/4240/image_other/2025-08/hydrogen-energy-skid.jpg';
$lists = $imageModel->selectField(['project_id'=>4636],'path');
// $lists = explode(',',$strImage);
$strImage = '/upload/p/2123/image_other/2025-10/1-361.jpg,/upload/p/2123/image_other/2025-10/3-255.jpg,/upload/p/2123/image_other/2025-10/4-237.jpg,/upload/p/2123/image_other/2025-10/5-104.jpg,/upload/p/2123/image_other/2025-10/6-53.jpg,/upload/p/2123/image_other/2025-10/7-31.jpg,/upload/p/2123/image_other/2025-10/8-27.jpg,/upload/p/2123/image_other/2025-10/5-105.jpg';
// $lists = $imageModel->selectField(['project_id'=>4636],'path');
$lists = explode(',',$strImage);
$domain = 'http://globalso-v6-1309677403.cos.ap-hongkong.myqcloud.com';//cos域名
foreach ($lists as $v){
$url = $domain . $v.'?'.$str;
... ...
... ... @@ -100,7 +100,9 @@ class UpdateKeyword extends Command
}else{
$randomNumber = $text[$key] ?? rand(0, $number - 1);
}
$keywordModel->edit(['keyword_content'=>$text[$randomNumber]],['title'=>$item]);
if(isset($text[$randomNumber])){
$keywordModel->edit(['keyword_content'=>$text[$randomNumber]],['title'=>$item]);
}
}
}
//按给定的数量更新
... ... @@ -112,7 +114,9 @@ class UpdateKeyword extends Command
}else{
$randomNumber = $text[$key] ?? rand(0, $number - 1);
}
$keywordModel->edit(['keyword_content'=>$text[$randomNumber]],['title'=>$item]);
if(isset($text[$randomNumber])){
$keywordModel->edit(['keyword_content'=>$text[$randomNumber]],['title'=>$item]);
}
}
}
}
... ...
... ... @@ -163,6 +163,62 @@ class FormGlobalsoApi
}
/**
*
* 自定义邮件发送接口
* @param string $to_email 接收邮箱
* @param string $subject 标题
* @param string $message 发送内容
* @param string $ip 发送ip
* @param string $refer 来源页面
* @param string $submit_time 询盘发送时间
* @param string $reply_email 回复邮箱
* @return bool
*/
function customizeInquiryEmail($to_email, $subject, $message, $ip, $refer, $submit_time, $reply_email = '')
{
$post_data['from_email'] = 'mail@goodao.cn';
$post_data['to_email'] = trim($to_email);
$post_data['title'] = trim($subject);
$post_data['message'] = $message;
$post_data['ip'] = trim($ip);
$post_data['refer'] = trim($refer);
$post_data['reply_email'] = trim($reply_email);
if(!$reply_email){
$post_data['reply_email'] = $post_data['from_email'];
}
$post_data['submit_time'] = $submit_time;
$url = "https://form.globalso.com/api/b4153b7556";
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.87 Safari/537.36');
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
$curl_response = curl_exec($curl);
curl_close($curl);
return true;
}
/**
* 推送自定义询盘短信
* @param string $phones 多个手机号码, 使用英文逗号隔开
* @param string $country
* @param string $domain
* @return mixed|string
*/
public function customizeInquirySms($phones, $country, $domain)
{
$param = compact('phones', 'country', 'domain');
$url = 'https://form.globalso.com/api/send_sms?' . http_build_query($param);
$result = http_get($url,['charset=utf-8']);
return $result;
}
/**
* @remark :获取当前项目所有询盘及询盘国家
* @name :getInquiryAll
* @author :lyh
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2025/10/23
* Time: 17:29
*/
namespace App\Http\Controllers\Api;
use App\Models\Geo\GeoConfirm;
use App\Models\Geo\GeoWritings;
use App\Models\Project\Project;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
/**
* GEO相关
* Class GeoController
* @package App\Http\Controllers\Api
*/
class GeoController extends BaseController
{
/**
* 获取确认文章列表
* @param Request $request
* @return false|string
*/
public function getWritingsList()
{
try {
$token = trim($this->param['token']);
$param = Crypt::decrypt($token);
if ($param['send_at'] + 86400 < time()) {}
$project_id = $param['project_id'];
} catch (\Exception $e) {
return $this->error('非法请求');
}
$project = Project::select('title', 'version')->where(['project_id' => $this->param['project_id']])->first();
$list = GeoWritings::select(['title', 'status', 'uniqid', 'confirm_at'])->where(['project_id' => $project_id, 'is_del' => GeoWritings::IS_DEL_FALSE])->get();
$result = [
'project' => $project,
'list' => $list
];
return $this->success($result);
}
/**
* 获取详情
* @param Request $request
* @return false|string
*/
public function getWritingsDetail(Request $request)
{
$token = trim($request->input('token'));
$detail = GeoWritings::select(['title', 'content', 'status'])->where(['uniqid' => $token])->first();
return $this->success($detail);
}
/**
* 确认核心文章数据
* @param Request $request
* @return false|string
*/
public function confirmWritings(Request $request)
{
$request->validate([
'token' => 'required',
'title' => 'required|max:120',
'content' => 'required|max:5000'
], [
'token.required' => '非法请求',
'title.required' => '标题不能为空',
'title.max' => '最大长度不能超过120字符',
'content.required' => '内容不能为空',
'content.max' => '内容过长保存失败',
]);
$token = trim($request->input('token'));
$data = GeoWritings::where(['uniqid' => $token])->first();
if (empty($data)){
return $this->error('非法请求');
}
if ($data->status != GeoWritings::STATUS_RUNNING){
return $this->error('当前文章已确认,不可再次确认');
}
// FIXME 验证完成,保存数据,计算内容长度,处理内容中的资源, IP 确认时间 状态
return $data;
}
/**
* 获取确认数据
* @param Request $request
* @return false|string
*/
public function getConfirm(Request $request)
{
$token = trim($request->input('token'));
$data = GeoConfirm::where(['uniqid' => $token])->first();
if (empty($data)){
return $this->error('当前授权已失效');
}
$content = explode("\n", $data->content);
$confirm = explode("\n", $data->confirm);
$type = $data->type;
$status = $data->status;
$result = compact('content', 'confirm', 'type', 'status');
return $this->success($result);
}
/**
* 保存确认数据
* 验证当前确认数据状态, 不可重复确认
* @param Request $request
*/
public function saveConfirm(Request $request)
{}
}
... ...
<?php
/**
* @remark :
* @name :GeoConfirmController.php
* @author :lyh
* @method :post
* @time :2025/10/25 11:35
*/
namespace App\Http\Controllers\Aside\Geo;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Logic\Aside\Geo\GeoConfirmLogic;
use Illuminate\Http\Request;
/**
* @remark :用户确认信息表
* @name :GeoConfirmController
* @author :lyh
* @method :post
* @time :2025/10/25 11:37
*/
class GeoConfirmController extends BaseController
{
public function __construct(Request $request){
parent::__construct($request);
$this->logic = new GeoConfirmLogic();
}
/**
* 保存确认数据, 并推送微信群
* @param Request $request
* @throws \App\Exceptions\AsideGlobalException
*/
public function saveConfirmContent()
{
$this->request->validate([
'project_id' => 'required',
'type' => 'required|integer',
'content' => 'required',
'max_num' => 'required',
], [
'project_id.required' => '项目ID不能为空',
'type.required' => '确定数据类型不能为空',
'type.integer' => '确定数据类型不正确',
'content.required' => '确定数据不能为空',
'max_num.required' => '最大确认数量不能为空',
]);
$data = $this->logic->saveConfirmContent($this->param);
$this->response('success', Code::SUCCESS, $data);
}
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2025/10/23
* Time: 10:23
*/
namespace App\Http\Controllers\Aside\Geo;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Logic\Aside\Geo\GeoLogic;
use App\Models\Geo\GeoConf;
use App\Models\Geo\GeoConfirm;
use App\Models\Manage\ManageHr;
use App\Models\Project\KeywordPrefix;
use App\Models\Project\Project;
use App\Models\ProjectAssociation\ProjectAssociation;
use Illuminate\Http\Request;
/**
* Class GeoController
* @package App\Http\Controllers\Aside\Geo
*/
class GeoController extends BaseController
{
public function __construct(Request $request){
parent::__construct($request);
$this->logic = new GeoLogic();
}
/**
* 获取GEO相关配置
* @param Request $request
*/
public function getConfig()
{
$this->request->validate([
'project_id' => 'required',
], [
'project_id.required' => '项目ID不能为空',
]);
$data = $this->logic->getCongInfo($this->param['project_id']);
$this->response('success', Code::SUCCESS, $data);
}
/**
* 保存GEO配置
* TODO 单独保存GEO开启状态, 达标数量
* @param Request $request
* @throws \App\Exceptions\AsideGlobalException
*/
public function saveConfig()
{
$this->request->validate([
'project_id' => 'required',
'manager_id' => 'nullable|integer',
'company' => 'nullable|max:200',
'brand' => 'nullable|max:200',
'description' => 'nullable|max:500',
], [
'project_id.required' => '项目ID不能为空',
'manager_id.integer' => '管理员参数非法',
'company.max' => '公司名称不能超过200个字符',
'brand.max' => '品牌名不能超过200个字符',
'description.max' => '描述不能超过500个字符',
]);
$data = $this->logic->saveConfig($this->param);
$this->response('success', Code::SUCCESS, $data);
}
/**
* OA后台管理员,保存确认数据
* 客户可以进行确认, OA后台也可以进行确认,以及修改
* @param Request $request
*/
public function saveConfirmData(Request $request)
{
}
}
... ...
... ... @@ -11,7 +11,7 @@ namespace App\Http\Controllers\Aside\Geo;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Logic\Aside\Geo\GeoLogic;
use App\Http\Logic\Aside\Geo\GeoQuestionLogic;
use Illuminate\Http\Request;
/**
... ... @@ -26,7 +26,7 @@ class GeoQuestionController extends BaseController
public function __construct(Request $request)
{
parent::__construct($request);
$this->logic = new GeoLogic();
$this->logic = new GeoQuestionLogic();
}
/**
... ...
<?php
/**
* @remark :
* @name :GeoWritingTaskController.php
* @author :lyh
* @method :post
* @time :2025/10/25 10:40
*/
namespace App\Http\Controllers\Aside\Geo;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Logic\Aside\Geo\GeoWritingsTaskLogic;
use App\Http\Requests\Aside\Geo\GeoWritingsTaskRequest;
use Illuminate\Http\Request;
/**
* @remark :文章任务(收集数据)
* @name :GeoWritingTaskController
* @author :lyh
* @method :post
* @time :2025/10/25 10:40
*/
class GeoWritingTaskController extends BaseController
{
public function __construct(Request $request)
{
parent::__construct($request);
$this->logic = new GeoWritingsTaskLogic();
}
/**
* @remark :ai文章列表页
* @name :lists
* @author :lyh
* @method :post
* @time :2025/10/25 15:12
*/
public function lists(){
$data = $this->logic->listWritingTask($this->map,$this->page,$this->row,$this->order);
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :保存geoAi文章生成数据
* @name :lists
* @author :lyh
* @method :post
* @time :2025/10/25 10:41
*/
public function saveWritingTask(){
$request = new GeoWritingsTaskRequest();
$request->validated();
$data = $this->logic->saveWritingTask();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :批量删除文章任务
* @name :delWritingTask
* @author :lyh
* @method :post
* @time :2025/10/25 15:03
*/
public function delWritingTask(){
$this->request->validate([
'id'=>'required|array',
],[
'id.required' => 'ID不能为空',
'id.array' => 'ID为数组',
]);
$data = $this->logic->delWritingTask();
$this->response('success',Code::SUCCESS,$data);
}
}
... ...
<?php
/**
* @remark :
* @name :GeoWritingsController.php
* @author :lyh
* @method :post
* @time :2025/10/25 10:41
*/
namespace App\Http\Controllers\Aside\Geo;
use App\Http\Controllers\Aside\BaseController;
/**
* @remark :geo文章
* @name :GeoWritingsController
* @author :lyh
* @method :post
* @time :2025/10/25 10:41
*/
class GeoWritingsController extends BaseController
{
}
... ...
... ... @@ -160,7 +160,7 @@ class OptimizeController extends BaseController
$manageModel = new ManageHr();
$plan = Project::planMap();
$seo_plan = Project::seoMap();
$item['plan'] = $plan[$item['plan']] ?? $seo_plan[1];
$item['plan'] = $plan[$item['plan']] ?? ($seo_plan[$item['seo_plan']] ?? '');
$item['channel'] = Channel::getChannelText($item['channel']['user_id'] ?? 0);
$item['build_leader'] = $manageModel->getName($item['leader_mid']);
$item['build_manager'] = $manageModel->getName($item['manager_mid']);
... ...
... ... @@ -10,7 +10,6 @@ use App\Http\Logic\Aside\Project\OnlineCheckLogic;
use App\Http\Logic\Aside\Project\ProcessRecordsLogic;
use App\Http\Logic\Aside\Project\ProjectLogic;
use App\Http\Requests\Aside\Project\ProcessRecordsRequest;
use App\Models\Ai\AiVideo;
use App\Models\ASide\APublicModel;
use App\Models\Channel\Channel;
use App\Models\Channel\User;
... ... @@ -23,7 +22,10 @@ use App\Models\Devops\ServersIp;
use App\Models\Domain\DomainCreateTask;
use App\Models\Domain\DomainInfo;
use App\Models\Domain\DomainInfo as DomainInfoModel;
use App\Models\Geo\GeoArticle;
use App\Models\Geo\GeoConf;
use App\Models\Geo\GeoLink;
use App\Models\Geo\GeoQuestionResult;
use App\Models\HomeCount\Count;
use App\Models\Industry\ProjectIndustry;
use App\Models\Inquiry\InquirySet;
... ... @@ -31,7 +33,6 @@ use App\Models\Manage\BelongingGroup;
use App\Models\Manage\ManageHr;
use App\Models\Project\AiVideoTask;
use App\Models\Project\DeployBuild;
use App\Models\Project\DeployOptimize;
use App\Models\Project\Payment;
use App\Models\Project\ProcessRecords;
use App\Models\Project\Project;
... ... @@ -43,7 +44,7 @@ use App\Models\Task\Task;
use App\Models\WebSetting\WebLanguage;
use App\Models\WorkOrder\TicketProject;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
/**
* 项目管理
... ... @@ -225,7 +226,7 @@ class ProjectController extends BaseController
if($this->map['domain_type'] == 'domain'){
$parsedUrl = parse_url($this->map['domain_search']);
$this->map['domain_search'] = $parsedUrl['host'] ?? $this->map['domain_search'];
$ids = DomainInfo::where('domain', 'like', '%'.$this->map['domain_search'].'%')->pluck('id')->toArray();
$ids = DomainInfoModel::where('domain', 'like', '%'.$this->map['domain_search'].'%')->pluck('id')->toArray();
$query->whereIn('gl_project_deploy_optimize.domain', $ids);
}else{
$query->where('gl_project_deploy_build.test_domain','like','%'.$this->map['domain_search'].'%');
... ... @@ -319,8 +320,12 @@ class ProjectController extends BaseController
});
}
}
if(isset($this->map['seo_plan'])){
$query = $query->where('gl_project_deploy_build.seo_plan','!=',0);
if (isset($this->map['seo_plan'])) {
$query = $query->where('gl_project_deploy_build.seo_plan', '!=', 0)
->where(function ($subQuery) {
$subQuery->where('gl_project_deploy_build.plan', '=', 0)
->orWhere('gl_project_deploy_build.seo_plan', '!=', 9);
});
}
if(isset($this->map['site_status'])){
$query = $query->where('gl_project.site_status',$this->map['site_status']);
... ... @@ -444,32 +449,45 @@ class ProjectController extends BaseController
$item['type'] = $item['extend_type'];
}
$manageModel = new ManageHr();
$item['channel'] = Channel::getChannelText($item['channel']['user_id'] ?? 0);
$item['build_leader'] = $manageModel->getName($item['leader_mid']);
$item['build_manager'] = $manageModel->getName($item['manager_mid']);
$item['build_designer'] = $manageModel->getName($item['designer_mid']);
$item['build_tech'] = $manageModel->getName($item['tech_mid']);
$item['optimize_manager'] = $manageModel->getName($item['optimize_manager_mid']);
$item['optimize_optimist'] = $manageModel->getName($item['optimize_optimist_mid']);
$item['optimize_assist'] = $manageModel->getName($item['optimize_assist_mid']);
$item['optimize_tech'] = $manageModel->getName($item['optimize_tech_mid']);
$item['quality_mid_name'] = $manageModel->getName($item['quality_mid']);
//geo项目
if(($item['plan'] == 0) && ($item['seo_plan'] != 0)){
//geo项目负责人
$geoConfModel = new GeoConf();
$manage_id = $geoConfModel->getValue(['project_id'=>$item['id']],'manager_id');
$item['geo_manage_name'] = $manageModel->getName($manage_id);
$geoArticleModel = new GeoArticle();
$item['geo_article_num'] = $geoArticleModel->counts(['project_id'=>$item['id']]);//文章数量
$geoLinkModel = new GeoLink();
$item['geo_link_num'] = $geoLinkModel->counts(['project_id'=>$item['id']]);//权威新闻数量
$questionResModel = new GeoQuestionResult();
$item['geo_qualify_num'] = $questionResModel->counts(['project_id'=>$item['id'],'hit'=>['!=',0],'platform'=>['in',['openai', 'gemini','google_ai_overview']]]);//排名
}
$item['build_leader'] = $manageModel->getName($item['leader_mid']);
$item['build_manager'] = $manageModel->getName($item['manager_mid']);
$item['build_designer'] = $manageModel->getName($item['designer_mid']);
$item['build_tech'] = $manageModel->getName($item['tech_mid']);
$item['optimize_manager'] = $manageModel->getName($item['optimize_manager_mid']);
$item['optimize_optimist'] = $manageModel->getName($item['optimize_optimist_mid']);
$item['optimize_assist'] = $manageModel->getName($item['optimize_assist_mid']);
$item['optimize_tech'] = $manageModel->getName($item['optimize_tech_mid']);
$item['quality_mid_name'] = $manageModel->getName($item['quality_mid']);
$planMap = Project::planMap();
$seoPlanMap = Project::seoMap();
$item['plan'] = $planMap[$item['plan']] ?? '';
$item['seo_plan'] = $seoPlanMap[$item['seo_plan']] ?? '';
$item['created_at'] = date('Y年m月d日', strtotime($item['cooperate_date']));
$item['autologin_code'] = getAutoLoginCode($item['id']);
$domainModel = new DomainInfo();
$domainModel = new DomainInfoModel();
$item['domain'] = !empty($item['domain']) ? $domainModel->getDomain($item['domain']) : '';
$item['uuid'] = TicketProject::where('table_id', $item['id'])->where('project_cate', 2)->value('uuid') ?? null;
$item['friend_id'] = ProjectAssociation::where('project_id', $item['id'])->where('status', ProjectAssociation::STATUS_NORMAL)->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)->value('friend_id') ?? null;
$item['autologin_code'] = getAutoLoginCode($item['id']);
$item['created_at'] = date('Y年m月d日', strtotime($item['cooperate_date']));
$item['product_num'] = $data['product'] ?? 0;
$item['keyword_num'] = $data['key'] ?? 0;
$item['article_num'] = ($data['blog'] ?? 0) + ($data['news'] ?? 0);
$item['task_finish_num'] = Task::getNumByProjectId($item['id'], Task::STATUS_DOWN);
$item['task_pending_num'] = Task::getNumByProjectId($item['id'], [Task::STATUS_DONGING, Task::STATUS_WAIT]);
$item['collect_time'] = $item['is_upgrade'] ? UpdateLog::getProjectUpdate($item['id']) : '';
$item['uuid'] = TicketProject::where('table_id', $item['id'])->where('project_cate', 2)->value('uuid') ?? null;
$item['friend_id'] = ProjectAssociation::where('project_id', $item['id'])->where('status', ProjectAssociation::STATUS_NORMAL)->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)->value('friend_id') ?? null;
$item['channel'] = Channel::getChannelText($item['channel']['user_id'] ?? 0);
return $item;
}
... ... @@ -792,7 +810,6 @@ class ProjectController extends BaseController
$order_by_sort = $request->input('order_by_sort', 'desc');
$start_time = $this->param['start_time'] ?? '';
$end_time = $this->param['end_time'] ?? '';
if(!$source_id && !$id){
$this->response('参数异常',Code::SYSTEM_ERROR);
}
... ... @@ -886,7 +903,6 @@ class ProjectController extends BaseController
$param['yesterday_ip_count'] = $yesterday_count['ip_num'] ?? 0;
$param['today_ip_count'] = $today_count['ip_num'] ?? 0;
$param['inquiry_num'] = $today_count['inquiry_num'] ?? 0;
$list[] = $param;
}
$data['list'] = $list;
... ... @@ -1177,7 +1193,6 @@ class ProjectController extends BaseController
'id.required' => '项目id不能为空',
'site_status.required' => '状态不能为空',
]);
//获取项目数据
$projectModel = new Project();
$projectInfo = $projectModel->read(['id'=>$this->param['id']],['project_type','serve_id','site_status','site_token']);
... ... @@ -1187,14 +1202,12 @@ class ProjectController extends BaseController
if($projectInfo['site_status'] == $this->param['site_status']){
$this->response('success');
}
//获取服务器数据
$serverIpModel = new ServersIp();
$serversIpInfo = $serverIpModel->read(['id' => $projectInfo['serve_id']], ['servers_id']);
if(!$serversIpInfo){
$this->fail('获取项目所属服务器失败');
}
if($serversIpInfo['servers_id'] == ServerConfig::SELF_SITE_ID){
//自建站项目
if($this->param['site_status'] == 1){
... ... @@ -1204,7 +1217,6 @@ class ProjectController extends BaseController
//开启站点
$site_token = str_replace('_expired','',$projectInfo['site_token']);
}
$projectModel->edit(['site_status'=>$this->param['site_status'],'site_token'=>$site_token],['id'=>$this->param['id']]);
}else{
//普通项目
... ... @@ -1214,7 +1226,6 @@ class ProjectController extends BaseController
if(!$domainInfo){
$this->fail('获取域名数据失败');
}
if($this->param['site_status'] == 1){
//关闭站点:通知C端
$re = curl_get('https://'.$domainInfo['domain'].'/api/stop_or_start_website');
... ... @@ -1255,10 +1266,8 @@ class ProjectController extends BaseController
}
}
}
$projectModel->edit(['site_status'=>$this->param['site_status']],['id'=>$this->param['id']]);
}
$this->response('success');
}
... ... @@ -1328,4 +1337,27 @@ class ProjectController extends BaseController
$data['videoFrequency'] =$videoModel->videoFrequency();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :获取广告先投特批
* @name :getSpAdsLists
* @author :lyh
* @method :post
* @time :2025/10/22 16:59
*/
public function getSpAdsLists()
{
$params = $this->param;
$url = 'https://oa.cmer.com/api/sp_ads_lists';
// 发送 GET 请求(附带 token)
$params['token'] = md5('qqs' . date('Y-m-d'));
$response = Http::get($url, $params);
// 判断请求是否成功
if ($response->successful()) {
$data = $response->json(); // 自动解析 JSON
$this->response('success', Code::SUCCESS, $data);
} else {
$this->fail('拉取结果失败,请联系管理员');
}
}
}
... ...
... ... @@ -21,6 +21,9 @@ class AiBlogController extends BaseController
* @time :2025/2/14 13:59
*/
public function getAiBlog(AiBlog $aiBlog){
if(isset($this->map['new_title']) && !empty($this->map['new_title'])){
$this->map['new_title'] = ['like', '%'.$this->map['new_title'].'%'];
}
$lists = $aiBlog->lists($this->map,$this->page,$this->row,'id',['id','keyword','new_title','route','image','task_id','status','created_at','updated_at']);
if(!empty($lists) && !empty($lists['list'])){
foreach ($lists['list'] as $k => $v){
... ...
... ... @@ -389,14 +389,17 @@ class FileController
*/
public function getFileList(){
if(isset($this->cache['project_id']) && !empty($this->cache['project_id'])){
$this->map['project_id'] = $this->cache['project_id'];
$this->param['project_id'] = $this->cache['project_id'];
}
$fileModel = new File();
$this->map['type'] = 'mp4';
$lists = $fileModel->list($this->map,'id',['id','hash','type','path','created_at','name']);
foreach ($lists as $k => $v){
$this->param['type'] = 'mp4';
$page = $this->param['page'] ?? 1;
$row = $this->param['row'] ?? 20;
unset($this->param['page'],$this->param['row']);
$lists = $fileModel->lists($this->param,$page,$row,'id',['id','hash','type','path','created_at','name']);
foreach ($lists['list'] as $k => $v){
$v['file_link'] = getFileUrl($v['path'],$this->cache['storage_type'] ?? 0,$this->cache['project_location'] ?? 0,$this->cache['file_cdn'] ?? 0);
$lists[$k] = $v;
$lists['list'][$k] = $v;
}
$this->response('success',Code::SUCCESS,$lists);
}
... ...
<?php
/**
* @remark :
* @name :GeoConfirmLogic.php
* @author :lyh
* @method :post
* @time :2025/10/25 11:36
*/
namespace App\Http\Logic\Aside\Geo;
use App\Enums\Common\Code;
use App\Http\Logic\Aside\BaseLogic;
use App\Models\Geo\GeoConfirm;
use App\Models\ProjectAssociation\ProjectAssociation;
/**
* @remark :用户确认信息
* @name :GeoConfirmLogic
* @author :lyh
* @method :post
* @time :2025/10/25 11:37
*/
class GeoConfirmLogic extends BaseLogic
{
public function __construct()
{
parent::__construct();
$this->param = $this->requestAll;
$this->model = new GeoConfirm();
}
/**
* @remark :保存数据->并推送微信群客户确认
* @name :saveConfirmContent
* @author :lyh
* @method :post
* @time :2025/10/25 11:41
*/
public function saveConfirmContent($param)
{
try {
$info = $this->model->read(['project_id' => $param['project_id']]);
if($info === false){
$id = $this->model->addReturnId($param);
}else{
$id = $param['id'];
$this->model->edit($param,['id'=>$info['id']]);
}
$friend = ProjectAssociation::where(['project_id' => $param['project_id']])->first();
if (empty($friend)){
$this->fail('项目未绑定微信群, 推送消息失败!');
}
GeoConfirm::sendConfirmMessage($id, $friend->friend_id);
} catch (\Exception $e) {
$this->fail('操作失败, error:' . $e->getMessage());
}
return $this->success(['id'=>$id]);
}
}
... ...
... ... @@ -4,109 +4,99 @@
* @name :GeoLogic.php
* @author :lyh
* @method :post
* @time :2025/7/2 17:51
* @time :2025/10/25 11:08
*/
namespace App\Http\Logic\Aside\Geo;
use App\Enums\Common\Code;
use App\Http\Logic\Aside\BaseLogic;
use App\Models\Geo\GeoPlatform;
use App\Models\Geo\GeoConf;
use App\Models\Geo\GeoQuestion;
use App\Models\Manage\ManageHr;
use App\Models\Project\KeywordPrefix;
use App\Models\Project\Project;
/**
* @remark :geo设置
* @name :GeoLogic
* @author :lyh
* @method :post
* @time :2025/10/25 11:08
*/
class GeoLogic extends BaseLogic
{
public function __construct()
{
parent::__construct();
$this->param = $this->requestAll;
$this->model = new GeoQuestion();
$this->model = new GeoConf();
}
/**
* @remark :设置geo状态
* @name :setGeoStatus
* @remark :获取geo设置数据详情
* @name :getCongInfo
* @author :lyh
* @method :post
* @time :2025/7/2 17:51
* @time :2025/10/25 11:10
*/
public function setGeoStatus(){
public function getCongInfo($project_id)
{
$projectModel = new Project();
$data = $projectModel->edit(['geo_status'=>$this->param['geo_status'],'geo_frequency'=>$this->param['geo_frequency']],['id'=>$this->param['project_id']]);
$questionModel = new GeoQuestion();
$questionModel->edit(['status'=>$this->param['geo_status']],['project_id'=>$this->param['project_id']]);
return $this->success($data);
}
/**
* @remark :获取类型
* @name :getType
* @author :lyh
* @method :post
* @time :2025/7/3 10:47
*/
public function getType(){
$data['type'] = $this->model->brandType();
$data['frequency'] = $this->model->frequency;
$geoPlatformModel = new GeoPlatform();
$data['platform'] = $geoPlatformModel->getList();
return $this->success($data);
}
/**
* @remark :获取问题列表
* @name :getGeoQuestionList
* @author :lyh
* @method :post
* @time :2025/7/3 9:12
*/
public function getGeoQuestionList($map,$page,$row,$order,$field = ['*']){
$data = $this->model->lists($map,$page,$row,$order,$field);
if(!empty($data) && !empty($data['list'])){
foreach ($data['list'] as $key => $item){
$item['type_name'] = $this->model->brandType()[$item['type']];
$data['list'][$key] = $item;
}
$project_geo_conf = $projectModel->read(['id' => $project_id],['title', 'version', 'geo_status', 'geo_qualify_num']);
$geoConfModel = new GeoConf();
$geo_conf = $geoConfModel->read(['project_id' => $project_id]);
if($geo_conf === false){//数据未初始化
$geo_conf = [
'project_id' => $project_id, 'manager_id'=>0, 'company'=>$project_geo_conf['title'], 'brand'=>'', 'description'=>''
];
}
return $this->success($data);
//负责人集合
$geo_manage_list = $geoConfModel->geoManage();
// geo配置管理员,已经移除管理员列表,补充管理员信息
if ($geo_conf && $geo_conf['manager_id'] && empty($geo_manage_list[$geo_conf['manager_id']])) {
$manage = ManageHr::where(['id' => $geo_conf['manager_id']])->pluck('name', 'id')->toArray();
$geo_manage_list = array_merge($geo_manage_list, $manage);
}
$result = [
'project_geo_conf' => $project_geo_conf,
'geo_conf' => $geo_conf,
'geo_manage_list' => $geo_manage_list,
'geo_keyword' => [
'prefix' => KeywordPrefix::getKeyword($project_id, KeywordPrefix::TYPE_GEO_PREFIX),
'suffix' => KeywordPrefix::getKeyword($project_id, KeywordPrefix::TYPE_GEO_SUFFIX),
],
];
return $this->success($result);
}
/**
* @remark :保存数据
* @name :saveGeoQuestion
* @remark :保存数据详情
* @name :saveCongInfo
* @author :lyh
* @method :post
* @time :2025/7/3 9:47
* @param : question->提交的问题
* @param : url->提交的网址
* @param : keywords->提交的关键字
* @param : project_id->项目id
* @time :2025/10/25 11:14
* @param->gl_geo_conf : project_id->项目id;manager_id->管理员id;company->公司名称;brand->品牌名;description->描述(必传)
* @param :prefix->前缀;suffix->后缀(非必传)
* @param->gl_project : geo_status->开启/关闭状态 geo_qualify_num->达标数量
*/
public function saveGeoQuestion(){
//处理数据
$this->param['question'] = json_encode($this->param['question'] ?? [],true);
$this->param['url'] = json_encode($this->param['url'] ?? [],true);
$this->param['keywords'] = json_encode($this->param['keywords'] ?? [],true);
//执行时间设置为今天
if(isset($this->param['id']) && !empty($this->param['id'])){
$id = $this->param['id'];
$this->model->edit($this->param,['id'=>$id]);
}else{
$this->param['next_time'] = date('Y-m-d');
$id = $this->model->addReturnId($this->param);
public function saveConfig($param)
{
$projectModel = new Project();
$projectModel->edit(['geo_status'=>$param['geo_status'],'geo_qualify_num'=>$param['geo_qualify_num']],['id'=>$param['project_id']]);
try {
unset($param['geo_status'],$param['geo_qualify_num']);//无需保存
$info = $this->model->read(['project_id' => $param['project_id']]);
if($info === false){
$id = $this->model->addReturnId($param);
}else{
$id = $param['id'];
$this->model->edit($param,['id'=>$info['id']]);
}
} catch (\Exception $e) {
$this->fail('配置保存失败, error:' . $e->getMessage());
}
return $this->success(['id'=>$id]);
}
/**
* @remark :删除数据
* @name :delGeoQuestion
* @author :lyh
* @method :post
* @time :2025/7/3 10:13
*/
public function delGeoQuestion(){
$data = $this->model->del(['id'=>['in',$this->param['ids']]]);
return $this->success($data);
}
}
... ...
<?php
/**
* @remark :
* @name :GeoQuestionLogic.php
* @author :lyh
* @method :post
* @time :2025/7/2 17:51
*/
namespace App\Http\Logic\Aside\Geo;
use App\Http\Logic\Aside\BaseLogic;
use App\Models\Geo\GeoPlatform;
use App\Models\Geo\GeoQuestion;
use App\Models\Project\Project;
class GeoQuestionLogic extends BaseLogic
{
public function __construct()
{
parent::__construct();
$this->param = $this->requestAll;
$this->model = new GeoQuestion();
}
/**
* @remark :设置geo状态
* @name :setGeoStatus
* @author :lyh
* @method :post
* @time :2025/7/2 17:51
*/
public function setGeoStatus(){
$projectModel = new Project();
$data = $projectModel->edit(['geo_status'=>$this->param['geo_status'],'geo_frequency'=>$this->param['geo_frequency']],['id'=>$this->param['project_id']]);
$questionModel = new GeoQuestion();
$questionModel->edit(['status'=>$this->param['geo_status']],['project_id'=>$this->param['project_id']]);
return $this->success($data);
}
/**
* @remark :获取类型
* @name :getType
* @author :lyh
* @method :post
* @time :2025/7/3 10:47
*/
public function getType(){
$data['type'] = $this->model->brandType();
$data['frequency'] = $this->model->frequency;
$geoPlatformModel = new GeoPlatform();
$data['platform'] = $geoPlatformModel->getList();
return $this->success($data);
}
/**
* @remark :获取问题列表
* @name :getGeoQuestionList
* @author :lyh
* @method :post
* @time :2025/7/3 9:12
*/
public function getGeoQuestionList($map,$page,$row,$order,$field = ['*']){
$data = $this->model->lists($map,$page,$row,$order,$field);
if(!empty($data) && !empty($data['list'])){
foreach ($data['list'] as $key => $item){
$item['type_name'] = $this->model->brandType()[$item['type']];
$data['list'][$key] = $item;
}
}
return $this->success($data);
}
/**
* @remark :保存数据
* @name :saveGeoQuestion
* @author :lyh
* @method :post
* @time :2025/7/3 9:47
* @param : question->提交的问题
* @param : url->提交的网址
* @param : keywords->提交的关键字
* @param : project_id->项目id
*/
public function saveGeoQuestion(){
//处理数据
$this->param['question'] = json_encode($this->param['question'] ?? [],true);
$this->param['url'] = json_encode($this->param['url'] ?? [],true);
$this->param['keywords'] = json_encode($this->param['keywords'] ?? [],true);
//执行时间设置为今天
if(isset($this->param['id']) && !empty($this->param['id'])){
$id = $this->param['id'];
$this->model->edit($this->param,['id'=>$id]);
}else{
$this->param['next_time'] = date('Y-m-d');
$id = $this->model->addReturnId($this->param);
}
return $this->success(['id'=>$id]);
}
/**
* @remark :删除数据
* @name :delGeoQuestion
* @author :lyh
* @method :post
* @time :2025/7/3 10:13
*/
public function delGeoQuestion(){
$data = $this->model->del(['id'=>['in',$this->param['ids']]]);
return $this->success($data);
}
}
... ...
<?php
/**
* @remark :
* @name :GeoWritingsLogic.php
* @author :lyh
* @method :post
* @time :2025/10/25 10:43
*/
namespace App\Http\Logic\Aside\Geo;
use App\Http\Logic\Aside\BaseLogic;
use App\Models\Geo\GeoWritings;
/**
* @remark :文章任务
* @name :GeoWritingsLogic
* @author :lyh
* @method :post
* @time :2025/10/25 10:44
*/
class GeoWritingsLogic extends BaseLogic
{
public function __construct()
{
parent::__construct();
$this->param = $this->requestAll;
$this->model = new GeoWritings();
}
}
... ...
<?php
/**
* @remark :
* @name :GeoWritingsTaskLogic.php
* @author :lyh
* @method :post
* @time :2025/10/25 10:45
*/
namespace App\Http\Logic\Aside\Geo;
use App\Http\Logic\Aside\BaseLogic;
use App\Models\Geo\GeoWritings;
use App\Models\Geo\GeoWritingsTask;
class GeoWritingsTaskLogic extends BaseLogic
{
public function __construct()
{
parent::__construct();
$this->param = $this->requestAll;
$this->model = new GeoWritingsTask();
}
/**
* @remark :
* @name :listWritingTask
* @author :lyh
* @method :post
* @time :2025/10/25 15:13
*/
public function listWritingTask($map,$page,$row,$order){
$data = $this->model->lists($map,$page,$row,$order);
return $this->success($data);
}
/**
* @remark :保存AI文章数据
* @name :saveWritingTask
* @author :lyh
* @method :post
* @time :2025/10/25 14:41
* @param :project_id->项目ID;company->公司名称;brand->品牌词;keyword->关键词;prefix->前缀;suffix->后缀;event_title->事件标题;
* event_content->事件内容;title->标题;description->描述;footer->结尾引用;img->图片;ai_model->ai_model
*/
public function saveWritingTask(){
try {
if(isset($this->param['id']) &&!empty($this->param['id'])){
$id = $this->param['id'];
$this->model->edit($this->param,['id'=>$id]);
}else{
$id = $this->model->addReturnId($this->param);
}
}catch (\Exception $e){
$this->fail('保存数据失败,请联系管理员'.$e->getMessage());
}
return $this->success(['id'=>$id]);
}
/**
* @remark :删除数据
* @name :delWritingTask
* @author :lyh
* @method :post
* @time :2025/10/25 15:05
*/
public function delWritingTask()
{
$res = $this->model->del(['id'=>['in',$this->param['id']]]);
if($res === false){
$this->fail('删除失败,请联系管理员');
}
return $this->success();
}
}
... ...
... ... @@ -123,10 +123,6 @@ class ProjectLogic extends BaseLogic
$info['collect_test_domain'] = $info['is_upgrade'] ? CollectLog::getCollectTestDomain($id) : '';
//获取项目所属行业
$info['deploy_optimize']['industry'] = ProjectIndustryRelated::where('project_id', $id)->pluck('industry_id')->toArray();
$questionModel = new GeoQuestionResult();
$info['question_qualify_count'] = $questionModel->where('project_id', $id)
->where('hit','!=',0)->whereIn('platform',['openai', 'gemini','google_ai_overview'])
->count();
return $this->success($info);
}
... ...
... ... @@ -34,9 +34,20 @@ class GeoQuestionResLogic extends BaseLogic
* @time :2025/7/8 17:16
*/
public function getCount(){
$total = $this->model->counts(['project_id'=>$this->user['project_id']]);
$type_1 = $this->model->counts(['type'=>$this->model::BRAND_TYPE,'project_id'=>$this->user['project_id']]);
$type_2 = $this->model->counts(['type'=>$this->model::MARKETING_TYPE,'project_id'=>$this->user['project_id']]);
$total = $this->model
->where('project_id', $this->user['project_id'])
->distinct('question')
->count('question');
$type_1 = $this->model
->where('type', $this->model::BRAND_TYPE)
->where('project_id', $this->user['project_id'])
->distinct('question')
->count('question');
$type_2 = $this->model
->where('type', $this->model::MARKETING_TYPE)
->where('project_id', $this->user['project_id'])
->distinct('question')
->count('question');
return $this->success(['total'=>$total,'type_1'=>$type_1,'type_2'=>$type_2]);
}
... ...
... ... @@ -5,6 +5,8 @@ namespace App\Http\Logic\Bside\HomeCount;
use App\Helper\FormGlobalsoApi;
use App\Http\Logic\Bside\BaseLogic;
use App\Models\Geo\GeoCount;
use App\Models\RankData\RankDataBmseo;
use App\Models\Template\BCustomTemplate;
use App\Models\Visit\Visit;
use App\Models\Visit\VisitItem;
... ... @@ -101,8 +103,13 @@ class CountLogic extends BaseLogic
* @time :2023/5/24 14:03
*/
public function keyword_data_count(){
$version = $this->project['version'] ?? '';
$yesterday = date('Y-m-d');
$rankDataModel = new RankDataModel();
if($this->user['project_seo_type'] == 1){
$rankDataModel = new RankDataBmseo();
}else{
$rankDataModel = new RankDataModel();
}
$param = [
'updated_date' => $yesterday,
'project_id' => $this->user['project_id']
... ... @@ -117,6 +124,13 @@ class CountLogic extends BaseLogic
$data = [];
}
}
if(!empty($version) && ($version == '7.5')){
$geoCountModel = new GeoCount();
$geoInfo = $geoCountModel->where('project_id',$this->user['project_id'])->where('date', '<=', date('Y-m-d'))->orderBy('date', 'desc')->first();
$geoInfo = $geoInfo ? $geoInfo->toArray() : [];
$data['geo_qualify_total'] = $geoInfo['qualify_total'] ?? 0;
$data['total_count'] = ($this->project['deploy_build']['keyword_num'] ?? 0) + ($this->project['geo_qualify_num'] ?? 0);
}
return $this->success($data);
}
... ...
... ... @@ -93,7 +93,7 @@ class ProductLogic extends BaseLogic
$route = $param['route'];
$is_upgrade = $param['is_upgrade'] ?? 0;//1:5.0数据 0:6.0
$six_read = $param['six_read'] ?? 0;//是否按6.0显示
if($six_read == 0 && $is_upgrade == 1){
if($six_read == 1 && $is_upgrade == 1){
unset($param['route']);
}else{
$param['route'] = RouteMap::setRoute($param['route'], RouteMap::SOURCE_PRODUCT, $this->param['id'], $this->user['project_id']);
... ...
... ... @@ -35,6 +35,9 @@ class WebSettingReceivingLogic extends BaseLogic
*/
public function setting_receiving_save(){
$data = [];
if(!isset($this->param['data']) || empty($this->param['data'])){
$this->fail('参数错误,请联系管理员');
}
foreach ($this->param['data'] as $v){
if($v['type'] == 1){
// 使用正则表达式匹配中国大陆手机号
... ...
... ... @@ -286,6 +286,7 @@ class UserLoginLogic
$info['is_watermark'] = $project['is_watermark'];
$info['configuration'] = $project['deploy_build']['configuration'];
$info['project_type'] = $project['type'];
$info['version'] = $project['version'] ?? 6;
$info['project_seo_type'] = $project['project_type'];
$info['storage_type'] = $project['storage_type'];
$info['open_export_product'] = $project['open_export_product'];
... ...
... ... @@ -55,7 +55,7 @@ class LoginAuthMiddleware
//操作权限设置
$projectRoleModel = new ProjectRoleModel();
$role_info = $projectRoleModel->read(['id'=>$info['role_id']]);
if($role_info['status'] != 0){
if($role_info === false || $role_info['status'] != 0){
return response(['code'=>Code::USER_LOGIN_ERROE,'message'=>'当前用户角色被禁用']);
}
return $role_info;
... ...
<?php
/**
* @remark :
* @name :GeoWritingsTaskRequest.php
* @author :lyh
* @method :post
* @time :2025/10/25 14:21
*/
namespace App\Http\Requests\Aside\Geo;
use Illuminate\Foundation\Http\FormRequest;
class GeoWritingsTaskRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'project_id' => 'required',
'company' => 'required|string',
'brand' => 'required|string',
'keyword' => 'required|string',
'prefix' => 'required|string',
'suffix' => 'required|string',
'event_title' => 'required|string',
'event_content' => 'required|string',
'title' => 'required|string',
'description' => 'required|string',
'footer' => 'required|string',
'img' => 'required|string',
'ai_model' => 'required|string',
];
}
}
... ...
... ... @@ -26,7 +26,7 @@ class NewsRequest extends FormRequest
return [
'name'=>'required|max:200',
'url'=>'required',
// 'seo_title' => 'max:70',
'seo_title' => 'max:70',
'seo_keywords' => 'max:300',
'seo_description' => 'max:200',
];
... ... @@ -38,7 +38,7 @@ class NewsRequest extends FormRequest
'name.required'=>'请填写名称',
'name.max'=>'名称超过最长长度200',
'url.required'=>'链接不能为空',
// 'seo_title.max' => 'SEO标题不能超过70个字符',
'seo_title.max' => 'SEO标题不能超过70个字符',
'seo_keywords.max' => 'SEO关键词不能超过300个字符',
'seo_description.max' => 'SEO描述不能超过200个字符',
];
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2025/10/22
* Time: 17:01
*/
namespace App\Models\Geo;
use App\Models\Base;
use App\Models\Manage\ManageHr;
use Illuminate\Support\Facades\Cache;
/**
* GEO 相关配置
* Class GeoConf
* @package App\Models\Geo
*/
class GeoConf extends Base
{
/**
* @var string table
*/
protected $table = 'gl_project_geo_conf';
/**
* GEO 负责人集合
* TODO 负责人:优化师 + 陶婵 + 艾媛媛
* @return array
*/
public function geoManage()
{
$key = 'geo_manage_list_' . date('Ymd');
$optimize = Cache::get($key);
if (empty($optimize)) {
$optimize = ManageHr::where(['status' => ManageHr::STATUS_ONE, 'entry_position' => 46])->pluck('name', 'id')->toArray();
$optimize[1] = '陶婵';
$optimize[875] = '艾媛媛';
ksort($optimize);
Cache::put($key, $optimize, 3600);
}
return $optimize;
}
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2025/10/22
* Time: 17:03
*/
namespace App\Models\Geo;
use App\Models\Base;
use App\Models\Workchat\MessagePush;
/**
* GEO 客户确认相关数据
* Class GeoConfirm
* @package App\Models\Geo
*/
class GeoConfirm extends Base
{
/**
* @var string table
*/
protected $table = 'gl_project_geo_confirm';
/**
* 客户确认类型
*/
const TYPE_TITLE = 1;
const TYPE_KEYWORD = 2;
/**
* 数据状态
*/
const STATUS_INIT = 1; # 初始化数据,仅保存完成
const STATUS_RUNNING = 2; # 已推送客户,等待客户确认
const STATUS_FINISH = 3; # 客户已确认完成
/**
* 客户确认数据类型
* @return array
*/
public static function typeMapping()
{
return [
self::TYPE_TITLE => '确认标题',
self::TYPE_KEYWORD => '确认关键词'
];
}
/**
* 客户确认数据状态
* @return array
*/
public static function statusMapping()
{
return [
self::STATUS_INIT => '初始数据',
self::STATUS_RUNNING => '数据确认中',
self::STATUS_FINISH => '客户已确认'
];
}
/**
* 保存确认数据
* @param $project_id
* @param $type
* @param $confirm
* @param $confirm_num
* @param $confirm_ip
* @return bool
*/
public static function saveConfirm($project_id, $type, $confirm, $confirm_num, $confirm_ip)
{
$data = self::where(compact('project_id', 'type'))->first();
if (empty($data))
return false;
$data->confirm = $confirm;
$data->confirm_ip = $confirm_ip;
$data->confirm_num = $confirm_num;
$data->confirm_at = now();
$data->status = self::STATUS_FINISH;
$data->save();
return $data;
}
/**
* 推送确认消息
* @param $id
* @return bool
*/
public static function sendConfirmMessage($id, $friend_id)
{
$data = self::where(compact('id'))->first();
$project_id = $data->project_id;
$content_type = 'Link';
$send_time = now();
$type = MessagePush::TYPE_GEO_CONFIRM;
$token = uniqid().$friend_id;
$created_at = $updated_at = now();
$content_array = [
'title' => self::typeMapping()[$data->type],
'desc' => self::typeMapping()[$data->type],
'size' => 0,
'thumbSize' => 0,
'thumbUrl' => 'https://hub.globalso.com/logocm.png',
'url' => 'https://oa.quanqiusou.cn/public-geo-confirm?token=' . $token
];
$content = json_encode($content_array, JSON_UNESCAPED_UNICODE);
MessagePush::insert(compact('project_id', 'friend_id', 'type', 'content_type', 'content', 'send_time', 'updated_at', 'created_at'));
// 消息推送, 更新数据
$data->confirm = '';
$data->send_at = now();
$data->uniqid = $token;
$data->status = self::STATUS_RUNNING;
$data->save();
return $data;
}
}
... ...
... ... @@ -35,7 +35,7 @@ class GeoPlatform extends Base
public function getList(){
// $data = Cache::get('geo_platform');
// if(empty($data)){
$data = $this->list(['status'=>$this::STATUS_ON],'id',['name','en_name','icon','sort'],'asc');
$data = $this->list(['status'=>$this::STATUS_ON],'sort',['name','en_name','icon','sort'],'desc');
Cache::put('geo_platform',$data,'12 * 3600');
// }
return $data;
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2025/10/22
* Time: 17:25
*/
namespace App\Models\Geo;
use App\Models\Base;
use App\Models\ProjectAssociation\ProjectAssociation;
use App\Models\Workchat\MessagePush;
use Illuminate\Support\Facades\Crypt;
/**
* GEO 文章相关
* Class GeoWritings
* @package App\Models\Geo
*/
class GeoWritings extends Base
{
/**
* @var string $table
*/
protected $table = 'gl_project_geo_writings';
/**
* 文章来源类型
*/
const TYPE_AI_CREATE = 1;
const TYPE_SUBMIT = 2;
/**
* 文章状态类型
*/
const STATUS_INIT = 1; # 文章仅保存完成,未推送给客户
const STATUS_RUNNING = 2; # 已推送客户,等待客户确认
const STATUS_FINISH = 3; # 客户已确认完成
const STATUS_AI_WAIT = 7; # 任务提交成功,等待AI生成
const STATUS_AI_RUNNING = 8; # AI生成中
const IS_DEL_FALSE = 0;
const IS_DEL_TRUE = 1;
/**
* @return array
*/
public static function typeMapping()
{
return [
self::TYPE_AI_CREATE => 'AI生成文章',
self::TYPE_SUBMIT => '上传已有文章'
];
}
/**
* 状态隐私
* @return array
*/
public static function statusMapping()
{
return [
self::STATUS_INIT => '已就绪,待推送',
self::STATUS_RUNNING => '已推送,待确认',
self::STATUS_FINISH => '已确认',
self::STATUS_AI_WAIT => '等待生成',
self::STATUS_AI_RUNNING => '生成中',
];
}
/**
* 推送确认消息
* TODO 通过项目ID,时间生成推送token,页面打开后, 回传token解码确定展示项目数据
* @param $project_id
* @return bool
* @throws \Exception
*/
public static function sendConfirmMessage($project_id)
{
$friend = ProjectAssociation::where(['project_id' => $project_id])->first();
if (empty($friend)) {
throw new \Exception('项目未绑定微信群');
}
$content_type = 'Link';
$send_time = now();
$type = MessagePush::TYPE_GEO_CONFIRM;
$friend_id = $friend->friend_id;
$created_at = $updated_at = now();
$param = [
'project_id' => $project_id,
'send_at' => time()
];
$token = Crypt::encrypt($param);
$content_array = [
'title' => "确认核心文章",
'desc' => '确认核心文章',
'size' => 0,
'thumbSize' => 0,
'thumbUrl' => 'https://hub.globalso.com/logocm.png',
'url' => 'https://oa.quanqiusou.cn/public-geo-article-list?token=' . $token
];
$content = json_encode($content_array, JSON_UNESCAPED_UNICODE);
MessagePush::insert(compact('project_id', 'friend_id', 'type', 'content_type', 'content', 'send_time', 'updated_at', 'created_at'));
return true;
}
}
... ...
<?php
/**
* @remark :
* @name :GeoWritingsTask.php
* @author :lyh
* @method :post
* @time :2025/10/25 10:47
*/
namespace App\Models\Geo;
use App\Models\Base;
/**
* @remark :文章生成任务
* @name :GeoWritingsTask
* @author :lyh
* @method :post
* @time :2025/10/25 10:48
*/
class GeoWritingsTask extends Base
{
protected $table = 'gl_project_geo_writings_task';
}
... ...
... ... @@ -5,10 +5,10 @@ namespace App\Models\Inquiry;
use App\Helper\Arr;
use App\Helper\FormGlobalsoApi;
use App\Models\Base;
use App\Utils\LogUtils;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
/**
* Class InquiryFormData
... ... @@ -42,7 +42,7 @@ class InquiryFormData extends Base
* @author zbj
* @date 2023/12/4
*/
public static function saveData($form_id, $domain, $ip, $country, $referer, $user_agent, $submit_at, $data, $traffic = 0){
public static function saveData($form_id, $domain, $ip, $country, $referer, $user_agent, $submit_at, $data, $project_id, $traffic = 0){
if(!empty($data['email'])){
$data['email'] = str_replace(' ', '', $data['email']);
}
... ... @@ -96,6 +96,14 @@ class InquiryFormData extends Base
}
}
$res = (new FormGlobalsoApi())->submitInquiry($ip, $referer, $submit_at, $data, $traffic);
//自定义推送
if($project_id == 3870) { //走这里发送的,关杰那边也要取消发送邮件
list($mobile, $email) = self::SpecialMailPhone($referer);
Log::channel('inquiry')->info('自定义推送', [$mobile, $email]);
$body = self::specialInquiryTemplate($data['name'], $data['email'], $data['phone'] ?? "", $data['message'], $domain, $ip, $country);
(new FormGlobalsoApi())->customizeInquiryEmail($email, '询盘客户<' . $data['name'] . '>', $body, $ip, $referer, $submit_at);
(new FormGlobalsoApi())->customizeInquirySms($mobile, $country, $domain);
}
Log::channel('inquiry')->info('询盘发送邮件', [$data, $res]);
if(!$res){
throw new \Exception('询盘发送邮件失败');
... ... @@ -118,6 +126,123 @@ class InquiryFormData extends Base
}
/**
* 获取推送邮箱和手机号
* 邮件地址和手机号 根据链接匹配,没匹配到随机取一个邮件和手机号
* @param $referer
* @author zbj
* @date 2025/10/25
*/
public static function SpecialMailPhone($referer)
{
$mail_mobile_map = [
'luchfitness' => [15262817624, 'Sales02@luchfitness.com'],
'modernsporting' => [13073258707,'sales@modernsporting.com'],
'liveupsports' => [18355892630,'sales@liveupsports.com'],
'topflor' => [13912296731,'alan@topflor.cn'],
];
$url_map = [
'luchfitness' => [
'/supplier/nantong-luch-fitness-co-ltd',
'/benches-machines-luch-fitness',
'/free-weight-luch-fitness',
'/functional-training-luch-fitness/s',
'/studio-exercise-luch-fitness',
'/commercial-gym-half-power-rack-for-strength-training-product',
'/heavy-duty-fitness-flat-bench-for-home-commercial-gym-product',
'/durable-gym-dumbbell-flat-bench-for-weight-training-product',
'/rubber-colored-bumper-plate-for-olympic-weightlifting-product',
'/urethane-competition-bumper-plate-for-strength-training-product',
'/10-pair-dumbbell-storage-rack-for-commercial-gyms-product',
'/tpu-coated-dumbbells-for-home-and-commercial-fitness-product',
'/urethane-weight-plate-for-powerlifting-strength-gym-product',
'/iphifun-cpu-weight-plate-for-professional-training-product',
'/men-s-weightlifting-barbell-bar-for-strength-training-product',
'/women-s-weightlifting-barbell-bar-for-olympic-lifts-product',
'/steel-competition-kettlebell-for-functional-training-product',
'/adjustable-strength-jump-plyo-boxes-for-gym-training-product',
'/commercial-soft-plyo-box-for-functional-gym-training-product',
'/power-training-weight-bull-bag-for-strength-workout-product',
'/pu-leather-power-bag-for-functional-strength-training-product',
'/abdominal-muscle-mat-for-core-and-sit-up-training-product',
'/gymnastics-training-mat-for-fitness-and-yoga-workout-product',
'/adjustable-bench-stepper-for-aerobic-and-strength-gym-product',
'/rubber-body-pump-set-for-cardio-and-strength-training-product',
'/balance-training-yoga-ball-for-core-stability-workout-product',
'/pull-up-resistance-band-for-strength-and-stretching-product',
],
'modernsporting' => [
'/supplier/nantong-modern-sporting-industrial-co-ltd',
'/barbell-bars-modern-sporting',
'/storage-racks-modern-sporting',
'/dumbbells-modern-sporting',
'/rubber-plates-modern-sporting',
'/training-handles-modern-sporting',
'/md4137-201cm-weightlifting-beginner-bar-product',
'/md4143-220cm-hybrid-training-bar-product',
'/md4149-powder-coated-pentagon-bar-product',
'/gym-home-10-pairs-dumbbell-rack-stand-md6242-product',
'/10-pairs-dumbbell-rack-for-home-gym-workouts-md6247-product',
'/10-pairs-dumbbell-storage-rack-for-gym-home-md6250-product',
'/urethane-dumbbells-set-for-strength-training-md2117-product',
'/md2123-rubber-coated-studio-dumbbells-product',
'/md2135-12-side-tpu-coated-dumbbells-product',
'/rubber-bumper-plates-for-weightlifting-gym-use-md1056-product',
'/high-quality-rubber-bumper-plates-for-gym-lifting-md1057-product',
'/md1058-competition-weight-plates-product',
'/tricep-press-down-bar-for-cable-machines-strength-md5128-product',
'/md5132-tricep-press-down-bar-product',
'/md5132-tricep-press-down-bar-product',
],
'liveupsports' => [
'/supplier/nantong-liveup-sports-co-ltd',
'/yoga-pilates-liveup-sports',
'/training-equipment-liveup-sports',
'/gym-racks-and-equipment-liveup-sports',
'/20-25-30cm-pvc-customised-pilates-stability-exercise-ball-inflatable-soft-mini-pilates-small-yoga-bal-product',
'/55-65-75cm-bloom-workout-fitness-exercise-balance-gym-abs-anti-burst-yoga-ball-anti-slip-swiss-pilates-ball-product',
'/bodybuilding-fitness-exercise-custom-logo-gym-yoga-latex-hip-resistance-bands-loop-bands-set-product',
'/bloom-2080x4-5cm-natural-latex-loop-yoga-elastic-stretch-long-resistance-bands-exercise-band-product',
'/custom-logo-manufacturer-high-quality-eco-friendly-fitness-pilates-anti-slip-3-10mm-pu-rubber-tpe-cork-nbr-pvc-printed-yoga-mat-product',
'/custom-professional-manufacture-cheap-solid-cast-iron-dumbbell-weights-rubber-hex-dumbbell-sets-5-100lb-product',
'/professional-wholesale-custom-logo-free-weight-colored-rubber-bumper-gym-weight-barbell-plates-with-kg-mark-product',
'/gym-equipment-free-weights-fitness-12-sided-weightlifting-exercise-barbell-weight-lifting-polyurethane-fixed-barbell-product',
'/livepro-high-quality-steel-fitness-equipment-training-competition-gym-power-weightlifting-20kg-barbell-bar-product',
'/livepro-gym-commercial-equipment-fitness-storage-system-professional-multifunctional-training-frame-gym-storage-rack-product',
'/livepro-commercial-floor-mount-free-standing-cross-training-gym-rigs-multi-functional-training-rack-fitness-gym-rig-product',
'/livepro-oem-odm-fitness-equipment-multi-functional-trainer-smith-machine-station-home-using-gym-full-frame-cage-squat-power-rack-product',
],
'topflor' => [
'/supplier/nantong-topflor-co-ltd',
'/sports-flooring-topflor',
'/healthcare-flooring-topflor',
'/gym-flooring-topflor',
'/sprint-track-turf-for-high-performance-indoor-and-outdoor-training-product',
'/durable-gym-rubber-flooring-with-shock-absorption-and-slip-resistance-product',
'/interlocking-gym-flooring-tilesinterlocking-gym-flooring-tiles-product',
'/premium-hospital-vinyl-flooring-with-anti-bacterial-and-easy-clean-surface-product',
'/high-performance-healthcare-flooring-for-safe-and-hygienic-environments-product',
'/homogeneous-vinyl-flooring-with-high-durability-and-low-maintenance-product',
'/clinic-vinyl-flooring-with-anti-bacterial-and-wear-resistant-properties-product',
'/medical-pvc-flooring-with-anti-slip-hygienic-and-durable-features-product',
'/professional-badminton-court-pvc-sports-flooring-with-high-grip-and-shock-absorption-product',
'/indoor-basketball-sports-flooring-with-superior-ball-bounce-and-player-comfort-product',
'/multi-sport-indoor-pvc-flooring-for-volleyball-handball-badminton-and-fitness-product',
'/durable-multi-purpose-sports-flooring-for-schools-gyms-and-community-centers-product',
'/high-performance-multi-use-indoor-sports-flooring-with-easy-maintenance-product',
],
];
foreach ($url_map as $k=>$urls) {
foreach ($urls as $url) {
if(Str::contains($referer, $url)){
return $mail_mobile_map[$k];
}
}
}
return $mail_mobile_map[array_rand($mail_mobile_map)];
}
/**
* 特殊项目 邮件模版
* FIXME 后期有多个特殊项目,需要按照项目ID设置模板
* @param $name
... ... @@ -128,7 +253,7 @@ class InquiryFormData extends Base
* @param $country
* @return string
*/
public static function specialInquiryTemplate($name, $email, $phone, $message, $domain, $country)
public static function specialInquiryTemplate($name, $email, $phone, $message, $domain, $ip, $country)
{
$template = "
客户姓名:$name
... ... @@ -139,7 +264,7 @@ class InquiryFormData extends Base
$message
--------------------------------------------------------------------
发送询盘网址: $domain
客户IP地址: [ip]
客户IP地址: $ip
IP所在国家/地区: $country
--------------------------------------------------------------------";
return $template;
... ...
... ... @@ -10,6 +10,7 @@
namespace App\Models\Project;
use App\Models\Base;
use Illuminate\Support\Facades\Cache;
/**
* @remark :关键字前缀/后缀
... ... @@ -21,4 +22,68 @@ use App\Models\Base;
class KeywordPrefix extends Base
{
protected $table = 'gl_project_keyword_prefix';
/**
* 前后缀类型
*/
const TYPE_OPTIMIZE_PREFIX = 1;
const TYPE_OPTIMIZE_SUFFIX = 2;
const TYPE_GEO_PREFIX = 3;
const TYPE_GEO_SUFFIX = 4;
/**
* 前后缀类型映射
* @return array
*/
public static function typeMapping()
{
return [
self::TYPE_OPTIMIZE_PREFIX => '优化关键词前缀',
self::TYPE_OPTIMIZE_SUFFIX => '优化关键词后缀',
self::TYPE_GEO_PREFIX => 'GEO前缀',
self::TYPE_GEO_SUFFIX => 'GEO后缀',
];
}
/**
* 保存关键词前后缀
* @param $project_id
* @param $type
* @param $keyword
* @param $remark
* @return KeywordPrefix
*/
public static function saveKeyword($project_id, $type, $keyword, $remark)
{
$data = self::where(['project_id' => $project_id, 'keyword' => $keyword])->first();
if (empty($data)) {
$data = new self();
$data->project_id = $project_id;
$data->type = $type;
$data->keyword = $keyword;
}
$data->remark = $remark;
$data->save();
Cache::forget('project_keyword_ps_' . $project_id . '_' . $type);
return $data;
}
/**
* 获取关键词前后缀
* @param $project_id
* @param $type
* @return array|mixed
*/
public static function getKeyword($project_id, $type)
{
$key = 'project_keyword_ps_' . $project_id . '_' . $type;
$list = Cache::get($key);
if (empty($list)) {
$list = self::select(['id', 'type', 'keyword', 'remark'])->where(['type' => $type])->whereIn('project_id', [0, $project_id])->orderBy('project_id', 'asc')->get();
$list = $list->isEmpty() ? [] : $list->toArray();
if ($list)
Cache::put($key, $list, 1800);
}
return $list;
}
}
... ...
... ... @@ -79,7 +79,9 @@ class Project extends Base
3=>'2-4',
4=>'3-5',
5=>'5-7',
6=>'1/2'
6=>'1/2',
7=>'1/3',
8=>'1/1',
];
if($val){
return $arr[$val] ?? '';
... ...
... ... @@ -30,6 +30,7 @@ class MessagePush extends Base
const TYPE_TICKET = 'Ticket';
const TYPE_DOMAIN = 'domain';
const TYPE_DOMAIN_V5 = 'domain_v5';
const TYPE_GEO_CONFIRM = 'geo_confirm';
//设置关联表名
/**
* @var mixed
... ...
... ... @@ -283,7 +283,7 @@ class SyncSubmitTaskService
$data['referer'] = $this->handle_referer($data['referer']);
$id = InquiryFormData::saveData($form_id, $data['domain'], $data['ip'], $data['country'], $data['referer'], $data['user_agent'], $data['submit_at'], $data['data'], $traffic);
$id = InquiryFormData::saveData($form_id, $data['domain'], $data['ip'], $data['country'], $data['referer'], $data['user_agent'], $data['submit_at'], $data['data'], $data['project_id'], $traffic);
//转化询盘
... ... @@ -318,6 +318,10 @@ class SyncSubmitTaskService
*/
public function visit($data, $date, $task_id, $traffic = 0)
{
//特殊过滤 无锡动为储能
if($data['country'] == '新加坡' && $data['data']['referrer_url'] == 'https://www.google.com/' && $data['domain'] == 'www.dowellelectronic.com'){
throw new InquiryFilterException( '特殊过滤');
}
$visit_data = $data['data'];
$referrer_url = '';
... ... @@ -492,6 +496,11 @@ class SyncSubmitTaskService
throw new InquiryFilterException( '全局过滤3');
}
//新加坡 并且 没有来源信息的访问信息屏蔽了 海龙 25/10/21
if($data['country'] == '新加坡' && empty($data['referer'])){
throw new InquiryFilterException( '全局过滤4');
}
//1913宁波市鄞州永鑫 ip荷兰 message 8-16 纯字母不含空格
if($project_id == 1913 && in_array($data['country']??'', ['荷兰', '俄罗斯'])
&& strlen($data['data']['message']??"") >= 8
... ...
... ... @@ -107,4 +107,8 @@ Route::prefix('ticket_upload')->group(function () {
Route::any('/saveLayoutDesign', [\App\Http\Controllers\Api\WorkOrder\TicketUploadDataController::class, 'saveLayoutDesign'])->name('ticket_upload.saveLayoutDesign');
Route::any('/getLayoutDesignInfo', [\App\Http\Controllers\Api\WorkOrder\TicketUploadDataController::class, 'getLayoutDesignInfo'])->name('ticket_upload.getLayoutDesignInfo');
});
//geo设置
Route::prefix('geo')->group(function () {
Route::any('/getConfirm', [\App\Http\Controllers\Api\GeoController::class, 'getConfirm'])->name('geo.getConfirm');
});
... ...
... ... @@ -207,6 +207,7 @@ Route::middleware(['aloginauth'])->group(function () {
Route::any('/saveSiteStatus', [Aside\Project\ProjectController::class, 'saveSiteStatus'])->name('admin.project_saveSiteStatus');
Route::any('/updateTdk', [Aside\Project\ProjectController::class, 'updateTdk'])->name('admin.project_updateTdk');//更新项目tdk
Route::any('/videoSetting', [Aside\Project\ProjectController::class, 'videoSetting'])->name('admin.project_videoSetting');//项目管理AI.video设置
Route::any('/getSpAdsLists', [Aside\Project\ProjectController::class, 'getSpAdsLists'])->name('admin.project_getSpAdsLists');//项目管理广告投放
//获取关键词前缀和后缀
Route::prefix('keyword')->group(function () {
Route::any('/getKeywordPrefix', [Aside\Project\KeywordPrefixController::class, 'getKeywordPrefix'])->name('admin.keyword_getKeywordPrefix');
... ... @@ -585,6 +586,21 @@ Route::middleware(['aloginauth'])->group(function () {
Route::any('/downloadGeoLink', [Aside\Geo\GeoLinkController::class, 'downloadGeoLink'])->name('admin.geo_link_downloadGeoLink');
Route::any('/daResultData', [Aside\Geo\GeoLinkController::class, 'daResultData'])->name('admin.geo_link_daResultData');
});
//geo信息详情设置
Route::prefix('conf')->group(function () {
Route::any('/getConfig', [Aside\Geo\GeoController::class, 'getConfig'])->name('admin.geo_conf_getConfig');
Route::any('/saveConfig', [Aside\Geo\GeoController::class, 'saveConfig'])->name('admin.geo_conf_saveConfig');
});
//geo客户确认信息
Route::prefix('confirm')->group(function () {
Route::any('/saveConfirmContent', [Aside\Geo\GeoConfirmController::class, 'saveConfirmContent'])->name('admin.geo_confirm_saveConfirmContent');
});
//geoai文章任务管理
Route::prefix('writing_task')->group(function () {
Route::any('/', [Aside\Geo\GeoWritingTaskController::class, 'lists'])->name('admin.geo_writing_task_lists');
Route::any('/saveWritingTask', [Aside\Geo\GeoWritingTaskController::class, 'saveWritingTask'])->name('admin.geo_writing_task_saveWritingTask');
Route::any('/delWritingTask', [Aside\Geo\GeoWritingTaskController::class, 'delWritingTask'])->name('admin.geo_writing_task_delWritingTask');
});
});
// 任务相关
Route::prefix('task')->group(function () {
... ... @@ -651,6 +667,8 @@ Route::middleware(['aloginauth'])->group(function () {
Route::any('/save', [Aside\Ticket\TicketUploadDataController::class,'save'])->name('ticket_upload_save');
Route::any('/detail', [Aside\Ticket\TicketUploadDataController::class,'detail'])->name('ticket_upload_detail');
});
});
//无需登录验证的路由组
... ...