Files
tnsu-approval-route/backend/Tnsu.ApprovalRoute.Application/Reminders/ApprovalRow.cs
T
2026-08-12 15:12:11 +05:00

96 lines
3.9 KiB
C#

using System.Text.Json;
namespace Tnsu.ApprovalRoute.Application.Reminders;
/// <summary>
/// Строка списка «… — согласование», разобранная из ответа SharePoint.
/// Имена колонок — как в request-portal (FIELDS в approvalList.service.ts):
/// именно он эти списки и создаёт.
/// </summary>
internal sealed record ApprovalRow(
int Id,
int TicketId,
int StepOrder,
string Status,
string Phase,
DateTimeOffset? Date,
string? ApproverEmail,
int? SlaHours,
bool? AskOverdueReason)
{
public static ApprovalRow? TryParse(JsonElement item)
{
// TicketId — Lookup на заявку; без него строку не с чем связать.
if (!TryGetInt(item, "TicketId", out var ticketId)) return null;
var approver = item.TryGetProperty("TansuSiteApprover", out var a) && a.ValueKind == JsonValueKind.Object
? GetString(a, "EMail")
: null;
return new ApprovalRow(
TryGetInt(item, "Id", out var id) ? id : 0,
ticketId,
TryGetInt(item, "StepOrder", out var order) ? order : 0,
GetString(item, "ApprovalStatus") ?? string.Empty,
// Пустая фаза — старые строки, заведённые до появления колонки;
// трактуем как «Согласование» (так же поступает request-portal).
GetString(item, "Phase") is { Length: > 0 } phase ? phase : ApprovalPhases.Approval,
TryGetDate(item, "Date"),
approver,
TryGetInt(item, "SLAhours", out var sla) ? sla : null,
TryGetBool(item, "AskOverdueReason", out var ask) ? ask : null);
}
private static string? GetString(JsonElement e, string name) =>
e.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.String ? v.GetString() : null;
private static bool TryGetInt(JsonElement e, string name, out int value)
{
value = 0;
return e.TryGetProperty(name, out var v) && v.ValueKind == JsonValueKind.Number && v.TryGetInt32(out value);
}
private static bool TryGetBool(JsonElement e, string name, out bool value)
{
value = false;
if (!e.TryGetProperty(name, out var v)) return false;
if (v.ValueKind is not (JsonValueKind.True or JsonValueKind.False)) return false;
value = v.GetBoolean();
return true;
}
private static DateTimeOffset? TryGetDate(JsonElement e, string name) =>
e.TryGetProperty(name, out var v) &&
v.ValueKind == JsonValueKind.String &&
DateTimeOffset.TryParse(v.GetString(), out var parsed)
? parsed
: null;
}
internal static class ApprovalPhases
{
public const string Approval = "Согласование";
public const string Execution = "Исполнение";
}
internal static class ApprovalStatuses
{
public const string OnApproval = "На согласовании";
public const string OnExecution = "На исполнении";
}
/// <summary>
/// Managed property статуса строки маршрута для KQL — ApprovalStatusOWSCHCS.
/// Имя нестандартное (суффикс OWSCHCS у Choice-поля, префикс группы колонок
/// сайта у самого имени) — совпадает с MP_STATUS в
/// request-portal/src/webparts/requestPortal/features/myRequests/myRequests.service.ts,
/// это тот же список и та же колонка, имя проверено там же.
/// </summary>
internal static class SlaStatus
{
private const string ManagedProperty = "ApprovalStatusOWSCHCS";
public static readonly string OnApprovalProperty = $"{ManagedProperty}:\"{ApprovalStatuses.OnApproval}\"";
public static readonly string OnExecutionProperty = $"{ManagedProperty}:\"{ApprovalStatuses.OnExecution}\"";
}