作者 lyh

gx

  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :UpgradeProjectCount.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2024/1/8 9:03
  8 + */
  9 +
  10 +namespace App\Console\Commands\DayCount;
  11 +
  12 +use App\Models\Project\Project;
  13 +use App\Models\Visit\Visit;
  14 +use App\Services\ProjectServer;
  15 +use Illuminate\Console\Command;
  16 +use Illuminate\Support\Facades\DB;
  17 +use App\Models\HomeCount\Count;
  18 +
  19 +class UpgradeCount extends Command
  20 +{
  21 + /**
  22 + * The name and signature of the console command.
  23 + *
  24 + * @var string
  25 + */
  26 + protected $signature = 'upgrade_count';
  27 +
  28 + /**
  29 + * The console command description.
  30 + *
  31 + * @var string
  32 + */
  33 + protected $description = '升级项目统计';
  34 +
  35 + public function handle(){
  36 + $projectModel = new Project();
  37 + $list = $projectModel->list(['is_upgrade'=>1,'delete_status'=>0]);
  38 + foreach ($list as $v) {
  39 + echo date('Y-m-d H:i:s') . '项目id:'.$v['id'] . PHP_EOL;
  40 + ProjectServer::useProject($v['id']);
  41 + $this->count($v['id']);
  42 + DB::disconnect('custom_mysql');
  43 + }
  44 + return true;
  45 + }
  46 +
  47 + /**
  48 + * @remark :日统计记录
  49 + * @name :count
  50 + * @author :lyh
  51 + * @method :post
  52 + * @time :2024/1/8 9:05
  53 + */
  54 + public function count($project_id){
  55 + $list = DB::connection('custom_mysql')->table('gl_customer_visit')->select('updated_date')
  56 + ->groupBy('updated_date')->get()->toArray();
  57 + $project = new Project();
  58 + $projectInfo = $project->read(['id'=>$project_id]);
  59 + if(!empty($list)){
  60 + $arr = [];
  61 + foreach ($list as $k=>$v){
  62 + $v = (array)$v;
  63 + if($v['updated_date'] == date('Y-m-d')){
  64 + continue;
  65 + }
  66 + echo date('Y-m-d H:i:s') . '时间:'.$v['updated_date'] . PHP_EOL;
  67 + $count = new Count();
  68 + $arr['project_id'] = $project_id;
  69 + $arr['date'] = $v['updated_date'];
  70 + $arr['pv_num'] = $this->pv_num($v['updated_date']);
  71 + $arr['ip_num'] = $this->ip_num($v['updated_date']);
  72 + $arr['inquiry_num'] = $this->inquiry_num($v['updated_date']);
  73 + //服务达标天数
  74 + $arr['compliance_day'] = $projectInfo['finish_remain_day'];
  75 + //剩余服务时常
  76 + $arr['service_day'] = $projectInfo['remain_day'];
  77 + $arr['country'] = json_encode([]);
  78 + //查询当天数据是否存在 存在则更新
  79 + $info = $count->read(['date'=>$v['updated_date'],'project_id'=>$project_id]);
  80 + if($info === false){
  81 + $arr['created_at'] = $v['updated_date'].' 01:00:00';
  82 + $arr['updated_at'] = $v['updated_date'].' 01:00:00';
  83 + $count->insert($arr);
  84 + }else{
  85 + $count->edit($arr,['id'=>$info['id']]);
  86 + }
  87 + }
  88 + }
  89 + echo date('Y-m-d H:i:s') . 'end' . PHP_EOL;
  90 + }
  91 +
  92 + /**
  93 + * @remark :询盘数量
  94 + * @name :inquiry_num
  95 + * @author :lyh
  96 + * @method :post
  97 + * @time :2024/1/8 9:24
  98 + */
  99 + public function inquiry_num($day){
  100 + $count = DB::connection('custom_mysql')->table('gl_customer_visit')->whereDate('updated_date', $day)->where('is_inquiry',1)->count();
  101 + return $count;
  102 + }
  103 +
  104 + /**
  105 + * @name :(统计pv)pv_num
  106 + * @author :lyh
  107 + * @method :post
  108 + * @time :2023/6/14 15:40
  109 + */
  110 + public function pv_num($day){
  111 + //$pv = DB::connection('custom_mysql')->table('gl_customer_visit_item')->whereDate('updated_date', $day)->count();
  112 + $pv = DB::connection('custom_mysql')->table('gl_customer_visit')->whereDate('updated_date', $day)->sum('depth');
  113 + return $pv;
  114 + }
  115 +
  116 + /**
  117 + * @name :(统计ip)ip_num
  118 + * @author :lyh
  119 + * @method :post
  120 + * @time :2023/6/14 15:40
  121 + */
  122 + public function ip_num($day){
  123 + $ip = DB::connection('custom_mysql')->table('gl_customer_visit')->whereDate('updated_date', $day)->count();
  124 + return $ip;
  125 + }
  126 +
  127 +
  128 +
  129 +}
