feat(representation): add representation management for doctors, including attach and update functionality

This commit is contained in:
hamed
2026-07-12 14:20:42 +03:30
parent 1a8b3bfa53
commit c1e4cd7f94
5 changed files with 432 additions and 31 deletions
+120 -10
View File
@@ -228,6 +228,7 @@ class AdminApiController extends BaseController
'doctor' => $qb->andWhere("u.roles LIKE :role AND u.roles NOT LIKE :notRole")
->setParameter('role', '%ROLE_DOCTOR%')->setParameter('notRole', '%ROLE_ADMIN%'),
'secretary' => $qb->andWhere("u.roles LIKE :role")->setParameter('role', '%ROLE_SECRETARY%'),
'representation' => $qb->andWhere("u.roles LIKE :role")->setParameter('role', '%ROLE_REPRESENTATION%'),
'clinic' => $qb->andWhere("u.roles LIKE :role AND u.roles NOT LIKE :notRole")
->setParameter('role', '%ROLE_CLINIC%')->setParameter('notRole', '%ROLE_ADMIN%'),
'patient' => $qb->andWhere("u.roles NOT LIKE :d AND u.roles NOT LIKE :a")
@@ -297,6 +298,37 @@ class AdminApiController extends BaseController
return $this->success(['is_active' => $doctor->isActiveDoctorAppointment()]);
}
#[Route('/api/v1/admin/doctors/{uuid}/representation', methods: ['PUT'])]
public function setDoctorRepresentation(string $uuid, Request $request, RepresentationRepository $repRepo): JsonResponse
{
$doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]);
if ($doctor === null) {
return $this->error(ErrorCodes::DOCTOR_NOT_FOUND, 'پزشک یافت نشد', 404);
}
$body = json_decode($request->getContent(), true) ?? [];
$repId = $body['representation_id'] ?? null;
if ($repId === null || $repId === '') {
$doctor->setRepresentationId(null);
$this->em->flush();
return $this->success(['message' => 'نماینده حذف شد', 'representation' => null]);
}
$rep = $repRepo->find((int) $repId);
if ($rep === null) {
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نماینده یافت نشد', 404);
}
$doctor->setRepresentationId($rep->getId());
$this->em->flush();
return $this->success([
'message' => 'نماینده ثبت شد',
'representation' => ['id' => $rep->getId(), 'uuid' => $rep->getUuid(), 'full_name' => $rep->getFullName()],
]);
}
#[Route('/api/v1/admin/doctors', methods: ['GET'])]
public function doctorsList(Request $request): JsonResponse
{
@@ -1130,26 +1162,104 @@ class AdminApiController extends BaseController
$conn = $this->em->getConnection();
$total = (int) $conn->fetchOne('SELECT COUNT(*) FROM doctors WHERE representation_id = ?', [$repId]);
$now = time();
$offset = ($page - 1) * $limit;
$rows = $conn->fetchAllAssociative(
"SELECT d.id, d.uuid, d.name, d.gender, d.medical_system_code,
d.active_doctor_appointment, d.owner_status, d.created_at,
(SELECT COUNT(*) FROM appointments a WHERE a.doctor_id = d.id) AS appointment_count
(SELECT COUNT(*) FROM appointments a WHERE a.doctor_id = d.id AND a.slot_start < $now) AS past_count,
(SELECT COUNT(*) FROM appointments a WHERE a.doctor_id = d.id AND a.slot_start >= $now) AS upcoming_count
FROM doctors d
WHERE d.representation_id = ? ORDER BY d.created_at DESC LIMIT $limit OFFSET $offset",
[$repId]
);
$items = array_map(fn(array $d): array => [
'uuid' => $d['uuid'],
'id' => (int) $d['id'],
'name' => $d['name'],
'gender' => $d['gender'],
'medical_code' => $d['medical_system_code'],
'is_active' => (bool) $d['active_doctor_appointment'],
'owner_status' => $d['owner_status'],
'appointment_count' => (int) $d['appointment_count'],
'created_at' => date('c', (int) $d['created_at']),
'uuid' => $d['uuid'],
'id' => (int) $d['id'],
'name' => $d['name'],
'gender' => $d['gender'],
'medical_code' => $d['medical_system_code'],
'is_active' => (bool) $d['active_doctor_appointment'],
'owner_status' => $d['owner_status'],
'past_count' => (int) $d['past_count'],
'upcoming_count' => (int) $d['upcoming_count'],
'created_at' => date('c', (int) $d['created_at']),
], $rows);
return $this->paginated($items, $total, $page, $limit);
}
#[Route('/api/v1/admin/representations/{uuid}/doctors', methods: ['POST'])]
public function attachDoctorToRepresentation(string $uuid, Request $request, RepresentationRepository $repRepo): JsonResponse
{
$rep = $repRepo->findByUuid($uuid);
if ($rep === null) {
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نماینده یافت نشد', 404);
}
$body = json_decode($request->getContent(), true) ?? [];
$doctorUuid = trim((string) ($body['doctor_uuid'] ?? ''));
if ($doctorUuid === '') {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'شناسه‌ی پزشک الزامی است', 422);
}
$doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $doctorUuid]);
if ($doctor === null) {
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک یافت نشد', 404);
}
if ($doctor->getRepresentationId() !== null) {
return $this->error(ErrorCodes::ERR_CONFLICT_001, 'این پزشک از قبل به یک نماینده متصل است', 409);
}
$doctor->setRepresentationId($rep->getId());
$this->em->flush();
return $this->success(['message' => 'پزشک به نماینده متصل شد', 'doctor_uuid' => $doctorUuid, 'representation_id' => $rep->getId()]);
}
#[Route('/api/v1/admin/representations/{uuid}/appointments', methods: ['GET'])]
public function representationAppointments(string $uuid, Request $request, RepresentationRepository $repRepo): JsonResponse
{
$rep = $repRepo->findByUuid($uuid);
if ($rep === null) {
return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نماینده یافت نشد', 404);
}
$page = max(1, (int) $request->query->get('page', 1));
$limit = min(100, max(1, (int) $request->query->get('limit', 15)));
$scope = $request->query->get('scope') === 'past' ? 'past' : 'upcoming';
$repId = $rep->getId();
$now = time();
$conn = $this->em->getConnection();
$cmp = $scope === 'past' ? '<' : '>=';
$order = $scope === 'past' ? 'DESC' : 'ASC';
$total = (int) $conn->fetchOne(
"SELECT COUNT(*) FROM appointments a JOIN doctors d ON d.id = a.doctor_id
WHERE d.representation_id = ? AND a.slot_start $cmp ?",
[$repId, $now]
);
$offset = ($page - 1) * $limit;
$rows = $conn->fetchAllAssociative(
"SELECT a.uuid, a.slot_start, a.slot_end, a.status, a.patient_name,
d.uuid AS doctor_uuid, d.name AS doctor_name
FROM appointments a JOIN doctors d ON d.id = a.doctor_id
WHERE d.representation_id = ? AND a.slot_start $cmp ?
ORDER BY a.slot_start $order LIMIT $limit OFFSET $offset",
[$repId, $now]
);
$items = array_map(fn(array $a): array => [
'uuid' => $a['uuid'],
'slot_start' => (int) $a['slot_start'],
'slot_end' => (int) $a['slot_end'],
'status' => $a['status'],
'patient_name' => $a['patient_name'],
'doctor_uuid' => $a['doctor_uuid'],
'doctor_name' => $a['doctor_name'],
], $rows);
return $this->paginated($items, $total, $page, $limit);