Procházet zdrojové kódy

Merge branch 'raccount' into rchannel

xiaoyu před 2 roky
rodič
revize
91d3b8a6cd
81 změnil soubory, kde provedl 3417 přidání a 374 odebrání
  1. 93 2
      admin/control/ordersendlist.php
  2. 39 2
      admin/control/provider.php
  3. 190 173
      admin/control/refill_successful.php
  4. 1 3
      admin/control/refill_detail.php
  5. 12 0
      admin/control/refill_order.php
  6. 1 1
      admin/include/limit.php
  7. 1 1
      admin/include/menu.php
  8. 183 0
      admin/templates/default/analysis.new_version.php
  9. 4 3
      admin/templates/default/successful.provider.php
  10. 4 3
      admin/templates/default/successful.system.php
  11. 22 2
      admin/templates/default/css/skin_0.css
  12. 4 14
      admin/templates/default/merchant.ratios.php
  13. 10 0
      admin/templates/default/provider.index.php
  14. 3 3
      admin/templates/default/refill.buyback.order.php
  15. 303 0
      admin/templates/default/refill.detail.monitor.php
  16. 22 2
      admin/templates/default/refill.order.batch.php
  17. 313 0
      admin/templates/default/refill.order.exception.php
  18. 1 1
      admin/templates/default/refill.order.manual.index.php
  19. 8 0
      admin/templates/default/refill.order.monitor.notify.php
  20. 2 0
      admin/templates/default/refill.order.neterr.index.php
  21. 2 0
      admin/templates/default/refill.order.notify.err.index.php
  22. 8 0
      admin/templates/default/refill.order.send.index.php
  23. 1 1
      admin/templates/default/set.cancel.order.php
  24. 1 1
      admin/templates/default/transfer.order.php
  25. 1 1
      admin/templates/default/transfer.order.recharge.php
  26. 92 0
      data/config/xyz/refill.ini.php
  27. 100 0
      data/config/yl/refill.ini.php
  28. 42 0
      data/logic/queue.logic.php
  29. 35 0
      data/model/refill_exception.model.php
  30. 2 0
      docker/compose/homecuda/cli/docker-compose.yml
  31. 2 2
      docker/compose/workcuda/acc/docker-compose.yml
  32. 3 1
      docker/compose/workcuda/cli/docker-compose.yml
  33. 1 1
      docker/compose/workcuda/conf/nginx/nginx.conf
  34. 12 0
      docker/compose/workcuda/statsec/docker-compose.yml
  35. 10 10
      docker/compose/xiaoyu/admin/docker-compose.yml
  36. 1 1
      helper/rbridge/fulu/Bridge.php
  37. 63 53
      helper/refill/RefillBase.php
  38. 50 0
      helper/refill/api/xyz/dezhi/RefillCallBack.php
  39. 141 0
      helper/refill/api/xyz/dezhi/RefillPhone.php
  40. 12 0
      helper/refill/api/xyz/dezhi/api.txt
  41. 27 0
      helper/refill/api/xyz/dezhi/config.php
  42. binární
      helper/refill/api/xyz/guochuang/20220624辽宁广东移动调价函.png
  43. binární
      helper/refill/api/xyz/guochuang/20220625黑龙江移动调价函.jpg
  44. binární
      helper/refill/api/xyz/guochuang/20220627宁夏移动调价函.png
  45. binární
      helper/refill/api/xyz/guochuang/20220628黑龙江移动调价函.png
  46. 4 4
      helper/refill/api/xyz/guochuang/config.php
  47. 48 0
      helper/refill/api/xyz/xinruiheng/RefillCallBack.php
  48. 155 0
      helper/refill/api/xyz/xinruiheng/RefillPhone.php
  49. 50 0
      helper/refill/api/xyz/xinruiheng/config.php
  50. 31 0
      helper/refill/api/xyz/xinruiheng/对接文档-yezi.txt
  51. 50 0
      helper/refill/api/yl/dezhi/RefillCallBack.php
  52. 141 0
      helper/refill/api/yl/dezhi/RefillPhone.php
  53. 12 0
      helper/refill/api/yl/dezhi/api.txt
  54. 27 0
      helper/refill/api/yl/dezhi/config.php
  55. 4 4
      helper/refill/api/yl/guochuang/config.php
  56. 48 0
      helper/refill/api/yl/xinruiheng/RefillCallBack.php
  57. 155 0
      helper/refill/api/yl/xinruiheng/RefillPhone.php
  58. 50 0
      helper/refill/api/yl/xinruiheng/config.php
  59. 31 0
      helper/refill/api/yl/xinruiheng/对接文档-yezi.txt
  60. 35 0
      helper/refill/api/yl/xunyin_kami/RefillCallBack.php
  61. 32 0
      helper/refill/api/yl/xunyin_kami/RefillOil.php
  62. 9 0
      helper/refill/api/yl/xunyin_kami/config.php
  63. binární
      helper/refill/api/yl/xunyin_kami/迅银开放平台采购接口-采购商.docx
  64. 4 0
      mobile/callback/refill_dezhi.php
  65. 4 0
      mobile/callback/refill_xinruiheng.php
  66. 56 17
      plot/app.py
  67. 25 0
      plot/flask_test.py
  68. 70 0
      plot/plot_control.py
  69. 148 0
      plot/refill/ChannelPainter.py
  70. 76 7
      plot/refill/ChannelReader.py
  71. 103 37
      plot/refill/DataStream.py
  72. 0 1
      plot/refill/MerchantCalc.py
  73. 10 5
      plot/refill/__init__.py
  74. 14 0
      plot/refill/algorithm.py
  75. 41 0
      plot/refill/helper.py
  76. 12 0
      plot/testNumpy.py
  77. 79 12
      plot/testPlot.py
  78. 16 0
      plot/test_h5py.py
  79. 15 6
      rdispatcher/proxy.php
  80. 28 0
      test/TestRefill.php
  81. 12 0
      test/TestRefillYl.php

+ 93 - 2
admin/control/ordersendlist.php

@@ -18,7 +18,7 @@ class ordersendlistControl extends SystemControl
         {
             $mod = Model();
             $item = $mod->table('refill_detail')->field('min(order_time) as mintime')->where(['order_state' => 30])->find();
-            if(empty($item)) {
+            if(empty($item['mintime'])) {
                 return time() - 3600;
             }
             else {
@@ -166,6 +166,7 @@ class ordersendlistControl extends SystemControl
         Tpl::output('order_list', $order_list);
         Tpl::output('merchant_list', $merchant_list);
         Tpl::output('provider_list', $provider_list);
+        Tpl::output('except_stat', $this->except_stat());
         Tpl::output('show_page', $model_refill_order->showpage());
         Tpl::showpage('refill.order.send.index');
     }
@@ -246,7 +247,7 @@ class ordersendlistControl extends SystemControl
         $mintime_getter = function () {
             $mod = Model();
             $item = $mod->table('refill_detail')->field('min(order_time) as mintime')->where(['order_state'=>30])->find();
-            if(empty($item)) {
+            if(empty($item['mintime'])) {
                 return time() - 3600;
             }
             else {
@@ -355,6 +356,7 @@ class ordersendlistControl extends SystemControl
         Tpl::output('order_list', $order_list);
         Tpl::output('merchant_list', $merchant_list);
         Tpl::output('provider_list', $provider_list);
+        Tpl::output('except_stat', $this->except_stat());
         Tpl::output('show_page', $model_refill_order->showpage());
         Tpl::showpage('refill.order.monitor.notify');
     }
@@ -722,4 +724,93 @@ class ordersendlistControl extends SystemControl
         }
         return $orders;
     }
+
+    public function order_exceptionOp()
+    {
+        $mod_except = Model('refill_exception');
+        $_GET['except_state'] = $_GET['except_state'] ?? 0;
+
+        if (!empty($_GET['order_sns'])) {
+            $order_sns = rtrim($_GET['order_sns'],',');
+            $cond['order_sn'] = ['in', $order_sns];
+        }
+        if(in_array($_GET['except_state'], ['0', '1'])) {
+            $cond['except_state'] = $_GET['except_state'];
+        }
+        $list = $mod_except->getExceptionList($cond);
+
+        $merchant_list = $this->merchants();
+        foreach ($merchant_list as  $value) {
+            $merchants[$value['mchid']] = $value;
+        }
+        Tpl::output('merchant_list', $merchants);
+        Tpl::output('list', $list);
+        Tpl::output('except_stat', $this->except_stat());
+        Tpl::output('show_page', $mod_except->showpage());
+        Tpl::showpage('refill.order.exception');
+    }
+
+    public function order_exception_handledOp()
+    {
+        $except_ids = $_GET['except_ids'];
+        $except_ids = explode(',', $except_ids);
+        $cond['except_id'] = ['in',$except_ids];
+        $cond['except_state'] = 0;
+
+        $mod_except = Model('refill_exception');
+
+        $excepts = $mod_except->getExceptionList($cond);
+        if(empty($excepts)) {
+            showMessage('操作完成');
+        }
+
+        $admininfo = $this->getAdminInfo();
+        $update['admin_id'] = $admininfo['id'];
+        $update['admin_name'] = $admininfo['name'];
+        $update['oper_time'] = time();
+        foreach ($excepts as $except) {
+            $exc_id = $except['except_id'];
+            $mod_except->setHandled($exc_id, $update);
+        }
+        showMessage('操作完成');
+    }
+
+    private function except_stat()
+    {
+        $mod_except = Model('refill_exception');
+        $cond['except_state'] = 0;
+        $except_stat = $mod_except->where($cond)->count();
+        return intval($except_stat);
+    }
+
+    public function refill_detail_monitorOp()
+    {
+        $mod_detail = Model('refill_detail');
+        $cond['order_state'] = ['neq', ORDER_STATE_HANDLED];
+
+        $merchant_list = $this->merchants();
+        foreach ($merchant_list as  $value) {
+            $merchants[$value['mchid']] = $value;
+        }
+
+        $list = $mod_detail->getRefillDetailList($cond, 30, 30, '*', 'order_time asc');
+        foreach ($list as $key => $value) {
+            $params = json_decode($value['params'],true);
+            $list[$key]['company_name'] = $merchants[$value['mchid']]['company_name'];
+            $list[$key]['params'] = $params;
+            if($value['order_state'] == ORDER_STATE_SEND) {
+                $list[$key]['order_state_text'] = '待收货';
+            } elseif ($value['order_state'] == ORDER_STATE_QUEUE) {
+                $list[$key]['order_state_text'] = '队列中';
+            } elseif ($value['order_state'] == ORDER_STATE_HANDLED) {
+                $list[$key]['order_state_text'] = '已处理';
+            } else {
+                $list[$key]['order_state_text'] = '/';
+            }
+        }
+
+        Tpl::output('list', $list);
+        Tpl::output('show_page', $mod_detail->showpage());
+        Tpl::showpage('refill.detail.monitor');
+    }
 }

+ 39 - 2
admin/control/provider.php

@@ -26,6 +26,9 @@ class providerControl extends SystemControl
         if(!empty($_GET['quality'])) {
             $condition['qualitys'] = ['like', '%' . $_GET['quality'] . '%'];
         }
+        if(!empty($_GET['export'])) {
+            $condition['opened'] = 1;
+        }
 
         $provider_items = $provider_model->table('refill_provider,store')
             ->field('refill_provider.*,store.store_name,store.member_id')
@@ -65,14 +68,19 @@ class providerControl extends SystemControl
                     $pid = $mid_pids[$mid];
                     $providers[$pid]['available_predeposit'] = $member['available_predeposit'];
                     if($providers[$pid]['opened'] != 1) continue;
-                    if($member['available_predeposit'] >= 0) {
+                    if ($member['available_predeposit'] >= 0) {
                         $available_total += $member['available_predeposit'];
-                    }else{
+                    } else {
                         $debt_total += $member['available_predeposit'];
                     }
                 }
             }
         }
+        if(!empty($_GET['export']))
+        {
+            $this->provider_export($providers);
+            return;
+        }
         $stats = ['available_total' => $available_total, 'debt_total' => $debt_total];
         $opened_text = ['使用中', '已禁用'];
         $type_text = ['油卡', '手机充值卡', '增值业务'];
@@ -583,4 +591,33 @@ class providerControl extends SystemControl
             Tpl::showpage('provider.amount.edit');
         }
     }
+
+    public function provider_export($providers)
+    {
+        Language::read('export');
+        import('libraries.excel');
+        $excel_obj = new Excel();
+        $excel_data = [];
+        //设置样式
+        $excel_obj->setStyle(['id' => 's_title', 'Font' => ['FontName' => '宋体', 'Size' => '12', 'Bold' => '1']]);
+        //header
+        $excel_data[0][] = ['styleid' => 's_title', 'data' => '通道ID'];
+        $excel_data[0][] = ['styleid' => 's_title', 'data' => '通道名称'];
+        $excel_data[0][] = ['styleid' => 's_title', 'data' => '订单成功后余额'];
+        $excel_data[0][] = ['styleid' => 's_title', 'data' => '接口查询余额'];
+        //data
+        foreach ($providers as $provider) {
+            $tmp = [];
+            $store_name = "{$provider['name']}({$provider['store_name']})";
+            $tmp[] = ['data' => $provider['provider_id']];
+            $tmp[] = ['data' => $store_name];
+            $tmp[] = ['data' => $provider['available_predeposit']];
+            $tmp[] = ['data' => $provider['balance']];
+            $excel_data[] = $tmp;
+        }
+        $excel_data = $excel_obj->charset($excel_data, CHARSET);
+        $excel_obj->addArray($excel_data);
+        $excel_obj->addWorksheet($excel_obj->charset(L('exp_od_order'), CHARSET));
+        $excel_obj->generateXML('通道余额明细导出-' . date('Y-m-d-H', time()));
+    }
 }

+ 190 - 173
admin/control/refill_successful.php

@@ -1,173 +1,190 @@
-<?php
-include(BASE_CONFIG_PATH . CONFIG_PREFIX . '/refill.ini.php');
-require_once(BASE_ROOT_PATH . '/core/framework/function/http.php');
-class refill_successfulControl extends SystemControl
-{
-    private $SUCCESSFUL_URL = BASE_SITE_URL;
-    
-    public function __construct()
-    {
-        parent::__construct();
-    }
-
-    public function indexOp()
-    {
-        $type = $_GET['type'] ?? 'provider';
-        $page = "successful.{$type}";
-        $days = $this->successful_where($type);
-        Tpl::output('days', $days);
-        Tpl::showpage($page);
-    }
-
-    private function successful_where($type) {
-        if ($type == 'system') {
-            $url = $this->SUCCESSFUL_URL . '/plot/mchdays';
-        } elseif($type == 'provider') {
-            $url = $this->SUCCESSFUL_URL . '/plot/days';
-        }
-
-        Log::record("successful get timestamp url : {$url}", Log::DEBUG);
-        $data = http_request($url);
-        if(empty($data)) return [];
-        preg_match_all('/\d{10}/', $data, $matches);
-        $days = $matches[0];
-        if(empty($days)) return [];
-        $result = [];
-        foreach ($days as $day) {
-            $result[$day] = date("Y-m-d",$day);
-        }
-        return $result;
-    }
-
-    public function successful_time_get_whereOp(){
-        $type = $_GET['type'] ?? 'provider';
-        if ($type == 'system') {
-            $url = $this->SUCCESSFUL_URL . '/plot/mchpaths?time_stamp='.$_GET['timestamp'];
-            $pattern = '/\d{10}\/\d+\/\d{1,2}\/\d\/\d{2,4}/';
-        } elseif($type == 'provider') {
-            $pattern = '/\d{10}\/[a-z]+_*[a-z]*\/\d{1,2}\/\d\/\d{2,4}/';
-            $url = $this->SUCCESSFUL_URL . '/plot/paths?time_stamp='.$_GET['timestamp'];
-        }
-        Log::record("successful get where url : {$url}", Log::DEBUG);
-        $data = http_request($url);
-        if(empty($data) || empty($pattern)) {
-            echo(json_encode(''));
-            return;
-        }
-
-        preg_match_all($pattern, $data, $matches);
-        $conds = $matches[0];
-        if(empty($conds)) {
-            echo(json_encode(''));
-            return;
-        }
-        foreach ($conds as $cond) {
-            $arr = explode('/',$cond);
-            $chname[] = $arr[1];
-            $qualitys[] = $arr[2];
-            $card_types[] = $arr[3];
-            $amount[] = intval($arr[4]);
-        }
-        $chnameData = array_unique($chname);
-        sort($chnameData);
-
-        if ($type == 'system') {
-            $merchants = [];
-            $merchant_list = Model('')->table('merchant')->limit(1000)->field('mchid,name,company_name,time_out')->select();
-            foreach ($merchant_list as $merchant) {
-                $merchants[$merchant['mchid']] = $merchant;
-            }
-            foreach ($chnameData as $mchid) {
-                $merchant_name = $merchants[$mchid]['company_name'] == '' ? $merchants[$mchid]['name'] : $merchants[$mchid]['company_name'];
-                $mchname[$mchid] = "{$mchid}-{$merchant_name}-{$merchants[$mchid]['time_out']}";
-            }
-            asort($mchname);
-            $result['chname'] = $mchname;
-        } elseif($type == 'provider') {
-            $providers = [];
-            $provider_list = $this->providers();
-            foreach ($provider_list as $provider) {
-                $providers[$provider['name']] = $provider;
-            }
-            foreach ($chnameData as $provider_name) {
-                $store_names[$provider_name] = $providers[$provider_name]['store_name'];
-            }
-            $result['chname'] = $store_names;
-        }
-        $amountData = array_unique($amount);
-        sort($amountData);
-        $result['amount'] = $amountData;
-
-        foreach (array_unique($card_types) as $card_type) {
-            $card_type_text = $this->scard_type($card_type);
-            if($card_type_text != 'unknown') {
-                $card_typeData[$card_type] = $card_type_text;
-            }
-        }
-        ksort($card_typeData);
-        $result['card_type'] = $card_typeData;
-
-        $quality_txt = [refill\Quality::Normal=>'普充', refill\Quality::Quick=>'快充', refill\Quality::CardKey=>'卡密',
-            refill\Quality::ThirdShop=>'三方', refill\Quality::SlowTwentyFour=>'慢24', refill\Quality::SlowSix=>'慢6', refill\Quality::SlowTwo=>'慢2'];
-        foreach (array_unique($qualitys) as $quality) {
-            if(array_key_exists($quality, $quality_txt)) {
-                $qualityData[$quality] = $quality_txt[$quality];
-            }
-        }
-        ksort($qualityData);
-        $result['quality'] = $qualityData;
-        echo(json_encode($result));
-    }
-
-    public function merchant_ratiosOp()
-    {
-        //0:成功单,1:失败单,2:成功率
-        $ins = Cache::getInstance('cacheredis');
-        $json = $ins->get_org('merchant_card_type_ratios');
-        $data = json_decode($json);
-        $data = $this->object_array($data);
-        $result = [];
-        $stats = [];
-        $card_types = ['YD', 'LT', 'DX'];
-        if(!empty($data)) {
-            $merchant_list = $this->merchants();
-
-            foreach ($merchant_list as $value) {
-                $mchid = $value['mchid'];
-                if(array_key_exists($mchid, $data) && is_array($data[$mchid])) {
-                    $result[$mchid]['ratio'] = $data[$mchid];
-                    $result[$mchid]['company_name'] = $value['company_name'];
-                    $result[$mchid]['time_out'] = $this->elapse_time($value['time_out']);
-
-                    foreach ($card_types as $type) {
-                        if($data[$mchid][$type][3600][0] + $data[$mchid][$type][3600][1] > 0) {
-                            $stats[$type]['success'] += $data[$mchid][$type][3600][0];
-                            $stats[$type]['ratio'] += $data[$mchid][$type][3600][2];
-                            $stats[$type]['count'] += 1;
-                        }
-                    }
-                }
-            }
-            Tpl::output('stats', $stats);
-            Tpl::output('count', count($result));
-        }
-        Tpl::output('list', $result);
-        Tpl::showpage('merchant.ratios');
-    }
-
-    private function object_array($array)
-    {
-        if(is_object($array))
-        {
-            $array = (array)$array;
-        }
-        if(is_array($array))
-        {
-            foreach($array as $key=>$value)
-            {
-                $array[$key] = $this->object_array($value);
-            }
-        }
-        return $array;
-    }
-}
+<?php
+include(BASE_CONFIG_PATH . CONFIG_PREFIX . '/refill.ini.php');
+require_once(BASE_ROOT_PATH . '/core/framework/function/http.php');
+class refill_analysisControl extends SystemControl
+{
+    private $ANALYSIS_URL = BASE_SITE_URL;
+    
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    public function indexOp()
+    {
+        $type = $_GET['type'] ?? 'provider';
+        $page = "analysis.{$type}";
+        $days = $this->analysis_where($type);
+        Tpl::output('days', $days);
+        Tpl::showpage($page);
+    }
+
+    private function analysis_where($type) {
+        if ($type == 'system') {
+            $url = $this->ANALYSIS_URL . '/plot/mchdays';
+        } elseif($type == 'provider') {
+            $url = $this->ANALYSIS_URL . '/plot/days';
+        }
+
+        Log::record("analysis get timestamp url : {$url}", Log::DEBUG);
+        $data = http_request($url);
+        if(empty($data)) return [];
+        preg_match_all('/\d{10}/', $data, $matches);
+        $days = $matches[0];
+        if(empty($days)) return [];
+        $result = [];
+        foreach ($days as $day) {
+            $result[$day] = date("Y-m-d",$day);
+        }
+        return $result;
+    }
+
+    public function analysis_time_get_whereOp(){
+        $type = $_GET['type'] ?? 'provider';
+        if ($type == 'system') {
+            $url = $this->ANALYSIS_URL . '/plot/mchpaths?time_stamp='.$_GET['timestamp'];
+            $pattern = '/\d{10}\/\d+\/\d{1,2}\/\d\/\d{2,4}/';
+        } elseif($type == 'provider') {
+            $pattern = '/\d{10}\/[a-z]+_*[a-z]*\/\d{1,2}\/\d\/\d{2,4}/';
+            $url = $this->ANALYSIS_URL . '/plot/paths?time_stamp='.$_GET['timestamp'];
+        }
+        Log::record("analysis get where url : {$url}", Log::DEBUG);
+        $data = http_request($url);
+        if(empty($data) || empty($pattern)) {
+            echo(json_encode(''));
+            return;
+        }
+
+        preg_match_all($pattern, $data, $matches);
+        $conds = $matches[0];
+        if(empty($conds)) {
+            echo(json_encode(''));
+            return;
+        }
+        foreach ($conds as $cond) {
+            $arr = explode('/',$cond);
+            $chname[] = $arr[1];
+            $qualitys[] = $arr[2];
+            $card_types[] = $arr[3];
+            $amount[] = intval($arr[4]);
+        }
+        $chnameData = array_unique($chname);
+        sort($chnameData);
+
+        if ($type == 'system') {
+            $merchants = [];
+            $merchant_list = Model('')->table('merchant')->limit(1000)->field('mchid,name,company_name,time_out')->select();
+            foreach ($merchant_list as $merchant) {
+                $merchants[$merchant['mchid']] = $merchant;
+            }
+            foreach ($chnameData as $mchid) {
+                $merchant_name = $merchants[$mchid]['company_name'] == '' ? $merchants[$mchid]['name'] : $merchants[$mchid]['company_name'];
+                $mchname[$mchid] = "{$mchid}-{$merchant_name}-{$merchants[$mchid]['time_out']}";
+            }
+            asort($mchname);
+            $result['chname'] = $mchname;
+        } elseif($type == 'provider') {
+            $providers = [];
+            $provider_list = $this->providers();
+            foreach ($provider_list as $provider) {
+                $providers[$provider['name']] = $provider;
+            }
+            foreach ($chnameData as $provider_name) {
+                $store_names[$provider_name] = $providers[$provider_name]['store_name'];
+            }
+            $result['chname'] = $store_names;
+        }
+        $amountData = array_unique($amount);
+        sort($amountData);
+        $result['amount'] = $amountData;
+
+        foreach (array_unique($card_types) as $card_type) {
+            $card_type_text = $this->scard_type($card_type);
+            if($card_type_text != 'unknown') {
+                $card_typeData[$card_type] = $card_type_text;
+            }
+        }
+        ksort($card_typeData);
+        $result['card_type'] = $card_typeData;
+
+        $quality_txt = [refill\Quality::Normal=>'普充', refill\Quality::Quick=>'快充', refill\Quality::CardKey=>'卡密',
+            refill\Quality::ThirdShop=>'三方', refill\Quality::SlowTwentyFour=>'慢24', refill\Quality::SlowSix=>'慢6', refill\Quality::SlowTwo=>'慢2'];
+        foreach (array_unique($qualitys) as $quality) {
+            if(array_key_exists($quality, $quality_txt)) {
+                $qualityData[$quality] = $quality_txt[$quality];
+            }
+        }
+        ksort($qualityData);
+        $result['quality'] = $qualityData;
+        echo(json_encode($result));
+    }
+
+    public function merchant_ratiosOp()
+    {
+        //0:成功单,1:失败单,2:成功率
+        $ins = Cache::getInstance('cacheredis');
+        $json = $ins->get_org('merchant_card_type_ratios');
+        $data = json_decode($json);
+        $data = $this->object_array($data);
+        $result = [];
+        $stats = [];
+        $card_types = ['YD', 'LT', 'DX'];
+        if(!empty($data)) {
+            $merchant_list = $this->merchants();
+
+            foreach ($merchant_list as $value) {
+                $mchid = $value['mchid'];
+                if(array_key_exists($mchid, $data) && is_array($data[$mchid])) {
+                    $result[$mchid]['ratio'] = $data[$mchid];
+                    $result[$mchid]['company_name'] = $value['company_name'];
+                    $result[$mchid]['time_out'] = $this->elapse_time($value['time_out']);
+
+                    foreach ($card_types as $type) {
+                        if($data[$mchid][$type][3600][0] + $data[$mchid][$type][3600][1] > 0) {
+                            $stats[$type]['success'] += $data[$mchid][$type][3600][0];
+                            $stats[$type]['ratio'] += $data[$mchid][$type][3600][2];
+                            $stats[$type]['count'] += 1;
+                        }
+                    }
+                }
+            }
+            Tpl::output('stats', $stats);
+            Tpl::output('count', count($result));
+        }
+        Tpl::output('list', $result);
+        Tpl::showpage('merchant.ratios');
+    }
+
+    public function new_versionOp()
+    {
+        Tpl::showpage('analysis.new_version');
+    }
+
+    public function provider_dataOp()
+    {
+        $provider_list = $this->providers();
+        $result = [];
+        foreach ($provider_list as $value) {
+            $data['name'] = $value['store_name'] ?? $value['name'];
+            $data['value'] = $value['name'];
+            $result[] = $data;
+        }
+        echo json_encode($result);
+    }
+
+    private function object_array($array)
+    {
+        if(is_object($array))
+        {
+            $array = (array)$array;
+        }
+        if(is_array($array))
+        {
+            foreach($array as $key=>$value)
+            {
+                $array[$key] = $this->object_array($value);
+            }
+        }
+        return $array;
+    }
+}

