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() { }
+}