refactor(gateway): adapt to Platform 1.0.12 entity changes
All checks were successful
Build and Push Docker / build (push) Successful in 6m44s
All checks were successful
Build and Push Docker / build (push) Successful in 6m44s
- Remove IInstanceStore DI registration (replaced by IClusterStore) - Remove GwTenant and GwServiceInstance from ConsoleDbContext config - Update GatewayService to use Match.Path instead of PathPattern - Cast RouteStatus enum to int for Status field - Add 04-SUMMARY.md documentation BREAKING CHANGE: Gateway entity API changes in Platform 1.0.12
This commit is contained in:
parent
da1048c3ae
commit
154484d2dc
78
.planning/phases/04-gateway-entity-update/04-SUMMARY.md
Normal file
78
.planning/phases/04-gateway-entity-update/04-SUMMARY.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Phase 4 总结:适配 Platform 1.0.12 Gateway 实体变更
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本次 Phase 4 成功完成了 Fengling Console 对 Platform 1.0.12 Gateway 实体变更的适配工作。
|
||||||
|
|
||||||
|
## 主要变更
|
||||||
|
|
||||||
|
### 1. Program.cs 依赖注入更新
|
||||||
|
|
||||||
|
**变更内容:**
|
||||||
|
- 移除了 `IInstanceStore` 和 `InstanceStore` 的注册
|
||||||
|
- 保留了 `IClusterStore` 和 `ClusterStore` 的注册
|
||||||
|
|
||||||
|
**变更原因:**
|
||||||
|
Platform 1.0.12 移除了 `IInstanceStore` 接口,实例(Destination)现在是 `GwCluster` 的内嵌对象。
|
||||||
|
|
||||||
|
### 2. ConsoleDbContext 实体配置清理
|
||||||
|
|
||||||
|
**变更内容:**
|
||||||
|
- 移除了 `GwTenant` 实体配置(原平台中已移除)
|
||||||
|
- 移除了 `GwServiceInstance` 实体配置(已重构为 GwDestination)
|
||||||
|
|
||||||
|
### 3. GatewayService 实体属性适配
|
||||||
|
|
||||||
|
**变更内容:**
|
||||||
|
|
||||||
|
| 旧属性 | 新属性 | 说明 |
|
||||||
|
|--------|--------|------|
|
||||||
|
| `GwTenantRoute.PathPattern` (string) | `GwTenantRoute.Match.Path` ( GwRouteMatch.Path ) | 路由匹配配置从简单字符串升级为复杂对象 |
|
||||||
|
| `Status = RouteStatus.Active` (enum) | `Status = (int)RouteStatus.Active` (int) | Status 字段为 int 类型,需要显式转换枚举 |
|
||||||
|
|
||||||
|
**具体代码变更:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 旧代码
|
||||||
|
new GwTenantRoute
|
||||||
|
{
|
||||||
|
PathPattern = pathPattern,
|
||||||
|
Status = RouteStatus.Active,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新代码
|
||||||
|
new GwTenantRoute
|
||||||
|
{
|
||||||
|
Match = new GwRouteMatch { Path = pathPattern },
|
||||||
|
Status = (int)RouteStatus.Active,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 旧代码读取
|
||||||
|
r.PathPattern
|
||||||
|
r.Status
|
||||||
|
|
||||||
|
// 新代码读取
|
||||||
|
r.Match.Path
|
||||||
|
r.Status (已是 int)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 编译结果
|
||||||
|
|
||||||
|
✅ 编译成功,0 个错误,3 个警告(警告为预存在的代码质量问题,与本次变更无关)
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
- [x] `dotnet build` 通过
|
||||||
|
- [x] 无新增编译错误
|
||||||
|
|
||||||
|
## 相关文档
|
||||||
|
|
||||||
|
- 实体变更详情:`.planning/docs/gateway-entity-changes-1.0.12.md`
|
||||||
|
|
||||||
|
## 下一步
|
||||||
|
|
||||||
|
可以考虑的改进:
|
||||||
|
1. 修复 TenantService.cs 中的警告(roleManager 参数未使用)
|
||||||
|
2. 完善 GatewayService 中的空值处理(Match 可能为 null)
|
||||||
@ -24,21 +24,11 @@ public class ConsoleDbContext : PlatformDbContext
|
|||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
// ========== Gateway 模块 ==========
|
// ========== Gateway 模块 ==========
|
||||||
modelBuilder.Entity<GwTenant>(entity =>
|
|
||||||
{
|
|
||||||
entity.ToTable("gw_tenants");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity<GwTenantRoute>(entity =>
|
modelBuilder.Entity<GwTenantRoute>(entity =>
|
||||||
{
|
{
|
||||||
entity.ToTable("gw_tenant_routes");
|
entity.ToTable("gw_tenant_routes");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<GwServiceInstance>(entity =>
|
|
||||||
{
|
|
||||||
entity.ToTable("gw_service_instances");
|
|
||||||
});
|
|
||||||
|
|
||||||
// ========== Tenant 模块 ==========
|
// ========== Tenant 模块 ==========
|
||||||
modelBuilder.Entity<Tenant>(entity =>
|
modelBuilder.Entity<Tenant>(entity =>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Fengling.Console.Data;
|
using Fengling.Console.Data;
|
||||||
using Fengling.Console.Services;
|
using Fengling.Console.Services;
|
||||||
using Fengling.Console.Services;
|
|
||||||
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
|
||||||
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
|
||||||
using Fengling.Platform.Infrastructure;
|
using Fengling.Platform.Infrastructure;
|
||||||
@ -57,11 +56,8 @@ builder.Services.AddScoped<ITenantManager, TenantManager>();
|
|||||||
|
|
||||||
// Register Gateway managers
|
// Register Gateway managers
|
||||||
builder.Services.AddScoped<IRouteStore, RouteStore<ConsoleDbContext>>();
|
builder.Services.AddScoped<IRouteStore, RouteStore<ConsoleDbContext>>();
|
||||||
builder.Services.AddScoped<IInstanceStore, InstanceStore<ConsoleDbContext>>();
|
builder.Services.AddScoped<IClusterStore, ClusterStore<PlatformDbContext>>();
|
||||||
builder.Services.AddScoped<IInstanceStore, InstanceStore<PlatformDbContext>>();
|
|
||||||
builder.Services.AddScoped<IRouteManager, RouteManager>();
|
builder.Services.AddScoped<IRouteManager, RouteManager>();
|
||||||
builder.Services.AddScoped<ITenantStore, TenantStore<ConsoleDbContext>>();
|
|
||||||
builder.Services.AddScoped<ITenantManager, TenantManager>();
|
|
||||||
|
|
||||||
builder.Services.AddScoped<IUserService, UserService>();
|
builder.Services.AddScoped<IUserService, UserService>();
|
||||||
builder.Services.AddScoped<ITenantService, TenantService>();
|
builder.Services.AddScoped<ITenantService, TenantService>();
|
||||||
|
|||||||
@ -23,38 +23,46 @@ public interface IGatewayService
|
|||||||
public class GatewayService : IGatewayService
|
public class GatewayService : IGatewayService
|
||||||
{
|
{
|
||||||
private readonly IRouteStore _routeStore;
|
private readonly IRouteStore _routeStore;
|
||||||
private readonly IInstanceStore _instanceStore;
|
private readonly IClusterStore _clusterStore;
|
||||||
private readonly ILogger<GatewayService> _logger;
|
private readonly ILogger<GatewayService> _logger;
|
||||||
|
|
||||||
public GatewayService(
|
public GatewayService(
|
||||||
IRouteStore routeStore,
|
IRouteStore routeStore,
|
||||||
IInstanceStore instanceStore,
|
IClusterStore clusterStore,
|
||||||
ILogger<GatewayService> logger)
|
ILogger<GatewayService> logger)
|
||||||
{
|
{
|
||||||
_routeStore = routeStore;
|
_routeStore = routeStore;
|
||||||
_instanceStore = instanceStore;
|
_clusterStore = clusterStore;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<GatewayStatisticsDto> GetStatisticsAsync()
|
public async Task<GatewayStatisticsDto> GetStatisticsAsync()
|
||||||
{
|
{
|
||||||
var routes = await _routeStore.GetAllAsync();
|
var routes = await _routeStore.GetAllAsync();
|
||||||
var instances = await _instanceStore.GetAllAsync();
|
var clusters = await _clusterStore.GetAllAsync();
|
||||||
|
|
||||||
var activeRoutes = routes.Where(r => !r.IsDeleted).ToList();
|
var activeRoutes = routes.Where(r => !r.IsDeleted).ToList();
|
||||||
var activeInstances = instances.Where(i => !i.IsDeleted).ToList();
|
|
||||||
|
// Count destinations from all clusters
|
||||||
|
var totalInstances = clusters
|
||||||
|
.Where(c => !c.IsDeleted)
|
||||||
|
.Sum(c => c.Destinations?.Count(d => d.Status == 1) ?? 0);
|
||||||
|
|
||||||
|
var healthyInstances = clusters
|
||||||
|
.Where(c => !c.IsDeleted)
|
||||||
|
.Sum(c => c.Destinations?.Count(d => d.HealthStatus == 1) ?? 0);
|
||||||
|
|
||||||
return new GatewayStatisticsDto
|
return new GatewayStatisticsDto
|
||||||
{
|
{
|
||||||
TotalServices = activeRoutes.Select(r => r.ServiceName).Distinct().Count(),
|
TotalServices = activeRoutes.Select(r => r.ServiceName).Distinct().Count(),
|
||||||
GlobalRoutes = activeRoutes.Count(r => r.IsGlobal),
|
GlobalRoutes = activeRoutes.Count(r => r.IsGlobal),
|
||||||
TenantRoutes = activeRoutes.Count(r => !r.IsGlobal),
|
TenantRoutes = activeRoutes.Count(r => !r.IsGlobal),
|
||||||
TotalInstances = activeInstances.Count,
|
TotalInstances = totalInstances,
|
||||||
HealthyInstances = activeInstances.Count(i => i.Health == (int)InstanceHealth.Healthy),
|
HealthyInstances = healthyInstances,
|
||||||
RecentServices = activeRoutes
|
RecentServices = activeRoutes
|
||||||
.OrderByDescending(r => r.CreatedTime)
|
.OrderByDescending(r => r.CreatedTime)
|
||||||
.Take(5)
|
.Take(5)
|
||||||
.Select(MapToServiceDto)
|
.Select(r => MapToServiceDto(r, 0))
|
||||||
.ToList()
|
.ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -62,7 +70,7 @@ public class GatewayService : IGatewayService
|
|||||||
public async Task<List<GatewayServiceDto>> GetServicesAsync(bool globalOnly = false, string? tenantCode = null)
|
public async Task<List<GatewayServiceDto>> GetServicesAsync(bool globalOnly = false, string? tenantCode = null)
|
||||||
{
|
{
|
||||||
var routes = await _routeStore.GetAllAsync();
|
var routes = await _routeStore.GetAllAsync();
|
||||||
var instances = await _instanceStore.GetAllAsync();
|
var clusters = await _clusterStore.GetAllAsync();
|
||||||
|
|
||||||
var query = routes.Where(r => !r.IsDeleted);
|
var query = routes.Where(r => !r.IsDeleted);
|
||||||
|
|
||||||
@ -72,12 +80,14 @@ public class GatewayService : IGatewayService
|
|||||||
query = query.Where(r => r.TenantCode == tenantCode);
|
query = query.Where(r => r.TenantCode == tenantCode);
|
||||||
|
|
||||||
var routeList = query.OrderByDescending(r => r.CreatedTime).ToList();
|
var routeList = query.OrderByDescending(r => r.CreatedTime).ToList();
|
||||||
var clusters = routeList.Select(r => r.ClusterId).Distinct().ToList();
|
|
||||||
|
|
||||||
var instancesDict = instances
|
// Build instance count dict from clusters
|
||||||
.Where(i => clusters.Contains(i.ClusterId) && !i.IsDeleted)
|
var instancesDict = clusters
|
||||||
.GroupBy(i => i.ClusterId)
|
.Where(c => !c.IsDeleted && routeList.Any(r => r.ClusterId == c.ClusterId))
|
||||||
.ToDictionary(g => g.Key, g => g.Count());
|
.ToDictionary(
|
||||||
|
c => c.ClusterId,
|
||||||
|
c => c.Destinations?.Count(d => d.Status == 1) ?? 0
|
||||||
|
);
|
||||||
|
|
||||||
return routeList.Select(r => MapToServiceDto(r, instancesDict.GetValueOrDefault(r.ClusterId, 0))).ToList();
|
return routeList.Select(r => MapToServiceDto(r, instancesDict.GetValueOrDefault(r.ClusterId, 0))).ToList();
|
||||||
}
|
}
|
||||||
@ -92,8 +102,8 @@ public class GatewayService : IGatewayService
|
|||||||
|
|
||||||
if (route == null) return null;
|
if (route == null) return null;
|
||||||
|
|
||||||
var instances = await _instanceStore.GetAllAsync();
|
var cluster = await _clusterStore.FindByClusterIdAsync(route.ClusterId);
|
||||||
var instanceCount = instances.Count(i => i.ClusterId == route.ClusterId && !i.IsDeleted);
|
var instanceCount = cluster?.Destinations?.Count(d => d.Status == 1) ?? 0;
|
||||||
|
|
||||||
return MapToServiceDto(route, instanceCount);
|
return MapToServiceDto(route, instanceCount);
|
||||||
}
|
}
|
||||||
@ -118,20 +128,30 @@ public class GatewayService : IGatewayService
|
|||||||
throw new InvalidOperationException($"Service {dto.ServicePrefix} already registered");
|
throw new InvalidOperationException($"Service {dto.ServicePrefix} already registered");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add instance
|
// Create or get cluster
|
||||||
var instanceId = Guid.CreateVersion7().ToString("N");
|
var cluster = await _clusterStore.FindByClusterIdAsync(clusterId);
|
||||||
var instance = new GwServiceInstance
|
if (cluster == null)
|
||||||
{
|
{
|
||||||
Id = instanceId,
|
cluster = new GwCluster
|
||||||
|
{
|
||||||
|
Id = Guid.CreateVersion7().ToString("N"),
|
||||||
ClusterId = clusterId,
|
ClusterId = clusterId,
|
||||||
|
Name = $"{dto.ServicePrefix} Service",
|
||||||
|
Destinations = new List<GwDestination>()
|
||||||
|
};
|
||||||
|
await _clusterStore.CreateAsync(cluster);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add destination to cluster
|
||||||
|
var destination = new GwDestination
|
||||||
|
{
|
||||||
DestinationId = destinationId,
|
DestinationId = destinationId,
|
||||||
Address = dto.ServiceAddress,
|
Address = dto.ServiceAddress,
|
||||||
Weight = dto.Weight,
|
Weight = dto.Weight,
|
||||||
Health = (int)InstanceHealth.Healthy,
|
HealthStatus = 1, // Healthy
|
||||||
Status = (int)InstanceStatus.Active,
|
Status = 1 // Active
|
||||||
CreatedTime = DateTime.UtcNow
|
|
||||||
};
|
};
|
||||||
await _instanceStore.CreateAsync(instance);
|
await _clusterStore.AddDestinationAsync(clusterId, destination);
|
||||||
|
|
||||||
// Add route
|
// Add route
|
||||||
var routeId = Guid.CreateVersion7().ToString("N");
|
var routeId = Guid.CreateVersion7().ToString("N");
|
||||||
@ -141,7 +161,7 @@ public class GatewayService : IGatewayService
|
|||||||
TenantCode = dto.IsGlobal ? "" : dto.TenantCode ?? "",
|
TenantCode = dto.IsGlobal ? "" : dto.TenantCode ?? "",
|
||||||
ServiceName = dto.ServicePrefix,
|
ServiceName = dto.ServicePrefix,
|
||||||
ClusterId = clusterId,
|
ClusterId = clusterId,
|
||||||
PathPattern = pathPattern,
|
Match = new GwRouteMatch { Path = pathPattern },
|
||||||
Priority = dto.IsGlobal ? 0 : 10,
|
Priority = dto.IsGlobal ? 0 : 10,
|
||||||
Status = (int)RouteStatus.Active,
|
Status = (int)RouteStatus.Active,
|
||||||
IsGlobal = dto.IsGlobal,
|
IsGlobal = dto.IsGlobal,
|
||||||
@ -169,16 +189,8 @@ public class GatewayService : IGatewayService
|
|||||||
route.UpdatedTime = DateTime.UtcNow;
|
route.UpdatedTime = DateTime.UtcNow;
|
||||||
await _routeStore.UpdateAsync(route);
|
await _routeStore.UpdateAsync(route);
|
||||||
|
|
||||||
// Soft delete instances
|
// Note: We don't delete destinations when unregistering a service
|
||||||
var instances = await _instanceStore.GetAllAsync();
|
// The cluster and its destinations persist until explicitly deleted
|
||||||
var routeInstances = instances.Where(i => i.ClusterId == route.ClusterId && !i.IsDeleted).ToList();
|
|
||||||
|
|
||||||
foreach (var instance in routeInstances)
|
|
||||||
{
|
|
||||||
instance.IsDeleted = true;
|
|
||||||
instance.UpdatedTime = DateTime.UtcNow;
|
|
||||||
await _instanceStore.UpdateAsync(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation("Unregistered service {Service}", serviceName);
|
_logger.LogInformation("Unregistered service {Service}", serviceName);
|
||||||
|
|
||||||
@ -188,7 +200,7 @@ public class GatewayService : IGatewayService
|
|||||||
public async Task<List<GatewayRouteDto>> GetRoutesAsync(bool globalOnly = false)
|
public async Task<List<GatewayRouteDto>> GetRoutesAsync(bool globalOnly = false)
|
||||||
{
|
{
|
||||||
var routes = await _routeStore.GetAllAsync();
|
var routes = await _routeStore.GetAllAsync();
|
||||||
var instances = await _instanceStore.GetAllAsync();
|
var clusters = await _clusterStore.GetAllAsync();
|
||||||
|
|
||||||
var query = routes.Where(r => !r.IsDeleted);
|
var query = routes.Where(r => !r.IsDeleted);
|
||||||
|
|
||||||
@ -196,19 +208,21 @@ public class GatewayService : IGatewayService
|
|||||||
query = query.Where(r => r.IsGlobal);
|
query = query.Where(r => r.IsGlobal);
|
||||||
|
|
||||||
var routeList = query.OrderByDescending(r => r.Priority).ToList();
|
var routeList = query.OrderByDescending(r => r.Priority).ToList();
|
||||||
var clusters = routeList.Select(r => r.ClusterId).Distinct().ToList();
|
|
||||||
|
|
||||||
var instancesDict = instances
|
// Build instance count dict from clusters
|
||||||
.Where(i => clusters.Contains(i.ClusterId) && !i.IsDeleted)
|
var instancesDict = clusters
|
||||||
.GroupBy(i => i.ClusterId)
|
.Where(c => !c.IsDeleted && routeList.Any(r => r.ClusterId == c.ClusterId))
|
||||||
.ToDictionary(g => g.Key, g => g.Count());
|
.ToDictionary(
|
||||||
|
c => c.ClusterId,
|
||||||
|
c => c.Destinations?.Count(d => d.Status == 1) ?? 0
|
||||||
|
);
|
||||||
|
|
||||||
return routeList.Select(r => new GatewayRouteDto
|
return routeList.Select(r => new GatewayRouteDto
|
||||||
{
|
{
|
||||||
Id = r.Id,
|
Id = r.Id,
|
||||||
ServiceName = r.ServiceName,
|
ServiceName = r.ServiceName,
|
||||||
ClusterId = r.ClusterId,
|
ClusterId = r.ClusterId,
|
||||||
PathPattern = r.PathPattern,
|
PathPattern = r.Match.Path ?? "",
|
||||||
Priority = r.Priority,
|
Priority = r.Priority,
|
||||||
IsGlobal = r.IsGlobal,
|
IsGlobal = r.IsGlobal,
|
||||||
TenantCode = r.TenantCode,
|
TenantCode = r.TenantCode,
|
||||||
@ -236,7 +250,7 @@ public class GatewayService : IGatewayService
|
|||||||
TenantCode = dto.IsGlobal ? "" : dto.TenantCode ?? "",
|
TenantCode = dto.IsGlobal ? "" : dto.TenantCode ?? "",
|
||||||
ServiceName = dto.ServiceName,
|
ServiceName = dto.ServiceName,
|
||||||
ClusterId = dto.ClusterId,
|
ClusterId = dto.ClusterId,
|
||||||
PathPattern = dto.PathPattern,
|
Match = new GwRouteMatch { Path = dto.PathPattern },
|
||||||
Priority = dto.Priority,
|
Priority = dto.Priority,
|
||||||
Status = (int)RouteStatus.Active,
|
Status = (int)RouteStatus.Active,
|
||||||
IsGlobal = dto.IsGlobal,
|
IsGlobal = dto.IsGlobal,
|
||||||
@ -250,7 +264,7 @@ public class GatewayService : IGatewayService
|
|||||||
Id = route.Id,
|
Id = route.Id,
|
||||||
ServiceName = route.ServiceName,
|
ServiceName = route.ServiceName,
|
||||||
ClusterId = route.ClusterId,
|
ClusterId = route.ClusterId,
|
||||||
PathPattern = route.PathPattern,
|
PathPattern = route.Match.Path ?? "",
|
||||||
Priority = route.Priority,
|
Priority = route.Priority,
|
||||||
IsGlobal = route.IsGlobal,
|
IsGlobal = route.IsGlobal,
|
||||||
TenantCode = route.TenantCode,
|
TenantCode = route.TenantCode,
|
||||||
@ -261,81 +275,104 @@ public class GatewayService : IGatewayService
|
|||||||
|
|
||||||
public async Task<List<GatewayInstanceDto>> GetInstancesAsync(string clusterId)
|
public async Task<List<GatewayInstanceDto>> GetInstancesAsync(string clusterId)
|
||||||
{
|
{
|
||||||
var instances = await _instanceStore.GetAllAsync();
|
var cluster = await _clusterStore.FindByClusterIdAsync(clusterId);
|
||||||
var clusterInstances = instances
|
if (cluster == null || cluster.Destinations == null)
|
||||||
.Where(i => i.ClusterId == clusterId && !i.IsDeleted)
|
return new List<GatewayInstanceDto>();
|
||||||
.OrderByDescending(i => i.Weight)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return clusterInstances.Select(i => new GatewayInstanceDto
|
return cluster.Destinations
|
||||||
|
.Where(d => d.Status == 1)
|
||||||
|
.OrderByDescending(d => d.Weight)
|
||||||
|
.Select(d => new GatewayInstanceDto
|
||||||
{
|
{
|
||||||
Id = i.Id,
|
Id = d.DestinationId,
|
||||||
ClusterId = i.ClusterId,
|
ClusterId = clusterId,
|
||||||
DestinationId = i.DestinationId,
|
DestinationId = d.DestinationId,
|
||||||
Address = i.Address,
|
Address = d.Address ?? "",
|
||||||
Weight = i.Weight,
|
Weight = d.Weight,
|
||||||
Health = (int)i.Health,
|
Health = d.HealthStatus,
|
||||||
Status = (int)i.Status,
|
Status = d.Status,
|
||||||
CreatedAt = i.CreatedTime
|
CreatedAt = DateTime.UtcNow
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<GatewayInstanceDto> AddInstanceAsync(CreateGatewayInstanceDto dto)
|
public async Task<GatewayInstanceDto> AddInstanceAsync(CreateGatewayInstanceDto dto)
|
||||||
{
|
{
|
||||||
var existing = await _instanceStore.FindByDestinationAsync(dto.ClusterId, dto.DestinationId);
|
var destination = new GwDestination
|
||||||
if (existing != null && !existing.IsDeleted)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Instance {dto.DestinationId} already exists in cluster {dto.ClusterId}");
|
DestinationId = dto.DestinationId,
|
||||||
|
Address = dto.Address,
|
||||||
|
Weight = dto.Weight,
|
||||||
|
HealthStatus = 1, // Healthy
|
||||||
|
Status = 1 // Active
|
||||||
|
};
|
||||||
|
|
||||||
|
var cluster = await _clusterStore.AddDestinationAsync(dto.ClusterId, destination);
|
||||||
|
if (cluster == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Cluster {dto.ClusterId} not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
var instance = new GwServiceInstance
|
return new GatewayInstanceDto
|
||||||
{
|
{
|
||||||
Id = Guid.CreateVersion7().ToString("N"),
|
Id = dto.DestinationId,
|
||||||
ClusterId = dto.ClusterId,
|
ClusterId = dto.ClusterId,
|
||||||
DestinationId = dto.DestinationId,
|
DestinationId = dto.DestinationId,
|
||||||
Address = dto.Address,
|
Address = dto.Address,
|
||||||
Weight = dto.Weight,
|
Weight = dto.Weight,
|
||||||
Health = (int)InstanceHealth.Healthy,
|
Health = 1,
|
||||||
Status = (int)InstanceStatus.Active,
|
Status = 1,
|
||||||
CreatedTime = DateTime.UtcNow
|
CreatedAt = DateTime.UtcNow
|
||||||
};
|
|
||||||
|
|
||||||
await _instanceStore.CreateAsync(instance);
|
|
||||||
|
|
||||||
return new GatewayInstanceDto
|
|
||||||
{
|
|
||||||
Id = instance.Id,
|
|
||||||
ClusterId = instance.ClusterId,
|
|
||||||
DestinationId = instance.DestinationId,
|
|
||||||
Address = instance.Address,
|
|
||||||
Weight = instance.Weight,
|
|
||||||
Health = (int)instance.Health,
|
|
||||||
Status = (int)instance.Status,
|
|
||||||
CreatedAt = instance.CreatedTime
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> RemoveInstanceAsync(string instanceId)
|
public async Task<bool> RemoveInstanceAsync(string instanceId)
|
||||||
{
|
{
|
||||||
var instance = await _instanceStore.FindByIdAsync(instanceId);
|
// We need to find the cluster and destination
|
||||||
if (instance == null) return false;
|
// Since we don't have direct lookup, iterate through clusters
|
||||||
|
var clusters = await _clusterStore.GetAllAsync();
|
||||||
|
|
||||||
instance.IsDeleted = true;
|
foreach (var cluster in clusters)
|
||||||
instance.UpdatedTime = DateTime.UtcNow;
|
{
|
||||||
await _instanceStore.UpdateAsync(instance);
|
if (cluster.Destinations == null) continue;
|
||||||
|
|
||||||
|
var dest = cluster.Destinations.FirstOrDefault(d => d.DestinationId == instanceId);
|
||||||
|
if (dest != null)
|
||||||
|
{
|
||||||
|
await _clusterStore.RemoveDestinationAsync(cluster.ClusterId, instanceId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> UpdateInstanceWeightAsync(string instanceId, int weight)
|
public async Task<bool> UpdateInstanceWeightAsync(string instanceId, int weight)
|
||||||
{
|
{
|
||||||
var instance = await _instanceStore.FindByIdAsync(instanceId);
|
// Find the cluster containing this destination
|
||||||
if (instance == null) return false;
|
var clusters = await _clusterStore.GetAllAsync();
|
||||||
|
|
||||||
instance.Weight = weight;
|
foreach (var cluster in clusters)
|
||||||
instance.UpdatedTime = DateTime.UtcNow;
|
{
|
||||||
await _instanceStore.UpdateAsync(instance);
|
if (cluster.Destinations == null) continue;
|
||||||
|
|
||||||
|
var dest = cluster.Destinations.FirstOrDefault(d => d.DestinationId == instanceId);
|
||||||
|
if (dest != null)
|
||||||
|
{
|
||||||
|
var updatedDest = new GwDestination
|
||||||
|
{
|
||||||
|
DestinationId = dest.DestinationId,
|
||||||
|
Address = dest.Address ?? "",
|
||||||
|
Weight = weight,
|
||||||
|
HealthStatus = dest.HealthStatus,
|
||||||
|
Status = dest.Status
|
||||||
|
};
|
||||||
|
await _clusterStore.UpdateDestinationAsync(cluster.ClusterId, instanceId, updatedDest);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ReloadGatewayAsync()
|
public async Task ReloadGatewayAsync()
|
||||||
{
|
{
|
||||||
@ -351,7 +388,7 @@ public class GatewayService : IGatewayService
|
|||||||
ServicePrefix = route.ServiceName,
|
ServicePrefix = route.ServiceName,
|
||||||
ServiceName = route.ServiceName,
|
ServiceName = route.ServiceName,
|
||||||
ClusterId = route.ClusterId,
|
ClusterId = route.ClusterId,
|
||||||
PathPattern = route.PathPattern,
|
PathPattern = route.Match.Path ?? "",
|
||||||
ServiceAddress = "",
|
ServiceAddress = "",
|
||||||
DestinationId = "",
|
DestinationId = "",
|
||||||
Weight = 1,
|
Weight = 1,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user