med-cms 1.0.0-beta.2 → 1.0.0-beta.5

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.
Files changed (44) hide show
  1. package/database/config/db-init.js +67 -53
  2. package/database/models/appointments.js +35 -20
  3. package/database/models/clinics.js +25 -13
  4. package/database/models/diseases.js +21 -13
  5. package/database/models/districts.js +55 -0
  6. package/database/models/news.js +25 -0
  7. package/database/models/user-addresses.js +21 -14
  8. package/database/models/users.js +69 -26
  9. package/database/services/admin/create-clinic.js +18 -6
  10. package/database/services/admin/db-analytics.js +42 -16
  11. package/database/services/admin/index.js +6 -6
  12. package/database/services/admin/manage-staff.js +47 -17
  13. package/database/services/doctor/conduct-exam.js +26 -20
  14. package/database/services/doctor/get-schedule.js +26 -18
  15. package/database/services/doctor/index.js +6 -6
  16. package/database/services/doctor/patient-access.js +34 -25
  17. package/database/services/patient/book-appointment.js +37 -17
  18. package/database/services/patient/calc-district.js +70 -21
  19. package/database/services/patient/get-profile.js +49 -21
  20. package/database/services/patient/index.js +5 -5
  21. package/index.js +45 -22
  22. package/migrate-diseases.js +192 -120
  23. package/package.json +1 -1
  24. package/public/css/admin.css +80 -80
  25. package/public/css/auth.css +52 -44
  26. package/public/css/doctor.css +131 -125
  27. package/public/css/home.css +155 -0
  28. package/public/html/dashboard-admin.html +423 -101
  29. package/public/html/dashboard-doctor.html +155 -66
  30. package/public/html/dashboard-patient.html +199 -94
  31. package/public/html/home/about.html +0 -0
  32. package/public/html/home/contact.html +0 -0
  33. package/public/html/home/info.html +0 -0
  34. package/public/html/home.html +159 -0
  35. package/public/html/login.html +73 -43
  36. package/public/js/admin.js +329 -165
  37. package/public/js/auth.js +45 -41
  38. package/public/js/doctor.js +117 -103
  39. package/public/js/home.js +113 -0
  40. package/public/js/patient.js +130 -108
  41. package/server/midleware.js +33 -13
  42. package/server/permissions.js +105 -43
  43. package/server/server.js +493 -176
  44. package/test.js +12 -23
