# Требования к модулю: m01-server-db > Модуль — серверный слой данных v1: схема PostgreSQL (`src`, `ref`), > psql-функции, миграции, домен C# и DAL-репозитории, режим > `--mode migrate`. Самодостаточная копия требований (d-dev-6): внешние > документы продублированы по содержанию, ссылки на них — только для > контекста, не для смысла. ## 1. Назначение и границы **В границах модуля:** - SQL-миграции схемы `ref` и `src` (журнал `src.migration`, d-db-1); - psql-функции слоя `src` и `ref` (список утверждён — разд. 3); - psql-тесты функций; - доменные объекты C# (`{Сущность}.cs`, правила c-dm); - DAL-репозитории C# (`{Сущность}Repository.cs`, правила c-dal) поверх `Kit.Helpers SqlHelperAbstract` (SqlHelperPostgres) (единственный путь к БД); - режим запуска `--mode migrate` сервера (применение миграций по журналу). **Вне границ модуля:** REST API (m02), бизнес-логика метрик/правил (m04, m05), импорт .fit (m06), админ-консоль (m08), всё на телефоне и часах. ## 2. Стек и соглашения - PostgreSQL; сервер — C# / .NET 10; доступ к БД — только `Kit.Helpers SqlHelperAbstract` (SqlHelperPostgres), строка — `Db.ConnectionString` (appsettings.json, тест подключения при старте — фатально при неудаче). - Логи — `Kit.Helpers Log.Logger` (файл, уровень Info). - Миграции — psql-файлы в `2-db\migrations\`; каждый применённый файл фиксируется в `src.migration(filename)`; повторный запуск пропускает применённые (журнал — источник истины). - Именование функций — `{сущность}_{действие}`; код функций — `{функция}.psql`, тесты — `test_{функция}.psql` (db-func-4, db-tst-1). - Статусная модель функций — d-dev-1..3 (`PLAN → DEV → DONE`; колонки «Функция»/«Репозиторий»: `- → REQ → CODE → DONE`). ## 3. Инварианты (🛡 — нарушать запрещено) 1. **Изоляция атлетов** — все функции слоя `src`, кроме `athlete_*` и `migration_*`, принимают `p_athlete_id` и выбирают/пишут только в пределах атлета. 2. **Идемпотентность** — `workout_insert` при повторе `client_guid` (PK `id` uuid) возвращает существующий id; PK uuid клиентских сущностей генерирует телефон (строки «только .fit» — сервер). 3. **Неизменяемость истории** — `program_version_update` меняет только `status` (переходы draft→active→archived; активной становится ровно одна версия на программу — частичный уникальный индекс); `command_log` не редактируется; `workout_set` без update/delete (правка = новая запись `source=edit`); `exercise_delete` — только логическое (`is_active=0`). 4. **Атлет без удаления** — `athlete` без `_delete`: блокировка `is_blocked`. ## 4. Схема данных (сводка; полная — 2-db\src\tables.md, 2-db\ref\tables.md) | Таблица | Ключевое | |---|---| | `src.athlete` | id serial PK; name; api_key_hash UNIQUE (SHA-256); is_blocked | | `src.athlete_profile` | 1:1 к athlete (UNIQUE athlete_id); sex, birth_year, weight_kg, hr_max (NULL → Танака) | | `src.exercise` | id uuid PK; UNIQUE(athlete_id, name); group_id → ref; is_base; is_active | | `src.exercise_replacement` | ММ-пара (PK exercise_id, replacement_id); CHECK ≠ | | `src.program` | UNIQUE(athlete_id) — одна программа; title | | `src.program_version` | id uuid PK; UNIQUE(program_id, version); status draft/active/archived (active — ровно одна); author user/admin/rules/llm; body jsonb; rationale | | `src.workout` | id uuid PK; UNIQUE(athlete_id, fit_file_id); status active/finished/cancelled; session_kind normal/return/deload/test; facts_json jsonb; hr_recovery_s | | `src.workout_set` | id uuid PK; athlete_id (денормализация); seq; weight_kg; reps; source voice/watch/edit; in_plan; rpe 1–10; knee_pain 0–10 | | `src.command_log` | id uuid PK; t_offset_s; recognized_text; semantics jsonb; accepted | | `src.daily_log` | id uuid PK; UNIQUE(athlete_id, date); bp_sys/dias, weight_kg, hr_resting, wellbeing 1–5 (waist_cm, sleep_h — v2, колонки есть) | | `src.workout_metrics` | id serial PK; UNIQUE(workout_id); adherence, tonnage, tonnage_by_group/e1pm_by_exercise/hr_metrics/red_flags jsonb | | `src.breaks` | id uuid PK; kind (NULL до ответа); source auto/manual; started_on; planned_return; ended_on; note | | `src.migration` | filename UNIQUE; applied_at | | `ref.exercise_group` | справочник двигательных групп (см. 2-db\ref\tables.md) | ## 5. Список функций (утверждён 2026-10-17, статусы — 2-db\{src,ref}\functions.md) `src`: athlete_insert/update/select/get; athlete_profile_upsert/get; exercise_insert/update/delete/select/get; exercise_replacement_insert/delete; program_insert/select/get; program_version_insert/update/select/get; workout_insert/update/select/get; workout_set_insert/get; command_log_insert/select; daily_log_upsert/select; workout_metrics_upsert/select; break_insert/update/select; migration_insert/select. `ref`: функции справочника (см. ref\functions.md). Ключевые контракты из списка: `workout_insert(p_athlete_id, p_id uuid, p_program_version_id, p_started_at, p_session_kind) → uuid` (идемпотентен); `workout_select` — карточки + подходы агрегатом jsonb (d-db-6, мультикурсор); `program_version_select` — без body (body — отдельный get); `exercise_select` — по умолчанию только активные + пары замен; `break_insert` — `source=manual` от телефона, `auto` проставляет сервер; `daily_log_upsert` — по `UNIQUE(athlete_id, date)`. ## 6. Критерии приёмки модуля 1. `--mode migrate` на чистой БД применяет все миграции; повторный запуск — 0 применений; сбой миграции откатывает её и завершается кодом возврата 1. 2. Все psql-функции списка имеют код и тесты; тесты проходят на чистой БД (включая проверки изоляции `p_athlete_id`, идемпотентности `workout_insert`, единственности `active`-версии). 3. DAL-репозитории покрывают все функции; юнит-/интеграционные тесты репозиториев проходят. 4. Статусы в 2-db\{src,ref}\functions.md доведены до `DONE`.