| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- namespace Admin.NET.Plugin.AiDOP.Infrastructure;
- /// <summary>
- /// 期量差与绿黄红(见 doc/plan/关键定义说明/期量差说明.md)。
- /// 达成比例 = 实际 ÷ 目标 × 100%。
- /// higher_is_better 默认黄 95 / 严重红 80;lower_is_better 默认黄 110 / 严重红 120。
- /// YellowThreshold 分绿黄红;RedThreshold 仅决定红色是否严重告警(闪烁)。
- /// </summary>
- public static class AidopS4KpiMerge
- {
- public const string Green = "green";
- public const string Yellow = "yellow";
- public const string Red = "red";
- public const decimal DefaultHigherYellow = 95m;
- public const decimal DefaultHigherRed = 80m;
- public const decimal DefaultLowerYellow = 110m;
- public const decimal DefaultLowerRed = 120m;
- public sealed record KpiAchievementEvaluation(
- string Status,
- decimal? RatioPercent,
- bool IsBeyondRedThreshold,
- decimal EffectiveYellowThreshold,
- decimal EffectiveRedThreshold);
- public static bool IsLowerBetter(string? direction) =>
- string.Equals(direction, "lower_is_better", StringComparison.OrdinalIgnoreCase);
- /// <summary>
- /// 保存校验。两个阈值都为空时通过;只配一个时另一个按方向默认值参与组合校验。
- /// 不交换配置值。
- /// </summary>
- public static bool TryValidateThresholds(
- string? direction,
- decimal? yellowThreshold,
- decimal? redThreshold,
- out string? error)
- {
- error = null;
- if (yellowThreshold == null && redThreshold == null)
- return true;
- var lower = IsLowerBetter(direction);
- var yellow = yellowThreshold ?? (lower ? DefaultLowerYellow : DefaultHigherYellow);
- var red = redThreshold ?? (lower ? DefaultLowerRed : DefaultHigherRed);
- if (lower)
- {
- if (yellow >= 100m && yellow <= red)
- return true;
- error = "lower_is_better 阈值不合法,应满足 100 <= YellowThreshold <= RedThreshold。";
- return false;
- }
- if (red > 0m && red <= yellow && yellow <= 100m)
- return true;
- error = "higher_is_better 阈值不合法,应满足 0 < RedThreshold <= YellowThreshold <= 100。";
- return false;
- }
- public static KpiAchievementEvaluation Evaluate(
- decimal? current,
- decimal? target,
- string? direction,
- decimal? yellowThreshold = null,
- decimal? redThreshold = null)
- {
- var (yellow, red) = ResolveEffectiveThresholds(direction, yellowThreshold, redThreshold);
- if (current == null || target == null || target.Value == 0)
- return new KpiAchievementEvaluation(Yellow, null, false, yellow, red);
- var ratio = current.Value / target.Value * 100m;
- if (IsLowerBetter(direction))
- {
- var status = ratio <= 100m ? Green : ratio <= yellow ? Yellow : Red;
- var blink = status == Red && ratio > red;
- return new KpiAchievementEvaluation(status, ratio, blink, yellow, red);
- }
- {
- var status = ratio >= 100m ? Green : ratio >= yellow ? Yellow : Red;
- var blink = status == Red && ratio < red;
- return new KpiAchievementEvaluation(status, ratio, blink, yellow, red);
- }
- }
- public static string AchievementLevel(
- decimal? current, decimal? target, string direction,
- decimal? yellowThreshold = null, decimal? redThreshold = null) =>
- Evaluate(current, target, direction, yellowThreshold, redThreshold).Status;
- public static bool IsBeyondRedThreshold(
- decimal? current,
- decimal? target,
- string? direction,
- decimal? yellowThreshold = null,
- decimal? redThreshold = null) =>
- Evaluate(current, target, direction, yellowThreshold, redThreshold).IsBeyondRedThreshold;
- /// <summary>期量差数值(当前−目标);展示用箭头与符号在调用方处理。</summary>
- public static decimal? GapValue(decimal? current, decimal? target)
- {
- if (current == null || target == null) return null;
- return current.Value - target.Value;
- }
- public static string GapArrow(decimal? gap)
- {
- if (gap == null) return "flat";
- return gap.Value >= 0 ? "up" : "down";
- }
- private static (decimal Yellow, decimal Red) ResolveEffectiveThresholds(
- string? direction,
- decimal? yellowThreshold,
- decimal? redThreshold)
- {
- var lower = IsLowerBetter(direction);
- var defaults = lower
- ? (DefaultLowerYellow, DefaultLowerRed)
- : (DefaultHigherYellow, DefaultHigherRed);
- if (!TryValidateThresholds(direction, yellowThreshold, redThreshold, out _))
- return defaults;
- return (
- yellowThreshold ?? defaults.Item1,
- redThreshold ?? defaults.Item2);
- }
- }
|