mratio_controlex.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. <?php
  2. namespace refill;
  3. use Log;
  4. class mratio_controlex
  5. {
  6. private $mTimesConfig; //对应refill.ini 配置文件数据
  7. private $mInterceptConfig;
  8. private $mGrossRatios;
  9. private $mDetailRatios;
  10. private $mMchQTS;
  11. private $mMixedPrices;
  12. public function __construct()
  13. {
  14. $this->mTimesConfig = [];
  15. $this->mInterceptConfig = [];
  16. $this->mGrossRatios = [];
  17. $this->mDetailRatios = [];
  18. $this->mMixedPrices = [];
  19. }
  20. public function load()
  21. {
  22. $this->load_retry();
  23. $this->load_intercept();
  24. }
  25. private function load_retry()
  26. {
  27. $isDay = functional::isDay();
  28. $mch_configs = function ($isDay) {
  29. $result = [];
  30. $i = 0;
  31. while (true) {
  32. $start = $i * 100;
  33. $items = Model()->table('merchant')->where(['mchid' => ['gt', 0], 'merchant_state' => 1])->field('mchid,retry_times_cfg')->order('mchid asc')->limit("{$start},100")->select();
  34. if (empty($items)) {
  35. break;
  36. }
  37. $i++;
  38. foreach ($items as $item) {
  39. $mchid = intval($item['mchid']);
  40. if ($mchid <= 0) continue;
  41. $retry_times_cfg = unserialize($item['retry_times_cfg']);
  42. if (empty($retry_times_cfg)) continue;
  43. $qualities = &$retry_times_cfg['qualities'];
  44. foreach ($qualities as $quality => $cfg) {
  45. if ($isDay) {
  46. $qualities[$quality]['secs'] = $cfg['day_secs'];
  47. } else {
  48. $qualities[$quality]['secs'] = $cfg['night_secs'];
  49. }
  50. }
  51. $result[$mchid] = $retry_times_cfg;
  52. }
  53. }
  54. return $result;
  55. };
  56. $this->mTimesConfig = $mch_configs($isDay);
  57. }
  58. private function load_intercept()
  59. {
  60. $mch_configs = function () {
  61. $result = [];
  62. $i = 0;
  63. while (true) {
  64. $start = $i * 100;
  65. $items = Model()->table('merchant')->where(['mchid' => ['gt', 0], 'merchant_state' => 1])->field('mchid,intercept_cfg')->order('mchid asc')->limit("{$start},100")->select();
  66. if (empty($items)) {
  67. break;
  68. }
  69. $i++;
  70. foreach ($items as $item) {
  71. $mchid = intval($item['mchid']);
  72. if ($mchid <= 0) continue;
  73. $cfg = unserialize($item['intercept_cfg']);
  74. if (empty($cfg)) continue;
  75. if (!empty($cfg['segment'])) {
  76. $segment = $cfg['segment'];
  77. $sitems = explode(',', $segment);
  78. $tmp = [];
  79. foreach ($sitems as $sitem) {
  80. $sitem = trim($sitem);
  81. if (!empty($sitem)) {
  82. $tmp[] = $sitem;
  83. }
  84. }
  85. if (!empty($tmp)) {
  86. $cfg['segment'] = implode('|', $tmp);
  87. } else {
  88. $cfg['segment'] = '';
  89. }
  90. }
  91. $result[$mchid] = $cfg;
  92. }
  93. }
  94. return $result;
  95. };
  96. $this->mInterceptConfig = $mch_configs();
  97. }
  98. public function update($gross_ratios, $detail_ratios)
  99. {
  100. if (!empty($gross_ratios)) {
  101. $this->mGrossRatios = $gross_ratios;
  102. }
  103. if (!empty($detail_ratios)) {
  104. $this->mDetailRatios = $detail_ratios;
  105. }
  106. $this->mMchQTS = [];
  107. foreach ($detail_ratios as $key => $val) {
  108. [$mchid, $card_type, $spec] = explode('-', $key);
  109. $mchid = intval($mchid);
  110. $card_type = intval($card_type);
  111. $spec = intval($spec);
  112. $this->mMchQTS[$mchid][] = [$card_type, $spec];
  113. }
  114. }
  115. public function setMixedPrice($mchid, $mixed_quality, $card_type, $spec, $prices)
  116. {
  117. $key = "{$card_type}-{$spec}";
  118. $this->mMixedPrices[$mchid][$mixed_quality][$key] = $prices;
  119. }
  120. public function total($mchid, $qualities)
  121. {
  122. if (array_key_exists($mchid, $this->mTimesConfig)) {
  123. $items = $this->mTimesConfig[$mchid]['qualities'];
  124. $times = 0;
  125. $secs = 0;
  126. foreach ($items as $quality => $val) {
  127. if (!in_array($quality, $qualities, true)) {
  128. continue;
  129. }
  130. $times += $val['times'] ?? 1;
  131. $secs += $val['secs'] ?? 180;
  132. }
  133. return [true, $times, $secs];
  134. } else {
  135. return [false, 0, 0];
  136. }
  137. }
  138. public function times($mchid, $quality)
  139. {
  140. if (array_key_exists($mchid, $this->mTimesConfig)) {
  141. $items = $this->mTimesConfig[$mchid]['qualities'] ?? [];
  142. if (array_key_exists($quality, $items)) {
  143. return $items[$quality]['times'];
  144. }
  145. }
  146. return false;
  147. }
  148. public function seconds($mchid, $quality)
  149. {
  150. if (array_key_exists($mchid, $this->mTimesConfig)) {
  151. $items = $this->mTimesConfig[$mchid]['qualities'] ?? [];
  152. if (array_key_exists($quality, $items)) {
  153. return $items[$quality]['secs'];
  154. }
  155. }
  156. return false;
  157. }
  158. public function exist($mchid)
  159. {
  160. if (array_key_exists($mchid, $this->mTimesConfig)) {
  161. return true;
  162. } else {
  163. return false;
  164. }
  165. }
  166. //{\"10202\":{\"ratio\":0.65,\"period\":86400,\"profit_ratio\":0.002,\"profit_formula\":\"all\"}
  167. //to-do for test
  168. // private function lowest_ratio($mchid)
  169. // {
  170. // $lower_ratio = $this->mTimesConfig[$mchid]['lower_ratio'] ?? [];
  171. // if (empty($lower_ratio)) {
  172. // return [0.30, 86400];
  173. // } else {
  174. // return [$lower_ratio['ratio'], $lower_ratio['period']];
  175. // }
  176. // }
  177. //
  178. // private function profit_ratio($mchid) {
  179. // $profit_ratio = $this->mTimesConfig[$mchid]['profit_ratio'] ?? 0.002;
  180. // return $profit_ratio;
  181. // }
  182. private function lowest_ratio($mchid)
  183. {
  184. $lower_ratio = $this->mTimesConfig[$mchid]['lower_ratio'] ?? [];
  185. if (empty($lower_ratio)) {
  186. return [0.0, 3600];
  187. } else {
  188. return [$lower_ratio['ratio'], $lower_ratio['period']];
  189. }
  190. }
  191. private function profit_ratio($mchid)
  192. {
  193. $profit_ratio = $this->mTimesConfig[$mchid]['profit_ratio'] ?? 0.0;
  194. return $profit_ratio;
  195. }
  196. //[submit_count, $succ_count, $fail_count, $succ_ratio, $profit, $profit_ratio]
  197. private function gross_ratio($mchid)
  198. {
  199. $mratios = $this->mGrossRatios;
  200. if (array_key_exists($mchid, $mratios)) {
  201. return $mratios[$mchid];
  202. }
  203. return [0, 0, 0, 0, 0, 0];
  204. }
  205. //[submit_count, $succ_count, $fail_count, $succ_ratio, $profit, $profit_ratio]
  206. private function detail_ratio($mchid, $card_type, $spec)
  207. {
  208. $key = "{$mchid}-{$card_type}-{$spec}";
  209. if (array_key_exists($key, $this->mDetailRatios)) {
  210. return $this->mDetailRatios[$key];
  211. }
  212. return [0, 0, 0, 0, 0, 0];
  213. }
  214. //return true 表示当前质量满足条件。
  215. public function ratio_match($mchid, $org_quality, $card_type, $spec, $qualities)
  216. {
  217. if (count($qualities) <= 1) {
  218. return true;
  219. }
  220. $header = __METHOD__ . " mchid={$mchid} card_type={$card_type} spec={$spec}";
  221. [$lowest_ratio, $period] = $this->lowest_ratio($mchid);
  222. [$submit_count, $succ_count, $fail_count, $gross_ratio, $profit, $profit_ratio] = $this->gross_ratio($mchid);
  223. Log::record("{$header} gross_ratio={$gross_ratio}, lower_ratio={$lowest_ratio}", Log::DEBUG);
  224. if (!PolicyUtil::mixed_quality($org_quality)) {
  225. if ($gross_ratio >= $lowest_ratio) {
  226. return true;
  227. } else {
  228. return false;
  229. }
  230. } elseif ($gross_ratio >= $lowest_ratio) {
  231. return $this->pre_checker($mchid, $card_type, $spec, $header, $lowest_ratio);
  232. } else {
  233. return $this->all_checker($mchid, $card_type, $spec, $header, $lowest_ratio);
  234. }
  235. }
  236. private function pre_checker($mchid, $card_type, $spec, $header, $lowest_ratio)
  237. {
  238. $ts_ratios = function ($mchid, $type_specs, $all_submit)
  239. {
  240. $result = [];
  241. if ($all_submit == 0) {
  242. $all_submit += 0.00001;
  243. }
  244. foreach ($type_specs as $item) {
  245. [$card_type, $spec] = $item;
  246. [$submit_count, $succ_count, $fail_count, $cur_ratio, $profit, $cur_pratio] = $this->detail_ratio($mchid, $card_type, $spec);
  247. $submit_ratio = $submit_count / $all_submit;
  248. $result[] = [$card_type, $spec, $submit_ratio, $cur_ratio, $cur_pratio];
  249. }
  250. return $result;
  251. };
  252. $spec_extract = function ($item) {
  253. [$card_type, $spec, $submit_ratio, $cur_ratio, $cur_pratio] = $item;
  254. return $spec;
  255. };
  256. $ts_sorter_spec = function ($left, $right) use ($spec_extract)
  257. {
  258. $l = $spec_extract($left);
  259. $r = $spec_extract($right);
  260. if ($l > $r)
  261. return 1;
  262. elseif ($l < $r)
  263. return -1;
  264. else
  265. return 0;
  266. };
  267. $ts_filter = function ($ts_ratios, $ratio, $lowest_ratio)
  268. {
  269. $result = [];
  270. $all = 0.00;
  271. foreach ($ts_ratios as $item)
  272. {
  273. [$card_type, $spec, $submit_ratio, $cur_ratio, $cur_pratio] = $item;
  274. $all += $submit_ratio * $cur_ratio;
  275. $r = $all / ($lowest_ratio + 0.00001);
  276. if ($r > $ratio)
  277. break;
  278. else {
  279. $result[] = "{$card_type}-{$spec}";
  280. }
  281. }
  282. return $result;
  283. };
  284. $lowest_pratio = $this->profit_ratio($mchid);
  285. [$all_submit, $succ_count, $fail_count, $gross_ratio, $profit, $cur_pratio] = $this->gross_ratio($mchid);
  286. if ($cur_pratio <= $lowest_pratio) {
  287. return true;
  288. }
  289. $type_specs = $this->mMchQTS[$mchid];
  290. $ts_ratios = $ts_ratios($mchid, $type_specs, $all_submit);
  291. usort($ts_ratios, $ts_sorter_spec);
  292. $ratio = $gross_ratio / ($lowest_ratio + 0.00001);
  293. if ($ratio > 1.1) {
  294. return true;
  295. } else {
  296. $meet = $ts_filter($ts_ratios, $ratio, $lowest_ratio);
  297. $exist = array_key_exists("{$card_type}-{$spec}", $meet);
  298. return !$exist;
  299. }
  300. }
  301. private function all_checker($mchid, $card_type, $spec, $header, $lowest_ratio)
  302. {
  303. $lowest_pratio = $this->profit_ratio($mchid);
  304. [$submit_count, $succ_count, $fail_count, $gross_ratio, $profit, $gross_pratio] = $this->gross_ratio($mchid);
  305. //当前毛利润率小于最低利润率的时候,不可以补充了
  306. if ($gross_pratio <= $lowest_pratio) {
  307. return true;
  308. } else {
  309. return false;
  310. }
  311. }
  312. public function need_intercept($mchid, $card_type, $card_state, $is_transfer, $card_no): bool
  313. {
  314. $start_with = function ($card_no, $segment) {
  315. $reg = "/^(?:{$segment})\d*$/";
  316. if (preg_match($reg, $card_no, $matches)) {
  317. return true;
  318. } else {
  319. return false;
  320. }
  321. };
  322. $mintercepts = $this->mInterceptConfig;
  323. if (array_key_exists($mchid, $mintercepts)) {
  324. $mintercepts = $mintercepts[$mchid];
  325. if (!empty($mintercepts['card_states']) && in_array($card_state, $mintercepts['card_states'], true)) {
  326. return true;
  327. }
  328. if (!empty($mintercepts['card_types']) && in_array($card_type, $mintercepts['card_types'], true)) {
  329. return true;
  330. }
  331. if ($mintercepts['is_transfer'] && $is_transfer) {
  332. return true;
  333. }
  334. if (!empty($mintercepts['segment']) && $start_with($card_no, $mintercepts['segment'])) {
  335. return true;
  336. }
  337. }
  338. return false;
  339. }
  340. }