日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TP5 封装多业务的发送短信功能(包括国际短信)

發布時間:2025/7/14 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TP5 封装多业务的发送短信功能(包括国际短信) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、準備工作

1.1、準備依賴包
  • 這個網站提供的都是PHP包,挺有用的 https://packagist.org/
  • 另外還可以在GitHub上面下載 https://github.com/

1、短信方面 我這里用到 packagist 里面的 overtrue/easy-sms

下載依賴包之前記得要看下依賴的PHP版本、短信平臺等。

我們可以看到這個依賴包提供的平臺有以下:

2、Redis方面 我用到了Predis的包

另外他包里面提供了使用方法和類型,需要去看下,不過本人進行了代碼封裝。
安裝這些包的話直接打開cmd命令行輸入 composer require XXX 即可,
XXX是對應包名,XXX后面如果加 **:1.***就相當于版本號,包里面會介紹相關的操作。
##2、代碼封裝
#####2.1、封裝的文件

  • 包括兩個 服務類config.php ,用于封裝不同業務類型的存儲和發送的方法,如下圖

2.2、配置文件
  • config.php 文件,用于保存短信配置和白名單等,內容如下
//通用發送短信配置(短信消息配置) 'easy_sms' => [// HTTP 請求的超時時間(秒)'timeout' => 5.0,// 默認發送配置'default' => [// 網關調用策略,默認:順序調用'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class,// 默認可用的發送網關'gateways' => ['yunpian',],],// 可用的網關配置'gateways' => ['errorlog' => ['file' => '/tmp/easy-sms.log',],//云片'yunpian' => ['api_key' => '',],//阿里云'aliyun' => ['access_key_id' => '','access_key_secret' => '','sign_name' => 'aa',],//...], ],//發送消息限制配置 'sms_limit' => ['white_list' => [ //發送短信白名單'13512341234','18664337604','13450681681',],'save_time' => 3600, //設置保存時間 默認一小時'send_total' => 5, //限制時間內最多發送5條'expires' => 900, //設置驗證碼過期時間 默認15分鐘 ],//Redis配置 'redis_host' => Env::get('redis.hostname','192.168.2.168'), 'redis_password' => Env::get('redis.password',''), 'redis_port' => Env::get('redis.hostport','6379'), 'redis_prefix' => Env::get('redis.prefix','su::'),

其中 Env 是對應的配置文件,你也可以直接在第二個參數填寫默認的配置即可。