+ 1 - 3
admin/control/refill_detail.php

@@ -164,7 +164,7 @@ class refill_detailControl extends SystemControl
                     //零元订单失败
                     Log::record("cancel",Log::DEBUG);
                     if ($detail['order_state'] == ORDER_STATE_SEND) {
-                        util::pop_queue_order($mchid, $mch_order);
+                        util::push_add_zero($params);
                     } elseif ($detail['order_state'] == ORDER_STATE_QUEUE) {
                         util::push_add_zero($params);
                     }
@@ -173,8 +173,6 @@ class refill_detailControl extends SystemControl
                     //重新补充
                     Log::record("anew",Log::DEBUG);
                     util::push_add($params);
-                } else {
-                    continue;
                 }
             }
             else {

+ 12 - 0
admin/control/refill_order.php

@@ -350,6 +350,18 @@ class refill_orderControl extends SystemControl
         echo json_encode($result);
     }
 
+    public function card_type_dataOp()
+    {
+        $result = [
+            ['name' => '中石油', 'value' => mtopcard\PetroChinaCard],
+            ['name' => '中石化', 'value' => mtopcard\SinopecCard],
+            ['name' => '中国移动', 'value' => mtopcard\ChinaMobileCard],
+            ['name' => '中国联通', 'value' => mtopcard\ChinaUnicomCard],
+            ['name' => '中国电信', 'value' => mtopcard\ChinaTelecomCard],
+        ];
+        echo json_encode($result);
+    }
+
     public function refill_third_infoOp()
     {
         $order_id = $_GET['order_id'];

+ 1 - 1
admin/include/limit.php

@@ -42,7 +42,7 @@ $_limit =  array(
         array('name'=> '充值拦截设置', 'op'=>null, 'act'=>'refill_config'),
         array('name'=> '卡密管理', 'op'=>null, 'act'=>'card_key'),
         array('name'=> '库存管理', 'op'=>null, 'act'=>'refill_stock'),
-        array('name'=> '成功率监控', 'op'=>null, 'act'=>'refill_successful'),
+        array('name'=> '成功率监控', 'op'=>null, 'act'=>'refill_analysis'),
         array('name'=> '订单手动处理', 'op'=>null, 'act'=>'refill_order_manual'),
         array('name'=> '上游信息管理', 'op'=>null, 'act'=>'provider_info'),
         array('name'=> '机构信息管理', 'op'=>null, 'act'=>'merchant_info'),

+ 1 - 1
admin/include/menu.php

@@ -106,7 +106,7 @@ $arr = array(
 					array('args'=>'stats,card_key,merchant',				'text'=>'卡密管理'),
 					array('args'=>'index,refill_third,merchant',			'text'=>'增值业务管理'),
 					array('args'=>'index,refill_stock,merchant',			'text'=>'库存管理'),
-					array('args'=>'index,refill_successful,merchant',		'text'=>'成功率监控'),
+					array('args'=>'index,refill_analysis,merchant',			'text'=>'成功率监控'),
 					array('args'=>'index,refill_order_manual,merchant',		'text'=>'订单手动处理'),
 					array('args'=>'index,provider_info,merchant',			'text'=>'上游信息管理'),
 					array('args'=>'index,merchant_info,merchant',			'text'=>'机构信息管理'),

+ 183 - 0
admin/templates/default/analysis.new_version.php

@@ -0,0 +1,183 @@
+<div class="page">
+    <div class="fixed-bar">
+        <div class="item-title">
+            <h3>成功率监控列表</h3>
+            <ul class="tab-base">
+                <li><a href="?index.php&act=refill_analysis&op=index&type=provider"><span>通道成功率监控</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=index&type=system"><span>平台成功率监控</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=merchant_ratios"><span>机构成功率统计</span></a></li>
+                <li><a href="JavaScript:void(0);" class="current"><span>新通道成功率监控</span></a></li>
+            </ul>
+        </div>
+    </div>
+    <div class="fixed-empty"></div>
+    <div style="margin-top: 10px;color: #e60d0d;">
+        请先选择时间
+    </div>
+    <form method="get" action="index.php" name="formSearch" id="formSearch" style="min-width: 1200px;">
+        <table class="tb-type1 noborder search tableFixed">
+            <tr>
+                <th><label for="query_start_time">统计时间</label></th>
+                <td>
+                    <input class="txt date" type="text" value="<?php echo $_GET['query_start_time']; ?>"
+                           id="startTime" name="query_start_time" autocomplete="off" style="width:120px" />
+                    <label for="query_start_time">~</label>
+                    <input class="txt date" type="text" value="<?php echo $_GET['query_end_time']; ?>"
+                           id="endTime" name="query_end_time" autocomplete="off" style="width:120px" />
+                </td>
+                <th><label>通道选择</label></th>
+                <td>
+                    <div id="selest_chnames"></div>
+                </td>
+                <th><label>面额</label></th>
+                <td>
+                    <select name="amount">
+                        <option value=""><?php echo $lang['nc_please_choose']; ?></option>
+                        <option value="10" <?php if ($_GET['refill_amount'] == '10') { ?>selected<?php } ?>>10</option>
+                        <option value="20" <?php if ($_GET['refill_amount'] == '20') { ?>selected<?php } ?>>20</option>
+                        <option value="30" <?php if ($_GET['refill_amount'] == '30') { ?>selected<?php } ?>>30</option>
+                        <option value="50" <?php if ($_GET['refill_amount'] == '50') { ?>selected<?php } ?>>50</option>
+                        <option value="100" <?php if ($_GET['refill_amount'] == '100') { ?>selected<?php } ?>>100</option>
+                        <option value="200" <?php if ($_GET['refill_amount'] == '200') { ?>selected<?php } ?>>200</option>
+                        <option value="300" <?php if ($_GET['refill_amount'] == '300') { ?>selected<?php } ?>>300</option>
+                        <option value="500" <?php if ($_GET['refill_amount'] == '500') { ?>selected<?php } ?>>500</option>
+                        <option value="1000" <?php if ($_GET['refill_amount'] == '1000') { ?>selected<?php } ?>>1000</option>
+                        <option value="2000" <?php if ($_GET['refill_amount'] == '2000') { ?>selected<?php } ?>>2000</option>
+                    </select>
+                </td>
+                <th><label>卡类型</label></th>
+                <td>
+                    <div id="select_cardtype"></div>
+                </td>
+                <th><label>滤波器</label></th>
+                <td>
+                    <select name="filter_wave">
+                        <option value=""><?php echo $lang['nc_please_choose']; ?></option>
+                        <option value="60">1分钟</option>
+                        <option value="300">5分钟</option>
+                        <option value="600">10分钟</option>
+                        <option value="900">15分钟</option>
+                        <option value="0" selected>关闭</option>
+                    </select>
+                </td>
+                <td>
+                    <a href="javascript:void(0);" id="ncsubmit" class="btn-search"
+                       title="<?php echo $lang['nc_query']; ?>">&nbsp;
+                    </a>
+                </td>
+            </tr>
+        </table>
+    </form>
+    <div id="box">
+        <!-- <iframe
+            src="https://www.xyzshops.cn/plot/index?time_stamp=1621488600&interval=60" scrolling="no" id="Iframe" frameborder="0"></iframe> -->
+    </div>
+</div>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/laydate/laydate.js"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/js/jquery-ui/jquery.ui.js"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/js/jquery-ui/i18n/zh-CN.js"
+        charset="utf-8"></script>
+<script type="text/javascript" src="<?php echo ADMIN_TEMPLATES_URL;?>/js/xm-select.js"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL;?>/refill/layer.js"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL;?>/refill/moment-with-locales.js"></script>
+<link rel="stylesheet" type="text/css"
+      href="<?php echo RESOURCE_SITE_URL; ?>/js/jquery-ui/themes/ui-lightness/jquery.ui.css"/>
+<script type="text/javascript">
+    $(function () {
+        laydate.render({
+            elem: '#startTime',
+            type: 'datetime'
+        });
+        laydate.render({
+            elem: '#endTime',
+            type: 'datetime'
+        });
+
+        let selest_chnames
+        $.get('index.php?act=refill_analysis&op=provider_data', function (data) {
+            data = JSON.parse(data)
+            selest_chnames = xmSelect.render({
+                el: '#selest_chnames',
+                size: 'mini',
+                filterable: true,
+                style: {
+                    minHeight: '27px',
+                    lineHeight: '27px',
+                    marginLeft: '4px',
+                    width: '250px'
+                },
+                language: 'zn',
+                data: data
+            })
+        })
+
+        let select_cardtype
+        $.get('index.php?act=refill_order&op=card_type_data', function (data) {
+            data = JSON.parse(data)
+            select_cardtype = xmSelect.render({
+                el: '#select_cardtype',
+                size: 'mini',
+                filterable: true,
+                style: {
+                    minHeight: '27px',
+                    lineHeight: '27px',
+                    marginLeft: '4px',
+                    width: '250px'
+                },
+                language: 'zn',
+                data: data
+            })
+        })
+
+        function select_set(selectArr){
+            let selectStr = ''
+            for (let i = 0; i < selectArr.length; i++) {
+                selectStr += selectArr[i].value+','
+            }
+            selectStr = selectStr.substr(0, selectStr.length-1)
+            return selectStr
+        }
+
+        $('#ncsubmit').click(function () {
+            let query_start_time = $("input[name=query_start_time]").val()
+            let start_time = parseInt((new Date(query_start_time)).getTime()/1000);
+            let query_end_time = $("input[name=query_end_time]").val()
+            let end_time = parseInt((new Date(query_end_time)).getTime()/1000);
+
+            let chnames = select_set(selest_chnames.getValue())
+            let card_types = select_set(select_cardtype.getValue())
+            let spec = $("select[name=amount]").val()
+            let filter_wave = $("select[name=filter_wave]").val()
+            let src = window.location.origin + "/plot/ch_ratio?"
+            if (start_time) {
+                src += "&start_time=" + start_time;
+            }
+            if (end_time) {
+                if(end_time < start_time || !start_time)
+                {
+                    layer.msg('日期有误,结束日期需大于开始日期');
+                    return
+                }
+                src += "&end_time=" + end_time;
+            }
+            if (chnames) {
+                src += "&chnames=" + chnames;
+            }
+            if (card_types) {
+                src += "&card_types=" + card_types;
+            }
+            if (spec) {
+                src += "&spec=" + spec;
+            }
+            if(filter_wave > 0) {
+                src += '&filter_wave=' + filter_wave;
+            }
+            $.get(src, function (data){
+                if (!data) {
+                    return
+                }
+                $('#box').html(data)
+            });
+        });
+    })
+</script> 

+ 4 - 3
admin/templates/default/successful.provider.php

@@ -4,8 +4,9 @@
             <h3>成功率监控列表</h3>
             <ul class="tab-base">
                 <li><a href="JavaScript:void(0);" class="current" ><span>通道成功率监控</span></a></li>
-                <li><a href="?index.php&act=refill_successful&op=index&type=system"><span>平台成功率监控</span></a></li>
-                <li><a href="?index.php&act=refill_successful&op=merchant_ratios"><span>机构成功率统计</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=index&type=system"><span>平台成功率监控</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=merchant_ratios"><span>机构成功率统计</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=new_version"><span>新通道成功率监控</span></a></li>
             </ul>
         </div>
     </div>
@@ -125,7 +126,7 @@
         });
 
         function TimeGetWhere(time){
-            $.get("index.php?act=refill_successful&op=successful_time_get_where&type=provider&timestamp="+time, function (data){
+            $.get("index.php?act=refill_analysis&op=analysis_time_get_where&type=provider&timestamp="+time, function (data){
                 if (!data) {
                     return
                 }

+ 4 - 3
admin/templates/default/successful.system.php

@@ -3,9 +3,10 @@
         <div class="item-title">
             <h3>成功率监控列表</h3>
             <ul class="tab-base">
-                <li><a href="?index.php&act=refill_successful&op=index&type=provider"><span>通道成功率监控</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=index&type=provider"><span>通道成功率监控</span></a></li>
                 <li><a href="JavaScript:void(0);" class="current"><span>平台成功率监控</span></a></li>
-                <li><a href="?index.php&act=refill_successful&op=merchant_ratios"><span>机构成功率统计</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=merchant_ratios"><span>机构成功率统计</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=new_version"><span>新通道成功率监控</span></a></li>
             </ul>
         </div>
     </div>
@@ -126,7 +127,7 @@
         });
 
         function TimeGetWhere(time){
-            $.get("index.php?act=refill_successful&op=successful_time_get_where&type=system&timestamp="+time, function (data){
+            $.get("index.php?act=refill_analysis&op=analysis_time_get_where&type=system&timestamp="+time, function (data){
                 if (!data) {
                     return
                 }

+ 22 - 2
admin/templates/default/css/skin_0.css

@@ -201,9 +201,29 @@ h6 { font-size:12px;}
 .fixed-empty { height: 50px !important;}
 .item-title { line-height: 20px; margin-bottom: 0 !important; clear: both; overflow: hidden; _padding-bottom: 10px; +padding-bottom: 10px;}
 	.item-title h3 { float: left; margin-right:20px; }
-.tab-base {float: left; overflow: hidden; padding-top:16px; }
+.tab-base {float: left; padding-top:16px; }
 	.tab-base li { float: left;}
-		.tab-base a { font-weight: 700; line-height: 20px; background: #FFF url(../images/sky/bg_position.gif) no-repeat 0px -200px; height: 20px; float: left; padding-left: 9px; margin-right:2px;  cursor: pointer;}
+		.tab-base a { font-weight: 700; line-height: 20px; background: #FFF url(../images/sky/bg_position.gif) no-repeat 0px -200px; height: 20px; float: left; padding-left: 9px; margin-right:2px;  cursor: pointer; position: relative;}
+		.tab-base a .badge {
+			position: absolute;
+			top: 0;
+			right: 0;
+			transform: translate(50%,-50%);
+			transform-origin: 100% 0%;
+			min-width: 6px;
+			height: 18px;
+			padding: 0 6px;
+			color: #fff;
+			font-weight: 400;
+			font-size: 12px;
+			line-height: 18px;
+			white-space: nowrap;
+			text-align: center;
+			background: #ff4d4f;
+			border-radius: 10px;
+			box-shadow: 0 0 0 1px #fff;
+			font-weight: bolder;
+		}
 		.tab-base a:hover { color: #09C; background-position: 0 -220px; }
 			.tab-base a span { color: #555; background: url(../images/sky/bg_position.gif) no-repeat 9999px 9999px; float: left; padding-right: 9px; }
 				.tab-base a:hover span {  background-position: 100% -220px;}

+ 4 - 14
admin/templates/default/merchant.ratios.php

@@ -47,15 +47,16 @@
         <div class="item-title">
             <h3>成功率监控列表</h3>
             <ul class="tab-base">
-                <li><a href="?index.php&act=refill_successful&op=index&type=provider"><span>通道成功率监控</span></a></li>
-                <li><a href="?index.php&act=refill_successful&op=index&type=system"><span>平台成功率监控</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=index&type=provider"><span>通道成功率监控</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=index&type=system"><span>平台成功率监控</span></a></li>
                 <li><a href="JavaScript:void(0);" class="current" ><span>机构成功率统计</span></a></li>
+                <li><a href="?index.php&act=refill_analysis&op=new_version"><span>新通道成功率监控</span></a></li>
             </ul>
         </div>
     </div>
     <div class="fixed-empty"></div>
     <form method="get" name="formSearch" id="formSearch" class="layui-form">
-        <input type="hidden" value="refill_refund" name="act">
+        <input type="hidden" value="refill_analysis" name="act">
         <input type="hidden" value="merchant_ratios" name="op">
         <div style="float: right; margin: 0px 69px 0 0">
             <p>三分钟:移 | 联 | 电(平均成功率/成功单量)</p>
@@ -181,17 +182,6 @@
                 $("tbody>tr:even").css("background-color","#f5f6ff");
             })
         })
-        // 导出
-        $('#ncexport').click(function () {
-            $('input[name="export"]').val('1');
-            $('input[name="op"]').val('index');
-            $('#formSearch').submit();
-            $('input[name="export"]').val('');
-            let ii = layer.load();
-            setTimeout(function(){
-                layer.close(ii);
-            }, 800);
-        });
 
     });
 </script>

+ 10 - 0
admin/templates/default/provider.index.php

@@ -24,6 +24,7 @@
     <form method="get" name="formSearch" id="formSearch">
         <input type="hidden" value="provider" name="act">
         <input type="hidden" value="index" name="op">
+        <input type="hidden" value="" name="export">
         <table class="tb-type1 noborder search">
             <tbody>
                 <tr>
@@ -76,6 +77,9 @@
                         <a href="index.php?act=provider&op=refresh_price" class="btns">
                             <span><i class="icon-edit"></i>调价-立即生效</span>
                         </a>
+                        <a href="#" class="btns" id="ncexport">
+                            <span><i class="icon-edit"></i>通道-余额明细导出</span>
+                        </a>
                     </td>
                 </tr>
             </tbody>
@@ -224,6 +228,12 @@
             $('input[name="op"]').val('index');
             $('#formSearch').submit();
         });
+        // 导出
+        $('#ncexport').click(function () {
+            $('input[name="export"]').val('1');
+            $('#formSearch').submit();
+            $('input[name="export"]').val('');
+        })
         // 表格hover时背景
         $('.trFlex').each(function() {
             $(this).hover(function() {

+ 3 - 3
admin/templates/default/refill.buyback.order.php

@@ -155,7 +155,7 @@
                 <li><a href="index.php?act=refill_order_manual&op=set_cancel_order"><span>拦截订单</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order"><span>预回调订单转快充</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order_recharge"><span>预回调订单补充</span></a></li>
-                <li><a href="JavaScript:void(0);" class="current"><span>返销订单查询</span></a></li>
+                <li><a href="JavaScript:void(0);" class="current"><span>订单手动操作记录</span></a></li>
             </ul>
         </div>
     </div>
@@ -292,13 +292,13 @@
             <?php } ?>
         <?php } else { ?>
             <tr class="no_data">
-                <td colspan="17"><?php echo $lang['nc_no_record']; ?></td>
+                <td colspan="18"><?php echo $lang['nc_no_record']; ?></td>
             </tr>
         <?php } ?>
         </tbody>
         <tfoot>
         <tr class="tfoot">
-            <td colspan="17" id="dataFuncs">
+            <td colspan="18" id="dataFuncs">
                 <div class="pagination"> <?php echo $output['show_page']; ?> </div>
             </td>
         </tr>

+ 303 - 0
admin/templates/default/refill.detail.monitor.php

@@ -0,0 +1,303 @@
+<style>
+    th label {
+        display: inline-block;
+        width: 60px;
+        margin-left: 10px;
+    }
+    .lineLi {
+        display: inline-block;
+        min-width: 150px;
+        font-size: 14px;
+    }
+    .page .fixed-bar .item-title h3 {
+        margin-top: 18px !important;
+        margin-bottom: 10px !important;
+        font-weight: 700 !important;
+    }
+    .tab-base li span {
+        font-size: 12px !important;
+    }
+    .layui-form-select .layui-input {
+        height: 23px;
+        padding-left: 11px;
+        padding-right: 0 !important;
+    }
+    input::placeholder {
+        color: #333;
+    }
+    .layui-form-selected dl {
+        display: flex !important;
+        flex-wrap: wrap !important;
+    }
+    .layui-form-select dl {
+        top: 29px !important;
+        left: 4px !important;
+        min-width: 802% !important;
+        max-height: 280px !important;
+        padding: 14px 0 !important;
+    }
+    .layui-form-select dl dd.layui-this {
+        display: none;
+    }
+    .layui-form-select dl dd {
+        cursor: pointer;
+        width: 130px;
+    }
+    .lefto {
+        margin-left: 6px;
+    }
+    .db-right {
+        padding-right: 134px !important;
+        border-bottom: 1px solid #ccc;
+    }
+    .db-center {
+        padding: 9px 0;
+        border-bottom: 1px solid #ccc;
+    }
+    .db-top {
+        padding: 0 30px;
+    }
+    #selectAll {
+        cursor: pointer;
+    }
+    .query_ors {
+        width: 140px;
+    }
+</style>
+
+<?php defined('InShopNC') or exit('Access Invalid!'); ?>
+<div class="page">
+    <div class="fixed-bar">
+        <div class="item-title">
+            <h3>问题订单监控</h3>
+            <ul class="tab-base">
+                <li><a href="index.php?act=ordersendlist&op=index"><span>商户超时订单监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=monitor_notify"><span>渠道回调超时监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=neterr_order"><span>网络错误订单监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=notify_err_order"><span>未回调订单监控</span></a></li>
+                <li><a href="JavaScript:void(0);" class="current"><span>接单记录监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=order_exception"><span>异常订单监控</span></a></li>
+            </ul>
+        </div>
+    </div>
+    <div class="fixed-empty"></div>
+    <form method="get" action="index.php" name="formSearch" id="formSearch">
+
+        <table class="tb-type1 noborder search">
+            <tr>
+                <td>
+                    <a href="#" class="btns" onclick="hCopyCardNo(event)">
+                        <span><i class="icon-edit"></i>拷贝充值卡号</span>
+                    </a>
+                    <a href="#" class="btns" onclick="hCopyOrder(event)">
+                        <span><i class="icon-edit"></i>拷贝客户单号</span>
+                    </a>
+<!--                    <a href="#" class="btns"  id="hFail">-->
+<!--                        <span><i class="icon-edit"></i>批量失败</span>-->
+<!--                    </a>-->
+<!--                    <a href="#" class="btns" id="hSupplement">-->
+<!--                        <span><i class="icon-edit"></i>批量补充</span>-->
+<!--                    </a>-->
+<!--                    <a href="#" class="btns" id="hdelqueue">-->
+<!--                        <span><i class="icon-edit"></i>清除queue</span>-->
+<!--                    </a>-->
+                </td>
+            </tr>
+        </table>
+    </form>
+    <table class="table tb-type2 nobdb">
+        <thead>
+        <tr class="thead">
+<!--            <th class="align-center" id="selectAll">-->
+<!--            <input type="checkbox" name="chbox" value="">-->
+<!--            </th>-->
+            <th class="align-left">编号</th>
+            <th class="align-left">机构编号</th>
+            <th class="align-left">机构名称</th>
+            <th class="align-left">商家单号</th>
+            <th class="align-left">充值卡号</th>
+            <th class="align-right">充值额度</th>
+            <th class="align-right">下单日期</th>
+            <th class="align-center">订单状态</th>
+<!--            <th class="align-center">--><?php //echo $lang['nc_handle']; ?><!--</th>            -->
+        </tr>
+        </thead>
+        <tbody id="tbody">
+        <?php if (count($output['list']) > 0) { ?>
+            <?php
+            foreach ($output['list'] as $key => $order) { ?>
+                <tr class="hover trFlex">
+<!--                    <td class="align-center">-->
+<!--                    <input type="checkbox" id="checkBoxList" name="checkbox" value="--><?php //echo $order['detail_id'];?><!--">-->
+<!--                    </td>-->
+                    <td class="align-left"><?php echo $key+1;?></td>
+                    <td class="align-left"><?php echo $order['mchid']; ?></td>
+                    <td class="align-left"><?php echo $order['company_name'];?></td>
+                    <td class="align-left"><?php echo $order['mch_order']; ?></td>
+                    <td class="align-left"><?php echo $order['params']['card_no']; ?></td>
+                    <td class="align-right"><?php echo $order['params']['amount']; ?></td>
+                    <td class="align-right"><?php echo date('Y-m-d H:i:s', $order['order_time']) ?? '/'; ?></td>
+                    <td class="align-center"><?php echo $order['order_state_text']; ?></td>
+                    <td class="align-center">
+<!--                        <a href="index.php?act=refill_detail&op=order_dispose&type=cancel&id=--><?php //echo $order['detail_id']; ?><!--" id="hdFail">-->
+<!--                        回调失败</a>-->
+<!--                        |-->
+<!--                        <a href="index.php?act=refill_detail&op=order_dispose&type=anew&id=--><?php //echo $order['detail_id']; ?><!--">-->
+<!--                        重新补充</a>-->
+                    </td>
+                </tr>
+            <?php } ?>
+        <?php } else { ?>
+            <tr class="no_data">
+                <td colspan="8"><?php echo $lang['nc_no_record']; ?></td>
+            </tr>
+        <?php } ?>
+        </tbody>
+        <tfoot>
+        <tr class="tfoot">
+            <td colspan="8" id="dataFuncs">
+                <div class="pagination"> <?php echo $output['show_page']; ?> </div>
+            </td>
+        </tr>
+        </tfoot>
+    </table>
+    <!-- 预警提示 -->
+    <audio id="auto" src="<?php echo RESOURCE_SITE_URL; ?>/warning.mp3"></audio>
+</div>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/laydate/laydate.js"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/js/jquery-ui/jquery.ui.js"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/js/jquery-ui/i18n/zh-CN.js"
+        charset="utf-8"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL;?>/refill/layer.js"></script>
+<script type="text/javascript" src="<?php echo ADMIN_TEMPLATES_URL;?>/layui/layui.js"></script>
+<link rel="stylesheet" type="text/css" href="<?php echo ADMIN_TEMPLATES_URL; ?>/layui/css/layui.css"/>
+<link rel="stylesheet" type="text/css"
+      href="<?php echo RESOURCE_SITE_URL; ?>/js/jquery-ui/themes/ui-lightness/jquery.ui.css"/>
+<script type="text/javascript">
+    $(function () {
+        $('#ncsubmit').click(function () {
+            $('#formSearch').submit();
+        });
+        $('#query_start_time').datepicker({dateFormat: 'yy-mm-dd'});
+        $('#query_end_time').datepicker({dateFormat: 'yy-mm-dd'});
+        // 日期选择器
+        laydate.render({
+            elem: '#startTime',
+            type: 'datetime'
+        });
+        laydate.render({
+            elem: '#endTime',
+            type: 'datetime'
+        });
+          //全选
+        $('#selectAll' ).click ( function () {
+          if ($("input[name='chbox']").is(':checked')) {
+              $("input[name='checkbox']").each(function() {
+                  this.checked = true;
+              })
+          } else {
+              $("input[name='checkbox']").each(function() {
+                  this.checked = false;
+              })
+          }
+            })
+
+        //过滤
+        $("#mch_orders").blur(function () {
+            let test_mch =  $("#mch_orders").val();
+            let result = test_mch.replace(/[\  \r\n\,]+/g, ",");
+            $(this).val(result)
+            })
+
+        let get_sel_id = function (){
+            let arr = [];
+            $("input:checkbox:checked").each(function () {
+                let sel_id = $(this).val();
+                if(sel_id !== '') {
+                    arr.push(sel_id);
+                }
+            })
+            if (arr.length <= 0) { return}
+            return arr.join(",")
+        }
+
+        //批量失败
+        $('#hFail').click(function () {
+            layer.confirm('您确定要批量失败', {
+                btn: ['确定', '取消'],
+                title: '批量失败'
+            }, function () {
+                const batch = get_sel_id();
+                window.location.href = `index.php?act=refill_detail&op=order_dispose&id= ${batch ? batch : ''}&type=cancel`
+            }, function () {
+                layer.msg('取消成功')
+            });
+        })
+        //批量补充
+        $('#hSupplement').click(function () {
+            layer.confirm('您确定要批量补充', {
+                btn: ['确定', '取消'],
+                title: '批量失败'
+            }, function () {
+                const batch = get_sel_id();
+                window.location.href = `index.php?act=refill_detail&op=order_dispose&id= ${batch ? batch : ''}&type=anew`
+            }, function () {
+                layer.msg('取消成功')
+            });
+        })
+
+        //批量清除queue
+        $('#hdelqueue').click(function () {
+            layer.confirm('您确定要清除queue', {
+                btn: ['确定', '取消'],
+                title: '批量失败'
+            }, function () {
+                const batch = get_sel_id();
+                window.location.href = `index.php?act=refill_detail&op=DelQueueOrder&id= ${batch}`
+            }, function () {layer.msg('取消成功')});
+        })
+
+        // 表格hover时背景
+        $('.trFlex').each(function () {
+            let data_color = $(this).attr('data-timeout_State');
+            $(this).css('background', '#fff')
+            $(this).hover(function () {
+                $(this)[0].style.backgroundColor = '#cbe9f3'
+            }, function () {
+                $(this).css('background', '#fff')
+            })
+        })
+    });
+    function hCopyOrder(e) {
+        let str = ''
+        $('#tbody tr').each(function () {
+            let res = $(this).find('td').eq(3).text()
+            str += res + '\n'
+        })
+        let oInput = document.createElement("textarea");
+        oInput.style.border = "0 none";
+        oInput.style.color = "transparent";
+        oInput.value = str;
+        document.body.appendChild(oInput);
+        oInput.select(); // 选择对象
+        document.execCommand("Copy"); // 执行浏览器复制命令
+        oInput.parentNode.removeChild(oInput)
+    }
+
+    function hCopyCardNo(e) {
+        let str = ''
+        $('#tbody tr').each(function () {
+            let res = $(this).find('td').eq(4).text()
+            str += res + '\n'
+        })
+        let oInput = document.createElement("textarea");
+        oInput.style.border = "0 none";
+        oInput.style.color = "transparent";
+        oInput.value = str;
+        document.body.appendChild(oInput);
+        oInput.select(); // 选择对象
+        document.execCommand("Copy"); // 执行浏览器复制命令
+        oInput.parentNode.removeChild(oInput)
+    }
+
+</script> 

+ 22 - 2
admin/templates/default/refill.order.batch.php

@@ -173,7 +173,7 @@
                <li><a href="index.php?act=refill_order_manual&op=set_cancel_order"><span>拦截订单</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order"><span>预回调订单转快充</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order_recharge"><span>预回调订单补充</span></a></li>
-                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>返销订单查询</span></a></li>
+                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>订单手动操作记录</span></a></li>
             </ul>
         </div>
     </div>
@@ -284,7 +284,11 @@
                     </a>
                 </td>
                 <th></th>
-                <td></td>
+                <td>
+                    <a href="#" class="btns" onclick="hCopyCardNo(event)">
+                        <span><i class="icon-edit"></i>拷贝充值卡号</span>
+                    </a>
+                </td>
                 <th><label class="query_ors">返销备注</label></th>
                 <td>
                     <textarea name="bz" id="bz" cols="30" rows="10"></textarea>
@@ -641,4 +645,20 @@
             }, function () {layer.msg('取消成功');});
         })
     });
