You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
7.1 KiB
232 lines
7.1 KiB
<?php
|
|
|
|
namespace App\Services\Sms;
|
|
|
|
use App\Models\AudienceRegistration;
|
|
use App\Models\AudienceSmsLog;
|
|
use App\Support\SmsConfig;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class AudienceRegistrationSmsNotifier
|
|
{
|
|
public const SCENE_PRE_REGISTER = AudienceSmsLog::SCENE_PRE_REGISTER;
|
|
|
|
public const SCENE_APPROVED = AudienceSmsLog::SCENE_APPROVED;
|
|
|
|
public function __construct(private readonly SubmailSmsClient $client)
|
|
{
|
|
}
|
|
|
|
public function notifyPreRegister(AudienceRegistration $registration): ?AudienceSmsLog
|
|
{
|
|
$registration->loadMissing('session');
|
|
$vars = [
|
|
'session' => $this->clip((string) ($registration->session?->name ?? ''), 40),
|
|
];
|
|
|
|
return $this->send(
|
|
$registration,
|
|
AudienceSmsLog::SCENE_PRE_REGISTER,
|
|
trim((string) config('sms.submail.audience_pre_register_template_id')),
|
|
$vars,
|
|
(string) config('sms.submail.audience_pre_register_content'),
|
|
'sms.audience_pre_register'
|
|
);
|
|
}
|
|
|
|
public function notifyApproved(AudienceRegistration $registration): ?AudienceSmsLog
|
|
{
|
|
$registration->loadMissing('session');
|
|
$vars = [
|
|
'session' => $this->clip((string) ($registration->session?->name ?? ''), 40),
|
|
'time' => $this->clip((string) ($registration->approved_event_time_text ?? ''), 40),
|
|
'address' => $this->clip((string) ($registration->approved_event_address_text ?? ''), 40),
|
|
'url' => $this->ticketUrl($registration),
|
|
];
|
|
|
|
return $this->send(
|
|
$registration,
|
|
AudienceSmsLog::SCENE_APPROVED,
|
|
trim((string) config('sms.submail.audience_approved_template_id')),
|
|
$vars,
|
|
(string) config('sms.submail.audience_approved_content'),
|
|
'sms.audience_approved'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, string> $vars
|
|
*/
|
|
private function send(
|
|
AudienceRegistration $registration,
|
|
string $scene,
|
|
string $project,
|
|
array $vars,
|
|
string $contentTemplate,
|
|
string $logPrefix,
|
|
): ?AudienceSmsLog {
|
|
if (! (bool) config('sms.enabled')) {
|
|
return null;
|
|
}
|
|
if ((string) config('sms.driver') !== 'submail') {
|
|
return null;
|
|
}
|
|
|
|
$mobile = trim((string) $registration->phone);
|
|
if (preg_match('/^1[3-9]\d{9}$/', $mobile) !== 1) {
|
|
Log::warning($logPrefix.' skipped_invalid_mobile', [
|
|
'registration_id' => $registration->id,
|
|
]);
|
|
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
SmsConfig::assertReadyForRealSending();
|
|
} catch (\Throwable $e) {
|
|
Log::error($logPrefix.' config_incomplete', [
|
|
'registration_id' => $registration->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return $this->persistLog(
|
|
$registration,
|
|
$scene,
|
|
$project,
|
|
$mobile,
|
|
$vars,
|
|
$contentTemplate,
|
|
AudienceSmsLog::STATUS_FAILED,
|
|
null,
|
|
$e->getMessage(),
|
|
);
|
|
}
|
|
|
|
if ($project === '') {
|
|
Log::error($logPrefix.' missing_template');
|
|
|
|
return $this->persistLog(
|
|
$registration,
|
|
$scene,
|
|
$project,
|
|
$mobile,
|
|
$vars,
|
|
$contentTemplate,
|
|
AudienceSmsLog::STATUS_FAILED,
|
|
null,
|
|
'缺少短信模板',
|
|
);
|
|
}
|
|
|
|
$result = $this->client->sendTemplate($mobile, $project, $vars);
|
|
$fee = is_array($result->response) && isset($result->response['fee']) && is_numeric($result->response['fee'])
|
|
? (int) $result->response['fee']
|
|
: null;
|
|
|
|
if (! $result->success) {
|
|
Log::error($logPrefix.' failed', [
|
|
'registration_id' => $registration->id,
|
|
'mobile_mask' => substr($mobile, 0, 3).'****'.substr($mobile, -4),
|
|
'provider_code' => $result->providerCode,
|
|
'provider_message' => $result->providerMessage,
|
|
'provider_request_id' => $result->requestId,
|
|
]);
|
|
|
|
return $this->persistLog(
|
|
$registration,
|
|
$scene,
|
|
$project,
|
|
$mobile,
|
|
$vars,
|
|
$contentTemplate,
|
|
AudienceSmsLog::STATUS_FAILED,
|
|
$result->requestId,
|
|
$result->providerMessage,
|
|
$fee,
|
|
);
|
|
}
|
|
|
|
return $this->persistLog(
|
|
$registration,
|
|
$scene,
|
|
$project,
|
|
$mobile,
|
|
$vars,
|
|
$contentTemplate,
|
|
AudienceSmsLog::STATUS_PENDING,
|
|
$result->requestId,
|
|
null,
|
|
$fee,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, string> $vars
|
|
*/
|
|
private function persistLog(
|
|
AudienceRegistration $registration,
|
|
string $scene,
|
|
string $project,
|
|
string $mobile,
|
|
array $vars,
|
|
string $contentTemplate,
|
|
string $status,
|
|
?string $sendId,
|
|
?string $droppedReason,
|
|
?int $fee = null,
|
|
): AudienceSmsLog {
|
|
return AudienceSmsLog::query()->create([
|
|
'competition_id' => $registration->competition_id,
|
|
'audience_registration_id' => $registration->id,
|
|
'scene' => $scene,
|
|
'mobile' => $mobile,
|
|
'template_id' => $project !== '' ? $project : null,
|
|
'content' => $this->renderContent($contentTemplate, $vars),
|
|
'vars_json' => $vars,
|
|
'send_id' => $sendId,
|
|
'status' => $status,
|
|
'fee' => $fee,
|
|
'dropped_reason' => $droppedReason,
|
|
'sent_at' => now(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, string> $vars
|
|
*/
|
|
public function renderContent(string $template, array $vars): string
|
|
{
|
|
$content = $template;
|
|
foreach ($vars as $key => $value) {
|
|
$content = str_replace('{'.$key.'}', $value, $content);
|
|
}
|
|
|
|
return $content;
|
|
}
|
|
|
|
public function ticketUrl(AudienceRegistration $registration): string
|
|
{
|
|
$registration->loadMissing('competition');
|
|
$slug = rawurlencode((string) ($registration->competition?->slug ?? ''));
|
|
$ticket = rawurlencode((string) $registration->ticket_code);
|
|
$base = rtrim((string) (config('sms.audience_ticket_base_url') ?: config('app.url')), '/');
|
|
$path = (string) config('sms.audience_ticket_path', '/admin/c/{slug}/t/{ticket}');
|
|
$path = str_replace(['{slug}', '{ticket}'], [$slug, $ticket], $path);
|
|
if ($path === '' || $path[0] !== '/') {
|
|
$path = '/'.$path;
|
|
}
|
|
|
|
return $base.$path;
|
|
}
|
|
|
|
private function clip(string $value, int $max): string
|
|
{
|
|
$value = trim($value);
|
|
if (mb_strlen($value) <= $max) {
|
|
return $value;
|
|
}
|
|
|
|
return mb_substr($value, 0, $max);
|
|
}
|
|
}
|