stanley-king 2 jaren geleden
bovenliggende
commit
328ffca30f

+ 1 - 1
docker/compose/homecuda/acc/docker-compose.yml

@@ -45,7 +45,7 @@ services:
     volumes:
       - ../../../../:/var/www/html
       - ../conf/etc/localtime:/etc/localtime:ro
-      - ../conf/php/php-debug.ini:/usr/local/etc/php/php.ini
+      - ../conf/php/php.ini:/usr/local/etc/php/php.ini
       - /mnt/upload:/var/www/html/data/upload
       - /mnt/shoplog:/var/www/html/data/log
       - ../conf/php/vapi-spwan-start:/usr/local/bin/docker-spwan-start

+ 11 - 0
docker/compose/homecuda/statcalc/docker-compose.yml

@@ -11,3 +11,14 @@ services:
       - /mnt/stdata:/var/www/html/data/stdata
     container_name: "panda-mamount"
     command: [ 'python','mamount.py', '-h', '192.168.3.104', '-p', '6379' ]
+
+  mpratios:
+    image: pycpu:3.7.10
+    volumes:
+      - ../../../../:/var/www/html
+      - ../conf/etc/localtime:/etc/localtime:ro
+      - /mnt/upload:/var/www/html/data/upload
+      - /mnt/shoplog:/var/www/html/data/log
+      - /mnt/stdata:/var/www/html/data/stdata
+    container_name: "panda-mpratios"
+    command: [ 'python','mprofit_ratio.py', '-h', '192.168.3.104', '-p', '6379' ]

+ 1 - 1
docker/compose/homecuda/worker/docker-compose.yml

@@ -6,7 +6,7 @@ services:
     volumes:
       - ../../../../:/var/www/html
       - ../conf/etc/localtime:/etc/localtime:ro
-      - ../conf/php/php-swoole.ini:/usr/local/etc/php/php.ini
+      - ../conf/php/php-swoole-debug.ini:/usr/local/etc/php/php.ini
       - /mnt/upload:/var/www/html/data/upload
       - /mnt/shoplog:/var/www/html/data/log
     container_name: "panda-codispatcher"

+ 11 - 0
docker/compose/workcuda/statcalc/docker-compose.yml

@@ -11,3 +11,14 @@ services:
       - /mnt/stdata:/var/www/html/data/stdata
     container_name: "panda-mamount"
     command: [ 'python','mamount.py', '-h', '192.168.3.46', '-p', '6379' ]
+
+  mpratios:
+    image: pycpu:3.7.10
+    volumes:
+      - ../../../../:/var/www/html
+      - ../conf/etc/localtime:/etc/localtime:ro
+      - /mnt/upload:/var/www/html/data/upload
+      - /mnt/shoplog:/var/www/html/data/log
+      - /mnt/stdata:/var/www/html/data/stdata
+    container_name: "panda-mpratios"
+    command: [ 'python','mprofit_ratio.py', '-h', '192.168.3.46', '-p', '6379' ]

+ 3 - 0
helper/refill/XYZRefillFactory.php

@@ -26,7 +26,10 @@ require_once(BASE_HELPER_PATH . '/refill/policy/PolicyUtil.php');
 require_once(BASE_HELPER_PATH . '/refill/policy/xyz/quality_ploy.php');
 require_once(BASE_HELPER_PATH . '/refill/policy/mgroup.php');
 require_once(BASE_HELPER_PATH . '/refill/policy/channel_filter.php');
+
 require_once(BASE_HELPER_PATH . '/refill/policy/mratio_control.php');
+require_once(BASE_HELPER_PATH . '/refill/policy/mratio_controlex.php');
+
 require_once(BASE_HELPER_PATH . '/refill/policy/overload_assigner.php');
 require_once(BASE_HELPER_PATH . '/refill/policy/interceptor.php');
 require_once(BASE_HELPER_PATH . '/refill/policy/transfer.php');

+ 4 - 4
helper/refill/api/test/qianqian/RefillPhone.php

@@ -26,11 +26,11 @@ class RefillPhone extends refill\IRefillPhone
 
     public function add($card_no, $card_type, $amount, $params,&$net_errno = 0)
     {
-        $net_errno = "HTTP-504";
-        return [false, '网络错误', true];
+//        $net_errno = "HTTP-504";
+//        return [false, '网络错误', true];
 
-//        refill\util::send_normal($params['order_sn']);
-//        return [true , '',false];
+        refill\util::send_normal($params['order_sn']);
+        return [true , '',false];
     }
 
     public function query($refill_info)

+ 3 - 0
helper/refill/order.php

@@ -199,6 +199,7 @@ class order
     public function is_phone() {
         return in_array($this->mCardType,[mtopcard\ChinaMobileCard,mtopcard\ChinaUnicomCard,mtopcard\ChinaTelecomCard]);
     }
