Files
clinicpro/src/Auth/Controller/PreRegistrationController.php
T
hamed 8ad983310c feat: add clinic management and financial reporting features
- Implemented ClinicFormPage for adding new clinics with validation.
- Created MyFinancialPage to display financial summaries and charts.
- Developed MyPatientsPage for managing patient data with search and pagination.
- Added PreRegistrationsPage for handling pre-registration requests with approval and rejection functionalities.
- Introduced database migration for pre_registrations table.
- Built PreRegistrationController for managing pre-registration logic, including submission, approval, and rejection.
- Created PreRegistration entity and repository for handling pre-registration data.
2026-06-12 12:31:27 +03:30

189 lines
7.5 KiB
PHP

<?php
namespace App\Auth\Controller;
use App\Auth\Entity\PreRegistration;
use App\Auth\Entity\User;
use App\Auth\Repository\PreRegistrationRepository;
use App\Auth\Repository\UserRepository;
use App\Clinic\Entity\Clinic;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Entity\Doctor;
use App\Doctor\Repository\DoctorRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Sms\Service\SmsService;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class PreRegistrationController extends BaseController
{
public function __construct(
private readonly EntityManagerInterface $em,
private readonly PreRegistrationRepository $preRegRepo,
private readonly UserRepository $userRepo,
private readonly DoctorRepository $doctorRepo,
private readonly ClinicRepository $clinicRepo,
private readonly UserPasswordHasherInterface $hasher,
private readonly SmsService $sms,
private readonly LoggerInterface $logger,
) {}
#[Route('/api/v1/pre-registration', methods: ['POST'])]
public function submit(Request $request): JsonResponse
{
$data = json_decode($request->getContent(), true) ?? [];
$type = trim($data['type'] ?? '');
$name = trim($data['name'] ?? '');
$mobile = trim($data['mobile'] ?? '');
$info = trim($data['info'] ?? '') ?: null;
$validTypes = [
PreRegistration::TYPE_INDEPENDENT_DOCTOR,
PreRegistration::TYPE_DOCTOR_WITH_CLINIC,
PreRegistration::TYPE_CLINIC_MANAGER,
];
if (!in_array($type, $validTypes, true)) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'نوع حساب معتبر نیست', 422);
}
if (mb_strlen($mobile) < 10 || mb_strlen($mobile) > 15) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'شماره موبایل معتبر نیست', 422);
}
if (mb_strlen($name) < 2) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'نام الزامی است', 422);
}
if ($this->preRegRepo->hasPendingForMobile($mobile)) {
return $this->error('DUPLICATE_REQUEST', 'درخواست ثبت‌نام شما در حال بررسی است', 409);
}
$preReg = new PreRegistration($type, $name, $mobile, $info);
$this->em->persist($preReg);
$this->em->flush();
return $this->success(['uuid' => $preReg->getUuid(), 'status' => $preReg->getStatus()]);
}
#[Route('/api/v1/admin/pre-registrations', methods: ['GET'])]
#[IsGranted('ROLE_ADMIN')]
public function list(Request $request): JsonResponse
{
$page = max(1, (int) $request->query->get('page', 1));
$limit = min(50, max(1, (int) $request->query->get('limit', 20)));
$status = $request->query->get('status', 'pending');
$qb = $this->em->createQueryBuilder()
->select('p.uuid, p.type, p.name, p.mobile, p.info, p.status, p.adminNote AS admin_note, p.createdAt AS created_at')
->from(PreRegistration::class, 'p');
if ($status !== 'all') {
$qb->where('p.status = :status')->setParameter('status', $status);
}
$total = (clone $qb)->select('COUNT(p.id)')->getQuery()->getSingleScalarResult();
$items = $qb
->orderBy('p.createdAt', 'DESC')
->setFirstResult(($page - 1) * $limit)
->setMaxResults($limit)
->getQuery()
->getArrayResult();
return $this->paginated($items, (int) $total, $page, $limit);
}
#[Route('/api/v1/admin/pre-registrations/{uuid}/approve', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
public function approve(string $uuid): JsonResponse
{
$preReg = $this->preRegRepo->findOneBy(['uuid' => $uuid]);
if (!$preReg) {
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'درخواست یافت نشد', 404);
}
if (!$preReg->isPending()) {
return $this->error(ErrorCodes::ERR_CONFLICT_001, 'این درخواست قبلاً پردازش شده است', 409);
}
$password = bin2hex(random_bytes(4));
$user = $this->userRepo->findOneBy(['mobileNumber' => $preReg->getMobile()]);
if (!$user) {
$user = new User($preReg->getMobile());
}
$user->setPasswordHash($this->hasher->hashPassword($user, $password));
$user->setRealName($preReg->getName());
$this->em->persist($user);
$type = $preReg->getType();
$doctor = null;
if ($type === PreRegistration::TYPE_INDEPENDENT_DOCTOR || $type === PreRegistration::TYPE_DOCTOR_WITH_CLINIC) {
$user->addRole('ROLE_DOCTOR');
$doctor = $this->doctorRepo->findOneBy(['user' => $user]);
if (!$doctor) {
$doctor = new Doctor($user, $preReg->getName());
$doctor->setMobileNumber($preReg->getMobile());
$this->em->persist($doctor);
}
}
if ($type === PreRegistration::TYPE_DOCTOR_WITH_CLINIC || $type === PreRegistration::TYPE_CLINIC_MANAGER) {
$user->addRole('ROLE_CLINIC');
if (!$this->clinicRepo->findOneBy(['user' => $user])) {
$clinic = new Clinic($user);
$clinic->setName($preReg->getName());
$clinic->setTelephone($preReg->getMobile());
$clinic->setNotificationMobile($preReg->getMobile());
if ($doctor !== null) {
$clinic->getDoctors()->add($doctor);
}
$this->em->persist($clinic);
}
}
$preReg->approve();
$this->em->flush();
try {
$this->sms->dispatchAsync(
$preReg->getMobile(),
sprintf(
'به کلینیک پرو خوش آمدید! شماره‌کاربری: %s | رمز عبور: %s | لینک ورود: https://clinic-pro.ddev.site/admin',
$preReg->getMobile(),
$password
)
);
} catch (\Throwable $e) {
$this->logger->warning('PreRegistration SMS failed', ['uuid' => $uuid, 'error' => $e->getMessage()]);
}
return $this->success(['message' => 'تأیید شد و اطلاعات ورود ارسال گردید']);
}
#[Route('/api/v1/admin/pre-registrations/{uuid}/reject', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
public function reject(string $uuid, Request $request): JsonResponse
{
$preReg = $this->preRegRepo->findOneBy(['uuid' => $uuid]);
if (!$preReg) {
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'درخواست یافت نشد', 404);
}
if (!$preReg->isPending()) {
return $this->error(ErrorCodes::ERR_CONFLICT_001, 'این درخواست قبلاً پردازش شده است', 409);
}
$data = json_decode($request->getContent(), true) ?? [];
$preReg->reject($data['note'] ?? null);
$this->em->flush();
return $this->success(['message' => 'درخواست رد شد']);
}
}