VEU.WathBody/1-docs/2-db/src/tables.md

15 KiB
Raw Blame History

Слой src — основные данные

Таблицы слоя src; правила — d-db-2, d-db-3 (std1\_rules\d\d-db.md). 🛡 Все функции слоя принимают p_athlete_id; доступ — только в пределах атлета (изоляция, requirements Ф-8.5).

Перечень таблиц

Таблица Тип Набор функций Функции Комментарий
src.athlete сущность athlete_insert, athlete_update, athlete_select, athlete_get CUSTOM без _delete: блокировка вместо удаления (is_blocked); запись/чтение — владелец и сервисы сервера, не атлет
src.athlete_profile сущность athlete_profile_upsert, athlete_profile_get CUSTOM 1:1 к athlete; upsert вместо insert/update (одна запись)
src.exercise сущность exercise_insert, exercise_update, exercise_delete, exercise_select, exercise_get CRUD _delete — логический (is_active=0, requirements Ф-4.2); функции фильтруют is_active по умолчанию
src.exercise_replacement связь ММ exercise_replacement_insert, exercise_replacement_delete MM типовой набор связи ММ — d-db-3; пара-замена для правила 6 (документ метрик); выборка — внутри exercise_select
src.program сущность program_insert, program_select, program_get CUSTOM контейнер версий; без update/delete — жизненный цикл ведут версии
src.program_version сущность program_version_insert, program_version_update, program_version_select, program_version_get CRUD body jsonb (схема дизайна LLM-планирования разд. 2); 🛡 неизменяемость: _update меняет только status
src.workout сущность workout_insert, workout_update, workout_select, workout_get CRUD PK UUID — идемпотентность (Р2-Б); facts_json — факты .fit
src.workout_set сущность workout_set_insert, workout_select (в составе), workout_set_get CUSTOM выборка — агрегатом в workout_select (подходы в карточке); _delete/_update нет: правка = новая пометка source=edit (история сохраняется)
src.command_log сущность command_log_insert, command_log_select CUSTOM журнал голосовых команд (Ф-1.5, 🛡 не редактируется); update/delete нет
src.daily_log сущность daily_log_upsert, daily_log_select CUSTOM день уникален для атлета (UNIQUE(athlete_id, date)) — upsert
src.workout_metrics сущность workout_metrics_upsert, workout_metrics_select CUSTOM 1:1 к workout, считает сервер (MetricsEngine) — телефон только читает; upsert (пересчёт)
src.breaks сущность break_insert, break_update, break_select CUSTOM перерывы (Ф-14); закрытие = update (ended_on); delete нет — журнал
src.migration служебная migration_insert, migration_select CUSTOM журнал применённых миграций (d-db-1); пишет --mode migrate

Новая редакция (2026-10-17, ревью №2): FK-колонки приведены к uuid (Р2-Б доведён, З-16); src.breaks — planned_return, source = auto|manual (З-17); src.migration — журнал миграций (З-25). УТВЕРЖДЕНО (2026-10-17). Правки ревью №3: src.program.external_id, примечание body (З-31), is_active=false (З-34) — УТВЕРЖДЕНО (2026-10-17).

Описания

src.athlete — атлет

Поле Тип Описание
id serial PK —
name text NOT NULL отображаемое имя (AdminWeb, Ф-9.1)
api_key_hash text NOT NULL UNIQUE SHA-256 ключа (ключ в открытом виде не хранится; сопоставление запроса — по хэшу)
is_blocked boolean NOT NULL DEFAULT false блокировка ключа → 401 (Ф-9.1)
created_at timestamptz NOT NULL DEFAULT now() —

src.athlete_profile — профиль атлета (Ф-6, 1:1 к athlete)

Поле Тип Описание
id serial PK —
athlete_id int NOT NULL UNIQUE → src.athlete.id
sex text m / f
birth_year int год рождения (Танака: 208 − 0,7×возраст)
weight_kg numeric(5,1) вес тела (последний введённый)
hr_max int NULL ручной ввод; NULL → расчёт по Танаке (ОВ-1)
updated_at timestamptz NOT NULL —

