feat(saas): implement marketplace and tenant onboarding

This commit is contained in:
2026-07-29 13:58:59 +08:00
parent 76606029e2
commit 6db200a2fc
145 changed files with 16862 additions and 94529 deletions

View File

@@ -58,12 +58,12 @@ public sealed record PlatformTenantDomainItem(
public sealed record PlatformTenantSubscriptionItem(
Guid Id,
Guid TenantId,
string PlanCode,
TenantSubscriptionStatus Status,
DateTimeOffset? StartsAt,
DateTimeOffset? ExpiresAt,
string? BillingCycle,
int AmountCents,
Guid BaseOfferingVersionId,
TenantSaasSubscriptionStatus Status,
DateTimeOffset StartsAt,
DateTimeOffset CurrentPeriodStart,
DateTimeOffset CurrentPeriodEnd,
bool CancelAtPeriodEnd,
JsonElement Metadata);
public sealed record TenantBillingProfileItem(
@@ -75,7 +75,7 @@ public sealed record TenantBillingProfileItem(
string? ContactEmail,
string? BillingAddress,
string? InvoiceTitle,
TenantInvoiceTitleType? InvoiceType,
TenantBillingInvoiceTitleType? InvoiceType,
string? BankName,
string? BankAccountMasked,
JsonElement Metadata);
@@ -86,7 +86,17 @@ public sealed record CreatePlatformTenantCommand(
string? LegalName,
TenantStatus Status,
BillingStatus BillingStatus,
JsonElement Metadata);
JsonElement Metadata,
string? OwnerEmail,
string? OwnerPhone,
string OwnerName,
string TemporaryPassword);
public sealed record PlatformTenantProvisioningResult(
PlatformTenantItem Tenant,
Guid OwnerUserId,
string OwnerIdentifier,
bool MustChangePassword);
public sealed record UpdatePlatformTenantStatusCommand(
Guid TenantId,
@@ -103,45 +113,11 @@ public sealed record UpsertPlatformTenantBillingProfileCommand(
string? ContactEmail,
string? BillingAddress,
string? InvoiceTitle,
TenantInvoiceTitleType? InvoiceType,
TenantBillingInvoiceTitleType? InvoiceType,
string? BankName,
string? BankAccountMasked,
JsonElement Metadata);
public sealed record PlatformPlanList(IReadOnlyCollection<PlatformSaasPlan> Items);
public sealed record PlatformPlanModuleEntitlements(
string PlanCode,
IReadOnlyCollection<string> ModuleCodes);
public sealed record ReplacePlatformPlanModulesCommand(
string PlanCode,
IReadOnlyCollection<string> ModuleCodes);
public sealed record PlatformTenantModuleOverrideItem(
Guid TenantId,
string ModuleCode,
TenantModuleOverrideMode Mode,
DateTimeOffset? ExpiresAt,
string? Reason);
public sealed record UpsertPlatformTenantModuleOverrideCommand(
Guid TenantId,
string ModuleCode,
TenantModuleOverrideMode Mode,
DateTimeOffset? ExpiresAt,
string Reason);
public sealed record UpsertPlatformSubscriptionCommand(
Guid TenantId,
string PlanCode,
TenantSubscriptionStatus Status,
DateTimeOffset? StartsAt,
DateTimeOffset? ExpiresAt,
string? BillingCycle,
int AmountCents,
JsonElement Metadata);
public sealed record PlatformDomainList(IReadOnlyCollection<PlatformTenantDomainItem> Items);
public sealed record PlatformDomainRecheckResult(Guid DomainId, TenantDomainStatus Status, DateTimeOffset LastCheckedAt);
@@ -189,15 +165,15 @@ public sealed record UpdatePlatformAuditAlertStatusCommand(
PlatformAuditAlertStatus Status,
string? ResolutionNote);
public sealed record PlatformDunningChannelList(IReadOnlyCollection<PlatformDunningChannelItem> Items);
public sealed record PlatformBillingDunningChannelList(IReadOnlyCollection<PlatformBillingDunningChannelItem> Items);
public sealed record PlatformDunningChannelItem(
public sealed record PlatformBillingDunningChannelItem(
Guid Id,
string ChannelCode,
string Name,
string? Description,
bool Enabled,
PlatformDunningProvider Provider,
PlatformBillingDunningProvider Provider,
string WebhookUrlMasked,
string? SecretRef,
IReadOnlyCollection<string> ReminderTypes,
@@ -209,13 +185,13 @@ public sealed record PlatformDunningChannelItem(
DateTimeOffset CreatedAt,
DateTimeOffset UpdatedAt);
public sealed record UpsertPlatformDunningChannelCommand(
public sealed record UpsertPlatformBillingDunningChannelCommand(
Guid? ChannelId,
string ChannelCode,
string Name,
string? Description,
bool Enabled,
PlatformDunningProvider Provider,
PlatformBillingDunningProvider Provider,
string WebhookUrl,
string? SecretRef,
IReadOnlyCollection<string> ReminderTypes,
@@ -225,18 +201,18 @@ public sealed record UpsertPlatformDunningChannelCommand(
int TimeoutSeconds,
JsonElement Metadata);
public sealed record DisablePlatformDunningChannelCommand(Guid ChannelId, string? Reason);
public sealed record DisablePlatformBillingDunningChannelCommand(Guid ChannelId, string? Reason);
public sealed record PlatformDunningEventList(IReadOnlyCollection<PlatformDunningEventItem> Items);
public sealed record PlatformBillingDunningEventList(IReadOnlyCollection<PlatformBillingDunningEventItem> Items);
public sealed record PlatformDunningEventItem(
public sealed record PlatformBillingDunningEventItem(
Guid Id,
Guid TenantId,
Guid ChannelId,
Guid ReminderId,
Guid InvoiceId,
PlatformDunningProvider Provider,
PlatformDunningNotificationStatus Status,
PlatformBillingDunningProvider Provider,
PlatformBillingDunningNotificationStatus Status,
int Attempts,
DateTimeOffset ScheduledAt,
DateTimeOffset? NextAttemptAt,
@@ -250,20 +226,16 @@ public sealed record PlatformDunningEventItem(
DateTimeOffset CreatedAt,
DateTimeOffset UpdatedAt);
public sealed record RetryPlatformDunningEventCommand(Guid EventId, string? Reason);
public sealed record RetryPlatformBillingDunningEventCommand(Guid EventId, string? Reason);
public interface IPlatformAdminService
{
Task<PlatformOverview> GetOverviewAsync(PlatformAdminActor actor, CancellationToken cancellationToken = default);
Task<PlatformTenantList> GetTenantsAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
Task<PlatformTenantDetail> GetTenantDetailAsync(PlatformAdminActor actor, Guid tenantId, CancellationToken cancellationToken = default);
Task<PlatformTenantItem> CreateTenantAsync(PlatformAdminActor actor, CreatePlatformTenantCommand command, CancellationToken cancellationToken = default);
Task<PlatformTenantProvisioningResult> CreateTenantAsync(PlatformAdminActor actor, CreatePlatformTenantCommand command, CancellationToken cancellationToken = default);
Task<PlatformTenantItem> UpdateTenantStatusAsync(PlatformAdminActor actor, UpdatePlatformTenantStatusCommand command, CancellationToken cancellationToken = default);
Task<TenantBillingProfileItem> UpsertTenantBillingProfileAsync(PlatformAdminActor actor, UpsertPlatformTenantBillingProfileCommand command, CancellationToken cancellationToken = default);
Task<PlatformPlanList> GetPlansAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
Task<PlatformPlanModuleEntitlements> ReplacePlanModulesAsync(PlatformAdminActor actor, ReplacePlatformPlanModulesCommand command, CancellationToken cancellationToken = default);
Task<PlatformTenantSubscriptionItem> UpsertSubscriptionAsync(PlatformAdminActor actor, UpsertPlatformSubscriptionCommand command, CancellationToken cancellationToken = default);
Task<PlatformTenantModuleOverrideItem> UpsertTenantModuleOverrideAsync(PlatformAdminActor actor, UpsertPlatformTenantModuleOverrideCommand command, CancellationToken cancellationToken = default);
Task<PlatformDomainList> GetDomainsAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
Task<PlatformDomainRecheckResult> RecheckDomainAsync(PlatformAdminActor actor, Guid domainId, CancellationToken cancellationToken = default);
Task<PlatformStaffList> GetStaffAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
@@ -272,12 +244,12 @@ public interface IPlatformAdminService
Task<PlatformAuditLogList> GetAuditLogsAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
Task<PlatformAuditAlertList> GetAuditAlertsAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
Task<PlatformAuditAlert> UpdateAuditAlertStatusAsync(PlatformAdminActor actor, UpdatePlatformAuditAlertStatusCommand command, CancellationToken cancellationToken = default);
Task<PlatformDunningChannelList> GetDunningChannelsAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
Task<PlatformDunningChannelItem> UpsertDunningChannelAsync(PlatformAdminActor actor, UpsertPlatformDunningChannelCommand command, CancellationToken cancellationToken = default);
Task<PlatformDunningChannelItem> DisableDunningChannelAsync(PlatformAdminActor actor, DisablePlatformDunningChannelCommand command, CancellationToken cancellationToken = default);
Task<PlatformDunningEventList> GetDunningEventsAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
Task<PlatformDunningEventItem> GetDunningEventDetailAsync(PlatformAdminActor actor, Guid eventId, CancellationToken cancellationToken = default);
Task<PlatformDunningEventItem> RetryDunningEventAsync(PlatformAdminActor actor, RetryPlatformDunningEventCommand command, CancellationToken cancellationToken = default);
Task<PlatformBillingDunningChannelList> GetBillingDunningChannelsAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
Task<PlatformBillingDunningChannelItem> UpsertBillingDunningChannelAsync(PlatformAdminActor actor, UpsertPlatformBillingDunningChannelCommand command, CancellationToken cancellationToken = default);
Task<PlatformBillingDunningChannelItem> DisableBillingDunningChannelAsync(PlatformAdminActor actor, DisablePlatformBillingDunningChannelCommand command, CancellationToken cancellationToken = default);
Task<PlatformBillingDunningEventList> GetBillingDunningEventsAsync(PlatformAdminActor actor, PlatformAdminQuery query, CancellationToken cancellationToken = default);
Task<PlatformBillingDunningEventItem> GetBillingDunningEventDetailAsync(PlatformAdminActor actor, Guid eventId, CancellationToken cancellationToken = default);
Task<PlatformBillingDunningEventItem> RetryBillingDunningEventAsync(PlatformAdminActor actor, RetryPlatformBillingDunningEventCommand command, CancellationToken cancellationToken = default);
}
public sealed class PlatformAdminException(string message, string code) : Exception(message)