3.6 KiB
3.6 KiB
معماری — تسک ۱۳: سرویسهای کلینیک
ساختار فایلها
src/ClinicService/
├── Controller/
│ └── ClinicServiceController.php
├── Entity/
│ ├── ServiceSection.php
│ └── ServiceItem.php
└── Repository/
├── ServiceSectionRepository.php
└── ServiceItemRepository.php
Entity: ServiceSection
#[ORM\Entity(repositoryClass: ServiceSectionRepository::class)]
#[ORM\Table(name: 'service_sections')]
#[ORM\Index(columns: ['entity_type', 'entity_id'], name: 'idx_service_sections_entity')]
class ServiceSection
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string', length: 36, unique: true)]
private string $uuid;
#[ORM\Column(type: 'string', length: 10)]
private string $entityType; // 'doctor' | 'clinic'
#[ORM\Column(type: 'integer')]
private int $entityId;
#[ORM\Column(type: 'string', length: 200)]
private string $name;
#[ORM\Column(type: 'boolean')]
private bool $active = true;
#[ORM\Column(type: 'integer')]
private int $createdAt;
#[ORM\Column(type: 'integer')]
private int $updatedAt;
}
Entity: ServiceItem
#[ORM\Entity(repositoryClass: ServiceItemRepository::class)]
#[ORM\Table(name: 'service_items')]
class ServiceItem
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column(type: 'integer')]
private ?int $id = null;
#[ORM\Column(type: 'string', length: 36, unique: true)]
private string $uuid;
#[ORM\ManyToOne(targetEntity: ServiceSection::class)]
#[ORM\JoinColumn(name: 'section_id', nullable: false, onDelete: 'CASCADE')]
private ServiceSection $section;
// staff nullable — پرسنل انجامدهنده
#[ORM\ManyToOne(targetEntity: \App\Staff\Entity\ClinicStaff::class)]
#[ORM\JoinColumn(name: 'staff_id', nullable: true, onDelete: 'SET NULL')]
private ?\App\Staff\Entity\ClinicStaff $staff = null;
#[ORM\Column(type: 'string', length: 200)]
private string $name;
#[ORM\Column(type: 'integer')]
private int $priceRials = 0;
#[ORM\Column(type: 'boolean')]
private bool $active = true;
#[ORM\Column(type: 'integer')]
private int $createdAt;
#[ORM\Column(type: 'integer')]
private int $updatedAt;
}
Controller: ClinicServiceController
#[Route('/api/v1')]
class ClinicServiceController extends BaseController
{
// برای همه write actions → gate check اول:
private function assertServicesFeature(): void
{
[$entityType, $entityId] = $this->resolveEntityContext();
if (!$this->subscriptionService->hasFeature($entityType, $entityId, 'services')) {
throw new AppException(ErrorCodes::ERR_SUBSCRIPTION_REQUIRED, null, 403);
}
}
#[Route('/service-sections', methods: ['GET'])]
public function listSections(): JsonResponse { ... }
#[Route('/service-section', methods: ['POST'])]
public function createSection(Request $request): JsonResponse
{
$this->assertServicesFeature();
// ...
}
// سایر endpoint ها ...
}
نکته DELETE سرویس با استفاده در پرونده
// اگر session_services.service_item_id → این item باشد:
// FK ON DELETE RESTRICT → Doctrine exception → catch و return 409
try {
$this->em->remove($item);
$this->em->flush();
} catch (\Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException $e) {
return $this->error(ErrorCodes::ERR_SERVICE_ITEM_IN_USE, 'این سرویس در پرونده بیمار استفاده شده است', 409);
}