作者 刘锟

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

  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/09/30
  6 + * Time: 11:51
  7 + */
  8 +namespace App\Console\Commands\Inquiry;
  9 +
  10 +use App\Helper\Translate;
  11 +use App\Models\Inquiry\ReInquiryDetail;
  12 +use App\Models\Inquiry\ReInquiryDetailLog;
  13 +use App\Models\Inquiry\ReInquiryForm;
  14 +use App\Models\Inquiry\ReInquiryTask;
  15 +use App\Models\Inquiry\ReInquiryText;
  16 +use App\Models\Project\Project;
  17 +use App\Models\WebSetting\WebLanguage;
  18 +use Illuminate\Console\Command;
  19 +use Illuminate\Support\Arr;
  20 +use Illuminate\Support\Facades\Cache;
  21 +use Illuminate\Support\Facades\DB;
  22 +use Illuminate\Support\Facades\Http;
  23 +use Illuminate\Support\Facades\Log;
  24 +use Illuminate\Support\Str;
  25 +
  26 +/**
  27 + * Class RelayInquiry
  28 + * @package App\Console\Commands\Inquiry
  29 + */
  30 +class RelayInquiry extends Command
  31 +{
  32 + /**
  33 + * The name and signature of the console command.
  34 + *
  35 + * @var string
  36 + */
  37 + protected $signature = 'relay_inquiry';
  38 +
  39 + /**
  40 + * The console command description.
  41 + *
  42 + * @var string
  43 + */
  44 + protected $description = '转发询盘:拆分转发数据';
  45 +
  46 + /**
  47 + * Create a new command instance.
  48 + *
  49 + * @return void
  50 + */
  51 + public function __construct()
  52 + {
  53 + parent::__construct();
  54 + }
  55 +
  56 + /**
  57 + * 模拟访问来源占比
  58 + * @var array
  59 + */
  60 + protected $lyzb = [
  61 + 'https://www.google.com/' => 630,
  62 + 'http://www.google.com/' => 30,
  63 + 'http://www.bing.com/' => 20,
  64 + 'https://www.bing.com/' => 5,
  65 + 'https://www.youtube.com/' => 5,
  66 + 'https://search.yahoo.com/' => 5,
  67 + 'https://www.facebook.com/' => 5,
  68 + ];
  69 +
  70 + /**
  71 + * 俄语站 模拟访问来源占比
  72 + * @var array
  73 + */
  74 + protected $eylyzb = [
  75 + 'https://www.yandex.com/' => 630,
  76 + 'https://www.google.com/' => 30,
  77 + 'http://www.google.com/' => 30,
  78 + 'http://www.bing.com/' => 20,
  79 + 'https://www.bing.com/' => 5,
  80 + 'https://www.youtube.com/' => 5,
  81 + 'https://search.yahoo.com/' => 5,
  82 + 'https://www.facebook.com/' => 5,
  83 + ];
  84 +
  85 + /**
  86 + * PC端访问头信息
  87 + * @var array
  88 + */
  89 + protected $pc_ua = [
  90 + 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',
  91 + 1 => 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
  92 + 2 => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'
  93 + ];
  94 +
  95 + /**
  96 + * 移动端访问头信息
  97 + * @var array
  98 + */
  99 + protected $mobile_ua = [
  100 + 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',
  101 + ];
  102 +
  103 + /**
  104 + * google域名后缀
  105 + * @var string[]
  106 + */
  107 + protected $suffix = [
  108 + 'co.jp' => '日本',
  109 + 'com.tr' => '土耳其',
  110 + 'nl' => '荷兰',
  111 + 'ru' => '俄罗斯',
  112 + 'fr' => '法国',
  113 + 'co.kr' => '韩国',
  114 + 'fi' => '芬兰',
  115 + 'be' => '比利时',
  116 + 'lt' => '立陶宛',
  117 + 'es' => '西班牙',
  118 + 'it' => '意大利',
  119 + 'com.au' => '澳大利亚',
  120 + 'no' => '挪威',
  121 + 'al' => '阿尔巴尼亚',
  122 + 'pt' => '葡萄牙',
  123 + 'lv' => '拉脱维亚',
  124 + 'hu' => '匈牙利',
  125 + 'cz' => '捷克',
  126 + 'de' => '德国',
  127 + 'ca' => '加拿大',
  128 + 'co.in' => '印度',
  129 + 'co.uk' => '英国',
  130 + 'com.vn' => '越南',
  131 + 'com.br' => '巴西',
  132 + 'co.il' => '以色列',
  133 + 'pl' => '波兰',
  134 + 'com.eg' => '埃及',
  135 + 'co.th' => '泰国',
  136 + 'sk' => '斯洛伐克',
  137 + 'ro' => '罗马尼亚',
  138 + 'com.mx' => '墨西哥',
  139 + 'com.my' => '马来西亚',
  140 + 'com.pk' => '巴基斯坦',
  141 + 'co.nz' => '新西兰',
  142 + 'co.za' => '南非',
  143 + 'com.ar' => '阿根廷',
  144 + 'com.kw' => '科威特',
  145 + 'com.sg' => '新加坡',
  146 + 'com.co' => '哥伦比亚',
  147 + 'co.id' => '印度尼西亚',
  148 + 'gr' => '希腊',
  149 + 'bg' => '保加利亚',
  150 + 'mn' => '蒙古',
  151 + 'dk' => '丹麦',
  152 + 'com.sa' => '沙特阿拉伯',
  153 + 'com.pe' => '秘鲁',
  154 + 'com.ph' => '菲律宾',
  155 + 'com.ua' => '乌克兰',
  156 + 'ge' => '格鲁吉亚',
  157 + 'ae' => '阿拉伯联合酋长国',
  158 + 'tn' => '突尼斯',
  159 + ];
  160 +
  161 + protected $otherzb = [700, 300]; //模拟访问来源占比 (非美国) google.com|google.其他后缀
  162 +
  163 + /**
  164 + * @return bool
  165 + */
  166 + public function handle()
  167 + {
  168 + while (true) {
  169 + $inquiry = $this->getInquiry();
  170 + if ($inquiry->isEmpty()){
  171 + $this->logChannel()->info('未发现待处理询盘!');
  172 + $this->output('未发现待处理询盘!');
  173 + sleep(60);
  174 + continue;
  175 + }
  176 +
  177 + $this->output('开始处理本轮询盘!');
  178 + foreach ($inquiry as $key=>$val) {
  179 + $this->output('询盘ID:' . $val->id);
  180 + //询盘时间超过半小时 就不处理了
  181 + if(time() - strtotime($val->inquiry_date) > 1800){
  182 + $val->status = ReInquiryForm::STATUS_FORGO;
  183 + $val->remark = '超时30分钟未处理!';
  184 + $val->save();
  185 + continue;
  186 + }
  187 + // 询盘对应广告
  188 + $ad_task = $this->getAdTask($val->ad_id);
  189 + // 没有获取到转发任务
  190 + if (empty($ad_task)) {
  191 + $val->status = ReInquiryForm::STATUS_FORGO;
  192 + $val->remark = '未找到需要转发的广告任务!';
  193 + $val->save();
  194 + continue;
  195 + }
  196 + // 未设置对法对象
  197 + if (empty($ad_task['target'])) {
  198 + $val->status = ReInquiryForm::STATUS_FORGO;
  199 + $val->remark = '广告任务转发对象为空!';
  200 + $val->save();
  201 + continue;
  202 + }
  203 +
  204 + try {
  205 + $this->relayDetail($ad_task, $val);
  206 + } catch (\Exception $e) {
  207 + $this->logChannel()->info('执行询盘错误:' . $e->getMessage());
  208 + $this->output('执行询盘错误:' . $e->getMessage());
  209 + }
  210 + }
  211 + $this->output('本轮询盘处理结束!');
  212 + }
  213 + return true;
  214 + }
  215 +
  216 + /**
  217 + * 创建转发详情
  218 + * TODO 通过任务生成转发对象, 更具转发对象获取对应数据, 写入着陆记录
  219 + * @param $task
  220 + * @param $form
  221 + * @return bool
  222 + */
  223 + public function relayDetail($task, $form)
  224 + {
  225 + $this->output('获取转发对象');
  226 + // 获取转发对象 重置num数量, array_rand数量不足会报错
  227 + $task['num'] = $task['num'] > count($task['target']) ? count($task['target']) : $task['num'];
  228 + $relay_target_key = array_rand($task['target'], $task['num']);
  229 + if (empty($relay_target_key)) {
  230 + $this->logChannel()->info('当前任务未发现转发对象!', ['广告任务ID:' . $task['id'], '询盘ID:' . $form->id]);
  231 + $form->status = ReInquiryForm::STATUS_FORGO;
  232 + $form->remark = '当前任务未发现转发对象,广告ID: ' . $form->ad_id . '!';
  233 + $form->save();
  234 + return false;
  235 + }
  236 +
  237 + foreach ($relay_target_key as $key) {
  238 + // 推送站点
  239 + $domain = $task['target'][$key]['url'];
  240 + $is_v6 = $task['target'][$key]['is_v6'];
  241 + $re_website = 'https://' . $domain . '/';
  242 +
  243 + $this->output('转发对象:' . $domain);
  244 + $this->output('获取转发链接');
  245 + // v6:有邮箱推送主站,没有邮箱推送AMP站;v5:仅推送有邮箱到主站
  246 + $lang = '';
  247 + if ($is_v6) {
  248 + // 获取语种, 6.0是可以确定语种的
  249 + $project = Project::getProjectByDomain($domain);
  250 + if (empty($project)) {
  251 + $this->logChannel()->info('广告任务ID:' . $task['id'] . ', 转发对象:' . $re_website . '非v6链接,转发失败;', ['广告任务ID:' . $task['id'], '询盘ID:' . $form->id]);
  252 + continue;
  253 + }
  254 + $lang = WebLanguage::getLangById($project->main_lang_id ?? 1)['short'];
  255 +
  256 + // 获取访问明细和着陆页
  257 + $product_url = $this->getLinksFromSitemap($re_website . 'product_sitemap.xml');
  258 + $product_cate_url = $this->getLinksFromSitemap($re_website . 'product_category_sitemap.xml');
  259 + $keywords_url = $this->getLinksFromSitemap($re_website . 'product_keywords_sitemap.xml');
  260 + $page_url = $this->getLinksFromSitemap($re_website . 'page_sitemap.xml');
  261 + } else {
  262 + if($form->email){
  263 + //通过sitemap拿访问页面
  264 + $product_url = $this->getLinksFromSitemap($re_website . 'sitemap_post.xml');
  265 + $product_cate_url = $this->getLinksFromSitemap($re_website . 'sitemap_category.xml');
  266 + $keywords_url = $this->getLinksFromSitemap($re_website . 'sitemap_post_tag.xml');
  267 + $page_url = $this->getLinksFromSitemap($re_website . 'sitemap_page.xml');
  268 + }else{
  269 + //m站先就往contact-us着陆
  270 + $product_url = $product_cate_url = $keywords_url = [];
  271 + $page_url = [$re_website . 'contact-us/'];
  272 + }
  273 + }
  274 + // 所有可用url
  275 + $urls = $inquiry_urls = [];
  276 + $urls[] = $inquiry_urls[] = $re_website;
  277 + $all_urls = array_merge($urls, $product_url, $product_cate_url, $keywords_url, $page_url);
  278 + $inquiry_urls = array_merge($urls, $product_cate_url, $keywords_url, $page_url);
  279 +
  280 + // 随机访问1-3个页面
  281 + $visit_urls = Arr::random($all_urls, rand(1, count($all_urls) > 3 ? 3 : count($all_urls)));
  282 + $urls = array_merge($urls, $visit_urls);
  283 + // 推送着落页只能是 首页、产品分类、单页面、聚合页
  284 + if(!in_array(end($urls), $inquiry_urls)){
  285 + $urls[] = Arr::random($inquiry_urls);
  286 + }
  287 +
  288 + $this->output('获取转发ip');
  289 + // TODO 获取IP:如果是简码,查询数据库获取对应的国家, 如果是国家使用翻译, 再转化成IP
  290 + if (strlen($form->country) == 2) {
  291 + $country = $this->getCountry($form->country);
  292 + } else {
  293 + $country = Translate::tran($form->country, 'zh');
  294 + }
  295 + // 有国家 通过国家查询, 如果没有获取到就随机获取
  296 + $where = [];
  297 + $country && $where['ip_area'] = $country;
  298 + $ip_data = DB::table('gl_xunpan_ipdata')->where($where)->inRandomOrder()->first();
  299 + if (empty($ip_data)) {
  300 + $ip_data = DB::table('gl_xunpan_ipdata')->inRandomOrder()->first();
  301 + }
  302 + $ip = $ip_data->ip;
  303 + $country_name = $ip_data->ip_area;
  304 +
  305 + $this->output('转发内容');
  306 + // 推送消息 消息内容小于10个字符, 使用内置询盘内容
  307 + $message = $form->message;
  308 + $message_id = 0;
  309 + if (strlen($message) < 10) {
  310 + $use_ids = ReInquiryDetail::where(['re_website' => $domain])->where('status', '<>', ReInquiryDetail::STATUS_FAIL)->pluck('text_id')->toArray();
  311 + $text = ReInquiryText::whereNotIn('id', $use_ids)->where('status', ReInquiryText::STATUS_USABLE)->inRandomOrder()->first();
  312 + $message = $text->content;
  313 + $message_id = $text->id;
  314 + // 获取后,使用次数+1
  315 + $text->use_time += 1;
  316 + $text->save();
  317 + }
  318 +
  319 + $this->output('获取转发设备信息');
  320 + // 客户端 头信息 来源
  321 + $device_port = $form->email ? '1' : '2'; //1 pc 2移动端
  322 + $user_agent = $form->email ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua);
  323 + $referrer = $this->getReferer($country_name, $lang);
  324 + $this->output('写入数据');
  325 +
  326 + $pre = 0;
  327 + $start_time = time();
  328 + $seconds = rand(300, 900); // 开始时间 从5-15分钟后开始
  329 + // 写入推送详情
  330 + $re_detail = ReInquiryDetail::createInquiry($task['id'], $form->id, $domain, $country_name, $ip, $form->full_name, $form->email, $form->phone, $message, $message_id, $device_port,
  331 + $user_agent, $referrer, $urls, $is_v6, date('Y-m-d H:i:s', $start_time + $seconds));
  332 + foreach ($urls as $k=>$v){
  333 + $pre++;
  334 + $seconds += rand(3,10);
  335 + ReInquiryDetailLog::createInquiryLog($re_detail->id, ReInquiryDetailLog::TYPE_VISIT, $pre, $v, date('Y-m-d H:i:s', $start_time + $seconds));
  336 + // 最后一次访问询盘 加上询盘
  337 + if($k+1 >= count($urls)){
  338 + $seconds += rand(10,30);
  339 + $pre++;
  340 + ReInquiryDetailLog::createInquiryLog($re_detail->id, ReInquiryDetailLog::TYPE_INQUIRY, $pre, $v, date('Y-m-d H:i:s', $start_time + $seconds));
  341 + }
  342 + }
  343 + }
  344 +
  345 + $form->status = ReInquiryForm::STATUS_SUCCESS;
  346 + $form->save();
  347 + return true;
  348 + }
  349 +
  350 + /**
  351 + * 获取待处理询盘表单
  352 + * @param int $num
  353 + * @return mixed
  354 + */
  355 + public function getInquiry($num = 10)
  356 + {
  357 + $result = ReInquiryForm::where(['status' => ReInquiryForm::STATUS_INIT])->orderBy('id', 'asc')->limit($num)->get();
  358 + return $result;
  359 + }
  360 +
  361 + /**
  362 + * 获取广告任务配置数据
  363 + * @param $ad_id
  364 + * @return array
  365 + */
  366 + public function getAdTask($ad_id)
  367 + {
  368 + $cache_key = 'inquiry_ads_task';
  369 + $ads = Cache::get($cache_key, function () use ($cache_key) {
  370 + $ads = ReInquiryTask::where(['status' => ReInquiryTask::STATUS_OPEN])->get(['id', 'ad_id', 'num', 'target']);
  371 + $array = [];
  372 + foreach ($ads as $key=>$val) {
  373 + $array[$val->ad_id] = $val;
  374 + }
  375 + if ($array)
  376 + Cache::put($cache_key, $array, 60);
  377 + return $array;
  378 + });
  379 + return empty($ads[$ad_id]) ? [] : $ads[$ad_id];
  380 + }
  381 +
  382 + /**
  383 + * 通过国家简码获取国家名称
  384 + * @param $code
  385 + * @return string
  386 + */
  387 + public function getCountry($code)
  388 + {
  389 + $cache_key = 'inquiry_world_country';
  390 + $country_code = Cache::get($cache_key, function () use ($cache_key) {
  391 + $country_code = DB::table('gl_world_country_city')->where(['pid' => 0])->pluck('chinese_name', 'iso2')->toArray();
  392 + Cache::put($cache_key, $country_code, 86400);
  393 + return $country_code;
  394 + });
  395 + return empty($country_code[$code]) ? '' : $country_code[$code];
  396 + }
  397 +
  398 + /**
  399 + * 获取头信息
  400 + * @param $ip_area
  401 + * @param $lang
  402 + * @return int|string
  403 + */
  404 + public function getReferer($ip_area, $lang)
  405 + {
  406 + if($lang == 'ru'){
  407 + return $this->get_rand($this->eylyzb);
  408 + }
  409 + if($ip_area == '美国'){
  410 + $referer = $this->get_rand($this->lyzb);
  411 + }else{
  412 + $referer = 'https://www.google.com/';
  413 + $suffix = array_search($ip_area, $this->suffix);
  414 + if($suffix){
  415 + $res_qtzb = $this->get_rand($this->otherzb);
  416 + if($res_qtzb == 1){
  417 + $referer = 'https://www.google.'.$suffix.'/';
  418 + }
  419 + }
  420 + }
  421 + return $referer;
  422 + }
  423 +
  424 + /**
  425 + * 概率算法
  426 + * @param $proArr
  427 + * @return int|string
  428 + */
  429 + protected function get_rand($proArr)
  430 + {
  431 + $result = '';
  432 + $proSum = array_sum($proArr);
  433 + foreach ($proArr as $key => $proCur) {
  434 + $randNum = mt_rand(1, $proSum);
  435 + if ($randNum <= $proCur) {
  436 + $result = $key;
  437 + break;
  438 + } else {
  439 + $proSum -= $proCur;
  440 + }
  441 + }
  442 + unset ($proArr);
  443 + return $result;
  444 + }
  445 +
  446 + /**
  447 + * 获取sitemap内容
  448 + * @param $sitemapUrl
  449 + * @return array|mixed
  450 + */
  451 + function getLinksFromSitemap($sitemapUrl) {
  452 + try {
  453 + //忽略cert证书 先下载到临时文件
  454 + $result = Http::withoutVerifying()->get($sitemapUrl)->body();
  455 + $tempFilePath = tempnam(sys_get_temp_dir(), 'remote_file_');
  456 + file_put_contents($tempFilePath, $result);
  457 + $xml = simplexml_load_file($tempFilePath);
  458 + $links = [];
  459 + foreach ($xml->url as $url) {
  460 + $loc = (string) $url->loc;
  461 + if(!Str::contains($loc, ['404', 'thanks'])){
  462 + $links[] = $loc;
  463 + }
  464 + }
  465 + //随机取20个
  466 + $total = count($links);
  467 + return Arr::random($links, $total > 20 ? 20 : $total);
  468 + }catch (\Exception $e){
  469 + echo date('Y-m-d H:i:s') . 'sitemap获取失败:' . $e->getMessage() . PHP_EOL;
  470 + return $links??[];
  471 + }
  472 + }
  473 +
  474 + /**
  475 + * @return \Psr\Log\LoggerInterface
  476 + */
  477 + public function logChannel()
  478 + {
  479 + return Log::channel('inquiry_relay');
  480 + }
  481 +
  482 + public function output($message)
  483 + {
  484 + echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL;
  485 + }
  486 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/09/30
  6 + * Time: 14:01
  7 + */
  8 +namespace App\Console\Commands\Inquiry;
  9 +
  10 +use App\Enums\Common\Code;
  11 +use App\Helper\Arr;
  12 +use App\Models\Inquiry\ReInquiryForm;
  13 +use App\Utils\HttpUtils;
  14 +use GuzzleHttp\Exception\GuzzleException;
  15 +use Illuminate\Console\Command;
  16 +
  17 +/**
  18 + * Class SyncInquiry
  19 + * @package App\Console\Commands\Inquiry
  20 + */
  21 +class SyncInquiry extends Command
  22 +{
  23 + /**
  24 + * The name and signature of the console command.
  25 + *
  26 + * @var string
  27 + */
  28 + protected $signature = 'sync_inquiry';
  29 +
  30 + /**
  31 + * The console command description.
  32 + *
  33 + * @var string
  34 + */
  35 + protected $description = '同步询盘信息';
  36 +
  37 + /**
  38 + * Create a new command instance.
  39 + *
  40 + * @return void
  41 + */
  42 + public function __construct()
  43 + {
  44 + parent::__construct();
  45 + }
  46 +
  47 + /**
  48 + * TODO 定时任务,去同步询盘列表
  49 + * FIXME 不在同步时直接拆分转发目标, 是考虑到之后有其他渠道也需要转发, 就只需要在询盘表添加数据即可完成转发功能
  50 + * @return bool
  51 + */
  52 + public function handle()
  53 + {
  54 + while (true) {
  55 + $result = $this->getInquiry();
  56 + if (!$result){
  57 + //5分钟同步一次
  58 + sleep(300);
  59 + }
  60 + // 询盘数据入库
  61 + foreach ($result as $key=>$val) {
  62 + ReInquiryForm::createInquiry($val['id'], $val['origin_id'], $val['adset_id'], $val['adset_name'], $val['ad_id'], $val['ad_name'], $val['full_name'], $val['email'], $val['phone_number'], $val['whatapp'], $val['ai_inquery'], $val['country'], $val['inquiry_time']);
  63 + }
  64 + exit;
  65 + }
  66 + return true;
  67 + }
  68 +
  69 + /**
  70 + * 获取远程询盘信息
  71 + * @return array
  72 + * @throws \GuzzleHttp\Exception\GuzzleException
  73 + */
  74 + public function getInquiry()
  75 + {
  76 + $last_origin_id = ReInquiryForm::orderBy('id', 'desc')->value('id') ?: 0;
  77 + try {
  78 + $res = HttpUtils::get('https://fob.ai.cc/api/fb_inquery_lists', ['order'=> 'asc', 'id' => $last_origin_id, 'limit' => 100]);
  79 + $res = Arr::s2a($res);
  80 + } catch (\Exception | GuzzleException $e) {
  81 + $this->output($e->getMessage());
  82 + return [];
  83 + }
  84 + return $res['data']['data'] ?? [];
  85 + }
  86 +
  87 +
  88 + public function output($message)
  89 + {
  90 + echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL;
  91 + }
  92 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/10/8
  6 + * Time: 15:32
  7 + */
  8 +namespace App\Http\Controllers\Aside\Task;
  9 +
  10 +use App\Enums\Common\Code;
  11 +use App\Helper\Arr;
  12 +use App\Http\Controllers\Aside\BaseController;
  13 +use App\Models\Channel\Channel;
  14 +use App\Models\Domain\DomainInfo;
  15 +use App\Models\Inquiry\ReInquiryDetail;
  16 +use App\Models\Inquiry\ReInquiryForm;
  17 +use App\Models\Inquiry\ReInquiryTask;
  18 +use App\Models\Project\Project;
  19 +use App\Utils\HttpUtils;
  20 +use GuzzleHttp\Exception\GuzzleException;
  21 +use Illuminate\Http\Request;
  22 +use Illuminate\Support\Facades\Http;
  23 +
  24 +/**
  25 + * Class AdsController
  26 + * @package App\Http\Controllers\Aside\Special
  27 + */
  28 +class AdsController extends BaseController
  29 +{
  30 +
  31 + /**
  32 + * 广告列表
  33 + * @param Request $request
  34 + * @return \Illuminate\Http\JsonResponse
  35 + */
  36 + public function fbAdsList(Request $request)
  37 + {
  38 + $ads_id = trim($request->input('ads_id'));
  39 + $industry = trim($request->input('industry'));
  40 + $result = ReInquiryTask::where(['status' => ReInquiryTask::STATUS_OPEN])
  41 + ->when($ads_id, function ($query, $ads_id) {
  42 + return $query->where('ad_id', 'like', '%' . $ads_id . '%');
  43 + })
  44 + ->when($industry, function ($query, $industry) {
  45 + return $query->where('industry', $industry);
  46 + })
  47 + ->orderBy('id', 'desc')
  48 + ->paginate();
  49 + return $this->response('success', Code::SUCCESS, $result);
  50 + }
  51 +
  52 + /**
  53 + * 新增或修改广告
  54 + * @param Request $request
  55 + * @return \Illuminate\Http\JsonResponse
  56 + */
  57 + public function setFbAds(Request $request)
  58 + {
  59 + $id = intval($request->input('id'));
  60 + $title = trim($request->input('title'));
  61 + $industry = trim($request->input('industry'));
  62 + $ad_id = trim($request->input('ad_id'));
  63 + $ad_url = trim($request->input('ad_url'));
  64 + $ad_img = trim($request->input('ad_img'));
  65 + $num = intval($request->input('num'));
  66 + $status = intval($request->input('status'));
  67 + if (empty($title) || empty($ad_id))
  68 + return $this->response('请填写完整信息!', Code::USER_ERROR, []);
  69 +
  70 + ReInquiryTask::createTask($id, $title, $industry, $ad_id, $ad_url, $ad_img, $num, $status);
  71 + return $this->response('success', Code::SUCCESS, []);
  72 + }
  73 +
  74 + /**
  75 + * 设置转发站点
  76 + * @param Request $request
  77 + * @return \Illuminate\Http\JsonResponse
  78 + */
  79 + public function setRelaySite(Request $request)
  80 + {
  81 + $id = intval($request->input('id', 0));
  82 + $target = $request->input('target');
  83 + $task = ReInquiryTask::find($id);
  84 + if(!$task){
  85 + return $this->response('广告不存在!', Code::USER_ERROR, []);
  86 + }
  87 + if(empty($target)){
  88 + return $this->response('请添加关联网站!', Code::USER_ERROR, []);
  89 + }
  90 + foreach ($target as &$item){
  91 + if(empty($item['url'])){
  92 + return $this->response('网站域名不能为空!', Code::USER_ERROR, []);
  93 + }
  94 + if(empty($item['agent'])){
  95 + return $this->response('代理不能为空!', Code::USER_ERROR, []);
  96 + }
  97 + $item['url'] = trim(str_replace(['http://', 'https://'], '', $item['url']), '/');
  98 + }
  99 + $task->target = json_encode($target);
  100 + $task->save();
  101 + return $this->response('success', Code::SUCCESS, []);
  102 + }
  103 +
  104 + /**
  105 + * 验证站点5.0还是6.0, 代理商信息
  106 + * @param Request $request
  107 + */
  108 + public function checkDomain(Request $request)
  109 + {
  110 + $domain = $request->input('domain');
  111 + $domain = trim(str_replace(['http://', 'https://'], '', $domain), '/');
  112 + //是否v6
  113 + $domain_info = DomainInfo::where('domain', $domain)->first();
  114 + if($domain_info){
  115 + $channel = Project::where('id', $domain_info['project_id'])->value('channel');
  116 + $data = [
  117 + 'is_v6' => 1,
  118 + 'agent' => Channel::getChannelText($channel['channel']['user_id']??0)
  119 + ];
  120 + return $this->response('success', Code::SUCCESS, $data);
  121 + }
  122 +
  123 + $token = md5($domain.'qqs');
  124 + try {
  125 + $res = HttpUtils::get('https://quanqiusou.cn/extend_api/api/get_agent_by_domain.php', ['token' => $token, 'domain' => $domain]);
  126 + $res = Arr::s2a($res);
  127 + } catch (\Exception | GuzzleException $e) {
  128 + return $this->response('验证失败,请稍后再试!', Code::USER_ERROR, []);
  129 + }
  130 + if(empty($res['status']) || $res['status'] != 200){
  131 + return $this->response($res['msg'], Code::USER_ERROR, []);
  132 + }
  133 + $data = [
  134 + 'is_v6' => 0,
  135 + 'agent' => $res['data']
  136 + ];
  137 + return $this->response('success', Code::SUCCESS, $data);
  138 + }
  139 +
  140 + /**
  141 + * FB广告产生询盘列表
  142 + * @param Request $request
  143 + * @return \Illuminate\Http\JsonResponse
  144 + */
  145 + public function fbInquiryList(Request $request)
  146 + {
  147 + $status = intval($request->input('status'));
  148 + $result = ReInquiryForm::when($status, function ($query, $status) {
  149 + return $query->where('status', $status);
  150 + })
  151 + ->orderBy('id', 'desc')
  152 + ->paginate();
  153 + return $this->response('success', Code::SUCCESS, $result);
  154 + }
  155 +
  156 + /**
  157 + * FB询盘转发详情
  158 + * @param Request $request
  159 + * @return \Illuminate\Http\JsonResponse
  160 + */
  161 + public function fbRelayDetail(Request $request)
  162 + {
  163 + $status = intval($request->input('status'));
  164 + $form_id = intval($request->input('form_id'));
  165 + $result = ReInquiryDetail::with('detailLog')
  166 + ->when($status, function ($query, $status) {
  167 + return $query->where('status', $status);
  168 + })
  169 + ->when($form_id, function ($query, $form_id) {
  170 + return $query->where('form_id', $form_id);
  171 + })
  172 + ->orderBy('id', 'desc')
  173 + ->paginate();
  174 + return $this->response('success', Code::SUCCESS, $result);
  175 + }
  176 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/9/30
  6 + * Time: 14:56
  7 + */
  8 +namespace App\Models\Inquiry;
  9 +
  10 +use Illuminate\Database\Eloquent\Model;
  11 +
  12 +/**
  13 + * 询盘转发详情
  14 + * Class ReInquiryDetail
  15 + * @package App\Models\Inquiry
  16 + */
  17 +class ReInquiryDetail extends Model
  18 +{
  19 + /**
  20 + * @var string
  21 + */
  22 + protected $table = 'gl_re_inquiry_detail';
  23 +
  24 + /**
  25 + * 任务状态, 0:初始化,1:待处理,2:处理成功,3:抛弃数据,9:处理失败
  26 + */
  27 + const STATUS_INIT = 0;
  28 + const STATUS_PEND = 1;
  29 + const STATUS_SUCCESS = 2;
  30 + const STATUS_FORGO = 3;
  31 + const STATUS_FAIL = 9;
  32 +
  33 + /**
  34 + * 状态映射
  35 + * @return array
  36 + */
  37 + public static function statusMap()
  38 + {
  39 + return [
  40 + self::STATUS_INIT => '初始化',
  41 + self::STATUS_PEND => '待处理',
  42 + self::STATUS_SUCCESS => '处理成功',
  43 + self::STATUS_FORGO => '抛弃数据',
  44 + self::STATUS_FAIL => '处理失败',
  45 + ];
  46 + }
  47 +
  48 + /**
  49 + * 创建询盘转发详情待处理任务
  50 + * @param $task_id
  51 + * @param $form_id
  52 + * @param $re_website
  53 + * @param $country
  54 + * @param $ip
  55 + * @param $name
  56 + * @param $email
  57 + * @param $phone
  58 + * @param $message
  59 + * @param $text_id
  60 + * @param $device_port
  61 + * @param $user_agent
  62 + * @param $referrer
  63 + * @param $urls
  64 + * @param $is_v6
  65 + * @param $start_at
  66 + * @param int $status
  67 + * @return ReInquiryDetail
  68 + */
  69 + public static function createInquiry($task_id, $form_id, $re_website, $country, $ip, $name, $email, $phone, $message, $text_id, $device_port,
  70 + $user_agent, $referrer, $urls, $is_v6, $start_at, $status = self::STATUS_INIT)
  71 + {
  72 + $self = new self();
  73 + $self->task_id = $task_id;
  74 + $self->form_id = $form_id;
  75 + $self->re_website = $re_website;
  76 + $self->country = $country;
  77 + $self->ip = $ip;
  78 + $self->name = $name;
  79 + $self->email = $email;
  80 + $self->phone = $phone;
  81 + $self->message = $message;
  82 + $self->text_id = $text_id;
  83 + $self->device_port = $device_port;
  84 + $self->user_agent = $user_agent;
  85 + $self->referrer = $referrer;
  86 + $self->urls = json_encode($urls);
  87 + $self->is_v6 = $is_v6;
  88 + $self->num = count($urls) + 1;
  89 + $self->start_at = $start_at;
  90 + $self->status = $status;
  91 + $self->save();
  92 + return $self;
  93 + }
  94 +
  95 + /**
  96 + * 转发日志详情
  97 + * @return \Illuminate\Database\Eloquent\Relations\HasMany
  98 + */
  99 + public function detailLog()
  100 + {
  101 + return $this->hasMany(ReInquiryDetailLog::class, 'detail_id', 'id');
  102 + }
  103 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/10/08
  6 + * Time: 14:10
  7 + */
  8 +namespace App\Models\Inquiry;
  9 +
  10 +use Illuminate\Database\Eloquent\Model;
  11 +
  12 +/**
  13 + * 转发详情日志
  14 + * Class ReInquiryDetailLog
  15 + * @package App\Models\Inquiry
  16 + */
  17 +class ReInquiryDetailLog extends Model
  18 +{
  19 + /**
  20 + * @var string
  21 + */
  22 + protected $table = 'gl_re_inquiry_detail_log';
  23 +
  24 + /**
  25 + * 任务状态, 0:初始化,1:待处理,2:处理成功,9:处理失败
  26 + */
  27 + const STATUS_INIT = 0;
  28 + const STATUS_PEND = 1;
  29 + const STATUS_SUCCESS = 2;
  30 + const STATUS_FAIL = 9;
  31 +
  32 + /**
  33 + * 执行类型, 1:访问, 2:询盘
  34 + */
  35 + const TYPE_VISIT = 1;
  36 + const TYPE_INQUIRY = 2;
  37 +
  38 + /**
  39 + * 状态映射
  40 + * @return array
  41 + */
  42 + public static function statusMap()
  43 + {
  44 + return [
  45 + self::STATUS_INIT => '初始化',
  46 + self::STATUS_PEND => '待处理',
  47 + self::STATUS_SUCCESS => '处理成功',
  48 + self::STATUS_FAIL => '处理失败',
  49 + ];
  50 + }
  51 +
  52 +
  53 + /**
  54 + * 创建转发详情日志
  55 + * @param $detail_id
  56 + * @param $type
  57 + * @param $pre
  58 + * @param $url
  59 + * @param $start_at
  60 + * @return ReInquiryDetailLog
  61 + */
  62 + public static function createInquiryLog($detail_id, $type, $pre, $url, $start_at)
  63 + {
  64 + $self = self::where(compact('detail_id', 'type', 'pre'))->first();
  65 + if ($self)
  66 + return $self;
  67 + $self = new self();
  68 + $self->detail_id = $detail_id;
  69 + $self->type = $type;
  70 + $self->pre = $pre;
  71 + $self->url = $url;
  72 + $self->start_at = $start_at;
  73 + $self->save();
  74 + return $self;
  75 + }
  76 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/9/30
  6 + * Time: 14:31
  7 + */
  8 +namespace App\Models\Inquiry;
  9 +
  10 +use Illuminate\Database\Eloquent\Model;
  11 +
  12 +/**
  13 + * 广告询盘表单内容
  14 + * Class ReInquiryForm
  15 + * @package App\Models\Inquiry
  16 + */
  17 +class ReInquiryForm extends Model
  18 +{
  19 + /**
  20 + * @var string
  21 + */
  22 + protected $table = 'gl_re_inquiry_form';
  23 +
  24 + /**
  25 + * 任务状态, 1:待处理,2:处理成功,3:抛弃数据,9:处理失败
  26 + */
  27 + const STATUS_INIT = 1;
  28 + const STATUS_SUCCESS = 2;
  29 + const STATUS_FORGO = 3;
  30 + const STATUS_FAIL = 9;
  31 +
  32 + /**
  33 + * 状态映射
  34 + * @return array
  35 + */
  36 + public static function statusMap()
  37 + {
  38 + return [
  39 + self::STATUS_INIT => '待处理',
  40 + self::STATUS_SUCCESS => '处理成功',
  41 + self::STATUS_FORGO => '抛弃数据',
  42 + self::STATUS_FAIL => '处理失败',
  43 + ];
  44 + }
  45 +
  46 + /**
  47 + * 创建询盘表单信息
  48 + * @param $id
  49 + * @param $origin_id
  50 + * @param $ad_set_id
  51 + * @param $ad_set_name
  52 + * @param $ad_id
  53 + * @param $ad_name
  54 + * @param $full_name
  55 + * @param $email
  56 + * @param $phone
  57 + * @param $whatsapp
  58 + * @param $message
  59 + * @param $country
  60 + * @param $inquiry_date
  61 + * @return mixed
  62 + */
  63 + public static function createInquiry($id, $origin_id, $ad_set_id, $ad_set_name, $ad_id, $ad_name, $full_name, $email, $phone, $whatsapp, $message, $country, $inquiry_date)
  64 + {
  65 + $self = self::where(['id' => $id])->first();
  66 + if (empty($self)) {
  67 + $self = new self();
  68 + // 同步 表单内容,以ID为唯一值
  69 + $self->id = $id;
  70 + }
  71 + $self->origin_id = $origin_id;
  72 + $self->ad_set_id = $ad_set_id;
  73 + $self->ad_set_name = $ad_set_name;
  74 + $self->ad_id = $ad_id;
  75 + $self->ad_name = $ad_name;
  76 + $self->full_name = $full_name;
  77 + $self->email = $email;
  78 + $self->phone = $phone;
  79 + $self->whatsapp = $whatsapp;
  80 + $self->message = $message;
  81 + $self->inquiry_date = $inquiry_date;
  82 + $self->country = $country;
  83 + $self->save();
  84 + return $self;
  85 + }
  86 +
  87 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/9/30
  6 + * Time: 14:12
  7 + */
  8 +namespace App\Models\Inquiry;
  9 +
  10 +use App\Models\Base;
  11 +use Illuminate\Database\Eloquent\Model;
  12 +
  13 +/**
  14 + * 广告转询盘任务
  15 + * Class ReInquiryTask
  16 + * @package App\Models\Inquiry
  17 + */
  18 +class ReInquiryTask extends Base
  19 +{
  20 + /**
  21 + * @var string
  22 + */
  23 + protected $table = 'gl_re_inquiry_task';
  24 +
  25 + /**
  26 + * 任务状态, 1:开启(默认), 0:关闭
  27 + */
  28 + const STATUS_OPEN = 1;
  29 + const STATUS_CLOSE = 0;
  30 +
  31 + /**
  32 + * 创建询盘任务
  33 + * @param $id
  34 + * @param $title
  35 + * @param $industry
  36 + * @param $ad_id
  37 + * @param $ad_url
  38 + * @param $ad_img
  39 + * @param $num
  40 + * @param int $status
  41 + * @return ReInquiryTask
  42 + */
  43 + public static function createTask($id, $title, $industry, $ad_id, $ad_url, $ad_img, $num, $status = self::STATUS_OPEN)
  44 + {
  45 + $self = self::where(['id' => $id])->first();
  46 + if (empty($self))
  47 + $self = new self();
  48 + $self->title = $title;
  49 + $self->industry = $industry;
  50 + $self->ad_id = $ad_id;
  51 + $self->ad_url = $ad_url;
  52 + $self->ad_img = $ad_img;
  53 + $self->num = $num;
  54 + $self->status = $status;
  55 + $self->save();
  56 + return $self;
  57 + }
  58 +
  59 + public function setTarget($id, $target){
  60 + $self = self::where(['id' => $id])->first();
  61 + if (empty($self)) {
  62 +
  63 + }
  64 + }
  65 +
  66 + /**
  67 + * @param $value
  68 + * @return mixed
  69 + */
  70 + public function getTargetAttribute($value)
  71 + {
  72 + return json_decode($value, true);
  73 + }
  74 +}
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/9/30
  6 + * Time: 14:23
  7 + */
  8 +namespace App\Models\Inquiry;
  9 +
  10 +use Illuminate\Database\Eloquent\Model;
  11 +
  12 +/**
  13 + * 询盘内置文案
  14 + * Class ReInquiryText
  15 + * @package App\Models\Inquiry
  16 + */
  17 +class ReInquiryText extends Model
  18 +{
  19 + /**
  20 + * @var string
  21 + */
  22 + protected $table = 'gl_re_inquiry_text';
  23 +
  24 + /**
  25 + * 文案状态, 1:开启(默认), 2:禁用
  26 + */
  27 + const STATUS_USABLE = 1;
  28 + const STATUS_DISABLE = 2;
  29 +
  30 + /**
  31 + * 创建询盘内置文案
  32 + * @param $id
  33 + * @param $title
  34 + * @param $content
  35 + * @param int $status
  36 + * @return ReInquiryText
  37 + */
  38 + public static function createText($id, $title, $content, $status = self::STATUS_USABLE)
  39 + {
  40 + $self = self::where(['id' => $id])->first();
  41 + if (empty($self)) {
  42 + $self = new self();
  43 + // 同步 询盘文案,以ID为唯一值
  44 + $self->id = $id;
  45 + }
  46 + $self->title = $title;
  47 + $self->content = $content;
  48 + $self->status = $status;
  49 + $self->save();
  50 + return $self;
  51 + }
  52 +
  53 +}
@@ -172,6 +172,13 @@ return [ @@ -172,6 +172,13 @@ return [
172 'emergency' => [ 172 'emergency' => [
173 'path' => storage_path('logs/laravel.log'), 173 'path' => storage_path('logs/laravel.log'),
174 ], 174 ],
  175 +
  176 + 'inquiry_relay' => [
  177 + 'driver' => 'daily',
  178 + 'path' => storage_path('logs/inquiry_relay/laravel.log'),
  179 + 'level' => 'debug',
  180 + 'days' => 14,
  181 + ],
175 ], 182 ],
176 //操作日志 183 //操作日志
177 'operator_log' =>[ 184 'operator_log' =>[
@@ -480,6 +480,17 @@ Route::middleware(['aloginauth'])->group(function () { @@ -480,6 +480,17 @@ Route::middleware(['aloginauth'])->group(function () {
480 Route::any('/', [Aside\PackDir\PackDirController::class, 'getTaskLists'])->name('admin.pack_dir_getTaskLists'); 480 Route::any('/', [Aside\PackDir\PackDirController::class, 'getTaskLists'])->name('admin.pack_dir_getTaskLists');
481 Route::any('/saveTask', [Aside\PackDir\PackDirController::class, 'saveTask'])->name('admin.pack_dir_saveTask'); 481 Route::any('/saveTask', [Aside\PackDir\PackDirController::class, 'saveTask'])->name('admin.pack_dir_saveTask');
482 }); 482 });
  483 +
  484 + // 任务相关
  485 + Route::prefix('task')->group(function () {
  486 + // FB广告相关路由
  487 + Route::any('/fb_list', [Aside\Task\AdsController::class, 'fbAdsList'])->name('admin.fb_ads_task_list');
  488 + Route::any('/fb_set', [Aside\Task\AdsController::class, 'setFbAds'])->name('admin.fb_ads_task_set');
  489 + Route::any('/fb_set_site', [Aside\Task\AdsController::class, 'setRelaySite'])->name('admin.fb_set_site');
  490 + Route::any('/fb_check_domain', [Aside\Task\AdsController::class, 'checkDomain'])->name('admin.fb_check_domain');
  491 + Route::any('/fb_inquiry_list', [Aside\Task\AdsController::class, 'fbInquiryList'])->name('admin.fb_ads_inquiry_list');
  492 + Route::any('/fb_relay_detail_list', [Aside\Task\AdsController::class, 'fbRelayDetail'])->name('admin.fb_ads_relay_detail_list');
  493 + });
483 }); 494 });
484 495
485 //无需登录验证的路由组 496 //无需登录验证的路由组