Files
tnsu-approval-route/backend/Tnsu.ApprovalRoute.Api/Controllers/RequestManageController.cs
T
gorbenko.pavel d9d68405ab feat: читать заявки в admin-вкладках от имени приложения, а не пользователя
«Редактирование заявок» и «Заявки по согласующему» раньше читали списки
SharePoint напрямую с фронтенда (PnP от имени залогиненного администратора),
из-за чего видимые заявки молча урезались правами САМОГО администратора на
списки. Добавлен RequestManageService/RequestManageController на бэкенде
(app-only SharePointClient, по образцу ReminderService) — теперь чтение
(поиск заявки, матрица маршрута, строки по согласующему) идёт через него,
а правка (переназначить/удалить/переставить/уведомить) остаётся на PnP.

Заодно кнопка уведомления в таблице маршрута включена и для строк в статусе
«Возвращена на доработку» — по клику поток Power Automate уведомляет автора
заявки, что её вернули на доработку.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-25 10:51:08 +05:00

58 lines
3.1 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Tnsu.ApprovalRoute.Api.Common;
using Tnsu.ApprovalRoute.Application.RequestManage;
namespace Tnsu.ApprovalRoute.Api.Controllers;
/// <summary>
/// Чтение заявок и маршрутов согласования от имени приложения (см.
/// RequestManageService) — для вкладок «Редактирование заявок» и «Заявки по
/// согласующему»: результат не зависит от прав вызывающего пользователя на
/// списки SharePoint, в отличие от прежнего прямого PnP-чтения с фронтенда.
/// </summary>
[ApiController]
[Authorize]
[Route("api/request-manage")]
[Produces("application/json")]
public sealed class RequestManageController(RequestManageService service) : ControllerBase
{
/// <summary>Заявки типа requestTypeId по всем партициям (архив + текущая), постранично.</summary>
[HttpGet("search")]
[ProducesResponseType(typeof(ApiResponse<ManagedRequestPageDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)]
public async Task<IActionResult> Search(
[FromQuery] int requestTypeId,
[FromQuery] string query,
[FromQuery] int page,
[FromQuery] int? pageSize,
CancellationToken ct) =>
(await service.SearchAsync(requestTypeId, query ?? string.Empty, page < 1 ? 1 : page, pageSize, ct)).ToActionResult();
/// <summary>Заявка + полный маршрут согласования (обе фазы, по StepOrder).</summary>
[HttpGet("matrix")]
[ProducesResponseType(typeof(ApiResponse<ManagedRequestMatrixDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ApiError), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Matrix(
[FromQuery] int requestTypeId, [FromQuery] string listId, [FromQuery] int requestId, CancellationToken ct) =>
(await service.GetMatrixAsync(requestTypeId, listId, requestId, ct)).ToActionResult();
/// <summary>
/// Активные строки согласования одного человека сразу по всем типам заявок и
/// партициям. statuses — через запятую (см. REQUEST_STATUS во фронтенде).
/// </summary>
[HttpGet("by-approver")]
[ProducesResponseType(typeof(ApiResponse<IReadOnlyList<UserApprovalRowDto>>), StatusCodes.Status200OK)]
public async Task<IActionResult> ByApprover(
[FromQuery] string approverEmail, [FromQuery] string statuses, CancellationToken ct)
{
var activeStatuses = (statuses ?? string.Empty)
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.ToList();
return (await service.FindActiveApprovalRowsForApproverAsync(approverEmail ?? string.Empty, activeStatuses, ct))
.ToActionResult();
}
}