SmsService.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <?php
  2. namespace App\Services;
  3. use App\Exceptions\SmsException;
  4. use App\Models\SmsRecord;
  5. use Illuminate\Support\Facades\Redis;
  6. use Illuminate\Support\Str;
  7. use Overtrue\EasySms\EasySms;
  8. use Overtrue\EasySms\Exceptions\Exception;
  9. use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
  10. class SmsService
  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. //'login' => 377532, //好邻盟短信验证码为:{1},请在{2}分钟内输入,如非本人操作,请忽略本短信
  17. 'login' => "SMS_187241612", //好邻盟短信验证码为:{1},请在{2}分钟内输入,如非本人操作,请忽略本短信
  18. 'forget' => 377532, //好邻盟短信验证码为:{1},请在{2}分钟内输入,如非本人操作,请忽略本短信
  19. ];
  20. const EVENT_ITEM_FUNCTION = [
  21. ];
  22. /**
  23. * 获取easySms配置
  24. * @return \Illuminate\Config\Repository|\Illuminate\Contracts\Foundation\Application|mixed
  25. */
  26. static public function getEasySmsConfig()
  27. {
  28. return config('easysms');
  29. }
  30. /**
  31. * 获取 easySms 实例
  32. * @return EasySms
  33. */
  34. static public function easySms()
  35. {
  36. return new EasySms(self::getEasySmsConfig());
  37. }
  38. static public function send(int $mobile, string $event, array $eventData, $user = null)
  39. {
  40. try {
  41. self::verifySmsEvent($event);
  42. $templateId = self::getTemplateIdByEvent($event);
  43. $smsCode = self::getSmsCode();
  44. $result = self::easySms()->send($mobile, [
  45. 'template' => $templateId,
  46. 'content' => 'null',
  47. 'data' => [
  48. 'code' => $smsCode,
  49. // 'expTime' => 10,
  50. ]
  51. ]);
  52. //$result = true;
  53. //缓存验证码
  54. $verifyKey = self::getVerifyKey();
  55. self::cacheSmsCodeByVerifyKey($mobile, $smsCode, $verifyKey);
  56. //存入发送记录
  57. self::storeSmsRecord($mobile, $event, $smsCode, $verifyKey, $result);
  58. return [
  59. 'verifyKey' => $verifyKey,
  60. 'code' => $smsCode,
  61. ];
  62. } catch (SmsException $e) {
  63. return ['error' => $e->getMessage()];
  64. } catch (NoGatewayAvailableException $e) {
  65. // return ['error' => json_encode($e)];
  66. return ['error' => '发送短信失败'];
  67. } catch (Exception $e) {
  68. return ['error' => '发送短信失败'];
  69. }
  70. }
  71. /**
  72. * 生成随机字符串用户校验验证码
  73. * @param int $length
  74. * @return string
  75. */
  76. static public function getVerifyKey(int $length = 10)
  77. {
  78. return Str::random($length);
  79. }
  80. /**
  81. * 生成短信验证码
  82. * @param int $length
  83. * @return int
  84. */
  85. static public function getSmsCode(int $length = self::DEFAULT_SMS_CODE_LENGTH)
  86. {
  87. return rand(1000, 9999);
  88. }
  89. /**
  90. * 根据验证码,verifyKey 校验是否正确
  91. * @param string $verifyKey
  92. * @param string $smsCode
  93. * @return bool
  94. * @throws SmsException
  95. */
  96. static public function checkSmsCodeByVerifyKey(string $verifyKey, string $smsCode)
  97. {
  98. if (!RedisService::redis()->exists('smsCode:verifyCode' . $verifyKey)) {
  99. throw new SmsException('验证码已过期');
  100. }
  101. $record = RedisService::redis()->get('smsCode:verifyCode' . $verifyKey);
  102. if (!json_decode($record, true)) {
  103. throw new SmsException('验证码解析失败');
  104. }
  105. $record = json_decode($record, true);
  106. if ($smsCode != $record['smsCode']) {
  107. throw new SmsException('验证码错误');
  108. }
  109. RedisService::redis()->del('smsCode:verifyCode' . $verifyKey);
  110. return true;
  111. }
  112. /**
  113. * 缓存短信验证码
  114. * @param int $mobile
  115. * @param string $smsCode
  116. * @param string $verifyKey
  117. * @param int $expTime
  118. * @return mixed
  119. */
  120. static public function cacheSmsCodeByVerifyKey(int $mobile, string $smsCode, string $verifyKey, int $expTime = self::DEFAULT_SMS_CODE_EXP_TIME)
  121. {
  122. return RedisService::redis()->SETEX('smsCode:verifyCode' . $verifyKey, $expTime, json_encode(['mobile' => $mobile, 'smsCode' => $smsCode]));
  123. }
  124. /**
  125. * 根据事件获取 短信模板ID
  126. * @param string $event
  127. * @return string
  128. * @throws SmsException
  129. */
  130. static public function getTemplateIdByEvent(string $event)
  131. {
  132. self::verifySmsEvent($event);
  133. return self::EVENTS_TO_TEMPLATE[$event];
  134. }
  135. /**
  136. * 验证发送事件
  137. * @param string $event
  138. * @return bool
  139. * @throws SmsException
  140. */
  141. static public function verifySmsEvent(string $event)
  142. {
  143. if (!array_key_exists($event, self::EVENTS_TO_TEMPLATE)) {
  144. throw new SmsException('事件类型错误');
  145. }
  146. return true;
  147. }
  148. /**
  149. * 将发送记录存到数据库以便调试
  150. * @param int $mobile
  151. * @param string $event
  152. * @param string $smsCode
  153. * @param string $verifyKey
  154. * @param $sendResult
  155. * @return mixed
  156. */
  157. static public function storeSmsRecord(int $mobile, string $event, string $smsCode, string $verifyKey, $sendResult)
  158. {
  159. return SmsRecord::create([
  160. 'mobile' => $mobile,
  161. 'event' => $event,
  162. 'sms_code' => $smsCode,
  163. 'verify_key' => $verifyKey,
  164. 'sms_result' => $sendResult
  165. ]);
  166. }
  167. }