50 lines
2.7 KiB
Markdown
50 lines
2.7 KiB
Markdown
# Спецификация 02: FitFetcher — цикл забора и дедупликация
|
||
|
||
Источники: Ф-8.1, module_reqs разд. 2 (два рубежа дедупа), 🛡3 (сбой
|
||
одного файла не останавливает пакет).
|
||
|
||
## 1. Сигнатура
|
||
|
||
```csharp
|
||
public FitFetcher(IConnectionString cs, GarminClient client,
|
||
string downloadDir, string athleteName);
|
||
public (int Downloaded, int Skipped, int Failed) FetchNew(
|
||
int limit = 20, CancellationToken ct = default);
|
||
```
|
||
|
||
## 2. Алгоритм
|
||
|
||
1. **Атлет**: `athleteName` → `src.athlete` (первый по имени);
|
||
не найден → `FetchException` (CLI переведёт в код 1; 🛡1 —
|
||
факты без атлета не пишутся).
|
||
2. `client.Login()` — исключения пробрасываются (без частичного
|
||
результата).
|
||
3. `ListActivities(limit)` → фильтр `activityType == "strength_training"`.
|
||
4. Для каждой активности, по порядку списка:
|
||
- `downloadDir\{activityId}.fit` существует → `Skipped++`
|
||
(рубеж 1 — без сети), далее;
|
||
- скачать → записать во временный файл → `FitParser.Parse`:
|
||
сбой → файл остаётся как `{activityId}.fit`? **нет** — битый
|
||
download переносится в `{activityId}.fit.bad` (не мешает кэшу),
|
||
`Failed++`, далее (🛡3);
|
||
- `WorkoutImporter.Import(athleteId, facts)` — дедуп по
|
||
`fit_file_id` внутри (возврат существующего id, записи нет);
|
||
если это дубль (возврат id существующей строки, файл не новый)
|
||
→ скачанный файл **удаляется** (рубеж 2, 🛡2), `Skipped++`;
|
||
- иначе файл переименовывается в `{activityId}.fit` (кэш),
|
||
`Downloaded++`.
|
||
5. Возврат (Downloaded, Skipped, Failed).
|
||
|
||
## 3. Ошибки и коды
|
||
|
||
- `FetchException` (атлет/конфигурация) → CLI код 1;
|
||
- `GarminAuthException` → CLI код 2 + stdout-подсказка
|
||
«используйте папку импорта (--mode import)» (fallback О-1-А);
|
||
- сбои отдельных файлов → код 2 при `Failed > 0`.
|
||
|
||
## 4. Инварианты 🛡
|
||
|
||
- записи в БД — только через `WorkoutImporter` (конвейер m06);
|
||
- изоляция: факты пишутся только `athleteId` атлета-владельца учётки;
|
||
- повторный запуск не качает дубли и не создаёт дублей в БД.
|