SmsServer.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <?php
  2. namespace App\Services;
  3. use App\Exceptions\SmsException;
  4. use Illuminate\Support\Facades\Redis;
  5. use PHPUnit\Util\Exception;
  6. use App\Models\SmsRecord;
  7. use Illuminate\Support\Str;
  8. use Overtrue\EasySms\EasySms;
  9. //use Overtrue\EasySms\Exceptions\Exception;
  10. use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
  11. class SmsServer
  12. {
  13. const EVENT_LOGIN = 100;
  14. const DEFAULT_SMS_CODE_LENGTH = 4;
  15. const DEFAULT_SMS_CODE_EXP_TIME = 5*60;
  16. const EVENTS_TO_TEMPLATE = [
  17. '63' => [ //菲律宾
  18. 'login' => 'SMS_234414831',
  19. 'forget' => 'SMS_234414831',
  20. ],
  21. '86' => [ //中国
  22. //'login' => 'SMS_137515003',
  23. //'forget' => 'SMS_137515003',
  24. 'login' => 'SMS_227262168',
  25. 'forget' => 'SMS_227262168',
  26. ],
  27. ];
  28. const EVENT_ITEM_FUNCTION = [];
  29. /**
  30. * 获取easySms配置
  31. * @return \Illuminate\Config\Repository|\Illuminate\Contracts\Foundation\Application|mixed
  32. */
  33. static public function getEasySmsConfig()
  34. {
  35. return config('easysms');
  36. }
  37. /**
  38. * 获取 easySms 实例
  39. * @return EasySms
  40. */
  41. static public function easySms()
  42. {
  43. return new EasySms(self::getEasySmsConfig());
  44. }
  45. static public function send(int $mobile, string $event, int $prefix)
  46. {
  47. try {
  48. $templateId = self::getTemplateIdByEvent($prefix, $event);
  49. $smsCode = self::getSmsCode();
  50. //注意! 实际发送的电话号码,要加 国际号码前缀
  51. $result = self::easySms()->send($prefix.$mobile, [
  52. 'template' => $templateId,
  53. 'content' => 'null',
  54. 'data' => [
  55. 'code' => $smsCode,
  56. 'expTime' => 5,
  57. ]
  58. ]);
  59. //缓存验证码
  60. $verifyKey = self::getVerifyKey();
  61. self::cacheSmsCodeByVerifyKey($mobile, $smsCode, $verifyKey);
  62. //存入发送记录
  63. self::storeSmsRecord($prefix, $mobile, $event, $smsCode, $verifyKey, $result);
  64. return [
  65. 'verifyKey' => $verifyKey,
  66. 'smsCode' => $smsCode
  67. ];
  68. } catch (SmsException $e) {
  69. return ['error' => $e->getMessage()];
  70. } catch (NoGatewayAvailableException $e) {
  71. return ['error' => trans('api.SMS_SENDING_FAILED')];
  72. } catch (Exception $e) {
  73. return ['error' => trans('api.SMS_SENDING_FAILED')];
  74. }
  75. }
  76. /**
  77. * 生成随机字符串用户校验验证码
  78. * @param int $length
  79. * @return string
  80. */
  81. static public function getVerifyKey(int $length = 10)
  82. {
  83. return Str::random($length);
  84. }
  85. /**
  86. * 生成短信验证码
  87. * @param int $length
  88. * @return int
  89. */
  90. static public function getSmsCode($length = self::DEFAULT_SMS_CODE_LENGTH)
  91. {
  92. return rand(1000, 9999);
  93. }
  94. /**
  95. * 根据验证码,verifyKey 校验是否正确
  96. * @param string $verifyKey
  97. * @param string $smsCode
  98. * @return bool
  99. * @throws SmsException
  100. */
  101. static public function checkSmsCodeByVerifyKey(string $verifyKey, string $smsCode)
  102. {
  103. $record = Redis::get('smsCode:verifyCode' . $verifyKey);
  104. if (empty($record)) {
  105. throw new Exception('验证码过期或已失效');
  106. }
  107. $record = json_decode($record, true);
  108. if ($smsCode != $record['smsCode']) {
  109. throw new Exception('验证码错误');
  110. }
  111. Redis::del('smsCode:verifyCode' . $verifyKey);
  112. return true;
  113. }
  114. /**
  115. * 缓存短信验证码
  116. * @param int $mobile
  117. * @param string $smsCode
  118. * @param string $verifyKey
  119. * @param int $expTime
  120. * @return mixed
  121. */
  122. static public function cacheSmsCodeByVerifyKey(int $mobile, string $smsCode, string $verifyKey, int $expTime = self::DEFAULT_SMS_CODE_EXP_TIME)
  123. {
  124. return Redis::setex('smsCode:verifyCode' . $verifyKey, $expTime, json_encode(['mobile' => $mobile, 'smsCode' => $smsCode]));
  125. }
  126. /**
  127. * 根据事件获取 短信模板ID
  128. * @param string $event
  129. * @return string
  130. * @throws SmsException
  131. */
  132. static public function getTemplateIdByEvent($prefix, string $event)
  133. {
  134. //self::verifySmsEvent($event);
  135. return self::EVENTS_TO_TEMPLATE[$prefix][$event];
  136. }
  137. /**
  138. * 验证发送事件
  139. * @param string $event
  140. * @return bool
  141. * @throws SmsException
  142. */
  143. static public function verifySmsEvent(string $event)
  144. {
  145. if (!array_key_exists($event, self::EVENTS_TO_TEMPLATE)) {
  146. throw new Exception(trans('api.WRONG_EVENT_TYPE'));
  147. }
  148. return true;
  149. }
  150. /**
  151. * @param string $prefix
  152. * @param int $mobile
  153. * @param string $event
  154. * @param string $smsCode
  155. * @param string $verifyKey
  156. * @param $sendResult
  157. * @return mixed
  158. */
  159. static public function storeSmsRecord(string $prefix, int $mobile, string $event, string $smsCode, string $verifyKey, $sendResult)
  160. {
  161. return SmsRecord::create([
  162. 'prefix' => $prefix,
  163. 'mobile' => $mobile,
  164. 'event' => $event,
  165. 'sms_code' => $smsCode,
  166. 'verify_key' => $verifyKey,
  167. 'sms_result' => $sendResult
  168. ]);
  169. }
  170. }