diff --git a/Fengling.Platform.Infrastructure/ClusterStore.cs b/Fengling.Platform.Infrastructure/ClusterStore.cs
new file mode 100644
index 0000000..f7a294d
--- /dev/null
+++ b/Fengling.Platform.Infrastructure/ClusterStore.cs
@@ -0,0 +1,153 @@
+using Microsoft.AspNetCore.Identity;
+using Microsoft.EntityFrameworkCore;
+using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
+
+namespace Fengling.Platform.Infrastructure;
+
+///
+/// 集群存储实现
+///
+public class ClusterStore : IClusterStore
+ where TContext : PlatformDbContext
+{
+ private readonly TContext _context;
+ private readonly DbSet _clusters;
+
+ public ClusterStore(TContext context)
+ {
+ _context = context;
+ _clusters = context.GwClusters;
+ }
+
+ public void Dispose() { }
+
+ public virtual Task FindByIdAsync(string? id, CancellationToken cancellationToken = default)
+ {
+ if (id == null) return Task.FromResult(null);
+ return _clusters.FirstOrDefaultAsync(c => c.Id == id, cancellationToken);
+ }
+
+ public virtual Task FindByClusterIdAsync(string clusterId, CancellationToken cancellationToken = default)
+ {
+ return _clusters.FirstOrDefaultAsync(c => c.ClusterId == clusterId && !c.IsDeleted, cancellationToken);
+ }
+
+ public virtual async Task> GetAllAsync(CancellationToken cancellationToken = default)
+ {
+ return await _clusters.Where(c => !c.IsDeleted).ToListAsync(cancellationToken);
+ }
+
+ public virtual async Task> GetPagedAsync(int page, int pageSize, string? clusterId = null,
+ string? name = null, int? status = null, CancellationToken cancellationToken = default)
+ {
+ var query = _clusters.AsQueryable();
+
+ if (!string.IsNullOrEmpty(clusterId))
+ query = query.Where(c => c.ClusterId.Contains(clusterId));
+
+ if (!string.IsNullOrEmpty(name))
+ query = query.Where(c => c.Name.Contains(name));
+
+ if (status.HasValue)
+ query = query.Where(c => c.Status == status.Value);
+
+ return await query
+ .Where(c => !c.IsDeleted)
+ .OrderByDescending(c => c.CreatedTime)
+ .Skip((page - 1) * pageSize)
+ .Take(pageSize)
+ .ToListAsync(cancellationToken);
+ }
+
+ public virtual async Task GetCountAsync(string? clusterId = null, string? name = null,
+ int? status = null, CancellationToken cancellationToken = default)
+ {
+ var query = _clusters.AsQueryable();
+
+ if (!string.IsNullOrEmpty(clusterId))
+ query = query.Where(c => c.ClusterId.Contains(clusterId));
+
+ if (!string.IsNullOrEmpty(name))
+ query = query.Where(c => c.Name.Contains(name));
+
+ if (status.HasValue)
+ query = query.Where(c => c.Status == status.Value);
+
+ return await query.Where(c => !c.IsDeleted).CountAsync(cancellationToken);
+ }
+
+ public virtual async Task CreateAsync(GwCluster cluster, CancellationToken cancellationToken = default)
+ {
+ _clusters.Add(cluster);
+ await _context.SaveChangesAsync(cancellationToken);
+ return IdentityResult.Success;
+ }
+
+ public virtual async Task UpdateAsync(GwCluster cluster, CancellationToken cancellationToken = default)
+ {
+ cluster.UpdatedTime = DateTime.UtcNow;
+ _clusters.Update(cluster);
+ await _context.SaveChangesAsync(cancellationToken);
+ return IdentityResult.Success;
+ }
+
+ public virtual async Task DeleteAsync(GwCluster cluster, CancellationToken cancellationToken = default)
+ {
+ // 软删除
+ cluster.IsDeleted = true;
+ cluster.UpdatedTime = DateTime.UtcNow;
+ _clusters.Update(cluster);
+ await _context.SaveChangesAsync(cancellationToken);
+ return IdentityResult.Success;
+ }
+
+ public virtual async Task AddDestinationAsync(string clusterId, GwDestination destination, CancellationToken cancellationToken = default)
+ {
+ var cluster = await _clusters.FirstOrDefaultAsync(c => c.ClusterId == clusterId && !c.IsDeleted, cancellationToken);
+ if (cluster == null) return null;
+
+ cluster.Destinations.Add(destination);
+ cluster.UpdatedTime = DateTime.UtcNow;
+ await _context.SaveChangesAsync(cancellationToken);
+ return cluster;
+ }
+
+ public virtual async Task UpdateDestinationAsync(string clusterId, string destinationId, GwDestination destination, CancellationToken cancellationToken = default)
+ {
+ var cluster = await _clusters
+ .Include(c => c.Destinations)
+ .FirstOrDefaultAsync(c => c.ClusterId == clusterId && !c.IsDeleted, cancellationToken);
+
+ if (cluster == null) return null;
+
+ var existingDest = cluster.Destinations.FirstOrDefault(d => d.DestinationId == destinationId);
+ if (existingDest == null) return null;
+
+ existingDest.Address = destination.Address;
+ existingDest.Health = destination.Health;
+ existingDest.Weight = destination.Weight;
+ existingDest.HealthStatus = destination.HealthStatus;
+ existingDest.Status = destination.Status;
+
+ cluster.UpdatedTime = DateTime.UtcNow;
+ await _context.SaveChangesAsync(cancellationToken);
+ return cluster;
+ }
+
+ public virtual async Task RemoveDestinationAsync(string clusterId, string destinationId, CancellationToken cancellationToken = default)
+ {
+ var cluster = await _clusters
+ .Include(c => c.Destinations)
+ .FirstOrDefaultAsync(c => c.ClusterId == clusterId && !c.IsDeleted, cancellationToken);
+
+ if (cluster == null) return null;
+
+ var destination = cluster.Destinations.FirstOrDefault(d => d.DestinationId == destinationId);
+ if (destination == null) return null;
+
+ cluster.Destinations.Remove(destination);
+ cluster.UpdatedTime = DateTime.UtcNow;
+ await _context.SaveChangesAsync(cancellationToken);
+ return cluster;
+ }
+}
diff --git a/Fengling.Platform.Infrastructure/Extensions.cs b/Fengling.Platform.Infrastructure/Extensions.cs
index f52b040..cf27382 100644
--- a/Fengling.Platform.Infrastructure/Extensions.cs
+++ b/Fengling.Platform.Infrastructure/Extensions.cs
@@ -25,11 +25,11 @@ public static class Extensions
// Gateway 服务
services.AddScoped>();
- services.AddScoped>();
+ services.AddScoped>();
services.AddScoped();
serviceAction?.Invoke(services);
return services;
}
-}
\ No newline at end of file
+}
diff --git a/Fengling.Platform.Infrastructure/GatewayExtensions.cs b/Fengling.Platform.Infrastructure/GatewayExtensions.cs
index 4cea94b..91313bc 100644
--- a/Fengling.Platform.Infrastructure/GatewayExtensions.cs
+++ b/Fengling.Platform.Infrastructure/GatewayExtensions.cs
@@ -18,11 +18,11 @@ public static class GatewayExtensions
{
// 注册 Gateway stores
services.AddScoped>();
- services.AddScoped>();
+ services.AddScoped>();
// 注册 Gateway managers
services.AddScoped();
return services;
}
-}
\ No newline at end of file
+}
diff --git a/Fengling.Platform.Infrastructure/IClusterStore.cs b/Fengling.Platform.Infrastructure/IClusterStore.cs
new file mode 100644
index 0000000..f5fff4a
--- /dev/null
+++ b/Fengling.Platform.Infrastructure/IClusterStore.cs
@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Identity;
+
+using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
+
+namespace Fengling.Platform.Infrastructure;
+
+///
+/// 集群存储接口
+///
+public interface IClusterStore
+{
+ Task FindByIdAsync(string? id, CancellationToken cancellationToken = default);
+ Task FindByClusterIdAsync(string clusterId, CancellationToken cancellationToken = default);
+ Task> GetAllAsync(CancellationToken cancellationToken = default);
+ Task> GetPagedAsync(int page, int pageSize, string? clusterId = null,
+ string? name = null, int? status = null, CancellationToken cancellationToken = default);
+ Task GetCountAsync(string? clusterId = null, string? name = null,
+ int? status = null, CancellationToken cancellationToken = default);
+ Task CreateAsync(GwCluster cluster, CancellationToken cancellationToken = default);
+ Task UpdateAsync(GwCluster cluster, CancellationToken cancellationToken = default);
+ Task DeleteAsync(GwCluster cluster, CancellationToken cancellationToken = default);
+
+ // Destination management
+ Task AddDestinationAsync(string clusterId, GwDestination destination, CancellationToken cancellationToken = default);
+ Task UpdateDestinationAsync(string clusterId, string destinationId, GwDestination destination, CancellationToken cancellationToken = default);
+ Task RemoveDestinationAsync(string clusterId, string destinationId, CancellationToken cancellationToken = default);
+}
diff --git a/Fengling.Platform.Infrastructure/IInstanceStore.cs b/Fengling.Platform.Infrastructure/IInstanceStore.cs
deleted file mode 100644
index 7e145aa..0000000
--- a/Fengling.Platform.Infrastructure/IInstanceStore.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Microsoft.AspNetCore.Identity;
-
-using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
-
-namespace Fengling.Platform.Infrastructure;
-
-///
-/// 服务实例存储接口
-///
-public interface IInstanceStore
-{
- Task FindByIdAsync(string? id, CancellationToken cancellationToken = default);
- Task FindByClusterIdAsync(string clusterId, CancellationToken cancellationToken = default);
- Task FindByDestinationAsync(string clusterId, string destinationId, CancellationToken cancellationToken = default);
- Task> GetAllAsync(CancellationToken cancellationToken = default);
- Task> GetPagedAsync(int page, int pageSize, string? clusterId = null,
- InstanceHealth? health = null, InstanceStatus? status = null, CancellationToken cancellationToken = default);
- Task GetCountAsync(string? clusterId = null,
- InstanceHealth? health = null, InstanceStatus? status = null, CancellationToken cancellationToken = default);
- Task CreateAsync(GwServiceInstance instance, CancellationToken cancellationToken = default);
- Task UpdateAsync(GwServiceInstance instance, CancellationToken cancellationToken = default);
- Task DeleteAsync(GwServiceInstance instance, CancellationToken cancellationToken = default);
-}
diff --git a/Fengling.Platform.Infrastructure/InstanceStore.cs b/Fengling.Platform.Infrastructure/InstanceStore.cs
deleted file mode 100644
index a48c7b5..0000000
--- a/Fengling.Platform.Infrastructure/InstanceStore.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using Microsoft.AspNetCore.Identity;
-using Microsoft.EntityFrameworkCore;
-using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
-
-namespace Fengling.Platform.Infrastructure;
-
-///
-/// 服务实例存储实现
-///
-public class InstanceStore : IInstanceStore
- where TContext : PlatformDbContext
-{
- private readonly TContext _context;
- private readonly DbSet _instances;
-
- public InstanceStore(TContext context)
- {
- _context = context;
- _instances = context.GwServiceInstances;
- }
-
- public void Dispose() { }
-
- public virtual Task FindByIdAsync(string? id, CancellationToken cancellationToken = default)
- {
- if (id == null) return Task.FromResult(null);
- return _instances.FirstOrDefaultAsync(i => i.Id == id, cancellationToken);
- }
-
- public virtual Task FindByClusterIdAsync(string clusterId, CancellationToken cancellationToken = default)
- {
- return _instances.FirstOrDefaultAsync(i => i.ClusterId == clusterId && !i.IsDeleted, cancellationToken);
- }
-
- public virtual Task FindByDestinationAsync(string clusterId, string destinationId, CancellationToken cancellationToken = default)
- {
- return _instances.FirstOrDefaultAsync(i => i.ClusterId == clusterId && i.DestinationId == destinationId && !i.IsDeleted, cancellationToken);
- }
-
- public virtual async Task> GetAllAsync(CancellationToken cancellationToken = default)
- {
- return await _instances.Where(i => !i.IsDeleted).ToListAsync(cancellationToken);
- }
-
- public virtual async Task> GetPagedAsync(int page, int pageSize, string? clusterId = null,
- InstanceHealth? health = null, InstanceStatus? status = null, CancellationToken cancellationToken = default)
- {
- var query = _instances.AsQueryable();
-
- if (!string.IsNullOrEmpty(clusterId))
- query = query.Where(i => i.ClusterId.Contains(clusterId));
-
- if (health.HasValue)
- query = query.Where(i => i.Health == (int)health.Value);
-
- if (status.HasValue)
- query = query.Where(i => i.Status == (int)status.Value);
-
- return await query
- .Where(i => !i.IsDeleted)
- .OrderByDescending(i => i.CreatedTime)
- .Skip((page - 1) * pageSize)
- .Take(pageSize)
- .ToListAsync(cancellationToken);
- }
-
- public virtual async Task GetCountAsync(string? clusterId = null,
- InstanceHealth? health = null, InstanceStatus? status = null, CancellationToken cancellationToken = default)
- {
- var query = _instances.AsQueryable();
-
- if (!string.IsNullOrEmpty(clusterId))
- query = query.Where(i => i.ClusterId.Contains(clusterId));
-
- if (health.HasValue)
- query = query.Where(i => i.Health == (int)health.Value);
-
- if (status.HasValue)
- query = query.Where(i => i.Status == (int)status.Value);
-
- return await query.Where(i => !i.IsDeleted).CountAsync(cancellationToken);
- }
-
- public virtual async Task CreateAsync(GwServiceInstance instance, CancellationToken cancellationToken = default)
- {
- _instances.Add(instance);
- await _context.SaveChangesAsync(cancellationToken);
- return IdentityResult.Success;
- }
-
- public virtual async Task UpdateAsync(GwServiceInstance instance, CancellationToken cancellationToken = default)
- {
- instance.UpdatedTime = DateTime.UtcNow;
- _instances.Update(instance);
- await _context.SaveChangesAsync(cancellationToken);
- return IdentityResult.Success;
- }
-
- public virtual async Task DeleteAsync(GwServiceInstance instance, CancellationToken cancellationToken = default)
- {
- // 软删除
- instance.IsDeleted = true;
- instance.UpdatedTime = DateTime.UtcNow;
- _instances.Update(instance);
- await _context.SaveChangesAsync(cancellationToken);
- return IdentityResult.Success;
- }
-}
diff --git a/Fengling.Platform.Infrastructure/PlatformDbContext.cs b/Fengling.Platform.Infrastructure/PlatformDbContext.cs
index 584346f..bdd6bfa 100644
--- a/Fengling.Platform.Infrastructure/PlatformDbContext.cs
+++ b/Fengling.Platform.Infrastructure/PlatformDbContext.cs
@@ -18,9 +18,8 @@ public class PlatformDbContext(DbContextOptions options)
public DbSet AuditLogs => Set();
// Gateway 实体
- public DbSet GwTenants => Set();
public DbSet GwTenantRoutes => Set();
- public DbSet GwServiceInstances => Set();
+ public DbSet GwClusters => Set();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -86,14 +85,6 @@ public class PlatformDbContext(DbContextOptions options)
});
// Gateway 实体配置
- modelBuilder.Entity(entity =>
- {
- entity.HasKey(e => e.Id);
- entity.Property(e => e.TenantCode).HasMaxLength(50).IsRequired();
- entity.Property(e => e.TenantName).HasMaxLength(100).IsRequired();
- entity.HasIndex(e => e.TenantCode).IsUnique();
- });
-
modelBuilder.Entity(entity =>
{
entity.HasKey(e => e.Id);
@@ -107,14 +98,41 @@ public class PlatformDbContext(DbContextOptions options)
entity.HasIndex(e => new { e.ServiceName, e.IsGlobal, e.Status });
});
- modelBuilder.Entity(entity =>
+ // GwCluster 聚合根配置 - 使用 Owned 类型
+ modelBuilder.Entity(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.ClusterId).HasMaxLength(100).IsRequired();
- entity.Property(e => e.DestinationId).HasMaxLength(100).IsRequired();
- entity.Property(e => e.Address).HasMaxLength(200).IsRequired();
- entity.HasIndex(e => new { e.ClusterId, e.DestinationId }).IsUnique();
- entity.HasIndex(e => e.Health);
+ entity.Property(e => e.Name).HasMaxLength(100).IsRequired();
+ entity.Property(e => e.Description).HasMaxLength(500);
+ entity.Property(e => e.LoadBalancingPolicy).HasMaxLength(50);
+ entity.HasIndex(e => e.ClusterId).IsUnique();
+ entity.HasIndex(e => e.Name);
+ entity.HasIndex(e => e.Status);
+
+ // 配置内嵌的目标端点列表 - 使用 OwnedMany
+ entity.OwnsMany(e => e.Destinations, owned =>
+ {
+ owned.WithOwner().HasForeignKey("ClusterId");
+ owned.Property("ClusterId").HasMaxLength(100);
+ owned.Property(d => d.DestinationId).HasMaxLength(100).IsRequired();
+ owned.Property(d => d.Address).HasMaxLength(200).IsRequired();
+ owned.Property(d => d.Health).HasMaxLength(200);
+ owned.HasIndex("ClusterId", "DestinationId");
+ });
+
+ // 配置内嵌健康检查配置
+ entity.OwnsOne(e => e.HealthCheck, owned =>
+ {
+ owned.Property(h => h.Path).HasMaxLength(200);
+ });
+
+ // 配置内嵌会话亲和配置
+ entity.OwnsOne(e => e.SessionAffinity, owned =>
+ {
+ owned.Property(s => s.Policy).HasMaxLength(50);
+ owned.Property(s => s.AffinityKeyName).HasMaxLength(50);
+ });
});
modelBuilder.ApplyConfigurationsFromAssembly(typeof(PlatformDbContext).Assembly);