feat(03-gateway-infrastructure-update): update Infrastructure layer for GwCluster
- Updated PlatformDbContext: removed GwTenant/GwServiceInstance DbSets, added GwCluster with EF Core config - Created IClusterStore interface with CRUD and Destination management methods - Created ClusterStore<TContext> implementation with soft delete and embedded Destinations support - Deleted obsolete IInstanceStore and InstanceStore (replaced by IClusterStore) - Updated Extensions.cs and GatewayExtensions.cs to register IClusterStore Plan 03 of Phase 03 complete.
This commit is contained in:
parent
b058c3ea56
commit
a6558137af
153
Fengling.Platform.Infrastructure/ClusterStore.cs
Normal file
153
Fengling.Platform.Infrastructure/ClusterStore.cs
Normal file
@ -0,0 +1,153 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
||||
|
||||
namespace Fengling.Platform.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// 集群存储实现
|
||||
/// </summary>
|
||||
public class ClusterStore<TContext> : IClusterStore
|
||||
where TContext : PlatformDbContext
|
||||
{
|
||||
private readonly TContext _context;
|
||||
private readonly DbSet<GwCluster> _clusters;
|
||||
|
||||
public ClusterStore(TContext context)
|
||||
{
|
||||
_context = context;
|
||||
_clusters = context.GwClusters;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public virtual Task<GwCluster?> FindByIdAsync(string? id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (id == null) return Task.FromResult<GwCluster?>(null);
|
||||
return _clusters.FirstOrDefaultAsync(c => c.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public virtual Task<GwCluster?> FindByClusterIdAsync(string clusterId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _clusters.FirstOrDefaultAsync(c => c.ClusterId == clusterId && !c.IsDeleted, cancellationToken);
|
||||
}
|
||||
|
||||
public virtual async Task<IList<GwCluster>> GetAllAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _clusters.Where(c => !c.IsDeleted).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public virtual async Task<IList<GwCluster>> 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<int> 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<IdentityResult> CreateAsync(GwCluster cluster, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_clusters.Add(cluster);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
public virtual async Task<IdentityResult> UpdateAsync(GwCluster cluster, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cluster.UpdatedTime = DateTime.UtcNow;
|
||||
_clusters.Update(cluster);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
public virtual async Task<IdentityResult> 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<GwCluster?> 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<GwCluster?> 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<GwCluster?> 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;
|
||||
}
|
||||
}
|
||||
@ -25,11 +25,11 @@ public static class Extensions
|
||||
|
||||
// Gateway 服务
|
||||
services.AddScoped<IRouteStore, RouteStore<TContext>>();
|
||||
services.AddScoped<IInstanceStore, InstanceStore<TContext>>();
|
||||
services.AddScoped<IClusterStore, ClusterStore<TContext>>();
|
||||
services.AddScoped<IRouteManager, RouteManager>();
|
||||
|
||||
serviceAction?.Invoke(services);
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,11 +18,11 @@ public static class GatewayExtensions
|
||||
{
|
||||
// 注册 Gateway stores
|
||||
services.AddScoped<IRouteStore, RouteStore<TContext>>();
|
||||
services.AddScoped<IInstanceStore, InstanceStore<TContext>>();
|
||||
services.AddScoped<IClusterStore, ClusterStore<TContext>>();
|
||||
|
||||
// 注册 Gateway managers
|
||||
services.AddScoped<IRouteManager, RouteManager>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
Fengling.Platform.Infrastructure/IClusterStore.cs
Normal file
27
Fengling.Platform.Infrastructure/IClusterStore.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
||||
|
||||
namespace Fengling.Platform.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// 集群存储接口
|
||||
/// </summary>
|
||||
public interface IClusterStore
|
||||
{
|
||||
Task<GwCluster?> FindByIdAsync(string? id, CancellationToken cancellationToken = default);
|
||||
Task<GwCluster?> FindByClusterIdAsync(string clusterId, CancellationToken cancellationToken = default);
|
||||
Task<IList<GwCluster>> GetAllAsync(CancellationToken cancellationToken = default);
|
||||
Task<IList<GwCluster>> GetPagedAsync(int page, int pageSize, string? clusterId = null,
|
||||
string? name = null, int? status = null, CancellationToken cancellationToken = default);
|
||||
Task<int> GetCountAsync(string? clusterId = null, string? name = null,
|
||||
int? status = null, CancellationToken cancellationToken = default);
|
||||
Task<IdentityResult> CreateAsync(GwCluster cluster, CancellationToken cancellationToken = default);
|
||||
Task<IdentityResult> UpdateAsync(GwCluster cluster, CancellationToken cancellationToken = default);
|
||||
Task<IdentityResult> DeleteAsync(GwCluster cluster, CancellationToken cancellationToken = default);
|
||||
|
||||
// Destination management
|
||||
Task<GwCluster?> AddDestinationAsync(string clusterId, GwDestination destination, CancellationToken cancellationToken = default);
|
||||
Task<GwCluster?> UpdateDestinationAsync(string clusterId, string destinationId, GwDestination destination, CancellationToken cancellationToken = default);
|
||||
Task<GwCluster?> RemoveDestinationAsync(string clusterId, string destinationId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
||||
|
||||
namespace Fengling.Platform.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// 服务实例存储接口
|
||||
/// </summary>
|
||||
public interface IInstanceStore
|
||||
{
|
||||
Task<GwServiceInstance?> FindByIdAsync(string? id, CancellationToken cancellationToken = default);
|
||||
Task<GwServiceInstance?> FindByClusterIdAsync(string clusterId, CancellationToken cancellationToken = default);
|
||||
Task<GwServiceInstance?> FindByDestinationAsync(string clusterId, string destinationId, CancellationToken cancellationToken = default);
|
||||
Task<IList<GwServiceInstance>> GetAllAsync(CancellationToken cancellationToken = default);
|
||||
Task<IList<GwServiceInstance>> GetPagedAsync(int page, int pageSize, string? clusterId = null,
|
||||
InstanceHealth? health = null, InstanceStatus? status = null, CancellationToken cancellationToken = default);
|
||||
Task<int> GetCountAsync(string? clusterId = null,
|
||||
InstanceHealth? health = null, InstanceStatus? status = null, CancellationToken cancellationToken = default);
|
||||
Task<IdentityResult> CreateAsync(GwServiceInstance instance, CancellationToken cancellationToken = default);
|
||||
Task<IdentityResult> UpdateAsync(GwServiceInstance instance, CancellationToken cancellationToken = default);
|
||||
Task<IdentityResult> DeleteAsync(GwServiceInstance instance, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@ -1,108 +0,0 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
||||
|
||||
namespace Fengling.Platform.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// 服务实例存储实现
|
||||
/// </summary>
|
||||
public class InstanceStore<TContext> : IInstanceStore
|
||||
where TContext : PlatformDbContext
|
||||
{
|
||||
private readonly TContext _context;
|
||||
private readonly DbSet<GwServiceInstance> _instances;
|
||||
|
||||
public InstanceStore(TContext context)
|
||||
{
|
||||
_context = context;
|
||||
_instances = context.GwServiceInstances;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public virtual Task<GwServiceInstance?> FindByIdAsync(string? id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (id == null) return Task.FromResult<GwServiceInstance?>(null);
|
||||
return _instances.FirstOrDefaultAsync(i => i.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public virtual Task<GwServiceInstance?> FindByClusterIdAsync(string clusterId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _instances.FirstOrDefaultAsync(i => i.ClusterId == clusterId && !i.IsDeleted, cancellationToken);
|
||||
}
|
||||
|
||||
public virtual Task<GwServiceInstance?> 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<IList<GwServiceInstance>> GetAllAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _instances.Where(i => !i.IsDeleted).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public virtual async Task<IList<GwServiceInstance>> 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<int> 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<IdentityResult> CreateAsync(GwServiceInstance instance, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_instances.Add(instance);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
public virtual async Task<IdentityResult> UpdateAsync(GwServiceInstance instance, CancellationToken cancellationToken = default)
|
||||
{
|
||||
instance.UpdatedTime = DateTime.UtcNow;
|
||||
_instances.Update(instance);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
public virtual async Task<IdentityResult> DeleteAsync(GwServiceInstance instance, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 软删除
|
||||
instance.IsDeleted = true;
|
||||
instance.UpdatedTime = DateTime.UtcNow;
|
||||
_instances.Update(instance);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
}
|
||||
@ -18,9 +18,8 @@ public class PlatformDbContext(DbContextOptions options)
|
||||
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
|
||||
|
||||
// Gateway 实体
|
||||
public DbSet<GwTenant> GwTenants => Set<GwTenant>();
|
||||
public DbSet<GwTenantRoute> GwTenantRoutes => Set<GwTenantRoute>();
|
||||
public DbSet<GwServiceInstance> GwServiceInstances => Set<GwServiceInstance>();
|
||||
public DbSet<GwCluster> GwClusters => Set<GwCluster>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@ -86,14 +85,6 @@ public class PlatformDbContext(DbContextOptions options)
|
||||
});
|
||||
|
||||
// Gateway 实体配置
|
||||
modelBuilder.Entity<GwTenant>(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<GwTenantRoute>(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<GwServiceInstance>(entity =>
|
||||
// GwCluster 聚合根配置 - 使用 Owned 类型
|
||||
modelBuilder.Entity<GwCluster>(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<string>("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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user