< Summary

Information
Class: Services.SupplierStatusServices.Providers.ForumAutoRostovSupplierStatusProvider
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/SupplierStatusServices/Providers/ForumAutoSupplierStatusProvider.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 3
Coverable lines: 3
Total lines: 179
Line coverage: 0%
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
.ctor(...)100%210%
get_ProviderKey()100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/SupplierStatusServices/Providers/ForumAutoSupplierStatusProvider.cs

#LineLine coverage
 1using System.Globalization;
 2using Microsoft.EntityFrameworkCore;
 3using Microsoft.Extensions.Logging;
 4using Newtonsoft.Json.Linq;
 5using OzonDomains.Models;
 6using OzonDomains.Models.SupplierStatuses;
 7using OzonRepositories.Context;
 8using Services.ApiServices.ForumAutoApi;
 9
 10namespace Services.SupplierStatusServices.Providers;
 11
 12/// <summary>
 13/// Статусы Forum-Auto. Корзина локальная — читается из БД; заказ опрашивается через
 14/// listOrders?did=, по одному вызову на локально известный Did (ответ сразу содержит состав,
 15/// второй запрос за деталями не нужен). Базовый класс общий для «Союз»/«Интерпартс».
 16/// </summary>
 17public abstract class ForumAutoSupplierStatusProviderBase : ISupplierStatusProvider
 18{
 19    /// <summary>Один заказ — отдельный запрос; та же граница, что у ML-Auto.</summary>
 20    private const int DetailsConcurrency = 4;
 21
 22    private readonly ApplicationDbContext _db;
 23    private readonly ForumAutoDataManager _forumAuto;
 24    private readonly LocalCartStatusReader _cartReader;
 25    private readonly ILogger _logger;
 26
 27    protected ForumAutoSupplierStatusProviderBase(
 28        ApplicationDbContext db, ForumAutoDataManager forumAuto, LocalCartStatusReader cartReader, ILogger logger)
 29    {
 30        _db = db;
 31        _forumAuto = forumAuto;
 32        _cartReader = cartReader;
 33        _logger = logger;
 34    }
 35
 36    public abstract string ProviderKey { get; }
 37
 38    public Task<IReadOnlyList<SupplierStatusSnapshot>> FetchCartAsync(
 39        IReadOnlyList<OrderSupplierStateRef> states, CancellationToken ct) =>
 40        _cartReader.ReadAsync(ProviderKey, states, ct);
 41
 42    public async Task<IReadOnlyList<SupplierStatusSnapshot>> FetchOrderAsync(
 43        IReadOnlyList<OrderSupplierStateRef> states, CancellationToken ct)
 44    {
 45        if (states.Count == 0) return [];
 46
 47        var orderIds = states.Select(x => x.OrderId).Distinct().ToList();
 48        var links = await _db.ForumAutoOrderItems
 49            .Where(x => orderIds.Contains(x.StudioOrderId)
 50                        && x.ForumAutoOrder != null && x.ForumAutoOrder.ProviderKey == ProviderKey
 51                        && x.ForumAutoOrder.Did != null)
 52            .Select(x => new { x.StudioOrderId, Did = x.ForumAutoOrder!.Did!.Value })
 53            .ToListAsync(ct);
 54        if (links.Count == 0) return [];
 55
 56        var byDid = links.GroupBy(x => x.Did).ToList();
 57        var gate = new SemaphoreSlim(DetailsConcurrency);
 58        var tasks = byDid.Select(async group =>
 59        {
 60            await gate.WaitAsync(ct);
 61            try
 62            {
 63                var response = await _forumAuto.ListOrdersAsync(group.Key, ct);
 64                // Ответ listOrders — массив заказов, даже когда did задан.
 65                var order = (response as JArray)?.OfType<JObject>().FirstOrDefault() ?? response as JObject;
 66                return (Group: group, Order: order, Error: (string?)null);
 67            }
 68            catch (Exception ex) when (ex is ForumAutoApiException or HttpRequestException or TaskCanceledException)
 69            {
 70                _logger.LogInformation("{Provider}: не удалось прочитать заказ {Did}: {Error}", ProviderKey, group.Key, 
 71                return (Group: group, Order: (JObject?)null, Error: $"{ProviderKey}: {ex.Message}");
 72            }
 73            finally
 74            {
 75                gate.Release();
 76            }
 77        });
 78
 79        var result = new List<SupplierStatusSnapshot>();
 80        foreach (var (group, order, error) in await Task.WhenAll(tasks))
 81        {
 82            if (error is not null)
 83            {
 84                foreach (var link in group)
 85                {
 86                    result.Add(new SupplierStatusSnapshot
 87                    {
 88                        OrderId = link.StudioOrderId, Kind = SupplierStatusKind.Order, StatusName = string.Empty, Error 
 89                    });
 90                }
 91                continue;
 92            }
 93
 94            var statusName = order?.Value<string>("status");
 95            if (order is null || string.IsNullOrWhiteSpace(statusName)) continue;
 96
 97            var statusId = order.Value<int?>("statusId");
 98            var expectedAt = ParseDate(order.Value<string>("mdt"));
 99            foreach (var link in group)
 100            {
 101                result.Add(new SupplierStatusSnapshot
 102                {
 103                    OrderId = link.StudioOrderId,
 104                    Kind = SupplierStatusKind.Order,
 105                    ExternalCode = statusId?.ToString(CultureInfo.InvariantCulture),
 106                    StatusName = statusName,
 107                    ExternalOrderNumber = group.Key.ToString(CultureInfo.InvariantCulture),
 108                    ExpectedDeliveryAt = expectedAt
 109                });
 110            }
 111        }
 112
 113        return result;
 114    }
 115
 116    private static DateTime? ParseDate(string? value) =>
 117        DateTime.TryParseExact(value, "dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var 
 118        : DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var fallback) ? fallback : nul
 119}
 120
 121public sealed class ForumAutoSupplierStatusProvider : ForumAutoSupplierStatusProviderBase
 122{
 123    public ForumAutoSupplierStatusProvider(
 124        ApplicationDbContext db, ForumAutoDataManager forumAuto, LocalCartStatusReader cartReader,
 125        ILogger<ForumAutoSupplierStatusProvider> logger)
 126        : base(db, forumAuto, cartReader, logger)
 127    {
 128    }
 129
 130    public override string ProviderKey => ApiSupplierSettings.ForumAutoProviderKey;
 131}
 132
 133public sealed class ForumAutoInterpartsSupplierStatusProvider : ForumAutoSupplierStatusProviderBase
 134{
 135    public ForumAutoInterpartsSupplierStatusProvider(
 136        ApplicationDbContext db, ForumAutoInterpartsDataManager forumAuto, LocalCartStatusReader cartReader,
 137        ILogger<ForumAutoInterpartsSupplierStatusProvider> logger)
 138        : base(db, forumAuto, cartReader, logger)
 139    {
 140    }
 141
 142    public override string ProviderKey => ApiSupplierSettings.ForumAutoInterpartsProviderKey;
 143}
 144
 145public sealed class ForumAutoPiterSupplierStatusProvider : ForumAutoSupplierStatusProviderBase
 146{
 147    public ForumAutoPiterSupplierStatusProvider(
 148        ApplicationDbContext db, ForumAutoPiterDataManager forumAuto, LocalCartStatusReader cartReader,
 149        ILogger<ForumAutoPiterSupplierStatusProvider> logger)
 150        : base(db, forumAuto, cartReader, logger)
 151    {
 152    }
 153
 154    public override string ProviderKey => ApiSupplierSettings.ForumAutoPiterProviderKey;
 155}
 156
 157public sealed class ForumAutoRostovSupplierStatusProvider : ForumAutoSupplierStatusProviderBase
 158{
 159    public ForumAutoRostovSupplierStatusProvider(
 160        ApplicationDbContext db, ForumAutoRostovDataManager forumAuto, LocalCartStatusReader cartReader,
 161        ILogger<ForumAutoRostovSupplierStatusProvider> logger)
 0162        : base(db, forumAuto, cartReader, logger)
 163    {
 0164    }
 165
 0166    public override string ProviderKey => ApiSupplierSettings.ForumAutoRostovProviderKey;
 167}
 168
 169public sealed class ForumAutoIstraSupplierStatusProvider : ForumAutoSupplierStatusProviderBase
 170{
 171    public ForumAutoIstraSupplierStatusProvider(
 172        ApplicationDbContext db, ForumAutoIstraDataManager forumAuto, LocalCartStatusReader cartReader,
 173        ILogger<ForumAutoIstraSupplierStatusProvider> logger)
 174        : base(db, forumAuto, cartReader, logger)
 175    {
 176    }
 177
 178    public override string ProviderKey => ApiSupplierSettings.ForumAutoIstraProviderKey;
 179}