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

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;
}
}