refactor(gateway): 使用值对象替代字符串类型属性
- GwRouteMatch: 路由匹配配置值对象(Path, Methods, Hosts, Headers, QueryParameters) - GwRouteHeader: Header 匹配规则值对象 - GwRouteQueryParameter: 查询参数匹配规则值对象 - GwLoadBalancingPolicy: 负载均衡策略枚举 - GwTransform: 请求/响应转换规则值对象 - EF Core 使用 ToJson() 将值对象映射为 JSON 列
This commit is contained in:
parent
0841d81318
commit
033fcc9e9b
@ -25,20 +25,20 @@ public class GwCluster
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 目标端点列表(内嵌)
|
/// 目标端点列表(内嵌)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<GwDestination> Destinations { get; set; } = new();
|
public List<GwDestination> Destinations { get; set; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 负载均衡策略
|
/// 负载均衡策略
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string LoadBalancingPolicy { get; set; } = "RoundRobin";
|
public GwLoadBalancingPolicy LoadBalancingPolicy { get; set; } = GwLoadBalancingPolicy.RoundRobin;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 健康检查配置
|
/// 健康检查配置(JSON 列存储)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GwHealthCheckConfig? HealthCheck { get; set; }
|
public GwHealthCheckConfig? HealthCheck { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 会话亲和配置
|
/// 会话亲和配置(JSON 列存储)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GwSessionAffinityConfig? SessionAffinity { get; set; }
|
public GwSessionAffinityConfig? SessionAffinity { get; set; }
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 负载均衡策略
|
||||||
|
/// </summary>
|
||||||
|
public enum GwLoadBalancingPolicy
|
||||||
|
{
|
||||||
|
RoundRobin = 0,
|
||||||
|
Random = 1,
|
||||||
|
PowerOfTwoChoices = 2,
|
||||||
|
LeastRequests = 3,
|
||||||
|
First = 4,
|
||||||
|
WeightedRoundRobin = 5
|
||||||
|
}
|
||||||
@ -0,0 +1,108 @@
|
|||||||
|
namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 路由匹配配置(值对象)
|
||||||
|
/// 对应 YARP 的 RouteMatch,以 JSON 存储在数据库中
|
||||||
|
/// </summary>
|
||||||
|
public class GwRouteMatch
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 路径匹配模式
|
||||||
|
/// </summary>
|
||||||
|
public string Path { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTTP 方法列表(如 ["GET", "POST"])
|
||||||
|
/// </summary>
|
||||||
|
public List<string>? Methods { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Host 匹配列表(如 ["api.example.com", "*.example.com"])
|
||||||
|
/// </summary>
|
||||||
|
public List<string>? Hosts { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header 匹配规则
|
||||||
|
/// </summary>
|
||||||
|
public List<GwRouteHeader>? Headers { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询参数匹配规则
|
||||||
|
/// </summary>
|
||||||
|
public List<GwRouteQueryParameter>? QueryParameters { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header 匹配规则(值对象)
|
||||||
|
/// </summary>
|
||||||
|
public class GwRouteHeader
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Header 名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 匹配值列表
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Values { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 匹配模式
|
||||||
|
/// </summary>
|
||||||
|
public GwHeaderMatchMode Mode { get; set; } = GwHeaderMatchMode.ExactHeader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否区分大小写
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCaseSensitive { get; set; } = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询参数匹配规则(值对象)
|
||||||
|
/// </summary>
|
||||||
|
public class GwRouteQueryParameter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 参数名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 匹配值列表
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Values { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 匹配模式
|
||||||
|
/// </summary>
|
||||||
|
public GwQueryParameterMatchMode Mode { get; set; } = GwQueryParameterMatchMode.Exact;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否区分大小写
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCaseSensitive { get; set; } = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header 匹配模式
|
||||||
|
/// </summary>
|
||||||
|
public enum GwHeaderMatchMode
|
||||||
|
{
|
||||||
|
ExactHeader = 0,
|
||||||
|
Prefix = 1,
|
||||||
|
Contains = 2,
|
||||||
|
NotContains = 3,
|
||||||
|
Exists = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询参数匹配模式
|
||||||
|
/// </summary>
|
||||||
|
public enum GwQueryParameterMatchMode
|
||||||
|
{
|
||||||
|
Exact = 0,
|
||||||
|
Contains = 1,
|
||||||
|
Prefix = 2,
|
||||||
|
Exists = 3
|
||||||
|
}
|
||||||
@ -23,15 +23,45 @@ public class GwTenantRoute
|
|||||||
public string ClusterId { get; set; } = string.Empty;
|
public string ClusterId { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 路径匹配模式
|
/// 路由匹配配置(JSON 列存储)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PathPattern { get; set; } = string.Empty;
|
public GwRouteMatch Match { get; set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 优先级
|
/// 优先级(对应 YARP Order,数值越小优先级越高)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Priority { get; set; } = 0;
|
public int Priority { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 路由级别负载均衡策略覆盖(可选,默认使用集群策略)
|
||||||
|
/// </summary>
|
||||||
|
public GwLoadBalancingPolicy? LoadBalancingPolicy { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 授权策略名称
|
||||||
|
/// </summary>
|
||||||
|
public string? AuthorizationPolicy { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CORS 策略名称
|
||||||
|
/// </summary>
|
||||||
|
public string? CorsPolicy { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 限流策略名称
|
||||||
|
/// </summary>
|
||||||
|
public string? RateLimiterPolicy { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求/响应转换规则(JSON 列存储)
|
||||||
|
/// </summary>
|
||||||
|
public List<GwTransform>? Transforms { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求超时时间(秒)
|
||||||
|
/// </summary>
|
||||||
|
public int? TimeoutSeconds { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 状态
|
/// 状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -71,45 +101,4 @@ public class GwTenantRoute
|
|||||||
/// 版本号,用于乐观并发
|
/// 版本号,用于乐观并发
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Version { get; set; } = 0;
|
public int Version { get; set; } = 0;
|
||||||
|
|
||||||
// ===== 路由匹配能力 =====
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HTTP 方法匹配(如 "GET,POST")
|
|
||||||
/// </summary>
|
|
||||||
public string? Methods { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Host 头匹配(如 "api.example.com")
|
|
||||||
/// </summary>
|
|
||||||
public string? Hosts { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Header 匹配规则(JSON 格式)
|
|
||||||
/// </summary>
|
|
||||||
public string? Headers { get; set; }
|
|
||||||
|
|
||||||
// ===== 策略配置 =====
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 路由级别负载均衡策略覆盖
|
|
||||||
/// </summary>
|
|
||||||
public string? LoadBalancingPolicy { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 授权策略
|
|
||||||
/// </summary>
|
|
||||||
public string? AuthorizationPolicy { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CORS 策略
|
|
||||||
/// </summary>
|
|
||||||
public string? CorsPolicy { get; set; }
|
|
||||||
|
|
||||||
// ===== 请求转换 =====
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 请求/响应转换规则(JSON 格式)
|
|
||||||
/// </summary>
|
|
||||||
public string? Transforms { get; set; }
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求/响应转换规则(值对象)
|
||||||
|
/// 以 JSON 列存储在数据库中
|
||||||
|
/// </summary>
|
||||||
|
public class GwTransform
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 转换规则键值对
|
||||||
|
/// 例如: {"RequestHeader": "X-Custom-Header", "Set": "value"}
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<string, string> Rules { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 常用转换规则工厂
|
||||||
|
/// </summary>
|
||||||
|
public static class GwTransforms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 设置请求头
|
||||||
|
/// </summary>
|
||||||
|
public static GwTransform SetRequestHeader(string headerName, string value)
|
||||||
|
=> new() { Rules = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "RequestHeader", headerName },
|
||||||
|
{ "Set", value }
|
||||||
|
}};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加请求头
|
||||||
|
/// </summary>
|
||||||
|
public static GwTransform AppendRequestHeader(string headerName, string value)
|
||||||
|
=> new() { Rules = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "RequestHeader", headerName },
|
||||||
|
{ "Append", value }
|
||||||
|
}};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置响应头
|
||||||
|
/// </summary>
|
||||||
|
public static GwTransform SetResponseHeader(string headerName, string value)
|
||||||
|
=> new() { Rules = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "ResponseHeader", headerName },
|
||||||
|
{ "Set", value }
|
||||||
|
}};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除路径前缀
|
||||||
|
/// </summary>
|
||||||
|
public static GwTransform PathRemovePrefix(string prefix)
|
||||||
|
=> new() { Rules = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "PathRemovePrefix", prefix }
|
||||||
|
}};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置路径前缀
|
||||||
|
/// </summary>
|
||||||
|
public static GwTransform PathSetPrefix(string prefix)
|
||||||
|
=> new() { Rules = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "PathPrefix", prefix }
|
||||||
|
}};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用原始 Host 头
|
||||||
|
/// </summary>
|
||||||
|
public static GwTransform RequestHeaderOriginalHost()
|
||||||
|
=> new() { Rules = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "RequestHeaderOriginalHost", "true" }
|
||||||
|
}};
|
||||||
|
}
|
||||||
@ -1,15 +1,12 @@
|
|||||||
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
|
||||||
|
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
|
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
||||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
||||||
namespace Fengling.Platform.Infrastructure;
|
namespace Fengling.Platform.Infrastructure;
|
||||||
|
|
||||||
|
|
||||||
public class PlatformDbContext(DbContextOptions options)
|
public class PlatformDbContext(DbContextOptions options)
|
||||||
: IdentityDbContext<ApplicationUser, ApplicationRole, long>(options)
|
: IdentityDbContext<ApplicationUser, ApplicationRole, long>(options)
|
||||||
{
|
{
|
||||||
@ -91,26 +88,57 @@ public class PlatformDbContext(DbContextOptions options)
|
|||||||
entity.Property(e => e.TenantCode).HasMaxLength(50);
|
entity.Property(e => e.TenantCode).HasMaxLength(50);
|
||||||
entity.Property(e => e.ServiceName).HasMaxLength(100).IsRequired();
|
entity.Property(e => e.ServiceName).HasMaxLength(100).IsRequired();
|
||||||
entity.Property(e => e.ClusterId).HasMaxLength(100).IsRequired();
|
entity.Property(e => e.ClusterId).HasMaxLength(100).IsRequired();
|
||||||
entity.Property(e => e.PathPattern).HasMaxLength(200).IsRequired();
|
entity.Property(e => e.AuthorizationPolicy).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.CorsPolicy).HasMaxLength(100);
|
||||||
|
entity.Property(e => e.RateLimiterPolicy).HasMaxLength(100);
|
||||||
|
|
||||||
|
// 枚举转换为字符串
|
||||||
|
entity.Property(e => e.LoadBalancingPolicy)
|
||||||
|
.HasConversion(
|
||||||
|
v => v.HasValue ? v.Value.ToString() : null,
|
||||||
|
v => v != null ? Enum.Parse<GwLoadBalancingPolicy>(v) : null
|
||||||
|
)
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
// 值对象映射为 JSON 列
|
||||||
|
entity.OwnsOne(e => e.Match, navigationBuilder =>
|
||||||
|
{
|
||||||
|
navigationBuilder.ToJson();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 转换规则映射为 JSON 列
|
||||||
|
entity.OwnsMany(e => e.Transforms, navigationBuilder =>
|
||||||
|
{
|
||||||
|
navigationBuilder.ToJson();
|
||||||
|
});
|
||||||
|
|
||||||
entity.HasIndex(e => e.TenantCode);
|
entity.HasIndex(e => e.TenantCode);
|
||||||
entity.HasIndex(e => e.ServiceName);
|
entity.HasIndex(e => e.ServiceName);
|
||||||
entity.HasIndex(e => e.ClusterId);
|
entity.HasIndex(e => e.ClusterId);
|
||||||
entity.HasIndex(e => new { e.ServiceName, e.IsGlobal, e.Status });
|
entity.HasIndex(e => new { e.ServiceName, e.IsGlobal, e.Status });
|
||||||
});
|
});
|
||||||
|
|
||||||
// GwCluster 聚合根配置 - 使用 Owned 类型
|
// GwCluster 聚合根配置
|
||||||
modelBuilder.Entity<GwCluster>(entity =>
|
modelBuilder.Entity<GwCluster>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasKey(e => e.Id);
|
entity.HasKey(e => e.Id);
|
||||||
entity.Property(e => e.ClusterId).HasMaxLength(100).IsRequired();
|
entity.Property(e => e.ClusterId).HasMaxLength(100).IsRequired();
|
||||||
entity.Property(e => e.Name).HasMaxLength(100).IsRequired();
|
entity.Property(e => e.Name).HasMaxLength(100).IsRequired();
|
||||||
entity.Property(e => e.Description).HasMaxLength(500);
|
entity.Property(e => e.Description).HasMaxLength(500);
|
||||||
entity.Property(e => e.LoadBalancingPolicy).HasMaxLength(50);
|
|
||||||
|
// 枚举转换为字符串
|
||||||
|
entity.Property(e => e.LoadBalancingPolicy)
|
||||||
|
.HasConversion(
|
||||||
|
v => v.ToString(),
|
||||||
|
v => Enum.Parse<GwLoadBalancingPolicy>(v)
|
||||||
|
)
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
entity.HasIndex(e => e.ClusterId).IsUnique();
|
entity.HasIndex(e => e.ClusterId).IsUnique();
|
||||||
entity.HasIndex(e => e.Name);
|
entity.HasIndex(e => e.Name);
|
||||||
entity.HasIndex(e => e.Status);
|
entity.HasIndex(e => e.Status);
|
||||||
|
|
||||||
// 配置内嵌的目标端点列表 - 使用 OwnedMany
|
// 配置内嵌的目标端点列表
|
||||||
entity.OwnsMany(e => e.Destinations, owned =>
|
entity.OwnsMany(e => e.Destinations, owned =>
|
||||||
{
|
{
|
||||||
owned.WithOwner().HasForeignKey("ClusterId");
|
owned.WithOwner().HasForeignKey("ClusterId");
|
||||||
@ -121,17 +149,16 @@ public class PlatformDbContext(DbContextOptions options)
|
|||||||
owned.HasIndex("ClusterId", "DestinationId");
|
owned.HasIndex("ClusterId", "DestinationId");
|
||||||
});
|
});
|
||||||
|
|
||||||
// 配置内嵌健康检查配置
|
// 配置内嵌健康检查配置(JSON 列)
|
||||||
entity.OwnsOne(e => e.HealthCheck, owned =>
|
entity.OwnsOne(e => e.HealthCheck, owned =>
|
||||||
{
|
{
|
||||||
owned.Property(h => h.Path).HasMaxLength(200);
|
owned.ToJson();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 配置内嵌会话亲和配置
|
// 配置内嵌会话亲和配置(JSON 列)
|
||||||
entity.OwnsOne(e => e.SessionAffinity, owned =>
|
entity.OwnsOne(e => e.SessionAffinity, owned =>
|
||||||
{
|
{
|
||||||
owned.Property(s => s.Policy).HasMaxLength(50);
|
owned.ToJson();
|
||||||
owned.Property(s => s.AffinityKeyName).HasMaxLength(50);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user