@@ -55,7 +55,7 @@ class UpdateRoute extends Command @@ -55,7 +55,7 @@ class UpdateRoute extends Command
55 */ 55 */
56 public function handle(){ 56 public function handle(){
57 $projectModel = new Project(); 57 $projectModel = new Project();
58 - $list = $projectModel->list(['id'=>249]); 58 + $list = $projectModel->list(['id'=>264]);
59 $data = []; 59 $data = [];
60 foreach ($list as $v){ 60 foreach ($list as $v){
61 echo date('Y-m-d H:i:s') . 'project_id:'.$v['id'] . PHP_EOL; 61 echo date('Y-m-d H:i:s') . 'project_id:'.$v['id'] . PHP_EOL;
@@ -39,7 +39,7 @@ class UpdateSeoTdkCrontab extends Command @@ -39,7 +39,7 @@ class UpdateSeoTdkCrontab extends Command
39 try { 39 try {
40 ProjectUpdateTdk::add_task($project_id); 40 ProjectUpdateTdk::add_task($project_id);
41 }catch (\Exception $e){ 41 }catch (\Exception $e){
42 - 42 + dump($e->getMessage());
43 } 43 }
44 } 44 }
45 } 45 }
@@ -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 }
@@ -430,7 +433,7 @@ class WebTrafficRussia extends Command @@ -430,7 +433,7 @@ class WebTrafficRussia extends Command
430 $data = []; 433 $data = [];
431 for ($i=0;$i<$num;$i++){ 434 for ($i=0;$i<$num;$i++){
432 //ip国家占比 435 //ip国家占比
433 - $ip_area = $this->get_rand($this->country_ratio); 436 + $ip_area = $this->get_rand($country_ratio);
434 $res = DB::table('gl_xunpan_ipdata')->where('ip_area', $ip_area)->inRandomOrder()->first(); 437 $res = DB::table('gl_xunpan_ipdata')->where('ip_area', $ip_area)->inRandomOrder()->first();
435 $res = (array)$res; 438 $res = (array)$res;
436 $res['diff'] = $time_zones[$res['ip_area']]; 439 $res['diff'] = $time_zones[$res['ip_area']];
  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 +}
@@ -24,11 +24,10 @@ class Kernel extends ConsoleKernel @@ -24,11 +24,10 @@ class Kernel extends ConsoleKernel
24 $schedule->command('rank_data_external_links')->dailyAt('18:00')->withoutOverlapping(1); // 排名数据-外链,每周一凌晨执行一次 24 $schedule->command('rank_data_external_links')->dailyAt('18:00')->withoutOverlapping(1); // 排名数据-外链,每周一凌晨执行一次
25 $schedule->command('rank_data_indexed_pages')->dailyAt('07:30')->withoutOverlapping(1); // 排名数据-页面收录,每周一凌晨执行一次 25 $schedule->command('rank_data_indexed_pages')->dailyAt('07:30')->withoutOverlapping(1); // 排名数据-页面收录,每周一凌晨执行一次
26 $schedule->command('rank_data_recomm_domain')->dailyAt('07:40')->withoutOverlapping(1); // 排名数据-引荐域名,每周一凌晨执行一次 26 $schedule->command('rank_data_recomm_domain')->dailyAt('07:40')->withoutOverlapping(1); // 排名数据-引荐域名,每周一凌晨执行一次
27 - $schedule->command('rank_data_week')->weeklyOn([1,2], '08:300')->withoutOverlapping(1); // 排名数据,每周一、二早上执行一次  
28 -// $schedule->command('share_user')->dailyAt('01:00')->withoutOverlapping(1); // 清除用户ayr_share数据,每天凌晨1点执行一次  
29 -// $schedule->command('count')->dailyAt('00:30')->withoutOverlapping(1); //每天凌晨1点执行一次 27 + $schedule->command('rank_data_week')->weeklyOn([1,2], '08:30')->withoutOverlapping(1); // 排名数据,每周一、二早上执行一次
30 $schedule->command('service_count')->dailyAt('01:00')->withoutOverlapping(1); //服务器使用情况,每天凌晨1点执行一次 28 $schedule->command('service_count')->dailyAt('01:00')->withoutOverlapping(1); //服务器使用情况,每天凌晨1点执行一次
31 $schedule->command('web_traffic_special')->everyMinute()->withoutOverlapping(1); // 特殊引流 29 $schedule->command('web_traffic_special')->everyMinute()->withoutOverlapping(1); // 特殊引流
  30 + $schedule->command('web_traffic_russia_special')->everyMinute()->withoutOverlapping(1); // 特殊引流
32 $schedule->command('web_traffic 1')->everyThirtyMinutes(); // 引流 1-3个月的项目,半小时一次 31 $schedule->command('web_traffic 1')->everyThirtyMinutes(); // 引流 1-3个月的项目,半小时一次
33 $schedule->command('web_traffic 2')->cron('*/18 * * * *'); // 引流 4-8个月的项目,18分钟一次 32 $schedule->command('web_traffic 2')->cron('*/18 * * * *'); // 引流 4-8个月的项目,18分钟一次
34 $schedule->command('web_traffic 3')->cron('*/12 * * * *'); // 引流 大于9个月的项目,12分钟一次 33 $schedule->command('web_traffic 3')->cron('*/12 * * * *'); // 引流 大于9个月的项目,12分钟一次
@@ -36,16 +35,12 @@ class Kernel extends ConsoleKernel @@ -36,16 +35,12 @@ class Kernel extends ConsoleKernel
36 $schedule->command('web_traffic_russia 2')->cron('*/18 * * * *'); // 俄语站引流 4-8个月的项目,18分钟一次 35 $schedule->command('web_traffic_russia 2')->cron('*/18 * * * *'); // 俄语站引流 4-8个月的项目,18分钟一次
37 $schedule->command('web_traffic_russia 3')->cron('*/12 * * * *'); // 俄语站引流 大于9个月的项目,12分钟一次 36 $schedule->command('web_traffic_russia 3')->cron('*/12 * * * *'); // 俄语站引流 大于9个月的项目,12分钟一次
38 $schedule->command('sync_channel')->dailyAt('06:00')->withoutOverlapping(1); // 渠道信息,每天执行一次 37 $schedule->command('sync_channel')->dailyAt('06:00')->withoutOverlapping(1); // 渠道信息,每天执行一次
39 -// $schedule->command('month_count')->monthlyOn(1,'01:00')->withoutOverlapping(1);//没月月初1号执行月统计记录  
40 $schedule->command('forward_count')->monthlyOn(1,'01:00')->withoutOverlapping(1);//没月月初1号执行月统计转发询盘记录 38 $schedule->command('forward_count')->monthlyOn(1,'01:00')->withoutOverlapping(1);//没月月初1号执行月统计转发询盘记录
41 $schedule->command('inquiry_delay')->everyMinute()->withoutOverlapping(1);//TODO::上线放开,转发询盘,每分钟执行一次 39 $schedule->command('inquiry_delay')->everyMinute()->withoutOverlapping(1);//TODO::上线放开,转发询盘,每分钟执行一次
42 $schedule->command('inquiry_count')->dailyAt('01:00')->withoutOverlapping(1); // 询盘统计数据,每天凌晨执行一次 40 $schedule->command('inquiry_count')->dailyAt('01:00')->withoutOverlapping(1); // 询盘统计数据,每天凌晨执行一次
43 $schedule->command('domain_info')->dailyAt('01:20')->withoutOverlapping(1);// 更新域名|证书结束时间,每天凌晨1点执行一次 41 $schedule->command('domain_info')->dailyAt('01:20')->withoutOverlapping(1);// 更新域名|证书结束时间,每天凌晨1点执行一次
44 $schedule->command('last_inquiry')->dailyAt('04:00')->withoutOverlapping(1);// 最近一次询盘信息 42 $schedule->command('last_inquiry')->dailyAt('04:00')->withoutOverlapping(1);// 最近一次询盘信息
45 -// $schedule->command('update_progress')->everyThirtyMinutes()->withoutOverlapping(1);//监控更新  
46 $schedule->command('update_seo_tdk_crontab')->dailyAt('20:00')->withoutOverlapping(1); //更新上线项目TDK 43 $schedule->command('update_seo_tdk_crontab')->dailyAt('20:00')->withoutOverlapping(1); //更新上线项目TDK
47 -// $schedule->command('website_data')->dailyAt('01:00')->withoutOverlapping(1); // 向AICC推送数据  
48 -// $schedule->command('project_file_pdf')->dailyAt('00:00')->withoutOverlapping(1); // 网站项目数据,生成PDF文件  
49 $schedule->command('sync_manager')->dailyAt('01:00')->withoutOverlapping(1); //TODO::手机号码同步 每天执行一次 44 $schedule->command('sync_manager')->dailyAt('01:00')->withoutOverlapping(1); //TODO::手机号码同步 每天执行一次
50 $schedule->command('update_keyword_route')->dailyAt('01:00')->withoutOverlapping(1); //升级项目--清除路由相同的关键字 45 $schedule->command('update_keyword_route')->dailyAt('01:00')->withoutOverlapping(1); //升级项目--清除路由相同的关键字
51 $schedule->command('recommended_suppliers')->dailyAt('03:00')->withoutOverlapping(1); //每天凌晨1点执行一次生成推荐商 46 $schedule->command('recommended_suppliers')->dailyAt('03:00')->withoutOverlapping(1); //每天凌晨1点执行一次生成推荐商
@@ -4,6 +4,7 @@ namespace App\Http\Logic\Aside\Domain; @@ -4,6 +4,7 @@ namespace App\Http\Logic\Aside\Domain;
4 4
5 5
6 use App\Http\Logic\Aside\BaseLogic; 6 use App\Http\Logic\Aside\BaseLogic;
  7 +use App\Jobs\EditCustomDomainBt;
7 use App\Jobs\EditDomainBt; 8 use App\Jobs\EditDomainBt;
8 use App\Models\Devops\ServerConfig; 9 use App\Models\Devops\ServerConfig;
9 use App\Models\Domain\DomainInfo; 10 use App\Models\Domain\DomainInfo;
@@ -403,16 +404,18 @@ class DomainInfoLogic extends BaseLogic @@ -403,16 +404,18 @@ class DomainInfoLogic extends BaseLogic
403 $info = $custom_model->read(['project_id'=>$project_id,'language_id'=>$this->param['language_id']]); 404 $info = $custom_model->read(['project_id'=>$project_id,'language_id'=>$this->param['language_id']]);
404 if($info === false){ 405 if($info === false){
405 $this->param['project_id'] = $project_id; 406 $this->param['project_id'] = $project_id;
406 - $custom_model->add($this->param); 407 + $id = $custom_model->addReturnId($this->param);
407 }else{ 408 }else{
408 $custom_model->edit($this->param,['id'=>$info['id']]); 409 $custom_model->edit($this->param,['id'=>$info['id']]);
  410 + $id = $info['id'];
409 } 411 }
410 412
411 if($this->param['is_create']){ 413 if($this->param['is_create']){
412 //创建站点,设置证书 414 //创建站点,设置证书
413 - $this->param['key'] = $this->param['private_key'] ?? '';  
414 - $this->param['cert'] = $this->param['private_cert'] ?? '';  
415 - $this->setDomainSsl($server_info['init_domain'],$this->param['custom_domain'],[],[],1); 415 + EditCustomDomainBt::dispatch($id);
  416 +// $this->param['key'] = $this->param['private_key'] ?? '';
  417 +// $this->param['cert'] = $this->param['private_cert'] ?? '';
  418 +// $this->setDomainSsl($server_info['init_domain'],$this->param['custom_domain'],[],[],1);
416 } 419 }
417 420
418 return $this->success(); 421 return $this->success();
@@ -397,7 +397,7 @@ class ProjectLogic extends BaseLogic @@ -397,7 +397,7 @@ class ProjectLogic extends BaseLogic
397 $info = $this->model->read(['id'=>$param['id']]); 397 $info = $this->model->read(['id'=>$param['id']]);
398 if($info['delete_status'] == 0){ 398 if($info['delete_status'] == 0){
399 //删除原始项目 399 //删除原始项目
400 - $this->edit(['delete_status' => 1], ['id' => $param['id']]); 400 + $this->edit(['delete_status' => 1,'type'=>$param['type']], ['id' => $param['id']]);
401 //添加到续费单 401 //添加到续费单
402 $data = [ 402 $data = [
403 'title' => '【续费单】' . $param['title'], 403 'title' => '【续费单】' . $param['title'],
@@ -411,8 +411,8 @@ class ProjectLogic extends BaseLogic @@ -411,8 +411,8 @@ class ProjectLogic extends BaseLogic
411 'service_duration' => $param['deploy_build']['service_duration'], 411 'service_duration' => $param['deploy_build']['service_duration'],
412 'plan' => $param['deploy_build']['plan'], 412 'plan' => $param['deploy_build']['plan'],
413 'amount' => $param['payment']['amount'], 413 'amount' => $param['payment']['amount'],
414 - 'contract' => json_encode($param['payment']['contract']),  
415 - 'bill' => json_encode($param['payment']['bill']), 414 + 'contract' => json_encode($param['payment']['contract'] ?? []),
  415 + 'bill' => json_encode($param['payment']['bill'] ?? []),
416 ]; 416 ];
417 $renewModel = new ProjectRenew(); 417 $renewModel = new ProjectRenew();
418 $renewModel->add($data); 418 $renewModel->add($data);
@@ -217,10 +217,14 @@ class NewsLogic extends BaseLogic @@ -217,10 +217,14 @@ class NewsLogic extends BaseLogic
217 */ 217 */
218 public function getCategory($category){ 218 public function getCategory($category){
219 $str = ''; 219 $str = '';
220 - foreach ($category as $v){  
221 - $str .= $v.','; 220 + if(is_array($category) && $category){
  221 + $str = ','.implode(',',$category).',';
222 } 222 }
223 - return !empty(trim($str,',')) ? ','.$str.',' : ''; 223 + return $str;
  224 +// foreach ($category as $v){
  225 +// $str .= $v.',';
  226 +// }
  227 +// return !empty(trim($str,',')) ? ','.$str.',' : '';
224 } 228 }
225 229
226 /** 230 /**
@@ -247,12 +247,16 @@ class CategoryLogic extends BaseLogic @@ -247,12 +247,16 @@ class CategoryLogic extends BaseLogic
247 */ 247 */
248 public function getLastCategory($category){ 248 public function getLastCategory($category){
249 $str = ''; 249 $str = '';
250 - if(isset($category) && !empty($category)){  
251 - foreach ($category as $v){  
252 - $str .= $v.',';  
253 - } 250 + if(is_array($category) && $category){
  251 + $str = ','.implode(',',$category).',';
254 } 252 }
255 - return !empty(trim($str,',')) ? ','.$str.',' : ''; 253 + return $str;
  254 +// if(isset($category) && !empty($category)){
  255 +// foreach ($category as $v){
  256 +// $str .= $v.',';
  257 +// }
  258 +// }
  259 +// return !empty(trim($str,',')) ? ','.$str.',' : '';
256 } 260 }
257 261
258 /** 262 /**
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 namespace App\Http\Logic\Bside\Setting; 10 namespace App\Http\Logic\Bside\Setting;
11 11
12 use App\Http\Logic\Bside\BaseLogic; 12 use App\Http\Logic\Bside\BaseLogic;
  13 +use App\Models\Com\UpdateLog;
  14 +use App\Models\User\UserLog;
13 use App\Models\WebSetting\Translate as TranslateModel; 15 use App\Models\WebSetting\Translate as TranslateModel;
14 use App\Models\WebSetting\WebLanguage; 16 use App\Models\WebSetting\WebLanguage;
15 use App\Helper\Translate; 17 use App\Helper\Translate;
@@ -271,9 +273,10 @@ class TranslateLogic extends BaseLogic @@ -271,9 +273,10 @@ class TranslateLogic extends BaseLogic
271 } 273 }
272 } 274 }
273 } 275 }
  276 + $this->param['data'] = $data;
274 } 277 }
275 try { 278 try {
276 - $info = $this->model->read(['language_id'=>$this->param['language_id'],'url'=>$this->param['url'],'type'=>$this->param['type']]); 279 + $info = $this->model->read(['language_id'=>$this->param['language_id'],'url'=>$this->param['url'],'project_id'=>$this->user['project_id'],'type'=>$this->param['type']]);
277 if($info === false){ 280 if($info === false){
278 $param = [ 281 $param = [
279 'type'=>$this->param['type'], 282 'type'=>$this->param['type'],
@@ -285,9 +288,14 @@ class TranslateLogic extends BaseLogic @@ -285,9 +288,14 @@ class TranslateLogic extends BaseLogic
285 $param['data'] = json_encode($data,JSON_UNESCAPED_UNICODE); 288 $param['data'] = json_encode($data,JSON_UNESCAPED_UNICODE);
286 $this->model->add($param); 289 $this->model->add($param);
287 }else{ 290 }else{
288 - $data = json_encode($data,JSON_UNESCAPED_UNICODE);  
289 - $this->model->edit(['data'=>$data],['language_id'=>$this->param['language_id'],'url'=>$this->param['url'],'type'=>$this->param['type']]); 291 + if(!empty($data)){
  292 + $data = json_encode($data,JSON_UNESCAPED_UNICODE);
  293 + $this->model->edit(['data'=>$data],['language_id'=>$this->param['language_id'],'project_id'=>$this->user['project_id'],'url'=>$this->param['url'],'type'=>$this->param['type']]);
  294 + }
290 } 295 }
  296 + //写日志
  297 + $userLogModel = new UserLog();
  298 + $userLogModel->add(['model'=>'translate/save','remark'=>json_encode($this->param,true),'type'=>0,'operator_id'=>$this->user['id'],'project_id'=>$this->user['project_id']]);
291 }catch (\Exception $e){ 299 }catch (\Exception $e){
292 $this->fail('系统错误请联系管理员'); 300 $this->fail('系统错误请联系管理员');
293 } 301 }
  1 +<?php
  2 +
  3 +namespace App\Jobs;
  4 +
  5 +use App\Models\Devops\ServerConfig;
  6 +use App\Models\Project\CountryCustom;
  7 +use App\Models\Project\Project;
  8 +use App\Utils\HttpUtils;
  9 +use GuzzleHttp\Exception\GuzzleException;
  10 +use Illuminate\Bus\Queueable;
  11 +use Illuminate\Contracts\Queue\ShouldQueue;
  12 +use Illuminate\Foundation\Bus\Dispatchable;
  13 +use Illuminate\Queue\InteractsWithQueue;
  14 +use Illuminate\Queue\SerializesModels;
  15 +
  16 +class EditCustomDomainBt implements ShouldQueue
  17 +{
  18 + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
  19 +
  20 + public $tries = 1; // 可配置任务重试次数
  21 +
  22 + protected $domain_id;
  23 +
  24 + /**
  25 + * Create a new job instance.
  26 + *
  27 + * @param $domain_id
  28 + */
  29 + public function __construct($domain_id)
  30 + {
  31 + $this->domain_id = $domain_id;
  32 + }
  33 +
  34 + /**
  35 + * Execute the job.
  36 + *
  37 + * @return bool
  38 + */
  39 + public function handle()
  40 + {
  41 + //获取域名数据
  42 + $domain_model = new CountryCustom();
  43 + $domain_info = $domain_model->read(['id' => $this->domain_id]);
  44 + if ($domain_info === false) {
  45 + return $this->output($domain_info['custom_domain'] . ':获取域名数据失败');
  46 + }
  47 +
  48 + //获取项目数据
  49 + $project_model = new Project();
  50 + $project_info = $project_model->read(['id' => $domain_info['project_id']], 'serve_id');
  51 + if ($project_info === false) {
  52 + return $this->output($domain_info['custom_domain'] . ':获取项目数据失败');
  53 + }
  54 +
  55 + //获取服务器数据
  56 + $server_model = new ServerConfig();
  57 + $server_info = $server_model->read(['id' => $project_info['serve_id']], ['init_domain', 'host']);
  58 + if ($server_info === false) {
  59 + return $this->output($domain_info['custom_domain'] . ':获取服务器数据失败');
  60 + }
  61 +
  62 + //编辑站点
  63 + if ($domain_info['type'] == 2) {
  64 + $api_url = 'http://' . $server_info['init_domain'] . '/api/setSsl';
  65 + $api_param = [
  66 + 'domain' => $domain_info['custom_domain'],
  67 + 'private_key' => $domain_info['private_key'],
  68 + 'cert' => $domain_info['private_cert'],
  69 + 'rewrite' => [],
  70 + 'other_domain' => [],
  71 + 'is_https' => 1
  72 + ];
  73 + } else {
  74 + $api_url = 'http://' . $server_info['init_domain'] . '/api/applySsl';
  75 + $api_param = [
  76 + 'domain' => $domain_info['custom_domain'],
  77 + 'rewrite' => [],
  78 + 'other_domain' => [],
  79 + 'is_https' => 1
  80 + ];
  81 + }
  82 + try {
  83 + $rs = HttpUtils::get($api_url, $api_param);
  84 + $rs = json_decode($rs, true);
  85 + if (isset($rs['status']) && $rs['status'] == 200) {
  86 + $this->output($domain_info['custom_domain'] . ':站点编辑成功');
  87 + } else {
  88 + $this->output($domain_info['custom_domain'] . ':站点编辑失败,原因:' . ($rs['message'] ?? ''));
  89 + }
  90 + } catch (\Exception | GuzzleException $e) {
  91 + $this->output($domain_info['custom_domain'] . ':站点编辑失败,原因:' . $e->getMessage());
  92 + }
  93 +
  94 + return true;
  95 + }
  96 +
  97 + /**
  98 + * 输出处理日志
  99 + * @param $message
  100 + * @return bool
  101 + */
  102 + public function output($message)
  103 + {
  104 + echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL;
  105 + return true;
  106 + }
  107 +
  108 + public function failed(\Exception $exception)
  109 + {
  110 + return $this->output($exception->getMessage());
  111 + }
  112 +}