feat(validation): enforce naming rules for doctors and clinics to prevent placeholders

This commit is contained in:
hamed
2026-07-19 08:38:11 +03:30
parent 44b89d7add
commit d780b5cbb6
9 changed files with 461 additions and 3 deletions
@@ -10,12 +10,15 @@ use App\ClinicInvitation\Repository\ClinicDoctorInvitationRepository;
use App\Doctor\Entity\Doctor;
use App\Doctor\Repository\DoctorRepository;
use App\Shared\Exception\AppException;
use App\Shared\Util\DisplayName;
use App\Sms\Service\SmsService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
class ClinicInvitationService
{
private const UNNAMED_DOCTOR = 'پزشک دعوت‌شده';
public function __construct(
private readonly ClinicDoctorInvitationRepository $repo,
private readonly DoctorRepository $doctorRepo,
@@ -161,7 +164,9 @@ class ClinicInvitationService
}
$mobile = $inv->getMobile();
$name = $inv->getInvitedName() ?: $mobile;
// شمارهٔ موبایل نامِ پزشک نیست. دعوت بدون نام قبلاً موبایل را به‌عنوان نام
// می‌نشاند و همان رکورد در نتایج عمومی و <title> صفحات سایت منتشر می‌شد.
$name = $this->resolveInvitedName($inv->getInvitedName());
$user = $this->userRepo->findOneBy(['mobileNumber' => $mobile]);
if ($user === null) {
@@ -173,7 +178,7 @@ class ClinicInvitationService
$doctor = $this->doctorRepo->findOneBy(['user' => $user]);
if ($doctor === null) {
$doctor = new Doctor($user, $user->getRealName() ?: $name);
$doctor = new Doctor($user, $this->resolveInvitedName($user->getRealName()));
$doctor->setMobileNumber($mobile);
$doctor->setOwnerStatus('unclaimed');
$this->em->persist($doctor);
@@ -182,6 +187,16 @@ class ClinicInvitationService
return $doctor;
}
/**
* نام پزشکِ دعوت‌شده. کلینیک اغلب فقط شمارهٔ موبایل را دارد، پس نام واقعی هنوز
* ناشناخته است — یک برچسب خنثی می‌نشیند تا وقتی خود پزشک پروفایلش را claim کند.
* هرگز موبایل یا مقدار آزمایشی برنمی‌گرداند.
*/
private function resolveInvitedName(?string $candidate): string
{
return DisplayName::isPlaceholder($candidate) ? self::UNNAMED_DOCTOR : $candidate;
}
/**
* برای کاربری که هنوز رمز عبور ندارد یک رمز تولید می‌کند تا بتواند وارد شود.
* رمز کاربران موجود هرگز بازنویسی نمی‌شود.