< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Controllers.ProductAnalysisController
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/ProductAnalysisController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 80
Coverable lines: 80
Total lines: 237
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 20
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/ProductAnalysisController.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Authorization;
 2using Microsoft.AspNetCore.Mvc;
 3using OzonOrdersWeb.Areas.Studio2.AnalysisServices;
 4using OzonOrdersWeb.WebServices.Grid;
 5using Services.DataServices;
 6
 7namespace OzonOrdersWeb.Areas.Studio2.Controllers
 8{
 9    /// <summary>
 10    /// Страница «Анализ продуктов». Тяжёлые отчёты считают <see cref="ProductAnalysisGridService"/> и
 11    /// <see cref="ProductAnalysisReportService"/> (узкая выборка без Include + кэш), контроллер только маршрутизирует.
 12    /// </summary>
 13    [Authorize]
 14    [Area("Studio2")]
 15    public class ProductAnalysisController : Controller
 16    {
 17        private readonly OzonClientServices _ozonClientServices;
 18        private readonly AppStatusDataServices _appStatusServices;
 19        private readonly ProductAnalysisGridService _productGridService;
 20        private readonly ProductAnalysisReportService _reportService;
 21        private readonly OzonErrorLinksService _ozonErrorLinks;
 22        private readonly string _ozonErrorDashboardBaseUrl;
 23
 024        public ProductAnalysisController(
 025            OzonClientServices ozonClientServices,
 026            AppStatusDataServices appStatusServices,
 027            ProductAnalysisGridService productGridService,
 028            ProductAnalysisReportService reportService,
 029            OzonErrorLinksService ozonErrorLinks,
 030            IConfiguration configuration)
 31        {
 032            _ozonClientServices = ozonClientServices;
 033            _appStatusServices = appStatusServices;
 034            _productGridService = productGridService;
 035            _reportService = reportService;
 036            _ozonErrorLinks = ozonErrorLinks;
 037            _ozonErrorDashboardBaseUrl = (configuration["OzonErrorDashboard:BaseUrl"] ?? string.Empty).TrimEnd('/');
 038        }
 39
 40        private async Task<IActionResult> RunReport(string errorTitle, Func<Task<IActionResult>> action, CancellationTok
 41        {
 42            try
 43            {
 044                return await action();
 45            }
 046            catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
 47            {
 048                return new EmptyResult();
 49            }
 050            catch (Exception ex)
 51            {
 052                return StatusCode(500, new { error = errorTitle, details = ex.Message });
 53            }
 054        }
 55
 56        /// <summary>
 57        /// Главная страница анализа продуктов
 58        /// </summary>
 59        [HttpGet]
 60        public async Task<IActionResult> Index()
 61        {
 62            // Получаем список клиентов для фильтра
 063            var clients = await _ozonClientServices.GetOzonClients();
 064            ViewBag.Clients = clients.OrderBy(c => c.Name).Select(c => c.Name).Distinct().ToList();
 065            ViewBag.OzonErrorDashboardBaseUrl = _ozonErrorDashboardBaseUrl;
 66
 067            return View();
 068        }
 69
 70        /// <summary>
 71        /// Получить список клиентов для фильтра
 72        /// </summary>
 73        [HttpGet]
 74        public async Task<IActionResult> GetClients()
 75        {
 76            try
 77            {
 078                var clients = await _ozonClientServices.GetOzonClients();
 079                var clientNames = clients.OrderBy(c => c.Name).Select(c => c.Name).Distinct().ToList();
 080                return Ok(clientNames);
 81            }
 082            catch (Exception ex)
 83            {
 084                return StatusCode(500, new { error = "Ошибка при получении списка клиентов", details = ex.Message });
 85            }
 086        }
 87
 88        /// <summary>Ссылки на dashboard ошибок Ozon по клиентам; refresh=true обходит кэш.</summary>
 89        [HttpGet]
 90        public Task<IActionResult> GetOzonErrorLinks([FromQuery] bool refresh = false, CancellationToken cancellationTok
 091            RunReport("Ошибка при формировании ссылок ошибок Ozon", async () => Ok(await _ozonErrorLinks.GetAsync(refres
 92
 93        /// <summary>
 94        /// Блок строк грида «Продукты» (Infinite Row Model): фильтры, поиск и сортировка по всему набору групп.
 95        /// </summary>
 96        [HttpPost]
 97        [ValidateAntiForgeryToken]
 98        public async Task<IActionResult> GetProductGroupsGrid([FromBody] ServerGridRequest? request, CancellationToken c
 99        {
 0100            var error = ServerGridQuery.Validate(request);
 0101            if (error != null) return BadRequest(new { success = false, error });
 102
 0103            var page = await _productGridService.QueryAsync(request!, cancellationToken);
 0104            var lastRow = request!.StartRow + page.Rows.Count >= page.TotalCount ? page.TotalCount : -1;
 0105            return Ok(new { success = true, rows = page.Rows, totalCount = page.TotalCount, lastRow, stats = page.Stats 
 0106        }
 107
 108        /// <summary>Значения колонки с количеством продуктов при остальных фильтрах — для set-фильтров грида.</summary>
 109        [HttpPost]
 110        [ValidateAntiForgeryToken]
 111        public async Task<IActionResult> GetProductGroupsFacets([FromBody] ServerGridRequest? request, CancellationToken
 112        {
 0113            if (request == null || string.IsNullOrWhiteSpace(request.ColId))
 0114                return BadRequest(new { success = false, error = "colId обязателен" });
 0115            request.StartRow = 0;
 0116            request.EndRow = 1;
 0117            var error = ServerGridQuery.Validate(request);
 0118            if (error != null) return BadRequest(new { success = false, error });
 119
 0120            var values = await _productGridService.FacetsAsync(request, cancellationToken);
 0121            return Ok(new { success = true, values });
 0122        }
 123
 124        /// <summary>
 125        /// Детализация по конкретному ProductKey
 126        /// </summary>
 127        [HttpGet]
 128        public async Task<IActionResult> GetProductDetail(
 129            [FromQuery] string productKey,
 130            [FromQuery] DateTime? startDate,
 131            [FromQuery] DateTime? endDate,
 132            CancellationToken cancellationToken)
 133        {
 0134            if (string.IsNullOrWhiteSpace(productKey))
 0135                return BadRequest(new { error = "ProductKey не может быть пустым" });
 136
 137            try
 138            {
 0139                var result = await _productGridService.GetDetailAsync(productKey, startDate, endDate, cancellationToken)
 0140                return result == null
 0141                    ? NotFound(new { error = $"Заказы с ProductKey '{productKey}' не найдены" })
 0142                    : Ok(result);
 143            }
 0144            catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
 145            {
 0146                return new EmptyResult();
 147            }
 0148            catch (Exception ex)
 149            {
 0150                return StatusCode(500, new { error = "Ошибка при получении детальной информации", details = ex.Message }
 151            }
 0152        }
 153
 154        /// <summary>Стартовая сводка: главные показатели периода, динамика, топ-продукты, убыточные продукты, топ-клиен
 155        [HttpGet]
 156        public Task<IActionResult> GetSummary([FromQuery] DateTime? startDate, [FromQuery] DateTime? endDate, Cancellati
 0157            RunReport("Ошибка при получении сводки", async () => Ok(await _reportService.SummaryAsync(startDate, endDate
 158
 159        /// <summary>Общая статистика: прибыль, средний чек и число заказов по месяцам и клиентам (топ-10).</summary>
 160        [HttpGet]
 161        public Task<IActionResult> GetOverallProfitStatistics([FromQuery] DateTime? startDate, [FromQuery] DateTime? end
 0162            RunReport("Ошибка при получении общей статистики прибыли", async () => Ok(await _reportService.OverallAsync(
 163
 164        /// <summary>Дополнительные метрики: ROI, возвраты, время выполнения, популярные товары.</summary>
 165        [HttpGet]
 166        public Task<IActionResult> GetAdditionalMetrics([FromQuery] DateTime? startDate, [FromQuery] DateTime? endDate, 
 0167            RunReport("Ошибка при получении дополнительных метрик", async () => Ok(await _reportService.MetricsAsync(sta
 168
 169        /// <summary>Сравнительная аналитика: текущий месяц против предыдущего периода.</summary>
 170        [HttpGet]
 171        public Task<IActionResult> GetComparativeAnalysis([FromQuery] int monthsBack = 1, [FromQuery] string? productKey
 0172            RunReport("Ошибка при получении сравнительной аналитики", async () => Ok(await _reportService.ComparativeAsy
 173
 174        /// <summary>Сезонность спроса: по месяцам, дням недели и часам.</summary>
 175        [HttpGet]
 176        public Task<IActionResult> GetSeasonalityAnalysis([FromQuery] int? appStatusId = null, [FromQuery] string? produ
 177            [FromQuery] DateTime? startDate = null, [FromQuery] DateTime? endDate = null, CancellationToken cancellation
 0178            RunReport("Ошибка при получении анализа сезонности", async () => Ok(await _reportService.SeasonalityAsync(ap
 179
 180        /// <summary>
 181        /// Получить список статусов для фильтра
 182        /// </summary>
 183        [HttpGet]
 184        public async Task<IActionResult> GetAppStatuses()
 185        {
 186            try
 187            {
 0188                var statuses = await _appStatusServices.GetAppStatuses();
 0189                return Ok(statuses.Select(s => new { id = s.Id, name = s.Name }));
 190            }
 0191            catch (Exception ex)
 192            {
 0193                return StatusCode(500, new { error = "Ошибка при получении статусов", details = ex.Message });
 194            }
 0195        }
 196
 197        /// <summary>Анализ по статусам заказов (приложения или Ozon-клиента): распределение и динамика по месяцам.</sum
 198        [HttpGet]
 199        public Task<IActionResult> GetOrderStatusesAnalysis([FromQuery] string statusType = "app", [FromQuery] string? p
 200            [FromQuery] DateTime? startDate = null, [FromQuery] DateTime? endDate = null, CancellationToken cancellation
 0201            RunReport("Ошибка при получении анализа по статусам", async () => Ok(await _reportService.OrderStatusesAsync
 202
 203        /// <summary>Анализ по складам и регионам: итоги, топы и динамика по месяцам.</summary>
 204        [HttpGet]
 205        public Task<IActionResult> GetWarehouseRegionAnalysis([FromQuery] DateTime? startDate, [FromQuery] DateTime? end
 206            [FromQuery] string? productKey = null, CancellationToken cancellationToken = default) =>
 0207            RunReport("Ошибка при получении анализа по складам и регионам", async () => Ok(await _reportService.Warehous
 208
 209        /// <summary>Прогноз продаж, тренды по продуктам/складам/регионам и рекомендации по закупкам.</summary>
 210        [HttpGet]
 211        public Task<IActionResult> GetForecastTrendAnalysis(string? productKey = null, DateTime? startDate = null, DateT
 212            int forecastDays = 30, CancellationToken cancellationToken = default) =>
 0213            RunReport("Ошибка при получении прогнозов и трендов", async () => Ok(await _reportService.ForecastAsync(prod
 214
 215        /// <summary>
 216        /// Возвращает список Ozon-кабинетов с расшифрованными Client ID для запроса индекса ошибок с клиента
 217        /// </summary>
 218        [HttpGet]
 219        public async Task<IActionResult> GetDefectIndexClients()
 220        {
 221            try
 222            {
 0223                var clients = await _ozonClientServices.GetOzonClients();
 0224                var result = clients
 0225                    .Where(c => c.ClientType == OzonDomains.ClientType.OZON && !string.IsNullOrEmpty(c.DecryptClientId))
 0226                    .Select(c => new { name = c.Name, clientId = c.DecryptClientId })
 0227                    .ToList();
 0228                return Ok(result);
 229            }
 0230            catch (Exception ex)
 231            {
 0232                return StatusCode(500, new { error = "Ошибка при получении списка кабинетов", details = ex.Message });
 233            }
 0234        }
 235    }
 236}
 237

Methods/Properties

.ctor(Services.DataServices.OzonClientServices,Services.DataServices.AppStatusDataServices,OzonOrdersWeb.Areas.Studio2.AnalysisServices.ProductAnalysisGridService,OzonOrdersWeb.Areas.Studio2.AnalysisServices.ProductAnalysisReportService,OzonOrdersWeb.Areas.Studio2.AnalysisServices.OzonErrorLinksService,Microsoft.Extensions.Configuration.IConfiguration)
RunReport()
Index()
GetClients()
GetOzonErrorLinks(System.Boolean,System.Threading.CancellationToken)
GetProductGroupsGrid()
GetProductGroupsFacets()
GetProductDetail()
GetSummary(System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.Threading.CancellationToken)
GetOverallProfitStatistics(System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.Threading.CancellationToken)
GetAdditionalMetrics(System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.Threading.CancellationToken)
GetComparativeAnalysis(System.Int32,System.String,System.Threading.CancellationToken)
GetSeasonalityAnalysis(System.Nullable`1<System.Int32>,System.String,System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.Threading.CancellationToken)
GetAppStatuses()
GetOrderStatusesAnalysis(System.String,System.String,System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.Threading.CancellationToken)
GetWarehouseRegionAnalysis(System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.String,System.Threading.CancellationToken)
GetForecastTrendAnalysis(System.String,System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.Int32,System.Threading.CancellationToken)
GetDefectIndexClients()