/
ivanstrike
/
tasker
Обзор
Документация
Войти
/
ivanstrike
/
tasker
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Controllers/V2/TasksController.cs
378 строк
14 KB
ivanstrike
Лабораторная работа 2
08 ноя 2025, 11:16
08 ноя 2025, 11:16
ec39293
Код
Авторство
О чём код?
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using TaskerApi.Data; using TaskerApi.DTOs; using TaskerApi.Models; using TaskerApi.Helpers; using TaskStatus = TaskerApi.Models.TaskStatus; namespace TaskerApi.Controllers.V2; /// <summary> /// Контроллер для управления задачами (API v2) - расширенная версия с тегами и оценкой времени /// </summary> [ApiController] [ApiVersion("2.0")] [Route("api/v{version:apiVersion}/[controller]")] [Authorize] public class TasksController : ControllerBase { private readonly ApplicationDbContext _context; private readonly ILogger<TasksController> _logger; public TasksController(ApplicationDbContext context, ILogger<TasksController> logger) { _context = context; _logger = logger; } /// <summary> /// Получить все задачи текущего пользователя с пагинацией (V2 - включает EstimatedHours и Tags) /// </summary> /// <param name="include">Список полей для включения в ответ (например: id,title,status,tags)</param> [HttpGet] [ProducesResponseType(typeof(PaginatedResult<TaskResponseV2Dto>), StatusCodes.Status200OK)] public async Task<ActionResult> GetTasks( [FromQuery] TaskStatus? status = null, [FromQuery] Guid? projectId = null, [FromQuery] TaskPriority? priority = null, [FromQuery] string? tag = null, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? include = null) { var userId = GetCurrentUserId(); var query = _context.Tasks .Include(t => t.AssignedTo) .Include(t => t.Project) .Where(t => t.AssignedToId == userId); if (status.HasValue) { query = query.Where(t => t.Status == status.Value); } if (projectId.HasValue) { query = query.Where(t => t.ProjectId == projectId.Value); } if (priority.HasValue) { query = query.Where(t => t.Priority == priority.Value); } // Новая функция V2: фильтрация по тегам if (!string.IsNullOrEmpty(tag)) { query = query.Where(t => t.Tags != null && t.Tags.Contains(tag)); } var totalCount = await query.CountAsync(); var pagination = new PaginationParams { Page = page, PageSize = pageSize }; var tasks = await query .OrderByDescending(t => t.CreatedAt) .Skip(pagination.Skip) .Take(pagination.PageSize) .Select(t => new TaskResponseV2Dto { Id = t.Id, Title = t.Title, Description = t.Description, Status = t.Status, Priority = t.Priority, ProjectId = t.ProjectId, ProjectName = t.Project != null ? t.Project.Name : null, AssignedToId = t.AssignedToId, AssignedToUsername = t.AssignedTo.Username, DueDate = t.DueDate, CreatedAt = t.CreatedAt, UpdatedAt = t.UpdatedAt, CompletedAt = t.CompletedAt, EstimatedHours = t.EstimatedHours, Tags = t.Tags != null ? t.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList() : null }) .ToListAsync(); var result = new PaginatedResult<TaskResponseV2Dto> { Items = tasks, Page = pagination.Page, PageSize = pagination.PageSize, TotalCount = totalCount }; Response.Headers.Append("X-Pagination-Page", result.Page.ToString()); Response.Headers.Append("X-Pagination-PageSize", result.PageSize.ToString()); Response.Headers.Append("X-Pagination-TotalCount", result.TotalCount.ToString()); Response.Headers.Append("X-Pagination-TotalPages", result.TotalPages.ToString()); if (!string.IsNullOrWhiteSpace(include)) { var filteredResult = FieldSelector.SelectFieldsFromPaginated(result, include); return Ok(filteredResult); } return Ok(result); } /// <summary> /// Получить задачу по ID (V2) /// </summary> /// <param name="include">Список полей для включения в ответ (например: id,title,status,tags)</param> [HttpGet("{id}")] [ProducesResponseType(typeof(TaskResponseV2Dto), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task<ActionResult> GetTask(Guid id, [FromQuery] string? include = null) { var userId = GetCurrentUserId(); var task = await _context.Tasks .Include(t => t.AssignedTo) .Include(t => t.Project) .FirstOrDefaultAsync(t => t.Id == id && t.AssignedToId == userId); if (task == null) { return NotFound(new { message = "Task not found" }); } var response = new TaskResponseV2Dto { Id = task.Id, Title = task.Title, Description = task.Description, Status = task.Status, Priority = task.Priority, ProjectId = task.ProjectId, ProjectName = task.Project?.Name, AssignedToId = task.AssignedToId, AssignedToUsername = task.AssignedTo.Username, DueDate = task.DueDate, CreatedAt = task.CreatedAt, UpdatedAt = task.UpdatedAt, CompletedAt = task.CompletedAt, EstimatedHours = task.EstimatedHours, Tags = task.Tags != null ? task.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList() : null }; if (!string.IsNullOrWhiteSpace(include)) { var filteredResult = FieldSelector.SelectFields(response, include); return Ok(filteredResult); } return Ok(response); } /// <summary> /// Создать новую задачу (V2 - с поддержкой EstimatedHours и Tags) /// </summary> [HttpPost] [ProducesResponseType(typeof(TaskResponseV2Dto), StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task<ActionResult<TaskResponseV2Dto>> CreateTask([FromBody] CreateTaskV2Dto dto) { var userExists = await _context.Users.AnyAsync(u => u.Id == dto.AssignedToId); if (!userExists) { return BadRequest(new { message = "Assigned user not found" }); } if (dto.ProjectId.HasValue) { var projectExists = await _context.Projects.AnyAsync(p => p.Id == dto.ProjectId.Value); if (!projectExists) { return BadRequest(new { message = "Project not found" }); } } var task = new TaskItem { Title = dto.Title, Description = dto.Description, Status = dto.Status, Priority = dto.Priority, ProjectId = dto.ProjectId, AssignedToId = dto.AssignedToId, DueDate = dto.DueDate, EstimatedHours = dto.EstimatedHours, Tags = dto.Tags != null ? string.Join(",", dto.Tags) : null, CreatedAt = DateTime.UtcNow }; _context.Tasks.Add(task); await _context.SaveChangesAsync(); await _context.Entry(task).Reference(t => t.AssignedTo).LoadAsync(); await _context.Entry(task).Reference(t => t.Project).LoadAsync(); var response = new TaskResponseV2Dto { Id = task.Id, Title = task.Title, Description = task.Description, Status = task.Status, Priority = task.Priority, ProjectId = task.ProjectId, ProjectName = task.Project?.Name, AssignedToId = task.AssignedToId, AssignedToUsername = task.AssignedTo.Username, DueDate = task.DueDate, CreatedAt = task.CreatedAt, UpdatedAt = task.UpdatedAt, CompletedAt = task.CompletedAt, EstimatedHours = task.EstimatedHours, Tags = dto.Tags }; _logger.LogInformation("Task created (V2): {TaskId} with estimated hours: {Hours}", task.Id, task.EstimatedHours); return CreatedAtAction(nameof(GetTask), new { id = task.Id, version = "2.0" }, response); } /// <summary> /// Обновить задачу (V2) /// </summary> [HttpPut("{id}")] [ProducesResponseType(typeof(TaskResponseV2Dto), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task<ActionResult<TaskResponseV2Dto>> UpdateTask(Guid id, [FromBody] UpdateTaskV2Dto dto) { var task = await _context.Tasks .Include(t => t.AssignedTo) .Include(t => t.Project) .FirstOrDefaultAsync(t => t.Id == id); if (task == null) { return NotFound(new { message = "Task not found" }); } if (dto.Title != null) task.Title = dto.Title; if (dto.Description != null) task.Description = dto.Description; if (dto.Status.HasValue) { task.Status = dto.Status.Value; if (dto.Status.Value == TaskStatus.Completed && task.CompletedAt == null) { task.CompletedAt = DateTime.UtcNow; } } if (dto.Priority.HasValue) task.Priority = dto.Priority.Value; if (dto.ProjectId.HasValue) task.ProjectId = dto.ProjectId.Value; if (dto.AssignedToId.HasValue) task.AssignedToId = dto.AssignedToId.Value; if (dto.DueDate.HasValue) task.DueDate = dto.DueDate.Value; // Новые поля V2 if (dto.EstimatedHours.HasValue) task.EstimatedHours = dto.EstimatedHours.Value; if (dto.Tags != null) task.Tags = string.Join(",", dto.Tags); task.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); await _context.Entry(task).Reference(t => t.AssignedTo).LoadAsync(); await _context.Entry(task).Reference(t => t.Project).LoadAsync(); var response = new TaskResponseV2Dto { Id = task.Id, Title = task.Title, Description = task.Description, Status = task.Status, Priority = task.Priority, ProjectId = task.ProjectId, ProjectName = task.Project?.Name, AssignedToId = task.AssignedToId, AssignedToUsername = task.AssignedTo.Username, DueDate = task.DueDate, CreatedAt = task.CreatedAt, UpdatedAt = task.UpdatedAt, CompletedAt = task.CompletedAt, EstimatedHours = task.EstimatedHours, Tags = task.Tags != null ? task.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList() : null }; _logger.LogInformation("Task updated (V2): {TaskId}", task.Id); return Ok(response); } /// <summary> /// Удалить задачу (V2) /// </summary> [HttpDelete("{id}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task<IActionResult> DeleteTask(int id) { var task = await _context.Tasks.FindAsync(id); if (task == null) { return NotFound(new { message = "Task not found" }); } _context.Tasks.Remove(task); await _context.SaveChangesAsync(); _logger.LogInformation("Task deleted (V2): {TaskId}", task.Id); return NoContent(); } /// <summary> /// Получить статистику по задачам (новая функция V2) /// </summary> [HttpGet("statistics")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task<ActionResult> GetStatistics() { var userId = GetCurrentUserId(); var tasks = await _context.Tasks .Where(t => t.AssignedToId == userId) .ToListAsync(); var statistics = new { totalTasks = tasks.Count, openTasks = tasks.Count(t => t.Status == TaskStatus.Open), inProgressTasks = tasks.Count(t => t.Status == TaskStatus.InProgress), completedTasks = tasks.Count(t => t.Status == TaskStatus.Completed), totalEstimatedHours = tasks.Where(t => t.EstimatedHours.HasValue).Sum(t => t.EstimatedHours), averageEstimatedHours = tasks.Where(t => t.EstimatedHours.HasValue).Any() ? tasks.Where(t => t.EstimatedHours.HasValue).Average(t => t.EstimatedHours) : 0, tasksByPriority = new { low = tasks.Count(t => t.Priority == TaskPriority.Low), medium = tasks.Count(t => t.Priority == TaskPriority.Medium), high = tasks.Count(t => t.Priority == TaskPriority.High), critical = tasks.Count(t => t.Priority == TaskPriority.Critical) }, popularTags = tasks .Where(t => !string.IsNullOrEmpty(t.Tags)) .SelectMany(t => t.Tags!.Split(',', StringSplitOptions.RemoveEmptyEntries)) .GroupBy(tag => tag.Trim()) .OrderByDescending(g => g.Count()) .Take(10) .Select(g => new { tag = g.Key, count = g.Count() }) .ToList() }; return Ok(statistics); } private Guid GetCurrentUserId() { return Guid.Parse(User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value ?? "0"); } }