refill_balance.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. <?php
  2. namespace statistics;
  3. use Log;
  4. use merchantModel;
  5. class refill_balance
  6. {
  7. const DaySecs = 86400;
  8. private $mMerchantNames = [];
  9. private $mProviderNames = [];
  10. private $mStoreidPID = [];
  11. private $mTimesTypes = [];
  12. public function __construct()
  13. {
  14. $mod_merchant = Model('merchant');
  15. $items = $mod_merchant->getMerchantList(['mchid' => ['gt',0]]);
  16. foreach ($items as $item) {
  17. $mchid = intval($item['mchid']);
  18. $this->mMerchantNames[$mchid] = !empty($item['company_name']) ? $item['company_name'] : $item['name'];
  19. }
  20. $items = Model('')->table('refill_provider,store')
  21. ->field('refill_provider.store_id,store.store_name,refill_provider.provider_id')->join('inner')
  22. ->on('store.store_id=refill_provider.store_id')
  23. ->where(['refill_provider.provider_id' => ['gt',0]])
  24. ->select();
  25. foreach ($items as $item) {
  26. $store_id = intval($item['store_id']);
  27. $this->mProviderNames[$store_id] = $item['store_name'];
  28. $this->mStoreidPID[$store_id] = intval($item['provider_id']);
  29. }
  30. $cfg_reader = function ($name, $field) {
  31. $data = rcache($name, 'refill-', $field);
  32. $val = $data[$field] ?? serialize([]);
  33. return unserialize($val);
  34. };
  35. $this->mTimesTypes['system'] = [0 => ['order_time']];
  36. $this->mTimesTypes['merchant'] = $cfg_reader('balance-cfg','merchant');
  37. $this->mTimesTypes['provider'] = $cfg_reader('balance-cfg','provider');
  38. }
  39. private function find_timetypes($type,$cid)
  40. {
  41. if(array_key_exists($type,$this->mTimesTypes))
  42. {
  43. if(array_key_exists($cid,$this->mTimesTypes[$type])) {
  44. return $this->mTimesTypes[$type][$cid];
  45. }
  46. }
  47. return [];
  48. }
  49. public function stat_all($end)
  50. {
  51. $mchids = function ()
  52. {
  53. $result = [];
  54. foreach ($this->mMerchantNames as $mchid => $name) {
  55. $result[] = $mchid;
  56. }
  57. return $result;
  58. };
  59. $store_ids = function ()
  60. {
  61. $result = [];
  62. foreach ($this->mProviderNames as $storeid => $name) {
  63. $result[] = $storeid;
  64. }
  65. return $result;
  66. };
  67. $types = ['system','merchant','provider'];
  68. foreach ($types as $type)
  69. {
  70. if ($type == 'system') {
  71. $cids = [0];
  72. } elseif ($type == 'merchant') {
  73. $cids = $mchids();
  74. } else {
  75. $cids = $store_ids();
  76. }
  77. foreach ($cids as $cid)
  78. {
  79. $time_types = $this->find_timetypes($type,$cid);
  80. foreach ($time_types as $time_type)
  81. {
  82. [$parent_id,$start,$confirmed] = $this->find_parent($type, $cid, $time_type);
  83. if($start == $end) {
  84. Log::record("type=$type cid=$cid, time_type=$time_type has been stat.",Log::DEBUG);
  85. continue;
  86. }
  87. if($parent_id > 0 && $confirmed == false) {
  88. Log::record("type=$type cid=$cid, time_type=$time_type had not confirmed.",Log::DEBUG);
  89. continue;
  90. }
  91. $ret = $this->add_balance($type, $cid, $start, $end, $time_type, $parent_id);
  92. if(!$ret) {
  93. Log::record("type=$type cid=$cid, time_type=$time_type fail",Log::ERR);
  94. }
  95. }
  96. }
  97. }
  98. }
  99. private function find_parent($type, $cid, $time_type)
  100. {
  101. $mod = Model('refill_balance');
  102. $cond = ['type' => $type, 'cid' => $cid, 'time_type' => $time_type];
  103. $item = $mod->field('balance_id,end_stamp,confirmed')->where($cond)->order('end_stamp desc')->find();
  104. $confirmed = boolval($item['confirmed']);
  105. return [intval($item['balance_id']), intval($item['end_stamp']), $confirmed];
  106. }
  107. private function refill_time_finder($type, $cid, $start,$end)
  108. {
  109. if ($start != 0) {
  110. return $start;
  111. }
  112. if ($type == 'system') {
  113. $mod_refill = Model('refill_order');
  114. $item = $mod_refill->table('refill_order')->field('order_id,order_time')
  115. ->where(['order_time' => ['lt', $end]])
  116. ->order('order_id asc')->find();
  117. $time = $item['order_time'] ?? $end;
  118. } elseif ($type == 'merchant') {
  119. $mod_refill = Model('refill_order');
  120. $item = $mod_refill->table('refill_order')->field('order_id,order_time')
  121. ->where(['mchid' => $cid, 'order_time' => ['lt', $end]])
  122. ->order('order_id asc')->find();
  123. $time = $item['order_time'] ?? $end;
  124. } else {
  125. $mod_refill = Model('vr_order');
  126. $item = $mod_refill->table('vr_order')->field('order_id,add_time')
  127. ->where(['store_id' => $cid,'add_time' => ['lt', $end]])
  128. ->order('order_id asc')->find();
  129. $time = $item['add_time'] ?? $end;
  130. }
  131. return $time;
  132. }
  133. private function merchant_paytime_finder($cid, $start, $end)
  134. {
  135. if ($start != 0) {
  136. return $start;
  137. }
  138. $mod_pay = Model();
  139. if ($cid == 0)
  140. {
  141. $item = $mod_pay->table('refill_evidence')
  142. ->field('min(add_time) as madd_time')
  143. ->where(['add_time' => ['lt', $end]])
  144. ->find();
  145. }
  146. else
  147. {
  148. $item = $mod_pay->table('refill_evidence')
  149. ->field('min(add_time) as madd_time')
  150. ->where(['mchid' => $cid,'add_time' => ['lt', $end]])
  151. ->find();
  152. }
  153. $time = $item['madd_time'] ?? $end;
  154. return $time;
  155. }
  156. private function provider_paytime_finder($cid, $start,$end)
  157. {
  158. if ($start != 0) {
  159. return $start;
  160. }
  161. $mod_pay = Model();
  162. if ($cid == 0)
  163. {
  164. $item = $mod_pay->table('provider_amount')
  165. ->field('min(add_time) as madd_time')
  166. ->where(['add_time' => ['lt', $end]])
  167. ->find();
  168. }
  169. else
  170. {
  171. $item = $mod_pay->table('provider_amount')
  172. ->field('min(add_time) as madd_time')
  173. ->where(['provider_id' => $cid,'add_time' => ['lt', $end]])
  174. ->find();
  175. }
  176. $time = $item['madd_time'] ?? $end;
  177. return $time;
  178. }
  179. private function balance_data($type, $cid, $start, $end, $time_type, $parent_balance, $remark)
  180. {
  181. $refill_time = $this->refill_time_finder($type, $cid, $start,$end);
  182. $order_stat = $this->order_stat($type, $cid, $refill_time, $end, $time_type);
  183. $transfer_calcer = function ($detail,$types = [])
  184. {
  185. $result = 0.0;
  186. foreach ($detail as $type => $amount)
  187. {
  188. if(empty($types)) {
  189. $result += $amount;
  190. }
  191. elseif(in_array($type,$types)) {
  192. $result += $amount;
  193. }
  194. }
  195. return $result;
  196. };
  197. if ($type == 'system') {
  198. $merchant_time = $this->merchant_paytime_finder($cid, $start,$end);
  199. $transfer_detail = $this->merchant_evidence_stat($cid, $merchant_time, $end);
  200. $provider_time = $this->provider_paytime_finder($cid, $start,$end);
  201. $out = $this->provider_amount_stat(0, $provider_time, $end);
  202. $cname = 'system';
  203. $pay_time = min($merchant_time,$provider_time);
  204. $in = $transfer_calcer($transfer_detail);
  205. $balance = ncPriceFormat($in - $out);
  206. } elseif ($type == 'merchant') {
  207. $pay_time = $this->merchant_paytime_finder($cid, $start, $end);
  208. $transfer_detail = $this->merchant_evidence_stat($cid, $pay_time, $end);
  209. $out = "0.0000";
  210. $cname = $this->mMerchantNames[$cid];
  211. $in = $transfer_calcer($transfer_detail);
  212. $balance = ncPriceFormat($in - $order_stat['mch_amounts']);
  213. } else {
  214. $pid = $this->mStoreidPID[$cid];
  215. $pay_time = $this->provider_paytime_finder($pid, $start,$end);
  216. $out = $this->provider_amount_stat($pid, $pay_time, $end);
  217. $cname = $this->mProviderNames[$cid];
  218. $balance = ncPriceFormat($out - $order_stat['channel_amounts']);
  219. }
  220. if ($start == 0) {
  221. $time = min($refill_time, $pay_time);
  222. $start = strtotime(date('Y-m-d', $time));
  223. }
  224. $accumuter = function ($parent_id)
  225. {
  226. if ($parent_id > 0) {
  227. $item = Model('refill_balance')->getBalance(['balance_id' => $parent_id]);
  228. } else {
  229. $item = [];
  230. }
  231. if (empty($item)) {
  232. return 0.00;
  233. } else {
  234. return $item['accumulate_balance'] + $item['balance'];
  235. }
  236. };
  237. $transfer_in = $transfer_calcer($transfer_detail,[merchantModel::type_mch_deposit,merchantModel::type_adm_deposit]);
  238. $except_amount = $transfer_calcer($transfer_detail,[merchantModel::type_adm_adjust,merchantModel::type_adm_finpos]);
  239. $refund_amount = $transfer_calcer($transfer_detail,[merchantModel::type_refund_back]);
  240. return ['parent_id' => $parent_balance, 'type' => $type, 'cid' => $cid, 'cname' => $cname,
  241. 'start_stamp' => $start, 'end_stamp' => $end, 'end_text' => date('Y-m-d H:i:s', $end),
  242. 'success_count' => intval($order_stat['order_counts']),
  243. 'refill_amount' => ncPriceFormat($order_stat['refill_amounts']),
  244. 'mch_amount' => ncPriceFormat($order_stat['mch_amounts']),
  245. 'channel_amount' => ncPriceFormat($order_stat['channel_amounts']),
  246. 'service_amount' => ncPriceFormat(0),
  247. 'profit_amount' => ncPriceFormat($order_stat['profit_amounts']),
  248. 'transfer_detail' => json_encode($transfer_detail),
  249. 'transfer_in' => ncPriceFormat($transfer_in),
  250. 'except_amount' => ncPriceFormat($except_amount),
  251. 'refund_amount' => ncPriceFormat($refund_amount),
  252. 'transfer_out' => ncPriceFormat($out),
  253. 'accumulate_balance' => ncPriceFormat($accumuter($parent_balance)),
  254. 'balance' => ncPriceFormat($balance),
  255. 'update_time' => time(),
  256. 'confirmed' => 0,
  257. 'time_type' => $time_type,
  258. 'remark' => $remark,
  259. ];
  260. }
  261. //system,provider,merchant
  262. //cid=0,mchid,store_id
  263. public function add_balance($type, $cid, $start, $end, $time_type, $parent_balance = 0, $remark = '系统自动生成')
  264. {
  265. $data = $this->balance_data($type, $cid, $start, $end, $time_type, $parent_balance, $remark);
  266. if(empty($data)) {
  267. return false;
  268. }
  269. $ret = Model('refill_balance')->insert($data);
  270. return $ret != false;
  271. }
  272. public function rebuild_balance($balance_id)
  273. {
  274. $balance = Model('refill_balance')->getBalance(['balance_id' => $balance_id]);
  275. if(empty($balance)) return false;
  276. $type = $balance['type'];
  277. $cid = $balance['cid'];
  278. $start = $balance['start_stamp'];
  279. $end = $balance['end_stamp'];
  280. $time_type = $balance['time_type'];
  281. $parent_balance = $balance['parent_id'];
  282. $remark = '记录重新生成';
  283. $data = $this->balance_data($type, $cid, $start, $end, $time_type, $parent_balance, $remark);
  284. if(empty($data)) {
  285. return false;
  286. }
  287. $ret = Model('refill_balance')->where(['balance_id' => $balance_id])->update($data);
  288. return $ret != false;
  289. }
  290. private function order_stat($type, $cid, $start, $end, $time_type)
  291. {
  292. $cond = [
  293. 'refill_order.inner_status' => 0,
  294. 'vr_order.order_state' => 40
  295. ];
  296. if($type == 'provider') {
  297. $cond['vr_order.store_id'] = $cid;
  298. }
  299. elseif($type == 'merchant') {
  300. $cond['refill_order.mchid'] = $cid;
  301. }
  302. if($time_type == 'notify_time') {
  303. $order_start = $start - 3 * self::DaySecs;
  304. if($order_start < 0) {
  305. $order_start = 0;
  306. }
  307. $cond["refill_order.notify_time&refill_order.notify_time"] = ['_multi' => true, ['egt', $start], ['lt', $end]];
  308. $cond["refill_order.order_time&refill_order.order_time"] = ['_multi' => true, ['egt', $order_start], ['lt', $end]];
  309. $cond["vr_order.add_time&vr_order.add_time"] = ['_multi' => true, ['egt', $order_start], ['lt', $end]];
  310. }
  311. else {
  312. $add_end = $end + 3 * self::DaySecs;
  313. $cond["refill_order.order_time&refill_order.order_time"] = ['_multi' => true, ['egt', $start], ['lt', $end]];
  314. $cond["vr_order.add_time&vr_order.add_time"] = ['_multi' => true, ['egt', $start], ['lt', $add_end]];
  315. }
  316. $record = Model('')->table('refill_order,vr_order')
  317. ->field('count(*) as order_counts, sum(refill_amount) as refill_amounts, sum(mch_amount) as mch_amounts, sum(channel_amount) as channel_amounts')
  318. ->join('inner')
  319. ->on('refill_order.order_id=vr_order.order_id')
  320. ->where($cond)
  321. ->find();
  322. if(!empty($record)) {
  323. $record['profit_amounts'] = ncPriceFormat($record['mch_amounts'] - $record['channel_amounts']);
  324. }
  325. return $record;
  326. }
  327. private function merchant_evidence_stat($cid, $start, $end)
  328. {
  329. $cond['status'] = merchantModel::status_passed;
  330. $cond['is_operation'] = merchantModel::oper_deposited;
  331. if($cid > 0) {
  332. $cond['mchid'] = $cid;
  333. }
  334. $cond['check_time&check_time'] = ['_multi' => true, ['egt', $start], ['lt', $end]];
  335. $items = Model('')->table('refill_evidence')
  336. ->field('add_type,sum(amount) as amounts')
  337. ->where($cond)
  338. ->group('add_type')
  339. ->select();
  340. $records = [];
  341. foreach ($items as $item) {
  342. $add_type = intval($item['add_type']);
  343. $amounts = floatval(ncPriceFormat($item['amounts']));
  344. $records[$add_type] = $amounts;
  345. }
  346. return $records;
  347. }
  348. private function provider_amount_stat($provider_id, $start, $end)
  349. {
  350. if($provider_id > 0) {
  351. $cond['provider_id'] = $provider_id;
  352. }
  353. $cond['add_time&add_time'] = ['_multi' => true, ['egt', $start], ['lt', $end]];
  354. $record = Model('')->table('provider_amount')
  355. ->field('sum(amount) as amounts')
  356. ->where($cond)
  357. ->find();
  358. return ncPriceFormat($record['amounts']);
  359. }
  360. }