+
+    function hCopyCardNo(e) {
+        let str = ''
+        $('#tbody tr').each(function () {
+            let card_no = $(this).find('td').eq(5).text()
+            str += card_no + '\n'
+        })
+        let oInput = document.createElement("textarea");
+        oInput.style.border = "0 none";
+        oInput.style.color = "transparent";
+        oInput.value = str;
+        document.body.appendChild(oInput);
+        oInput.select(); // 选择对象
+        document.execCommand("Copy"); // 执行浏览器复制命令
+        oInput.parentNode.removeChild(oInput)
+    }
 </script> 

+ 313 - 0
admin/templates/default/refill.order.exception.php

@@ -0,0 +1,313 @@
+<style>
+    th label {
+        display: inline-block;
+        width: 60px;
+        margin-left: 10px;
+    }
+
+    .lineLi {
+        display: inline-block;
+        min-width: 150px;
+        font-size: 14px;
+    }
+
+    .page .fixed-bar .item-title h3 {
+        margin-top: 18px !important;
+        margin-bottom: 10px !important;
+        font-weight: 700 !important;
+    }
+
+    .tab-base li span {
+        font-size: 12px !important;
+    }
+
+    .layui-form-select .layui-input {
+        height: 23px;
+        padding-left: 11px;
+        padding-right: 0 !important;
+    }
+
+    input::placeholder {
+        color: #333;
+    }
+
+    .layui-form-selected dl {
+        display: flex !important;
+        flex-wrap: wrap !important;
+    }
+
+    .layui-form-select dl {
+        top: 29px !important;
+        left: 4px !important;
+        min-width: 802% !important;
+        max-height: 280px !important;
+        padding: 14px 0 !important;
+    }
+
+    .layui-form-select dl dd.layui-this {
+        display: none;
+    }
+
+    .layui-form-select dl dd {
+        cursor: pointer;
+        width: 130px;
+    }
+
+    .lefto {
+        margin-left: 6px;
+    }
+
+    .db-right {
+        padding-right: 134px !important;
+        border-bottom: 1px solid #ccc;
+    }
+
+    .db-center {
+        padding: 9px 0;
+        border-bottom: 1px solid #ccc;
+    }
+
+    .db-top {
+        padding: 0 30px;
+    }
+
+    #selectAll {
+        cursor: pointer;
+    }
+
+    .query_ors {
+        width: 140px;
+    }
+</style>
+
+<?php defined('InShopNC') or exit('Access Invalid!'); ?>
+<div class="page">
+    <div class="fixed-bar">
+        <div class="item-title">
+            <h3>问题订单监控</h3>
+            <ul class="tab-base">
+                <li><a href="index.php?act=ordersendlist&op=index"><span>商户超时订单监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=monitor_notify"><span>渠道回调超时监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=neterr_order"><span>网络错误订单监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=notify_err_order"><span>未回调订单监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=refill_detail_monitor"><span>接单记录监控</span></a></li>
+                <li>
+                    <a href="JavaScript:void(0);" class="current"><span>异常订单监控</span>
+                        <?php if($output['except_stat'] > 0){?>
+                        <span class="badge"><?php echo $output['except_stat'];?></span>
+                        <?php }?>
+                    </a>
+                </li>
+            </ul>
+        </div>
+    </div>
+    <div class="fixed-empty"></div>
+    <form method="get" action="index.php" name="formSearch" id="formSearch">
+        <input type="hidden" name="act" value="ordersendlist" />
+        <input type="hidden" name="op" value="order_exception" />
+        <table class="tb-type1 noborder search">
+            <tr>
+                <th><label class="query_ors">订单号(多行查询)</label></th>
+                <td><textarea name="order_sns" id="order_sns" cols="30" rows="10"><?php echo $_GET['order_sns']; ?></textarea></td>
+                <th><label>异常状态</label></th>
+                <td>
+
+                    <select name="except_state" class="querySelect">
+                        <option value=""><?php echo $lang['nc_please_choose']; ?></option>
+                        <option value="0" <?php if ($_GET['except_state'] == '0') { ?>selected<?php } ?>>未处理
+                        </option>
+                        <option value="1" <?php if ($_GET['except_state'] == '1') { ?>selected<?php } ?>>已处理
+                        </option>
+                    </select>
+                </td>
+                <td>
+                    <a href="javascript:void(0);" id="ncsubmit" class="btn-search" title="<?php echo $lang['nc_query']; ?>">&nbsp;
+                    </a>
+                </td>
+            </tr>
+        </table>
+        <table class="tb-type1 noborder search">
+            <tr>
+                <td>
+                    <a href="#" class="btns" onclick="hCopyOrder(event)">
+                        <span><i class="icon-edit"></i>拷贝订单号</span>
+                    </a>
+                    <a href="#" class="btns" id="hHandled">
+                        <span><i class="icon-edit"></i>批量标记为已处理</span>
+                    </a>
+                </td>
+            </tr>
+        </table>
+    </form>
+    <table class="table tb-type2 nobdb">
+        <thead>
+            <tr class="thead">
+                <th class="align-center" id="selectAll">
+                    <input type="checkbox" name="chbox" value="">
+                </th>
+                <th class="align-left">编号</th>
+                <th class="align-left">机构编号</th>
+                <th class="align-left">机构名称</th>
+                <th class="align-left">通道名称</th>
+                <th class="align-left">订单号</th>
+                <th class="align-left">异常事件</th>
+                <th class="align-left">异常信息</th>
+                <th class="align-left">生成日期</th>
+                <th class="align-left">更新日期</th>
+                <th class="align-center">异常状态</th>
+                <th class="align-left">处理人</th>
+                <th class="align-center"><?php echo $lang['nc_handle']; ?></th>
+            </tr>
+        </thead>
+        <tbody id="tbody">
+            <?php if (count($output['list']) > 0) { ?>
+                <?php
+                foreach ($output['list'] as $key => $order) { ?>
+                    <tr class="hover trFlex">
+                        <td class="align-center">
+                            <input type="checkbox" id="checkBoxList" name="checkbox" value="<?php echo $order['except_id']; ?>">
+                        </td>
+                        <td class="align-left"><?php echo $key + 1; ?></td>
+                        <td class="align-left"><?php echo $order['mchid']; ?></td>
+                        <td class="align-left"><?php echo $output['merchant_list'][$order['mchid']]['company_name']; ?></td>
+                        <td class="align-left"><?php echo $order['store_name']; ?></td>
+                        <td class="align-left"><?php echo $order['order_sn']; ?></td>
+                        <td class="align-left"><?php echo $order['title']; ?></td>
+                        <td class="align-left"><?php echo $order['except_desc']; ?></td>
+                        <td class="align-left"><?php echo date('Y-m-d H:i:s', $order['add_time']) ?? '/'; ?></td>
+                        <td class="align-left"><?php echo !empty($order['oper_time']) ? date('Y-m-d H:i:s', $order['oper_time']) : '/'; ?></td>
+                        <td class="align-center">
+                            <?php if ($order['except_state'] == '0') { ?>
+                                <span style="color: #E53737">未处理</span>
+                                <?php } ?><?php if ($order['except_state'] == '1') { ?>
+                                <span style="color: #48975A">已处理</span>
+                            <?php } ?>
+                        </td>
+                        <td class="align-left"><?php echo $order['admin_name']; ?></td>
+                        <td class="align-center">
+                            <?php if ($order['except_state'] == 0) { ?>
+                                <a href="index.php?act=ordersendlist&op=order_exception_handled&except_ids=<?php echo $order['except_id']; ?>">
+                                    已处理</a>
+                            <?php } ?>
+                        </td>
+                    </tr>
+                <?php } ?>
+            <?php } else { ?>
+                <tr class="no_data">
+                    <td colspan="13"><?php echo $lang['nc_no_record']; ?></td>
+                </tr>
+            <?php } ?>
+        </tbody>
+        <tfoot>
+            <tr class="tfoot">
+                <td colspan="13" id="dataFuncs">
+                    <div class="pagination"> <?php echo $output['show_page']; ?> </div>
+                </td>
+            </tr>
+        </tfoot>
+    </table>
+    <!-- 预警提示 -->
+    <audio id="auto" src="<?php echo RESOURCE_SITE_URL; ?>/warning.mp3"></audio>
+</div>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/laydate/laydate.js"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/js/jquery-ui/jquery.ui.js"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/js/jquery-ui/i18n/zh-CN.js" charset="utf-8"></script>
+<script type="text/javascript" src="<?php echo RESOURCE_SITE_URL; ?>/refill/layer.js"></script>
+<script type="text/javascript" src="<?php echo ADMIN_TEMPLATES_URL; ?>/layui/layui.js"></script>
+<link rel="stylesheet" type="text/css" href="<?php echo ADMIN_TEMPLATES_URL; ?>/layui/css/layui.css" />
+<link rel="stylesheet" type="text/css" href="<?php echo RESOURCE_SITE_URL; ?>/js/jquery-ui/themes/ui-lightness/jquery.ui.css" />
+<script type="text/javascript">
+    $(function() {
+        $('#ncsubmit').click(function() {
+            $('#formSearch').submit();
+        });
+        $('#query_start_time').datepicker({
+            dateFormat: 'yy-mm-dd'
+        });
+        $('#query_end_time').datepicker({
+            dateFormat: 'yy-mm-dd'
+        });
+        // 日期选择器
+        laydate.render({
+            elem: '#startTime',
+            type: 'datetime'
+        });
+        laydate.render({
+            elem: '#endTime',
+            type: 'datetime'
+        });
+        //全选
+        $('#selectAll').click(function() {
+            if ($("input[name='chbox']").is(':checked')) {
+                $("input[name='checkbox']").each(function() {
+                    this.checked = true;
+                })
+            } else {
+                $("input[name='checkbox']").each(function() {
+                    this.checked = false;
+                })
+            }
+        })
+
+        $("#order_sns").blur(function() {
+            let test_mch = $("#order_sns").val();
+            let result = test_mch.replace(/[\  \r\n\,]+/g, ",");
+            $(this).val(result)
+        })
+
+        let get_sel_id = function() {
+            let arr = [];
+            $("input:checkbox:checked").each(function() {
+                let sel_id = $(this).val();
+                if (sel_id !== '') {
+                    arr.push(sel_id);
+                }
+            })
+            if (arr.length <= 0) {
+                return
+            }
+            return arr.join(",")
+        }
+
+        //批量失败
+        $('#hHandled').click(function() {
+            layer.confirm('您确定要批量处理', {
+                btn: ['确定', '取消'],
+                title: '批量失败'
+            }, function() {
+                const batch = get_sel_id();
+                window.location.href = `index.php?act=ordersendlist&op=order_exception_handled&except_ids= ${batch ? batch : ''}`
+            }, function() {
+                layer.msg('取消成功')
+            });
+        })
+
+
+        // 表格hover时背景
+        $('.trFlex').each(function() {
+            let data_color = $(this).attr('data-timeout_State');
+            $(this).css('background', '#fff')
+            $(this).hover(function() {
+                $(this)[0].style.backgroundColor = '#cbe9f3'
+            }, function() {
+                $(this).css('background', '#fff')
+            })
+        })
+    });
+
+    function hCopyOrder(e) {
+        let str = ''
+        $('#tbody tr').each(function() {
+            let res = $(this).find('td').eq(5).text()
+            str += res + '\n'
+        })
+        let oInput = document.createElement("textarea");
+        oInput.style.border = "0 none";
+        oInput.style.color = "transparent";
+        oInput.value = str;
+        document.body.appendChild(oInput);
+        oInput.select(); // 选择对象
+        document.execCommand("Copy"); // 执行浏览器复制命令
+        oInput.parentNode.removeChild(oInput)
+    }
+</script>

