| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- namespace Admin.NET.Plugin.AiDOP.SmartOps;
- public sealed record KpiResultDecision(string Status, string? Reason, decimal? Value);
- /// <summary>KPI 日值写入 result_status / result_reason 的共用片段。</summary>
- public static class KpiResultWrite
- {
- public const string SetClause = "result_status=@ResultStatus, result_reason=@ResultReason, ";
- public const string InsertCols = "result_status, result_reason, ";
- public const string InsertVals = "@ResultStatus, @ResultReason, ";
- public const string FromMetricSet =
- "result_status=IF(@MetricValue IS NULL,'NO_DATA','OK'), result_reason=IF(@MetricValue IS NULL,'窗口内无业务数据',NULL), ";
- public const string FromMetricCols = "result_status, result_reason, ";
- public const string FromMetricVals =
- "IF(@MetricValue IS NULL,'NO_DATA','OK'), IF(@MetricValue IS NULL,'窗口内无业务数据',NULL), ";
- public static SugarParameter Status(string status) => new("@ResultStatus", status);
- public static SugarParameter Reason(string? reason) => new("@ResultReason", (object?)reason ?? DBNull.Value);
- }
- /// <summary>
- /// 有值 → OK;无业务 → NO_DATA;依赖语义被登记来源标明不支持 → SOURCE_UNSUPPORTED,值置空。
- /// 对象类语义未登记 → FAILED。
- /// </summary>
- public static class KpiResultStatusResolver
- {
- public static KpiResultDecision Resolve(
- string metricCode,
- decimal? value,
- IReadOnlyDictionary<string, string> unsupported,
- IReadOnlySet<string> registeredObjects)
- {
- var reasons = new List<string>();
- foreach (var dep in KpiSemanticDependency.For(metricCode))
- {
- if (dep is "无")
- continue;
- if (dep is "S6_REPORT" or "FQC")
- {
- var obj = dep == "FQC" ? "FQC_TASK" : "S6_REPORT";
- if (!registeredObjects.Contains(obj))
- reasons.Add($"租户未登记 {obj} 来源");
- continue;
- }
- if (unsupported.TryGetValue(dep, out var reason))
- reasons.Add(string.IsNullOrWhiteSpace(reason) ? dep : reason);
- }
- if (reasons.Count > 0)
- {
- var failed = reasons.Any(r => r.Contains("未登记", StringComparison.Ordinal));
- var status = failed && reasons.All(r => r.Contains("未登记", StringComparison.Ordinal))
- ? "FAILED"
- : "SOURCE_UNSUPPORTED";
- return new KpiResultDecision(status, string.Join(";", reasons), null);
- }
- return value is null
- ? new KpiResultDecision("NO_DATA", "窗口内无业务数据", null)
- : new KpiResultDecision("OK", null, value);
- }
- }
- public sealed class KpiResultStatusService : ITransient
- {
- private readonly ISqlSugarClient _db;
- public KpiResultStatusService(ISqlSugarClient db) => _db = db;
- public async Task<KpiResultDecision> ResolveAsync(long tenantId, string metricCode, decimal? value, CancellationToken cancellationToken = default)
- {
- var unsupportedRows = await _db.Ado.SqlQueryAsync<CapRow>(
- """
- SELECT c.semantic_code AS SemanticCode, c.reason AS Reason
- FROM mdp_tenant_std_source s
- JOIN mdp_source_capability c ON c.source_system = s.source_system AND c.supported = 0
- WHERE s.tenant_id = @tenant
- """,
- new { tenant = tenantId });
- cancellationToken.ThrowIfCancellationRequested();
- var unsupported = unsupportedRows
- .GroupBy(r => r.SemanticCode, StringComparer.Ordinal)
- .ToDictionary(g => g.Key, g => g.First().Reason ?? "", StringComparer.Ordinal);
- var objects = await _db.Ado.SqlQueryAsync<string>(
- "SELECT std_object FROM mdp_tenant_std_source WHERE tenant_id=@tenant",
- new { tenant = tenantId });
- return KpiResultStatusResolver.Resolve(metricCode, value, unsupported, objects.ToHashSet(StringComparer.Ordinal));
- }
- private sealed class CapRow
- {
- public string SemanticCode { get; set; } = "";
- public string? Reason { get; set; }
- }
- }
|