|
@@ -86,7 +86,7 @@ class chctlex
|
|
|
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);
|
|
|
+ $this->mSpeedtable[$key] = new ctl_itemex($name,$card_type,$amount,$speed,$sort,0,$opened,$quality);
|
|
|
$this->mNameMapQuality["{$name}-{$card_type}-{$amount}"] = $quality;
|
|
|
}
|
|
|
}
|
|
@@ -95,32 +95,22 @@ class chctlex
|
|
|
|
|
|
public function update_chctl($params)
|
|
|
{
|
|
|
+ foreach ($params as $key => $val)
|
|
|
+ {
|
|
|
+ [$name, $spec, $card_type] = explode('-', $key);
|
|
|
|
|
|
+ $quality = $this->mNameMapQuality["{$name}-{$card_type}-{$spec}"] ?? 0;
|
|
|
+ if($quality <=0) continue;
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- 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);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ $prefix = $this->prefix($name,$spec,$card_type,$quality);
|
|
|
+ $ctl = $this->mSpeedtable[$prefix] ?? null;
|
|
|
+ if(is_null($ctl)) continue;
|
|
|
|
|
|
- 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);
|
|
|
- }
|
|
|
+ $ctl->update_params($val);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ //
|
|
|
public function match($names, int $spec, int $card_type, int $quality, $max_inprice)
|
|
|
{
|
|
|
$trace = new scope_trace(__METHOD__);
|
|
@@ -140,16 +130,12 @@ class chctlex
|
|
|
$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;
|
|
|
};
|
|
@@ -162,10 +148,6 @@ class chctlex
|
|
|
{
|
|
|
if($item->opened()) {
|
|
|
$usable_items[] = $item;
|
|
|
- $item->calc_speed(); //此处必须先计算速率,因为usort函数里面不能使用协程。
|
|
|
- } else {
|
|
|
-// $key = $item->prefix();
|
|
|
-// Log::record("key={$key} has not opened",Log::DEBUG);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -181,16 +163,14 @@ class chctlex
|
|
|
$lproity = $l->priority();
|
|
|
$rproity = $r->priority();
|
|
|
|
|
|
- //usort 函数内部,不可使用协程等待之类的代码.
|
|
|
$lover = $l->speed_overload() ? 1 : 0;
|
|
|
$rover = $r->speed_overload() ? 1 : 0;
|
|
|
|
|
|
if($lover == $rover)
|
|
|
{
|
|
|
- if($lover) {
|
|
|
+ if ($lover) {
|
|
|
return $lproity > $rproity ? -1 : 1; //如果都过载保优先级高的
|
|
|
- }
|
|
|
- else {
|
|
|
+ } else {
|
|
|
return $lproity < $rproity ? -1 : 1;
|
|
|
}
|
|
|
}
|
|
@@ -234,253 +214,49 @@ class chctlex
|
|
|
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)
|
|
|
+ $desc_profit = function ($l, $r) use($out_price)
|
|
|
{
|
|
|
- [$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
|
|
|
+ $lVal = $l->compile_val($lProfit);
|
|
|
+ $rVal = $r->compile_val($lProfit);
|
|
|
+
|
|
|
+ if ($lVal == $rVal)
|
|
|
{
|
|
|
- if($lCount > $rCount) //此时优先喂单
|
|
|
+ if($lVal == 0.0)
|
|
|
{
|
|
|
- if($rCount >= self::profit_count) //单量都大于阈值,优先利润
|
|
|
- {
|
|
|
- if($lProfit > $rRrofit) return -1;
|
|
|
- elseif($lProfit < $rRrofit) return 1;
|
|
|
- else return 0;
|
|
|
- }
|
|
|
- else {
|
|
|
+ $lSubmits = $l->submits();
|
|
|
+ $rSubmits = $r->submits();
|
|
|
+
|
|
|
+ if($lSubmits > $rSubmits) {
|
|
|
return 1;
|
|
|
}
|
|
|
- }
|
|
|
- elseif($lCount < $rCount)
|
|
|
- {
|
|
|
- if($lCount >= self::profit_count) //单量都大于阈值,优先利润
|
|
|
- {
|
|
|
- if($lProfit > $rRrofit) return -1;
|
|
|
- elseif($lProfit < $rRrofit) return 1;
|
|
|
- else return 0;
|
|
|
- }
|
|
|
- else {
|
|
|
+ elseif ($lProfit < $rRrofit) {
|
|
|
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 ($lProfit > $rRrofit) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ elseif ($lProfit < $rRrofit) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ elseif ($lVal > $rVal) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return 1;
|
|
|
}
|
|
|
- elseif ($lRatio > $rRatio) return -1; //以下是利润概率相等,优先成功率保障
|
|
|
- elseif ($lRatio < $rRatio) return 1;
|
|
|
- else return 0;
|
|
|
};
|
|
|
|
|
|
|
|
@@ -494,8 +270,6 @@ class chctlex
|
|
|
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;
|
|
@@ -510,78 +284,92 @@ class chctlex
|
|
|
return $ctls;
|
|
|
};
|
|
|
|
|
|
- $logger = function ($items,$label) use ($desctor)
|
|
|
+ $ctl_splitor = function ($ctls)
|
|
|
{
|
|
|
- foreach ($items as $item) {
|
|
|
- $msg = $desctor($item);
|
|
|
- Log::record("auto_match {$label} = {$msg}",Log::DEBUG);
|
|
|
+ $feeds = [];
|
|
|
+ $workers = [];
|
|
|
+ $overs = [];
|
|
|
+
|
|
|
+ foreach ($ctls as $item)
|
|
|
+ {
|
|
|
+ if($item->need_feed()) {
|
|
|
+ $feeds[] = $item;
|
|
|
+ }
|
|
|
+ elseif($item->speed_overload()) {
|
|
|
+ $overs[] = $item;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ $workers[] = $item;
|
|
|
+ }
|
|
|
}
|
|
|
- };
|
|
|
|
|
|
- $names = array_unique($names);
|
|
|
- Log::record("auto_match outprice= {$out_price} names=" . implode(',', $names), Log::DEBUG);
|
|
|
+ return [$feeds,$workers,$overs];
|
|
|
+ };
|
|
|
|
|
|
- $can_feed = true;
|
|
|
- $ctls = $price_filter($names,$spec,$card_type,$quality);
|
|
|
- [$sleeps,$wakeups] = $this->knockout($ctls,$out_price);
|
|
|
+ $assign_feeds = function ($feeds)
|
|
|
+ {
|
|
|
+ if(empty($feeds)) {
|
|
|
+ return [[],[]];
|
|
|
+ }
|
|
|
|
|
|
- $logger($sleeps,'sleeps');
|
|
|
- $logger($wakeups,'wakeups');
|
|
|
+ $count = count($feeds);
|
|
|
+ $pos = mt_rand(0, $count * 60);
|
|
|
|
|
|
- if(empty($wakeups)) {
|
|
|
- $ctls = $sleeps;
|
|
|
- $sleeps = [];
|
|
|
- } else {
|
|
|
- $ctls = $wakeups;
|
|
|
- }
|
|
|
+ $cur_feeds = [];
|
|
|
+ $next_feeds = [];
|
|
|
|
|
|
- //找出需要喂单
|
|
|
- [$feeds,$profits] = $this->feed_as_commit($ctls);
|
|
|
- usort($feeds, $desc_profit);
|
|
|
+ $i = 0;
|
|
|
+ foreach ($feeds as $item)
|
|
|
+ {
|
|
|
+ if($i == $pos) {
|
|
|
+ $cur_feeds[] = $item;
|
|
|
+ } else {
|
|
|
+ $next_feeds[] = $item;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- $profits = array_merge($profits, $sleeps);
|
|
|
- usort($profits, $desc_profit);
|
|
|
+ return [$cur_feeds,$next_feeds];
|
|
|
+ };
|
|
|
|
|
|
- $normals = [];
|
|
|
- $overloads = [];
|
|
|
- foreach ($profits as $item)
|
|
|
+ $time_map = function ($ctls, $left_time)
|
|
|
{
|
|
|
- if($item->speed_overload()) {
|
|
|
- $overloads[] = $item;
|
|
|
- }
|
|
|
- else {
|
|
|
- $normals[] = $item;
|
|
|
+ $header = [];
|
|
|
+ $ender = [];
|
|
|
+ foreach ($ctls as $item)
|
|
|
+ {
|
|
|
+ if($item->notify_time() > $left_time) {
|
|
|
+ $header[] = $item;
|
|
|
+ } else {
|
|
|
+ $ender[] = $item;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
+
|
|
|
+ return array_merge($header,$ender);
|
|
|
+ };
|
|
|
+
|
|
|
+ $names = array_unique($names);
|
|
|
+ Log::record("auto_match outprice= {$out_price} names=" . implode(',', $names), Log::DEBUG);
|
|
|
+ $ctls = $price_filter($names,$spec,$card_type,$quality);
|
|
|
+ [$feeds,$workers,$overloads] = $ctl_splitor($ctls);
|
|
|
+
|
|
|
+ [$cur_feeds,$next_feeds] = $assign_feeds($feeds);
|
|
|
+ usort($workers, $desc_profit);
|
|
|
|
|
|
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();
|
|
|
- }
|
|
|
+ $ctls = array_merge($cur_feeds, $workers, $overloads, $next_feeds);
|
|
|
+ $ctls = $time_map($ctls,$left_time);
|
|
|
|
|
|
- $profit_names = [];
|
|
|
- foreach ($profits as $item) {
|
|
|
- $profit_names[] = $item->name();
|
|
|
+ $names = [];
|
|
|
+ foreach ($ctls as $item) {
|
|
|
+ $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;
|
|
|
+ Log::record("auto_match result names=" . implode(',', $names), Log::DEBUG);
|
|
|
+ return $names;
|
|
|
}
|
|
|
}
|