feat: Add tagging system for SMS logs and templates
- Introduced a `tag` field in the `SmsLog` entity to categorize SMS messages. - Updated the `SmsService` to handle the new `tag` parameter during SMS dispatch. - Implemented a `SmsTextResolver` service to resolve SMS message templates based on tags. - Created a new `SmsMessageTemplate` entity for editable SMS templates with placeholders. - Added endpoints for managing SMS message templates in the admin panel. - Enhanced existing SMS dispatching methods across various controllers to utilize the tagging system. - Migrated the database to include the new `tag` field and created a seeding command for default SMS templates. - Updated admin API to filter SMS logs by tag and include tag information in responses.
This commit is contained in:
@@ -72,7 +72,7 @@ class SmsController extends BaseController
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'mobile و message الزامی است', 422);
|
||||
}
|
||||
|
||||
$this->smsService->dispatchAsync($mobile, $message, $provider);
|
||||
$this->smsService->dispatchAsync($mobile, $message, $provider, tag: \App\Sms\Entity\SmsLog::TAG_USER_TEMPLATE);
|
||||
|
||||
return $this->success(['message' => 'پیامک در صف ارسال قرار گرفت']);
|
||||
}
|
||||
@@ -483,7 +483,7 @@ class SmsController extends BaseController
|
||||
$message = $template->renderBody($vars);
|
||||
$this->smsService->dispatchAsync(
|
||||
$mobile, $message, $provider, $template->getUuid(),
|
||||
$vars, $template->getProviderCode()
|
||||
$vars, $template->getProviderCode(), \App\Sms\Entity\SmsLog::TAG_USER_TEMPLATE
|
||||
);
|
||||
|
||||
return $this->success(['message' => 'پیامک در صف ارسال قرار گرفت']);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace App\Sms\Controller;
|
||||
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Controller\BaseController;
|
||||
use App\Sms\Entity\SmsMessageTemplate;
|
||||
use App\Sms\Repository\SmsMessageTemplateRepository;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
/**
|
||||
* مدیریت متن ویرایشپذیر پیامکهای سیستمی (بر اساس تگ).
|
||||
*/
|
||||
#[OA\Tag(name: 'SMS')]
|
||||
#[IsGranted('ROLE_ADMIN')]
|
||||
class SmsMessageController extends BaseController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly SmsMessageTemplateRepository $repo,
|
||||
) {}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/api/v1/admin/sms/messages',
|
||||
summary: 'لیست متنهای سیستمی پیامک (بر اساس تگ)',
|
||||
security: [['bearerAuth' => []]],
|
||||
responses: [new OA\Response(response: 200, description: 'لیست متنها')]
|
||||
)]
|
||||
#[Route('/api/v1/admin/sms/messages', methods: ['GET'])]
|
||||
public function list(): JsonResponse
|
||||
{
|
||||
$existing = [];
|
||||
foreach ($this->repo->findAll() as $tpl) {
|
||||
$existing[$tpl->getTag()] = $tpl->toArray();
|
||||
}
|
||||
|
||||
// تگهای پیشفرضی که هنوز در DB رکورد ندارند را هم با مقدار پیشفرض نشان بده.
|
||||
$items = [];
|
||||
foreach (SmsMessageTemplate::DEFAULTS as $tag => $def) {
|
||||
$items[] = $existing[$tag] ?? [
|
||||
'tag' => $tag,
|
||||
'title' => $def['title'],
|
||||
'body' => $def['body'],
|
||||
'variables' => $def['variables'],
|
||||
'updated_at' => null,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->success(['data' => $items]);
|
||||
}
|
||||
|
||||
#[OA\Patch(
|
||||
path: '/api/v1/admin/sms/messages/{tag}',
|
||||
summary: 'ویرایش متن یک پیامک سیستمی',
|
||||
security: [['bearerAuth' => []]],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
required: ['body'],
|
||||
properties: [new OA\Property(property: 'body', type: 'string')]
|
||||
)
|
||||
),
|
||||
responses: [
|
||||
new OA\Response(response: 200, description: 'متن بهروزرسانی شد'),
|
||||
new OA\Response(response: 404, description: 'تگ ناشناخته'),
|
||||
new OA\Response(response: 422, description: 'placeholder نامعتبر'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/admin/sms/messages/{tag}', methods: ['PATCH'])]
|
||||
public function update(string $tag, Request $request): JsonResponse
|
||||
{
|
||||
if (!isset(SmsMessageTemplate::DEFAULTS[$tag])) {
|
||||
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'تگ پیامک ناشناخته است', 404);
|
||||
}
|
||||
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
$body = trim((string) ($data['body'] ?? ''));
|
||||
if ($body === '') {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'متن پیامک الزامی است', 422);
|
||||
}
|
||||
|
||||
$allowed = SmsMessageTemplate::DEFAULTS[$tag]['variables'];
|
||||
preg_match_all('/\{([a-zA-Z0-9_]+)\}/', $body, $m);
|
||||
$used = array_unique($m[1]);
|
||||
$unknown = array_diff($used, $allowed);
|
||||
if (!empty($unknown)) {
|
||||
return $this->error(
|
||||
ErrorCodes::ERR_VALIDATION_001,
|
||||
'placeholder نامعتبر: ' . implode(', ', $unknown) . ' — مجاز: ' . implode(', ', $allowed),
|
||||
422,
|
||||
'body',
|
||||
);
|
||||
}
|
||||
|
||||
$tpl = $this->repo->findByTag($tag);
|
||||
if ($tpl === null) {
|
||||
$def = SmsMessageTemplate::DEFAULTS[$tag];
|
||||
$tpl = new SmsMessageTemplate($tag, $def['title'], $body, $def['variables']);
|
||||
} else {
|
||||
$tpl->setBody($body);
|
||||
}
|
||||
$this->repo->save($tpl);
|
||||
|
||||
return $this->success(['data' => $tpl->toArray()]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user