refactor(jobs): modularize background job handlers
Some checks failed
ci / release-gate (push) Has been cancelled

This commit is contained in:
2026-08-03 13:28:21 +08:00
parent c497a3ca8d
commit 7adcb6a3d5
21 changed files with 871 additions and 645 deletions

View File

@@ -0,0 +1,82 @@
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Tiku.Application.Jobs;
using Tiku.Domain.Commerce;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Jobs;
using Tiku.Infrastructure.Persistence;
namespace Tiku.Infrastructure.Commerce.Reconciliation;
internal sealed class CommerceReconciliationJobHandler(TikuDbContext dbContext) : IBackgroundJobHandler
{
public string JobType => "commerce_reconciliation";
public async Task<BackgroundJobHandlerResult> HandleAsync(
BackgroundJobExecutionContext context,
CancellationToken cancellationToken = default)
{
var provider = NormalizeProvider(BackgroundJobPayload.GetString(context.Payload, "provider"));
var hasProviderConfig = await dbContext.TenantExternalProviders.AnyAsync(
item =>
item.TenantId == context.TenantId &&
item.Capability == TenantExternalProviderCapability.Payment &&
item.Provider == provider &&
item.Status == TenantExternalProviderStatus.Active,
cancellationToken);
if (!hasProviderConfig)
throw new InvalidOperationException(
$"Active payment provider '{provider}' is required for commerce reconciliation job.");
var billDate = BackgroundJobPayload.GetDateOnly(context.Payload, "billDate") ??
DateOnly.FromDateTime(DateTime.UtcNow.Date);
var billType = BackgroundJobPayload.GetEnum(context.Payload, "billType", ReconciliationBillType.Combined);
var sourceHash = $"background-job:{context.JobId:N}";
var batch = await dbContext.CommerceReconciliationBatches.SingleOrDefaultAsync(
item =>
item.TenantId == context.TenantId &&
item.Provider == provider &&
item.Source == ReconciliationSource.ProviderDownload &&
item.SourceHash == sourceHash,
cancellationToken);
if (batch is null)
{
batch = new CommerceReconciliationBatch
{
TenantId = context.TenantId,
Provider = provider,
BillDate = billDate,
BillType = billType,
Source = ReconciliationSource.ProviderDownload,
SourceName = $"provider-bill:{provider}:{billDate:yyyyMMdd}",
SourceHash = sourceHash,
Status = ReconciliationBatchStatus.Pending,
Metadata = JsonSerializer.SerializeToElement(new
{
jobId = context.JobId,
note =
"Provider bill job created the reconciliation batch; provider download/parser is handled by a dedicated provider processor."
})
};
dbContext.CommerceReconciliationBatches.Add(batch);
await dbContext.SaveChangesAsync(cancellationToken);
}
return new BackgroundJobHandlerResult(JsonSerializer.SerializeToElement(new
{
batchId = batch.Id,
provider,
billDate,
billType = billType.ToString(),
status = batch.Status.ToString()
}));
}
private static string NormalizeProvider(string? provider)
{
var normalized = (provider ?? string.Empty).Trim().ToLowerInvariant();
return string.IsNullOrWhiteSpace(normalized)
? throw new InvalidOperationException("Background job provider is required.")
: normalized;
}
}