SmartOpsKpiAggregateRuleRegistry.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. namespace Admin.NET.Plugin.AiDOP.SmartOps;
  2. /// <summary>
  3. /// KPI 原子聚合规则注册表(全局 S1-S9):指标如何从原子层二次聚合,以及支持哪些筛选字段。
  4. /// </summary>
  5. public static class SmartOpsKpiAggregateRuleRegistry
  6. {
  7. public const string DomainOrderDelivery = "order_delivery";
  8. public const string DomainWorkSchedule = "work_schedule";
  9. public const string DomainSupplyPurchase = "supply_purchase";
  10. public const string DomainInventory = "inventory";
  11. public const string AggSum = "sum";
  12. public const string AggRatio = "ratio";
  13. public const string AggAvg = "avg";
  14. public const string AggMin = "min";
  15. public const string AggMax = "max";
  16. public const string FilterFull = "filterable_full";
  17. public const string FilterPartial = "filterable_partial";
  18. public const string FilterSummaryOnly = "summary_only";
  19. private static readonly string[] OrderDeliveryFilters =
  20. ["dateStart", "dateEnd", "customer", "product", "orderNo", "productionLine"];
  21. private static readonly string[] WorkScheduleFilters =
  22. ["dateStart", "dateEnd", "product", "orderNo", "productionLine", "workOrder", "equipment"];
  23. private static readonly string[] SupplyPurchaseFilters =
  24. ["dateStart", "dateEnd", "supplier", "material", "poNo"];
  25. private static readonly string[] InventoryFilters =
  26. ["dateStart", "dateEnd", "warehouse", "material", "workOrder", "outboundNo"];
  27. public static SmartOpsKpiAggregateRule? TryGet(string? metricCode)
  28. {
  29. if (string.IsNullOrWhiteSpace(metricCode)) return null;
  30. return ResolveByPattern(metricCode.Trim());
  31. }
  32. public static bool SupportsFilter(string metricCode, SmartOpsDashboardFilter filter)
  33. {
  34. var rule = TryGet(metricCode);
  35. if (rule == null || rule.FilterMode == FilterSummaryOnly) return false;
  36. if (filter.IsEmpty) return true;
  37. var active = GetActiveFilterKeys(filter);
  38. if (active.Count == 0) return true;
  39. return active.All(k => rule.SupportedFilters.Contains(k, StringComparer.OrdinalIgnoreCase));
  40. }
  41. public static IReadOnlyList<string> GetUnsupportedFilters(string metricCode, SmartOpsDashboardFilter filter)
  42. {
  43. var rule = TryGet(metricCode);
  44. if (rule == null || filter.IsEmpty) return Array.Empty<string>();
  45. return GetActiveFilterKeys(filter)
  46. .Where(k => !rule.SupportedFilters.Contains(k, StringComparer.OrdinalIgnoreCase))
  47. .ToList();
  48. }
  49. public static IEnumerable<string> GetDomainsForModule(string moduleCode)
  50. {
  51. return moduleCode.ToUpperInvariant() switch
  52. {
  53. "S1" or "S7" => new[] { DomainOrderDelivery, DomainInventory },
  54. "S2" or "S6" => new[] { DomainWorkSchedule },
  55. "S3" or "S4" => new[] { DomainSupplyPurchase },
  56. "S5" => new[] { DomainInventory, DomainSupplyPurchase },
  57. "S9" => new[] { DomainOrderDelivery, DomainWorkSchedule, DomainSupplyPurchase, DomainInventory },
  58. _ => Array.Empty<string>()
  59. };
  60. }
  61. public static string ResolveFilterMode(string metricCode, SmartOpsDashboardFilter filter)
  62. {
  63. var rule = TryGet(metricCode);
  64. if (rule == null || rule.FilterMode == FilterSummaryOnly) return FilterSummaryOnly;
  65. if (filter.IsEmpty) return FilterSummaryOnly;
  66. if (SupportsFilter(metricCode, filter)) return FilterFull;
  67. return FilterPartial;
  68. }
  69. private static SmartOpsKpiAggregateRule? ResolveByPattern(string metricCode)
  70. {
  71. var parts = metricCode.Split('_', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
  72. if (parts.Length < 3) return null;
  73. var module = parts[0].ToUpperInvariant();
  74. if (module == "S8") return SummaryRule(metricCode);
  75. var domain = ResolveDomain(module, metricCode);
  76. if (domain == null) return SummaryRule(metricCode);
  77. var suffix = parts[^1];
  78. var aggregation = ResolveAggregation(suffix, metricCode);
  79. var filters = ResolveFilters(domain);
  80. return new SmartOpsKpiAggregateRule(metricCode, domain, aggregation, FilterPartial, filters);
  81. }
  82. private static string? ResolveDomain(string module, string metricCode)
  83. {
  84. if (module == "S9")
  85. {
  86. return metricCode.ToUpperInvariant() switch
  87. {
  88. var c when c.StartsWith("S9_L1_001", StringComparison.OrdinalIgnoreCase) => null,
  89. var c when c.StartsWith("S9_L1_002", StringComparison.OrdinalIgnoreCase) ||
  90. c.StartsWith("S9_L1_003", StringComparison.OrdinalIgnoreCase) => DomainOrderDelivery,
  91. var c when c.StartsWith("S9_L1_004", StringComparison.OrdinalIgnoreCase) => DomainWorkSchedule,
  92. var c when c.StartsWith("S9_L1_005", StringComparison.OrdinalIgnoreCase) => DomainInventory,
  93. _ => ResolveDomainByModule(module)
  94. };
  95. }
  96. return ResolveDomainByModule(module);
  97. }
  98. private static string? ResolveDomainByModule(string module) => module switch
  99. {
  100. "S1" or "S7" => DomainOrderDelivery,
  101. "S2" or "S6" => DomainWorkSchedule,
  102. "S3" or "S4" => DomainSupplyPurchase,
  103. "S5" => DomainInventory,
  104. _ => null
  105. };
  106. private static string ResolveAggregation(string suffix, string metricCode)
  107. {
  108. if (metricCode.Contains("数量", StringComparison.Ordinal) ||
  109. metricCode.EndsWith("_001", StringComparison.Ordinal) && metricCode.StartsWith("S8", StringComparison.OrdinalIgnoreCase))
  110. {
  111. return AggSum;
  112. }
  113. return suffix switch
  114. {
  115. "001" => AggAvg,
  116. "002" => AggRatio,
  117. "003" => AggAvg,
  118. "004" => AggAvg,
  119. "005" => AggAvg,
  120. _ => AggAvg
  121. };
  122. }
  123. private static string[] ResolveFilters(string domain) => domain switch
  124. {
  125. DomainOrderDelivery => OrderDeliveryFilters,
  126. DomainWorkSchedule => WorkScheduleFilters,
  127. DomainSupplyPurchase => SupplyPurchaseFilters,
  128. DomainInventory => InventoryFilters,
  129. _ => Array.Empty<string>()
  130. };
  131. private static SmartOpsKpiAggregateRule SummaryRule(string metricCode) =>
  132. new(metricCode, string.Empty, AggAvg, FilterSummaryOnly, Array.Empty<string>());
  133. private static List<string> GetActiveFilterKeys(SmartOpsDashboardFilter filter)
  134. {
  135. var keys = new List<string>();
  136. if (filter.DateStart.HasValue || filter.DateEnd.HasValue) keys.Add("dateStart");
  137. if (!string.IsNullOrWhiteSpace(filter.Product)) keys.Add("product");
  138. if (!string.IsNullOrWhiteSpace(filter.OrderNo)) keys.Add("orderNo");
  139. if (!string.IsNullOrWhiteSpace(filter.ProductionLine)) keys.Add("productionLine");
  140. if (!string.IsNullOrWhiteSpace(filter.Customer)) keys.Add("customer");
  141. if (!string.IsNullOrWhiteSpace(filter.Supplier)) keys.Add("supplier");
  142. if (!string.IsNullOrWhiteSpace(filter.Material)) keys.Add("material");
  143. if (!string.IsNullOrWhiteSpace(filter.PoNo)) keys.Add("poNo");
  144. if (!string.IsNullOrWhiteSpace(filter.Warehouse)) keys.Add("warehouse");
  145. if (!string.IsNullOrWhiteSpace(filter.WorkOrder)) keys.Add("workOrder");
  146. if (!string.IsNullOrWhiteSpace(filter.Equipment)) keys.Add("equipment");
  147. if (!string.IsNullOrWhiteSpace(filter.OutboundNo)) keys.Add("outboundNo");
  148. return keys;
  149. }
  150. }
  151. public sealed record SmartOpsKpiAggregateRule(
  152. string MetricCode,
  153. string DomainCode,
  154. string AggregationType,
  155. string FilterMode,
  156. string[] SupportedFilters);