namespace Admin.NET.Plugin.AiDOP.SmartOps; /// /// KPI 原子聚合规则注册表(全局 S1-S9):指标如何从原子层二次聚合,以及支持哪些筛选字段。 /// public static class SmartOpsKpiAggregateRuleRegistry { public const string DomainOrderDelivery = "order_delivery"; public const string DomainWorkSchedule = "work_schedule"; public const string DomainSupplyPurchase = "supply_purchase"; public const string DomainInventory = "inventory"; public const string AggSum = "sum"; public const string AggRatio = "ratio"; public const string AggAvg = "avg"; public const string AggMin = "min"; public const string AggMax = "max"; public const string FilterFull = "filterable_full"; public const string FilterPartial = "filterable_partial"; public const string FilterSummaryOnly = "summary_only"; private static readonly string[] OrderDeliveryFilters = ["dateStart", "dateEnd", "customer", "product", "orderNo", "productionLine"]; private static readonly string[] WorkScheduleFilters = ["dateStart", "dateEnd", "product", "orderNo", "productionLine", "workOrder", "equipment"]; private static readonly string[] SupplyPurchaseFilters = ["dateStart", "dateEnd", "supplier", "material", "poNo"]; private static readonly string[] InventoryFilters = ["dateStart", "dateEnd", "warehouse", "material", "workOrder", "outboundNo"]; public static SmartOpsKpiAggregateRule? TryGet(string? metricCode) { if (string.IsNullOrWhiteSpace(metricCode)) return null; return ResolveByPattern(metricCode.Trim()); } public static bool SupportsFilter(string metricCode, SmartOpsDashboardFilter filter) { var rule = TryGet(metricCode); if (rule == null || rule.FilterMode == FilterSummaryOnly) return false; if (filter.IsEmpty) return true; var active = GetActiveFilterKeys(filter); if (active.Count == 0) return true; return active.All(k => rule.SupportedFilters.Contains(k, StringComparer.OrdinalIgnoreCase)); } public static IReadOnlyList GetUnsupportedFilters(string metricCode, SmartOpsDashboardFilter filter) { var rule = TryGet(metricCode); if (rule == null || filter.IsEmpty) return Array.Empty(); return GetActiveFilterKeys(filter) .Where(k => !rule.SupportedFilters.Contains(k, StringComparer.OrdinalIgnoreCase)) .ToList(); } public static IEnumerable GetDomainsForModule(string moduleCode) { return moduleCode.ToUpperInvariant() switch { "S1" or "S7" => new[] { DomainOrderDelivery, DomainInventory }, "S2" or "S6" => new[] { DomainWorkSchedule }, "S3" or "S4" => new[] { DomainSupplyPurchase }, "S5" => new[] { DomainInventory, DomainSupplyPurchase }, "S9" => new[] { DomainOrderDelivery, DomainWorkSchedule, DomainSupplyPurchase, DomainInventory }, _ => Array.Empty() }; } public static string ResolveFilterMode(string metricCode, SmartOpsDashboardFilter filter) { var rule = TryGet(metricCode); if (rule == null || rule.FilterMode == FilterSummaryOnly) return FilterSummaryOnly; if (filter.IsEmpty) return FilterSummaryOnly; if (SupportsFilter(metricCode, filter)) return FilterFull; return FilterPartial; } private static SmartOpsKpiAggregateRule? ResolveByPattern(string metricCode) { var parts = metricCode.Split('_', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (parts.Length < 3) return null; var module = parts[0].ToUpperInvariant(); if (module == "S8") return SummaryRule(metricCode); var domain = ResolveDomain(module, metricCode); if (domain == null) return SummaryRule(metricCode); var suffix = parts[^1]; var aggregation = ResolveAggregation(suffix, metricCode); var filters = ResolveFilters(domain); return new SmartOpsKpiAggregateRule(metricCode, domain, aggregation, FilterPartial, filters); } private static string? ResolveDomain(string module, string metricCode) { if (module == "S9") { return metricCode.ToUpperInvariant() switch { var c when c.StartsWith("S9_L1_001", StringComparison.OrdinalIgnoreCase) => null, var c when c.StartsWith("S9_L1_002", StringComparison.OrdinalIgnoreCase) || c.StartsWith("S9_L1_003", StringComparison.OrdinalIgnoreCase) => DomainOrderDelivery, var c when c.StartsWith("S9_L1_004", StringComparison.OrdinalIgnoreCase) => DomainWorkSchedule, var c when c.StartsWith("S9_L1_005", StringComparison.OrdinalIgnoreCase) => DomainInventory, _ => ResolveDomainByModule(module) }; } return ResolveDomainByModule(module); } private static string? ResolveDomainByModule(string module) => module switch { "S1" or "S7" => DomainOrderDelivery, "S2" or "S6" => DomainWorkSchedule, "S3" or "S4" => DomainSupplyPurchase, "S5" => DomainInventory, _ => null }; private static string ResolveAggregation(string suffix, string metricCode) { if (metricCode.Contains("数量", StringComparison.Ordinal) || metricCode.EndsWith("_001", StringComparison.Ordinal) && metricCode.StartsWith("S8", StringComparison.OrdinalIgnoreCase)) { return AggSum; } return suffix switch { "001" => AggAvg, "002" => AggRatio, "003" => AggAvg, "004" => AggAvg, "005" => AggAvg, _ => AggAvg }; } private static string[] ResolveFilters(string domain) => domain switch { DomainOrderDelivery => OrderDeliveryFilters, DomainWorkSchedule => WorkScheduleFilters, DomainSupplyPurchase => SupplyPurchaseFilters, DomainInventory => InventoryFilters, _ => Array.Empty() }; private static SmartOpsKpiAggregateRule SummaryRule(string metricCode) => new(metricCode, string.Empty, AggAvg, FilterSummaryOnly, Array.Empty()); private static List GetActiveFilterKeys(SmartOpsDashboardFilter filter) { var keys = new List(); if (filter.DateStart.HasValue || filter.DateEnd.HasValue) keys.Add("dateStart"); if (!string.IsNullOrWhiteSpace(filter.Product)) keys.Add("product"); if (!string.IsNullOrWhiteSpace(filter.OrderNo)) keys.Add("orderNo"); if (!string.IsNullOrWhiteSpace(filter.ProductionLine)) keys.Add("productionLine"); if (!string.IsNullOrWhiteSpace(filter.Customer)) keys.Add("customer"); if (!string.IsNullOrWhiteSpace(filter.Supplier)) keys.Add("supplier"); if (!string.IsNullOrWhiteSpace(filter.Material)) keys.Add("material"); if (!string.IsNullOrWhiteSpace(filter.PoNo)) keys.Add("poNo"); if (!string.IsNullOrWhiteSpace(filter.Warehouse)) keys.Add("warehouse"); if (!string.IsNullOrWhiteSpace(filter.WorkOrder)) keys.Add("workOrder"); if (!string.IsNullOrWhiteSpace(filter.Equipment)) keys.Add("equipment"); if (!string.IsNullOrWhiteSpace(filter.OutboundNo)) keys.Add("outboundNo"); return keys; } } public sealed record SmartOpsKpiAggregateRule( string MetricCode, string DomainCode, string AggregationType, string FilterMode, string[] SupportedFilters);