using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Nashel.Modules.Catalog.Application.Commands;
using Nashel.Modules.Catalog.Application.Queries;
namespace Nashel.Modules.Catalog.Presentation.Endpoints;
///
/// API Каталога (Категории и Услуги).
///
public static class CatalogEndpoints
{
public static void MapCatalogEndpoints(this IEndpointRouteBuilder app)
{
var catalogGroup = app.MapGroup("/api/catalog").WithTags("Catalog");
// --- Категории ---
catalogGroup.MapPost("/categories", async ([FromBody] CreateCategoryCommand command, ISender sender) =>
{
var result = await sender.Send(command);
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error);
})
.WithName("CreateCategory")
.WithOpenApi(operation => new(operation) { Summary = "Создать категорию (Admin)", Description = "Создает новую категорию. Требуются права администратора." });
catalogGroup.MapGet("/categories", async (ISender sender) =>
{
var result = await sender.Send(new GetCategoriesQuery());
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error);
})
.WithName("GetCategories")
.WithOpenApi(operation => new(operation) { Summary = "Получить все категории", Description = "Возвращает плоский список категорий с указанием ParentId для иерархии." });
// --- Услуги (Offers) ---
var protectedOffersGroup = catalogGroup.MapGroup("").RequireAuthorization();
protectedOffersGroup.MapGet("/offers/my", async (ISender sender) =>
{
var result = await sender.Send(new GetMyOffersQuery());
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error);
})
.WithName("GetMyOffers")
.WithOpenApi(operation => new(operation) { Summary = "Получить мои услуги", Description = "Возвращает список услуг текущего пользователя." });
protectedOffersGroup.MapPost("/offers", async ([FromBody] CreateOfferCommand command, ISender sender) =>
{
var result = await sender.Send(command);
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error);
})
.WithName("CreateOffer")
.WithOpenApi(operation => new(operation) { Summary = "Создать оффер/услугу", Description = "Создает новое предложение услуги в указанной категории." });
catalogGroup.MapGet("/offers/{id:guid}", async (Guid id, ISender sender) =>
{
var result = await sender.Send(new GetOfferByIdQuery(id));
return result.IsSuccess && result.Value is not null ? Results.Ok(result.Value) : Results.NotFound();
})
.WithName("GetOfferById")
.WithOpenApi(operation => new(operation) { Summary = "Получить детали услуги по ID", Description = "Возвращает полную информацию об услуге." });
protectedOffersGroup.MapPut("/offers/{id:guid}", async (Guid id, [FromBody] UpdateOfferPayload payload, ISender sender) =>
{
var command = new UpdateOfferCommand(id, payload.Title, payload.Description, payload.Price.Amount, payload.Price.Type, payload.Attributes, payload.Images);
await sender.Send(command);
return Results.NoContent();
})
.WithName("UpdateOffer")
.WithOpenApi(operation => new(operation) { Summary = "Обновить услугу" });
protectedOffersGroup.MapPatch("/offers/{id:guid}/toggle", async (Guid id, ISender sender) =>
{
var isActive = await sender.Send(new ToggleOfferStatusCommand(id));
return Results.Ok(new { IsActive = isActive });
})
.WithName("ToggleOfferStatus")
.WithOpenApi(operation => new(operation) { Summary = "Приостановить/активировать услугу" });
protectedOffersGroup.MapDelete("/offers/{id:guid}", async (Guid id, ISender sender) =>
{
await sender.Send(new DeleteOfferCommand(id));
return Results.NoContent();
})
.WithName("DeleteOffer")
.WithOpenApi(operation => new(operation) { Summary = "Удалить услугу" });
protectedOffersGroup.MapPost("/offers/image", async (IFormFile file, ISender sender) =>
{
using var ms = new MemoryStream();
await file.CopyToAsync(ms);
var url = await sender.Send(new UploadOfferImageCommand(ms.ToArray(), file.FileName));
return Results.Ok(new { url });
})
.WithName("UploadOfferImage")
.WithOpenApi(operation => new(operation) { Summary = "Загрузить изображение для услуги" })
.DisableAntiforgery();
}
}
public record UpdateOfferPayload(
string Title,
string Description,
PricePayload Price,
Dictionary? Attributes,
List? Images);
public record PricePayload(decimal Amount, int Type);