131 lines
3.6 KiB
Markdown
131 lines
3.6 KiB
Markdown
# معماری — تسک ۱۳: سرویسهای کلینیک
|
|
|
|
## ساختار فایلها
|
|
|
|
```
|
|
src/ClinicService/
|
|
├── Controller/
|
|
│ └── ClinicServiceController.php
|
|
├── Entity/
|
|
│ ├── ServiceSection.php
|
|
│ └── ServiceItem.php
|
|
└── Repository/
|
|
├── ServiceSectionRepository.php
|
|
└── ServiceItemRepository.php
|
|
```
|
|
|
|
## Entity: ServiceSection
|
|
|
|
```php
|
|
#[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
|
|
|
|
```php
|
|
#[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
|
|
|
|
```php
|
|
#[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 سرویس با استفاده در پرونده
|
|
|
|
```php
|
|
// اگر 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);
|
|
}
|
|
```
|