作者 lyh

Merge branch 'master' of http://47.244.231.31:8099/zhl/globalso-v6 into master-server

<?php
/**
* Created by PhpStorm.
* User: akun
* Date: 2025/02/26
* Time: 10:14
*/
namespace App\Console\Commands\Inquiry;
use App\Models\Inquiry\InquiryInfo;
use App\Models\Inquiry\InquiryProject;
use App\Models\Inquiry\InquiryRelayDetail;
use App\Models\Inquiry\InquiryRelayDetailLog;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
/**
* Class postInquiry
* @package App\Console\Commands\Inquiry
*/
class PostInquiryForward extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'post_inquiry_forward';
/**
* The console command description.
*
* @var string
*/
protected $description = '执行询盘请求';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
public function handle()
{
while (true) {
$list = InquiryRelayDetailLog::where('status', InquiryRelayDetailLog::STATUS_INIT)->where('start_at', '<=', date('Y-m-d H:i:s'))->orderBy('id', 'asc')->limit(100)->get();
if (!$list->count()) {
sleep(1);
}
// 询盘数据入库
foreach ($list as $key => $val) {
//加个锁 避免重复执行
$lockKey = 'inquiry_relay_detail_log_lock_' . $val->id;
if (!Redis::setnx($lockKey, 1)) {
continue;
}
Redis::expire($lockKey, 60);
$log = InquiryRelayDetailLog::find($val->id);
if ($log->status != InquiryRelayDetailLog::STATUS_INIT) {
continue;
}
$this->output('开始执行' . $val->id);
try {
$detail = InquiryRelayDetail::find($val['detail_id']);
//防止 程序中断 启动时 同一时间一下就发了
$last_log = InquiryRelayDetailLog::where('detail_id', $val['detail_id'])->where('id', '<', $val['id'])->orderBy('id', 'desc')->first();
//不是第一个 或者 上一个处理失败了 直接处理
if (!empty($last_log)) {
//上一个还没有处理 直接跳过
if ($last_log->status == InquiryRelayDetailLog::STATUS_INIT) {
continue;
}
//上一个处理完成
if ($last_log->status == InquiryRelayDetailLog::STATUS_SUCCESS) {
//上次处理时间间隔达到没
$interval = strtotime($val->start_at) - strtotime($last_log->start_at);
if (time() - strtotime($last_log->updated_at) < $interval) {
continue;
}
}
}
if ($val['type'] == 1) {
$this->visit($detail, $val);
} else {
$res = $this->inquiry($detail, $val);
//转发表单
if ($res) {
$form = InquiryInfo::find($detail['form_id']);
$form->success = $form->success + 1;
$form->save();
Log::channel('inquiry_forward')->info('询盘成功:', [$detail['form_id'], $val->id, getmypid()]);
}
}
} catch (\Exception $e) {
Log::channel('inquiry_forward')->error('post_inquiry_forward handle error', [$e->getMessage(), $e->getFile(), $e->getLine()]);
$val->status = InquiryRelayDetailLog::STATUS_FAIL;
$val->remark = mb_substr($e->getMessage(), 0, 200);
$val->save();
}
}
}
}
public function visit(InquiryRelayDetail $detail, InquiryRelayDetailLog $log)
{
$website = 'https://' . $detail['website'] . '/';
if ($detail['is_v6']) {
$data = [
'ip' => $detail['ip'],
'url' => $log['url'],
'device_port' => $detail['device_port'],
'referrer_url' => $detail['referrer'],
'user_agent' => $detail['user_agent'],
];
$res = Http::withoutVerifying()->timeout(30)->post($website . 'api/traffic_visit/', $data)->json();
if (empty($res['status']) || $res['status'] != 200) {
$log->status = InquiryRelayDetailLog::STATUS_FAIL;
$log->remark = mb_substr($res['message'] ?? '', 0, 200);
$log->save();
Log::channel('inquiry_forward')->error('post_inquiry_forward visit error', [$res, $website . 'api/traffic_visit/', $data]);
return false;
}
} else {
//v4 v5分离项目 往测试链接推
$project_info = InquiryProject::select(['is_split', 'test_domain'])->where('domain', 'like', '%' . $detail['website'] . '%')->first();
if (!$project_info) {
$log->status = InquiryRelayDetailLog::STATUS_FAIL;
$log->remark = '获取域名对应项目失败';
$log->save();
return false;
}
if ($project_info['is_split'] == 1) {
$website = $project_info['test_domain'];
}
$data = [
'action' => 'stats_init',
'assort' => 0,
'referrer' => $detail['referrer'],
'currweb' => $log['url'],
'user_agent' => $detail['user_agent'],
"ip" => $detail['ip'],
];
$res = Http::get($website . 'wp-admin/admin-ajax.php', $data);
$status = $res->status();
if ($status != 200) {
$log->status = InquiryRelayDetailLog::STATUS_FAIL;
$log->remark = mb_substr($res->body() ?? '', 0, 200);
$log->save();
Log::channel('inquiry_forward')->error('post_inquiry_forward v4|v5 visit error', [$res->body(), $website . 'wp-admin/admin-ajax.php', $data]);
return false;
}
}
$log->status = InquiryRelayDetailLog::STATUS_SUCCESS;
$log->save();
return true;
}
public function inquiry(InquiryRelayDetail $detail, InquiryRelayDetailLog $log)
{
// v6
if ($detail['is_v6']) {
$res = $this->v6Inquiry($detail, $log);
} else {
$res = $this->v5Inquiry($detail, $log);
}
if (!$res) {
return false;
}
$log->status = InquiryRelayDetailLog::STATUS_SUCCESS;
$log->save();
return true;
}
public function v6Inquiry($detail, $log)
{
$website = 'https://' . $detail['website'] . '/';
$data = [
'name' => $detail['name'],
'phone' => $detail['phone'],
'message' => $detail['message'],
'submit_ip' => $detail['ip'],
'refer' => $log['url'],
];
if ($detail['email']) {
$data['email'] = $detail['email'];
} else {
$data['__amp_source_origin'] = trim($website, '/');
}
$res = Http::withoutVerifying()->timeout(30)->withHeaders(['User-Agent' => $detail['user_agent']])->post($website . 'api/inquiryQd?source=5', $data)->json();
if (empty($res['code']) || $res['code'] != 200) {
$log->status = InquiryRelayDetailLog::STATUS_FAIL;
$log->remark = mb_substr($res['message'] ?? '', 0, 200);
$log->save();
Log::channel('inquiry_forward')->error('post_inquiry_forward v6 inquiry error', [$res, $website . 'api/inquiryQd/', $data]);
return false;
}
return true;
}
public function v5Inquiry($detail, $log)
{
$data = [
'name' => $detail['name'],
'phone' => $detail['phone'],
'message' => $detail['message'],
'email' => $detail['email'],
'ip' => $detail['ip'],
'token' => md5($log['url'] . $detail['name'] . $detail['ip'] . date("Y-m-d")),
'refer' => $log['url'],
'submit_time' => date('Y-m-d H:i:s'),
'source' => 5,
];
$result = Http::withoutVerifying()->timeout(30)->post('https://www.globalso.site/api/external-interface/add/fa043f9cbec6b38f', $data);
$res['data'][0]['status'] = 'success';
//兼容接口返回格式
if (!empty($res['data'][0]['status'])) {
$res['data'][0]['code'] = $res['data'][0]['status'] == 'success' ? 200 : 400;
!empty($res['data'][0]['msg']) && $res['message'] = $res['data'][0]['msg'];
}
if (empty($res['data'][0]['code']) || !in_array($res['data'][0]['code'], [200, 300])) {
$log->status = InquiryRelayDetailLog::STATUS_FAIL;
$log->remark = mb_substr($res['message'] ?? '', 0, 200);
$log->save();
Log::channel('inquiry_forward')->error('post_inquiry_forward v4|v5 inquiry error', [$result->body(), 'https://www.globalso.site/api/external-interface/add/fa043f9cbec6b38f', $data]);
return false;
}
return true;
}
public function output($message)
{
echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL;
}
}
... ...
... ... @@ -10,6 +10,7 @@ namespace App\Console\Commands\Inquiry;
use App\Helper\Common;
use App\Helper\Gpt;
use App\Helper\Translate;
use App\Helper\Validate;
use App\Models\Ai\AiCommand;
use App\Models\Inquiry\ReInquiryConfig;
use App\Models\Inquiry\ReInquiryDetail;
... ... @@ -329,7 +330,10 @@ class RelayInquiry extends Command
$val->save();
continue;
}
//验证手机号 无效 号码不推送
if(!Validate::phone($val->phone)){
$val->phone = '';
}
try {
$res = false;
foreach ($ad_task as $task){
... ... @@ -357,10 +361,10 @@ class RelayInquiry extends Command
//FB询盘的全局过滤规则
$fb_config = ReInquiryConfig::getDefaultConfigCache(ReInquiryConfig::TYPE_FILTER_WORDS);
$fb_config['filter_contents'] = array_filter(explode("\r\n", $fb_config['filter_contents']?:''));
$fb_config['filter_emails'] = array_filter(explode("\r\n", $fb_config['filter_emails']?:''));
$fb_config['filter_mobiles'] = array_filter(explode("\r\n", $fb_config['filter_mobiles']?:''));
$fb_config['filter_names'] = array_filter(explode("\r\n", $fb_config['filter_names']?:''));
$fb_config['filter_contents'] = array_filter(explode("\r\n", $fb_config['filter_contents']??''));
$fb_config['filter_emails'] = array_filter(explode("\r\n", $fb_config['filter_emails']??''));
$fb_config['filter_mobiles'] = array_filter(explode("\r\n", $fb_config['filter_mobiles']??''));
$fb_config['filter_names'] = array_filter(explode("\r\n", $fb_config['filter_names']??''));
$config['filter_contents'] = array_unique(array_merge($fb_config['filter_contents'],$config['filter_contents']));
$config['filter_emails'] = array_unique(array_merge($fb_config['filter_emails'],$config['filter_emails']));
... ... @@ -382,6 +386,10 @@ class RelayInquiry extends Command
return '过滤邮箱:' . $filter_email;
}
}
//邮箱有效性
if(!Validate::email($data['email'])){
return '邮箱无效';
}
}
//过滤电话
if(!empty($data['phone']) && !empty($config['filter_mobiles'])){
... ...
<?php
/**
* Created by PhpStorm.
* User: akun
* Date: 2025/02/24
* Time: 14:14
*/
namespace App\Console\Commands\Inquiry;
use App\Helper\Translate;
use App\Models\Inquiry\InquiryRelayDetail;
use App\Models\Inquiry\InquiryRelayDetailLog;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
/**
* Class RelayInquiry
* @package App\Console\Commands\Inquiry
*/
class RelayInquiryForward extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'relay_inquiry_forward';
/**
* The console command description.
*
* @var string
*/
protected $description = '转发询盘:拆分转发数据';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* 模拟访问来源占比
* @var array
*/
protected $lyzb = [
'https://www.google.com/' => 630,
'http://www.google.com/' => 30,
'http://www.bing.com/' => 20,
'https://www.bing.com/' => 5,
'https://www.youtube.com/' => 5,
'https://search.yahoo.com/' => 5,
'https://www.facebook.com/' => 5,
];
/**
* 俄语站 模拟访问来源占比
* @var array
*/
protected $eylyzb = [
'https://www.yandex.com/' => 630,
'https://www.google.com/' => 30,
'http://www.google.com/' => 30,
'http://www.bing.com/' => 20,
'https://www.bing.com/' => 5,
'https://www.youtube.com/' => 5,
'https://search.yahoo.com/' => 5,
'https://www.facebook.com/' => 5,
];
/**
* PC端访问头信息
* @var array
*/
protected $pc_ua = [
0 => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
1 => 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
2 => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'
];
/**
* 移动端访问头信息
* @var array
*/
protected $mobile_ua = [
0 => 'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19',
];
/**
* google域名后缀
* @var string[]
*/
protected $suffix = [
'co.jp' => '日本',
'com.tr' => '土耳其',
'nl' => '荷兰',
'ru' => '俄罗斯',
'fr' => '法国',
'co.kr' => '韩国',
'fi' => '芬兰',
'be' => '比利时',
'lt' => '立陶宛',
'es' => '西班牙',
'it' => '意大利',
'com.au' => '澳大利亚',
'no' => '挪威',
'al' => '阿尔巴尼亚',
'pt' => '葡萄牙',
'lv' => '拉脱维亚',
'hu' => '匈牙利',
'cz' => '捷克',
'de' => '德国',
'ca' => '加拿大',
'co.in' => '印度',
'co.uk' => '英国',
'com.vn' => '越南',
'com.br' => '巴西',
'co.il' => '以色列',
'pl' => '波兰',
'com.eg' => '埃及',
'co.th' => '泰国',
'sk' => '斯洛伐克',
'ro' => '罗马尼亚',
'com.mx' => '墨西哥',
'com.my' => '马来西亚',
'com.pk' => '巴基斯坦',
'co.nz' => '新西兰',
'co.za' => '南非',
'com.ar' => '阿根廷',
'com.kw' => '科威特',
'com.sg' => '新加坡',
'com.co' => '哥伦比亚',
'co.id' => '印度尼西亚',
'gr' => '希腊',
'bg' => '保加利亚',
'mn' => '蒙古',
'dk' => '丹麦',
'com.sa' => '沙特阿拉伯',
'com.pe' => '秘鲁',
'com.ph' => '菲律宾',
'com.ua' => '乌克兰',
'ge' => '格鲁吉亚',
'ae' => '阿拉伯联合酋长国',
'tn' => '突尼斯',
];
protected $otherzb = [700, 300]; //模拟访问来源占比 (非美国) google.com|google.其他后缀
public function handle()
{
while (true) {
$this->startInquiryDetail();
}
}
public function startInquiryDetail()
{
$inquiry_detail = InquiryRelayDetail::where('status', InquiryRelayDetail::STATUS_INIT)->orderBy('start_at', 'asc')->orderBy('id', 'asc')->first();
if (!$inquiry_detail) {
//所有任务执行完成
sleep(60);
return true;
}
try {
$this->output('询盘详情ID:' . $inquiry_detail['id'] . ',开始拆分转发数据');
$inquiry_detail->status = InquiryRelayDetail::STATUS_PEND;//拆分中
$inquiry_detail->save();
//先补全询盘详情数据
$urls = $this->completeDetail($inquiry_detail);
//再拆分转发数据
$this->relayDetail($inquiry_detail['id'], $urls, $inquiry_detail['start_at']);
$inquiry_detail->status = InquiryRelayDetail::STATUS_SUCCESS;
$inquiry_detail->save();
$this->output('询盘详情ID:' . $inquiry_detail['id'] . ',拆分转发数据结束');
} catch (\Exception $e) {
$inquiry_detail->status = InquiryRelayDetail::STATUS_FAIL;
$inquiry_detail->remark = mb_substr($e->getMessage(), 0, 200);;
$inquiry_detail->save();
$this->output('询盘详情ID:' . $inquiry_detail['id'] . ',拆分转发数据失败,原因:' . $e->getMessage());
}
return true;
}
/**
* 补全询盘详情数据
* @param $inquiry_detail
* @return array
* @author Akun
* @date 2025/02/25 17:19
*/
public function completeDetail($inquiry_detail)
{
//visit urls
$visit_urls = $this->getUrls($inquiry_detail['is_v6'], $inquiry_detail['website'], $inquiry_detail['email']);
$inquiry_url = json_decode($inquiry_detail['urls'], true);
$urls = array_merge($visit_urls, $inquiry_url);
//lang
if (is_numeric($inquiry_detail['message'])) { //数字会被识别为中文
$lang = 'en';
} else {
$translateSl = Translate::translateSl($inquiry_detail['message']);
$lang = $translateSl['texts']['sl'] ?? 'en';
}
// 客户端 头信息 来源
$device_port = $inquiry_detail['email'] ? '1' : '2'; //1 pc 2移动端
$user_agent = $inquiry_detail['email'] ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua);
$referrer = $this->getReferer($inquiry_detail['country'], $lang);
$inquiry_detail->device_port = $device_port;
$inquiry_detail->user_agent = $user_agent;
$inquiry_detail->referrer = $referrer;
$inquiry_detail->urls = json_encode($urls);
$inquiry_detail->num = count($urls) + 1;
$inquiry_detail->save();
return $urls;
}
/**
* 创建转发详情
* @param $task_id
* @param $urls
* @param $start_at
* @author Akun
* @date 2025/02/25 15:41
*/
public function relayDetail($task_id, $urls, $start_at)
{
$pre = 0;
$seconds = rand(300, 7200); // 开始时间 从5分钟-2小时后开始
foreach ($urls as $k => $v) {
$pre++;
$seconds += rand(5, 60);
InquiryRelayDetailLog::createInquiryLog($task_id, InquiryRelayDetailLog::TYPE_VISIT, $pre, $v, date('Y-m-d H:i:s', strtotime($start_at) + $seconds));
// 最后一次访问询盘 加上询盘
if ($k + 1 >= count($urls)) {
$seconds += rand(30, 120);
$pre++;
InquiryRelayDetailLog::createInquiryLog($task_id, InquiryRelayDetailLog::TYPE_INQUIRY, $pre, $v, date('Y-m-d H:i:s', strtotime($start_at) + $seconds));
}
}
}
/**
* 获取访问url
* @param $is_v6
* @param $website
* @param $email
* @return array
* @author Akun
* @date 2025/02/25 15:45
*/
public function getUrls($is_v6, $website, $email)
{
$domain = 'https://' . $website . '/';
// v6:有邮箱推送主站,没有邮箱推送AMP站;v5:仅推送有邮箱到主站
if ($is_v6) {
// 获取访问明细和着陆页
$product_url = $this->getLinksFromSitemap($domain . 'product_sitemap.xml');
$product_cate_url = $this->getLinksFromSitemap($domain . 'product_category_sitemap.xml');
$keywords_url = $this->getLinksFromSitemap($domain . 'product_keywords_sitemap.xml');
$page_url = $this->getLinksFromSitemap($domain . 'page_sitemap.xml');
} else {
if ($email) {
//通过sitemap拿访问页面
$product_url = $this->getLinksFromSitemap($domain . 'sitemap_post.xml');
$product_cate_url = $this->getLinksFromSitemap($domain . 'sitemap_category.xml');
$keywords_url = $this->getLinksFromSitemap($domain . 'sitemap_post_tag.xml');
$page_url = $this->getLinksFromSitemap($domain . 'sitemap_page.xml');
} else {
//m站先就往contact-us着陆
$product_url = $product_cate_url = $keywords_url = [];
$page_url = [$domain . 'contact-us/'];
}
}
// 所有可用url
$urls = [];
//入口url 首页30%,单页10%,聚合页60%
$type = getRandByRatio([30, 10, 60]);
$inlet = $domain;
$type == 1 && $inlet = $page_url ? Arr::random($page_url) : $domain;
$type == 2 && $inlet = $keywords_url ? Arr::random($keywords_url) : $domain;
$urls[] = $inlet;
$all_urls = array_merge($urls, $product_url, $product_cate_url, $keywords_url, $page_url);
if (count($all_urls) > 1) {
// 随机访问1-6个页面
$deep = rand(1, 6);
$visit_urls = Arr::random($all_urls, $deep > count($all_urls) ? count($all_urls) : $deep);
$urls = array_merge($urls, $visit_urls);
}
return $urls;
}
/**
* 获取头信息
* @param $ip_area
* @param $lang
* @return int|string
*/
public function getReferer($ip_area, $lang)
{
if ($lang == 'ru') {
return $this->get_rand($this->eylyzb);
}
if ($ip_area == '美国') {
$referer = $this->get_rand($this->lyzb);
} else {
$referer = 'https://www.google.com/';
$suffix = array_search($ip_area, $this->suffix);
if ($suffix) {
$res_qtzb = $this->get_rand($this->otherzb);
if ($res_qtzb == 1) {
$referer = 'https://www.google.' . $suffix . '/';
}
}
}
return $referer;
}
/**
* 概率算法
* @param $proArr
* @return int|string
*/
protected function get_rand($proArr)
{
$result = '';
$proSum = array_sum($proArr);
foreach ($proArr as $key => $proCur) {
$randNum = mt_rand(1, $proSum);
if ($randNum <= $proCur) {
$result = $key;
break;
} else {
$proSum -= $proCur;
}
}
unset ($proArr);
return $result;
}
/**
* 获取sitemap内容
* @param $sitemapUrl
* @return array|mixed
*/
function getLinksFromSitemap($sitemapUrl)
{
try {
//忽略cert证书 先下载到临时文件
$result = Http::withoutVerifying()->get($sitemapUrl)->body();
$tempFilePath = tempnam(sys_get_temp_dir(), 'remote_file_');
file_put_contents($tempFilePath, $result);
$xml = simplexml_load_file($tempFilePath);
$links = [];
foreach ($xml->url as $url) {
$loc = (string)$url->loc;
if (!Str::contains($loc, ['404', 'thanks', 'test'])) {
$links[] = $loc;
}
}
//随机取20个
$total = count($links);
return Arr::random($links, $total > 20 ? 20 : $total);
} catch (\Exception $e) {
echo date('Y-m-d H:i:s') . 'sitemap获取失败:' . $e->getMessage() . PHP_EOL;
return $links ?? [];
}
}
public function output($message)
{
echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL;
}
}
... ...
... ... @@ -53,7 +53,7 @@ class SyncInquiryProject extends Command
/**
* 同步优化项目及路由
* TODO 同步v4 v5 v6项目以及路由, 删除过期项目及路由
* 同步v4 v5 v6项目以及路由, 删除过期项目及路由
* @return bool
*/
public function handle()
... ... @@ -133,7 +133,6 @@ class SyncInquiryProject extends Command
$query->orwhere('gl_project_online_check.qa_status', OnlineCheck::STATUS_ONLINE_TRUE)
->orwhere('gl_project.is_upgrade', Project::IS_UPGRADE_TRUE);
})
->limit(5)
->get(['gl_project.id', 'gl_project.title', 'gl_project.level', 'gl_project.channel']);
// 获取所有项目域名
$domains = DomainInfo::whereIn('project_id', $project->pluck('id')->toArray())->pluck('domain', 'project_id')->toArray();
... ... @@ -142,7 +141,7 @@ class SyncInquiryProject extends Command
// 过滤暂停优化项目, 映射类型2, model没有定义常量
if (in_array(2, $val->level))
continue;
$project = InquiryProject::saveProject($date,InquiryProject::VERSION_SIX, $val->id, $val->title, $val->channel['channel_id'], $domains[$val->id]);
$project = InquiryProject::saveProject($date,InquiryProject::VERSION_SIX, $val->id, $val->title, $val->channel['channel_id'], 'https://'.$domains[$val->id].'/');
$this->pushTask($project->id);
}
... ... @@ -190,4 +189,4 @@ class SyncInquiryProject extends Command
file_put_contents(storage_path('logs/zhl/output') . date('Y-m-d') . '.log', $message, FILE_APPEND);
return true;
}
}
\ No newline at end of file
}
... ...
... ... @@ -53,7 +53,7 @@ class SyncInquiryProjectRoute extends Command
/**
* 同步优化项目及路由
* TODO 同步v4 v5 v6项目以及路由, 删除过期项目及路由
* 同步v4 v5 v6项目以及路由, 删除过期项目及路由
* @return bool
*/
public function handle()
... ... @@ -130,7 +130,7 @@ class SyncInquiryProjectRoute extends Command
echo '同步项目ID:' . $task->id . PHP_EOL;
$date = intval(date('Ymd'));
ProjectServer::useProject($task->primary_id);
// TODO 产品分类标题、路由, 产品标题、路由, 同步到路由表
// 产品分类标题、路由, 产品标题、路由, 同步到路由表
$category = Category::where('status', Category::STATUS_ACTIVE)->get(['title', 'route']);
foreach ($category as $key=>$val) {
InquiryProjectRoute::saveProjectRoute($task->id, $val->title, $val->route, $date);
... ... @@ -181,4 +181,4 @@ class SyncInquiryProjectRoute extends Command
file_put_contents(storage_path('logs/zhl/output') . date('Y-m-d') . '.log', $message, FILE_APPEND);
return true;
}
}
\ No newline at end of file
}
... ...
<?php
namespace App\Helper;
use App\Utils\HttpUtils;
use GuzzleHttp\Exception\GuzzleException;
/**
* Class Validate
* @package App\Helper
* @author zbj
* @date 2025/2/27
*/
class Validate
{
/**
* 邮箱有效性
* @param $email
* @return bool
* @author zbj
* @date 2025/2/27
*/
public static function email($email)
{
try {
$res = HttpUtils::get('https://fob.ai.cc/api/check_email', ['email' => $email]);
$res = Arr::s2a($res);
$status = $res['data']['status'] ?? 0;
} catch (\Exception | GuzzleException $e) {
$status = 0;
}
return !($status == 4);
}
/**
* 邮箱有效性
* @param $email
* @return bool
* @author zbj
* @date 2025/2/27
*/
public static function phone($email)
{
try {
$res = HttpUtils::get('https://fob.ai.cc/api/check_phone', ['phone' => $email]);
$res = Arr::s2a($res);
$status = $res['data']['valid_status'] ?? 0;
} catch (\Exception | GuzzleException $e) {
$status = 0;
}
return !($status == 2);
}
}
... ...
<?php
namespace App\Http\Controllers\Aside\Optimize;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Logic\Aside\Optimize\InquiryForwardLogic;
use Illuminate\Support\Facades\DB;
class InquiryForwardController extends BaseController
{
/**
* 获取询信息发列表
* @param InquiryForwardLogic $inquiryForwardLogic
* @author Akun
* @date 2025/02/21 10:55
*/
public function lists(InquiryForwardLogic $inquiryForwardLogic)
{
if (isset($this->param['url'])) {
$this->map['url'] = ['like', '%' . $this->map['url'] . '%'];
}
if (isset($this->param['email'])) {
$this->map['email'] = ['like', '%' . $this->map['email'] . '%'];
}
if (isset($this->param['url_title'])) {
$this->map['url_title'] = ['like', '%' . $this->map['url_title'] . '%'];
}
if (isset($this->param['url_keyword'])) {
$this->map['url_keyword'] = ['like', '%' . $this->map['url_keyword'] . '%'];
}
$lists = $inquiryForwardLogic->getInquiryLists($this->map, $this->page, $this->row, $this->order);
$this->response('success', Code::SUCCESS, $lists);
}
/**
* 获取询盘类型
* @param InquiryForwardLogic $inquiryForwardLogic
* @author Akun
* @date 2025/02/21 11:20
*/
public function getInquiryType(InquiryForwardLogic $inquiryForwardLogic)
{
$lists = $inquiryForwardLogic->getType();
$this->response('success', Code::SUCCESS, $lists);
}
/**
* 手动转发询盘
* @param InquiryForwardLogic $inquiryForwardLogic
* @throws \App\Exceptions\AsideGlobalException
* @throws \App\Exceptions\BsideGlobalException
* @author Akun
* @date 2025/02/21 14:54
*/
public function forwardInquiry(InquiryForwardLogic $inquiryForwardLogic)
{
$this->validationParam();
$inquiryForwardLogic->inquiryForward();
$this->response('success');
}
/**
* 手动转发询盘:参数验证
* @author Akun
* @date 2025/02/21 14:19
*/
public function validationParam()
{
$this->request->validate([
'id' => 'required',//ID
'name' => 'required',//名称
'email' => 'required',//邮箱
'phone' => 'required',//电话号码
'ip' => 'required',//ip
'forward_url' => 'required',//转发网址
'message' => 'required',//发送内容
'inquiry_diff' => 'required',//发送时区
'inquiry_date' => 'required',//发送时间
], [
'id.required' => 'ID不能为空',
'name.required' => '名称不能为空',
'email.required' => '邮箱不能为空',
'phone.required' => '电话号码不能为空',
'ip.required' => 'ip不能为空',
'forward_url.required' => '转发网址不能为空',
'message.required' => '内容不能为空',
'inquiry_diff.required' => '发送时区不能为空',
'inquiry_date.required' => '发送时间不能为空',
]);
}
/**
* 根据国家获取随机ip
* @param InquiryForwardLogic $inquiryForwardLogic
* @throws \App\Exceptions\AsideGlobalException
* @throws \App\Exceptions\BsideGlobalException
* @author Akun
* @date 2025/02/26 15:50
*/
public function getInquiryIp(InquiryForwardLogic $inquiryForwardLogic)
{
$this->request->validate([
'country' => 'required',
], [
'country.required' => '国家不能为空',
]);
$data = $inquiryForwardLogic->getIp();
$this->response('success', Code::SUCCESS, $data);
}
/**
* 关键词查询项目着陆页
* @param InquiryForwardLogic $inquiryForwardLogic
* @author Akun
* @date 2025/02/26 17:12
*/
public function searchInquiryKeywords(InquiryForwardLogic $inquiryForwardLogic)
{
$this->request->validate([
'keywords' => 'required',
'type' => 'required',
], [
'keywords.required' => '关键词不能为空',
'type.required' => '搜索类型不能为空',
]);
$lists = $inquiryForwardLogic->searchKeywords();
$this->response('success', Code::SUCCESS, $lists);
}
/**
* AI重写询盘文案
* @param InquiryForwardLogic $inquiryForwardLogic
* @throws \App\Exceptions\AsideGlobalException
* @throws \App\Exceptions\BsideGlobalException
* @author Akun
* @date 2025/02/27 10:40
*/
public function aiRewriteInquiry(InquiryForwardLogic $inquiryForwardLogic)
{
$this->request->validate([
'message' => 'required',
], [
'message.required' => '需要重写的文案不能为空',
]);
$data = $inquiryForwardLogic->aiRewrite();
$this->response('success', Code::SUCCESS, $data);
}
}
... ...
<?php
namespace App\Http\Logic\Aside\Optimize;
use App\Helper\Common;
use App\Helper\Gpt;
use App\Helper\Translate;
use App\Http\Logic\Aside\BaseLogic;
use App\Models\Ai\AiCommand;
use App\Models\Inquiry\InquiryInfo;
use App\Models\Inquiry\InquiryProject;
use App\Models\Inquiry\InquiryProjectRoute;
use App\Models\Inquiry\InquiryRelayDetail;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
/**
* @remark :询盘中心
* @class :InquiryForwardLogic.php
* @author :akun
* @time :2025/2/21 11:17
*/
class InquiryForwardLogic extends BaseLogic
{
public function __construct()
{
parent::__construct();
$this->param = $this->requestAll;
$this->model = new InquiryInfo();
}
/**
* 获取类型
* @return array
* @author Akun
* @date 2025/02/21 11:19
*/
public function getType()
{
return $this->success($this->model->typeMap());
}
/**
* 获取列表
* @param $map
* @param $page
* @param $row
* @param string $order
* @param string[] $filed
* @return array
* @author Akun
* @date 2025/02/21 11:19
*/
public function getInquiryLists($map, $page, $row, $order = 'id', $filed = ['*'])
{
$lists = $this->model->lists($map, $page, $row, $order, $filed);
return $this->success($lists);
}
/**
* 转发询盘
* @return array
* @throws \App\Exceptions\AsideGlobalException
* @throws \App\Exceptions\BsideGlobalException
* @author Akun
* @date 2025/02/21 14:54
*/
public function inquiryForward()
{
$info = $this->model->read(['id' => $this->param['id']]);
if ($info === false) {
$this->fail('获取询盘详情失败');
}
if ($info['status'] != InquiryInfo::STATUS_INIT) {
$this->fail('当前询盘状态无法转发');
}
DB::beginTransaction();
try {
$num = 0;
$now = date('Y-m-d H:i:s');
foreach ($this->param['forward_url'] as $url) {
$domain_array = parse_url($url);
$website = $domain_array['host'] ?? '';
if (!$website) {
continue;
}
//获取站点对应项目版本
$project_version = InquiryProject::select(['version'])->where('domain', 'like', '%' . $website . '%')->first();
if (!$project_version) {
continue;
}
$is_v6 = $project_version->version == 6 ? 1 : 0;
//计算发送时间
if ($this->param['inquiry_diff'] > 0) {
$start_at = date('Y-m-d H:i:s', strtotime($this->param['inquiry_date'] . ' -' . $this->param['inquiry_diff'] . ' hours'));
} elseif ($this->param['inquiry_diff'] < 0) {
$start_at = date('Y-m-d H:i:s', strtotime($this->param['inquiry_date'] . ' +' . abs($this->param['inquiry_diff']) . ' hours'));
} else {
$start_at = $this->param['inquiry_date'];
}
if ($start_at < $now) {
$start_at = $now;
}
InquiryRelayDetail::createInquiry($info['id'], $website, $info['country'], $this->param['ip'], $this->param['name'], $this->param['email'], $this->param['phone'], $this->param['message'], $is_v6, json_encode([$url]), $start_at);
$num += 1;
}
//更改询盘状态及转发数量
$this->model->edit(['status' => 1, 'num' => $num], ['id' => $this->param['id']]);
DB::commit();
} catch (\Exception $e) {
DB::rollback();
Log::error('inquiry_forward error:' . $e->getMessage());
$this->fail('转发询盘失败');
}
return $this->success();
}
/**
* 获取随机ip
* @return array
* @throws \App\Exceptions\AsideGlobalException
* @throws \App\Exceptions\BsideGlobalException
* @author Akun
* @date 2025/02/26 15:50
*/
public function getIp()
{
$ip = DB::table('gl_xunpan_ipdata')->where('ip_area', $this->param['country'])->inRandomOrder()->value('ip');
if (!$ip) {
$this->fail('当前国家无法获取随机ip');
}
return $this->success(['ip' => $ip]);
}
/**
* 关键词查询项目着陆页
* @return int|mixed
* @author Akun
* @date 2025/02/26 17:13
*/
public function searchKeywords()
{
$num = $this->param['num'] ?? 3;
$model = new InquiryProjectRoute();
if ($this->param['type'] == 1) {
//使用全文索引搜索
$routeQuery = $model->select(['project_id', 'route'])->whereRaw("MATCH(title) AGAINST(? IN BOOLEAN MODE)", [$this->param['keywords']]);
} else {
//使用like查询
$routeQuery = $model->select(['project_id', 'route'])->where('title', 'like', '%' . $this->param['keywords'] . '%');
}
$re_route = $routeQuery->inRandomOrder()->take(100)->get()->toArray();
$lists = [];
if (count($re_route) > 0) {
//根据项目id查询项目,并根据询盘量排序
$project_ids = array_column($re_route, 'project_id');
$re_project = InquiryProject::select(['id', 'project', 'channel', 'domain'])->whereIn('id', $project_ids)->orderBy('recent_inquiry', 'asc')->get();
if ($re_project->count() > 0) {
//根据代理商去重
$channel = [];
foreach ($re_project as $vp) {
$vp_channel = explode(',', $vp->channel);
$has_channel = 0;
foreach ($vp_channel as $vpc) {
if (in_array($vpc, $channel)) {
$has_channel = 1;
}
}
if ($has_channel == 0) {
$channel = array_merge($channel, $vp_channel);
$route = '';
foreach ($re_route as $vr) {
if ($vr['project_id'] == $vp->id) {
$route = $vr['route'];
break;
}
}
if (substr($route, -5) != '.html') {
$route = $route . '/';
}
$lists[] = [
'id' => $vp->id,
'project' => $vp->project,
'route' => $vp->domain . $route
];
}
}
}
}
return $this->success(array_slice($lists, 0, $num, true));
}
/**
* AI重写询盘文案
* @return array
* @throws \App\Exceptions\AsideGlobalException
* @throws \App\Exceptions\BsideGlobalException
* @author Akun
* @date 2025/02/27 10:40
*/
public function aiRewrite()
{
$ai_command = AiCommand::where('key', 'inquiry_text_rewrite')->value('ai');
if (!$ai_command) {
$this->fail('AI重写指令未配置');
}
$in_content = $this->param['message'];
$translateSl = Translate::translateSl($in_content);
$lang = $translateSl['texts']['sl'] ?? 'en';
if ($lang == 'en' || $lang == 'ja' || $lang == 'ko' || Str::contains($lang, 'zh')) {
$lang = 'en';
}
$ai_command = str_replace('{incontent}', Arr::random(explode("\r\n", $in_content)), $ai_command);
$text = Gpt::instance()->openai_chat_qqs($ai_command);
if ($lang != 'en' && !Str::contains($lang, 'zh')) {
$text = Translate::tran($text, $lang);
}
return $this->success(['ai_message' => Common::deal_str($text)]);
}
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2024/9/30
* Time: 14:56
*/
namespace App\Models\Inquiry;
use Illuminate\Database\Eloquent\Model;
/**
* 询盘转发详情
* Class ReInquiryDetail
* @package App\Models\Inquiry
*/
class InquiryRelayDetail extends Model
{
/**
* @var string
*/
protected $table = 'gl_inquiry_relay_detail';
/**
* 任务状态, 0:待处理,1:处理中,2:处理成功,3:抛弃数据,9:处理失败
*/
const STATUS_INIT = 0;
const STATUS_PEND = 1;
const STATUS_SUCCESS = 2;
const STATUS_FORGO = 3;
const STATUS_FAIL = 9;
/**
* 状态映射
* @return array
*/
public static function statusMap()
{
return [
self::STATUS_INIT => '待处理',
self::STATUS_PEND => '处理中',
self::STATUS_SUCCESS => '处理成功',
self::STATUS_FORGO => '抛弃数据',
self::STATUS_FAIL => '处理失败',
];
}
/**
* 创建询盘转发详情待处理任务
* @param $form_id
* @param $website
* @param $country
* @param $ip
* @param $name
* @param $email
* @param $phone
* @param $message
* @param $is_v6
* @param $urls
* @param $start_at
* @param int $status
*/
public static function createInquiry($form_id, $website, $country, $ip, $name, $email, $phone, $message, $is_v6, $urls, $start_at, $status = self::STATUS_INIT)
{
$self = self::where(compact('form_id', 'website'))->first();
if(!$self){
$self = new self();
$self->form_id = $form_id;
$self->website = $website;
$self->country = $country;
$self->ip = $ip;
$self->name = $name;
$self->email = $email;
$self->phone = $phone;
$self->message = $message;
$self->is_v6 = $is_v6;
$self->urls = $urls;
$self->start_at = $start_at;
$self->status = $status;
$self->save();
}
}
/**
* 转发日志详情
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function detailLog()
{
return $this->hasMany(InquiryRelayDetailLog::class, 'detail_id', 'id');
}
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2024/10/08
* Time: 14:10
*/
namespace App\Models\Inquiry;
use Illuminate\Database\Eloquent\Model;
/**
* 转发详情日志
* Class ReInquiryDetailLog
* @package App\Models\Inquiry
*/
class InquiryRelayDetailLog extends Model
{
/**
* @var string
*/
protected $table = 'gl_inquiry_relay_detail_log';
/**
* 任务状态, 0:初始化,1:待处理,2:处理成功,9:处理失败
*/
const STATUS_INIT = 0;
const STATUS_PEND = 1;
const STATUS_SUCCESS = 2;
const STATUS_FAIL = 9;
/**
* 执行类型, 1:访问, 2:询盘
*/
const TYPE_VISIT = 1;
const TYPE_INQUIRY = 2;
/**
* 状态映射
* @return array
*/
public static function statusMap()
{
return [
self::STATUS_INIT => '初始化',
self::STATUS_PEND => '待处理',
self::STATUS_SUCCESS => '处理成功',
self::STATUS_FAIL => '处理失败',
];
}
/**
* 创建转发详情日志
* @param $detail_id
* @param $type
* @param $pre
* @param $url
* @param $start_at
*/
public static function createInquiryLog($detail_id, $type, $pre, $url, $start_at)
{
$self = self::where(compact('detail_id', 'type', 'pre'))->first();
if (!$self) {
$self = new self();
$self->detail_id = $detail_id;
$self->type = $type;
$self->pre = $pre;
$self->url = $url;
$self->start_at = $start_at;
$self->save();
}
}
}
... ...
... ... @@ -340,6 +340,16 @@ Route::middleware(['aloginauth'])->group(function () {
Route::any('/getInternalCount', [Aside\Optimize\InquiryInfoController::class, 'getInternalCount'])->name('admin.inquiry_getInternalCount');
});
//手动转发询盘
Route::prefix('inquiry_forward')->group(function () {
Route::any('/', [Aside\Optimize\InquiryForwardController::class, 'lists'])->name('admin.inquiry_lists');
Route::any('/getInquiryType', [Aside\Optimize\InquiryForwardController::class, 'getInquiryType'])->name('admin.inquiry_forward_getInquiryType');
Route::any('/forwardInquiry', [Aside\Optimize\InquiryForwardController::class, 'forwardInquiry'])->name('admin.inquiry_forward_forwardInquiry');
Route::any('/getInquiryIp', [Aside\Optimize\InquiryForwardController::class, 'getInquiryIp'])->name('admin.inquiry_forward_getInquiryIp');
Route::any('/searchInquiryKeywords', [Aside\Optimize\InquiryForwardController::class, 'searchInquiryKeywords'])->name('admin.inquiry_forward_searchInquiryKeywords');
Route::any('/aiRewriteInquiry', [Aside\Optimize\InquiryForwardController::class, 'aiRewriteInquiry'])->name('admin.inquiry_forward_aiRewriteInquiry');
});
Route::prefix('custom_module')->group(function () {
Route::any('/', [\App\Http\Controllers\Aside\CustomModule\CustomModuleController::class, 'lists'])->name('custom_lists');
Route::any('/save', [\App\Http\Controllers\Aside\CustomModule\CustomModuleController::class, 'save'])->name('custom_save');
... ...