< Summary

Information
Class: Services.ApiServices.InterpartsPriceApi.InterpartsCurrencyDirectory
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/InterpartsPriceApi/InterpartsCurrencyRateService.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 3
Coverable lines: 3
Total lines: 82
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_Rates()100%210%
get_Names()100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/InterpartsPriceApi/InterpartsCurrencyRateService.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using Microsoft.Extensions.Caching.Memory;
 3using OzonRepositories.Context;
 4
 5namespace Services.ApiServices.InterpartsPriceApi;
 6
 7/// <summary>Справочник валют Interparts: курс к рублю и отображаемое имя по id.</summary>
 8/// <param name="Rates">id валюты → курс к рублю.</param>
 9/// <param name="Names">id валюты → название из справочника.</param>
 010public sealed record InterpartsCurrencyDirectory(
 011    IReadOnlyDictionary<int, decimal> Rates,
 012    IReadOnlyDictionary<int, string> Names);
 13
 14/// <summary>
 15/// Курсы валют из таблицы InterpartsSutCurrencies (основная MySQL БД) для перевода цен Interparts в рубли.
 16/// </summary>
 17public sealed class InterpartsCurrencyRateService
 18{
 19    private const string CacheKey = "Interparts_SutCurrency_Directory_v3_mysql";
 20    private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(10);
 21
 22    private readonly ApplicationDbContext _db;
 23    private readonly IMemoryCache _cache;
 24
 25    public InterpartsCurrencyRateService(ApplicationDbContext db, IMemoryCache cache)
 26    {
 27        _db = db;
 28        _cache = cache;
 29    }
 30
 31    public async Task<IReadOnlyDictionary<int, decimal>> GetRatesToRubAsync(CancellationToken cancellationToken = defaul
 32    {
 33        var directory = await GetDirectoryAsync(cancellationToken).ConfigureAwait(false);
 34        return directory.Rates;
 35    }
 36
 37    /// <summary>Курсы вместе с названиями валют — для подсказки «цена × курс» в проценке.</summary>
 38    public async Task<InterpartsCurrencyDirectory> GetDirectoryAsync(CancellationToken cancellationToken = default)
 39    {
 40        if (_cache.TryGetValue(CacheKey, out InterpartsCurrencyDirectory? cached) && cached != null)
 41            return cached;
 42
 43        var rows = await _db.InterpartsSutCurrencies
 44            .AsNoTracking()
 45            .Select(c => new { c.Id, c.Name, c.RateToRub })
 46            .ToListAsync(cancellationToken)
 47            .ConfigureAwait(false);
 48
 49        var directory = new InterpartsCurrencyDirectory(
 50            rows.ToDictionary(x => x.Id, x => x.RateToRub),
 51            rows.ToDictionary(x => x.Id, x => x.Name));
 52
 53        _cache.Set(CacheKey, directory, new MemoryCacheEntryOptions
 54        {
 55            AbsoluteExpirationRelativeToNow = CacheTtl
 56        });
 57
 58        return directory;
 59    }
 60
 61    /// <summary>Сбрасывает кэш после ручного изменения курсов в админке.</summary>
 62    public void InvalidateRatesCache() => _cache.Remove(CacheKey);
 63
 64    /// <summary>
 65    /// Курс для SupplierCurrencyId; при отсутствии id или записи — 1 и <paramref name="usedFallback"/> = true.
 66    /// </summary>
 67    public static decimal GetRateToRub(int? supplierCurrencyId, IReadOnlyDictionary<int, decimal> rates, out bool usedFa
 68    {
 69        usedFallback = false;
 70        if (!supplierCurrencyId.HasValue || supplierCurrencyId.Value <= 0)
 71        {
 72            usedFallback = true;
 73            return 1m;
 74        }
 75
 76        if (rates.TryGetValue(supplierCurrencyId.Value, out var rate))
 77            return rate;
 78
 79        usedFallback = true;
 80        return 1m;
 81    }
 82}