Refactor SMS sending to use KavehNegar VerifyLookup templates

- Removed SmsTextResolver dependency from multiple services and controllers.
- Introduced dispatchTemplate method in SmsService to handle SMS sending with templates.
- Updated existing SMS sending logic across various services (OtpService, PreRegistrationController, ClinicInvitationService, PaymentManager, SecretaryController, RepresentationActionController) to utilize the new dispatchTemplate method.
- Enhanced SmsMessageTemplate entity to include kavenegar_template and token_map fields.
- Created migration to add new fields to the sms_message_templates table and populate them with existing data.
- Updated SeedSmsMessageTemplatesCommand to handle new template structure.
- Added documentation for the new SMS template structure and usage.
This commit is contained in:
hamed
2026-07-05 11:20:53 +03:30
parent 828e3552c0
commit 969dc9651f
17 changed files with 480 additions and 102 deletions
+62 -19
View File
@@ -15,43 +15,66 @@ class SmsMessageTemplate
{
/**
* متن و متادیتای پیش‌فرض هر تگ — مرجع برای seed و fallback.
* @var array<string, array{title: string, body: string, variables: string[]}>
* kavenegar_template: نام تمپلت ساخته‌شده در پنل کاوه‌نگار (VerifyLookup).
* token_map: نگاشت متغیر منطقی → جایگاه کاوه‌نگار (token/token2/token3 بدون فاصله، token10/token20 با فاصله).
* @var array<string, array{title: string, body: string, variables: string[], kavenegar_template: string, token_map: array<string,string>}>
*/
public const DEFAULTS = [
SmsLog::TAG_OTP => [
'title' => 'کد تأیید ورود',
'body' => 'کد تأیید شما: {code}',
'variables' => ['code', 'site'],
'kavenegar_template' => 'clinicpro-otp',
'token_map' => ['code' => 'token', 'site' => 'token10'],
],
SmsLog::TAG_PAYMENT => [
'title' => 'تأیید پرداخت و نوبت',
'body' => 'نوبت شما با {doctor} در تاریخ {date} ثبت و تأیید شد.',
'variables' => ['doctor', 'date'],
'kavenegar_template' => 'clinicpro-payment',
'token_map' => ['doctor' => 'token10', 'date' => 'token20'],
],
SmsLog::TAG_CLINIC_INVITATION => [
'title' => 'دعوت پزشک به کلینیک',
'body' => "دکتر گرامی، کلینیک {clinic} شما را برای همکاری دعوت کرده است.\nبرای بررسی: {link}\nاین لینک تا ۷۲ ساعت معتبر است.",
'variables' => ['clinic', 'link'],
'kavenegar_template' => 'clinicpro-clinic-invite',
'token_map' => ['clinic' => 'token10', 'link' => 'token'],
],
SmsLog::TAG_PRE_REGISTRATION => [
'title' => 'پیش‌ثبت‌نام',
'body' => 'به کلینیک پرو خوش آمدید! شماره‌کاربری: {username} | رمز عبور: {password} | لینک ورود: {link}',
'variables' => ['username', 'password', 'link'],
'kavenegar_template' => 'clinicpro-pre-register',
'token_map' => ['username' => 'token', 'password' => 'token2', 'link' => 'token3'],
],
SmsLog::TAG_NOTIFICATION_MOBILE => [
'title' => 'تأیید شماره اعلان',
'body' => "کد تأیید شماره اعلان شما: {code}\nاعتبار: ۵ دقیقه",
'variables' => ['code'],
'kavenegar_template' => 'clinicpro-notify-code',
'token_map' => ['code' => 'token'],
],
SmsLog::TAG_SECRETARY => [
'title' => 'دعوت به‌عنوان منشی',
'body' => "شما به‌عنوان منشیِ {owner} در کلینیک‌پرو تعریف شدید.\nشماره‌کاربری: {username}\nلینک ورود: {link}",
'variables' => ['owner', 'username', 'link'],
'kavenegar_template' => 'clinicpro-secretary',
'token_map' => ['owner' => 'token10', 'username' => 'token', 'link' => 'token3'],
],
SmsLog::TAG_DOCTOR_APPOINTMENT => [
'title' => 'نوبت جدید (اعلان به پزشک)',
'body' => "نوبت جدید ثبت شد.\nبیمار: {patient}\nتاریخ: {date} ساعت {time}\n\nبرای مدیریت نوبت‌های خود به‌صورت رایگان به سایت clinic-pro.ir مراجعه کنید.\nکلینیک پرو",
'variables' => ['patient', 'date', 'time'],
'kavenegar_template' => 'clinicpro-doctor-appt',
'token_map' => ['patient' => 'token10', 'date' => 'token2', 'time' => 'token3'],
],
SmsLog::TAG_WELCOME => [
'title' => 'خوش‌آمدگویی',
'body' => '{name} عزیز، به {site} خوش آمدید. پروفایل شما توسط نماینده ثبت شد.',
'variables' => ['name', 'site'],
'kavenegar_template' => 'clinicpro-welcome',
'token_map' => ['name' => 'token10', 'site' => 'token20'],
],
];
@@ -72,36 +95,56 @@ class SmsMessageTemplate
#[ORM\Column(type: 'json')]
private array $variables = [];
#[ORM\Column(name: 'kavenegar_template', type: 'string', length: 100, nullable: true)]
private ?string $kavenegarTemplate = null;
#[ORM\Column(name: 'token_map', type: 'json')]
private array $tokenMap = [];
#[ORM\Column(name: 'updated_at', type: 'integer')]
private int $updatedAt;
public function __construct(string $tag, string $title, string $body, array $variables = [])
{
$this->tag = $tag;
$this->title = $title;
$this->body = $body;
$this->variables = $variables;
$this->updatedAt = time();
public function __construct(
string $tag,
string $title,
string $body,
array $variables = [],
?string $kavenegarTemplate = null,
array $tokenMap = [],
) {
$this->tag = $tag;
$this->title = $title;
$this->body = $body;
$this->variables = $variables;
$this->kavenegarTemplate = $kavenegarTemplate;
$this->tokenMap = $tokenMap;
$this->updatedAt = time();
}
public function getId(): ?int { return $this->id; }
public function getTag(): string { return $this->tag; }
public function getTitle(): string { return $this->title; }
public function getBody(): string { return $this->body; }
public function getVariables(): array { return $this->variables; }
public function getUpdatedAt(): int { return $this->updatedAt; }
public function getId(): ?int { return $this->id; }
public function getTag(): string { return $this->tag; }
public function getTitle(): string { return $this->title; }
public function getBody(): string { return $this->body; }
public function getVariables(): array { return $this->variables; }
public function getKavenegarTemplate(): ?string { return $this->kavenegarTemplate; }
public function getTokenMap(): array { return $this->tokenMap; }
public function getUpdatedAt(): int { return $this->updatedAt; }
public function setTitle(string $v): self { $this->title = $v; $this->updatedAt = time(); return $this; }
public function setBody(string $v): self { $this->body = $v; $this->updatedAt = time(); return $this; }
public function setKavenegarTemplate(?string $v): self { $this->kavenegarTemplate = $v; $this->updatedAt = time(); return $this; }
public function setTokenMap(array $v): self { $this->tokenMap = $v; $this->updatedAt = time(); return $this; }
public function toArray(): array
{
return [
'tag' => $this->tag,
'title' => $this->title,
'body' => $this->body,
'variables' => $this->variables,
'updated_at' => $this->updatedAt,
'tag' => $this->tag,
'title' => $this->title,
'body' => $this->body,
'variables' => $this->variables,
'kavenegar_template' => $this->kavenegarTemplate,
'token_map' => $this->tokenMap,
'updated_at' => $this->updatedAt,
];
}
}