diff --git a/src/Models/K8s/GatewayConfigModel.cs b/src/Models/K8s/GatewayConfigModel.cs
new file mode 100644
index 0000000..59a02ea
--- /dev/null
+++ b/src/Models/K8s/GatewayConfigModel.cs
@@ -0,0 +1,130 @@
+namespace Fengling.Console.Models.K8s;
+
+///
+/// K8s Service 路由配置模型
+/// 从 Service Label 解析得到的网关配置信息
+///
+public sealed class GatewayConfigModel
+{
+ ///
+ /// 服务名称(必需)
+ /// 对应 Label: app-router-name
+ ///
+ public string ServiceName { get; set; } = "";
+
+ ///
+ /// 集群 ID(必需)
+ /// 对应 Label: app-cluster-name
+ ///
+ public string ClusterId { get; set; } = "";
+
+ ///
+ /// 目标地址 ID(必需)
+ /// 对应 Label: app-cluster-destination
+ ///
+ public string DestinationId { get; set; } = "";
+
+ ///
+ /// 主机地址(可选)
+ /// 对应 Label: app-router-host
+ ///
+ public string? Host { get; set; }
+
+ ///
+ /// 路径前缀(必需)
+ /// 对应 Label: app-router-prefix
+ ///
+ public string PathPrefix { get; set; } = "";
+
+ ///
+ /// 租户代码(可选)
+ ///
+ public string? TenantCode { get; set; }
+
+ ///
+ /// 命名空间
+ ///
+ public string Namespace { get; set; } = "";
+
+ ///
+ /// 服务地址(ClusterIP 或 ExternalIP)
+ ///
+ public string? ServiceAddress { get; set; }
+
+ ///
+ /// 服务端口号
+ ///
+ public int? Port { get; set; }
+
+ ///
+ /// 是否全局路由
+ ///
+ public bool IsGlobal { get; set; } = true;
+
+ ///
+ /// 权重
+ ///
+ public int Weight { get; set; } = 1;
+
+ ///
+ /// 版本
+ ///
+ public string Version { get; set; } = "v1";
+
+ ///
+ /// 原始标签字典
+ ///
+ public IDictionary RawLabels { get; set; } = new Dictionary();
+}
+
+///
+/// 配置解析结果
+///
+public sealed class GatewayConfigParseResult
+{
+ ///
+ /// 是否解析成功
+ ///
+ public bool Success { get; init; }
+
+ ///
+ /// 解析成功的配置模型
+ ///
+ public GatewayConfigModel? Config { get; init; }
+
+ ///
+ /// 错误信息列表
+ ///
+ public IReadOnlyList Errors { get; init; } = Array.Empty();
+
+ ///
+ /// 警告信息列表
+ ///
+ public IReadOnlyList Warnings { get; init; } = Array.Empty();
+
+ ///
+ /// 创建成功结果
+ ///
+ public static GatewayConfigParseResult Succeed(GatewayConfigModel config, IEnumerable? warnings = null)
+ {
+ return new GatewayConfigParseResult
+ {
+ Success = true,
+ Config = config,
+ Warnings = warnings?.ToList() ?? new List()
+ };
+ }
+
+ ///
+ /// 创建失败结果
+ ///
+ public static GatewayConfigParseResult Failed(IEnumerable errors, IEnumerable? warnings = null)
+ {
+ return new GatewayConfigParseResult
+ {
+ Success = false,
+ Errors = errors.ToList(),
+ Warnings = warnings?.ToList() ?? new List()
+ };
+ }
+}
diff --git a/src/Services/ServiceLabelParser.cs b/src/Services/ServiceLabelParser.cs
new file mode 100644
index 0000000..41bfcae
--- /dev/null
+++ b/src/Services/ServiceLabelParser.cs
@@ -0,0 +1,246 @@
+using Fengling.Console.Models.K8s;
+using k8s.Models;
+
+namespace Fengling.Console.Services;
+
+///
+/// K8s Service Label 解析器
+/// 负责从 Service 的 Labels 中解析网关配置信息
+///
+public sealed class ServiceLabelParser
+{
+ // Label 键名常量
+ private const string LabelHost = "app-router-host";
+ private const string LabelServiceName = "app-router-name";
+ private const string LabelPathPrefix = "app-router-prefix";
+ private const string LabelClusterId = "app-cluster-name";
+ private const string LabelDestinationId = "app-cluster-destination";
+ private const string LabelTenantCode = "app-router-tenant";
+ private const string LabelVersion = "app-router-version";
+ private const string LabelWeight = "app-router-weight";
+
+ private readonly ILogger _logger;
+
+ public ServiceLabelParser(ILogger logger)
+ {
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ }
+
+ ///
+ /// 解析 Service 的 Label,转换为 GatewayConfigModel
+ ///
+ /// K8s Service 对象
+ /// 解析结果
+ public GatewayConfigParseResult Parse(V1Service service)
+ {
+ ArgumentNullException.ThrowIfNull(service);
+
+ var metadata = service.Metadata;
+ if (metadata == null)
+ {
+ return GatewayConfigParseResult.Failed(new[] { "Service Metadata 为空" });
+ }
+
+ var labels = metadata.Labels ?? new Dictionary();
+ var errors = new List();
+ var warnings = new List();
+
+ // 提取必需字段
+ var serviceName = GetLabelValue(labels, LabelServiceName);
+ var clusterId = GetLabelValue(labels, LabelClusterId);
+ var pathPrefix = GetLabelValue(labels, LabelPathPrefix);
+ var destinationId = GetLabelValue(labels, LabelDestinationId);
+
+ // 验证必需字段
+ if (string.IsNullOrWhiteSpace(serviceName))
+ {
+ errors.Add($"缺少必需的 Label: {LabelServiceName}");
+ }
+
+ if (string.IsNullOrWhiteSpace(clusterId))
+ {
+ errors.Add($"缺少必需的 Label: {LabelClusterId}");
+ }
+
+ if (string.IsNullOrWhiteSpace(pathPrefix))
+ {
+ errors.Add($"缺少必需的 Label: {LabelPathPrefix}");
+ }
+
+ if (string.IsNullOrWhiteSpace(destinationId))
+ {
+ errors.Add($"缺少必需的 Label: {LabelDestinationId}");
+ }
+
+ // 如果有验证错误,返回失败结果
+ if (errors.Count > 0)
+ {
+ _logger.LogWarning("Service {ServiceName}/{Namespace} Label 验证失败: {Errors}",
+ metadata.Name,
+ metadata.NamespaceProperty,
+ string.Join("; ", errors));
+
+ return GatewayConfigParseResult.Failed(errors, warnings);
+ }
+
+ // 提取可选字段
+ var host = GetLabelValue(labels, LabelHost);
+ var tenantCode = GetLabelValue(labels, LabelTenantCode);
+ var version = GetLabelValue(labels, LabelVersion) ?? "v1";
+
+ // 解析权重
+ var weight = 1;
+ var weightStr = GetLabelValue(labels, LabelWeight);
+ if (!string.IsNullOrWhiteSpace(weightStr) && !int.TryParse(weightStr, out weight))
+ {
+ warnings.Add($"Label {LabelWeight} 值 '{weightStr}' 不是有效的整数,使用默认值 1");
+ weight = 1;
+ }
+
+ // 获取服务地址
+ var serviceAddress = ExtractServiceAddress(service);
+ var port = ExtractServicePort(service);
+
+ // 构建配置模型
+ var config = new GatewayConfigModel
+ {
+ ServiceName = serviceName!,
+ ClusterId = clusterId!,
+ DestinationId = destinationId!,
+ Host = host,
+ PathPrefix = pathPrefix!,
+ TenantCode = tenantCode,
+ Namespace = metadata.NamespaceProperty ?? "",
+ ServiceAddress = serviceAddress,
+ Port = port,
+ Version = version,
+ Weight = weight,
+ IsGlobal = string.IsNullOrWhiteSpace(tenantCode),
+ RawLabels = new Dictionary(labels)
+ };
+
+ _logger.LogDebug("成功解析 Service {ServiceName}/{Namespace} 的 Label 配置",
+ metadata.Name,
+ metadata.NamespaceProperty);
+
+ return GatewayConfigParseResult.Succeed(config, warnings);
+ }
+
+ ///
+ /// 批量解析多个 Service
+ ///
+ /// Service 列表
+ /// 解析结果列表
+ public IEnumerable ParseMany(IEnumerable services)
+ {
+ ArgumentNullException.ThrowIfNull(services);
+
+ foreach (var service in services)
+ {
+ yield return Parse(service);
+ }
+ }
+
+ ///
+ /// 检查 Service 是否包含必需的网关路由 Label
+ ///
+ /// K8s Service 对象
+ /// 是否包含必需 Label
+ public bool HasRequiredLabels(V1Service service)
+ {
+ ArgumentNullException.ThrowIfNull(service);
+
+ var metadata = service.Metadata;
+ if (metadata?.Labels == null)
+ {
+ return false;
+ }
+
+ var labels = metadata.Labels;
+
+ // 检查所有必需 Label 是否存在且不为空
+ return !string.IsNullOrWhiteSpace(GetLabelValue(labels, LabelServiceName))
+ && !string.IsNullOrWhiteSpace(GetLabelValue(labels, LabelClusterId))
+ && !string.IsNullOrWhiteSpace(GetLabelValue(labels, LabelPathPrefix))
+ && !string.IsNullOrWhiteSpace(GetLabelValue(labels, LabelDestinationId));
+ }
+
+ ///
+ /// 获取 Label 值(不区分大小写)
+ ///
+ private static string? GetLabelValue(IDictionary labels, string key)
+ {
+ if (labels.TryGetValue(key, out var value))
+ {
+ return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
+ }
+
+ // 尝试不区分大小写查找
+ var match = labels.FirstOrDefault(kvp =>
+ kvp.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
+
+ return string.IsNullOrWhiteSpace(match.Value) ? null : match.Value.Trim();
+ }
+
+ ///
+ /// 提取服务地址
+ ///
+ private static string? ExtractServiceAddress(V1Service service)
+ {
+ var spec = service.Spec;
+ if (spec == null)
+ {
+ return null;
+ }
+
+ // 优先使用 ClusterIP(如果不是 None)
+ if (!string.IsNullOrWhiteSpace(spec.ClusterIP) && spec.ClusterIP != "None")
+ {
+ return spec.ClusterIP;
+ }
+
+ // 其次使用 ExternalIP
+ if (spec.ExternalIPs?.Count > 0)
+ {
+ return spec.ExternalIPs[0];
+ }
+
+ // 最后使用 ExternalName
+ if (!string.IsNullOrWhiteSpace(spec.ExternalName))
+ {
+ return spec.ExternalName;
+ }
+
+ return null;
+ }
+
+ ///
+ /// 提取服务端口号
+ ///
+ private static int? ExtractServicePort(V1Service service)
+ {
+ var spec = service.Spec;
+ if (spec?.Ports == null || spec.Ports.Count == 0)
+ {
+ return null;
+ }
+
+ // 优先返回第一个端口的端口号
+ return spec.Ports[0].Port;
+ }
+}
+
+///
+/// ServiceLabelParser 扩展方法
+///
+public static class ServiceLabelParserExtensions
+{
+ ///
+ /// 添加 ServiceLabelParser 服务
+ ///
+ public static IServiceCollection AddServiceLabelParser(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ return services;
+ }
+}