SmsService.php 5.6 KB

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