235 lines
7.7 KiB
PHP
235 lines
7.7 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @author Any
|
|
* @description KISS
|
|
* @date 2020-11-3
|
|
* @version 1.0.0
|
|
*
|
|
* _____LOG_____
|
|
*
|
|
*/
|
|
namespace app\models\sms;
|
|
|
|
use app\components\FlashStorage;
|
|
use app\components\sms\AliSmsMsgSender;
|
|
use app\components\sms\TnxSmsMsgSender;
|
|
use app\components\EncryptHelper;
|
|
|
|
|
|
class SmsMsgHelper
|
|
{
|
|
public $cx_mch_id;
|
|
|
|
private $sms_setting;
|
|
private $is_prod;
|
|
|
|
|
|
public function __construct($cx_mch_id) {
|
|
$this->cx_mch_id = $cx_mch_id;
|
|
$this->sms_setting = SmsSetting::findOne(['cx_mch_id' => $cx_mch_id]);
|
|
if($this->sms_setting == null){
|
|
throw new \Exception("短信未配置");
|
|
}
|
|
$this->is_prod = $this->sms_setting->is_prod == 1 ? true : false;
|
|
}
|
|
|
|
private function check_limit($mobile, $tpl_code, $mobile_prefix, $user_id)
|
|
{
|
|
//手机号加密
|
|
$mobile = EncryptHelper::encryptMobilePhone($mobile);
|
|
|
|
$limit = $this->sms_setting->day_limit;
|
|
|
|
$count = SmsRecord::find()->where([
|
|
'mobile_phone' => $mobile,
|
|
'mobile_prefix' => $mobile_prefix,
|
|
'tpl_code' => $tpl_code,
|
|
'user_id' => $user_id,
|
|
])->andWhere([
|
|
'>',
|
|
'created_at',
|
|
time() - 86400
|
|
])->count();
|
|
if($count >= $limit && $limit != 0){
|
|
return [
|
|
'code' => 1,
|
|
'msg' => '今日申请验证码次数超额'
|
|
];
|
|
}
|
|
return [
|
|
'code' => 0,
|
|
'msg' => 'ok'
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 发送验证码
|
|
* @param string $mobile 手机号
|
|
* @param string $tpl_type 短信类型
|
|
* @param string $mobile_prefix 手机号国家代码
|
|
* @param integer $user_id 用户ID
|
|
*/
|
|
public function sender($mobile, $tpl_type, $mobile_prefix = "86", $user_id = 0)
|
|
{
|
|
$tpl_code = SmsTpl::getTpl($tpl_type, $this->sms_setting->type, $this->cx_mch_id);
|
|
if($tpl_code == null){
|
|
return [
|
|
'code' => 1,
|
|
'msg' => '未设置短信模板'
|
|
];
|
|
}
|
|
//每日限额检查
|
|
$res = $this->check_limit($mobile, $tpl_code, $mobile_prefix, $user_id);
|
|
if($res['code'] != 0){
|
|
return $res;
|
|
}
|
|
//速率检查
|
|
$cache_key = $this->get_cache_key($tpl_type, $mobile, $mobile_prefix, $user_id);
|
|
$cache_val = FlashStorage::getCache($cache_key);
|
|
$time_delay = $this->sms_setting->time_delay;
|
|
if($cache_val !== false && time() < $cache_val['timestamp'] + $time_delay){
|
|
return [
|
|
'code' => 1,
|
|
'msg' => '请求过快,稍后再试'
|
|
];
|
|
}
|
|
$code = $this->get_code();
|
|
if($this->is_prod){
|
|
//正式环境
|
|
try{
|
|
$res = $this->_sender($mobile, $code, $tpl_code, $mobile_prefix);
|
|
} catch (\Exception $ex){
|
|
$res = [
|
|
'code' => 1,
|
|
'msg' => $ex->getMessage()
|
|
];
|
|
}
|
|
} else {
|
|
$res['data'] = $code;
|
|
}
|
|
if($res['code'] == 0){
|
|
//记录短信发送记录
|
|
$resp = SmsRecord::logger($mobile, $tpl_code, $code, $user_id, $this->cx_mch_id, $mobile_prefix);
|
|
if($resp['code'] != 0){
|
|
//@TODO 记录错误
|
|
}
|
|
//cache
|
|
$cache_val = [];
|
|
$cache_val['timestamp'] = time();
|
|
$cache_val['code'] = $code;
|
|
$cache_val['mobile'] = $mobile;
|
|
$cache_val['mobile_prefix'] = $mobile_prefix;
|
|
$max_age = $this->sms_setting->expire_time;
|
|
FlashStorage::setCache($cache_key, $cache_val,$max_age);
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
private function get_code()
|
|
{
|
|
$code_len = $this->sms_setting->code_len;
|
|
$code = "";
|
|
for($i = 0; $i < $code_len; $i++){
|
|
$code .= mt_rand(0, 9);
|
|
}
|
|
return $code;
|
|
}
|
|
|
|
|
|
private function _sender($mobile, $code, $tpl_code, $mobile_prefix)
|
|
{
|
|
$res = [
|
|
'code' => 0,
|
|
'msg' => 'ok'
|
|
];
|
|
|
|
//阿里云
|
|
if($this->sms_setting->type == SmsSetting::TYPE_ALIYUN){
|
|
$sms_sender = new AliSmsMsgSender();
|
|
$sms_sender->access_key_id = $this->sms_setting->access_key_id;
|
|
$sms_sender->access_secret = $this->sms_setting->access_secret;
|
|
$sms_sender->signname = $this->sms_setting->sign_name;
|
|
$sms_sender->tpl_code = $tpl_code;
|
|
$params = [
|
|
'code' => $code
|
|
];
|
|
$res = $sms_sender->sender($mobile, $params, $mobile_prefix);
|
|
}
|
|
//腾讯云
|
|
if($this->sms_setting->type == SmsSetting::TYPE_TENCENT){
|
|
$sms_sender = new TnxSmsMsgSender();
|
|
$sms_sender->secret_id = $this->sms_setting->secret_id;
|
|
$sms_sender->secret_key = $this->sms_setting->secret_key;
|
|
$sms_sender->sdk_app_id = $this->sms_setting->sdk_app_id;
|
|
$sms_sender->signname = $this->sms_setting->sign_name;
|
|
$sms_sender->tpl_code = $tpl_code;
|
|
$params = [$code];
|
|
$res = $sms_sender->sender($mobile, $params, $mobile_prefix);
|
|
}
|
|
//@TODO 扩展其他短信服务商
|
|
return $res;
|
|
}
|
|
|
|
private function get_cache_key($prefix, $mobile, $mobile_prefix, $user_id)
|
|
{
|
|
return "sm_{$this->cx_mch_id}_{$user_id}_{$prefix}_{$mobile_prefix}_{$mobile}";
|
|
}
|
|
|
|
/**
|
|
* 验证验证码
|
|
* @param string $mobile 手机号
|
|
* @param string $code 验证码
|
|
* @param string $tpl_type 短信类型
|
|
* @param string $mobile_prefix 手机号国家代码
|
|
* @param integer $user_id 用户ID
|
|
*/
|
|
public function validate($mobile, $code, $tpl_type, $mobile_prefix = "86", $user_id = 0)
|
|
{
|
|
$cache_key = $this->get_cache_key($tpl_type, $mobile, $mobile_prefix, $user_id);
|
|
$cache_val = FlashStorage::getCache($cache_key);
|
|
if($cache_val === false){
|
|
return [
|
|
'code' => 1,
|
|
'msg' => '验证码无效或已失效'
|
|
];
|
|
}
|
|
$timestamp = time();
|
|
if(!isset($cache_val['mobile']) || !isset($cache_val['code']) || !isset($cache_val['timestamp']) ){
|
|
return [
|
|
'code' => 1,
|
|
'msg' => '验证码验证码失败,系统内部错误'
|
|
];
|
|
}
|
|
$max_age = $this->sms_setting->expire_time;
|
|
if($timestamp > $cache_val['timestamp'] + $max_age){
|
|
return [
|
|
'code' => 1,
|
|
'msg' => '验证码失效'
|
|
];
|
|
}
|
|
if($cache_val['mobile'] == $mobile && $cache_val['code'] == $code){
|
|
//验证码通过后清除缓存
|
|
FlashStorage::deleteCache($cache_key);
|
|
return [
|
|
'code' => 0,
|
|
'msg' => 'ok'
|
|
];
|
|
}
|
|
if(!$this->is_prod && $cache_val['mobile'] == $mobile){
|
|
$super_code = $this->sms_setting->super_code;
|
|
if($super_code == $code){
|
|
//验证码通过后清除缓存
|
|
FlashStorage::deleteCache($cache_key);
|
|
return [
|
|
'code' => 0,
|
|
'msg' => 'ok'
|
|
];
|
|
}
|
|
}
|
|
return [
|
|
'code' => 1,
|
|
'msg' => '验证码无效'
|
|
];
|
|
}
|
|
} |