Files
OMS/app/taoexlib/lib/utils.php
2025-12-28 23:13:25 +08:00

605 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Copyright © ShopeX http://www.shopex.cn. All rights reserved.
* See LICENSE file for license details.
*/
class taoexlib_utils {
const VERSION = '1.0';
// const API_URL = 'http://newsms.ex-sandbox.com/sms_webapi/index.php';
// const SERVICE_URL = 'http://webpy.ex-sandbox.com/';
const API_URL = 'http://webapi.sms.shopex.cn/';
const SERVICE_URL = 'http://api.sms.shopex.cn/';
const SINGLE_SMS_LENGTH = 70;
public static $blackList = null;
public static $sumProcess = 0;
public static $maxProcess = 30;
public static $serverTimestamp = null;
public static $localTimestamp = null;
public static $writeLog = true;//开启日志
public static function make_shopex_ac($arr, $token) {
$temp_arr = $arr;
ksort($temp_arr);
$str = '';
foreach ($temp_arr as $key => $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;
}
}
}