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