< Summary

Information
Class: Services.DataServices.OrderRollback.OrderHistorySnapshotReconstructor
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/DataServices/OrderRollback/OrderHistorySnapshotReconstructor.cs
Line coverage
100%
Covered lines: 18
Uncovered lines: 0
Coverable lines: 18
Total lines: 67
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Reconstruct(...)100%1414100%
Normalize(...)100%44100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/DataServices/OrderRollback/OrderHistorySnapshotReconstructor.cs

#LineLine coverage
 1using OzonDomains.Models;
 2
 3namespace Services.DataServices.OrderRollback;
 4
 5/// <summary>
 6/// Чистая (без БД) логика реконструкции состояния полей заказа на произвольный момент времени T
 7/// по записям истории <see cref="OrderHistory"/>.
 8/// </summary>
 9/// <remarks>
 10/// Для каждого отслеживаемого поля значение «как было на момент T» вычисляется так:
 11/// <list type="number">
 12/// <item>Если есть изменения поля с <c>ChangedAt &lt;= T</c> — значение = <c>NewValue</c> последнего из них.</item>
 13/// <item>Иначе, если есть изменения с <c>ChangedAt &gt; T</c> — значение = <c>OldValue</c> самого раннего из них.</item
 14/// <item>Иначе поле никогда не менялось — в результат не попадает (его трогать не нужно).</item>
 15/// </list>
 16/// Строка <c>"NULL"</c> в истории трактуется как <c>null</c>. Записи без <c>ColumnName</c>
 17/// (например <c>RecordCreated</c>) и колонки вне allow-list игнорируются.
 18/// </remarks>
 19public static class OrderHistorySnapshotReconstructor
 20{
 21    public const string NullToken = "NULL";
 22
 23    /// <summary>
 24    /// Восстанавливает снимок отслеживаемых полей на момент <paramref name="pointInTime"/>.
 25    /// Возвращает только те колонки, у которых есть история (значение — строка или <c>null</c>).
 26    /// </summary>
 27    public static IReadOnlyDictionary<string, string?> Reconstruct(
 28        IEnumerable<OrderHistory> history,
 29        DateTime pointInTime,
 30        IReadOnlySet<string>? trackedColumns = null)
 31    {
 2332        trackedColumns ??= OrderRollbackFieldCatalog.TrackedColumns;
 2333        var result = new Dictionary<string, string?>(StringComparer.Ordinal);
 34
 2335        var grouped = history
 2836            .Where(h => !string.IsNullOrEmpty(h.ColumnName) && trackedColumns.Contains(h.ColumnName!))
 4937            .GroupBy(h => h.ColumnName!, StringComparer.Ordinal);
 38
 9039        foreach (var group in grouped)
 40        {
 4841            var ordered = group.OrderBy(h => h.ChangedAt).ToList();
 42
 4643            var lastLe = ordered.LastOrDefault(h => h.ChangedAt <= pointInTime);
 2244            if (lastLe != null)
 45            {
 2046                result[group.Key] = Normalize(lastLe.NewValue);
 2047                continue;
 48            }
 49
 450            var firstGt = ordered.FirstOrDefault(h => h.ChangedAt > pointInTime);
 251            if (firstGt != null)
 52            {
 253                result[group.Key] = Normalize(firstGt.OldValue);
 54            }
 55        }
 56
 2357        return result;
 58    }
 59
 60    /// <summary>Строка истории → нормализованное значение (<c>"NULL"</c>/пусто → <c>null</c>).</summary>
 61    public static string? Normalize(string? raw)
 62    {
 2263        if (string.IsNullOrEmpty(raw) || string.Equals(raw, NullToken, StringComparison.Ordinal))
 364            return null;
 1965        return raw;
 66    }
 67}