Health Tracker - Система мониторинга медицинских анализов
Практическое руководство для тестирования полнофункционального веб-приложения
Описание системы
Health Tracker - полнофункциональная система для мониторинга медицинских анализов с возможностями OCR распознавания медицинских справок, ведения дневника здоровья и планирования медицинских мероприятий.
Технологический стек:
- Frontend: React 18 + TypeScript + Tailwind CSS
- Backend: Node.js + Express + TypeScript
- База данных: PostgreSQL + Prisma ORM
- OCR: Google Gemini API
- Аутентификация: JWT токены
- Деплой: Render.com
🌐 API Документация
Базовый URL и аутентификация
Базовый URL: https://health-tracker.ru/api
Аутентификация:
- Заголовок: Authorization: Bearer {JWT_TOKEN}
- Content-Type: application/json
Получение токена:
POST /auth/login
{
"email": "user@example.com",
"password": "password123"
}
Ответ:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "uuid",
"email": "user@example.com",
"firstName": "Иван"
}
}
📊 Структура данных
Пользователь (User)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "ivan@example.com",
"firstName": "Иван",
"lastName": "Петров",
"isEmailVerified": true,
"createdAt": "2024-01-15T10:30:00Z"
}
Профиль (Profile)
{
"id": "profile-uuid",
"userId": "user-uuid",
"name": "Основной профиль",
"birthDate": "1990-05-15T00:00:00Z",
"gender": "MALE",
"isPrimary": true,
"createdAt": "2024-01-15T10:30:00Z"
}
Анализ (Analysis)
{
"id": "analysis-uuid",
"profileId": "profile-uuid",
"date": "2024-06-15T00:00:00Z",
"laboratory": "Инвитро",
"documentUrl": "/uploads/scan123.jpg",
"values": [
{
"metricId": "metric-uuid",
"value": 4.5,
"isAbnormal": false
}
]
}
Метрика (Metric)
{
"id": "metric-uuid",
"name": "Гемоглобин",
"unit": "г/л",
"description": "Белок крови, переносящий кислород",
"minValue": 120,
"maxValue": 160,
"isGlobal": true,
"category": {
"keyName": "hematology",
"displayName": "Гематология"
}
}
🔐 Аутентификация и пользователи
Регистрация нового пользователя
POST /auth/register
{
"firstName": "Анна",
"lastName": "Смирнова",
"email": "anna@example.com",
"password": "SecurePass123!",
"confirmPassword": "SecurePass123!"
}
Ответ 201:
{
"message": "Пользователь зарегистрирован. Проверьте email для подтверждения."
}
Подтверждение email
POST /auth/verify-email
{
"token": "verification-token-from-email"
}
Ответ 200:
{
"message": "Email успешно подтвержден"
}
Сброс пароля
POST /auth/forgot-password
{
"email": "user@example.com"
}
POST /auth/reset-password
{
"token": "reset-token-from-email",
"password": "NewPassword123!",
"confirmPassword": "NewPassword123!"
}
📋 Анализы и метрики
Получение таблицы анализов
GET /analyses/table-data
Authorization: Bearer {token}
Ответ 200:
{
"rows": [
{
"metric": {
"id": "uuid",
"name": "Гемоглобин",
"unit": "г/л",
"refRange": "120-160"
},
"values": {
"2024-06-15": {
"value": 145,
"isAbnormal": false
},
"2024-03-10": {
"value": 110,
"isAbnormal": true
}
}
}
],
"dates": ["2024-06-15", "2024-03-10"]
}
Добавление нового анализа
POST /analyses/data
Authorization: Bearer {token}
{
"date": "2024-07-20",
"laboratory": "Хеликс",
"metrics": [
{
"metricId": "metric-uuid-1",
"value": 4.8
},
{
"metricId": "metric-uuid-2",
"value": 8.2
}
]
}
Ответ 201:
{
"id": "new-analysis-uuid",
"date": "2024-07-20T00:00:00Z",
"laboratory": "Хеликс",
"values": [...]
}
Получение списка метрик
GET /metrics
Authorization: Bearer {token}
Ответ 200:
[
{
"id": "uuid",
"name": "Глюкоза",
"unit": "ммоль/л",
"minValue": 3.3,
"maxValue": 5.5,
"category": {
"displayName": "Биохимия"
}
}
]
Создание пользовательской метрики
POST /metrics
Authorization: Bearer {token}
{
"name": "Витамин B12",
"unit": "пмоль/л",
"description": "Водорастворимый витамин",
"categoryId": "biochemistry-uuid",
"minValue": 118,
"maxValue": 701
}
🖼️ OCR функциональность
Загрузка изображения для распознавания
POST /ocr/upload
Authorization: Bearer {token}
Content-Type: multipart/form-data
Form data:
- image: [файл изображения] (max 10MB)
Ответ 201:
{
"importId": "ocr-import-uuid",
"status": "PROCESSING",
"message": "Изображение загружено и обрабатывается"
}
Проверка статуса обработки
GET /ocr/imports/{importId}
Authorization: Bearer {token}
Ответ 200:
{
"id": "ocr-import-uuid",
"status": "READY_FOR_REVIEW",
"imageUrl": "/uploads/image123.jpg",
"entries": [
{
"id": "entry-uuid",
"metricName": "Гемоглобин",
"value": 145,
"unit": "г/л",
"matchedId": "metric-uuid"
}
]
}
Редактирование распознанных данных
PATCH /ocr/entries/{entryId}
Authorization: Bearer {token}
{
"metricName": "Лейкоциты",
"value": 6.2,
"unit": "×10⁹/л"
}
Подтверждение и сохранение OCR данных
POST /ocr/imports/{importId}/confirm
Authorization: Bearer {token}
{
"date": "2024-07-20",
"laboratory": "СМ-Клиника",
"entries": [
{
"entryId": "entry-uuid",
"metricId": "metric-uuid",
"value": 145
}
]
}
Планирование событий
Получение запланированных событий
GET /planning/events?month=2024-07
Authorization: Bearer {token}
Ответ 200:
[
{
"id": "event-uuid",
"title": "Общий анализ крови",
"description": "Плановое обследование",
"date": "2024-07-25T09:00:00Z",
"status": "PLANNED",
"healthGroup": {
"name": "Базовые анализы",
"metrics": ["Гемоглобин", "Лейкоциты"]
}
}
]
Создание нового события
POST /planning/events
Authorization: Bearer {token}
{
"title": "УЗИ брюшной полости",
"description": "Профилактическое обследование",
"date": "2024-08-15T14:30:00Z",
"reminderDate": "2024-08-14T09:00:00Z"
}
Отметка события как выполненного
PATCH /planning/events/{eventId}/complete
Authorization: Bearer {token}
{
"completedDate": "2024-07-25T09:30:00Z",
"analysisId": "analysis-uuid"
}
Дневник артериального давления
Добавление записи о давлении
POST /blood-pressure
Authorization: Bearer {token}
{
"date": "2024-07-20",
"morningSystolic": 120,
"morningDiastolic": 80,
"morningPulse": 72,
"eveningSystolic": 125,
"eveningDiastolic": 82,
"eveningPulse": 68,
"comment": "Чувствую себя хорошо"
}
Получение записей за период
GET /blood-pressure?startDate=2024-07-01&endDate=2024-07-31
Authorization: Bearer {token}
Ответ 200:
[
{
"id": "bp-record-uuid",
"date": "2024-07-20T00:00:00Z",
"morningSystolic": 120,
"morningDiastolic": 80,
"morningPulse": 72,
"eveningSystolic": 125,
"eveningDiastolic": 82,
"eveningPulse": 68,
"comment": "Чувствую себя хорошо"
}
]
Публичные ссылки
Создание публичной ссылки
POST /public-links
Authorization: Bearer {token}
{
"title": "Анализы за 2024 год",
"selectedMetricIds": ["metric-uuid-1", "metric-uuid-2"],
"selectedDates": ["2024-06-15", "2024-03-10"],
"expiresAt": "2024-12-31T23:59:59Z"
}
Ответ 201:
{
"id": "link-uuid",
"token": "pub_1234567890abcdef",
"title": "Анализы за 2024 год",
"shareUrl": "https://health-tracker.ru/share/pub_1234567890abcdef"
}
Просмотр публичных данных (без аутентификации)
GET /share/{token}
Ответ 200:
{
"title": "Анализы за 2024 год",
"profileName": "Основной профиль",
"data": {
"rows": [...],
"dates": [...]
}
}
🧪 Frontend функциональность
Структура приложения
┌─────────────────────────────────────┐
│ Header │
│ - Логотип Health Tracker │
│ - Навигационное меню │
│ - Профиль пользователя │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Main Content │
│ │
│ ┌─────────┐ ┌─────────────────────┐ │
│ │Sidebar │ │ Page Content │ │
│ │- Анализы│ │ │ │
│ │- Графики│ │ Динамический │ │
│ │- План │ │ контент страницы │ │
│ │- OCR │ │ │ │
│ │- АД │ │ │ │
│ │- Профиль│ │ │ │
│ └─────────┘ └─────────────────────┘ │
└─────────────────────────────────────┘
Основные маршруты:
/
- Dashboard (главная страница)/analyses
- Таблица анализов/planning
- Календарь планирования/ocr-upload
- Загрузка OCR/ocr-review/:importId
- Редактирование OCR/blood-pressure
- Дневник АД/profile
- Настройки профиля/share/:token
- Публичный просмотр
SQL запросы для тестирования БД
Проверка целостности данных:
-- Анализы без профиля (ошибки foreign key)
SELECT a.id, a.profile_id
FROM analyses a
LEFT JOIN profiles p ON a.profile_id = p.id
WHERE p.id IS NULL;
-- Результаты метрик без анализа
SELECT mr.id, mr.analysis_id
FROM metric_results mr
LEFT JOIN analyses a ON mr.analysis_id = a.id
WHERE a.id IS NULL;
-- Пользователи без первичного профиля
SELECT u.id, u.email
FROM users u
WHERE u.id NOT IN (
SELECT DISTINCT user_id
FROM profiles
WHERE is_primary = true
);
Проверка бизнес-логики:
-- Аномальные значения метрик
SELECT mr.value, m.name, m.min_value, m.max_value
FROM metric_results mr
JOIN metrics m ON mr.metric_id = m.id
WHERE (mr.value < m.min_value OR mr.value > m.max_value)
AND mr.is_abnormal = false;
-- OCR импорты в статусе PROCESSING дольше часа
SELECT id, created_at, status
FROM ocr_imports
WHERE status = 'PROCESSING'
AND created_at < NOW() - INTERVAL '1 hour';
-- Публичные ссылки с истекшим сроком
SELECT id, token, expires_at
FROM public_links
WHERE expires_at < NOW() AND is_active = true;