Исправления сваггер

This commit is contained in:
Халимов Рустам
2026-02-11 13:24:56 +03:00
parent 3b2c0029a1
commit b7ba835689
9 changed files with 210 additions and 47 deletions
+94 -17
View File
@@ -6,24 +6,101 @@ namespace Nashel.Modules.Catalog.Application.Common;
/// <summary> /// <summary>
/// DTO категории. /// DTO категории.
/// </summary> /// </summary>
public record CategoryDto( public record CategoryDto
Guid Id, {
string Title, /// <summary>
string Slug, /// Уникальный идентификатор категории.
Guid? ParentId, /// </summary>
JsonDocument? AttributeSchema public Guid Id { get; init; }
);
/// <summary>
/// Название категории.
/// </summary>
public string Title { get; init; } = default!;
/// <summary>
/// URL-совместимый идентификатор (slug).
/// </summary>
public string Slug { get; init; } = default!;
/// <summary>
/// Идентификатор родительской категории (null, если корневая).
/// </summary>
public Guid? ParentId { get; init; }
/// <summary>
/// JSON-схема характеристик, специфичных для категории.
/// </summary>
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() { }
}
/// <summary> /// <summary>
/// DTO услуги/оффера. /// DTO услуги/оффера.
/// </summary> /// </summary>
public record OfferDto( public record OfferDto
Guid Id, {
Guid OwnerId, /// <summary>
Guid CategoryId, /// Уникальный идентификатор услуги.
string Title, /// </summary>
string Description, public Guid Id { get; init; }
Price Price,
JsonDocument? Attributes, /// <summary>
bool IsActive /// Идентификатор владельца (исполнителя).
); /// </summary>
public Guid OwnerId { get; init; }
/// <summary>
/// Идентификатор категории.
/// </summary>
public Guid CategoryId { get; init; }
/// <summary>
/// Заголовок услуги.
/// </summary>
public string Title { get; init; } = default!;
/// <summary>
/// Подробное описание услуги.
/// </summary>
public string Description { get; init; } = default!;
/// <summary>
/// Цена услуги.
/// </summary>
public Price Price { get; init; } = default!;
/// <summary>
/// JSON-объект с характеристиками услуги.
/// </summary>
public JsonDocument? Attributes { get; init; }
/// <summary>
/// Флаг активности услуги.
/// </summary>
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() { }
}
@@ -25,7 +25,7 @@ public static class CatalogEndpoints
return Results.Ok(id); return Results.Ok(id);
}) })
.WithName("CreateCategory") .WithName("CreateCategory")
.WithSummary("Создать категорию (Admin)"); .WithOpenApi(operation => new(operation) { Summary = "Создать категорию (Admin)", Description = "Создает новую категорию. Требуются права администратора." });
catalogGroup.MapGet("/categories", async (ISender sender) => catalogGroup.MapGet("/categories", async (ISender sender) =>
{ {
@@ -33,7 +33,7 @@ public static class CatalogEndpoints
return Results.Ok(result); return Results.Ok(result);
}) })
.WithName("GetCategories") .WithName("GetCategories")
.WithSummary("Получить все категории (плоский список с ParentId)"); .WithOpenApi(operation => new(operation) { Summary = "Получить все категории", Description = "Возвращает плоский список категорий с указанием ParentId для иерархии." });
// --- Услуги (Offers) --- // --- Услуги (Offers) ---
@@ -43,7 +43,7 @@ public static class CatalogEndpoints
return Results.Ok(id); return Results.Ok(id);
}) })
.WithName("CreateOffer") .WithName("CreateOffer")
.WithSummary("Создать оффер/услугу"); .WithOpenApi(operation => new(operation) { Summary = "Создать оффер/услугу", Description = "Создает новое предложение услуги в указанной категории." });
catalogGroup.MapGet("/offers/{id:guid}", async (Guid id, ISender sender) => 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(); return result is not null ? Results.Ok(result) : Results.NotFound();
}) })
.WithName("GetOfferById") .WithName("GetOfferById")
.WithSummary("Получить детали услуги по ID"); .WithOpenApi(operation => new(operation) { Summary = "Получить детали услуги по ID", Description = "Возвращает полную информацию об услуге." });
} }
} }
@@ -13,4 +13,7 @@
<ItemGroup> <ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
</ItemGroup>
</Project> </Project>
+6 -2
View File
@@ -24,7 +24,9 @@ public static class GeoEndpoints
var command = new UpdateGeoCommand(request.PerformerId, request.Latitude, request.Longitude, request.Status); var command = new UpdateGeoCommand(request.PerformerId, request.Latitude, request.Longitude, request.Status);
await sender.Send(command, ct); await sender.Send(command, ct);
return Results.Ok(); return Results.Ok();
}); })
.WithName("UpdateGeoStatus")
.WithOpenApi(operation => new(operation) { Summary = "Обновить геопозицию и статус", Description = "Обновляет координаты и статус исполнителя (Available, Busy, DayOff)." });
// GET /api/geo/nearby // GET /api/geo/nearby
group.MapGet("nearby", async ( group.MapGet("nearby", async (
@@ -36,7 +38,9 @@ public static class GeoEndpoints
{ {
var result = await sender.Send(new SearchNearbyQuery(lat, lon, radius), ct); var result = await sender.Send(new SearchNearbyQuery(lat, lon, radius), ct);
return Results.Ok(result); return Results.Ok(result);
}); })
.WithName("SearchNearby")
.WithOpenApi(operation => new(operation) { Summary = "Поиск исполнителей рядом", Description = "Возвращает список ID исполнителей в радиусе поиска." });
} }
/// <summary> /// <summary>
@@ -14,4 +14,8 @@
<ItemGroup> <ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
</ItemGroup>
</Project> </Project>
@@ -16,13 +16,17 @@ public static class IdentityEndpoints
{ {
var result = await sender.Send(command); var result = await sender.Send(command);
return Results.Ok(result); return Results.Ok(result);
}); })
.WithName("Register")
.WithOpenApi(operation => new(operation) { Summary = "Регистрация пользователя", Description = "Регистрирует нового пользователя по номеру телефона и паролю." });
authGroup.MapPost("/login", async (LoginCommand command, ISender sender) => authGroup.MapPost("/login", async (LoginCommand command, ISender sender) =>
{ {
var token = await sender.Send(command); 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(); var profileGroup = app.MapGroup("/api/profile").WithTags("Profile").RequireAuthorization();
@@ -30,6 +34,14 @@ public static class IdentityEndpoints
{ {
await sender.Send(new BecomePerformerCommand()); await sender.Send(new BecomePerformerCommand());
return Results.Ok(); return Results.Ok();
}); })
.WithName("BecomePerformer")
.WithOpenApi(operation => new(operation) { Summary = "Стать исполнителем", Description = "Присваивает текущему пользователю роль исполнителя." });
} }
} }
/// <summary>
/// Ответ на успешный вход в систему.
/// </summary>
/// <param name="Token">JWT токен доступа.</param>
public record LoginResponse(string Token);
@@ -13,4 +13,7 @@
<ItemGroup> <ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
</ItemGroup>
</Project> </Project>
@@ -14,7 +14,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="MediatR" Version="14.0.0" /> <PackageReference Include="MediatR" Version="14.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -44,7 +44,7 @@ public static class OrderEndpoints
return Results.Ok(); return Results.Ok();
}) })
.WithName("AddOrderApplication") .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) => group.MapPost("/{id:guid}/select-performer", async (Guid id, SelectPerformerRequest request, ISender sender) =>
{ {
@@ -53,35 +53,95 @@ public static class OrderEndpoints
return Results.Ok(); return Results.Ok();
}) })
.WithName("SelectPerformer") .WithName("SelectPerformer")
.WithOpenApi(operation => new(operation) { Summary = "Выбор исполнителя для заказа." }); .WithOpenApi(operation => new(operation) { Summary = "Выбор исполнителя для заказа.", Description = "Позволяет заказчику выбрать конкретного исполнителя." });
} }
} }
/// <summary> /// <summary>
/// Запрос на создание заказа. /// Запрос на создание заказа.
/// </summary> /// </summary>
public record CreateOrderRequest( public record CreateOrderRequest
Guid CustomerId, {
Guid ServiceId, /// <summary>
OrderType Type, /// Идентификатор заказчика.
string Address, /// </summary>
double Latitude, public Guid CustomerId { get; init; }
double Longitude,
DateTime? Deadline, /// <summary>
Guid? PerformerId /// Идентификатор услуги.
); /// </summary>
public Guid ServiceId { get; init; }
/// <summary>
/// Тип заказа (прямой, публичный).
/// </summary>
public OrderType Type { get; init; }
/// <summary>
/// Текстовый адрес выполнения заказа.
/// </summary>
public string Address { get; init; } = default!;
/// <summary>
/// Географическая широта места выполнения.
/// </summary>
public double Latitude { get; init; }
/// <summary>
/// Географическая долгота места выполнения.
/// </summary>
public double Longitude { get; init; }
/// <summary>
/// Крайний срок выполнения заказа (опционально).
/// </summary>
public DateTime? Deadline { get; init; }
/// <summary>
/// Идентификатор конкретного исполнителя (если прямой заказ).
/// </summary>
public Guid? PerformerId { get; init; }
public CreateOrderRequest() { }
}
/// <summary> /// <summary>
/// Запрос на добавление отклика. /// Запрос на добавление отклика.
/// </summary> /// </summary>
public record AddApplicationRequest( public record AddApplicationRequest
Guid PerformerId, {
decimal Amount, /// <summary>
string Currency, /// Идентификатор исполнителя.
string Comment /// </summary>
); public Guid PerformerId { get; init; }
/// <summary>
/// Сумма предложения.
/// </summary>
public decimal Amount { get; init; }
/// <summary>
/// Валюта предложения (например, RUB).
/// </summary>
public string Currency { get; init; } = "RUB";
/// <summary>
/// Комментарий к отклику.
/// </summary>
public string Comment { get; init; } = default!;
public AddApplicationRequest() { }
}
/// <summary> /// <summary>
/// Запрос на выбор исполнителя. /// Запрос на выбор исполнителя.
/// </summary> /// </summary>
public record SelectPerformerRequest(Guid PerformerId); public record SelectPerformerRequest
{
/// <summary>
/// Идентификатор выбранного исполнителя.
/// </summary>
public Guid PerformerId { get; init; }
public SelectPerformerRequest() { }
}