+ 1 - 1
admin/templates/default/refill.order.manual.index.php

@@ -42,7 +42,7 @@
                 <li><a href="index.php?act=refill_order_manual&op=set_cancel_order"><span>拦截订单</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order"><span>预回调订单转快充</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order_recharge"><span>预回调订单补充</span></a></li>
-                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>返销订单查询</span></a></li>
+                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>订单手动操作记录</span></a></li>
             </ul>
         </div>
     </div>

+ 8 - 0
admin/templates/default/refill.order.monitor.notify.php

@@ -71,6 +71,14 @@
                 <li><a href="JavaScript:void(0);" class="current"><span>渠道回调超时监控</span></a></li>
                 <li><a href="index.php?act=ordersendlist&op=neterr_order"><span>网络错误订单监控</span></a></li>
                 <li><a href="index.php?act=ordersendlist&op=notify_err_order"><span>未回调订单监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=refill_detail_monitor"><span>接单记录监控</span></a></li>
+                <li>
+                    <a href="index.php?act=ordersendlist&op=order_exception"><span>异常订单监控</span>
+                        <?php if($output['except_stat'] > 0){?>
+                            <span class="badge"><?php echo $output['except_stat'];?></span>
+                        <?php }?>
+                    </a>
+                </li>
             </ul>
         </div>
     </div>

+ 2 - 0
admin/templates/default/refill.order.neterr.index.php

@@ -71,6 +71,8 @@
                 <li><a href="index.php?act=ordersendlist&op=monitor_notify"><span>渠道回调超时监控</span></a></li>
                 <li><a href="JavaScript:void(0);" class="current"><span>网络错误订单监控</span></a></li>
                 <li><a href="index.php?act=ordersendlist&op=notify_err_order"><span>未回调订单监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=refill_detail_monitor"><span>接单记录监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=order_exception"><span>异常订单监控</span></a></li>
             </ul>
         </div>
     </div>

+ 2 - 0
admin/templates/default/refill.order.notify.err.index.php

@@ -72,6 +72,8 @@
                 <li><a href="index.php?act=ordersendlist&op=monitor_notify"><span>渠道回调超时监控</span></a></li>
                 <li><a href="index.php?act=ordersendlist&op=neterr_order"><span>网络错误订单监控</span></a></li>
                 <li><a href="JavaScript:void(0);" class="current"><span>未回调订单监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=refill_detail_monitor"><span>接单记录监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=order_exception"><span>异常订单监控</span></a></li>
             </ul>
         </div>
     </div>

+ 8 - 0
admin/templates/default/refill.order.send.index.php

@@ -71,6 +71,14 @@
                 <li><a href="index.php?act=ordersendlist&op=monitor_notify"><span>渠道回调超时监控</span></a></li>
                 <li><a href="index.php?act=ordersendlist&op=neterr_order"><span>网络错误订单监控</span></a></li>
                 <li><a href="index.php?act=ordersendlist&op=notify_err_order"><span>未回调订单监控</span></a></li>
+                <li><a href="index.php?act=ordersendlist&op=refill_detail_monitor"><span>接单记录监控</span></a></li>
+                <li>
+                    <a href="index.php?act=ordersendlist&op=order_exception"><span>异常订单监控</span>
+                        <?php if($output['except_stat'] > 0){?>
+                            <span class="badge"><?php echo $output['except_stat'];?></span>
+                        <?php }?>
+                    </a>
+                </li>
             </ul>
         </div>
     </div>

+ 1 - 1
admin/templates/default/set.cancel.order.php

@@ -76,7 +76,7 @@
                 <li><a href="JavaScript:void(0);" class="current"><span>拦截订单</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order"><span>预回调订单转快充</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order_recharge"><span>预回调订单补充</span></a></li>
-                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>返销订单查询</span></a></li>
+                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>订单手动操作记录</span></a></li>
             </ul>
         </div>
     </div>

+ 1 - 1
admin/templates/default/transfer.order.php

@@ -95,7 +95,7 @@
                 <li><a href="index.php?act=refill_order_manual&op=set_cancel_order"><span>拦截订单</span></a></li>
                 <li><a href="JavaScript:void(0);" class="current"><span>预回调订单转快充</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order_recharge"><span>预回调订单补充</span></a></li>
-                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>返销订单查询</span></a></li>
+                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>订单手动操作记录</span></a></li>
             </ul>
         </div>
     </div>

+ 1 - 1
admin/templates/default/transfer.order.recharge.php

@@ -95,7 +95,7 @@
                 <li><a href="index.php?act=refill_order_manual&op=set_cancel_order"><span>拦截订单</span></a></li>
                 <li><a href="index.php?act=refill_order_manual&op=transfer_order"><span>预回调订单转快充</span></a></li>
                 <li><a href="JavaScript:void(0);" class="current"><span>预回调订单补充</span></a></li>
-                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>返销订单查询</span></a></li>
+                <li><a href="index.php?act=refill_order_manual&op=refill_buyback"><span>订单手动操作记录</span></a></li>
             </ul>
         </div>
     </div>

+ 92 - 0
data/config/xyz/refill.ini.php

@@ -6184,6 +6184,96 @@ $huoshenguo_yd_phone = ['name' => 'huoshenguo_yd', 'store_id' => 234, 'qualitys'
     ],
     'official_sn' => true, 'refill_type' => 'api'];
 
