Files
tiku-backend.net/Tiku.Infrastructure/PlatformAdmin/Operations/PlatformOperationsQueryService.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

111 lines
5.3 KiB
C#

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Tiku.Application.Assets;
using Tiku.Application.PlatformAdmin.Operations;
using Tiku.Application.Security;
using Tiku.Application.Storage;
using Tiku.Domain.Operations;
using Tiku.Domain.Platform;
using Tiku.Infrastructure.Persistence;
using Tiku.Infrastructure.Storage;
namespace Tiku.Infrastructure.PlatformAdmin.Operations;
internal sealed class PlatformOperationsQueryService(
IPlatformControlPlanePersistence platformControlPlanePersistence,
IJobsOperationsPersistence jobsOperationsPersistence,
IRedisSecurityStore redisSecurityStore,
IAssetSecurityScanner assetSecurityScanner,
IObjectStorageService objectStorageService,
IOptions<AliyunOssOptions> aliyunOssOptions) : IPlatformOperationsQueryService
{
public async Task<PlatformDependencyHealth> GetHealthAsync(CancellationToken cancellationToken = default)
{
var database = await platformControlPlanePersistence.Database.CanConnectAsync(cancellationToken);
var redis = !redisSecurityStore.IsConfigured || await redisSecurityStore.PingAsync(cancellationToken);
var clamAv = await assetSecurityScanner.CheckHealthAsync(cancellationToken);
var storageProvider = objectStorageService.ConfiguredDefaultProvider();
var storageConfigured = storageProvider switch
{
ObjectStorageProviders.AliyunOss => aliyunOssOptions.Value.IsConfigured,
ObjectStorageProviders.LocalDev => true,
_ => false
};
var heartbeat = await jobsOperationsPersistence.WorkerHeartbeats.AsNoTracking()
.MaxAsync(item => (DateTimeOffset?)item.LastHeartbeatAt, cancellationToken);
var workerReady = heartbeat >= DateTimeOffset.UtcNow.AddMinutes(-2);
return new PlatformDependencyHealth(
database && redis && clamAv && workerReady && storageConfigured,
database,
redisSecurityStore.IsConfigured,
redis,
workerReady,
heartbeat,
clamAv,
storageProvider,
storageConfigured,
DateTimeOffset.UtcNow);
}
public async Task<IReadOnlyCollection<PlatformWorkerState>> GetWorkersAsync(
CancellationToken cancellationToken = default)
{
var staleBefore = DateTimeOffset.UtcNow.AddMinutes(-2);
return await jobsOperationsPersistence.WorkerHeartbeats.AsNoTracking()
.OrderBy(item => item.WorkerId)
.ThenBy(item => item.Processor)
.Select(item => new PlatformWorkerState(
item.WorkerId,
item.Processor,
item.StartedAt,
item.LastHeartbeatAt,
item.LastIterationStartedAt,
item.LastIterationCompletedAt,
item.LastSucceededAt,
item.LastError,
item.IsRunning,
item.LastHeartbeatAt < staleBefore))
.ToArrayAsync(cancellationToken);
}
public async Task<PlatformJobMetrics> GetJobMetricsAsync(CancellationToken cancellationToken = default)
{
var now = DateTimeOffset.UtcNow;
var counts = await jobsOperationsPersistence.BackgroundJobs.AsNoTracking()
.GroupBy(item => item.Status)
.Select(group => new PlatformMetricCount(group.Key.ToString(), group.Count()))
.ToArrayAsync(cancellationToken);
var oldest = await jobsOperationsPersistence.BackgroundJobs.AsNoTracking()
.Where(item => item.Status == BackgroundJobStatus.Pending)
.MinAsync(item => (DateTimeOffset?)item.CreatedAt, cancellationToken);
var expired = await jobsOperationsPersistence.BackgroundJobs.AsNoTracking()
.CountAsync(item => item.Status == BackgroundJobStatus.Processing && item.LockExpiresAt < now,
cancellationToken);
return new PlatformJobMetrics(
counts,
oldest,
oldest.HasValue ? Math.Max(0, (now - oldest.Value).TotalSeconds) : 0,
expired,
now);
}
public async Task<PlatformGovernanceMetrics> GetGovernanceMetricsAsync(
CancellationToken cancellationToken = default)
{
var now = DateTimeOffset.UtcNow;
var approvals = await platformControlPlanePersistence.PlatformApprovalRequests.AsNoTracking()
.GroupBy(item => item.Status)
.Select(group => new PlatformMetricCount(group.Key.ToString(), group.Count()))
.ToArrayAsync(cancellationToken);
var expired = await platformControlPlanePersistence.PlatformApprovalRequests.AsNoTracking()
.CountAsync(item => item.Status == PlatformApprovalRequestStatus.Pending && item.ExpiresAt <= now,
cancellationToken);
var drafts = await platformControlPlanePersistence.PlatformConfigurationVersions.AsNoTracking()
.CountAsync(item => item.Status == PlatformConfigurationVersionStatus.Draft, cancellationToken);
var notifications = await platformControlPlanePersistence.PlatformNotificationDeliveries.AsNoTracking()
.GroupBy(item => item.Status)
.Select(group => new PlatformMetricCount(group.Key.ToString(), group.Count()))
.ToArrayAsync(cancellationToken);
return new PlatformGovernanceMetrics(approvals, expired, drafts, notifications, now);
}
}