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

@@ -1,7 +1,8 @@
using System.Diagnostics;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Tiku.Application.Assets;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Application.Jobs;
using Tiku.Application.Security;
using Tiku.Domain.Common;
using Tiku.Domain.Operations;
@@ -137,11 +138,18 @@ internal sealed partial class BackgroundJobService
try
{
var result = await tenantExecutionScope.ExecuteAsync(
var handlerResult = await tenantExecutionScope.ExecuteAsync(
new SystemScopeRequest(
job.TenantId, SystemScopeCallerType.Worker, workerId,
$"Background job {job.JobType}", job.Id.ToString("N")),
(provider, token) => ProcessCoreAsync(provider, job, token),
async (provider, token) =>
{
var registry = provider.GetRequiredService<BackgroundJobHandlerRegistry>();
var handler = registry.GetRequired(job.JobType);
return await handler.HandleAsync(
new BackgroundJobExecutionContext(job.Id, job.TenantId, job.JobType, job.Payload),
token);
},
cancellationToken);
var cancellationRequested = await dbContext.BackgroundJobs.AsNoTracking()
.Where(item => item.Id == job.Id)
@@ -150,22 +158,42 @@ internal sealed partial class BackgroundJobService
job.Status = cancellationRequested ? BackgroundJobStatus.Cancelled : BackgroundJobStatus.Succeeded;
job.CompletedAt = DateTimeOffset.UtcNow;
job.LastError = null;
job.Result = result;
job.Result = handlerResult.Result;
job.OutputAssetId = handlerResult.OutputAssetId;
}
catch (Exception exception) when (exception is not OperationCanceledException)
{
job.RetryCount++;
job.LastError = exception is AssetSecurityScannerException scannerException
? $"{scannerException.Code}: {scannerException.Message}"
job.LastError = exception is BackgroundJobHandlerException handlerException
? $"{handlerException.Code}: {handlerException.Message}"
: exception.Message;
if (exception is AssetSecurityScannerException assetScanException)
await RecordAssetScanRetryAsync(job, assetScanException, cancellationToken);
job.Status = job.RetryCount > job.MaxRetries
? BackgroundJobStatus.Failed
: BackgroundJobStatus.Pending;
job.RunAfter = job.Status == BackgroundJobStatus.Pending
? DateTimeOffset.UtcNow.AddSeconds(Math.Min(300, 10 * job.RetryCount))
: null;
try
{
await tenantExecutionScope.ExecuteAsync(
new SystemScopeRequest(
job.TenantId, SystemScopeCallerType.Worker, workerId,
$"Background job failure compensation {job.JobType}", job.Id.ToString("N")),
async (provider, token) =>
{
var handler = provider.GetRequiredService<BackgroundJobHandlerRegistry>()
.GetRequired(job.JobType);
await handler.HandleFailureAsync(
new BackgroundJobExecutionContext(job.Id, job.TenantId, job.JobType, job.Payload),
exception,
token);
},
cancellationToken);
}
catch (Exception compensationException) when (compensationException is not OperationCanceledException)
{
job.LastError = $"{job.LastError} Compensation failed: {compensationException.Message}";
}
}
finally
{
@@ -199,4 +227,4 @@ internal sealed partial class BackgroundJobService
.SetProperty(value => value.LockExpiresAt, (DateTimeOffset?)null),
cancellationToken);
}
}
}