Sms.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <?php
  2. /**
  3. * @link:http://www.gdqijianshi.com/
  4. * @copyright: Copyright (c) 2020 广东七件事集团
  5. * 短信信息
  6. * Author: zal
  7. * Date: 2020-04-18
  8. * Time: 15:11
  9. */
  10. namespace common\helpers\sms;
  11. use common\models\common\MallSetting;
  12. use common\models\common\SmsLog;
  13. use Overtrue\EasySms\EasySms;
  14. use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
  15. use Overtrue\EasySms\PhoneNumber;
  16. use yii\db\Exception;
  17. use common\models\common\PlatformSetting;
  18. class Sms
  19. {
  20. const SMS_TYPE_USER_LOGIN = 'user_login'; //类型:用户登录
  21. const SMS_TYPE_REGISTER = 'register'; //类型:注册
  22. const SMS_TYPE_UPDATE_PASS = 'update_pass'; //类型:修改登录密码
  23. const SMS_TYPE_UPDATE_PAY_PASS = 'pay_pass'; //类型:修改支付密码
  24. const SMS_TYPE_MOBILE_BIND = 'mobile_bind'; //类型:绑定手机号
  25. const SMS_TYPE_UPDATE_MOBILE = 'update_mobile'; //类型:修改手机号
  26. const SMS_TYPE_BANK_CARD_DELETE = 'card_delete'; //类型:删除银行卡
  27. const SMS_TYPE_REGISTER_INFO = 'register_info'; //类型:注册信息插件
  28. const SMS_TYPE_USER_CANCELLATION = 'user_cancellation'; //类型:注册信息插件
  29. public $config;
  30. public $smsConfig;
  31. public $easySms;
  32. public $minutes = 1;// 短信发送间隔(分钟)
  33. public static $validityMinutes = 5;// 验证码有效时间(分钟)
  34. public $gateways = 'domestic';// domestic:国内,international:国际
  35. public $mall_id = 0;
  36. public $sms_status;
  37. public function __construct($mall_id='', $gateways = 'domestic', $type = 'mall')
  38. {
  39. $this->config = [
  40. // HTTP 请求的超时时间(秒)
  41. 'timeout' => 5.0,
  42. // 默认发送配置
  43. 'default' => [
  44. // 网关调用策略,默认:顺序调用
  45. 'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class,
  46. // 默认可用的发送网关
  47. 'gateways' => [
  48. 'aliyun',
  49. 'qcloud',
  50. 'submail',
  51. ],
  52. ],
  53. // 可用的网关配置
  54. 'gateways' => [
  55. 'errorlog' => [
  56. 'file' => '/runtime/easy-sms.log',
  57. ],
  58. //...
  59. ],
  60. ];
  61. $this->mall_id = $mall_id;
  62. // 阿里云短信配置
  63. if($type == 'mall'){
  64. $data = MallSetting::getConfByGroup($mall_id, ['notice_sms'], true);
  65. $notice_sms = $data['notice_sms'];
  66. }else{
  67. $notice_sms = PlatformSetting::getConfByGroup('sms', true);
  68. }
  69. $this->sms_status = $notice_sms['status']['value'] ?? 0;
  70. $this->gateways = $gateways;
  71. if (!empty($notice_sms)) {
  72. switch ($notice_sms['platform']['value']) {
  73. case 'aliyun'://阿里云配置
  74. (new SmsConfig())->aliyunConfig($notice_sms, $this->config, $this->smsConfig, $this->gateways, $type);
  75. break;
  76. case 'qcloud'://腾讯云配置
  77. (new SmsConfig())->qcloudConfig($notice_sms, $this->config, $this->smsConfig, $this->gateways, $type);
  78. break;
  79. case 'submail'://赛邮云配置
  80. (new SmsConfig())->submailConfig($notice_sms, $this->config, $this->smsConfig, $this->gateways, $type);
  81. break;
  82. case 'yunpian'://云片配置
  83. // (new SmsConfig())->yunpianConfig($notice_sms, $this->config, $this->smsConfig, $this->gateways, $type);
  84. break;
  85. }
  86. } else {
  87. //默认的短信验证码配置
  88. $this->config['gateways']['aliyun'] = [
  89. 'access_key_id' => \Yii::$app->params['aliyun']['access_key_id']??'',
  90. 'access_key_secret' =>\Yii::$app->params['aliyun']['access_key_secret']??'',
  91. 'sign_name' => \Yii::$app->params['aliyun']['sign_name']??'',
  92. ];
  93. $this->smsConfig['captcha'] = [
  94. 'template_id'=>\Yii::$app->params['aliyun']['captcha']['template_id']??'',
  95. 'template_variable'=>\Yii::$app->params['aliyun']['captcha']['template_variable']??'',
  96. ];
  97. }
  98. $this->easySms = new EasySms($this->config);
  99. }
  100. /**
  101. * 发送短信验证码
  102. * @param string $mobile
  103. * @return bool
  104. * @throws NoGatewayAvailableException
  105. * @throws \Exception
  106. */
  107. public function sendCaptcha(string $mobile = '', $area_code = '',$type='')
  108. {
  109. try {
  110. if (empty($this->sms_status)) throw new \Exception('短信功能尚未开通');
  111. $captcha = (string)mt_rand(100000, 999999);
  112. $message = new CaptchaMessage($captcha, $this->smsConfig['captcha']);
  113. $send_mobile = $mobile;
  114. if (!empty($area_code) && $mobile != 86) {
  115. $send_mobile = new PhoneNumber($mobile, $area_code);
  116. }
  117. if(SmsLog::setAliSms($mobile, $this->mall_id) === false) throw new \Exception(SmsLog::getStaticError());// 短信记录
  118. $results = $this->easySms->send($send_mobile, $message);
  119. \Yii::$app->cache->set($mobile.'_'.$type, $captcha, self::$validityMinutes * 60);
  120. return true;
  121. } catch (NoGatewayAvailableException $e) {
  122. $res = $e->getExceptions();
  123. if(!empty($res)){
  124. foreach ($res as $exception){
  125. if(is_string($exception->getMessage())){
  126. throw new Exception($exception->getMessage());
  127. \Yii::error('短信发送失败:' .$exception->getMessage());
  128. }
  129. }
  130. }
  131. return false;
  132. }
  133. }
  134. public function sendNewUser(string $mobile = '', $nickname='', $area_code = '')
  135. {
  136. if(empty($this->smsConfig['new_user']['template_id']) && empty($this->smsConfig['new_user']['project'])) throw new Exception('模板ID不能为空');
  137. if(empty($this->smsConfig['new_user']['template_variable'])) throw new Exception('模板签名不能为空');
  138. if (empty($this->sms_status)) throw new \Exception('短信功能尚未开通');
  139. try {
  140. $message = new CaptchaMessage($nickname, $this->smsConfig['new_user']);
  141. $send_mobile = $mobile;
  142. if (!empty($area_code) && $mobile != 86) {
  143. $send_mobile = new PhoneNumber($mobile, $area_code);
  144. }
  145. if(SmsLog::setAliSms($mobile, $this->mall_id, 'newuser') === false) throw new \Exception(SmsLog::getStaticError());
  146. $this->easySms->send($send_mobile, $message);
  147. return true;
  148. } catch (NoGatewayAvailableException $e) {
  149. $res = $e->getExceptions();
  150. if(!empty($res)){
  151. foreach ($res as $exception){
  152. if(is_string($exception->getMessage())){
  153. \Yii::error('短信发送失败:' .$exception->getMessage());
  154. }
  155. }
  156. }
  157. return false;
  158. }
  159. }
  160. public function sendOrderOn(string $mobile = '', $order_no='', $area_code = '')
  161. {
  162. if(empty($this->smsConfig['order']['template_id']) && empty($this->smsConfig['order']['project'])) throw new Exception('模板ID不能为空');
  163. if(empty($this->smsConfig['order']['template_variable'])) throw new Exception('模板签名不能为空');
  164. if (empty($this->sms_status)) throw new \Exception('短信功能尚未开通');
  165. try {
  166. $message = new CaptchaMessage($order_no, $this->smsConfig['order']);
  167. $send_mobile = $mobile;
  168. if (!empty($area_code) && $mobile != 86) {
  169. $send_mobile = new PhoneNumber($mobile, $area_code);
  170. }
  171. if(SmsLog::setAliSms($mobile, $this->mall_id, 'order') === false) throw new \Exception(SmsLog::getStaticError());
  172. $this->easySms->send($send_mobile, $message);
  173. return true;
  174. } catch (NoGatewayAvailableException $e) {
  175. $res = $e->getExceptions();
  176. if(!empty($res)){
  177. foreach ($res as $exception){
  178. if(is_string($exception->getMessage())){
  179. \Yii::error('短信发送失败:' .$exception->getMessage());
  180. }
  181. }
  182. }
  183. return false;
  184. }
  185. }
  186. public function sendRefundOrderOn(string $mobile = '', $order_no='', $area_code = '')
  187. {
  188. if(empty($this->smsConfig['order_refund']['template_id']) && empty($this->smsConfig['order_refund']['project'])) throw new Exception('模板ID不能为空');
  189. if(empty($this->smsConfig['order_refund']['template_variable'])) throw new Exception('模板签名不能为空');
  190. if (empty($this->sms_status)) throw new \Exception('短信功能尚未开通');
  191. try {
  192. $message = new CaptchaMessage($order_no, $this->smsConfig['order_refund']);
  193. $send_mobile = $mobile;
  194. if (!empty($area_code) && $mobile != 86) {
  195. $send_mobile = new PhoneNumber($mobile, $area_code);
  196. }
  197. if(SmsLog::setAliSms($mobile, $this->mall_id, 'refund_order') === false) throw new \Exception(SmsLog::getStaticError());
  198. $this->easySms->send($send_mobile, $message);
  199. return true;
  200. } catch (NoGatewayAvailableException $e) {
  201. $res = $e->getExceptions();
  202. if(!empty($res)){
  203. foreach ($res as $exception){
  204. if(is_string($exception->getMessage())){
  205. \Yii::error('短信发送失败:' .$exception->getMessage());
  206. }
  207. }
  208. }
  209. return false;
  210. }
  211. }
  212. /**
  213. * 会员到期短信提醒
  214. *
  215. * @param string $mobile
  216. * @param string $nickname
  217. * @param string $area_code
  218. *
  219. * @return bool
  220. * @throws Exception
  221. * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
  222. */
  223. public function sendUserLevelExpired(string $mobile = '', string $nickname = '', string $area_code = '')
  224. {
  225. if(empty($this->smsConfig['user_level_expired']['template_id']) && empty($this->smsConfig['user_level_expired']['project'])) throw new Exception('模板ID不能为空');
  226. if(empty($this->smsConfig['user_level_expired']['template_variable'])) throw new Exception('模板签名不能为空');
  227. if (empty($this->sms_status)) throw new \Exception('短信功能尚未开通');
  228. try {
  229. $message = new CaptchaMessage($nickname, $this->smsConfig['user_level_expired']);
  230. $send_mobile = $mobile;
  231. if (!empty($area_code) && $area_code != 86) {
  232. $send_mobile = new PhoneNumber($mobile, $area_code);
  233. }
  234. if(SmsLog::setAliSms($mobile, $this->mall_id, 'user_level_expired') === false) throw new \Exception(SmsLog::getStaticError());
  235. $this->easySms->send($send_mobile, $message);
  236. return true;
  237. } catch (NoGatewayAvailableException $e) {
  238. $res = $e->getExceptions();
  239. if(!empty($res)){
  240. foreach ($res as $exception){
  241. if(is_string($exception->getMessage())){
  242. \Yii::error('短信发送失败:' .$exception->getMessage());
  243. }
  244. }
  245. }
  246. return false;
  247. }
  248. }
  249. /**
  250. * 检测短信验证码是否有效
  251. * @param $mobile
  252. * @param $code
  253. * @return bool
  254. * @throws
  255. */
  256. public static function checkValidateCode($mobile, $code,$type='')
  257. {
  258. //限制验证码错误次数
  259. $redis = \Yii::$app->redis;
  260. $error_key = 'validate_code_error_num_'.$mobile.'_'.$type;
  261. $error_num = $redis->get($error_key) ?? 0;
  262. if($error_num > 2) throw new \Exception('验证码不正确,请5分钟后重试');
  263. $mobile = (string)$mobile;
  264. $no_type = '';
  265. //|| \Yii::$app->cache->get($mobile.'_'.$no_type) == $code
  266. if ( !empty(\Yii::$app->cache->get($mobile.'_'.$type)) && \Yii::$app->cache->get($mobile.'_'.$type) == $code) {
  267. \Yii::$app->cache->delete($mobile.'_'.$type);
  268. return true;
  269. }else{
  270. $redis->incr($error_key);
  271. $redis->expire($error_key,300);
  272. }
  273. return false;
  274. }
  275. /**
  276. * 支付成功发送短信
  277. * @Author: lun
  278. * @DateTime: 2022/6/22 0022 15:06
  279. * @Copyright: copyright (c) 2021 广东七件事集团
  280. * @param string $order_no
  281. * @param string $mobile
  282. * @param string $area_code
  283. * @return bool
  284. * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
  285. */
  286. public function sendOrder($order_no, $mobile = '', $area_code = '')
  287. {
  288. try {
  289. if (empty($this->sms_status)) throw new \Exception('短信功能尚未开通');
  290. if (empty($mobile)) return true;
  291. if (empty($this->smsConfig['order']['template_id']) && empty($this->smsConfig['order']['project'])) return true;
  292. $message = new CaptchaMessage($order_no, $this->smsConfig['order']);
  293. if (!empty($area_code) && $area_code != 86) {
  294. $mobile = new PhoneNumber($mobile, $area_code);
  295. }
  296. if(SmsLog::setAliSms($mobile, $this->mall_id, 'order') === false) throw new \Exception(SmsLog::getStaticError());
  297. $this->easySms->send($mobile, $message);
  298. return true;
  299. } catch (NoGatewayAvailableException $e) {
  300. $res = $e->getExceptions();
  301. if(!empty($res)){
  302. foreach ($res as $exception){
  303. if(is_string($exception->getMessage())){
  304. \Yii::error('短信发送失败:' .$exception->getMessage());
  305. }
  306. }
  307. }
  308. return false;
  309. }
  310. }
  311. }