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