Files
clinicpro/src/Sms/Service/SmsService.php
T
hamed a2c809b7de Add SmsServiceLookupOnlyTest AST and migration for template_code in sms_logs
- Created a new AST JSON file for SmsServiceLookupOnlyTest.php, detailing nodes, edges, and raw calls for better code analysis.
- Added a new migration (Version20260707202553) to alter the sms_logs table by adding a nullable template_code column.
2026-07-08 00:03:33 +03:30

111 lines
4.3 KiB
PHP

<?php
namespace App\Sms\Service;
use App\Sms\Entity\SmsLog;
use App\Sms\Entity\SmsMessageTemplate;
use App\Sms\Message\SendSmsMessage;
use App\Sms\Provider\KavehNegarProvider;
use App\Sms\Provider\RanginehProvider;
use App\Sms\Provider\SmsProviderInterface;
use App\Sms\Repository\SmsLogRepository;
use App\Sms\Repository\SmsMessageTemplateRepository;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\MessageBusInterface;
class SmsService
{
private array $providers;
public function __construct(
private readonly KavehNegarProvider $kavenegar,
private readonly RanginehProvider $rangineh,
private readonly SmsLogRepository $logRepo,
private readonly MessageBusInterface $bus,
private readonly SmsMessageTemplateRepository $messageTemplateRepo,
private readonly SmsTextResolver $textResolver,
private readonly LoggerInterface $logger,
) {
$this->providers = [
'kavenegar' => $kavenegar,
'rangineh' => $rangineh,
];
}
/**
* ارسال یک پیامک سیستمی از طریق تمپلت VerifyLookup کاوه‌نگار.
* فقط تگ + متغیرهای منطقی می‌گیرد؛ نام تمپلت و نگاشت token از SmsMessageTemplate (یا DEFAULTS) خوانده می‌شود.
*
* @param array<string,string|int> $vars متغیرهای منطقی (کلیدهای {...} بدنه)
*/
public function dispatchTemplate(string $tag, string $mobile, array $vars = [], string $provider = 'kavenegar'): void
{
$tpl = $this->messageTemplateRepo->findByTag($tag);
$kaveTemplate = $tpl?->getKavenegarTemplate()
?? (SmsMessageTemplate::DEFAULTS[$tag]['kavenegar_template'] ?? null);
$tokenMap = $tpl?->getTokenMap()
?: (SmsMessageTemplate::DEFAULTS[$tag]['token_map'] ?? []);
$message = $this->textResolver->resolve($tag, $vars);
// سیاست lookup-only: بدون الگوی کاوه‌نگار نباید با sms/send.json خام ارسال شود.
if ($kaveTemplate === null || $tokenMap === []) {
$this->logger->error(sprintf('SMS tag "%s" has no Kavenegar template/token_map; refusing raw send', $tag), [
'tag' => $tag, 'mobile' => $mobile,
]);
$this->logRepo->save(new SmsLog($mobile, $message, $provider, false, $tag));
return;
}
$slotVars = [];
foreach ($tokenMap as $logicalKey => $slot) {
if (array_key_exists($logicalKey, $vars)) {
$slotVars[$slot] = (string) $vars[$logicalKey];
}
}
$this->dispatchAsync($mobile, $message, $provider, templateVars: $slotVars, templateCode: $kaveTemplate, tag: $tag);
}
public function dispatchAsync(
string $mobile,
string $message,
string $provider = 'kavenegar',
?string $templateUuid = null,
array $templateVars = [],
?string $templateCode = null,
string $tag = SmsLog::TAG_GLOBAL,
): void {
$this->bus->dispatch(new SendSmsMessage(
$mobile, $message, $provider, $templateUuid, $templateVars, $templateCode, $tag
));
}
public function sendNow(SendSmsMessage $msg): bool
{
$provider = $this->resolveProvider($msg->provider);
// سیاست lookup-only: پیام بدون templateCode نباید با sms/send.json خام برود.
if ($msg->templateCode === null) {
$this->logger->error('SMS refused: no templateCode (lookup-only policy)', [
'mobile' => $msg->mobile, 'tag' => $msg->tag,
]);
$success = false;
} else {
$success = $provider->sendTemplate($msg->mobile, $msg->templateCode, $msg->templateVars);
}
$log = new SmsLog($msg->mobile, $msg->message, $provider->getName(), $success, $msg->tag);
if ($msg->templateUuid) $log->setTemplateUuid($msg->templateUuid);
if ($msg->templateCode) $log->setTemplateCode($msg->templateCode);
$this->logRepo->save($log);
return $success;
}
private function resolveProvider(string $name): SmsProviderInterface
{
return $this->providers[$name] ?? $this->kavenegar;
}
}