Шаг 5 утверждён; шаг 6: решение о БД, readme БД, tables.md слоёв src и ref
This commit is contained in:
parent
2b3b0125fa
commit
74f57a5f6e
|
|
@ -13,19 +13,18 @@
|
||||||
| 2. Создание рабочей области | `readme.md` системы, структура репозитория, `.gitignore` | ✅ |
|
| 2. Создание рабочей области | `readme.md` системы, структура репозитория, `.gitignore` | ✅ |
|
||||||
| 3. Разработка функциональных требований | `1-docs\1-reqs\requirements.md` (утверждён 2026-10-17) | ✅ |
|
| 3. Разработка функциональных требований | `1-docs\1-reqs\requirements.md` (утверждён 2026-10-17) | ✅ |
|
||||||
| 4. Разработка архитектуры | `1-docs\2-arch\architecture.md`, `hld.puml` (+ рендер) (утверждена 2026-10-17) | ✅ |
|
| 4. Разработка архитектуры | `1-docs\2-arch\architecture.md`, `hld.puml` (+ рендер) (утверждена 2026-10-17) | ✅ |
|
||||||
| 5. Разработка технических требований | `1-docs\3-tech\tech-requirements.md`, спецификации библиотек | 🔄 |
|
| 5. Разработка технических требований | `1-docs\3-tech\tech-requirements.md`, спецификации библиотек (утверждены 2026-10-17) | ✅ |
|
||||||
| 6. Проектирование баз данных | решение о составе данных (фиксируется здесь) | ⬜ |
|
| 6. Проектирование баз данных | `1-docs\2-db\` — readme, `src\tables.md`, `ref\tables.md`, `functions.md` | 🔄 |
|
||||||
| 7. Итерационная разработка | `3-app\` (код), `1-docs\4-modules\` (документы модулей) | ⬜ |
|
| 7. Итерационная разработка | `3-app\` (код), `1-docs\4-modules\` (документы модулей) | ⬜ |
|
||||||
|
|
||||||
## Решение шага 6 (состав данных)
|
## Решение шага 6 (состав данных)
|
||||||
|
|
||||||
Не принято (шаг 6 не начинался). Предварительно по vision и дизайну
|
Принято (2026-10-17): серверная БД — **PostgreSQL** (реляционные
|
||||||
LLM-планирования: серверная БД PostgreSQL обязательна — программы
|
данные, ссылочная целостность, изоляция атлетов); состояние телефона —
|
||||||
(версионированный JSON), факты тренировок, метрики, журнал LLM
|
локальная Room/SQLite; файлы (`.fit`, логи) — вне БД. Схемы-слои:
|
||||||
(таблицы `programs`, `program_changes`, `workout_facts`,
|
`src` — данные тренировок, программ, атлетов, метрик; `ref` —
|
||||||
`workout_metrics`, `athlete_state`, `llm_calls` — набросок из дизайна
|
справочники (двигательные группы). Документация — `1-docs\2-db\`.
|
||||||
LLM-планирования, разд. 8); состояние телефона — локальная БД
|
JSON-тела (версии программ, факты .fit) — `jsonb` внутри таблиц.
|
||||||
(Room/SQLite); окончательное решение — на шаге 6.
|
|
||||||
|
|
||||||
## Состав версий (vision 2.2–2.4, решения опросников)
|
## Состав версий (vision 2.2–2.4, решения опросников)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
# БД WatchBody (PostgreSQL)
|
||||||
|
|
||||||
|
> Документация БД серверной части — шаг 6 процесса
|
||||||
|
> (`std1\_process\process.md`), правила d-db (`std1\_rules\d\d-db.md`),
|
||||||
|
> db-func (`std1\_rules\db\db-func.md`). Решение шага 6 —
|
||||||
|
> [0-mng\plan.md](../0-mng/plan.md).
|
||||||
|
|
||||||
|
## Общие сведения
|
||||||
|
|
||||||
|
- **СУБД:** PostgreSQL 17+; доступ — только через хранимые функции
|
||||||
|
(`Kit.Helpers\Db.Postgres.SqlServerHelper`, tech-requirements разд. 3).
|
||||||
|
- **Схемы-слои:** `src` — основные данные (атлеты, тренировки,
|
||||||
|
программы, метрики); `ref` — справочники (двигательные группы).
|
||||||
|
- **Изоляция атлетов (🛡):** каждая таблица данных несёт `athlete_id`;
|
||||||
|
все функции слоя `src` принимают `p_athlete_id` и выбирают/пишут
|
||||||
|
только в пределах атлета (requirements, Ф-8.5, инвариант раздела 5).
|
||||||
|
- **Идемпотентность (🛡):** клиентские сущности несут `client_guid`
|
||||||
|
(UNIQUE) — повторная вставка возвращает существующий id
|
||||||
|
(`ON CONFLICT DO NOTHING` + `SELECT`).
|
||||||
|
- **JSON:** версии программ и факты .fit — `jsonb` с проверкой схемы
|
||||||
|
на стороне приложения (не в БД).
|
||||||
|
|
||||||
|
## Состав
|
||||||
|
|
||||||
|
| Файл | Содержание |
|
||||||
|
|---|---|
|
||||||
|
| [ref\tables.md](ref/tables.md) | справочники слоя `ref` |
|
||||||
|
| [src\tables.md](src/tables.md) | таблицы слоя `src` |
|
||||||
|
| `{слой}\functions.md` | списки функций (после утверждения таблиц) |
|
||||||
|
|
||||||
|
## Таблица `llm_calls` (v2)
|
||||||
|
|
||||||
|
Журнал вызовов LLM-планировщика (дизайн LLM-планирования, разд. 8)
|
||||||
|
создаётся в v2 вместе с `LlmPlanner` — в v1 не проектируется.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Слой ref — справочники
|
||||||
|
|
||||||
|
> Таблицы слоя `ref`; правила — d-db-2, d-db-3 (`std1\_rules\d\d-db.md`).
|
||||||
|
|
||||||
|
## Перечень таблиц
|
||||||
|
|
||||||
|
| Таблица | Тип | Набор функций | Функции | Комментарий |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `ref.exercise_group` | системный справочник | exercise_group_select | SELECT | редактирование не предусмотрено (правила замены опираются на стабильный список) |
|
||||||
|
|
||||||
|
## Описания
|
||||||
|
|
||||||
|
### `ref.exercise_group` — двигательные группы упражнений
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | int PK | код группы |
|
||||||
|
| `code` | text UNIQUE | символьный код (`push`, `pull`, `legs`, `core`, `cardio`) |
|
||||||
|
| `title` | text | название («Жимовые», «Тяговые», «Ноги», «Кор», «Кардио») |
|
||||||
|
|
||||||
|
Значения: 1 `push`, 2 `pull`, 3 `legs`, 4 `core`, 5 `cardio`.
|
||||||
|
|
||||||
|
Группа определяет: баланс тоннажа (Метрики, разд. 2.5), допустимость
|
||||||
|
замены (правило 6 — замена в пределах группы), показ поля «боль
|
||||||
|
в колене» (только `legs`).
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
# Слой 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 | пара-замена для правила 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 | `client_guid` UNIQUE — идемпотентность; `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.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` | serial PK | 🛡 неизменяемый `exercise_id` (Ф-4.3) |
|
||||||
|
| `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 | логическое удаление (Ф-4.2) |
|
||||||
|
|
||||||
|
### `src.exercise_replacement` — пара-замена (ММ, правило 6)
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `exercise_id` | int | → `src.exercise.id` |
|
||||||
|
| `replacement_id` | int | → `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») |
|
||||||
|
| `created_at` | timestamptz NOT NULL DEFAULT now() | — |
|
||||||
|
|
||||||
|
### `src.program_version` — неизменяемая версия программы (Ф-10, 🛡)
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | serial PK | `program_version_id` для тренировок |
|
||||||
|
| `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 |
|
||||||
|
| `rationale` | text | «почему» — журнал изменений, показывается пользователю |
|
||||||
|
| `created_at` | timestamptz NOT NULL DEFAULT now() | — |
|
||||||
|
|
||||||
|
### `src.workout` — тренировка (телефон + факт .fit)
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | serial PK | — |
|
||||||
|
| `athlete_id` | int NOT NULL | → `src.athlete.id` |
|
||||||
|
| `client_guid` | uuid NOT NULL UNIQUE | идемпотентность (Ф-7.4) |
|
||||||
|
| `program_version_id` | int 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) |
|
||||||
|
| `avg_hr` | int NULL | средний пульс |
|
||||||
|
| `fit_workout_id` | int NULL | сопоставленная тренировка .fit — см. ниже |
|
||||||
|
| `facts_json` | jsonb NULL | канонический факт из .fit (`workout-facts/1`, tech-requirements 2.3); заполняет FitParser |
|
||||||
|
| `fit_file_id` | text NULL UNIQUE | дедупликация fetch (Ф-8.1) |
|
||||||
|
| `hr_recovery_s` | int NULL | возврат пульса к <90 (Ф-13.4, из .fit) |
|
||||||
|
|
||||||
|
`UNIQUE(athlete_id, fit_file_id)`; сопоставление телефон ↔ .fit — окно
|
||||||
|
±15 минут по `started_at` (Ф-8.4): факты пишутся в ту же строку
|
||||||
|
(`facts_json`), либо создается строка «только .fit» без `client_guid`
|
||||||
|
пары — тогда `client_guid` генерирует сервер.
|
||||||
|
|
||||||
|
### `src.workout_set` — подход (телефон)
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | serial PK | — |
|
||||||
|
| `workout_id` | int NOT NULL | → `src.workout.id` |
|
||||||
|
| `athlete_id` | int NOT NULL | денормализация для изоляции выборок |
|
||||||
|
| `exercise_id` | int 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) |
|
||||||
|
| `client_guid` | uuid NOT NULL UNIQUE | идемпотентность |
|
||||||
|
|
||||||
|
### `src.command_log` — журнал голосовых команд (Ф-1.5, 🛡)
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | serial PK | — |
|
||||||
|
| `workout_id` | int 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 | принята грамматикой/диапазоном |
|
||||||
|
| `client_guid` | uuid NOT NULL UNIQUE | идемпотентность |
|
||||||
|
|
||||||
|
### `src.daily_log` — дневник самоконтроля (Ф-13.1)
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | serial PK | — |
|
||||||
|
| `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 | пульс покоя (v2) |
|
||||||
|
| `waist_cm` | numeric(5,1) NULL | талия (v2) |
|
||||||
|
| `sleep_h` | numeric(3,1) NULL | сон, часов (v2) |
|
||||||
|
| `wellbeing` | int NULL CHECK (1–5) | самочувствие (v2) |
|
||||||
|
| `client_guid` | uuid NOT NULL UNIQUE | идемпотентность |
|
||||||
|
|
||||||
|
### `src.workout_metrics` — метрики тренировки (MetricsEngine, 1:1)
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|---|---|---|
|
||||||
|
| `id` | serial PK | — |
|
||||||
|
| `workout_id` | int 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 | — |
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
> [requirements.md](../1-reqs/requirements.md) (утверждён, раздел 7 —
|
> [requirements.md](../1-reqs/requirements.md) (утверждён, раздел 7 —
|
||||||
> открытые вопросы), [architecture.md](../2-arch/architecture.md)
|
> открытые вопросы), [architecture.md](../2-arch/architecture.md)
|
||||||
> (утверждена), дизайн LLM-планирования, документ форматов.
|
> (утверждена), дизайн LLM-планирования, документ форматов.
|
||||||
> Версия: черновик 1 (2026-10-17).
|
> Версия 1.1 — **УТВЕРЖДЕНО** (2026-10-17; интеграция документа метрик —
|
||||||
|
> Ф-13, Safety-пороги, daily_log — утверждена вместе с документом).
|
||||||
|
|
||||||
## 1. Решения открытых вопросов (закрытие раздела 7 requirements)
|
## 1. Решения открытых вопросов (закрытие раздела 7 requirements)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue