135 lines
5.2 KiB
C#
135 lines
5.2 KiB
C#
using System.Text.Json;
|
|
using Tiku.Application.Backoffice;
|
|
using Tiku.Application.PlatformBilling;
|
|
using Tiku.Domain.Platform;
|
|
|
|
namespace Tiku.Application.PlatformAdmin;
|
|
|
|
public static class PlatformApprovalPolicyCodes
|
|
{
|
|
public const string TenantArchive = "tenant.archive";
|
|
public const string SuperAdminGrant = "security.super-admin-grant";
|
|
public const string PaymentChannelChange = "payment.channel-change";
|
|
public const string FinancialAdjustment = "finance.adjustment";
|
|
}
|
|
|
|
public sealed record PlatformApprovalActor(Guid UserId, IReadOnlySet<string> Permissions);
|
|
|
|
public sealed record PlatformCommandSubmission(
|
|
string ExecutionStatus,
|
|
JsonElement? Result,
|
|
PlatformApprovalRequestItem? ApprovalRequest);
|
|
|
|
public sealed record PlatformApprovalRequestItem(
|
|
Guid Id,
|
|
string RequestNo,
|
|
string PolicyCode,
|
|
int PolicyVersion,
|
|
PlatformApprovalRequestStatus Status,
|
|
Guid RequestedBy,
|
|
Guid? DecidedBy,
|
|
string RequiredPermission,
|
|
string CommandType,
|
|
string TargetType,
|
|
string TargetId,
|
|
int? AmountCents,
|
|
JsonElement RequestSnapshot,
|
|
string? RequestReason,
|
|
string? DecisionReason,
|
|
string? Error,
|
|
DateTimeOffset ExpiresAt,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record PlatformApprovalPolicyItem(
|
|
Guid Id,
|
|
string Code,
|
|
string Name,
|
|
string RequiredPermission,
|
|
bool Enabled,
|
|
bool AlwaysRequireApproval,
|
|
int? AmountThresholdCents,
|
|
int Version,
|
|
int ExpiresAfterHours,
|
|
JsonElement Conditions);
|
|
|
|
public sealed record UpdatePlatformApprovalPolicyCommand(
|
|
string Code,
|
|
bool Enabled,
|
|
bool AlwaysRequireApproval,
|
|
int? AmountThresholdCents,
|
|
int ExpiresAfterHours,
|
|
JsonElement Conditions);
|
|
|
|
public interface IPlatformApprovalService
|
|
{
|
|
Task<IReadOnlyCollection<PlatformApprovalRequestItem>> ListAsync(PlatformApprovalActor actor,
|
|
PlatformApprovalRequestStatus? status, int limit, CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformApprovalRequestItem> GetAsync(PlatformApprovalActor actor, Guid requestId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
Task<IReadOnlyCollection<PlatformApprovalPolicyItem>> ListPoliciesAsync(PlatformApprovalActor actor,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformApprovalPolicyItem> UpdatePolicyAsync(PlatformApprovalActor actor,
|
|
UpdatePlatformApprovalPolicyCommand command, CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformApprovalRequestItem> ApproveAsync(PlatformApprovalActor actor, Guid requestId, string reason,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformApprovalRequestItem> RejectAsync(PlatformApprovalActor actor, Guid requestId, string reason,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformApprovalRequestItem> CancelAsync(PlatformApprovalActor actor, Guid requestId, string reason,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
Task<int> ProcessApprovedAsync(int batchSize = 20, CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformCommandSubmission> SubmitRefundAsync(SaasCatalogActor actor, RequestPlatformRefundCommand command,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformCommandSubmission> ConfirmManualPaymentAsync(SaasCatalogActor actor,
|
|
ConfirmManualPaymentCommand command, string idempotencyKey, CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformCommandSubmission> UpdateTenantStatusAsync(PlatformAdminActor actor,
|
|
UpdatePlatformTenantStatusCommand command, string idempotencyKey,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformCommandSubmission> UpsertPaymentChannelAsync(PlatformCapabilityActor actor,
|
|
UpsertPlatformPaymentChannelCommand command, string idempotencyKey,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
Task<PlatformCommandSubmission> ReplaceRoleBindingsAsync(BackofficeActor actor, ReplaceRoleBindingsCommand command,
|
|
string idempotencyKey, CancellationToken cancellationToken = default);
|
|
}
|
|
|
|
public sealed class PlatformApprovalException(string message, string code) : Exception(message)
|
|
{
|
|
public string Code { get; } = code;
|
|
}
|
|
|
|
public static class PlatformApprovalRules
|
|
{
|
|
public static bool RequiresApproval(bool enabled, bool alwaysRequireApproval, int? amountThresholdCents,
|
|
int? amountCents)
|
|
{
|
|
return enabled && (alwaysRequireApproval ||
|
|
(amountThresholdCents.HasValue && amountCents >= amountThresholdCents));
|
|
}
|
|
|
|
public static string? DecisionDenialCode(
|
|
PlatformApprovalRequestStatus status,
|
|
Guid requestedBy,
|
|
Guid decisionActor,
|
|
DateTimeOffset expiresAt,
|
|
string requiredPermission,
|
|
IReadOnlySet<string> permissions,
|
|
DateTimeOffset now)
|
|
{
|
|
if (status != PlatformApprovalRequestStatus.Pending) return "approval_request_not_pending";
|
|
if (expiresAt <= now) return "approval_request_expired";
|
|
if (requestedBy == decisionActor) return "approval_maker_checker_required";
|
|
return permissions.Contains(requiredPermission) ? null : "approval_business_permission_required";
|
|
}
|
|
} |