using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using UniVerse.Application.DTOs.Common; using UniVerse.Application.DTOs.Users; using UniVerse.Application.Interfaces; using UniVerse.Domain.Enums; using System.Security.Claims; namespace UniVerse.Api.Controllers; /// Управление пользователями, профилями и геймификацией. [ApiController] [Route("api/v1/users")] [Authorize] [Produces("application/json")] public class UsersController : ControllerBase { private readonly IUserService _users; private readonly IReviewService _reviews; private readonly IGamificationService _gamification; public UsersController(IUserService users, IReviewService reviews, IGamificationService gamification) { _users = users; _reviews = reviews; _gamification = gamification; } private int CurrentUserId => int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub") ?? "0"); /// Получить профиль пользователя по ID. /// ID пользователя. /// Данные пользователя. /// Требуется аутентификация. /// Пользователь не найден. [HttpGet("{id:int}")] [ProducesResponseType(typeof(UserDto), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> Get(int id) => Ok(await _users.GetByIdAsync(id)); /// Обновить профиль пользователя (displayName, avatarUrl). /// Разрешено только самому пользователю или Admin. /// ID пользователя. /// Обновляемые поля профиля. /// Обновлённые данные пользователя. /// Требуется аутентификация. /// Нет прав — только владелец профиля или Admin. /// Пользователь не найден. [HttpPut("{id:int}")] [ProducesResponseType(typeof(UserDto), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> Update(int id, [FromBody] UpdateUserRequest req) { if (CurrentUserId != id && !User.IsInRole("Admin")) return Forbid(); return Ok(await _users.UpdateProfileAsync(id, req)); } /// Получить статистику пользователя (XP, монеты, уровень, посещения). /// ID пользователя. /// Статистика пользователя. /// Требуется аутентификация. /// Пользователь не найден. [HttpGet("{id:int}/stats")] [ProducesResponseType(typeof(UserStatsDto), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> Stats(int id) => Ok(await _users.GetStatsAsync(id)); /// Получить список записей пользователя на лекции. /// Разрешено только самому пользователю или Admin. /// ID пользователя. /// Параметры пагинации. /// Список записей (пагинированный). /// Требуется аутентификация. /// Нет прав — только владелец или Admin. [HttpGet("{id:int}/enrollments")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task Enrollments(int id, [FromQuery] PaginationRequest pagination) { if (CurrentUserId != id && !User.IsInRole("Admin")) return Forbid(); // Delegate to lecture service would be more proper, but returning reviews for now return Ok(); } /// Получить отзывы пользователя. /// ID пользователя. /// Параметры пагинации. /// Список отзывов (пагинированный). /// Требуется аутентификация. [HttpGet("{id:int}/reviews")] [ProducesResponseType(typeof(PagedResult), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task Reviews(int id, [FromQuery] PaginationRequest pagination) => Ok(await _reviews.GetByUserAsync(id, pagination)); /// Получить достижения пользователя. /// ID пользователя. /// Список полученных достижений. /// Требуется аутентификация. [HttpGet("{id:int}/achievements")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task Achievements(int id) => Ok(await _gamification.GetUserAchievementsAsync(id)); /// Получить историю транзакций монет пользователя. /// Разрешено только самому пользователю или Admin. /// ID пользователя. /// Параметры пагинации. /// История транзакций (пагинированная). /// Требуется аутентификация. /// Нет прав — только владелец или Admin. [HttpGet("{id:int}/transactions")] [ProducesResponseType(typeof(PagedResult), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task Transactions(int id, [FromQuery] PaginationRequest pagination) { if (CurrentUserId != id && !User.IsInRole("Admin")) return Forbid(); return Ok(await _gamification.GetTransactionsAsync(id, pagination)); } /// Получить список всех пользователей с фильтрацией и пагинацией. /// Только Admin. /// Параметры фильтрации (поиск, роль, активность) и пагинации. /// Список пользователей (пагинированный). /// Требуется аутентификация. /// Требуется роль Admin. [Authorize(Roles = "Admin")] [HttpGet] [ProducesResponseType(typeof(PagedResult), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task GetAll([FromQuery] UserFilterRequest filter) => Ok(await _users.GetAllAsync(filter)); /// Изменить роль пользователя. /// Только Admin. Доступные роли: Student, Teacher, Admin. /// ID пользователя. /// Новая роль. /// Роль успешно изменена. /// Требуется аутентификация. /// Требуется роль Admin. /// Пользователь не найден. [Authorize(Roles = "Admin")] [HttpPatch("{id:int}/role")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task SetRole(int id, [FromBody] UserRole role) { await _users.SetRoleAsync(id, role); return NoContent(); } /// Активировать или деактивировать аккаунт пользователя. /// Только Admin. Деактивированный пользователь не может войти в систему. /// ID пользователя. /// true — активировать, false — деактивировать. /// Статус успешно изменён. /// Требуется аутентификация. /// Требуется роль Admin. /// Пользователь не найден. [Authorize(Roles = "Admin")] [HttpPatch("{id:int}/active")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task SetActive(int id, [FromBody] bool isActive) { await _users.SetActiveAsync(id, isActive); return NoContent(); } }