src.exercise — упражнение библиотеки атлета (Ф-4)

Поле Тип Описание
id uuid PK DEFAULT gen_random_uuid() 🛡 неизменяемый exercise_id (Ф-4.3); UUID генерирует телефон (Р2-Б)
athlete_id int NOT NULL → src.athlete.id; 🛡 библиотека принадлежит атлету (Ф-4.4)
name text NOT NULL название; UNIQUE(athlete_id, name)
group_id int NOT NULL → ref.exercise_group.id
is_base boolean NOT NULL DEFAULT true базовое/изоляция (шаг прогрессии, Ф-12.2)
is_active boolean NOT NULL DEFAULT true логическое удаление — is_active=false (Ф-4.2, З-34)

src.exercise_replacement — пара-замена (ММ, правило 6)

Поле Тип Описание
exercise_id uuid → src.exercise.id
replacement_id uuid → src.exercise.id; CHECK (exercise_id <> replacement_id); PK (exercise_id, replacement_id)

Замена допустима только внутри группы — проверяет приложение (группы у обоих равны); БД фиксирует пары.

src.program — программа атлета (Ф-10)

Поле Тип Описание
id serial PK —
athlete_id int NOT NULL → src.athlete.id; UNIQUE(athlete_id) — одна программа на атлета (версии — в program_version)
title text NOT NULL название («Программа v4»)
external_id text UNIQUE NULL идентификатор пакета (program_id из program-import.json, З-31)
created_at timestamptz NOT NULL DEFAULT now() —

src.program_version — неизменяемая версия программы (Ф-10, 🛡)

Поле Тип Описание
id uuid PK DEFAULT gen_random_uuid() program_version_id для тренировок; UUID — телефон (Р2-Б)
program_id int NOT NULL → src.program.id
version int NOT NULL UNIQUE(program_id, version); 1, 2, 3…
based_on_version int NULL предыдущая версия (NULL — первая)
status text NOT NULL DEFAULT 'draft' draft | active | archived; 🛡 active — ровно одна на программу (частичный уникальный индекс WHERE status='active')
author text NOT NULL user | admin | rules | llm (v2)
body jsonb NOT NULL недели → тренировки → упражнения (exercise_id) → подходы-план; схема — дизайн LLM-планирования разд. 2; секция profile пакета
(program-import.json) в body не переносится (v1, З-31)
rationale text «почему» — журнал изменений, показывается пользователю
created_at timestamptz NOT NULL DEFAULT now() —

src.workout — тренировка (телефон + факт .fit)

Поле Тип Описание
id uuid PK UUID — телефон (строки «только .fit» — сервер); идемпотентность (Ф-7.4, Р2-Б)
athlete_id int NOT NULL → src.athlete.id
program_version_id uuid NULL → src.program_version.id; версия, активная на начало (Ф-10.3)
started_at timestamptz NOT NULL начало (UTC)
ended_at timestamptz NULL конец
status text NOT NULL DEFAULT 'active' active | finished | cancelled (Ф-3.5)
session_kind text NOT NULL DEFAULT 'normal' normal | return | deload | test — интерпретация снижения весов (Ф-14.5)
avg_hr int NULL средний пульс
facts_json jsonb NULL канонический факт из .fit (workout-facts/1, tech-requirements 2.3); заполняет FitParser
fit_file_id text NULL дедупликация fetch (Ф-8.1); уникальность — составная UNIQUE(athlete_id, fit_file_id)
hr_recovery_s int NULL возврат пульса к <90 (Ф-13.4, из .fit)

UNIQUE(athlete_id, fit_file_id) — единственная уникальность fit_file_id; сопоставление телефон ↔ .fit — окно ±15 минут по started_at (Ф-8.4): факты пишутся в ту же строку (facts_json); если тренировки с телефона не было — создаётся строка «только .fit» с UUID, который генерирует сервер (Р2-Б).

