$value) { if ($key != 'certi_ac') { $str .= $value; } } return md5($str . md5($token)); } public static function base_make_shopex_ac($arr, $token) { ksort($arr); $str = ''; foreach ($arr as $key => $value) { if ($key != 'ac') { $str .= $value; } } return strtolower(md5($str . strtolower(md5($token)))); } public static function no_slashes($temp_arr) { foreach ($temp_arr as $key => $value) { if (is_array($value)) { $value = self::no_slashes($value); $array_temp[$key] = $value; } else { $array_temp[$key]=stripslashes($value); } } return $array_temp; } public static function province_mapping($provinceName) { $mapping = array( '北京' => '110000', '天津' => '120000', '上海' => '310000', '重庆' => '500000', '香港' => '810000', '台湾' => '710000', '澳门' => '820000', '新疆' => '650000', '宁夏' => '640000', '内蒙古' => '150000', '广西' => '450000', '西藏' => '540000', '河北' => '130000', '河南' => '410000', '陕西' => '610000', '辽宁' => '210000', '吉林' => '220000', '黑龙江' => '230000', '江苏' => '320000', '浙江' => '330000', '安徽' => '340000', '福建' => '350000', '江西' => '360000', '山东' => '370000', '山西' => '140000', '湖北' => '420000', '湖南' => '430000', '广东' => '440000', '海南' => '460000', '四川' => '510000', '贵州' => '520000', '云南' => '530000', '陕西' => '610000', '甘肃' => '620000', '青海' => '630000', ); return $mapping[trim($provinceName)]; } public static function register($arr) { $param = $arr; $param['certi_app'] = 'sms.reg'; $param['version'] = '1.0'; $param['format'] = 'json'; $param['timestamp'] = self::get_server_time(); $param['certi_ac'] = self::make_shopex_ac($param, 'SMS_REG'); $http = new base_httpclient; $result = $http->post(self::API_URL, $param); return json_decode($result); } public static function registerErrorMsg($code) { $msg = array( 'EmailExist' => 'Email已被使用,请更换后再注册', ); return $msg[$code]; } public static function get_user_info($arr) { $param = array( 'certi_app' => 'sms.info', 'entId' => $arr['entid'], 'entPwd' => md5($arr['password'] . 'ShopEXUser'), 'source' => APP_SOURCE, 'version' => self::VERSION, 'format' => 'json', 'timestamp' => self::get_server_time(), ); $param['certi_ac'] = self::base_make_shopex_ac($param, APP_TOKEN); $http = new base_httpclient; $result = $http->post(self::SERVICE_URL, $param); return json_decode($result); } public static function login($arr) { $param = array( 'certi_app' => 'sms.login', 'identifier' => $arr['identifier'], 'password' => $arr['password'], 'version' => self::VERSION, 'format' => 'json', 'timestamp' => self::get_server_time(), ); $param['certi_ac'] = self::make_shopex_ac($param, 'SMS_LOGIN'); $http = new base_httpclient; $result = $http->post(self::API_URL, $param); return json_decode($result); } /** * * 短信包购买/赠送接口 * @param array $arr * @param string $pid 短信包id */ public static function buy($arr, $pid, $startTime) { $param = array( 'certi_app' => 'sms.buy', 'startTime' => $startTime, 'prdId' => $pid, 'entId' => $arr['entid'], 'entPwd' => md5($arr['password'] . 'ShopEXUser'), 'license' => base_certificate::get('certificate_id') ? base_certificate::get('certificate_id') : 1, 'source' => APP_SOURCE, 'version' => self::VERSION, 'format' => 'json', 'timestamp' => self::get_server_time() ); $param['certi_ac'] = self::make_shopex_ac($param, APP_TOKEN); $http = new base_httpclient; $result = $http->post(self::API_URL, $param); return json_decode($result); } /** * * 发送通知类短信 * @param array $arr * array( * 'entid' => '123456789012', * 'password' => '123456', * 'source' => '616525', * ) * @param array $content * 必须是一个手机号码和一条短信内容 * eg. * array( * 'phones' => '13636348683', * 'content' => '短信测试', * ) * @param string $token * @param bool $filter 是否开始禁词检查 */ public static function send_notice($arr, $content, $filter = false) { $msgCount = 0; // 检查是否包含禁词 if ($filter && self::is_banned_keywords($content['content'])) { if (self::$writeLog) { self::writeSpecialLog(-3, array( 'phones' => array($content['phones']), 'content' => $content['content'] )); } return false; } if (!self::isMobilePhone($content['phones'])) { if (self::$writeLog) { self::writeSpecialLog(-4, array( 'phones' => array($content['phones']), 'content' => $content['content'] )); } return false; } //检查短信账户信息是否存在 base_kvstore::instance('taoexlib')->fetch('account', $account); if (!unserialize($account)) { return false; } $mscontent =array( 'phones' => $content['phones'], 'replace' => $content['replace'], 'tplid' =>$content['tplid'], 'content'=>$content['content'], ); return kernel::single('erpapi_router_request')->set('sms', $account)->sms_sendOne($mscontent, !$echostr); } /** * * 群发短信 * @param array $arr * @param array $content * eg. * array( * array( * 'phones' => array('13636348683', '13838383838'), * 'content' => '短信测试', * ), * array( * 'phones' => array('13100000000', '13900000000'), * 'content' => '端午节快乐', * ) * ... * ) * @param string $token * */ public static function send_fanout($arr, $content, $filter = true) { $msgCount = 0; // 检查是否包含禁词 and 统计有效待发短信数量 $contents = array(); foreach ($content as $msg) { if ($filter && self::is_banned_keywords($msg['content'])) { if (self::$writeLog) { self::writeSpecialLog(-3, $msg); } continue; } else { foreach ($msg['phones'] as $key => $num) { if (!self::isMobilePhone($num)) { if (self::$writeLog) { self::writeSpecialLog(-4, array( 'phones' => array($num), 'content' => $msg['content'] )); } unset($msg['phones'][$key]); } } $msgCount += ceil(self::utf8_strlen($msg['content']) / self::SINGLE_SMS_LENGTH) * count($msg['phones']); $msg['phones'] = implode(',', $msg['phones']); $contents[] = $msg; } } if (!$msgCount) { return null; } // 获取剩余短信条数 $info = self::get_user_info($arr); if (('succ' == $info->res) && ($info->info->month_residual >= $msgCount)) { $contentsArr = array_chunk($contents, 30); // Because pcntl_fork cause mysql connection missed, so do not use pcntl_fork $flag = false; if ($flag && function_exists('pcntl_fork')) { foreach ($contentsArr as $value) { taoexlib_utils::send_fanout_multi_process($arr, json_encode($value)); } } else { foreach ($contentsArr as $value) { sms_utils::send($arr, json_encode($value), 'fan-out'); } } } } /** * * 短信发送接口 * @param json $content * @param string $type * value "fan-out" or "notice" * @param string $token */ public static function send($arr, $content, $type) { $content_request = json_decode($content,1); $result = kernel::single('taoexlib_request_sms')->sms_request('sendByTmpl','post',$content_request[0]); if (self::$writeLog) { self::writeLog($content, $result); } return json_decode($result); } public static function update_blacklist() { $param = array( 'certi_app' => 'sms.backlist', 'version' => self::VERSION, 'format' => 'json', 'timestamp' => self::get_server_time(), ); $param['certi_ac'] = self::make_shopex_ac($param, 'SMS_BACK'); $http = new base_httpclient; $result = $http->post(self::API_URL, $param); $result = json_decode($result); if ('succ' == $result->res) { $logFile = self::get_resource_path() . 'blacklistLog.txt'; $lastUpdateTime = 0; if (file_exists($logFile)) { $lastUpdateTime = file_get_contents($logFile); } if ($lastUpdateTime != $result->info->update_time) { $remoteUrl = $result->info->download_url; $http = new base_httpclient; $words = $http->get($remoteUrl); $wordsArr = array(); if ($words) { $wordsArr = explode("\n", $words); } $dataFile = self::get_resource_path() . 'blacklist.php'; $dataHandle = fopen($dataFile, 'w'); fwrite($dataHandle, json_encode($wordsArr)); fclose($dataHandle); $logHandle = fopen($logFile, 'w'); fwrite($logHandle, $result->info->update_time); fclose($logHandle); } } } /** * * 取禁词 * @return array */ public static function get_blacklist() { if (null === self::$blackList) { $dataFile = self::get_resource_path() . 'blacklist.php'; if (file_exists($dataFile)) { $dataHandle = fopen($dataFile, 'r'); $contents = fread($dataHandle, filesize($dataFile)); fclose($dataHandle); self::$blackList = json_decode($contents); } } return self::$blackList; } /** * * 检查是否包含禁词 * @param string $str * @return 如果包含禁词则返回禁词,如果未包含禁词返回false */ public static function is_banned_keywords($str) { if ($blackList = self::get_blacklist()) { foreach ($blackList as $value) { if ($value == '') { continue; } if (false !== strpos($str, $value)) { return $value; } } } return false; } public static function get_server_time() { if (null === self::$serverTimestamp || null === self::$localTimestamp) { $param = array( 'certi_app' => 'sms.servertime', 'version' => self::VERSION, 'format' => 'json', ); $param['certi_ac'] = self::make_shopex_ac($param, 'SMS_TIME'); $http = new base_httpclient; $result = $http->post(self::API_URL, $param); $result = json_decode($result); self::$serverTimestamp = ('succ' == $result->res) ? $result->info : 0; self::$localTimestamp = time(); return self::$serverTimestamp; } else { return self::$serverTimestamp + time() - self::$localTimestamp; } } public static function check_account_status() { if (!defined('APP_TOKEN') || !defined('APP_SOURCE')) { return false; } base_kvstore::instance('taoexlib')->fetch('account', $account); if (unserialize($account)) { $param = unserialize($account); if ($info = self::get_user_info($param)) { if ('succ' == $info->res) { return $info->info->month_residual; } } } return false; } public static function get_resource_path() { return dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'resource' . DIRECTORY_SEPARATOR; } public static function pattern() { return array( '+'=>'_1_', '/'=>'_2_', '='=>'_3_', ); } public static function encode($str) { $str = base64_encode($str); return strtr($str, self::pattern()); } public static function decode($str) { $str = strtr($str, array_flip(self::pattern())); return base64_decode($str); } /** * * 发送短信过程中使用多进程 * 主进程作为调度器,子进程负责短信发送 * */ public function send_fanout_multi_process($arr, $content) { $pid = pcntl_fork(); if ($pid == -1) { die("could not fork\n"); } elseif ($pid) { self::$sumProcess++; echo str_pad(">>> [Process/Sum ".self::$sumProcess."/".self::$maxProcess."]", 30); echo "Send Sms\n"; if (self::$sumProcess >= self::$maxProcess) { pcntl_wait($status); self::$sumProcess--; } return $pid; } else { self::send($arr, $content, 'fan-out'); exit(); } } public static function hasRegister() { base_kvstore::instance('taoexlib')->fetch('hasRegister', $hasRegister); return $hasRegister; } //返回注册的Email public static function regEmail() { base_kvstore::instance('taoexlib')->fetch('regEmail', $regEmail); return $regEmail; } public static function utf8_strlen($string = null) { preg_match_all("/./us", $string, $match); return count($match[0]); } public static function get_account() { base_kvstore::instance('taoexlib')->fetch('account', $account); if (!unserialize($account)) { return false; } $param = unserialize($account); return $param; } public static function setLog($writeLog = false) { self::$writeLog = $writeLog; } /** * * 记录特殊的短信日志,包括短信包含禁词,手机号码不正确。 * @param int $errno, 值为-3(包含禁词)或-4(手机号码不正确) * @param array $content, 一维数组 */ public static function writeSpecialLog($errno, $content) { $batchno = $errno; //-3(包含禁词)或-4(手机号码不正确) $status = 0; if($batchno=='-3'){ $msg = '包含禁词'; }elseif($batchno=='-4'){ $msg = '手机号码不正确'; }else{ $msg = ''; } $logObj = app::get('taoexlib')->model('log'); foreach ($content['phones'] as $mobile) { $log = array( 'batchno' => $batchno, 'mobile' => $mobile, 'content' => $content['content'], 'status' => $status, 'msg' => $msg, 'sendtime' => time() ); $logObj->insert($log); } } /** * * 记录短信发送日志 * @param json array $content * @param mix type $result * 如果请求发送短信api失败, $result为false * 如果请求成功,则为api返回的json格式的结果 */ public static function writeLog($content, $result) { $logObj = app::get('taoexlib')->model('log'); $content = json_decode($content); if (false === $result) { // 特殊批次号,api请求不成功时为-1 $batchno = -1; $status = 0; $msg = '请求api失败'; } else { $result = json_decode($result); $batchno = ('succ' == $result->res) ? $result->info->msgid : -2; //请求api成功但是发送失败的时候,批次号记为-2 $status = ('succ' == $result->res) ? 1 : 0; $msg = ('succ' == $result->res) ? '' : (is_object($result->info) ? $result->info->msg : $result->info); } foreach ($content as $value) { $mobiles = explode(",", $value->phones); foreach ($mobiles as $mobile) { $log = array( 'batchno' => $batchno, 'mobile' => $mobile, 'content' => $value->content, 'status' => $status, 'msg' => $msg, 'sendtime' => time() ); $logObj->insert($log); } } } public static function isMobilePhone($num) { if (strpos($num,'@hash')) { return true; } $pattern = '/^1\d{10}$/'; if (preg_match($pattern, $num)) { return true; } else { return false; } } }