123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- <?php
- namespace refill;
- use Log;
- use mtopcard;
- use algorithm;
- use scope_trace;
- class chctl
- {
- protected $mSpeedtable;
- static $cache_names = [
- ['quality'=> 1,'name' => 'channel-ctl-oil-common-limit'],
- ['quality'=> 2,'name' => 'channel-ctl-oil-fast-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 update_speeds($speeds)
- {
- foreach ($speeds as $key => $speed)
- {
- if(array_key_exists($key,$this->mSpeedtable)) {
- $item = $this->mSpeedtable[$key];
- $item->set_speed($speed);
- }
- }
- }
- 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, $max_inprice)
- {
- $trace = new scope_trace(__METHOD__);
- if ($card_type == mtopcard\ThirdRefillCard) {
- return $names;
- }
- $pThis = $this;
- $price_filter = function ($names,$spec,$card_type,$quality) use($pThis,$max_inprice)
- {
- $ctls = [];
- foreach ($names as $name)
- {
- $key = $pThis->prefix($name,$spec,$card_type,$quality);
- if(array_key_exists($key,$pThis->mSpeedtable))
- {
- $item = $pThis->mSpeedtable[$key];
- $inPrice = $item->price();
- // Log::record("max_price = {$max_inprice},in_price={$inPrice}",Log::DEBUG);
- if ($max_inprice !== false && $inPrice > $max_inprice) {
- continue;
- } else {
- $ctls[] = $item;
- }
- }
- else {
- // Log::record("auto_match speed table key={$key} is empty.",Log::DEBUG);
- }
- }
- return $ctls;
- };
- $ctl_items = $price_filter($names,$spec,$card_type,$quality);
- $chooser = function ($ctl_items)
- {
- $usable_items = [];
- foreach ($ctl_items as $item)
- {
- if($item->opened()) {
- $usable_items[] = $item;
- $item->calc_speed(); //此处必须先计算速率,因为usort函数里面不能使用协程。
- } else {
- // $key = $item->prefix();
- // Log::record("key={$key} has not opened",Log::DEBUG);
- }
- }
- return $usable_items;
- };
- //去掉已经关闭通道
- $usable_items = $chooser($ctl_items);
- //不过载的排在前面
- $ascending = function ($l, $r)
- {
- $lproity = $l->priority();
- $rproity = $r->priority();
- //usort 函数内部,不可使用协程等待之类的代码.
- $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 = [];
- $over_loads = [];
- foreach ($usable_items as $item)
- {
- $name = $item->name();
- $over_load = $item->speed_overload();
- if($over_load) {
- $over_loads[] = $item;
- } else {
- $result[] = $name;
- }
- }
- if(!empty($over_loads))
- {
- $assigner = new overload_assigner();
- $assigner->add($over_loads);
- $over_loads = $assigner->assign();
- foreach ($over_loads as $item) {
- $result[] = $item->name();
- }
- }
- return $result;
- }
- private function prefix($name,$spec,$card_type,$quality)
- {
- return "{$name}-{$spec}-{$card_type}-{$quality}";
- }
- const feed_minorder = 1;
- const sleep_ratio = 0.005;
- const max_sleep_time = 120;
- const sleep_count = 5;
- const wakeup_commit_count = 10;
- const sleep_commit_count = 15;
- const sleep_notify_count = 15;
- const profit_count = 90;
- const avg_order_time = 120;
- 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)
- {
- foreach ($workers as $item)
- {
- [$notify_count, $ratio] = $item->notify_ratio();
- [$commit_count, $commit_succ] = $item->commit_statics();
- if ($commit_succ >= self::sleep_commit_count && $notify_count >= self::sleep_notify_count && $ratio < self::sleep_ratio) {
- $item->sleep();
- $sleeps[] = $item;
- } else {
- $wakeups[] = $item;
- }
- }
- 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);
- }
- private 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($comit_count < 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, $max_inprice, $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_count={$commit_count} 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;
- $price_filter = function ($names,$spec,$card_type,$quality) use($pThis,$max_inprice)
- {
- $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();
- $inPrice = $item->price();
- if ($max_inprice !== false && $inPrice > $max_inprice) {
- continue;
- } else {
- $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;
- $ctls = $price_filter($names,$spec,$card_type,$quality);
- [$sleeps,$wakeups] = $this->knockout($ctls,$out_price);
- $logger($sleeps,'sleeps');
- $logger($wakeups,'wakeups');
- if(empty($wakeups)) {
- $ctls = $sleeps;
- $sleeps = [];
- } else {
- $ctls = $wakeups;
- }
- //找出需要喂单
- [$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;
- }
- }
- if(!empty($overloads)) {
- $assigner = new overload_assigner();
- $assigner->add($overloads);
- $overloads = $assigner->assign();
- }
- $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;
- }
- }
|