< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Controllers.FidStatItemRequest
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/FidStatController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 6
Coverable lines: 6
Total lines: 257
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
get_Id()100%210%
get_RangeFrom()100%210%
get_RangeTo()100%210%
get_Count()100%210%
get_Percentage()100%210%
get_Indices()100%210%

File(s)

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

#LineLine coverage
 1using Microsoft.AspNetCore.Authorization;
 2using Microsoft.AspNetCore.Mvc;
 3using OzonDomains.Models;
 4using Services.DataServices;
 5using System;
 6using System.Collections.Generic;
 7using System.ComponentModel.DataAnnotations;
 8using System.Linq;
 9using System.Text.Json;
 10using System.Threading;
 11using System.Threading.Tasks;
 12
 13namespace OzonOrdersWeb.Areas.Studio2.Controllers
 14{
 15    [Area("Studio2")]
 16    [Route("Studio2/FidStat")]
 17    [Route("FidStat")]
 18    public class FidStatController : Controller
 19    {
 20        private static readonly JsonSerializerOptions PushJsonOptions = new()
 21        {
 22            PropertyNameCaseInsensitive = true
 23        };
 24
 25        private readonly FidStatDataServices _fidStatDataServices;
 26
 27        public FidStatController(FidStatDataServices fidStatDataServices)
 28        {
 29            _fidStatDataServices = fidStatDataServices;
 30        }
 31
 32        // ──────────────────────────────────────────────────────────────────────
 33        // POST /Studio2/FidStat/Push (или /FidStat/Push)
 34        // Публичный endpoint — принимает данные статистики фида без авторизации.
 35        // Тело — JSON (Content-Type необязателен: внешние скрипты часто шлют без заголовка).
 36        // Возвращает JSON с подтверждением: { status, snapshotId, itemsCount, fidName }
 37        // ──────────────────────────────────────────────────────────────────────
 38        [HttpPost("Push")]
 39        [AllowAnonymous]
 40        public async Task<IActionResult> Push(CancellationToken cancellationToken)
 41        {
 42            FidStatRequest request;
 43            try
 44            {
 45                request = await JsonSerializer.DeserializeAsync<FidStatRequest>(
 46                             Request.Body, PushJsonOptions, cancellationToken)
 47                         ?? new FidStatRequest();
 48            }
 49            catch (JsonException)
 50            {
 51                return BadRequest(new { status = "error", errors = new[] { "Некорректный JSON в теле запроса" } });
 52            }
 53
 54            if (!TryValidateModel(request))
 55                return BadRequest(new { status = "error", errors = ModelState.Values
 56                    .SelectMany(v => v.Errors)
 57                    .Select(e => e.ErrorMessage) });
 58
 59            var snapshot = new FidStatSnapshot
 60            {
 61                FidName = request.FidName,
 62                PostTime = request.PostTime,
 63                ReceivedAt = DateTime.UtcNow,
 64                Items = request.Items.Select(i => new FidStatItem
 65                {
 66                    RowNumber = i.Id,
 67                    RangeFrom = i.RangeFrom,
 68                    RangeTo = i.RangeTo,
 69                    Count = i.Count,
 70                    Percentage = i.Percentage,
 71                    Indices = i.Indices ?? string.Empty
 72                }).ToList()
 73            };
 74
 75            await _fidStatDataServices.Add(snapshot);
 76
 77            return Ok(new
 78            {
 79                status = "ok",
 80                snapshotId = snapshot.Id,
 81                itemsCount = snapshot.Items.Count,
 82                fidName = snapshot.FidName
 83            });
 84        }
 85
 86        // ──────────────────────────────────────────────────────────────────────
 87        // GET /Studio2/FidStat/Index — страница просмотра статистики фидов
 88        // ──────────────────────────────────────────────────────────────────────
 89        [HttpGet("")]
 90        [HttpGet("Index")]
 91        [Authorize(Roles = "User,Admin,SuperAdmin")]
 92        public async Task<IActionResult> Index()
 93        {
 94            var fidNames = await _fidStatDataServices.GetDistinctFidNames();
 95            ViewBag.FidNames = fidNames;
 96            return View();
 97        }
 98
 99        // ──────────────────────────────────────────────────────────────────────
 100        // GET /Studio2/FidStat/GetSnapshots
 101        // AJAX: список снимков с фильтрацией, сортировкой и пагинацией
 102        // ──────────────────────────────────────────────────────────────────────
 103        [HttpGet("GetSnapshots")]
 104        [Authorize(Roles = "User,Admin,SuperAdmin")]
 105        public async Task<IActionResult> GetSnapshots(
 106            string? fidName,
 107            DateTime? dateFrom,
 108            DateTime? dateTo,
 109            int page = 1,
 110            int pageSize = 20)
 111        {
 112            if (page < 1) page = 1;
 113            if (pageSize < 1 || pageSize > 200) pageSize = 20;
 114
 115            var skip = (page - 1) * pageSize;
 116
 117            var items = await _fidStatDataServices.GetFiltered(fidName, dateFrom, dateTo, skip, pageSize);
 118            var total = await _fidStatDataServices.GetFilteredCount(fidName, dateFrom, dateTo);
 119
 120            return Ok(new
 121            {
 122                Items = items.Select(s => new
 123                {
 124                    s.Id,
 125                    s.FidName,
 126                    PostTime = s.PostTime.ToUniversalTime().ToString("o"),
 127                    ReceivedAt = DateTime.SpecifyKind(s.ReceivedAt, DateTimeKind.Utc).ToString("o"),
 128                }),
 129                TotalCount = total,
 130                Pages = (int)Math.Ceiling((double)total / pageSize),
 131                Page = page
 132            });
 133        }
 134
 135        // ──────────────────────────────────────────────────────────────────────
 136        // GET /Studio2/FidStat/GetSnapshotDetail?id=N
 137        // AJAX: полный снимок с items для отображения таблицы диапазонов
 138        // ──────────────────────────────────────────────────────────────────────
 139        [HttpGet("GetSnapshotDetail")]
 140        [Authorize(Roles = "User,Admin,SuperAdmin")]
 141        public async Task<IActionResult> GetSnapshotDetail(int id)
 142        {
 143            var snapshot = await _fidStatDataServices.GetByIdWithItems(id);
 144            if (snapshot == null) return NotFound(new { status = "not_found" });
 145
 146            return Ok(new
 147            {
 148                snapshot.Id,
 149                snapshot.FidName,
 150                PostTime = snapshot.PostTime.ToUniversalTime().ToString("o"),
 151                ReceivedAt = DateTime.SpecifyKind(snapshot.ReceivedAt, DateTimeKind.Utc).ToString("o"),
 152                TotalCount = snapshot.Items.Sum(i => i.Count),
 153                Items = snapshot.Items.OrderBy(i => i.RangeFrom).Select(i => new
 154                {
 155                    i.RowNumber,
 156                    RangeFrom = i.RangeFrom,
 157                    RangeTo = i.RangeTo,
 158                    i.Count,
 159                    i.Percentage,
 160                    i.Indices,
 161                    RangeLabel = $"{i.RangeFrom:0.000} - {i.RangeTo:0.000}"
 162                })
 163            });
 164        }
 165
 166        // ──────────────────────────────────────────────────────────────────────
 167        // GET /Studio2/FidStat/GetFidNames
 168        // AJAX: список уникальных FidName для фильтра-дропдауна
 169        // ──────────────────────────────────────────────────────────────────────
 170        [HttpGet("GetFidNames")]
 171        [Authorize(Roles = "User,Admin,SuperAdmin")]
 172        public async Task<IActionResult> GetFidNames()
 173        {
 174            var names = await _fidStatDataServices.GetDistinctFidNames();
 175            return Ok(names);
 176        }
 177
 178        // GET /Studio2/FidStat/GetLatestSnapshot
 179        // AJAX: последний снимок со всеми элементами для badge обновления
 180        // ──────────────────────────────────────────────────────────────────────
 181        [HttpGet("GetLatestSnapshot")]
 182        [Authorize(Roles = "User,Admin,SuperAdmin")]
 183        public async Task<IActionResult> GetLatestSnapshot()
 184        {
 185            try
 186            {
 187                var snapshots = await _fidStatDataServices.GetFiltered(null, null, null, 0, 1);
 188
 189                if (snapshots == null || !snapshots.Any())
 190                {
 191                    return Ok(new { items = new List<object>() });
 192                }
 193
 194                var latest = snapshots.First();
 195                var detailedSnapshot = await _fidStatDataServices.GetByIdWithItems(latest.Id);
 196
 197                if (detailedSnapshot == null)
 198                {
 199                    return Ok(new { items = new List<object>() });
 200                }
 201
 202                return Ok(new
 203                {
 204                    Id = detailedSnapshot.Id,
 205                    FidName = detailedSnapshot.FidName,
 206                    PostTime = detailedSnapshot.PostTime.ToUniversalTime().ToString("o"),
 207                    TotalCount = detailedSnapshot.Items.Sum(i => i.Count),
 208                    Items = detailedSnapshot.Items.OrderBy(i => i.RangeFrom).Select(i => new
 209                    {
 210                        i.RowNumber,
 211                        RangeFrom = i.RangeFrom,
 212                        RangeTo = i.RangeTo,
 213                        i.Count,
 214                        i.Percentage,
 215                        RangeLabel = $"{i.RangeFrom:0.000} - {i.RangeTo:0.000}"
 216                    })
 217                });
 218            }
 219            catch (Exception ex)
 220            {
 221                return StatusCode(500, new { error = ex.Message });
 222            }
 223        }
 224    }
 225
 226    // ──────────────────────────────────────────────────────────────────────────
 227    // Request DTOs
 228    // ──────────────────────────────────────────────────────────────────────────
 229
 230    public class FidStatRequest
 231    {
 232        [Required]
 233        [MaxLength(100)]
 234        public string FidName { get; set; } = string.Empty;
 235
 236        public DateTimeOffset PostTime { get; set; }
 237
 238        [Required]
 239        [MinLength(1)]
 240        public List<FidStatItemRequest> Items { get; set; } = new();
 241    }
 242
 243    public class FidStatItemRequest
 244    {
 0245        public int Id { get; set; }
 246
 0247        public decimal RangeFrom { get; set; }
 248
 0249        public decimal RangeTo { get; set; }
 250
 0251        public int Count { get; set; }
 252
 0253        public decimal Percentage { get; set; }
 254
 0255        public string? Indices { get; set; }
 256    }
 257}