作者 赵彬吉

update

@@ -210,9 +210,11 @@ class WebTrafficRussia extends Command @@ -210,9 +210,11 @@ class WebTrafficRussia extends Command
210 } 210 }
211 $need_project = []; 211 $need_project = [];
212 foreach ($project_list as $project) { 212 foreach ($project_list as $project) {
  213 + Log::channel('traffic')->info('开始俄语站引流 project_id:' . $project['project_id']);
213 //随机引流间隔 214 //随机引流间隔
214 $res_sjjg = $this->get_rand($this->sjjg); 215 $res_sjjg = $this->get_rand($this->sjjg);
215 if ($res_sjjg == 1) { 216 if ($res_sjjg == 1) {
  217 + Log::channel('traffic')->info('随机不俄语站引流 project_id:' . $project['project_id']);
216 continue; 218 continue;
217 } 219 }
218 220
@@ -260,6 +262,7 @@ class WebTrafficRussia extends Command @@ -260,6 +262,7 @@ class WebTrafficRussia extends Command
260 } 262 }
261 //不访问 263 //不访问
262 if($res == 1){ 264 if($res == 1){
  265 + Log::channel('traffic')->info('特殊日期随机不引流 project_id:' . $project['project_id']);
263 unset($need_project[$project_key]); 266 unset($need_project[$project_key]);
264 unset($ips[$project_key]); 267 unset($ips[$project_key]);
265 } 268 }
  1 +<?php
  2 +
  3 +namespace App\Console\Commands;
  4 +
  5 +use App\Helper\Arr;
  6 +use App\Models\Product\Category;
  7 +use App\Models\Product\Product;
  8 +use App\Models\Project\OnlineCheck;
  9 +use App\Models\Project\Project;
  10 +use App\Models\Template\BCustomTemplate;
  11 +use App\Models\WebSetting\WebLanguage;
  12 +use App\Services\ProjectServer;
  13 +use Carbon\Carbon;
  14 +use GuzzleHttp\Client;
  15 +use GuzzleHttp\Promise\Utils;
  16 +use Illuminate\Console\Command;
  17 +use Illuminate\Support\Facades\Cache;
  18 +use Illuminate\Support\Facades\DB;
  19 +use Illuminate\Support\Facades\Log;
  20 +use Illuminate\Support\Str;
  21 +
  22 +/**
  23 + * 俄语站网站特殊引流
  24 + * Class Traffic
  25 + * @package App\Console\Commands
  26 + * @author zbj
  27 + * @date 2023/5/18
  28 + */
  29 +class WebTrafficRussiaSpecial extends Command
  30 +{
  31 + /**
  32 + * The name and signature of the console command.
  33 + *
  34 + * @var string
  35 + */
  36 + protected $signature = 'web_traffic_russia_special';
  37 +
  38 + /**
  39 + * The console command description.
  40 + *
  41 + * @var string
  42 + */
  43 + protected $description = '俄语站网站特殊引流';
  44 +
  45 + /**
  46 + * Create a new command instance.
  47 + *
  48 + * @return void
  49 + */
  50 + public function __construct()
  51 + {
  52 + parent::__construct();
  53 + }
  54 +
  55 + protected $projects = [
  56 + 969 => 30,
  57 + ];
  58 +
  59 + /**
  60 + * 国家概率
  61 + * @var array
  62 + */
  63 + protected $country_ratio = [
  64 + '俄罗斯' => 75,
  65 + '白俄罗斯' => 6,
  66 + '哈萨克斯坦' => 6,
  67 + '乌兹别克斯坦' => 2,
  68 + '塔吉克斯坦' => 2,
  69 + '土耳其' => 2,
  70 + '吉尔吉斯坦' => 1,
  71 + '土库曼斯坦' => 1,
  72 + '乌克兰' => 1,
  73 + '亚美尼亚' => 2,
  74 + '阿塞拜疆' => 2,
  75 + '摩尔多瓦' => 2,
  76 + '格鲁吉亚' => 2,
  77 + '美国' => 1,
  78 + '德国' => 1,
  79 + '法国' => 1,
  80 + '意大利' => 1,
  81 + '加拿大' => 1,
  82 + '西班牙' => 1,
  83 + '葡萄牙' => 1,
  84 + '韩国' => 1,
  85 + '日本' => 1,
  86 + '印度' => 1,
  87 + '菲律宾' => 1,
  88 + '越南' => 1,
  89 + '荷兰' => 1,
  90 + '瑞典' => 1,
  91 + '泰国' => 1,
  92 + '英国' => 1,
  93 + ];
  94 +
  95 + /**
  96 + * google域名后缀
  97 + * @var string[]
  98 + */
  99 + protected $suffix = [
  100 + 'co.jp' => '日本',
  101 + 'com.tr' => '土耳其',
  102 + 'nl' => '荷兰',
  103 + 'ru' => '俄罗斯',
  104 + 'fr' => '法国',
  105 + 'co.kr' => '韩国',
  106 + 'fi' => '芬兰',
  107 + 'be' => '比利时',
  108 + 'lt' => '立陶宛',
  109 + 'es' => '西班牙',
  110 + 'it' => '意大利',
  111 + 'com.au' => '澳大利亚',
  112 + 'no' => '挪威',
  113 + 'al' => '阿尔巴尼亚',
  114 + 'pt' => '葡萄牙',
  115 + 'lv' => '拉脱维亚',
  116 + 'hu' => '匈牙利',
  117 + 'cz' => '捷克',
  118 + 'de' => '德国',
  119 + 'ca' => '加拿大',
  120 + 'co.in' => '印度',
  121 + 'co.uk' => '英国',
  122 + 'com.vn' => '越南',
  123 + 'com.br' => '巴西',
  124 + 'co.il' => '以色列',
  125 + 'pl' => '波兰',
  126 + 'com.eg' => '埃及',
  127 + 'co.th' => '泰国',
  128 + 'sk' => '斯洛伐克',
  129 + 'ro' => '罗马尼亚',
  130 + 'com.mx' => '墨西哥',
  131 + 'com.my' => '马来西亚',
  132 + 'com.pk' => '巴基斯坦',
  133 + 'co.nz' => '新西兰',
  134 + 'co.za' => '南非',
  135 + 'com.ar' => '阿根廷',
  136 + 'com.kw' => '科威特',
  137 + 'com.sg' => '新加坡',
  138 + 'com.co' => '哥伦比亚',
  139 + 'co.id' => '印度尼西亚',
  140 + 'gr' => '希腊',
  141 + 'bg' => '保加利亚',
  142 + 'mn' => '蒙古',
  143 + 'dk' => '丹麦',
  144 + 'com.sa' => '沙特阿拉伯',
  145 + 'com.pe' => '秘鲁',
  146 + 'com.ph' => '菲律宾',
  147 + 'com.ua' => '乌克兰',
  148 + 'ge' => '格鲁吉亚',
  149 + 'ae' => '阿拉伯联合酋长国',
  150 + 'tn' => '突尼斯',
  151 + ];
  152 +
  153 + /**
  154 + * 概率值
  155 + * @var int[]
  156 + */
  157 + protected $sjjg = [360, 640];//访问间隔占比 访问|不访问 比非俄语站降一般
  158 + //访问页面类型占比 产品详情页、单页|产品分类页
  159 + protected $ymzb = [
  160 + 'urls_cats' => 700,
  161 + 'urls_details' => 300
  162 + ];
  163 + protected $sdzb = [600, 200, 150, 50]; //访问页面深度占比 1页|2页|3-6页|7-11页
  164 + protected $yddzb = [1 => 700, 2 => 300]; //移动端占比 pc|mobile
  165 + //模拟访问来源占比 (美国)
  166 + protected $lyzb = [
  167 + 'https://www.google.com/' => 630,
  168 + 'http://www.google.com/' => 30,
  169 + 'http://www.bing.com/' => 20,
  170 + 'https://www.bing.com/' => 5,
  171 + 'https://www.youtube.com/' => 5,
  172 + 'https://search.yahoo.com/' => 5,
  173 + 'https://www.facebook.com/' => 5,
  174 + ];
  175 +
  176 + //俄语
  177 + protected $eylyzb = [
  178 + 'https://www.yandex.com/' => 630,
  179 + 'https://www.google.com/' => 30,
  180 + 'http://www.google.com/' => 30,
  181 + 'http://www.bing.com/' => 20,
  182 + 'https://www.bing.com/' => 5,
  183 + 'https://www.youtube.com/' => 5,
  184 + 'https://search.yahoo.com/' => 5,
  185 + 'https://www.facebook.com/' => 5,
  186 + ];
  187 +
  188 + protected $otherzb = [700, 300]; //模拟访问来源占比 (非美国) google.com|google.其他后缀
  189 +
  190 + protected $pc_ua = [
  191 + 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',
  192 + 1 => 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
  193 + 2 => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'
  194 + ];
  195 +
  196 + protected $mobile_ua = [
  197 + 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',
  198 + ];
  199 +
  200 + /**
  201 + * @return bool
  202 + */
  203 + public function handle()
  204 + {
  205 + try {
  206 + $this->sleep();
  207 +
  208 + $page = 1;
  209 + while (true){
  210 + $project_list = $this->getProjectList($page);
  211 + if(!$project_list){
  212 + break;
  213 + }
  214 + $need_project = [];
  215 + foreach ($project_list as $project) {
  216 + Log::channel('traffic')->info('开始俄语站特殊引流 project_id:' . $project['project_id']);
  217 + //随机引流间隔
  218 + $res_sjjg = $this->get_rand($this->sjjg);
  219 + if ($res_sjjg == 1) {
  220 + Log::channel('traffic')->info('随机不俄语站特殊引流 project_id:' . $project['project_id']);
  221 + continue;
  222 + }
  223 +
  224 + $project_urls = $this->getProductUrls($project['project_id']);
  225 + $project_urls['home'] = $project['domain'];
  226 + //随机访问页面
  227 + $project['visit_urls'] = $this->getVisitUrls($project_urls);
  228 + //随机客户端
  229 + $project['device_port'] = $this->get_rand($this->yddzb);
  230 + $project['user_agent'] = $project['device_port'] == 1 ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua);
  231 +
  232 + $need_project[] = $project;
  233 + }
  234 + //随机访问ip
  235 + $ips = $this->getIpAreas(count($need_project));
  236 +
  237 + //特殊日期 降访问率
  238 + foreach ($need_project as $project_key => $project){
  239 + $diff = $ips[$project_key]['diff'];
  240 + //当地时间
  241 + $w = date('w', strtotime($diff . 'hour'));
  242 + $date = date('m-d', strtotime($diff . 'hour'));
  243 + //元旦节和圣诞节按照周六的比例处理
  244 + if(in_array($date, ['01-01', '12-25'])){
  245 + $w = 6;
  246 + }
  247 + switch ($w){
  248 + case 0:
  249 + //周日降70-80%
  250 + $rate = rand(70, 80) * 10;
  251 + $res = $this->get_rand([1000-$rate, $rate]);
  252 + break;
  253 + case 5:
  254 + //周5降30-40%
  255 + $rate = rand(30, 40) * 10;
  256 + $res = $this->get_rand([1000-$rate, $rate]);
  257 + break;
  258 + case 6:
  259 + //周6降60-70%
  260 + $rate = rand(60, 70) * 10;
  261 + $res = $this->get_rand([1000-$rate, $rate]);
  262 + break;
  263 + default:
  264 + $res = 0;
  265 + }
  266 + //不访问
  267 + if($res == 1){
  268 + Log::channel('traffic')->info('特殊日期随机不特殊引流 project_id:' . $project['project_id']);
  269 + unset($need_project[$project_key]);
  270 + unset($ips[$project_key]);
  271 + }
  272 + }
  273 +
  274 + //最多10层深度
  275 + $client = new Client(['verify' => false]);
  276 + for ($j = 0; $j < 10; $j++) {
  277 + for ($j = 0; $j < 10; $j++) {
  278 + //并发请求
  279 + $promises = [];
  280 + foreach ($need_project as $project_key => $project) {
  281 + if (empty($project['visit_urls'][$j])) {
  282 + continue;
  283 + }
  284 +
  285 + $data = [
  286 + 'ip' => $ips[$project_key]['ip'],
  287 + 'url' => $project['visit_urls'][$j],
  288 + 'device_port' => $project['device_port'],
  289 + 'referrer_url' => $this->getReferer($ips[$project_key]['ip_area'], $project['lang']),
  290 + 'user_agent' => $project['user_agent'],
  291 + ];
  292 + Log::channel('traffic')->info('ru_traffic project_id:' . $project['project_id'], $data);
  293 + $promises[] = $client->postAsync($project['domain'] . 'api/traffic_visit', ['form_params' => $data]);
  294 + }
  295 +
  296 + if($promises){
  297 + Utils::settle($promises)->wait();
  298 + //每个深度随机等待
  299 + sleep(rand(2, 10));
  300 + }
  301 + }
  302 + }
  303 +
  304 + $page++;
  305 + }
  306 + }catch (\Exception $e){
  307 + Log::channel('traffic')->error("ru:line" . $e->getLine() . ',error:' . $e->getMessage());
  308 + }
  309 + }
  310 +
  311 + /**
  312 + * 不同项目 休眠
  313 + */
  314 + protected function sleep(){
  315 + sleep(rand(5,480));
  316 + }
  317 +
  318 + /**
  319 + * 俄语站引流的项目
  320 + */
  321 + protected function getProjectList($page){
  322 + $ru_lang_id = WebLanguage::getIdByLang('ru');
  323 +
  324 + //推广项目
  325 + $list = Project::with('domainInfo')
  326 + ->leftJoin('gl_project_deploy_optimize as pdo', 'pdo.project_id', '=', 'gl_project.id')
  327 + ->leftJoin('gl_project_online_check as poc', 'poc.project_id', '=', 'gl_project.id')
  328 + ->where('pdo.domain', '>', 0)
  329 + ->where('poc.qa_status', OnlineCheck::STATUS_ONLINE_TRUE)
  330 + ->whereIn('gl_project.type', [Project::TYPE_TWO, Project::TYPE_FOUR])
  331 + ->where('gl_project.is_upgrade', 0) //非升级项目
  332 + ->where('gl_project.main_lang_id', $ru_lang_id)// 俄语站
  333 + ->whereIn('gl_project.id', array_keys($this->projects))
  334 + ->select(['pdo.project_id','gl_project.main_lang_id','gl_project.id'])->forPage($page, 500)->get();
  335 + //其他地方在引流的域名
  336 +// $other = DB::connection('projects_mysql')->table('projects')->where('switch', 1)->pluck('domain')->toArray();
  337 + $other = [];
  338 + $data = [];
  339 + foreach ($list as $project) {
  340 +
  341 + //根据增加的次数 设置间隔
  342 + if(Cache::get('web_traffic_russia_special_' . $project['project_id'])){
  343 + continue;
  344 + }else{
  345 + $ttl = 24 * 60 * 60 / $this->projects[$project['project_id']];
  346 + Cache::put('web_traffic_russia_special_' . $project['project_id'], 1, $ttl);
  347 + }
  348 +
  349 + $lang = WebLanguage::getLangById($project['main_lang_id']??1)['short'];
  350 + if(empty($project->domainInfo['domain'])){
  351 + continue;
  352 + }
  353 + //其他地方在引流就不再引流了
  354 + if(in_array($project->domainInfo['domain'], $other)){
  355 + continue;
  356 + }
  357 + $data[] = [
  358 + 'project_id' => $project['project_id'],
  359 + 'domain' => 'https://' . $project->domainInfo['domain'] . '/',
  360 + 'lang' => $lang
  361 + ];
  362 + }
  363 + return $data;
  364 + }
  365 +
  366 + /**
  367 + * 获取产品分类、单页和详情链接
  368 + */
  369 + protected function getProductUrls($project_id){
  370 + ProjectServer::useProject($project_id);
  371 + //已发布产品分类页面
  372 + $data['urls_cats'] = DB::connection('custom_mysql')->table('gl_product_category')
  373 + ->where('project_id', $project_id)->where('status', Category::STATUS_ACTIVE)
  374 + ->whereNull('deleted_at')
  375 + ->pluck('route','id')->toArray();
  376 +
  377 + //已发布单页面
  378 + $data['urls_page'] = [];
  379 +// $data['urls_page'] = DB::connection('custom_mysql')->table('gl_web_custom_template')
  380 +// ->where('project_id', $project_id)->where('url', '<>', '404')->where('status', BCustomTemplate::STATUS_ACTIVE)->pluck('url', 'id')->toArray();
  381 +
  382 + //已发布产品详情页
  383 + $data['urls_details'] = DB::connection('custom_mysql')->table('gl_product')
  384 + ->where('project_id', $project_id)->where('status', Product::STATUS_ON)
  385 + ->whereNull('deleted_at')
  386 + ->pluck('route', 'id')->toArray();
  387 +
  388 + $data['urls_cats'] = array_merge($data['urls_cats'], $data['urls_page']);
  389 + if(empty($data['urls_cats'])){
  390 + $data['urls_cats'] = $data['urls_details'];
  391 + }
  392 + DB::disconnect('custom_mysql');
  393 + return $data;
  394 + }
  395 +
  396 + /**
  397 + * 获取地区IP
  398 + */
  399 + protected function getIpAreas($num)
  400 + {
  401 + //本地时间为7-23点的地区
  402 + $h = date('H');
  403 + $areas = [];
  404 + $list = DB::table('gl_area_timezone')->whereIn('name', array_keys($this->country_ratio))->get();
  405 + $time_zones = [];
  406 + foreach ($list as $v) {
  407 + $v = (array)$v;
  408 + $country_hour = $h + $v['diff'];
  409 + if ($country_hour < 0) {
  410 + $country_hour = 24 + $country_hour;
  411 + }
  412 + if ($country_hour >= 7 && $country_hour < 23) {
  413 + $areas[] = $v['name'];
  414 + $time_zones[$v['name']] = $v['diff'];
  415 + }
  416 + }
  417 + //不在时区的国家概率去掉
  418 + $country_ratio = $this->country_ratio;
  419 + foreach ($country_ratio as $k => $v){
  420 + if(!in_array($k, array_keys($time_zones))){
  421 + unset($country_ratio[$k]);
  422 + }
  423 + }
  424 +
  425 + $data = [];
  426 + for ($i=0;$i<$num;$i++){
  427 + //ip国家占比
  428 + $ip_area = $this->get_rand($country_ratio);
  429 + $res = DB::table('gl_xunpan_ipdata')->where('ip_area', $ip_area)->inRandomOrder()->first();
  430 + $res = (array)$res;
  431 + $res['diff'] = $time_zones[$res['ip_area']];
  432 + $data[] = $res;
  433 + }
  434 + return $data;
  435 + }
  436 +
  437 + /**
  438 + * 概率算法
  439 + */
  440 + protected function get_rand($proArr) {
  441 + $result = '';
  442 + $proSum = array_sum($proArr);
  443 + foreach ($proArr as $key => $proCur) {
  444 + $randNum = mt_rand(1, $proSum);
  445 + if ($randNum <= $proCur) {
  446 + $result = $key;
  447 + break;
  448 + } else {
  449 + $proSum -= $proCur;
  450 + }
  451 + }
  452 + unset ($proArr);
  453 + return $result;
  454 + }
  455 +
  456 + /**
  457 + * 根据随机访问深度 随机获取访问页面
  458 + */
  459 + protected function getVisitUrls($project_urls){
  460 + //没有分类页 就只访问首页
  461 + if(!$project_urls['urls_cats']){
  462 + $url[] = $project_urls['home'];
  463 + return $url;
  464 + }
  465 + //随机访问深度
  466 + $res_sdzb = $this->get_rand($this->sdzb);
  467 + //随机访问页面类型
  468 + $res_ymzb = $this->get_rand($this->ymzb);
  469 +
  470 + $all_url = array_merge($project_urls['urls_cats'],$project_urls['urls_details']);
  471 + if(!$all_url){
  472 + $url[] = $project_urls['home'];
  473 + return $url;
  474 + }
  475 +
  476 + $url = [];
  477 + if($res_sdzb == 0){//深度一页
  478 + $url[] = $project_urls[$res_ymzb] ? Arr::random($project_urls[$res_ymzb]) : '';
  479 + }elseif($res_sdzb == 1){//深度两页
  480 + $url[] = $project_urls['home'];
  481 + $url[] = $project_urls[$res_ymzb] ? Arr::random($project_urls[$res_ymzb]) : '';
  482 + }elseif($res_sdzb == 2){//深度3-6页
  483 + $yms = rand(2,5); //随机页面数
  484 + $url = Arr::random($all_url, $yms);
  485 + $url = Arr::prepend($url, $project_urls['home']);//首页加到最前面去
  486 + }elseif($res_sdzb == 3){//深度7-11页
  487 + $yms = rand(6,10); //随机页面数
  488 + $url = Arr::random($all_url, $yms);
  489 + $url = Arr::prepend($url, $project_urls['home']);//首页加到最前面去
  490 + }
  491 + foreach ($url as &$v){
  492 + if(!Str::contains($v, $project_urls['home'])){
  493 + if (FALSE === strpos($v, '.htm')) {
  494 + $v .= '/';
  495 + }
  496 + $v = $project_urls['home'] . $v;
  497 + }
  498 + }
  499 + return array_unique(array_filter($url));
  500 + }
  501 +
  502 + /**
  503 + * 获取访问来路
  504 + */
  505 + protected function getReferer($ip_area, $lang){
  506 +
  507 + if($lang == 'ru'){
  508 + return $this->get_rand($this->eylyzb);
  509 + }
  510 +
  511 + if($ip_area == '美国'){
  512 + $referer = $this->get_rand($this->lyzb);
  513 + }else{
  514 + $referer = 'https://www.google.com/';
  515 +
  516 + $suffix = array_search($ip_area, $this->suffix);
  517 + if($suffix){
  518 + $res_qtzb = $this->get_rand($this->otherzb);
  519 + if($res_qtzb == 1){
  520 + $referer = 'https://www.google.'.$suffix.'/';
  521 + }
  522 + }
  523 + }
  524 + return $referer;
  525 + }
  526 +}
