docs(phase-03): add research and 4 execution plans for gateway restructuring

This commit is contained in:
movingsam 2026-03-03 15:24:43 +08:00
parent 8dce917105
commit 75b0f9bd35
6 changed files with 839 additions and 0 deletions

View File

View File

@ -0,0 +1,325 @@
# Phase 3: 调整网关部分的需求 - 技术研究
**日期:** 2026-03-03
**状态:** 研究完成
---
## 1. EF Core Owned Entity 配置模式
### GwDestination 内嵌实体
```csharp
// 在 GwCluster 中配置 Owned Entity
modelBuilder.Entity<GwCluster>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.ClusterId).HasMaxLength(100).IsRequired();
entity.HasIndex(e => e.ClusterId).IsUnique();
// 配置内嵌的 Destinations 列表
entity.OwnsMany(e => e.Destinations, dest =>
{
dest.WithOwner().HasForeignKey("GwClusterId");
dest.Property(d => d.DestinationId).HasMaxLength(100).IsRequired();
dest.Property(d => d.Address).HasMaxLength(200).IsRequired();
dest.Property(d => d.Health).HasMaxLength(200);
dest.Property(d => d.Weight).HasDefaultValue(1);
dest.Property(d => d.HealthStatus).HasDefaultValue(1);
dest.Property(d => d.Status).HasDefaultValue(1);
// 复合唯一索引
dest.HasIndex(d => new { d.DestinationId }).IsUnique();
});
});
```
### GwHealthCheckConfig 内嵌值对象
```csharp
entity.OwnsOne(e => e.HealthCheck, hc =>
{
hc.Property(h => h.Enabled).HasDefaultValue(false);
hc.Property(h => h.Path).HasMaxLength(100).HasDefaultValue("/health");
hc.Property(h => h.IntervalSeconds).HasDefaultValue(30);
hc.Property(h => h.TimeoutSeconds).HasDefaultValue(10);
});
```
### GwSessionAffinityConfig 内嵌值对象
```csharp
entity.OwnsOne(e => e.SessionAffinity, sa =>
{
sa.Property(s => s.Enabled).HasDefaultValue(false);
sa.Property(s => s.Policy).HasMaxLength(50).HasDefaultValue("Header");
sa.Property(s => s.AffinityKeyName).HasMaxLength(100).HasDefaultValue("X-Session-Key");
});
```
---
## 2. 实体迁移策略
### 删除现有实体
```csharp
// 1. 从 PlatformDbContext 中移除 DbSet
public DbSet<GwTenant> GwTenants => Set<GwTenant>(); // 删除
public DbSet<GwServiceInstance> GwServiceInstances => Set<GwServiceInstance>(); // 删除
// 2. 移除 OnModelCreating 中的配置
// modelBuilder.Entity<GwTenant>(...) - 删除
// modelBuilder.Entity<GwServiceInstance>(...) - 删除
```
### 创建 EF Core 迁移
```bash
# 创建迁移
dotnet ef migrations add RestructureGatewayEntities --project Fengling.Platform.Infrastructure
# 迁移将执行:
# - DROP TABLE GwTenants
# - DROP TABLE GwServiceInstances
# - CREATE TABLE GwClusters (包含 Destinations 作为 JSON 或关联表)
# - ALTER TABLE GwTenantRoutes (添加新字段)
```
---
## 3. GwCluster → YARP ClusterConfig 映射
```csharp
public static ClusterConfig ToClusterConfig(this GwCluster cluster)
{
return new ClusterConfig
{
ClusterId = cluster.ClusterId,
LoadBalancingPolicy = cluster.LoadBalancingPolicy ?? "PowerOfTwoChoices",
Destinations = cluster.Destinations
.Where(d => d.Status == 1)
.ToDictionary(
d => d.DestinationId,
d => new DestinationConfig
{
Address = d.Address,
Health = d.Health,
Metadata = new Dictionary<string, string>
{
["Weight"] = d.Weight.ToString()
}
}
),
HealthCheck = cluster.HealthCheck?.Enabled == true
? new HealthCheckConfig
{
Active = new ActiveHealthCheckConfig
{
Enabled = true,
Path = cluster.HealthCheck.Path ?? "/health",
Interval = TimeSpan.FromSeconds(cluster.HealthCheck.IntervalSeconds),
Timeout = TimeSpan.FromSeconds(cluster.HealthCheck.TimeoutSeconds)
}
}
: null,
SessionAffinity = cluster.SessionAffinity?.Enabled == true
? new SessionAffinityConfig
{
Enabled = true,
Policy = cluster.SessionAffinity.Policy,
AffinityKeyName = cluster.SessionAffinity.AffinityKeyName
}
: null
};
}
```
---
## 4. GwTenantRoute → YARP RouteConfig 映射
```csharp
public static RouteConfig ToRouteConfig(this GwTenantRoute route)
{
return new RouteConfig
{
RouteId = route.Id,
Match = new RouteMatch
{
Path = route.PathPattern,
Methods = route.Methods?.Split(',').ToList(),
Hosts = route.Hosts?.Split(',').ToList(),
Headers = ParseHeaderMatch(route.Headers)
},
ClusterId = route.ClusterId,
Order = route.Priority,
Metadata = new Dictionary<string, string>
{
["TenantCode"] = route.TenantCode,
["ServiceName"] = route.ServiceName,
["IsGlobal"] = route.IsGlobal.ToString()
},
Transforms = ParseTransforms(route.Transforms)
};
}
private static IReadOnlyList<RouteHeader>? ParseHeaderMatch(string? headersJson)
{
if (string.IsNullOrEmpty(headersJson)) return null;
// 解析 JSON 格式的 Header 匹配规则
// [{"Name":"X-Custom","Values":["value1"],"Mode":"ExactHeader"}]
return JsonSerializer.Deserialize<List<RouteHeader>>(headersJson);
}
private static IReadOnlyList<IReadOnlyDictionary<string, string>>? ParseTransforms(string? transformsJson)
{
if (string.IsNullOrEmpty(transformsJson)) return null;
// 解析 JSON 格式的转换规则
return JsonSerializer.Deserialize<List<Dictionary<string, string>>>(transformsJson);
}
```
---
## 5. IClusterStore 接口设计
```csharp
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? name = null,
ClusterStatus? status = null,
CancellationToken cancellationToken = default);
Task<int> GetCountAsync(
string? name = null,
ClusterStatus? status = null,
CancellationToken cancellationToken = default);
// CRUD 操作
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 管理
Task<IdentityResult> AddDestinationAsync(string clusterId, GwDestination destination, CancellationToken cancellationToken = default);
Task<IdentityResult> UpdateDestinationAsync(string clusterId, GwDestination destination, CancellationToken cancellationToken = default);
Task<IdentityResult> RemoveDestinationAsync(string clusterId, string destinationId, CancellationToken cancellationToken = default);
}
```
---
## 6. 会话亲和实现
### 中间件:设置会话键
```csharp
public class SessionAffinityMiddleware
{
private readonly RequestDelegate _next;
public SessionAffinityMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 优先使用 UserId
var userId = context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
// 其次使用 TenantCode
var tenantCode = context.User?.FindFirst("TenantCode")?.Value
?? context.Request.Headers["X-Tenant-Code"].FirstOrDefault();
// 设置会话亲和键
var sessionKey = userId ?? tenantCode ?? "anonymous";
context.Items["SessionAffinityKey"] = sessionKey;
// 添加到请求头供 YARP 使用
context.Request.Headers["X-Session-Key"] = sessionKey;
await _next(context);
}
}
```
---
## 7. 依赖关系图
```
┌─────────────────────────────────────────────────────────────┐
│ Wave 1 │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ GwCluster.cs │ │ GwTenantRoute │ │
│ │ GwDestination │ │ (扩展字段) │ │
│ │ 值对象 │ │ │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
└───────────┼────────────────────┼────────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Wave 2 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PlatformDbContext.cs │ │
│ │ - 移除 GwTenant, GwServiceInstance DbSet │ │
│ │ - 添加 GwCluster DbSet │ │
│ │ - 配置 Owned Entities │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Wave 3 │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ IClusterStore │ │ IRouteStore │ │
│ │ ClusterStore │ │ RouteStore │ │
│ │ (替换Instance) │ │ (更新) │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Extensions.cs │ │
│ │ - 移除 IInstanceStore 注册 │ │
│ │ - 添加 IClusterStore 注册 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## 8. 风险评估
| 风险 | 影响 | 缓解措施 |
|------|------|----------|
| 删除 GwTenant 导致数据丢失 | 高 | 确认无数据后再删除,或提供迁移脚本 |
| 删除 GwServiceInstance 导致数据丢失 | 高 | 同上 |
| EF Core 迁移失败 | 中 | 手动编写 SQL 迁移脚本 |
| Owned Entity 查询性能 | 低 | EF Core 8+ 已优化 |
---
## 9. 参考资源
- [EF Core Owned Entities](https://learn.microsoft.com/ef/core/modeling/owned-entities)
- [YARP Configuration](https://microsoft.github.io/reverse-proxy/)
- [YARP GitHub](https://github.com/microsoft/reverse-proxy)
- 项目文档: `docs/yarp-configuration-model.md`
---
*研究完成日期: 2026-03-03*

View File

@ -0,0 +1,207 @@
---
phase: 03-
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwCluster.cs
- Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwDestination.cs
- Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwHealthCheckConfig.cs
- Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwSessionAffinityConfig.cs
autonomous: true
requirements:
- GATEWAY-RESTRUCTURE-01
- GATEWAY-RESTRUCTURE-02
must_haves:
truths:
- "GwCluster 使用 string Id (GUID)"
- "GwDestination 作为 Owned Entity 内嵌"
- "值对象使用 Owned Entity 配置"
artifacts:
- path: "Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwCluster.cs"
provides: "集群聚合根"
min_lines: 50
- path: "Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwDestination.cs"
provides: "目标端点值对象"
min_lines: 30
- path: "Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwHealthCheckConfig.cs"
provides: "健康检查配置值对象"
min_lines: 20
- path: "Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwSessionAffinityConfig.cs"
provides: "会话亲和配置值对象"
min_lines: 20
---
# 计划 01: 创建 GwCluster 聚合根和值对象
## 目标
创建新的 GwCluster 聚合根及相关值对象,替代原有的 GwServiceInstance 实体设计。
**目的:** 将服务实例管理改为集群聚合根模式,内嵌 Destinations 列表,符合 YARP ClusterConfig 结构。
**输出:** GwCluster 聚合根、GwDestination 值对象、GwHealthCheckConfig 值对象、GwSessionAffinityConfig 值对象。
## 上下文
@.planning/phases/03-/03-CONTEXT.md
@.planning/phases/03-/03-RESEARCH.md
@Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenantRoute.cs (现有实体参考)
@Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GatewayEnums.cs (枚举)
## 任务
<task type="auto">
<name>任务 1: 创建 GwHealthCheckConfig 值对象</name>
<files>Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwHealthCheckConfig.cs</files>
<action>
创建健康检查配置值对象:
```csharp
namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
/// <summary>
/// 健康检查配置(值对象)
/// </summary>
public class GwHealthCheckConfig
{
/// <summary>
/// 是否启用健康检查
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// 健康检查路径
/// </summary>
public string? Path { get; set; }
/// <summary>
/// 检查间隔(秒)
/// </summary>
public int IntervalSeconds { get; set; } = 30;
/// <summary>
/// 超时时间(秒)
/// </summary>
public int TimeoutSeconds { get; set; } = 10;
}
```
</action>
<verify>文件可编译</verify>
<done>GwHealthCheckConfig 值对象已创建</done>
</task>
<task type="auto">
<name>任务 2: 创建 GwSessionAffinityConfig 值对象</name>
<files>Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwSessionAffinityConfig.cs</files>
<action>
创建会话亲和配置值对象:
```csharp
namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
/// <summary>
/// 会话亲和配置(值对象)
/// </summary>
public class GwSessionAffinityConfig
{
/// <summary>
/// 是否启用会话亲和
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// 策略Header, Cookie
/// </summary>
public string Policy { get; set; } = "Header";
/// <summary>
/// 亲和键名称
/// </summary>
public string AffinityKeyName { get; set; } = "X-Session-Key";
}
```
</action>
<verify>文件可编译</verify>
<done>GwSessionAffinityConfig 值对象已创建</done>
</task>
<task type="auto">
<name>任务 3: 创建 GwDestination 值对象</name>
<files>Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwDestination.cs</files>
<action>
创建目标端点值对象:
```csharp
namespace Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
/// <summary>
/// 目标端点(值对象,内嵌于 GwCluster
/// </summary>
public class GwDestination
{
/// <summary>
/// 目标标识
/// </summary>
public string DestinationId { get; set; } = string.Empty;
/// <summary>
/// 后端地址
/// </summary>
public string Address { get; set; } = string.Empty;
/// <summary>
/// 健康检查端点
/// </summary>
public string? Health { get; set; }
/// <summary>
/// 权重(用于加权负载均衡)
/// </summary>
public int Weight { get; set; } = 1;
/// <summary>
/// 健康状态
/// </summary>
public int HealthStatus { get; set; } = 1;
/// <summary>
/// 状态
/// </summary>
public int Status { get; set; } = 1;
}
```
</action>
<verify>文件可编译</verify>
<done>GwDestination 值对象已创建</done>
</task>
<task type="auto">
<name>任务 4: 创建 GwCluster 聚合根</name>
<files>Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwCluster.cs</files>
<action>
创建 GwCluster 聚合根:
- Id: string (GUID)
- ClusterId: string (业务标识)
- Name: string
- Description: string?
- Destinations: List&lt;GwDestination&gt; (内嵌)
- LoadBalancingPolicy: string
- HealthCheck: GwHealthCheckConfig?
- SessionAffinity: GwSessionAffinityConfig?
- Status: int
- 审计字段
参考现有 GwTenantRoute 的结构风格。
</action>
<verify>dotnet build Fengling.Platform.Domain 通过</verify>
<done>GwCluster 聚合根已创建,包含所有字段</done>
</task>
## 验证
- [ ] 所有 4 个文件已创建
- [ ] Build 无错误通过
- [ ] 值对象结构符合 YARP 配置模型
## 成功标准
Domain 实体准备好进行 Infrastructure 层更新。

View File

@ -0,0 +1,74 @@
---
phase: 03-
plan: 04
type: execute
wave: 3
depends_on:
- 03-gateway-infrastructure-update
files_modified:
- Fengling.Platform.Infrastructure/Extensions.cs
- Fengling.Platform.Infrastructure/GatewayExtensions.cs
autonomous: true
requirements:
- GATEWAY-RESTRUCTURE-07
must_haves:
truths:
- "Extensions.cs 注册 IClusterStore 而非 IInstanceStore"
- "GatewayExtensions 可独立使用"
---
# 计划 04: 更新 Extensions 和 DI 注册
## 目标
更新 Extensions.cs 以注册新的 IClusterStore清理旧的 IInstanceStore 注册。
**目的:** 完成 DI 容器配置的更新,使新服务可被注入使用。
**输出:** 更新的 Extensions.cs。
## 上下文
@Fengling.Platform.Infrastructure/Extensions.cs
@Fengling.Platform.Infrastructure/GatewayExtensions.cs
## 任务
<task type="auto">
<name>任务 1: 更新 Extensions.cs</name>
<files>Fengling.Platform.Infrastructure/Extensions.cs</files>
<action>
在 AddPlatformCore 方法中:
1. 移除旧注册:
- 移除 services.AddScoped&lt;IInstanceStore, InstanceStore&lt;TContext&gt;&gt;()
- 移除 services.AddScoped&lt;IRouteManager, RouteManager&gt;()(如果之前在 Gateway 部分)
2. 添加新注册:
- 添加 services.AddScoped&lt;IClusterStore, ClusterStore&lt;TContext&gt;&gt;()
注意:保持与现有 GatewayExtensions 的兼容性。
</action>
<verify>dotnet build 通过</verify>
<done>Extensions.cs 已更新</done>
</task>
<task type="auto">
<name>任务 2: 更新 GatewayExtensions.cs如需要</name>
<files>Fengling.Platform.Infrastructure/GatewayExtensions.cs</files>
<action>
检查 GatewayExtensions.cs
- 如果已注册 IClusterStore确保与 Extensions.cs 一致
- 如果需要,添加注释说明两种注册方式
</action>
<verify>dotnet build 通过</verify>
<done>GatewayExtensions 检查完成</done>
</task>
## 验证
- [ ] Extensions.cs 已更新
- [ ] Build 无错误通过
## 成功标准
Gateway 模块重构完成,所有服务正确注册。

View File

@ -0,0 +1,128 @@
---
phase: 03-
plan: 03
type: execute
wave: 2
depends_on:
- 03-gateway-cluster-entities
- 03-gateway-route-update
files_modified:
- Fengling.Platform.Infrastructure/PlatformDbContext.cs
- Fengling.Platform.Infrastructure/IInstanceStore.cs
- Fengling.Platform.Infrastructure/InstanceStore.cs
- Fengling.Platform.Infrastructure/IClusterStore.cs
- Fengling.Platform.Infrastructure/ClusterStore.cs
autonomous: true
requirements:
- GATEWAY-RESTRUCTURE-05
- GATEWAY-RESTRUCTURE-06
must_haves:
truths:
- "PlatformDbContext 包含GwCluster DbSet"
- "IClusterStore 替代 IInstanceStore"
artifacts:
- path: "Fengling.Platform.Infrastructure/PlatformDbContext.cs"
provides: "更新的 DbContext"
- path: "Fengling.Platform.Infrastructure/IClusterStore.cs"
provides: "集群存储接口"
min_lines: 40
---
# 计划 03: 更新 Infrastructure 层
## 目标
更新 PlatformDbContext 配置,创建 IClusterStore 接口和实现,替换原有的 IInstanceStore。
**目的:** 支持新的 GwCluster 聚合根,移除已废弃的实体 DbSet。
**输出:** 更新的 PlatformDbContext、IClusterStore/ClusterStore。
## 上下文
@Fengling.Platform.Infrastructure/PlatformDbContext.cs
@Fengling.Platform.Infrastructure/IInstanceStore.cs
@Fengling.Platform.Infrastructure/RouteStore.cs (参考模式)
## 任务
<task type="auto">
<name>任务 1: 更新 PlatformDbContext</name>
<files>Fengling.Platform.Infrastructure/PlatformDbContext.cs</files>
<action>
1. 移除 DbSet
- 移除 DbSet&lt;GwTenant&gt; GwTenants
- 移除 DbSet&lt;GwServiceInstance&gt; GwServiceInstances
2. 添加 DbSet
- 添加 DbSet&lt;GwCluster&gt; GwClusters
3. 在 OnModelCreating 中配置:
- GwCluster 聚合根配置
- OwnsMany GwDestinations 配置
- OwnsOne GwHealthCheckConfig 配置
- OwnsOne GwSessionAffinityConfig 配置
参考 03-RESEARCH.md 中的 EF Core 配置代码。
</action>
<verify>dotnet build 通过</verify>
<done>PlatformDbContext 已更新</done>
</task>
<task type="auto">
<name>任务 2: 创建 IClusterStore 接口</name>
<files>Fengling.Platform.Infrastructure/IClusterStore.cs</files>
<action>
创建 IClusterStore 接口,包含:
- FindByIdAsync, FindByClusterIdAsync
- GetAllAsync, GetPagedAsync, GetCountAsync
- CreateAsync, UpdateAsync, DeleteAsync
- AddDestinationAsync, UpdateDestinationAsync, RemoveDestinationAsync
参考 IRouteStore 模式。
</action>
<verify>文件可编译</verify>
<done>IClusterStore 接口已创建</done>
</task>
<task type="auto">
<name>任务 3: 创建 ClusterStore 实现</name>
<files>Fengling.Platform.Infrastructure/ClusterStore.cs</files>
<action>
创建 ClusterStore&lt;TContext&gt; 实现 IClusterStore
- 泛型约束: where TContext : PlatformDbContext
- 实现所有接口方法
- 支持软删除
- 支持内嵌 Destination 的 CRUD
参考 RouteStore 模式。
</action>
<verify>dotnet build 通过</verify>
<done>ClusterStore 实现已创建</done>
</task>
<task type="auto">
<name>任务 4: 删除 IInstanceStore 和 InstanceStore</name>
<files>
Fengling.Platform.Infrastructure/IInstanceStore.cs
Fengling.Platform.Infrastructure/InstanceStore.cs
</files>
<action>
删除这两个文件:
- 已被 IClusterStore/ClusterStore 替代
- 确认无其他引用
</action>
<verify>dotnet build 通过</verify>
<done>旧文件已删除</done>
</task>
## 验证
- [ ] PlatformDbContext 已更新
- [ ] IClusterStore/ClusterStore 已创建
- [ ] 旧文件已删除
- [ ] Build 无错误通过
## 成功标准
Infrastructure 层更新完成,准备更新 DI 注册。

View File

@ -0,0 +1,105 @@
---
phase: 03-
plan: 02
type: execute
wave: 1
depends_on:
- 03-gateway-cluster-entities
files_modified:
- Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenantRoute.cs
- Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenant.cs
- Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwServiceInstance.cs
autonomous: true
requirements:
- GATEWAY-RESTRUCTURE-03
- GATEWAY-RESTRUCTURE-04
must_haves:
truths:
- "GwTenantRoute 扩展了 Methods, Hosts, Headers, Transforms 等字段"
- "旧实体 GwTenant 和 GwServiceInstance 标记为删除或移除"
artifacts:
- path: "Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenantRoute.cs"
provides: "扩展的路由实体"
min_lines: 50
---
# 计划 02: 扩展 GwTenantRoute 并删除旧实体
## 目标
扩展 GwTenantRoute 实体以支持完整的路由匹配能力和转换规则,并删除已废弃的 GwTenant 和 GwServiceInstance 实体。
**目的:** 添加 YARP 所需的路由匹配字段,同时清理不再需要的实体。
**输出:** 更新的 GwTenantRoute.cs删除的 GwTenant.cs 和 GwServiceInstance.cs。
## 上下文
@.planning/phases/03-/03-CONTEXT.md
@Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenantRoute.cs (现有)
## 任务
<task type="auto">
<name>任务 1: 扩展 GwTenantRoute 字段</name>
<files>Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenantRoute.cs</files>
<action>
在 GwTenantRoute 中添加以下新字段:
1. 路由匹配能力:
- Methods?: string (HTTP 方法,如 "GET,POST")
- Hosts?: string (Host 头匹配,如 "api.example.com")
- Headers?: string (Header 匹配规则JSON 格式)
2. 策略配置:
- LoadBalancingPolicy?: string (路由级别负载均衡策略覆盖)
- AuthorizationPolicy?: string (授权策略)
- CorsPolicy?: string (CORS 策略)
3. 请求转换:
- Transforms?: string (请求/响应转换规则JSON 格式)
保留现有字段Id, TenantCode, ServiceName, ClusterId, PathPattern, Priority, Status, IsGlobal
</action>
<verify>dotnet build 通过</verify>
<done>GwTenantRoute 已扩展新字段</done>
</task>
<task type="auto">
<name>任务 2: 删除 GwTenant 实体</name>
<files>
Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwTenant.cs
</files>
<action>
删除 GwTenant.cs 文件:
- 原因:使用 Platform.Tenant 通过 TenantCode 关联
- 确认无其他依赖引用此实体
</action>
<verify>dotnet build 通过</verify>
<done>GwTenant 实体已删除</done>
</task>
<task type="auto">
<name>任务 3: 删除 GwServiceInstance 实体</name>
<files>
Fengling.Platform.Domain/AggregatesModel/GatewayAggregate/GwServiceInstance.cs
</files>
<action>
删除 GwServiceInstance.cs 文件:
- 原因:改用 GwCluster 聚合根内嵌 Destination
- 确认无其他依赖引用此实体
</action>
<verify>dotnet build 通过</verify>
<done>GwServiceInstance 实体已删除</done>
</task>
## 验证
- [ ] GwTenantRoute 扩展字段已添加
- [ ] GwTenant.cs 已删除
- [ ] GwServiceInstance.cs 已删除
- [ ] Build 无错误通过
## 成功标准
Domain 层实体重构完成,准备进行 Infrastructure 层更新。