186 lines
6.3 KiB
C#
186 lines
6.3 KiB
C#
// 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;
|
|
|
|
/// <summary>
|
|
/// EN: Admin controller for shop management operations (Backoffice).
|
|
/// VI: Controller Admin cho các thao tác quản lý shop (Backoffice).
|
|
/// </summary>
|
|
[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<AdminShopsController> _logger;
|
|
|
|
public AdminShopsController(
|
|
IMediator mediator,
|
|
IShopRepository shopRepository,
|
|
ILogger<AdminShopsController> logger)
|
|
{
|
|
_mediator = mediator;
|
|
_shopRepository = shopRepository;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Get all shops with pagination.
|
|
/// VI: Lấy tất cả shops với phân trang.
|
|
/// </summary>
|
|
[HttpGet]
|
|
[ProducesResponseType(typeof(AdminShopListResultDto), StatusCodes.Status200OK)]
|
|
public async Task<IActionResult> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Get shop details by ID.
|
|
/// VI: Lấy chi tiết shop theo ID.
|
|
/// </summary>
|
|
[HttpGet("{shopId:guid}")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public async Task<IActionResult> 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
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Suspend a shop.
|
|
/// VI: Tạm ngưng shop.
|
|
/// </summary>
|
|
[HttpPost("{shopId:guid}/suspend")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public async Task<IActionResult> 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 });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Reactivate a suspended shop.
|
|
/// VI: Kích hoạt lại shop bị tạm ngưng.
|
|
/// </summary>
|
|
[HttpPost("{shopId:guid}/reactivate")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public async Task<IActionResult> 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 });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// EN: Close a shop permanently.
|
|
/// VI: Đóng cửa shop vĩnh viễn.
|
|
/// </summary>
|
|
[HttpPost("{shopId:guid}/close")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public async Task<IActionResult> 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;
|
|
}
|
|
}
|