feat: Implement SMS sending functionality with KavehNegar and Rangineh providers

- Add SendSmsMessage class for encapsulating SMS message data.
- Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS.
- Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates.
- Develop SendSmsHandler for handling SMS sending messages.
- Create SmsService to manage SMS dispatching and logging.
- Add UserProfileController for managing user profiles with CRUD operations.
- Implement UserProfile entity and repository for user profile data management.
- Update symfony.lock and bootstrap.php for project dependencies and environment setup.
This commit is contained in:
hamed
2026-06-09 22:00:34 +03:30
commit de1a78a235
222 changed files with 36388 additions and 0 deletions
@@ -0,0 +1,144 @@
<?php
namespace App\UserProfile\Controller;
use App\Auth\Entity\User;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\UserProfile\Entity\UserProfile;
use App\UserProfile\Repository\UserProfileRepository;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class UserProfileController extends BaseController
{
public function __construct(
private readonly UserProfileRepository $repository,
) {}
#[Route('/api/v1/user-profile', methods: ['POST'])]
public function create(Request $request, #[CurrentUser] User $user): JsonResponse
{
if ($this->repository->findByUser($user) !== null) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'پروفایل قبلاً ایجاد شده است', 409);
}
$data = json_decode($request->getContent(), true) ?? [];
$profile = new UserProfile($user);
$this->hydrate($profile, $data);
$this->repository->save($profile);
return $this->success(['data' => $profile->toArray()], 201);
}
#[Route('/api/v1/user-profile/{uuid}', methods: ['GET'])]
public function show(string $uuid, #[CurrentUser] User $user): JsonResponse
{
$profile = $this->repository->findByUuid($uuid);
if ($profile === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'پروفایل یافت نشد', 404);
}
if (!$this->canAccess($profile, $user)) {
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
}
return $this->success(['data' => $profile->toArray()]);
}
#[Route('/api/v1/user-profile/{uuid}', methods: ['PATCH'])]
public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
$profile = $this->repository->findByUuid($uuid);
if ($profile === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'پروفایل یافت نشد', 404);
}
if (!$this->canAccess($profile, $user)) {
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
}
$data = json_decode($request->getContent(), true) ?? [];
$this->hydrate($profile, $data);
$this->repository->save($profile);
return $this->success(['data' => $profile->toArray()]);
}
#[Route('/api/v1/user-profile/{uuid}', methods: ['DELETE'])]
#[IsGranted('ROLE_ADMIN')]
public function delete(string $uuid): JsonResponse
{
$profile = $this->repository->findByUuid($uuid);
if ($profile === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'پروفایل یافت نشد', 404);
}
$this->repository->remove($profile);
return $this->success(['message' => 'پروفایل با موفقیت حذف شد']);
}
private function canAccess(UserProfile $profile, User $currentUser): bool
{
return $profile->getUser()->getId() === $currentUser->getId()
|| $currentUser->hasRole('ROLE_ADMIN');
}
private function hydrate(UserProfile $profile, array $data): void
{
if (array_key_exists('name', $data)) $profile->setLabel($data['name']);
if (array_key_exists('label', $data)) $profile->setLabel($data['label']);
if (array_key_exists('family', $data)) $profile->setFamily($data['family']);
if (array_key_exists('fathers_name', $data)) $profile->setFathersName($data['fathers_name']);
if (array_key_exists('national_code', $data)) $profile->setNationalCode($data['national_code']);
if (array_key_exists('gender', $data)) $profile->setGender($data['gender']);
if (array_key_exists('blood_type', $data)) $profile->setBloodType($data['blood_type']);
if (array_key_exists('marital_status', $data)) $profile->setMaritalStatus($data['marital_status']);
if (array_key_exists('education', $data)) $profile->setEducation($data['education']);
if (array_key_exists('job', $data)) $profile->setJob($data['job']);
if (array_key_exists('address', $data)) $profile->setAddress($data['address']);
if (array_key_exists('home_phone', $data)) $profile->setHomePhone($data['home_phone']);
if (array_key_exists('work_phone', $data)) $profile->setWorkPhone($data['work_phone']);
if (array_key_exists('insurance_id', $data)) $profile->setInsuranceId($data['insurance_id']);
if (array_key_exists('description', $data)) $profile->setDescription(
is_array($data['description']) ? ($data['description'][0]['value'] ?? null) : $data['description']
);
if (array_key_exists('sharing_with_user', $data)) $profile->setSharingWithUser((bool) $data['sharing_with_user']);
// birthday: accept Jalali string "1370-05-15" stored as-is converted to Unix
if (array_key_exists('birthday', $data) && $data['birthday'] !== null) {
// Store as string-encoded Unix; for now keep as null if conversion unavailable
// Will be replaced with JalaliDateService in Task 16
$profile->setDateOfBirth(null);
}
if (array_key_exists('date_of_birth', $data)) $profile->setDateOfBirth($data['date_of_birth']);
// Insurance references (category IDs)
if (array_key_exists('basic_insurance', $data)) {
$id = is_array($data['basic_insurance']) ? ($data['basic_insurance'][0] ?? null) : $data['basic_insurance'];
$profile->setBasicInsuranceId($id !== null ? (int) $id : null);
}
if (array_key_exists('supplementary_insurance', $data)) {
$id = is_array($data['supplementary_insurance'])
? ($data['supplementary_insurance'][0] ?? null)
: $data['supplementary_insurance'];
$profile->setSupplementaryInsuranceId($id !== null ? (int) $id : null);
}
// Medical history JSON
if (array_key_exists('other', $data)) {
$other = is_array($data['other']) && isset($data['other'][0])
? $data['other'][0]
: $data['other'];
$profile->setOther($other);
}
}
}