SmsServer.php 5.0 KB

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