6.4 KiB
6.4 KiB
Требования к модулю: m09-phone-core
Модуль — телефонное ядро Phone.WatchBody (Android, Kotlin, архитектура разд. 1.2):
WorkoutCore(тренировка/допуск/дневник/ перерывы — единственный писательLocalDb),LocalDb(Room/SQLite, офлайн-first),SyncQueue(очередь сclient_guid, пакеты ≤ 50, идемпотентная отправкаPOST /api/v1/sync). UI — m10 (Compose), голосовой ввод — m11, канал часов — m12. Самодостаточная копия требований (d-dev-6).
1. Назначение и границы
В границах модуля:
- Gradle-проект
3-app\4-phone\Phone.WatchBody(app-модуль:app, Kotlin, minSdk 26, targetSdk 36; Compose-зависимость подключается в m10 — здесь только ядро и unit-тесты); LocalDb(Room): таблицы-зеркала сервера с UUID-PK телефона (Р2-Б):exercise,program_version,workout,workout_set,command_log,daily_log,breaks,profile(singleton), плюсsync_pending(guid, entity, payload, created_at); настройки — DataStore (Server.BaseUrl,Server.ApiKey,Sync.WifiOnly,Rest.DefaultSec,Safety.*— Р3-А, телефонные пороги допуска);WorkoutCore: старт/финиш/отмена тренировки (session_kind, привязка активной версии программы); фиксация подхода с пометкой «по плану» (план сессии дня: вес ± 2,5 кг, повторения ± 0 — Ф-11.2); журнал команд (заглушка ручного ввода — голос m11); дневник самоконтроля (день уникален, upsert); предстартовый допуск (АД > 150/90 → отмена; 140–150/90–95 → только кардио; пульс сидя > 90 → перенос — порогиSafety.*, 🛡 блокировка обходится логированием, Ф-13.2); перерывы: объявлениеsource=manual(тип/план. возврат), закрытие первой сессией (данные для правил сервера); 🛡 единственный писатель LocalDb;SyncQueue: каждая запись получаетclient_guid(UUID v4) и payload по контракту sync (tech-req 2.1/2.2, snake_case); WorkManager-воркер: пакеты ≤ 50 по времени создания, отправкаX-Api-Key, подтверждённые GUID удаляются; ретрай 1→64 мин экспоненциально; ограничениеSync.WifiOnly; тихий статус (баннеры — m10);ApiClient: HTTP-клиент/api/v1/sync(+GET /api/v1/workouts,GET /api/v1/programs/active— тянет активную программу);- unit-тесты (JUnit, pure JVM): WorkoutCore (in_plan, допуск, статусы), batcher очереди, сериализация payload (контракт snake_case против фикстур сервера).
Вне границ модуля: экраны (m10 TrainingUi), STT/TTS и грамматика (m11), companion-канал (m12), LocalDb↔сервер маппинг серверных вычислений (метрики/правила — на сервере), восстановление пульса пропущенного периода (m12 — по каналу часов).
2. Контракт sync (tech-req 2.1/2.2)
- конверт:
{"client_guid", "entity", "op": "upsert", "data"}; - entity ∈ workout | workout_set | command_log | daily_log | break | exercise | program_version | profile; поля — snake_case, как серверные sync-сущности (m02 spec 03);
workout_set.exercise_id,workout.program_version_id— локальные UUID (Р2-Б: маппинг id не нужен);break.sourceтелефон передаёт толькоmanual(🛡 авто-детект — сервер, Ф-14.1).
3. Инварианты (🛡 — нарушать запрещено)
- Офлайн-first — сеть только на пути
SyncQueue; запись в LocalDb никогда не ждёт сети (КУ-3). - Единственный писатель — LocalDb пишет только WorkoutCore (+ SyncQueue свой статус); UI/m10 — через WorkoutCore.
- UUID генерирует телефон для всех клиентских сущностей (Р2-Б);
client_guid= PK записи. - Идемпотентность — повторная отправка не меняет данные (сервер считает по GUID; очередь удаляет только подтверждённые).
- Тайны — API-ключ только в DataStore, в логи не пишется (З-6).
- Допуск — пороги только из
Safety.*(Р3-А); обход блокировки («всё равно начать») логируется.
4. Критерии приёмки модуля
- Unit-тесты зелёные: in_plan (вес ± 2,5 кг / повторы ± 0 от плана), допуск (отмена/кардио/перенос/норма), жизненный цикл тренировки, batcher (порция ≤ 50, порядок по created_at, удаление accepted).
- Payload всех 8 сущностей соответствует фикстурам контракта (snake_case, обязательные поля).
- Сборка проекта Gradle проходит (
assembleDebugбез UI — модуль app-библиотеки-каркаса m10); при отсутствии локального Android SDK — сборка у владельца по инструкции (решение гейта 1.3). - Интеграция со стеклянным (docker) сервером: сценарий «запись
офлайн → sync → данные в
src.*» проверен вручную curl-фикстурой payload-ов (при доступном SDK — инструментальным тестом).