feat: add patient appointment interface and endpoints
- Introduced `PatientAppointment` interface to define appointment structure. - Implemented `findByUserAndDoctorIds` method in `AppointmentRepository` to retrieve appointments for a user filtered by doctor IDs. - Added `acceptedDoctorIdsByClinic` method in `ClinicDoctorInvitationRepository` to get accepted doctor IDs for a clinic. - Created new endpoint in `PatientController` to fetch appointments for a patient, ensuring only relevant doctors' appointments are displayed.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -502,6 +502,17 @@ export interface SessionServiceLine {
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
export interface PatientAppointment {
|
||||
uuid: string;
|
||||
starts_at: number;
|
||||
ends_at: number | null;
|
||||
status: string;
|
||||
doctor_name: string | null;
|
||||
service_name: string | null;
|
||||
price_rials: number | null;
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
export interface PatientSession {
|
||||
uuid: string;
|
||||
record_uuid: string;
|
||||
|
||||
@@ -128,6 +128,29 @@ class AppointmentRepository extends ServiceEntityRepository
|
||||
return $this->findBy($criteria, ['slotStart' => 'DESC']);
|
||||
}
|
||||
|
||||
/**
|
||||
* نوبتهای یک بیمار (کاربر) که با پزشک(های) مشخص گرفته شدهاند — برای نمایش در
|
||||
* پروندهٔ بیمار. اگر لیست پزشک خالی باشد، آرایهٔ خالی برمیگرداند.
|
||||
*
|
||||
* @param int[] $doctorIds
|
||||
* @return Appointment[]
|
||||
*/
|
||||
public function findByUserAndDoctorIds(User $user, array $doctorIds): array
|
||||
{
|
||||
if ($doctorIds === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->createQueryBuilder('a')
|
||||
->where('a.user = :user')
|
||||
->andWhere('a.doctor IN (:doctorIds)')
|
||||
->setParameter('user', $user)
|
||||
->setParameter('doctorIds', $doctorIds)
|
||||
->orderBy('a.slotStart', 'DESC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/** Whether the user had a confirmed appointment with this doctor within the last $sinceDays days. */
|
||||
public function hasRecentConfirmed(User $user, Doctor $doctor, int $sinceDays = 30): bool
|
||||
{
|
||||
|
||||
@@ -46,6 +46,26 @@ class ClinicDoctorInvitationRepository extends ServiceEntityRepository
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* شناسهٔ پزشکانی که دعوت پذیرفتهشده در این کلینیک دارند.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function acceptedDoctorIdsByClinic(int $clinicId): array
|
||||
{
|
||||
$rows = $this->createQueryBuilder('i')
|
||||
->select('IDENTITY(i.doctor) AS doctorId')
|
||||
->where('i.clinic = :clinicId')
|
||||
->andWhere('i.status = :accepted')
|
||||
->andWhere('i.doctor IS NOT NULL')
|
||||
->setParameter('clinicId', $clinicId)
|
||||
->setParameter('accepted', ClinicDoctorInvitation::STATUS_ACCEPTED)
|
||||
->getQuery()
|
||||
->getScalarResult();
|
||||
|
||||
return array_map(static fn(array $r) => (int) $r['doctorId'], $rows);
|
||||
}
|
||||
|
||||
public function save(ClinicDoctorInvitation $invitation): void
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
@@ -47,6 +47,8 @@ class PatientController extends BaseController
|
||||
private readonly InvoiceService $invoiceService,
|
||||
private readonly ClaimService $claimService,
|
||||
private readonly InvoiceRepository $invoiceRepo,
|
||||
private readonly \App\Appointment\Repository\AppointmentRepository $appointmentRepo,
|
||||
private readonly \App\ClinicInvitation\Repository\ClinicDoctorInvitationRepository $invitationRepo,
|
||||
private readonly LoggerInterface $logger,
|
||||
) {}
|
||||
|
||||
@@ -318,6 +320,37 @@ class PatientController extends BaseController
|
||||
);
|
||||
}
|
||||
|
||||
#[Route('/api/v1/patient/{uuid}/appointments', methods: ['GET'])]
|
||||
public function appointments(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
[$entityType, $entityId] = $this->resolveEntity($user);
|
||||
$this->assertPatientGate($entityType, $entityId);
|
||||
|
||||
$record = $this->recordRepo->findByUuid($uuid);
|
||||
if ($record === null || !$this->ownsRecord($record, $entityType, $entityId)) {
|
||||
return $this->error(ErrorCodes::ERR_PATIENT_NOT_FOUND, ErrorCodes::message(ErrorCodes::ERR_PATIENT_NOT_FOUND), 404);
|
||||
}
|
||||
|
||||
// نوبتهای این بیمار فقط با پزشک(های) همین ارائهدهنده نمایش داده میشوند تا
|
||||
// نوبتهای او با کلینیکهای دیگر نشت نکند.
|
||||
$doctorIds = $entityType === 'doctor'
|
||||
? [$entityId]
|
||||
: $this->invitationRepo->acceptedDoctorIdsByClinic($entityId);
|
||||
|
||||
$appointments = $this->appointmentRepo->findByUserAndDoctorIds($record->getUser(), $doctorIds);
|
||||
|
||||
return $this->success(array_map(fn(\App\Appointment\Entity\Appointment $a) => [
|
||||
'uuid' => $a->getUuid(),
|
||||
'starts_at' => $a->getSlotStart(),
|
||||
'ends_at' => $a->getSlotEnd(),
|
||||
'status' => $a->getStatus(),
|
||||
'doctor_name' => $a->getDoctor()->getName(),
|
||||
'service_name' => null,
|
||||
'price_rials' => null,
|
||||
'created_at' => $a->getSlotStart(),
|
||||
], $appointments));
|
||||
}
|
||||
|
||||
/**
|
||||
* خروجی session بههمراه خلاصهی صورتحساب: uuid فاکتور (در صورت وجود) و
|
||||
* ماندهی بدهیِ سهم بیمار. اگر session تسویه شده باشد (payment_method != pending)
|
||||
|
||||
Reference in New Issue
Block a user