You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.3 KiB
PHTML
121 lines
3.3 KiB
PHTML
2 years ago
|
<?php
|
||
|
|
||
|
declare(strict_types=1);
|
||
|
|
||
|
namespace App\Service;
|
||
|
|
||
|
use App\Contract\SmsSender;
|
||
|
use App\Model\Sms;
|
||
|
use App\Exception\BusinessException;
|
||
|
use App\Helper\Log;
|
||
|
|
||
|
class SmsService extends Service
|
||
|
{
|
||
|
const INTERVAL_MINUTE = 1;
|
||
|
const IP_DAY_COUNT = 100;
|
||
|
|
||
|
public function send($mobile, $content)
|
||
|
{
|
||
|
$smsSender = make(SmsSender::class);
|
||
|
|
||
|
if (!$smsSender) {
|
||
|
throw new BusinessException('系统异常,请联系客服');
|
||
|
}
|
||
|
$smsSender->send($mobile, $content);
|
||
|
}
|
||
|
|
||
|
public function sendBatch(array $mobile, $content)
|
||
|
{
|
||
|
$smsSender = make(SmsSender::class);
|
||
|
|
||
|
if (!$smsSender) {
|
||
|
throw new BusinessException('系统异常,请联系客服');
|
||
|
}
|
||
|
|
||
|
$smsSender->sendBatch($mobile, $content);
|
||
|
}
|
||
|
|
||
|
public function sendCode($mobile, array $options)
|
||
|
{
|
||
|
$clientIp = $options['client_ip'] ?? '';
|
||
|
$smsSender = make(SmsSender::class);
|
||
|
|
||
|
if (!$smsSender) {
|
||
|
throw new BusinessException('系统异常,请联系客服');
|
||
|
}
|
||
|
if ($clientIp && $this->isIpOutOfDayCount($clientIp)) {
|
||
|
throw new BusinessException('每天发送数量不能超过' . self::IP_DAY_COUNT . '条');
|
||
|
}
|
||
|
if ($this->isOutOfRate($mobile)) {
|
||
|
throw new BusinessException('发送过于频繁,请稍后再试');
|
||
|
}
|
||
|
|
||
|
$code = $this->getCode();
|
||
|
$sms = new Sms();
|
||
|
$sms->code = $code;
|
||
|
$sms->mobile = $mobile;
|
||
|
$sms->client_ip = $clientIp;
|
||
|
$sms->channel = $smsSender->channel;
|
||
|
$sms->save();
|
||
|
|
||
|
try {
|
||
|
$smsSender->sendCode($mobile, $code, Sms::ACTIVE_MINUTE);
|
||
|
$sms->status = Sms::STATUS_SUCCESS;
|
||
|
$sms->result = 'success';
|
||
|
$sms->save();
|
||
|
} catch (\Exception $e) {
|
||
|
$sms->status = Sms::STATUS_ERROR;
|
||
|
$sms->result = 'error';
|
||
|
$sms->save();
|
||
|
Log::error(
|
||
|
'SMS_SEND_ERROR: ' . $e->getMessage(),
|
||
|
['mobile' => $mobile, 'options' => $options],
|
||
|
'sms'
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function check($mobile, $code): bool
|
||
|
{
|
||
|
$sms = Sms::where('mobile', $mobile)
|
||
|
->where('is_checked', 0)
|
||
|
->where('status', Sms::STATUS_SUCCESS)
|
||
|
->orderBy('created_at', 'desc')
|
||
|
->first();
|
||
|
if (!$sms) {
|
||
|
return false;
|
||
|
}
|
||
|
if ($sms->isExpired()) {
|
||
|
return false;
|
||
|
}
|
||
|
if ($sms->code == $code) {
|
||
|
$sms->is_checked = 1;
|
||
|
$sms->save();
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private function getCode()
|
||
|
{
|
||
|
return rand(100000, 999999);
|
||
|
}
|
||
|
|
||
|
private function isOutOfRate($mobile): bool
|
||
|
{
|
||
|
$lastSms = Sms::select(['created_at'])->where(['mobile' => $mobile])->orderBy('created_at', 'desc')->first();
|
||
|
if ($lastSms && strtotime($lastSms->created_at) + self::INTERVAL_MINUTE * 60 > time()) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private function isIpOutOfDayCount($ip): bool
|
||
|
{
|
||
|
$count = Sms::where('client_ip', $ip)
|
||
|
->where('status', Sms::STATUS_SUCCESS)
|
||
|
->whereBetween('created_at', [date('Y-m-d 00:00:00'), date('Y-m-d 23:59:59')])
|
||
|
->count();
|
||
|
return $count > self::IP_DAY_COUNT;
|
||
|
}
|
||
|
}
|