| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- <?php
- namespace App\Repositories\Utils;
- use App\Constants\PatternConstant;
- use Carbon\Carbon;
- use Psr\SimpleCache\CacheInterface;
- use Hyperf\Guzzle\ClientFactory;
- use App\Repositories\ConfigRepoistory;
- class SmsRepository
- {
- /**
- * 缓存仓库
- * @var null|CacheInterface
- */
- private $cacheRepo = null;
- /**
- * http客户端仓库
- * @var null|\GuzzleHttp\Client
- */
- private $httpClient = null;
- private $configRepo = null;
- /**
- * 缓存前缀
- * @var string
- */
- public static $cachePrefix = 'sms:';
- /**
- * 实效性.
- * @var int
- */
- public static $cacheTimeoutMinutes = 5;
- /**
- * 发送校验短信
- * @param string $mobile 手机号
- * @param number $code 短信code
- * @return array
- * @throws \GuzzleHttp\Exception\GuzzleException|array
- * @throws \Psr\SimpleCache\InvalidArgumentException
- */
- public function sendVerify($mobile, $code)
- {
- $type = 'verify_text';
- $result = ['code' => 200, 'message' => '', 'data' => []];
- $lastDateKey = self::$cachePrefix . $type . ':' . $mobile . ':last_date';
- $lastDate = $this->getCacheRepo()->get($lastDateKey);
- $lastDateObj = Carbon::parse($lastDate);
- $nowDateObj = Carbon::now();
- //比较发送频率
- if (!empty($lastDate) && abs($nowDateObj->diffInSeconds($lastDateObj)) <= 30) {
- $result['code'] = '601011';
- $result['message'] = '请间隔30秒发送短信';
- return $result;
- }
- $sentLogKey = self::$cachePrefix . $type . ':'. $mobile. ':log';
- //去掉1小时前的发送记录
- $sendLogs = $this->getCacheRepo()->get($sentLogKey, []);
- if (!empty($sendLogs)) {
- foreach ($sendLogs as $k => $logDate) {
- if (abs($nowDateObj->diffInMinutes(Carbon::parse($logDate))) >= 60) {
- // 剔除超过1小时的记录.
- unset($sendLogs[$k]);
- }
- }
- }
- // 1小时3次限制
- if (count($sendLogs) >= 3) {
- $result['code'] = '601012';
- $result['message'] = '一小时之内验证码的发送次数不得超过3次';
- return $result;
- }
- if (!self::isChinaMobile($mobile)) {
- $result['code'] = '600012';
- $result['message'] = '仅限发送中国大陆手机号';
- return $result;
- }
- $code = $this->createCode($mobile, $type,$code);
- // 记录会话时间
- $this->getCacheRepo()->set($lastDateKey, Carbon::now()->toDateTimeString(), 2 * 60 * 60);
- //发送短信 + 1
- if ($result['code'] == 200) {
- $arrConfig = $this->getConfigRepo()->gets(['tg_api_token', 'tg_msg_server'], 'sys', []);
- $token = data_get($arrConfig, 'tg_api_token', '');
- $sendLogs[] = Carbon::now()->toDateTimeString();
- $this->getCacheRepo()->set($sentLogKey, $sendLogs, 24 * 60 * 60);
- $uri = '/api/sms/send/verify';
- $options = [
- 'query' => [
- 'AUTH-TOKEN' => $token,
- ],
- 'json' => [
- 'mobile' => $mobile,
- 'code' => $code,
- ],
- ];
- $ret = $this->getHttpClient()->post($uri, $options);
- $httpStatus = $ret->getStatusCode();
- if ($httpStatus == 200) {
- $arrResp = json_decode($ret->getBody()->getContents(), true);
- $result['code'] = $arrResp['code'];
- $result['message'] = $arrResp['message'];
- $result['data'] = $arrResp['data'];
- }
- }
- return $result;
- }
- /**
- * 创建CODE
- * @param int|string $mobile 手机号
- * @param string $type 类型
- * @param number $code 短信验验证码
- * @return mixed
- */
- public function createCode($mobile, $type, $code)
- {
- $cacheKey = self::$cachePrefix . $type . ':' . $mobile;
- $this->getCacheRepo()->set($cacheKey, $code, self::$cacheTimeoutMinutes * 60);
- return $code;
- }
- /**
- * 获取Code
- * @param int|string $mobile 手机号
- * @param string $type 类型
- * @return mixed
- */
- public function getCode($mobile, $type)
- {
- $cacheKey = self::$cachePrefix . $type . ':' . $mobile;
- return $this->getCacheRepo()->get($cacheKey, null);
- }
- /**
- * 检查Code
- * @param int|string $code 校验码
- * @param int|string $mobile 手机号
- * @param string $type 类型
- * @return bool
- */
- public function checkCode($code, $mobile, $type)
- {
- $cacheCode = $this->getCode($mobile, $type);
- if ($cacheCode) {
- $cacheCode = (string)$cacheCode;
- if ($cacheCode === (string)($code)) {
- // 认证了就马上删除.
- $this->deleteCode($mobile, $type);
- return true;
- }
- }
- return false;
- }
- /**
- * 检查Code但不删除code
- * @param int|string $code 校验码
- * @param int|string $mobile 手机号
- * @param string $type 类型
- * @return bool
- */
- public function checkCodeNotDelete($code, $mobile, $type)
- {
- $cache_code = $this->getCode($mobile, $type);
- if ($cache_code) {
- $cache_code = (string)$cache_code;
- if ($cache_code === (string)($code)) {
- return true;
- }
- }
- return false;
- }
- /**
- * 删除Code
- * @param int|string $mobile 手机号
- * @param string $type 类型
- */
- public function deleteCode($mobile, $type)
- {
- $cacheKey = self::$cachePrefix . $type . ':' . $mobile;
- $this->getCacheRepo()->forget($cacheKey);
- }
- /**
- * 是否是中国手机号.
- * @param string $number
- * @return bool
- */
- public function isChinaMobile($number) :bool
- {
- return boolval(preg_match(PatternConstant::CHINA_MOBILE, $number));
- }
- /**
- * 获取缓存仓库.
- * @return mixed|CacheInterface
- */
- private function getCacheRepo()
- {
- if (!$this->cacheRepo instanceof CacheInterface) {
- $this->cacheRepo = make(CacheInterface::class);
- }
- return $this->cacheRepo;
- }
- /**
- * @return \GuzzleHttp\Client|ClientFactory|null
- */
- public function getHttpClient()
- {
- if (!$this->httpClient instanceof ClientFactory) {
- $arrConfig = $this->getConfigRepo()->gets(['tg_api_token', 'tg_msg_server'], 'sys', []);
- $token = data_get($arrConfig, 'tg_api_token', '');
- $baseUrl = rtrim(data_get($arrConfig, 'tg_msg_server', ''), '/');
- $options = array(
- 'base_uri' => $baseUrl,
- 'timeout' => 15, // 超时,单位秒.
- 'verify' => false, // 是否校验SSL证书.
- 'http_errors' => false, // 如果跟随http异常头部是否中断.
- 'debug' => false, // 是否启用调试模式.
- 'allow_redirects' => false, // 不跟随301/302跳转.
- 'headers' => [
- 'AUTH-TOKEN' => $token,
- ],
- );
- $this->httpClient = make(ClientFactory::class)->create($options);
- }
- return $this->httpClient;
- }
- /**
- * @return ConfigRepoistory|mixed
- */
- private function getConfigRepo()
- {
- if (!$this->configRepo instanceof ConfigRepoistory) {
- $this->configRepo = make(ConfigRepoistory::class);
- }
- return $this->configRepo;
- }
- }
|