+$xinruiheng_phone = ['name' => 'xinruiheng', 'store_id' => 235, 'qualitys' => '1',
+    'amount' => [
+//        10 => [
+//            ['goods_id' => 7943, 'price' => 9.66, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7943, 'price' => 9.45, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7943, 'price' => 9.47, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+//        20 => [
+//            ['goods_id' => 7944, 'price' => 19.32, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7944, 'price' => 18.9, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7944, 'price' => 18.94, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+        30 => [
+            ['goods_id' => 7945, 'price' => 28.98, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7945, 'price' => 28.35, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7945, 'price' => 28.41, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        50 => [
+            ['goods_id' => 7946, 'price' => 48.3, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7946, 'price' => 47.25, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7946, 'price' => 47.35, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        100 => [
+            ['goods_id' => 7947, 'price' => 96.6, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7947, 'price' => 94.5, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7947, 'price' => 94.7, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        200 => [
+            ['goods_id' => 7948, 'price' => 193.2, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7948, 'price' => 189, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7948, 'price' => 189.4, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+//        300 => [
+//            ['goods_id' => 7949, 'price' => 289.8, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7949, 'price' => 283.5, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7949, 'price' => 284.1, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+//        500 => [
+//            ['goods_id' => 7950, 'price' => 483, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7950, 'price' => 472.5, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7950, 'price' => 473.5, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ]
+    ],
+    'official_sn' => true, 'refill_type' => 'api'];
+
+$dezhi_phone = ['name' => 'dezhi', 'store_id' => 236, 'qualitys' => '1',
+    'amount' => [
+//        10 => [
+//            ['goods_id' => 7951, 'price' => 9.67, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7951, 'price' => 9.53, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7951, 'price' => 9.55, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+//        20 => [
+//            ['goods_id' => 7952, 'price' => 19.34, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7952, 'price' => 19.06, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7952, 'price' => 19.1, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+        30 => [
+            ['goods_id' => 7953, 'price' => 29.01, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7953, 'price' => 28.59, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7953, 'price' => 28.65, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        50 => [
+            ['goods_id' => 7954, 'price' => 48.35, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7954, 'price' => 47.65, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7954, 'price' => 47.75, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        100 => [
+            ['goods_id' => 7955, 'price' => 96.7, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7955, 'price' => 95.3, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7955, 'price' => 95.5, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        200 => [
+            ['goods_id' => 7956, 'price' => 193.4, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7956, 'price' => 190.6, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7956, 'price' => 191, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+//        300 => [
+//            ['goods_id' => 7957, 'price' => 290.1, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7957, 'price' => 285.9, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7957, 'price' => 286.5, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+//        500 => [
+//            ['goods_id' => 7958, 'price' => 483.5, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7958, 'price' => 476.5, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7958, 'price' => 477.5, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ]
+    ],
+    'official_sn' => true, 'refill_type' => 'api'];
+
 $phone_providers = [
 //    ['name' => 'beixt', 'cfg' => $beixt_phone],
 //    ['name' => 'bxtwt', 'cfg' => $bxtwt_phone],
@@ -6375,6 +6465,8 @@ $phone_providers = [
     ['name' => 'yuke_dx', 'cfg' => $yuke_dx_phone],
     ['name' => 'gaobiao', 'cfg' => $gaobiao_phone],
     ['name' => 'huoshenguo_yd', 'cfg' => $huoshenguo_yd_phone],
+    ['name' => 'xinruiheng', 'cfg' => $xinruiheng_phone],
+    ['name' => 'dezhi', 'cfg' => $dezhi_phone],
 
 ];
 $config['phone_providers'] = $phone_providers;

+ 100 - 0
data/config/yl/refill.ini.php

@@ -224,6 +224,12 @@ $menggu_oil = ['name' => 'menggu', 'store_id' => 130, 'qualitys' => '2',
     ],
     'period' => [], 'refill_type' => 'api'];
 
+$xunyin_kami_oil = ['name' => 'xunyin_kami', 'store_id' => 228, 'qualitys' => '3',
+    'amount' => [
+        100 => [['goods_id' => 6579, 'price' => 104, 'quality' => 3, 'card_type' => 'sinopec,petrochina']]
+    ],
+    'period' => [], 'refill_type' => 'api'];
+
 $oil_providers = [
 //    ['name' => 'tianjt', 'cfg' => $tianjt_oil],
 //    ['name' => 'suhctm', 'cfg' => $suhctm_oil],
@@ -244,6 +250,8 @@ $oil_providers = [
 //    ['name' => 'fetch_lingzh', 'cfg' => $fetch_lingzh_oil],
 //    ['name' => 'xunyinoil', 'cfg' => $xunyinoil_oil],
 //    ['name' => 'menggu', 'cfg' => $menggu_oil]
+    ['name' => 'xunyin_kami', 'cfg' => $xunyin_kami_oil],
+
 ];
 $config['oil_providers'] = $oil_providers;
 
@@ -6007,6 +6015,96 @@ $huoshenguo_yd_phone = ['name' => 'huoshenguo_yd', 'store_id' => 225, 'qualitys'
     ],
     'official_sn' => true, 'refill_type' => 'api'];
 
+$xinruiheng_phone = ['name' => 'xinruiheng', 'store_id' => 226, 'qualitys' => '1',
+    'amount' => [
+//        10 => [
+//            ['goods_id' => 7898, 'price' => 9.66, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7898, 'price' => 9.45, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7898, 'price' => 9.47, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+//        20 => [
+//            ['goods_id' => 7899, 'price' => 19.32, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7899, 'price' => 18.9, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7899, 'price' => 18.94, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+        30 => [
+            ['goods_id' => 7900, 'price' => 28.98, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7900, 'price' => 28.35, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7900, 'price' => 28.41, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        50 => [
+            ['goods_id' => 7901, 'price' => 48.3, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7901, 'price' => 47.25, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7901, 'price' => 47.35, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        100 => [
+            ['goods_id' => 7902, 'price' => 96.6, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7902, 'price' => 94.5, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7902, 'price' => 94.7, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        200 => [
+            ['goods_id' => 7903, 'price' => 193.2, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7903, 'price' => 189, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7903, 'price' => 189.4, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+//        300 => [
+//            ['goods_id' => 7904, 'price' => 289.8, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7904, 'price' => 283.5, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7904, 'price' => 284.1, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+//        500 => [
+//            ['goods_id' => 7905, 'price' => 483, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7905, 'price' => 472.5, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7905, 'price' => 473.5, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ]
+    ],
+    'official_sn' => true, 'refill_type' => 'api'];
+
+$dezhi_phone = ['name' => 'dezhi', 'store_id' => 227, 'qualitys' => '1',
+    'amount' => [
+//        10 => [
+//            ['goods_id' => 7906, 'price' => 9.67, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7906, 'price' => 9.53, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7906, 'price' => 9.55, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+//        20 => [
+//            ['goods_id' => 7907, 'price' => 19.34, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7907, 'price' => 19.06, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7907, 'price' => 19.1, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+        30 => [
+            ['goods_id' => 7908, 'price' => 29.01, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7908, 'price' => 28.59, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7908, 'price' => 28.65, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        50 => [
+            ['goods_id' => 7909, 'price' => 48.35, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7909, 'price' => 47.65, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7909, 'price' => 47.75, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        100 => [
+            ['goods_id' => 7910, 'price' => 96.7, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7910, 'price' => 95.3, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7910, 'price' => 95.5, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+        200 => [
+            ['goods_id' => 7911, 'price' => 193.4, 'quality' => 1, 'card_type' => 'chinamobile'],
+            ['goods_id' => 7911, 'price' => 190.6, 'quality' => 1, 'card_type' => 'chinaunicom'],
+            ['goods_id' => 7911, 'price' => 191, 'quality' => 1, 'card_type' => 'chinatelecom']
+        ],
+//        300 => [
+//            ['goods_id' => 7912, 'price' => 290.1, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7912, 'price' => 285.9, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7912, 'price' => 286.5, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ],
+//        500 => [
+//            ['goods_id' => 7913, 'price' => 483.5, 'quality' => 1, 'card_type' => 'chinamobile'],
+//            ['goods_id' => 7913, 'price' => 476.5, 'quality' => 1, 'card_type' => 'chinaunicom'],
+//            ['goods_id' => 7913, 'price' => 477.5, 'quality' => 1, 'card_type' => 'chinatelecom']
+//        ]
+    ],
+    'official_sn' => true, 'refill_type' => 'api'];
+
 $phone_providers = [
 //    ['name' => 'beixt', 'cfg' => $beixt_phone],
 //    ['name' => 'bxtwt', 'cfg' => $bxtwt_phone],
@@ -6194,6 +6292,8 @@ $phone_providers = [
     ['name' => 'guitong', 'cfg' => $guitong_phone],
     ['name' => 'gaobiao', 'cfg' => $gaobiao_phone],
     ['name' => 'huoshenguo_yd', 'cfg' => $huoshenguo_yd_phone],
+    ['name' => 'xinruiheng', 'cfg' => $xinruiheng_phone],
+    ['name' => 'dezhi', 'cfg' => $dezhi_phone],
 
 ];
 $config['phone_providers'] = $phone_providers;

+ 42 - 0
data/logic/queue.logic.php

@@ -1459,8 +1459,50 @@ class queueLogic
 
     public function OnRiskSN($params)
     {
+        $msg_adder = function ($order_id)
+        {
+            if($order_id <= 0) return false;
+
+            $mod_order = Model('vr_order');
+            $mod_refill = Model('refill_order');
+            $mod_except = Model('refill_exception');
+
+            $order_info = $mod_order->getOrderInfo(['order_id' => $order_id]);
+            $refill_info = $mod_refill->getOrderInfo(['order_id' => $order_id]);
+
+            $order_sn = $refill_info['order_sn'];
+            if($mod_except->exist_order($order_sn)) {
+                return false;
+            }
+
+            if(empty($refill_info) || empty($order_info)) {
+                return false;
+            }
+
+            $sn = $refill_info['official_sn'];
+            if(empty($sn)) {
+                $desc = "渠道返回成功,但不含流水号。已经关闭通道,未给用户回调。";
+            } else {
+                $desc = "问题流水号:{$sn},有风险。已经关闭通道,未给用户回调。";
+            }
+
+            $params = ['title' => '风险号预警',
+                'order_id' => $refill_info['order_id'],
+                'order_sn' => $refill_info['order_sn'],
+                'mchid' => $refill_info['mchid'],
+                'store_id' => $order_info['store_id'],
+                'store_name' => $order_info['store_name'],
+                'except_desc' => $desc,
+                'add_time' => time()];
+
+            return $mod_except->add_except($params);
+        };
+
         $store_id = intval($params['store_id']);
         $channel_name = $params['channel_name'];
+        $order_id = intval($params['order_id']);
+
+        $msg_adder($order_id);
 
         if($store_id <= 0 || empty($channel_name)) {
             return callback(false, 'OnRiskSN 参数有空');

+ 35 - 0
data/model/refill_exception.model.php

@@ -0,0 +1,35 @@
+<?php
+
+defined('InShopNC') or exit('Access Invalid!');
+
+class refill_exceptionModel extends Model
+{
+    public function __construct()
+    {
+        parent::__construct('refill_exception');
+    }
+
+    public function getExceptionList($condition, $pagesize = '',$total = 0, $field = '*', $order = 'add_time desc', $limit = '', $master = false)
+    {
+        $list = $this->field($field)->where($condition)->page($pagesize,$total)->order($order)->limit($limit)->master($master)->select();
+        if (empty($list)) return [];
+        return $list;
+    }
+
+    public function setHandled($exc_id, $update)
+    {
+        $update['except_state'] = 1;
+        return $this->where(['except_id' => $exc_id])->update($update);
+    }
+
+    public function add_except($datas)
+    {
+        return $this->insert($datas);
+    }
+
+    public function exist_order($order_sn)
+    {
+        $item = $this->field('*')->where(['order_sn' => $order_sn])->find();
+        return !empty($item);
+    }
+}

+ 2 - 0
docker/compose/homecuda/cli/docker-compose.yml

@@ -41,6 +41,8 @@ services:
 
   pythoncli:
     image: pycpu:3.7.10
+    ports:
+      - 5000:5000
     volumes:
       - /mnt/xyzshop/docker/compose/homecuda/conf/etc/localtime:/etc/localtime:ro
       - /mnt/xyzshop:/var/www/html

+ 2 - 2
docker/compose/workcuda/acc/docker-compose.yml

@@ -4,7 +4,7 @@ services:
   nginxsrv:
     image: nginx:alpine
     ports:
-      - "80:80"
+      - 80:80
     volumes:
       - ../../../../:/var/www/html
       - ../conf/etc/localtime:/etc/localtime:ro
@@ -23,7 +23,7 @@ services:
   mobilesrv:
     image: php-zts:7.3.18
     ports:
-      - "9100:9100"
+      - 9100:9100
     volumes:
       - ../../../../:/var/www/html
       - ../conf/etc/localtime:/etc/localtime:ro

+ 3 - 1
docker/compose/workcuda/cli/docker-compose.yml

@@ -40,7 +40,9 @@ services:
     command: ['vender-init']
 
   pythoncli:
-    image: pycpu:3.7.10
+    image: pyflask:3.7.10
+    ports:
+      - 5000:5000
     volumes:
       - /mnt/xyzshop/docker/compose/workcuda/conf/etc/localtime:/etc/localtime:ro
       - /mnt/xyzshop:/var/www/html

+ 1 - 1
docker/compose/workcuda/conf/nginx/nginx.conf

@@ -31,7 +31,7 @@ http
     {
     	listen       80;
         set  $folder_name /var/www/html;
-        server_name 192.168.1.83;
+        server_name 192.168.3.46;
         root $folder_name;
         index index.html index.php; 
 

+ 12 - 0
docker/compose/workcuda/statsec/docker-compose.yml

@@ -1,6 +1,18 @@
 version: "3.7"
 
 services:
+  flasksrv:
+    image: pycpu:3.7.10
+    volumes:
+      - ../../../../:/var/www/html
+      - ../conf/etc/localtime:/etc/localtime:ro
+      - /mnt/shoplog:/var/www/html/data/log
+      - /mnt/stdata:/var/www/html/data/stdata
+    ports:
+      - 5000:5000
+    container_name: "panda-flask"
+    command: ['python','plot_control.py']
+
   qreader:
     image: pycpu:3.7.10
     volumes:

+ 10 - 10
docker/compose/xiaoyu/admin/docker-compose.yml

@@ -76,13 +76,13 @@ services:
     container_name: "panda-task"
     command: [ php,"/var/www/html/crontab/index.php",'minutes','task' ]
 
-  token_refresh:
-    image: php-zts-debug:7.3.18
-    volumes:
-      - ../../../../:/var/www/html
-      - ../conf/etc/localtime:/etc/localtime:ro
-      - ../conf/php/php-local-debug.ini:/usr/local/etc/php/php.ini
-      - ../../../../data/upload:/var/www/html/data/upload
-      - ../../../../data/log:/var/www/html/data/log
-    container_name: "panda-token-refresh"
-    command: [ php,"/var/www/html/crontab/index.php",'vendor','token_refresh' ]
+#  token_refresh:
+#    image: php-zts-debug:7.3.18
+#    volumes:
+#      - ../../../../:/var/www/html
+#      - ../conf/etc/localtime:/etc/localtime:ro
+#      - ../conf/php/php-local-debug.ini:/usr/local/etc/php/php.ini
+#      - ../../../../data/upload:/var/www/html/data/upload
+#      - ../../../../data/log:/var/www/html/data/log
+#    container_name: "panda-token-refresh"
+#    command: [ php,"/var/www/html/crontab/index.php",'vendor','token_refresh' ]

+ 1 - 1
helper/rbridge/fulu/Bridge.php

@@ -116,7 +116,7 @@ class Bridge implements IBridge
         $body = [
             "OrderNo" => $params['order_sn'],
             "Status" => $success ? 1 : 2,
-            "Description" => '充值失败',
+            "Description" => '',
             "RealCost" => "",
             "SubstituteAccount" => "",
             "ChannelAccountId" => "",

+ 63 - 53
helper/refill/RefillBase.php

@@ -15,7 +15,6 @@ require_once(BASE_HELPER_PATH . '/refill/util.php');
 require_once(BASE_HELPER_PATH . '/refill/errcode.php');
 
 use Log;
-use refill\afd\config;
 use scope_trace;
 use mtopcard;
 use QueueClient;
@@ -81,6 +80,44 @@ class RefillBase
         return true;
     }
 
+    private function risksn_check($refill_info)
+    {
+        $card_type = intval($refill_info['card_type']);
+        $chk_types = [mtopcard\ChinaMobileCard , mtopcard\ChinaUnicomCard , mtopcard\ChinaTelecomCard];
+
+        if(!in_array($card_type,$chk_types)) {
+            return false;
+        }
+
+        $quality = intval($refill_info['quality']);
+        $official_sn = $refill_info['official_sn'];
+
+        if($quality === Quality::Normal && empty($official_sn)) {
+            return true;
+        }
+
+        $start_with = function ($haystack, $needle) {
+            $length = strlen($needle);
+            return (substr($haystack, 0, $length) === $needle);
+        };
+
+        $headers = ['0095','SP','J98','WX'];
+        $spcheker = function () use ($start_with, $headers, $official_sn)
+        {
+            $official_sn = strtoupper($official_sn);
+            foreach ($headers as $header)
+            {
+                if ($start_with($official_sn, $header)) {
+                    return true;
+                }
+            }
+
+            return false;
+        };
+
+        return $spcheker();
+    }
+
     private function proc_notify($order_id, $success, $can_try, $chname, $input = [])
     {
         $mod_order = Model('vr_order');
@@ -127,6 +164,16 @@ class RefillBase
             $mchid = intval($refill_info['mchid']);
             $mch_order = $refill_info['mch_order'];
 
+            if($success && $this->risksn_check($refill_info)) {
+                $tran->commit();
+                $params = ['store_id' => $order_info['store_id'],
+                    'channel_name' => $refill_info['channel_name'],
+                    'order_sn' => $refill_info['order_sn'],
+                    'order_id' => $refill_info['order_id']];
+                QueueClient::async_push("OnRiskSN", $params, 1);
+                return false;
+            }
+
             $period = time() - intval($refill_info['commit_time']);
             util::monitor_notify($chname,$spec,$card_type,$refill_info['channel_amount'],$period,$success);
 
@@ -310,19 +357,23 @@ class RefillBase
 
     public function mch_amount(order $order)
     {
-        $mchid = $order->mchid();
-        $org_quality = $order->org_quality();
+        try
+        {
+            $mchid = $order->mchid();
+            $org_quality = $order->org_quality();
 
-        if(PolicyUtil::mixed_quality($org_quality)) {
-            $calc = new CalcMerchantPrice($mchid, $order->spec(), $order->card_type(),$org_quality,$this->mPolicy,$order->thrid_params());
-        }
-        else {
-            $calc = new CalcMerchantPrice($mchid, $order->spec(), $order->card_type(),$order->cur_quality(),$this->mPolicy,$order->thrid_params());
-        }
-        $mch_price = $calc->calc_vgoods_price([]);
-        $mch_amount = $mch_price * $order->quantity();
+            if (PolicyUtil::mixed_quality($org_quality)) {
+                $calc = new CalcMerchantPrice($mchid, $order->spec(), $order->card_type(), $org_quality, $this->mPolicy, $order->thrid_params());
+            } else {
+                $calc = new CalcMerchantPrice($mchid, $order->spec(), $order->card_type(), $order->cur_quality(), $this->mPolicy, $order->thrid_params());
+            }
+            $mch_price = $calc->calc_vgoods_price([]);
+            $mch_amount = $mch_price * $order->quantity();
 
-        return $mch_amount;
+            return $mch_amount;
+        } catch (Exception $ex) {
+            return false;
+        }
     }
 
     //返回值:[ 错误码,错误信息,订单ID,是否是网络错误]
@@ -588,42 +639,6 @@ class RefillBase
         return ($api_pay_amount == ncPriceFormat(0.0000));
     }
 
-    private function risksn_check($refill_info,$order_info)
-    {
-        $start_with = function ($haystack, $needle) {
-            $length = strlen($needle);
-            return (substr($haystack, 0, $length) === $needle);
-        };
-
-        $spcheker = function ($refill_info) use($start_with)
-        {
-            $official_sn = $refill_info['official_sn'];
-            $card_type = intval($refill_info['card_type']);
-
-            if(empty($official_sn)) return false;
-
-            $official_sn = strtoupper($official_sn);
-            if($start_with($official_sn,'0095')) {
-                return true;
-            }
-            elseif($start_with($official_sn,'SP')) {
-                return true;
-            }
-            elseif($start_with($official_sn,'J98')) {
-                return true;
-            }
-            elseif($start_with($official_sn,'WX')) {
-                return true;
-            }
-            
-            return false;
-        };
-
-        if($spcheker($refill_info)) {
-            QueueClient::async_push("OnRiskSN", ['store_id' => $order_info['store_id'],'channel_name' => $refill_info['channel_name']], 1);
-        }
-    }
-
     public function notify_merchant($order_id,$manual)
     {
         if ($order_id <= 0) {
@@ -657,11 +672,6 @@ class RefillBase
             return [false, "错误的订单状态,不能通知."];
         }
 
-        //流水号风险检测
-        if ($order_state === ORDER_STATE_SUCCESS && $refill_info['mch_notify_times'] < 1) {
-            $this->risksn_check($refill_info, $order_info);
-        }
-
         $resp = $this->mPolicy->notify($order_info,$refill_info);
         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
         if ($resp) {

+ 50 - 0
helper/refill/api/xyz/dezhi/RefillCallBack.php

@@ -0,0 +1,50 @@
+<?php
+namespace refill\dezhi;
+
+require_once(BASE_HELPER_RAPI_PATH . '/dezhi/config.php');
+
+use refill;
+class RefillCallBack implements refill\IRefillCallBack
+{
+    public function verify($params): bool
+    {
+        $sign = $this->sign($params);
+        if ($params['szVerifyString'] == $sign) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private function sign($params)
+    {
+        $userid = config::USER_ID;
+        $key = config::KEY;
+        $content = "szAgentId={$userid}&szOrderId={$params['szOrderId']}&szPhoneNum={$params['szPhoneNum']}&nDemo={$params['nDemo']}&fSalePrice={$params['fSalePrice']}";
+        $content .= "&nFlag={$params['nFlag']}&szKey={$key}";
+        return md5($content);
+    }
+
+    public function notify($params)
+    {
+        $status = intval($params['nFlag']);
+        $order_sn = $params['szOrderId'];
+        $order_info = Model('vr_order')->getOrderInfo(['order_sn' => $order_sn]);
+        if (empty($order_info)) {
+            return [false, false, false,false];
+        }
+        $order_id = $order_info['order_id'];
+
+        if ($status === 2) {
+            $data['official_sn'] = strtolower($params['szRtnMsg']) == 'null' ? '' : $params['szRtnMsg'];
+            Model('refill_order')->edit($order_id, $data);
+            return [$order_id, true, false,true];
+        }
+        elseif ($status === 3) {
+            return [$order_id, false, true,true];
+        }
+        else {
+            return [$order_id, false, false,false];
+        }
+    }
+}

+ 141 - 0
helper/refill/api/xyz/dezhi/RefillPhone.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace refill\dezhi;
+
+require_once(BASE_HELPER_RAPI_PATH . '/dezhi/config.php');
+
+use refill;
+use Log;
+
+class RefillPhone extends refill\IRefillPhone
+{
+    public function __construct($cfgs)
+    {
+        parent::__construct($cfgs);
+    }
+
+    private function req_params(int $phone, int $amount, int $card_type, string $order_sn)
+    {
+        $params['szAgentId'] = config::USER_ID;
+        $params['szOrderId'] = $order_sn;
+        $params['szPhoneNum'] = $phone;
+        $params['nMoney'] = $amount;
+        $params['nSortType'] = config::operator[$card_type];
+        $params['nProductClass'] = 1;
+        $params['nProductType'] = 1;
+        $params['szTimeStamp'] = date("Y-m-d H:i:s");
+        $params['szNotifyUrl'] = config::NOTIFY_URL;
+        return $params;
+    }
+
+    public function add($card_no, $card_type, $amount, $params,&$net_errno = 0)
+    {
+        $order_sn = $params['order_sn'];
+        $params = $this->req_params($card_no, $amount, $card_type, $order_sn);
+
+        $sign = $this->sign($params);
+        $params['szVerifyString'] = $sign;
+
+        $resp = http_request(config::ORDER_URL, $params, 'POST', false, config::ExtHeaders, $net_errno);
+
+        if (empty($resp)) {
+            return [false, '网络错误', true];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = json_decode($resp, true);
+
+            $nRtn = $resp['nRtn'];
+            if (empty($resp)) {
+                return [false, '网络错误', true];
+            } elseif ($nRtn === 0) {
+                return [true, '', false];
+            } elseif (in_array($nRtn, config::ERR_NOS, true)) {
+                return [false, $resp['szRtnCode'], false];
+            } elseif (in_array($nRtn, [2050, 999], true)) {
+                $net_errno = "HTTP-{$nRtn}";
+                return [false, $resp['szRtnCode'], true];
+            } else {
+                $err = 998;
+                $net_errno = "HTTP-{$err}";
+                return [false, $resp['szRtnCode'], true];
+            }
+        }
+    }
+
+    public function query($refill_info)
+    {
+        $params['szAgentId'] = config::USER_ID;
+        $params['szOrderId'] = $refill_info['order_sn'];
+        $key = config::KEY;
+        $content = "szAgentId={$params['szAgentId']}&szOrderId={$params['szOrderId']}&szKey={$key}";
+        $params['szVerifyString'] = md5($content);
+
+        $resp = http_request(config::QUERY_URL, $params, 'POST', false, config::ExtHeaders);
+
+        if (empty($resp)) {
+            return [false, '网络错误'];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = json_decode($resp, true);
+            if (empty($resp)) {
+                return [false, '网络错误'];
+            }
+
+            $status = $resp['nRtn'];
+            if ($status === 5012) {
+                $updata['official_sn'] = $resp['szRtnMsg'];
+                Model('refill_order')->edit($refill_info['order_id'], $updata);
+                $order_state = ORDER_STATE_SUCCESS;
+            } elseif ($status === 5013) {
+                $order_state = ORDER_STATE_CANCEL;
+            } elseif (in_array($status, [5011,5019],true)) {
+                $order_state = ORDER_STATE_SEND;
+            } elseif ($status === 5005 && (time() - $refill_info['commit_time'] >= 300)) {
+                $order_state = ORDER_STATE_NOEXIST;
+            } else {
+                return [false, $resp['szRtnMsg']];
+            }
+
+            return [true, $order_state];
+        }
+    }
+
+    public function balance()
+    {
+        $params['szAgentId'] = config::USER_ID;
+        $key = config::KEY;
+        $content = "szAgentId={$params['szAgentId']}&szKey={$key}";
+        $params['szVerifyString'] = md5($content);
+
+        $resp = http_request(config::BALANCE_URL, $params, 'POST', false, config::ExtHeaders);
+
+        if (empty($resp)) {
+            return [false, '网络错误'];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = json_decode($resp, true);
+            if (empty($resp)) {
+                return [false, '网络错误'];
+            } elseif ($resp['nRtn'] === 0) {
+                return [true, $resp['fBalance']];
+            } else {
+                return [false, $resp['szRtnCode']];
+            }
+        }
+    }
+
+    private function sign($params)
+    {
+        $userid = config::USER_ID;
+        $key = config::KEY;
+        $content = "szAgentId={$userid}&szOrderId={$params['szOrderId']}&szPhoneNum={$params['szPhoneNum']}&nMoney={$params['nMoney']}&nSortType={$params['nSortType']}";
+        $content .= "&nProductClass={$params['nProductClass']}&nProductType={$params['nProductType']}&szTimeStamp={$params['szTimeStamp']}&szKey={$key}";
+        return md5($content);
+    }
+}

+ 12 - 0
helper/refill/api/xyz/dezhi/api.txt

@@ -0,0 +1,12 @@
+正式环境:
+话费下单地址:http://121.196.98.113:10186/plat/api/old/submitorder
+查询地址:http://121.196.98.113:10186/plat/api/old/queryorder
+查询余额地址:http://121.196.98.113:10186/plat/api/old/queryBalance
+
+ID:200002
+秘钥:4c789456e08c4d7a915cea0f2fe0b58a
+
+对接文档地址:https://docs.qq.com/doc/DWkV1VkxQVk13eEtQ
+
+http://121.196.98.113:10186/plat/index
+登陆账号 北京椰子    密码123456

+ 27 - 0
helper/refill/api/xyz/dezhi/config.php

@@ -0,0 +1,27 @@
+<?php
+
+
+namespace refill\dezhi;
+
+use mtopcard;
+class config
+{
+    const ORDER_URL = 'http://121.196.98.113:10186/plat/api/old/submitorder';
+    const QUERY_URL= 'http://121.196.98.113:10186/plat/api/old/queryorder';
+    const BALANCE_URL = 'http://121.196.98.113:10186/plat/api/old/queryBalance';
+
+    const USER_ID= '200002';
+    const KEY = '4c789456e08c4d7a915cea0f2fe0b58a';
+    const NOTIFY_URL = BASE_SITE_URL . "/mobile/callback/refill_dezhi.php";
+
+    const operator = [
+        mtopcard\ChinaMobileCard  => 1,
+        mtopcard\ChinaUnicomCard  => 2,
+        mtopcard\ChinaTelecomCard => 3
+    ];
+    const ExtHeaders = ['Content-Type:application/x-www-form-urlencoded;charset=utf-8'];
+
+    const ERR_NOS = [
+        1000,1001,1003,1004,2001,2002,2003,2020,2021,1006,2030,3003
+    ];
+}

binární
helper/refill/api/xyz/guochuang/20220624辽宁广东移动调价函.png


binární
helper/refill/api/xyz/guochuang/20220625黑龙江移动调价函.jpg


binární
helper/refill/api/xyz/guochuang/20220627宁夏移动调价函.png


binární
helper/refill/api/xyz/guochuang/20220628黑龙江移动调价函.png


+ 4 - 4
helper/refill/api/xyz/guochuang/config.php

@@ -64,15 +64,15 @@ class config
     const Price = [
         //移动
         "4-10-2" => 10.15, "4-20-2" => 19.92, "4-30-2" => 29.94, "4-50-2" => 49.9, "4-100-2" => 99.8, "4-200-2" => 199.6, "4-300-2" => 299.4, "4-500-2" => 499,//天津 2
-        "4-10-6" => 9.755, "4-20-6" => 19.51, "4-30-6" => 29.265, "4-50-6" => 48.775, "4-100-6" => 97.55, "4-200-6" => 195.1, "4-300-6" => 292.65, "4-500-6" => 487.75,//辽宁 6
+        "4-10-6" => 9.75, "4-20-6" => 19.5, "4-30-6" => 29.25, "4-50-6" => 48.75, "4-100-6" => 97.5, "4-200-6" => 195, "4-300-6" => 292.5, "4-500-6" => 487.5,//辽宁 6
         "4-10-9" => 9.94, "4-20-9" => 19.88, "4-30-9" => 29.82, "4-50-9" => 49.7, "4-100-9" => 99.4,//上海 9
-        "4-10-8" => 10.055, "4-20-8" => 20.11, "4-30-8" => 30.165, "4-50-8" => 50.275, "4-100-8" => 100.55, "4-200-8" => 201.1, "4-300-8" => 301.65, "4-500-8" => 502.75,//黑龙江 8
+        "4-10-8" => 10.015, "4-20-8" => 20.03, "4-30-8" => 30.045, "4-50-8" => 50.075, "4-100-8" => 100.15, "4-200-8" => 200.3, "4-300-8" => 300.9, "4-500-8" => 501.5,//黑龙江 8
         "4-10-29" => 9.755, "4-20-29" => 19.51, "4-30-29" => 29.265, "4-50-29" => 48.775, "4-100-29" => 97.55, "4-200-29" => 195.1, "4-300-29" => 292.65, "4-500-29" => 487.75,//青海 29
         "4-10-28" => 9.985, "4-20-28" => 19.97, "4-30-28" => 29.955, "4-50-28" => 49.925, "4-100-28" => 99.85, "4-200-28" => 199.7, "4-300-28" => 299.55, "4-500-28" => 499.25,//甘肃 28
         "4-10-13" => 10.22, "4-20-13" => 19.94, "4-30-13" => 29.91, "4-50-13" => 49.85, "4-100-13" => 99.7, "4-200-13" => 199.4, "4-300-13" => 300.9, "4-500-13" => 501.5,//福建 13
         "4-10-5" => 10.035, "4-20-5" => 20.07, "4-30-5" => 30.09, "4-50-5" => 50.15, "4-100-5" => 100.3, "4-200-5" => 200.4, "4-300-5" => 300.6, "4-500-5" => 501,//内蒙古 5
         "4-30-18" => 29.835, "4-50-18" => 49.725, "4-100-18" => 99.45, "4-200-18" => 198.3,//湖南 18
-        "4-10-19" => 9.975, "4-20-19" => 19.95, "4-30-19" => 29.895, "4-50-19" => 49.825, "4-100-19" => 99.65, "4-200-19" => 199.3, "4-300-19" => 298.95, "4-500-19" => 498.25,//广东 19
+        "4-10-19" => 9.975, "4-20-19" => 19.95, "4-30-19" => 29.865, "4-50-19" => 49.775, "4-100-19" => 99.55, "4-200-19" => 199.1, "4-300-19" => 298.65, "4-500-19" => 497.75,//广东 19
         "4-10-7" => 9.82, "4-20-7" => 19.64, "4-30-7" => 29.46, "4-50-7" => 49.1, "4-100-7" => 98.2, "4-200-7" => 196.4, "4-300-7" => 294.6, "4-500-7" => 491,//吉林 7
         "4-10-1" => 10.31, "4-20-1" => 20.32, "4-30-1" => 30.33, "4-50-1" => 50.35, "4-100-1" => 100.4, "4-200-1" => 200.3, "4-300-1" => 300.45, "4-500-1" => 500.75,//北京 1
         "4-10-22" => 10.015, "4-20-22" => 20.03, "4-30-22" => 30.045, "4-50-22" => 50.075, "4-100-22" => 100.15, "4-200-22" => 200.3, "4-300-22" => 300.45, "4-500-22" => 500.75,//重庆 22
@@ -83,7 +83,7 @@ class config
         "4-10-16" => 10.14, "4-20-16" => 20.28, "4-30-16" => 30.21, "4-50-16" => 50.15, "4-100-16" => 100.3, "4-200-16" => 200.6, "4-300-16" => 300.9, "4-500-16" => 501.5,//河南 16
         "4-50-4" => 49.75, "4-100-4" => 99.5, "4-200-4" => 199,//山西 4
         "4-10-24" => 10.21, "4-20-24" => 20.22, "4-30-24" => 30.24, "4-50-24" => 50.25, "4-100-24" => 100.3, "4-200-24" => 200.4, "4-300-24" => 300.6, "4-500-24" => 501,//贵州 24
-        "4-10-30" => 10.12, "4-20-30" => 20.24, "4-30-30" => 30.03, "4-50-30" => 50.05, "4-100-30" => 100.1, "4-200-30" => 200.2, "4-300-30" => 300.3, "4-500-30" => 500.5,//宁夏 30
+        "4-10-30" => 10.12, "4-20-30" => 20.24, "4-30-30" => 30.129, "4-50-30" => 50.15, "4-100-30" => 100.2, "4-200-30" => 200.4, "4-300-30" => 300.3, "4-500-30" => 500.5,//宁夏 30
         //联通
         "5-10-19" => 10.04, "5-20-19" => 20.08, "5-30-19" => 30.06, "5-50-19" => 50.1, "5-100-19" => 100.2, "5-200-19" => 200.4, "5-300-19" => 300.6, "5-500-19" => 501,//广东 19
         "5-10-1" => 9.99, "5-20-1" => 19.98, "5-30-1" => 29.97, "5-50-1" => 49.95, "5-100-1" => 99.9, "5-200-1" => 199.8, "5-300-1" => 299.7, "5-500-1" => 499.5,//北京 1

+ 48 - 0
helper/refill/api/xyz/xinruiheng/RefillCallBack.php

@@ -0,0 +1,48 @@
+<?php
+namespace refill\xinruiheng;
+
+require_once(BASE_HELPER_RAPI_PATH . '/xinruiheng/config.php');
+
+use refill;
+class RefillCallBack implements refill\IRefillCallBack
+{
+    public function verify($params): bool
+    {
+        $sign = $this->sign($params);
+        if ($params['sign'] == $sign) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private function sign($params)
+    {
+        $userid = config::USER_ID;
+        $key = config::KEY;
+        $content = "userid={$userid}&orderid={$params['orderid']}&sporderid={$params['sporderid']}&merchantsubmittime={$params['merchantsubmittime']}";
+        $content .= "&resultno={$params['resultno']}&key={$key}";
+        return strtoupper(md5($content));
+    }
+
+    public function notify($params)
+    {
+        $status = intval($params['resultno']);
+        $order_sn = $params['sporderid'];
+        $order_info = Model('vr_order')->getOrderInfo(['order_sn' => $order_sn]);
+        if (empty($order_info)) {
+            return [false, false, false,false];
+        }
+
+        $order_id = $order_info['order_id'];
+        if ($status === 1) {
+            $data['official_sn'] = strtolower($params['remark1']) == 'null' ? '' : $params['remark1'];
+            Model('refill_order')->edit($order_id, $data);
+            return [$order_id, true, false, true];
+        } elseif ($status === 9) {
+            return [$order_id, false, true, true];
+        } else {
+            return [$order_id, false, false, false];
+        }
+    }
+}

+ 155 - 0
helper/refill/api/xyz/xinruiheng/RefillPhone.php

@@ -0,0 +1,155 @@
+<?php
+
+namespace refill\xinruiheng;
+
+require_once(BASE_HELPER_RAPI_PATH . '/xinruiheng/config.php');
+
+use refill;
+use Log;
+
+class RefillPhone extends refill\IRefillPhone
+{
+    public function __construct($cfgs)
+    {
+        parent::__construct($cfgs);
+    }
+
+    private function req_params(int $phone, int $amount, $card_type, string $order_sn)
+    {
+        $params['userid'] = config::USER_ID;
+        $params['productid'] = config::ProductIdS[$card_type][$amount];
+        $params['price'] = $amount;
+        $params['num'] = 1;
+        $params['mobile'] = $phone;
+        $params['spordertime'] = date("YmdHis");
+        $params['sporderid'] = $order_sn;
+        $params['back_url'] = config::NOTIFY_URL;
+        $params['paytype'] = config::operator[$card_type];
+
+        return $params;
+    }
+
+    public function add($card_no, $card_type, $amount, $params,&$net_errno = 0)
+    {
+        $order_sn = $params['order_sn'];
+        $params = $this->req_params($card_no, $amount, $card_type, $order_sn);
+
+        $sign = $this->sign($params);
+        $params['sign'] = $sign;
+
+        $resp = http_request(config::ORDER_URL, $params, 'POST', false, config::ExtHeaders, $net_errno);
+
+        if (empty($resp)) {
+            return [false, '网络错误', true];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = $this->xmlToArray($resp);
+            if (empty($resp)) {
+                return [false, '网络错误', true];
+            }
+
+            $resultno = $resp['resultno'];
+            if (in_array($resultno, ['0', '2'], true)) {
+                return [true, $resp['orderid'], false];
+            } elseif (in_array($resultno, config::ERR_NOS, true)) {
+                return [false, $resultno, false];
+            } elseif ($resultno === '9999' || $resultno === '5006') {
+                $net_errno = "HTTP-{$resultno}";
+                return [false, $resultno, true];
+            } else {
+                //未知结果码
+                $err = 998;
+                $net_errno = "HTTP-{$err}";
+                return [false, $resultno, true];
+            }
+        }
+    }
+
+    public function query($refill_info)
+    {
+        $params['userid'] = config::USER_ID;
+        $params['sporderid'] = $refill_info['order_sn'];
+        $key = config::KEY;
+        $content = "userid={$params['userid']}&sporderid={$params['sporderid']}&key={$key}";
+        $params['sign'] = strtoupper(md5($content));
+
+        $resp = http_request(config::QUERY_URL, $params, 'POST', false, config::ExtHeaders);
+
+        if (empty($resp)) {
+            return [false, '网络错误'];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = $this->xmlToArray($resp);
+            if (empty($resp))
+            {
+                return [false, '网络错误'];
+            }
+            else
+            {
+                $resultno = $resp['resultno'];
+                if ($resultno === '1') {
+                    $updata['official_sn'] = $resp['remark1'];
+                    Model('refill_order')->edit($refill_info['order_id'], $updata);
+                    $order_state = ORDER_STATE_SUCCESS;
+                } elseif ($resultno === '9') {
+                    $order_state = ORDER_STATE_CANCEL;
+                } elseif (in_array($resultno, ['0','2'], true)) {
+                    $order_state = ORDER_STATE_SEND;
+                } elseif ($resultno === '5007' && (time() - $refill_info['commit_time'] > 600)) {
+                    $order_state = ORDER_STATE_NOEXIST;
+                } else {
+                    return [false, $resultno];
+                }
+
+                return [true, $order_state];
+            }
+        }
+    }
+
+    public function balance()
+    {
+        $params['userid'] = config::USER_ID;
+        $key = config::KEY;
+        $content = "userid={$params['userid']}&key={$key}";
+        $params['sign'] = strtoupper(md5($content));
+
+        $resp = http_request(config::BALANCE_URL, $params, 'POST', false, config::ExtHeaders);
+
+        if (empty($resp)) {
+            return [false, '网络错误'];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = $this->xmlToArray($resp);
+            if (empty($resp)) {
+                return [false, '网络错误'];
+            } elseif ($resp['resultno'] === '1') {
+                return [true, $resp['balance']];
+            } else {
+                return [false, $resp['resultno']];
+            }
+        }
+    }
+
+    private function sign($params)
+    {
+        $userid = config::USER_ID;
+        $key = config::KEY;
+        $content = "userid={$userid}&productid={$params['productid']}&price={$params['price']}&num={$params['num']}&mobile={$params['mobile']}&spordertime={$params['spordertime']}";
+        $content .= "&sporderid={$params['sporderid']}&key={$key}";
+        return strtoupper(md5($content));
+    }
+
+    public function xmlToArray($xml)
+    {
+        $replace_str = str_replace(' ','','encoding="gb2312"');
+        $xml = mb_convert_encoding($xml,"UTF-8","gb2312");
+        $xml = str_replace($replace_str, "encoding='utf-8'", $xml);
+        return refill\util::xmlToArray($xml);
+    }
+}

+ 50 - 0
helper/refill/api/xyz/xinruiheng/config.php

@@ -0,0 +1,50 @@
+<?php
+
+
+namespace refill\xinruiheng;
+
+use mtopcard;
+class config
+{
+    //http://www.firstsup.net/showdoc
+    const ORDER_URL = 'http://121.37.166.253:9086/onlinepay.do';
+    const QUERY_URL= 'http://121.37.166.253:9086/searchpay.do';
+    const BALANCE_URL = 'http://121.37.166.253:9086/searchbalance.do';
+
+    const USER_ID= '10002683';
+    const KEY = 'nERcExFyjZndzkYwtBpdQtw5J3cerhmJ';
+    const NOTIFY_URL = BASE_SITE_URL . "/mobile/callback/refill_xinruiheng.php";
+
+    const operator = [
+        mtopcard\ChinaMobileCard  => 'yd',
+        mtopcard\ChinaUnicomCard  => 'lt',
+        mtopcard\ChinaTelecomCard => 'dx'
+    ];
+    const ERR_NOS = [
+        '5001','5002','5003','5004','5005','5008','5009','5010','5011','5012','7001'
+    ];
+    const ExtHeaders = ['Content-Type:application/x-www-form-urlencoded;'];
+    const ProductIdS = [
+        mtopcard\ChinaMobileCard =>
+            [
+                30  => '101687',
+                50  => '101688',
+                100 => '101689',
+                200 => '101690'
+            ],
+        mtopcard\ChinaUnicomCard =>
+            [
+                30  => '101705',
+                50  => '101706',
+                100 => '101707',
+                200 => '101708'
+            ],
+        mtopcard\ChinaTelecomCard =>
+            [
+                30  => '101723',
+                50  => '101724',
+                100 => '101725',
+                200 => '101726',
+            ]
+    ];
+}

+ 31 - 0
helper/refill/api/xyz/xinruiheng/对接文档-yezi.txt

@@ -0,0 +1,31 @@
+查单平台http://121.37.166.253:7080/
+登录名  椰子
+初始密码 yezi123456
+
+客户号 10002683
+密钥 nERcExFyjZndzkYwtBpdQtw5J3cerhmJ
+下单地址:
+http://121.37.166.253:9086/onlinepay.do
+余额查询:
+http://121.37.166.253:9086/searchbalance.do
+订单主动查询地址:
+http://121.37.166.253:9086/searchpay.do
+
+http://www.firstsup.net/showdoc
+
+
+
+全国移动话费30元直充         101687
+全国移动话费50元直充         101688
+全国移动话费100元直充        101689
+全国移动话费200元直充        101690
+
+全国联通话费30元直充         101705
+全国联通话费50元直充         101706
+全国联通话费100元直充        101707
+全国联通话费200元直充        101708
+
+全国电信话费30元直充         101723
+全国电信话费50元直充         101724
+全国电信话费100元直充        101725
+全国电信话费200元直充        101726

+ 50 - 0
helper/refill/api/yl/dezhi/RefillCallBack.php

@@ -0,0 +1,50 @@
+<?php
+namespace refill\dezhi;
+
+require_once(BASE_HELPER_RAPI_PATH . '/dezhi/config.php');
+
+use refill;
+class RefillCallBack implements refill\IRefillCallBack
+{
+    public function verify($params): bool
+    {
+        $sign = $this->sign($params);
+        if ($params['szVerifyString'] == $sign) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private function sign($params)
+    {
+        $userid = config::USER_ID;
+        $key = config::KEY;
+        $content = "szAgentId={$userid}&szOrderId={$params['szOrderId']}&szPhoneNum={$params['szPhoneNum']}&nDemo={$params['nDemo']}&fSalePrice={$params['fSalePrice']}";
+        $content .= "&nFlag={$params['nFlag']}&szKey={$key}";
+        return md5($content);
+    }
+
+    public function notify($params)
+    {
+        $status = intval($params['nFlag']);
+        $order_sn = $params['szOrderId'];
+        $order_info = Model('vr_order')->getOrderInfo(['order_sn' => $order_sn]);
+        if (empty($order_info)) {
+            return [false, false, false,false];
+        }
+        $order_id = $order_info['order_id'];
+
+        if ($status === 2) {
+            $data['official_sn'] = strtolower($params['szRtnMsg']) == 'null' ? '' : $params['szRtnMsg'];
+            Model('refill_order')->edit($order_id, $data);
+            return [$order_id, true, false,true];
+        }
+        elseif ($status === 3) {
+            return [$order_id, false, true,true];
+        }
+        else {
+            return [$order_id, false, false,false];
+        }
+    }
+}

+ 141 - 0
helper/refill/api/yl/dezhi/RefillPhone.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace refill\dezhi;
+
+require_once(BASE_HELPER_RAPI_PATH . '/dezhi/config.php');
+
+use refill;
+use Log;
+
+class RefillPhone extends refill\IRefillPhone
+{
+    public function __construct($cfgs)
+    {
+        parent::__construct($cfgs);
+    }
+
+    private function req_params(int $phone, int $amount, int $card_type, string $order_sn)
+    {
+        $params['szAgentId'] = config::USER_ID;
+        $params['szOrderId'] = $order_sn;
+        $params['szPhoneNum'] = $phone;
+        $params['nMoney'] = $amount;
+        $params['nSortType'] = config::operator[$card_type];
+        $params['nProductClass'] = 1;
+        $params['nProductType'] = 1;
+        $params['szTimeStamp'] = date("Y-m-d H:i:s");
+        $params['szNotifyUrl'] = config::NOTIFY_URL;
+        return $params;
+    }
+
+    public function add($card_no, $card_type, $amount, $params,&$net_errno = 0)
+    {
+        $order_sn = $params['order_sn'];
+        $params = $this->req_params($card_no, $amount, $card_type, $order_sn);
+
+        $sign = $this->sign($params);
+        $params['szVerifyString'] = $sign;
+
+        $resp = http_request(config::ORDER_URL, $params, 'POST', false, config::ExtHeaders, $net_errno);
+
+        if (empty($resp)) {
+            return [false, '网络错误', true];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = json_decode($resp, true);
+
+            $nRtn = $resp['nRtn'];
+            if (empty($resp)) {
+                return [false, '网络错误', true];
+            } elseif ($nRtn === 0) {
+                return [true, '', false];
+            } elseif (in_array($nRtn, config::ERR_NOS, true)) {
+                return [false, $resp['szRtnCode'], false];
+            } elseif (in_array($nRtn, [2050, 999], true)) {
+                $net_errno = "HTTP-{$nRtn}";
+                return [false, $resp['szRtnCode'], true];
+            } else {
+                $err = 998;
+                $net_errno = "HTTP-{$err}";
+                return [false, $resp['szRtnCode'], true];
+            }
+        }
+    }
+
+    public function query($refill_info)
+    {
+        $params['szAgentId'] = config::USER_ID;
+        $params['szOrderId'] = $refill_info['order_sn'];
+        $key = config::KEY;
+        $content = "szAgentId={$params['szAgentId']}&szOrderId={$params['szOrderId']}&szKey={$key}";
+        $params['szVerifyString'] = md5($content);
+
+        $resp = http_request(config::QUERY_URL, $params, 'POST', false, config::ExtHeaders);
+
+        if (empty($resp)) {
+            return [false, '网络错误'];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = json_decode($resp, true);
+            if (empty($resp)) {
+                return [false, '网络错误'];
+            }
+
+            $status = $resp['nRtn'];
+            if ($status === 5012) {
+                $updata['official_sn'] = $resp['szRtnMsg'];
+                Model('refill_order')->edit($refill_info['order_id'], $updata);
+                $order_state = ORDER_STATE_SUCCESS;
+            } elseif ($status === 5013) {
+                $order_state = ORDER_STATE_CANCEL;
+            } elseif (in_array($status, [5011,5019],true)) {
+                $order_state = ORDER_STATE_SEND;
+            } elseif ($status === 5005 && (time() - $refill_info['commit_time'] >= 300)) {
+                $order_state = ORDER_STATE_NOEXIST;
+            } else {
+                return [false, $resp['szRtnMsg']];
+            }
+
+            return [true, $order_state];
+        }
+    }
+
+    public function balance()
+    {
+        $params['szAgentId'] = config::USER_ID;
+        $key = config::KEY;
+        $content = "szAgentId={$params['szAgentId']}&szKey={$key}";
+        $params['szVerifyString'] = md5($content);
+
+        $resp = http_request(config::BALANCE_URL, $params, 'POST', false, config::ExtHeaders);
+
+        if (empty($resp)) {
+            return [false, '网络错误'];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = json_decode($resp, true);
+            if (empty($resp)) {
+                return [false, '网络错误'];
+            } elseif ($resp['nRtn'] === 0) {
+                return [true, $resp['fBalance']];
+            } else {
+                return [false, $resp['szRtnCode']];
+            }
+        }
+    }
+
+    private function sign($params)
+    {
+        $userid = config::USER_ID;
+        $key = config::KEY;
+        $content = "szAgentId={$userid}&szOrderId={$params['szOrderId']}&szPhoneNum={$params['szPhoneNum']}&nMoney={$params['nMoney']}&nSortType={$params['nSortType']}";
+        $content .= "&nProductClass={$params['nProductClass']}&nProductType={$params['nProductType']}&szTimeStamp={$params['szTimeStamp']}&szKey={$key}";
+        return md5($content);
+    }
+}

+ 12 - 0
helper/refill/api/yl/dezhi/api.txt

@@ -0,0 +1,12 @@
+正式环境:
+话费下单地址:http://121.196.98.113:10186/plat/api/old/submitorder
+查询地址:http://121.196.98.113:10186/plat/api/old/queryorder
+查询余额地址:http://121.196.98.113:10186/plat/api/old/queryBalance
+
+ID:	200003
+秘钥;51649f34c38542cd8476f37d60b959af
+
+对接文档地址:https://docs.qq.com/doc/DWkV1VkxQVk13eEtQ
+
+http://121.196.98.113:10186/plat/index
+登陆账号  北京椰林      密码123456

+ 27 - 0
helper/refill/api/yl/dezhi/config.php

@@ -0,0 +1,27 @@
+<?php
+
+
+namespace refill\dezhi;
+
+use mtopcard;
+class config
+{
+    const ORDER_URL = 'http://121.196.98.113:10186/plat/api/old/submitorder';
+    const QUERY_URL= 'http://121.196.98.113:10186/plat/api/old/queryorder';
+    const BALANCE_URL = 'http://121.196.98.113:10186/plat/api/old/queryBalance';
+
+    const USER_ID= '200003';
+    const KEY = '51649f34c38542cd8476f37d60b959af';
+    const NOTIFY_URL = BASE_SITE_URL . "/mobile/callback/refill_dezhi.php";
+
+    const operator = [
+        mtopcard\ChinaMobileCard  => 1,
+        mtopcard\ChinaUnicomCard  => 2,
+        mtopcard\ChinaTelecomCard => 3
+    ];
+    const ExtHeaders = ['Content-Type:application/x-www-form-urlencoded;charset=utf-8'];
+
+    const ERR_NOS = [
+        1000,1001,1003,1004,2001,2002,2003,2020,2021,1006,2030,3003
+    ];
+}

+ 4 - 4
helper/refill/api/yl/guochuang/config.php

@@ -64,15 +64,15 @@ class config
     const Price = [
         //移动
         "4-10-2" => 10.15, "4-20-2" => 19.92, "4-30-2" => 29.94, "4-50-2" => 49.9, "4-100-2" => 99.8, "4-200-2" => 199.6, "4-300-2" => 299.4, "4-500-2" => 499,//天津 2
-        "4-10-6" => 9.755, "4-20-6" => 19.51, "4-30-6" => 29.265, "4-50-6" => 48.775, "4-100-6" => 97.55, "4-200-6" => 195.1, "4-300-6" => 292.65, "4-500-6" => 487.75,//辽宁 6
+        "4-10-6" => 9.75, "4-20-6" => 19.5, "4-30-6" => 29.25, "4-50-6" => 48.75, "4-100-6" => 97.5, "4-200-6" => 195, "4-300-6" => 292.5, "4-500-6" => 487.5,//辽宁 6
         "4-10-9" => 9.94, "4-20-9" => 19.88, "4-30-9" => 29.82, "4-50-9" => 49.7, "4-100-9" => 99.4,//上海 9
-        "4-10-8" => 10.055, "4-20-8" => 20.11, "4-30-8" => 30.165, "4-50-8" => 50.275, "4-100-8" => 100.55, "4-200-8" => 201.1, "4-300-8" => 301.65, "4-500-8" => 502.75,//黑龙江 8
+        "4-10-8" => 10.015, "4-20-8" => 20.03, "4-30-8" => 30.045, "4-50-8" => 50.075, "4-100-8" => 100.15, "4-200-8" => 200.3, "4-300-8" => 300.9, "4-500-8" => 501.5,//黑龙江 8
         "4-10-29" => 9.755, "4-20-29" => 19.51, "4-30-29" => 29.265, "4-50-29" => 48.775, "4-100-29" => 97.55, "4-200-29" => 195.1, "4-300-29" => 292.65, "4-500-29" => 487.75,//青海 29
         "4-10-28" => 9.985, "4-20-28" => 19.97, "4-30-28" => 29.955, "4-50-28" => 49.925, "4-100-28" => 99.85, "4-200-28" => 199.7, "4-300-28" => 299.55, "4-500-28" => 499.25,//甘肃 28
         "4-10-13" => 10.22, "4-20-13" => 19.94, "4-30-13" => 29.91, "4-50-13" => 49.85, "4-100-13" => 99.7, "4-200-13" => 199.4, "4-300-13" => 300.9, "4-500-13" => 501.5,//福建 13
         "4-10-5" => 10.035, "4-20-5" => 20.07, "4-30-5" => 30.09, "4-50-5" => 50.15, "4-100-5" => 100.3, "4-200-5" => 200.4, "4-300-5" => 300.6, "4-500-5" => 501,//内蒙古 5
         "4-30-18" => 29.835, "4-50-18" => 49.725, "4-100-18" => 99.45, "4-200-18" => 198.3,//湖南 18
-        "4-10-19" => 9.975, "4-20-19" => 19.95, "4-30-19" => 29.895, "4-50-19" => 49.825, "4-100-19" => 99.65, "4-200-19" => 199.3, "4-300-19" => 298.95, "4-500-19" => 498.25,//广东 19
+        "4-10-19" => 9.975, "4-20-19" => 19.95, "4-30-19" => 29.865, "4-50-19" => 49.775, "4-100-19" => 99.55, "4-200-19" => 199.1, "4-300-19" => 298.65, "4-500-19" => 497.75,//广东 19
         "4-10-7" => 9.82, "4-20-7" => 19.64, "4-30-7" => 29.46, "4-50-7" => 49.1, "4-100-7" => 98.2, "4-200-7" => 196.4, "4-300-7" => 294.6, "4-500-7" => 491,//吉林 7
         "4-10-1" => 10.31, "4-20-1" => 20.32, "4-30-1" => 30.33, "4-50-1" => 50.35, "4-100-1" => 100.4, "4-200-1" => 200.3, "4-300-1" => 300.45, "4-500-1" => 500.75,//北京 1
         "4-10-22" => 10.015, "4-20-22" => 20.03, "4-30-22" => 30.045, "4-50-22" => 50.075, "4-100-22" => 100.15, "4-200-22" => 200.3, "4-300-22" => 300.45, "4-500-22" => 500.75,//重庆 22
@@ -83,7 +83,7 @@ class config
         "4-10-16" => 10.14, "4-20-16" => 20.28, "4-30-16" => 30.21, "4-50-16" => 50.15, "4-100-16" => 100.3, "4-200-16" => 200.6, "4-300-16" => 300.9, "4-500-16" => 501.5,//河南 16
         "4-50-4" => 49.75, "4-100-4" => 99.5, "4-200-4" => 199,//山西 4
         "4-10-24" => 10.21, "4-20-24" => 20.22, "4-30-24" => 30.24, "4-50-24" => 50.25, "4-100-24" => 100.3, "4-200-24" => 200.4, "4-300-24" => 300.6, "4-500-24" => 501,//贵州 24
-        "4-10-30" => 10.12, "4-20-30" => 20.24, "4-30-30" => 30.03, "4-50-30" => 50.05, "4-100-30" => 100.1, "4-200-30" => 200.2, "4-300-30" => 300.3, "4-500-30" => 500.5,//宁夏 30
+        "4-10-30" => 10.12, "4-20-30" => 20.24, "4-30-30" => 30.129, "4-50-30" => 50.15, "4-100-30" => 100.2, "4-200-30" => 200.4, "4-300-30" => 300.3, "4-500-30" => 500.5,//宁夏 30
         //联通
         "5-10-19" => 10.04, "5-20-19" => 20.08, "5-30-19" => 30.06, "5-50-19" => 50.1, "5-100-19" => 100.2, "5-200-19" => 200.4, "5-300-19" => 300.6, "5-500-19" => 501,//广东 19
         "5-10-1" => 9.99, "5-20-1" => 19.98, "5-30-1" => 29.97, "5-50-1" => 49.95, "5-100-1" => 99.9, "5-200-1" => 199.8, "5-300-1" => 299.7, "5-500-1" => 499.5,//北京 1

+ 48 - 0
helper/refill/api/yl/xinruiheng/RefillCallBack.php

@@ -0,0 +1,48 @@
+<?php
+namespace refill\xinruiheng;
+
+require_once(BASE_HELPER_RAPI_PATH . '/xinruiheng/config.php');
+
+use refill;
+class RefillCallBack implements refill\IRefillCallBack
+{
+    public function verify($params): bool
+    {
+        $sign = $this->sign($params);
+        if ($params['sign'] == $sign) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private function sign($params)
+    {
+        $userid = config::USER_ID;
+        $key = config::KEY;
+        $content = "userid={$userid}&orderid={$params['orderid']}&sporderid={$params['sporderid']}&merchantsubmittime={$params['merchantsubmittime']}";
+        $content .= "&resultno={$params['resultno']}&key={$key}";
+        return strtoupper(md5($content));
+    }
+
+    public function notify($params)
+    {
+        $status = intval($params['resultno']);
+        $order_sn = $params['sporderid'];
+        $order_info = Model('vr_order')->getOrderInfo(['order_sn' => $order_sn]);
+        if (empty($order_info)) {
+            return [false, false, false,false];
+        }
+
+        $order_id = $order_info['order_id'];
+        if ($status === 1) {
+            $data['official_sn'] = strtolower($params['remark1']) == 'null' ? '' : $params['remark1'];
+            Model('refill_order')->edit($order_id, $data);
+            return [$order_id, true, false, true];
+        } elseif ($status === 9) {
+            return [$order_id, false, true, true];
+        } else {
+            return [$order_id, false, false, false];
+        }
+    }
+}

+ 155 - 0
helper/refill/api/yl/xinruiheng/RefillPhone.php

@@ -0,0 +1,155 @@
+<?php
+
+namespace refill\xinruiheng;
+
+require_once(BASE_HELPER_RAPI_PATH . '/xinruiheng/config.php');
+
+use refill;
+use Log;
+
+class RefillPhone extends refill\IRefillPhone
+{
+    public function __construct($cfgs)
+    {
+        parent::__construct($cfgs);
+    }
+
+    private function req_params(int $phone, int $amount, $card_type, string $order_sn)
+    {
+        $params['userid'] = config::USER_ID;
+        $params['productid'] = config::ProductIdS[$card_type][$amount];
+        $params['price'] = $amount;
+        $params['num'] = 1;
+        $params['mobile'] = $phone;
+        $params['spordertime'] = date("YmdHis");
+        $params['sporderid'] = $order_sn;
+        $params['back_url'] = config::NOTIFY_URL;
+        $params['paytype'] = config::operator[$card_type];
+
+        return $params;
+    }
+
+    public function add($card_no, $card_type, $amount, $params,&$net_errno = 0)
+    {
+        $order_sn = $params['order_sn'];
+        $params = $this->req_params($card_no, $amount, $card_type, $order_sn);
+
+        $sign = $this->sign($params);
+        $params['sign'] = $sign;
+
+        $resp = http_request(config::ORDER_URL, $params, 'POST', false, config::ExtHeaders, $net_errno);
+
+        if (empty($resp)) {
+            return [false, '网络错误', true];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = $this->xmlToArray($resp);
+            if (empty($resp)) {
+                return [false, '网络错误', true];
+            }
+
+            $resultno = $resp['resultno'];
+            if (in_array($resultno, ['0', '2'], true)) {
+                return [true, $resp['orderid'], false];
+            } elseif (in_array($resultno, config::ERR_NOS, true)) {
+                return [false, $resultno, false];
+            } elseif ($resultno === '9999' || $resultno === '5006') {
+                $net_errno = "HTTP-{$resultno}";
+                return [false, $resultno, true];
+            } else {
+                //未知结果码
+                $err = 998;
+                $net_errno = "HTTP-{$err}";
+                return [false, $resultno, true];
+            }
+        }
+    }
+
+    public function query($refill_info)
+    {
+        $params['userid'] = config::USER_ID;
+        $params['sporderid'] = $refill_info['order_sn'];
+        $key = config::KEY;
+        $content = "userid={$params['userid']}&sporderid={$params['sporderid']}&key={$key}";
+        $params['sign'] = strtoupper(md5($content));
+
+        $resp = http_request(config::QUERY_URL, $params, 'POST', false, config::ExtHeaders);
+
+        if (empty($resp)) {
+            return [false, '网络错误'];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = $this->xmlToArray($resp);
+            if (empty($resp))
+            {
+                return [false, '网络错误'];
+            }
+            else
+            {
+                $resultno = $resp['resultno'];
+                if ($resultno === '1') {
+                    $updata['official_sn'] = $resp['remark1'];
+                    Model('refill_order')->edit($refill_info['order_id'], $updata);
+                    $order_state = ORDER_STATE_SUCCESS;
+                } elseif ($resultno === '9') {
+                    $order_state = ORDER_STATE_CANCEL;
+                } elseif (in_array($resultno, ['0','2'], true)) {
+                    $order_state = ORDER_STATE_SEND;
+                } elseif ($resultno === '5007' && (time() - $refill_info['commit_time'] > 600)) {
+                    $order_state = ORDER_STATE_NOEXIST;
+                } else {
+                    return [false, $resultno];
+                }
+
+                return [true, $order_state];
+            }
+        }
+    }
+
+    public function balance()
+    {
+        $params['userid'] = config::USER_ID;
+        $key = config::KEY;
+        $content = "userid={$params['userid']}&key={$key}";
+        $params['sign'] = strtoupper(md5($content));
+
+        $resp = http_request(config::BALANCE_URL, $params, 'POST', false, config::ExtHeaders);
+
+        if (empty($resp)) {
+            return [false, '网络错误'];
+        }
+        else
+        {
+            Log::record($resp, Log::DEBUG);
+            $resp = $this->xmlToArray($resp);
+            if (empty($resp)) {
+                return [false, '网络错误'];
+            } elseif ($resp['resultno'] === '1') {
+                return [true, $resp['balance']];
+            } else {
+                return [false, $resp['resultno']];
+            }
+        }
+    }
+
+    private function sign($params)
+    {
+        $userid = config::USER_ID;
+        $key = config::KEY;
+        $content = "userid={$userid}&productid={$params['productid']}&price={$params['price']}&num={$params['num']}&mobile={$params['mobile']}&spordertime={$params['spordertime']}";
+        $content .= "&sporderid={$params['sporderid']}&key={$key}";
+        return strtoupper(md5($content));
+    }
+
+    public function xmlToArray($xml)
+    {
+        $replace_str = str_replace(' ','','encoding="gb2312"');
+        $xml = mb_convert_encoding($xml,"UTF-8","gb2312");
+        $xml = str_replace($replace_str, "encoding='utf-8'", $xml);
+        return refill\util::xmlToArray($xml);
+    }
+}

+ 50 - 0
helper/refill/api/yl/xinruiheng/config.php

@@ -0,0 +1,50 @@
+<?php
+
+
+namespace refill\xinruiheng;
+
+use mtopcard;
+class config
+{
+    //http://www.firstsup.net/showdoc
+    const ORDER_URL = 'http://121.37.166.253:9086/onlinepay.do';
+    const QUERY_URL= 'http://121.37.166.253:9086/searchpay.do';
+    const BALANCE_URL = 'http://121.37.166.253:9086/searchbalance.do';
+
+    const USER_ID= '10002684';
+    const KEY = 'ypiMPR52DwMDZ7speb2bMbzF5fHDMxzG';
+    const NOTIFY_URL = BASE_SITE_URL . "/mobile/callback/refill_xinruiheng.php";
+
+    const operator = [
+        mtopcard\ChinaMobileCard  => 'yd',
+        mtopcard\ChinaUnicomCard  => 'lt',
+        mtopcard\ChinaTelecomCard => 'dx'
+    ];
+    const ERR_NOS = [
+        '5001','5002','5003','5004','5005','5008','5009','5010','5011','5012','7001'
+    ];
+    const ExtHeaders = ['Content-Type:application/x-www-form-urlencoded;'];
+    const ProductIdS = [
+        mtopcard\ChinaMobileCard =>
+            [
+                30  => '101687',
+                50  => '101688',
+                100 => '101689',
+                200 => '101690'
+            ],
+        mtopcard\ChinaUnicomCard =>
+            [
+                30  => '101705',
+                50  => '101706',
+                100 => '101707',
+                200 => '101708'
+            ],
+        mtopcard\ChinaTelecomCard =>
+            [
+                30  => '101723',
+                50  => '101724',
+                100 => '101725',
+                200 => '101726',
+            ]
+    ];
+}

+ 31 - 0
helper/refill/api/yl/xinruiheng/对接文档-yezi.txt

@@ -0,0 +1,31 @@
+查单平台http://121.37.166.253:7080/
+登录名  椰林
+初始密码 yelin123456
+
+客户号 10002684
+密钥 ypiMPR52DwMDZ7speb2bMbzF5fHDMxzG
+下单地址:
+http://121.37.166.253:9086/onlinepay.do
+余额查询:
+http://121.37.166.253:9086/searchbalance.do
+订单主动查询地址:
+http://121.37.166.253:9086/searchpay.do
+
+http://www.firstsup.net/showdoc
+
+
+
+全国移动话费30元直充         101687
+全国移动话费50元直充         101688
+全国移动话费100元直充        101689
+全国移动话费200元直充        101690
+
+全国联通话费30元直充         101705
+全国联通话费50元直充         101706
+全国联通话费100元直充        101707
+全国联通话费200元直充        101708
+
+全国电信话费30元直充         101723
+全国电信话费50元直充         101724
+全国电信话费100元直充        101725
+全国电信话费200元直充        101726

+ 35 - 0
helper/refill/api/yl/xunyin_kami/RefillCallBack.php

@@ -0,0 +1,35 @@
+<?php
+namespace refill\xunyin_kami;
+
+require_once(BASE_HELPER_RAPI_PATH . '/xunyin_kami/config.php');
+use refill;
+class RefillCallBack implements refill\IRefillCallBack
+{
+    public function verify($params): bool
+    {
+        return false;
+    }
+
+    public function notify($params)
+    {
+        $status = intval($params['code']);
+        $order_sn = $params['id'];
+        $order_info = Model('vr_order')->getOrderInfo(['order_sn' => $order_sn]);
+        if (empty($order_info)) {
+            return [false, false, false,false];
+        }
+        $order_id = $order_info['order_id'];
+        $data['official_sn'] = strtolower($params['operatorid']) == 'null' ? '' : $params['operatorid'];
+
+        if ($status === 8888) {
+            Model('refill_order')->edit($order_id, $data);
+            return [$order_id, true, false,true];
+        }
+        elseif ($status === 8030) {
+            return [$order_id, false, true,true];
+        }
+        else {
+            return [$order_id, false, false,false];
+        }
+    }
+}

+ 32 - 0
helper/refill/api/yl/xunyin_kami/RefillOil.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace refill\xunyin_kami;
+
+require_once(BASE_HELPER_RAPI_PATH . '/xunyin_kami/config.php');
+
+use refill;
+use Log;
+
+class RefillOil extends refill\IRefillOil
+{
+    public function __construct($cfgs)
+    {
+        parent::__construct($cfgs);
+    }
+
+    public function add($card_no, $card_type, $amount, $params,&$net_errno = 0)
+    {
+        return [false, '不支持提单', false];
+    }
+
+    public function query($refill_info)
+    {
+        return [false, '暂无查询接口'];
+    }
+
+    public function balance()
+    {
+        return [false, '暂无余额接口'];
+
+    }
+}

+ 9 - 0
helper/refill/api/yl/xunyin_kami/config.php

@@ -0,0 +1,9 @@
+<?php
+
+
+namespace refill\xunyin_kami;
+
+class config
+{
+
+}

binární
helper/refill/api/yl/xunyin_kami/迅银开放平台采购接口-采购商.docx


+ 4 - 0
mobile/callback/refill_dezhi.php

@@ -0,0 +1,4 @@
+<?php
+
+refill\util::push_notify('dezhi',$_POST);
+echo ('ok');

+ 4 - 0
mobile/callback/refill_xinruiheng.php

@@ -0,0 +1,4 @@
+<?php
+
+refill\util::push_notify('xinruiheng',$_POST);
+echo ('OK');

+ 56 - 17
plot/app.py

@@ -1,5 +1,3 @@
-import os
-
 from gevent import monkey
 from gevent.pywsgi import WSGIServer
 from gevent import signal as geventsig
@@ -25,9 +23,52 @@ app.debug = True
 
 curname = __name__
 
+logging.basicConfig(filename='/var/www/html/data/log/flask.log',
+                    format='%(levelname)10s  %(asctime)s  %(name)10s %(thread)d %(message)s',
+                    level=logging.DEBUG)
+logger = logging.getLogger('plot')
+
+@app.route('/plot/ch_ratio')
+def ch_ratio():
+    from refill import ChannelPainter
+    from refill import filter_chname,filter_cardtype
+
+    try:
+        logger.debug('start chratio')
+        cur_time = int(time.time())
+
+        start_time = request.args.get('start_time')
+        start_time = cur_time - 7200 if start_time is None else int(start_time.strip())
+
+        end_time = request.args.get('end_time')
+        end_time = cur_time if end_time is None else int(end_time.strip())
+
+        stime = lambda t: time.strftime('%d-%H:%M:%S', time.localtime(t))
+        logger.debug("start_time=%s end_time=%s",stime(start_time) ,stime(end_time))
+
+        chnames = request.args.get('chnames')
+        chnames = filter_chname(chnames)
+
+        card_types = request.args.get('card_types')
+        card_types = filter_cardtype(card_types)
+
+        spec = request.args.get('spec')
+        if spec is not None:
+            spec = int(spec.strip())
+
+        painter = ChannelPainter(start_time=start_time, end_time=end_time, chnames=chnames, card_types=card_types, spec=spec)
+        buf = painter.paint()
+
+        data = base64.b64encode(buf.getbuffer()).decode("ascii")
+        return f"<img src='data:image/png;base64,{data}'/>"
+    except Exception as ex:
+        logger.error(ex)
+        return f"<img src='data:image/png;base64,{[]}'/>"
+
 
 @app.route('/plot/index')
 def index():
+    app.logger.debug('start')
     time_stamp = request.args.get('time_stamp')
     interval = request.args.get('interval')
     chname = request.args.get('chname')
@@ -121,18 +162,16 @@ def mchplot():
 
 
 if __name__ == "__main__":
-    handler = logging.FileHandler(filename='/var/www/html/data/log/plot.log')
-    handler.setLevel(logging.DEBUG)
-    logging_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',
-                                       datefmt='%Y-%m-%d %H:%M:%S')
-    handler.setFormatter(logging_format)
-    app.logger.addHandler(handler)
-
-    logger = logging.getLogger('app')
-    logger.info('app')
-
-    monkey.patch_all()
-    http_server = WSGIServer(('0.0.0.0', 5000), app)
-    geventsig.signal(sig.SIGTERM, lambda: http_server.stop())
-    geventsig.signal(sig.SIGINT, lambda: http_server.stop())
-    http_server.serve_forever()
+    debug_mode = False
+    if debug_mode:
+        app.run(debug=True, host='0.0.0.0', port=5000)
+    else:
+        from gevent import monkey
+        from gevent.pywsgi import WSGIServer
+        from gevent import signal as geventsig
+
+        monkey.patch_all()
+        http_server = WSGIServer(('0.0.0.0', 5000), app)
+        geventsig.signal(sig.SIGTERM, lambda: http_server.stop())
+        geventsig.signal(sig.SIGINT, lambda: http_server.stop())
+        http_server.serve_forever()

+ 25 - 0
plot/flask_test.py

@@ -0,0 +1,25 @@
+from flask import Flask
+from flask import request, jsonify
+
+app = Flask(__name__)
+
+import logging
+logging.basicConfig(filename='/var/www/html/data/log/flask.log',
+                    format='%(levelname)10s  %(asctime)s  %(name)10s %(thread)d %(message)s',
+                    level=logging.DEBUG)
+logger = logging.getLogger('plot')
+
+@app.route('/hello')
+def hello_world():
+    logger.debug('start chratio')
+    start_time = request.args.get('start_time') or None
+    end_time = request.args.get('end_time') or None
+    interval = request.args.get('interval') or None
+    chnames = request.args.get('chnames') or None
+    card_type = request.args.get('card_type') or None
+    spec = request.args.get('spec') or None
+    return 'Hello World'
+
+
+if __name__ == '__main__':
+    app.run(debug=True, host='0.0.0.0', port=5000)

+ 70 - 0
plot/plot_control.py

@@ -0,0 +1,70 @@
+from flask import Flask, request, jsonify
+app = Flask(__name__)
+
+import base64
+from io import BytesIO
+from matplotlib.figure import Figure
+import json
+
+import logging
+import time
+import signal as sig
+import sys, getopt
+
+from refill import ChannelPainter
+from refill import filter_chname,filter_cardtype
+
+logging.basicConfig(filename='/var/www/html/data/log/flask.log',
+                    format='%(levelname)10s  %(asctime)s  %(name)10s %(thread)d %(message)s',
+                    level=logging.DEBUG)
+logger = logging.getLogger('plot')
+
+@app.route('/plot/ch_ratio')
+def ch_ratio():
+    try:
+        logger.debug('start chratio')
+        cur_time = int(time.time())
+
+        start_time = request.args.get('start_time')
+        start_time = cur_time - 7200 if start_time is None else int(start_time.strip())
+
+        end_time = request.args.get('end_time')
+        end_time = cur_time if end_time is None else int(end_time.strip())
+
+        stime = lambda t: time.strftime('%d-%H:%M:%S', time.localtime(t))
+        logger.debug("start_time=%s end_time=%s",stime(start_time) ,stime(end_time))
+
+        chnames = request.args.get('chnames')
+        chnames = filter_chname(chnames)
+
+        card_types = request.args.get('card_types')
+        card_types = filter_cardtype(card_types)
+
+        spec = request.args.get('spec')
+        if spec is not None:
+                spec = int(spec.strip())
+
+        painter = ChannelPainter(start_time=start_time, end_time=end_time, chnames=chnames, card_types=card_types, spec=spec)
+        buf = painter.paint()
+
+        data = base64.b64encode(buf.getbuffer()).decode("ascii")
+        return f"<img src='data:image/png;base64,{data}'/>"
+    except Exception as ex:
+        logger.error(ex)
+        return f"<img src='data:image/png;base64,{[]}'/>"
+
+
+if __name__ == "__main__":
+    debug_mode = True
+    if debug_mode:
+        app.run(debug=True, host='0.0.0.0', port=5000)
+    else:
+        from gevent import monkey
+        from gevent.pywsgi import WSGIServer
+        from gevent import signal as geventsig
+
+        monkey.patch_all()
+        http_server = WSGIServer(('0.0.0.0', 5000), app)
+        geventsig.signal(sig.SIGTERM, lambda: http_server.stop())
+        geventsig.signal(sig.SIGINT, lambda: http_server.stop())
+        http_server.serve_forever()

+ 148 - 0
plot/refill/ChannelPainter.py

@@ -0,0 +1,148 @@
+from .DataStream import EChPosmap as pos_map, day_stamp, span_days, time_border, calc_interval
+from .ChannelReader import ChannelReader
+from collections import defaultdict
+from matplotlib.figure import Figure
+from matplotlib import ticker
+from io import BytesIO
+import numpy as np
+from .algorithm import calc_chratios
+import time as time
+
+import logging
+logger = logging.getLogger('painter')
+
+def allpathes(reader: ChannelReader, 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 name, tup in tuple_pathes.items():
+        ch_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, name, _card_type, _spec)
+                if data is not None:
+                    column_pos = i * 86400
+                    view = ch_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 name, _card_type, _spec, detail_datas
+        if all_datas is not None:
+            all_datas += ch_datas
+        yield name, None, None, ch_datas
+
+    if show_detail == False:
+        yield 'all', None, None, all_datas
+
+
+class ChannelPainter(object):
+    def __init__(self, start_time: int, end_time: int, chnames: set = None, card_types: set = None, spec: int = None):
+        self._reader = ChannelReader()
+        _start_time, _end_time, self._chnames, self._card_types, self._spec = start_time, end_time, chnames, card_types, spec
+
+        start_time = self._reader.near_stamp(_start_time,True)
+        end_time = self._reader.near_stamp(_end_time,False)
+
+        stime = lambda t: time.strftime('%d-%H:%M:%S', time.localtime(t))
+        logger.debug("start_time=%s end_time=%s",stime(start_time) ,stime(end_time))
+
+        interval = calc_interval(start_time, end_time)
+        start_time = time_border(interval, start_time, True)
+        end_time = time_border(interval, end_time, False)
+        self._days = span_days(start_time, end_time)
+        self._start_time = start_time
+        self._end_time = end_time
+        self._interval = interval
+        pass
+
+    def _fig_funs(self):
+        def create():
+            fig = Figure(figsize=(19, 8))
+            ax = fig.subplots()
+            ax.set_title('success ratio')
+            ax.set(xlabel='time', ylabel='ratio')
+            return ax, fig
+
+        def flush(ax, fig, ticks, lables):
+            ax.yaxis.set_major_formatter(ticker.PercentFormatter(xmax=1, decimals=4))
+            ax.set_xticks(ticks=ticks)
+            ax.set_xticklabels(lables)
+            fig.autofmt_xdate()
+            ax.grid()
+            fig.subplots_adjust(left=0.1, right=0.8, top=0.95, bottom=0.1)
+            ax.legend(bbox_to_anchor=(1, 1), loc='upper left')
+
+            buf = BytesIO()
+            fig.savefig(buf, format="png")
+            return buf
+
+        return create, flush
+
+    def paint(self):
+        reader = ChannelReader()
+        tuple_pathes = reader.many_tuple_path(self._days, self._chnames, self._card_types, self._spec)
+        gen = allpathes(self._reader, tuple_pathes, self._days, self._spec)
+        if len(self._days) == 0:
+            return []
+
+        day_stamp = self._days[0]
+        fig_create, fig_flush = self._fig_funs()
+        ax, fig = fig_create()
+        x = np.array([d - self._start_time for d in range(self._start_time, self._end_time)])
+
+        window = np.ones(600) / 600
+        for _chname, _card_type, _spec, _data in gen:
+            succ, count, y = calc_chratios(_data, pos_map, self._start_time - day_stamp, self._end_time - day_stamp)
+            print(_chname)
+            y = np.convolve(y, window, 'same')
+            ax.plot(x, y, ls='-', label=self._label(chname=_chname, succ=succ, count=count, card_type=_card_type, spec=_spec))
+
+        ticks = [d - self._start_time for d in range(self._start_time, self._end_time + 1, self._interval)]
+        xlables = [time.strftime('%d-%H:%M:%S', time.localtime(d + self._start_time)) for d in ticks]
+
+        return fig_flush(ax, fig, ticks, xlables)
+
+    def _label(self, chname, succ, count, card_type=None, spec=None):
+        _card_type = None
+        if card_type == 1:
+            _card_type = 'SY'
+        elif card_type == 2:
+            _card_type = 'SH'
+        elif card_type == 4:
+            _card_type = 'YD'
+        elif card_type == 5:
+            _card_type = 'LT'
+        elif card_type == 6:
+            _card_type = 'DX'
+        elif card_type == 7:
+            _card_type = 'TH'
+
+        lable = f"{chname}"
+        if _card_type is not None:
+            lable += f"-{_card_type}"
+
+        if spec is not None:
+            lable += f"-{spec}"
+
+        if count > 0:
+            ratio = round(succ * 100 / count, 2)
+        else:
+            ratio = 0.00
+        lable += f":{succ}/{count}={ratio}%"
+
+        return lable

+ 76 - 7
plot/refill/ChannelReader.py

@@ -1,24 +1,93 @@
 # from . import DataHandler #此时是导入文件
-from .DataStream import DataReadStream, day_stamp,open_hdf5
+from .DataStream import DataReadStream, day_stamp, open_hdf5
 from .DataStream import EChPosmap as pos_map
 import numpy as np
+import re
+from collections import defaultdict
 
 __all__ = ['MerchantReader']
 
 import logging
+
 log = logging.getLogger('reader')
 
+
 class ChannelReader(DataReadStream):
     def __init__(self):
-        file = '/var/www/html/data/stdata/merchant.hdf5'
-        hfive = open_hdf5(file,False)
-        super(ChannelReader,self).__init__(hfive)
+        file = '/var/www/html/data/stdata/channel.hdf5'
+        hfive = open_hdf5(file, False)
+        super(ChannelReader, self).__init__(hfive)
 
     def __del__(self):
         self.close()
         super(ChannelReader, self).__del__()
         pass
 
-    def read(self, path):
-        pass
-    pass
+    def tuple_path(self, day: int, chnames: set = None, card_types: set = None, spec: int = None):
+        def parse(path):
+            items = re.split(r'/', path)
+            (_prifix, _version, today, chnam, card_type, spec) = items
+            return chnam, int(card_type), int(spec)
+
+        tuples = defaultdict(list)
+        pathes = self.datasets(day)
+        for path in pathes:
+            _chnam, _card_type, _spec = parse(path)
+            tuples[_chnam].append((_card_type, _spec))
+
+        def name_filter(tuples, chnames):
+            all = True if chnames is None or len(chnames) == 0 else False
+            if all:
+                return tuples
+            else:
+                result = defaultdict(list)
+                for chame, tup in tuples.items():
+                    if chame in chnames:
+                        result[chame] = tup
+                return result
+
+        tuples = name_filter(tuples, chnames)
+
+        def typespec_filter(tuples, card_types, spec):
+            result = defaultdict(list)
+            for name, ls in tuples.items():
+                for tup in ls:
+                    _card_type, _spec = tup
+                    if _card_type in card_types:
+                        if spec is None or _spec == spec:
+                            result[name].append((_card_type, _spec))
+
+            return result
+        tuples = typespec_filter(tuples,card_types,spec)
+        return tuples
+
+    def many_tuple_path(self, days: list, chnames: set = None, card_types: set = None, spec: int = None):
+        def merge(l,r):
+            for name,ls in l.items():
+                if name in r:
+                    r[name].union(set(ls))
+                else:
+                    r[name] = set(ls)
+            return r
+
+        all = defaultdict(set)
+        for day in days:
+            tuples = self.tuple_path(day, chnames, card_types, spec)
+            all = merge(tuples,all)
+
+        return all
+
+    def init_data(self, days):
+        dim = pos_map.dim()
+        return np.zeros((dim, 86400 * days))
+
+    def read(self, day_stamp, chname, card_type, spec):
+        path = f'/{self._version}/{day_stamp}/{chname}/{card_type}/{spec}'
+
+        hfive = self.file
+        if path in hfive:
+            return hfive[path]
+        else:
+            return None
+
+    pass

+ 103 - 37
plot/refill/DataStream.py

@@ -1,3 +1,4 @@
+import time
 from abc import ABCMeta, abstractmethod, ABC
 from datetime import timedelta
 from mpi4py import MPI
@@ -5,10 +6,12 @@ import h5py
 from enum import IntEnum
 import threading
 
-__all__ = ['DataWriteStream', 'DataReadStream', 'day_stamp', 'EMchPosmap', 'EChPosmap', 'ENetPosmap', 'open_hdf5']
+__all__ = ['DataWriteStream', 'DataReadStream', 'day_stamp', 'EMchPosmap', 'EChPosmap', 'ENetPosmap', 'open_hdf5', 'time_border']
 
 import logging
-log = logging.getLogger('stream')
+
+logger = logging.getLogger('stream')
+
 
 def day_stamp(stamp):
     import time as stime
@@ -22,12 +25,50 @@ def day_stamp(stamp):
 
 def open_hdf5(file, is_wirte):
     if is_wirte:
-        # return h5py.File(file, 'a', driver='mpio', comm=MPI.COMM_WORLD)
         return h5py.File(file, 'a')
     else:
         return h5py.File(file, 'r')
 
 
+def time_border(interval, time_stamp, lt):
+    day = day_stamp(time_stamp)
+    pos = time_stamp - day
+
+    if lt:
+        pos = pos - pos % interval
+    elif pos % interval > 0:
+        pos += interval - pos % interval
+    else:
+        pos = pos
+
+    return pos + day
+
+
+def calc_interval(start, end):
+    period = end - start
+    segment = int(period / 24)
+    if segment == 0:
+        return 1
+    else:
+        intervals = [30, 60, 300, 600, 900, 1800, 3600, 7200, 14400, 21600, 28800, 36000, 43200, 86400, 172800]
+        for i in intervals:
+            if segment < i:
+                return i
+    pass
+
+
+def span_days(start_time, end_time):
+    start_day = day_stamp(start_time)
+    end_day = day_stamp(end_time)
+
+    days = list()
+    while start_day <= end_day:
+        days.append(start_day)
+        start_day += 86400
+
+    return days
+
+
 class DataWriteStream(metaclass=ABCMeta):
     _version = 20200618
     _paths = dict()
@@ -56,17 +97,25 @@ class DataWriteStream(metaclass=ABCMeta):
             self._hfive.close()
             self._hfive = None
         self._lock.release()
+
     pass
 
+
 class DataReadStream(metaclass=ABCMeta):
     _version = 20200618
+    _days = list()
 
     def __init__(self, hfive):
         self._hfive = hfive
+        self._days = self._getdays()
+        logger.debug(self._days)
 
     def __del__(self):
         pass
 
+    def days(self):
+        return self._days
+
     # Getter function
     @property
     def file(self):
@@ -77,59 +126,76 @@ class DataReadStream(metaclass=ABCMeta):
     def file(self, value):
         self._hfive = value
 
-    @abstractmethod
-    def read(self, path):
-        pass
-
     def close(self):
         if self._hfive is not None:
             self._hfive.close()
             self._hfive = None
 
-    def _sub_dirs(self, root):
-        result = []
-        try:
-            for name, sub in root.items():
+    def _root_path(self, day_stamp=None):
+        if day_stamp is None:
+            return f'/{self._version}/'
+        else:
+            return f'/{self._version}/{day_stamp}/'
+
+    def datasets(self, day_stamp):
+        def dir(group):
+            result = []
+            for name, sub in group.items():
                 if isinstance(sub, h5py.Group):
-                    result.append(name)
-        except Exception as ex:
-            log.error(ex)
-        finally:
+                    result.extend(dir(sub))
+                else:
+                    result.append(sub.name)
             return result
 
-    def dir(self, group):
-        result = []
-        for name, sub in group.items():
-            if isinstance(sub, h5py.Group):
-                result.extend(self.dir(sub))
-            else:
-                result.append(sub.name)
-        return result
-
-    def _root_path(self):
-        return f'/{self._version}/'
-
-    def dirs(self):
         try:
-            root_ptah = self._root_path()
-            root = self.file.require_group(root_ptah)
-            days = self.dir(root)
-            return days
+            path = self._root_path(day_stamp)
+            if path in self.file:
+                group = self.file.require_group(path)
+                days = dir(group)
+                return days
+            else:
+                return []
         except Exception as ex:
-            log.error(ex)
+            logger.error(ex)
             return []
 
-    def days(self):
+    def _getdays(self):
+        def sub(root):
+            result = []
+            try:
+                for name, sub in root.items():
+                    if isinstance(sub, h5py.Group):
+                        result.append(name)
+            except Exception as ex:
+                logger.error(ex)
+            finally:
+                return result
+
         try:
             root_ptah = self._root_path()
             root = self.file.require_group(root_ptah)
-            days = self._sub_dirs(root)
+            tmp = sub(root)
+            days = [int(day) for day in tmp]
+            days.sort()
             return days
         except Exception as ex:
-            log.error(ex)
+            logger.error(ex)
             return []
 
-    pass
+    def near_stamp(self, time_stamp, left=True):
+        if len(self._days) == 0:
+            return None
+
+        if time_stamp > int(time.time()):
+            time_stamp = int(time.time())
+
+        if left:
+            min = self._days[0]
+            time_stamp = min if time_stamp < min else time_stamp
+        else:
+            max = self._days[-1] + 86400 - 1
+            time_stamp = max if time_stamp > max else time_stamp
+        return time_stamp
 
 
 class EMchPosmap(IntEnum):

+ 0 - 1
plot/refill/MerchantCalc.py

@@ -2,5 +2,4 @@
 from .DataStream import EMchPosmap as pos_map
 
 class MerchantCalc(object):
-
     pass

+ 10 - 5
plot/refill/__init__.py

@@ -1,6 +1,5 @@
-
 from .QueueListener import queueListener
-from .DataStream import DataWriteStream,DataReadStream,open_hdf5
+from .DataStream import DataWriteStream, DataReadStream, open_hdf5, day_stamp, time_border
 from .MerchantWriter import MerchantWriter
 from .ChannelWriter import ChannelWriter
 from .NetchkWriter import NetchkWriter
@@ -9,7 +8,13 @@ from .MerchantReader import MerchantReader
 from .NetchkReader import NetchkReader
 from .ChannelReader import ChannelReader
 
+from .ChannelPainter import ChannelPainter
+from .helper import filter_chname, filter_cardtype
+
 __all__ = ['DataWriteStream', 'DataReadStream',
-           'MerchantWriter', 'ChannelWriter','NetchkWriter',
-           'MerchantReader','NetchkReader','ChannelReader',
-           'queueListener','open_hdf5']
+           'MerchantWriter', 'ChannelWriter', 'NetchkWriter',
+           'MerchantReader', 'NetchkReader', 'ChannelReader',
+           'ChannelPainter',
+           'queueListener', 'open_hdf5', 'day_stamp', 'time_border',
+           'filter_chname', 'filter_cardtype'
+           ]

+ 14 - 0
plot/refill/algorithm.py

@@ -0,0 +1,14 @@
+
+import numpy as np
+
+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]
+
+    all = np.cumsum(view, axis=1)
+    succ = all[0, :]
+    commit = all[0, :] + all[1, :]
+    commit += 0.0000001
+    y = succ / commit
+    y = y.ravel()
+    return int(all[0, -1]), int(all[0, -1] + all[1, -1]), y

+ 41 - 0
plot/refill/helper.py

@@ -0,0 +1,41 @@
+
+import re
+import logging
+
+logger = logging.getLogger('plot')
+
+def filter_chname(chnames):
+    if chnames is not None:
+        chnames = chnames.strip()
+
+    if chnames is None or len(chnames) == 0:
+        chnames = set()
+    else:
+        items = re.split(r',', chnames)
+        names = [name.strip() for name in items]
+        namelist = []
+        for name in names:
+            if len(name) > 0:
+                namelist.append(name)
+        chnames = set(namelist)
+    return chnames
+
+def filter_cardtype(card_types):
+    if card_types is not None:
+        card_types = card_types.strip()
+
+    if card_types is None or len(card_types) == 0:
+        result = {4, 5, 6} #默认话费类型
+    else:
+        items = re.split(r',', card_types)
+        stypes = [type.strip() for type in items]
+        types = []
+        for stype in stypes:
+            try:
+                type = int(stype)
+                types.append(type)
+            except Exception as ex:
+                logger.error(ex)
+
+        result = set(types)
+    return result

+ 12 - 0
plot/testNumpy.py

@@ -0,0 +1,12 @@
+import unittest
+import numpy as np
+
+
+class MyTestCase(unittest.TestCase):
+    def test_randn(self):
+        data = np.random.randn(2,3)
+        print(data)
+
+
+if __name__ == '__main__':
+    unittest.main()

+ 79 - 12
plot/testPlot.py

@@ -1,12 +1,15 @@
+import time
 import unittest
 import logging
 
+from refill import time_border
+
 logging.basicConfig(filename='/var/www/html/data/log/qreader.log', level=logging.DEBUG)
 log = logging.getLogger('reader')
 
 class MyTestCase(unittest.TestCase):
-    # __redis_host = '192.168.3.104'
-    __redis_host = '192.168.3.46'
+    __redis_host = '192.168.3.104'
+    # __redis_host = '192.168.3.46'
     def test_something(self):
         self.assertEqual(True, False)  # add assertion here
 
@@ -19,22 +22,48 @@ class MyTestCase(unittest.TestCase):
             log.error(ex)
         pass
 
+    def test_timestart(self):
+        stamp = int(time.time())
+        for i in range(300):
+            x = stamp + i
+            l = time_border(300,x,True)
+            r = time_border(300,x,False)
+            print('l=',l,'r=',r)
+
+    def testChannel(self):
+        from refill import ChannelReader
+        reader = ChannelReader()
+        days = reader.days()
+        print('days=',days)
+
+        for day in days:
+            x = reader.tuple_path(day, card_types={4, 5, 6})
+            x1 = reader.tuple_path(day, {'chizeng', 'ainika'}, {4, 5, 6})
+            x2 = reader.tuple_path(day, {'chizeng', 'ainika'}, {4, 5, 6}, 50)
+
+    def test_chpainter(self):
+        from refill import ChannelPainter
+
+        start_time = int(time.time()) - 2 * 86400 - 3600
+        end_time = int(time.time()) - 2 * 86400
+
+        # painter = ChannelPainter(start_time=start_time, end_time=end_time, interval=300, chnames={'chizeng', 'ainika'}, card_types={4, 5, 6})
+        painter = ChannelPainter(start_time=start_time, end_time=end_time, interval=300, chnames=set(), card_types={4, 5, 6})
+        painter.paint()
+
     def testDays(self):
         from refill import MerchantReader
+        from refill import ChannelReader
 
         try:
-            reader = MerchantReader()
-            days = reader.days()
-        except Exception as ex:
-            log.error(ex)
-        pass
+            chreader = ChannelReader()
+            chdays = chreader.days()
+            xlables = [time.strftime('%d-%H:%M:%S', time.localtime(d)) for d in chdays]
+            print(xlables)
 
-    def testDirs(self):
-        from refill import NetchkReader
 
-        try:
-            reader = NetchkReader()
-            paths = reader.dirs()
+            reader = MerchantReader()
+            days = reader.days()
         except Exception as ex:
             log.error(ex)
         pass
@@ -48,6 +77,44 @@ class MyTestCase(unittest.TestCase):
         except Exception as ex:
             print(ex)
 
+    def test_partial(self):
+        from functools import partial
+        add_five = partial()
+
+    def test_key(self):
+        def person(name, age, *, city, job):
+            print(name, age, city, job)
+
+        person('Jack', 24, city='Beijing', job='Engineer')
+        person('Jack', 24, 'Beijing', job='Engineer')
+        person('Jack', 24, 'Beijing', 'Engineer')
+
+    def test_none(self):
+        x = None
+        len = len(x)
+        print(len)
+
+    def test_set(self):
+        x = set([(4,50),(4,100)])
+        y = set([(4,50),(5,100)])
+        z = x | y
+        print(z)
+
+    def test_env(self):
+        import os
+        x = os.getenv("PYCHARM_DISPLAY_PORT", "-1")
+        print(x)
+
+    def test_matplot(self):
+        import matplotlib
+        x = matplotlib.__version__
+        print(x)
+
+    def test_time(self):
+        x = int(time.time())
+        print(x)
+
+
 
 if __name__ == '__main__':
     unittest.main()

+ 16 - 0
plot/test_h5py.py

@@ -0,0 +1,16 @@
+import unittest
+
+from .refill import open_hdf5
+
+
+
+class MyTestCase(unittest.TestCase):
+    def test_h5py(self):
+        file = '/var/www/html/data/stdata/channel.hdf5'
+        hfive = open_hdf5(file, False)
+        x = list(hfive.keys())
+        print(x)
+
+
+if __name__ == '__main__':
+    unittest.main()

+ 15 - 6
rdispatcher/proxy.php

@@ -144,9 +144,14 @@ class proxy
         {
             if (!$order->match_card_type()) {
                 return $this->onEerror($order, '卡类型和产品类型不符.');
-            } else {
+            }
+
+            $mch_amount = refill\RefillFactory::instance()->mch_amount($order);
+            if($mch_amount === false) {
+                return $this->onEerror($order, '没有协商商品购买价格.');
+            }
+            else {
                 refill\util::incr_user_commit($mchid, $order->card_type(), $order->spec(), $org_quality);
-                $mch_amount = refill\RefillFactory::instance()->mch_amount($order);
                 refill\util::monitor_submit($order->mchid(), $order->spec(), $order->card_type(), $mch_amount);
             }
         }
@@ -300,16 +305,20 @@ class proxy
 
         $mchid = $order->mchid();
         $mch_order = $order->mch_order();
-        $card_type = $order->card_type();
-        $spec = $order->spec();
         $org_quality = $order->org_quality();
 
         $refill_order = Model('refill_order');
         refill\util::push_queue_order($mchid,$mch_order,ORDER_STATE_SEND);
         $refill_order->edit_detail($mchid,$mch_order,['order_state' => ORDER_STATE_SEND]);
-        refill\util::incr_user_commit($mchid,$card_type,$spec,$org_quality);
+
         $mch_amount = refill\RefillFactory::instance()->mch_amount($order);
-        refill\util::monitor_submit($mchid, $order->spec(), $order->card_type(), $mch_amount);
+        if($mch_amount === false) {
+            return $this->onEerror($order, '没有协商商品购买价格.');
+        }
+        else {
+            refill\util::incr_user_commit($mchid, $order->card_type(), $order->spec(), $org_quality);
+            refill\util::monitor_submit($order->mchid(), $order->spec(), $order->card_type(), $mch_amount);
+        }
 
         [$errcode, $errmsg, $order_id, $neterr,$net_errno] = refill\RefillFactory::instance()->add($order);
         if($errcode !== true)

+ 28 - 0
test/TestRefill.php

@@ -2389,6 +2389,34 @@ class TestRefill extends TestCase
         $resp = $provider->notify($params);
     }
 
+    public function testXinruiheng()
+    {
+//        $provider = $this->getProvider('xinruiheng');
+//        $resp = $provider->balance();
+//        $resp = $provider->add(13699279618, 4, 30, ['order_sn' => $this->make_sn()]);
+//        $resp = $provider->query(['order_sn' => '39051656036848508036']);
+
+        $body = '{"orderid":"SPN22062410147271198","sporderid":"39051656036848508036","userid":"10002683","merchantsubmittime":"20220624101713","resultno":"9","parvalue":"30","remark1":"","payno":"","fundbalance":"-28.98","sign":"0BDCBB9BF688B35E55B1B16E88D5E5FD"}';
+        $params = json_decode($body, true);
+        $provider = $this->getProvider('xinruiheng','RefillCallBack');
+        $ret = $provider->verify($params);
+        $resp = $provider->notify($params);
+    }
+
+    public function testDezhi()
+    {
+//        $provider = $this->getProvider('dezhi');
+//        $resp = $provider->balance();
+//        $resp = $provider->add(18500608333, 5, 30, ['order_sn' => $this->make_sn()]);
+//        $resp = $provider->query(['order_sn' => '86811656400057476279']);
+
+        $body = '{"szOrderId":"61171656397023525645","fSalePrice":"28.59","szAgentId":"200002","nFlag":"3","szVerifyString":"1a56ff14e3edae6e526d0f8418050530","szPhoneNum":"18500608333","szRtnMsg":"","nDemo":"30"}';
+        $params = json_decode($body, true);
+        $provider = $this->getProvider('dezhi','RefillCallBack');
+        $ret = $provider->verify($params);
+        $resp = $provider->notify($params);
+    }
+
     public function testAmingjd()
     {
 //        $provider = new refill\amingjd\RefillPhone([]);

+ 12 - 0
test/TestRefillYl.php

@@ -762,4 +762,16 @@ class TestRefillYl extends TestCase
         $provider = $this->getProvider('huoshenguo_yd');
         $resp = $provider->balance();
     }
+
+    public function testXinruiheng()
+    {
+        $provider = $this->getProvider('xinruiheng');
+        $resp = $provider->balance();
+    }
+
+    public function testDezhi()
+    {
+        $provider = $this->getProvider('dezhi');
+        $resp = $provider->balance();
+    }
 }