SmsServer.php 5.5 KB

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