namespace Admin.NET.Plugin.AiDOP.SmartOps; /// /// 九宫格卡片同时保留最后可用值和最新一次计算。展示状态只看行主键,不看日期是否相同。 /// public static class HomeGridDisplayPolicy { public sealed class Row { public long Id { get; init; } public decimal? MetricValue { get; init; } public decimal? TargetValue { get; init; } public string? ResultStatus { get; init; } public string? ResultReason { get; init; } public DateTime BizDate { get; init; } } public sealed class Card { public decimal? CurrentValue { get; init; } public decimal? TargetValue { get; init; } public string? ResultStatus { get; init; } public string? ResultReason { get; init; } public DateTime? BizDate { get; init; } public string? LatestResultStatus { get; init; } public string? LatestResultReason { get; init; } public DateTime? LatestBizDate { get; init; } public string DisplayState { get; init; } = "NO_DATA"; public string FreshnessState { get; init; } = "NO_VALUE"; public int? ValueAgeDays { get; init; } public int? FreshnessSlaDays { get; init; } public bool FutureBizDate { get; init; } } public static Card Decide(Row? usable, Row? latest, int? slaDays, DateTime today) { string displayState; if (usable == null) { displayState = string.Equals(latest?.ResultStatus, "SOURCE_UNSUPPORTED", StringComparison.OrdinalIgnoreCase) ? "SOURCE_UNSUPPORTED" : "NO_DATA"; } else if (latest != null && usable.Id == latest.Id) { displayState = "CURRENT_VALUE"; } else { displayState = "LAST_KNOWN_VALUE"; } var freshness = "NO_VALUE"; int? age = null; var future = false; if (usable != null) { if (slaDays is null or <= 0) { freshness = "POLICY_MISSING"; } else { var rawAge = (today.Date - usable.BizDate.Date).Days; if (rawAge < 0) { future = true; age = 0; } else { age = rawAge; } freshness = age > slaDays ? "STALE" : "FRESH"; } } return new Card { CurrentValue = usable?.MetricValue, TargetValue = usable?.TargetValue, ResultStatus = usable == null ? latest?.ResultStatus : NormalizeUsableStatus(usable.ResultStatus), ResultReason = usable?.ResultReason, BizDate = usable?.BizDate, LatestResultStatus = latest?.ResultStatus, LatestResultReason = latest?.ResultReason, LatestBizDate = latest?.BizDate, DisplayState = displayState, FreshnessState = freshness, ValueAgeDays = age, FreshnessSlaDays = slaDays, FutureBizDate = future, }; } private static string NormalizeUsableStatus(string? status) => string.IsNullOrWhiteSpace(status) ? "OK" : status; }