/
Kovalenko
/
TODO
Обзор
Документация
Войти
/
Kovalenko
/
TODO
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
TaskApi/Controllers/V2/TasksController.cs
234 строки
8 KB
Kovalenko1
pag
07 мар 2026, 13:46
07 мар 2026, 13:46
18fca63
Код
Авторство
О чём код?
using System.Globalization; using System.ComponentModel; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using TaskApi.Data; using TaskApi.Dtos.V2; using TaskApi.Models; using TaskStatus = TaskApi.Models.TaskStatus; namespace TaskApi.Controllers.V2; [ApiController] [Authorize] [ApiExplorerSettings(GroupName = "v2")] [Route("api/v2/tasks")] public class TasksController : ControllerBase { private readonly AppDbContext _db; public TasksController(AppDbContext db) { _db = db; } [HttpGet] [ProducesResponseType(typeof(IEnumerable<TaskResponseV2>), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task<ActionResult<IEnumerable<TaskResponseV2>>> GetTasks( [FromQuery] string? status, [FromQuery, DefaultValue(1)] int? page, [FromQuery, DefaultValue(ApiHelpers.DefaultPageSize)] int? pageSize, [FromQuery] string? include) { var includeFields = ApiHelpers.ParseIncludeFields(include, ApiHelpers.TaskFieldsV2, out var includeError); if (includeError is not null) { return BadRequest(includeError); } if (!ApiHelpers.TryGetPagination(page, pageSize, out var resolvedPage, out var resolvedPageSize, out var paginationError)) { return BadRequest(paginationError); } var userId = ApiHelpers.GetUserId(User); var query = _db.Tasks.AsNoTracking().Where(task => task.UserId == userId); if (!string.IsNullOrWhiteSpace(status)) { if (!Enum.TryParse<TaskStatus>(status, true, out var parsedStatus)) { return BadRequest("Invalid status filter."); } query = query.Where(task => task.Status == parsedStatus); } var totalCount = await query.CountAsync(); var totalPages = ApiHelpers.GetTotalPages(totalCount, resolvedPageSize); Response.Headers["X-Total-Count"] = totalCount.ToString(CultureInfo.InvariantCulture); Response.Headers["X-Page"] = resolvedPage.ToString(CultureInfo.InvariantCulture); Response.Headers["X-Page-Size"] = resolvedPageSize.ToString(CultureInfo.InvariantCulture); Response.Headers["X-Total-Pages"] = totalPages.ToString(CultureInfo.InvariantCulture); IQueryable<TaskItem> ordered = query.OrderByDescending(task => task.CreatedAt) .Skip((resolvedPage - 1) * resolvedPageSize) .Take(resolvedPageSize); var tasks = await ordered.ToListAsync(); if (includeFields is null) { return Ok(tasks.Select(ApiHelpers.MapTaskV2)); } return Ok(tasks.Select(task => ApiHelpers.ShapeTask(task, includeFields))); } [HttpGet("{id:guid}")] [ProducesResponseType(typeof(TaskResponseV2), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task<ActionResult<TaskResponseV2>> GetTask(Guid id, [FromQuery] string? include) { var includeFields = ApiHelpers.ParseIncludeFields(include, ApiHelpers.TaskFieldsV2, out var includeError); if (includeError is not null) { return BadRequest(includeError); } var userId = ApiHelpers.GetUserId(User); var task = await _db.Tasks.AsNoTracking() .FirstOrDefaultAsync(item => item.Id == id && item.UserId == userId); if (task is null) { return NotFound(); } if (includeFields is null) { return Ok(ApiHelpers.MapTaskV2(task)); } return Ok(ApiHelpers.ShapeTask(task, includeFields)); } [HttpPost] [ProducesResponseType(typeof(TaskResponseV2), StatusCodes.Status201Created)] [ProducesResponseType(typeof(TaskResponseV2), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status409Conflict)] public async Task<ActionResult<TaskResponseV2>> CreateTask( [FromBody] TaskCreateRequestV2 request, [FromHeader(Name = "Idempotency-Key")] string? idempotencyKey) { var title = ApiHelpers.NormalizeRequired(request.Title); if (title is null) { return BadRequest("Title is required."); } if (request.Priority is < 1 or > 5) { return BadRequest("Priority must be between 1 and 5."); } var userId = ApiHelpers.GetUserId(User); var key = ApiHelpers.NormalizeOptional(idempotencyKey); var requestHash = key is null ? null : ApiHelpers.ComputeRequestHash(request); if (key is not null && requestHash is not null) { var (existingTask, error) = await ApiHelpers.TryGetIdempotentTaskAsync(_db, userId, key, requestHash); if (error is not null) { return Conflict(error); } if (existingTask is not null) { return Ok(ApiHelpers.MapTaskV2(existingTask)); } } var now = DateTimeOffset.UtcNow; var task = new TaskItem { Id = Guid.NewGuid(), UserId = userId, Title = title, Description = request.Description?.Trim() ?? string.Empty, Status = request.Status, DueDate = request.DueDate, Assignee = ApiHelpers.NormalizeOptional(request.Assignee), Priority = request.Priority, CreatedAt = now, UpdatedAt = now }; _db.Tasks.Add(task); if (key is not null && requestHash is not null) { _db.IdempotencyKeys.Add(new IdempotencyKey { Id = Guid.NewGuid(), UserId = userId, Key = key, RequestHash = requestHash, TaskId = task.Id, CreatedAt = now }); } await _db.SaveChangesAsync(); return Created($"/api/v2/tasks/{task.Id}", ApiHelpers.MapTaskV2(task)); } [HttpPut("{id:guid}")] [ProducesResponseType(typeof(TaskResponseV2), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task<ActionResult<TaskResponseV2>> UpdateTask(Guid id, [FromBody] TaskUpdateRequestV2 request) { var title = ApiHelpers.NormalizeRequired(request.Title); if (title is null) { return BadRequest("Title is required."); } if (request.Priority is < 1 or > 5) { return BadRequest("Priority must be between 1 and 5."); } var userId = ApiHelpers.GetUserId(User); var task = await _db.Tasks.FirstOrDefaultAsync(item => item.Id == id && item.UserId == userId); if (task is null) { return NotFound(); } task.Title = title; task.Description = request.Description?.Trim() ?? string.Empty; task.Status = request.Status; task.DueDate = request.DueDate; task.Assignee = ApiHelpers.NormalizeOptional(request.Assignee); task.Priority = request.Priority; task.UpdatedAt = DateTimeOffset.UtcNow; await _db.SaveChangesAsync(); return Ok(ApiHelpers.MapTaskV2(task)); } [HttpDelete("{id:guid}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task<IActionResult> DeleteTask(Guid id) { var userId = ApiHelpers.GetUserId(User); var task = await _db.Tasks.FirstOrDefaultAsync(item => item.Id == id && item.UserId == userId); if (task is null) { return NotFound(); } _db.Tasks.Remove(task); await _db.SaveChangesAsync(); return NoContent(); } }