< Summary

Information
Class: Services.CacheServices.Cache.OzonOrdersCache.OrderCache
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/CacheServices/Cache/OzonOrdersCache/OrderCache.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 115
Coverable lines: 115
Total lines: 246
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 38
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
SetCache(...)100%210%
GetCache(...)0%620%
Get()0%7280%
Get()0%2040%
GetAllOrdersCached()0%620%
Get()0%2040%
Get()100%210%
Update()100%210%
UpdateCacheIncrementally()0%110100%
RemoveOrderFromCache()0%2040%
RemoveOrdersFromCache()0%2040%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/CacheServices/Cache/OzonOrdersCache/OrderCache.cs

#LineLine coverage
 1using Microsoft.Extensions.Caching.Memory;
 2using OzonDomains.Models;
 3using OzonDomains.Models.AccessControl;
 4using Services.AccessControl;
 5using Services.DataServices;
 6
 7namespace Services.CacheServices.Cache.OzonOrdersCache
 8{
 9    public class OrderCache : IRedisAppCache<Order>
 10    {
 11        private readonly IMemoryCache _memoryCache;
 12        private readonly OrdersDataServices _ordersService;
 13        private readonly IDataScopeEvaluator _dataScopeEvaluator;
 14        private const string CacheKey = "Orders";
 15        private const string AllOrdersCacheKey = "AllOrders";
 16        private const int DefaultPageSize = 15;
 17        private const int MinInitialLoadCount = 50;
 018        private int _maxPage = 1;
 019        private int _maxItemsToLoad = MinInitialLoadCount;
 20
 021        public OrderCache(IMemoryCache memoryCache, OrdersDataServices ordersDataServices,
 022            IDataScopeEvaluator dataScopeEvaluator)
 23        {
 024            _memoryCache = memoryCache;
 025            _ordersService = ordersDataServices;
 026            _dataScopeEvaluator = dataScopeEvaluator;
 027        }
 28
 29        private void SetCache(string key, object value, TimeSpan expiration)
 30        {
 031            var cacheEntryOptions = new MemoryCacheEntryOptions()
 032                .SetAbsoluteExpiration(expiration);
 033            _memoryCache.Set(key, value, cacheEntryOptions);
 034        }
 35
 36        private T? GetCache<T>(string key)
 37        {
 038            return _memoryCache.TryGetValue(key, out T? value) ? value : default;
 39        }
 40
 41        /// <summary>
 42        /// Получение заказов с постраничной загрузкой без учета прав доступа.
 43        /// Загружает только данные для текущей страницы (pageSize заказов на страницу).
 44        /// </summary>
 45        /// <param name="page">Номер страницы (1-based)</param>
 46        /// <param name="pageSize">Количество заказов на страницу</param>
 47        public async Task<List<Order>> Get(int page, int pageSize = DefaultPageSize)
 48        {
 049            if (pageSize <= 0) pageSize = DefaultPageSize;
 50
 051            var cachedOrders = GetCache<List<Order>>(CacheKey);
 052            int requiredItemCount = page * pageSize;
 053            int initialLoadCount = Math.Max(MinInitialLoadCount, pageSize);
 54
 055            if (cachedOrders == null)
 56            {
 057                cachedOrders = (await _ordersService.GetOrders(0, initialLoadCount)).ToList();
 058                cachedOrders = cachedOrders.GroupBy(o => o.Id).Select(g => g.First()).ToList();
 059                _maxPage = 1;
 060                _maxItemsToLoad = initialLoadCount;
 061                SetCache(CacheKey, cachedOrders, TimeSpan.FromMinutes(10));
 62            }
 63
 064            if (cachedOrders.Count < requiredItemCount)
 65            {
 066                int itemsToLoad = requiredItemCount - cachedOrders.Count;
 067                var additionalOrders = (await _ordersService.GetOrders(cachedOrders.Count, itemsToLoad)).ToList();
 68
 069                if (additionalOrders.Any())
 70                {
 071                    var existingIds = new HashSet<int>(cachedOrders.Select(o => o.Id));
 072                    var newOrders = additionalOrders.Where(o => !existingIds.Contains(o.Id)).ToList();
 073                    newOrders = newOrders.GroupBy(o => o.Id).Select(g => g.First()).ToList();
 074                    cachedOrders.AddRange(newOrders);
 075                    SetCache(CacheKey, cachedOrders, TimeSpan.FromMinutes(10));
 76                }
 77
 078                _maxPage = Math.Max(_maxPage, page);
 079                _maxItemsToLoad = Math.Max(_maxItemsToLoad, itemsToLoad);
 80            }
 81
 082            return cachedOrders
 083                .GroupBy(o => o.Id)
 084                .Select(g => g.First())
 085                .Take(requiredItemCount)
 086                .OrderBy(o => o.ProcessingDate)
 087                .ToList();
 088        }
 89
 90        /// <summary>
 91        /// Получение заказов с постраничной загрузкой с учетом профиля доступа пользователя.
 92        /// Загружает только данные для текущей страницы.
 93        /// </summary>
 94        /// <param name="page">Номер страницы (1-based)</param>
 95        /// <param name="pageSize">Количество заказов на страницу</param>
 96        /// <param name="accessProfile">Профиль доступа пользователя</param>
 97        public async Task<List<Order>> Get(int page, int pageSize, AccessProfile? accessProfile)
 98        {
 099            if (pageSize <= 0) pageSize = DefaultPageSize;
 100
 0101            var scope = _dataScopeEvaluator.Evaluate(accessProfile);
 102
 0103            if (!scope.IsUnrestricted)
 104            {
 0105                var allOrders = await GetAllOrdersCached();
 0106                return _dataScopeEvaluator.ApplyToOrdersList(allOrders, scope)
 0107                    .OrderBy(o => o.ProcessingDate).ToList();
 108            }
 109
 0110            return await Get(page, pageSize);
 0111        }
 112
 113        /// <summary>
 114        /// Получение ВСЕХ заказов с кэшированием. Используется для пользователей с ограничениями.
 115        /// </summary>
 116        private async Task<List<Order>> GetAllOrdersCached()
 117        {
 0118            var cachedOrders = GetCache<List<Order>>(AllOrdersCacheKey);
 0119            if (cachedOrders != null)
 0120                return cachedOrders;
 121
 122            // Загружаем все заказы из БД один раз и кэшируем
 0123            var allOrdersQuery = await _ordersService.GetOrders(includeArchived: false);
 0124            var allOrders = allOrdersQuery
 0125                .GroupBy(o => o.Id)
 0126                .Select(g => g.First())
 0127                .OrderBy(o => o.ProcessingDate)
 0128                .ToList();
 129
 0130            SetCache(AllOrdersCacheKey, allOrders, TimeSpan.FromMinutes(10));
 0131            return allOrders;
 0132        }
 133
 134        /// <summary>
 135        /// Получение всех заказов без учета прав доступа.
 136        /// Сохранено для обратной совместимости.
 137        /// </summary>
 138        public async Task<List<Order>> Get()
 139        {
 0140            var cachedOrders = GetCache<List<Order>>(CacheKey);
 0141            if (cachedOrders != null)
 0142                return cachedOrders.OrderBy(o => o.ProcessingDate).ToList();
 143
 0144            await Update();
 0145            cachedOrders = GetCache<List<Order>>(CacheKey);
 146
 0147            if (cachedOrders != null)
 0148                return cachedOrders.OrderBy(o => o.ProcessingDate).ToList();
 149
 0150            throw new InvalidOperationException("Данные не найдены в кэше после обновления.");
 0151        }
 152
 153        /// <summary>
 154        /// Получение всех заказов с учетом профиля доступа пользователя.
 155        /// </summary>
 156        public async Task<List<Order>> Get(AccessProfile? accessProfile)
 157        {
 0158            var orders = await Get();
 0159            var scope = _dataScopeEvaluator.Evaluate(accessProfile);
 0160            return _dataScopeEvaluator.ApplyToOrdersList(orders, scope)
 0161                .OrderBy(o => o.ProcessingDate).ToList();
 0162        }
 163
 164        public async Task Update()
 165        {
 0166            var loadCount = Math.Max(_maxItemsToLoad, MinInitialLoadCount);
 0167            var orders = (await _ordersService.GetOrders(0, loadCount)).ToList();
 0168            SetCache(CacheKey, orders, TimeSpan.FromMinutes(10));
 0169        }
 170
 171        public async Task UpdateCacheIncrementally(int orderId)
 172        {
 0173            var updatedOrder = await _ordersService.GetOrder(orderId);
 0174            if (updatedOrder == null) return;
 175
 176            // Обновляем основной кэш
 0177            var cachedOrders = GetCache<List<Order>>(CacheKey) ?? new List<Order>();
 0178            var existingOrderIndex = cachedOrders.FindIndex(o => o.Id == orderId);
 179
 0180            if (existingOrderIndex != -1)
 181            {
 0182                cachedOrders[existingOrderIndex] = updatedOrder;
 183            }
 184            else
 185            {
 0186                cachedOrders.Add(updatedOrder);
 187            }
 0188            SetCache(CacheKey, cachedOrders, TimeSpan.FromMinutes(10));
 189
 190            // Обновляем также кэш всех заказов
 0191            var allCachedOrders = GetCache<List<Order>>(AllOrdersCacheKey);
 0192            if (allCachedOrders != null)
 193            {
 0194                var allExistingIndex = allCachedOrders.FindIndex(o => o.Id == orderId);
 0195                if (allExistingIndex != -1)
 196                {
 0197                    allCachedOrders[allExistingIndex] = updatedOrder;
 198                }
 199                else
 200                {
 0201                    allCachedOrders.Add(updatedOrder);
 202                }
 0203                SetCache(AllOrdersCacheKey, allCachedOrders, TimeSpan.FromMinutes(10));
 204            }
 0205        }
 206
 207        public async Task RemoveOrderFromCache(int orderId)
 208        {
 0209            await Task.CompletedTask;
 0210            var cachedOrders = GetCache<List<Order>>(CacheKey);
 0211            if (cachedOrders != null)
 212            {
 0213                cachedOrders.RemoveAll(o => o.Id == orderId);
 0214                SetCache(CacheKey, cachedOrders, TimeSpan.FromMinutes(10));
 215            }
 216
 217            // Очищаем также кэш всех заказов
 0218            var allCachedOrders = GetCache<List<Order>>(AllOrdersCacheKey);
 0219            if (allCachedOrders != null)
 220            {
 0221                allCachedOrders.RemoveAll(o => o.Id == orderId);
 0222                SetCache(AllOrdersCacheKey, allCachedOrders, TimeSpan.FromMinutes(10));
 223            }
 0224        }
 225
 226        public async Task RemoveOrdersFromCache(int[] orderIds)
 227        {
 0228            await Task.CompletedTask;
 0229            var cachedOrders = GetCache<List<Order>>(CacheKey);
 0230            if (cachedOrders != null)
 231            {
 0232                cachedOrders.RemoveAll(o => orderIds.Contains(o.Id));
 0233                SetCache(CacheKey, cachedOrders, TimeSpan.FromMinutes(10));
 234            }
 235
 236            // Очищаем также кэш всех заказов
 0237            var allCachedOrders = GetCache<List<Order>>(AllOrdersCacheKey);
 0238            if (allCachedOrders != null)
 239            {
 0240                allCachedOrders.RemoveAll(o => orderIds.Contains(o.Id));
 0241                SetCache(AllOrdersCacheKey, allCachedOrders, TimeSpan.FromMinutes(10));
 242            }
 0243        }
 244
 245    }
 246}