diff --git a/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwCluster.cs b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwCluster.cs
index 9139bd6..f268df1 100644
--- a/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwCluster.cs
+++ b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwCluster.cs
@@ -25,20 +25,20 @@ public class GwCluster
///
/// 目标端点列表(内嵌)
///
- public List Destinations { get; set; } = new();
+ public List Destinations { get; set; } = [];
///
/// 负载均衡策略
///
- public string LoadBalancingPolicy { get; set; } = "RoundRobin";
+ public GwLoadBalancingPolicy LoadBalancingPolicy { get; set; } = GwLoadBalancingPolicy.RoundRobin;
///
- /// 健康检查配置
+ /// 健康检查配置(JSON 列存储)
///
public GwHealthCheckConfig? HealthCheck { get; set; }
///
- /// 会话亲和配置
+ /// 会话亲和配置(JSON 列存储)
///
public GwSessionAffinityConfig? SessionAffinity { get; set; }
@@ -76,4 +76,4 @@ public class GwCluster
/// 版本号,用于乐观并发
///
public int Version { get; set; } = 0;
-}
+}
\ No newline at end of file
diff --git a/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwLoadBalancingPolicy.cs b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwLoadBalancingPolicy.cs
new file mode 100644
index 0000000..05ca97d
--- /dev/null
+++ b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwLoadBalancingPolicy.cs
@@ -0,0 +1,14 @@
+namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
+
+///
+/// 负载均衡策略
+///
+public enum GwLoadBalancingPolicy
+{
+ RoundRobin = 0,
+ Random = 1,
+ PowerOfTwoChoices = 2,
+ LeastRequests = 3,
+ First = 4,
+ WeightedRoundRobin = 5
+}
\ No newline at end of file
diff --git a/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwRouteMatch.cs b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwRouteMatch.cs
new file mode 100644
index 0000000..69a5704
--- /dev/null
+++ b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwRouteMatch.cs
@@ -0,0 +1,108 @@
+namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
+
+///
+/// 路由匹配配置(值对象)
+/// 对应 YARP 的 RouteMatch,以 JSON 存储在数据库中
+///
+public class GwRouteMatch
+{
+ ///
+ /// 路径匹配模式
+ ///
+ public string Path { get; set; } = string.Empty;
+
+ ///
+ /// HTTP 方法列表(如 ["GET", "POST"])
+ ///
+ public List? Methods { get; set; }
+
+ ///
+ /// Host 匹配列表(如 ["api.example.com", "*.example.com"])
+ ///
+ public List? Hosts { get; set; }
+
+ ///
+ /// Header 匹配规则
+ ///
+ public List? Headers { get; set; }
+
+ ///
+ /// 查询参数匹配规则
+ ///
+ public List? QueryParameters { get; set; }
+}
+
+///
+/// Header 匹配规则(值对象)
+///
+public class GwRouteHeader
+{
+ ///
+ /// Header 名称
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 匹配值列表
+ ///
+ public List Values { get; set; } = [];
+
+ ///
+ /// 匹配模式
+ ///
+ public GwHeaderMatchMode Mode { get; set; } = GwHeaderMatchMode.ExactHeader;
+
+ ///
+ /// 是否区分大小写
+ ///
+ public bool IsCaseSensitive { get; set; } = false;
+}
+
+///
+/// 查询参数匹配规则(值对象)
+///
+public class GwRouteQueryParameter
+{
+ ///
+ /// 参数名称
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// 匹配值列表
+ ///
+ public List Values { get; set; } = [];
+
+ ///
+ /// 匹配模式
+ ///
+ public GwQueryParameterMatchMode Mode { get; set; } = GwQueryParameterMatchMode.Exact;
+
+ ///
+ /// 是否区分大小写
+ ///
+ public bool IsCaseSensitive { get; set; } = false;
+}
+
+///
+/// Header 匹配模式
+///
+public enum GwHeaderMatchMode
+{
+ ExactHeader = 0,
+ Prefix = 1,
+ Contains = 2,
+ NotContains = 3,
+ Exists = 4
+}
+
+///
+/// 查询参数匹配模式
+///
+public enum GwQueryParameterMatchMode
+{
+ Exact = 0,
+ Contains = 1,
+ Prefix = 2,
+ Exists = 3
+}
\ No newline at end of file
diff --git a/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenantRoute.cs b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenantRoute.cs
index 660a7ea..467f09d 100644
--- a/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenantRoute.cs
+++ b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenantRoute.cs
@@ -23,15 +23,45 @@ public class GwTenantRoute
public string ClusterId { get; set; } = string.Empty;
///
- /// 路径匹配模式
+ /// 路由匹配配置(JSON 列存储)
///
- public string PathPattern { get; set; } = string.Empty;
+ public GwRouteMatch Match { get; set; } = new();
///
- /// 优先级
+ /// 优先级(对应 YARP Order,数值越小优先级越高)
///
public int Priority { get; set; } = 0;
+ ///
+ /// 路由级别负载均衡策略覆盖(可选,默认使用集群策略)
+ ///
+ public GwLoadBalancingPolicy? LoadBalancingPolicy { get; set; }
+
+ ///
+ /// 授权策略名称
+ ///
+ public string? AuthorizationPolicy { get; set; }
+
+ ///
+ /// CORS 策略名称
+ ///
+ public string? CorsPolicy { get; set; }
+
+ ///
+ /// 限流策略名称
+ ///
+ public string? RateLimiterPolicy { get; set; }
+
+ ///
+ /// 请求/响应转换规则(JSON 列存储)
+ ///
+ public List? Transforms { get; set; }
+
+ ///
+ /// 请求超时时间(秒)
+ ///
+ public int? TimeoutSeconds { get; set; }
+
///
/// 状态
///
@@ -71,45 +101,4 @@ public class GwTenantRoute
/// 版本号,用于乐观并发
///
public int Version { get; set; } = 0;
-
- // ===== 路由匹配能力 =====
-
- ///
- /// HTTP 方法匹配(如 "GET,POST")
- ///
- public string? Methods { get; set; }
-
- ///
- /// Host 头匹配(如 "api.example.com")
- ///
- public string? Hosts { get; set; }
-
- ///
- /// Header 匹配规则(JSON 格式)
- ///
- public string? Headers { get; set; }
-
- // ===== 策略配置 =====
-
- ///
- /// 路由级别负载均衡策略覆盖
- ///
- public string? LoadBalancingPolicy { get; set; }
-
- ///
- /// 授权策略
- ///
- public string? AuthorizationPolicy { get; set; }
-
- ///
- /// CORS 策略
- ///
- public string? CorsPolicy { get; set; }
-
- // ===== 请求转换 =====
-
- ///
- /// 请求/响应转换规则(JSON 格式)
- ///
- public string? Transforms { get; set; }
-}
+}
\ No newline at end of file
diff --git a/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTransform.cs b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTransform.cs
new file mode 100644
index 0000000..4922fd7
--- /dev/null
+++ b/Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTransform.cs
@@ -0,0 +1,77 @@
+namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
+
+///
+/// 请求/响应转换规则(值对象)
+/// 以 JSON 列存储在数据库中
+///
+public class GwTransform
+{
+ ///
+ /// 转换规则键值对
+ /// 例如: {"RequestHeader": "X-Custom-Header", "Set": "value"}
+ ///
+ public Dictionary Rules { get; set; } = new();
+}
+
+///
+/// 常用转换规则工厂
+///
+public static class GwTransforms
+{
+ ///
+ /// 设置请求头
+ ///
+ public static GwTransform SetRequestHeader(string headerName, string value)
+ => new() { Rules = new Dictionary
+ {
+ { "RequestHeader", headerName },
+ { "Set", value }
+ }};
+
+ ///
+ /// 添加请求头
+ ///
+ public static GwTransform AppendRequestHeader(string headerName, string value)
+ => new() { Rules = new Dictionary
+ {
+ { "RequestHeader", headerName },
+ { "Append", value }
+ }};
+
+ ///
+ /// 设置响应头
+ ///
+ public static GwTransform SetResponseHeader(string headerName, string value)
+ => new() { Rules = new Dictionary
+ {
+ { "ResponseHeader", headerName },
+ { "Set", value }
+ }};
+
+ ///
+ /// 移除路径前缀
+ ///
+ public static GwTransform PathRemovePrefix(string prefix)
+ => new() { Rules = new Dictionary
+ {
+ { "PathRemovePrefix", prefix }
+ }};
+
+ ///
+ /// 设置路径前缀
+ ///
+ public static GwTransform PathSetPrefix(string prefix)
+ => new() { Rules = new Dictionary
+ {
+ { "PathPrefix", prefix }
+ }};
+
+ ///
+ /// 使用原始 Host 头
+ ///
+ public static GwTransform RequestHeaderOriginalHost()
+ => new() { Rules = new Dictionary
+ {
+ { "RequestHeaderOriginalHost", "true" }
+ }};
+}
\ No newline at end of file
diff --git a/Fengling.Platform.Infrastructure/PlatformDbContext.cs b/Fengling.Platform.Infrastructure/PlatformDbContext.cs
index bdd6bfa..b5b71bc 100644
--- a/Fengling.Platform.Infrastructure/PlatformDbContext.cs
+++ b/Fengling.Platform.Infrastructure/PlatformDbContext.cs
@@ -1,15 +1,12 @@
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
-
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
-
namespace Fengling.Platform.Infrastructure;
-
public class PlatformDbContext(DbContextOptions options)
: IdentityDbContext(options)
{
@@ -91,26 +88,57 @@ public class PlatformDbContext(DbContextOptions options)
entity.Property(e => e.TenantCode).HasMaxLength(50);
entity.Property(e => e.ServiceName).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(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.ServiceName);
entity.HasIndex(e => e.ClusterId);
entity.HasIndex(e => new { e.ServiceName, e.IsGlobal, e.Status });
});
- // GwCluster 聚合根配置 - 使用 Owned 类型
+ // GwCluster 聚合根配置
modelBuilder.Entity(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.ClusterId).HasMaxLength(100).IsRequired();
entity.Property(e => e.Name).HasMaxLength(100).IsRequired();
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(v)
+ )
+ .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");
@@ -121,21 +149,20 @@ public class PlatformDbContext(DbContextOptions options)
owned.HasIndex("ClusterId", "DestinationId");
});
- // 配置内嵌健康检查配置
+ // 配置内嵌健康检查配置(JSON 列)
entity.OwnsOne(e => e.HealthCheck, owned =>
{
- owned.Property(h => h.Path).HasMaxLength(200);
+ owned.ToJson();
});
- // 配置内嵌会话亲和配置
+ // 配置内嵌会话亲和配置(JSON 列)
entity.OwnsOne(e => e.SessionAffinity, owned =>
{
- owned.Property(s => s.Policy).HasMaxLength(50);
- owned.Property(s => s.AffinityKeyName).HasMaxLength(50);
+ owned.ToJson();
});
});
modelBuilder.ApplyConfigurationsFromAssembly(typeof(PlatformDbContext).Assembly);
base.OnModelCreating(modelBuilder);
}
-}
+}
\ No newline at end of file