< Summary

Information
Class: Services.OzonReturnsServices.ClientLoadTotals
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/OzonReturnsServices/ReturnsLoadModels.cs
Line coverage
100%
Covered lines: 2
Uncovered lines: 0
Coverable lines: 2
Total lines: 91
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Processed()100%11100%
Add(...)100%11100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/OzonReturnsServices/ReturnsLoadModels.cs

#LineLine coverage
 1using System.Text;
 2
 3namespace Services.OzonReturnsServices;
 4
 5/// <summary>Итоги загрузки по одному клиенту Ozon за прогон.</summary>
 146public readonly record struct ClientLoadTotals(int Processed, int Inserted, int StatusChanged, int Linked)
 7{
 8    public ClientLoadTotals Add(int processed, int inserted, int statusChanged, int linked) =>
 29        new(Processed + processed, Inserted + inserted, StatusChanged + statusChanged, Linked + linked);
 10}
 11
 12/// <summary>Итог одного прогона загрузки возвратов.</summary>
 13/// <param name="Report">Краткий текст для Hangfire и SignalR.</param>
 14/// <param name="Errors">Ошибки по клиентам; непустой список означает неуспешный прогон.</param>
 15public sealed record ReturnsLoadOutcome(string Report, IReadOnlyList<string> Errors)
 16{
 17    public bool HasErrors => Errors.Count > 0;
 18
 19    public static ReturnsLoadOutcome Success(string report) => new(report, Array.Empty<string>());
 20
 21    public static ReturnsLoadOutcome Failure(string report, IReadOnlyList<string> errors) => new(report, errors);
 22}
 23
 24/// <summary>
 25/// Сводка прогона: счётчики -> тексты для тоста, Telegram и Hangfire.
 26/// Чистое форматирование, вынесенное из джоба, чтобы его можно было проверить тестами.
 27/// </summary>
 28public sealed class ReturnsRunSummary
 29{
 30    public int Processed { get; init; }
 31    public int Inserted { get; init; }
 32    public int StatusChanged { get; init; }
 33    public int Linked { get; init; }
 34    public IReadOnlyList<string> Errors { get; init; } = Array.Empty<string>();
 35    public IReadOnlyDictionary<string, ClientLoadTotals> ClientTotals { get; init; } =
 36        new Dictionary<string, ClientLoadTotals>();
 37    public DateTime FromDate { get; init; }
 38    public DateTime ToDate { get; init; }
 39    public bool Incremental { get; init; }
 40
 41    public bool HasErrors => Errors.Count > 0;
 42
 43    /// <summary>Что-то реально изменилось в БД — единственный повод беспокоить людей на регулярном тике.</summary>
 44    public bool HasChanges => Inserted + StatusChanged > 0;
 45
 46    /// <summary>Короткие части сводки; нулевые счётчики не печатаются вовсе.</summary>
 47    public IReadOnlyList<string> BuildParts()
 48    {
 49        var parts = new List<string>();
 50        if (Inserted > 0) parts.Add($"новых {Inserted}");
 51        if (StatusChanged > 0) parts.Add($"статус {StatusChanged}");
 52        if (Linked > 0) parts.Add($"заказов {Linked}");
 53        if (Errors.Count > 0) parts.Add($"ошибок {Errors.Count}");
 54        if (parts.Count == 0) parts.Add("без изменений");
 55        return parts;
 56    }
 57
 58    /// <summary>Однострочный текст тоста в общей системе алертов.</summary>
 59    public string BuildAlertText() => string.Join(" | ", BuildParts());
 60
 61    /// <summary>
 62    /// Текст для Hangfire и SignalR. Счётчики честные: «получено» — размер выдачи API,
 63    /// «новых» и «со сменой статуса» — то, что действительно изменилось в БД.
 64    /// </summary>
 65    public string BuildReport() =>
 66        $"Получено {Processed} возвратов: новых {Inserted}, со сменой статуса {StatusChanged}, " +
 67        $"связано с заказами {Linked}." +
 68        (Errors.Count > 0 ? $" Ошибок: {Errors.Count}." : string.Empty);
 69
 70    /// <summary>
 71    /// Развёрнутая сводка для Telegram: период, итог, разбивка по клиентам (только когда их больше
 72    /// одного) и тексты ошибок.
 73    /// </summary>
 74    public string BuildTelegramBody()
 75    {
 76        var sb = new StringBuilder();
 77        sb.AppendLine($"{FromDate:dd.MM.yyyy} — {ToDate:dd.MM.yyyy}{(Incremental ? ", инкрементально" : "")}");
 78        sb.AppendLine(BuildAlertText());
 79
 80        if (ClientTotals.Count > 1)
 81        {
 82            foreach (var kv in ClientTotals)
 83                sb.AppendLine($"  {kv.Key}: новых {kv.Value.Inserted}, статус {kv.Value.StatusChanged}, заказов {kv.Valu
 84        }
 85
 86        foreach (var error in Errors)
 87            sb.AppendLine($"  ! {error}");
 88
 89        return sb.ToString();
 90    }
 91}