2.3、Redis服務類
<?phpnamespace app\common\service;use Predis\Client;class RedisService {//Redis保存的key//短信部分const SU_SMS_LOGIN = 'sms::login::'; //短信驗證碼|登錄和找回密碼(后面跟 國際區號-用戶手機號)const SU_SMS_RESET_PWD = 'sms::reset::pwd::'; //短信驗證碼|重置賬號密碼(后面跟 國際區號-用戶手機號)const SU_SMS_RESET_MOBILE = 'sms::reset::mobile::'; //短信驗證碼|重置手機號碼(后面跟 國際區號-用戶手機號)const SU_SMS_CREATE_ACCOUNT_MOBILE = 'sms::create::account::mobile::'; //創建子賬號|修改子賬號手機號(后面跟 國際區號-用戶手機號)const SU_SMS_OTHER = 'sms::other::'; //短信驗證碼|其他情況(后面跟 國際區號-用戶手機號)const SU_SMS_NUM = 'sms::num::'; //手機短信發送次數(后面跟 國際區號-用戶手機號)private static $prefix = '';private static $client;/*** 單例模式獲取redis連接實例* @return Client*/public static function connect(){if (!self::$client) {self::$prefix = config('redis_prefix');$config = ['scheme' => 'tcp','host' => config('redis_host'),'port' => config('redis_port'),];//沒有配置密碼時,不傳入密碼項參數if (config('redis_password')) $config['password'] = config('redis_password');self::$client = new Client($config, ['prefix' => self::$prefix]);}return self::$client;}/*** 校驗短信驗證碼* @param string $areaCode 手機國際區號* @param string $mobile 手機號* @param string $smsCode 驗證碼* @param string $prefix 根據業務區分的短信前綴* @param bool $isThrowException 是否拋出異常* @param bool $isDel 檢查完后是否刪除該緩存* @return array*/public static function checkSmsCode(string $areaCode, string $mobile, string $smsCode, string $prefix = self::SU_SMS_LOGIN, bool $isThrowException = true, bool $isDel = true){$res = [true, '短信驗證碼正確!'];if (!self::connect()->exists($prefix .$areaCode . '-' . $mobile)) {$isThrowException ? throwResult('手機驗證碼失效') : $res = [false, '手機驗證碼失效'];} else {if (!hash_equals($smsCode, self::connect()->get($prefix . $areaCode . '-' . $mobile)))$isThrowException ? throwResult('手機驗證碼不正確') : $res = [false, '手機驗證碼不正確'];}if ($isDel) self::connect()->del($prefix . $mobile);return $res;}

最上面定義的常量都是用于定義業務類型,后面跟著手機號
throwResult 是封裝好的拋出異常的方法,使用自己封裝的即可。
connect 方法是用于實例化,每次用redis時候直接RedisService::connect();
checkSmsCode 方法是用于檢驗驗證碼是否正確的,其中需要傳業務類型前綴。

2.4、發送短信服務類
<?phpnamespace app\common\service;use Overtrue\EasySms\EasySms; use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;class SmsService {/*** 發送通用短信驗證碼* @param string $areaCode 手機國際區號* @param string $mobile 手機號* @param int $smsUseType 業務類型:0=注冊登錄,1=更換密碼,2=修改手機號,3=創建子賬號,10=其他* @param int $userId 用戶ID* @return int* @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException* @throws \app\common\exception\WorkException*/public static function sendSmsCode(string $areaCode,string $mobile, int $smsUseType = 0, int $userId = 0){$redis = RedisService::connect();$countKey = RedisService::SU_SMS_NUM . $mobile; //記錄該手機發送的次數//驗證碼業務類型switch ($smsUseType) {case 0://注冊登錄$smsKey = RedisService::SU_SMS_LOGIN . $areaCode . '-' . $mobile;break;case 1://重置密碼$smsKey = RedisService::SU_SMS_RESET_PWD . $areaCode . '-' . $mobile;break;case 2://修改手機號$smsKey = RedisService::SU_SMS_RESET_MOBILE . $areaCode . '-' . $mobile;break;case 3://創建子賬號$smsKey = RedisService::SU_SMS_CREATE_ACCOUNT_MOBILE . $areaCode . '-' . $mobile;break;case 10://其他$smsKey = RedisService::SU_SMS_OTHER . $areaCode . '-' . $mobile;break;default://通用$smsKey = RedisService::SU_SMS_OTHER . $areaCode . '-' . $mobile;break;}//白名單可以無限制使用發送次數if (!in_array($mobile, config('sms_limit.white_list'))) {//防止惡意發送,每小時限制5次$count = $redis->get($countKey);if ($count && $count >= config('sms_limit.send_total')) {throwResult('超過發送次數限制,請稍后再試');}}//檢查該手機號一段時間內已發送的次數$redisIncr = $redis->incr($countKey);if ($redisIncr == 1) $redis->expire($countKey, config('sms_limit.save_time')); //設置保存時間 默認一小時//查找如果存在相同鍵名的短信碼則更新采用舊的編碼$smsCode = config('app_debug') ? 8888 : ($redis->get($smsKey) ?: mt_rand(1000, 9999));//保存驗證碼并設置15分鐘有效時間$redis->set($smsKey, $smsCode, 'EX', config('sms_limit.expires')); //todo 短信驗證碼過期時間//發送短信并保存記錄if (!config('app_debug')) self::send($areaCode,$mobile, ['content' => $smsContent], $smsCode, $smsUseType, $userId);return $smsCode;}/*** 通用短信發送方法* @param string $areaCode 手機國際區號* @param string $mobile 手機號碼* @param string $content 發送內容* @param string $smsCode 手機驗證碼(沒有則不需要傳參)* @param int $smsUseType 0=注冊登錄,1=更換密碼,2=修改手機號,3=創建子賬號,10=其他* @param int $userId 用戶ID 默認為0(用戶不存在時候不需要傳參)* @return bool* @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException*/public static function send(string $areaCode, string $mobile, string $content, string $smsCode = '', int $smsUseType = 0, int $userId = 0){//發送短信的內容$isSuccess = 1;try {$mobile = new PhoneNumber($mobile, $areaCode);$option = ['content' => $content, //文字內容,使用在像云片類似的以文字內容發送的平臺'template' => 'SMS_001', // 模板 ID,使用在以模板ID來發送短信的平臺'data' => //模板變量,使用在以模板ID來發送短信的平臺['code' => 6379],];$easySms = new EasySms(config('easy_sms'));$easySms->send($mobile, $option);} catch (NoGatewayAvailableException $e) {$isSuccess = 3;}//短信驗證碼記錄成功與失敗到表 // SmsModel::createOne($mobile, $option['content'], $isSuccess, $smsUseType, $smsCode, $userId);return $isSuccess == 1 ? true : false;} }

我封裝的時候加了一些附加條件:

  • 業務類型 我們根據不同的業務類型進行不同的 鍵值 保存和操作。

  • 限制次數 白名單用戶可以無限次發送,而普通用戶限制每小時最多發送多少次,主要通過 SU_SMS_NUM 這個redis 鍵值去判斷,這個鍵值需要根據具體業務設置個時間,比如1小時。

  • 驗證碼時長 驗證碼需要存入redis中并設置一個時間,保證驗證碼的有效時長。

  • 穩定驗證碼 每次發送驗證碼之前需要去查看是否存在 有效驗證碼 ,有的話則直接發送該驗證碼。

3、實現過程

3.1、發送短信
/*** @ApiTitle (發送短信驗證碼)* @ApiSummary (用于發送短信驗證碼)* @ApiMethod (POST)* @ApiRoute (/api/User/sendSmsByPhone)* @ApiHeaders (name=Authorization, type=string, required=false, description="用戶Token,其中修改手機號需要傳")* @ApiParams (name="mobile", type="string", required=true, description="手機號")* @ApiParams (name="area_code", type="string", required=true, description="國際區號")* @ApiParams (name="sms_type", type="string", required=false, description="業務類型:0=注冊登錄,1=更換密碼,2=修改手機號,3=創建子賬號和修改子賬號手機號,10=其他,默認為0")*/ public function sendSmsByPhone() {$areaCode = input('area_code'); //國際區號$mobile = input('mobile'); //手機號$smsType = intval(input('sms_type')) ?? 0; //短信類型//驗證數據$this->validate(['mobile' => $mobile,'area_code' => $areaCode,'sms_type' => $smsType,], 'UserValidate.send_sms_by_phone');//業務類型判斷if ($smsType == 1) { //重置、找回密碼 // $user = $this->auth->getUser(); // if (!$user) $this->error(MSG_NEED_LOGIN);} elseif ($smsType == 2) { //修改手機號需要登錄和驗證手機號$user = $this->auth->getUser();if (!$user) $this->error(MSG_NEED_LOGIN);$oldMobile = $user->toArray()['mobile'];$oldAreaCode = $user->toArray()['area_code'];if ($areaCode . $mobile == $oldAreaCode . $oldMobile) $this->error('修改的手機號和原手機號相同!');} elseif ($smsType == 3) { //創建子賬號//判斷賬戶是否存在$id = db('user')->where('mobile', $mobile)->where('area_code', $areaCode)->value('id');if ($id) {$this->error('該賬戶已存在');}}//驗證該手機號是否存在,存在則驗證狀態 狀態:0=正常,1=檢測中,2=已凍結,3=封號$user = (new UserModel)->where('mobile', $mobile)->field(['id', 'status'])->find();$userid = 0;if ($user) {if ($user['status'] == 2) $this->error(MSG_USER_STATUS_FREEZE);if ($user['status'] == 3) $this->error(MSG_USER_STATUS_BAN);$userid = $user['id'];}//判斷業務類型$smsCode = SmsService::sendSmsCode($areaCode, $mobile, $smsType, $userid);//調試模式時,接口返回短信驗證碼的值$res['mobile'] = $mobile;$res['area_code'] = $areaCode;if (config('app_debug')) $res['_sms_code'] = $smsCode;$this->success(MSG_OK, $res, '手機驗證碼發送成功'); }
3.2、驗證短信并登陸
/*** @ApiTitle (會員手機號注冊和登錄)* @ApiSummary (用戶用于手機號注冊和登錄平臺)* @ApiMethod (POST)* @ApiRoute (/api/User/mobileLogin)* @ApiParams (name="area_code", type="string", required=true, description="手機國際區號")* @ApiParams (name="mobile", type="string", required=true, description="手機號")* @ApiParams (name="sms_code", type="string", required=true, description="手機驗證碼")* @ApiParams (name="type", type="int", required=true, description="類型:0=未知設備,1=安卓APP,2=IOSAPP,3=微信小程序,4=H5頁面,5=PC端")*/public function mobileLogin(){$areaCode = input('area_code'); //手機國際區號$mobile = input('mobile'); //手機號$smsCode = input('sms_code'); //手機驗證碼$type = input('type') ?? 0; //類型:0=未知設備,1=安卓APP,2=IOSAPP,3=微信小程序,4=H5頁面,5=PC端//驗證數據$this->validate(['mobile' => $mobile,'sms_code' => $smsCode,'area_code' => $areaCode,], 'UserValidate.mobile_login');//發送短信檢測RedisService::checkSmsCode(areaCode,$mobile, $smsCode, RedisService::SU_SMS_LOGIN);$userId = (new UserModel)->where('mobile', $mobile)->where('area_code', $areaCode)->value('id');$ip = request()->ip();$time = time();$redis = new Redis();if (!$userId) {//如果不存在則直接注冊$data = ['area_code' => $areaCode, //手機國際區號'mobile' => $mobile, //手機號'score' => 0, //積分(考慮第一次注冊是否有積分)'logintime' => $time, //登錄時間'loginip' => $ip, //登錄IP'prevtime' => $time, //上次登錄時間'status' => 0 //狀態:0=正常,1=檢測中,2=已凍結,3=封號];//賬號注冊時需要開啟事務,避免出現垃圾數據Db::startTrans();try {//插入用戶表$userId = (new UserModel)->insertGetId($data);$rand1 = rand(10000000, 99999999);$rand2 = rand(10, 99);$username = 'SuLink-' . substr($rand1 . $userId . $rand2, -11); //取10位數//再設置用戶昵稱(new UserModel)->where('id', $userId)->update(['nickname' => $username, //昵稱'username' => $username, //用戶名'avatar' => letter_avatar($username)]);//插入登錄記錄(new UserLoginLogModel)->insert(['user_id' => $userId,'type' => $type,'user_agent' => \request()->header('User-Agent'),'login_ip' => $ip,]);//新增用戶設置表(new UserSettingModel)->insert(['user_id' => $userId]);//設置Token$token = Random::uuid();$redis->set($token, $userId, config('token.keep_time'));Db::commit();} catch (Exception $e) {Db::rollback();$this->error(MSG_ERR, null, $e->getMessage());}} else {$token = $this->loginSuccess($userId, $type, $time);}$this->success(MSG_OK, ['id' => $userId, 'mobile' => $mobile, 'token' => $token], '登錄成功');}

#####3.3、總結

  • 從上面我們可以提取出主要的部分,調用時候主要使用兩個部分就行了

1、發送驗證碼只需要簡單的調用短信服務類方法 sendSmsCode

//判斷業務類型 $smsCode = SmsService::sendSmsCode($mobile, $smsType, $userid);

2、驗證驗證碼只需要簡單的調用Redis服務類方法 checkSmsCode

//發送短信檢測 RedisService::checkSmsCode($mobile, $smsCode, RedisService::SU_SMS_LOGIN);

如果覺得那部分不清楚的可以評論發出提問,或者哪里寫的不好的也可以提出來。
謝謝大家的觀賞,樓上的方法都是本人親自封裝。

總結

以上是生活随笔為你收集整理的TP5 封装多业务的发送短信功能(包括国际短信)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。