< Summary

Information
Class: Services.SupplierStatusServices.StudioOrderCommentLink
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/SupplierStatusServices/StudioOrderCommentLink.cs
Line coverage
97%
Covered lines: 45
Uncovered lines: 1
Coverable lines: 46
Total lines: 120
Line coverage: 97.8%
Branch coverage
82%
Covered branches: 38
Total branches: 46
Branch coverage: 82.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
MaxLengthFor(...)70%101083.33%
BuildFor(...)100%22100%
BuildBatch(...)100%66100%
ExtractOrderIds(...)75%88100%
IsStudioComment(...)50%22100%
Build(...)100%11100%
Build(...)75%44100%
TryExtractOrderId(...)100%66100%
ExtractOrderId(...)100%22100%
ExtractOrderLabel(...)83.33%66100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/SupplierStatusServices/StudioOrderCommentLink.cs

#LineLine coverage
 1using System.Text.RegularExpressions;
 2
 3namespace Services.SupplierStatusServices;
 4
 5/// <summary>
 6/// Привязка строки корзины/заказа внешнего поставщика к заказу Studio2 через комментарий.
 7/// У АВД это единственный канал связи: собственных полей под наш идентификатор в API нет,
 8/// но комментарий, переданный в InsertToBasket, возвращается и в GetBasketItem, и в GetOrders.
 9/// </summary>
 10public static class StudioOrderCommentLink
 11{
 12    /// <summary>Новый формат — "Studio2 {номер отправления} (ID {orderId})".</summary>
 113    private static readonly Regex StudioOrderIdPattern = new(@"^Studio2 .+ \(ID (\d+)\)$", RegexOptions.Compiled);
 14
 15    /// <summary>Старый формат для позиций, добавленных до перехода на новый: "Studio2 заказ {orderId}".</summary>
 116    private static readonly Regex LegacyStudioOrderCommentPattern = new(@"Studio2 заказ (\d+)", RegexOptions.Compiled);
 17
 18    /// <summary>
 19    /// Максимальная длина метки для провайдеров, у которых лимит поля подтверждён: Armtek — <c>COMMENT</c> позиции и
 20    /// <c>TEXT_ORD</c> заказа по 100 символов (docs/api/armtek.md), FavoritParts — 100. АВД и Japarts ставят свою метку
 21    /// в корзину собственным кодом (<c>Studio2 {метка} (ID n)</c>) без лимита из этого хелпера; у остальных провайдеров
 22    /// метка не ставится, пока поле комментария не подтверждено.
 23    /// </summary>
 4624    public static int? MaxLengthFor(string providerKey) => providerKey switch
 4625    {
 4526        "Armtek" or "ArmtekBy" => 100,
 027        "FavoritParts" or "FavoritPartsIstra" or "FavoritPartsRostov" => 100,
 128        _ => null
 4629    };
 30
 31    /// <summary>Метка Studio2 для провайдера в единой форме и в его лимите длины; <c>null</c> — у провайдера метка не п
 32    public static string? BuildFor(string providerKey, string orderLabel, int orderId) =>
 3233        MaxLengthFor(providerKey) is { } max ? Build(orderLabel, orderId, max) : null;
 34
 135    private static readonly Regex BatchPattern = new(@"Studio2 IDs: ([\d,]+)", RegexOptions.Compiled);
 36
 37    /// <summary>
 38    /// Комментарий к заказу целиком, когда в один запрос поставщику попадают несколько заказов Studio2:
 39    /// "Studio2 IDs: 101,102". Список укорачивается до лимита (id целиком, без обрезанных цифр).
 40    /// </summary>
 41    public static string BuildBatch(IEnumerable<int> orderIds, int maxLength)
 42    {
 43        const string prefix = "Studio2 IDs: ";
 1544        var text = prefix;
 1545        var first = true;
 8346        foreach (var id in orderIds.Distinct())
 47        {
 2748            var next = (first ? string.Empty : ",") + id;
 2849            if (text.Length + next.Length > maxLength) break;
 2650            text += next;
 2651            first = false;
 52        }
 53
 1554        return text;
 55    }
 56
 57    /// <summary>Все Id заказов Studio2 в комментарии: формат одного заказа и пакетный.</summary>
 58    public static IReadOnlyList<int> ExtractOrderIds(string? comment)
 59    {
 960        var ids = new List<int>();
 961        if (string.IsNullOrWhiteSpace(comment)) return ids;
 1362        if (TryExtractOrderId(comment, out var single)) ids.Add(single);
 963        var batch = BatchPattern.Match(comment);
 964        if (batch.Success)
 365            ids.AddRange(batch.Groups[1].Value.Split(',', StringSplitOptions.RemoveEmptyEntries)
 3366                .Select(x => int.TryParse(x, out var id) ? id : 0).Where(x => x > 0));
 967        return ids.Distinct().ToList();
 68    }
 69
 70    /// <summary>Есть ли в комментарии метка Studio2 вообще (для проверки, что поле комментария читается у поставщика).<
 71    public static bool IsStudioComment(string? comment) =>
 772        !string.IsNullOrWhiteSpace(comment) && comment.Contains("Studio2 ", StringComparison.Ordinal);
 73
 74    /// <summary>Формирует комментарий для передачи поставщику.</summary>
 5075    public static string Build(string orderLabel, int orderId) => $"Studio2 {orderLabel} (ID {orderId})";
 76
 77    /// <summary>
 78    /// Комментарий, укладывающийся в лимит поля поставщика (например, у FavoritParts — 100 символов).
 79    /// Обрезается только метка заказа: хвост "(ID {orderId})" обязан уцелеть, иначе Id обратно не извлечь.
 80    /// </summary>
 81    public static string Build(string orderLabel, int orderId, int maxLength)
 82    {
 3383        var full = Build(orderLabel, orderId);
 6484        if (full.Length <= maxLength) return full;
 85
 286        var overhead = full.Length - orderLabel.Length;
 287        var allowedLabel = maxLength - overhead;
 88        // Метке нужен хотя бы один символ: регулярка разбора требует непустую метку между "Studio2 " и "(ID n)".
 289        return allowedLabel < 1 ? full : Build(orderLabel[..allowedLabel].TrimEnd(), orderId);
 90    }
 91
 92    /// <summary>Извлекает Id заказа Studio2 из комментария. Пробует новый формат, затем старый.</summary>
 93    public static bool TryExtractOrderId(string? comment, out int orderId)
 94    {
 4095        orderId = 0;
 4496        if (string.IsNullOrWhiteSpace(comment)) return false;
 3697        var match = StudioOrderIdPattern.Match(comment);
 4898        if (!match.Success) match = LegacyStudioOrderCommentPattern.Match(comment);
 3699        return match.Success && int.TryParse(match.Groups[1].Value, out orderId);
 100    }
 101
 102    /// <summary>Id заказа Studio2 или null, если комментарий не наш.</summary>
 103    public static int? ExtractOrderId(string? comment) =>
 24104        TryExtractOrderId(comment, out var orderId) ? orderId : null;
 105
 106    /// <summary>Метка заказа (номер отправления) из комментария вида "Studio2 {метка} (ID n)" или "Studio2 {метка}".
 107    /// Нужна для строк, добавленных до появления Id в комментарии: по метке заказ ещё можно найти
 108    /// в базе по ShipmentNumber. Возвращает null, если комментарий не наш.</summary>
 109    public static string? ExtractOrderLabel(string? comment)
 110    {
 5111        if (string.IsNullOrWhiteSpace(comment)) return null;
 3112        var match = StudioOrderLabelPattern.Match(comment.Trim());
 4113        if (!match.Success) return null;
 2114        var label = match.Groups[1].Value.Trim();
 2115        return label.Length == 0 ? null : label;
 116    }
 117
 1118    private static readonly Regex StudioOrderLabelPattern =
 1119        new(@"^Studio2 (.+?)(?: \(ID \d+\))?$", RegexOptions.Compiled);
 120}