order_clear.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <?php
  2. namespace refill;
  3. use Log;
  4. use trans_wapper;
  5. use Exception;
  6. class order_clear
  7. {
  8. public function clear($start_date, $end_date, $start_line = 0)
  9. {
  10. $mchorder_gen = function ($start)
  11. {
  12. $end = $start + 3600;
  13. $cond = ['order_time&order_time' => ['_multi' => true, ['egt', $start], ['lt', $end]], 'inner_status' => 0];
  14. $i = 0;
  15. while (true)
  16. {
  17. $start = $i * 1000;
  18. $items = Model()->table('refill_order')
  19. ->field('mchid,mch_order,order_time')
  20. ->where($cond)
  21. ->order('order_time asc')->limit("{$start},1000")->select();
  22. $i++;
  23. if(empty($items)) break;
  24. foreach ($items as $item) {
  25. yield $item;
  26. }
  27. }
  28. };
  29. $mchorder_saver = function ($start,$file) use ($mchorder_gen)
  30. {
  31. if (file_exists($file)) {
  32. return true;
  33. }
  34. $fp = fopen($file,'w');
  35. if($fp === false) {
  36. Log::record("Cannot open file {$file} with write.",Log::ERR);
  37. return false;
  38. }
  39. $end = $start + 86400;
  40. for ($hour = $start; $hour < $end; $hour += 3600)
  41. {
  42. $orders = $mchorder_gen($hour);
  43. foreach ($orders as $order)
  44. {
  45. $ret = fputcsv($fp,$order);
  46. if($ret === false) {
  47. return false;
  48. }
  49. }
  50. }
  51. fclose($fp);
  52. return true;
  53. };
  54. $handle_days = function ($start_date, $end_date,$line = 0) use ($mchorder_saver)
  55. {
  56. for ($date = $start_date; $date < $end_date; $date += 86400)
  57. {
  58. $sdate = date('Y-m-d',$date);
  59. $morder_file = BASE_DATA_PATH . "/log/order/{$sdate}-morder.csv";
  60. $check_file = BASE_DATA_PATH . "/log/order/{$sdate}-check.csv";
  61. $refill_file = BASE_DATA_PATH . "/log/order/{$sdate}-refill.csv";
  62. $vr_file = BASE_DATA_PATH . "/log/order/{$sdate}-vr.csv";
  63. $ret = $mchorder_saver($date,$morder_file);
  64. if($ret === false) {
  65. Log::record("fetch or save {$sdate} mch_order fail.",Log::ERR);
  66. break;
  67. }
  68. $this->order_delete($morder_file,$check_file,$refill_file,$vr_file,$line);
  69. }
  70. };
  71. $handle_days($start_date, $end_date,$start_line);
  72. }
  73. private function order_delete($mch_file, $check_file, $refill_file, $vr_file, $line)
  74. {
  75. $rorder_getter = function ($mchid,$mch_order,$mod_refill)
  76. {
  77. $result = [];
  78. $i = 0;
  79. while(true)
  80. {
  81. $start = $i * 1000;
  82. $ritems = $mod_refill->table('refill_order')->field('*')
  83. ->where(['mchid' => $mchid, 'mch_order' => $mch_order])
  84. ->order('order_id asc')
  85. ->limit("{$start},1000")->master(true)->select();
  86. $i++;
  87. $result = array_merge($ritems,$result);
  88. if(count($ritems) < 1000) {
  89. break;
  90. }
  91. }
  92. return $result;
  93. };
  94. $vorder_getter = function ($order_ids,$mod_vr)
  95. {
  96. $result = [];
  97. $i = 0;
  98. while(true)
  99. {
  100. $start = $i * 1000;
  101. $vitems = $mod_vr->table('vr_order')->field('*')
  102. ->where(['order_id' => ['in', $order_ids]])
  103. ->order('order_id asc')
  104. ->limit("{$start},1000")->master(true)->select();
  105. $i++;
  106. $result = array_merge($vitems,$result);
  107. if(count($vitems) < 1000) {
  108. break;
  109. }
  110. }
  111. return $result;
  112. };
  113. $order_checker = function ($mchid,$mch_order,$frefill,$fvr) use($rorder_getter,$vorder_getter)
  114. {
  115. $mod_refill = Model('refill_order');
  116. $ritems = $rorder_getter($mchid,$mch_order,$mod_refill);
  117. if(empty($ritems)) {
  118. return [false, [], 'order count = 0'];
  119. }
  120. $oids = [];
  121. $inner_state = [0 => 0 ,1 => 0];
  122. $is_retrying = 0;
  123. $mch_notify_counts = 0;
  124. foreach ($ritems as $item)
  125. {
  126. $oids[] = $item['order_id'];
  127. $state = intval($item['inner_status']);
  128. $inner_state[$state] += 1;
  129. $mch_notify_counts += intval($item['mch_notify_state']) >= 1 ? 1 : 0;
  130. if($state === 0 && $is_retrying === 0) {
  131. $is_retrying = intval($item['is_retrying']);
  132. }
  133. }
  134. //检查inner_status=0唯一性。
  135. if($inner_state[0] != 1) {
  136. return [false, [], "inner_status=0 counts={$inner_state[0]}"];
  137. }
  138. if($mch_notify_counts === 0) {
  139. return [false, [], "not notify merchant"];
  140. }
  141. elseif($mch_notify_counts > 1) {
  142. return [false, [], "more than one order notify merchant"];
  143. }
  144. if($is_retrying) {
  145. return [false, [], 'is_retrying = 1'];
  146. }
  147. $mod_vr = Model('vr_order');
  148. $vitems = $vorder_getter($oids,$mod_vr);
  149. //检查refill 表记录和vr_order表记录是否一致
  150. if(count($ritems) !== count($vitems)) {
  151. return [false, [], 'refill count != vr count'];
  152. }
  153. $order_state = [];
  154. foreach ($vitems as $item)
  155. {
  156. $state = intval($item['order_state']);
  157. if(!array_key_exists($state,$order_state)) {
  158. $order_state[$state] = 0;
  159. }
  160. $order_state[$state] += 1;
  161. }
  162. //检查一种订单只能有,成功或者失败状态.
  163. $vcount = count($vitems);
  164. if($vcount != $order_state[ORDER_STATE_SUCCESS] + $order_state[ORDER_STATE_CANCEL]) {
  165. return [false, [], 'ORDER: SUCCESS + CANCEL != COUNT.'];
  166. }
  167. elseif($order_state[ORDER_STATE_SUCCESS] > 1) {
  168. //成功订单只能小于等于1,否则为错误
  169. return [false, [], 'ORDER:SUCCESS COUNT > 1'];
  170. }
  171. else
  172. {
  173. foreach ($ritems as $item) {
  174. fputcsv($frefill,$item);
  175. }
  176. foreach ($vitems as $item) {
  177. fputcsv($fvr,$item);
  178. }
  179. }
  180. $inner_oids = [];
  181. foreach ($ritems as $item)
  182. {
  183. $status = intval($item['inner_status']);
  184. if($status === 1) {
  185. $inner_oids[] = $item['order_id'];
  186. }
  187. }
  188. return [true,$inner_oids,''];
  189. };
  190. $delter = function ($order_ids,$order_time)
  191. {
  192. if(empty($order_ids)) return;
  193. try {
  194. $mod_refill = Model('refill_order');
  195. $mod_vr = Model('vr_order');
  196. $trans = new trans_wapper($mod_refill, __METHOD__);
  197. $mod_refill->table('refill_order')->where(['order_id' => ['in',$order_ids],'order_time' => $order_time])->delete();
  198. $mod_vr->table('vr_order')->where(['order_id' => ['in',$order_ids]])->delete();
  199. $trans->commit();
  200. } catch (Exception $e) {
  201. $trans->rollback();
  202. }
  203. };
  204. $file_opener = function ($mch_file, $check_file, $refill_file, $vr_file)
  205. {
  206. $fp = fopen($mch_file,'r');
  207. $fcheck = fopen($check_file,'a+');
  208. $frefill = fopen($refill_file,'a+');
  209. $fvr = fopen($vr_file,'a+');
  210. if($fp === false || $fcheck === false || $frefill === false || $fvr === false)
  211. {
  212. if($fp !== false) {
  213. fclose($fp);
  214. }
  215. if($fcheck !== false) {
  216. fclose($fcheck);
  217. }
  218. if($frefill !== false) {
  219. fclose($frefill);
  220. }
  221. if($fvr !== false) {
  222. fclose($fvr);
  223. }
  224. return [false,false,false,false,false];
  225. }
  226. else {
  227. return [true,$fp,$fcheck,$frefill,$fvr];
  228. }
  229. };
  230. $file_closer = function($files)
  231. {
  232. foreach ($files as $fp) {
  233. fclose($fp);
  234. }
  235. };
  236. $goto_line = function ($fp,$line)
  237. {
  238. $index = 0;
  239. while(!feof($fp) && $index < $line) {
  240. $items = fgetcsv($fp);
  241. $index += 1;
  242. }
  243. };
  244. $mch_order_reader = function($fp)
  245. {
  246. while(!feof($fp))
  247. {
  248. [$mchid, $mch_order, $order_time] = fgetcsv($fp);
  249. if(empty($mchid) || empty($mch_order)) continue;
  250. yield [$mchid,$mch_order,$order_time];
  251. }
  252. };
  253. $mod_refill_err = Model('refill_error');
  254. $err_recorder = function($fcheck,$mchid,$mch_order,$order_time,$err) use($mod_refill_err)
  255. {
  256. fputcsv($fcheck,[$mchid,$mch_order,$err]);
  257. $mod_refill_err->insert(['mchid' => $mchid, 'mch_order' => $mch_order, 'order_time' => $order_time, 'msg' => $err, 'add_time' => time()]);
  258. };
  259. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  260. [$succ,$fp,$fcheck,$frefill,$fvr] = $file_opener($mch_file, $check_file, $refill_file, $vr_file);
  261. if(!$succ) {
  262. return false;
  263. }
  264. $goto_line($fp,$line);
  265. $mch_orders = $mch_order_reader($fp);
  266. foreach ($mch_orders as $order)
  267. {
  268. [$mchid, $mch_order, $order_time] = $order;
  269. [$succ, $order_ids, $err] = $order_checker($mchid, $mch_order, $frefill, $fvr);
  270. if ($succ) {
  271. $delter($order_ids, $order_time);
  272. } else {
  273. $err_recorder($fcheck, $mchid, $mch_order, $order_time, $err);
  274. }
  275. }
  276. $file_closer([$fp,$fcheck,$frefill,$fvr]);
  277. return true;
  278. }
  279. }