Files
tiku-backend.net/Tiku.Infrastructure/Commerce/Reconciliation/CommerceReconciliationJobHandler.cs
xiong 33375a38d7
Some checks failed
ci / release-gate (push) Has been cancelled
refactor(architecture): harden module boundaries
2026-08-04 12:10:36 +08:00

84 lines
3.6 KiB
C#

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(ICommercePersistence commercePersistence,
ITenancyPersistence tenancyPersistence) : 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 tenancyPersistence.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 commercePersistence.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."
})
};
commercePersistence.CommerceReconciliationBatches.Add(batch);
await commercePersistence.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;
}
}