med-cms 1.0.0-beta.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +13 -0
- package/README.md +117 -0
- package/database/config/db-init.js +67 -53
- package/database/models/appointments.js +35 -20
- package/database/models/clinics.js +25 -13
- package/database/models/diseases.js +21 -13
- package/database/models/districts.js +55 -0
- package/database/models/news.js +25 -0
- package/database/models/user-addresses.js +21 -14
- package/database/models/users.js +69 -26
- package/database/services/admin/create-clinic.js +18 -6
- package/database/services/admin/db-analytics.js +42 -16
- package/database/services/admin/index.js +6 -6
- package/database/services/admin/manage-staff.js +47 -17
- package/database/services/doctor/conduct-exam.js +26 -20
- package/database/services/doctor/get-schedule.js +26 -18
- package/database/services/doctor/index.js +6 -6
- package/database/services/doctor/patient-access.js +34 -25
- package/database/services/patient/book-appointment.js +37 -17
- package/database/services/patient/calc-district.js +70 -21
- package/database/services/patient/get-profile.js +49 -21
- package/database/services/patient/index.js +5 -5
- package/env_init.js +55 -0
- package/index.d.ts +38 -24
- package/index.js +48 -25
- package/migrate-diseases.js +192 -120
- package/package.json +3 -3
- package/public/css/admin.css +80 -80
- package/public/css/auth.css +52 -44
- package/public/css/doctor.css +131 -125
- package/public/css/home.css +155 -0
- package/public/html/dashboard-admin.html +423 -101
- package/public/html/dashboard-doctor.html +155 -66
- package/public/html/dashboard-patient.html +199 -94
- package/public/html/home/about.html +0 -0
- package/public/html/home/contact.html +0 -0
- package/public/html/home/info.html +0 -0
- package/public/html/home.html +159 -0
- package/public/html/login.html +73 -43
- package/public/js/admin.js +329 -165
- package/public/js/auth.js +45 -41
- package/public/js/doctor.js +117 -103
- package/public/js/home.js +113 -0
- package/public/js/patient.js +130 -108
- package/server/midleware.js +33 -13
- package/server/permissions.js +105 -43
- package/server/server.js +517 -176
- package/README +0 -161
- package/test.js +0 -27
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2026 RPM-programmer (github) / prm-programmer (npmjs)
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or
|
|
4
|
+
without fee is hereby granted, provided that the above copyright notice and this permission
|
|
5
|
+
notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
8
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
9
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
10
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
11
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
12
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
13
|
+
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# MED-CMS (v1.0.0)
|
|
2
|
+
|
|
3
|
+
> ## `med-cms` — Это автономный медецинский движок для управления сетями клиник, персоналом и расписание приймов и т.д. Написан `med-cms` на node.js с использованием **express**, **sequelize**
|
|
4
|
+
|
|
5
|
+
### Модуль поставляется со встроенной базой данных, системой разграничения прав доступа на основе **34 медицинских специальностей** и готовым веб-интерфейсом (АРМ Врача, Личный Кабинет Пациента, Панель Суперадминистратора), а также готовой главной страницей
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Ключевые возможности
|
|
10
|
+
|
|
11
|
+
- **Автономность** База данных SQLite3 не требует установки тяжелых серверов (PostgreSQL/MySQL/MongoDB). Файл БД и таблицы создаются автоматически прямо в папке проекта.
|
|
12
|
+
- **Безопасность и Сессии:** Авторизация реализована через зашифрованные JWT-токены, хранящиеся в защищенных **HttpOnly Cookie**. JS на клиенте не имеет доступа к токену.
|
|
13
|
+
- **Матрица прав доступа (34 специальности):** Гибкое разграничение возможностей врачей. Стоматолог или Эндокринолог не смогут получить доступ к эндпоинтам назначения нейрохирургических операций — то-есть не к своим данным
|
|
14
|
+
- **Автоматический расчет участков:** При регистрации или изменении адреса проживания пациента система сама вычисляет номер его терапевтического участка и закрепленный кабинет врача. Участки настраиваются админом.
|
|
15
|
+
- **Статистика болезней и МКБ-10:** Выделенный независимый справочник заболеваний в базе данных с возможностью автономной миграции и автоматическим инкрементом счетчиков заболеваемости при закрытии приемов врачами.
|
|
16
|
+
- **Возможность управлять новостями** А админ панеле есть форма позволяющая выкладывать новости.
|
|
17
|
+
- **Автозапись в `.env`** При запуске проекта код проверяет есть ли `.env` и настройки в нём, если то записывает настройки в него.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Установка и быстрая настройка
|
|
22
|
+
|
|
23
|
+
### 1. Установка модуля
|
|
24
|
+
|
|
25
|
+
#### Установите пакет в ваше Node.js приложение:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install med-cms
|
|
29
|
+
# или
|
|
30
|
+
yarn add med-cms
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
> **Важно для пользователей Linux:** Пакетам `sequelize` и `sqlite3` иногда требуются компиляторы C++ для сборки бинарных файлов под вашу архитектуру ядра. Перед развертыванием убедитесь, что в системе установлены базовые утилиты сборки:
|
|
34
|
+
>
|
|
35
|
+
> ```bash
|
|
36
|
+
> sudo apt update && sudo apt install -y build-essential python3
|
|
37
|
+
> ```
|
|
38
|
+
|
|
39
|
+
### 2. Подключение в Express
|
|
40
|
+
|
|
41
|
+
#### Для интеграции CMS в ваш Express-сервер импортируйте функцию инициализации и передайте туда ваш инстанс `app`:
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const express = require("express");
|
|
45
|
+
require("dotenv").config();
|
|
46
|
+
|
|
47
|
+
const app = express();
|
|
48
|
+
|
|
49
|
+
// Импортируем инициализатор нашего npm-модуля
|
|
50
|
+
const MI = require("med-cms");
|
|
51
|
+
|
|
52
|
+
// здесь ваши роуты ...
|
|
53
|
+
app.get("/", async (req, res) => {
|
|
54
|
+
res.end("HELLO!");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
(async function startServer() {
|
|
58
|
+
// Запускаем медицинский движок med-cms
|
|
59
|
+
// требуется асинхронность из-за запросов в к бд
|
|
60
|
+
await MI(app);
|
|
61
|
+
|
|
62
|
+
app.listen(3000, () => {
|
|
63
|
+
console.log(`Сервер успешно запущен!`);
|
|
64
|
+
});
|
|
65
|
+
})();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Миграция расширенного справочника болезней (РБ)
|
|
71
|
+
|
|
72
|
+
### Справочник заболеваний полностью изолирован от кода ядра CMS. Для наполнения пустой таблицы `Diseases` актуальными и социально значимыми кодами МКБ-10 для Республики Беларусь, запустите команду миграции перед стартом основного сервера:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
node node_modules/med-cms/migrate-diseases.js
|
|
76
|
+
# или
|
|
77
|
+
node node_modules\med-cms\migrate-diseases.js
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Это наполнит отдельную таблицу базы данных 18 распространенными диагнозами (ОРВИ, Острый бронхит, Внебольничная пневмония, Артериальная гипертензия, Остеохондроз, Эндемический зоб и др.), которые автоматически начнут подтягиваться в HTML-тег `<select>` на рабочем месте врача.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Роли и Маршрутизация (API & Web)
|
|
85
|
+
|
|
86
|
+
### Архитектура роутера жестко разделяет отдачу интерфейсов и данных, исключая ошибки парсинга `JSON.parse` в браузере:
|
|
87
|
+
|
|
88
|
+
- **Интерфейс (Web-страницы):** `http://localhost:3000/med/...`
|
|
89
|
+
- **Данные (JSON API):** `http://localhost:3000/med/api/...`
|
|
90
|
+
|
|
91
|
+
### Матрица маршрутов
|
|
92
|
+
|
|
93
|
+
| Действие | Метод | Маршрут (URL) | Доступ (Роль) |
|
|
94
|
+
| :--------------------------- | :---- | :---------------------------------------- | :------------ |
|
|
95
|
+
| Страница авторизации и входа | `GET` | `/med/html/login.html` (или `/med/login`) | Публичный |
|
|
96
|
+
| Главная страница | `GET` | `/med/html/home.html` (или `/med/home`) | Публичный |
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Алгоритм проведения инициялизации:
|
|
101
|
+
|
|
102
|
+
1. Выполните миграцию справочника болезней: `node node_modules/med-cms/migrate-diseases.js` или `node node_modules\med-cms\migrate-diseases.js`.
|
|
103
|
+
2. Откройте браузер на `http://localhost:3000/med/home` и нажмите на кнопку **`вход`**.
|
|
104
|
+
3. Авторизируйтесь (заполните поля почты и пароля) (данные можно узнать в настройках `.env`)
|
|
105
|
+
4. В левой форме добавьте поликлинику. Она запишется в SQLite под числовым `ID: 1`.
|
|
106
|
+
5. Кликните на карточку «Всего клиник», чтобы развернуть интерактивную таблицу и проверить числовой ID.
|
|
107
|
+
6. В правой форме зарегистрируйте аккаунт врача, выбрав созданную поликлинику №1 из выпадающего списка.
|
|
108
|
+
7. Выйдите из админки, нажмите «Зарегистрироваться как пациент» и создайте профиль.
|
|
109
|
+
8. В кабинете пациента выберите созданного врача, дату, время и нажмите **«Забронировать талон»**. Талон мгновенно отобразится в блоке предстоящих приемов.
|
|
110
|
+
9. Авторизуйтесь под созданным врачом. Пациент появится в вашей очереди. Нажмите «Принять», заполните жалобы, выберите из подгрузившегося справочника диагноз _«Острый бронхит»_ и закройте прием. Системные счетчики зафиксируют визит, а статистика болезни обновится в SQLite.
|
|
111
|
+
10. Также можете с аккаунта админа настроить участки и новости!
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Лицензия
|
|
116
|
+
|
|
117
|
+
### Проект распространяется на условиях открытой лицензии [**ISC**](LICENSE.txt). Разработано [RPM-programmer](https://github.com/RPM-programmer/) для создания масштабируемых и отказоустойчивых медицинских информационных систем.
|
|
@@ -1,88 +1,102 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Полностью рабочий database/config/db-init.js с динамическим расширением модели
|
|
2
|
+
|
|
3
|
+
const { Sequelize, DataTypes } = require("sequelize");
|
|
4
|
+
const path = require("path");
|
|
3
5
|
const logger = require("custom-color-logs").print;
|
|
4
6
|
|
|
5
7
|
let sequelize;
|
|
6
8
|
|
|
7
9
|
function getSequelizeInstance(dbPath) {
|
|
8
10
|
if (!sequelize) {
|
|
9
|
-
const storagePath = dbPath || path.join(process.cwd(),
|
|
11
|
+
const storagePath = dbPath || path.join(process.cwd(), "med-cms.sqlite");
|
|
10
12
|
sequelize = new Sequelize({
|
|
11
|
-
dialect:
|
|
13
|
+
dialect: "sqlite",
|
|
12
14
|
storage: storagePath,
|
|
13
|
-
logging: false
|
|
15
|
+
logging: false,
|
|
14
16
|
});
|
|
15
17
|
}
|
|
16
18
|
return sequelize;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
async function initDatabase(dbPath) {
|
|
20
|
-
const instance = getSequelizeInstance(dbPath);
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
await instance.authenticate();
|
|
24
|
-
console.log(logger.DatabaseMedInfo("Успешное подключение к SQLite через Sequelize."));
|
|
25
|
-
|
|
26
|
-
// 1. Импортируем функции моделей и сразу инициализируем их инстансом sequelize
|
|
27
|
-
const models = {
|
|
28
|
-
Clinic: require(path.join(__dirname, '..', 'models', 'clinics'))(instance, DataTypes),
|
|
29
|
-
User: require(path.join(__dirname, '..', 'models', 'users'))(instance, DataTypes),
|
|
30
|
-
UserAddress: require(path.join(__dirname, '..', 'models', 'user-addresses'))(instance, DataTypes),
|
|
31
|
-
Disease: require(path.join(__dirname, '..', 'models', 'diseases'))(instance, DataTypes),
|
|
32
|
-
Appointment: require(path.join(__dirname, '..', 'models', 'appointments'))(instance, DataTypes)
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// 2. Автоматически запускаем связи, которые мы прописали внутри файлов таблиц
|
|
36
|
-
Object.keys(models).forEach((modelName) => {
|
|
37
|
-
if (models[modelName].associate) {
|
|
38
|
-
models[modelName].associate(models);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
await instance.sync({ alter: true });
|
|
43
|
-
console.log(logger.DatabaseMedInfo("Все таблицы базы данных успешно синхронизированы."));
|
|
44
|
-
} catch (error) {
|
|
45
|
-
console.log(logger.DatabaseMedError('Ошибка инициализации базы данных:', error));
|
|
46
|
-
throw error;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// В самом конце вашего database/config/db-init.js вместо старого exports:
|
|
51
|
-
|
|
52
21
|
const db = {
|
|
53
22
|
getSequelizeInstance,
|
|
54
23
|
initDatabase,
|
|
55
|
-
get sequelize() {
|
|
56
|
-
|
|
57
|
-
|
|
24
|
+
get sequelize() {
|
|
25
|
+
return sequelize;
|
|
26
|
+
},
|
|
27
|
+
models: {},
|
|
58
28
|
};
|
|
59
29
|
|
|
60
|
-
// Перепишем функцию initDatabase, чтобы она сохраняла модели в db.models и самом объекте db
|
|
61
30
|
async function initDatabase(dbPath) {
|
|
62
31
|
const instance = getSequelizeInstance(dbPath);
|
|
63
|
-
|
|
32
|
+
|
|
64
33
|
try {
|
|
65
34
|
await instance.authenticate();
|
|
66
|
-
console.log(
|
|
35
|
+
console.log(
|
|
36
|
+
logger.DatabaseMedInfo("Успешное подключение к SQLite через Sequelize."),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// 1. Инициализируем базовые модели из фабрик
|
|
40
|
+
db.models.Clinic = db.Clinic = require(
|
|
41
|
+
path.join(__dirname, "..", "models", "clinics"),
|
|
42
|
+
)(instance, DataTypes);
|
|
43
|
+
db.models.User = db.User = require(
|
|
44
|
+
path.join(__dirname, "..", "models", "users"),
|
|
45
|
+
)(instance, DataTypes);
|
|
46
|
+
db.models.UserAddress = db.UserAddress = require(
|
|
47
|
+
path.join(__dirname, "..", "models", "user-addresses"),
|
|
48
|
+
)(instance, DataTypes);
|
|
49
|
+
db.models.Disease = db.Disease = require(
|
|
50
|
+
path.join(__dirname, "..", "models", "diseases"),
|
|
51
|
+
)(instance, DataTypes);
|
|
52
|
+
db.models.Appointment = db.Appointment = require(
|
|
53
|
+
path.join(__dirname, "..", "models", "appointments"),
|
|
54
|
+
)(instance, DataTypes);
|
|
55
|
+
db.models.District = db.District = require(
|
|
56
|
+
path.join(__dirname, "..", "models", "districts"),
|
|
57
|
+
)(instance, DataTypes);
|
|
58
|
+
db.models.News = db.News = require(
|
|
59
|
+
path.join(__dirname, "..", "models", "news"),
|
|
60
|
+
)(instance, DataTypes);
|
|
61
|
+
// =========================================================================
|
|
62
|
+
// 🔥 ДИНАМИЧЕСКИЙ ХАК: Расширяем модель User прямо в памяти до синхронизации!
|
|
63
|
+
// =========================================================================
|
|
64
|
+
|
|
65
|
+
// А) Принудительно внедряем колонку должностей (position), которой нет в файле
|
|
66
|
+
db.User.rawAttributes.position = {
|
|
67
|
+
type: DataTypes.STRING,
|
|
68
|
+
allowNull: true,
|
|
69
|
+
defaultValue: "Пациент",
|
|
70
|
+
};
|
|
71
|
+
db.User.refreshAttributes(); // Заставляем Sequelize пересобрать схему модели
|
|
72
|
+
|
|
73
|
+
// Б) Снимаем жесткие ограничения ENUM с поля specialty, превращая его в гибкую строку
|
|
74
|
+
db.User.rawAttributes.specialty = {
|
|
75
|
+
type: DataTypes.STRING,
|
|
76
|
+
allowNull: true,
|
|
77
|
+
};
|
|
78
|
+
db.User.refreshAttributes(); // Синхронизируем изменения атрибутов
|
|
67
79
|
|
|
68
|
-
//
|
|
69
|
-
db.models.Clinic = db.Clinic = require(path.join(__dirname, '..', 'models', 'clinics'))(instance, DataTypes);
|
|
70
|
-
db.models.User = db.User = require(path.join(__dirname, '..', 'models', 'users'))(instance, DataTypes);
|
|
71
|
-
db.models.UserAddress = db.UserAddress = require(path.join(__dirname, '..', 'models', 'user-addresses'))(instance, DataTypes);
|
|
72
|
-
db.models.Disease = db.Disease = require(path.join(__dirname, '..', 'models', 'diseases'))(instance, DataTypes);
|
|
73
|
-
db.models.Appointment = db.Appointment = require(path.join(__dirname, '..', 'models', 'appointments'))(instance, DataTypes);
|
|
80
|
+
// =========================================================================
|
|
74
81
|
|
|
75
|
-
//
|
|
82
|
+
// 2. Безопасно запускаем связи (Associations) между всеми таблицами
|
|
76
83
|
Object.keys(db.models).forEach((modelName) => {
|
|
77
84
|
if (db.models[modelName].associate) {
|
|
78
85
|
db.models[modelName].associate(db.models);
|
|
79
86
|
}
|
|
80
87
|
});
|
|
81
88
|
|
|
89
|
+
// 3. Синхронизируем базу данных с учётом динамически добавленных полей
|
|
82
90
|
await instance.sync({ alter: true });
|
|
83
|
-
console.log(
|
|
91
|
+
console.log(
|
|
92
|
+
logger.DatabaseMedInfo(
|
|
93
|
+
"Все таблицы базы данных успешно синхронизированы.",
|
|
94
|
+
),
|
|
95
|
+
);
|
|
84
96
|
} catch (error) {
|
|
85
|
-
console.log(
|
|
97
|
+
console.log(
|
|
98
|
+
logger.DatabaseMedError("Ошибка инициализации базы данных:", error),
|
|
99
|
+
);
|
|
86
100
|
throw error;
|
|
87
101
|
}
|
|
88
102
|
}
|
|
@@ -1,28 +1,43 @@
|
|
|
1
1
|
module.exports = (sequelize, DataTypes) => {
|
|
2
|
-
const Appointment = sequelize.define(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
const Appointment = sequelize.define(
|
|
3
|
+
"Appointment",
|
|
4
|
+
{
|
|
5
|
+
id: {
|
|
6
|
+
type: DataTypes.UUID,
|
|
7
|
+
defaultValue: DataTypes.UUIDV4,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
},
|
|
10
|
+
date: { type: DataTypes.DATEONLY, allowNull: false },
|
|
11
|
+
time: { type: DataTypes.STRING, allowNull: false },
|
|
12
|
+
status: {
|
|
13
|
+
type: DataTypes.ENUM("ожидается", "завершен", "отменен"),
|
|
14
|
+
defaultValue: "ожидается",
|
|
15
|
+
allowNull: false,
|
|
16
|
+
},
|
|
17
|
+
complaints: { type: DataTypes.TEXT, allowNull: true },
|
|
18
|
+
objectively: { type: DataTypes.TEXT, allowNull: true },
|
|
19
|
+
doctorNotes: { type: DataTypes.TEXT, allowNull: true },
|
|
7
20
|
},
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
status: {
|
|
11
|
-
type: DataTypes.ENUM('ожидается', 'завершен', 'отменен'),
|
|
12
|
-
defaultValue: 'ожидается',
|
|
13
|
-
allowNull: false
|
|
21
|
+
{
|
|
22
|
+
timestamps: true,
|
|
14
23
|
},
|
|
15
|
-
|
|
16
|
-
objectively: { type: DataTypes.TEXT, allowNull: true },
|
|
17
|
-
doctorNotes: { type: DataTypes.TEXT, allowNull: true }
|
|
18
|
-
}, {
|
|
19
|
-
timestamps: true
|
|
20
|
-
});
|
|
24
|
+
);
|
|
21
25
|
|
|
22
26
|
Appointment.associate = (models) => {
|
|
23
|
-
Appointment.belongsTo(models.User, {
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
Appointment.belongsTo(models.User, {
|
|
28
|
+
as: "Patient",
|
|
29
|
+
foreignKey: "PatientId",
|
|
30
|
+
onDelete: "CASCADE",
|
|
31
|
+
});
|
|
32
|
+
Appointment.belongsTo(models.User, {
|
|
33
|
+
as: "Doctor",
|
|
34
|
+
foreignKey: "DoctorId",
|
|
35
|
+
onDelete: "CASCADE",
|
|
36
|
+
});
|
|
37
|
+
Appointment.belongsTo(models.Disease, {
|
|
38
|
+
foreignKey: "DiseaseId",
|
|
39
|
+
onDelete: "SET NULL",
|
|
40
|
+
});
|
|
26
41
|
};
|
|
27
42
|
|
|
28
43
|
return Appointment;
|
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
module.exports = (sequelize, DataTypes) => {
|
|
2
|
-
const Clinic = sequelize.define(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
const Clinic = sequelize.define(
|
|
3
|
+
"Clinic",
|
|
4
|
+
{
|
|
5
|
+
id: {
|
|
6
|
+
type: DataTypes.INTEGER,
|
|
7
|
+
autoIncrement: true,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
},
|
|
10
|
+
name: { type: DataTypes.STRING, allowNull: false, unique: true },
|
|
11
|
+
type: {
|
|
12
|
+
type: DataTypes.ENUM(
|
|
13
|
+
"больница",
|
|
14
|
+
"поликлиника",
|
|
15
|
+
"станция скорой помощи",
|
|
16
|
+
"частный медицинский центр",
|
|
17
|
+
),
|
|
18
|
+
allowNull: false,
|
|
19
|
+
},
|
|
20
|
+
address: { type: DataTypes.STRING, allowNull: false },
|
|
21
|
+
phone: { type: DataTypes.STRING, allowNull: true },
|
|
7
22
|
},
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}, {
|
|
13
|
-
timestamps: true
|
|
14
|
-
});
|
|
23
|
+
{
|
|
24
|
+
timestamps: true,
|
|
25
|
+
},
|
|
26
|
+
);
|
|
15
27
|
|
|
16
28
|
// Настройка связи прямо в таблице клиник
|
|
17
29
|
Clinic.associate = (models) => {
|
|
18
|
-
Clinic.hasMany(models.User, { foreignKey:
|
|
30
|
+
Clinic.hasMany(models.User, { foreignKey: "ClinicId" });
|
|
19
31
|
};
|
|
20
32
|
|
|
21
33
|
return Clinic;
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
module.exports = (sequelize, DataTypes) => {
|
|
2
|
-
const Disease = sequelize.define(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
const Disease = sequelize.define(
|
|
3
|
+
"Disease",
|
|
4
|
+
{
|
|
5
|
+
id: {
|
|
6
|
+
type: DataTypes.INTEGER,
|
|
7
|
+
autoIncrement: true,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
},
|
|
10
|
+
name: { type: DataTypes.STRING, allowNull: false, unique: true },
|
|
11
|
+
code: { type: DataTypes.STRING, allowNull: false, unique: true },
|
|
12
|
+
treatmentRecommendations: { type: DataTypes.TEXT, allowNull: true },
|
|
13
|
+
recommendedMedications: { type: DataTypes.TEXT, allowNull: true },
|
|
14
|
+
casesCount: {
|
|
15
|
+
type: DataTypes.INTEGER,
|
|
16
|
+
defaultValue: 0,
|
|
17
|
+
allowNull: false,
|
|
18
|
+
},
|
|
7
19
|
},
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
casesCount: { type: DataTypes.INTEGER, defaultValue: 0, allowNull: false }
|
|
13
|
-
}, {
|
|
14
|
-
timestamps: true
|
|
15
|
-
});
|
|
20
|
+
{
|
|
21
|
+
timestamps: true,
|
|
22
|
+
},
|
|
23
|
+
);
|
|
16
24
|
|
|
17
25
|
// У этой модели нет прямых исходящих связей (зависит от Appointment, но связь прописана там)
|
|
18
26
|
return Disease;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module.exports = (sequelize, DataTypes) => {
|
|
2
|
+
const District = sequelize.define(
|
|
3
|
+
"District",
|
|
4
|
+
{
|
|
5
|
+
id: {
|
|
6
|
+
type: DataTypes.INTEGER,
|
|
7
|
+
autoIncrement: true,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
},
|
|
10
|
+
name: {
|
|
11
|
+
type: DataTypes.STRING, // Например: "Терапевтический участок №3"
|
|
12
|
+
allowNull: false,
|
|
13
|
+
},
|
|
14
|
+
city: {
|
|
15
|
+
type: DataTypes.STRING, // Например: "Минск"
|
|
16
|
+
allowNull: false,
|
|
17
|
+
},
|
|
18
|
+
street: {
|
|
19
|
+
type: DataTypes.STRING, // Например: "Машерова"
|
|
20
|
+
allowNull: false,
|
|
21
|
+
set(value) {
|
|
22
|
+
this.setDataValue("street", value.toLowerCase().trim());
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
// Диапазон обслуживаемых домов (например: "1-20" или "все")
|
|
26
|
+
houses: {
|
|
27
|
+
type: DataTypes.STRING,
|
|
28
|
+
allowNull: false,
|
|
29
|
+
defaultValue: "все",
|
|
30
|
+
},
|
|
31
|
+
cabinet: {
|
|
32
|
+
type: DataTypes.STRING, // Например: "Кабинет №302"
|
|
33
|
+
allowNull: false,
|
|
34
|
+
},
|
|
35
|
+
doctorName: {
|
|
36
|
+
type: DataTypes.STRING,
|
|
37
|
+
allowNull: true,
|
|
38
|
+
defaultValue: "Врач не назначен",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
timestamps: true,
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
District.associate = (models) => {
|
|
47
|
+
// ЖЕСТКАЯ ПРИВЯЗКА: Участок и его кабинет принадлежат конкретному учреждению
|
|
48
|
+
District.belongsTo(models.Clinic, {
|
|
49
|
+
foreignKey: "ClinicId",
|
|
50
|
+
onDelete: "CASCADE",
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return District;
|
|
55
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module.exports = (sequelize, DataTypes) => {
|
|
2
|
+
const News = sequelize.define(
|
|
3
|
+
"News",
|
|
4
|
+
{
|
|
5
|
+
id: {
|
|
6
|
+
type: DataTypes.INTEGER,
|
|
7
|
+
autoIncrement: true,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
},
|
|
10
|
+
title: {
|
|
11
|
+
type: DataTypes.STRING,
|
|
12
|
+
allowNull: false,
|
|
13
|
+
},
|
|
14
|
+
content: {
|
|
15
|
+
type: DataTypes.TEXT,
|
|
16
|
+
allowNull: false,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
timestamps: true,
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return News;
|
|
25
|
+
};
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
module.exports = (sequelize, DataTypes) => {
|
|
2
|
-
const UserAddress = sequelize.define(
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
const UserAddress = sequelize.define(
|
|
3
|
+
"UserAddress",
|
|
4
|
+
{
|
|
5
|
+
id: {
|
|
6
|
+
type: DataTypes.UUID,
|
|
7
|
+
defaultValue: DataTypes.UUIDV4,
|
|
8
|
+
primaryKey: true,
|
|
9
|
+
},
|
|
10
|
+
city: { type: DataTypes.STRING, allowNull: false },
|
|
11
|
+
street: { type: DataTypes.STRING, allowNull: false },
|
|
12
|
+
houseNumber: { type: DataTypes.STRING, allowNull: false },
|
|
13
|
+
assignedDistrict: { type: DataTypes.STRING, allowNull: true },
|
|
14
|
+
assignedCabinet: { type: DataTypes.STRING, allowNull: true },
|
|
7
15
|
},
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
assignedCabinet: { type: DataTypes.STRING, allowNull: true }
|
|
13
|
-
}, {
|
|
14
|
-
timestamps: true
|
|
15
|
-
});
|
|
16
|
+
{
|
|
17
|
+
timestamps: true,
|
|
18
|
+
},
|
|
19
|
+
);
|
|
16
20
|
|
|
17
21
|
UserAddress.associate = (models) => {
|
|
18
|
-
UserAddress.belongsTo(models.User, {
|
|
22
|
+
UserAddress.belongsTo(models.User, {
|
|
23
|
+
foreignKey: "UserId",
|
|
24
|
+
onDelete: "CASCADE",
|
|
25
|
+
});
|
|
19
26
|
};
|
|
20
27
|
|
|
21
28
|
return UserAddress;
|