mTimesConfig = []; $this->mInterceptConfig = []; $this->mGrossRatios = []; $this->mDetailRatios = []; $this->mMixedPrices = []; } public function load() { $this->load_retry(); $this->load_intercept(); } private function load_retry() { $isDay = functional::isDay(); $mch_configs = function ($isDay) { $result = []; $i = 0; while (true) { $start = $i * 100; $items = Model()->table('merchant')->where(['mchid' => ['gt', 0], 'merchant_state' => 1])->field('mchid,retry_times_cfg')->order('mchid asc')->limit("{$start},100")->select(); if (empty($items)) { break; } $i++; foreach ($items as $item) { $mchid = intval($item['mchid']); if ($mchid <= 0) continue; $retry_times_cfg = unserialize($item['retry_times_cfg']); if (empty($retry_times_cfg)) continue; $qualities = &$retry_times_cfg['qualities']; foreach ($qualities as $quality => $cfg) { if ($isDay) { $qualities[$quality]['secs'] = $cfg['day_secs']; } else { $qualities[$quality]['secs'] = $cfg['night_secs']; } } $result[$mchid] = $retry_times_cfg; } } return $result; }; $this->mTimesConfig = $mch_configs($isDay); } private function load_intercept() { $mch_configs = function () { $result = []; $i = 0; while (true) { $start = $i * 100; $items = Model()->table('merchant')->where(['mchid' => ['gt', 0], 'merchant_state' => 1])->field('mchid,intercept_cfg')->order('mchid asc')->limit("{$start},100")->select(); if (empty($items)) { break; } $i++; foreach ($items as $item) { $mchid = intval($item['mchid']); if ($mchid <= 0) continue; $cfg = unserialize($item['intercept_cfg']); if (empty($cfg)) continue; if (!empty($cfg['segment'])) { $segment = $cfg['segment']; $sitems = explode(',', $segment); $tmp = []; foreach ($sitems as $sitem) { $sitem = trim($sitem); if (!empty($sitem)) { $tmp[] = $sitem; } } if (!empty($tmp)) { $cfg['segment'] = implode('|', $tmp); } else { $cfg['segment'] = ''; } } $result[$mchid] = $cfg; } } return $result; }; $this->mInterceptConfig = $mch_configs(); } public function update($gross_ratios, $detail_ratios) { if (!empty($gross_ratios)) { $this->mGrossRatios = $gross_ratios; } if (!empty($detail_ratios)) { $this->mDetailRatios = $detail_ratios; } $this->mMchQTS = []; foreach ($detail_ratios as $key => $val) { [$mchid, $card_type, $spec] = explode('-', $key); $mchid = intval($mchid); $card_type = intval($card_type); $spec = intval($spec); $this->mMchQTS[$mchid][] = [$card_type, $spec]; } } public function setMixedPrice($mchid, $mixed_quality, $card_type, $spec, $prices) { $key = "{$card_type}-{$spec}"; $this->mMixedPrices[$mchid][$mixed_quality][$key] = $prices; } public function total($mchid, $qualities) { if (array_key_exists($mchid, $this->mTimesConfig)) { $items = $this->mTimesConfig[$mchid]['qualities']; $times = 0; $secs = 0; foreach ($items as $quality => $val) { if (!in_array($quality, $qualities, true)) { continue; } $times += $val['times'] ?? 1; $secs += $val['secs'] ?? 180; } return [true, $times, $secs]; } else { return [false, 0, 0]; } } public function times($mchid, $quality) { if (array_key_exists($mchid, $this->mTimesConfig)) { $items = $this->mTimesConfig[$mchid]['qualities'] ?? []; if (array_key_exists($quality, $items)) { return $items[$quality]['times']; } } return false; } public function seconds($mchid, $quality) { if (array_key_exists($mchid, $this->mTimesConfig)) { $items = $this->mTimesConfig[$mchid]['qualities'] ?? []; if (array_key_exists($quality, $items)) { return $items[$quality]['secs']; } } return false; } public function exist($mchid) { if (array_key_exists($mchid, $this->mTimesConfig)) { return true; } else { return false; } } //{\"10202\":{\"ratio\":0.65,\"period\":86400,\"profit_ratio\":0.002,\"profit_formula\":\"all\"} //to-do for test // private function lowest_ratio($mchid) // { // $lower_ratio = $this->mTimesConfig[$mchid]['lower_ratio'] ?? []; // if (empty($lower_ratio)) { // return [0.30, 86400]; // } else { // return [$lower_ratio['ratio'], $lower_ratio['period']]; // } // } // // private function profit_ratio($mchid) { // $profit_ratio = $this->mTimesConfig[$mchid]['profit_ratio'] ?? 0.002; // return $profit_ratio; // } private function lowest_ratio($mchid) { $lower_ratio = $this->mTimesConfig[$mchid]['lower_ratio'] ?? []; if (empty($lower_ratio)) { return [0.0, 3600]; } else { return [$lower_ratio['ratio'], $lower_ratio['period']]; } } private function profit_ratio($mchid) { $profit_ratio = $this->mTimesConfig[$mchid]['profit_ratio'] ?? 0.0; return $profit_ratio; } //[submit_count, $succ_count, $fail_count, $succ_ratio, $profit, $profit_ratio] private function gross_ratio($mchid) { $mratios = $this->mGrossRatios; if (array_key_exists($mchid, $mratios)) { return $mratios[$mchid]; } return [0, 0, 0, 0, 0, 0]; } //[submit_count, $succ_count, $fail_count, $succ_ratio, $profit, $profit_ratio] private function detail_ratio($mchid, $card_type, $spec) { $key = "{$mchid}-{$card_type}-{$spec}"; if (array_key_exists($key, $this->mDetailRatios)) { return $this->mDetailRatios[$key]; } return [0, 0, 0, 0, 0, 0]; } //return true 表示当前质量满足条件。 public function ratio_match($mchid, $org_quality, $card_type, $spec, $qualities) { if (count($qualities) <= 1) { return true; } $header = __METHOD__ . " mchid={$mchid} card_type={$card_type} spec={$spec}"; [$lowest_ratio, $period] = $this->lowest_ratio($mchid); [$submit_count, $succ_count, $fail_count, $gross_ratio, $profit, $profit_ratio] = $this->gross_ratio($mchid); Log::record("{$header} gross_ratio={$gross_ratio}, lower_ratio={$lowest_ratio}", Log::DEBUG); if (!PolicyUtil::mixed_quality($org_quality)) { if ($gross_ratio >= $lowest_ratio) { return true; } else { return false; } } elseif ($gross_ratio >= $lowest_ratio) { return $this->pre_checker($mchid, $card_type, $spec, $header, $lowest_ratio); } else { return $this->all_checker($mchid, $card_type, $spec, $header, $lowest_ratio); } } private function pre_checker($mchid, $card_type, $spec, $header, $lowest_ratio) { $ts_ratios = function ($mchid, $type_specs, $all_submit) { $result = []; if ($all_submit == 0) { $all_submit += 0.00001; } foreach ($type_specs as $item) { [$card_type, $spec] = $item; [$submit_count, $succ_count, $fail_count, $cur_ratio, $profit, $cur_pratio] = $this->detail_ratio($mchid, $card_type, $spec); $submit_ratio = $submit_count / $all_submit; $result[] = [$card_type, $spec, $submit_ratio, $cur_ratio, $cur_pratio]; } return $result; }; $spec_extract = function ($item) { [$card_type, $spec, $submit_ratio, $cur_ratio, $cur_pratio] = $item; return $spec; }; $ts_sorter_spec = function ($left, $right) use ($spec_extract) { $l = $spec_extract($left); $r = $spec_extract($right); if ($l > $r) return 1; elseif ($l < $r) return -1; else return 0; }; $ts_filter = function ($ts_ratios, $ratio, $lowest_ratio) { $result = []; $all = 0.00; foreach ($ts_ratios as $item) { [$card_type, $spec, $submit_ratio, $cur_ratio, $cur_pratio] = $item; $all += $submit_ratio * $cur_ratio; $r = $all / ($lowest_ratio + 0.00001); if ($r > $ratio) break; else { $result[] = "{$card_type}-{$spec}"; } } return $result; }; $lowest_pratio = $this->profit_ratio($mchid); [$all_submit, $succ_count, $fail_count, $gross_ratio, $profit, $cur_pratio] = $this->gross_ratio($mchid); if ($cur_pratio <= $lowest_pratio) { return true; } $type_specs = $this->mMchQTS[$mchid]; $ts_ratios = $ts_ratios($mchid, $type_specs, $all_submit); usort($ts_ratios, $ts_sorter_spec); $ratio = $gross_ratio / ($lowest_ratio + 0.00001); if ($ratio > 1.1) { return true; } else { $meet = $ts_filter($ts_ratios, $ratio, $lowest_ratio); $exist = array_key_exists("{$card_type}-{$spec}", $meet); return !$exist; } } private function all_checker($mchid, $card_type, $spec, $header, $lowest_ratio) { $lowest_pratio = $this->profit_ratio($mchid); [$submit_count, $succ_count, $fail_count, $gross_ratio, $profit, $gross_pratio] = $this->gross_ratio($mchid); //当前毛利润率小于最低利润率的时候,不可以补充了 if ($gross_pratio <= $lowest_pratio) { return true; } else { return false; } } public function need_intercept($mchid, $card_type, $card_state, $is_transfer, $card_no): bool { $start_with = function ($card_no, $segment) { $reg = "/^(?:{$segment})\d*$/"; if (preg_match($reg, $card_no, $matches)) { return true; } else { return false; } }; $mintercepts = $this->mInterceptConfig; if (array_key_exists($mchid, $mintercepts)) { $mintercepts = $mintercepts[$mchid]; if (!empty($mintercepts['card_states']) && in_array($card_state, $mintercepts['card_states'], true)) { return true; } if (!empty($mintercepts['card_types']) && in_array($card_type, $mintercepts['card_types'], true)) { return true; } if ($mintercepts['is_transfer'] && $is_transfer) { return true; } if (!empty($mintercepts['segment']) && $start_with($card_no, $mintercepts['segment'])) { return true; } } return false; } }