feat: разбор и хранение тренировок и состояния разума

- секции `workouts` и `stateOfMind` покрыты разбором: тренировка лежит одной
  строкой вместе с маршрутом и внутренними рядами, запись — по ключу `род + id`;
  миграция 00007 заводит обе таблицы и возвращает в очередь `partial`-доставки
  с этими ключами
- сущность заменяется целиком, но условно: приехавшая побеждает, если не теряет
  содержания сохранённой (множество ключей и длины верхнеуровневых массивов), а
  при равном содержании выигрывает версия из более поздней доставки ЖУРНАЛА —
  «побеждает приехавшая» было бы функцией порядка свёртки, и живая витрина
  расходилась бы с пересборкой молча
- отпечаток витрины покрывает тренировки и записи и снимается одним снимком
  базы; отчёт `reindex` считает «было и стало» по каждой единице хранения
This commit is contained in:
av
2026-08-02 13:05:16 +03:00
parent c28de9796e
commit f8200f7f80
47 changed files with 5817 additions and 301 deletions
+176
View File
@@ -0,0 +1,176 @@
{
"data": {
"workouts": [
{
"id": "19336133-5198-0770-0219-503343909539",
"maxHeartRate": {
"qty": 199,
"units": "count/min"
},
"temperature": {
"units": "degC",
"qty": 25.642900109344569
},
"name": "В помещении Ходьба",
"avgHeartRate": {
"qty": 47.271142352144859,
"units": "count/min"
},
"isIndoor": true,
"duration": 11.104378534563007,
"walkingAndRunningDistance": [
{
"qty": 0.051178653222924688,
"date": "2025-06-05 21:07:25 +0300",
"units": "km",
"source": "Device A  "
},
{
"qty": 0.0022744856624713684,
"date": "2025-06-05 21:08:25 +0300",
"units": "km",
"source": "Device A  "
}
],
"intensity": {
"qty": 1.1101433306047975,
"units": "kcal/hr·kg"
},
"start": "2025-06-05 21:07:25 +0300",
"end": "2025-06-05 21:08:56 +0300",
"activeEnergy": [
{
"units": "kJ",
"date": "2025-06-05 21:07:25 +0300",
"qty": 49.484435766191329,
"source": "Device A  "
},
{
"date": "2025-06-05 21:08:25 +0300",
"qty": 1.2893292371894432,
"units": "kJ",
"source": "Device A  "
}
],
"basalEnergy": [
{
"units": "kJ",
"qty": 8.118214886809112,
"date": "2025-06-05 21:07:25 +0300",
"source": "Device A  "
},
{
"units": "kJ",
"date": "2025-06-05 21:08:25 +0300",
"source": "Device A  ",
"qty": 8.3170473912094565
}
],
"location": "В помещении",
"metadata": {},
"distance": {
"units": "km",
"qty": 0.097916506499049191
},
"totalEnergy": {
"qty": 66.474914481955616,
"units": "kJ"
},
"activeEnergyBurned": {
"units": "kJ",
"qty": 21.260499065517783
},
"speed": {
"qty": 3.842565481147650,
"units": "km/hr"
},
"heartRateData": [
{
"Max": 199,
"date": "2025-06-05 21:07:25 +0300",
"Avg": 86.126023892474565,
"source": "Device A  ",
"Min": 68,
"units": "count/min"
},
{
"Min": 62,
"source": "Device A  ",
"date": "2025-06-05 21:08:25 +0300",
"Avg": 42.420241306988357,
"Max": 11,
"units": "count/min"
}
],
"heartRateRecovery": [
{
"Avg": 18,
"units": "count/min",
"Max": 18,
"date": "2025-06-05 21:09:01 +0300",
"Min": 18,
"source": "Device A  "
},
{
"Min": 18,
"date": "2025-06-05 21:09:05 +0300",
"Avg": 18,
"Max": 18,
"units": "count/min",
"source": "Device A  "
},
{
"Max": 18,
"date": "2025-06-05 21:09:07 +0300",
"Avg": 18,
"source": "Device A  ",
"Min": 18,
"units": "count/min"
},
{
"Max": 64,
"Avg": 64,
"Min": 64,
"source": "Device A  |Device B",
"date": "2025-06-05 21:10:44 +0300",
"units": "count/min"
},
{
"Avg": 84,
"Max": 84,
"units": "count/min",
"Min": 84,
"source": "Device A  |Device B",
"date": "2025-06-05 21:10:49 +0300"
},
{
"source": "Device A  |Device B",
"Min": 16,
"units": "count/min",
"Max": 16,
"Avg": 16,
"date": "2025-06-05 21:10:54 +0300"
}
],
"humidity": {
"units": "%",
"qty": 49
},
"heartRate": {
"max": {
"qty": 199,
"units": "count/min"
},
"avg": {
"qty": 47.271142352144859,
"units": "count/min"
},
"min": {
"qty": 68,
"units": "count/min"
}
}
}
]
}
}