114 lines
4.7 KiB
C#
114 lines
4.7 KiB
C#
using System.Globalization;
|
||
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,
|
||
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,
|
||
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;
|
||
|
||
/// <summary>
|
||
/// Целое из JSON — с поправкой на то, КАК SharePoint отдаёт Number-колонки.
|
||
///
|
||
/// Одного TryGetInt32 здесь мало: у колонки типа Number значение приходит
|
||
/// дробным («1.0», а не «1»), а TryGetInt32 на числе с десятичной точкой
|
||
/// возвращает false — и поле молча становилось null. На фронтенде этой
|
||
/// проблемы нет вовсе: в JavaScript 1.0 и 1 — одно и то же число, поэтому
|
||
/// там те же данные читались верно, а на бэкенде терялись.
|
||
///
|
||
/// Строку тоже принимаем: с odata=verbose (и у части полей) SharePoint
|
||
/// отдаёт числа строками.
|
||
/// </summary>
|
||
private static bool TryGetInt(JsonElement e, string name, out int value)
|
||
{
|
||
value = 0;
|
||
if (!e.TryGetProperty(name, out var v)) return false;
|
||
|
||
if (v.ValueKind == JsonValueKind.Number)
|
||
{
|
||
if (v.TryGetInt32(out value)) return true;
|
||
if (!v.TryGetDouble(out var d) || d is < int.MinValue or > int.MaxValue) return false;
|
||
value = (int)Math.Round(d);
|
||
return true;
|
||
}
|
||
|
||
if (v.ValueKind != JsonValueKind.String) return false;
|
||
if (!double.TryParse(v.GetString(), NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed))
|
||
return false;
|
||
if (parsed is < int.MinValue or > int.MaxValue) return false;
|
||
|
||
value = (int)Math.Round(parsed);
|
||
return true;
|
||
}
|
||
|
||
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 JsonValueKind.True or JsonValueKind.False)
|
||
{
|
||
value = v.GetBoolean();
|
||
return true;
|
||
}
|
||
|
||
// Да/Нет-колонка тоже может прийти строкой («true»/«false») — см. TryGetInt.
|
||
return v.ValueKind == JsonValueKind.String && bool.TryParse(v.GetString(), out value);
|
||
}
|
||
|
||
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 = "На исполнении";
|
||
}
|