diff --git a/src/YhCore.WebApi/HealthChecks/DependencyHealthChecks.cs b/src/YhCore.WebApi/HealthChecks/DependencyHealthChecks.cs
new file mode 100644
index 0000000..ab303b0
--- /dev/null
+++ b/src/YhCore.WebApi/HealthChecks/DependencyHealthChecks.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using StackExchange.Redis;
+using YhCore.Repository;
+
+namespace YhCore.WebApi.HealthChecks
+{
+ ///
+ /// 数据库连通性检查:复用已注册的 YHDbContext(随 DatabaseProvider 自动适配 SqlServer/MySql)。
+ /// 每个检查自带 2 秒超时,防止连接池耗尽时探针自身挂死、反而探不出来。
+ ///
+ public class DbContextHealthCheck : IHealthCheck
+ {
+ private readonly YHDbContext _dbContext;
+ private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2);
+
+ public DbContextHealthCheck(YHDbContext dbContext)
+ {
+ _dbContext = dbContext;
+ }
+
+ public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ var probe = Task.Run(async () =>
+ {
+ await _dbContext.Database.OpenConnectionAsync(cancellationToken);
+ using var command = _dbContext.Database.GetDbConnection().CreateCommand();
+ command.CommandText = "SELECT 1";
+ await command.ExecuteScalarAsync(cancellationToken);
+ }, CancellationToken.None);
+
+ var finished = await Task.WhenAny(probe, Task.Delay(Timeout, CancellationToken.None));
+ if (finished != probe)
+ return HealthCheckResult.Unhealthy("database probe timed out after 2s");
+
+ await probe; // 传播异常
+ return HealthCheckResult.Healthy();
+ }
+ catch (Exception ex)
+ {
+ return HealthCheckResult.Unhealthy("database probe failed: " + ex.Message, ex);
+ }
+ }
+ }
+
+ ///
+ /// Redis 连通性检查:复用全局单例 IConnectionMultiplexer(YhCore.Common.Cache 注册)。
+ ///
+ public class RedisHealthCheck : IHealthCheck
+ {
+ private readonly IConnectionMultiplexer _multiplexer;
+ private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2);
+
+ public RedisHealthCheck(IConnectionMultiplexer multiplexer)
+ {
+ _multiplexer = multiplexer;
+ }
+
+ public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ if (!_multiplexer.IsConnected)
+ return HealthCheckResult.Unhealthy("redis connection is not established");
+
+ var probe = _multiplexer.GetDatabase().PingAsync();
+ var finished = await Task.WhenAny(probe, Task.Delay(Timeout, CancellationToken.None));
+ if (finished != probe)
+ return HealthCheckResult.Unhealthy("redis ping timed out after 2s");
+
+ var latency = await probe;
+ return HealthCheckResult.Healthy("ping " + latency);
+ }
+ catch (Exception ex)
+ {
+ return HealthCheckResult.Unhealthy("redis probe failed: " + ex.Message, ex);
+ }
+ }
+ }
+}
diff --git a/src/YhCore.WebApi/Startup.cs b/src/YhCore.WebApi/Startup.cs
index b486b13..ea5c05b 100644
--- a/src/YhCore.WebApi/Startup.cs
+++ b/src/YhCore.WebApi/Startup.cs
@@ -205,6 +205,9 @@ namespace YhCore.WebApi
{
endpoints.MapControllers();
+ // 免认证健康探针:健康 200 / 不健康 503(容器与负载均衡用)
+ endpoints.MapHealthChecks("/health");
+
// 根路径登录状态页:SSO 开启时未登录会先被中间件 302 到 CAS;登录后在此显示当前用户。
endpoints.MapGet("/", async context =>
{