+
     private function setParams($params)
     {
         $this->mMchid = intval($params['mchid']);
@@ -215,8 +216,10 @@ class order
         $this->mCommitTimes = $params['commit_times'] ?? 0;
 
         $this->mLastOrderID = $params['order_id'] ?? 0;
+
         $this->mQuantity   = $params['quantity'] ?? 1;
         $this->mOriginQuality = intval($params['org_quality']) ?? 0;
+
         $this->mMatchRatio = $params['match_ratio'] ?? false;
         $this->mMchCardTypes = $params['mch_card_types'] ?? [];
 

+ 373 - 0
helper/refill/policy/mratio_controlex.php

@@ -0,0 +1,373 @@
+<?php
+
+namespace refill;
+
+use Log;
+
+class mratio_controlex
+{
+    private $mTimesConfig; //对应refill.ini 配置文件数据
+    private $mInterceptConfig;
+
+    private $mGrossRatios;
+    private $mDetailRatios;
+
+    private $mMchQTS;
+    private $mMixedPrices;
+
+
+    public function __construct()
+    {
+        $this->mTimesConfig = [];
+        $this->mInterceptConfig = [];
+
+        $this->mGrossRatios = [];
+        $this->mDetailRatios = [];
+
+        $this->mMixedPrices = [];
+    }
+
+    public function load()
+    {
+        $this->load_retry();
+        $this->load_intercept();
+    }
+
+    private function load_retry()
+    {
+        $isDay = functional::isDay();
+        $mch_configs = function ($isDay)
+        {
+            $result = [];
+
+            $i = 0;
+            while (true)
+            {
+                $start = $i * 100;
+                $items = Model()->table('merchant')->where(['mchid' => ['gt', 0], 'merchant_state' => 1])->field('mchid,retry_times_cfg')->order('mchid asc')->limit("{$start},100")->select();
+                if(empty($items)) {
+                    break;
+                }
+                $i++;
+
+                foreach ($items as $item)
+                {
+                    $mchid = intval($item['mchid']);
+                    if($mchid <= 0) continue;
+
+                    $retry_times_cfg = unserialize($item['retry_times_cfg']);
+                    if(empty($retry_times_cfg)) continue;
+
+                    $qualities = &$retry_times_cfg['qualities'];
+                    foreach ($qualities as $quality => $cfg)
+                    {
+                        if ($isDay) {
+                            $qualities[$quality]['secs'] = $cfg['day_secs'];
+                        } else {
+                            $qualities[$quality]['secs'] = $cfg['night_secs'];
+                        }
+                    }
+
+                    $result[$mchid] = $retry_times_cfg;
+                }
+            }
+
+            return $result;
+        };
+
+        $this->mTimesConfig = $mch_configs($isDay);
+    }
+
+    private function load_intercept()
+    {
+        $mch_configs = function ()
+        {
+            $result = [];
+
+            $i = 0;
+            while (true)
+            {
+                $start = $i * 100;
+                $items = Model()->table('merchant')->where(['mchid' => ['gt', 0], 'merchant_state' => 1])->field('mchid,intercept_cfg')->order('mchid asc')->limit("{$start},100")->select();
+                if(empty($items)) {
+                    break;
+                }
+                $i++;
+
+                foreach ($items as $item)
+                {
+                    $mchid = intval($item['mchid']);
+                    if($mchid <= 0) continue;
+
+                    $cfg = unserialize($item['intercept_cfg']);
+                    if(empty($cfg)) continue;
+
+                    if(!empty($cfg['segment']))
+                    {
+                        $segment = $cfg['segment'];
+                        $sitems = explode(',',$segment);
+
+                        $tmp = [];
+                        foreach ($sitems as $sitem)
+                        {
+                            $sitem = trim($sitem);
+                            if(!empty($sitem)) {
+                                $tmp[] = $sitem;
+                            }
+                        }
+
+                        if(!empty($tmp)) {
+                            $cfg['segment'] = implode('|',$tmp);
+                        } else {
+                            $cfg['segment'] = '';
+                        }
+                    }
+
+                    $result[$mchid] = $cfg;
+                }
+            }
+
+            return $result;
+        };
+
+        $this->mInterceptConfig = $mch_configs();
+    }
+
+    public function update($gross_ratios, $detail_ratios)
+    {
+        if (!empty($gross_ratios)) {
+            $this->mGrossRatios = $gross_ratios;
+        }
+        if (!empty($detail_ratios)) {
+            $this->mDetailRatios = $detail_ratios;
+        }
+
+        $this->mMchQTS = [];
+        foreach ($detail_ratios as $key=>$val)
+        {
+            [$mchid,$card_type,$spec] = explode('-',$key);
+
+            $mchid = intval($mchid);
+            $card_type = intval($card_type);
+            $spec = intval($spec);
+
+            $this->mMchQTS[$mchid][] = [$card_type,$spec];
+        }
+    }
+
+    public function setMixedPrice($mchid, $mixed_quality, $card_type, $spec, $prices)
+    {
+        $key = "{$card_type}-{$spec}";
+        $this->mMixedPrices[$mchid][$mixed_quality][$key] = $prices;
+    }
+
+    public function total($mchid,$qualities)
+    {
+        if (array_key_exists($mchid, $this->mTimesConfig))
+        {
+            $items = $this->mTimesConfig[$mchid]['qualities'];
+
+            $times = 0;
+            $secs = 0;
+            foreach ($items as $quality => $val)
+            {
+                if (!in_array($quality,$qualities,true)) {
+                    continue;
+                }
+
+                $times += $val['times'] ?? 1;
+                $secs += $val['secs'] ?? 180;
+            }
+
+            return [true, $times, $secs];
+        } else {
+            return [false, 0, 0];
+        }
+    }
+
+    public function times($mchid, $quality)
+    {
+        if (array_key_exists($mchid, $this->mTimesConfig)) {
+            $items = $this->mTimesConfig[$mchid]['qualities'] ?? [];
+            if (array_key_exists($quality, $items)) {
+                return $items[$quality]['times'];
+            }
+        }
+
+        return false;
+    }
+
+    public function seconds($mchid, $quality)
+    {
+        if (array_key_exists($mchid, $this->mTimesConfig))
+        {
+            $items = $this->mTimesConfig[$mchid]['qualities'] ?? [];
+            if (array_key_exists($quality, $items)) {
+                return $items[$quality]['secs'];
+            }
+        }
+
+        return false;
+    }
+
+    public function exist($mchid)
+    {
+        if (array_key_exists($mchid, $this->mTimesConfig)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private function lower_ratio($mchid)
+    {
+        $lower_ratio = $this->mTimesConfig[$mchid]['lower_ratio'] ?? [];
+        if (empty($lower_ratio)) {
+            return [0.0, 3600];
+        } else {
+            return [$lower_ratio['ratio'], $lower_ratio['period']];
+        }
+    }
+
+    private function profit_ratio($mchid) {
+        $profit_ratio = $this->mTimesConfig[$mchid]['profit_ratio'] ?? 0.0;
+        return $profit_ratio;
+    }
+
+    //[$succ_count, $fail_count, $succ_ratio, $profit, $profit_ratio]
+    private function gross_ratio($mchid)
+    {
+        $mratios = $this->mGrossRatios;
+        if (array_key_exists($mchid, $mratios)) {
+            return $mratios[$mchid];
+        }
+
+        return [0, 0, 0, 0, 0];
+    }
+
+    //[$succ_count, $fail_count, $succ_ratio, $profit, $profit_ratio]
+    private function detail_ratio($mchid, $card_type, $spec)
+    {
+        $key = "{$mchid}-{$card_type}-{$spec}";
+        if(array_key_exists($key,$this->mDetailRatios)) {
+            return $this->mDetailRatios[$key];
+        }
+
+        return [0, 0, 0, 0, 0];
+    }
+
+    //return true 表示当前质量满足条件。
+    public function ratio_match($mchid, $org_quality, $card_type, $spec, $qualities)
+    {
+        if(count($qualities) <= 1) {
+            return true;
+        }
+
+        $header = __METHOD__ . " mchid={$mchid} card_type={$card_type} spec={$spec}";
+        [$lower_ratio, $period] = $this->lower_ratio($mchid);
+
+        [$submit_count, $succ_count, $fail_count, $gross_ratio, $profit, $profit_ratio] = $this->gross_ratio($mchid);
+        Log::record("{$header} gross_ratio = {$gross_ratio},lower_ratio={$lower_ratio}",Log::DEBUG);
+
+        if (!PolicyUtil::mixed_quality($org_quality))
+        {
+            if ($gross_ratio >= $lower_ratio) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+        elseif ($gross_ratio >= $lower_ratio) {
+            return true;
+        } 
+        else {
+            return $this->all_checker($mchid, $card_type, $spec, $header, $lower_ratio);
+        }
+    }
+
+    private function pre_checker($mchid, $card_type, $spec, $header, $lower_ratio)
+    {
+        [$succ_count, $fail_count, $cur_ratio, $profit, $cur_pratio] = $this->detail_ratio($mchid, $card_type, $spec);
+        Log::record("{$header} $cur_pratio={$cur_pratio},lower_pratio={$lowest_pratio}",Log::DEBUG);
+
+        $accumute_count = function ($mchid,$type_specs)
+        {
+            $succs = 0;
+            $fails = 0;
+            foreach ($type_specs as $item) {
+                [$card_type, $spec] = $item;
+                [$succ_count, $fail_count, $cur_ratio, $profit, $cur_pratio] = $this->detail_ratio($mchid, $card_type, $spec);
+                $succs += $succ_count;
+                $fails += $fail_count;
+            }
+
+            return [$succs,$fails];
+        };
+
+        $type_specs_calc = function ($mchid,$succs,$fails,$type_specs)
+        {
+
+        };
+
+
+        $type_specs = $this->mMchQTS[$mchid];
+        [$succs, $fails] = $accumute_count($mchid, $type_specs);
+
+        if ($cur_pratio >= $lowest_pratio)
+        {
+            if($cur_ratio >= $lowest_ratio) {
+                return false;
+            }
+
+            return false;
+        }
+    }
+
+    private function all_checker($mchid, $card_type, $spec, $header, $lowest_ratio)
+    {
+        $lowest_pratio = $this->profit_ratio($mchid);
+        [$succ_count, $fail_count, $gross_ratio, $profit, $gross_pratio] = $this->gross_ratio($mchid);
+        //当前毛利润率小于最低利润率的时候,不可以补充了
+        if ($gross_pratio <= $lowest_pratio) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public function need_intercept($mchid,$card_type,$card_state,$is_transfer,$card_no) : bool
+    {
+        $start_with = function ($card_no, $segment)
+        {
+            $reg = "/^(?:{$segment})\d*$/";
+            if(preg_match($reg, $card_no, $matches)) {
+                return true;
+            } else {
+                return false;
+            }
+        };
+
+        $mintercepts = $this->mInterceptConfig;
+        if(array_key_exists($mchid,$mintercepts))
+        {
+            $mintercepts = $mintercepts[$mchid];
+            if(!empty($mintercepts['card_states']) && in_array($card_state,$mintercepts['card_states'],true)) {
+                return true;
+            }
+
+            if(!empty($mintercepts['card_types']) && in_array($card_type,$mintercepts['card_types'],true)) {
+                return true;
+            }
+
+            if($mintercepts['is_transfer'] && $is_transfer) {
+                return true;
+            }
+
+            if (!empty($mintercepts['segment']) && $start_with($card_no,$mintercepts['segment'])) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 452 - 0
helper/refill/policy/quaility-bak.php

@@ -0,0 +1,452 @@
+<?php
+
+namespace refill;
+
+use Log;
+use mtopcard;
+use scope_trace;
+
+class Quality
+{
+    const LowestQuality  = 1;
+    const Normal = 1;
+    const Quick = 2;
+    const CardKey = 3;
+    const ThirdShop = 4;
+    const SlowTwentyFour = 5;
+    const SlowSix = 6;
+    const SlowTwo = 7;
+    const SlowFortyEight = 8;
+    const SlowSeventyTwo = 9;
+    const Fastest = 10;
+    const HighestQuality = 10;
+
+    const SlowNormal  = 11;  // 7 -> 1
+    const ThirdNormal  = 12; // 4 -> 1
+    const DefSuccess = 13;   // 4 -> 3 -> 1 -> 2
+    const NormalQuick = 14;
+    const SlowSixNormal = 15;
+
+    const OilWithoutSN = 1;
+    const OilQuick = 2;
+    const OilCardKey   = 3;
+    const OilWithSN    = 5;
+
+    const OilSN_NONE_HAS   = 20;
+    const OilSN_HAS_NONE   = 21;
+    const OIL_SN_CARDKEY        = 22;
+    const OIL_SNNONE_CARDKEY    = 23;
+    const OIL_SN_SNNONE_CARDKEY = 24;
+    const OIL_SNNONE_SN_CARDKEY = 25;
+    const OIL_SNNONE_SN_CARDKEY_QUICK = 26;
+
+    protected $mMchPhonectl;
+    protected $mMchoilctl;
+    protected $mSpeeds;
+    protected $mQualities;
+    protected $mTryAdjuster;
+
+    protected $mRatioCtl;
+
+    public function __construct()
+    {
+        $this->mMchPhonectl = new mchctl();
+        $this->mMchoilctl = new mchoilctl();
+        $this->mTryAdjuster = new try_judge();
+        $this->mRatioCtl = null;
+    }
+
+    public function load()
+    {
+        $this->mMchPhonectl->load();
+        $this->mMchoilctl->load();
+        $this->mTryAdjuster->load();
+    }
+
+    public function setRatioCtl($ratio_ctl) {
+        $this->mRatioCtl = $ratio_ctl;
+    }
+
+    public function qualities($quality)
+    {
+        if (array_key_exists($quality, $this->mQualities)) {
+            return $this->mQualities[$quality];
+        } else {
+            return [];
+        }
+    }
+
+    public function mechants_quality() {
+        return $this->mMchPhonectl->mechants_quality();
+    }
+
+    public function find_quality($mchid, $card_type, $spec, $org_quality, $cur_quality, $times, $used_time, $caller): array
+    {
+        $trace = new scope_trace(__METHOD__);
+        if($card_type == mtopcard\ChinaMobileCard || $card_type == mtopcard\ChinaUnicomCard || $card_type == mtopcard\ChinaTelecomCard) {
+            return $this->mobile_quality($mchid, $card_type, $spec, $org_quality, $cur_quality, $times, $used_time);
+        }
+        elseif($card_type == mtopcard\PetroChinaCard || $card_type == mtopcard\SinopecCard) {
+            [$org,$qualities] = $this->oil_quality($mchid, $org_quality, $times,$used_time,$caller);
+            return [$org,$qualities,false];
+        }
+        else {
+            return [0, [],false];
+        }
+    }
+
+    private function oil_quality($mchid, $quality, $times,$used_time,$caller): array
+    {
+        Log::record("oil_quality mchid={$mchid},quality={$quality},times={$times}",Log::DEBUG);
+        if($quality == 0)
+        {
+            [$success,$setting_quality,$time_out] = $this->mMchoilctl->getCtls($mchid);
+            if($success)
+            {
+                if(array_key_exists($setting_quality,$this->mQualities)) {
+                    $org = $setting_quality;
+                    $qualities = $this->mQualities[$setting_quality];
+                }
+                else {
+                    $org = $setting_quality;
+                    $qualities = [$setting_quality];
+                }
+            }
+            else {
+                $org = self::Normal;
+                $qualities = $this->mQualities[$org];
+            }
+        }
+        elseif(array_key_exists($quality,$this->mQualities)) {
+            $org = $quality;
+            $qualities = $this->mQualities[$quality];
+        }
+        else {
+            Log::record("find_quality: cannot find any quality",Log::DEBUG);
+            return [0,[]];
+        }
+
+        Log::record("oil_quality find qualities = " . implode(',',$qualities),Log::DEBUG);
+        $qualities = $this->calc_oil_quality($qualities,$times,$used_time,$caller);
+
+        return [$org,$qualities];
+    }
+
+    //通过每种类型通道耗时,倒推当前可用通道,并优先走推荐通道.
+    private function calc_oil_quality($qualities, $times, $used_time, $caller)
+    {
+        $result = [];
+        if($used_time > 900) {
+            return $result;
+        }
+
+        $total_times = 0;
+        foreach ($qualities as $quality)
+        {
+            $cur_times = $caller->calc_times($quality);
+            if($cur_times <= 0) continue;
+
+            $total_times += $cur_times;
+            if($total_times > $times) {
+                $result[] = $quality;
+            }
+        }
+
+        Log::record("calc_oil_quality result = " . implode(',',$result),Log::DEBUG);
+        return $result;
+    }
+
+
+    private function mobile_quality($mchid, $card_type, $spec, $org_quality, $cur_quality, $times, $used_time): array
+    {
+        if($org_quality == 0)
+        {
+            [$success,$setting_quality,$time_out] = $this->mMchPhonectl->getCtls($mchid);
+            if($success)
+            {
+                if(array_key_exists($setting_quality,$this->mQualities)) {
+                    $org_quality = $setting_quality;
+                }
+                else {
+                    $org_quality = $setting_quality;
+                }
+            }
+            else {
+                $org_quality = self::Normal; //如果没设置质量,默认为普通
+                $time_out = $this->mSpeeds[$org_quality]['retry_timeout'];
+            }
+        }
+        elseif(array_key_exists($org_quality,$this->mQualities)) {
+            [$success, $setting_quality, $time_out] = $this->mMchPhonectl->getCtls($mchid);
+        }
+        else {
+            Log::record("find_quality: cannot find any quality",Log::DEBUG);
+            return [0,[],false];
+        }
+
+        if($time_out <= 0) {
+            $time_out = $this->mSpeeds[$org_quality]['retry_timeout'];
+        }
+
+        $qualities = $this->qualities($org_quality);
+        $max_times = $this->max_times($mchid, $org_quality, $qualities);
+        if ($this->mRatioCtl->exist($mchid)) {
+            Log::record("ratio_phone_quality exist=true",Log::DEBUG);
+            [$qualities,$match_ratio] = $this->ratio_phone_quality($org_quality, $qualities, $cur_quality, $times, $used_time, $mchid, $card_type, $spec);
+            return [$org_quality, $qualities,$match_ratio];
+        } else {
+            $fMixed = PolicyUtil::mixed_quality($org_quality);
+            $qualities = $this->calc_phone_quality($qualities, $fMixed, $cur_quality, $times, $used_time, $time_out, $max_times, $mchid);
+            return [$org_quality, $qualities,false];
+        }
+    }
+
+    private function ratio_phone_quality($org_quality,$qualities, $cur_quality, $times, $used_time, $mchid, $card_type, $spec)
+    {
+        [$succ,$max_times,$time_out] = $this->mRatioCtl->total($mchid,$qualities);
+
+        $left_time = $time_out - $used_time;
+        Log::record("left_time = {$left_time} used_time={$used_time}",Log::DEBUG);
+        if ($left_time <= 0 || $max_times <= $times) {
+            return [[],false];
+        }
+
+
+        $match_ratio = $this->mRatioCtl->ratio_match($mchid, $org_quality, $cur_quality, $card_type, $spec, $qualities);
+
+        Log::record("match_ratio = {$match_ratio}",Log::DEBUG);
+        Log::record("ratio_phone_quality begin qualities= " . implode(',',$qualities),Log::DEBUG);
+
+        $times_checker = function ($qualities, $times) use ($match_ratio, $cur_quality, $max_times, $mchid)
+        {
+            $result = [];
+            $pre_times = 0;
+
+            $compare = $cur_quality > 0 ? false : true;
+
+            foreach ($qualities as $quality)
+            {
+                $qtimes = $this->mRatioCtl->times($mchid, $quality);
+
+                if($compare == false)
+                {
+                    if($quality == $cur_quality) {
+                        $compare = true;
+                    }
+                }
+
+                if($qtimes == false) {
+                    continue;
+                }
+                else {
+                    $pre_times += $qtimes;
+                }
+
+                if($compare == false) continue;
+
+                if ($match_ratio) {
+                    $result[] = $quality;
+                }
+                elseif ($times < $pre_times) {
+                    $result[] = $quality;
+                }
+            }
+
+            return $result;
+        };
+
+        $timeout_checker = function ($qualities, $left_time) use($cur_quality,$match_ratio,$mchid)
+        {
+            if($match_ratio)
+            {
+                $result = [];
+                $compare = $cur_quality > 0 ? false : true;
+                foreach ($qualities as $quality)
+                {
+                    if($compare == false)
+                    {
+                        if($quality == $cur_quality) {
+                            $compare = true;
+                        }
+                    }
+                    if($compare == false) continue;
+
+                    $per_secs = $this->mSpeeds[$quality]['per_secs'];
+                    $left_time -= $per_secs;
+                    if($left_time > 0) {
+                        $result[] = $quality;
+                    } else {
+                        break;
+                    }
+                }
+
+                return $result;
+            }
+            else
+            {
+                $result = [];
+
+                $qualities = array_reverse($qualities);
+                foreach ($qualities as $quality)
+                {
+                    $per_secs = $this->mSpeeds[$quality]['per_secs'];
+                    $qsecs = $this->mRatioCtl->seconds($mchid, $quality);
+                    Log::record("left_time={$left_time} quality={$quality} secs={$qsecs}",Log::DEBUG);
+                    if($qsecs == false) continue;
+
+                    if($left_time - $qsecs > 0) {
+                        //时间满足该质量配置
+                        $left_time -= $qsecs;
+                        $result[] = $quality;
+                    }
+                    elseif($left_time - $per_secs > 0) {
+                        //时间够跑一次,不能再找其它质量
+                        $result[] = $quality;
+                        break;
+                    }
+                    else {
+                        //时间完全不够跑一次
+                        break;
+                    }
+
+                    if($quality == $cur_quality) {
+                        break;
+                    }
+                }
+
+                $result = array_reverse($result);
+                return $result;
+            }
+        };
+
+        $pTryAdjuster = $this->mTryAdjuster;
+        $timeing_checker = function ($qualities) use($pTryAdjuster,$mchid)
+        {
+            foreach ($qualities as $quality)
+            {
+                $ret = $pTryAdjuster->can_try($mchid,$quality);
+                if($ret == false) {
+                    return false;
+                }
+            }
+
+            return true;
+        };
+
+        if($times > 0)
+        {
+            $qualities = $times_checker($qualities,$times);
+            Log::record("calc_quality times_checker result = " . implode(',', $qualities), Log::DEBUG);
+            $qualities = $timeout_checker($qualities,$left_time);
+            Log::record("calc_quality timeout_checker result = " . implode(',', $qualities), Log::DEBUG);
+
+            if(!$timeing_checker($qualities)) {
+                $qualities = [];
+                Log::record("calc_quality timeing_checker result is empty", Log::DEBUG);
+            }
+        }
+
+        if ($match_ratio) {
+            $all = $this->qualities($org_quality);
+            $qualities = PolicyUtil::mixed_remove_last($org_quality, $qualities, $all);
+        }
+
+        return [$qualities,$match_ratio];
+
+    }
+
+    private function max_times($mchid,$quality,$qualities)
+    {
+        [$succ, $times,$secs] = $this->mRatioCtl->total($mchid,$qualities);
+        if ($succ) {
+            return $times;
+        } else {
+            return $this->mSpeeds[$quality]['retry_times'];
+        }
+    }
+
+    //通过每种类型通道耗时,倒推当前可用通道,并优先走推荐通道.
+    private function calc_phone_quality($qualities,$fMixed, $cur_quality, $times, $used_time, $time_out, $max_times, $mchid)
+    {
+        $left_time = $time_out - $used_time;
+        if(!$fMixed)
+        {
+            if($left_time <= 0 || $max_times <= $times) {
+                return [];
+            }
+        }
+
+        Log::record("calc_phone_quality begin qualities= " . implode(',',$qualities),Log::DEBUG);
+        $times_checker = function($qualities, $times)
+        {
+            $result = [];
+            $pre_times = 0;
+            foreach ($qualities as $quality)
+            {
+                $pre_times += $this->mSpeeds[$quality]['retry_times'];
+                if($times < $pre_times) {
+                    $result[] = $quality;
+                }
+            }
+
+            return $result;
+        };
+
+        $timeout_checker = function ($qualities, $left_time) use($fMixed,$cur_quality)
+        {
+            $qualities = array_reverse($qualities);
+            if ($fMixed) {
+                $fLasted = $cur_quality != $qualities[0];
+            } else {
+                $fLasted = false;
+            }
+
+            $result = [];
+            foreach ($qualities as $quality)
+            {
+                $per_secs = $this->mSpeeds[$quality]['per_secs'];
+                $left_time -= $per_secs;
+                if($left_time > 0) {
+                    $result[] = $quality;
+                } else {
+                    break;
+                }
+            }
+
+            if(empty($result) && $fLasted) {
+                $result[] = $qualities[0];
+            }
+            $result = array_reverse($result);
+            return $result;
+        };
+
+        $pTryAdjuster = $this->mTryAdjuster;
+        $timeing_checker = function ($qualities) use($pTryAdjuster,$mchid)
+        {
+            foreach ($qualities as $quality)
+            {
+                $ret = $pTryAdjuster->can_try($mchid,$quality);
+                if($ret == false) {
+                    return false;
+                }
+            }
+
+            return true;
+        };
+
+        if($times > 0)
+        {
+            $qualities = $times_checker($qualities,$times);
+            Log::record("calc_quality times_checker result = " . implode(',', $qualities), Log::DEBUG);
+            $qualities = $timeout_checker($qualities,$left_time);
+            Log::record("calc_quality timeout_checker result = " . implode(',', $qualities), Log::DEBUG);
+
+            if(!$timeing_checker($qualities)) {
+                $qualities = [];
+                Log::record("calc_quality timeing_checker result is empty", Log::DEBUG);
+            }
+        }
+
+        return $qualities;
+    }
+}

+ 51 - 44
helper/refill/policy/quaility.php

@@ -80,14 +80,13 @@ class Quality
         return $this->mMchPhonectl->mechants_quality();
     }
 
-    public function find_quality($mchid, $card_type, $spec, $org_quality, $cur_quality, $times, $used_time, $caller): array
+    public function find_quality($mchid, $card_type, $spec, $org_quality, $cur_quality, $used_times, $used_time, $caller): array
     {
-        $trace = new scope_trace(__METHOD__);
         if($card_type == mtopcard\ChinaMobileCard || $card_type == mtopcard\ChinaUnicomCard || $card_type == mtopcard\ChinaTelecomCard) {
-            return $this->mobile_quality($mchid, $card_type, $spec, $org_quality, $cur_quality, $times, $used_time);
+            return $this->mobile_quality($mchid, $card_type, $spec, $org_quality, $cur_quality, $used_times, $used_time);
         }
         elseif($card_type == mtopcard\PetroChinaCard || $card_type == mtopcard\SinopecCard) {
-            [$org,$qualities] = $this->oil_quality($mchid, $org_quality, $times,$used_time,$caller);
+            [$org,$qualities] = $this->oil_quality($mchid, $org_quality, $used_times,$used_time,$caller);
             return [$org,$qualities,false];
         }
         else {
@@ -157,7 +156,7 @@ class Quality
     }
 
 
-    private function mobile_quality($mchid, $card_type, $spec, $org_quality, $cur_quality, $times, $used_time): array
+    private function mobile_quality($mchid, $card_type, $spec, $org_quality, $cur_quality, $used_times, $used_time): array
     {
         if($org_quality == 0)
         {
@@ -189,44 +188,43 @@ class Quality
         }
 
         $qualities = $this->qualities($org_quality);
-        $max_times = $this->max_times($mchid,$org_quality,$qualities);
+        $max_times = $this->max_times($mchid, $org_quality, $qualities);
         if ($this->mRatioCtl->exist($mchid)) {
             Log::record("ratio_phone_quality exist=true",Log::DEBUG);
-            [$qualities,$match_ratio] = $this->ratio_phone_quality($org_quality, $qualities, $cur_quality, $times, $used_time, $mchid, $card_type, $spec);
+            [$qualities,$match_ratio] = $this->ratio_phone_quality($org_quality, $qualities, $cur_quality, $used_times, $used_time, $mchid, $card_type, $spec);
             return [$org_quality, $qualities,$match_ratio];
         } else {
             $fMixed = PolicyUtil::mixed_quality($org_quality);
-            $qualities = $this->calc_phone_quality($qualities, $fMixed, $cur_quality, $times, $used_time, $time_out, $max_times, $mchid);
+            $qualities = $this->calc_phone_quality($qualities, $fMixed, $cur_quality, $used_times, $used_time, $time_out, $max_times, $mchid);
             return [$org_quality, $qualities,false];
         }
     }
 
-    private function ratio_phone_quality($org_quality,$qualities, $cur_quality, $times, $used_time, $mchid, $card_type, $spec)
+    private function ratio_phone_quality($org_quality, $all_qualities, $cur_quality, $used_times, $used_time, $mchid, $card_type, $spec)
     {
-        [$succ,$max_times,$time_out] = $this->mRatioCtl->total($mchid,$qualities);
+        [$succ,$max_times,$time_out] = $this->mRatioCtl->total($mchid,$all_qualities);
 
         $left_time = $time_out - $used_time;
         Log::record("left_time = {$left_time} used_time={$used_time}",Log::DEBUG);
-        if ($left_time <= 0 || $max_times <= $times) {
+        if ($left_time <= 0 || $max_times <= $used_times) {
             return [[],false];
         }
+        
+        if (count($all_qualities) == 1) {
+            return [$all_qualities, true];
+        }
 
-        $match_ratio = $this->mRatioCtl->ratio_match($mchid, $org_quality, $cur_quality, $card_type, $spec, $qualities);
-
-        Log::record("match_ratio = {$match_ratio}",Log::DEBUG);
-        Log::record("ratio_phone_quality begin qualities= " . implode(',',$qualities),Log::DEBUG);
-
-        $times_checker = function ($qualities, $times) use ($match_ratio, $cur_quality, $max_times, $mchid)
+        $times_checker = function ($match_ratio, $qualities, $used_times) use ($cur_quality, $max_times, $mchid)
         {
             $result = [];
             $pre_times = 0;
 
+            //默认不配置的情况下,首次$cur_quality等于0
             $compare = $cur_quality > 0 ? false : true;
 
             foreach ($qualities as $quality)
             {
                 $qtimes = $this->mRatioCtl->times($mchid, $quality);
-
                 if($compare == false)
                 {
                     if($quality == $cur_quality) {
@@ -234,19 +232,16 @@ class Quality
                     }
                 }
 
-                if($qtimes == false) {
+                if ($qtimes == false) {
                     continue;
-                }
-                else {
+                } else {
                     $pre_times += $qtimes;
                 }
 
                 if($compare == false) continue;
-
                 if ($match_ratio) {
                     $result[] = $quality;
-                }
-                elseif ($times < $pre_times) {
+                } elseif ($used_times < $pre_times) {
                     $result[] = $quality;
                 }
             }
@@ -254,7 +249,7 @@ class Quality
             return $result;
         };
 
-        $timeout_checker = function ($qualities, $left_time) use($cur_quality,$match_ratio,$mchid)
+        $timeout_checker = function ($match_ratio, $qualities, $left_time) use ($cur_quality, $mchid)
         {
             if($match_ratio)
             {
@@ -286,29 +281,27 @@ class Quality
                 $result = [];
 
                 $qualities = array_reverse($qualities);
-                foreach ($qualities as $quality)
-                {
+                foreach ($qualities as $quality) {
                     $per_secs = $this->mSpeeds[$quality]['per_secs'];
                     $qsecs = $this->mRatioCtl->seconds($mchid, $quality);
-                    Log::record("left_time={$left_time} quality={$quality} secs={$qsecs}",Log::DEBUG);
-                    if($qsecs == false) continue;
+                    Log::record("left_time={$left_time} quality={$quality} secs={$qsecs}", Log::DEBUG);
+
+                    if ($qsecs == false) continue;
 
-                    if($left_time - $qsecs > 0) {
+                    if ($left_time - $qsecs > 0) {
                         //时间满足该质量配置
                         $left_time -= $qsecs;
                         $result[] = $quality;
-                    }
-                    elseif($left_time - $per_secs > 0) {
+                    } elseif ($left_time - $per_secs > 0) {
                         //时间够跑一次,不能再找其它质量
                         $result[] = $quality;
                         break;
-                    }
-                    else {
+                    } else {
                         //时间完全不够跑一次
                         break;
                     }
 
-                    if($quality == $cur_quality) {
+                    if ($quality == $cur_quality) {
                         break;
                     }
                 }
@@ -332,25 +325,39 @@ class Quality
             return true;
         };
 
-        if($times > 0)
+        if($used_times > 0)
         {
-            $qualities = $times_checker($qualities,$times);
-            Log::record("calc_quality times_checker result = " . implode(',', $qualities), Log::DEBUG);
-            $qualities = $timeout_checker($qualities,$left_time);
-            Log::record("calc_quality timeout_checker result = " . implode(',', $qualities), Log::DEBUG);
+            $times_qualities = $times_checker(true, $all_qualities, $used_times);
+            $time_qualities = $timeout_checker(true, $all_qualities, $left_time);
 
-            if(!$timeing_checker($qualities)) {
-                $qualities = [];
+            $pre_qualities = array_intersect($times_qualities, $time_qualities, $all_qualities);
+            if(count($pre_qualities) < count($all_qualities))
+            {
+                $match_ratio = $this->mRatioCtl->ratio_match($mchid, $org_quality, $card_type, $spec, $all_qualities);
+
+            }
+            else {
+                $match_ratio = true;
+            }
+
+            if(!$timeing_checker($all_qualities)) {
+                $timeing_qualities = [];
                 Log::record("calc_quality timeing_checker result is empty", Log::DEBUG);
+            } else {
+                $timeing_qualities = $all_qualities;
             }
         }
+        else {
+            $match_ratio = true;
+            $cur_qualities = $all_qualities;
+        }
 
         if ($match_ratio) {
             $all = $this->qualities($org_quality);
-            $qualities = PolicyUtil::mixed_remove_last($org_quality, $qualities, $all);
+            $all_qualities = PolicyUtil::mixed_remove_last($org_quality, $cur_qualities, $all);
         }
 
-        return [$qualities,$match_ratio];
+        return [$all_qualities,$match_ratio];
 
     }
 

+ 1 - 1
helper/refill/policy/xyz/policy.php

@@ -30,7 +30,7 @@ class policy extends ProviderManager implements IPolicy
         $this->mStorageLocker = new rstorage();
         $this->mGroupCtl = new rgroup_ctl();
 
-        $this->mRatioCtl = new mratio_control();
+        $this->mRatioCtl = new mratio_controlex();
         $this->mQuality->setRatioCtl($this->mRatioCtl);
         $this->mGlobalInterceptor = new interceptor();
         $this->mMChannels = new mchannel();

+ 5 - 5
plot/mprofit_ratio.py

@@ -1,4 +1,4 @@
-from refill import opt_parse,MProfitRatioCalc
+from refill import opt_parse, MProfitRatioCalc
 import signal as sig
 import logging
 
@@ -6,14 +6,14 @@ logging.basicConfig(filename='/var/www/html/data/log/statcalc.log',
                     format='%(levelname)10s  %(asctime)s  %(name)10s %(thread)d %(message)s',
                     level=logging.DEBUG)
 
-log = logging.getLogger('profit_ratio')
+logger = logging.getLogger('mprofit_ratio')
 
 if __name__ == '__main__':
     try:
-        rhost, rhost = opt_parse()
+        rhost, rport = opt_parse()
         calc = MProfitRatioCalc()
-        calc.set_redis(rhost,rport)
+        calc.set_redis(rhost, rport)
         sig.signal(sig.SIGINT, lambda: calc.stop())
         calc.run()
     except Exception as ex:
-        log.error(ex)
+        logger.error(ex)

+ 5 - 3
plot/refill/CalcBase.py

@@ -2,6 +2,9 @@ import redis
 import time as time
 import logging
 import json
+from .DataStream import span_days
+import logging
+logger = logging.getLogger('CalcBase')
 
 class CalcBase(object):
     def __init__(self):
@@ -34,7 +37,7 @@ class CalcBase(object):
             try:
                 if client is None:
                     client = redis_client()
-                self._calc_handler(rclient)
+                self._calc_handler(client)
             except redis.RedisError as ex:
                 logger.error(ex)
             except Exception as ex:
@@ -43,8 +46,7 @@ class CalcBase(object):
                 time.sleep(1)
                 loop += 1
 
-    def calc_time(self, start_time: int, end_time: int):
-        reader = self.get_reader()
+    def calc_time(self, reader, start_time: int, end_time: int):
         end_time = reader.near_stamp(end_time, False)
         if end_time is None:
             raise Exception('end_time data is empty')

+ 61 - 2
plot/refill/MProfitRatioCalc.py

@@ -1,6 +1,65 @@
-from .MerchantCalcBase import MerchantCalcBase
+from .MerchantCalcBase import MerchantCalcBase, mch_detail_paths, mch_paths
+from .algorithm import calc_mch_profit
+from .DataStream import EMchPosmap as pos_map
+
+import logging
+import time as time
+import logging
+import json
+
+logger = logging.getLogger('mch_profit_ratio')
+
+
+def mixed_ratio(rclient):
+    result = list()
+    data = rclient.get('nc_refill-stat-merchant-mixed')
+    if data is not None:
+        mch_cfgs = json.loads(data)
+        for mchid, val in mch_cfgs.items():
+            mchid = int(mchid)
+            period = int(val['period'])
+            formula = val['profit_formula'].strip()
+            result.append((mchid, period, formula))
+    return result
 
 
 class MProfitRatioCalc(MerchantCalcBase):
     def _calc_handler(self, rclient):
-        pass
+        logger.debug('MProfitRatioCalc _calc_handler')
+        mixed_ratios = mixed_ratio(rclient)
+        reader = self._reader()
+
+        gross = dict()
+        detail = dict()
+
+        for mchid, period, formula in mixed_ratios:
+            end_time = int(time.time() - 86400 * 2)
+            days, start_time, end_time = self.calc_time(reader, end_time - period, end_time)
+            if len(days) == 0:
+                continue
+
+            day_stamp = days[0]
+            tuple_pathes = reader.many_tuple_path(days, mchids={mchid})
+            gen = mch_detail_paths(reader, tuple_pathes, days)
+
+            start = start_time - day_stamp
+            end = end_time - day_stamp
+
+            spec_amount = 0.0
+            for _mchid, _card_type, _spec, _data in gen:
+                submit_count, succ_count, fail_count, succ_ratio, profit = calc_mch_profit(_data, pos_map, start, end)
+                if _card_type is None and _spec is None:
+                    profit_ratio = profit / (spec_amount + 0.000001)
+                    gross[_mchid] = [submit_count, succ_count, fail_count, succ_ratio, profit, round(profit_ratio, 5)]
+                    spec_amount = 0.0
+                else:
+                    key = f"{_mchid}-{_card_type}-{_spec}"
+                    amount = succ_count * _spec
+                    spec_amount += amount
+                    profit_ratio = profit / (amount + 0.000001)
+                    detail[key] = [submit_count, succ_count, fail_count, succ_ratio, profit, round(profit_ratio, 5)]
+
+        result = {'gross': gross, 'detail': detail}
+        if len(gross) != 0 or len(detail) != 0:
+            rclient.set(f"nc_refill_merchant_profit_ratio", json.dumps(result))
+            rclient.publish('refill', json.dumps({'type': 'mch_profit_ratio', 'value': 0}))

+ 1 - 26
plot/refill/MerchantCalc.py

@@ -42,19 +42,6 @@ def earliest_time(rclient: redis.client):
     else:
         return result, min_time
 
-def mixed_ratio(rclient: redis.client):
-    result = list()
-    data = rclient.get('nc_refill-stat-merchant-mixed')
-    if data is not None:
-        mch_cfgs = json.loads(data)
-        for mchid,val in mch_cfgs.items():
-            mchid = int(mchid)
-            period = int(val['period'])
-            formula = val['profit_formula'].strip()
-            result.append((mchid, period, formula))
-    return result
-
-
 class MerchantCalc(object):
     def __init__(self):
         self._mQuit = False
@@ -161,16 +148,4 @@ class MerchantCalc(object):
             result[_mchid] = {'send_amounts': _send_amounts, 'lack_amounts': _lack_amounts}
 
         logger.debug(result)
-        return result
-
-    def _profit_ratio(self, rclient):
-        mixed_ratios = mixed_ratio(rclient)
-
-        for mchid, period, formula in mixed_ratios:
-            end_time = int(time.time())
-            reader, days, start_time, end_time = self.calc_time(end_time - period, end_time)
-            day_stamp = days[0]
-            tuple_pathes = reader.many_tuple_path(days, mchids={mchid})
-            gen = detail_paths(reader, tuple_pathes, days)
-
-        pass
+        return result

+ 84 - 0
plot/refill/MerchantCalcBase.py

@@ -3,6 +3,90 @@ from .DataStream import EMchPosmap as pos_map, span_days
 from .MerchantReader import MerchantReader
 from .algorithm import calc_morder_lack
 
+import logging
+logger = logging.getLogger('MerchantCalcBase')
+
+def detail_paths(reader: MerchantReader, tuple_pathes: dict, days: list):
+    count = len(days)
+    for mchid, tup in tuple_pathes.items():
+        for _card_type, _spec in tup:
+            detail_datas = reader.init_data(count)
+            for i, day in enumerate(days):
+                data = reader.read(day, mchid, _card_type, _spec)
+                if data is not None:
+                    column_pos = i * 86400
+                    view = detail_datas[:, column_pos:column_pos + 86400]
+                    view += data
+            yield mchid, _card_type, _spec, detail_datas
+
+def mch_detail_paths(reader: MerchantReader, tuple_pathes: dict, days: list):
+    count = len(days)
+    for mchid, tup in tuple_pathes.items():
+        mch_datas = reader.init_data(count)
+        for _card_type, _spec in tup:
+            detail_datas = reader.init_data(count)
+            for i, day in enumerate(days):
+                data = reader.read(day, mchid, _card_type, _spec)
+                if data is not None:
+                    column_pos = i * 86400
+                    view = detail_datas[:, column_pos:column_pos + 86400]
+                    view += data
+                    view = mch_datas[:, column_pos:column_pos + 86400]
+                    view += data
+            yield mchid, _card_type, _spec, detail_datas
+        yield mchid, None, None, mch_datas
+
+def mch_paths(reader: MerchantReader, tuple_pathes: dict, days: list):
+    count = len(days)
+    for mchid, tup in tuple_pathes.items():
+        mch_datas = reader.init_data(count)
+        for _card_type, _spec in tup:
+            for i, day in enumerate(days):
+                data = reader.read(day, mchid, _card_type, _spec)
+                if data is not None:
+                    column_pos = i * 86400
+                    view = mch_datas[:, column_pos:column_pos + 86400]
+                    view += data
+        yield mchid, None, None, mch_datas
+
+def allpathes(reader: MerchantReader, tuple_pathes: dict, days: list, spec=None):
+    count = len(days)
+    show_detail = True if len(list(tuple_pathes.keys())) == 1 else False
+    if show_detail == False:
+        all_datas = reader.init_data(count)
+    else:
+        all_datas = None
+
+    for mchid, tup in tuple_pathes.items():
+        add_mchid(mchid)
+        mch_datas = reader.init_data(count)
+        for _card_type, _spec in tup:
+            if spec is not None and _spec != spec:
+                continue
+
+            if show_detail:
+                detail_datas = reader.init_data(count)
+            else:
+                detail_datas = None
+
+            for i, day in enumerate(days):
+                data = reader.read(day, mchid, _card_type, _spec)
+                if data is not None:
+                    column_pos = i * 86400
+                    view = mch_datas[:, column_pos:column_pos + 86400]
+                    view += data
+
+                    if show_detail:
+                        view = detail_datas[:, column_pos:column_pos + 86400]
+                        view += data
+            if show_detail:
+                yield mchid, _card_type, _spec, detail_datas
+        if all_datas is not None:
+            all_datas += mch_datas
+        yield mchid, None, None, mch_datas
+
+    if show_detail == False:
+        yield 'all', None, None, all_datas
 
 class MerchantCalcBase(CalcBase):
     def _reader(self):

+ 22 - 4
plot/refill/algorithm.py

@@ -1,11 +1,11 @@
-
 from .DataStream import EMchPosmap
 import numpy as np
 import logging
 
 logger = logging.getLogger('calcer')
 
-def calc_chratios(data,pos_map,start,end):
+
+def calc_chratios(data, pos_map, start, end):
     view = data[[pos_map.succ_count, pos_map.fail_count, pos_map.commit_count], :]
     view = view[:, start:end]
 
@@ -18,7 +18,7 @@ def calc_chratios(data,pos_map,start,end):
     return int(all[0, -1]), int(all[0, -1] + all[1, -1]), y
 
 
-def calc_mchratios(data,pos_map,start,end):
+def calc_mchratios(data, pos_map, start, end):
     view = data[[pos_map.succ_count, pos_map.fail_count, pos_map.submit_count], :]
     view = view[:, start:end]
 
@@ -35,7 +35,7 @@ def calc_morder_send(data, pos_map: type(EMchPosmap), start: int, end: int):
     view = data[:, start:end]
     sums = np.sum(view, axis=1)
 
-    all_return =  sums[pos_map.succ_mch_amounts] + sums[pos_map.fail_mch_amounts] + 0.0000001
+    all_return = sums[pos_map.succ_mch_amounts] + sums[pos_map.fail_mch_amounts] + 0.0000001
     ratio = sums[pos_map.succ_mch_amounts] / all_return
 
     send_count = sums[pos_map.submit_count] - sums[pos_map.succ_count] - sums[pos_map.fail_count]
@@ -59,3 +59,21 @@ def calc_morder_lack(data, pos_map: type(EMchPosmap), start: int, end: int):
     logger.info("send_count=%d send_amounts=%.4f ratio=%.4f lack_amounts=%.4f", send_count, send_amounts, ratio, lack_amounts)
 
     return send_amounts, lack_amounts
+
+
+# 用于计算成功率及利润率
+# succ_count, fail_count, succ_ratio, profit,profit_ratio
+def calc_mch_profit(data, pos_map: type(EMchPosmap), start: int, end: int):
+    view = data[:, start:end]
+    sums = np.sum(view, axis=1)
+
+    submit_count = sums[pos_map.submit_count]
+    succ_count = sums[pos_map.succ_count]
+    fail_count = sums[pos_map.fail_count]
+    succ_ratio = succ_count / (succ_count + fail_count + 0.0000001)
+
+    ch_amounts = sums[pos_map.succ_ch_amounts]
+    mch_amounts = sums[pos_map.succ_mch_amounts]
+    profit = mch_amounts - ch_amounts
+
+    return int(submit_count), int(succ_count), int(fail_count), round(succ_ratio, 5), round(profit, 3)

+ 2 - 2
rdispatcher/codispatcher.php

@@ -93,9 +93,9 @@ function subscribe_message(&$quit, &$redis, $channels)
 
                         refill\RefillFactory::instance()->UpdateRatio($ratios);
                     }
-                    elseif($type == 'mch_counts') {
+                    elseif($type == 'mch_profit_ratio') {
                         $ins = Cache::getInstance('cacheredis');
-                        $content = $ins->get_org('merchant_refill_counts');
+                        $content = $ins->get_org('refill_merchant_profit_ratio');
 
                         if(empty($content)) continue;
                         $counts = json_decode($content,true);

+ 14 - 0
test/TestBigData.php

@@ -91,8 +91,22 @@ class TestBigData extends TestCase
         $detail = $counts['detail'];
 
         refill\RefillFactory::instance()->UpdateMchRatios($gross,$detail);
+    }
+
+    public function testMchProfitRatio()
+    {
+        $ins = Cache::getInstance('cacheredis');
+        $content = $ins->get_org('refill_merchant_profit_ratio');
 
+        $data = json_decode($content,true);
+
+        $gross = $data['gross'];
+        $detail = $data['detail'];
+
+        refill\RefillFactory::instance()->UpdateMchRatios($gross,$detail);
     }
+    
+
 
     public function testXml()
     {

+ 23 - 5
test/TestRefillPhone.php

@@ -59,7 +59,6 @@ class TestRefillPhone extends TestCase
 
         $url = $this->requrl();
         $order_sn = $this->make_sn();
-        $order_sn = '      smsasdadad23';
 
         $params = ['mchid' => $this->mMchid,
             'cardno' => $phone,
@@ -70,10 +69,29 @@ class TestRefillPhone extends TestCase
             'notifyurl' => "http://www.baidu.com"];
 
         $proxy = new refill_proxy($this->mKey);
-        for ($i = 0; $i < 10; $i++) {
-            $resp = $proxy->send($url, $params);
-            Log::record("resp={$resp}", Log::DEBUG);
-        }
+        $resp = $proxy->send($url, $params);
+        Log::record("resp={$resp}", Log::DEBUG);
+    }
+
+    public function testAddByYaoyou()
+    {
+        $phone = '13911129867';
+        $amount = 100;
+
+        $url = $this->requrl();
+        $order_sn = $this->make_sn();
+
+        $params = ['mchid' => 10202,
+            'cardno' => $phone,
+            'amount' => $amount,
+            "act" => "refill",
+            "op" => "add",
+            'order_sn' => $order_sn,
+            'notifyurl' => "http://www.baidu.com"];
+
+        $proxy = new refill_proxy('KxK9f3yaBDOXVwvuOKDozvQ9Mt1CoqRX');
+        $resp = $proxy->send($url, $params);
+        Log::record("resp={$resp}", Log::DEBUG);
     }
 
     public function testCardQuery()