time()], ''); return [true, 0]; } else { $latest = current($result); $cur = time(); $success = ($cur - $latest) > 2; if ($success) { wcache("card_expired", [$card_no => time()], ''); } return [$success, $latest + 2 - $cur]; } } else { return [true, 0]; } } public static function can_commit($card_no, $card_type) { if ($card_type == mtopcard\SinopecCard || $card_type == mtopcard\PetroChinaCard) { $result = rcache('card_expired', '', "{$card_no}"); if (empty($result)) { wcache("card_expired", [$card_no => time()], ''); return [true, 0]; } else { $latest = current($result); $cur = time(); $lowest = 30; if ($cur > $latest && ($cur - $latest) >= $lowest) { wcache("card_expired", [$card_no => time()], ''); return [true, 0]; } else { wcache("card_expired", [$card_no => $latest + $lowest], ''); return [false, $latest + $lowest - $cur]; } } } else { return [true, 0]; } } static function write_card($card_no, $card_type,$bind_phone) { if(empty($bind_phone)) { return false; } if ($card_type !== mtopcard\SinopecCard && $card_type !== mtopcard\PetroChinaCard) { return false; } $mobile_types = [mtopcard\ChinaMobileCard,mtopcard\ChinaUnicomCard,mtopcard\ChinaTelecomCard]; $ctype = mtopcard\simple_card_type($bind_phone); if (!in_array($ctype,$mobile_types)) { return false; } $mod_topcard = Model('topcard'); $ret = $mod_topcard->get_card($card_no); if (empty($ret)) { $mod_topcard->add($card_no, $card_type, time(), $bind_phone); } else { $mod_topcard->edit($card_no,$bind_phone); } dcache($card_no, 'cardrefill-'); return true; } static function read_card($card_no, $card_type = 0) { if (empty($card_no)) return false; $data = rcache($card_no, 'cardrefill-'); if (empty($data)) { $mod_topcard = Model('topcard'); $ret = $mod_topcard->get_card($card_no); if (empty($ret)) { if ($card_type === 0) { $card_type = mtopcard\card_type($card_no,$regin_no); } $bind_phone = util::make_mobile(); $mod_topcard->add($card_no, $card_type, time(), $bind_phone); $data['bind_phone'] = $bind_phone; $data['refill_time'] = time(); $data['times'] = 0; $data['black_card'] = 0; wcache($card_no, $data, 'cardrefill-'); } else { $val = $ret[0]; $data['bind_phone'] = $val['bind_phone']; $data['black_card'] = $val['black_card']; $data['refill_time'] = time(); $data['times'] = 0; } } //之前没加black_card处理,这个字段不存在. if (!array_key_exists('black_card', $data)) { $data['black_card'] = 0; } return $data; } static function inc_card($card_no, $card_info) { $card_info['times'] += 1; $card_info['refill_time'] = time(); wcache($card_no, $card_info, 'cardrefill-'); } public static function del_card($card_no) { dcache($card_no, 'cardrefill-'); } public static function set_black($card_no) { if (empty($card_no)) return false; $card_info = util::read_card($card_no); if (!empty($card_info)) { $card_info['black_card'] = 1; $mod_topcard = Model('topcard'); $mod_topcard->table('topcard')->where(['card_no' => $card_no])->update(['black_card' => 1]); wcache($card_no, $card_info, 'cardrefill-'); return true; } else { return false; } } private static function black_order($order_sn, $msg) { static $errMsgs = ["只能给主卡且卡状态正常的加油卡充值", "加油卡卡号错误或不支持"]; if (empty($msg)) return false; if (in_array($msg, $errMsgs)) { $refill = Model('refill_order'); $order = $refill->getOrderInfo(['order_sn' => $order_sn]); if (empty($order)) return false; $card_no = $order['card_no']; return util::set_black($card_no); } } public static function black_from_log($file_name) { $fn = fopen($file_name, "r"); if (empty($fn)) { Log::record("Open File {$file_name} error.", Log::ERR); return false; } else { Log::record("{$file_name} start woring", Log::DEBUG); } $errs = []; while (!feof($fn)) { $line = trim(fgets($fn)); $ret = preg_match('/[\w\W]+"channelOrderNumber":"(?P[^"]+)"[\w\W]+"message":"(?P[\x{4e00}-\x{9fa5}]+)"[\w\W]+"status":109/u', $line, $matches); if ($ret) { $order_sn = $matches['order_sn']; $message = $matches['message']; self::black_order($order_sn, $message); $errs[$message] = empty($errs[$message]) ? 1 : $errs[$message] + 1; } } foreach ($errs as $msg => $count) { Log::record("msg:{$msg} count:{$count}", Log::DEBUG); } fclose($fn); return true; } public static function async_add($params, $period = 10) { try { QueueClient::async_push("AysncAddDispatcher", ['method' => 'add', 'params' => $params], $period); return true; } catch (Exception $ex) { return false; } } public static function async_notify($chname,$data, $period) { try { QueueClient::async_push("AysncAddDispatcher", ['method' => 'notify', 'params' => ['channel' => $chname, 'params' => $data]], $period); return true; } catch (Exception $ex) { return false; } } public static function push_add($params) { try { $ret = self::push_queue('add', $params); return $ret !== false; } catch (Exception $ex) { return false; } } public static function push_add_zero($params) { try { $ret = self::push_queue('add_zero', $params); return $ret !== false; } catch (Exception $ex) { return false; } } public static function push_addthird($params) { try { $ret = self::push_queue('addthird', $params); return $ret !== false; } catch (Exception $ex) { return false; } } public static function push_notify($chname, $params) { try { $ret = self::push_queue('notify', ['channel' => $chname, 'params' => $params]); return $ret !== false; } catch (Exception $ex) { return false; } } public static function push_notify_merchant($order_id, $manual) { try { $ret = self::push_queue('notify_mechant', ['order_id' => $order_id, 'manual' => $manual]); return $ret !== false; } catch (Exception $ex) { return false; } } public static function push_query($order_id) { try { $ret = self::push_queue('query', ['order_id' => $order_id]); return $ret !== false; } catch (Exception $ex) { return false; } } public static function push_auto_query($order_id,$query_times) { try { $ret = self::push_queue('query_auto', ['order_id' => $order_id,'query_times' => $query_times]); return $ret !== false; } catch (Exception $ex) { return false; } } public static function push_query_net($order_id) { try { $ret = self::push_queue('query_net', ['order_id' => $order_id]); return $ret !== false; } catch (Exception $ex) { return false; } } public static function manual_success($order_id) { try { $ret = self::push_queue('manual_success', ['order_id' => $order_id]); return $ret !== false; } catch (Exception $ex) { return false; } } public static function manual_cancel($order_id) { try { $ret = self::push_queue('manual_cancel', ['order_id' => $order_id]); return $ret !== false; } catch (Exception $ex) { return false; } } public static function push_queue($method, $value) { if (defined('USE_COROUTINE') && USE_COROUTINE && defined('COROUTINE_HOOK_TCP') && COROUTINE_HOOK_TCP) { $queue_name = 'QUEUE_DISPATCHER_CO'; $ins = Cache::getInstance('cacheredis'); return $ins->lpush($queue_name, serialize([$method => $value])); } else { return queue\DispatcherClient::instance()->push($method,$value); } } public static function dispatcher_queue_length() { $ins = Cache::getInstance('cacheredis'); return $ins->lLen('QUEUE_DISPATCHER_CO'); } public static function monitor_queue_length() { $ins = Cache::getInstance('cacheredis'); return $ins->lLen('REFILL_MONITOR_QUEUE'); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static function monitor_submit($mchid, $spec, $card_type, $mch_amount, $time) { $time = intval($time); queue\MonitorClient::instance()->onSubmit($mchid,$time,$spec,$card_type,$mch_amount); } public static function monitor_callback($mchid,$spec,$card_type,$mch_amount,$channel_amount,$succ,$time) { queue\MonitorClient::instance()->onCallback($mchid,$time,$spec,$card_type,floatval($mch_amount),floatval($channel_amount),$succ); } public static function monitor_netchk($chname,$succ) { queue\MonitorClient::instance()->onNetCheck($chname, time(),$succ); } public static function monitor_commit($chname, $spec, $card_type, $channel_amount,$commit_time) { queue\MonitorClient::instance()->onCommit($chname, $commit_time, $spec, $card_type, $channel_amount); } public static function monitor_notify($chname, $spec, $card_type, $channel_amount, $period, $succ,$commit_time,$mch_amount) { if ($succ) { $mch_amount = floatval($mch_amount); } else { $mch_amount = 0; } queue\MonitorClient::instance()->onNotify($chname, $commit_time, $spec, $card_type, floatval($channel_amount), $period, $succ,$mch_amount); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static function onEventBeforeSubmit(order $order) : bool { $card_type = $order->card_type(); Log::record("onEvent before submit {$order->unique_id()} card_type=$card_type",Log::DEBUG); return EventManager::instance()->onBeforeSubmit($order); } public static function onEventSubmit(order $order) { Log::record("onEvent submit {$order->unique_id()}",Log::DEBUG); EventManager::instance()->onSubmit($order); } public static function onEventBeforeCommit(order $order, $ch_name): bool { Log::record("onEvent before commit uid={$order->unique_id()} channel=$ch_name", Log::DEBUG); return EventManager::instance()->onBeforeCommit($order,$ch_name); } public static function onEventCommit(order $order, $ch_name) { Log::record("onEvent commit uid={$order->unique_id()} channel=$ch_name", Log::DEBUG); EventManager::instance()->onCommit($order,$ch_name); } public static function onEventNeterror(order $order, $ch_name) { Log::record("onEvent neterror uid={$order->unique_id()} channel=$ch_name", Log::DEBUG); EventManager::instance()->onNeterror($order,$ch_name); } public static function onEventNotify($refill_info, $order_info, $success) { $uid = "{$refill_info['mchid']}-{$refill_info['mch_order']}"; $ch_name = $refill_info['channel_name']; Log::record("onEvent notify uid=$uid channel=$ch_name success=$success", Log::DEBUG); EventManager::instance()->onNotify($refill_info, $order_info, $success); } public static function onEventComplete($refill_info, $order_info, $success) { $uid = "{$refill_info['mchid']}-{$refill_info['mch_order']}"; $ch_name = $refill_info['channel_name']; Log::record("onEvent complete uid=$uid channel=$ch_name success=$success", Log::DEBUG); EventManager::instance()->onComplete($refill_info, $order_info, $success); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static function set_order_channels($mchid,$mchorder,$datas) { $ins = Cache::getInstance('cacheredis'); $name = 'order_channels'; $key = "{$mchid}-{$mchorder}"; $ins->hset($name, '', [$key => serialize($datas)]); } public static function get_order_channels($mchid, $mchorder) { //old-name oil_exclude_channels $ins = Cache::getInstance('cacheredis'); $name = 'order_channels'; $key = "{$mchid}-{$mchorder}"; $chnames = $ins->hget($name, '', $key); $chnames = unserialize($chnames); if(is_array($chnames)) { return $chnames; } else { return []; } } public static function del_order_channels($mchid, $mchorder) { $ins = Cache::getInstance('cacheredis'); $name = 'order_channels'; $key = "{$mchid}-{$mchorder}"; $ins->hdel($name, '', $key); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static function set_cancel_order($mchid,$mch_order) { $ins = Cache::getInstance('cacheredis'); $name = 'order_cancel_hash'; $key = "{$mchid}-{$mch_order}"; $ins->hset($name, '', [$key=> 1]); } public static function query_cancel_order($mchid,$mch_order) { $ins = Cache::getInstance('cacheredis'); $name = 'order_cancel_hash'; $key = "{$mchid}-{$mch_order}"; $value = $ins->hget($name,'',$key); return $value; } public static function del_cancel_order($mchid,$mch_order) { $ins = Cache::getInstance('cacheredis'); $name = 'order_cancel_hash'; $key = "{$mchid}-{$mch_order}"; $ins->hdel($name, $key); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static function set_next_order($mchid,$mch_order) { $ins = Cache::getInstance('cacheredis'); $name = 'order_next_hash'; $key = "{$mchid}-{$mch_order}"; $ins->hset($name, '', [$key=> 1]); } public static function query_next_order($mchid,$mch_order) { $ins = Cache::getInstance('cacheredis'); $name = 'order_next_hash'; $key = "{$mchid}-{$mch_order}"; $value = $ins->hget($name,'',$key); return $value; } public static function del_next_order($mchid,$mch_order) { $ins = Cache::getInstance('cacheredis'); $name = 'order_success_hash'; $key = "{$mchid}-{$mch_order}"; $ins->hdel($name, $key); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static function merchant_debt_stoped($mchid) { if($mchid > 0) { $ret = rcache('merchant-debt-judge', 'refill-',"{$mchid}"); if(empty($ret)) { return false; } $stoped = intval($ret[$mchid]); return ($stoped === 1); } else { return false; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static function loop_order_inc($card_no, $spec) { if(defined('COMPANY_NAME') && in_array(COMPANY_NAME,['ZY_COMPANY'])) { $ins = Cache::getInstance('cacheredis'); $name = 'loop_order_check_query'; $key = "{$card_no}-{$spec}"; $count = $ins->hget($name, '', $key); $count = intval($count); if($count < 0) { $ins->hset($name, '', [$key => 0]); return false; } elseif($count === 0) { $ins->hIncrBy($name, $key, 1); return false; } else { $ins->hIncrBy($name, $key, 1); return true; } } else { return false; } } public static function loop_order_dec($card_no, $spec) { if (defined('COMPANY_NAME') && in_array(COMPANY_NAME, ['ZY_COMPANY'])) { $ins = Cache::getInstance('cacheredis'); $name = 'loop_order_check_query'; $spec = intval($spec); $key = "{$card_no}-{$spec}"; $ins->hIncrBy($name, $key, -1); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static function push_queue_order($mchid,$mch_order,$order_state) { if(empty($mch_order)) return; $ins = Cache::getInstance('cacheredis'); $name = 'merchant_order_query'; $key = "{$mchid}-{$mch_order}"; $ins->hset($name, '', [$key => $order_state]); } public static function del_queue_order($mchid,$mch_order) { if(empty($mch_order)) return; $ins = Cache::getInstance('cacheredis'); $name = 'merchant_order_query'; $key = "{$mchid}-{$mch_order}"; $ret = $ins->hdel($name, '', $key); } public static function pop_queue_order($mchid,$mch_order,$order_time = 0) { util::del_order_channels($mchid,$mch_order); Model('refill_order')->partition(util::part_refill($order_time))->edit_detail($mchid,$mch_order,['order_state' => ORDER_STATE_HANDLED]); $ins = Cache::getInstance('cacheredis'); $name = 'merchant_order_query'; $key = "{$mchid}-{$mch_order}"; $ret = $ins->hdel($name, '', $key); } public static function query_queue_order($mchid,$mch_order) { $ins = Cache::getInstance('cacheredis'); $name = 'merchant_order_query'; $key = "{$mchid}-{$mch_order}"; $value = $ins->hget($name,'',$key); return $value; } public static function need_check($net_errno) { if(empty($net_errno)) return false; [$type,$code] = explode('-',$net_errno); $code = intval($code); if($type == "CURL") { static $errors = [CURLE_GOT_NOTHING,CURLE_RECV_ERROR]; return in_array($code,$errors); } elseif($type == "HTTP") { static $excludes = [404]; return !in_array($code,$excludes); } else { return false; } } public static function order_errflag($net_errno) { return ($net_errno === 'ORDER_CREATE_FAIL'); } public static function onOrderSuccess($refill_info,$order_info) { $data = store_member::instance()->get_member($order_info['store_id']); if(empty($data)) { Log::record("cannot find member when store_id={$order_info['store_id']}",Log::ERR); return false; } $data['order_sn'] = $refill_info['order_sn']; $data['amount'] = $refill_info['channel_amount']; $model_pd = Model('predeposit'); $model_pd->changePd('order_pay',$data,true); return true; } public static function getProvider($name,$type = 'RefillPhone') { $file = BASE_HELPER_RAPI_PATH . "/$name/{$type}.php"; if(!file_exists($file)){ Log::record("provider api file={$file} not exist.",Log::DEBUG); return false; } else { require_once($file); Log::record("file={$file} load success.",Log::DEBUG); } $class_name = "refill\\{$name}\\{$type}"; if (class_exists($class_name, false)) { $caller = new $class_name([]); return $caller; } else { $error = "Base Error: class {$class_name} isn't exists!"; Log::record($error, Log::ERR); return false; } } public static function xmlToArray($xml) { $object = simplexml_load_string($xml); $val = json_decode(json_encode($object), true); $msg = json_encode($val); Log::record("xmlToArray result={$msg}", Log::DEBUG); return $val; } //for tester public static function send_normal($order_sn) { $status = mt_rand(1,100); if($status > 97) { $status = 1; } else { $status = 0; } $url = BASE_SITE_URL . "/mobile/callback/refill_baidu.php"; go(function () use ($url, $status,$order_sn) { sleep(3); while (true) { $resp = http_request($url,['status' => $status,'order_sn' => $order_sn],'GET', false, [], $net_errno); if($resp == 'SUCCESS') { break; } } Log::record("resp = {$resp}",Log::DEBUG); }); } public static function send_quick($order_sn) { $status = mt_rand(1,10); if($status > 3) { $status = 1; } else { $status = 0; } $url = BASE_SITE_URL . "/mobile/callback/refill_baidu.php"; go(function () use ($url, $status,$order_sn) { sleep(3); while (true) { $resp = http_request($url,['status' => $status,'order_sn' => $order_sn],'GET', false, [], $net_errno); if($resp == 'SUCCESS') { break; } } Log::record("resp = {$resp}",Log::DEBUG); }); } public static function retry_canceled_order($order_id, $skip) { $mod_order = Model('vr_order'); $mod_refill = Model('refill_order'); $order_info = $mod_order->getOrderInfo(['order_id' => $order_id]); $refill_info = $mod_refill->getOrderInfo(['order_id' => $order_id,'inner_status' => 0]); if(empty($refill_info) || empty($order_info)) { return [false,'无此订单或者订单已经重试中了...']; } $tran = new trans_wapper($mod_order,'notify change order state trans'); try { $order_info = $mod_order->getOrderInfo(['order_id' => $order_id], '*', true, true); $refill_info = $mod_refill->getOrderInfo(['order_id' => $order_id, 'inner_status' => 0], '*', true, true); $order_state = intval($order_info['order_state']); if(empty($refill_info) || $refill_info['is_retrying'] == 1 || $order_state != ORDER_STATE_CANCEL) { $tran->commit(); return [false,'订单已经在重试']; } $mod_refill->edit($order_id, ['is_retrying' => 1]); $tran->commit(); $order = refill\order::from_db($refill_info,$order_info); $params = $order->queue_params(); $params['order_time'] = time(); if ($skip) { $mchid = $refill_info['mchid']; $mch_order = $refill_info['mch_order']; refill\util::set_next_order($mchid, $mch_order); } if(util::push_add($params)) { return [true,'']; } else { return [false,'加入队列出错']; } } catch (Exception $ex) { $tran->rollback(); Log::record($ex->getMessage(),Log::ERR); return [false,"{$ex->getMessage()}"]; } } public static function transfer_success_order($order_id, $manual_recharge_amount) { $mod_order = Model('vr_order'); $mod_refill = Model('refill_order'); $order_info = $mod_order->getOrderInfo(['order_id' => $order_id]); $refill_info = $mod_refill->getOrderInfo(['order_id' => $order_id,'inner_status' => 0]); if(empty($refill_info) || empty($order_info)) { return [false,'无此订单']; } $tran = new trans_wapper($mod_order,'notify change order state trans'); try { $order_info = $mod_order->getOrderInfo(['order_id' => $order_id], '*', true, true); $refill_info = $mod_refill->getOrderInfo(['order_id' => $order_id, 'inner_status' => 0], '*', true, true); $order_state = intval($order_info['order_state']); if(empty($refill_info) || $refill_info['is_retrying'] == 1 || $order_state != ORDER_STATE_CANCEL) { $tran->commit(); return [false,'订单已经在重试']; } $mod_refill->edit($order_id, ['is_retrying' => 1]); $order = refill\order::from_db($refill_info,$order_info); $mchid = $order->mchid(); [$success,$success_order_id,$errmsg] = refill\RefillFactory::instance()->success_order($order); if(!$success) { $tran->rollback(); return [false, $errmsg]; } $mod_refill->edit($success_order_id, ['mch_notify_state' => 1, 'mch_notify_times' => ['exp', 'mch_notify_times+1']]); $mod_refill->edit($order_id, ['is_retrying' => 0]); if($manual_recharge_amount > 0) { $mod_refill->edit($success_order_id, ['channel_amount' => $manual_recharge_amount]); } $tran->commit(); return [true,'']; } catch (Exception $ex) { $tran->rollback(); Log::record($ex->getMessage(),Log::ERR); return [false,"{$ex->getMessage()}"]; } } public static function vr_order_part() { $miner = function ($time) { return strtotime(date('Y-m-d',$time)) - 86400 * 15; }; $maxer = function ($time) { return $time + 3600; }; $now = time(); return [['egt', $miner($now)], ['elt', $maxer($now)], 'and']; } public static function refill_order_part($order_time = 0) { $miner = function ($time) { return strtotime(date('Y-m-d',$time)) - 86400 * 15; }; $maxer = function ($time) { return $time + 3600; }; if($order_time == 0) { $now = time(); return [['egt', $miner($now)], ['elt', $maxer($now)], 'and']; } else { return $order_time; } } private static function part_calc($time, $sub_period, $add_period) { //查询30天的订单 $miner = function ($time) use($sub_period) { return strtotime(date('Y-m-d', $time)) - $sub_period; }; $maxer = function ($time) use($add_period) { return $time + $add_period; }; $namer = function ($time) { return 'p'.date('Ym', $time); }; if (defined('DB_PARTIONED') && DB_PARTIONED) { if(is_string($time)) { $time = intval($time); } if($time == 0) { $now = time(); $a = $namer($miner($now)); $b = $namer($maxer($now)); if($a != $b) { return [$a,$b]; } else { return $a; } } else { return $namer($time); } } else { return ''; } } public static function part_query($order_time = 0) { return self::part_calc($order_time, 86400 * 30, 3600); } public static function part_notify() { return self::part_calc(0, 86400 * 7, 3600); } public static function part_refill($order_time) { return self::part_calc($order_time, 86400 * 2, 3600); } public static function part_vr_order($add_time) { return self::part_calc($add_time, 86400 * 2, 3600); } public static function part_vr_order_time($order_time) { $namer = function ($time) { return 'p'.date('Ym', $time); }; if (defined('DB_PARTIONED') && DB_PARTIONED) { if(is_string($order_time)) { $order_time = intval($order_time); } if ($order_time == 0) { return ''; } else { $a = $namer($order_time); $b = $namer(time() + 3600); //当前时间加上一个小时误差 if ($a == $b) { return $a; } else { return [$a, $b]; } } } else { return ''; } } public static function part_vr_create() { return self::part_calc(0, 3600, 3600); } public static function calc_part($time = 0) { return self::part_calc($time, 86400 * 15, 3600); } public static function write_ch_submit_times($chname, $fail_times, $enough_times) { $name = 'channel_submit_times_limit'; $data = ["$chname" => json_encode([$fail_times, $enough_times])]; wcache($name,$data,'refill-'); } public static function read_ch_submit_times($chname) { $name = 'channel_submit_times_limit'; $ret = rcache($name,'refill-',$chname); if(empty($ret)) { return [-1, -1]; } $times = $ret[$chname]; [$fail_times, $enough_times] = json_decode($times,true); return [$fail_times, $enough_times]; } public static function read_ches_submit_times() { $name = 'channel_submit_times_limit'; $datas = rcache($name,'refill-'); if(empty($datas)) { return [-1, -1]; } $ret = []; foreach ($datas as $ch_name => $times) { $ret[$ch_name] = json_decode($times, true); } return $ret; } }