@@ -1,88 +1,102 @@
1
- const { Sequelize, DataTypes } = require('sequelize');
2
- const path = require('path');
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(), 'med-cms.sqlite');
11
+ const storagePath = dbPath || path.join(process.cwd(), "med-cms.sqlite");
10
12
  sequelize = new Sequelize({
11
- dialect: 'sqlite',
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() { return sequelize; },
56
- // Динамически прокидываем модели наружу, чтобы они были доступны как db.User, db.Clinic и т.д.
57
- models: {}
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(logger.DatabaseMedInfo("Успешное подключение к SQLite через Sequelize."));
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(logger.DatabaseMedInfo("Все таблицы базы данных успешно синхронизированы."));
91
+ console.log(
92
+ logger.DatabaseMedInfo(
93
+ "Все таблицы базы данных успешно синхронизированы.",
94
+ ),
95
+ );
84
96
  } catch (error) {
85
- console.log(logger.DatabaseMedError('Ошибка инициализации базы данных:', error));
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('Appointment', {
3
- id: {
4
- type: DataTypes.UUID,
5
- defaultValue: DataTypes.UUIDV4,
6
- primaryKey: true
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
- date: { type: DataTypes.DATEONLY, allowNull: false },
9
- time: { type: DataTypes.STRING, allowNull: false },
10
- status: {
11
- type: DataTypes.ENUM('ожидается', 'завершен', 'отменен'),
12
- defaultValue: 'ожидается',
13
- allowNull: false
21
+ {
22
+ timestamps: true,
14
23
  },
15
- complaints: { type: DataTypes.TEXT, allowNull: true },
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, { as: 'Patient', foreignKey: 'PatientId', onDelete: 'CASCADE' });
24
- Appointment.belongsTo(models.User, { as: 'Doctor', foreignKey: 'DoctorId', onDelete: 'CASCADE' });
25
- Appointment.belongsTo(models.Disease, { foreignKey: 'DiseaseId', onDelete: 'SET NULL' });
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('Clinic', {
3
- id: {
4
- type: DataTypes.INTEGER,
5
- autoIncrement: true,
6
- primaryKey: true
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
- name: { type: DataTypes.STRING, allowNull: false, unique: true },
9
- type: { type: DataTypes.ENUM('больница', 'поликлиника', 'станция скорой помощи', 'частный медицинский центр'), allowNull: false },
10
- address: { type: DataTypes.STRING, allowNull: false },
11
- phone: { type: DataTypes.STRING, allowNull: true }
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: 'ClinicId' });
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('Disease', {
3
- id: {
4
- type: DataTypes.INTEGER,
5
- autoIncrement: true,
6
- primaryKey: true
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
- name: { type: DataTypes.STRING, allowNull: false, unique: true },
9
- code: { type: DataTypes.STRING, allowNull: false, unique: true },
10
- treatmentRecommendations: { type: DataTypes.TEXT, allowNull: true },
11
- recommendedMedications: { type: DataTypes.TEXT, allowNull: true },
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('UserAddress', {
3
- id: {
4
- type: DataTypes.UUID,
5
- defaultValue: DataTypes.UUIDV4,
6
- primaryKey: true
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
- city: { type: DataTypes.STRING, allowNull: false },
9
- street: { type: DataTypes.STRING, allowNull: false },
10
- houseNumber: { type: DataTypes.STRING, allowNull: false },
11
- assignedDistrict: { type: DataTypes.STRING, allowNull: true },
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, { foreignKey: 'UserId', onDelete: 'CASCADE' });
22
+ UserAddress.belongsTo(models.User, {
23
+ foreignKey: "UserId",
24
+ onDelete: "CASCADE",
25
+ });
19
26
  };
20
27
 
21
28
  return UserAddress;
@@ -1,35 +1,78 @@
1
1
  module.exports = (sequelize, DataTypes) => {
2
- const User = sequelize.define('User', {
3
- id: {
4
- type: DataTypes.INTEGER,
5
- autoIncrement: true,
6
- primaryKey: true
2
+ const User = sequelize.define(
3
+ "User",
4
+ {
5
+ id: {
6
+ type: DataTypes.INTEGER,
7
+ autoIncrement: true,
8
+ primaryKey: true,
9
+ },
10
+ email: {
11
+ type: DataTypes.STRING,
12
+ allowNull: false,
13
+ unique: true,
14
+ validate: { isEmail: true },
15
+ },
16
+ password: { type: DataTypes.STRING, allowNull: false },
17
+ fullName: { type: DataTypes.STRING, allowNull: false },
18
+ role: {
19
+ type: DataTypes.ENUM("patient", "doctor", "admin"),
20
+ defaultValue: "patient",
21
+ allowNull: false,
22
+ },
23
+ specialty: {
24
+ type: DataTypes.ENUM(
25
+ "Терапевт",
26
+ "Педиатр",
27
+ "Врач общей практики",
28
+ "Кардиолог",
29
+ "Невролог",
30
+ "Эндокринолог",
31
+ "Гастроэнтеролог",
32
+ "Пульмонолог",
33
+ "Нефролог",
34
+ "Ревматолог",
35
+ "Гематолог",
36
+ "Хирург",
37
+ "Травматолог-ортопед",
38
+ "Сосудистый хирург",
39
+ "Нейрохирург",
40
+ "Кардиохирург",
41
+ "Пластический хирург",
42
+ "Офтальмолог",
43
+ "Отоларинголог",
44
+ "Дерматолог",
45
+ "Стоматолог",
46
+ "Гинеколог",
47
+ "Уролог",
48
+ "Онколог",
49
+ "Инфекционист",
50
+ "Аллерголог-иммунолог",
51
+ "Психиатр",
52
+ "Психотерапевт",
53
+ "Нарколог",
54
+ "Анестезиолог-реаниматолог",
55
+ "Рентгенолог",
56
+ "Врач УЗД",
57
+ "Эндоскопист",
58
+ "Патологоанатом",
59
+ ),
60
+ allowNull: true,
61
+ },
62
+ isTemporaryPassword: { type: DataTypes.BOOLEAN, defaultValue: false },
7
63
  },
8
- email: { type: DataTypes.STRING, allowNull: false, unique: true, validate: { isEmail: true } },
9
- password: { type: DataTypes.STRING, allowNull: false },
10
- fullName: { type: DataTypes.STRING, allowNull: false },
11
- role: { type: DataTypes.ENUM('patient', 'doctor', 'admin'), defaultValue: 'patient', allowNull: false },
12
- specialty: {
13
- type: DataTypes.ENUM(
14
- 'Терапевт', 'Педиатр', 'Врач общей практики', 'Кардиолог', 'Невролог',
15
- 'Эндокринолог', 'Гастроэнтеролог', 'Пульмонолог', 'Нефролог', 'Ревматолог',
16
- 'Гематолог', 'Хирург', 'Травматолог-ортопед', 'Сосудистый хирург', 'Нейрохирург',
17
- 'Кардиохирург', 'Пластический хирург', 'Офтальмолог', 'Отоларинголог', 'Дерматолог',
18
- 'Стоматолог', 'Гинеколог', 'Уролог', 'Онколог', 'Инфекционист', 'Аллерголог-иммунолог',
19
- 'Психиатр', 'Психотерапевт', 'Нарколог', 'Анестезиолог-реаниматолог', 'Рентгенолог',
20
- 'Врач УЗД', 'Эндоскопист', 'Патологоанатом'
21
- ),
22
- allowNull: true
64
+ {
65
+ timestamps: true,
23
66
  },
24
- isTemporaryPassword: { type: DataTypes.BOOLEAN, defaultValue: false }
25
- }, {
26
- timestamps: true
27
- });
67
+ );
28
68
 
29
69
  // Настройка связей пользователя
30
70
  User.associate = (models) => {
31
- User.belongsTo(models.Clinic, { foreignKey: 'ClinicId', onDelete: 'SET NULL' });
32
- User.hasOne(models.UserAddress, { foreignKey: 'UserId' });
71
+ User.belongsTo(models.Clinic, {
72
+ foreignKey: "ClinicId",
73
+ onDelete: "SET NULL",
74
+ });
75
+ User.hasOne(models.UserAddress, { foreignKey: "UserId" });
33
76
  };
34
77
 
35
78
  return User;
@@ -1,19 +1,31 @@
1
1
  const path = require("path");
2
- const Clinic = require(path.join(__dirname, '..', '..', 'models', 'clinics'));
2
+ const dbConfig = require(
3
+ path.join(__dirname, "..", "..", "config", "db-init.js"),
4
+ );
5
+
3
6
  const createClinic = {
4
7
  async create(clinicData) {
8
+ const Clinic = dbConfig.models.Clinic || dbConfig.Clinic;
5
9
  const { name, type, address, phone } = clinicData;
10
+
6
11
  if (!name || !type || !address) {
7
- throw new Error('Поля name, type и address обязательны для заполнения');
12
+ throw new Error("Поля name, type и address обязательны для заполнения");
8
13
  }
14
+
9
15
  const candidate = await Clinic.findOne({ where: { name } });
10
16
  if (candidate) {
11
- throw new Error('Медицинское учреждение с таким названием уже зарегистрировано');
17
+ throw new Error(
18
+ "Медицинское учреждение с таким названием уже зарегистрировано",
19
+ );
12
20
  }
21
+
13
22
  return await Clinic.create({ name, type, address, phone });
14
23
  },
24
+
15
25
  async getAll() {
16
- return await Clinic.findAll({ order: [['name', 'ASC']] });
17
- }
26
+ const Clinic = dbConfig.models.Clinic || dbConfig.Clinic;
27
+ return await Clinic.findAll({ order: [["name", "ASC"]] });
28
+ },
18
29
  };
19
- module.exports = createClinic;
30
+
31
+ module.exports = createClinic;
@@ -1,34 +1,60 @@
1
- const path = require("path")
2
- const User = require(path.join(__dirname, '..', '..', 'models', 'users'));
3
- const Clinic = require(path.join(__dirname, '..', '..', 'models', 'clinics'));
4
- const Disease = require(path.join(__dirname, '..', '..', 'models', 'diseases'));
5
- const Appointment = require(path.join(__dirname, '..', '..', 'models', 'appointments'));
1
+ const path = require("path");
2
+ // Импортируем только синглтон конфигурации базы данных
3
+ const dbConfig = require(
4
+ path.join(__dirname, "..", "..", "config", "db-init.js"),
5
+ );
6
+
6
7
  const dbAnalytics = {
8
+ /**
9
+ * Собрать общую медицинскую статистику и данные для дашборда
10
+ */
7
11
  async getStats() {
12
+ // Безопасно достаем проинициализированные фабриками модели онлайн
13
+ const Clinic = dbConfig.models.Clinic || dbConfig.Clinic;
14
+ const User = dbConfig.models.User || dbConfig.User;
15
+ const Disease = dbConfig.models.Disease || dbConfig.Disease;
16
+ const Appointment = dbConfig.models.Appointment || dbConfig.Appointment;
17
+
18
+ if (!Clinic || !User || !Disease || !Appointment) {
19
+ throw new Error(
20
+ "Критическая ошибка: Модели базы данных еще не проинициализированы!",
21
+ );
22
+ }
23
+
24
+ // Собираем агрегированные цифры для карточек админки через Sequelize
8
25
  const totalClinics = await Clinic.count();
9
- const totalPatients = await User.count({ where: { role: 'patient' } });
10
- const totalStaff = await User.count({ where: { role: 'doctor' } });
26
+ const totalPatients = await User.count({ where: { role: "patient" } });
27
+ const totalStaff = await User.count({ where: { role: "doctor" } });
11
28
  const totalAppointments = await Appointment.count();
29
+
30
+ // Топ-5 самых частых болезней по инкрементируемому счетчику
12
31
  const topDiseases = await Disease.findAll({
13
32
  limit: 5,
14
- order: [['casesCount', 'DESC']],
15
- attributes: ['name', 'code', 'casesCount']
33
+ order: [["casesCount", "DESC"]],
34
+ attributes: ["name", "code", "casesCount"],
16
35
  });
36
+
37
+ // Разбивка персонала по специальностям (для графиков или таблиц)
17
38
  const staffBySpecialty = await User.findAll({
18
- where: { role: 'doctor' },
19
- attributes: ['specialty', [User.sequelize.fn('COUNT', User.sequelize.col('id')), 'count']],
20
- group: ['specialty']
39
+ where: { role: "doctor" },
40
+ attributes: [
41
+ "specialty",
42
+ [User.sequelize.fn("COUNT", User.sequelize.col("id")), "count"],
43
+ ],
44
+ group: ["specialty"],
21
45
  });
46
+
22
47
  return {
23
48
  summary: {
24
49
  totalClinics,
25
50
  totalPatients,
26
51
  totalStaff,
27
- totalAppointments
52
+ totalAppointments,
28
53
  },
29
54
  topDiseases,
30
- staffBySpecialty
55
+ staffBySpecialty,
31
56
  };
32
- }
57
+ },
33
58
  };
34
- module.exports = dbAnalytics;
59
+
60
+ module.exports = dbAnalytics;