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:
@@ -0,0 +1,286 @@
|
||||
<?php
|
||||
|
||||
namespace App\Appointment\Controller;
|
||||
|
||||
use App\Appointment\Entity\DateOverride;
|
||||
use App\Appointment\Entity\Holiday;
|
||||
use App\Appointment\Entity\WeeklySchedule;
|
||||
use App\Appointment\Repository\DateOverrideRepository;
|
||||
use App\Appointment\Repository\HolidayRepository;
|
||||
use App\Appointment\Repository\WeeklyScheduleRepository;
|
||||
use App\Auth\Entity\User;
|
||||
use App\Doctor\Repository\DoctorRepository;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Controller\BaseController;
|
||||
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 AppointmentSettingsController extends BaseController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly WeeklyScheduleRepository $scheduleRepo,
|
||||
private readonly DateOverrideRepository $overrideRepo,
|
||||
private readonly HolidayRepository $holidayRepo,
|
||||
private readonly DoctorRepository $doctorRepo,
|
||||
) {}
|
||||
|
||||
// ── Weekly Schedule ───────────────────────────────────────────────────────
|
||||
|
||||
#[Route('/api/v1/appointment-settings/weekly-schedule', methods: ['POST'])]
|
||||
public function createSchedule(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
$doctorUuid = trim($data['doctor_uuid'] ?? '');
|
||||
|
||||
$doctor = $this->doctorRepo->findByUuid($doctorUuid);
|
||||
if ($doctor === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($doctor->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
// Only one schedule per doctor — upsert
|
||||
$schedule = $this->scheduleRepo->findByDoctor($doctor);
|
||||
if ($schedule !== null) {
|
||||
$schedule->setSetting($data['schedule'] ?? []);
|
||||
} else {
|
||||
$schedule = new WeeklySchedule($doctor, $data['schedule'] ?? []);
|
||||
}
|
||||
|
||||
$this->scheduleRepo->save($schedule);
|
||||
|
||||
return $this->success(['data' => $schedule->toArray()], 201);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/appointment-settings/weekly-schedule/{uuid}', methods: ['PATCH'])]
|
||||
public function updateSchedule(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
// uuid may be doctor uuid or schedule uuid
|
||||
$schedule = $this->scheduleRepo->findByUuid($uuid);
|
||||
if ($schedule === null) {
|
||||
$doctor = $this->doctorRepo->findByUuid($uuid);
|
||||
$schedule = $doctor ? $this->scheduleRepo->findByDoctor($doctor) : null;
|
||||
}
|
||||
|
||||
if ($schedule === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'برنامه یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($schedule->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
if (isset($data['schedule'])) {
|
||||
$schedule->setSetting($data['schedule']);
|
||||
}
|
||||
|
||||
$this->scheduleRepo->save($schedule);
|
||||
|
||||
return $this->success(['data' => $schedule->toArray()]);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/appointment-settings/weekly-schedule/{uuid}', methods: ['GET'])]
|
||||
public function getSchedule(string $uuid): JsonResponse
|
||||
{
|
||||
// Try doctor uuid first, then schedule uuid
|
||||
$doctor = $this->doctorRepo->findByUuid($uuid);
|
||||
$schedule = $doctor
|
||||
? $this->scheduleRepo->findByDoctor($doctor)
|
||||
: $this->scheduleRepo->findByUuid($uuid);
|
||||
|
||||
if ($schedule === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'برنامه یافت نشد', 404);
|
||||
}
|
||||
|
||||
return $this->success(['data' => $schedule->toArray()]);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/booking-setting/{uuid}', methods: ['DELETE'])]
|
||||
public function deleteSchedule(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$schedule = $this->scheduleRepo->findByUuid($uuid);
|
||||
if ($schedule === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'برنامه یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($schedule->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
$this->scheduleRepo->remove($schedule);
|
||||
|
||||
return $this->success(['message' => 'برنامه هفتگی با موفقیت حذف شد']);
|
||||
}
|
||||
|
||||
// ── Date Overrides ────────────────────────────────────────────────────────
|
||||
|
||||
#[Route('/api/v1/appointment-settings/date-override/list/{doctorUuid}', methods: ['GET'])]
|
||||
public function listOverrides(string $doctorUuid): JsonResponse
|
||||
{
|
||||
$doctor = $this->doctorRepo->findByUuid($doctorUuid);
|
||||
if ($doctor === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
|
||||
}
|
||||
|
||||
$overrides = array_map(
|
||||
fn(DateOverride $o) => $o->toArray(),
|
||||
$this->overrideRepo->findByDoctor($doctor)
|
||||
);
|
||||
|
||||
return $this->success(['data' => $overrides]);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/appointment-settings/date-override', methods: ['POST'])]
|
||||
public function createOverride(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
$doctorUuid = trim($data['doctor_uuid'] ?? '');
|
||||
$dateStr = trim($data['date'] ?? '');
|
||||
|
||||
$doctor = $this->doctorRepo->findByUuid($doctorUuid);
|
||||
if ($doctor === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($doctor->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
$timestamp = strtotime($dateStr);
|
||||
if ($timestamp === false || $timestamp === -1) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'فرمت تاریخ نادرست است', 422, 'date');
|
||||
}
|
||||
|
||||
$override = new DateOverride($doctor, $timestamp, (bool) ($data['active'] ?? false));
|
||||
if (isset($data['reason'])) $override->setReason($data['reason']);
|
||||
if (isset($data['custom_slots'])) $override->setSetting($data['custom_slots']);
|
||||
|
||||
$this->overrideRepo->save($override);
|
||||
|
||||
return $this->success(['data' => $override->toArray()], 201);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/appointment-settings/date-override/{uuid}', methods: ['PATCH'])]
|
||||
public function updateOverride(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$override = $this->overrideRepo->findByUuid($uuid);
|
||||
if ($override === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'Override یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($override->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
if (array_key_exists('active', $data)) $override->setActive((bool) $data['active']);
|
||||
if (array_key_exists('reason', $data)) $override->setReason($data['reason']);
|
||||
if (array_key_exists('custom_slots', $data)) $override->setSetting($data['custom_slots']);
|
||||
if (!empty($data['date'])) {
|
||||
$ts = strtotime($data['date']);
|
||||
if ($ts !== false) $override->setDate($ts);
|
||||
}
|
||||
|
||||
$this->overrideRepo->save($override);
|
||||
|
||||
return $this->success(['data' => $override->toArray()]);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/appointment-settings/date-override/{uuid}', methods: ['DELETE'])]
|
||||
public function deleteOverride(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$override = $this->overrideRepo->findByUuid($uuid);
|
||||
if ($override === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'Override یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($override->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
$this->overrideRepo->remove($override);
|
||||
|
||||
return $this->success(['message' => 'Override با موفقیت حذف شد']);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/appointment-settings/date-override/{uuid}', methods: ['GET'])]
|
||||
public function getOverride(string $uuid): JsonResponse
|
||||
{
|
||||
$override = $this->overrideRepo->findByUuid($uuid);
|
||||
if ($override === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'Override یافت نشد', 404);
|
||||
}
|
||||
|
||||
return $this->success(['data' => $override->toArray()]);
|
||||
}
|
||||
|
||||
// ── Holidays ──────────────────────────────────────────────────────────────
|
||||
|
||||
#[Route('/api/v1/appointment-settings/holidays', methods: ['POST'])]
|
||||
public function createHoliday(Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
$doctorUuid = trim($data['doctor_uuid'] ?? '');
|
||||
$startStr = trim($data['start_date'] ?? '');
|
||||
$endStr = trim($data['end_date'] ?? '');
|
||||
|
||||
$doctor = $this->doctorRepo->findByUuid($doctorUuid);
|
||||
if ($doctor === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($doctor->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
$startTs = strtotime($startStr);
|
||||
$endTs = strtotime($endStr);
|
||||
|
||||
if (!$startTs || !$endTs || $endTs < $startTs) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'تاریخ نادرست است', 422);
|
||||
}
|
||||
|
||||
$holiday = new Holiday($doctor, $startTs, $endTs);
|
||||
if (isset($data['reason'])) $holiday->setReason($data['reason']);
|
||||
|
||||
$this->holidayRepo->save($holiday);
|
||||
|
||||
return $this->success(['data' => $holiday->toArray()], 201);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/appointment-settings/holidays/{uuid}', methods: ['PATCH'])]
|
||||
public function updateHoliday(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$holiday = $this->holidayRepo->findByUuid($uuid);
|
||||
if ($holiday === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'تعطیلات یافت نشد', 404);
|
||||
}
|
||||
|
||||
if ($holiday->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
|
||||
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
|
||||
}
|
||||
|
||||
$data = json_decode($request->getContent(), true) ?? [];
|
||||
if (array_key_exists('active', $data)) $holiday->setActive((bool) $data['active']);
|
||||
if (array_key_exists('reason', $data)) $holiday->setReason($data['reason']);
|
||||
if (!empty($data['start_date'])) {
|
||||
$ts = strtotime($data['start_date']);
|
||||
if ($ts) $holiday->setStartDate($ts);
|
||||
}
|
||||
if (!empty($data['end_date'])) {
|
||||
$ts = strtotime($data['end_date']);
|
||||
if ($ts) $holiday->setEndDate($ts);
|
||||
}
|
||||
|
||||
$this->holidayRepo->save($holiday);
|
||||
|
||||
return $this->success(['data' => $holiday->toArray()]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user