|
|
@@ -1,6 +1,7 @@
|
|
|
using Admin.NET.Plugin.AiDOP.Dto.S8;
|
|
|
using Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
|
|
|
using Admin.NET.Plugin.AiDOP.Entity.S8;
|
|
|
+using Admin.NET.Plugin.AiDOP.Entity.S8.OrderFlow;
|
|
|
using Admin.NET.Plugin.AiDOP.Infrastructure.S8;
|
|
|
|
|
|
namespace Admin.NET.Plugin.AiDOP.Service.S8;
|
|
|
@@ -15,17 +16,21 @@ public class S8MonitoringService : ITransient
|
|
|
private readonly SqlSugarRepository<AdoS0DepartmentMaster> _deptRep;
|
|
|
// TASK-012-S9-QDC-RATIO-MIGRATION-2:result KPI summary 读取 RATIO 指标字典
|
|
|
private readonly SqlSugarRepository<AdoS8MonitorMetric> _metricRep;
|
|
|
+ // S9-RESULT-KPI-DEMO-BASELINE-AND-ORDER-FLOW-CALC-1:ORDER_FLOW_CYCLE_RATIO 半真实计算依赖订单链路阶段表
|
|
|
+ private readonly SqlSugarRepository<AdoS8OrderFlowStage> _orderFlowStageRep;
|
|
|
|
|
|
public S8MonitoringService(
|
|
|
SqlSugarRepository<AdoS8Exception> rep,
|
|
|
SqlSugarRepository<AdoS8ExceptionType> typeRep,
|
|
|
SqlSugarRepository<AdoS0DepartmentMaster> deptRep,
|
|
|
- SqlSugarRepository<AdoS8MonitorMetric> metricRep)
|
|
|
+ SqlSugarRepository<AdoS8MonitorMetric> metricRep,
|
|
|
+ SqlSugarRepository<AdoS8OrderFlowStage> orderFlowStageRep)
|
|
|
{
|
|
|
_rep = rep;
|
|
|
_typeRep = typeRep;
|
|
|
_deptRep = deptRep;
|
|
|
_metricRep = metricRep;
|
|
|
+ _orderFlowStageRep = orderFlowStageRep;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -741,10 +746,11 @@ public class S8MonitoringService : ITransient
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// TASK-012-S9-QDC-RATIO-MIGRATION-2:S9 result KPI summary(mock 阶段)。
|
|
|
- /// 来源:ado_s8_monitor_metric WHERE mechanism='RATIO' AND is_result_kpi=1。
|
|
|
- /// 本期 currentValue 始终 NULL;前端展示需标识"待接入真实计算"。
|
|
|
- /// 合并策略:baseline (tenant_id=0/factory_id=0) + 当前 tenant/factory 覆盖。
|
|
|
+ /// S9-RESULT-KPI-DEMO-BASELINE-AND-ORDER-FLOW-CALC-1:A+B 混合方案。
|
|
|
+ /// 4 个 KPI(ORDER_DELIVERY_RATE/PO_DELIVERY_RATE/IQC_PASS_RATE/WO_COMPLETION_RATE)→ DEMO_BASELINE 演示基线;
|
|
|
+ /// 1 个 KPI(ORDER_FLOW_CYCLE_RATIO)→ ORDER_FLOW_CALC 基于 ado_s8_order_flow_stage 半真实计算;
|
|
|
+ /// 不写 DB / 不接 period / 不启用 watch_rule。
|
|
|
+ /// 来源:ado_s8_monitor_metric WHERE mechanism='RATIO' AND is_result_kpi=1(保留字典顺序与 metricName)。
|
|
|
/// </summary>
|
|
|
public async Task<AdoS8ResultKpiSummaryDto> GetResultKpiSummaryAsync(long tenantId, long factoryId)
|
|
|
{
|
|
|
@@ -754,7 +760,6 @@ public class S8MonitoringService : ITransient
|
|
|
|| (x.TenantId == tenantId && x.FactoryId == factoryId)))
|
|
|
.ToListAsync();
|
|
|
|
|
|
- // tenant/factory 覆盖 baseline
|
|
|
var resolved = rows
|
|
|
.GroupBy(x => x.MetricCode)
|
|
|
.Select(g => g.OrderByDescending(x => x.FactoryId).ThenByDescending(x => x.TenantId).First())
|
|
|
@@ -762,22 +767,97 @@ public class S8MonitoringService : ITransient
|
|
|
.ThenBy(x => x.MetricCode)
|
|
|
.ToList();
|
|
|
|
|
|
- var items = resolved.Select(m => new AdoS8ResultKpiItemDto
|
|
|
+ var items = new List<AdoS8ResultKpiItemDto>(resolved.Count);
|
|
|
+ foreach (var m in resolved)
|
|
|
{
|
|
|
- MetricCode = m.MetricCode,
|
|
|
- MetricName = m.MetricName,
|
|
|
- ObjectCode = m.ObjectCode,
|
|
|
- Unit = string.IsNullOrWhiteSpace(m.Unit) ? "%" : m.Unit!,
|
|
|
- CurrentValue = null,
|
|
|
- TargetRatio = m.DefaultTargetRatio,
|
|
|
- DictionaryEnabled = m.Enabled,
|
|
|
- Remark = m.Remark,
|
|
|
- }).ToList();
|
|
|
+ var unit = string.IsNullOrWhiteSpace(m.Unit) ? "%" : m.Unit!;
|
|
|
+ if (_demoBaselineMap.TryGetValue(m.MetricCode, out var baseline))
|
|
|
+ {
|
|
|
+ items.Add(new AdoS8ResultKpiItemDto
|
|
|
+ {
|
|
|
+ MetricCode = m.MetricCode,
|
|
|
+ MetricName = m.MetricName,
|
|
|
+ ObjectCode = m.ObjectCode,
|
|
|
+ Unit = unit,
|
|
|
+ CurrentValue = baseline.CurrentValue,
|
|
|
+ TargetRatio = baseline.TargetRatio,
|
|
|
+ DictionaryEnabled = true,
|
|
|
+ Remark = baseline.Remark,
|
|
|
+ Source = "DEMO_BASELINE",
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else if (m.MetricCode == "ORDER_FLOW_CYCLE_RATIO")
|
|
|
+ {
|
|
|
+ var (cv, source) = await ComputeOrderFlowCycleRatioAsync(tenantId, factoryId);
|
|
|
+ items.Add(new AdoS8ResultKpiItemDto
|
|
|
+ {
|
|
|
+ MetricCode = m.MetricCode,
|
|
|
+ MetricName = m.MetricName,
|
|
|
+ ObjectCode = m.ObjectCode,
|
|
|
+ Unit = unit,
|
|
|
+ CurrentValue = cv,
|
|
|
+ TargetRatio = 90.0m,
|
|
|
+ DictionaryEnabled = true,
|
|
|
+ Remark = "基于已完成订单链路阶段 actual_days/planned_days 计算,pending 阶段暂不纳入",
|
|
|
+ Source = source,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ items.Add(new AdoS8ResultKpiItemDto
|
|
|
+ {
|
|
|
+ MetricCode = m.MetricCode,
|
|
|
+ MetricName = m.MetricName,
|
|
|
+ ObjectCode = m.ObjectCode,
|
|
|
+ Unit = unit,
|
|
|
+ CurrentValue = null,
|
|
|
+ TargetRatio = m.DefaultTargetRatio,
|
|
|
+ DictionaryEnabled = false,
|
|
|
+ Remark = m.Remark,
|
|
|
+ Source = "PENDING_REAL",
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
return new AdoS8ResultKpiSummaryDto
|
|
|
{
|
|
|
Items = items,
|
|
|
- Source = "DICTIONARY_MOCK",
|
|
|
+ Source = "MIXED_BASELINE",
|
|
|
};
|
|
|
}
|
|
|
+
|
|
|
+ private static readonly Dictionary<string, (decimal CurrentValue, decimal TargetRatio, string Remark)> _demoBaselineMap = new()
|
|
|
+ {
|
|
|
+ ["ORDER_DELIVERY_RATE"] = (92.0m, 95.0m, "演示基线,待真实订单交付口径接入"),
|
|
|
+ ["PO_DELIVERY_RATE"] = (88.0m, 95.0m, "演示基线,待采购到货真实表接入"),
|
|
|
+ ["IQC_PASS_RATE"] = (96.0m, 98.0m, "演示基线,待 IQC 检验真实表接入"),
|
|
|
+ ["WO_COMPLETION_RATE"] = (90.0m, 95.0m, "演示基线,待工单完工真实表接入"),
|
|
|
+ };
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 候选 A:actual_days IS NOT NULL 的已完成阶段中 actual_days <= planned_days 的占比。
|
|
|
+ /// pending 阶段不纳入 denominator;不依赖 status 字段;按 tenant/factory 过滤。
|
|
|
+ /// </summary>
|
|
|
+ private async Task<(decimal? CurrentValue, string Source)> ComputeOrderFlowCycleRatioAsync(long tenantId, long factoryId)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var stages = await _orderFlowStageRep.AsQueryable()
|
|
|
+ .Where(x => !x.IsDeleted && x.TenantId == tenantId && x.FactoryId == factoryId
|
|
|
+ && x.ActualDays != null)
|
|
|
+ .Select(x => new { x.ActualDays, x.PlannedDays })
|
|
|
+ .ToListAsync();
|
|
|
+
|
|
|
+ var denominator = stages.Count;
|
|
|
+ if (denominator == 0) return (null, "ORDER_FLOW_CALC_EMPTY");
|
|
|
+
|
|
|
+ var numerator = stages.Count(s => s.ActualDays!.Value <= s.PlannedDays);
|
|
|
+ var value = Math.Round(numerator * 100m / denominator, 1);
|
|
|
+ return (value, "ORDER_FLOW_CALC");
|
|
|
+ }
|
|
|
+ catch
|
|
|
+ {
|
|
|
+ return (null, "ORDER_FLOW_CALC_EMPTY");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|