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

@@ -30,34 +30,94 @@ public sealed class ArchitectureBoundaryTests
}
[Fact]
public void New_service_files_do_not_exceed_the_hard_size_limit()
public void Service_classes_do_not_exceed_the_hard_size_limit_or_grow_legacy_debt()
{
var root = FindRepositoryRoot();
var legacyFacades = new HashSet<string>(StringComparer.Ordinal)
var legacyLineBudgets = new Dictionary<string, int>(StringComparer.Ordinal)
{
"TenantAdminDirectService.cs",
"DirectContentService.cs",
"LearningActivityService.cs",
"CommerceAdminService.cs",
"PlatformAdminService.cs",
"BackgroundJobService.cs"
["TenantAdminDirectService"] = 3190,
["DirectContentService"] = 2015,
["CommerceAdminService"] = 1842,
["LearningActivityService"] = 1781,
["PlatformAdminService"] = 1719,
["ContentManagementService"] = 1096,
["PlatformQuestionBankService"] = 1033,
["CommerceService"] = 983,
["AssetManagementService"] = 948,
["AuthService"] = 876,
["ReferralService"] = 849,
["PlatformTenantCapabilitiesService"] = 805
};
var violations = Directory.EnumerateFiles(
var serviceGroups = Directory.EnumerateFiles(
Path.Combine(root, "Tiku.Infrastructure"),
"*Service.cs",
"*Service*.cs",
SearchOption.AllDirectories)
.Where(path => !path.Contains(
$"{Path.DirectorySeparatorChar}Migrations{Path.DirectorySeparatorChar}",
StringComparison.Ordinal))
.Where(path => !legacyFacades.Contains(Path.GetFileName(path)))
.Select(path => new { path, Lines = File.ReadLines(path).Count() })
.Where(item => item.Lines > 800)
.Select(item => $"{Path.GetRelativePath(root, item.path)} ({item.Lines} lines)")
.GroupBy(path => Path.GetFileNameWithoutExtension(path).Split('.')[0], StringComparer.Ordinal)
.Select(group => new
{
Service = group.Key,
Lines = group.Sum(path => File.ReadLines(path).Count()),
Files = group.Count()
})
.ToArray();
var violations = serviceGroups
.Where(item => legacyLineBudgets.TryGetValue(item.Service, out var budget)
? item.Lines > budget
: item.Lines > 800)
.Select(item => legacyLineBudgets.TryGetValue(item.Service, out var budget)
? $"{item.Service} ({item.Lines} lines across {item.Files} files; legacy budget {budget})"
: $"{item.Service} ({item.Lines} lines across {item.Files} files; hard limit 800)")
.ToArray();
Assert.True(
violations.Length == 0,
$"Service files over the 800-line hard limit were found:{Environment.NewLine}{string.Join(Environment.NewLine, violations)}");
$"Service classes exceeded the hard limit or grew legacy debt:{Environment.NewLine}{string.Join(Environment.NewLine, violations)}");
}
[Fact]
public void Background_jobs_are_dispatched_by_module_registered_handlers()
{
var root = FindRepositoryRoot();
var infrastructureRoot = Path.Combine(root, "Tiku.Infrastructure");
var sourceFiles = Directory.EnumerateFiles(infrastructureRoot, "*.cs", SearchOption.AllDirectories)
.Where(path => !path.Contains(
$"{Path.DirectorySeparatorChar}Persistence{Path.DirectorySeparatorChar}Migrations{Path.DirectorySeparatorChar}",
StringComparison.Ordinal))
.ToArray();
var handlerFiles = sourceFiles
.Where(path => File.ReadAllText(path).Contains(": IBackgroundJobHandler", StringComparison.Ordinal))
.ToArray();
var expectedJobTypes = new[]
{
"content_export",
"content_import",
"asset_security_scan",
"tenant_export",
"statistics_aggregation",
"commerce_reconciliation",
"tenant_domain_recheck"
};
Assert.Equal(expectedJobTypes.Length, handlerFiles.Length);
foreach (var jobType in expectedJobTypes)
Assert.Single(handlerFiles, path =>
File.ReadAllText(path).Contains($"JobType => \"{jobType}\"", StringComparison.Ordinal));
var moduleRegistrations = Directory
.EnumerateFiles(Path.Combine(infrastructureRoot, "Modules"), "*.cs", SearchOption.AllDirectories)
.Sum(path => File.ReadLines(path).Count(line =>
line.Contains("AddScoped<IBackgroundJobHandler", StringComparison.Ordinal)));
Assert.Equal(expectedJobTypes.Length, moduleRegistrations);
var jobsSource = string.Join(Environment.NewLine, Directory
.EnumerateFiles(Path.Combine(infrastructureRoot, "Jobs"), "*.cs", SearchOption.AllDirectories)
.Select(File.ReadAllText));
Assert.DoesNotContain("Dictionary<string, Func<Task<JsonElement>>>", jobsSource, StringComparison.Ordinal);
}
[Fact]
@@ -111,7 +171,6 @@ public sealed class ArchitectureBoundaryTests
{
Path.Combine(root, "Tiku.Infrastructure", "Growth", "CommissionService.cs"),
Path.Combine(root, "Tiku.Infrastructure", "Growth", "ReferralService.cs"),
Path.Combine(root, "Tiku.Application", "TenantAdmin", "TenantAdminDirectModels.cs"),
Path.Combine(root, "Tiku.Api", "Controllers", "TenantAdminDirectController.cs")
};
@@ -373,4 +432,4 @@ public sealed class ArchitectureBoundaryTests
violations.Length == 0,
$"{failureMessage}:{Environment.NewLine}{string.Join(Environment.NewLine, violations)}");
}
}
}