| | | 1 | | using Microsoft.EntityFrameworkCore; |
| | | 2 | | using OzonDomains.Models; |
| | | 3 | | using OzonDomains.Models.SupplierStatuses; |
| | | 4 | | using OzonRepositories.Context; |
| | | 5 | | using Services.ApiServices.BergApi; |
| | | 6 | | |
| | | 7 | | namespace Services.SupplierStatusServices.Providers; |
| | | 8 | | |
| | | 9 | | /// <summary> |
| | | 10 | | /// Статусы Berg. Корзина у Berg локальная, поэтому её стадия читается из BergCartItems, а статусы |
| | | 11 | | /// оформленных заказов — из ordering/states.json по сохранённым идентификаторам. Статус у Berg |
| | | 12 | | /// живёт на строке заказа, а не на заказе: сопоставление идёт через BergOrderItems. |
| | | 13 | | /// </summary> |
| | | 14 | | public sealed class BergSupplierStatusProvider : ISupplierStatusProvider |
| | | 15 | | { |
| | | 16 | | private readonly ApplicationDbContext _db; |
| | | 17 | | private readonly BergDataManager _berg; |
| | | 18 | | private readonly LocalCartStatusReader _localCart; |
| | | 19 | | |
| | 32 | 20 | | public BergSupplierStatusProvider(ApplicationDbContext db, BergDataManager berg, LocalCartStatusReader localCart) |
| | | 21 | | { |
| | 32 | 22 | | _db = db; |
| | 32 | 23 | | _berg = berg; |
| | 32 | 24 | | _localCart = localCart; |
| | 32 | 25 | | } |
| | | 26 | | |
| | 9 | 27 | | public string ProviderKey => ApiSupplierSettings.BergProviderKey; |
| | | 28 | | |
| | | 29 | | public Task<IReadOnlyList<SupplierStatusSnapshot>> FetchCartAsync(IReadOnlyList<OrderSupplierStateRef> states, |
| | 7 | 30 | | CancellationToken ct) => _localCart.ReadAsync(ProviderKey, states, ct); |
| | | 31 | | |
| | | 32 | | public async Task<IReadOnlyList<SupplierStatusSnapshot>> FetchOrderAsync(IReadOnlyList<OrderSupplierStateRef> states |
| | | 33 | | CancellationToken ct) |
| | | 34 | | { |
| | 25 | 35 | | if (states.Count == 0) return []; |
| | | 36 | | |
| | 51 | 37 | | var orderIds = states.Select(x => x.OrderId).Distinct().ToList(); |
| | 23 | 38 | | var lines = await _db.BergOrderItems.AsNoTracking() |
| | 23 | 39 | | .Where(x => orderIds.Contains(x.StudioOrderId)) |
| | 23 | 40 | | .Select(x => new { x.StudioOrderId, x.Sequence, x.BergOrder!.ExternalOrderId, x.BergOrder.CreatedAtUtc }) |
| | 23 | 41 | | .ToListAsync(ct); |
| | 24 | 42 | | if (lines.Count == 0) return []; |
| | | 43 | | |
| | | 44 | | // Одна строка Studio2 может попасть в несколько заказов Berg (повторное оформление); |
| | | 45 | | // актуален последний. |
| | 22 | 46 | | var latest = lines |
| | 25 | 47 | | .GroupBy(x => x.StudioOrderId) |
| | 95 | 48 | | .ToDictionary(g => g.Key, g => g.OrderByDescending(x => x.CreatedAtUtc).First()); |
| | | 49 | | |
| | 46 | 50 | | var externalIds = latest.Values.Select(x => x.ExternalOrderId.ToString()).Distinct().ToList(); |
| | | 51 | | |
| | | 52 | | IReadOnlyList<BergOrderState> orders; |
| | | 53 | | try |
| | | 54 | | { |
| | 22 | 55 | | var response = await _berg.GetStatesAsync(externalIds, ct); |
| | 17 | 56 | | orders = BergDataManager.ParseOrders(response); |
| | 17 | 57 | | } |
| | 5 | 58 | | catch (Exception ex) when (ex is BergApiException or HttpRequestException or TaskCanceledException) |
| | | 59 | | { |
| | 11 | 60 | | return states.Select(x => new SupplierStatusSnapshot |
| | 11 | 61 | | { |
| | 11 | 62 | | OrderId = x.OrderId, Kind = SupplierStatusKind.Order, StatusName = string.Empty, |
| | 11 | 63 | | Error = $"Berg: {ex.Message}" |
| | 11 | 64 | | }).ToList(); |
| | | 65 | | } |
| | | 66 | | |
| | 36 | 67 | | var byExternalId = orders.ToDictionary(x => x.Id); |
| | 17 | 68 | | var result = new List<SupplierStatusSnapshot>(); |
| | 74 | 69 | | foreach (var state in states) |
| | | 70 | | { |
| | 20 | 71 | | if (!latest.TryGetValue(state.OrderId, out var link) || |
| | 20 | 72 | | !byExternalId.TryGetValue(link.ExternalOrderId, out var order)) |
| | | 73 | | continue; |
| | | 74 | | |
| | | 75 | | // Berg нумерует строки sequence; если номер не сохранился, заказ односточный — |
| | | 76 | | // берём единственную строку. |
| | 38 | 77 | | var line = order.Items.FirstOrDefault(x => x.Sequence == link.Sequence) |
| | 18 | 78 | | ?? (order.Items.Count == 1 ? order.Items[0] : null); |
| | 18 | 79 | | if (line?.StateId is null) |
| | | 80 | | continue; |
| | | 81 | | |
| | 16 | 82 | | result.Add(new SupplierStatusSnapshot |
| | 16 | 83 | | { |
| | 16 | 84 | | OrderId = state.OrderId, |
| | 16 | 85 | | Kind = SupplierStatusKind.Order, |
| | 16 | 86 | | ExternalCode = line.StateId.Value.ToString(), |
| | 16 | 87 | | StatusName = string.IsNullOrWhiteSpace(line.StateName) ? $"Статус {line.StateId}" : line.StateName!, |
| | 16 | 88 | | ExternalOrderNumber = order.Id.ToString(), |
| | 16 | 89 | | ExternalOrderId = order.Id.ToString(), |
| | 16 | 90 | | ExternalLineId = line.Sequence.ToString(), |
| | 16 | 91 | | QuantityConfirmed = line.Quantity, |
| | 16 | 92 | | ExpectedDeliveryAt = line.AssuredTime ?? line.AverageTime |
| | 16 | 93 | | }); |
| | | 94 | | } |
| | | 95 | | |
| | 17 | 96 | | return result; |
| | 24 | 97 | | } |
| | | 98 | | } |