1,'name' => 'channel-ctl-oil-common-limit'], ['quality'=> 3,'name' => 'channel-ctl-oil-card-limit'], ['quality'=> 5,'name' => 'channel-ctl-oil-slow-limit'], ['quality'=> 1,'name' => 'channel-ctl-phone-common-limit'], ['quality'=> 2,'name' => 'channel-ctl-phone-fast-limit'], ['quality'=> 3,'name' => 'channel-ctl-phone-card-limit'], ['quality'=> 4,'name' => 'channel-ctl-phone-third-limit'], ['quality'=> 5,'name' => 'channel-ctl-phone-slow-limit'], //24 hour ['quality'=> 6,'name' => 'channel-ctl-phone-slow6-limit'],//6 hour ['quality'=> 7,'name' => 'channel-ctl-phone-slow2-limit'], //2 hour ['quality'=> 8,'name' => 'channel-ctl-phone-slow48-limit'], //48 hour ['quality'=> 9,'name' => 'channel-ctl-phone-slow72-limit'] //72 hour ]; public function __construct() { $this->mSpeedtable = []; } public function update_price($policy) { $QPTA = $policy->getQPTA(); foreach ($this->mSpeedtable as $key => $item) { $quality = $item->quality(); $name = $item->name(); $spec = $item->spec(); $card_type = $item->card_type(); $prefix = "{$name}-{$card_type}-{$spec}"; if(array_key_exists($quality,$QPTA) && array_key_exists($prefix,$QPTA[$quality])) { $price = $QPTA[$quality][$prefix]['price']; $item->set_price($price); } } } public function update_ratios($ratios) { foreach ($ratios as $key => $ratio) { if(array_key_exists($key,$this->mSpeedtable)) { $item = $this->mSpeedtable[$key]; $item->set_ratio($ratio); } } } public function load($opened_names) { $this->mSpeedtable = []; foreach (self::$cache_names as $cache) { $quality = $cache['quality']; $cache_name = $cache['name']; $data = rcache($cache_name,"provider-"); $data = unserialize($data['data']); $cfgs = empty($data) ? [] : $data; foreach ($cfgs as $items) { foreach ($items as $item) { $name = $item['name']; if(!algorithm::binary_search($opened_names,$name)) { continue; } $amount = $item['amount']; $card_type = $item['type']; $opened = $item['opened'] == 1; $sort = $item['sort']; $speed = $item['speed']; if($opened == false) continue; $key = $this->prefix($name,$amount,$card_type,$quality); $this->mSpeedtable[$key] = new ctl_item($name,$card_type,$amount,$speed,$sort,0,$opened,$quality); } } } } public function match($names,int $spec, int $card_type,int $quality) { if($card_type == mtopcard\ThirdRefillCard) { $result = []; foreach ($names as $name) { $result[$name] = false; } return $result; } $ctl_items = []; foreach ($names as $name) { $key = $this->prefix($name,$spec,$card_type,$quality); if(array_key_exists($key,$this->mSpeedtable)) { $ctl_items[] = $this->mSpeedtable[$key]; } else { Log::record("unavaliable key={$key}",Log::DEBUG); } } //去掉已经关闭通道 $usable_items = []; foreach ($ctl_items as $item) { if($item->opened()) { $usable_items[] = $item; } else { Log::record("key={$key} has not opened",Log::DEBUG); } } //不过载的排在前面 $ascending = function ($l, $r) { $lproity = $l->priority(); $rproity = $r->priority(); $lover = $l->speed_overload() ? 1 : 0; $rover = $r->speed_overload() ? 1 : 0; if($lover == $rover) { if($lover) { return $lproity > $rproity ? -1 : 1; //如果都过载保优先级高的 } else { return $lproity < $rproity ? -1 : 1; } } else { return $lover < $rover ? -1 : 1; } }; usort($usable_items, $ascending); $result = []; foreach ($usable_items as $item) { $name = $item->name(); $result[$name] = $item->speed_overload(); } return $result; } private function prefix($name,$spec,$card_type,$quality) { return "{$name}-{$spec}-{$card_type}-{$quality}"; } const feed_minorder = 1; const lowest_ratio = 0.15; const sleep_ratio = 0.05; const timeout_level = 180; const max_sleep_time = 120; const sleep_count = 5; const wakeup_commit_count = 5; const sleep_commit_count = 5; const sleep_notify_count = 5; const profit_count = 90; const avg_order_time = 180; private function sleep_item($ctls) { $sleeps = []; $workers = []; foreach ($ctls as $item) { [$fSleep,$time] = $item->sleeping(); if(!$fSleep) { [$count,$ratio] = $item->notify_ratio(); if($count >= self::sleep_count && $ratio < self::sleep_ratio) { $item->sleep(); $sleeps[] = $item; } else { $workers[] = $item; } } elseif(time() < $time + self::max_sleep_time) { $item->wakeup(); $workers[] = $item; } else { $sleeps[] = $item; } } return [$sleeps,$workers]; } private function knockout($ctls,$amount) { $waker = function ($items,$max_sleep_time) { $workers = []; $wakeups = []; $sleeps = []; foreach ($items as $item) { [$fSleep,$time] = $item->sleeping(); if($fSleep) { if(time() > $time + $max_sleep_time) { $item->wakeup(); $wakeups[] = $item; } else { $sleeps[] = $item; } } else { $workers[] = $item; } } return [$sleeps,$wakeups,$workers]; }; $sleeper = function($sleeps,$wakeups,$workers) { $index = 0; foreach ($workers as $item) { if($index == 0) { $wakeups [] = $item; } else { [$notify_count,$notify_succ] = $item->notify_statics(); [$commit_count,$commit_succ] = $item->commit_statics(); if($commit_succ >= self::sleep_commit_count && $notify_count >= self::sleep_notify_count) { $item->sleep(); $sleeps[] = $item; } else { $wakeups[] = $item; } } $index++; } return [$sleeps,$wakeups]; }; $desc_profit = function ($l, $r) use($amount) { [$lCount,$lRatio] = $l->notify_ratio(); [$rCount,$rRatio] = $r->notify_ratio(); $lProfit = $amount - $l->price(); $rRrofit = $amount - $r->price(); $lProfitRatio = $lProfit * $lRatio; $rRrofitRatio = $rRrofit * $rRatio; if($lProfitRatio > $rRrofitRatio) return -1; elseif($lProfitRatio < $rRrofitRatio) return 1; else return 0; }; [$sleeps,$wakeups,$workers] = $waker($ctls,self::max_sleep_time); usort($workers, $desc_profit); return $sleeper($sleeps,$wakeups,$workers); } public function feed_as_lazy_speed($ctls) { //当前提交量计算喂订单 $asc_speed = function ($l, $r) { $lspeed = $l->lazy_speed(); $rspeed = $r->lazy_speed(); return $lspeed < $rspeed ? -1 : 1; }; usort($ctls, $asc_speed); $feeds = []; $profits = []; foreach ($ctls as $item) { $name = $item->name(); [$count,$ratio] = $item->notify_ratio(); $lazy_spped = $item->lazy_speed(); Log::record("auto_match channel {$name} count = {$count} ratio={$ratio} speed={$lazy_spped}",Log::DEBUG); if($lazy_spped < self::feed_minorder) { $feeds[] = $item; } else { $profits[] = $item; } } return [$feeds,$profits]; } public function feed_as_commit($ctls) { //当前提交量计算喂订单 $ascer = function ($l, $r) { [$lCount,$lSucc] = $l->commit_statics(); [$rCount,$rSucc] = $r->commit_statics(); return $lSucc < $rSucc ? -1 : 1; }; usort($ctls, $ascer); $feeds = []; $profits = []; foreach ($ctls as $item) { $name = $item->name(); [$comit_count,$commit_succ] = $item->commit_statics(); [$notify_count,$notify_succ] = $item->notify_statics(); $speed = $item->lazy_speed(); if($commit_succ < self::wakeup_commit_count && $speed < self::feed_minorder) { $feeds[] = $item; $state = 'feed'; } else { $profits[] = $item; $state = 'profit'; } Log::record("auto_match channel {$name} state={$state} comit_succ={$commit_succ} count={$comit_count} notify_count={$notify_count} notify_succ={$notify_succ} speed={$speed}", Log::DEBUG); } return [$feeds,$profits]; } public function auto_match($names, int $spec, int $card_type, int $quality, $out_price, $left_time) : array { $formater = function ($val) { return number_format($val,10,'.',''); }; $desctor = function ($item) use($out_price,$formater,$card_type,$spec) { $name = $item->name(); [$notify_count,$ratio] = $item->notify_ratio(); [$commit_count,$commit_succ] = $item->commit_statics(); $prifit = $out_price - $item->price(); $profit_ratio = $formater($prifit * $ratio); [$fSleep,$time] = $item->sleeping(); if($fSleep) { $state = "sleeping"; $time = $time + 120 - time(); } else { $state = "waking"; } return "{$card_type}-{$spec} {$name} {$state} time={$time} commit_succ={$commit_succ} notify_count={$notify_count} ratio={$ratio} profit_ratio={$profit_ratio}"; }; $left_times = intval($left_time / self::avg_order_time); $left_times = $left_times <= 0 ? 1 : $left_times; Log::record("left_times = {$left_times}",Log::DEBUG); $desc_profit = function ($l, $r) use($out_price,$formater,$left_times) { [$lCount,$lRatio] = $l->notify_ratio(); [$rCount,$rRatio] = $r->notify_ratio(); $lProfit = $out_price - $l->price(); $rRrofit = $out_price - $r->price(); $lProfitRatio = $lProfit * $lRatio; $rRrofitRatio = $rRrofit * $rRatio; $lProfit = $formater($lProfit); $rRrofit = $formater($rRrofit); $lProfitRatio = $formater($lProfitRatio); $rRrofitRatio = $formater($rRrofitRatio); $lRatio = $formater($lRatio); $rRatio = $formater($rRatio); $lRatios = intval($lRatio * $left_times + 0.05); $rRatios = intval($rRatio * $left_times + 0.05); $lRatios = $lRatios > 1 ? 1 : $lRatios; $rRatios = $rRatios > 1 ? 1 : $rRatios; if($lRatios == $rRatios && $lRatios == 1) { //次数大概率成功,按利润优先 if($lProfit > $rRrofit) return -1; elseif($lProfit < $rRrofit) return 1; else return 0; } elseif($lProfitRatio > $rRrofitRatio) return -1; elseif($lProfitRatio < $rRrofitRatio) return 1; elseif($lProfitRatio == 0) //利润获得概率为0,说明成功率或者利润可能性为0 { if($lCount > $rCount) //此时优先喂单 { if($rCount >= self::profit_count) //单量都大于阈值,优先利润 { if($lProfit > $rRrofit) return -1; elseif($lProfit < $rRrofit) return 1; else return 0; } else { return 1; } } elseif($lCount < $rCount) { if($lCount >= self::profit_count) //单量都大于阈值,优先利润 { if($lProfit > $rRrofit) return -1; elseif($lProfit < $rRrofit) return 1; else return 0; } else { return -1; } } else { $count = $lCount; if($count >= self::profit_count) //单量都大于阈值,优先利润 { if($lProfit > $rRrofit) return -1; elseif($lProfit < $rRrofit) return 1; else return 0; } else { return 0; } } } elseif ($lRatio > $rRatio) return -1; //以下是利润概率相等,优先成功率保障 elseif ($lRatio < $rRatio) return 1; else return 0; }; $pThis = $this; $speed_locker = function ($names,$spec,$card_type,$quality) use($pThis) { $ctls = []; foreach ($names as $name) { $key = $pThis->prefix($name,$spec,$card_type,$quality); if(array_key_exists($key,$pThis->mSpeedtable)) { $item = $pThis->mSpeedtable[$key]; $item->calc_speed(); $ctls[] = $item; } else { Log::record("auto_match speed table key={$key} is empty.",Log::DEBUG); } } return $ctls; }; $logger = function ($items,$label) use ($desctor) { foreach ($items as $item) { $msg = $desctor($item); Log::record("auto_match {$label} = {$msg}",Log::DEBUG); } }; $names = array_unique($names); Log::record("auto_match outprice= {$out_price} names=" . implode(',', $names), Log::DEBUG); $can_feed = true;//$left_times > 1; $ctls = $speed_locker($names,$spec,$card_type,$quality); [$sleeps,$workers] = $this->knockout($ctls,$out_price); $logger($sleeps,'sleeps'); $logger($workers,'workers'); if(empty($workers)) { $ctls = $sleeps; $sleeps = []; } else { $ctls = $workers; } //找出需要喂单 [$feeds,$profits] = $this->feed_as_commit($ctls); usort($feeds, $desc_profit); $profits = array_merge($profits, $sleeps); usort($profits, $desc_profit); $normals = []; $overloads = []; foreach ($profits as $item) { if($item->speed_overload()) { $overloads[] = $item; } else { $normals[] = $item; } } $profits = array_merge($normals, $overloads); $feed_names = []; foreach ($feeds as $item) { $feed_names[] = $item->name(); } $profit_names = []; foreach ($profits as $item) { $profit_names[] = $item->name(); } if ($can_feed) { $result = array_merge($feed_names, $profit_names); } else { $result = array_merge($profit_names,$feed_names); } $scan_feed = $can_feed ? 'true' : 'false'; $logger($feeds,'feeds'); $logger($profits,'profits'); Log::record("auto_match {$card_type}-{$spec} can_feed = {$scan_feed} result =" . implode(',',$result),Log::DEBUG); return $result; } }