// EN: Admin controller for shop management (Backoffice). // VI: Controller Admin cho quản lý shop (Backoffice). using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using MerchantService.API.Application.Queries.Admin; using MerchantService.Domain.AggregatesModel.ShopAggregate; using MerchantService.Domain.Exceptions; namespace MerchantService.API.Controllers.Admin; /// /// EN: Admin controller for shop management operations (Backoffice). /// VI: Controller Admin cho các thao tác quản lý shop (Backoffice). /// [ApiController] [Route("api/v1/admin/shops")] [Authorize(Roles = "Admin,SuperAdmin")] public class AdminShopsController : ControllerBase { private readonly IMediator _mediator; private readonly IShopRepository _shopRepository; private readonly ILogger _logger; public AdminShopsController( IMediator mediator, IShopRepository shopRepository, ILogger logger) { _mediator = mediator; _shopRepository = shopRepository; _logger = logger; } /// /// EN: Get all shops with pagination. /// VI: Lấy tất cả shops với phân trang. /// [HttpGet] [ProducesResponseType(typeof(AdminShopListResultDto), StatusCodes.Status200OK)] public async Task GetAllShops( [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? status = null, [FromQuery] Guid? merchantId = null, [FromQuery] string? search = null) { var query = new GetAllShopsQuery(page, pageSize, status, merchantId, search); var result = await _mediator.Send(query); return Ok(result); } /// /// EN: Get shop details by ID. /// VI: Lấy chi tiết shop theo ID. /// [HttpGet("{shopId:guid}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetShopById(Guid shopId) { var shop = await _shopRepository.GetByIdAsync(shopId); if (shop == null) return NotFound(new { message = "Shop not found" }); return Ok(new { shop.Id, shop.MerchantId, shop.Name, shop.Slug, Status = shop.Status.Name, Category = shop.Category?.Name, shop.Description, shop.CreatedAt, shop.UpdatedAt }); } /// /// EN: Suspend a shop. /// VI: Tạm ngưng shop. /// [HttpPost("{shopId:guid}/suspend")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task SuspendShop(Guid shopId, [FromBody] AdminActionRequest request) { try { var shop = await _shopRepository.GetByIdAsync(shopId) ?? throw new DomainException("Shop not found"); shop.SetInactive(); _shopRepository.Update(shop); await _shopRepository.UnitOfWork.SaveEntitiesAsync(); _logger.LogInformation("Shop {ShopId} suspended by Admin {AdminId}. Reason: {Reason}", shopId, GetAdminId(), request.Reason); return Ok(new { message = "Shop suspended successfully", shopId, status = shop.Status.Name }); } catch (DomainException ex) { if (ex.Message.Contains("not found")) return NotFound(new { message = ex.Message }); return BadRequest(new { message = ex.Message }); } } /// /// EN: Reactivate a suspended shop. /// VI: Kích hoạt lại shop bị tạm ngưng. /// [HttpPost("{shopId:guid}/reactivate")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task ReactivateShop(Guid shopId) { try { var shop = await _shopRepository.GetByIdAsync(shopId) ?? throw new DomainException("Shop not found"); shop.Publish(); _shopRepository.Update(shop); await _shopRepository.UnitOfWork.SaveEntitiesAsync(); _logger.LogInformation("Shop {ShopId} reactivated by Admin {AdminId}", shopId, GetAdminId()); return Ok(new { message = "Shop reactivated successfully", shopId, status = shop.Status.Name }); } catch (DomainException ex) { if (ex.Message.Contains("not found")) return NotFound(new { message = ex.Message }); return BadRequest(new { message = ex.Message }); } } /// /// EN: Close a shop permanently. /// VI: Đóng cửa shop vĩnh viễn. /// [HttpPost("{shopId:guid}/close")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task CloseShop(Guid shopId, [FromBody] AdminActionRequest request) { try { var shop = await _shopRepository.GetByIdAsync(shopId) ?? throw new DomainException("Shop not found"); shop.Close(); _shopRepository.Update(shop); await _shopRepository.UnitOfWork.SaveEntitiesAsync(); _logger.LogWarning("Shop {ShopId} CLOSED by Admin {AdminId}. Reason: {Reason}", shopId, GetAdminId(), request.Reason); return Ok(new { message = "Shop closed successfully", shopId, status = shop.Status.Name }); } catch (DomainException ex) { if (ex.Message.Contains("not found")) return NotFound(new { message = ex.Message }); return BadRequest(new { message = ex.Message }); } } private Guid GetAdminId() { var userIdClaim = User.FindFirst("sub")?.Value ?? User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value; return Guid.TryParse(userIdClaim, out var userId) ? userId : Guid.Empty; } }