@@ -29,6 +29,7 @@ class Kernel extends ConsoleKernel @@ -29,6 +29,7 @@ class Kernel extends ConsoleKernel
29 // $schedule->command('count')->dailyAt('00:30')->withoutOverlapping(1); //每天凌晨1点执行一次 29 // $schedule->command('count')->dailyAt('00:30')->withoutOverlapping(1); //每天凌晨1点执行一次
30 $schedule->command('service_count')->dailyAt('01:00')->withoutOverlapping(1); //服务器使用情况,每天凌晨1点执行一次 30 $schedule->command('service_count')->dailyAt('01:00')->withoutOverlapping(1); //服务器使用情况,每天凌晨1点执行一次
31 $schedule->command('web_traffic_special')->everyMinute()->withoutOverlapping(1); // 特殊引流 31 $schedule->command('web_traffic_special')->everyMinute()->withoutOverlapping(1); // 特殊引流
  32 + $schedule->command('web_traffic_russia_special')->everyMinute()->withoutOverlapping(1); // 特殊引流
32 $schedule->command('web_traffic 1')->everyThirtyMinutes(); // 引流 1-3个月的项目,半小时一次 33 $schedule->command('web_traffic 1')->everyThirtyMinutes(); // 引流 1-3个月的项目,半小时一次
33 $schedule->command('web_traffic 2')->cron('*/18 * * * *'); // 引流 4-8个月的项目,18分钟一次 34 $schedule->command('web_traffic 2')->cron('*/18 * * * *'); // 引流 4-8个月的项目,18分钟一次
34 $schedule->command('web_traffic 3')->cron('*/12 * * * *'); // 引流 大于9个月的项目,12分钟一次 35 $schedule->command('web_traffic 3')->cron('*/12 * * * *'); // 引流 大于9个月的项目,12分钟一次