267 lines
12 KiB
C#
267 lines
12 KiB
C#
using System.Security.Claims;
|
|
using MediatR;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Nashel.Modules.Order.Application.Commands;
|
|
using Nashel.Modules.Order.Application.Queries;
|
|
using Nashel.Modules.Order.Domain.Enums;
|
|
|
|
namespace Nashel.Modules.Order.Presentation;
|
|
|
|
public static class OrderEndpoints
|
|
{
|
|
public static void MapOrderEndpoints(this IEndpointRouteBuilder app)
|
|
{
|
|
// ─── Создание заказа (временно без авторизации) ─────────────────────────────
|
|
// AllowAnonymous не перекрывает RequireAuthorization на группе в Minimal API,
|
|
// поэтому создание вынесено в отдельную группу.
|
|
app.MapPost("/api/orders", async ([FromBody] CreateOrderRequest request, ISender sender) =>
|
|
{
|
|
var command = new CreateOrderCommand(
|
|
request.CustomerId,
|
|
request.CustomerName,
|
|
request.ServiceId,
|
|
request.ServiceTitle,
|
|
request.PriceAmount,
|
|
request.PriceType,
|
|
request.Type,
|
|
request.Address,
|
|
request.Latitude,
|
|
request.Longitude,
|
|
request.Deadline,
|
|
request.PerformerId,
|
|
request.PerformerName
|
|
);
|
|
var orderId = await sender.Send(command);
|
|
return Results.Ok(orderId);
|
|
})
|
|
.WithTags("Заказы")
|
|
.WithName("CreateOrder")
|
|
.WithOpenApi(op => new(op)
|
|
{
|
|
Summary = "Создать заказ",
|
|
Description = "Создаёт прямой заказ (конкретному мастеру) или публичную заявку."
|
|
})
|
|
.AllowAnonymous();
|
|
|
|
var group = app.MapGroup("/api/orders")
|
|
.WithTags("Заказы")
|
|
.RequireAuthorization();
|
|
|
|
// ─── Получить мои заказы (как заказчик) ─────────────────────────────────────
|
|
group.MapGet("/as-customer/{userId:guid}", async (Guid userId, ISender sender) =>
|
|
{
|
|
var result = await sender.Send(new GetMyOrdersAsCustomerQuery(userId));
|
|
return Results.Ok(result);
|
|
})
|
|
.WithName("GetMyOrdersAsCustomer")
|
|
.WithOpenApi(op => new(op)
|
|
{
|
|
Summary = "Мои заказы (роль: Заказчик)",
|
|
Description = "Возвращает список всех заказов, где текущий пользователь является заказчиком, отсортированных по дате создания."
|
|
});
|
|
|
|
// ─── Получить мои заказы (как исполнитель) ───────────────────────────────────
|
|
group.MapGet("/as-performer/{userId:guid}", async (Guid userId, ISender sender) =>
|
|
{
|
|
var result = await sender.Send(new GetMyOrdersAsPerformerQuery(userId));
|
|
return Results.Ok(result);
|
|
})
|
|
.WithName("GetMyOrdersAsPerformer")
|
|
.WithOpenApi(op => new(op)
|
|
{
|
|
Summary = "Мои заказы (роль: Исполнитель)",
|
|
Description = "Возвращает список всех заказов, где текущий пользователь является назначенным исполнителем."
|
|
});
|
|
|
|
// ─── Принять заказ (мастер) ──────────────────────────────────────────────────
|
|
group.MapPost("/{id:guid}/accept", async (Guid id, AcceptOrderRequest request, ISender sender) =>
|
|
{
|
|
var command = new AcceptOrderCommand(id, request.PerformerId);
|
|
await sender.Send(command);
|
|
return Results.Ok();
|
|
})
|
|
.WithName("AcceptOrder")
|
|
.WithOpenApi(op => new(op)
|
|
{
|
|
Summary = "Принять заказ",
|
|
Description = "Исполнитель принимает заказ. Переводит заказ в статус «В работе»."
|
|
});
|
|
|
|
// ─── Завершить заказ ─────────────────────────────────────────────────────────
|
|
group.MapPost("/{id:guid}/complete", async (Guid id, CompleteOrderRequest request, ISender sender) =>
|
|
{
|
|
var command = new CompleteOrderCommand(id, request.RequesterId);
|
|
await sender.Send(command);
|
|
return Results.Ok();
|
|
})
|
|
.WithName("CompleteOrder")
|
|
.WithOpenApi(op => new(op)
|
|
{
|
|
Summary = "Завершить заказ",
|
|
Description = "Заказчик или исполнитель завершает заказ. Переводит в статус «Завершён»."
|
|
});
|
|
|
|
// ─── Отменить заказ ──────────────────────────────────────────────────────────
|
|
group.MapPost("/{id:guid}/cancel", async (Guid id, CancelOrderRequest request, ISender sender) =>
|
|
{
|
|
var command = new CancelOrderCommand(id, request.RequesterId, request.Reason);
|
|
await sender.Send(command);
|
|
return Results.Ok();
|
|
})
|
|
.WithName("CancelOrder")
|
|
.WithOpenApi(op => new(op)
|
|
{
|
|
Summary = "Отменить заказ",
|
|
Description = "Заказчик или исполнитель отменяет заказ с указанием причины."
|
|
});
|
|
|
|
// ─── Добавить отклик (мастер на публичный заказ) ─────────────────────────────
|
|
group.MapPost("/{id:guid}/applications", async (Guid id, AddApplicationRequest request, ISender sender) =>
|
|
{
|
|
var command = new AddApplicationCommand(id, request.PerformerId, request.Amount, request.Currency, request.Comment);
|
|
await sender.Send(command);
|
|
return Results.Ok();
|
|
})
|
|
.WithName("AddOrderApplication")
|
|
.WithOpenApi(op => new(op)
|
|
{
|
|
Summary = "Откликнуться на заказ",
|
|
Description = "Исполнитель отправляет отклик на публичный заказ с указанием цены и комментария."
|
|
});
|
|
|
|
// ─── Выбрать исполнителя из откликов (заказчик) ─────────────────────────────
|
|
group.MapPost("/{id:guid}/select-performer", async (Guid id, SelectPerformerRequest request, ISender sender) =>
|
|
{
|
|
var command = new SelectPerformerCommand(id, request.PerformerId);
|
|
await sender.Send(command);
|
|
return Results.Ok();
|
|
})
|
|
.WithName("SelectPerformer")
|
|
.WithOpenApi(op => new(op)
|
|
{
|
|
Summary = "Выбрать исполнителя",
|
|
Description = "Заказчик выбирает конкретного исполнителя из откликнувшихся. Запускает SLA-таймер 60 минут."
|
|
});
|
|
}
|
|
}
|
|
|
|
// ─── Request DTOs ────────────────────────────────────────────────────────────
|
|
|
|
/// <summary>
|
|
/// Запрос на создание заказа.
|
|
/// </summary>
|
|
public record CreateOrderRequest
|
|
{
|
|
/// <summary>Идентификатор заказчика.</summary>
|
|
public Guid CustomerId { get; init; }
|
|
|
|
/// <summary>ФИО заказчика.</summary>
|
|
public string CustomerName { get; init; } = default!;
|
|
|
|
/// <summary>Идентификатор услуги.</summary>
|
|
public Guid ServiceId { get; init; }
|
|
|
|
/// <summary>Название услуги.</summary>
|
|
public string ServiceTitle { get; init; } = default!;
|
|
|
|
/// <summary>Сумма услуги.</summary>
|
|
public decimal PriceAmount { get; init; }
|
|
|
|
/// <summary>Тип цены (0=Фикс, 1=Почасовая, 2=Договорная).</summary>
|
|
public PriceType PriceType { get; init; }
|
|
|
|
/// <summary>Тип заказа: Direct (прямой) или PublicJob (публичный).</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; }
|
|
|
|
/// <summary>Имя исполнителя (обязателен для прямого заказа).</summary>
|
|
public string? PerformerName { get; init; }
|
|
|
|
public CreateOrderRequest() { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Запрос принятия заказа исполнителем.
|
|
/// </summary>
|
|
public record AcceptOrderRequest
|
|
{
|
|
/// <summary>Идентификатор исполнителя, принимающего заказ.</summary>
|
|
public Guid PerformerId { get; init; }
|
|
|
|
public AcceptOrderRequest() { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Запрос завершения заказа.
|
|
/// </summary>
|
|
public record CompleteOrderRequest
|
|
{
|
|
/// <summary>Идентификатор пользователя (заказчик или исполнитель).</summary>
|
|
public Guid RequesterId { get; init; }
|
|
|
|
public CompleteOrderRequest() { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Запрос отмены заказа.
|
|
/// </summary>
|
|
public record CancelOrderRequest
|
|
{
|
|
/// <summary>Идентификатор пользователя, инициирующего отмену.</summary>
|
|
public Guid RequesterId { get; init; }
|
|
|
|
/// <summary>Причина отмены (необязательно).</summary>
|
|
public string? Reason { get; init; }
|
|
|
|
public CancelOrderRequest() { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Запрос добавления отклика на заказ.
|
|
/// </summary>
|
|
public record AddApplicationRequest
|
|
{
|
|
/// <summary>Идентификатор исполнителя.</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>
|
|
public record SelectPerformerRequest
|
|
{
|
|
/// <summary>Идентификатор выбранного исполнителя.</summary>
|
|
public Guid PerformerId { get; init; }
|
|
|
|
public SelectPerformerRequest() { }
|
|
}
|