83 lines
3.3 KiB
C#
83 lines
3.3 KiB
C#
using Server.WatchBody.Core.Dal.Src.Domain;
|
||
using Server.WatchBody.Core.Dal.Src.Repository;
|
||
|
||
namespace Server.WatchBody.Import;
|
||
|
||
/// <summary>
|
||
/// Запись фактов в src.workout (spec 02 m06): дедуп по fit_file_id,
|
||
/// сопоставление телефонной тренировке ± 15 минут (Ф-8.4), иначе
|
||
/// строка «только .fit». 🛡 Телефонные поля не меняются; записи только
|
||
/// в facts_json/avg_hr/hr_recovery_s/fit_file_id.
|
||
/// </summary>
|
||
public class WorkoutImporter(
|
||
IWorkoutRepository workouts,
|
||
decimal restingHrSitting = 90)
|
||
{
|
||
public Guid Import(int athleteId, ActivityFacts facts)
|
||
{
|
||
var day = DateOnly.FromDateTime(facts.StartedAt);
|
||
var candidates = workouts.Select(new WorkoutFilter
|
||
{
|
||
AthleteId = athleteId,
|
||
DateFrom = day.AddDays(-1),
|
||
DateTo = day.AddDays(1),
|
||
}).ToList();
|
||
|
||
// 1. дедуп: тот же fit_file_id — пропуск (🛡3)
|
||
var sameFile = candidates.FirstOrDefault(w => w.FitFileId == facts.FitFileId);
|
||
if (sameFile != null) return sameFile.Id;
|
||
|
||
// 2. сопоставление: ближайшая без fit_file_id в окне ± 15 минут
|
||
var match = candidates
|
||
.Where(w => w.FitFileId == null)
|
||
.Select(w => (workout: w, gap: Math.Abs((w.StartedAt - facts.StartedAt).TotalMinutes)))
|
||
.Where(x => x.gap <= 15)
|
||
.OrderBy(x => x.gap)
|
||
.ThenBy(x => x.workout.Id)
|
||
.FirstOrDefault();
|
||
|
||
if (match.workout != null)
|
||
{
|
||
var existing = match.workout;
|
||
workouts.Update(athleteId, new Workout
|
||
{
|
||
Id = existing.Id,
|
||
AthleteId = athleteId,
|
||
ProgramVersionId = existing.ProgramVersionId,
|
||
StartedAt = existing.StartedAt,
|
||
EndedAt = existing.EndedAt,
|
||
Status = existing.Status,
|
||
SessionKind = existing.SessionKind,
|
||
AvgHr = facts.AvgHr ?? existing.AvgHr,
|
||
FactsJson = facts.FactsJson,
|
||
FitFileId = facts.FitFileId,
|
||
HrRecoveryS = facts.HrRecoveryS ?? existing.HrRecoveryS,
|
||
Sets = [],
|
||
Commands = [],
|
||
});
|
||
return existing.Id;
|
||
}
|
||
|
||
// 3. строка «только .fit» (uuid генерирует сервер, Р2-Б);
|
||
// workout_insert fit-полей не принимает — вставка + дозакрытие
|
||
// (как sync, errata m02)
|
||
var id = Guid.NewGuid();
|
||
workouts.Insert(athleteId, new Workout
|
||
{
|
||
Id = id, AthleteId = athleteId, StartedAt = facts.StartedAt,
|
||
Status = "finished", SessionKind = "normal", Sets = [], Commands = [],
|
||
});
|
||
workouts.Update(athleteId, new Workout
|
||
{
|
||
Id = id, AthleteId = athleteId, StartedAt = facts.StartedAt,
|
||
Status = "finished", SessionKind = "normal",
|
||
AvgHr = facts.AvgHr,
|
||
FactsJson = facts.FactsJson,
|
||
FitFileId = facts.FitFileId,
|
||
HrRecoveryS = facts.HrRecoveryS,
|
||
Sets = [], Commands = [],
|
||
});
|
||
return id;
|
||
}
|
||
}
|