From b7ba83568973a954ddd6ede673ed5a4df3a29612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A5=D0=B0=D0=BB=D0=B8=D0=BC=D0=BE=D0=B2=20=D0=A0=D1=83?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BC?= Date: Wed, 11 Feb 2026 13:24:56 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=81=D0=B2=D0=B0=D0=B3=D0=B3=D0=B5?= =?UTF-8?q?=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Catalog/Application/Common/Dtos.cs | 111 +++++++++++++++--- .../Endpoints/CatalogEndpoints.cs | 8 +- ...Nashel.Modules.Catalog.Presentation.csproj | 3 + src/Modules/Geo/Presentation/GeoEndpoints.cs | 8 +- .../Nashel.Modules.Geo.Presentation.csproj | 4 + .../Endpoints/IdentityEndpoints.cs | 20 +++- ...ashel.Modules.Identity.Presentation.csproj | 3 + .../Nashel.Modules.Order.Presentation.csproj | 2 +- .../Order/Presentation/OrderEndpoints.cs | 98 +++++++++++++--- 9 files changed, 210 insertions(+), 47 deletions(-) diff --git a/src/Modules/Catalog/Application/Common/Dtos.cs b/src/Modules/Catalog/Application/Common/Dtos.cs index 3e51daf..44bd5cb 100644 --- a/src/Modules/Catalog/Application/Common/Dtos.cs +++ b/src/Modules/Catalog/Application/Common/Dtos.cs @@ -6,24 +6,101 @@ namespace Nashel.Modules.Catalog.Application.Common; /// /// DTO категории. /// -public record CategoryDto( - Guid Id, - string Title, - string Slug, - Guid? ParentId, - JsonDocument? AttributeSchema -); +public record CategoryDto +{ + /// + /// Уникальный идентификатор категории. + /// + public Guid Id { get; init; } + + /// + /// Название категории. + /// + public string Title { get; init; } = default!; + + /// + /// URL-совместимый идентификатор (slug). + /// + public string Slug { get; init; } = default!; + + /// + /// Идентификатор родительской категории (null, если корневая). + /// + public Guid? ParentId { get; init; } + + /// + /// JSON-схема характеристик, специфичных для категории. + /// + public JsonDocument? AttributeSchema { get; init; } + + public CategoryDto(Guid id, string title, string slug, Guid? parentId, JsonDocument? attributeSchema) + { + Id = id; + Title = title; + Slug = slug; + ParentId = parentId; + AttributeSchema = attributeSchema; + } + + public CategoryDto() { } +} /// /// DTO услуги/оффера. /// -public record OfferDto( - Guid Id, - Guid OwnerId, - Guid CategoryId, - string Title, - string Description, - Price Price, - JsonDocument? Attributes, - bool IsActive -); +public record OfferDto +{ + /// + /// Уникальный идентификатор услуги. + /// + public Guid Id { get; init; } + + /// + /// Идентификатор владельца (исполнителя). + /// + public Guid OwnerId { get; init; } + + /// + /// Идентификатор категории. + /// + public Guid CategoryId { get; init; } + + /// + /// Заголовок услуги. + /// + public string Title { get; init; } = default!; + + /// + /// Подробное описание услуги. + /// + public string Description { get; init; } = default!; + + /// + /// Цена услуги. + /// + public Price Price { get; init; } = default!; + + /// + /// JSON-объект с характеристиками услуги. + /// + public JsonDocument? Attributes { get; init; } + + /// + /// Флаг активности услуги. + /// + public bool IsActive { get; init; } + + public OfferDto(Guid id, Guid ownerId, Guid categoryId, string title, string description, Price price, JsonDocument? attributes, bool isActive) + { + Id = id; + OwnerId = ownerId; + CategoryId = categoryId; + Title = title; + Description = description; + Price = price; + Attributes = attributes; + IsActive = isActive; + } + + public OfferDto() { } +} diff --git a/src/Modules/Catalog/Presentation/Endpoints/CatalogEndpoints.cs b/src/Modules/Catalog/Presentation/Endpoints/CatalogEndpoints.cs index a5e76da..fa4cce2 100644 --- a/src/Modules/Catalog/Presentation/Endpoints/CatalogEndpoints.cs +++ b/src/Modules/Catalog/Presentation/Endpoints/CatalogEndpoints.cs @@ -25,7 +25,7 @@ public static class CatalogEndpoints return Results.Ok(id); }) .WithName("CreateCategory") - .WithSummary("Создать категорию (Admin)"); + .WithOpenApi(operation => new(operation) { Summary = "Создать категорию (Admin)", Description = "Создает новую категорию. Требуются права администратора." }); catalogGroup.MapGet("/categories", async (ISender sender) => { @@ -33,7 +33,7 @@ public static class CatalogEndpoints return Results.Ok(result); }) .WithName("GetCategories") - .WithSummary("Получить все категории (плоский список с ParentId)"); + .WithOpenApi(operation => new(operation) { Summary = "Получить все категории", Description = "Возвращает плоский список категорий с указанием ParentId для иерархии." }); // --- Услуги (Offers) --- @@ -43,7 +43,7 @@ public static class CatalogEndpoints return Results.Ok(id); }) .WithName("CreateOffer") - .WithSummary("Создать оффер/услугу"); + .WithOpenApi(operation => new(operation) { Summary = "Создать оффер/услугу", Description = "Создает новое предложение услуги в указанной категории." }); catalogGroup.MapGet("/offers/{id:guid}", async (Guid id, ISender sender) => { @@ -51,6 +51,6 @@ public static class CatalogEndpoints return result is not null ? Results.Ok(result) : Results.NotFound(); }) .WithName("GetOfferById") - .WithSummary("Получить детали услуги по ID"); + .WithOpenApi(operation => new(operation) { Summary = "Получить детали услуги по ID", Description = "Возвращает полную информацию об услуге." }); } } diff --git a/src/Modules/Catalog/Presentation/Nashel.Modules.Catalog.Presentation.csproj b/src/Modules/Catalog/Presentation/Nashel.Modules.Catalog.Presentation.csproj index 7839126..5baaca8 100644 --- a/src/Modules/Catalog/Presentation/Nashel.Modules.Catalog.Presentation.csproj +++ b/src/Modules/Catalog/Presentation/Nashel.Modules.Catalog.Presentation.csproj @@ -13,4 +13,7 @@ + + + \ No newline at end of file diff --git a/src/Modules/Geo/Presentation/GeoEndpoints.cs b/src/Modules/Geo/Presentation/GeoEndpoints.cs index ab75910..379399f 100644 --- a/src/Modules/Geo/Presentation/GeoEndpoints.cs +++ b/src/Modules/Geo/Presentation/GeoEndpoints.cs @@ -24,7 +24,9 @@ public static class GeoEndpoints var command = new UpdateGeoCommand(request.PerformerId, request.Latitude, request.Longitude, request.Status); await sender.Send(command, ct); return Results.Ok(); - }); + }) + .WithName("UpdateGeoStatus") + .WithOpenApi(operation => new(operation) { Summary = "Обновить геопозицию и статус", Description = "Обновляет координаты и статус исполнителя (Available, Busy, DayOff)." }); // GET /api/geo/nearby group.MapGet("nearby", async ( @@ -36,7 +38,9 @@ public static class GeoEndpoints { var result = await sender.Send(new SearchNearbyQuery(lat, lon, radius), ct); return Results.Ok(result); - }); + }) + .WithName("SearchNearby") + .WithOpenApi(operation => new(operation) { Summary = "Поиск исполнителей рядом", Description = "Возвращает список ID исполнителей в радиусе поиска." }); } /// diff --git a/src/Modules/Geo/Presentation/Nashel.Modules.Geo.Presentation.csproj b/src/Modules/Geo/Presentation/Nashel.Modules.Geo.Presentation.csproj index 4174f35..03f0a11 100644 --- a/src/Modules/Geo/Presentation/Nashel.Modules.Geo.Presentation.csproj +++ b/src/Modules/Geo/Presentation/Nashel.Modules.Geo.Presentation.csproj @@ -14,4 +14,8 @@ + + + + \ No newline at end of file diff --git a/src/Modules/Identity/Presentation/Endpoints/IdentityEndpoints.cs b/src/Modules/Identity/Presentation/Endpoints/IdentityEndpoints.cs index 64ff2e5..9fdf7b6 100644 --- a/src/Modules/Identity/Presentation/Endpoints/IdentityEndpoints.cs +++ b/src/Modules/Identity/Presentation/Endpoints/IdentityEndpoints.cs @@ -16,13 +16,17 @@ public static class IdentityEndpoints { var result = await sender.Send(command); return Results.Ok(result); - }); + }) + .WithName("Register") + .WithOpenApi(operation => new(operation) { Summary = "Регистрация пользователя", Description = "Регистрирует нового пользователя по номеру телефона и паролю." }); authGroup.MapPost("/login", async (LoginCommand command, ISender sender) => { var token = await sender.Send(command); - return Results.Ok(new { Token = token }); - }); + return Results.Ok(new LoginResponse(token)); + }) + .WithName("Login") + .WithOpenApi(operation => new(operation) { Summary = "Вход в систему", Description = "Аутентификация пользователя и получение JWT токена." }); var profileGroup = app.MapGroup("/api/profile").WithTags("Profile").RequireAuthorization(); @@ -30,6 +34,14 @@ public static class IdentityEndpoints { await sender.Send(new BecomePerformerCommand()); return Results.Ok(); - }); + }) + .WithName("BecomePerformer") + .WithOpenApi(operation => new(operation) { Summary = "Стать исполнителем", Description = "Присваивает текущему пользователю роль исполнителя." }); } } + +/// +/// Ответ на успешный вход в систему. +/// +/// JWT токен доступа. +public record LoginResponse(string Token); diff --git a/src/Modules/Identity/Presentation/Nashel.Modules.Identity.Presentation.csproj b/src/Modules/Identity/Presentation/Nashel.Modules.Identity.Presentation.csproj index 97e0a68..2fafd91 100644 --- a/src/Modules/Identity/Presentation/Nashel.Modules.Identity.Presentation.csproj +++ b/src/Modules/Identity/Presentation/Nashel.Modules.Identity.Presentation.csproj @@ -13,4 +13,7 @@ + + + \ No newline at end of file diff --git a/src/Modules/Order/Presentation/Nashel.Modules.Order.Presentation.csproj b/src/Modules/Order/Presentation/Nashel.Modules.Order.Presentation.csproj index 164294b..ec786b0 100644 --- a/src/Modules/Order/Presentation/Nashel.Modules.Order.Presentation.csproj +++ b/src/Modules/Order/Presentation/Nashel.Modules.Order.Presentation.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Modules/Order/Presentation/OrderEndpoints.cs b/src/Modules/Order/Presentation/OrderEndpoints.cs index 76b5a86..bd15a5c 100644 --- a/src/Modules/Order/Presentation/OrderEndpoints.cs +++ b/src/Modules/Order/Presentation/OrderEndpoints.cs @@ -44,7 +44,7 @@ public static class OrderEndpoints return Results.Ok(); }) .WithName("AddOrderApplication") - .WithOpenApi(operation => new(operation) { Summary = "Добавление отклика на заказ." }); + .WithOpenApi(operation => new(operation) { Summary = "Добавление отклика на заказ.", Description = "Позволяет исполнителю откликнуться на заказ." }); group.MapPost("/{id:guid}/select-performer", async (Guid id, SelectPerformerRequest request, ISender sender) => { @@ -53,35 +53,95 @@ public static class OrderEndpoints return Results.Ok(); }) .WithName("SelectPerformer") - .WithOpenApi(operation => new(operation) { Summary = "Выбор исполнителя для заказа." }); + .WithOpenApi(operation => new(operation) { Summary = "Выбор исполнителя для заказа.", Description = "Позволяет заказчику выбрать конкретного исполнителя." }); } } /// /// Запрос на создание заказа. /// -public record CreateOrderRequest( - Guid CustomerId, - Guid ServiceId, - OrderType Type, - string Address, - double Latitude, - double Longitude, - DateTime? Deadline, - Guid? PerformerId -); +public record CreateOrderRequest +{ + /// + /// Идентификатор заказчика. + /// + public Guid CustomerId { get; init; } + + /// + /// Идентификатор услуги. + /// + public Guid ServiceId { get; init; } + + /// + /// Тип заказа (прямой, публичный). + /// + public OrderType Type { get; init; } + + /// + /// Текстовый адрес выполнения заказа. + /// + public string Address { get; init; } = default!; + + /// + /// Географическая широта места выполнения. + /// + public double Latitude { get; init; } + + /// + /// Географическая долгота места выполнения. + /// + public double Longitude { get; init; } + + /// + /// Крайний срок выполнения заказа (опционально). + /// + public DateTime? Deadline { get; init; } + + /// + /// Идентификатор конкретного исполнителя (если прямой заказ). + /// + public Guid? PerformerId { get; init; } + + public CreateOrderRequest() { } +} /// /// Запрос на добавление отклика. /// -public record AddApplicationRequest( - Guid PerformerId, - decimal Amount, - string Currency, - string Comment -); +public record AddApplicationRequest +{ + /// + /// Идентификатор исполнителя. + /// + public Guid PerformerId { get; init; } + + /// + /// Сумма предложения. + /// + public decimal Amount { get; init; } + + /// + /// Валюта предложения (например, RUB). + /// + public string Currency { get; init; } = "RUB"; + + /// + /// Комментарий к отклику. + /// + public string Comment { get; init; } = default!; + + public AddApplicationRequest() { } +} /// /// Запрос на выбор исполнителя. /// -public record SelectPerformerRequest(Guid PerformerId); +public record SelectPerformerRequest +{ + /// + /// Идентификатор выбранного исполнителя. + /// + public Guid PerformerId { get; init; } + + public SelectPerformerRequest() { } +}