src.workout_set — подход (телефон)

Поле Тип Описание
id uuid PK UUID — телефон (Р2-Б)
workout_id uuid NOT NULL → src.workout.id
athlete_id int NOT NULL денормализация для изоляции выборок
exercise_id uuid NULL → src.exercise.id; NULL — упражнение не выбрано
seq int NOT NULL порядковый номер подхода в тренировке
weight_kg numeric(6,2) NOT NULL вес
reps int NOT NULL повторения
done_at timestamptz NOT NULL момент фиксации
source text NOT NULL voice | watch | edit
in_plan boolean NOT NULL «по плану» (Ф-11.2)
rpe int NULL CHECK (rpe BETWEEN 1 AND 10) субъективная нагрузка (Ф-13.3)
knee_pain int NULL CHECK (0–10) боль в колене (группа legs, Ф-13.3)

src.command_log — журнал голосовых команд (Ф-1.5, 🛡)

Поле Тип Описание
id uuid PK UUID — телефон (Р2-Б)
workout_id uuid NOT NULL → src.workout.id
t_offset_s int NOT NULL секунды от начала тренировки (сопоставление с .fit)
recognized_text text NOT NULL распознанный текст (диагностика STT)
semantics jsonb NOT NULL {cmd, weight, reps, rest_sec, rpe}
accepted boolean NOT NULL принята грамматикой/диапазоном

src.daily_log — дневник самоконтроля (Ф-13.1)

Поле Тип Описание
id uuid PK UUID — телефон (Р2-Б)
athlete_id int NOT NULL → src.athlete.id; UNIQUE(athlete_id, date)
date date NOT NULL день (локальная дата атлета)
bp_sys int NULL систолическое АД
bp_dias int NULL диастолическое АД
weight_kg numeric(5,1) NULL вес тела
hr_resting int NULL пульс покоя, утро (Р1-А — поле v1, данные правила 1)
waist_cm numeric(5,1) NULL талия (v2)
sleep_h numeric(3,1) NULL сон, часов (v2)
wellbeing int NULL CHECK (1–5) самочувствие 1–5 (Р1-А — поле v1, данные правила 1)

src.breaks — перерывы (Ф-14)

Поле Тип Описание
id uuid PK UUID — телефон/сервер (Р2-Б)
athlete_id int NOT NULL → src.athlete.id
kind text NULL ответ атлета: illness | vacation | workload | injury | other; NULL — ещё не отвечен (правила 12–14 ждут ответа)
source text NOT NULL DEFAULT 'auto' auto — сервер по пропускам (Ф-14.1); manual — запланированный простой, объявленный атлетом заранее с телефона (Ф-14.2). Из sync принимается только manual; auto проставляет сервер
started_on date NOT NULL первый пропущенный день
planned_return date NULL предполагаемая дата возврата (ответ атлета, Ф-14.2)
ended_on date NULL возврат (заполняется первой сессией после break)
note text причина-текст пользователя

src.workout_metrics — метрики тренировки (MetricsEngine, 1:1)

Поле Тип Описание
id serial PK —
workout_id uuid NOT NULL UNIQUE → src.workout.id
athlete_id int NOT NULL изоляция выборок
adherence numeric(4,1) % закрытых подходов плана (КУ-5)
tonnage numeric(9,1) Σ вес×повторы, кг
tonnage_by_group jsonb {"push": 4200, …}
e1pm_by_exercise jsonb {"<exercise_id>": 82.5} (Эпли, ОВ-7)
hr_metrics jsonb средний/пиковый пульс подхода, отдых между подходами
red_flags jsonb сработавшие красные флаги (АД, пульс, колено — документ метрик)
computed_at timestamptz NOT NULL —

src.migration — журнал применённых миграций (служебная, d-db-1)

Поле Тип Описание
id serial PK —
filename text NOT NULL UNIQUE имя применённого psql-файла из 2-db\migrations\
applied_at timestamptz NOT NULL DEFAULT now() момент применения