# Спецификация: DTO пакета sync и ответов (единица 7) ## Назначение C#-модели запроса/ответа REST API (проект `Server.WatchBody.Web`, каталог `Dto\`); сериализация — System.Text.Json, camelCase, регистронезависимая десериализация. Это транспортные модели — доменные объекты m01 (`Server.WatchBody.Core`) в API наружу не выдаются. Примечание (З-42): корень запроса — **сырой JSON-массив** (обёртки `SyncRequest` нет); разбор поля `data` — не strongly-typed модели, а `SyncItemParser` над `JsonElement` (проверка обязательных полей при разборе, лишние игнорируются, недостающие обязательные → 400). Модели `SyncRequest`/`RecordData*` из ранней редакции спецификации не реализованы. ## Модели | Класс | Поля | Комментарий | |---|---|---| | `SyncItem` | `Guid ClientGuid`, `string Entity`, `string Op`, `JsonElement Data` | элемент массива-корня; `Data` — сырой JSON, разбирается по `Entity` (`SyncItemParser`) | | `SyncResponse` | `List Accepted` | `{"accepted": [...]}` | | `ErrorResponse` | `string Error` | `{"error": "..."}` для 400/401/404/409 | | `WorkoutListResponse` | `List Items`, `int Total` | список тренировок | | `WorkoutDto` | `Guid WorkoutId`, `DateTime? StartedAt/EndedAt`, `string Status`, `string SessionKind`, `int? AvgHr`, `List Sets` | `sets` — из агрегата `workout_select` | | `WorkoutSetDto` | `Guid SetId`, `Guid? ExerciseId`, `int Seq`, `decimal WeightKg`, `int Reps`, `DateTime DoneAt`, `string Source`, `bool InPlan`, `int? Rpe`, `int? KneePain` | — | | `CommandLogDto` | `Guid Id`, `int TOffsetS`, `string RecognizedText`, `JsonElement Semantics`, `bool Accepted` | `semantics` — jsonb как есть | | `ActiveProgramResponse` | `int ProgramId`, `Guid VersionId`, `int Version`, `int? BasedOnVersion`, `string Author`, `string Status`, `JsonElement Body`, `string? Rationale`, `DateTime CreatedAt` | `body` — jsonb как есть | ## Правила 0. `exercise` с `is_active=false` — логическое удаление через `src.exercise_delete` (З-41(а), Ф-4.2); повторное `true` после удаления не восстанавливает (путь восстановления — m08, v1 нет). 1. Даты: `started_at`/`ended_at`/`done_at` — UTC ISO 8601 (`DateTimeKind.Utc`); `date` (daily_log), `started_on`/ `planned_return` (break) — `DateOnly` (YYYY-MM-DD). 2. Числа с фиксированной точкой (`weight_kg`) — `decimal`. 3. Валидация диапазонов (`rpe` 1–10, `knee_pain` 0–10, `wellbeing` 1–5, пакет ≤ 50) — до открытия транзакции; нарушение → 400. 4. Названия полей — camelCase в JSON, PascalCase в C#. ## Тесты | # | Проверка | Ожидание | |---|---|---| | 1 | Десериализация конверта (все 7 `entity`) | поля заполнены, `Data` сохранён | | 2 | Отсутствие обязательного поля (`workout` без `started_at`) | ошибка валидации → 400 | | 3 | Сериализация ответов — camelCase, `null`-поля опущены | соответствует примерам спецификаций | | 4 | Дата без времени (`date: "2026-10-17"`) | разбирается в `DateOnly` |