feat: complete SaaS commercial delivery workflows

This commit is contained in:
2026-08-01 15:26:21 +08:00
parent 46abf4d62f
commit d58bcd97e9
56 changed files with 27505 additions and 181 deletions

View File

@@ -117,6 +117,10 @@ internal static class WorkerDependencyInjection
.Bind(builder.Configuration.GetSection("SaasSubscriptions"));
builder.Services.AddOptions<FeatureUsageReconciliationOptions>()
.Bind(builder.Configuration.GetSection("FeatureUsageReconciliation"));
builder.Services.AddOptions<CommercialBillingOptions>()
.Bind(builder.Configuration.GetSection("CommercialBilling"))
.Validate(options => options.BatchSize is >= 1 and <= 1000, "Commercial billing batch size is invalid.")
.ValidateOnStart();
builder.Services.AddSingleton<IPeriodicProcessorLock, PostgresPeriodicProcessorLock>();
builder.Services.AddSingleton<WorkerStateReporter>();
builder.Services.AddHostedService<TenantDomainWorker>();
@@ -124,6 +128,7 @@ internal static class WorkerDependencyInjection
builder.Services.AddHostedService<FeatureUsageWorker>();
builder.Services.AddHostedService<BackgroundJobsWorker>();
builder.Services.AddHostedService<AuthorizationCacheInvalidationWorker>();
builder.Services.AddHostedService<CommercialBillingWorker>();
return builder;
}

View File

@@ -280,3 +280,21 @@ internal sealed class AuthorizationCacheInvalidationWorker(
.ProcessPendingAsync(cancellationToken: cancellationToken);
}
}
internal sealed class CommercialBillingWorker(
IServiceScopeFactory scopeFactory,
IPeriodicProcessorLock processorLock,
WorkerStateReporter stateReporter,
IOptions<WorkerOptions> options,
ILogger<CommercialBillingWorker> logger)
: PeriodicWorker(logger, processorLock, stateReporter, "commercial-billing",
TimeSpan.FromSeconds(60), options.Value.Enabled)
{
protected override async Task<int> ProcessAsync(CancellationToken cancellationToken)
{
await using var scope = scopeFactory.CreateAsyncScope();
InitializeSystem(scope.ServiceProvider, "Commercial renewal, receivable, dunning and refund worker");
return await scope.ServiceProvider.GetRequiredService<ICommercialBillingProcessor>()
.ProcessDueAsync(cancellationToken);
}
}

View File

@@ -46,6 +46,11 @@
"BatchSize": 100,
"IntervalMinutes": 60
},
"CommercialBilling": {
"Enabled": true,
"BatchSize": 100,
"AllowedWebhookHosts": []
},
"Storage": {
"DefaultProvider": "aliyun_oss",
"DefaultBucket": "tenant-assets",