апи для работы с маршрутами согласования #2
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Tnsu.ApprovalRoute.Api.Common;
|
||||
using Tnsu.ApprovalRoute.Application.TemplateRoutes;
|
||||
using Tnsu.ApprovalRoute.Domain.Enums;
|
||||
|
||||
namespace Tnsu.ApprovalRoute.Api.Controllers;
|
||||
|
||||
@@ -11,6 +12,15 @@ namespace Tnsu.ApprovalRoute.Api.Controllers;
|
||||
[Route("api/template-routes")]
|
||||
public sealed class TemplateRoutesController(ISender mediator) : ControllerBase
|
||||
{
|
||||
[HttpGet("resolve")]
|
||||
public async Task<IActionResult> Resolve(
|
||||
[FromQuery] Guid requestTypeId,
|
||||
[FromQuery] string? userEmail,
|
||||
[FromQuery] Guid? projectId,
|
||||
[FromQuery] RouteStepStatus status,
|
||||
CancellationToken ct) =>
|
||||
(await mediator.Send(new ResolveTemplateRouteQuery(requestTypeId, userEmail, projectId, status), ct)).ToActionResult();
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> List([FromQuery] Guid? requestTypeId, CancellationToken ct) =>
|
||||
(await mediator.Send(new ListTemplateRoutesQuery(requestTypeId), ct)).ToActionResult();
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using Tnsu.ApprovalRoute.Domain.Enums;
|
||||
|
||||
namespace Tnsu.ApprovalRoute.Application.TemplateRoutes;
|
||||
|
||||
public sealed record ResolvedRouteStepDto(
|
||||
Guid Id,
|
||||
int OrderNo,
|
||||
string ExecutorEmail,
|
||||
string Position,
|
||||
ProjectRole ProjectRole,
|
||||
HeadType HeadType,
|
||||
RouteStepStatus Status,
|
||||
bool AllowReassignToOtherApprovers);
|
||||
|
||||
public sealed record ResolveTemplateRouteResultDto(
|
||||
Guid RouteId,
|
||||
RouteType RouteType,
|
||||
bool RequiresControl,
|
||||
IReadOnlyList<ResolvedRouteStepDto> Steps);
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
using FluentResults;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Tnsu.ApprovalRoute.Application.Common.Errors;
|
||||
using Tnsu.ApprovalRoute.Application.Common.Interfaces;
|
||||
using Tnsu.ApprovalRoute.Domain.Entities;
|
||||
using Tnsu.ApprovalRoute.Domain.Enums;
|
||||
using Tnsu.ApprovalRoute.Infrastructure.Persistence;
|
||||
using Tnsu.ApprovalRoute.Infrastructure.TnsuPortal;
|
||||
|
||||
namespace Tnsu.ApprovalRoute.Application.TemplateRoutes;
|
||||
|
||||
public sealed record ResolveTemplateRouteQuery(
|
||||
Guid RequestTypeId,
|
||||
string? UserEmail,
|
||||
Guid? ProjectId,
|
||||
RouteStepStatus Status) : IRequest<Result<ResolveTemplateRouteResultDto>>;
|
||||
|
||||
public sealed class ResolveTemplateRouteHandler(
|
||||
ApprovalRouteDbContext db,
|
||||
HttpTnsuPortalClient portalClient,
|
||||
ICurrentUser currentUser)
|
||||
: IRequestHandler<ResolveTemplateRouteQuery, Result<ResolveTemplateRouteResultDto>>
|
||||
{
|
||||
public async Task<Result<ResolveTemplateRouteResultDto>> Handle(ResolveTemplateRouteQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.Status == RouteStepStatus.None)
|
||||
return Result.Fail<ResolveTemplateRouteResultDto>(
|
||||
new ValidationError("Статус обязателен: Approval или Execution."));
|
||||
|
||||
var email = string.IsNullOrWhiteSpace(request.UserEmail) ? currentUser.Email : request.UserEmail;
|
||||
if (string.IsNullOrWhiteSpace(email))
|
||||
return Result.Fail<ResolveTemplateRouteResultDto>(
|
||||
new ValidationError("Не удалось определить email инициатора: не передан явно и отсутствует в токене."));
|
||||
|
||||
var requestTypeExists = await db.RequestTypes.AnyAsync(x => x.Id == request.RequestTypeId, cancellationToken);
|
||||
if (!requestTypeExists)
|
||||
return Result.Fail<ResolveTemplateRouteResultDto>(new NotFoundError("Тип заявки", request.RequestTypeId));
|
||||
|
||||
var employee = await portalClient.GetEmployeeByEmailAsync(email, cancellationToken);
|
||||
var byProject = request.ProjectId is not null;
|
||||
|
||||
var route = await db.TemplateRoutes.AsNoTracking()
|
||||
.Include(x => x.Steps)
|
||||
.Where(x =>
|
||||
x.RequestTypeId == request.RequestTypeId &&
|
||||
x.CompanyId == employee.CompanyId &&
|
||||
x.ByProject == byProject &&
|
||||
x.DepartmentId == employee.DepartmentId)
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
?? await db.TemplateRoutes.AsNoTracking()
|
||||
.Include(x => x.Steps)
|
||||
.Where(x =>
|
||||
x.RequestTypeId == request.RequestTypeId &&
|
||||
x.CompanyId == employee.CompanyId &&
|
||||
x.ByProject == byProject &&
|
||||
x.DepartmentId == null)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (route is null)
|
||||
return Result.Fail<ResolveTemplateRouteResultDto>(new NotFoundError(
|
||||
"Маршрут для этого типа заявки, отдела/компании инициатора и признака «по проекту»",
|
||||
request.RequestTypeId));
|
||||
|
||||
IReadOnlyList<ProjectExecutorInfo> projectExecutors = [];
|
||||
if (request.ProjectId is { } projectId)
|
||||
projectExecutors = await portalClient.GetProjectExecutorsAsync(projectId, cancellationToken);
|
||||
|
||||
var steps = route.Steps
|
||||
.Where(s => s.Status == request.Status)
|
||||
.OrderBy(s => s.OrderNo)
|
||||
.Select(s => ResolveStep(s, projectExecutors))
|
||||
.ToList();
|
||||
|
||||
return Result.Ok(new ResolveTemplateRouteResultDto(route.Id, route.RouteType, route.RequiresControl, steps));
|
||||
}
|
||||
|
||||
private static ResolvedRouteStepDto ResolveStep(TemplateRouteStep step, IReadOnlyList<ProjectExecutorInfo> projectExecutors)
|
||||
{
|
||||
// Для шагов с ролью в проекте фактический исполнитель приходит из данных проекта (api.tnsu.kz),
|
||||
// а не из статичного ExecutorEmail шаблона — тот используется только для шагов без привязки к проекту.
|
||||
if (step.ProjectRole != ProjectRole.None)
|
||||
{
|
||||
var projectExecutor = projectExecutors.FirstOrDefault(e => e.ProjectRole == step.ProjectRole);
|
||||
if (projectExecutor is not null)
|
||||
return new ResolvedRouteStepDto(
|
||||
step.Id, step.OrderNo, projectExecutor.Email, projectExecutor.Position,
|
||||
step.ProjectRole, step.HeadType, step.Status, step.AllowReassignToOtherApprovers);
|
||||
}
|
||||
|
||||
return new ResolvedRouteStepDto(
|
||||
step.Id, step.OrderNo, step.ExecutorEmail, step.Position,
|
||||
step.ProjectRole, step.HeadType, step.Status, step.AllowReassignToOtherApprovers);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user