feat: add clinic doctor invitation feature
- Implemented ClinicInvitationController to handle doctor invitations. - Created ClinicDoctorInvitation entity and repository for managing invitations. - Added ClinicInvitationService for business logic related to invitations. - Introduced endpoints for inviting, listing, resending, changing status, and deleting invitations. - Updated security configuration to allow public access to invitation endpoints. - Added migration for clinic_doctor_invitations table. - Enhanced DoctorRepository with a method to find doctors by mobile number. - Updated ClinicDetailPage to include invitation management UI.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\ClinicInvitation\Service;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Clinic\Entity\Clinic;
|
||||
use App\ClinicInvitation\Entity\ClinicDoctorInvitation;
|
||||
use App\ClinicInvitation\Repository\ClinicDoctorInvitationRepository;
|
||||
use App\Doctor\Repository\DoctorRepository;
|
||||
use App\Shared\Exception\AppException;
|
||||
use App\Sms\Service\SmsService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
class ClinicInvitationService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ClinicDoctorInvitationRepository $repo,
|
||||
private readonly DoctorRepository $doctorRepo,
|
||||
private readonly SmsService $smsService,
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly string $appUrl,
|
||||
) {}
|
||||
|
||||
public function invite(Clinic $clinic, User $invitedBy, string $mobile, ?string $name, ?string $specialty): ClinicDoctorInvitation
|
||||
{
|
||||
$existing = $this->repo->findPendingByMobileAndClinic($mobile, $clinic->getId());
|
||||
if ($existing !== null) {
|
||||
throw new AppException('ERR_CONFLICT_001', 'این شماره قبلاً برای این کلینیک دعوت شده است', 409);
|
||||
}
|
||||
|
||||
$inv = new ClinicDoctorInvitation($clinic, $invitedBy, $mobile);
|
||||
$inv->setInvitedName($name);
|
||||
$inv->setInvitedSpecialty($specialty);
|
||||
|
||||
$doctor = $this->doctorRepo->findOneByMobile($mobile);
|
||||
if ($doctor !== null) {
|
||||
$inv->setDoctor($doctor);
|
||||
}
|
||||
|
||||
$this->repo->save($inv);
|
||||
$this->sendSms($inv, $clinic);
|
||||
|
||||
return $inv;
|
||||
}
|
||||
|
||||
public function resend(ClinicDoctorInvitation $inv): void
|
||||
{
|
||||
if (in_array($inv->getStatus(), [ClinicDoctorInvitation::STATUS_REMOVED, ClinicDoctorInvitation::STATUS_ACCEPTED], true)) {
|
||||
throw new AppException('ERR_CONFLICT_001', 'امکان ارسال مجدد دعوتنامه وجود ندارد', 409);
|
||||
}
|
||||
|
||||
$inv->refresh();
|
||||
$this->em->flush();
|
||||
$this->sendSms($inv, $inv->getClinic());
|
||||
}
|
||||
|
||||
public function changeStatus(ClinicDoctorInvitation $inv, string $status): void
|
||||
{
|
||||
$allowed = [ClinicDoctorInvitation::STATUS_SUSPENDED, ClinicDoctorInvitation::STATUS_REMOVED];
|
||||
if (!in_array($status, $allowed, true)) {
|
||||
throw new AppException('ERR_VALIDATION_001', 'وضعیت نامعتبر است', 422);
|
||||
}
|
||||
$inv->setStatus($status);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
public function delete(ClinicDoctorInvitation $inv): void
|
||||
{
|
||||
$this->em->remove($inv);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
public function accept(ClinicDoctorInvitation $inv): void
|
||||
{
|
||||
if (!$inv->isUsable()) {
|
||||
throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه منقضی یا غیرمعتبر است', 410);
|
||||
}
|
||||
$inv->setStatus(ClinicDoctorInvitation::STATUS_ACCEPTED);
|
||||
$inv->markUsed();
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
public function reject(ClinicDoctorInvitation $inv): void
|
||||
{
|
||||
if (!$inv->isUsable()) {
|
||||
throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه منقضی یا غیرمعتبر است', 410);
|
||||
}
|
||||
$inv->setStatus(ClinicDoctorInvitation::STATUS_REJECTED);
|
||||
$inv->markUsed();
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
private function sendSms(ClinicDoctorInvitation $inv, Clinic $clinic): void
|
||||
{
|
||||
$clinicName = $clinic->getName() ?? 'کلینیک';
|
||||
$link = rtrim($this->appUrl, '/') . '/clinic-invitation/' . $inv->getToken();
|
||||
|
||||
$message = "دکتر گرامی، کلینیک {$clinicName} شما را برای همکاری دعوت کرده است.\n"
|
||||
. "برای بررسی: {$link}\n"
|
||||
. "این لینک تا ۷۲ ساعت معتبر است.";
|
||||
|
||||
$this->smsService->dispatchAsync($inv->getMobile(), $message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user