475 lines
12 KiB
Markdown
475 lines
12 KiB
Markdown
# Модели данных
|
||
|
||
Эта страница описывает основные сущности, найденные в `apps/backend/src/Entity` и `apps/cabinet/src/Entity`. В проектах есть много справочных полей; ниже выделены доменные ядра и связи, которые важны для понимания системы.
|
||
|
||
## Backend ER-схема
|
||
|
||
```mermaid
|
||
erDiagram
|
||
SPECIALIST ||--o{ LOCATION : "id -> specialist_id"
|
||
SPECIALIST ||--o{ REVIEW : "id -> specialist_id"
|
||
SPECIALIST ||--o{ SPECIALIST_DOCS : "id -> specialist_id"
|
||
SPECIALIST ||--o{ SPECIALIST_DCODE_DESCRIPTION : "id -> specialist_id"
|
||
SPECIALIST }o--o{ STOCK : "join table"
|
||
RECORD ||--o| ALERT_SMS : "id -> record_id"
|
||
WIDGET_FORM ||--o{ WIDGET_FORM_INPUT : "id -> widget_form_id"
|
||
|
||
SPECIALIST {
|
||
int id PK
|
||
string name
|
||
string alias
|
||
int region_id
|
||
string post
|
||
string experience
|
||
bool active
|
||
bool display_schedule
|
||
jsonb dcodes
|
||
}
|
||
|
||
LOCATION {
|
||
bigint id PK
|
||
int specialist_id FK
|
||
bigint dcode
|
||
bigint department
|
||
int filial
|
||
bool online_mode
|
||
date nearest_date
|
||
}
|
||
|
||
REVIEW {
|
||
int id PK
|
||
int specialist_id FK
|
||
bool active
|
||
date date_create
|
||
string author
|
||
float rating
|
||
string source
|
||
int external_id
|
||
}
|
||
|
||
SPECIALIST_DOCS {
|
||
int id PK
|
||
int specialist_id FK
|
||
string name
|
||
string picture
|
||
bool active
|
||
string type
|
||
}
|
||
|
||
SPECIALIST_DCODE_DESCRIPTION {
|
||
int id PK
|
||
int specialist_id FK
|
||
bigint dcode
|
||
bigint department
|
||
text content
|
||
}
|
||
|
||
STOCK {
|
||
int id PK
|
||
string name
|
||
text content
|
||
date start_date
|
||
date end_date
|
||
}
|
||
|
||
RECORD {
|
||
int id PK
|
||
int specialist_id
|
||
string phone
|
||
datetime create_at
|
||
string hash
|
||
json reserve
|
||
}
|
||
|
||
ALERT_SMS {
|
||
int id PK
|
||
int record_id FK
|
||
datetime date_create
|
||
text response
|
||
}
|
||
|
||
WIDGET_FORM {
|
||
int id PK
|
||
string name
|
||
}
|
||
|
||
WIDGET_FORM_INPUT {
|
||
int id PK
|
||
int widget_form_id FK
|
||
string text
|
||
string type
|
||
string bitrix24_id
|
||
int sort
|
||
}
|
||
```
|
||
|
||
### Backend: перевод сущностей
|
||
|
||
| Сущность на диаграмме | Русское название | Смысл |
|
||
| --- | --- | --- |
|
||
| `SPECIALIST` | Врач | Карточка врача в новом API |
|
||
| `LOCATION` | Локация приема | Привязка врача к отделению, филиалу и режиму приема |
|
||
| `REVIEW` | Отзыв | Отзыв о враче |
|
||
| `SPECIALIST_DOCS` | Документ врача | Сертификат/документ/изображение врача |
|
||
| `SPECIALIST_DCODE_DESCRIPTION` | Описание врача по `dcode` | Текстовое описание для конкретного кода врача/отделения |
|
||
| `STOCK` | Акция | Акция, связанная с врачами через `ManyToMany` |
|
||
| `RECORD` | Запись пациента | Локальная запись факта бронирования |
|
||
| `ALERT_SMS` | SMS-уведомление | Ответ SMS-провайдера по записи |
|
||
| `WIDGET_FORM` | Форма виджета | Конструктор формы |
|
||
| `WIDGET_FORM_INPUT` | Поле формы | Поле формы виджета |
|
||
|
||
### Backend: ключи связей
|
||
|
||
| Связь | Тип | Ключи |
|
||
| --- | --- | --- |
|
||
| Врач -> Локация приема | `OneToMany` / `ManyToOne` | `specialist.id = location.specialist_id` |
|
||
| Врач -> Отзыв | `OneToMany` / `ManyToOne` | `specialist.id = review.specialist_id` |
|
||
| Врач -> Документ врача | `OneToMany` / `ManyToOne` | Doctrine создает `specialist_docs.specialist_id -> specialist.id` |
|
||
| Врач -> Описание по dcode | `ManyToOne` на стороне описания | `specialist_dcode_description.specialist_id -> specialist.id` |
|
||
| Врач <-> Акция | `ManyToMany` | join-таблица генерируется Doctrine; владелец связи `Stock::$specialist` |
|
||
| Запись -> SMS-уведомление | `OneToOne` | Doctrine создает `alert_sms.record_id -> record.id` |
|
||
| Форма виджета -> Поле формы | `OneToMany` / `ManyToOne` | Doctrine создает `widget_form_input.widget_form_id -> widget_form.id` |
|
||
|
||
`Record.specialistId` в backend не является Doctrine-связью с `Specialist`: это логический внешний идентификатор врача, сохраненный как число.
|
||
|
||
## Backend справочники и контент
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class Department {
|
||
id
|
||
did
|
||
name
|
||
groupName
|
||
onlineMode
|
||
alias
|
||
active
|
||
}
|
||
|
||
class Filial {
|
||
id
|
||
fid
|
||
name
|
||
address
|
||
regionId
|
||
siteId
|
||
company
|
||
phone
|
||
email
|
||
}
|
||
|
||
class PriceDepartment {
|
||
id
|
||
name
|
||
groupId
|
||
doctCount
|
||
viewInWeb
|
||
}
|
||
|
||
class PriceList {
|
||
id
|
||
kodoper
|
||
schname
|
||
specname
|
||
speccode
|
||
priceInfo
|
||
discprice
|
||
filial
|
||
groupId
|
||
}
|
||
|
||
class SiteService {
|
||
id
|
||
name
|
||
alias
|
||
regionId
|
||
anons
|
||
content
|
||
faq
|
||
tags
|
||
clinics
|
||
}
|
||
|
||
class Disease {
|
||
id
|
||
name
|
||
alias
|
||
regionId
|
||
symptom
|
||
content
|
||
tags
|
||
staffList
|
||
}
|
||
|
||
class MedicalCenter {
|
||
id
|
||
name
|
||
alias
|
||
regionId
|
||
doctors
|
||
services
|
||
content
|
||
}
|
||
|
||
class Article {
|
||
id
|
||
name
|
||
alias
|
||
doctors
|
||
services
|
||
content
|
||
}
|
||
|
||
class News {
|
||
id
|
||
name
|
||
alias
|
||
regionId
|
||
content
|
||
photos
|
||
}
|
||
|
||
class Promo {
|
||
id
|
||
name
|
||
alias
|
||
regionId
|
||
clinics
|
||
period
|
||
}
|
||
```
|
||
|
||
## Cabinet ER-схема
|
||
|
||
```mermaid
|
||
erDiagram
|
||
CITY ||--o{ FILIAL : "id -> city_id"
|
||
CITY ||--o{ REVIEW_SOURCE : "id -> city_id"
|
||
CITY ||--o| BANNER : "id -> city_id"
|
||
FILIAL ||--o{ REVIEW_SOURCE : "id -> filial_id"
|
||
CATEGORY_PAGE ||--o{ PAGE : "id -> category_id"
|
||
RECORD ||--o| ALERT_SMS : "id -> record_id"
|
||
WIDGET_FORM ||--o{ WIDGET_FORM_INPUT : "id -> widget_form_id"
|
||
|
||
CITY {
|
||
int id PK
|
||
string name
|
||
int region_id
|
||
int time_zone
|
||
}
|
||
|
||
FILIAL {
|
||
int id PK
|
||
int city_id FK
|
||
int fid
|
||
string name
|
||
string address
|
||
int site_id
|
||
bool active
|
||
string company
|
||
}
|
||
|
||
REVIEW_SOURCE {
|
||
int id PK
|
||
int city_id FK
|
||
int filial_id FK
|
||
string name
|
||
int count_row
|
||
bool active
|
||
float rating
|
||
date date_create
|
||
}
|
||
|
||
BANNER {
|
||
int id PK
|
||
int city_id FK
|
||
string href
|
||
string src
|
||
bool active
|
||
}
|
||
|
||
CATEGORY_PAGE {
|
||
int id PK
|
||
string name
|
||
bool active
|
||
}
|
||
|
||
PAGE {
|
||
int id PK
|
||
int category_id FK
|
||
string name
|
||
string alias
|
||
text description
|
||
bool active
|
||
}
|
||
|
||
RECORD {
|
||
int id PK
|
||
int specialist_id
|
||
string phone
|
||
datetime create_at
|
||
string hash
|
||
json reserve
|
||
}
|
||
|
||
ALERT_SMS {
|
||
int id PK
|
||
int record_id FK
|
||
datetime date_create
|
||
text response
|
||
}
|
||
|
||
WIDGET_FORM {
|
||
int id PK
|
||
string name
|
||
}
|
||
|
||
WIDGET_FORM_INPUT {
|
||
int id PK
|
||
int widget_form_id FK
|
||
string text
|
||
string type
|
||
string bitrix24_id
|
||
int sort
|
||
}
|
||
```
|
||
|
||
### Cabinet: перевод сущностей
|
||
|
||
| Сущность на диаграмме | Русское название | Смысл |
|
||
| --- | --- | --- |
|
||
| `CITY` | Город | Региональная привязка кабинета |
|
||
| `FILIAL` | Филиал | Клиника/филиал, привязанный к городу |
|
||
| `REVIEW_SOURCE` | Источник отзывов | Внешний источник рейтинга/отзывов по городу или филиалу |
|
||
| `BANNER` | Баннер | Региональный баннер |
|
||
| `CATEGORY_PAGE` | Категория страниц | Группа CMS-страниц |
|
||
| `PAGE` | Страница | CMS-страница |
|
||
| `RECORD` | Запись пациента | Локальная запись факта бронирования |
|
||
| `ALERT_SMS` | SMS-уведомление | Ответ SMS-провайдера по записи |
|
||
| `WIDGET_FORM` | Форма виджета | Конструктор формы |
|
||
| `WIDGET_FORM_INPUT` | Поле формы | Поле формы виджета |
|
||
|
||
### Cabinet: ключи связей
|
||
|
||
| Связь | Тип | Ключи |
|
||
| --- | --- | --- |
|
||
| Город -> Филиал | `OneToMany` / `ManyToOne` | Doctrine создает `filial.city_id -> city.id` |
|
||
| Город -> Источник отзывов | `OneToMany` / `ManyToOne` | Doctrine создает `review_source.city_id -> city.id`, `nullable=false` |
|
||
| Город -> Баннер | `OneToOne` | Doctrine создает `banner.city_id -> city.id` |
|
||
| Филиал -> Источник отзывов | `OneToMany` / `ManyToOne` | Doctrine создает `review_source.filial_id -> filial.id` |
|
||
| Категория страниц -> Страница | `OneToMany` / `ManyToOne` | Doctrine создает `page.category_id -> category_page.id`, `nullable=false` |
|
||
| Запись -> SMS-уведомление | `OneToOne` | Doctrine создает `alert_sms.record_id -> record.id` |
|
||
| Форма виджета -> Поле формы | `OneToMany` / `ManyToOne` | Doctrine создает `widget_form_input.widget_form_id -> widget_form.id` |
|
||
|
||
`Record.specialistId`, `SpecialistView.dcode`, `LocationView.specialistId`, `PriceList.filial`, `PriceList.groupId`, `PriceList.kodoper` в cabinet используются как логические внешние ключи и фильтры, но не оформлены как Doctrine relations.
|
||
|
||
## Cabinet представления и справочники
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class SpecialistView {
|
||
id
|
||
name
|
||
speciality
|
||
category
|
||
experience
|
||
description
|
||
alias
|
||
dcode
|
||
regionId
|
||
kodoper
|
||
acceptsDms
|
||
}
|
||
|
||
class LocationView {
|
||
id
|
||
dcode
|
||
department
|
||
filial
|
||
specialistId
|
||
onlineMode
|
||
active
|
||
nearestDate
|
||
}
|
||
|
||
class PriceDepartment {
|
||
id
|
||
name
|
||
groupId
|
||
groupName
|
||
doctCount
|
||
viewInWeb
|
||
}
|
||
|
||
class PriceList {
|
||
id
|
||
kodoper
|
||
schname
|
||
specname
|
||
speccode
|
||
priceInfo
|
||
discprice
|
||
filial
|
||
groupId
|
||
}
|
||
|
||
class User {
|
||
id
|
||
email
|
||
roles
|
||
uid
|
||
token
|
||
fullName
|
||
phone
|
||
confirm
|
||
createdAt
|
||
lastActivityAt
|
||
}
|
||
|
||
class Usrlog {
|
||
id
|
||
pcode
|
||
agent
|
||
clientIp
|
||
method
|
||
createdAt
|
||
}
|
||
|
||
class DirectCompany {
|
||
id
|
||
name
|
||
companyId
|
||
city
|
||
}
|
||
|
||
class DirectReport {
|
||
id
|
||
date
|
||
adGroupId
|
||
campaignId
|
||
adId
|
||
impressions
|
||
clicks
|
||
cost
|
||
conversions
|
||
}
|
||
```
|
||
|
||
## Общие доменные понятия
|
||
|
||
- `Specialist` / `SpecialistView` - врач. В `backend` это полноценная сущность с явными связями; в `cabinet` это view-модель для чтения.
|
||
- `Location` / `LocationView` - где и как врач принимает.
|
||
- `PriceList` и `PriceDepartment` - прайс и группы услуг.
|
||
- `Record` - запись пациента, хранит payload бронирования в `reserve`.
|
||
- `AlertSms` - SMS-уведомление по записи.
|
||
- `Filial` и `Department` - филиалы и отделения.
|
||
- `Review` и `ReviewSource` - отзывы и источники отзывов.
|
||
- `WidgetForm` и `WidgetFormInput` - динамические формы для виджетов.
|
||
|
||
## Особенности модели
|
||
|
||
- В `backend` используются PHP attributes Doctrine, в `cabinet` - annotations.
|
||
- Часть связей хранится не как Doctrine relation, а как внешние идентификаторы (`dcode`, `fid`, `did`, `regionId`, `groupId`, `kodoper`). Это важно: не все связи можно увидеть по `ManyToOne`.
|
||
- `backend` подключается к нескольким источникам данных: основной PostgreSQL, Bitrix MySQL и базе cabinet.
|
||
- `cabinet` содержит view-сущности (`SpecialistView`, `LocationView`), поэтому часть данных, вероятно, приходит из SQL views или синхронизированных таблиц.
|