456 lines
19 KiB
Markdown
456 lines
19 KiB
Markdown
# Система тестирования сотрудников клиники
|
||
|
||
Веб-приложение для проведения внутреннего тестирования сотрудников клиники. Руководители подразделений и HR-менеджеры создают тесты и назначают их сотрудникам. Система фиксирует все попытки и результаты.
|
||
|
||
**Версия ТЗ:** 1.2
|
||
**Дата:** 2026-03-21
|
||
**Статус:** Согласовано
|
||
|
||
---
|
||
|
||
## Содержание
|
||
|
||
- [Технологический стек](#технологический-стек)
|
||
- [Функциональные возможности](#функциональные-возможности)
|
||
- [Роли и права доступа](#роли-и-права-доступа)
|
||
- [База данных](#база-данных)
|
||
- [API Endpoints](#api-endpoints)
|
||
- [Структура проекта](#структура-проекта)
|
||
- [Установка и запуск](#установка-и-запуск)
|
||
- [План разработки](#план-разработки)
|
||
|
||
---
|
||
|
||
## Технологический стек
|
||
|
||
| Компонент | Выбор |
|
||
|-----------|-------|
|
||
| Backend | Node.js + Express |
|
||
| Frontend | React (SPA) |
|
||
| Database | PostgreSQL |
|
||
| Authentication | JWT (токен в httpOnly cookie) |
|
||
| Password | bcrypt |
|
||
|
||
---
|
||
|
||
## Функциональные возможности
|
||
|
||
### 4.1 Управление пользователями и подразделениями
|
||
|
||
- Создание/редактирование/деактивация учётных записей сотрудников
|
||
- Каждый сотрудник принадлежит одному подразделению
|
||
- Создание/редактирование справочника подразделений
|
||
- Назначение роли сотруднику: HR-менеджер / Руководитель подразделения / Сотрудник
|
||
|
||
### 4.2 Создание и редактирование тестов
|
||
|
||
**Тест содержит:**
|
||
- Название теста
|
||
- Описание (опционально)
|
||
- Список вопросов (минимум 7)
|
||
- Порог зачёта — минимальный % правильных ответов
|
||
- Таймер прохождения — лимит в минутах (опционально)
|
||
|
||
**Вопрос содержит:**
|
||
- Текст вопроса
|
||
- Минимум 3 варианта ответа
|
||
- Один или несколько правильных ответов
|
||
|
||
**Настройки теста:**
|
||
- Разрешить возврат к предыдущему вопросу: да / нет
|
||
|
||
**Версионирование:**
|
||
- Автор может редактировать тест пока никто его не проходил
|
||
- Если тест уже проходили — создаётся новая версия (`version + 1`), старая сохраняется
|
||
- Все версии теста хранятся; результаты привязаны к конкретной версии
|
||
- Активная версия — та, которую видят сотрудники; автор может вручную переключить активную версию
|
||
- Тест можно деактивировать (скрыть из списка, не удалять)
|
||
|
||
### 4.3 Назначение теста
|
||
|
||
- Список получателей (отдел или конкретные сотрудники)
|
||
- Срок сдачи — дата дедлайна
|
||
- Допустимое количество попыток (1 или более)
|
||
|
||
### 4.4 Прохождение теста
|
||
|
||
- На главной странице сотрудник видит список назначенных тестов со статусами:
|
||
- `Не начат` — ещё не открывал
|
||
- `В процессе` — начал, не завершил
|
||
- `Завершён` — сдал/не сдал
|
||
- `Просрочен` — дедлайн прошёл, не сдан
|
||
- Если задан таймер — отображается обратный отсчёт, по истечении тест завершается автоматически
|
||
- Порядок вопросов **случайный** при каждом прохождении
|
||
- Возможность вернуться к предыдущему вопросу — определяется настройкой теста
|
||
|
||
### 4.5 Результаты после завершения теста
|
||
|
||
- Итоговый балл и процент правильных ответов
|
||
- Факт зачёта: **сдал / не сдал**
|
||
- Разбор ошибок: по каждому вопросу — его ответ и правильный ответ
|
||
|
||
### 4.6 Трекер попыток
|
||
|
||
Единый интерфейс просмотра всех попыток прохождения тестов:
|
||
- Фильтрация по подразделению, сотруднику, тесту, статусу, результату
|
||
- Пагинация и сортировка
|
||
|
||
### 4.7 AI-помощник (DeepSeek)
|
||
|
||
Интеграция с LLM DeepSeek для помощи при создании тестов:
|
||
|
||
| Функция | Описание |
|
||
|---------|----------|
|
||
| Генерация теста | AI генерирует готовый набор вопросов с вариантами ответов по теме |
|
||
| Улучшение формулировки | AI переформулирует выбранный вопрос более чётко |
|
||
| Добавление дистракторов | AI генерирует правдоподобные неправильные варианты ответов |
|
||
| Проверка качества | AI анализирует весь тест и выдаёт рекомендации |
|
||
|
||
---
|
||
|
||
## Роли и права доступа
|
||
|
||
| Роль | Кто | Создаёт тесты | Назначает тесты | Видит результаты |
|
||
|------|-----|:---:|:---:|:---:|
|
||
| **HR-менеджер** | Руководитель службы HR, Директор клиники | ✅ | Всем сотрудникам клиники | Всех сотрудников |
|
||
| **Руководитель подразделения** | Главный врач, рук. службы администраторов | ✅ | Только своему подразделению | Только своего подразделения |
|
||
| **Сотрудник** | Все остальные работники | ❌ | ❌ | Только свои |
|
||
|
||
---
|
||
|
||
## База данных
|
||
|
||
### Схема таблиц
|
||
|
||
#### `departments` — Подразделения
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| name | VARCHAR(255) | Название подразделения |
|
||
| created_at | TIMESTAMP | Дата создания |
|
||
| updated_at | TIMESTAMP | Дата обновления |
|
||
|
||
#### `users` — Пользователи
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| login | VARCHAR(100) | Уникальный логин |
|
||
| password_hash | VARCHAR(255) | Хеш пароля |
|
||
| full_name | VARCHAR(255) | ФИО |
|
||
| role | ENUM | 'hr', 'manager', 'employee' |
|
||
| department_id | UUID | FK -> departments |
|
||
| is_active | BOOLEAN | Активна ли учётная запись |
|
||
| created_at | TIMESTAMP | Дата создания |
|
||
| updated_at | TIMESTAMP | Дата обновления |
|
||
|
||
#### `tests` — Тесты
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| title | VARCHAR(255) | Название теста |
|
||
| description | TEXT | Описание (опционально) |
|
||
| passing_threshold | INTEGER | Порог зачёта (%) |
|
||
| time_limit | INTEGER | Таймер в минутах (nullable) |
|
||
| allow_back | BOOLEAN | Возврат к предыдущему |
|
||
| is_active | BOOLEAN | Активен |
|
||
| is_versioned | BOOLEAN | Есть версии |
|
||
| created_by | UUID | FK -> users |
|
||
| created_at | TIMESTAMP | Дата создания |
|
||
| updated_at | TIMESTAMP | Дата обновления |
|
||
|
||
#### `test_versions` — Версии тестов
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| test_id | UUID | FK -> tests |
|
||
| version | INTEGER | Номер версии |
|
||
| is_active | BOOLEAN | Активная версия |
|
||
| created_at | TIMESTAMP | Дата создания |
|
||
|
||
#### `questions` — Вопросы
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| test_version_id | UUID | FK -> test_versions |
|
||
| text | TEXT | Текст вопроса |
|
||
| question_order | INTEGER | Порядок вопроса |
|
||
| has_multiple_answers | BOOLEAN | Несколько правильных |
|
||
|
||
#### `answer_options` — Варианты ответов
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| question_id | UUID | FK -> questions |
|
||
| text | TEXT | Текст варианта |
|
||
| is_correct | BOOLEAN | Правильный ответ |
|
||
| option_order | INTEGER | Порядок варианта |
|
||
|
||
#### `test_assignments` — Назначения тестов
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| test_version_id | UUID | FK -> test_versions |
|
||
| assigned_by | UUID | FK -> users |
|
||
| deadline | DATE | Срок сдачи |
|
||
| max_attempts | INTEGER | Допустимое число попыток |
|
||
| created_at | TIMESTAMP | Дата назначения |
|
||
|
||
#### `test_assignment_targets` — Получатели назначений
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| assignment_id | UUID | FK -> test_assignments |
|
||
| target_type | ENUM | 'department', 'user' |
|
||
| target_id | UUID | FK (department или user) |
|
||
|
||
#### `test_attempts` — Попытки прохождения
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| test_version_id | UUID | FK -> test_versions |
|
||
| user_id | UUID | FK -> users |
|
||
| attempt_number | INTEGER | Номер попытки |
|
||
| status | ENUM | 'in_progress', 'completed', 'expired' |
|
||
| started_at | TIMESTAMP | Начало |
|
||
| completed_at | TIMESTAMP | Завершение |
|
||
| correct_count | INTEGER | Правильных ответов |
|
||
| total_questions | INTEGER | Всего вопросов |
|
||
| passed | BOOLEAN | Зачёт |
|
||
|
||
#### `user_answers` — Ответы пользователя
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| id | UUID | PK |
|
||
| attempt_id | UUID | FK -> test_attempts |
|
||
| question_id | UUID | FK -> questions |
|
||
| selected_options | UUID[] | Выбранные варианты |
|
||
|
||
#### `settings` — Настройки
|
||
|
||
| Поле | Тип | Описание |
|
||
|------|-----|----------|
|
||
| key | VARCHAR(100) | PK |
|
||
| value | TEXT | Значение |
|
||
|
||
---
|
||
|
||
## API Endpoints
|
||
|
||
### Аутентификация
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| POST | `/api/auth/login` | Вход в систему |
|
||
| POST | `/api/auth/logout` | Выход из системы |
|
||
|
||
### Подразделения
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| GET | `/api/departments` | Список подразделений |
|
||
| POST | `/api/departments` | Создать подразделение |
|
||
| PUT | `/api/departments/:id` | Редактировать |
|
||
| DELETE | `/api/departments/:id` | Деактивировать |
|
||
|
||
### Пользователи
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| GET | `/api/users` | Список сотрудников |
|
||
| POST | `/api/users` | Создать сотрудника |
|
||
| GET | `/api/users/:id` | Карточка сотрудника |
|
||
| PUT | `/api/users/:id` | Редактировать |
|
||
| DELETE | `/api/users/:id` | Деактивировать |
|
||
|
||
### Тесты
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| GET | `/api/tests` | Список тестов |
|
||
| POST | `/api/tests` | Создать тест |
|
||
| GET | `/api/tests/:id` | Тест с вопросами |
|
||
| PUT | `/api/tests/:id` | Редактировать |
|
||
| DELETE | `/api/tests/:id` | Деактивировать |
|
||
|
||
### Назначения тестов
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| GET | `/api/assignments` | Список назначений |
|
||
| POST | `/api/assignments` | Назначить тест |
|
||
| GET | `/api/assignments/:id` | Детали назначения |
|
||
| PUT | `/api/assignments/:id` | Редактировать |
|
||
| DELETE | `/api/assignments/:id` | Удалить |
|
||
| GET | `/api/my-assignments` | Назначенные тесты (для сотрудника) |
|
||
|
||
### Прохождение теста
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| POST | `/api/attempts/start` | Начать попытку |
|
||
| GET | `/api/attempts/:id` | Получить вопросы |
|
||
| POST | `/api/attempts/:id/answers` | Отправить ответ |
|
||
| GET | `/api/attempts/:id/answers` | Получить текущий прогресс |
|
||
| POST | `/api/attempts/:id/complete` | Завершить тест |
|
||
|
||
### Результаты
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| GET | `/api/attempts/:id/result` | Получить результат |
|
||
| GET | `/api/attempts/:id/review` | Получить разбор |
|
||
|
||
### Трекер попыток
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| GET | `/api/tracker` | Список всех попыток |
|
||
|
||
### AI-помощник
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| POST | `/api/ai/generate-test` | Сгенерировать тест |
|
||
| POST | `/api/ai/improve-question` | Улучшить вопрос |
|
||
| POST | `/api/ai/add-distractors` | Добавить неправильные варианты |
|
||
| POST | `/api/ai/check-quality` | Проверить качество теста |
|
||
|
||
### Настройки
|
||
|
||
| Метод | Эндпоинт | Описание |
|
||
|-------|----------|----------|
|
||
| GET | `/api/settings` | Получить настройки |
|
||
| PUT | `/api/settings` | Обновить настройки |
|
||
| POST | `/api/settings/test-connection` | Проверить подключение к DeepSeek |
|
||
|
||
---
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
TestingWebApp/
|
||
├── docs/ # Документация
|
||
│ ├── ТЗ.md # Техническое задание
|
||
│ └── шаги/ # План разработки (11 шагов)
|
||
├── backend/ # Node.js + Express
|
||
│ ├── src/
|
||
│ │ ├── index.js # Точка входа
|
||
│ │ ├── db/ # Работа с БД
|
||
│ │ │ ├── db.js # Подключение к PostgreSQL
|
||
│ │ │ ├── migrate.js # Миграции
|
||
│ │ │ └── migrations/
|
||
│ │ │ └── 001_initial.sql
|
||
│ │ ├── routes/ # API роуты
|
||
│ │ │ └── auth.js
|
||
│ │ ├── middleware/ # Промежуточное ПО
|
||
│ │ │ └── auth.js
|
||
│ │ └── utils/ # Утилиты
|
||
│ │ └── auth.js
|
||
│ └── package.json
|
||
├── frontend/ # React SPA
|
||
│ ├── src/
|
||
│ │ ├── main.jsx # Точка входа
|
||
│ │ └── App.jsx # Главный компонент
|
||
│ ├── index.html
|
||
│ ├── vite.config.js
|
||
│ └── package.json
|
||
├── docker-compose.yml # PostgreSQL
|
||
└── README.md # Этот файл
|
||
```
|
||
|
||
---
|
||
|
||
## Установка и запуск
|
||
|
||
### Требования
|
||
|
||
- Node.js 18+
|
||
- PostgreSQL 14+
|
||
- Docker (для PostgreSQL)
|
||
|
||
### Шаги
|
||
|
||
1. **Клонировать репозиторий**
|
||
|
||
2. **Настроить окружение**
|
||
|
||
Создать файл `backend/.env`:
|
||
```env
|
||
DATABASE_URL=postgresql://user:password@localhost:5432/clinic_tests
|
||
JWT_SECRET=your-secret-key
|
||
PORT=3000
|
||
```
|
||
|
||
3. **Запустить PostgreSQL**
|
||
```bash
|
||
docker-compose up -d
|
||
```
|
||
|
||
4. **Установить зависимости и запустить backend**
|
||
```bash
|
||
cd backend
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
5. **Запустить frontend**
|
||
```bash
|
||
cd frontend
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
6. **Открыть приложение**
|
||
- Frontend: http://localhost:5173
|
||
- Backend API: http://localhost:3000
|
||
|
||
---
|
||
|
||
## План разработки
|
||
|
||
| Шаг | Название | Описание |
|
||
|-----|----------|----------|
|
||
| 1 | Проект и инфраструктура | Структура проекта, Docker, настройка окружения |
|
||
| 2 | Проектирование БД | Создание всех таблиц и миграций |
|
||
| 3 | Аутентификация и авторизация | JWT, login/logout, RBAC |
|
||
| 4 | Управление пользователями | CRUD сотрудников и подразделений |
|
||
| 5 | Управление тестами | Создание, редактирование, версионирование |
|
||
| 6 | Назначение тестов | Выбор получателей, дедлайн, лимит попыток |
|
||
| 7 | Интерфейс прохождения | Прохождение с таймером, случайный порядок вопросов |
|
||
| 8 | Результаты и разбор | Итоговый балл, разбор ошибок |
|
||
| 9 | Трекер попыток | Единый интерфейс просмотра попыток |
|
||
| 10 | AI-помощник | Интеграция с DeepSeek |
|
||
| 11 | Страница настроек | Управление API ключами |
|
||
|
||
---
|
||
|
||
## Нефункциональные требования
|
||
|
||
| Параметр | Значение |
|
||
|----------|----------|
|
||
| Количество пользователей | 50–200 человек |
|
||
| Платформа | Веб-приложение, браузер (desktop-first) |
|
||
| Доступность | Внутренняя сеть клиники |
|
||
| Язык интерфейса | Русский |
|
||
| Время отклика | < 2 секунды |
|
||
|
||
---
|
||
|
||
## Вне scope (не реализуется в данной версии)
|
||
|
||
- Интеграция с AD/LDAP
|
||
- Мобильное приложение
|
||
- Вопросы с вложениями (изображения, видео)
|
||
- Экспорт отчётов в Excel / PDF
|
||
- Уведомления в MAX (отдельный спринт)
|