136 lines
5.3 KiB
C#
136 lines
5.3 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Tiku.Api.Contracts;
|
|
using Tiku.Application.Jobs;
|
|
using Tiku.Application.PlatformAdmin.Operations;
|
|
using Tiku.Application.Security;
|
|
using Tiku.Domain.Operations;
|
|
|
|
namespace Tiku.Api.Controllers;
|
|
|
|
[ApiController]
|
|
[Tags("平台端-运维")]
|
|
[Route("api/platform/operations")]
|
|
[Authorize(Policy = BackendPermissions.PlatformOperationsView)]
|
|
public sealed class PlatformOperationsController(
|
|
IBackgroundJobOperations backgroundJobService,
|
|
ICurrentUser currentUser,
|
|
IPlatformOperationsQueryService operationsQueries) : ControllerBase
|
|
{
|
|
[HttpGet("health")]
|
|
[EndpointSummary("查询受保护的依赖深度健康状态")]
|
|
public async Task<ActionResult<object>> Health(CancellationToken cancellationToken)
|
|
{
|
|
var health = await operationsQueries.GetHealthAsync(cancellationToken);
|
|
return Ok(new
|
|
{
|
|
status = health.Healthy ? "healthy" : "degraded",
|
|
database = health.Database,
|
|
redis = new { configured = health.RedisConfigured, ready = health.RedisReady },
|
|
worker = new { ready = health.WorkerReady, lastHeartbeatAt = health.LastHeartbeatAt },
|
|
clamAv = health.ClamAv,
|
|
storage = new { provider = health.StorageProvider, configured = health.StorageConfigured },
|
|
checkedAt = health.CheckedAt
|
|
});
|
|
}
|
|
|
|
[HttpGet("workers")]
|
|
[EndpointSummary("查询 Worker 与周期循环状态")]
|
|
public async Task<ActionResult<object>> Workers(CancellationToken cancellationToken)
|
|
{
|
|
var items = await operationsQueries.GetWorkersAsync(cancellationToken);
|
|
return Ok(new
|
|
{
|
|
staleAfterSeconds = 120,
|
|
items = items.Select(item => new
|
|
{
|
|
item.WorkerId,
|
|
item.Processor,
|
|
item.StartedAt,
|
|
item.LastHeartbeatAt,
|
|
item.LastIterationStartedAt,
|
|
item.LastIterationCompletedAt,
|
|
item.LastSucceededAt,
|
|
item.LastError,
|
|
item.IsRunning,
|
|
stale = item.Stale
|
|
})
|
|
});
|
|
}
|
|
|
|
[HttpGet("job-metrics")]
|
|
[EndpointSummary("查询后台任务队列指标")]
|
|
public async Task<ActionResult<object>> JobMetrics(CancellationToken cancellationToken)
|
|
{
|
|
var metrics = await operationsQueries.GetJobMetricsAsync(cancellationToken);
|
|
return Ok(new
|
|
{
|
|
counts = metrics.Counts.Select(item => new { status = item.Status, count = item.Count }),
|
|
oldestPendingAt = metrics.OldestPendingAt,
|
|
queueAgeSeconds = metrics.QueueAgeSeconds,
|
|
expiredLeases = metrics.ExpiredLeases,
|
|
checkedAt = metrics.CheckedAt
|
|
});
|
|
}
|
|
|
|
[HttpGet("governance-metrics")]
|
|
[EndpointSummary("查询审批、配置与通知治理指标")]
|
|
public async Task<ActionResult<object>> GovernanceMetrics(CancellationToken cancellationToken)
|
|
{
|
|
var metrics = await operationsQueries.GetGovernanceMetricsAsync(cancellationToken);
|
|
return Ok(new
|
|
{
|
|
approvals = metrics.Approvals.Select(item => new { status = item.Status, count = item.Count }),
|
|
expiredPending = metrics.ExpiredPending,
|
|
configurationDrafts = metrics.ConfigurationDrafts,
|
|
notifications = metrics.Notifications.Select(item => new { status = item.Status, count = item.Count }),
|
|
checkedAt = metrics.CheckedAt
|
|
});
|
|
}
|
|
|
|
[HttpGet("jobs")]
|
|
[EndpointSummary("查询全平台后台任务")]
|
|
public async Task<ActionResult<IReadOnlyCollection<BackgroundJobItem>>> Jobs(
|
|
[FromQuery] Guid? tenantId,
|
|
[FromQuery] string? jobType,
|
|
[FromQuery] BackgroundJobStatus? status,
|
|
[FromQuery] int? limit,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await backgroundJobService.ListPlatformAsync(
|
|
tenantId, jobType, status, limit ?? 100, cancellationToken));
|
|
}
|
|
|
|
[HttpGet("jobs/{jobId:guid}")]
|
|
[EndpointSummary("查询平台后台任务详情")]
|
|
public async Task<ActionResult<BackgroundJobItem>> Job(Guid jobId, CancellationToken cancellationToken)
|
|
{
|
|
var item = await backgroundJobService.GetAsync(jobId, null, cancellationToken);
|
|
return item is null ? NotFound() : Ok(item);
|
|
}
|
|
|
|
[HttpPost("jobs/{jobId:guid}/cancel")]
|
|
[Authorize(Policy = BackendPermissions.PlatformOperationsManage)]
|
|
[EndpointSummary("取消平台后台任务")]
|
|
public async Task<ActionResult<BackgroundJobItem>> CancelJob(
|
|
Guid jobId,
|
|
CancelBackgroundJobDto request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await backgroundJobService.RequestCancellationAsync(
|
|
jobId, null, ResolveUserId(), request.Reason, cancellationToken));
|
|
}
|
|
|
|
[HttpPost("jobs/{jobId:guid}/retry")]
|
|
[Authorize(Policy = BackendPermissions.PlatformOperationsManage)]
|
|
[EndpointSummary("重试平台后台任务")]
|
|
public async Task<ActionResult<BackgroundJobItem>> RetryJob(Guid jobId, CancellationToken cancellationToken)
|
|
{
|
|
return Ok(await backgroundJobService.RetryAsync(jobId, null, ResolveUserId(), cancellationToken));
|
|
}
|
|
|
|
private Guid ResolveUserId()
|
|
{
|
|
return currentUser.UserId ?? throw new InvalidOperationException("Current platform user was not resolved.");
|
|
}
|
|
} |