using Admin.NET.Plugin.AiDOP.Entity; using Microsoft.Extensions.Logging; namespace Admin.NET.Plugin.AiDOP.SmartOps; public sealed class KpiTargetResolver : IKpiTargetResolver, ITransient { private readonly IKpiTargetConfigStore _store; private readonly ILegacyKpiTargetProvider _legacy; private readonly ILogger _logger; public KpiTargetResolver( IKpiTargetConfigStore store, ILegacyKpiTargetProvider legacy, ILogger logger) { _store = store; _legacy = legacy; _logger = logger; } public async Task ResolveAsync( long tenantId, long factoryId, string metricCode, string moduleCode, DateTime bizDate, CancellationToken ct = default) { var day = bizDate.Date; var now = DateTime.Now; var factoryHits = KpiTargetRules.ActiveOn( await _store.ListActiveScopeAsync(tenantId, factoryId, metricCode, ct), day); if (factoryHits.Count > 0) { var cfg = KpiTargetRules.PickSingle(factoryHits, $"工厂 {factoryId}/{metricCode}/{day:yyyy-MM-dd}"); return Formal(cfg, KpiTargetSnapshotSource.FormalFactory, now); } if (factoryId != 0) { var tenantHits = KpiTargetRules.ActiveOn( await _store.ListActiveScopeAsync(tenantId, 0, metricCode, ct), day); if (tenantHits.Count > 0) { var cfg = KpiTargetRules.PickSingle(tenantHits, $"租户默认 {metricCode}/{day:yyyy-MM-dd}"); return Formal(cfg, KpiTargetSnapshotSource.FormalTenantDefault, now); } } var legacy = await _legacy.TryResolveLegacyAsync(tenantId, factoryId, metricCode, moduleCode, day, ct); if (legacy != null && legacy.TargetValue is > 0) { _logger.LogWarning( "KPI 目标走兼容来源 {Source} tenant={TenantId} factory={FactoryId} metric={MetricCode} date={BizDate} value={Value}", legacy.TargetSource, tenantId, factoryId, metricCode, day, legacy.TargetValue); return legacy; } _logger.LogInformation( "KPI 目标缺失 tenant={TenantId} factory={FactoryId} metric={MetricCode} date={BizDate}", tenantId, factoryId, metricCode, day); return new KpiTargetSnapshot(null, null, KpiTargetSnapshotSource.Missing, now); } private static KpiTargetSnapshot Formal(AdoSmartOpsKpiTargetConfig cfg, string source, DateTime now) => new( cfg.TargetValue, cfg.Id, source, now, DateOnly.FromDateTime(cfg.EffectiveFrom.Date), cfg.EffectiveTo.HasValue ? DateOnly.FromDateTime(cfg.EffectiveTo.Value.Date) : null); }