cxfoot/models/sms/SmsMsgHelper.php
2023-10-27 14:25:12 +08:00

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' => '验证码无效'
];
}
}