< Summary

Information
Class: OzonOrdersWeb.WebServices.Orders.OrderProblemInfoService
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Orders/OrderProblemInfoService.cs
Line coverage
100%
Covered lines: 47
Uncovered lines: 0
Coverable lines: 47
Total lines: 90
Line coverage: 100%
Branch coverage
81%
Covered branches: 18
Total branches: 22
Branch coverage: 81.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
PopulateForOrdersAsync()86.36%2222100%
Add(...)75%44100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/WebServices/Orders/OrderProblemInfoService.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using Microsoft.Extensions.Options;
 3using OzonDomains.Models;
 4using OzonOrdersWeb.Observability;
 5using OzonOrdersWeb.WebServices.SystemAlerts;
 6using OzonRepositories.Context;
 7
 8namespace OzonOrdersWeb.WebServices.Orders;
 9
 10public interface IOrderProblemInfoService
 11{
 12    /// <summary>
 13    /// Собирает проблемы заказов страницы для значка и строки в сетках: активные алерты (из памяти),
 14    /// непроведённые документы 1С и упавшие транзакции (по одному запросу на страницу).
 15    /// </summary>
 16    Task PopulateForOrdersAsync(IReadOnlyList<Order> ordersForPage, CancellationToken cancellationToken = default);
 17}
 18
 519public sealed class OrderProblemInfoService(
 520    ApplicationDbContext context,
 521    SystemAlertOrderIndex alertIndex,
 522    SystemAlertIncidentStore alertStore,
 523    SensitiveDataRedactor redactor,
 524    IOptions<SystemAlertsOptions> options) : IOrderProblemInfoService
 25{
 26    private const int MaxProblemsPerOrder = 5;
 27    private const string FailedStatus = "Failed";
 28    private const string SuccessStatus = "Success";
 29
 30    public async Task PopulateForOrdersAsync(IReadOnlyList<Order> ordersForPage, CancellationToken cancellationToken = d
 31    {
 532        if (ordersForPage.Count == 0) return;
 33
 534        var settings = options.Value;
 1535        var orderIds = ordersForPage.Select(o => o.Id).Distinct().ToList();
 536        var since = DateTime.UtcNow.AddHours(-Math.Max(1, settings.OrderProblemWindowHours));
 537        var problems = new Dictionary<int, List<OrderProblem>>();
 38
 539        await alertStore.EnsureOrderIndexAsync(cancellationToken);
 3040        foreach (var id in orderIds)
 41        {
 3042            foreach (var entry in alertIndex.Get(id))
 43            {
 44                // Непроведённые документы 1С показываем из первоисточника ниже: там учтено, что позже заказ мог пройти.
 545                if (entry.Key == "onec-posting") continue;
 446                Add(problems, id, entry.Severity, OrderProblemKind.Alert, string.IsNullOrWhiteSpace(entry.Reason) ? entr
 47            }
 48        }
 49
 50        // Ошибка проведения неактуальна, если позже по тому же заказу проведение прошло успешно.
 551        var failedPostings = await context.OrderHistoryOneCPostings.AsNoTracking()
 552            .Where(x => orderIds.Contains(x.OrderId) && !x.IsPosted && !string.IsNullOrEmpty(x.ErrorSummary) && x.Posted
 553            .Where(f => !context.OrderHistoryOneCPostings.Any(s => s.OrderId == f.OrderId && s.IsPosted && s.PostedAt > 
 554            .OrderByDescending(x => x.PostedAt)
 555            .Select(x => new { x.OrderId, x.ErrorSummary })
 556            .ToListAsync(cancellationToken);
 1857        foreach (var posting in failedPostings.GroupBy(x => x.OrderId).Select(g => g.First()))
 258            Add(problems, posting.OrderId, "danger", OrderProblemKind.OneC, SystemAlertOrderRef.OneCReason(posting.Error
 59
 60        // Транзакция неактуальна после более позднего успеха: той же автоматизации или ручного проведения в 1С.
 561        var failedTransactions = await context.BackgroundTransactionResults.AsNoTracking()
 562            .Where(x => orderIds.Contains(x.OrderId) && x.Status == FailedStatus && x.CreatedAt >= since)
 563            .Where(f => !context.BackgroundTransactionResults.Any(s => s.OrderId == f.OrderId && s.CreatedAt > f.Created
 564                                                                      && (s.Status == SuccessStatus || s.IsPosted))
 565                        && !context.OrderHistoryOneCPostings.Any(p => p.OrderId == f.OrderId && p.IsPosted && p.PostedAt
 566            .OrderByDescending(x => x.CreatedAt)
 567            .Select(x => new { x.OrderId, x.ErrorMessage })
 568            .ToListAsync(cancellationToken);
 1469        foreach (var transaction in failedTransactions.GroupBy(x => x.OrderId).Select(g => g.First()))
 170            Add(problems, transaction.OrderId, "danger", OrderProblemKind.Transaction,
 171                "Транзакция не выполнена" + (string.IsNullOrWhiteSpace(transaction.ErrorMessage) ? string.Empty : " — " 
 72
 3073        foreach (var order in ordersForPage)
 74        {
 1075            if (!problems.TryGetValue(order.Id, out var list)) continue;
 576            order.Problems = list
 777                .OrderBy(x => OrderProblem.SeverityRank(x.Severity))
 578                .Take(MaxProblemsPerOrder)
 779                .Select(x => x with { Text = SystemAlertOrderRef.Trim(redactor.Redact(x.Text, SystemAlertOrderRef.MaxRea
 580                .ToList();
 81        }
 582    }
 83
 84    private static void Add(Dictionary<int, List<OrderProblem>> map, int orderId, string severity, string kind, string t
 85    {
 1286        if (!map.TryGetValue(orderId, out var list)) map[orderId] = list = [];
 987        if (list.Any(x => x.Text == text)) return;
 788        list.Add(new OrderProblem(OrderProblem.NormalizeSeverity(severity), kind, text));
 789    }
 90}